# 需求文档：小程序核心业务模块（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。
