A:
要請幫忙一步一步詳細仔細的列出最終版的下列程式碼,謝謝
data/repository/*
AccountRepository.kt
CategoryRepository.kt
ProjectRepository.kt
SnapshotRepository.kt
TransactionRepository.kt
RecurringTransactionRepository.kt
Q:
好的,我們繼續進行專案的完整程式碼回顧。
接下來是 Repository (數據倉庫) 層。這一層扮演著至關重要的「橋樑」角色,它介於 ViewModel 和 DAO 之間,負責封裝所有數據來源的細節。ViewModel 只需與 Repository 溝通,而不需要知道數據究竟是來自資料庫、網路還是快取。這種設計大大提高了 App 的可維護性與可測試性。
以下是 data/repository/ 套件底下所有 Repository 介面與實作的最終版程式碼。
第廿三步:最終程式碼回顧 (Repository 篇)
檔案路徑: app/src/main/java/com/yourcompany/cashflow/data/repository/AccountRepository.kt
<details>
<summary><strong>點此展開 AccountRepository.kt 程式碼</strong></summary>
package com.yourcompany.cashflow.data.repository
import com.yourcompany.cashflow.data.local.dao.AccountDao
import com.yourcompany.cashflow.data.local.entity.Account
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import javax.inject.Singleton
interface AccountRepository {
fun getAllAccounts(): Flow<List<Account>>
suspend fun insert(account: Account)
suspend fun update(account: Account)
suspend fun delete(account: Account)
}
@Singleton
class AccountRepositoryImpl @Inject constructor(
private val accountDao: AccountDao
) : AccountRepository {
override fun getAllAccounts(): Flow<List<Account>> = accountDao.getAllAccounts()
override suspend fun insert(account: Account) = accountDao.insert(account)
override suspend fun update(account: Account) = accountDao.update(account)
override suspend fun delete(account: Account) = accountDao.delete(account)
}
</details>
檔案路徑: app/src/main/java/com/yourcompany/cashflow/data/repository/CategoryRepository.kt
<details>
<summary><strong>點此展開 CategoryRepository.kt 程式碼</strong></summary>
package com.yourcompany.cashflow.data.repository
import com.yourcompany.cashflow.data.local.dao.CategoryDao
import com.yourcompany.cashflow.data.local.entity.MainCategory
import com.yourcompany.cashflow.data.local.entity.SubCategory
import com.yourcompany.cashflow.data.local.entity.TransactionType
import com.yourcompany.cashflow.data.model.MainCategoryWithSubCategories
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import javax.inject.Singleton
interface CategoryRepository {
fun getMainCategoriesWithSubCategories(): Flow<List<MainCategoryWithSubCategories>>
fun getAllMainCategoriesByType(type: TransactionType): Flow<List<MainCategory>>
suspend fun insertMainCategory(category: MainCategory)
suspend fun insertSubCategory(category: SubCategory)
suspend fun updateMainCategory(category: MainCategory)
suspend fun updateSubCategory(category: SubCategory)
suspend fun deleteMainCategory(category: MainCategory)
suspend fun deleteSubCategory(category: SubCategory)
}
@Singleton
class CategoryRepositoryImpl @Inject constructor(
private val categoryDao: CategoryDao
) : CategoryRepository {
override fun getMainCategoriesWithSubCategories(): Flow<List<MainCategoryWithSubCategories>> =
categoryDao.getMainCategoriesWithSubCategories()
override fun getAllMainCategoriesByType(type: TransactionType): Flow<List<MainCategory>> =
categoryDao.getAllMainCategoriesByType(type)
override suspend fun insertMainCategory(category: MainCategory) = categoryDao.insertMainCategory(category)
override suspend fun insertSubCategory(category: SubCategory) = categoryDao.insertSubCategory(category)
override suspend fun updateMainCategory(category: MainCategory) = categoryDao.updateMainCategory(category)
override suspend fun updateSubCategory(category: SubCategory) = categoryDao.updateSubCategory(category)
override suspend fun deleteMainCategory(category: MainCategory) = categoryDao.deleteMainCategory(category)
override suspend fun deleteSubCategory(category: SubCategory) = categoryDao.deleteSubCategory(category)
}
</details>
檔案路徑: app/src/main/java/com/yourcompany/cashflow/data/repository/ProjectRepository.kt
<details>
<summary><strong>點此展開 ProjectRepository.kt 程式碼</strong></summary>
package com.yourcompany.cashflow.data.repository
import com.yourcompany.cashflow.data.local.dao.ProjectDao
import com.yourcompany.cashflow.data.local.entity.Project
import com.yourcompany.cashflow.data.local.entity.ProjectStatus
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import javax.inject.Singleton
interface ProjectRepository {
suspend fun insert(project: Project)
suspend fun update(project: Project)
suspend fun delete(project: Project)
fun getProjectById(id: Long): Flow<Project?>
fun getAllProjects(): Flow<List<Project>>
fun getProjectsByStatus(status: ProjectStatus): Flow<List<Project>>
suspend fun getProjectByName(name: String): Project?
}
@Singleton
class ProjectRepositoryImpl @Inject constructor(
private val projectDao: ProjectDao
) : ProjectRepository {
override suspend fun insert(project: Project) = projectDao.insert(project)
override suspend fun update(project: Project) = projectDao.update(project)
override suspend fun delete(project: Project) = projectDao.delete(project)
override fun getProjectById(id: Long): Flow<Project?> = projectDao.getProjectById(id)
override fun getAllProjects(): Flow<List<Project>> = projectDao.getAllProjects()
override fun getProjectsByStatus(status: ProjectStatus): Flow<List<Project>> = projectDao.getProjectsByStatus(status)
override suspend fun getProjectByName(name: String): Project? = projectDao.getProjectByName(name)
}
</details>
檔案路徑: app/src/main/java/com/yourcompany/cashflow/data/repository/SnapshotRepository.kt
<details>
<summary><strong>點此展開 SnapshotRepository.kt 程式碼</strong></summary>
package com.yourcompany.cashflow.data.repository
import com.yourcompany.cashflow.data.local.dao.SnapshotDao
import com.yourcompany.cashflow.data.model.AccountWithLatestValue
import com.yourcompany.cashflow.data.model.AssetAllocation
import com.yourcompany.cashflow.data.model.MonthlyTotal
import com.yourcompany.cashflow.data.model.NetWorth
import kotlinx.coroutines.flow.Flow
import java.time.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
interface SnapshotRepository {
suspend fun addOrUpdateSnapshot(accountId: Long, value: Double, date: LocalDate)
fun getYearlyAssetTrend(year: Int): Flow<List<MonthlyTotal>>
fun getLatestAssetAllocation(): Flow<List<AssetAllocation>>
fun getAccountsWithLatestValue(): Flow<List<AccountWithLatestValue>>
fun getLatestNetWorth(): Flow<NetWorth?>
}
@Singleton
class SnapshotRepositoryImpl @Inject constructor(
private val snapshotDao: SnapshotDao
) : SnapshotRepository {
override suspend fun addOrUpdateSnapshot(accountId: Long, value: Double, date: LocalDate) {
snapshotDao.addOrUpdateSnapshot(accountId, value, date)
}
override fun getYearlyAssetTrend(year: Int): Flow<List<MonthlyTotal>> {
return snapshotDao.getYearlyAssetTrend(year.toString())
}
override fun getLatestAssetAllocation(): Flow<List<AssetAllocation>> {
return snapshotDao.getLatestAssetAllocation()
}
override fun getAccountsWithLatestValue(): Flow<List<AccountWithLatestValue>> {
return snapshotDao.getAccountsWithLatestValue()
}
override fun getLatestNetWorth(): Flow<NetWorth?> {
return snapshotDao.getLatestNetWorth()
}
}
</details>
檔案路徑: app/src/main/java/com/yourcompany/cashflow/data/repository/TransactionRepository.kt
<details>
<summary><strong>點此展開 TransactionRepository.kt 程式碼</strong></summary>
package com.yourcompany.cashflow.data.repository
import com.yourcompany.cashflow.data.local.dao.TransactionDao
import com.yourcompany.cashflow.data.local.entity.Transaction
import com.yourcompany.cashflow.data.local.entity.TransactionType
import com.yourcompany.cashflow.data.model.CategorySum
import com.yourcompany.cashflow.data.model.SubCategorySum
import com.yourcompany.cashflow.data.model.TransactionWithDetails
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import javax.inject.Singleton
interface TransactionRepository {
suspend fun insertTransaction(transaction: Transaction)
suspend fun updateTransaction(transaction: Transaction)
suspend fun deleteTransaction(transaction: Transaction)
fun getTransactionDetailsById(id: Long): Flow<TransactionWithDetails?>
fun getAllTransactionDetails(): Flow<List<TransactionWithDetails>>
suspend fun transactionExists(subCategoryId: Long, amount: Double, startOfDay: Long, endOfDay: Long): Boolean
fun getExpensesBetween(startDate: Long, endDate: Long): Flow<Double?>
fun getSpendingForProject(projectId: Long): Flow<Double?>
fun getTransactionsForProject(projectId: Long): Flow<List<TransactionWithDetails>>
fun getUnassignedTransactions(): Flow<List<TransactionWithDetails>>
suspend fun linkTransactionsToProject(transactionIds: List<Long>, projectId: Long)
fun getCategoryStatistics(type: TransactionType, startDate: Long, endDate: Long): Flow<List<CategorySum>>
fun getProjectStatistics(type: TransactionType, startDate: Long, endDate: Long): Flow<List<CategorySum>>
fun getCategorySumsForProject(projectId: Long, type: TransactionType, startDate: Long, endDate: Long): Flow<List<CategorySum>>
fun getSubCategorySumsForProject(projectId: Long, mainCategoryName: String, type: TransactionType, startDate: Long, endDate: Long): Flow<List<SubCategorySum>>
}
@Singleton
class TransactionRepositoryImpl @Inject constructor(
private val transactionDao: TransactionDao
) : TransactionRepository {
override suspend fun insertTransaction(transaction: Transaction) = transactionDao.insert(transaction)
override suspend fun updateTransaction(transaction: Transaction) = transactionDao.update(transaction)
override suspend fun deleteTransaction(transaction: Transaction) = transactionDao.delete(transaction)
override fun getTransactionDetailsById(id: Long): Flow<TransactionWithDetails?> = transactionDao.getTransactionDetailsById(id)
override fun getAllTransactionDetails(): Flow<List<TransactionWithDetails>> = transactionDao.getAllTransactionDetails()
override suspend fun transactionExists(subCategoryId: Long, amount: Double, startOfDay: Long, endOfDay: Long): Boolean = transactionDao.transactionExists(subCategoryId, amount, startOfDay, endOfDay) > 0
override fun getExpensesBetween(startDate: Long, endDate: Long): Flow<Double?> = transactionDao.getExpensesBetween(startDate, endDate)
override fun getSpendingForProject(projectId: Long): Flow<Double?> = transactionDao.getSpendingForProject(projectId)
override fun getTransactionsForProject(projectId: Long): Flow<List<TransactionWithDetails>> = transactionDao.getTransactionsForProject(projectId)
override fun getUnassignedTransactions(): Flow<List<TransactionWithDetails>> = transactionDao.getUnassignedTransactions()
override suspend fun linkTransactionsToProject(transactionIds: List<Long>, projectId: Long) = transactionDao.linkTransactionsToProject(transactionIds, projectId)
override fun getCategoryStatistics(type: TransactionType, startDate: Long, endDate: Long): Flow<List<CategorySum>> = transactionDao.getCategoryStatistics(type, startDate, endDate)
override fun getProjectStatistics(type: TransactionType, startDate: Long, endDate: Long): Flow<List<CategorySum>> = transactionDao.getProjectStatistics(type, startDate, endDate)
override fun getCategorySumsForProject(projectId: Long, type: TransactionType, startDate: Long, endDate: Long): Flow<List<CategorySum>> = transactionDao.getCategorySumsForProject(projectId, type, startDate, endDate)
override fun getSubCategorySumsForProject(projectId: Long, mainCategoryName: String, type: TransactionType, startDate: Long, endDate: Long): Flow<List<SubCategorySum>> = transactionDao.getSubCategorySumsForProject(projectId, mainCategoryName, type, startDate, endDate)
}
</details>
檔案路徑: app/src/main/java/com/yourcompany/cashflow/data/repository/RecurringTransactionRepository.kt
<details>
<summary><strong>點此展開 RecurringTransactionRepository.kt 程式碼</strong></summary>
package com.yourcompany.cashflow.data.repository
import com.yourcompany.cashflow.data.local.dao.RecurringTransactionDao
import com.yourcompany.cashflow.data.local.entity.RecurringTransaction
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import javax.inject.Singleton
interface RecurringTransactionRepository {
fun getAllRecurringTransactions(): Flow<List<RecurringTransaction>>
suspend fun getAllRecurringTransactionsOnce(): List<RecurringTransaction>
suspend fun insert(recurringTransaction: RecurringTransaction)
suspend fun delete(recurringTransaction: RecurringTransaction)
}
@Singleton
class RecurringTransactionRepositoryImpl @Inject constructor(
private val recurringTransactionDao: RecurringTransactionDao
) : RecurringTransactionRepository {
override fun getAllRecurringTransactions(): Flow<List<RecurringTransaction>> = recurringTransactionDao.getAllRecurringTransactions()
override suspend fun getAllRecurringTransactionsOnce(): List<RecurringTransaction> = recurringTransactionDao.getAllRecurringTransactionsOnce()
override suspend fun insert(recurringTransaction: RecurringTransaction) = recurringTransactionDao.insert(recurringTransaction)
override suspend fun delete(recurringTransaction: RecurringTransaction) = recurringTransactionDao.delete(recurringTransaction)
}
</details>
以上就是我們專案數據層中,所有 Repository 的最終版本。它們忠實地執行了作為數據抽象層的職責,為 ViewModel 提供了乾淨、統一的數據接口。