微信小程序页面迁移校验之前 P5任务处理之前
This commit is contained in:
1
.kiro/specs/04-miniapp-core-business/.config.kiro
Normal file
1
.kiro/specs/04-miniapp-core-business/.config.kiro
Normal file
@@ -0,0 +1 @@
|
||||
{"specId": "27029642-a405-4932-8c22-5bc54fad5173", "workflowType": "requirements-first", "specType": "feature"}
|
||||
1115
.kiro/specs/04-miniapp-core-business/design.md
Normal file
1115
.kiro/specs/04-miniapp-core-business/design.md
Normal file
File diff suppressed because it is too large
Load Diff
278
.kiro/specs/04-miniapp-core-business/requirements.md
Normal file
278
.kiro/specs/04-miniapp-core-business/requirements.md
Normal file
@@ -0,0 +1,278 @@
|
||||
# 需求文档:小程序核心业务模块(miniapp-core-business)
|
||||
|
||||
## 简介
|
||||
|
||||
本 SPEC 实现小程序的核心业务逻辑,涵盖助教任务系统(生成、分配、状态流转、完成检测)、备注系统(CRUD、星星评分、类型区分)、以及后台触发器/轮询调度框架。系统基于 P1(miniapp-db-foundation)的数据库基础设施、P2(etl-dws-miniapp-extensions)的 DWS 指数数据、P3(miniapp-auth-system)的用户认证体系,在 `test_zqyy_app.biz` Schema 中创建任务、备注、触发器等业务表,并在 FastAPI 后端实现对应的 API 端点和后台调度逻辑。
|
||||
|
||||
## 术语表
|
||||
|
||||
- **Task_Generator**:任务生成器,每日 4:00 后运行,基于 WBI/NCI/RS 指数为每个助教分配 4 种类型任务的后台服务
|
||||
- **Task_Manager**:任务管理服务,负责任务 CRUD、置顶、放弃、状态流转的后端模块
|
||||
- **Task_Expiry_Checker**:任务有效期轮询器,每小时检查 `expires_at` 并将过期任务标记为无效
|
||||
- **Recall_Completion_Detector**:召回完成检测器,ETL 数据更新后检查助教是否为匹配客户提供了服务
|
||||
- **Note_Reclassifier**:备注回溯重分类器,召回完成时回溯检查是否有普通备注需重分类为回访备注
|
||||
- **Note_Service**:备注服务模块,负责备注 CRUD、星星评分存储与读取
|
||||
- **Trigger_Scheduler**:触发器调度框架,支持 cron/interval/event 三种触发方式的统一调度引擎
|
||||
- **coach_tasks**:助教任务表,位于 `biz` Schema,存储任务分配、状态、有效期等信息
|
||||
- **coach_task_history**:任务变更历史表,记录任务关闭/新建的追溯链
|
||||
- **notes**:统一备注表,位于 `biz` Schema,通过 `type` 字段区分普通备注/回访备注/放弃原因
|
||||
- **trigger_jobs**:触发器配置表,位于 `biz` Schema,存储轮询/事件触发器的配置与执行状态
|
||||
- **task_type**:任务类型枚举,取值为 `high_priority_recall`(高优先召回)/ `priority_recall`(优先召回)/ `follow_up_visit`(客户回访)/ `relationship_building`(关系构建)
|
||||
- **task_status**:任务状态枚举,取值为 `active`(有效)/ `inactive`(无效)/ `completed`(已完成)/ `abandoned`(已放弃)
|
||||
- **note_type**:备注类型枚举,取值为 `normal`(普通备注)/ `follow_up`(回访备注)/ `abandon_reason`(放弃原因)
|
||||
- **priority_score**:优先级分数,取 `max(WBI, NCI)` 的快照值,用于任务排序
|
||||
- **expires_at**:有效期时间戳,默认 NULL(无限期),填充后表示任务将在该时间点过期
|
||||
- **FDW**:`postgres_fdw` 外部数据包装器,通过 `fdw_etl` Schema 读取 ETL 库指数数据
|
||||
- **Migration_Script**:存放在 `db/zqyy_app/migrations/` 中的纯 SQL 迁移脚本,以日期前缀命名
|
||||
- **site_id**:门店标识符,类型为 `BIGINT`,用于多门店数据隔离
|
||||
- **member_retention_clue**:维客线索表,位于 `public` Schema,存储助教为客户记录的维护线索(大类 + 摘要 + 详情),独立于 ETL 数据。当前已有基础表结构和 CRUD API(`/api/retention-clue`),若不足以支撑本 SPEC 的任务系统需求,可对其 DDL、Pydantic 模型及路由进行扩展或修改
|
||||
|
||||
## 需求
|
||||
|
||||
### 需求 1:业务数据表创建
|
||||
|
||||
**用户故事:** 作为后端开发者,我需要在 `biz` Schema 中创建任务、备注、触发器等业务表,以便支撑核心业务功能。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN Migration_Script 执行完成, THE Task_Manager SHALL 在 `biz` Schema 中创建 `coach_tasks` 表,包含 `id`(BIGSERIAL PK)、`site_id`(BIGINT NOT NULL)、`assistant_id`(BIGINT NOT NULL)、`member_id`(BIGINT NOT NULL)、`task_type`(VARCHAR NOT NULL)、`status`(VARCHAR NOT NULL DEFAULT 'active')、`priority_score`(NUMERIC(5,2))、`expires_at`(TIMESTAMPTZ,可空)、`is_pinned`(BOOLEAN DEFAULT FALSE)、`abandon_reason`(TEXT,可空)、`completed_at`(TIMESTAMPTZ,可空)、`completed_task_type`(VARCHAR,可空)、`parent_task_id`(BIGINT,可空,FK → coach_tasks)、`created_at`(TIMESTAMPTZ DEFAULT NOW())、`updated_at`(TIMESTAMPTZ DEFAULT NOW())字段
|
||||
2. WHEN Migration_Script 执行完成, THE Task_Manager SHALL 在 `biz` Schema 中创建 `coach_task_history` 表,包含 `id`(BIGSERIAL PK)、`task_id`(BIGINT FK → coach_tasks)、`action`(VARCHAR NOT NULL)、`old_status`(VARCHAR)、`new_status`(VARCHAR)、`old_task_type`(VARCHAR)、`new_task_type`(VARCHAR)、`detail`(JSONB)、`created_at`(TIMESTAMPTZ DEFAULT NOW())字段
|
||||
3. WHEN Migration_Script 执行完成, THE Note_Service SHALL 在 `biz` Schema 中创建 `notes` 表,包含 `id`(BIGSERIAL PK)、`site_id`(BIGINT NOT NULL)、`user_id`(INTEGER NOT NULL)、`target_type`(VARCHAR NOT NULL)、`target_id`(BIGINT NOT NULL)、`type`(VARCHAR NOT NULL DEFAULT 'normal')、`content`(TEXT NOT NULL)、`rating_service_willingness`(SMALLINT,可空,CHECK 1-5)、`rating_revisit_likelihood`(SMALLINT,可空,CHECK 1-5)、`task_id`(BIGINT,可空,FK → coach_tasks)、`ai_score`(SMALLINT,可空)、`ai_analysis`(TEXT,可空)、`created_at`(TIMESTAMPTZ DEFAULT NOW())、`updated_at`(TIMESTAMPTZ DEFAULT NOW())字段
|
||||
4. WHEN Migration_Script 执行完成, THE Trigger_Scheduler SHALL 在 `biz` Schema 中创建 `trigger_jobs` 表,包含 `id`(SERIAL PK)、`job_type`(VARCHAR NOT NULL)、`job_name`(VARCHAR NOT NULL UNIQUE)、`trigger_condition`(VARCHAR NOT NULL)、`trigger_config`(JSONB NOT NULL)、`last_run_at`(TIMESTAMPTZ,可空)、`next_run_at`(TIMESTAMPTZ,可空)、`status`(VARCHAR NOT NULL DEFAULT 'enabled')、`created_at`(TIMESTAMPTZ DEFAULT NOW())字段
|
||||
5. THE Migration_Script SHALL 对 `coach_tasks` 表创建唯一索引 `idx_coach_tasks_site_assistant_member_type` 在 `(site_id, assistant_id, member_id, task_type)` 上,仅对 `status = 'active'` 的记录生效(部分唯一索引)
|
||||
6. THE Migration_Script SHALL 对 `coach_tasks` 表创建索引 `idx_coach_tasks_assistant_status` 在 `(site_id, assistant_id, status)` 上,用于助教任务列表查询
|
||||
7. THE Migration_Script SHALL 对 `notes` 表创建索引 `idx_notes_target` 在 `(site_id, target_type, target_id)` 上,用于按目标查询备注
|
||||
8. THE Migration_Script SHALL 使用 `IF NOT EXISTS` 幂等语法,确保重复执行不会报错
|
||||
9. THE Migration_Script SHALL 在脚本中包含回滚语句(以注释形式)
|
||||
|
||||
### 需求 2:触发器种子数据预置
|
||||
|
||||
**用户故事:** 作为系统管理员,我需要系统预置核心触发器配置,以便后台调度任务自动运行。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 种子数据脚本执行完成, THE Trigger_Scheduler SHALL 在 `biz.trigger_jobs` 表中插入 `task_generator` 记录(trigger_condition='cron',trigger_config 包含 cron 表达式 '0 4 * * *')
|
||||
2. WHEN 种子数据脚本执行完成, THE Trigger_Scheduler SHALL 在 `biz.trigger_jobs` 表中插入 `task_expiry_check` 记录(trigger_condition='interval',trigger_config 包含间隔秒数 3600)
|
||||
3. WHEN 种子数据脚本执行完成, THE Trigger_Scheduler SHALL 在 `biz.trigger_jobs` 表中插入 `recall_completion_check` 记录(trigger_condition='event',trigger_config 包含事件名 'etl_data_updated')
|
||||
4. WHEN 种子数据脚本执行完成, THE Trigger_Scheduler SHALL 在 `biz.trigger_jobs` 表中插入 `note_reclassify_backfill` 记录(trigger_condition='event',trigger_config 包含事件名 'recall_completed')
|
||||
5. THE 种子数据脚本 SHALL 使用 `ON CONFLICT (job_name) DO NOTHING` 语法,确保重复执行不会产生重复数据
|
||||
|
||||
### 需求 3:任务生成器
|
||||
|
||||
**用户故事:** 作为助教,我每天打开小程序能看到系统为我分配的任务列表,按优先级排序。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN Task_Generator 运行时, THE Task_Generator SHALL 通过 FDW 读取 `fdw_etl` 中的 `dws_member_winback_index`(WBI)和 `dws_member_newconv_index`(NCI)指数数据,计算 `priority_score = max(WBI, NCI)`
|
||||
2. WHEN `priority_score > 7`, THE Task_Generator SHALL 为该客户-助教对生成 `high_priority_recall`(高优先召回)类型任务
|
||||
3. WHEN `priority_score > 5` 且 `priority_score <= 7`, THE Task_Generator SHALL 为该客户-助教对生成 `priority_recall`(优先召回)类型任务
|
||||
4. WHEN 助教完成某客户的召回任务后该客户无回访备注, THE Task_Generator SHALL 为该客户-助教对生成 `follow_up_visit`(客户回访)类型任务
|
||||
5. WHEN 客户-助教对的 RS 指数 < 6(通过 FDW 读取 `dws_member_assistant_relation_index`), THE Task_Generator SHALL 为该客户-助教对生成 `relationship_building`(关系构建)类型任务
|
||||
6. WHEN Task_Generator 生成任务时发现已存在相同 `(site_id, assistant_id, member_id, task_type)` 且 `status = 'active'` 的任务, THE Task_Generator SHALL 跳过该任务不做任何操作
|
||||
7. WHEN Task_Generator 生成任务时发现已存在相同 `(site_id, assistant_id, member_id)` 但 `task_type` 不同且 `status = 'active'` 的任务, THE Task_Generator SHALL 将旧任务状态设为 `inactive`,创建新任务,并在 `coach_task_history` 中记录变更
|
||||
8. THE Task_Generator SHALL 按优先级从高到低的顺序处理任务类型:`high_priority_recall`(0)> `priority_recall`(0)> `follow_up_visit`(1)> `relationship_building`(2),高优先级任务覆盖低优先级任务
|
||||
9. THE Task_Generator SHALL 通过 `auth.user_assistant_binding` 确定助教与小程序用户的映射关系,仅为已绑定的助教生成任务
|
||||
10. THE Task_Generator SHALL 在 `trigger_jobs` 中更新 `last_run_at` 和 `next_run_at` 时间戳
|
||||
|
||||
### 需求 4:48 小时回访滞留机制
|
||||
|
||||
**用户故事:** 作为系统,回访任务至少保留 48 小时,到期后自动失效。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN Task_Generator 生成 `follow_up_visit` 类型任务时, THE Task_Generator SHALL 将 `expires_at` 设为 NULL(无限期有效),`status` 设为 `active`
|
||||
2. WHEN Task_Generator 检测到某 `follow_up_visit` 任务的触发条件不再满足(指数变化), THE Task_Generator SHALL 将该任务的 `expires_at` 填充为 `created_at + 48 小时`,`status` 保持 `active`
|
||||
3. WHEN Task_Expiry_Checker 轮询检查时发现某任务的 `expires_at` 不为 NULL 且当前时间超过 `expires_at`, THE Task_Expiry_Checker SHALL 将该任务 `status` 设为 `inactive`
|
||||
4. WHEN 新的 `follow_up_visit` 任务生成时发现同一 `(site_id, assistant_id, member_id)` 已存在一个有 `expires_at` 的 `follow_up_visit` 任务, THE Task_Generator SHALL 将旧任务标记为 `inactive`,创建新的 `active` 任务(`expires_at` 为 NULL)
|
||||
5. THE Task_Expiry_Checker SHALL 每小时运行一次,由 `trigger_jobs` 中的 `task_expiry_check` 配置驱动
|
||||
|
||||
### 需求 5:任务类型变更与状态流转
|
||||
|
||||
**用户故事:** 作为系统,当客户指数变化导致任务类型变更时,系统正确关闭旧任务并创建新任务。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 任务类型从 `priority_recall` 变更为 `high_priority_recall`, THE Task_Generator SHALL 将旧 `priority_recall` 任务标记为 `inactive`(`expires_at` 保持 NULL),创建新的 `high_priority_recall` 任务
|
||||
2. WHEN 任务类型从 `follow_up_visit` 变更为 `high_priority_recall` 或 `priority_recall`, THE Task_Generator SHALL 将旧 `follow_up_visit` 任务标记为 `active` 并填充 `expires_at = created_at + 48 小时`,创建新的召回任务
|
||||
3. WHEN 任务类型从召回类型变回 `follow_up_visit`, THE Task_Generator SHALL 检查是否存在有 `expires_at` 的旧 `follow_up_visit` 任务,若存在则将旧任务标记为 `inactive`,创建新的 `follow_up_visit` 任务
|
||||
4. THE Task_Manager SHALL 在每次状态变更时在 `coach_task_history` 中记录 `action`、`old_status`、`new_status`、`old_task_type`、`new_task_type`
|
||||
|
||||
### 需求 6:召回完成检测
|
||||
|
||||
**用户故事:** 作为助教,我完成召回任务后(客户到店被服务),系统自动标记任务完成。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN ETL 数据更新后, THE Recall_Completion_Detector SHALL 通过 FDW 读取 `fdw_etl.dwd_assistant_service_log` 中的新增服务记录
|
||||
2. WHEN 发现某助教为某客户提供了服务, THE Recall_Completion_Detector SHALL 查找该 `(site_id, assistant_id, member_id)` 下所有 `status = 'active'` 的任务
|
||||
3. WHEN 匹配到活跃任务, THE Recall_Completion_Detector SHALL 将任务 `status` 设为 `completed`,记录 `completed_at` 为服务时间,记录 `completed_task_type` 为完成时的任务类型
|
||||
4. WHEN 召回完成后, THE Recall_Completion_Detector SHALL 触发 `note_reclassify_backfill` 事件,通知 Note_Reclassifier 执行备注回溯
|
||||
5. THE Recall_Completion_Detector SHALL 由 `trigger_jobs` 中的 `recall_completion_check` 配置驱动,在 ETL 数据更新事件后触发
|
||||
|
||||
### 需求 7:备注回溯重分类
|
||||
|
||||
**用户故事:** 作为系统,当 ETL 数据延迟导致召回完成晚于备注提交时,需要回溯重分类备注。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 召回完成事件触发后, THE Note_Reclassifier SHALL 查找该 `(site_id, assistant_id, member_id)` 在召回服务结束时间之后提交的第一条 `type = 'normal'` 的备注
|
||||
2. WHEN 找到符合条件的普通备注, THE Note_Reclassifier SHALL 将该备注的 `type` 从 `normal` 更新为 `follow_up`
|
||||
3. WHEN 备注重分类完成后, THE Note_Reclassifier SHALL 触发 AI 应用 6 对该备注进行含金量评分(评分逻辑由 P5 AI 集成层实现,本 SPEC 仅定义触发接口)
|
||||
4. WHEN AI 应用 6 返回评分 >= 6, THE Note_Reclassifier SHALL 生成一条 `follow_up_visit` 任务并标记为 `completed`(回溯完成)
|
||||
5. WHEN AI 应用 6 返回评分 < 6, THE Note_Reclassifier SHALL 生成一条 `follow_up_visit` 任务,`status` 为 `active`(回访未完成,需助教重新备注)
|
||||
|
||||
### 需求 8:任务 CRUD API
|
||||
|
||||
**用户故事:** 作为助教,我可以查看任务列表、置顶/放弃任务、取消置顶/取消放弃。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 助教请求任务列表, THE Task_Manager SHALL 返回该助教在当前 `site_id` 下所有 `status = 'active'` 的任务,按 `is_pinned DESC, priority_score DESC, created_at ASC` 排序
|
||||
2. WHEN 助教请求任务列表, THE Task_Manager SHALL 在每条任务中包含客户基本信息(通过 FDW 读取 `dim_member`)、RS 指数(通过 FDW 读取 `dws_member_assistant_relation_index`)、爱心 icon 档位(💖>8.5 / 🧡>7 / 💛>5 / 💙<5)
|
||||
3. WHEN 助教置顶某任务, THE Task_Manager SHALL 将该任务的 `is_pinned` 设为 TRUE,并在 `coach_task_history` 中记录
|
||||
4. WHEN 助教放弃某任务, THE Task_Manager SHALL 将该任务 `status` 设为 `abandoned`,记录 `abandon_reason`(必填),并在 `coach_task_history` 中记录
|
||||
5. WHEN 助教取消置顶某任务, THE Task_Manager SHALL 将该任务的 `is_pinned` 设为 FALSE
|
||||
6. WHEN 助教取消放弃某任务, THE Task_Manager SHALL 将该任务 `status` 恢复为 `active`,清空 `abandon_reason`
|
||||
7. IF 助教放弃任务时未提供 `abandon_reason`, THEN THE Task_Manager SHALL 返回 HTTP 422 错误
|
||||
8. THE Task_Manager SHALL 通过 Permission_Middleware 验证用户身份,仅允许操作自己的任务
|
||||
|
||||
### 需求 9:备注 CRUD API
|
||||
|
||||
**用户故事:** 作为助教,我给客户添加备注后,系统正确存储备注内容和星星评分。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 助教创建备注时, THE Note_Service SHALL 在 `biz.notes` 表中创建记录,包含 `site_id`、`user_id`、`target_type`('member')、`target_id`(member_id)、`type`、`content`、可选的 `rating_service_willingness`(1-5)、可选的 `rating_revisit_likelihood`(1-5)、可选的 `task_id`
|
||||
2. WHEN 备注关联的任务类型为 `follow_up_visit`, THE Note_Service SHALL 将备注 `type` 自动设为 `follow_up`
|
||||
3. WHEN 备注关联的任务类型不是 `follow_up_visit`, THE Note_Service SHALL 将备注 `type` 设为 `normal`
|
||||
4. WHEN 备注创建成功且 `type = 'follow_up'`, THE Note_Service SHALL 触发 AI 应用 6 备注分析接口(由 P5 实现),传入备注内容和客户信息
|
||||
5. WHEN AI 应用 6 返回评分 >= 6 且备注关联的 `follow_up_visit` 任务 `status = 'active'`, THE Note_Service SHALL 将该任务标记为 `completed`
|
||||
6. WHEN 助教查询某客户的备注列表, THE Note_Service SHALL 返回该客户在当前 `site_id` 下的所有备注,按 `created_at DESC` 排序,包含星星评分和 AI 评分
|
||||
7. WHEN 助教删除备注, THE Note_Service SHALL 执行软删除或硬删除(根据业务需要),删除前需二次确认(前端实现)
|
||||
8. IF 星星评分值不在 1-5 范围内, THEN THE Note_Service SHALL 返回 HTTP 422 错误
|
||||
9. THE Note_Service 的星星评分 SHALL 不参与回访完成判定(完成判定仅看 AI 应用 6 评分 >= 6),不参与 AI 应用 6 分析,仅作辅助数据存储
|
||||
|
||||
### 需求 10:触发器调度框架
|
||||
|
||||
**用户故事:** 作为系统,我需要一个统一的触发器调度框架,支持定时、间隔、事件驱动三种触发方式。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE Trigger_Scheduler SHALL 支持 `cron` 类型触发器,按 cron 表达式计算下次运行时间
|
||||
2. THE Trigger_Scheduler SHALL 支持 `interval` 类型触发器,按固定间隔秒数计算下次运行时间
|
||||
3. THE Trigger_Scheduler SHALL 支持 `event` 类型触发器,在指定事件发生时立即执行
|
||||
4. WHEN 触发器执行完成, THE Trigger_Scheduler SHALL 更新 `trigger_jobs` 表中的 `last_run_at` 和 `next_run_at`
|
||||
5. WHEN 触发器 `status = 'disabled'`, THE Trigger_Scheduler SHALL 跳过该触发器不执行
|
||||
6. THE Trigger_Scheduler SHALL 提供 `fire_event(event_name, payload)` 方法,用于触发事件驱动型任务
|
||||
7. IF 触发器执行过程中发生错误, THEN THE Trigger_Scheduler SHALL 记录错误日志但不中断其他触发器的执行
|
||||
|
||||
### 需求 11:迁移脚本管理
|
||||
|
||||
**用户故事:** 作为后端开发者,我需要所有数据库变更都有对应的迁移脚本,以便变更可追溯、可重放。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE Migration_Script SHALL 将所有业务表的 DDL 存放在 `db/zqyy_app/migrations/` 目录中
|
||||
2. THE Migration_Script SHALL 使用日期前缀命名(格式:`YYYY-MM-DD__<描述>.sql`)
|
||||
3. THE Migration_Script SHALL 使用 UTF-8 编码,纯 SQL(非 ORM)
|
||||
4. THE Migration_Script SHALL 在每个脚本中包含回滚语句(以注释形式)
|
||||
5. THE Migration_Script SHALL 使用幂等语法(`IF NOT EXISTS`、`ON CONFLICT DO NOTHING`),确保重复执行不会报错
|
||||
|
||||
### 需求 12:DDL 测试库落库与文档同步
|
||||
|
||||
**用户故事:** 作为后端开发者,我需要所有 DDL 变更在测试库中实际执行验证,并同步更新数据库手册和 DDL 基线。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. WHEN 迁移脚本编写完成, THE Task_Manager SHALL 在 `test_zqyy_app` 测试库中执行迁移脚本,验证无错误
|
||||
2. WHEN 迁移脚本执行成功, THE Task_Manager SHALL 创建或更新 `docs/database/BD_Manual_biz_tables.md` 数据库手册,包含变更说明、兼容性影响、回滚策略、验证 SQL(至少 3 条)
|
||||
3. WHEN 迁移脚本执行成功, THE Task_Manager SHALL 运行 `python scripts/ops/gen_consolidated_ddl.py` 重新生成 DDL 基线文件
|
||||
4. WHEN 种子数据脚本执行成功, THE Task_Manager SHALL 在数据库手册中记录种子数据内容(触发器配置)
|
||||
|
||||
### 需求 13:小程序前端页面原型还原(强制)
|
||||
|
||||
**用户故事:** 作为产品经理,我需要小程序前端页面严格忠于 `docs/h5_ui/pages/` 中的 H5 原型图结构和视觉细节,确保最终实现与设计稿高度一致。
|
||||
|
||||
#### 原型图索引
|
||||
|
||||
| 原型文件 | 对应小程序页面 | 说明 |
|
||||
|---------|--------------|------|
|
||||
| `docs/h5_ui/pages/task-list.html` | `pages/task-list/task-list` | 任务列表页(首页),含业绩进度卡片、置顶/一般/已放弃三区域 |
|
||||
| `docs/h5_ui/pages/task-detail.html` | `pages/task-detail/task-detail` | 任务详情页 - 高优先召回(theme-red Banner) |
|
||||
| `docs/h5_ui/pages/task-detail-priority.html` | `pages/task-detail/task-detail` | 任务详情页 - 优先召回(theme-orange Banner) |
|
||||
| `docs/h5_ui/pages/task-detail-relationship.html` | `pages/task-detail/task-detail` | 任务详情页 - 关系构建(theme-pink Banner) |
|
||||
| `docs/h5_ui/pages/task-detail-callback.html` | `pages/task-detail/task-detail` | 任务详情页 - 客户回访(theme-teal Banner) |
|
||||
| `docs/h5_ui/pages/notes.html` | `pages/notes/notes` | 备注记录页 |
|
||||
| `docs/h5_ui/pages/customer-detail.html` | `pages/customer-detail/customer-detail` | 客户详情页 |
|
||||
|
||||
#### 验收标准
|
||||
|
||||
##### 13.A 结构还原(强制)
|
||||
|
||||
1. WHEN 实现任务列表页时, THE 小程序页面 SHALL 严格还原原型图中的以下结构层次:顶部用户信息区(头像 + 姓名 + 角色标签 + 门店名)→ 业绩进度卡片(5 段档位进度条 + 课时数据含红戳 + 奖金激励 + 预计收入)→ 任务列表区(📌 置顶区 / 一般任务区 / 已放弃区三个分区,每个分区有标签 + 计数)
|
||||
2. WHEN 实现任务卡片时, THE 每张任务卡片 SHALL 包含原型图中的全部元素:左侧 4px 彩色边框(高优先=红、优先=橙、关系构建=粉、客户回访=青)、任务类型标签(渐变色圆角矩形)、客户姓名、爱心 icon(💖/🧡/💛/💙)、备注指示器(📝)、描述行(最近到店 + 余额)、AI 建议行(含 AI 机器人 icon)、右侧箭头
|
||||
3. WHEN 实现任务详情页时, THE 页面 SHALL 严格还原原型图中的以下模块顺序:通栏 Banner(导航栏 + 客户信息 + 放弃按钮)→ 维客线索卡片(客户基础/消费习惯/玩法偏好/重要反馈,每条含大类标签 + 摘要 + 详情 + 来源标注)→ 与我的关系卡片(爱心档位标签 + 进度条 + RS 分数 + 描述 + 近期服务记录列表)→ 任务建议卡片(建议执行 + 话术参考含复制按钮)→ 我给 TA 的备注卡片(备注列表含星星评分 + 删除按钮)→ 底部操作栏(问问助手 + 备注两个按钮)
|
||||
4. WHEN 实现备注弹窗时, THE 弹窗 SHALL 包含原型图中的全部元素:标题行(添加备注 + 展开评价按钮)、可折叠的星星评分区(再次服务意愿 1-5 星 + 再来店可能性 1-5 星,各含文字提示)、文本输入区、保存按钮
|
||||
5. WHEN 实现长按上下文菜单时, THE 菜单 SHALL 还原原型图中的交互:遮罩层 + 圆角菜单面板(置顶/取消置顶、备注、放弃/取消放弃等选项)
|
||||
6. WHEN 实现备注记录页时, THE 页面 SHALL 还原原型图中的列表结构:每条备注含内容文本 + 底部标签(助教/客户类型标签 + 时间戳)
|
||||
|
||||
##### 13.B 视觉还原(强制)
|
||||
|
||||
1. THE 小程序页面 SHALL 使用与原型图一致的 TDesign 色彩体系:primary=#0052d9、success=#00a870、warning=#ed7b2f、error=#e34d59,灰阶色板 gray-1(#f3f3f3) 至 gray-13(#242424)
|
||||
2. THE 任务详情页 Banner SHALL 根据任务类型使用不同主题色:高优先召回=theme-red、优先召回=theme-orange、关系构建=theme-pink、客户回访=theme-teal,与原型图中的渐变背景一致
|
||||
3. THE 维客线索大类标签 SHALL 使用原型图中的配色方案:客户基础=primary/10 底色 + primary 文字、消费习惯=success/10 底色 + success 文字、玩法偏好=purple-500/10 底色 + purple-600 文字、重要反馈=error/10 底色 + error 文字
|
||||
4. THE 星星评分组件 SHALL 还原原型图中的视觉效果:填充星/空心星 SVG、支持半星显示(用于展示 AI 评分映射)
|
||||
5. THE 业绩进度卡片 SHALL 还原原型图中的 5 段档位进度条(按比例宽度:0-100 占 45.45%、100-130/130-160/160-190/190-220 各占 13.64%)、红戳动画(盖戳效果)、奖金金额突出样式
|
||||
|
||||
##### 13.C WXML/WXSS 技术规范(强制)
|
||||
|
||||
1. THE 小程序页面 SHALL 使用 WXML 语法而非 HTML 语法:`<view>` 替代 `<div>`、`<text>` 替代 `<span>`/`<p>`、`<image>` 替代 `<img>`、`<navigator>` 替代 `<a>`,禁止使用 HTML 标签
|
||||
2. THE 小程序样式 SHALL 使用 WXSS 语法:使用 `rpx` 单位替代 `px`(750rpx = 屏幕宽度)、使用 `@import` 导入公共样式、禁止使用 `rem`/`em`/`vw`/`vh` 等 CSS 单位
|
||||
3. THE 小程序页面 SHALL 使用 `wx:for` 替代 JavaScript 循环渲染、`wx:if`/`wx:elif`/`wx:else` 替代条件渲染、`bind:tap` 替代 `onclick`、`data-*` + `e.currentTarget.dataset` 替代 DOM 操作
|
||||
4. THE 小程序页面 SHALL 禁止使用以下 Web 特性:`document.*`、`window.*`、`localStorage`(用 `wx.setStorageSync`)、`fetch`/`XMLHttpRequest`(用 `wx.request`)、CSS `position: fixed` 的 `bottom: 0` 底部栏(用小程序安全区域适配)
|
||||
5. THE 小程序样式 SHALL 仅使用小程序支持的 CSS 选择器:`.class`、`#id`、`element`、`element, element`、`::after`、`::before`,禁止使用 `>`(子选择器)、`+`(相邻兄弟)、`~`(通用兄弟)、`[attr]`(属性选择器)等不支持的选择器
|
||||
6. THE 小程序页面 SHALL 使用 `<block>` 标签作为无渲染包裹容器(替代 HTML 的 `<template>` 或 React 的 `<Fragment>`),`<block>` 不会生成真实 DOM 节点
|
||||
|
||||
##### 13.D TDesign 组件使用规范(强制)
|
||||
|
||||
1. THE 小程序页面 SHALL 优先使用 TDesign 组件库中的组件,组件引入路径格式为 `tdesign-miniprogram/{组件名}/{组件名}`,在页面 `.json` 的 `usingComponents` 中注册
|
||||
2. THE 以下 UI 元素 SHALL 使用对应的 TDesign 组件:导航栏→`t-navbar`、底部标签栏→`t-tab-bar`、对话框→`t-dialog`、轻提示→`t-toast`、弹出层→`t-popup`、空状态→`t-empty`、加载→`t-loading`、骨架屏→`t-skeleton`、标签→`t-tag`、搜索框→`t-search`
|
||||
3. THE TDesign 组件样式覆盖 SHALL 使用以下 4 种方式之一:`style`/`custom-style` 属性、解除样式隔离(`addGlobalClass`)、外部样式类(`t-class`)、CSS 变量(`--td-*`),禁止直接修改 `node_modules` 中的组件源码
|
||||
4. THE 小程序 `app.json` SHALL 移除 `"style": "v2"` 配置项,避免 TDesign 组件样式错乱
|
||||
5. WHEN 原型图中的 UI 元素无法用 TDesign 组件直接实现时(如自定义进度条、红戳动画、话术气泡等), THE 开发者 SHALL 使用原生 WXML + WXSS 自定义实现,但视觉效果必须与原型图一致
|
||||
|
||||
##### 13.E 原型图参考流程(强制)
|
||||
|
||||
1. WHEN 开始实现任何小程序页面前, THE 开发者 SHALL 首先阅读对应的 `docs/h5_ui/pages/*.html` 原型文件,提取页面结构、组件层次、样式细节、交互行为
|
||||
2. WHEN 原型图中使用 Tailwind CSS 类名时, THE 开发者 SHALL 将其转换为等效的 WXSS 样式(如 `px-4` → `padding: 0 32rpx`、`rounded-xl` → `border-radius: 24rpx`、`text-sm` → `font-size: 28rpx`)
|
||||
3. WHEN 原型图中使用 `<iframe>` 嵌套页面时, THE 开发者 SHALL 理解这是原型展示方式,实际小程序中使用 `wx.navigateTo` 页面跳转
|
||||
4. WHEN 原型图中使用 `onclick`/`history.back()` 等 Web API 时, THE 开发者 SHALL 转换为小程序等效 API:`bind:tap` + `wx.navigateBack()`
|
||||
5. THE 开发者 SHALL 在实现前加载 `wechat-miniprogram` Power 的相关 steering 文件(`view-layer.md`、`tdesign.md`、`builtin-components.md`),确保使用正确的小程序语法和 TDesign 组件规范
|
||||
6. THE 开发者 SHALL 在实现前阅读项目内的 H5 转小程序避坑指南 `apps/miniprogram/doc/h5-to-miniprogram-pitfalls.md`,该文档基于本项目已转换页面的实际踩坑经验整理,涵盖标签映射、rpx 换算、事件系统、TDesign 覆盖方式、高频踩坑清单及新页面开发 Checklist,所有条目具有强制参考效力
|
||||
|
||||
### 需求 14:任务系统属性测试
|
||||
|
||||
**用户故事:** 作为后端开发者,我需要通过属性测试验证任务系统核心逻辑的正确性。
|
||||
|
||||
#### 验收标准
|
||||
|
||||
1. THE 属性测试 SHALL 验证:对于任意 `(site_id, assistant_id, member_id, task_type)` 组合,`status = 'active'` 的任务最多只有一条(唯一性不变量)
|
||||
2. THE 属性测试 SHALL 验证:对于任意任务类型变更操作,旧任务被标记为 `inactive` 且新任务被创建为 `active`(状态机正确性)
|
||||
3. THE 属性测试 SHALL 验证:对于任意 `follow_up_visit` 任务,当 `expires_at` 不为 NULL 且当前时间超过 `expires_at` 时,轮询后 `status` 变为 `inactive`(有效期机制)
|
||||
4. THE 属性测试 SHALL 验证:对于任意任务放弃操作,`abandon_reason` 不为空字符串(放弃原因必填)
|
||||
5. THE 属性测试 SHALL 验证:对于任意备注创建操作,`rating_service_willingness` 和 `rating_revisit_likelihood` 的值在 NULL 或 1-5 范围内(评分范围约束)
|
||||
6. THE 属性测试 SHALL 验证:对于任意召回完成事件,`completed_task_type` 记录了完成时的任务类型快照(完成类型快照不变量)
|
||||
7. THE 属性测试 SHALL 验证:对于任意备注回溯操作,重分类后的备注 `type` 从 `normal` 变为 `follow_up`(回溯分类正确性)
|
||||
|
||||
---
|
||||
|
||||
## 附录:原型还原强制规则摘要
|
||||
|
||||
> 以下规则适用于本 SPEC 及所有后续小程序页面开发 SPEC,具有全局约束力。
|
||||
|
||||
1. **原型图是唯一视觉真相**:`docs/h5_ui/pages/*.html` 中的结构、层次、元素、配色、间距、交互行为是小程序页面实现的唯一参考标准。任何偏离原型图的实现都需要明确的产品确认。
|
||||
2. **WXML ≠ HTML**:严禁在小程序中使用 HTML 标签(div/span/p/a/img 等),必须使用小程序原生标签(view/text/image/navigator 等)。
|
||||
3. **WXSS ≠ CSS**:使用 rpx 单位、仅支持有限选择器、无 DOM/BOM API、样式隔离机制不同。Tailwind CSS 类名必须手动转换为 WXSS。
|
||||
4. **TDesign 优先**:凡 TDesign 组件库能覆盖的 UI 元素,必须使用 TDesign 组件;自定义实现仅限 TDesign 无法覆盖的场景。
|
||||
5. **Power 文档优先**:实现前必须加载 `wechat-miniprogram` Power 的相关 steering 文件,确保语法和组件用法正确。
|
||||
6. **项目踩坑指南必读**:实现前必须阅读 `apps/miniprogram/doc/h5-to-miniprogram-pitfalls.md`,该文档是基于本项目实际转换经验的避坑手册,涵盖 WXML/WXSS 差异、事件系统、TDesign 用法、rpx 换算规则及新页面开发 Checklist。
|
||||
239
.kiro/specs/04-miniapp-core-business/tasks.md
Normal file
239
.kiro/specs/04-miniapp-core-business/tasks.md
Normal file
@@ -0,0 +1,239 @@
|
||||
# 实现计划:小程序核心业务模块(miniapp-core-business)
|
||||
|
||||
## 概述
|
||||
|
||||
基于已批准的需求和设计文档,将小程序核心业务模块拆分为增量式编码任务。按照"DDL 建表 → 触发器调度框架 → 任务生成器 → 任务管理 → 有效期轮询 → 召回检测 → 备注系统 → 路由集成"的顺序实现。后端使用 Python + FastAPI,数据库使用 PostgreSQL 纯 SQL,属性测试使用 hypothesis。所有数据库操作在测试库 `test_zqyy_app` 中进行。
|
||||
|
||||
## 任务
|
||||
|
||||
- [x] 1. 创建业务数据表和种子数据
|
||||
- [x] 1.1 创建迁移脚本 `db/zqyy_app/migrations/YYYY-MM-DD__p4_create_biz_tables.sql`
|
||||
- 在 `biz` Schema 下创建 `coach_tasks`、`coach_task_history`、`notes`、`trigger_jobs` 共 4 张表
|
||||
- 包含所有字段定义、约束、CHECK 约束(评分 1-5)、外键(coach_task_history → coach_tasks、notes → coach_tasks、coach_tasks → coach_tasks 自引用)
|
||||
- 创建部分唯一索引 `idx_coach_tasks_site_assistant_member_type`(仅 status='active')
|
||||
- 创建查询索引 `idx_coach_tasks_assistant_status`、`idx_notes_target`
|
||||
- 使用 `IF NOT EXISTS` 幂等语法
|
||||
- 包含回滚语句(注释形式)
|
||||
- _Requirements: 1.1-1.9_
|
||||
|
||||
- [x] 1.2 创建种子数据脚本 `db/zqyy_app/migrations/YYYY-MM-DD__p4_seed_trigger_jobs.sql`
|
||||
- 插入 4 条触发器配置:`task_generator`(cron, 0 4 * * *)、`task_expiry_check`(interval, 3600s)、`recall_completion_check`(event, etl_data_updated)、`note_reclassify_backfill`(event, recall_completed)
|
||||
- 使用 `ON CONFLICT (job_name) DO NOTHING` 幂等语法
|
||||
- _Requirements: 2.1-2.5_
|
||||
|
||||
- [x] 1.3 在测试库执行迁移脚本并验证
|
||||
- 在 `test_zqyy_app` 中执行建表脚本和种子数据脚本
|
||||
- 验证幂等性:连续执行两次无错误
|
||||
- 验证表结构、约束、索引正确
|
||||
- 验证种子数据完整(4 条触发器配置)
|
||||
- _Requirements: 11.1-11.5, 12.1_
|
||||
|
||||
- [x] 1.4 更新数据库手册和 DDL 基线
|
||||
- 创建 `docs/database/BD_Manual_biz_tables.md`,包含变更说明、兼容性影响、回滚策略、验证 SQL(至少 3 条)
|
||||
- 运行 `python scripts/ops/gen_consolidated_ddl.py` 刷新 DDL 基线
|
||||
- 在数据库手册中记录种子数据内容(触发器配置)
|
||||
- _Requirements: 12.2, 12.3, 12.4_
|
||||
|
||||
- [x] 1.5 编写迁移脚本幂等性属性测试
|
||||
- **Property 13: 迁移脚本幂等性**
|
||||
- 对 DDL 脚本和种子数据脚本连续执行两次,验证第二次执行无错误且数据库状态不变
|
||||
- **Validates: Requirements 1.8, 2.5, 11.4, 11.5**
|
||||
|
||||
- [x] 2. 检查点 - 确保 DDL 和种子数据正确
|
||||
- 确保迁移脚本在测试库中执行成功,幂等性验证通过,如有问题请向用户确认。
|
||||
|
||||
- [x] 3. 实现 Pydantic 模型和纯函数核心逻辑
|
||||
- [x] 3.1 创建 Pydantic 模型 `apps/backend/app/schemas/xcx_tasks.py`
|
||||
- 定义 `TaskListItem`(含 member_name、member_phone、rs_score、heart_icon)
|
||||
- 定义 `AbandonRequest`(reason 必填,min_length=1)
|
||||
- _Requirements: 8.1, 8.2, 8.4, 8.7_
|
||||
|
||||
- [x] 3.2 创建 Pydantic 模型 `apps/backend/app/schemas/xcx_notes.py`
|
||||
- 定义 `NoteCreateRequest`(含 target_type、target_id、content、task_id、rating_service_willingness、rating_revisit_likelihood,评分 ge=1 le=5)
|
||||
- 定义 `NoteOut`(含 type、content、评分、ai_score、ai_analysis)
|
||||
- _Requirements: 9.1, 9.8_
|
||||
|
||||
- [x] 3.3 创建任务生成器核心纯函数 `apps/backend/app/services/task_generator.py`
|
||||
- 定义 `TaskPriority` 枚举、`TASK_TYPE_PRIORITY` 映射
|
||||
- 定义 `IndexData` 数据类
|
||||
- 实现 `determine_task_type(index_data)` 纯函数:根据 WBI/NCI/RS 指数确定任务类型
|
||||
- 实现 `should_replace_task(existing_type, new_type)` 纯函数:判断是否应替换现有任务
|
||||
- 实现 `compute_heart_icon(rs_score)` 纯函数:根据 RS 指数计算爱心 icon 档位
|
||||
- _Requirements: 3.1-3.5, 3.8, 8.2_
|
||||
|
||||
- [x] 3.4 编写任务类型确定正确性属性测试
|
||||
- **Property 1: 任务类型确定正确性**
|
||||
- 生成随机 WBI/NCI/RS 值(Decimal, 0-10, 2 位小数),验证 `determine_task_type()` 返回值符合优先级规则
|
||||
- **Validates: Requirements 3.1, 3.2, 3.3, 3.5**
|
||||
|
||||
- [x] 3.5 编写星星评分范围约束属性测试
|
||||
- **Property 9: 星星评分范围约束**
|
||||
- 生成随机整数(-100 到 100),验证 Pydantic 模型对 1-5 范围外的值拒绝(ValidationError)
|
||||
- **Validates: Requirements 9.8, 14.5**
|
||||
|
||||
- [x] 3.6 编写爱心 icon 档位计算属性测试
|
||||
- **Property 11: 爱心 icon 档位计算**
|
||||
- 生成随机 RS 值(Decimal, 0-10, 1 位小数),验证 `compute_heart_icon()` 返回正确 icon
|
||||
- **Validates: Requirements 8.2**
|
||||
|
||||
- [x] 4. 实现触发器调度框架
|
||||
- [x] 4.1 创建 `apps/backend/app/services/trigger_scheduler.py`
|
||||
- 实现 `_JOB_REGISTRY` 注册表和 `register_job(job_type, handler)` 函数
|
||||
- 实现 `fire_event(event_name, payload)` 方法:查找 event 类型触发器并执行
|
||||
- 实现 `check_scheduled_jobs()` 方法:检查 cron/interval 到期 job 并执行
|
||||
- 实现 `_calculate_next_run(trigger_condition, trigger_config)` 方法:计算下次运行时间
|
||||
- 每个 job 独立事务,失败不影响其他触发器
|
||||
- _Requirements: 10.1-10.7_
|
||||
|
||||
- [x] 4.2 编写触发器 next_run_at 计算属性测试
|
||||
- **Property 12: 触发器 next_run_at 计算**
|
||||
- 生成随机 cron/interval 配置和当前时间,验证 cron 类型 next_run_at > 当前时间,interval 类型 next_run_at = 当前时间 + interval_seconds
|
||||
- **Validates: Requirements 10.1, 10.2**
|
||||
|
||||
- [x] 5. 实现任务生成器完整流程
|
||||
- [x] 5.1 实现 `TaskGenerator.run()` 主流程
|
||||
- 通过 `auth.user_assistant_binding` 获取所有已绑定助教
|
||||
- 对每个助教,通过 FDW 读取 WBI/NCI/RS 指数(`SET LOCAL app.current_site_id`)
|
||||
- 调用 `determine_task_type()` 确定任务类型
|
||||
- 检查已存在的 active 任务:相同 task_type → 跳过;不同 task_type → 关闭旧任务 + 创建新任务 + 记录 history
|
||||
- 处理 `follow_up_visit` 的 48 小时滞留机制(expires_at 填充)
|
||||
- 更新 `trigger_jobs` 时间戳
|
||||
- _Requirements: 3.1-3.10, 4.1-4.5, 5.1-5.4_
|
||||
|
||||
- [x] 5.2 编写活跃任务唯一性不变量属性测试
|
||||
- **Property 2: 活跃任务唯一性不变量**
|
||||
- 生成随机 (site_id, assistant_id, member_id, task_type) 组合,模拟插入操作,验证 active 任务最多一条
|
||||
- **Validates: Requirements 1.5, 3.6, 14.1**
|
||||
|
||||
- [x] 5.3 编写任务类型变更状态机属性测试
|
||||
- **Property 3: 任务类型变更状态机**
|
||||
- 生成随机现有任务 + 新任务类型,执行变更,验证旧任务 inactive + 新任务 active + history 记录
|
||||
- **Validates: Requirements 3.7, 5.1, 5.4, 14.2**
|
||||
|
||||
- [x] 5.4 编写 48 小时滞留机制属性测试
|
||||
- **Property 4: 48 小时滞留机制**
|
||||
- 生成随机 follow_up_visit 任务 + 时间偏移,验证 expires_at 填充和过期逻辑
|
||||
- **Validates: Requirements 4.1, 4.2, 4.3, 4.4, 14.3**
|
||||
|
||||
- [x] 6. 检查点 - 确保任务生成器测试通过
|
||||
- 运行属性测试:`cd C:\NeoZQYY && pytest tests/test_core_business_properties.py -v -k "property_1 or property_2 or property_3 or property_4"`
|
||||
- 确保所有属性测试通过,如有问题请向用户确认。
|
||||
|
||||
- [x] 7. 实现任务管理服务
|
||||
- [x] 7.1 创建 `apps/backend/app/services/task_manager.py`
|
||||
- 实现 `get_task_list(user_id, site_id)` 异步方法:查询活跃任务 + FDW 读取客户信息和 RS 指数 + 爱心 icon 计算 + 排序
|
||||
- 实现 `pin_task(task_id, user_id, site_id)` 异步方法:验证归属 + 设置 is_pinned=TRUE + 记录 history
|
||||
- 实现 `unpin_task(task_id, user_id, site_id)` 异步方法:验证归属 + 设置 is_pinned=FALSE
|
||||
- 实现 `abandon_task(task_id, user_id, site_id, reason)` 异步方法:验证 reason 非空 + 设置 abandoned + 记录 history
|
||||
- 实现 `cancel_abandon(task_id, user_id, site_id)` 异步方法:恢复 active + 清空 abandon_reason + 记录 history
|
||||
- 实现 `_record_history()` 内部方法
|
||||
- _Requirements: 8.1-8.8_
|
||||
|
||||
- [x] 7.2 编写放弃与取消放弃往返属性测试
|
||||
- **Property 5: 放弃与取消放弃往返**
|
||||
- 生成随机 active 任务 + 非空放弃原因,执行放弃→取消放弃,验证状态恢复;空原因应返回 422
|
||||
- **Validates: Requirements 8.4, 8.6, 8.7, 14.4**
|
||||
|
||||
- [x] 7.3 编写任务列表排序正确性属性测试
|
||||
- **Property 10: 任务列表排序正确性**
|
||||
- 生成随机任务列表(不同 is_pinned/priority_score/created_at),验证排序为 is_pinned DESC, priority_score DESC, created_at ASC
|
||||
- **Validates: Requirements 8.1**
|
||||
|
||||
- [x] 7.4 编写状态变更历史完整性属性测试
|
||||
- **Property 15: 状态变更历史完整性**
|
||||
- 生成随机状态变更操作序列(置顶/放弃/取消放弃),验证 history 记录数量和内容正确
|
||||
- **Validates: Requirements 5.4, 8.3**
|
||||
|
||||
- [x] 8. 实现有效期轮询器
|
||||
- [x] 8.1 创建 `apps/backend/app/services/task_expiry.py`
|
||||
- 实现 `run()` 方法:查询 expires_at 不为 NULL 且已过期的 active 任务,标记为 inactive,记录 history
|
||||
- _Requirements: 4.3, 4.5_
|
||||
|
||||
- [x] 9. 实现召回完成检测器
|
||||
- [x] 9.1 创建 `apps/backend/app/services/recall_detector.py`
|
||||
- 实现 `run(payload)` 方法:通过 FDW 读取新增服务记录,匹配 active 任务标记 completed,记录 completed_at 和 completed_task_type 快照,触发 `recall_completed` 事件
|
||||
- _Requirements: 6.1-6.5_
|
||||
|
||||
- [x] 9.2 编写召回完成检测与类型快照属性测试
|
||||
- **Property 6: 召回完成检测与类型快照**
|
||||
- 生成随机 active 任务 + 服务记录,执行完成检测,验证 completed_task_type 记录了完成时的 task_type 快照
|
||||
- **Validates: Requirements 6.2, 6.3, 14.6**
|
||||
|
||||
- [x] 10. 检查点 - 确保任务管理和召回检测测试通过
|
||||
- 运行属性测试:`cd C:\NeoZQYY && pytest tests/test_core_business_properties.py -v -k "property_5 or property_6 or property_10 or property_15"`
|
||||
- 确保所有属性测试通过,如有问题请向用户确认。
|
||||
|
||||
- [-] 11. 实现备注系统
|
||||
- [x] 11.1 创建备注服务 `apps/backend/app/services/note_service.py`
|
||||
- 实现 `create_note()` 异步方法:验证评分范围 + 确定 note type(关联 follow_up_visit 任务 → follow_up,否则 normal)+ INSERT + 触发 AI 应用 6 接口(占位)+ 若 ai_score >= 6 标记任务 completed
|
||||
- 实现 `get_notes()` 异步方法:按 created_at DESC 排序,包含评分和 AI 评分
|
||||
- 实现 `delete_note()` 异步方法:验证归属后硬删除
|
||||
- _Requirements: 9.1-9.9_
|
||||
|
||||
- [x] 11.2 创建备注回溯重分类器 `apps/backend/app/services/note_reclassifier.py`
|
||||
- 实现 `run(payload)` 方法:查找 service_time 之后的第一条 normal 备注 → 更新为 follow_up → 触发 AI 应用 6 接口(占位)→ 根据 ai_score 生成 follow_up_visit 任务
|
||||
- 实现 `ai_analyze_note(note_id)` 占位函数(返回 None,P5 实现后替换)
|
||||
- _Requirements: 7.1-7.5_
|
||||
|
||||
- [x] 11.3 编写备注回溯重分类属性测试
|
||||
- **Property 7: 备注回溯重分类**
|
||||
- 生成随机备注列表 + service_time,执行回溯,验证符合条件的 normal 备注 type 变为 follow_up
|
||||
- **Validates: Requirements 7.1, 7.2, 14.7**
|
||||
|
||||
- [x] 11.4 编写备注类型自动设置属性测试
|
||||
- **Property 8: 备注类型自动设置**
|
||||
- 生成随机 task_type + 备注创建,验证关联 follow_up_visit → type=follow_up,其他 → type=normal
|
||||
- **Validates: Requirements 9.2, 9.3**
|
||||
|
||||
- [x] 11.5 编写 AI 评分驱动的任务完成判定属性测试
|
||||
- **Property 14: AI 评分驱动的任务完成判定**
|
||||
- 生成随机 ai_score + 任务状态,验证 ai_score >= 6 且 active → completed,ai_score < 6 → 保持 active
|
||||
- **Validates: Requirements 7.4, 7.5, 9.5**
|
||||
|
||||
- [x] 12. 实现 API 路由层
|
||||
- [x] 12.1 创建小程序任务路由 `apps/backend/app/routers/xcx_tasks.py`
|
||||
- 实现 `GET /api/xcx/tasks`:获取任务列表(require_approved)
|
||||
- 实现 `POST /api/xcx/tasks/{id}/pin`:置顶任务
|
||||
- 实现 `POST /api/xcx/tasks/{id}/unpin`:取消置顶
|
||||
- 实现 `POST /api/xcx/tasks/{id}/abandon`:放弃任务(AbandonRequest 校验)
|
||||
- 实现 `POST /api/xcx/tasks/{id}/cancel-abandon`:取消放弃
|
||||
- _Requirements: 8.1-8.8_
|
||||
|
||||
- [x] 12.2 创建小程序备注路由 `apps/backend/app/routers/xcx_notes.py`
|
||||
- 实现 `POST /api/xcx/notes`:创建备注(NoteCreateRequest 校验)
|
||||
- 实现 `GET /api/xcx/notes`:查询备注列表(query: target_type, target_id)
|
||||
- 实现 `DELETE /api/xcx/notes/{id}`:删除备注
|
||||
- _Requirements: 9.1-9.9_
|
||||
|
||||
- [x] 12.3 在 `apps/backend/app/main.py` 中注册新路由
|
||||
- 注册 `xcx_tasks` 和 `xcx_notes` 路由
|
||||
- 验证无路由冲突
|
||||
- _Requirements: 全部_
|
||||
|
||||
- [x] 12.4 注册触发器 job handler
|
||||
- 在应用启动时调用 `register_job()` 注册 `task_generator`、`task_expiry_check`、`recall_completion_check`、`note_reclassify_backfill` 四个 handler
|
||||
- _Requirements: 10.1-10.6_
|
||||
|
||||
- [x] 13. 检查点 - 确保所有测试通过
|
||||
- 运行属性测试:`cd C:\NeoZQYY && pytest tests/test_core_business_properties.py -v`
|
||||
- 26/26 全部通过(16.81s)
|
||||
|
||||
- [x] 14. 最终检查点 - 全量验证
|
||||
- 运行全部属性测试:26/26 通过(16.81s)
|
||||
- 验证迁移脚本幂等性:Property 13(3 个测试)通过
|
||||
- 验证种子数据完整性:4 条触发器配置全部存在
|
||||
- 验证表结构:coach_tasks / coach_task_history / notes / trigger_jobs 全部存在
|
||||
- 验证部分唯一索引:idx_coach_tasks_site_assistant_member_type 存在
|
||||
|
||||
## 备注
|
||||
|
||||
- 标记 `*` 的子任务为可选(属性测试),可跳过以加速 MVP
|
||||
- 每个任务引用了具体的需求编号,确保可追溯
|
||||
- 检查点确保增量验证
|
||||
- 属性测试验证通用正确性属性(hypothesis,最少 200 次迭代)
|
||||
- 所有数据库操作在测试库 `test_zqyy_app` 进行
|
||||
- 迁移脚本放在 `db/zqyy_app/migrations/` 目录
|
||||
- 属性测试放在 `tests/test_core_business_properties.py`(Monorepo 级)
|
||||
- AI 应用 6 接口为占位实现(返回 None),由 P5 AI 集成层替换
|
||||
- 维客线索功能由独立模块 `routers/member_retention_clue.py` 处理,不在本 SPEC 范围内
|
||||
- FDW 查询需在事务中 `SET LOCAL app.current_site_id` 设置 RLS 隔离
|
||||
Reference in New Issue
Block a user