A:
要請幫忙一步一步詳細仔細的列出最終版的下列程式碼,謝謝
data/local/*
AppDatabase.kt (version 3)
Converters.kt
Migrations.kt (MIGRATION_1_2, MIGRATION_2_3)
data/model/*
FinancialSummary.kt (包含所有統計與資產負債表用的模型)
MainCategoryWithSubCategories.kt
TransactionWithDetails.kt
data/preferences/UserPreferencesRepository.kt
Q:
好的,我們繼續進行專案的完整程式碼回顧。
接下來,我將列出 data/local/、data/model/ 和 data/preferences/ 這幾個套件底下的所有檔案。這些檔案定義了我們 App 的資料庫主體、遷移邏輯、所有自訂的查詢結果模型,以及使用者偏好設定的儲存機制。它們是數據層的骨架與大腦。
第廿三步:最終程式碼回顧 (Data Local, Model, Preferences 篇)
套件: data/local/
檔案路徑: app/src/main/java/com/yourcompany/cashflow/data/local/AppDatabase.kt
<details>
<summary><strong>點此展開 AppDatabase.kt 程式碼</strong></summary>
package com.yourcompany.cashflow.data.local
import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import com.yourcompany.cashflow.data.local.dao.*
import com.yourcompany.cashflow.data.local.entity.*
@Database(
entities = [
Account::class,
AccountSnapshot::class,
MainCategory::class,
SubCategory::class,
Project::class,
RecurringTransaction::class,
Transaction::class
],
version = 3, // 最終版本號
exportSchema = false
)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun accountDao(): AccountDao
abstract fun categoryDao(): CategoryDao
abstract fun transactionDao(): TransactionDao
abstract fun recurringTransactionDao(): RecurringTransactionDao
abstract fun snapshotDao(): SnapshotDao
abstract fun projectDao(): ProjectDao
}
</details>
檔案路徑: app/src/main/java/com/yourcompany/cashflow/data/local/Converters.kt
<details>
<summary><strong>點此展開 Converters.kt 程式碼</strong></summary>
package com.yourcompany.cashflow.data.local
import androidx.room.TypeConverter
import com.yourcompany.cashflow.data.local.entity.*
class Converters {
@TypeConverter fun fromAccountType(value: AccountType): String = value.name
@TypeConverter fun toAccountType(value: String): AccountType = AccountType.valueOf(value)
@TypeConverter fun fromTransactionType(value: TransactionType): String = value.name
@TypeConverter fun toTransactionType(value: String): TransactionType = TransactionType.valueOf(value)
@TypeConverter fun fromFrequencyType(value: FrequencyType): String = value.name
@TypeConverter fun toFrequencyType(value: String): FrequencyType = FrequencyType.valueOf(value)
@TypeConverter fun fromAssetClass(value: AssetClass): String = value.name
@TypeConverter fun toAssetClass(value: String): AssetClass = AssetClass.valueOf(value)
@TypeConverter fun fromProjectStatus(value: ProjectStatus): String = value.name
@TypeConverter fun toProjectStatus(value: String): ProjectStatus = ProjectStatus.valueOf(value)
}
</details>
檔案路徑: app/src/main/java/com/yourcompany/cashflow/data/local/Migrations.kt
<details>
<summary><strong>點此展開 Migrations.kt 程式碼</strong></summary>
package com.yourcompany.cashflow.data.local
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
/**
* 從版本 1 遷移到版本 2
* 變更點:
* 1. 在 `accounts` 表中新增 `assetClass` 欄位。
* 2. 建立新的 `account_snapshots` 表。
*/
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
// 1. 為 accounts 表新增 assetClass 欄位,並提供 NOT NULL 和 DEFAULT 值
db.execSQL("ALTER TABLE accounts ADD COLUMN assetClass TEXT NOT NULL DEFAULT 'CASH'")
// 2. 建立新的 account_snapshots 表
db.execSQL("""
CREATE TABLE IF NOT EXISTS `account_snapshots` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
`accountId` INTEGER NOT NULL,
`snapshotDate` INTEGER NOT NULL,
`value` REAL NOT NULL,
FOREIGN KEY(`accountId`) REFERENCES `accounts`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE
)
""")
}
}
/**
* 從版本 2 遷移到版本 3
* 變更點:
* 1. 建立新的 `projects` 表。
*/
val MIGRATION_2_3 = object : Migration(2, 3) {
override fun migrate(db: SupportSQLiteDatabase) {
// 建立新的 projects 表
db.execSQL("""
CREATE TABLE IF NOT EXISTS `projects` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
`name` TEXT NOT NULL,
`budget` REAL NOT NULL,
`description` TEXT,
`status` TEXT NOT NULL DEFAULT 'ACTIVE'
)
""")
}
}
</details>
套件: data/model/
檔案路徑: app/src/main/java/com/yourcompany/cashflow/data/model/FinancialSummary.kt
<details>
<summary><strong>點此展開 FinancialSummary.kt 程式碼</strong></summary>
package com.yourcompany.cashflow.data.model
import androidx.room.Embedded
import com.yourcompany.cashflow.data.local.entity.Account
import com.yourcompany.cashflow.data.local.entity.AssetClass
/**
* 用於統計圖表:代表某個主分類或專案的總額
*/
data class CategorySum(
val categoryName: String,
val amount: Double
)
/**
* 用於統計圖表鑽取:代表某個子分類的總額
*/
data class SubCategorySum(
val subCategoryName: String,
val amount: Double
)
/**
* 用於資產負債表年度趨勢圖:代表某個月份的資產總額
*/
data class MonthlyTotal(
val yearMonth: String, // 格式: "2025-08"
val totalValue: Double
)
/**
* 用於資產負債表資產配置圖:代表某個資產類別的總額
*/
data class AssetAllocation(
val assetClass: AssetClass,
val totalValue: Double
)
/**
* 用於資產負債表下方的帳戶列表
*/
data class AccountWithLatestValue(
@Embedded
val account: Account,
val latestValue: Double? // 最新的快照餘額,可能為 null
)
/**
* 用於承載最新淨值的查詢結果
*/
data class NetWorth(
val totalAssets: Double,
val totalLiabilities: Double
) {
val value: Double
get() = totalAssets - totalLiabilities
}
</details>
檔案路徑: app/src/main/java/com/yourcompany/cashflow/data/model/MainCategoryWithSubCategories.kt
<details>
<summary><strong>點此展開 MainCategoryWithSubCategories.kt 程式碼</strong></summary>
package com.yourcompany.cashflow.data.model
import androidx.room.Embedded
import androidx.room.Relation
import com.yourcompany.cashflow.data.local.entity.MainCategory
import com.yourcompany.cashflow.data.local.entity.SubCategory
/**
* 用於分類管理頁面,一次性查詢出主分類及其所有子分類
*/
data class MainCategoryWithSubCategories(
@Embedded
val mainCategory: MainCategory,
@Relation(
parentColumn = "id",
entityColumn = "mainCategoryId"
)
val subCategories: List<SubCategory>
)
</details>
檔案路徑: app/src/main/java/com/yourcompany/cashflow/data/model/TransactionWithDetails.kt
<details>
<summary><strong>點此展開 TransactionWithDetails.kt 程式碼</strong></summary>
package com.yourcompany.cashflow.data.model
import androidx.room.Embedded
import androidx.room.Relation
import com.yourcompany.cashflow.data.local.entity.Account
import com.yourcompany.cashflow.data.local.entity.SubCategory
import com.yourcompany.cashflow.data.local.entity.Transaction
/**
* 用於交易明細列表,一次性查詢出交易紀錄及其關聯的帳戶和子分類資訊
*/
data class TransactionWithDetails(
@Embedded
val transaction: Transaction,
@Relation(
parentColumn = "subCategoryId",
entityColumn = "id"
)
val subCategory: SubCategory,
@Relation(
parentColumn = "accountId",
entityColumn = "id"
)
val account: Account
)
</details>
套件: data/preferences/
檔案路徑: app/src/main/java/com/yourcompany/cashflow/data/preferences/UserPreferencesRepository.kt
<details>
<summary><strong>點此展開 UserPreferencesRepository.kt 程式碼</strong></summary>
package com.yourcompany.cashflow.data.preferences
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.doublePreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.preferencesDataStore
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject
import javax.inject.Singleton
// 定義使用者預算的資料模型
data class UserBudget(
val annualBudget: Double,
val monthlyBudget: Double
)
// 建立一個 Context 的擴充屬性來取得 DataStore 實例
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "user_preferences")
@Singleton
class UserPreferencesRepository @Inject constructor(
@ApplicationContext private val context: Context
) {
// 定義我們要儲存的數據的 Key
private object Keys {
val ANNUAL_BUGET = doublePreferencesKey("annual_budget")
val MONTHLY_BUGET = doublePreferencesKey("monthly_budget")
}
// 提供一個 Flow 來讀取預算數據
val userBudget: Flow<UserBudget> = context.dataStore.data
.map { preferences ->
UserBudget(
annualBudget = preferences[Keys.ANNUAL_BUGET] ?: 0.0,
monthlyBudget = preferences[Keys.MONTHLY_BUGET] ?: 0.0
)
}
// 提供一個 suspend 函式來儲存預算
suspend fun saveBudget(annual: Double, monthly: Double) {
context.dataStore.edit { preferences ->
preferences[Keys.ANNUAL_BUGET] = annual
preferences[Keys.MONTHLY_BUGET] = monthly
}
}
}
</details>
以上就是我們專案 data/local/、data/model/ 和 data/preferences/ 套件下所有檔案的最終版本。它們共同構成了我們 App 數據層的靜態結構與定義,是所有業務邏輯的基礎。