微信小程序页面迁移校验之前 P5任务处理之前

This commit is contained in:
Neo
2026-03-09 01:19:21 +08:00
parent 263bf96035
commit 6e20987d2f
1112 changed files with 153824 additions and 219694 deletions

View File

@@ -0,0 +1,166 @@
# BD_Manualbiz Schema AI 表(对话记录 + 消息 + 缓存)
> 目标库:`test_zqyy_app`(通过 `APP_DB_DSN` 连接)
> 迁移脚本:`db/zqyy_app/migrations/2026-03-08__create_ai_tables.sql`
> 关联 SPEC`05-miniapp-ai-integration`P5 AI 集成层)
---
## 1. 变更说明
### 新增表3 张)
| # | 表名 | 用途 | 字段数 |
|---|------|------|--------|
| 1 | `biz.ai_conversations` | AI 对话记录:每次 AI 调用(用户主动或系统自动)创建一条 | 8 |
| 2 | `biz.ai_messages` | AI 消息记录:对话中的每条消息(输入/输出/系统) | 6 |
| 3 | `biz.ai_cache` | AI 应用缓存:各应用的结构化输出结果 | 9 |
### 表字段明细
#### biz.ai_conversations8 字段)
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `id` | BIGSERIAL | PK | 自增主键 |
| `user_id` | VARCHAR(50) | NOT NULL | 用户 ID系统自动调用时为 `system` |
| `nickname` | VARCHAR(100) | NOT NULL DEFAULT '' | 用户昵称 |
| `app_id` | VARCHAR(30) | NOT NULL | 应用标识app1_chat / app2_finance / app3_clue / app4_analysis / app5_tactics / app6_note / app7_customer / app8_consolidation |
| `site_id` | BIGINT | NOT NULL | 门店 ID多门店隔离 |
| `source_page` | VARCHAR(100) | 可空 | 来源页面标识 |
| `source_context` | JSONB | 可空 | 页面上下文 JSON |
| `created_at` | TIMESTAMPTZ | NOT NULL DEFAULT NOW() | 创建时间 |
#### biz.ai_messages6 字段)
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `id` | BIGSERIAL | PK | 自增主键 |
| `conversation_id` | BIGINT | NOT NULL, FK → `biz.ai_conversations(id)` ON DELETE CASCADE | 关联对话 |
| `role` | VARCHAR(10) | NOT NULL, CHECK IN ('user', 'assistant', 'system') | 消息角色 |
| `content` | TEXT | NOT NULL | 消息内容 |
| `tokens_used` | INTEGER | 可空 | 本条消息消耗的 token 数 |
| `created_at` | TIMESTAMPTZ | NOT NULL DEFAULT NOW() | 创建时间 |
#### biz.ai_cache9 字段)
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `id` | BIGSERIAL | PK | 自增主键 |
| `cache_type` | VARCHAR(30) | NOT NULL, CHECK 7 个枚举值 | 缓存类型(见下方枚举) |
| `site_id` | BIGINT | NOT NULL | 门店 ID多门店隔离 |
| `target_id` | VARCHAR(100) | NOT NULL | 目标 ID含义因 cache_type 而异 |
| `result_json` | JSONB | NOT NULL | 结构化输出结果 |
| `score` | INTEGER | 可空 | 评分:仅应用 6 使用1-10 分) |
| `triggered_by` | VARCHAR(100) | 可空 | 触发来源标识 |
| `created_at` | TIMESTAMPTZ | NOT NULL DEFAULT NOW() | 创建时间 |
| `expires_at` | TIMESTAMPTZ | 可空 | 可选过期时间 |
### cache_type 枚举值与 target_id 约定
| cache_type | target_id 格式 | 示例 |
|---|---|---|
| `app2_finance` | 时间维度编码 | `this_month`, `last_week` |
| `app3_clue` | member_id | `12345` |
| `app4_analysis` | `{assistant_id}_{member_id}` | `100_12345` |
| `app5_tactics` | `{assistant_id}_{member_id}` | `100_12345` |
| `app6_note_analysis` | member_id | `12345` |
| `app7_customer_analysis` | member_id | `12345` |
| `app8_clue_consolidated` | member_id | `12345` |
### 约束与索引
| 表 | 约束/索引名 | 类型 | 说明 |
|----|-----------|------|------|
| `ai_conversations` | `idx_ai_conv_user_site` | INDEX | `(user_id, site_id, created_at DESC)` — 用户历史对话列表查询 |
| `ai_conversations` | `idx_ai_conv_app_site` | INDEX | `(app_id, site_id, created_at DESC)` — 按应用查询对话 |
| `ai_messages` | FK `conversation_id` | FK | → `biz.ai_conversations(id)` ON DELETE CASCADE |
| `ai_messages` | `chk_ai_msg_role` | CHECK | `role IN ('user', 'assistant', 'system')` |
| `ai_messages` | `idx_ai_msg_conv` | INDEX | `(conversation_id, created_at)` — 对话消息列表 |
| `ai_cache` | `chk_ai_cache_type` | CHECK | 7 个枚举值 |
| `ai_cache` | `idx_ai_cache_lookup` | INDEX | `(cache_type, site_id, target_id, created_at DESC)` — 查询最新缓存 |
| `ai_cache` | `idx_ai_cache_cleanup` | INDEX | `(cache_type, site_id, target_id, created_at)` — 清理超限记录ASC 排序便于删除最旧) |
---
## 2. 兼容性影响
| 组件 | 影响 |
|------|------|
| ETL 任务 | 无影响。AI 表属于 `biz` Schema不参与 ETL 流程 |
| 后端 API | 直接依赖。P5 AI 模块(`apps/backend/app/ai/`将基于这三张表实现对话持久化、缓存读写、SSE 流式对话等功能 |
| 小程序 | 间接依赖。小程序通过后端 AI API 间接使用对话和缓存数据 |
| 管理后台 | 暂无影响。后续可能增加 AI 调用统计和缓存管理界面 |
| `member_retention_clue` | 间接关联。App8维客线索整理的结果同时写入 `ai_cache``member_retention_clue` 表 |
| 现有 `biz` Schema | 兼容。仅新增 3 张表不修改已有对象coach_tasks、notes、trigger_jobs 等) |
---
## 3. 回滚策略
按逆序 DROPCASCADE 处理外键依赖):
```sql
-- 删除索引
DROP INDEX IF EXISTS biz.idx_ai_cache_cleanup;
DROP INDEX IF EXISTS biz.idx_ai_cache_lookup;
DROP INDEX IF EXISTS biz.idx_ai_msg_conv;
DROP INDEX IF EXISTS biz.idx_ai_conv_app_site;
DROP INDEX IF EXISTS biz.idx_ai_conv_user_site;
-- 删除表ai_messages 依赖 ai_conversations需先删或用 CASCADE
DROP TABLE IF EXISTS biz.ai_cache CASCADE;
DROP TABLE IF EXISTS biz.ai_messages CASCADE;
DROP TABLE IF EXISTS biz.ai_conversations CASCADE;
```
注意:
- `ai_messages` 通过 FK 依赖 `ai_conversations`CASCADE 会级联删除消息
- 如果表中已有数据,需先备份再执行回滚
- 回滚不会删除 `biz` Schema 本身
---
## 4. 验证 SQL
```sql
-- 1. 验证 3 张 AI 表全部存在
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'biz'
AND table_name IN ('ai_conversations', 'ai_messages', 'ai_cache')
ORDER BY table_name;
-- 预期:返回 3 行ai_cache, ai_conversations, ai_messages
-- 2. 验证 ai_conversations 字段数量和关键字段
SELECT column_name, data_type, is_nullable
FROM information_schema.columns
WHERE table_schema = 'biz' AND table_name = 'ai_conversations'
ORDER BY ordinal_position;
-- 预期:返回 8 行,包含 id/user_id/nickname/app_id/site_id/source_page/source_context/created_at
-- 3. 验证 ai_messages 的外键和 CHECK 约束
SELECT conname, contype, pg_get_constraintdef(oid) AS constraint_def
FROM pg_constraint
WHERE conrelid = 'biz.ai_messages'::regclass
ORDER BY conname;
-- 预期:包含 chk_ai_msg_roleCHECK role IN ...)和 ai_messages_conversation_id_fkeyFK → ai_conversations
-- 4. 验证 ai_cache 的 CHECK 约束7 个枚举值)
SELECT conname, pg_get_constraintdef(oid) AS constraint_def
FROM pg_constraint
WHERE conrelid = 'biz.ai_cache'::regclass AND contype = 'c';
-- 预期:返回 1 行 chk_ai_cache_type包含 7 个枚举值
-- 5. 验证索引全部存在5 个)
SELECT indexname
FROM pg_indexes
WHERE schemaname = 'biz'
AND indexname IN (
'idx_ai_conv_user_site', 'idx_ai_conv_app_site',
'idx_ai_msg_conv',
'idx_ai_cache_lookup', 'idx_ai_cache_cleanup'
)
ORDER BY indexname;
-- 预期:返回 5 行
```