Vibe coding全紀錄-53

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>

Kotlin
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>

Kotlin
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>

Kotlin
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>

Kotlin
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>

Kotlin
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>

Kotlin
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 提供了乾淨、統一的數據接口。