# BD 手册：AI 事件触发器 + App2 cron 预热注册

## 背景

Phase 0 AI 模块收尾（2026-04-20）。dispatcher 已注册 5 个 handler 到
`trigger_scheduler._JOB_REGISTRY`（通过 `main.py` lifespan 调用），但
`biz.trigger_jobs` 缺对应数据行 → `fire_event()` 查不到事件绑定，
导致 4 个 fire_event 调用点（task_generator / note_service /
admin_task_engine / internal_events）全部失效。本迁移补齐数据行。

## 变更说明

| 库 | Schema | 表 | 变更类型 | 新增数据 |
|---|---|---|---|---|
| zqyy_app | biz | trigger_jobs | INSERT 5 行 | 4 event + 1 cron |

### 新增记录

| job_name | job_type | trigger_condition | trigger_config | 说明 |
|---|---|---|---|---|
| `ai_consumption_settled` | `ai_consumption_settled` | event | `{"event_name":"ai_consumption_settled"}` | 消费事件链 App3→App8→App7（+App4→App5） |
| `ai_note_created` | `ai_note_created` | event | `{"event_name":"ai_note_created"}` | 备注事件链 App6→App8 |
| `ai_task_assigned` | `ai_task_assigned` | event | `{"event_name":"ai_task_assigned"}` | 任务分配链 App4→App5 |
| `ai_dws_completed` | `ai_dws_completed` | event | `{"event_name":"ai_dws_completed"}` | App2 × 8 时间维度预生成 |
| `ai_dws_prewarm_0830` | `ai_dws_prewarm` | cron | `{"cron_expression":"30 8 * * *"}` | 每日 08:30 兜底触发，对所有门店预热 App2 |

### 迁移脚本路径

`db/zqyy_app/migrations/20260420_ai_trigger_jobs_and_app2_prewarm.sql`

### 配套代码变更（已在 Phase 0.1~0.3 内完成）

- `app/ai/dispatcher.py`：5 个 handler 注册（4 event + 1 cron），handle_app2_prewarm 新增
- `app/services/trigger_scheduler.py`：`_invoke_handler` 支持 async handler（识别 coroutine 自动调度）
- `app/services/task_generator.py`：run() 末尾对新建任务批量触发 `ai_consumption_settled`
- `app/services/note_service.py`：create_note 在异步 AI 评分同处增 `ai_note_created` 触发
- `app/routers/admin_task_engine.py`：reassign_task commit 后触发 `ai_task_assigned`
- `app/routers/internal_events.py`：etl_completed_endpoint 返回前对所有 site 触发 `ai_dws_completed`

## 兼容性

| 受影响方 | 影响 |
|---|---|
| ETL | 无影响。ETL 通过现有 POST `/api/internal/etl-completed` 触发，后端已自动触发 AI 事件 |
| 后端 API | 无破坏性改动。新增 cron job `ai_dws_prewarm_0830` 由 Scheduler 后台循环拾取，不影响现有调度 |
| 小程序 | 无直接影响。前端通过 `ai_cache` 读缓存，缓存由 dispatcher 链路写入 |
| admin-web | 无影响。AI 监控面板通过已有 `/api/admin/ai/*` 接口查询 |
| 其他门店隔离 | 无影响。5 条记录为全局配置（非 site 级别），dispatcher 处理时从 payload 取 site_id |

## 回滚策略

### SQL 回滚

```sql
-- 删除 5 条记录（幂等）
DELETE FROM biz.trigger_jobs
WHERE job_name IN (
    'ai_consumption_settled',
    'ai_note_created',
    'ai_task_assigned',
    'ai_dws_completed',
    'ai_dws_prewarm_0830'
);
```

### 代码回滚

若需完全回滚 Phase 0：`git revert` 对应提交即可。
4 个 fire_event 调用点在 try/except 内，即使事件不存在也不影响主流程。

## 验证 SQL

执行迁移后，依次运行以下 SQL 验证：

```sql
-- 1. 新增 5 条记录
SELECT count(*) AS ai_job_count
FROM biz.trigger_jobs
WHERE job_type LIKE 'ai_%';
-- 期望：5

-- 2. event 绑定正确（4 条）
SELECT job_name, trigger_config->>'event_name' AS event_name
FROM biz.trigger_jobs
WHERE trigger_condition = 'event'
  AND trigger_config->>'event_name' LIKE 'ai_%'
ORDER BY job_name;
-- 期望：4 条，job_name = event_name
--   ai_consumption_settled | ai_consumption_settled
--   ai_dws_completed       | ai_dws_completed
--   ai_note_created        | ai_note_created
--   ai_task_assigned       | ai_task_assigned

-- 3. cron 表达式正确
SELECT job_name, trigger_config->>'cron_expression' AS cron_expr
FROM biz.trigger_jobs
WHERE job_type = 'ai_dws_prewarm';
-- 期望：1 条
--   ai_dws_prewarm_0830 | 30 8 * * *
```

## 运行期验证

后端启动后，查询运行日志确认 5 个 handler 注册成功：

```bash
# 启动时应出现（dispatcher.register_ai_handlers 打印）：
# 已注册 AI 事件处理器: ai_consumption_settled
# 已注册 AI 事件处理器: ai_note_created
# 已注册 AI 事件处理器: ai_task_assigned
# 已注册 AI 事件处理器: ai_dws_completed
# 已注册 AI 事件处理器: ai_dws_prewarm
```

端到端冒烟（Phase 0.4）：构造一条消费事件 → 观察 App3/App8/App7
ai_cache 记录产生、ai_run_logs 新增、member_retention_clue 幂等替换。
