Vibe coding全紀錄-31

A:

 好的,謝謝你提出這個計畫,就請開始階段A的第十六步:為「專案」功能更新資料庫與數據層,謝謝 

Q:

好的,我們立刻開始執行新藍圖的階段 A

這一步的目標很明確:我們要再次修改資料庫,讓它能夠完整地支援「專案」功能,特別是專案的「狀態」(進行中/已完成),並建立起對應的數據操作管道。


第十六步:為「專案」功能更新資料庫與數據層

16.1 建立 ProjectStatus 枚舉 (Enum)

首先,我們定義專案的兩種狀態。打開 Enums.kt 檔案,在裡面加入 ProjectStatus

路徑: app/src/main/java/com/yourcompany/cashflow/data/local/entity/Enums.kt

Kotlin
package com.yourcompany.cashflow.data.local.entity

// ... (現有的 AccountType, TransactionType, FrequencyType, AssetClass)

// vvv-- 加入這個新的 Enum --vvv
enum class ProjectStatus {
    ACTIVE,    // 進行中
    COMPLETED  // 已完成
}

16.2 建立 Project 實體 (Entity)

我們在專案初期已經規劃了這個 Entity,現在我們正式建立它,並加入新的 status 欄位。

路徑: app/src/main/java/com/yourcompany/cashflow/data/local/entity/Project.kt (新檔案)

Kotlin
package com.yourcompany.cashflow.data.local.entity

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "projects")
data class Project(
    @PrimaryKey(autoGenerate = true)
    val id: Long = 0,
    val name: String,
    val budget: Double,
    val description: String?,
    
    // 我們為新欄位提供預設值,這對之後的資料庫遷移很重要
    @ColumnInfo(defaultValue = "ACTIVE")
    val status: ProjectStatus
)

16.3 建立 ProjectDao.kt 介面

這個 DAO 將負責所有對 projects 資料表的直接操作。

路徑: app/src/main/java/com/yourcompany/cashflow/data/local/dao/ProjectDao.kt (新檔案)

Kotlin
package com.yourcompany.cashflow.data.local.dao

import androidx.room.*
import com.yourcompany.cashflow.data.local.entity.Project
import com.yourcompany.cashflow.data.local.entity.ProjectStatus
import kotlinx.coroutines.flow.Flow

@Dao
interface ProjectDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(project: Project)

    @Update
    suspend fun update(project: Project)

    @Delete
    suspend fun delete(project: Project)

    @Query("SELECT * FROM projects WHERE id = :id")
    fun getProjectById(id: Long): Flow<Project?>

    @Query("SELECT * FROM projects ORDER BY name ASC")
    fun getAllProjects(): Flow<List<Project>>

    // 這個查詢將用於「新增交易」畫面,只顯示進行中的專案
    @Query("SELECT * FROM projects WHERE status = :status ORDER BY name ASC")
    fun getProjectsByStatus(status: ProjectStatus): Flow<List<Project>>
}

16.4 建立 ProjectRepository.kt

建立 Repository 來封裝 DAO 的操作。

路徑: app/src/main/java/com/yourcompany/cashflow/data/repository/ProjectRepository.kt (新檔案)

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

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 getActiveProjects(): Flow<List<Project>>
}

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 getActiveProjects(): Flow<List<Project>> = projectDao.getProjectsByStatus(ProjectStatus.ACTIVE)
}

16.5 最終整合:更新 AppDatabase 與 DI 模組

我們再次需要更新資料庫主體,並處理版本升級。

A. 更新 AppDatabase.kt

Kotlin
// /data/local/AppDatabase.kt
...
import com.yourcompany.cashflow.data.local.entity.Project // 匯入新的 Entity

@Database(
    entities = [
        // ... (現有的所有 Entities)
        AccountSnapshot::class,
        Project::class // <-- 1. 加入新的 Project Entity
    ],
    version = 3, // <-- 2. 非常重要!將版本號從 2 改為 3
    exportSchema = false
)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
    // ... (現有的所有 DAOs)
    abstract fun snapshotDao(): SnapshotDao
    abstract fun projectDao(): ProjectDao // <-- 3. 加入新的 DAO
}

B. 更新 AppModule.kt 和 RepositoryModule.kt

我們需要讓 Hilt 知道如何建立 ProjectDao 和 ProjectRepository。

路徑: app/src/main/java/com/yourcompany/cashflow/di/AppModule.kt

Kotlin
// ...
object AppModule {
    @Provides
    @Singleton
    fun provideAppDatabase(app: Application): AppDatabase {
        // fallbackToDestructiveMigration() 會再次幫我們處理 v2 到 v3 的升級
        return Room.databaseBuilder(...)
            .fallbackToDestructiveMigration()
            .build()
    }
    // ... (其他 provide 方法)

    // vvv-- 為新的 DAO 加入 Provider --vvv
    @Provides
    @Singleton
    fun provideProjectDao(db: AppDatabase): ProjectDao = db.projectDao()
}

路D徑: app/src/main/java/com/yourcompany/cashflow/di/RepositoryModule.kt

Kotlin
// ...
@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
    // ... (其他 Binds)
    
    // vvv-- 加入 ProjectRepository 的綁定 --vvv
    @Binds
    @Singleton
    abstract fun bindProjectRepository(impl: ProjectRepositoryImpl): ProjectRepository
}

C. 更新 Converters.kt

為新的 ProjectStatus enum 加入 TypeConverter。

路徑: app/src/main/java/com/yourcompany/cashflow/data/local/Converters.kt

Kotlin
// ...
class Converters {
    // ... (其他 Converters)
    
    @TypeConverter fun fromProjectStatus(value: ProjectStatus) = value.name
    @TypeConverter fun toProjectStatus(value: String) = ProjectStatus.valueOf(value)
}

基礎建設完成!

我們已經成功地在資料庫層級,為「專案」功能建立起了完整的骨架。我們的 App 現在已經具備了儲存、管理、讀取專案及其狀態的能力。

所有後台的準備工作都已就緒。

下一步,第十七步,我們將會建立對應的 ProjectViewModel 和使用者介面 (ProjectListScreen, AddEditProjectScreen),讓使用者可以真正地在畫面上與這些專案數據進行互動。準備好進入 UI 的部分了嗎?