# 变更审计记录：P17 助教客户归属与任务生成引擎

| 字段 | 值 |
|------|-----|
| 日期 | 2026-03-24 07:00:13 |
| Prompt-ID | P20260324-062345 |
| Session-ID | 415d32f9 |
| Session 路径 | docs/audit/session_logs/2026-03/24/25_d27915ce_055410 |
| PRD | docs/prd/specs/P17-assistant-ownership-task-engine.md |

## 操作摘要

P17 助教客户归属与任务生成引擎实施。核心变更：task_generator.py 完全重写（入口从 user_assistant_binding 改为 OS 归属对）、fdw_queries.py 新增 4 个批量查询方法、新建迁移脚本（coach_tasks 新增 3 字段 + 2 张新表 + 2 个新枚举值）、修复属性测试跳过问题。

## 变更文件清单

| 文件 | 变更类型 | 风险等级 |
|------|----------|----------|
| `apps/backend/app/services/task_generator.py` | 修改（完全重写） | 高 |
| `apps/backend/app/services/fdw_queries.py` | 修改（追加 4 方法） | 高 |
| `db/zqyy_app/migrations/2026-03-24__p17_task_engine_ownership.sql` | 新增 | 高 |
| `tests/test_core_business_properties.py` | 修改（修复） | 中 |

## 本次对话文件变更

### 新增文件
- `docs/audit/prompt_logs/prompt_log_20260324_062345.md`
- `docs/audit/session_logs/2026-03/24/25_d27915ce_055410/main_01_415d32f9.md`
- `docs/audit/session_logs/2026-03/24/26_b72f6c5a_062011/main_01_88b61816.md`
- `docs/prd/specs/P18-admin-task-engine-dashboard.md`

### 删除文件
- `docs/audit/session_logs/2026-03/24/25_d27915ce_055410/main_01_7469ca03.md`

## 数据库变更（db-schema-change）

### 迁移脚本
`db/zqyy_app/migrations/2026-03-24__p17_task_engine_ownership.sql`

### 变更内容

| 操作 | 对象 | 说明 |
|------|------|------|
| ALTER TABLE | `biz.coach_tasks` | 新增 3 字段：`transfer_count`（INTEGER NOT NULL DEFAULT 0）、`transferred_from`（BIGINT FK→self）、`transferred_at`（TIMESTAMPTZ） |
| CREATE TABLE | `biz.cfg_task_generator_params` | 任务引擎参数配置表（6 字段），支持全局默认 + 门店级覆盖，UNIQUE(site_id, param_key) |
| CREATE TABLE | `biz.coach_task_transfer_log` | 客户转移日志表（11 字段），记录每次转移的完整上下文（三重保护检查快照、转移得分） |
| ADD ENUM VALUE | `task_status` | 新增 `transferred`、`pending_review`（兼容 enum/varchar 两种情况） |
| INSERT SEED | `biz.cfg_task_generator_params` | 13 条全局默认参数（ON CONFLICT DO NOTHING） |
| CREATE INDEX | `idx_transfer_log_site_created` | `(site_id, created_at DESC)` |
| CREATE INDEX | `idx_transfer_log_member` | `(member_id, created_at DESC)` |

### ⚠️ DDL 基线待合并

`compliance.has_ddl_baseline` 为 false，新增的表和字段尚未合并到 DDL 基线文件（`docs/database/ddl/zqyy_app__biz.sql`）。

## 验证结果

- getDiagnostics: `task_generator.py` 和 `fdw_queries.py` 均无语法错误
- 属性测试: Property 1（任务类型判定）和 Property 11（爱心 icon 档位）各 200 用例全部通过
- 下游兼容性: 7 个下游模块均使用显式 status 过滤，新增 `transferred`/`pending_review` 状态不会被误匹配


## 改动注解

