Files
Neo-ZQYY/docs/database/BD_Manual_biz_tables.md
Neo 70324d8542 chore: 文档与 IDE 配置整理
- .kiro/specs/ → docs/specs/(41 个历史需求 spec 迁移,移除 .config.kiro)
- CLAUDE.md 三层拆分:根文件精简 + apps/backend/CLAUDE.md + .claude/commands/
- 新增 /spec-close、/pre-change 两个工作流命令
- DDL 基线刷新(从测试库重新导出 11 个文件,dws 35→38 表,biz 18→21 表)
- BD_Manual → BD_manual 命名统一(48 个文件)
- 修复 3 处文档与数据库不一致(auth.users.status 默认值、scheduled_tasks 字段、RLS 视图数)
- 新增 BD_manual_public_rbac_tables.md(public schema 8 张 RBAC/工作流表)
- 合并 biz.trigger_jobs 文档(10→12 字段,归档独立文档)
- docs/database/README.md 索引更新

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 00:02:37 +08:00

388 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# BD_Manualbiz Schema 业务表(助教任务系统 + 备注系统 + 触发器调度)
> 目标库:`test_zqyy_app`(通过 `APP_DB_DSN` 连接)
> 迁移脚本:
> - `db/zqyy_app/migrations/2026-02-27__p4_create_biz_tables.sql`(建表)
> - `db/zqyy_app/migrations/2026-02-27__p4_seed_trigger_jobs.sql`(种子数据)
> - `db/zqyy_app/migrations/2026-03-24__p17_task_engine_ownership.sql`P17 客户归属与转移)
> - `db/zqyy_app/migrations/2026-03-24__p18_task_engine_dashboard.sql`P18 运营看板字段扩展)
> 关联 SPEC`04-miniapp-core-business`P4、`P17-assistant-ownership-task-engine`P17、`P18-admin-task-engine-dashboard`P18
---
## 1. 变更说明
### 新增表6 张)
| # | 表名 | 用途 | 字段数 | 来源 |
|---|------|------|--------|------|
| 1 | `biz.coach_tasks` | 助教任务表:存储任务分配、状态、有效期、置顶、放弃原因、转移追踪等 | 18 | P4+P17 |
| 2 | `biz.coach_task_history` | 任务变更历史表:记录任务关闭/新建/置顶/放弃/转移的追溯链 | 9 | P4 |
| 3 | `biz.notes` | 统一备注表:通过 `type` 字段区分普通备注/回访备注/放弃原因,含星星评分 | 15 | P4 |
| 4 | `biz.trigger_jobs` | 触发器配置表:存储 cron/interval/event 三种触发方式的配置与执行状态 | 12 | P4+P18+P23 |
| 5 | `biz.cfg_task_generator_params` | 任务引擎参数配置表:支持全局默认 + 门店级覆盖 | 7 | P17+P18 |
| 6 | `biz.coach_task_transfer_log` | 客户转移日志表:记录每次转移的完整上下文 | 11 | P17 |
### 表字段明细
#### biz.coach_tasks18 字段)
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `id` | BIGSERIAL | PK | 自增主键 |
| `site_id` | BIGINT | NOT NULL | 门店 ID多门店隔离 |
| `assistant_id` | BIGINT | NOT NULL | 助教 ID |
| `member_id` | BIGINT | NOT NULL | 客户 ID |
| `task_type` | VARCHAR(50) | NOT NULL | 任务类型:`high_priority_recall` / `priority_recall` / `follow_up_visit` / `relationship_building` |
| `status` | VARCHAR(20) | NOT NULL DEFAULT 'active' | 状态:`active` / `inactive` / `completed` / `abandoned` / `transferred`P17/ `pending_review`P17 |
| `priority_score` | NUMERIC(5,2) | 可空 | 优先级分数,取 `max(WBI, NCI)` 快照 |
| `expires_at` | TIMESTAMPTZ | 可空 | 有效期时间戳NULL 表示无限期 |
| `is_pinned` | BOOLEAN | DEFAULT FALSE | 是否置顶 |
| `abandon_reason` | TEXT | 可空 | 放弃原因(放弃时必填) |
| `completed_at` | TIMESTAMPTZ | 可空 | 完成时间 |
| `completed_task_type` | VARCHAR(50) | 可空 | 完成时的任务类型快照 |
| `parent_task_id` | BIGINT | FK → `biz.coach_tasks(id)`,可空 | 父任务 ID自引用 |
| `transfer_count` | INTEGER | NOT NULL DEFAULT 0 | 该客户在此任务链上的累计转移次数P17 新增) |
| `transferred_from` | BIGINT | FK → `biz.coach_tasks(id)`,可空 | 转移来源任务 IDP17 新增) |
| `transferred_at` | TIMESTAMPTZ | 可空 | 转移发生时间P17 新增) |
| `created_at` | TIMESTAMPTZ | DEFAULT NOW() | 创建时间 |
| `updated_at` | TIMESTAMPTZ | DEFAULT NOW() | 更新时间 |
#### biz.coach_task_history9 字段)
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `id` | BIGSERIAL | PK | 自增主键 |
| `task_id` | BIGINT | NOT NULL, FK → `biz.coach_tasks(id)` | 关联任务 |
| `action` | VARCHAR(50) | NOT NULL | 操作类型:`created` / `type_changed` / `type_change_close` / `pinned` / `abandoned` / `cancel_abandon` / `expired` / `completed` / `expires_at_filled` / `transferred_out`P17/ `transferred_in`P17 |
| `old_status` | VARCHAR(20) | 可空 | 变更前状态 |
| `new_status` | VARCHAR(20) | 可空 | 变更后状态 |
| `old_task_type` | VARCHAR(50) | 可空 | 变更前任务类型 |
| `new_task_type` | VARCHAR(50) | 可空 | 变更后任务类型 |
| `detail` | JSONB | 可空 | 附加详情(如放弃原因等) |
| `created_at` | TIMESTAMPTZ | DEFAULT NOW() | 记录时间 |
#### biz.notes15 字段)
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `id` | BIGSERIAL | PK | 自增主键 |
| `site_id` | BIGINT | NOT NULL | 门店 ID |
| `user_id` | INTEGER | NOT NULL | 小程序用户 ID |
| `target_type` | VARCHAR(50) | NOT NULL | 目标类型(如 `member` |
| `target_id` | BIGINT | NOT NULL | 目标 ID如 member_id |
| `type` | VARCHAR(20) | NOT NULL DEFAULT 'normal' | 备注类型:`normal` / `follow_up` / `abandon_reason` |
| `content` | TEXT | NOT NULL | 备注内容 |
| `rating_service_willingness` | SMALLINT | CHECK (1-5),可空 | 再次服务意愿评分 |
| `rating_revisit_likelihood` | SMALLINT | CHECK (1-5),可空 | 再来店可能性评分 |
| `task_id` | BIGINT | FK → `biz.coach_tasks(id)`,可空 | 关联任务 |
| `ai_score` | SMALLINT | 可空 | AI 应用 6 评分P5 实现) |
| `ai_analysis` | TEXT | 可空 | AI 分析结果P5 实现) |
| `created_at` | TIMESTAMPTZ | DEFAULT NOW() | 创建时间 |
| `updated_at` | TIMESTAMPTZ | DEFAULT NOW() | 更新时间 |
| `score` | SMALLINT | CHECK (1-5),可空 | 备注星星评分,助教创建备注时可选填写,不参与 AI 分析RNS1.1 新增) |
#### biz.trigger_jobs12 字段)
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `id` | SERIAL | PK | 自增主键 |
| `job_type` | VARCHAR(100) | NOT NULL | 任务类型标识,映射到 Python handler`_JOB_REGISTRY` 注册键) |
| `job_name` | VARCHAR(100) | NOT NULL, UNIQUE | 任务名称(唯一标识,如 `task_generator` |
| `trigger_condition` | VARCHAR(20) | NOT NULL | 触发方式:`cron` / `interval` / `event` |
| `trigger_config` | JSONB | NOT NULL | 触发配置cron 表达式 / 间隔秒数 / 事件名) |
| `last_run_at` | TIMESTAMPTZ | 可空 | 上次运行时间 |
| `next_run_at` | TIMESTAMPTZ | 可空 | 下次运行时间event 类型为 NULL |
| `status` | VARCHAR(20) | NOT NULL DEFAULT 'enabled' | 状态:`enabled` / `disabled` |
| `description` | TEXT | 可空 | 任务中文描述管理后台页面展示P23 新增) |
| `last_error` | TEXT | 可空 | 最后一次执行异常的错误信息,成功后清空为 NULLP23 新增) |
| `last_stats` | JSONB | 可空 | 最近一次执行的统计结果 JSON`{"created":5,"replaced":2,"skipped":10,"transferred":1}`P18 新增) |
| `created_at` | TIMESTAMPTZ | DEFAULT NOW() | 创建时间 |
#### biz.cfg_task_generator_params7 字段P17+P18
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `id` | BIGSERIAL | PK | 自增主键 |
| `site_id` | BIGINT | 可空 | NULL=全局默认非NULL=门店级覆盖 |
| `param_key` | VARCHAR(64) | NOT NULL | 参数键名 |
| `param_value` | NUMERIC | NOT NULL | 参数值 |
| `description` | TEXT | 可空 | 参数说明 |
| `updated_at` | TIMESTAMPTZ | NOT NULL DEFAULT NOW() | 更新时间 |
| `updated_by` | BIGINT | 可空 | 最近修改人 user_id用于审计追溯P18 新增) |
继承链:代码默认 → 全局默认site_id IS NULL→ 门店覆盖site_id = ?
#### biz.coach_task_transfer_log11 字段P17 新增)
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `id` | BIGSERIAL | PK | 自增主键 |
| `site_id` | BIGINT | NOT NULL | 门店 ID |
| `member_id` | BIGINT | NOT NULL | 客户 ID |
| `from_assistant_id` | BIGINT | NOT NULL | 原助教 ID |
| `to_assistant_id` | BIGINT | NOT NULL | 新助教 ID |
| `from_task_id` | BIGINT | NOT NULL, FK → `biz.coach_tasks(id)` | 原任务 ID |
| `to_task_id` | BIGINT | FK → `biz.coach_tasks(id)`,可空 | 新任务 ID |
| `transfer_reason` | TEXT | 可空 | 转移原因描述 |
| `guard_checks` | JSONB | 可空 | 三重保护检查结果快照 |
| `transfer_score` | NUMERIC | 可空 | 转移候选得分 |
| `created_at` | TIMESTAMPTZ | NOT NULL DEFAULT NOW() | 创建时间 |
### 约束与索引
| 表 | 约束/索引名 | 类型 | 说明 |
|----|-----------|------|------|
| `coach_tasks` | `idx_coach_tasks_site_assistant_member_type` | UNIQUE INDEX (partial) | `(site_id, assistant_id, member_id, task_type) WHERE status = 'active'`,保证同一组合下活跃任务最多一条 |
| `coach_tasks` | `idx_coach_tasks_assistant_status` | INDEX | `(site_id, assistant_id, status)`,助教任务列表查询加速 |
| `coach_tasks` | FK `parent_task_id` | FK | → `biz.coach_tasks(id)`,自引用 |
| `coach_tasks` | FK `fk_coach_tasks_transferred_from` | FK | → `biz.coach_tasks(id)`转移来源任务P17 新增) |
| `coach_task_history` | FK `task_id` | FK | → `biz.coach_tasks(id)` |
| `notes` | `idx_notes_target` | INDEX | `(site_id, target_type, target_id)`,按目标查询备注加速 |
| `notes` | CHECK `rating_service_willingness` | CHECK | `BETWEEN 1 AND 5` |
| `notes` | CHECK `rating_revisit_likelihood` | CHECK | `BETWEEN 1 AND 5` |
| `notes` | CHECK `score` | CHECK | `score IS NULL OR (score >= 1 AND score <= 5)`RNS1.1 新增) |
| `notes` | FK `task_id` | FK | → `biz.coach_tasks(id)` |
| `trigger_jobs` | UNIQUE `job_name` | UNIQUE | 触发器名称唯一 |
| `cfg_task_generator_params` | UNIQUE `(site_id, param_key)` | UNIQUE | 全局+门店级参数唯一约束P17 新增) |
| `coach_task_transfer_log` | FK `from_task_id` | FK | → `biz.coach_tasks(id)`P17 新增) |
| `coach_task_transfer_log` | FK `to_task_id` | FK | → `biz.coach_tasks(id)`P17 新增) |
| `coach_task_transfer_log` | `idx_transfer_log_site_created` | INDEX | `(site_id, created_at DESC)`P17 新增) |
| `coach_task_transfer_log` | `idx_transfer_log_member` | INDEX | `(member_id, created_at DESC)`P17 新增) |
### P17 种子数据13 条任务引擎参数)
| param_key | param_value | description |
|-----------|-------------|-------------|
| `high_priority_recall_threshold` | 7.0 | max(WBI,NCI) 超过此值生成高优先召回 |
| `priority_recall_threshold` | 5.0 | max(WBI,NCI) 超过此值生成优先召回 |
| `rs_min_for_relationship` | 1.0 | RS ≤ 此值不生成关系构建 |
| `rs_max_for_relationship` | 6.0 | RS ≥ 此值不生成关系构建 |
| `consecutive_recall_fail_cycles` | 3 | 连续失败多少轮触发客户转移 |
| `min_wbi_for_transfer` | 5.0 | WBI 低于此值不触发转移 |
| `guard_assistant_coverage_ratio` | 0.5 | 绑定率低于此值禁用转移 |
| `guard_new_assistant_days` | 10 | 新助教入驻保护天数 |
| `transfer_score_w_rs` | 0.5 | 转移候选排序RS 权重 |
| `transfer_score_w_ms` | 0.3 | 转移候选排序MS 权重 |
| `transfer_score_w_ml` | 0.2 | 转移候选排序ML 权重 |
| `max_transfer_count` | 2 | 单客户最大累计转移次数 |
| `follow_up_visit_retention_hours` | 48 | 回访任务最低保留时长(小时) |
> 所有参数 `site_id IS NULL`(全局默认),门店可通过插入 `site_id = ?` 的行覆盖。
### 种子数据4 条触发器配置)
| job_name | job_type | trigger_condition | trigger_config | next_run_at | 说明 |
|----------|----------|-------------------|----------------|-------------|------|
| `task_generator` | `task_generator` | `cron` | `{"cron_expression": "0 4 * * *"}` | 次日 04:00 | 每日凌晨 4:00 运行任务生成器 |
| `task_expiry_check` | `task_expiry_check` | `interval` | `{"interval_seconds": 3600}` | NOW() + 1h | 每小时检查过期任务 |
| `recall_completion_check` | `recall_completion_check` | `event` | `{"event_name": "etl_data_updated"}` | NULL | ETL 数据更新后触发召回完成检测 |
| `note_reclassify_backfill` | `note_reclassify_backfill` | `event` | `{"event_name": "recall_completed"}` | NULL | 召回完成后触发备注回溯重分类 |
---
## 2. 兼容性影响
| 组件 | 影响 |
|------|------|
| ETL 任务 | 无直接影响。`biz` Schema 表不参与 ETL 流程,但任务生成器通过 FDW 只读访问 ETL 库的 WBI/NCI/RS 指数数据 |
| 后端 API | 直接依赖。FastAPI 后端将基于这些表实现任务 CRUD`/api/xcx/tasks`)、备注 CRUD`/api/xcx/notes`、触发器调度等功能。P17 新增:`task_generator.py` 完全重写,入口改为 OS 归属对;`fdw_queries.py` 新增 4 个批量查询方法 |
| 小程序 | 间接依赖。小程序通过后端 API 间接使用任务列表、备注功能 |
| 管理后台 | P18 已实施。`admin_task_engine` router 提供 9 个端点(转移日志分页+历史、待审核任务分页+重新分配+关闭、参数管理 CRUD前端 3 个页面TransferLog/PendingReview/TaskEngineConfig通过 `taskEngine.ts` API 层调用 |
| FDW 配置 | 无影响。`fdw_etl` Schema 独立于 `biz`,任务生成器和召回检测器通过 FDW 只读查询 ETL 库 |
| `auth` Schema | 间接依赖。P17 仍通过 `auth.user_assistant_binding` 获取 site_ids 和助教绑定信息(转移保护检查) |
| `public` Schema | 无影响。`member_retention_clue` 表独立于本次变更 |
| 现有 `biz` Schema | 兼容。`coach_tasks` 新增 3 字段均有默认值(`transfer_count DEFAULT 0`,其余可空),不影响现有查询。新增 `transferred`/`pending_review` 状态值7 个下游模块均使用显式 status 过滤,不会误匹配 |
---
## 2.1 RNS1.3 看板接口引用说明2026-03-20 补充)
RNS1.3三看板接口BOARD-1 助教看板的任务维度查询引用了 `biz.coach_tasks`(无 schema 变更,仅新增读取路径):
| 表 | 引用接口 | 用途 |
|----|---------|------|
| `biz.coach_tasks` | BOARD-1 `_query_coach_tasks()` | 按 `site_id` + `assistant_id` + 日期范围查询任务完成数,按 `task_type` 分类统计 `recall`(召回类:`high_priority_recall` / `priority_recall`)和 `callback`(回访类:`follow_up_visit` / `relationship_building`),筛选条件 `status = 'completed'` + `completed_at BETWEEN start_date AND end_date` |
查询模式:
```sql
SELECT assistant_id, task_type, COUNT(*) AS cnt
FROM biz.coach_tasks
WHERE site_id = :site_id
AND assistant_id = ANY(:assistant_ids)
AND status = 'completed'
AND completed_at BETWEEN :start_date AND :end_date
GROUP BY assistant_id, task_type
```
> 该查询走 `idx_coach_tasks_assistant_status` 索引(`site_id, assistant_id, status`),无需新增索引。
---
## 2.2 RNS1.2 接口引用说明2026-03-18 补充)
RNS1.2(客户与助教接口)新增 3 个端点,引用了以下 biz/public 表(无 schema 变更,仅新增读取路径):
| 表 | 引用接口 | 用途 |
|----|---------|------|
| `biz.coach_tasks` | CUST-1 `coachTasks` 模块、COACH-1 `visibleTasks`/`hiddenTasks`/`abandonedTasks`/`tasksCompleted` | 查询客户关联的助教任务(按 `member_id`);查询助教任务分组(按 `assistant_id``status` 分组);统计当月完成任务数 |
| `biz.notes` | CUST-1 `notes` 模块、COACH-1 `notes`/任务备注 | 查询客户备注(`target_type='member'`,最多 20 条);查询助教相关备注(最多 20 条);查询任务关联备注(`task_id` 关联) |
| `biz.ai_cache` | CUST-1 `aiInsight` 模块 | 查询 AI 分析缓存(`cache_type='app4_analysis'``target_id=customerId`),解析 `cache_value` JSON 生成洞察摘要和策略建议 |
| `public.member_retention_clue` | CUST-1 `retentionClues` 模块 | 查询维客线索(按 `created_at` 倒序),格式与 TASK-2 一致 |
> 以上均为只读查询,不涉及表结构变更。`biz.ai_cache` 表由 P5AI 集成)创建,`public.member_retention_clue` 表由独立迁移创建。
---
## 3. 回滚策略
### P4 回滚(原始 4 张表)
按逆序 `DROP TABLE IF EXISTS CASCADE`(迁移脚本末尾已包含注释形式的回滚语句):
```sql
-- 先删除种子数据(如需保留表结构)
DELETE FROM biz.trigger_jobs
WHERE job_name IN (
'task_generator',
'task_expiry_check',
'recall_completion_check',
'note_reclassify_backfill'
);
-- 删除索引
DROP INDEX IF EXISTS biz.idx_notes_target;
DROP INDEX IF EXISTS biz.idx_coach_tasks_assistant_status;
DROP INDEX IF EXISTS biz.idx_coach_tasks_site_assistant_member_type;
-- 删除表按逆序CASCADE 处理外键依赖)
DROP TABLE IF EXISTS biz.trigger_jobs CASCADE;
DROP TABLE IF EXISTS biz.notes CASCADE;
DROP TABLE IF EXISTS biz.coach_task_history CASCADE;
DROP TABLE IF EXISTS biz.coach_tasks CASCADE;
```
### P17 回滚(增量变更)
```sql
-- 1. 删除 P17 种子数据
DELETE FROM biz.cfg_task_generator_params WHERE site_id IS NULL;
-- 2. 删除 P17 新增表
DROP TABLE IF EXISTS biz.coach_task_transfer_log;
DROP TABLE IF EXISTS biz.cfg_task_generator_params;
-- 3. 删除 P17 新增字段
ALTER TABLE biz.coach_tasks DROP COLUMN IF EXISTS transfer_count;
ALTER TABLE biz.coach_tasks DROP COLUMN IF EXISTS transferred_from;
ALTER TABLE biz.coach_tasks DROP COLUMN IF EXISTS transferred_at;
-- 注意enum 值transferred/pending_review一旦添加无法直接删除需重建类型
```
### P18 回滚(字段扩展)
```sql
-- 删除 P18 新增字段
ALTER TABLE biz.trigger_jobs DROP COLUMN IF EXISTS last_stats;
ALTER TABLE biz.cfg_task_generator_params DROP COLUMN IF EXISTS updated_by;
```
注意:
- `CASCADE` 会级联删除依赖对象(外键引用的子表数据)
- 如果表中已有业务数据,需先备份再执行回滚
- 回滚不会删除 `biz` Schema 本身(由 P1 创建,其他表可能依赖)
---
## 4. 验证 SQL
```sql
-- 1. 验证 biz Schema 下 6 张业务表全部存在
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'biz'
AND table_name IN ('coach_tasks', 'coach_task_history', 'notes', 'trigger_jobs',
'cfg_task_generator_params', 'coach_task_transfer_log')
ORDER BY table_name;
-- 预期:返回 6 行
-- 2. 验证 coach_tasks 表字段数量P4 原 15 + P17 新增 3 = 18
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_schema = 'biz' AND table_name = 'coach_tasks'
ORDER BY ordinal_position;
-- 预期:返回 18 行,包含 transfer_count/transferred_from/transferred_at
-- 3. 验证 P17 新增字段的默认值和约束
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_schema = 'biz' AND table_name = 'coach_tasks'
AND column_name IN ('transfer_count', 'transferred_from', 'transferred_at');
-- 预期transfer_count: integer, NOT NULL, DEFAULT 0
-- transferred_from: bigint, YES (nullable)
-- transferred_at: timestamp with time zone, YES (nullable)
-- 4. 验证 cfg_task_generator_params 表结构
SELECT column_name, data_type
FROM information_schema.columns
WHERE table_schema = 'biz' AND table_name = 'cfg_task_generator_params'
ORDER BY ordinal_position;
-- 预期6 行id, site_id, param_key, param_value, description, updated_at
-- 5. 验证 P17 种子数据13 条全局默认参数)
SELECT param_key, param_value, description
FROM biz.cfg_task_generator_params
WHERE site_id IS NULL
ORDER BY param_key;
-- 预期:返回 13 行
-- 6. 验证 coach_task_transfer_log 表结构
SELECT column_name, data_type
FROM information_schema.columns
WHERE table_schema = 'biz' AND table_name = 'coach_task_transfer_log'
ORDER BY ordinal_position;
-- 预期11 行id, site_id, member_id, from_assistant_id, to_assistant_id,
-- from_task_id, to_task_id, transfer_reason, guard_checks, transfer_score, created_at
-- 7. 验证 P17 新增索引
SELECT indexname
FROM pg_indexes
WHERE schemaname = 'biz'
AND indexname IN ('idx_transfer_log_site_created', 'idx_transfer_log_member')
ORDER BY indexname;
-- 预期:返回 2 行
-- 8. 验证 transferred_from 外键约束
SELECT conname
FROM pg_constraint
WHERE conrelid = 'biz.coach_tasks'::regclass
AND conname = 'fk_coach_tasks_transferred_from';
-- 预期:返回 1 行
-- 9. 验证部分唯一索引存在P4 原有)
SELECT indexname, indexdef
FROM pg_indexes
WHERE schemaname = 'biz' AND indexname = 'idx_coach_tasks_site_assistant_member_type';
-- 预期:返回 1 行indexdef 包含 "WHERE ((status)::text = 'active'::text)"
-- 10. 验证种子数据4 条触发器配置P4 原有)
SELECT job_name, job_type, trigger_condition
FROM biz.trigger_jobs
WHERE job_name IN ('task_generator', 'task_expiry_check', 'recall_completion_check', 'note_reclassify_backfill')
ORDER BY job_name;
-- 预期:返回 4 行
-- 11. 验证 P18 新增字段trigger_jobs.last_stats
SELECT column_name, data_type, is_nullable
FROM information_schema.columns
WHERE table_schema = 'biz' AND table_name = 'trigger_jobs' AND column_name = 'last_stats';
-- 预期:返回 1 行data_type = 'jsonb'is_nullable = 'YES'
-- 12. 验证 P18 新增字段cfg_task_generator_params.updated_by
SELECT column_name, data_type, is_nullable
FROM information_schema.columns
WHERE table_schema = 'biz' AND table_name = 'cfg_task_generator_params' AND column_name = 'updated_by';
-- 预期:返回 1 行data_type = 'bigint'is_nullable = 'YES'
```