feat: 前后端功能对齐 - 实现8个对齐差距
P0-1: 分类拖拽排序 - category-manage 添加拖拽 UI P1-1: 反馈回复展示 - 新增 GET /feedback/mine + 前端我的反馈Tab P1-2: 备份管理页面 - 新增下载端点 + backup-manage 页面 P2-1: 统计年度/周视图 - stats 支持 period 参数 + 前端维度切换器 P2-2: 数据导入 - 新增 POST /import 端点 + data-import 页面 P2-3: 数据导出服务端化 - 新增 GET /export 流式端点 P3-1: 健康检查展示 - health 移到 auth 前 + admin 状态卡片 P3-2~4: 交易标签系统 - tags CRUD + 交易关联 + 按标签筛选统计 后端: 新增 tag.ts/export.ts 路由, 改造 feedback/backup/transaction/stats 前端: 新增 DragSortList 组件, 3个新页面, 改造 7 个现有页面 QA 修复: 5个严重Bug + 4个潜在问题
This commit is contained in:
577
docs/arch-alignment.md
Normal file
577
docs/arch-alignment.md
Normal file
@@ -0,0 +1,577 @@
|
||||
# 小菜记账 增量架构设计文档
|
||||
|
||||
> 版本:v1.0 | 架构师:高见远(Gao) | 日期:2025-07
|
||||
|
||||
---
|
||||
|
||||
## Part A: 系统设计
|
||||
|
||||
### 1. 实现方案 + 框架选型
|
||||
|
||||
#### 1.1 分类拖拽排序(P0-1)
|
||||
|
||||
**技术挑战**:微信小程序不支持 HTML5 Drag and Drop API,Uni-app 的 `movable-area`/`movable-view` 适合单元素拖拽而非列表重排。
|
||||
|
||||
**方案**:基于 Touch 事件的自定义拖拽排序组件 `DragSortList`。
|
||||
|
||||
- 监听 `@touchstart` / `@touchmove` / `@touchend` 三组事件
|
||||
- 拖拽时通过 `transform: translateY()` 实现元素位移动画
|
||||
- 拖拽过程中实时计算目标索引,交换数组元素位置
|
||||
- 松手后调用已就绪的 `sortCategories(ids)` API 批量更新排序
|
||||
- 支出/收入各自独立排序(复用现有 `tabType` 切换 + `currentCategories` computed)
|
||||
- **不引入第三方拖拽库**——Uni-app 小程序环境下无成熟可用方案,自研可控性更高
|
||||
|
||||
#### 1.2 服务端导出流式响应(P2-3)
|
||||
|
||||
**方案**:Node.js 原生 Stream + `pipeline()` 管道。
|
||||
|
||||
- **< 1 万条**:使用 `Readable.from()` 创建内存流,逐行 push 数据,通过 `pipeline()` 管道传输到 Response
|
||||
- **≥ 1 万条**:先写入临时文件(`os.tmpdir()`),再 `fs.createReadStream()` 流式返回,完成后删除临时文件
|
||||
- CSV 格式:手动拼装(无需额外依赖),添加 BOM 头确保中文兼容
|
||||
- JSON 格式:流式写入 `[` + 逐条 `,` 分隔 + `]`,避免全量 JSON.stringify
|
||||
- Response Headers:`Content-Type: text/csv; charset=utf-8` / `application/json; charset=utf-8`,`Content-Disposition: attachment; filename=...`
|
||||
- 超时保护:流式响应不设 `express.json()` body 限制,但设 60s 超时
|
||||
|
||||
#### 1.3 标签系统数据库设计(P3-2~4)
|
||||
|
||||
**设计原则**:标签不区分收支类型,跨分类使用,轻量关联表。
|
||||
|
||||
- `tags` 表:用户维度,每用户最多 20 个标签,8 色预设
|
||||
- `transaction_tags` 表:多对多关联,每笔交易最多 5 个标签
|
||||
- 标签与交易在同一事务中写入(`POST/PUT /transactions` 扩展 `tagIds` 字段)
|
||||
- 查询交易列表时 LEFT JOIN 聚合标签信息(避免 N+1 查询)
|
||||
- 统计按标签聚合使用 `GROUP BY tag_id`
|
||||
|
||||
#### 1.4 数据导入(P2-2)
|
||||
|
||||
**方案**:JSON 格式导入,服务端校验 + 批量写入 + 去重。
|
||||
|
||||
- 接收 JSON 数组,校验字段格式(必填:date, type, amount, category_name)
|
||||
- 按 `date + type + amount + note` 组合去重(查现有记录比对)
|
||||
- 使用 `INSERT ... VALUES (...), (...), ...` 批量写入(每批 100 条)
|
||||
- 返回结果:`{ total, imported, skipped, errors[] }`
|
||||
|
||||
#### 1.5 统计年度/周视图(P2-1)
|
||||
|
||||
**方案**:后端 `period` 参数扩展 + 前端维度切换器。
|
||||
|
||||
- 后端:`GET /stats/category` 和 `GET /stats/trend` 新增 `period=week|month|year` 参数
|
||||
- `week`:基于当前日期计算本周一至本周日,趋势 X 轴为周一~周日
|
||||
- `month`:保持现有行为(默认)
|
||||
- `year`:基于当前年份 1-12 月,趋势 X 轴为 1月~12月
|
||||
- 年视图日期范围计算:`startDate = ${year}-01-01`, `endDate = ${year}-12-31`
|
||||
- 周视图日期范围计算:获取本周一和本周日
|
||||
- 趋势数据 GROUP BY 逻辑:年视图按月 GROUP,周视图按日 GROUP
|
||||
|
||||
---
|
||||
|
||||
### 2. 文件列表及相对路径
|
||||
|
||||
#### 2.1 新建文件
|
||||
|
||||
| 文件路径 | 说明 |
|
||||
|---------|------|
|
||||
| `client/src/api/backup.ts` | 备份管理 API(下载备份) |
|
||||
| `client/src/api/tag.ts` | 标签 CRUD + 统计 API |
|
||||
| `client/src/api/export.ts` | 服务端导出 API |
|
||||
| `client/src/api/health.ts` | 健康检查 API |
|
||||
| `client/src/stores/tag.ts` | 标签 Pinia Store |
|
||||
| `client/src/components/DragSortList/DragSortList.vue` | 拖拽排序列表组件 |
|
||||
| `client/src/pages/backup-manage/index.vue` | 备份管理页面(仅管理员) |
|
||||
| `client/src/pages/data-import/index.vue` | 数据导入页面 |
|
||||
| `client/src/pages/tag-manage/index.vue` | 标签管理页面 |
|
||||
| `server/src/routes/tag.ts` | 标签 CRUD + 按标签统计路由 |
|
||||
| `server/src/routes/export.ts` | 服务端导出路由 |
|
||||
|
||||
#### 2.2 修改文件
|
||||
|
||||
| 文件路径 | 主要变更 |
|
||||
|---------|---------|
|
||||
| `server/src/db/schema.sql` | 新增 tags、transaction_tags 表定义 |
|
||||
| `server/src/db/init.ts` | 新增 tags、transaction_tags 表迁移 |
|
||||
| `server/src/routes/feedback.ts` | 新增 `GET /mine` 端点(用户查看自己的反馈) |
|
||||
| `server/src/routes/backup.ts` | 新增 `GET /:id/download` 端点(临时签名 URL) |
|
||||
| `server/src/routes/stats.ts` | category/trend 端点增加 `period` 参数支持 |
|
||||
| `server/src/routes/transaction.ts` | POST/PUT 支持 `tagIds`;GET 支持 `tagId` 筛选;GET/:id 返回 tags |
|
||||
| `server/src/index.ts` | health 端点移到 authMiddleware 之前;注册 tag、export 路由 |
|
||||
| `client/src/api/feedback.ts` | 新增 `getMyFeedbacks()` API 函数 |
|
||||
| `client/src/api/stats.ts` | 修改 `getCategoryStats`、`getTrend` 支持 `period` 参数 |
|
||||
| `client/src/api/transaction.ts` | 新增 `importTransactions()`;类型扩展 `tagIds`、`tagId` |
|
||||
| `client/src/api/admin.ts` | 新增 `getHealth()` API 函数 |
|
||||
| `client/src/stores/category.ts` | 无需修改(sortCategories 已就绪) |
|
||||
| `client/src/stores/stats.ts` | fetchCategoryStats/fetchTrend 支持 period 参数 |
|
||||
| `client/src/pages/category-manage/index.vue` | 集成 DragSortList 拖拽排序 |
|
||||
| `client/src/pages/feedback/index.vue` | 新增"我的反馈"Tab |
|
||||
| `client/src/pages/stats/index.vue` | 添加维度切换器(周/月/年) |
|
||||
| `client/src/pages/profile/index.vue` | 导出面板增加"服务端导出"选项 |
|
||||
| `client/src/pages/add/index.vue` | 添加标签选择入口 |
|
||||
| `client/src/pages/bills/index.vue` | 添加标签筛选功能 |
|
||||
| `client/src/pages/admin/index.vue` | 添加服务状态卡片 |
|
||||
| `client/src/pages.json` | 注册新页面路由 |
|
||||
|
||||
---
|
||||
|
||||
### 3. 数据结构和接口(类图)
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class Tag {
|
||||
+int id
|
||||
+int user_id
|
||||
+string name
|
||||
+string color
|
||||
+string created_at
|
||||
}
|
||||
|
||||
class TransactionTag {
|
||||
+int transaction_id
|
||||
+int tag_id
|
||||
}
|
||||
|
||||
class Transaction {
|
||||
+int id
|
||||
+int user_id
|
||||
+int amount
|
||||
+string type
|
||||
+int category_id
|
||||
+string note
|
||||
+string date
|
||||
+int group_id
|
||||
+int recurring_id
|
||||
+Tag[] tags
|
||||
}
|
||||
|
||||
class Feedback {
|
||||
+int id
|
||||
+int user_id
|
||||
+string type
|
||||
+string content
|
||||
+string contact
|
||||
+string status
|
||||
+string admin_reply
|
||||
+string created_at
|
||||
}
|
||||
|
||||
class Category {
|
||||
+int id
|
||||
+int user_id
|
||||
+string name
|
||||
+string icon
|
||||
+string color
|
||||
+string type
|
||||
+int sort_order
|
||||
+int is_custom
|
||||
}
|
||||
|
||||
Transaction "1" --o "*" TransactionTag : has
|
||||
Tag "1" --o "*" TransactionTag : referenced_by
|
||||
Transaction ..> Category : belongs_to
|
||||
|
||||
note for Tag "每用户最多 20 个标签\n8 色预设颜色选择器\n标签不区分收支类型"
|
||||
note for TransactionTag "复合主键 (transaction_id, tag_id)\n每笔交易最多 5 个标签"
|
||||
```
|
||||
|
||||
#### 3.1 新增数据库表
|
||||
|
||||
**tags 表**
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS tags (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id INT NOT NULL,
|
||||
name VARCHAR(20) NOT NULL COMMENT '标签名称',
|
||||
color VARCHAR(20) NOT NULL DEFAULT '#FF8C69' COMMENT '标签颜色',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uk_user_name (user_id, name),
|
||||
INDEX idx_user (user_id),
|
||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
```
|
||||
|
||||
**transaction_tags 表**
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS transaction_tags (
|
||||
transaction_id INT NOT NULL,
|
||||
tag_id INT NOT NULL,
|
||||
PRIMARY KEY (transaction_id, tag_id),
|
||||
INDEX idx_tag (tag_id),
|
||||
FOREIGN KEY (transaction_id) REFERENCES transactions(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
```
|
||||
|
||||
#### 3.2 新增/修改 API 端点
|
||||
|
||||
##### 标签 CRUD
|
||||
|
||||
| 方法 | 路径 | 请求 | 响应 | 说明 |
|
||||
|------|------|------|------|------|
|
||||
| GET | `/api/tags` | — | `{ list: Tag[] }` | 获取当前用户所有标签 |
|
||||
| POST | `/api/tags` | `{ name, color }` | `{ id }` | 创建标签(限 20 个/用户) |
|
||||
| PUT | `/api/tags/:id` | `{ name?, color? }` | — | 更新标签 |
|
||||
| DELETE | `/api/tags/:id` | — | — | 删除标签(级联删除关联) |
|
||||
| GET | `/api/stats/by-tag` | `?month&period&type` | `TagStat[]` | 按标签统计 |
|
||||
|
||||
**TagStat 类型**:
|
||||
```ts
|
||||
{ id: number; name: string; color: string; amount: number; count: number }
|
||||
```
|
||||
|
||||
##### 用户反馈列表
|
||||
|
||||
| 方法 | 路径 | 请求 | 响应 | 说明 |
|
||||
|------|------|------|------|------|
|
||||
| GET | `/api/feedback/mine` | `?page&pageSize` | `{ list, total, page, pageSize }` | 当前用户的反馈列表 |
|
||||
|
||||
##### 备份下载
|
||||
|
||||
| 方法 | 路径 | 请求 | 响应 | 说明 |
|
||||
|------|------|------|------|------|
|
||||
| GET | `/api/backup/:id/download` | — | 文件流 | 下载备份文件(临时签名 URL,1h 有效) |
|
||||
|
||||
**实现方式**:在 `backup` 表中无需存储,使用 HMAC-SHA256 生成带过期时间的签名 token,下载时校验签名。签名格式:`backupId:timestamp:hmac-sha256(backupId:timestamp)`。将签名作为 `?token=xxx` 传给前端,下载端点校验 token 有效性(无需认证中间件)。
|
||||
|
||||
##### 统计年度/周视图
|
||||
|
||||
| 方法 | 路径 | 请求参数 | 说明 |
|
||||
|------|------|---------|------|
|
||||
| GET | `/api/stats/category` | 新增 `period=week\|month\|year` | 周/月/年分类统计 |
|
||||
| GET | `/api/stats/trend` | 新增 `period=week\|month\|year` | 周/月/年趋势数据 |
|
||||
|
||||
**period 参数逻辑**:
|
||||
- `week`(新增):`startDate` = 本周一,`endDate` = 本周日,趋势按日 GROUP
|
||||
- `month`(默认):保持现有行为,趋势按日 GROUP
|
||||
- `year`(新增):`startDate` = 当年1月1日,`endDate` = 当年12月31日,趋势按月 GROUP(`DATE_FORMAT(date, '%Y-%m')`)
|
||||
|
||||
**年视图趋势响应**(新增 `label` 字段):
|
||||
```ts
|
||||
{ label: string; date: string; amount: number }[]
|
||||
// 年视图 label = "1月"~"12月"
|
||||
// 周视图 label = "周一"~"周日"
|
||||
// 月视图 label = "1日"~"31日"(保持兼容)
|
||||
```
|
||||
|
||||
##### 数据导入
|
||||
|
||||
| 方法 | 路径 | 请求 | 响应 | 说明 |
|
||||
|------|------|------|------|------|
|
||||
| POST | `/api/transactions/import` | `{ items: ImportItem[] }` | `{ total, imported, skipped, errors[] }` | 批量导入 |
|
||||
|
||||
**ImportItem 类型**:
|
||||
```ts
|
||||
{
|
||||
date: string // 必填,YYYY-MM-DD
|
||||
type: string // 必填,expense | income
|
||||
amount: number // 必填,单位:分
|
||||
category_name: string // 可选,匹配不到则归入"未分类"
|
||||
note?: string
|
||||
tag_names?: string[] // 可选,按名称匹配标签
|
||||
}
|
||||
```
|
||||
|
||||
##### 服务端导出
|
||||
|
||||
| 方法 | 路径 | 请求参数 | 响应 | 说明 |
|
||||
|------|------|---------|------|------|
|
||||
| GET | `/api/export` | `startDate, endDate, type, format=csv\|json` | 文件流 | 流式导出 |
|
||||
|
||||
##### 健康检查(改造)
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| GET | `/api/health` | 移到 `authMiddleware` 之前,无需认证 |
|
||||
|
||||
##### 交易记录(改造)
|
||||
|
||||
| 方法 | 路径 | 变更 |
|
||||
|------|------|------|
|
||||
| POST | `/api/transactions` | 请求新增 `tagIds: number[]`(可选,最多 5 个) |
|
||||
| PUT | `/api/transactions/:id` | 请求新增 `tagIds: number[]`(可选,最多 5 个,全量替换) |
|
||||
| GET | `/api/transactions` | 新增 `tagId` 查询参数筛选 |
|
||||
| GET | `/api/transactions/:id` | 响应新增 `tags: { id, name, color }[]` |
|
||||
|
||||
---
|
||||
|
||||
### 4. 程序调用流程(时序图)
|
||||
|
||||
#### 4.1 分类拖拽排序
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant U as 用户
|
||||
participant P as category-manage
|
||||
participant D as DragSortList
|
||||
participant S as categoryStore
|
||||
participant A as sortCategories API
|
||||
participant B as PUT /categories/sort
|
||||
|
||||
U->>P: 长按分类项进入排序模式
|
||||
P->>P: showSortMode = true
|
||||
P->>D: 渲染 DragSortList (items=currentCategories)
|
||||
U->>D: touchstart (记录起始位置)
|
||||
U->>D: touchmove (计算偏移, 交换元素位置)
|
||||
D->>D: 实时更新 items 数组顺序
|
||||
U->>D: touchend (拖拽结束)
|
||||
D->>P: @change事件 (新顺序ids)
|
||||
P->>S: sortCategories(newIds)
|
||||
S->>A: sortCategories(ids)
|
||||
A->>B: PUT /categories/sort { ids }
|
||||
B-->>A: { code: 0 }
|
||||
A-->>S: 成功
|
||||
S->>S: fetchCategories() 刷新
|
||||
```
|
||||
|
||||
#### 4.2 数据导入
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant U as 用户
|
||||
participant P as data-import
|
||||
participant A as transaction API
|
||||
participant S as POST /transactions/import
|
||||
participant DB as MySQL
|
||||
|
||||
U->>P: 选择 JSON 文件
|
||||
P->>P: uni.chooseFile / 读取文件内容
|
||||
P->>P: 解析 JSON,显示预览(条数、日期范围)
|
||||
U->>P: 确认导入
|
||||
P->>A: importTransactions(items)
|
||||
A->>S: POST /transactions/import { items }
|
||||
S->>S: 校验每条记录格式
|
||||
S->>DB: 查询已有记录 (去重比对)
|
||||
S->>DB: 批量 INSERT (每批100条)
|
||||
S->>DB: 写入 transaction_tags (如有 tag_names)
|
||||
S-->>A: { total, imported, skipped, errors }
|
||||
A-->>P: 导入结果
|
||||
P->>U: 显示导入结果(成功X条, 跳过Y条)
|
||||
```
|
||||
|
||||
#### 4.3 标签关联交易
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant U as 用户
|
||||
participant P as add/index
|
||||
participant TS as tagStore
|
||||
participant TA as transaction API
|
||||
participant S as POST /transactions
|
||||
participant DB as MySQL
|
||||
|
||||
U->>P: 点击"添加标签"
|
||||
P->>P: 显示标签选择面板(已有标签 + 新建入口)
|
||||
U->>P: 选择标签(最多5个)
|
||||
P->>P: selectedTagIds 更新
|
||||
U->>P: 保存交易
|
||||
P->>TA: createTransaction({...data, tagIds})
|
||||
TA->>S: POST /transactions { amount, type, ..., tagIds }
|
||||
S->>S: 校验 tagIds (≤5, 属于当前用户)
|
||||
S->>DB: INSERT INTO transactions
|
||||
S->>DB: INSERT INTO transaction_tags (批量)
|
||||
S-->>TA: { id }
|
||||
TA-->>P: 成功
|
||||
|
||||
Note over P,U: 编辑交易时同理,PUT 全量替换 tagIds
|
||||
```
|
||||
|
||||
#### 4.4 服务端导出
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant U as 用户
|
||||
participant P as profile/index
|
||||
participant A as export API
|
||||
participant S as GET /api/export
|
||||
participant DB as MySQL
|
||||
|
||||
U->>P: 点击"服务端导出"
|
||||
P->>A: serverExport(params)
|
||||
A->>S: GET /api/export?startDate=&endDate=&type=&format=
|
||||
S->>DB: SELECT COUNT(*) (判断数量)
|
||||
alt 记录 < 10000
|
||||
S->>DB: 流式查询 (cursor)
|
||||
S->>S: Readable.from() 逐行生成 CSV/JSON
|
||||
S-->>P: 流式响应 (Transfer-Encoding: chunked)
|
||||
else 记录 ≥ 10000
|
||||
S->>DB: 流式查询写入临时文件
|
||||
S->>S: fs.createReadStream()
|
||||
S-->>P: 流式响应
|
||||
S->>S: 删除临时文件
|
||||
end
|
||||
P->>U: 保存文件到本地
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. 待明确事项
|
||||
|
||||
| # | 事项 | 当前假设 |
|
||||
|---|------|---------|
|
||||
| 1 | 备份下载的签名 URL 是通过新端点直接下载,还是返回签名 URL 让前端跳转? | 采用直接下载方式:`GET /api/backup/:id/download?token=xxx`,后端校验 token 后流式返回文件 |
|
||||
| 2 | 年视图/周视图的 `month` 参数如何处理? | 新增 `period` 参数,当 `period=year` 时 `month` 参数被忽略,使用当前年份;当 `period=week` 时使用当前周 |
|
||||
| 3 | 导入 JSON 的 `category_name` 匹配不到时如何处理? | 归入"未分类"(category_id = NULL),不自动创建分类 |
|
||||
| 4 | 标签预设 8 色具体色值? | 复用现有 `colorOptions`:`#FF8C69, #7BC67E, #5B9BD5, #FFD700, #FF69B4, #8B5CF6, #F97316, #06B6D4` |
|
||||
| 5 | 健康检查移到 authMiddleware 前是否影响安全? | `/api/health` 仅返回 DB 连接状态和服务器时间,不泄露敏感信息,安全无影响 |
|
||||
| 6 | 数据导入文件大小限制? | 前端限制 5MB,后端 `express.json({ limit: '5mb' })` 仅对导入端点放宽(当前全局 10kb) |
|
||||
|
||||
---
|
||||
|
||||
## Part B: 任务分解
|
||||
|
||||
### 6. 需要新增的依赖包
|
||||
|
||||
#### 后端 (server)
|
||||
|
||||
```
|
||||
无新增依赖
|
||||
```
|
||||
|
||||
> 流式导出使用 Node.js 内置 `stream`、`fs`、`os` 模块,CSV 拼装手写无需 `json2csv`,签名 URL 使用已有 `crypto` 模块。
|
||||
|
||||
#### 前端 (client)
|
||||
|
||||
```
|
||||
无新增依赖
|
||||
```
|
||||
|
||||
> 拖拽排序使用 Touch 事件自研实现,不引入第三方拖拽库。
|
||||
|
||||
---
|
||||
|
||||
### 7. 任务列表(按依赖顺序)
|
||||
|
||||
#### T01: 后端基础设施 + 数据库迁移
|
||||
|
||||
**描述**:新增 tags/transaction_tags 表、修改 health 端点位置、注册新路由模块、调整 express.json 限制。
|
||||
|
||||
**涉及文件**:
|
||||
- `server/src/db/schema.sql` — 新增 tags、transaction_tags 表定义
|
||||
- `server/src/db/init.ts` — 新增迁移逻辑(创建 tags、transaction_tags 表)
|
||||
- `server/src/index.ts` — health 移到 authMiddleware 前;注册 tag、export 路由;导入端点 body 限制调整
|
||||
- `server/src/routes/tag.ts` — 新建:标签 CRUD 端点 + 按标签统计端点
|
||||
- `server/src/routes/export.ts` — 新建:服务端流式导出端点
|
||||
|
||||
**依赖任务**:无
|
||||
|
||||
**优先级**:P0
|
||||
|
||||
---
|
||||
|
||||
#### T02: 后端 API 改造(现有路由扩展)
|
||||
|
||||
**描述**:扩展现有路由以支持新功能——反馈 mine 端点、备份下载、交易 tagIds 支持、统计 period 参数。
|
||||
|
||||
**涉及文件**:
|
||||
- `server/src/routes/feedback.ts` — 新增 `GET /mine` 端点
|
||||
- `server/src/routes/backup.ts` — 新增 `GET /:id/download` 端点(签名 URL 校验)
|
||||
- `server/src/routes/transaction.ts` — POST/PUT 支持 tagIds;GET 支持 tagId 筛选;GET/:id 返回 tags
|
||||
- `server/src/routes/stats.ts` — category/trend 增加 period 参数(week/year 视图)
|
||||
|
||||
**依赖任务**:T01(tags 表需先存在)
|
||||
|
||||
**优先级**:P0
|
||||
|
||||
---
|
||||
|
||||
#### T03: 前端 API 层 + Store 层 + 新页面
|
||||
|
||||
**描述**:新增所有前端 API 函数和 Store,创建新页面(标签管理、备份管理、数据导入),创建拖拽组件。
|
||||
|
||||
**涉及文件**:
|
||||
- `client/src/api/feedback.ts` — 新增 `getMyFeedbacks()`
|
||||
- `client/src/api/backup.ts` — 新建:备份列表 + 下载 API
|
||||
- `client/src/api/tag.ts` — 新建:标签 CRUD + 统计 API
|
||||
- `client/src/api/export.ts` — 新建:服务端导出 API
|
||||
- `client/src/api/health.ts` — 新建:健康检查 API
|
||||
- `client/src/api/stats.ts` — 修改:getCategoryStats/getTrend 支持 period
|
||||
- `client/src/api/transaction.ts` — 新增 `importTransactions()`;类型扩展 tagIds/tagId
|
||||
- `client/src/api/admin.ts` — 新增 `getHealth()`
|
||||
- `client/src/stores/tag.ts` — 新建:标签 Pinia Store
|
||||
- `client/src/stores/stats.ts` — 修改:fetchCategoryStats/fetchTrend 支持 period
|
||||
- `client/src/components/DragSortList/DragSortList.vue` — 新建:拖拽排序组件
|
||||
- `client/src/pages/tag-manage/index.vue` — 新建:标签管理页面
|
||||
- `client/src/pages/backup-manage/index.vue` — 新建:备份管理页面
|
||||
- `client/src/pages/data-import/index.vue` — 新建:数据导入页面
|
||||
- `client/src/pages.json` — 注册新页面路由
|
||||
|
||||
**依赖任务**:T01(需知 API 端点格式)
|
||||
|
||||
**优先级**:P1
|
||||
|
||||
---
|
||||
|
||||
#### T04: 前端现有页面改造
|
||||
|
||||
**描述**:改造现有页面以集成新功能——分类拖拽、反馈列表、统计维度切换、标签入口、服务端导出、健康检查卡片。
|
||||
|
||||
**涉及文件**:
|
||||
- `client/src/pages/category-manage/index.vue` — 集成 DragSortList 拖拽排序
|
||||
- `client/src/pages/feedback/index.vue` — 新增"我的反馈"Tab
|
||||
- `client/src/pages/stats/index.vue` — 添加维度切换器(周/月/年)+ 标签统计入口
|
||||
- `client/src/pages/profile/index.vue` — 导出面板增加"服务端导出"选项
|
||||
- `client/src/pages/add/index.vue` — 添加标签选择入口
|
||||
- `client/src/pages/bills/index.vue` — 添加标签筛选功能
|
||||
- `client/src/pages/admin/index.vue` — 添加服务状态卡片(30s 自动刷新)
|
||||
|
||||
**依赖任务**:T03(依赖 API 层和组件)
|
||||
|
||||
**优先级**:P1
|
||||
|
||||
---
|
||||
|
||||
#### T05: 集成联调 + 边界处理
|
||||
|
||||
**描述**:前后端联调、边界场景处理(导入去重、大量数据导出、拖拽边界)、最终测试修复。
|
||||
|
||||
**涉及文件**:
|
||||
- 可能涉及 T01~T04 中所有文件的微调
|
||||
- 重点:`server/src/routes/export.ts`(大量数据流式导出稳定性)
|
||||
- 重点:`server/src/routes/transaction.ts`(导入去重逻辑)
|
||||
- 重点:`client/src/components/DragSortList/DragSortList.vue`(拖拽边界场景)
|
||||
|
||||
**依赖任务**:T04
|
||||
|
||||
**优先级**:P2
|
||||
|
||||
---
|
||||
|
||||
### 8. 共享知识(跨文件约定)
|
||||
|
||||
```
|
||||
- API 响应格式统一:{ code: 0, data: T } 成功;{ code: 5位数字, message: string } 失败
|
||||
- 错误码约定:
|
||||
- 40001: 参数无效
|
||||
- 40002: 类型无效
|
||||
- 40100: 未登录
|
||||
- 40101: token过期
|
||||
- 40300: 无权限
|
||||
- 40400: 资源不存在
|
||||
- 42900: 请求频繁
|
||||
- 50000: 服务器内部错误
|
||||
- 金额单位统一为"分"(INT),前端展示时 / 100
|
||||
- 日期格式统一为 YYYY-MM-DD,月份为 YYYY-MM
|
||||
- 认证使用 HMAC-SHA256 Token,Bearer 格式,30天有效期
|
||||
- 公开路径(无需认证):/api/auth/login, /api/auth/demo-login, /api/health, /api/backup/:id/download(token 校验)
|
||||
- 前端 request() 函数自动处理 401 重登录、错误 toast
|
||||
- Pinia Store 方法命名:fetch* (获取数据)、add/create (新增)、update (更新)、delete (删除)
|
||||
- 前端 API 函数命名:get* (GET), create*/submit* (POST), update*/sort* (PUT), delete* (DELETE)
|
||||
- SCSS 变量:$primary=#FF8C69, $success=#7BC67E, $danger=#FF6B6B, $text=#2D1B1B, $surface=#FFFFFF, $bg=#FFF8F0
|
||||
- 组件样式使用 scoped + @import '@/styles/mixins.scss'
|
||||
- 标签预设 8 色:#FF8C69, #7BC67E, #5B9BD5, #FFD700, #FF69B4, #8B5CF6, #F97316, #06B6D4
|
||||
- 每笔交易最多 5 个标签,每用户最多 20 个标签
|
||||
- 导入文件限制 5MB,导出流式响应超时 60s
|
||||
- 分页参数:page (从1开始), pageSize (默认20, 最大100)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 9. 任务依赖图
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
T01[T01: 后端基础设施+DB迁移] --> T02[T02: 后端API改造]
|
||||
T01 --> T03[T03: 前端API+Store+新页面]
|
||||
T02 --> T04[T04: 前端现有页面改造]
|
||||
T03 --> T04
|
||||
T04 --> T05[T05: 集成联调+边界处理]
|
||||
```
|
||||
55
docs/class-diagram.mermaid
Normal file
55
docs/class-diagram.mermaid
Normal file
@@ -0,0 +1,55 @@
|
||||
classDiagram
|
||||
class Tag {
|
||||
+int id
|
||||
+int user_id
|
||||
+string name
|
||||
+string color
|
||||
+string created_at
|
||||
}
|
||||
|
||||
class TransactionTag {
|
||||
+int transaction_id
|
||||
+int tag_id
|
||||
}
|
||||
|
||||
class Transaction {
|
||||
+int id
|
||||
+int user_id
|
||||
+int amount
|
||||
+string type
|
||||
+int category_id
|
||||
+string note
|
||||
+string date
|
||||
+int group_id
|
||||
+int recurring_id
|
||||
+Tag[] tags
|
||||
}
|
||||
|
||||
class Feedback {
|
||||
+int id
|
||||
+int user_id
|
||||
+string type
|
||||
+string content
|
||||
+string contact
|
||||
+string status
|
||||
+string admin_reply
|
||||
+string created_at
|
||||
}
|
||||
|
||||
class Category {
|
||||
+int id
|
||||
+int user_id
|
||||
+string name
|
||||
+string icon
|
||||
+string color
|
||||
+string type
|
||||
+int sort_order
|
||||
+int is_custom
|
||||
}
|
||||
|
||||
Transaction "1" --o "*" TransactionTag : has
|
||||
Tag "1" --o "*" TransactionTag : referenced_by
|
||||
Transaction ..> Category : belongs_to
|
||||
|
||||
note for Tag "每用户最多 20 个标签\n8 色预设颜色选择器\n标签不区分收支类型"
|
||||
note for TransactionTag "复合主键 (transaction_id, tag_id)\n每笔交易最多 5 个标签"
|
||||
254
docs/prd-alignment.md
Normal file
254
docs/prd-alignment.md
Normal file
@@ -0,0 +1,254 @@
|
||||
# 小菜记账 — 前后端对齐增量 PRD
|
||||
|
||||
> 版本: v1.0 | 日期: 2026-07-11
|
||||
> 类型: 增量 PRD(仅包含变更部分)
|
||||
> 基线: 前后端对齐分析发现的 8 项差距
|
||||
|
||||
---
|
||||
|
||||
## 一、产品目标
|
||||
|
||||
| # | 目标 | 说明 |
|
||||
|---|------|------|
|
||||
| G1 | 消灭前后端差距,实现已开发后端能力的前端落地 | 后端已有排序、备份、健康检查等端点,但前端缺失对应 UI |
|
||||
| G2 | 补齐用户反馈闭环,提升产品可信度 | 用户提交反馈后无法查看回复,体验断裂 |
|
||||
| G3 | 扩展数据维度与吞吐能力 | 支持年度/周度统计、服务端导出、数据导入,为数据量增长做准备 |
|
||||
|
||||
---
|
||||
|
||||
## 二、用户故事
|
||||
|
||||
### P0 — 分类拖拽排序
|
||||
|
||||
> As a 用户, I want 在分类管理页面拖拽调整分类顺序, so that 常用分类排在前面,记账时选得更快。
|
||||
|
||||
### P1 — 反馈回复展示
|
||||
|
||||
> As a 用户, I want 查看自己提交的反馈列表及管理员回复, so that 我知道反馈是否被处理以及处理结果。
|
||||
|
||||
### P1 — 备份管理页面
|
||||
|
||||
> As a 管理员, I want 在前端触发备份、查看备份列表并下载备份文件, so that 我可以在出现问题时快速恢复数据。
|
||||
|
||||
### P2 — 统计年度/周视图
|
||||
|
||||
> As a 用户, I want 在统计页面切换年/周/月维度, so that 我可以从不同时间粒度分析收支趋势。
|
||||
|
||||
### P2 — 数据导入与服务端导出
|
||||
|
||||
> As a 用户, I want 导入 JSON 格式的交易数据, so that 我可以从其他记账工具迁移历史数据。
|
||||
>
|
||||
> As a 用户, I want 大数据量导出走服务端生成, so that 导出速度快、不卡顿。
|
||||
|
||||
### P3 — 健康检查
|
||||
|
||||
> As a 管理员, I want 在管理页面看到后端服务运行状态, so that 我能第一时间发现服务异常。
|
||||
|
||||
### P3 — 交易标签系统
|
||||
|
||||
> As a 用户, I want 给交易添加标签(如"出差""聚餐"), so that 我可以按标签筛选和统计关联交易。
|
||||
>
|
||||
> As a 用户, I want 按标签筛选交易列表和查看标签维度的统计, so that 我可以追踪特定场景的收支。
|
||||
|
||||
---
|
||||
|
||||
## 三、需求池
|
||||
|
||||
### P0 — Must Have
|
||||
|
||||
| ID | 需求 | 前端 | 后端 | 验收标准 |
|
||||
|----|------|------|------|----------|
|
||||
| P0-1 | 分类拖拽排序 | 分类管理页面添加拖拽排序 UI,拖拽结束后调用 `sortCategories(ids)` | 无新增(`PUT /categories/sort` 已实现) | 拖拽后顺序持久化;刷新页面顺序保持;支出/收入分类各自排序 |
|
||||
|
||||
### P1 — Should Have
|
||||
|
||||
| ID | 需求 | 前端 | 后端 | 验收标准 |
|
||||
|----|------|------|------|----------|
|
||||
| P1-1 | 用户反馈列表 | 反馈页面新增"我的反馈"Tab,展示反馈列表及管理员回复 | 新增 `GET /feedback/mine` 端点(用户维度,返回当前用户的反馈+`admin_reply`) | 用户只能看自己的反馈;未回复显示"待处理"状态;已回复显示回复内容和时间 |
|
||||
| P1-2 | 备份管理页面 | 新增备份管理页面:触发备份按钮、备份列表、下载按钮 | 新增 `GET /backup/:id/download` 端点 | 管理员可触发备份并看到进度;列表显示备份时间和大小;点击下载获取备份文件 |
|
||||
|
||||
### P2 — Nice to Have
|
||||
|
||||
| ID | 需求 | 前端 | 后端 | 验收标准 |
|
||||
|----|------|------|------|----------|
|
||||
| P2-1 | 统计年度/周视图 | 统计页面添加维度切换器(周/月/年),切换后图表和排行联动更新 | `GET /stats/category` 和 `GET /stats/trend` 支持 `period=week|month|year` 参数,按对应维度聚合数据 | 周视图显示本周每日数据;年视图显示每月汇总;切换时图表平滑过渡 |
|
||||
| P2-2 | 数据导入 | 我的页面新增"数据导入"入口;支持 JSON 文件上传;导入前预览数据条数,确认后提交 | 新增 `POST /api/transactions/import` 端点,接收 JSON 文件,校验格式,批量写入 | 支持 JSON 格式;导入前展示条数预览;重复数据(同日期/金额/分类)跳过并提示;导入完成显示成功/跳过计数 |
|
||||
| P2-3 | 数据导出服务端化 | 数据导出页面新增"服务端导出"选项;大数据量(>1000条)自动走服务端 | 新增 `GET /api/export` 端点,支持 CSV/JSON 格式,流式响应 | 客户端导出保留作为轻量选项;服务端导出无条数限制;下载进度可感知 |
|
||||
|
||||
### P3 — Nice to Have(远期)
|
||||
|
||||
| ID | 需求 | 前端 | 后端 | 验收标准 |
|
||||
|----|------|------|------|----------|
|
||||
| P3-1 | 健康检查展示 | 管理页面新增服务状态卡片,显示后端健康信息 | 将 `/api/health` 端点移到 authMiddleware 之前 | 管理页面实时显示服务状态(正常/异常);无需登录即可调用健康检查 |
|
||||
| P3-2 | 交易标签 — 标签管理 | 新增标签管理页面:创建/编辑/删除标签(名称+颜色) | 新增 `tags` 表及 CRUD 端点(`GET/POST/PUT/DELETE /api/tags`) | 每个用户独立标签;标签不可重名;删除标签时解绑关联交易 |
|
||||
| P3-3 | 交易标签 — 交易关联 | 记账页面新增"添加标签"入口;交易详情/编辑页可修改标签 | `transactions` 表新增关联;`POST/PUT /transactions` 支持 `tagIds` 字段 | 一笔交易可关联多个标签;标签选择器展示用户已有标签 |
|
||||
| P3-4 | 交易标签 — 标签筛选统计 | 账单页支持按标签筛选;统计页新增标签维度视图 | `GET /transactions` 支持 `tagId` 筛选参数;`GET /stats/by-tag` 新端点 | 筛选结果仅显示含指定标签的交易;标签统计展示各标签下收支总额 |
|
||||
|
||||
---
|
||||
|
||||
## 四、UI 交互说明
|
||||
|
||||
### P0-1 分类拖拽排序
|
||||
|
||||
```
|
||||
分类管理页面(category-manage/index.vue)变更:
|
||||
|
||||
1. 页面顶部新增提示栏:"长按拖拽可调整分类顺序"
|
||||
2. 每个分类项左侧显示拖拽手柄图标(Lucide GripVertical, #BFB3B3)
|
||||
3. 长按分类项(300ms)进入拖拽模式:
|
||||
- 被拖拽项 elevation 提升(shadow-clay-float)
|
||||
- 背景色变为 #FFF0E6
|
||||
- 其余项轻微缩进让出空间
|
||||
4. 拖拽过程中实时交换位置(带 150ms 过渡动画)
|
||||
5. 松手后:
|
||||
- 调用 sortCategories(ids) 提交新顺序
|
||||
- 成功:轻量 toast "排序已保存"
|
||||
- 失败:回滚到原位置 + 错误提示
|
||||
6. 支出/收入 Tab 各自独立排序,互不影响
|
||||
```
|
||||
|
||||
### P1-1 用户反馈列表
|
||||
|
||||
```
|
||||
反馈页面(feedback/index.vue)变更:
|
||||
|
||||
1. 页面顶部新增 Tab 切换:"提交反馈" | "我的反馈"
|
||||
2. "我的反馈" Tab 内容:
|
||||
├── 列表按提交时间倒序排列
|
||||
├── 每项显示:
|
||||
│ ├── 反馈内容(截断2行,点击展开)
|
||||
│ ├── 提交时间(相对时间:"3天前")
|
||||
│ ├── 状态标签:
|
||||
│ │ ├── 待处理 → 灰色 #BFB3B3
|
||||
│ │ ├── 已回复 → 绿色 #7BC67E
|
||||
│ │ └── 已关闭 → 橙色 #FF8C69
|
||||
│ └── 管理员回复区域(仅已回复时显示):
|
||||
│ ├── 分割线 + "管理员回复" 标签
|
||||
│ ├── 回复内容
|
||||
│ └── 回复时间
|
||||
└── 空状态:插画 + "还没有提交过反馈"
|
||||
3. 下拉刷新更新列表
|
||||
4. 上拉加载更多(分页)
|
||||
```
|
||||
|
||||
### P1-2 备份管理页面
|
||||
|
||||
```
|
||||
新增页面(backup-manage/index.vue):
|
||||
|
||||
1. 顶部操作区:
|
||||
├── [立即备份] 主按钮(Clay 风格)
|
||||
├── 点击后按钮变为 loading 态
|
||||
└── 成功后列表顶部新增一条记录 + toast "备份成功"
|
||||
2. 备份列表:
|
||||
├── 按时间倒序
|
||||
├── 每项显示:
|
||||
│ ├── 备份时间(2026-07-11 14:30)
|
||||
│ ├── 文件大小(12.3 MB)
|
||||
│ └── [下载] 按钮(Lucide Download 图标)
|
||||
├── 点击下载:
|
||||
│ ├── 按钮变为进度条
|
||||
│ └── 完成后 toast "下载完成"
|
||||
└── 空状态:"暂无备份记录"
|
||||
3. 仅管理员角色可见此页面入口
|
||||
```
|
||||
|
||||
### P2-1 统计年度/周视图
|
||||
|
||||
```
|
||||
统计页面(stats/index.vue)变更:
|
||||
|
||||
1. 月份选择器升级为维度切换器:
|
||||
├── 新增三个 Tab:周 | 月 | 年
|
||||
├── 样式同现有支出/收入切换器
|
||||
└── 切换后日期选择器联动:
|
||||
├── 周:显示 "2026年7月 第2周",左右切换周
|
||||
├── 月:保持现有行为
|
||||
└── 年:显示 "2026年",左右切换年
|
||||
2. 图表联动更新:
|
||||
├── 周维度:趋势图 X 轴为周一~周日
|
||||
├── 年维度:趋势图 X 轴为1月~12月
|
||||
└── 环形图和排行同步更新为对应维度数据
|
||||
3. 概览卡片数值联动
|
||||
```
|
||||
|
||||
### P2-2 & P2-3 数据导入与导出
|
||||
|
||||
```
|
||||
我的页面变更 + 新增页面:
|
||||
|
||||
1. "数据导出"入口变更:
|
||||
├── 点击后进入导出页面
|
||||
├── 导出方式选择:
|
||||
│ ├── 客户端导出(< 1000 条推荐)— 现有逻辑
|
||||
│ └── 服务端导出(≥ 1000 条推荐)— 新增
|
||||
├── 格式选择:CSV | JSON
|
||||
├── 日期范围选择器
|
||||
└── [开始导出] 按钮
|
||||
2. 新增"数据导入"入口(我的页面功能列表):
|
||||
├── 点击后进入导入页面
|
||||
├── 步骤流程:
|
||||
│ ├── Step 1:选择文件(仅 .json)
|
||||
│ ├── Step 2:预览 — 显示识别条数、日期范围、分类映射
|
||||
│ ├── Step 3:确认导入
|
||||
│ └── 结果:成功 N 条 / 跳过 M 条
|
||||
└── 导入模板下载链接
|
||||
```
|
||||
|
||||
### P3-1 健康检查展示
|
||||
|
||||
```
|
||||
管理页面变更:
|
||||
|
||||
1. 页面顶部新增服务状态卡片:
|
||||
├── 正常:绿色圆点 + "服务运行正常" + 响应时间 "23ms"
|
||||
├── 异常:红色圆点 + "服务异常" + 错误信息
|
||||
└── 检测中:灰色圆点 + 旋转图标
|
||||
2. 每 30 秒自动刷新一次
|
||||
3. 点击卡片可手动刷新
|
||||
```
|
||||
|
||||
### P3-2~4 交易标签系统
|
||||
|
||||
```
|
||||
全局变更:
|
||||
|
||||
1. 记账页面(add/index.vue):
|
||||
├── 附加信息区新增"添加标签"行
|
||||
├── 点击弹出标签选择器(底部弹窗):
|
||||
│ ├── 已有标签列表(可多选)
|
||||
│ ├── 搜索框
|
||||
│ └── [新建标签] 快捷入口
|
||||
└── 已选标签以药丸标签形式展示在输入区
|
||||
2. 账单页面(bills/index.vue):
|
||||
├── 筛选栏新增标签筛选
|
||||
└── 筛选弹窗中增加标签选择
|
||||
3. 统计页面(stats/index.vue):
|
||||
└── 新增"标签统计"卡片(环形图 + 排行)
|
||||
4. 我的页面:
|
||||
└── 功能列表新增"标签管理"入口
|
||||
5. 标签管理页面(tag-manage/index.vue)— 新增:
|
||||
├── 标签列表(名称 + 颜色色块 + 关联交易数)
|
||||
├── [新建标签] 按钮
|
||||
├── 编辑弹窗:名称输入 + 颜色选择器(8色预设)
|
||||
└── 删除确认:关联 N 笔交易时提示"将解除关联"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、待确认问题
|
||||
|
||||
| # | 问题 | 影响范围 | 建议 |
|
||||
|---|------|----------|------|
|
||||
| Q1 | 分类拖拽排序是否需要动画反馈?拖拽手柄位置(左侧还是右侧)? | P0-1 | 建议左侧手柄 + 长按触发,符合主流交互习惯 |
|
||||
| Q2 | 用户反馈列表是否需要通知推送?当管理员回复后用户是否收到微信服务通知? | P1-1 | 后端已有通知逻辑,但微信订阅消息需用户主动订阅,建议先做页面内展示,通知作为后续增强 |
|
||||
| Q3 | 备份文件的下载鉴权方式?URL 是否需要时效性签名? | P1-2 | 建议使用临时签名 URL(有效期 1 小时),避免备份文件被猜测 URL 直接访问 |
|
||||
| Q4 | 统计年度视图的趋势图粒度:按月汇总还是按季汇总? | P2-1 | 建议按月汇总(12 个点),季度可通过后续分组实现 |
|
||||
| Q5 | 数据导入的 JSON 格式规范?是否需要支持其他记账 App 的导出格式? | P2-2 | 建议先定义小菜记账自有 JSON 格式,提供模板下载;其他 App 格式兼容作为远期需求 |
|
||||
| Q6 | 服务端导出的文件存储方式:内存生成直接流式返回 vs 先写文件再下载? | P2-3 | 建议 < 1 万条内存流式返回,≥ 1 万条先写临时文件再下载,避免内存溢出 |
|
||||
| Q7 | 交易标签上限:每笔交易最多关联几个标签?每个用户最多创建多少个标签? | P3-2~4 | 建议每笔交易最多 5 个标签,每用户最多 20 个标签,后续可调整 |
|
||||
| Q8 | 标签与分类的关系:标签是否可以跨分类?同一标签能否同时用于支出和收入? | P3-2~4 | 建议标签不区分收支类型,跨分类使用,这是标签区别于分类的核心价值 |
|
||||
|
||||
---
|
||||
|
||||
*文档版本: v1.0 | 最后更新: 2026-07-11*
|
||||
53
docs/sequence-diagram.mermaid
Normal file
53
docs/sequence-diagram.mermaid
Normal file
@@ -0,0 +1,53 @@
|
||||
sequenceDiagram
|
||||
participant U as 用户
|
||||
participant P as category-manage
|
||||
participant D as DragSortList
|
||||
participant S as categoryStore
|
||||
participant A as sortCategories API
|
||||
participant B as PUT /categories/sort
|
||||
|
||||
U->>P: 长按分类项进入排序模式
|
||||
P->>P: showSortMode = true
|
||||
P->>D: 渲染 DragSortList (items=currentCategories)
|
||||
U->>D: touchstart (记录起始位置)
|
||||
U->>D: touchmove (计算偏移, 交换元素位置)
|
||||
D->>D: 实时更新 items 数组顺序
|
||||
U->>D: touchend (拖拽结束)
|
||||
D->>P: @change事件 (新顺序ids)
|
||||
P->>S: sortCategories(newIds)
|
||||
S->>A: sortCategories(ids)
|
||||
A->>B: PUT /categories/sort { ids }
|
||||
B-->>A: { code: 0 }
|
||||
A-->>S: 成功
|
||||
S->>S: fetchCategories() 刷新
|
||||
|
||||
%% 数据导入流程
|
||||
|
||||
U->>P2 as data-import: 选择 JSON 文件
|
||||
P2->>P2: uni.chooseFile / 读取文件内容
|
||||
P2->>P2: 解析 JSON,显示预览(条数、日期范围)
|
||||
U->>P2: 确认导入
|
||||
P2->>A2 as transaction API: importTransactions(items)
|
||||
A2->>S2 as POST /transactions/import: POST { items }
|
||||
S2->>S2: 校验每条记录格式
|
||||
S2->>DB as MySQL: 查询已有记录 (去重比对)
|
||||
S2->>DB: 批量 INSERT (每批100条)
|
||||
S2->>DB: 写入 transaction_tags (如有 tag_names)
|
||||
S2-->>A2: { total, imported, skipped, errors }
|
||||
A2-->>P2: 导入结果
|
||||
P2->>U: 显示导入结果(成功X条, 跳过Y条)
|
||||
|
||||
%% 标签关联交易流程
|
||||
|
||||
U->>P3 as add/index: 点击"添加标签"
|
||||
P3->>P3: 显示标签选择面板(已有标签 + 新建入口)
|
||||
U->>P3: 选择标签(最多5个)
|
||||
P3->>P3: selectedTagIds 更新
|
||||
U->>P3: 保存交易
|
||||
P3->>A3 as transaction API: createTransaction({...data, tagIds})
|
||||
A3->>S3 as POST /transactions: POST { amount, type, ..., tagIds }
|
||||
S3->>S3: 校验 tagIds (≤5, 属于当前用户)
|
||||
S3->>DB: INSERT INTO transactions
|
||||
S3->>DB: INSERT INTO transaction_tags (批量)
|
||||
S3-->>A3: { id }
|
||||
A3-->>P3: 成功
|
||||
Reference in New Issue
Block a user