### `apps/backend/app/services/task_generator.py`
- 变更类型：修改（完全重写）
- 原始原因：P17 PRD 要求将任务生成入口从 `auth.user_assistant_binding`（用户-助教绑定表）改为 OS 归属对（`fdw_queries.get_ownership_pairs`），以支持 MAIN/COMANAGE/POOL 三级归属模型下的精确任务分配
- 思路分析：
  - `run()` 主流程保持"获取 site_ids → 逐门店处理"的骨架，但入口数据源从绑定表改为 OS 归属视图
  - 新增 `_run_for_site()` 封装单门店流程：加载参数 → 查询归属对 → 批量读取 WBI/NCI → 逐对生成任务 → 客户转移检查
  - `_process_pair()` 实现参数化四级漏斗（阈值从 `cfg_task_generator_params` 读取，不再硬编码）
  - `_run_transfer_check()` 实现客户转移子流程，含三重保护（门店助教规模、入驻时间、服务关系门槛）
  - `_do_transfer()` 执行单次转移：原任务标记 transferred → 新助教创建任务（继承 transfer_count+1）→ 写入转移日志
  - 纯函数 `determine_task_type`/`should_replace_task`/`compute_heart_icon` 保持不变，确保属性测试兼容
  - `load_params()` 实现三级继承链：代码默认 → 全局默认（site_id IS NULL）→ 门店覆盖
- 修改结果：任务生成器从"绑定关系驱动"升级为"归属关系驱动"，支持客户转移和参数化配置。影响范围：`trigger_scheduler.py`（调用 `run()`）、`biz.coach_tasks` 表（新增字段）、`biz.cfg_task_generator_params`（新表）、`biz.coach_task_transfer_log`（新表）

### `apps/backend/app/services/fdw_queries.py`
- 变更类型：修改（末尾追加 4 个方法）
- 原始原因：P17 任务生成器需要从 ETL 库读取 OS 归属对和批量指数数据，原有 fdw_queries 不提供这些查询
- 思路分析：
  - `get_ownership_pairs(conn, site_id)` — 查询 `v_dws_member_assistant_relation_index` WHERE `os_label IN ('MAIN', 'COMANAGE')`，返回 `[{assistant_id, member_id, rs}]`
  - `get_pool_assistants(conn, site_id, member_id)` — 查询同一客户的 POOL 助教候选池，返回 `[{assistant_id, rs, ms, ml}]`（用于转移候选排序）
  - `get_wbi_batch(conn, site_id)` / `get_nci_batch(conn, site_id)` — 批量读取 WBI/NCI 指数，返回 `{member_id: Decimal}`
  - 所有方法使用 `_fdw_context()` 连接 ETL 库，遵循 H2 FDW→直连改造后的统一模式
- 修改结果：为 P17 任务生成器提供完整的数据查询层。影响范围仅限 `task_generator.py` 调用

### `db/zqyy_app/migrations/2026-03-24__p17_task_engine_ownership.sql`
- 变更类型：新增
- 原始原因：P17 任务引擎需要新的数据库对象支持客户转移追踪和参数化配置
- 思路分析：
  - 使用 `DO` 块兼容 enum/varchar 两种 status 列类型，安全添加 `transferred`/`pending_review` 枚举值
  - `coach_tasks` 新增 3 字段使用 `ADD COLUMN IF NOT EXISTS` 保证幂等
  - `cfg_task_generator_params` 使用 `UNIQUE(site_id, param_key)` 支持全局+门店级覆盖的继承链
  - `coach_task_transfer_log` 的 `guard_checks` 字段使用 JSONB 存储三重保护检查快照，便于审计追溯
  - 种子数据使用 `ON CONFLICT DO NOTHING` 保证幂等
  - 文件末尾包含注释形式的 ROLLBACK 语句
- 修改结果：为 P17 提供完整的数据库支撑。新增 2 张表、3 个字段、2 个索引、13 条种子数据

### `tests/test_core_business_properties.py`
- 变更类型：修改（修复）
- 原始原因：module-level `pytest.skip` 导致整个测试文件被跳过（包括不依赖数据库的纯函数属性测试），需要改为更精确的跳过机制
- 思路分析：将 module-level skip 改为 class-level `@pytest.mark.skipif`，仅跳过需要数据库连接的测试类；加入 `apps/backend/` 到 `sys.path` 解决 `from app.*` 导入问题；更新 Property 1 断言以匹配 P17 的 RS > 1 下限规则（原规则 RS ≥ 0 生成关系构建，P17 改为 RS > 1）
- 修改结果：属性测试恢复正常运行，Property 1 和 Property 11 各 200 用例全部通过
