Files
xiaocai/docs/arch-alignment.md
wangxiaogang 31f6487d61 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个潜在问题
2026-06-10 17:30:36 +08:00

578 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 小菜记账 增量架构设计文档
> 版本v1.0 | 架构师高见远Gao | 日期2025-07
---
## Part A: 系统设计
### 1. 实现方案 + 框架选型
#### 1.1 分类拖拽排序P0-1
**技术挑战**:微信小程序不支持 HTML5 Drag and Drop APIUni-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` | — | 文件流 | 下载备份文件(临时签名 URL1h 有效) |
**实现方式**:在 `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 支持 tagIdsGET 支持 tagId 筛选GET/:id 返回 tags
- `server/src/routes/stats.ts` — category/trend 增加 period 参数week/year 视图)
**依赖任务**T01tags 表需先存在)
**优先级**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 TokenBearer 格式30天有效期
- 公开路径(无需认证):/api/auth/login, /api/auth/demo-login, /api/health, /api/backup/:id/downloadtoken 校验)
- 前端 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: 集成联调+边界处理]
```