|
|
@@ -0,0 +1,383 @@
|
|
|
+# 模板使用量与顶部统计设计
|
|
|
+
|
|
|
+## 1. 背景
|
|
|
+
|
|
|
+当前“我的发型模板”页面已经具备模板列表卡片和顶部 4 个统计卡片的 UI 骨架,但这部分数据还不是真实业务数据:
|
|
|
+
|
|
|
+- 模板列表接口 `GET /api/v1/templates` 当前返回的 `usedCount` 仍是固定值 `0`。
|
|
|
+- 前端 `TemplateListPage` 中顶部统计使用的是本地默认值,且请求的 `/api/v1/stylist/dashboard/template-summary` 在后端并没有真实实现。
|
|
|
+- 用户希望在当前模板表中新增“使用量”字段,用于支撑:
|
|
|
+ - 列表卡片中的“使用 x 次”
|
|
|
+ - 顶部统计中的“本周使用”
|
|
|
+ - 其余 4 个统计卡片的真实展示
|
|
|
+
|
|
|
+本轮目标是在尽量不改动现有模板列表 UI 结构的前提下,把模板使用量和顶部统计接成真实、可持续维护的数据链路。
|
|
|
+
|
|
|
+## 2. 目标
|
|
|
+
|
|
|
+本轮交付后,需要满足以下目标:
|
|
|
+
|
|
|
+1. `hair_template` 表新增累计使用量字段。
|
|
|
+2. 模板列表中的 `usedCount` 返回真实值,卡片展示“使用 x 次”。
|
|
|
+3. “我的发型模板”顶部 4 个统计卡片接入真实后端数据。
|
|
|
+4. “本周使用”按本周增量统计,而不是累计值。
|
|
|
+5. AI 发型模拟成功后,相关模板的累计使用量自动递增。
|
|
|
+
|
|
|
+## 3. 非目标
|
|
|
+
|
|
|
+以下内容不在本次范围内:
|
|
|
+
|
|
|
+1. 不补历史数据回填。当前项目没有历史数据,本轮无需做初始化聚合脚本。
|
|
|
+2. 不新增第五个或更多顶部统计卡片。
|
|
|
+3. 不调整模板列表已确认过的 UI 结构、卡片布局和交互。
|
|
|
+4. 不改变“使用量”的业务含义,本轮统一定义为“成功用于 AI 发型模拟的次数”。
|
|
|
+5. 不引入新的使用日志表。
|
|
|
+
|
|
|
+## 4. 方案选择
|
|
|
+
|
|
|
+### 方案 A:仅存累计使用量,顶部“本周使用”也直接显示累计值
|
|
|
+
|
|
|
+做法:
|
|
|
+
|
|
|
+- 只在 `hair_template` 增加累计字段。
|
|
|
+- 列表卡片和顶部“本周使用”都读累计值。
|
|
|
+
|
|
|
+优点:
|
|
|
+
|
|
|
+- 实现最简单。
|
|
|
+
|
|
|
+缺点:
|
|
|
+
|
|
|
+- “本周使用”语义错误,不符合用户确认的口径。
|
|
|
+
|
|
|
+### 方案 B:查询时临时聚合使用次数
|
|
|
+
|
|
|
+做法:
|
|
|
+
|
|
|
+- 不在 `hair_template` 落累计字段。
|
|
|
+- 列表和顶部统计都在查询时实时聚合 `ai_hair_simulation_task`。
|
|
|
+
|
|
|
+优点:
|
|
|
+
|
|
|
+- 数据完全从事实表推导。
|
|
|
+
|
|
|
+缺点:
|
|
|
+
|
|
|
+- 列表接口每次查询都要聚合,复杂度和查询成本更高。
|
|
|
+- 不符合用户已明确提出的“在模板表新增字段”要求。
|
|
|
+
|
|
|
+### 方案 C:模板表存累计使用量,顶部“本周使用”从任务表做周增量统计
|
|
|
+
|
|
|
+做法:
|
|
|
+
|
|
|
+- 在 `hair_template` 增加 `used_count`,保存累计使用量。
|
|
|
+- AI 模拟任务成功时,对对应模板 `used_count + 1`。
|
|
|
+- 顶部“本周使用”单独从 `ai_hair_simulation_task` 统计本周成功任务数。
|
|
|
+
|
|
|
+优点:
|
|
|
+
|
|
|
+- 列表卡片直接读累计值,查询简单。
|
|
|
+- 顶部“本周使用”保留正确的周增量语义。
|
|
|
+- 改动范围可控,符合当前系统结构。
|
|
|
+
|
|
|
+缺点:
|
|
|
+
|
|
|
+- 需要同时维护累计字段和周统计逻辑。
|
|
|
+
|
|
|
+### 结论
|
|
|
+
|
|
|
+采用 **方案 C:模板表存累计使用量,顶部“本周使用”从任务表做周增量统计**。
|
|
|
+
|
|
|
+## 5. 业务口径
|
|
|
+
|
|
|
+### 5.1 模板累计使用量
|
|
|
+
|
|
|
+- 落库字段:`hair_template.used_count`
|
|
|
+- 含义:该模板累计被成功用于 AI 发型模拟的次数
|
|
|
+- 消费位置:
|
|
|
+ - 模板列表卡片中的“使用 x 次”
|
|
|
+ - 未来如有模板详情统计,也可直接复用
|
|
|
+
|
|
|
+### 5.2 本周使用
|
|
|
+
|
|
|
+- 含义:当前自然周内,属于当前美发师模板的 AI 发型模拟成功任务总数
|
|
|
+- 统计来源:`ai_hair_simulation_task`
|
|
|
+- 判断条件:
|
|
|
+ - `task_status = 'SUCCESSES'`
|
|
|
+ - `task_finish_time` 落在当前自然周范围内
|
|
|
+ - `is_deleted = 0`
|
|
|
+ - `template_id` 能关联到当前美发师自己的 `hair_template`
|
|
|
+
|
|
|
+说明:
|
|
|
+
|
|
|
+- 统计单位是“成功任务数”,不是“去重后的模板数”。
|
|
|
+- 同一个模板在本周内成功使用多次,应累计多次。
|
|
|
+
|
|
|
+### 5.3 顶部 4 个统计卡片
|
|
|
+
|
|
|
+继续沿用当前页面已有的 4 个统计项,不改 UI 结构:
|
|
|
+
|
|
|
+1. `total`
|
|
|
+ - 含义:当前美发师未删除模板总数
|
|
|
+2. `published`
|
|
|
+ - 含义:`status = 'PUBLIC'` 的未删除模板数
|
|
|
+3. `weeklyUsed`
|
|
|
+ - 含义:本周成功使用次数
|
|
|
+4. `draft`
|
|
|
+ - 含义:`status = 'DRAFT'` 的未删除模板数
|
|
|
+
|
|
|
+## 6. 数据库设计
|
|
|
+
|
|
|
+### 6.1 表结构
|
|
|
+
|
|
|
+在 `hair_template` 新增字段:
|
|
|
+
|
|
|
+```sql
|
|
|
+used_count int unsigned not null default 0 comment '模板累计使用次数'
|
|
|
+```
|
|
|
+
|
|
|
+要求:
|
|
|
+
|
|
|
+- 默认为 `0`
|
|
|
+- 非空
|
|
|
+- 保留字段注释
|
|
|
+
|
|
|
+### 6.2 Prisma 模型
|
|
|
+
|
|
|
+在 `HairTemplate` 模型新增:
|
|
|
+
|
|
|
+```prisma
|
|
|
+usedCount Int @default(0) @map("used_count") @db.UnsignedInt
|
|
|
+```
|
|
|
+
|
|
|
+### 6.3 数据迁移策略
|
|
|
+
|
|
|
+- 本轮仅新增字段,不做历史数据回填。
|
|
|
+- 新建模板默认 `used_count = 0`。
|
|
|
+- 上线后由真实的 AI 模拟成功事件持续累积。
|
|
|
+
|
|
|
+## 7. 后端设计
|
|
|
+
|
|
|
+### 7.1 模板列表接口
|
|
|
+
|
|
|
+继续使用现有接口:
|
|
|
+
|
|
|
+```http
|
|
|
+GET /api/v1/templates
|
|
|
+```
|
|
|
+
|
|
|
+返回结构保持不变,但 `usedCount` 改为真实映射:
|
|
|
+
|
|
|
+```ts
|
|
|
+{
|
|
|
+ items: Array<{
|
|
|
+ id: string
|
|
|
+ name: string
|
|
|
+ category: 'LONG' | 'MEDIUM' | 'SHORT' | 'DYE'
|
|
|
+ status: 'DRAFT' | 'PUBLIC'
|
|
|
+ description: string
|
|
|
+ tags: string[]
|
|
|
+ image: string
|
|
|
+ makeTaskId?: string
|
|
|
+ makeTaskStatus?: string
|
|
|
+ usedCount: number
|
|
|
+ }>
|
|
|
+ total: number
|
|
|
+ page: number
|
|
|
+ pageSize: number
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+实现要求:
|
|
|
+
|
|
|
+- `usedCount` 直接来自 `hair_template.used_count`
|
|
|
+- 不再返回固定值 `0`
|
|
|
+
|
|
|
+### 7.2 顶部统计接口
|
|
|
+
|
|
|
+新增模板模块内的真实统计接口:
|
|
|
+
|
|
|
+```http
|
|
|
+GET /api/v1/templates/summary
|
|
|
+```
|
|
|
+
|
|
|
+返回结构:
|
|
|
+
|
|
|
+```json
|
|
|
+{
|
|
|
+ "total": 24,
|
|
|
+ "published": 18,
|
|
|
+ "weeklyUsed": 6,
|
|
|
+ "draft": 4
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+说明:
|
|
|
+
|
|
|
+- 不再依赖不存在的 `/api/v1/stylist/dashboard/template-summary`
|
|
|
+- 接口归属放在模板模块内,方便和模板列表、模板编辑共用权限与领域边界
|
|
|
+
|
|
|
+### 7.3 统计实现
|
|
|
+
|
|
|
+`TemplatesService` 新增 `getSummary(stylistId)` 方法,返回:
|
|
|
+
|
|
|
+- `total`
|
|
|
+- `published`
|
|
|
+- `weeklyUsed`
|
|
|
+- `draft`
|
|
|
+
|
|
|
+建议实现方式:
|
|
|
+
|
|
|
+1. `total`
|
|
|
+ - `hair_template.count`
|
|
|
+ - 条件:`hairstylist_id = 当前美发师`,`is_deleted = 0`
|
|
|
+2. `published`
|
|
|
+ - `hair_template.count`
|
|
|
+ - 条件加上 `status = 'PUBLIC'`
|
|
|
+3. `draft`
|
|
|
+ - `hair_template.count`
|
|
|
+ - 条件加上 `status = 'DRAFT'`
|
|
|
+4. `weeklyUsed`
|
|
|
+ - 统计 `ai_hair_simulation_task` 中本周成功任务数
|
|
|
+ - 为避免把其他人的模板统计进来,需要按模板归属过滤
|
|
|
+
|
|
|
+`weeklyUsed` 的推荐查询思路:
|
|
|
+
|
|
|
+1. 先计算当前自然周的起止时间
|
|
|
+2. 在 `ai_hair_simulation_task` 中筛选:
|
|
|
+ - `taskStatus = 'SUCCESSES'`
|
|
|
+ - `taskFinishTime` 在本周范围内
|
|
|
+ - `isDeleted = 0`
|
|
|
+3. 通过 `templateId` 关联 `hair_template`
|
|
|
+4. 仅统计:
|
|
|
+ - `hair_template.hairstylist_id = 当前美发师`
|
|
|
+ - `hair_template.is_deleted = 0`
|
|
|
+
|
|
|
+如果 Prisma 直接跨表过滤不够顺手,可以采用两段式最小实现:
|
|
|
+
|
|
|
+1. 先取当前美发师所有未删除模板 ID
|
|
|
+2. 再用这些模板 ID 统计本周成功任务数
|
|
|
+
|
|
|
+### 7.4 累计使用量更新
|
|
|
+
|
|
|
+累计值的更新触发点放在“AI 发型模拟任务成功”的链路里。
|
|
|
+
|
|
|
+当前系统已经存在 `ai_hair_simulation_task` 的状态刷新和结果落库逻辑,因此本轮在任务状态首次转为 `SUCCESSES` 时,补充:
|
|
|
+
|
|
|
+```ts
|
|
|
+hair_template.used_count += 1
|
|
|
+```
|
|
|
+
|
|
|
+要求:
|
|
|
+
|
|
|
+- 只在任务真正从未成功状态转为 `SUCCESSES` 时递增一次
|
|
|
+- 避免重复轮询、重复刷新导致同一个任务重复累加
|
|
|
+
|
|
|
+推荐做法:
|
|
|
+
|
|
|
+1. 在更新任务状态前读取旧状态
|
|
|
+2. 只有当:
|
|
|
+ - 旧状态不是 `SUCCESSES`
|
|
|
+ - 新状态是 `SUCCESSES`
|
|
|
+ 时,才对对应模板执行一次递增
|
|
|
+
|
|
|
+说明:
|
|
|
+
|
|
|
+- 触发位置应靠近 `SimulationsService` 中任务刷新成功的逻辑,而不是放在列表查询时临时聚合。
|
|
|
+- 这样可以保证模板表里的累计值始终可直接复用。
|
|
|
+
|
|
|
+## 8. 前端设计
|
|
|
+
|
|
|
+### 8.1 模板列表页
|
|
|
+
|
|
|
+`TemplateListPage` 保持现有布局与交互,数据层做以下调整:
|
|
|
+
|
|
|
+- 页面初始化时请求真实的模板 summary
|
|
|
+- 顶部 4 个统计卡片展示真实值
|
|
|
+- 列表卡片继续读 `template.usedCount`
|
|
|
+
|
|
|
+### 8.2 summary 接口调整
|
|
|
+
|
|
|
+前端 `getTemplateDashboardSummary()` 当前请求:
|
|
|
+
|
|
|
+```ts
|
|
|
+/stylist/dashboard/template-summary
|
|
|
+```
|
|
|
+
|
|
|
+本轮改为请求:
|
|
|
+
|
|
|
+```ts
|
|
|
+/templates/summary
|
|
|
+```
|
|
|
+
|
|
|
+### 8.3 初始值与失败态
|
|
|
+
|
|
|
+当前前端存在默认假数据:
|
|
|
+
|
|
|
+```ts
|
|
|
+{
|
|
|
+ total: 24,
|
|
|
+ published: 18,
|
|
|
+ weeklyUsed: 6,
|
|
|
+ draft: 4,
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+本轮改为:
|
|
|
+
|
|
|
+- 初始值使用全 `0`
|
|
|
+- 请求失败时回退到全 `0`
|
|
|
+
|
|
|
+原因:
|
|
|
+
|
|
|
+- 避免页面出现和真实业务无关的假统计
|
|
|
+- 保持失败时的 UI 稳定,不额外扩大本轮范围
|
|
|
+
|
|
|
+## 9. 测试设计
|
|
|
+
|
|
|
+### 9.1 后端单测
|
|
|
+
|
|
|
+更新 `templates.service.spec.ts`,至少覆盖:
|
|
|
+
|
|
|
+1. 模板列表返回真实 `usedCount`
|
|
|
+2. `getSummary()` 返回:
|
|
|
+ - `total`
|
|
|
+ - `published`
|
|
|
+ - `weeklyUsed`
|
|
|
+ - `draft`
|
|
|
+3. AI 模拟任务首次成功时会触发对应模板 `used_count + 1`
|
|
|
+4. 同一个任务重复刷新为成功时不会重复累加
|
|
|
+
|
|
|
+### 9.2 后端 E2E
|
|
|
+
|
|
|
+更新 `templates.e2e-spec.ts`,至少覆盖:
|
|
|
+
|
|
|
+1. `GET /api/v1/templates` 返回真实 `usedCount`
|
|
|
+2. `GET /api/v1/templates/summary` 返回 4 个真实统计字段
|
|
|
+
|
|
|
+如现有 E2E 框架不便直接覆盖“模拟任务成功触发模板使用量递增”,可优先用单测锁住该业务规则。
|
|
|
+
|
|
|
+### 9.3 前端测试
|
|
|
+
|
|
|
+更新 `TemplateListPage.test.tsx`,至少覆盖:
|
|
|
+
|
|
|
+1. 顶部 4 个统计卡片展示真实 summary
|
|
|
+2. 列表卡片中的“使用 x 次”展示接口返回值
|
|
|
+3. 不再依赖假默认 summary
|
|
|
+4. summary 请求失败时展示 `0`
|
|
|
+
|
|
|
+## 10. 风险与约束
|
|
|
+
|
|
|
+1. `weeklyUsed` 依赖 `task_finish_time`,因此只有成功完成并记录完成时间的任务才会被统计。
|
|
|
+2. `template_id` 在 `ai_hair_simulation_task` 中是字符串,需要在统计时注意与 `hair_template.id` 的类型转换。
|
|
|
+3. 若未来存在“删除模板但保留历史任务”的场景,本轮设计会按当前规则忽略已删除模板对应的周统计,这与当前“只看未删除模板业务数据”的页面语义一致。
|
|
|
+
|
|
|
+## 11. 交付结果
|
|
|
+
|
|
|
+本轮完成后,系统应具备以下结果:
|
|
|
+
|
|
|
+1. 模板表具备 `used_count` 字段
|
|
|
+2. 模板列表返回真实使用量
|
|
|
+3. 顶部 4 个统计卡片展示真实数据
|
|
|
+4. “本周使用”按本周增量统计
|
|
|
+5. AI 发型模拟成功后自动累计模板使用量
|