小菜记账 — 全量迭代架构设计 v2
版本: v2.0 | 日期: 2026-06-10
架构师: 高见远(Gao)
基线: PRD v2.0(35 项改进点,6 个维度)
技术栈: Uni-app + Vue 3 + Pinia + SCSS (前端) | Node.js + Express + MySQL 8.0 (后端)
一、实现方案 + 技术选型
1.1 安全修复方案
S1:.env 凭据泄露修复
| 项目 |
方案 |
| 核心思路 |
凭据轮换 + Git 历史重写 + pre-commit 防护 |
| 工具 |
git-filter-repo(Python,比 BFG 更安全,支持 blob 回调替换) |
| 防护 |
.githooks/pre-commit 拦截 .env 文件提交 |
| 回滚 |
操作前 git clone --mirror 完整备份 |
| 执行顺序 |
① 轮换所有凭据 → ② 重写 Git 历史 → ③ 强制推送 → ④ 验证 .gitignore |
S2:logs.ts SQL 拼接修复
| 项目 |
方案 |
| 当前问题 |
LIMIT ${pSize} OFFSET ${offset} 模板字符串直接拼接 |
| 修复方案 |
parseInt + 范围校验后使用 pool.query + ? 占位符 |
| 关键点 |
pool.query 支持 ? 占位符用于整数参数;pool.execute 的 prepared statement 对 LIMIT/OFFSET 类型要求严格,故选用 pool.query |
S3:TOKEN_SECRET 弱默认值 + 命名不一致
| 项目 |
方案 |
| 核心思路 |
新增 server/src/config/token.ts 集中管理,移除所有 fallback |
| 命名统一 |
backup.ts 中 JWT_SECRET → TOKEN_SECRET;所有引用改为 import { TOKEN_SECRET } from '../config/token' |
| 启动校验 |
TOKEN_SECRET 未设置或长度 < 32 时 process.exit(1) |
| 密钥生成 |
openssl rand -hex 32 生成 64 字符强密钥 |
S4:Export/Backup 短期一次性下载凭证
| 项目 |
方案 |
| 核心思路 |
新增 download_tokens 表,生成随机凭证(crypto.randomBytes(32)) |
| 有效期 |
5 分钟,一次性使用后标记 used_at |
| 存储 |
MySQL 表(写入频率极低,无需 Redis) |
| 清理 |
健康检查时附带清理 expires_at < NOW() 的记录 |
| 改动 |
backup.ts 和 export.ts 的下载逻辑改为先获取凭证再用凭证下载 |
S5:Refresh Token 机制
| 项目 |
方案 |
| 架构 |
双 Token 机制:Access Token(2h)+ Refresh Token(30d) |
| 存储 |
refresh_tokens 表,仅存储 SHA-256 hash(不存明文) |
| 轮换 |
每次刷新后旧 Refresh Token 立即失效(revoked_at),返回新 Token |
| 前端适配 |
request.ts 401 → 尝试 refresh → 成功则重试原请求 → refresh 也失败则清 token 跳登录 |
| 安全 |
修改密码时撤销所有 Refresh Token;单用户最多 5 个有效 Refresh Token |
| 优势 |
保持现有 HMAC-SHA256 签名方式不变,仅缩短 Access Token 有效期 |
S6:Admin 硬删除 → 软删除 + 二次确认
| 项目 |
方案 |
| DB 变更 |
users 表新增 deleted_at DATETIME DEFAULT NULL + 索引 |
| 删除操作 |
UPDATE users SET deleted_at = NOW() WHERE id = ? |
| 恢复接口 |
POST /api/admin/users/:id/restore → SET deleted_at = NULL |
| 全局过滤 |
所有涉及 users 表的查询增加 WHERE deleted_at IS NULL |
| 前端确认 |
管理页点击删除 → 弹窗二次确认 → 调用软删除接口 |
S7:DB 连接移除默认凭据
| 项目 |
方案 |
| 修复 |
移除所有 ` |
| 启动校验 |
遍历必需环境变量,缺失任一即 process.exit(1) |
1.2 通用组件抽取方案
ManageList 组件
ColorPicker 组件
EditModal 组件
1.3 乐观更新实现模式
1.4 Refresh Token 实现方案
后端关键实现:
auth.ts 登录成功时:生成 Access Token(2h)+ Refresh Token(randomBytes(32)),存 hash 到 refresh_tokens 表
- 新增
POST /auth/refresh:验证 Refresh Token hash → 撤销旧 Token → 生成新双 Token
middleware/auth.ts:Access Token 过期返回 40101(区别于无效 Token 的 40100)
前端关键实现:
request.ts:收到 40101 → 调用 /auth/refresh → 成功则重试 → 失败则清 token 跳登录
- 存储:
xc:accessToken + xc:refreshToken 分开存储
- 并发请求时只触发一次 refresh,其他请求排队等待
1.5 软删除方案
1.6 iconfont 迁移方案
1.7 离线队列方案
1.8 财务报告推送方案
1.9 预算预警方案
1.10 测试框架选型
| 层级 |
框架 |
理由 |
| 后端集成测试 |
Jest + supertest |
Express 生态标配;supertest 无需启动真实服务器;与 ts-jest 配合良好 |
| 前端 Store 测试 |
Vitest + @pinia/testing |
Vitest 与 Vite 原生集成(Uni-app 基于 Vite);@pinia/testing 提供 createTestingPinia() |
| 工具函数测试 |
Jest(后端)+ Vitest(前端) |
纯函数测试最简单,适合作为测试练手 |
| API 契约测试 |
Jest + ajv(JSON Schema) |
轻量级方案,不需要引入完整的契约测试框架 |
二、文件列表及相对路径
2.1 新建文件
| 文件路径 |
用途 |
server/src/config/token.ts |
TOKEN_SECRET 集中配置与启动校验 |
server/src/services/scheduler.ts |
定时任务调度(node-cron) |
server/src/services/report.ts |
财务报告生成逻辑 |
server/src/services/budget-alert.ts |
预算预警检查与推送逻辑 |
server/src/services/wechat-subscribe.ts |
微信订阅消息发送封装 |
server/src/utils/upload.ts |
公共上传处理函数(C3 抽取) |
server/src/routes/report.ts |
财务报告 API 路由 |
server/tests/setup.ts |
测试数据库初始化与清理 |
server/tests/auth.test.ts |
认证路由集成测试 |
server/tests/transaction.test.ts |
交易路由集成测试 |
server/tests/category.test.ts |
分类路由集成测试 |
server/tests/budget.test.ts |
预算路由集成测试 |
server/tests/stats.test.ts |
统计路由集成测试 |
server/jest.config.js |
Jest 配置 |
server/.env.test |
测试数据库环境变量 |
client/src/components/ManageList/ManageList.vue |
通用管理列表组件 |
client/src/components/ColorPicker/ColorPicker.vue |
颜色选择器组件 |
client/src/components/EditModal/EditModal.vue |
通用编辑弹窗组件 |
client/src/components/Snackbar/Snackbar.vue |
删除撤销 Snackbar 组件 |
client/src/utils/offline.ts |
离线队列管理器 |
client/src/utils/avatar.ts |
头像 URL 拼接工具函数(C9) |
client/src/static/iconfont/ |
iconfont 字体文件目录 |
client/src/styles/iconfont.scss |
iconfont 样式声明 |
client/tests/stores/transaction.test.ts |
交易 Store 单元测试 |
client/tests/stores/category.test.ts |
分类 Store 单元测试 |
client/tests/utils/format.test.ts |
格式化工具函数测试 |
client/tests/utils/app-ready.test.ts |
app-ready 工具函数测试 |
client/vitest.config.ts |
Vitest 配置 |
.githooks/pre-commit |
防止 .env 提交的 Git 钩子 |
2.2 修改文件
| 文件路径 |
主要变更 |
| 后端 - 安全 |
|
server/src/db/connection.ts |
移除默认凭据 fallback,增加启动校验 |
server/src/middleware/auth.ts |
引用 config/token.ts;Access Token 有效期缩短为 2h;区分 40100/40101 错误码 |
server/src/routes/auth.ts |
引用 config/token.ts;实现双 Token 机制;新增 POST /refresh 接口 |
server/src/routes/backup.ts |
JWT_SECRET → TOKEN_SECRET;下载改用 download_tokens 凭证 |
server/src/routes/admin.ts |
硬删除 → 软删除;新增恢复接口;N+1 优化 |
server/src/routes/logs.ts |
SQL 拼接改为参数化查询 |
server/src/routes/export.ts |
下载改用 download_tokens 凭证 |
| 后端 - 代码质量 |
|
server/src/routes/recurring.ts |
sync 接口增加事务包裹 |
server/src/routes/user.ts |
上传逻辑抽取使用公共函数 |
server/src/routes/notification.ts |
上传逻辑抽取使用公共函数 |
| 后端 - 性能 |
|
server/src/routes/stats.ts |
新增 GET /dashboard 聚合接口 |
server/src/routes/track.ts |
逐条 INSERT → 批量 INSERT |
server/src/routes/budget.ts |
支持 group_id 参数;记账后触发预算预警 |
server/src/routes/transaction.ts |
创建记账后触发预算预警检查 |
| 后端 - DB |
|
server/src/db/schema.sql |
新增 download_tokens、refresh_tokens、budget_alerts、user_settings 表;users 增加 deleted_at |
server/src/db/migrate.sql |
对应迁移脚本 |
| 后端 - 入口 |
|
server/src/index.ts |
移除 TOKEN_SECRET 警告;注册 report 路由;初始化 scheduler |
| 前端 - 代码质量 |
|
client/src/stores/category.ts |
乐观更新;try/catch;缓存标记 lastFetchTime |
client/src/stores/tag.ts |
乐观更新;try/catch;缓存标记 lastFetchTime |
client/src/stores/transaction.ts |
删除撤销;try/catch |
client/src/stores/budget.ts |
try/catch;支持 group_id |
client/src/stores/notification.ts |
未读计数缓存 30s |
client/src/stores/group.ts |
refreshAll 并行加载 |
client/src/stores/stats.ts |
使用 dashboard 聚合接口 |
client/src/stores/user.ts |
Refresh Token 适配 |
client/src/utils/format.ts |
去重(getCurrentMonth 已在后端存在) |
client/src/utils/request.ts |
Refresh Token 重试逻辑 |
client/src/utils/app-ready.ts |
超时 reject(非静默 resolve) |
client/src/utils/tracker.ts |
攒批发送(500ms 或 10 条) |
client/src/config.ts |
已集中化,搜索残留硬编码 URL |
client/src/components/Icon/Icon.vue |
PNG → iconfont 字体图标 |
client/src/components/TransactionItem/TransactionItem.vue |
群组只读标记 |
client/src/components/ChartWrapper/ |
延迟渲染(IntersectionObserver) |
| 前端 - 页面 |
|
client/src/pages/category-manage/index.vue |
使用 ManageList/ColorPicker/EditModal 组件 |
client/src/pages/tag-manage/index.vue |
使用 ManageList/ColorPicker/EditModal 组件 |
client/src/pages/bills/index.vue |
删除撤销 Snackbar |
client/src/pages/budget/index.vue |
群组预算视图 |
client/src/pages/stats/index.vue |
使用 dashboard 接口 |
client/src/pages/add/index.vue |
离线记账支持 |
client/src/pages.json |
全局 enablePullDownRefresh |
client/src/App.vue |
注册网络状态监听 |
| 配置 |
|
server/package.json |
新增 jest、ts-jest、supertest、node-cron 依赖 |
client/package.json |
新增 vitest、@pinia/testing、@vue/test-utils 依赖 |
三、数据结构和接口
3.1 新增/修改的数据库表结构
新增表
修改表
3.2 新增/修改的 API 端点定义
新增端点
| 方法 |
路径 |
说明 |
请求体 / 参数 |
响应 |
| POST |
/api/auth/refresh |
刷新 Access Token |
{ refreshToken: string } |
{ code: 0, data: { accessToken, refreshToken, expiresIn } } |
| GET |
/api/stats/dashboard |
聚合统计(一次返回 overview + category + trend) |
?month=2026-06&type=expense&period=month&group_id=null |
{ code: 0, data: { overview, category, trend } } |
| POST |
/api/admin/users/:id/restore |
恢复软删除用户 |
— |
{ code: 0 } |
| POST |
/api/download-tokens |
生成下载凭证 |
`{ resourceType: 'backup' |
'export', resourceId: string }` |
| GET |
/api/download/:token |
使用凭证下载文件 |
:token 路径参数 |
文件流或 403 |
| GET |
/api/reports/weekly |
获取上周报告 |
— |
{ code: 0, data: { startDate, endDate, expense, income, topCategories, dailyAvg } } |
| GET |
/api/reports/monthly |
获取上月报告 |
— |
{ code: 0, data: { month, expense, income, topCategories, dailyAvg, monthOverMonth } } |
| GET |
/api/user/settings |
获取用户设置 |
— |
{ code: 0, data: { reportPushEnabled, ... } } |
| PUT |
/api/user/settings |
更新用户设置 |
{ reportPushEnabled?: boolean } |
{ code: 0 } |
修改端点
| 方法 |
路径 |
变更说明 |
| POST |
/api/auth/login |
返回值新增 refreshToken 和 expiresIn 字段 |
| POST |
/api/auth/demo-login |
同上 |
| DELETE |
/api/admin/users/:id |
改为软删除(SET deleted_at = NOW()) |
| GET |
/api/budget |
支持 group_id 参数;群组视图返回 groupTotal + myAmount |
| POST |
/api/budget |
支持 group_id 参数 |
| POST |
/api/transactions |
创建后触发预算预警检查 |
| POST |
/api/track |
支持批量 events[] 数组,后端批量 INSERT |
| GET |
/api/backup/:id/download |
改用 download_tokens 凭证 |
| GET |
/api/export |
改用 download_tokens 凭证(GET 带 token 参数改为先获取凭证再下载) |
四、程序调用流图
4.1 Refresh Token 刷新流程
4.2 删除撤销流程
4.3 预算预警触发流程
五、任务列表
按批次分组,每批次包含 1 个任务,共 5 个任务
优先级:P0(阻塞上线)> P1(必须有)> P2(锦上添花)
T01: 项目基础设施与安全修复
| 字段 |
内容 |
| 任务 ID |
T01 |
| 优先级 |
P0 |
| 描述 |
搭建项目基础设施(依赖安装、配置文件、入口文件、数据库迁移),并完成所有 P0/P1 安全修复(S1-S7) |
| 依赖 |
无 |
| 涉及文件 |
新建:server/src/config/token.ts, .githooks/pre-commit, server/.env.test, server/jest.config.js, client/vitest.config.ts |
|
修改:server/package.json, client/package.json, server/src/db/schema.sql, server/src/db/migrate.sql, server/src/db/connection.ts, server/src/middleware/auth.ts, server/src/routes/auth.ts, server/src/routes/backup.ts, server/src/routes/admin.ts, server/src/routes/logs.ts, server/src/routes/export.ts, server/src/index.ts, client/src/pages.json |
详细子项:
- S1 .env 凭据泄露修复:安装 git-filter-repo、重写历史、添加 pre-commit 钩子
- S2 SQL 参数化:
logs.ts:167 LIMIT/OFFSET 改为 ? 占位符 + parseInt 校验
- S3 TOKEN_SECRET 统一:新建
config/token.ts,统一命名 + 强制启动校验 + 生成强密钥
- S4 下载凭证:新建
download_tokens 表,改造 backup.ts / export.ts 下载逻辑
- S5 Refresh Token:新建
refresh_tokens 表,双 Token 机制(Access 2h + Refresh 30d),前端 request.ts 401 自动刷新
- S6 软删除:
users 表新增 deleted_at,admin.ts DELETE → UPDATE,新增恢复接口,全局查询过滤
- S7 DB 连接校验:connection.ts 移除 fallback,启动时校验必需环境变量
- 基础设施:package.json 新增依赖(jest, supertest, node-cron, vitest 等),jest/vitest 配置,.env.test
T02: 代码质量重构
| 字段 |
内容 |
| 任务 ID |
T02 |
| 优先级 |
P1 |
| 描述 |
抽取通用组件消除重复代码(ManageList/ColorPicker/EditModal),补齐事务与异常处理,统一工具函数与硬编码 URL |
| 依赖 |
T01 |
| 涉及文件 |
新建:client/src/components/ManageList/ManageList.vue, client/src/components/ColorPicker/ColorPicker.vue, client/src/components/EditModal/EditModal.vue, server/src/utils/upload.ts, client/src/utils/avatar.ts, client/src/static/iconfont/*, client/src/styles/iconfont.scss |
|
修改:client/src/pages/category-manage/index.vue, client/src/pages/tag-manage/index.vue, client/src/components/Icon/Icon.vue, server/src/routes/recurring.ts, server/src/routes/user.ts, server/src/routes/notification.ts, client/src/utils/format.ts, client/src/config.ts |
详细子项:
- C2 ManageList 抽取:新建 ManageList 组件,category-manage 和 tag-manage 改为使用该组件,统一行为
- C2 ColorPicker 抽取:从两个管理页面的弹窗中抽取颜色选择器
- C2 EditModal 抽取:从两个管理页面的弹窗中抽取编辑弹窗
- C4 Recurring 事务:
recurring.ts /sync 接口用 getConnection() + beginTransaction() 包裹
- C3 上传逻辑抽取:
user.ts 和 notification.ts 的 multer 配置抽取为 utils/upload.ts
- C1 工具函数去重:前端
format.ts 的 getCurrentMonth 与后端 date.ts 重复,前端保留并标注来源
- C7 硬编码 URL:全局搜索
http:// 和 xiaocai.j35.site,统一从 config.ts 读取
- C8 Icon → iconfont:生成字体图标,Icon.vue 改为字体渲染
- C9 头像 URL 拼接:抽取
getAvatarUrl(filename) 工具函数
T03: 性能优化与 UX 增强
| 字段 |
内容 |
| 任务 ID |
T03 |
| 优先级 |
P1 |
| 描述 |
实现乐观更新、并行加载、聚合接口、删除撤销、群组只读、离线记账、下拉刷新等性能与体验提升 |
| 依赖 |
T01 |
| 涉及文件 |
新建:client/src/components/Snackbar/Snackbar.vue, client/src/utils/offline.ts |
|
修改:client/src/stores/category.ts, client/src/stores/tag.ts, client/src/stores/group.ts, client/src/stores/stats.ts, client/src/stores/notification.ts, client/src/stores/transaction.ts, client/src/stores/budget.ts, client/src/utils/request.ts, client/src/utils/app-ready.ts, client/src/utils/tracker.ts, client/src/components/TransactionItem/TransactionItem.vue, client/src/components/ChartWrapper/, client/src/pages/bills/index.vue, client/src/pages/budget/index.vue, client/src/pages/stats/index.vue, client/src/pages/add/index.vue, client/src/App.vue, server/src/routes/stats.ts, server/src/routes/track.ts, server/src/routes/budget.ts |
详细子项:
- Perf-1 乐观更新:category/tag Store 新增/编辑/排序操作先更新本地 state 再调接口,失败回滚
- Perf-2 并行加载:groupStore
refreshAll() 中无依赖的 fetchCategories() + fetchTags() 改为 Promise.all()
- Perf-3 聚合接口:后端
GET /stats/dashboard 用 Promise.all 并行查询 overview + category + trend
- Perf-4 批量 INSERT:前端 tracker.ts 攒批(500ms/10条),后端 track.ts 接收数组批量 INSERT
- Perf-5 Admin N+1:admin.ts 用户列表改为 LEFT JOIN 单次查询
- Perf-6 前端缓存:category/tag Store 增加
lastFetchTime,5 分钟内 onShow 跳过请求
- Perf-7 未读计数缓存:notification Store 缓存 30s
- UX-1 删除撤销:transaction Store 实现 pendingDelete Map + Snackbar 组件 + 3 秒超时
- UX-2 群组只读:TransactionItem 新增
isReadOnly prop,非本人记录显示锁图标
- UX-3 waitForReady 超时:app-ready.ts 超时后 reject,页面级 catch 显示错误提示
- UX-4 预算 group_id:budget Store/页面/路由 支持 group_id 参数
- UX-5 下拉刷新:pages.json 全局启用,各页面实现 onPullDownRefresh
- UX-6 离线记账:offline.ts 队列管理 + App.vue 网络监听 + add 页面适配
- UX-7 Loading 态:异步操作添加 loading ref 守卫 + 按钮禁用
T04: 测试体系建设
| 字段 |
内容 |
| 任务 ID |
T04 |
| 优先级 |
P1 |
| 描述 |
搭建前后端测试框架,编写核心路由集成测试与 Store 单元测试,建立持续测试基线 |
| 依赖 |
T01 |
| 涉及文件 |
新建:server/tests/setup.ts, server/tests/auth.test.ts, server/tests/transaction.test.ts, server/tests/category.test.ts, server/tests/budget.test.ts, server/tests/stats.test.ts, client/tests/stores/transaction.test.ts, client/tests/stores/category.test.ts, client/tests/utils/format.test.ts, client/tests/utils/app-ready.test.ts |
详细子项:
- 后端测试框架搭建:jest.config.js + setup.ts(测试数据库初始化与清理)
- auth 测试:登录成功/失败、Token 生成与验证、Refresh Token 刷新、无效 Token 拒绝
- transaction 测试:CRUD、group_id 过滤、分页、金额精度、权限校验
- category 测试:CRUD、默认/自定义分类隔离、排序、迁移
- budget 测试:CRUD、月度预算、群组视图
- stats 测试:overview/category/trend/dashboard 查询、period 参数校验
- 前端 Store 测试:transaction/category Store 核心方法测试
- 工具函数测试:format.ts(formatAmount、formatDate、getCurrentMonth)、app-ready.ts
T05: 新功能(财务报告 + 预算预警)
| 字段 |
内容 |
| 任务 ID |
T05 |
| 优先级 |
P1 |
| 描述 |
实现周/月财务报告推送、预算超支预警、微信订阅消息集成 |
| 依赖 |
T01 |
| 涉及文件 |
新建:server/src/services/scheduler.ts, server/src/services/report.ts, server/src/services/budget-alert.ts, server/src/services/wechat-subscribe.ts, server/src/routes/report.ts, client/src/api/report.ts |
|
修改:server/src/index.ts, server/src/routes/transaction.ts, server/src/routes/budget.ts |
详细子项:
- scheduler.ts:使用 node-cron 注册定时任务(每周一 09:00 推送周报、每月 1 号 09:00 推送月报、每日凌晨全量预算扫描)
- report.ts:查询指定时间范围的收支数据,生成摘要文本
- budget-alert.ts:实现 checkBudgetAlert 逻辑,80%/100%/120% 分级预警 + 去重
- wechat-subscribe.ts:封装微信订阅消息发送接口
- report 路由:GET /reports/weekly、GET /reports/monthly
- 用户设置:GET/PUT /user/settings,report_push_enabled 开关
- 触发集成:transaction.ts 创建记账后调用 budgetAlert.check();budget.ts 设置预算后调用 budgetAlert.check()
六、任务依赖图
并行策略:T01 完成后,T02/T03/T04/T05 可并行推进。其中 T03 与 T02 有轻微文件冲突(stores/),建议 T02 先行或约定合并策略。
七、依赖包列表
后端新增
前端新增
八、共享知识(跨文件约定)
8.1 API 响应格式
8.2 认证相关
8.3 金额处理
8.4 日期处理
8.5 软删除约定
8.6 乐观更新约定
8.7 前端缓存约定
8.8 组件命名约定
九、待明确事项
| # |
问题 |
影响范围 |
当前假设 |
风险 |
| Q1 |
Refresh Token 存储方式 |
S5 |
存 MySQL refresh_tokens 表;短中期可行,高并发时迁移 Redis |
当前写入频率极低,MySQL 足够 |
| Q2 |
软删除级联范围 |
S6 |
仅软删除用户账号,交易记录保留但通过 deleted_at IS NULL 过滤 |
保留交易可能影响群组统计,需标注"已注销用户" |
| Q3 |
删除撤销批量上限 |
UX-1 |
上限 5 条(Snackbar 最多显示"已删除 5 条记录"),超过直接删除 |
5 条覆盖绝大多数场景 |
| Q4 |
离线记账冲突策略 |
UX-6 |
MVP 仅支持离线新增(不涉及修改/删除),新增使用服务端生成 ID,不存在主键冲突 |
如需离线编辑需引入版本号机制 |
| Q5 |
微信订阅消息模板审核 |
F-1 |
优先站内通知,微信订阅消息作为增强项;需提前申请模板审核 |
微信审核可能被拒,需准备备选方案 |
| Q6 |
预算预警检查时机 |
F-2 |
双重策略:记账后实时检查 + 每日凌晨全量扫描 |
仅实时检查可能遗漏周期记账触发的超支 |
| Q7 |
乐观更新回滚粒度 |
Perf-1 |
排序操作全量回滚(语义清晰),新增/编辑单条回滚 |
全量回滚可能丢失已做的其他排序操作 |
| Q8 |
测试环境数据隔离 |
T-1 |
独立测试数据库(.env.test),CI 用 GitHub Actions service container |
本地需额外配置测试数据库 |
| Q9 |
budgets 表 group_id 迁移 |
UX-4 |
现有数据 group_id 默认 NULL(个人预算),UNIQUE KEY 从 (user_id, month) 改为 (user_id, group_id, month) |
迁移需确保不破坏现有预算数据 |
| Q10 |
iconfont 图标清单 |
C8 |
需从现有 Icon.vue 的 PNG 映射中提取完整图标列表,并在 iconfont.cn 重新制作 |
部分图标可能无现成 SVG,需设计师配合 |
文档版本: v2.0 | 最后更新: 2026-06-10