# P4：核心业务层 — miniapp-core-business

> 优先级：P4（依赖 P1 + P2 + P3）
> 预估工作量：大（本 SPEC 是业务核心）

---

## 需求（Requirements）

### 用户故事

1. 作为助教，我每天打开小程序能看到系统为我分配的任务列表，按优先级排序。
2. 作为助教，我可以置顶/放弃任务，放弃时必须填写原因。
3. 作为助教，我完成召回任务后（客户到店被服务），系统自动标记任务完成。
4. 作为助教，我给客户添加备注后，系统自动通过 AI 分析备注内容（应用 6），提取维客线索并评分。助教为客户提交备注即算回访完成（AI 评分仅用于后续绩效评估，不参与完成判定）。
5. 作为助教，我在添加备注时可以对客户进行星星评分（再次服务意愿、再来店可能性，各 1-5 星），回访任务默认展开评分区域，其他任务类型通过"展开评价"按钮手动打开。
6. 作为系统，回访任务至少保留 48 小时，到期后自动失效。
7. 作为系统，当 ETL 数据延迟导致召回完成晚于备注提交时，需要回溯重分类备注。

### 验收标准

- AC1：任务生成器每日 7:00 后运行，正确按 max(WBI,NCI) 分配 4 种任务类型
- AC2：同客户-助教-类型的任务跳过；不同类型则关闭旧任务+新建
- AC3：回访任务 48 小时滞留机制正常（生成时间算起）
- AC4：任务有效/无效状态 + 有效期字段正确流转
- AC5：召回完成检测在 ETL 数据到达后自动触发
- AC6：数据回溯将普通备注重分类为回访备注并触发 AI 备注分析
- AC7：备注 CRUD 正常，type 字段正确区分
- AC8：生日信息作为维客线索（客户基础信息类别）存储于 `member_retention_clue`，独立于 ETL 数据
- AC9：备注星星评分（再次服务意愿、再来店可能性）正确存储，不参与完成判定，不参与应用 6 分析，仅作辅助数据
- AC10：回访任务页面默认展开评分区域；其他任务类型默认隐藏，通过"展开评价"按钮手动打开（数据迟到场景：助教在召回任务中提前写备注+评分）

---

## 设计要点

### 任务类型与优先级

| 优先级 | 类型 | 触发条件 | 完成条件 |
|--------|------|---------|---------|
| 0 | 高优先召回 | max(WBI,NCI) > 7 | 助教为该客户服务（ETL 检测） |
| 0 | 优先召回 | max(WBI,NCI) > 5 | 同上 |
| 1 | 客户回访 | 完成召回后未备注 | 助教为该客户提交备注（AI 评分仅绩效用途） |
| 2 | 关系构建 | 1 < RS < 6 | 无自动完成条件（手动标记或指数变化） |

### 任务类型与任务状态的关系

**核心原则**：任务类型（task_type）和任务状态（status）是两套独立的维度，交叉描述一个任务，互不干扰。

- **任务类型**（task_type）：描述任务的业务性质，由系统根据客户指数自动分配，不因用户操作而改变
  - `high_priority_recall`（高优先召回）
  - `priority_recall`（优先召回）
  - `follow_up_visit`（客户回访）
  - `relationship_building`（关系构建）

- **任务状态**（status）：描述任务的生命周期阶段，可由用户操作或系统自动流转
  - `active`（有效）：任务当前有效，显示在列表中
  - `inactive`（无效）：任务已过期或被系统标记为无效，不显示在列表中
  - `completed`（已完成）：任务已完成
  - `abandoned`（已放弃）：用户主动放弃任务，记录放弃原因

- **置顶状态**（is_pinned）：独立于任务状态，用户可对任何有效任务置顶，已放弃任务不可置顶

- **前端展示规则**：
  - 置顶区域：显示 `is_pinned=true` 且 `status=active` 的任务
  - 一般任务区域：显示 `is_pinned=false` 且 `status=active` 的任务
  - 已放弃区域：显示 `status=abandoned` 的任务（任务类型保留，但灰化显示）
  - 排序规则：`is_pinned DESC → priority_score DESC NULLS LAST → created_at ASC`，已放弃任务排在所有有效任务之后

- **长按菜单规则**：
  - 置顶/一般任务：显示"置顶/取消置顶"、"备注"、"问问AI"、"放弃任务"
  - 已放弃任务：显示"取消放弃"（直接执行，无需二次确认，将任务恢复至 `status=active` 且 `is_pinned=false`）

### 任务状态机

```
[生成] → 有效(status=active, 无有效期)
  ├── 类型变更 → 旧任务无效(status=inactive, 无有效期) + 新任务有效
  ├── 指数不再满足 → 有效(status=active, 填充有效期=生成时间+48h)
  │     └── 轮询检查 → 超过有效期 → 无效(status=inactive)
  ├── 新回访任务顶替 → 旧任务无效(status=inactive) + 新任务有效
  ├── 助教放弃 → 无效(status=abandoned, 记录放弃原因)
  ├── 助教取消放弃 → 有效(status=active, is_pinned=false, 清除放弃原因)
  └── 完成 → 已完成(status=completed, 记录完成时间和完成时状态)
```

### coach_tasks 表核心字段

```
biz.coach_tasks
  - id, site_id, assistant_id, member_id
  - task_type (high_priority_recall / priority_recall / follow_up_visit / relationship_building)
  - status (active / inactive / completed / abandoned)
  - priority_score (max(WBI,NCI) 快照)
  - expires_at (有效期，默认 NULL)
  - is_pinned (置顶)
  - abandon_reason (放弃原因)
  - completed_at, completed_task_type (完成时的任务类型快照)
  - parent_task_id (被顶替的旧任务 ID)
  - created_at, updated_at
```

### notes 表核心字段

```
biz.notes
  - id, site_id, user_id, target_type, target_id
  - type (normal / follow_up / abandon_reason)
  - content (TEXT)
  - rating_service_willingness (SMALLINT 1-5, 可空，再次服务此客户意愿)
  - rating_revisit_likelihood (SMALLINT 1-5, 可空，再来店可能性)
  - task_id (关联任务 ID, 可空，备注可独立于任务创建)
  - created_at, updated_at
```

> 备注与任务的关系：
> - 备注不要求通过 `task_id` 显式关联回访任务才能触发完成
> - 助教为某客户提交备注时，系统检查该助教×客户是否有有效的回访任务，有则标记完成
> - `task_id` 为可选字段，备注可独立于任务创建（如直接在客户详情页写备注）

> 星星评分说明：
> - 回访任务（follow_up_visit）：备注弹窗默认展开评分区域
> - 其他任务类型：评分区域默认隐藏，通过"展开评价"按钮手动打开
> - 数据迟到场景：助教在召回任务中完成服务后顺手写备注，此时 ETL 数据未到，任务仍为召回类型，评分区域隐藏但可手动展开；当 ETL 数据到达、召回完成检测触发后，回溯机制将该备注重分类为回访备注，星星评分数据一并保留
> - 星星评分不参与回访完成判定（回访完成判定为：助教为该客户提交备注即完成），不参与应用 6 分析，仅作辅助数据存储，后期功能扩展使用

### 触发器机制

```
biz.trigger_jobs
  - id, job_type, job_name
  - trigger_condition (cron / event / interval)
  - trigger_config (JSON: cron表达式 / 事件名 / 间隔秒数)
  - last_run_at, next_run_at
  - status (enabled / disabled)
  - created_at
```

预置触发器：
1. `task_generator` — cron: 每日 07:00
2. `task_expiry_check` — interval: 每小时
3. `recall_completion_check` — event: ETL 数据更新后
4. `note_reclassify_backfill` — event: 召回完成时

---

## 任务清单

- [ ] T1：创建 `biz.coach_tasks` + `biz.coach_task_history` 表
- [ ] T2：创建 `biz.notes` 表（type: normal/follow_up/abandon_reason，含 `rating_service_willingness` SMALLINT 1-5 可空、`rating_revisit_likelihood` SMALLINT 1-5 可空）
- [ ] T3：创建 `biz.trigger_jobs` 表 + 轮询调度框架
- [ ] T4：实现任务生成器（读取指数 → 分配任务 → 跳过/更新逻辑）
- [ ] T5：实现 48 小时滞留机制 + 有效期轮询
- [ ] T6：实现召回完成检测（ETL 数据到达后匹配 service_log）
- [ ] T7：实现数据回溯机制（普通备注 → 回访备注 + 触发 AI 备注分析）
- [ ] T8：实现任务 CRUD API（列表/详情/置顶/放弃/取消置顶/取消放弃）
- [ ] T9：实现备注 CRUD API（创建/列表/删除，含星星评分字段的存储与读取）
- [ ] T10：生日信息已迁移至维客线索系统（`member_retention_clue`），无需单独处理
