feat: chat integration, tenant admin spec, backend chat service, miniprogram updates, DEMO moved to tmp, XCX-TEST removed, migrations & docs

This commit is contained in:
Neo
2026-03-20 09:02:10 +08:00
parent 3d2e5f8165
commit beb88d5bea
388 changed files with 6436 additions and 25458 deletions

View File

@@ -2,7 +2,7 @@
> 本文档记录项目中所有文档资产的位置、类型和内容概要,方便快速定位。
> 归档规则见末尾「文档归档规则」章节;程序输出路径规范见 `docs/deployment/EXPORT-PATHS.md`。
> 最后更新2026-03-19RNS1.3 三看板接口文档补充
> 最后更新2026-03-20RNS1.4 CHAT 模块重建 + FDW→直连统一 + R3 筛选修复 审计收口
---
@@ -55,7 +55,7 @@
| `README.md` | 审计目录说明 |
| `SESSION-LOG-GUIDE.md` | Session 日志使用指南:索引字段说明、查询方法、典型场景、与其他审计产物的关系 |
| `AUDIT-HOOKS-GUIDE.md` | 审计 Hooks 使用指南Hook 触发机制、配置方式、与审计流程的集成 |
| `changes/` | 56 份变更审计文档(`YYYY-MM-DD__<slug>.md` 格式),每份包含:变更原因、影响范围、回滚策略、验证 SQL |
| `changes/` | 67 份变更审计文档(`YYYY-MM-DD__<slug>.md` 格式),每份包含:变更原因、影响范围、回滚策略、验证 SQL |
| `prompt_logs/` | Prompt 日志(`prompt_log_YYYYMMDD_HHMMSS.md`),记录每次 AI 交互的输入输出 |
| `session_logs/` | 全量会话记录(按 `YYYY-MM/DD/` 分层),含双索引(`_session_index.json` / `_session_index_full.json`)、每轮 execution 的完整 Markdown 记录、LLM 生成的操作摘要 |
@@ -209,6 +209,14 @@ RNS1.3 新增模块(三看板接口):
| `app/schemas/xcx_board.py` | 看板相关 Pydantic Schema7 枚举 + ~40 响应 Schema |
| `app/schemas/xcx_config.py` | 配置相关 Pydantic SchemaSkillTypeItem |
RNS1.4 新增模块CHAT 对齐与联调收尾):
| 路径 | 内容 |
|------|------|
| `app/routers/xcx_chat.py` | CHAT 端点CHAT-1 对话历史、CHAT-2a/2b 消息查询、CHAT-3 发送消息、CHAT-4 SSE 流式(替代原 `xcx_ai_chat.py`,路径从 `/api/ai/*` 迁移到 `/api/xcx/chat/*` |
| `app/services/chat_service.py` | CHAT 业务逻辑对话管理、消息持久化、对话复用规则、referenceCard 组装、标题生成 |
| `app/schemas/xcx_chat.py` | CHAT 相关 Pydantic SchemaChatHistoryResponse、ChatMessagesResponse、ReferenceCard、SendMessageResponse、ChatStreamRequest 等) |
Monorepo 级属性测试(`tests/`
| 路径 | 内容 |
@@ -216,6 +224,12 @@ Monorepo 级属性测试(`tests/`
| `tests/test_rns12_properties.py` | RNS1.2 属性测试14 个 PropertyHypothesis 框架) |
| `tests/test_board_properties.py` | RNS1.3 属性测试18 个测试函数12 个 PropertyHypothesis 框架) |
| `tests/test_board_service_unit.py` | RNS1.3 看板工具函数单元测试 |
| `tests/test_rns1_chat_title_properties.py` | RNS1.4 属性测试标题生成优先级Property 4Hypothesis 框架) |
| `tests/test_rns1_chat_reuse_properties.py` | RNS1.4 属性测试对话复用规则Property 6 |
| `tests/test_rns1_chat_reference_card_properties.py` | RNS1.4 属性测试referenceCard round tripProperty 7 |
| `tests/test_rns1_chat_persistence_properties.py` | RNS1.4 属性测试消息持久化与会话元数据更新Property 8 |
| `tests/test_rns1_chat_sse_properties.py` | RNS1.4 属性测试SSE 事件类型有效性Property 9 |
| `tests/test_rns1_chat_ordering_properties.py` | RNS1.4 属性测试列表排序不变量Property 3 |
### 3.2 ETL Connector `apps/etl/connectors/feiqiu/`
@@ -382,6 +396,7 @@ Monorepo 级属性测试(`tests/`
| `rns1-task-performance-api` | RNS1.1 任务与绩效接口TASK-1 扩展、TASK-2、PERF-1、PERF-2、前端适配 |
| `rns1-customer-coach-api` | RNS1.2 客户与助教接口CUST-1 客户详情、CUST-2 客户服务记录、COACH-1 助教详情) |
| `rns1-board-apis` | RNS1.3 三看板接口BOARD-1 助教看板、BOARD-2 客户看板、BOARD-3 财务看板、CONFIG-1 技能类型) |
| `rns1-chat-integration` | RNS1.4 CHAT 对齐与联调收尾CHAT-1/2/3/4 路径迁移、对话复用、referenceCard、SSE 流式、FDW 验证、13 页面联调) |
| `spi-spending-power-index` | SPI 消费力指数 |
---

View File

@@ -111,20 +111,20 @@ graph TB
| 服务文件 | 职责 | 数据源 |
|----------|------|--------|
| `task_manager.py` | 任务 CRUD、`get_task_list_v2()``get_task_detail()` | biz.coach_tasks + FDW |
| `performance_service.py` | `get_overview()``get_records()`,绩效汇总与明细 | FDW |
| `task_manager.py` | 任务 CRUD、`get_task_list_v2()``get_task_detail()` | biz.coach_tasks + ETL 直连 |
| `performance_service.py` | `get_overview()``get_records()`,绩效汇总与明细 | ETL 直连 |
| `note_service.py` | 备注创建(含 score、AI 占位、回访触发 | biz.notes |
| `customer_service.py` | 客户详情(CUST-1)、客户服务记录(CUST-2)RNS1.2 新增) | biz.coach_tasks + biz.ai_cache + biz.notes + public.member_retention_clue + FDW |
| `coach_service.py` | 助教详情(COACH-1):绩效/收入/任务分组/TOP客户/历史月份RNS1.2 新增) | biz.coach_tasks + biz.notes + FDW |
| `customer_service.py` | 客户详情(CUST-1)、客户服务记录(CUST-2)RNS1.2 新增) | biz.coach_tasks + biz.ai_cache + biz.notes + public.member_retention_clue + ETL 直连 |
| `coach_service.py` | 助教详情(COACH-1):绩效/收入/任务分组/TOP客户/历史月份RNS1.2 新增) | biz.coach_tasks + biz.notes + ETL 直连 |
| `fdw_queries.py` | ETL RLS 视图查询集中封装,直连 ETL 库 + `SET LOCAL app.current_site_id` 门店隔离 | app.v_* (ETL 直连) |
| `task_generator.py` | 定时任务:基于 WBI/NCI/RS 指数自动生成助教任务 | biz + FDW |
| `task_generator.py` | 定时任务:基于 WBI/NCI/RS 指数自动生成助教任务 | biz + ETL 直连 |
| `task_expiry.py` | 定时任务:检测过期任务并标记 inactive | biz.coach_tasks |
| `recall_detector.py` | 事件驱动ETL 数据更新后检测召回完成 | biz + FDW |
| `recall_detector.py` | 事件驱动ETL 数据更新后检测召回完成 | biz + ETL 直连 |
| `note_reclassifier.py` | 事件驱动:召回完成后回溯重分类备注 | biz.notes |
| `wechat.py` | 微信 code2session、Token 管理 | 外部 API |
| `role.py` | 角色权限查询 | auth.* |
| `scheduler.py` | 触发器调度引擎 | biz.trigger_jobs |
| `board_service.py` | 三看板编排:`get_coach_board()``get_customer_board()``get_finance_board()`,含日期范围/环比/排序/分页/降级RNS1.3 新增) | FDW + biz.coach_tasks |
| `board_service.py` | 三看板编排:`get_coach_board()``get_customer_board()``get_finance_board()`,含日期范围/环比/排序/分页/降级RNS1.3 新增) | ETL 直连 + biz.coach_tasks |
| `application.py` | 入驻申请处理 | auth.applications |
## 4. FDW 查询封装fdw_queries.py
@@ -156,7 +156,7 @@ graph TB
| `get_all_assistants()` | BOARD-1按技能筛选助教列表RNS1.3 新增) | v_dim_assistant |
| `get_salary_calc_batch()` | BOARD-1批量查询当期/上期绩效RNS1.3 新增) | items_sum + assistant_pd/cx_money |
| `get_top_customers_for_coaches()` | BOARD-1按亲密度 Top3 客户 + 四级 emojiRNS1.3 新增) | DQ-6 + v_dws_member_assistant_relation_index |
| `get_coach_sv_data()` | BOARD-1助教客源储值数据RNS1.3 新增) | v_dws_assistant_monthly_summary |
| `get_coach_sv_data()` | BOARD-1助教客源储值数据RNS1.3 新增) | v_dws_assistant_recharge_commission |
| `get_customer_board_recall()` | BOARD-2召回维度WBI 降序RNS1.3 新增) | v_dws_member_winback_index + DQ-6 |
| `get_customer_board_potential()` | BOARD-2潜力维度SPI 降序RNS1.3 新增) | v_dws_member_spending_power_index |
| `get_customer_board_balance()` | BOARD-2余额维度RNS1.3 新增) | v_dim_member_card_account + DQ-7 |

View File

@@ -1,12 +1,17 @@
# 审计一览表
> 自动生成于 2026-03-19 18:47:42,请勿手动编辑。
> 自动生成于 2026-03-20 07:28:24,请勿手动编辑。
## 时间线视图
| 日期 | 项目 | 需求摘要 | 变更类型 | 影响模块 | 风险 | 详情 |
|------|------|----------|----------|----------|------|------|
| 2026-03-20 | 项目级 | H2 修复FDW → 直连 ETL 架构统一 | bugfix | 其他 | 未知 | [链接](changes/2026-03-20__h2-fdw-to-direct-etl-unification.md) |
| 2026-03-20 | ETL-feiqiu, 后端 | 变更审计记录R3 项目类型筛选接口重建fetchSkillTypes / cfg_area_category | bugfix | 其他, 文档 | 高 | [链接](changes/2026-03-20__r3-skill-type-filter-rebuild.md) |
| 2026-03-20 | 项目级 | RNS1 系列 AI 自主决策风险审计报告(完整版) | bugfix | 其他 | 高 | [链接](changes/2026-03-20__rns1-ai-autonomous-decision-risk-audit.md) |
| 2026-03-20 | ETL-feiqiu, 后端, 项目级 | 变更审计记录RNS1.3 三看板 FDW 查询层数据口径修复 | bugfix | 其他, 文档 | 未知 | [链接](changes/2026-03-20__rns13-board-apis-e2e-fix.md) |
| 2026-03-20 | 项目级 | 变更审计记录RNS1.4 CHAT 模块重建 + FDW→直连统一 + R3 筛选修复 | bugfix | 其他 | 未知 | [链接](changes/2026-03-20__rns14-chat-fdw-filter-audit.md) |
| 2026-03-20 | 项目级 | 变更审计记录RNS1.4 CHAT 模块迁移 + R3 项目类型筛选重建 | bugfix | 其他 | 高 | [链接](changes/2026-03-20__rns14-chat-module-r3-filter-rebuild.md) |
| 2026-03-19 | ETL-feiqiu, 后端 | 变更审计记录card_type_id 年卡/月卡映射文档同步 | bugfix | 其他, 文档 | 未知 | [链接](changes/2026-03-19__card-type-id-doc-sync.md) |
| 2026-03-19 | 项目级 | 变更审计记录coach_service 绩效档位硬编码修复 | bugfix | 其他 | 低 | [链接](changes/2026-03-19__coach-tier-hardcode-fix.md) |
| 2026-03-19 | ETL-feiqiu, 后端 | 变更审计记录助教等级映射硬编码修复P2-9 | bugfix | 其他, 文档 | 低 | [链接](changes/2026-03-19__level-map-hardcode-fix.md) |
@@ -75,6 +80,7 @@
| 日期 | 需求摘要 | 变更类型 | 影响模块 | 风险 | 详情 |
|------|----------|----------|----------|------|------|
| 2026-03-20 | 变更审计记录R3 项目类型筛选接口重建fetchSkillTypes / cfg_area_category | bugfix | 其他, 文档 | 高 | [链接](changes/2026-03-20__r3-skill-type-filter-rebuild.md) |
| 2026-03-20 | 变更审计记录RNS1.3 三看板 FDW 查询层数据口径修复 | bugfix | 其他, 文档 | 未知 | [链接](changes/2026-03-20__rns13-board-apis-e2e-fix.md) |
| 2026-03-19 | 变更审计记录card_type_id 年卡/月卡映射文档同步 | bugfix | 其他, 文档 | 未知 | [链接](changes/2026-03-19__card-type-id-doc-sync.md) |
| 2026-03-19 | 变更审计记录助教等级映射硬编码修复P2-9 | bugfix | 其他, 文档 | 低 | [链接](changes/2026-03-19__level-map-hardcode-fix.md) |
@@ -113,6 +119,7 @@
| 日期 | 需求摘要 | 变更类型 | 影响模块 | 风险 | 详情 |
|------|----------|----------|----------|------|------|
| 2026-03-20 | 变更审计记录R3 项目类型筛选接口重建fetchSkillTypes / cfg_area_category | bugfix | 其他, 文档 | 高 | [链接](changes/2026-03-20__r3-skill-type-filter-rebuild.md) |
| 2026-03-20 | 变更审计记录RNS1.3 三看板 FDW 查询层数据口径修复 | bugfix | 其他, 文档 | 未知 | [链接](changes/2026-03-20__rns13-board-apis-e2e-fix.md) |
| 2026-03-19 | 变更审计记录card_type_id 年卡/月卡映射文档同步 | bugfix | 其他, 文档 | 未知 | [链接](changes/2026-03-19__card-type-id-doc-sync.md) |
| 2026-03-19 | 变更审计记录助教等级映射硬编码修复P2-9 | bugfix | 其他, 文档 | 低 | [链接](changes/2026-03-19__level-map-hardcode-fix.md) |
@@ -154,7 +161,11 @@
| 日期 | 需求摘要 | 变更类型 | 影响模块 | 风险 | 详情 |
|------|----------|----------|----------|------|------|
| 2026-03-20 | H2 修复FDW → 直连 ETL 架构统一 | bugfix | 其他 | 未知 | [链接](changes/2026-03-20__h2-fdw-to-direct-etl-unification.md) |
| 2026-03-20 | RNS1 系列 AI 自主决策风险审计报告(完整版) | bugfix | 其他 | 高 | [链接](changes/2026-03-20__rns1-ai-autonomous-decision-risk-audit.md) |
| 2026-03-20 | 变更审计记录RNS1.3 三看板 FDW 查询层数据口径修复 | bugfix | 其他, 文档 | 未知 | [链接](changes/2026-03-20__rns13-board-apis-e2e-fix.md) |
| 2026-03-20 | 变更审计记录RNS1.4 CHAT 模块重建 + FDW→直连统一 + R3 筛选修复 | bugfix | 其他 | 未知 | [链接](changes/2026-03-20__rns14-chat-fdw-filter-audit.md) |
| 2026-03-20 | 变更审计记录RNS1.4 CHAT 模块迁移 + R3 项目类型筛选重建 | bugfix | 其他 | 高 | [链接](changes/2026-03-20__rns14-chat-module-r3-filter-rebuild.md) |
| 2026-03-19 | 变更审计记录coach_service 绩效档位硬编码修复 | bugfix | 其他 | 低 | [链接](changes/2026-03-19__coach-tier-hardcode-fix.md) |
| 2026-03-19 | 数据库变更审计RNS1.2 客户与助教接口 | 文档 | 其他 | 低 | [链接](changes/2026-03-19__rns12-db-audit.md) |
| 2026-03-18 | RNS1.1 E2E 测试 — FDW 直连改造 + performance_service bug 修复 | bugfix | 其他 | 未知 | [链接](changes/2026-03-18__rns1-e2e-fdw-direct-connect-bugfix.md) |
@@ -231,7 +242,12 @@
| 日期 | 需求摘要 | 变更类型 | 风险 | 详情 |
|------|----------|----------|------|------|
| 2026-03-20 | H2 修复FDW → 直连 ETL 架构统一 | bugfix | 未知 | [链接](changes/2026-03-20__h2-fdw-to-direct-etl-unification.md) |
| 2026-03-20 | 变更审计记录R3 项目类型筛选接口重建fetchSkillTypes / cfg_area_category | bugfix | 高 | [链接](changes/2026-03-20__r3-skill-type-filter-rebuild.md) |
| 2026-03-20 | RNS1 系列 AI 自主决策风险审计报告(完整版) | bugfix | 高 | [链接](changes/2026-03-20__rns1-ai-autonomous-decision-risk-audit.md) |
| 2026-03-20 | 变更审计记录RNS1.3 三看板 FDW 查询层数据口径修复 | bugfix | 未知 | [链接](changes/2026-03-20__rns13-board-apis-e2e-fix.md) |
| 2026-03-20 | 变更审计记录RNS1.4 CHAT 模块重建 + FDW→直连统一 + R3 筛选修复 | bugfix | 未知 | [链接](changes/2026-03-20__rns14-chat-fdw-filter-audit.md) |
| 2026-03-20 | 变更审计记录RNS1.4 CHAT 模块迁移 + R3 项目类型筛选重建 | bugfix | 高 | [链接](changes/2026-03-20__rns14-chat-module-r3-filter-rebuild.md) |
| 2026-03-19 | 变更审计记录card_type_id 年卡/月卡映射文档同步 | bugfix | 未知 | [链接](changes/2026-03-19__card-type-id-doc-sync.md) |
| 2026-03-19 | 变更审计记录coach_service 绩效档位硬编码修复 | bugfix | 低 | [链接](changes/2026-03-19__coach-tier-hardcode-fix.md) |
| 2026-03-19 | 变更审计记录助教等级映射硬编码修复P2-9 | bugfix | 低 | [链接](changes/2026-03-19__level-map-hardcode-fix.md) |
@@ -294,6 +310,7 @@
| 日期 | 需求摘要 | 变更类型 | 风险 | 详情 |
|------|----------|----------|------|------|
| 2026-03-20 | 变更审计记录R3 项目类型筛选接口重建fetchSkillTypes / cfg_area_category | bugfix | 高 | [链接](changes/2026-03-20__r3-skill-type-filter-rebuild.md) |
| 2026-03-20 | 变更审计记录RNS1.3 三看板 FDW 查询层数据口径修复 | bugfix | 未知 | [链接](changes/2026-03-20__rns13-board-apis-e2e-fix.md) |
| 2026-03-19 | 变更审计记录card_type_id 年卡/月卡映射文档同步 | bugfix | 未知 | [链接](changes/2026-03-19__card-type-id-doc-sync.md) |
| 2026-03-19 | 变更审计记录助教等级映射硬编码修复P2-9 | bugfix | 低 | [链接](changes/2026-03-19__level-map-hardcode-fix.md) |

View File

@@ -1,24 +1,33 @@
# 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 集成层
> 迁移脚本:
> - `db/zqyy_app/migrations/2026-03-08__create_ai_tables.sql`(初始建表
> - `db/zqyy_app/migrations/2026-03-20__rns14_chat_module_extend.sql`RNS1.4 CHAT 扩展)
> 关联 SPEC`05-miniapp-ai-integration`P5 AI 集成层)、`rns1-chat-integration`RNS1.4 CHAT 对齐与联调收尾)
---
## 1. 变更说明
### 新增表3 张)
### 新增表3 张P5 初始建表
| # | 表名 | 用途 | 字段数 |
|---|------|------|--------|
| 1 | `biz.ai_conversations` | AI 对话记录:每次 AI 调用(用户主动或系统自动)创建一条 | 8 |
| 2 | `biz.ai_messages` | AI 消息记录:对话中的每条消息(输入/输出/系统) | 6 |
| # | 表名 | 用途 | 字段数(初始→当前) |
|---|------|------|---------------------|
| 1 | `biz.ai_conversations` | AI 对话记录:每次 AI 调用(用户主动或系统自动)创建一条 | 8 → 13 |
| 2 | `biz.ai_messages` | AI 消息记录:对话中的每条消息(输入/输出/系统) | 6 → 7 |
| 3 | `biz.ai_cache` | AI 应用缓存:各应用的结构化输出结果 | 9 |
### RNS1.4 CHAT 模块扩展字段2026-03-20
| # | 表名 | 新增字段 | 用途 |
|---|------|---------|------|
| 1 | `biz.ai_conversations` | `context_type`, `context_id`, `title`, `last_message`, `last_message_at` | 多入口对话复用 + 历史列表展示与排序 |
| 2 | `biz.ai_messages` | `reference_card` | 引用卡片 JSON客户概览等结构化上下文数据 |
### 表字段明细
#### biz.ai_conversations8 字段)
#### biz.ai_conversations13 字段)
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
@@ -30,8 +39,13 @@
| `source_page` | VARCHAR(100) | 可空 | 来源页面标识 |
| `source_context` | JSONB | 可空 | 页面上下文 JSON |
| `created_at` | TIMESTAMPTZ | NOT NULL DEFAULT NOW() | 创建时间 |
| `context_type` | VARCHAR(20) | 可空 | **RNS1.4 新增** — 对话关联上下文类型task任务/ customer客户/ coach助教/ general通用 |
| `context_id` | VARCHAR(50) | 可空 | **RNS1.4 新增** — 关联上下文 IDtask 入口为 taskIdcustomer 入口为 customerIdcoach 入口为 coachIdgeneral 为 NULL |
| `title` | VARCHAR(200) | 可空 | **RNS1.4 新增** — 对话标题:自定义 > 上下文名称 > 首条消息前20字 |
| `last_message` | TEXT | 可空 | **RNS1.4 新增** — 最后一条消息内容摘要截断至100字 |
| `last_message_at` | TIMESTAMPTZ | 可空 | **RNS1.4 新增** — 最后消息时间,用于历史列表排序和对话复用时限判断 |
#### biz.ai_messages6 字段)
#### biz.ai_messages7 字段)
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
@@ -41,6 +55,7 @@
| `content` | TEXT | NOT NULL | 消息内容 |
| `tokens_used` | INTEGER | 可空 | 本条消息消耗的 token 数 |
| `created_at` | TIMESTAMPTZ | NOT NULL DEFAULT NOW() | 创建时间 |
| `reference_card` | JSONB | 可空 | **RNS1.4 新增** — 引用卡片 JSON`{type, title, summary, data}`,用于展示客户概览等结构化上下文数据 |
#### biz.ai_cache9 字段)
@@ -80,6 +95,8 @@
| `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 排序便于删除最旧) |
| `ai_conversations` | `idx_ai_conv_context` | INDEX条件 | **RNS1.4 新增**`(user_id, site_id, context_type, context_id, last_message_at DESC NULLS LAST) WHERE context_type IS NOT NULL` — 上下文对话查找(多入口复用) |
| `ai_conversations` | `idx_ai_conv_last_msg` | INDEX | **RNS1.4 新增**`(user_id, site_id, last_message_at DESC NULLS LAST)` — 历史列表排序优化CHAT-1 倒序) |
---
@@ -89,15 +106,44 @@
|------|------|
| ETL 任务 | 无影响。AI 表属于 `biz` Schema不参与 ETL 流程 |
| 后端 API | 直接依赖。P5 AI 模块(`apps/backend/app/ai/`将基于这三张表实现对话持久化、缓存读写、SSE 流式对话等功能 |
| 后端 APIRNS1.4 | **直接依赖**。CHAT 模块(`apps/backend/app/routers/xcx_chat.py``apps/backend/app/services/chat_service.py`)依赖 `ai_conversations` 的 5 个新字段(`context_type`/`context_id`/`title`/`last_message`/`last_message_at`)实现多入口对话复用、历史列表展示与排序;依赖 `ai_messages.reference_card` 存储引用卡片 JSON |
| 小程序 | 间接依赖。小程序通过后端 AI API 间接使用对话和缓存数据 |
| 小程序RNS1.4 | **间接依赖**`pages/chat/chat.ts``pages/chat-history/chat-history.ts` 通过 CHAT-1/2/3/4 端点间接依赖新字段(`title`→对话标题、`lastMessage`→摘要、`timestamp`→排序、`referenceCard`→引用卡片渲染) |
| 管理后台 | 暂无影响。后续可能增加 AI 调用统计和缓存管理界面 |
| `member_retention_clue` | 间接关联。App8维客线索整理的结果同时写入 `ai_cache``member_retention_clue` 表 |
| 现有 `biz` Schema | 兼容。新增 3 张表不修改已有对象coach_tasks、notes、trigger_jobs 等) |
| 现有 `biz` Schema | 兼容。P5 新增 3 张表RNS1.4 仅在已有表上 ADD COLUMN / CREATE INDEX不修改已有字段或约束 |
---
## 3. 回滚策略
### 3a. 回滚 RNS1.4 CHAT 扩展2026-03-20 迁移)
按逆序 DROP 新增索引和字段:
```sql
-- 删除 RNS1.4 新增索引
DROP INDEX IF EXISTS biz.idx_ai_conv_context;
DROP INDEX IF EXISTS biz.idx_ai_conv_last_msg;
-- 回退 ai_messages 新增字段
ALTER TABLE biz.ai_messages DROP COLUMN IF EXISTS reference_card;
-- 回退 ai_conversations 新增字段(逆序)
ALTER TABLE biz.ai_conversations DROP COLUMN IF EXISTS last_message_at;
ALTER TABLE biz.ai_conversations DROP COLUMN IF EXISTS last_message;
ALTER TABLE biz.ai_conversations DROP COLUMN IF EXISTS title;
ALTER TABLE biz.ai_conversations DROP COLUMN IF EXISTS context_id;
ALTER TABLE biz.ai_conversations DROP COLUMN IF EXISTS context_type;
```
注意:
- 回滚后 CHAT 模块xcx_chat 路由、chat_service将无法正常工作
- 如果新字段中已有数据,需先备份再执行回滚
- 回滚不影响 P5 初始建表的 8 个原始字段
### 3b. 回滚 P5 初始建表2026-03-08 迁移)
按逆序 DROPCASCADE 处理外键依赖):
```sql
@@ -123,6 +169,8 @@ DROP TABLE IF EXISTS biz.ai_conversations CASCADE;
## 4. 验证 SQL
### 4a. P5 初始建表验证
```sql
-- 1. 验证 3 张 AI 表全部存在
SELECT table_name
@@ -137,7 +185,7 @@ 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
-- 预期:返回 13 行P5 原始 8 字段 + RNS1.4 新增 5 字段)
-- 3. 验证 ai_messages 的外键和 CHECK 约束
SELECT conname, contype, pg_get_constraintdef(oid) AS constraint_def
@@ -152,7 +200,7 @@ FROM pg_constraint
WHERE conrelid = 'biz.ai_cache'::regclass AND contype = 'c';
-- 预期:返回 1 行 chk_ai_cache_type包含 7 个枚举值
-- 5. 验证索引全部存在5 个)
-- 5. 验证 P5 初始索引全部存在5 个)
SELECT indexname
FROM pg_indexes
WHERE schemaname = 'biz'
@@ -164,3 +212,35 @@ WHERE schemaname = 'biz'
ORDER BY indexname;
-- 预期:返回 5 行
```
### 4b. RNS1.4 CHAT 扩展验证
```sql
-- 6. 验证 ai_conversations 新增 5 个字段存在
SELECT column_name, data_type, character_maximum_length
FROM information_schema.columns
WHERE table_schema = 'biz' AND table_name = 'ai_conversations'
AND column_name IN ('context_type', 'context_id', 'title', 'last_message', 'last_message_at');
-- 预期:返回 5 行
-- context_type | character varying | 20
-- context_id | character varying | 50
-- title | character varying | 200
-- last_message | text | NULL
-- last_message_at | timestamp with time zone | NULL
-- 7. 验证 ai_messages 新增 reference_card 字段存在
SELECT column_name, data_type
FROM information_schema.columns
WHERE table_schema = 'biz' AND table_name = 'ai_messages'
AND column_name = 'reference_card';
-- 预期:返回 1 行data_type = 'jsonb'
-- 8. 验证 RNS1.4 新增 2 个索引存在
SELECT indexname, indexdef
FROM pg_indexes
WHERE schemaname = 'biz' AND tablename = 'ai_conversations'
AND indexname IN ('idx_ai_conv_context', 'idx_ai_conv_last_msg');
-- 预期:返回 2 行
-- idx_ai_conv_context — 含 WHERE context_type IS NOT NULL 条件
-- idx_ai_conv_last_msg — (user_id, site_id, last_message_at DESC NULLS LAST)
```

View File

@@ -1,9 +1,9 @@
# BD_Manualapp Schema 与 RLS 视图层
> 目标库:`test_etl_feiqiu`(通过 `PG_DSN` 连接)
> 迁移脚本:`db/etl_feiqiu/migrations/2026-02-24__p1_create_app_schema_rls_views.sql`、`db/etl_feiqiu/migrations/2026-03-19_add_board_rls_views.sql`
> 迁移脚本:`db/etl_feiqiu/migrations/2026-02-24__p1_create_app_schema_rls_views.sql`、`db/etl_feiqiu/migrations/2026-03-19_add_board_rls_views.sql`、`db/zqyy_app/migrations/2026-03-20_rebuild_rls_view_gift_breakdown.sql`
> DDL 位置:`docs/database/ddl/etl_feiqiu__app.sql`(执行后需重新生成)
> 关联 SPEC`miniapp-db-foundation`P1 基础设施层)、`rns1-board-apis`BOARD 看板)
> 关联 SPEC`miniapp-db-foundation`P1 基础设施层)、`rns1-board-apis`BOARD 看板)、`gift-card-breakdown`(赠送卡矩阵细分)
---
@@ -15,7 +15,7 @@
### 新增角色
- `app_reader`:只读角色(`LOGIN`),拥有 `app` Schema 的 `USAGE` + `SELECT` 权限
### 新增视图38 张)
### 新增视图39 张)
**DWD 层11 张,全部含 `site_id` 过滤):**
@@ -41,7 +41,6 @@
| `app.v_dws_member_visit_detail` | `dws.dws_member_visit_detail` |
| `app.v_dws_member_winback_index` | `dws.dws_member_winback_index` |
| `app.v_dws_member_newconv_index` | `dws.dws_member_newconv_index` |
| `app.v_dws_member_recall_index` | `dws.dws_member_recall_index` |
| `app.v_dws_member_assistant_relation_index` | `dws.dws_member_assistant_relation_index` |
| `app.v_dws_member_assistant_intimacy` | `dws.dws_member_assistant_intimacy` |
| `app.v_dws_assistant_daily_detail` | `dws.dws_assistant_daily_detail` |
@@ -61,7 +60,7 @@
| `app.v_dws_member_project_tag` | `dws.dws_member_project_tag` |
| `app.v_dws_member_spending_power_index` | `dws.dws_member_spending_power_index` |
**DWS 层 — cfg_* 配置表(4 张,无 `site_id`,直接 `SELECT *`**
**DWS 层 — cfg_* 配置表(5 张,无 `site_id`**
| 视图 | 源表 | 说明 |
|------|------|------|
@@ -69,6 +68,7 @@
| `app.v_cfg_assistant_level_price` | `dws.cfg_assistant_level_price` | 同上 |
| `app.v_cfg_bonus_rules` | `dws.cfg_bonus_rules` | 同上 |
| `app.v_cfg_index_parameters` | `dws.cfg_index_parameters` | 同上 |
| `app.v_cfg_area_category` | `dws.cfg_area_category` | DISTINCT 去重到 category 级别,排除 SPECIAL/OTHER按 sort_order 排序。用于项目类型筛选器CONFIG-1。2026-03-20 新增。 |
### 权限配置
@@ -81,6 +81,8 @@
> `v_dws_member_spending_power_index`、`v_dws_assistant_project_tag`、`v_dws_member_project_tag` 已于 2026-03-19 正式创建(迁移脚本 `2026-03-19_add_board_rls_views.sql`)。
> `v_dws_finance_recharge_summary` 已于 2026-03-20 重建,新增 6 个赠送卡细分字段(`gift_liquor_balance`、`gift_table_fee_balance`、`gift_voucher_balance`、`gift_liquor_recharge`、`gift_table_fee_recharge`、`gift_voucher_recharge`)。迁移脚本:`db/zqyy_app/migrations/2026-03-20_rebuild_rls_view_gift_breakdown.sql`。关联 SPEC`gift-card-breakdown`。
---
## 2. 兼容性影响
@@ -117,7 +119,7 @@ DROP ROLE IF EXISTS app_reader;
-- 1. 验证 app Schema 存在
SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'app';
-- 2. 验证视图数量(应为 38 张:原 35 + 2026-03-19 新增 3
-- 2. 验证视图数量(应为 39 张:原 35 + 2026-03-19 新增 3 + 2026-03-20 新增 1
SELECT count(*) FROM information_schema.views WHERE table_schema = 'app';
-- 3. 验证 app_reader 角色存在且有 app Schema 权限

View File

@@ -68,7 +68,7 @@ RNS1.2(客户与助教接口)通过 `fdw_queries.py` **直连 ETL 库**查
| 组件 | 影响 |
|------|------|
| ETL 任务 | 无影响。本脚本仅在业务库创建外部表映射,不修改 ETL 库 |
| 后端 API | 前置依赖。后端可通过 `fdw_etl.v_dim_member` 等外部表读取 ETL 数据,无需直连 ETL 库 |
| 后端 API | ⚠️ 已不再使用 FDW 外部表。后端自 2026-03-18 起通过 `get_etl_readonly_connection(site_id)` 直连 ETL 库查询 `app.v_*` RLS 视图(原因:`postgres_fdw` 不传递自定义 GUC 参数RLS 门店隔离失效。FDW 外部表仅作为备用保留,不再被后端代码引用 |
| 小程序 | 无直接影响。小程序通过后端 API 间接访问 |
| 管理后台 | 无直接影响 |
| `auth`/`biz` Schema | 无影响。FDW 配置独立于业务 Schema |

View File

@@ -104,6 +104,19 @@ SELECT tier_id,
FROM dws.cfg_performance_tier;
;
CREATE OR REPLACE VIEW app.v_cfg_area_category AS
SELECT DISTINCT
category_code,
category_name,
display_name,
short_name,
sort_order
FROM dws.cfg_area_category
WHERE is_active = TRUE
AND category_code NOT IN ('SPECIAL', 'OTHER')
ORDER BY sort_order;
;
CREATE OR REPLACE VIEW app.v_dim_assistant AS
SELECT assistant_id,
user_id,
@@ -950,31 +963,7 @@ SELECT newconv_id,
WHERE (site_id = (current_setting('app.current_site_id'::text))::bigint);
;
CREATE OR REPLACE VIEW app.v_dws_member_recall_index AS
SELECT recall_id,
site_id,
tenant_id,
member_id,
days_since_last_visit,
visit_interval_median,
visit_interval_mad,
days_since_first_visit,
days_since_last_recharge,
visits_last_14_days,
visits_last_60_days,
score_overdue,
score_new_bonus,
score_recharge_bonus,
score_hot_drop,
raw_score,
display_score,
calc_time,
calc_version,
created_at,
updated_at
FROM dws.dws_member_recall_index
WHERE (site_id = (current_setting('app.current_site_id'::text))::bigint);
;
CREATE OR REPLACE VIEW app.v_dws_member_visit_detail AS
SELECT id,

View File

@@ -32,7 +32,6 @@ CREATE SEQUENCE IF NOT EXISTS dws.dws_member_assistant_relation_index_relation_i
CREATE SEQUENCE IF NOT EXISTS dws.dws_member_consumption_summary_id_seq AS bigint;
CREATE SEQUENCE IF NOT EXISTS dws.dws_member_newconv_index_newconv_id_seq AS bigint;
CREATE SEQUENCE IF NOT EXISTS dws.dws_member_project_tag_id_seq AS bigint;
CREATE SEQUENCE IF NOT EXISTS dws.dws_member_recall_index_recall_id_seq AS bigint;
CREATE SEQUENCE IF NOT EXISTS dws.dws_member_spending_power_index_spi_id_seq AS bigint;
CREATE SEQUENCE IF NOT EXISTS dws.dws_member_visit_detail_id_seq AS bigint;
CREATE SEQUENCE IF NOT EXISTS dws.dws_member_winback_index_winback_id_seq AS bigint;
@@ -54,7 +53,8 @@ CREATE TABLE dws.cfg_area_category (
updated_at timestamp with time zone DEFAULT now() NOT NULL,
source_table_name character varying(100) DEFAULT NULL::character varying,
display_name character varying(50) DEFAULT NULL::character varying,
short_name character varying(20) DEFAULT NULL::character varying
short_name character varying(20) DEFAULT NULL::character varying,
sort_order integer DEFAULT 100 NOT NULL
);
CREATE TABLE dws.cfg_assistant_level_price (
@@ -741,30 +741,6 @@ CREATE TABLE dws.dws_member_project_tag (
updated_at timestamp with time zone DEFAULT now() NOT NULL
);
CREATE TABLE dws.dws_member_recall_index (
recall_id bigint DEFAULT nextval('dws.dws_member_recall_index_recall_id_seq'::regclass) NOT NULL,
site_id bigint NOT NULL,
tenant_id bigint NOT NULL,
member_id bigint NOT NULL,
days_since_last_visit integer,
visit_interval_median numeric(10,2),
visit_interval_mad numeric(10,2),
days_since_first_visit integer,
days_since_last_recharge integer,
visits_last_14_days integer DEFAULT 0 NOT NULL,
visits_last_60_days integer DEFAULT 0 NOT NULL,
score_overdue numeric(10,4),
score_new_bonus numeric(10,4),
score_recharge_bonus numeric(10,4),
score_hot_drop numeric(10,4),
raw_score numeric(14,6),
display_score numeric(4,2),
calc_time timestamp with time zone DEFAULT now() NOT NULL,
calc_version integer DEFAULT 1 NOT NULL,
created_at timestamp with time zone DEFAULT now() NOT NULL,
updated_at timestamp with time zone DEFAULT now() NOT NULL
);
CREATE TABLE dws.dws_member_spending_power_index (
spi_id bigint DEFAULT nextval('dws.dws_member_spending_power_index_spi_id_seq'::regclass) NOT NULL,
site_id bigint NOT NULL,
@@ -1025,8 +1001,6 @@ ALTER TABLE dws.dws_member_newconv_index ADD CONSTRAINT dws_member_newconv_index
ALTER TABLE dws.dws_member_newconv_index ADD CONSTRAINT uk_dws_member_newconv UNIQUE (site_id, member_id);
ALTER TABLE dws.dws_member_project_tag ADD CONSTRAINT pk_dws_member_project_tag PRIMARY KEY (id);
ALTER TABLE dws.dws_member_project_tag ADD CONSTRAINT uk_dws_member_project_tag UNIQUE (site_id, member_id, time_window, category_code);
ALTER TABLE dws.dws_member_recall_index ADD CONSTRAINT dws_member_recall_index_pkey PRIMARY KEY (recall_id);
ALTER TABLE dws.dws_member_recall_index ADD CONSTRAINT uk_dws_member_recall UNIQUE (site_id, member_id);
ALTER TABLE dws.dws_member_spending_power_index ADD CONSTRAINT dws_member_spending_power_index_pkey PRIMARY KEY (spi_id);
ALTER TABLE dws.dws_member_visit_detail ADD CONSTRAINT dws_member_visit_detail_pkey PRIMARY KEY (id);
ALTER TABLE dws.dws_member_visit_detail ADD CONSTRAINT uk_dws_member_visit UNIQUE (site_id, member_id, order_settle_id);
@@ -1098,7 +1072,6 @@ CREATE INDEX idx_dws_member_consumption_member ON dws.dws_member_consumption_sum
CREATE INDEX idx_dws_member_consumption_tier ON dws.dws_member_consumption_summary USING btree (customer_tier);
CREATE INDEX idx_dws_newconv_display ON dws.dws_member_newconv_index USING btree (site_id, display_score DESC);
CREATE INDEX idx_mpt_site_window_tagged ON dws.dws_member_project_tag USING btree (site_id, time_window) WHERE (is_tagged = true);
CREATE INDEX idx_dws_recall_display ON dws.dws_member_recall_index USING btree (site_id, display_score DESC);
CREATE INDEX idx_spi_display_score ON dws.dws_member_spending_power_index USING btree (site_id, display_score DESC);
CREATE UNIQUE INDEX idx_spi_site_member ON dws.dws_member_spending_power_index USING btree (site_id, member_id);
CREATE INDEX idx_dws_member_visit_date ON dws.dws_member_visit_detail USING btree (visit_date);
@@ -1760,94 +1733,94 @@ TRUNCATE TABLE dws.cfg_area_category RESTART IDENTITY CASCADE;
INSERT INTO dws.cfg_area_category (
source_area_name, source_table_name, category_code, category_name,
display_name, short_name,
match_type, match_priority, is_active, description
match_type, match_priority, is_active, description, sort_order
) VALUES
-- ============ BILLIARD 🎱 中式/追分 ============
-- ============ BILLIARD 🎱 中式/追分 (sort_order=10) ============
-- A区18台
('A区', 'A1', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A1'),
('A区', 'A2', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A2'),
('A区', 'A3', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A3'),
('A区', 'A4', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A4'),
('A区', 'A5', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A5'),
('A区', 'A6', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A6'),
('A区', 'A7', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A7'),
('A区', 'A8', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A8'),
('A区', 'A9', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A9'),
('A区', 'A10', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A10'),
('A区', 'A11', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A11'),
('A区', 'A12', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A12'),
('A区', 'A13', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A13'),
('A区', 'A14', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A14'),
('A区', 'A15', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A15'),
('A区', 'A16', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A16'),
('A区', 'A17', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A17'),
('A区', 'A18', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A18'),
('A区', 'A1', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A1', 10),
('A区', 'A2', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A2', 10),
('A区', 'A3', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A3', 10),
('A区', 'A4', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A4', 10),
('A区', 'A5', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A5', 10),
('A区', 'A6', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A6', 10),
('A区', 'A7', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A7', 10),
('A区', 'A8', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A8', 10),
('A区', 'A9', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A9', 10),
('A区', 'A10', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A10', 10),
('A区', 'A11', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A11', 10),
('A区', 'A12', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A12', 10),
('A区', 'A13', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A13', 10),
('A区', 'A14', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A14', 10),
('A区', 'A15', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A15', 10),
('A区', 'A16', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A16', 10),
('A区', 'A17', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A17', 10),
('A区', 'A18', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'A区-A18', 10),
-- B区15台
('B区', 'B1', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B1'),
('B区', 'B2', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B2'),
('B区', 'B3', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B3'),
('B区', 'B4', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B4'),
('B区', 'B5', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B5'),
('B区', 'B6', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B6'),
('B区', 'B7', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B7'),
('B区', 'B8', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B8'),
('B区', 'B9', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B9'),
('B区', 'B10', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B10'),
('B区', 'B11', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B11'),
('B区', 'B12', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B12'),
('B区', 'B13', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B13'),
('B区', 'B14', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B14'),
('B区', 'B15', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B15'),
('B区', 'B1', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B1', 10),
('B区', 'B2', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B2', 10),
('B区', 'B3', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B3', 10),
('B区', 'B4', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B4', 10),
('B区', 'B5', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B5', 10),
('B区', 'B6', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B6', 10),
('B区', 'B7', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B7', 10),
('B区', 'B8', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B8', 10),
('B区', 'B9', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B9', 10),
('B区', 'B10', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B10', 10),
('B区', 'B11', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B11', 10),
('B区', 'B12', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B12', 10),
('B区', 'B13', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B13', 10),
('B区', 'B14', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B14', 10),
('B区', 'B15', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'B区-B15', 10),
-- C区6台
('C区', 'C1', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'C区-C1'),
('C区', 'C2', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'C区-C2'),
('C区', 'C3', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'C区-C3'),
('C区', 'C4', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'C区-C4'),
('C区', 'C5', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'C区-C5'),
('C区', 'C6', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'C区-C6'),
('C区', 'C1', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'C区-C1', 10),
('C区', 'C2', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'C区-C2', 10),
('C区', 'C3', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'C区-C3', 10),
('C区', 'C4', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'C区-C4', 10),
('C区', 'C5', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'C区-C5', 10),
('C区', 'C6', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'C区-C6', 10),
-- VIP包厢 BILLIARD3台
('VIP包厢', 'VIP1', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'VIP包厢-VIP1'),
('VIP包厢', 'VIP2', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'VIP包厢-VIP2'),
('VIP包厢', 'VIP3', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'VIP包厢-VIP3'),
('VIP包厢', 'VIP1', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'VIP包厢-VIP1', 10),
('VIP包厢', 'VIP2', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'VIP包厢-VIP2', 10),
('VIP包厢', 'VIP3', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'VIP包厢-VIP3', 10),
-- TV台1台
('TV台', 'TV', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'TV台-TV'),
('TV台', 'TV', 'BILLIARD', '🎱 中式/追分', '🎱 中式/追分', '🎱', 'EXACT', 10, TRUE, 'TV台-TV', 10),
-- ============ SNOOKER 斯诺克 ============
('VIP包厢', 'VIP5', 'SNOOKER', '斯诺克', '斯诺克', '', 'EXACT', 10, TRUE, 'VIP包厢-VIP5→斯诺克'),
('斯诺克区', 'S1', 'SNOOKER', '斯诺克', '斯诺克', '', 'EXACT', 10, TRUE, '斯诺克区-S1'),
('斯诺克区', 'S2', 'SNOOKER', '斯诺克', '斯诺克', '', 'EXACT', 10, TRUE, '斯诺克区-S2'),
('斯诺克区', 'S3', 'SNOOKER', '斯诺克', '斯诺克', '', 'EXACT', 10, TRUE, '斯诺克区-S3'),
('斯诺克区', 'S4', 'SNOOKER', '斯诺克', '斯诺克', '', 'EXACT', 10, TRUE, '斯诺克区-S4'),
('VIP包厢', 'VIP5', 'SNOOKER', '斯诺克', '斯诺克', '', 'EXACT', 10, TRUE, 'VIP包厢-VIP5→斯诺克', 20),
('斯诺克区', 'S1', 'SNOOKER', '斯诺克', '斯诺克', '', 'EXACT', 10, TRUE, '斯诺克区-S1', 20),
('斯诺克区', 'S2', 'SNOOKER', '斯诺克', '斯诺克', '', 'EXACT', 10, TRUE, '斯诺克区-S2', 20),
('斯诺克区', 'S3', 'SNOOKER', '斯诺克', '斯诺克', '', 'EXACT', 10, TRUE, '斯诺克区-S3', 20),
('斯诺克区', 'S4', 'SNOOKER', '斯诺克', '斯诺克', '', 'EXACT', 10, TRUE, '斯诺克区-S4', 20),
-- ============ MAHJONG 🀄 麻将/棋牌 ============
('666', '董事办', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, '666-董事办'),
('666', '666', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, '666-666'),
('麻将房', '1', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, '麻将房-1'),
('麻将房', 'M2', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, '麻将房-M2'),
('麻将房', 'M3', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, '麻将房-M3'),
('麻将房', 'M4', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, '麻将房-M4'),
('麻将房', 'M5', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, '麻将房-M5'),
('M7', 'M7', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, 'M7-M7'),
('M7', '大包麻将房', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, 'M7-大包麻将房'),
('M8', 'M8', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, 'M8-M8'),
('发财', '发财', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, '发财-发财'),
('666', '董事办', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, '666-董事办', 30),
('666', '666', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, '666-666', 30),
('麻将房', '1', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, '麻将房-1', 30),
('麻将房', 'M2', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, '麻将房-M2', 30),
('麻将房', 'M3', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, '麻将房-M3', 30),
('麻将房', 'M4', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, '麻将房-M4', 30),
('麻将房', 'M5', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, '麻将房-M5', 30),
('M7', 'M7', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, 'M7-M7', 30),
('M7', '大包麻将房', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, 'M7-大包麻将房', 30),
('M8', 'M8', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, 'M8-M8', 30),
('发财', '发财', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, '发财-发财', 30),
-- ============ KTV 🎤 团建/K歌 ============
('K包', '常乐', 'KTV', '🎤 团建/K歌', '🎤 团建/K歌', '🎤', 'EXACT', 10, TRUE, 'K包-常乐'),
('K包', '幸会(纯k)', 'KTV', '🎤 团建/K歌', '🎤 团建/K歌', '🎤', 'EXACT', 10, TRUE, 'K包-幸会(纯k)'),
('K包', '虚拟188', 'KTV', '🎤 团建/K歌', '🎤 团建/K歌', '🎤', 'EXACT', 10, TRUE, 'K包-虚拟188'),
('K包', '888', 'KTV', '🎤 团建/K歌', '🎤 团建/K歌', '🎤', 'EXACT', 10, TRUE, 'K包-888'),
('k包活动区', '大包', 'KTV', '🎤 团建/K歌', '🎤 团建/K歌', '🎤', 'EXACT', 10, TRUE, 'k包活动区-大包'),
('k包活动区', '小包', 'KTV', '🎤 团建/K歌', '🎤 团建/K歌', '🎤', 'EXACT', 10, TRUE, 'k包活动区-小包'),
('幸会158', '纯k', 'KTV', '🎤 团建/K歌', '🎤 团建/K歌', '🎤', 'EXACT', 10, TRUE, '幸会158-纯k'),
('K包', '常乐', 'KTV', '🎤 团建/K歌', '🎤 团建/K歌', '🎤', 'EXACT', 10, TRUE, 'K包-常乐', 40),
('K包', '幸会(纯k)', 'KTV', '🎤 团建/K歌', '🎤 团建/K歌', '🎤', 'EXACT', 10, TRUE, 'K包-幸会(纯k)', 40),
('K包', '虚拟188', 'KTV', '🎤 团建/K歌', '🎤 团建/K歌', '🎤', 'EXACT', 10, TRUE, 'K包-虚拟188', 40),
('K包', '888', 'KTV', '🎤 团建/K歌', '🎤 团建/K歌', '🎤', 'EXACT', 10, TRUE, 'K包-888', 40),
('k包活动区', '大包', 'KTV', '🎤 团建/K歌', '🎤 团建/K歌', '🎤', 'EXACT', 10, TRUE, 'k包活动区-大包', 40),
('k包活动区', '小包', 'KTV', '🎤 团建/K歌', '🎤 团建/K歌', '🎤', 'EXACT', 10, TRUE, 'k包活动区-小包', 40),
('幸会158', '纯k', 'KTV', '🎤 团建/K歌', '🎤 团建/K歌', '🎤', 'EXACT', 10, TRUE, '幸会158-纯k', 40),
-- ============ SPECIAL 补时长/虚拟台 ============
('补时长', '补时长', 'SPECIAL', '补时长', '补时长', '', 'EXACT', 10, TRUE, '补时长-补时长'),
('补时长', '补时长2', 'SPECIAL', '补时长', '补时长', '', 'EXACT', 10, TRUE, '补时长-补时长2'),
('补时长', '补时长3', 'SPECIAL', '补时长', '补时长', '', 'EXACT', 10, TRUE, '补时长-补时长3'),
('补时长', '补时长4', 'SPECIAL', '补时长', '补时长', '', 'EXACT', 10, TRUE, '补时长-补时长4'),
('补时长', '补时长5', 'SPECIAL', '补时长', '补时长', '', 'EXACT', 10, TRUE, '补时长-补时长5'),
('补时长', '补时长6', 'SPECIAL', '补时长', '补时长', '', 'EXACT', 10, TRUE, '补时长-补时长6'),
('补时长', '补时长7', 'SPECIAL', '补时长', '补时长', '', 'EXACT', 10, TRUE, '补时长-补时长7'),
('虚拟台', '虚拟台1号', 'SPECIAL', '补时长', '补时长', '', 'EXACT', 10, TRUE, '虚拟台-虚拟台1号'),
('补时长', '补时长', 'SPECIAL', '补时长', '补时长', '', 'EXACT', 10, TRUE, '补时长-补时长', 900),
('补时长', '补时长2', 'SPECIAL', '补时长', '补时长', '', 'EXACT', 10, TRUE, '补时长-补时长2', 900),
('补时长', '补时长3', 'SPECIAL', '补时长', '补时长', '', 'EXACT', 10, TRUE, '补时长-补时长3', 900),
('补时长', '补时长4', 'SPECIAL', '补时长', '补时长', '', 'EXACT', 10, TRUE, '补时长-补时长4', 900),
('补时长', '补时长5', 'SPECIAL', '补时长', '补时长', '', 'EXACT', 10, TRUE, '补时长-补时长5', 900),
('补时长', '补时长6', 'SPECIAL', '补时长', '补时长', '', 'EXACT', 10, TRUE, '补时长-补时长6', 900),
('补时长', '补时长7', 'SPECIAL', '补时长', '补时长', '', 'EXACT', 10, TRUE, '补时长-补时长7', 900),
('虚拟台', '虚拟台1号', 'SPECIAL', '补时长', '补时长', '', 'EXACT', 10, TRUE, '虚拟台-虚拟台1号', 900),
-- ============ OTHER 兜底 ============
('DEFAULT', NULL, 'OTHER', '其他', '其他', '', 'DEFAULT', 999, TRUE, '兜底规则:无法匹配的归入其他');
('DEFAULT', NULL, 'OTHER', '其他', '其他', '', 'DEFAULT', 999, TRUE, '兜底规则:无法匹配的归入其他', 999);
-- =============================================================================

View File

@@ -1,6 +1,6 @@
-- =============================================================================
-- zqyy_app / biz核心业务表任务/备注/触发器))
-- 生成日期2026-03-15
-- 生成日期2026-03-20
-- 来源:测试库(通过脚本自动导出)
-- =============================================================================
@@ -36,6 +36,11 @@ CREATE TABLE biz.ai_conversations (
site_id bigint NOT NULL,
source_page character varying(100),
source_context jsonb,
context_type character varying(20),
context_id character varying(50),
title character varying(200),
last_message text,
last_message_at timestamp with time zone,
created_at timestamp with time zone DEFAULT now() NOT NULL
);
@@ -45,6 +50,7 @@ CREATE TABLE biz.ai_messages (
role character varying(10) NOT NULL,
content text NOT NULL,
tokens_used integer,
reference_card jsonb,
created_at timestamp with time zone DEFAULT now() NOT NULL
);
@@ -127,6 +133,8 @@ CREATE INDEX idx_ai_cache_cleanup ON biz.ai_cache USING btree (cache_type, site_
CREATE INDEX idx_ai_cache_lookup ON biz.ai_cache USING btree (cache_type, site_id, target_id, created_at DESC);
CREATE INDEX idx_ai_conv_app_site ON biz.ai_conversations USING btree (app_id, site_id, created_at DESC);
CREATE INDEX idx_ai_conv_user_site ON biz.ai_conversations USING btree (user_id, site_id, created_at DESC);
CREATE INDEX idx_ai_conv_context ON biz.ai_conversations USING btree (user_id, site_id, context_type, context_id, last_message_at DESC NULLS LAST) WHERE (context_type IS NOT NULL);
CREATE INDEX idx_ai_conv_last_msg ON biz.ai_conversations USING btree (user_id, site_id, last_message_at DESC NULLS LAST);
CREATE INDEX idx_ai_msg_conv ON biz.ai_messages USING btree (conversation_id, created_at);
CREATE INDEX idx_coach_tasks_assistant_status ON biz.coach_tasks USING btree (site_id, assistant_id, status);
CREATE UNIQUE INDEX idx_coach_tasks_site_assistant_member_type ON biz.coach_tasks USING btree (site_id, assistant_id, member_id, task_type) WHERE ((status)::text = 'active'::text);

View File

@@ -528,7 +528,7 @@ interface CoachTask {
// ─── 最亲密助教 ───
interface FavoriteCoach {
/** 亲密度 emoji如 "💖"、"💛" */
/** 亲密度 emoji如 "💖"、"🧡"、"💛"、"💙" */
emoji: string
/** 助教姓名 */
name: string
@@ -699,7 +699,7 @@ interface CustomerNote {
| 字段 | 类型 | 说明 | 数据源 |
|------|------|------|--------|
| `emoji` | string | 亲密度 emoji💖 = 高亲密度 ≥ 0.7,💛 = 中亲密度 < 0.7 | 后端根据 `relationIndex` 映射 |
| `emoji` | string | 亲密度 emoji💖(>8.5) / 🧡(>7) / 💛(>5) / 💙(≤5),后端 compute_heart_icon() 根据 rs_display 映射 | 后端根据 `relationIndex` 映射 |
| `name` | string | 助教姓名 | `biz.users.nickname` |
| `relationIndex` | string | 关系指数,如 `"0.92"` | 后端根据服务频率/消费金额综合计算 |
| `indexColor` | string | 关系指数对应颜色 | 后端根据 `relationIndex` 阈值映射 |
@@ -759,7 +759,7 @@ interface CustomerNote {
| `settle_type` 过滤 | 消费记录取正向交易 `settle_type IN (1, 3)`,本表无 `is_delete` 字段 |
| 废单排除 | 使用 `dwd_assistant_service_log_ex.is_trash` 排除废单,`dwd_assistant_trash_event` 已废弃 |
| `type` 枚举映射 | `table`(台桌消费)/ `shop`(酒水/商品消费)/ `recharge`(充值),后端根据 `settle_type` 和业务逻辑映射 |
| 亲密度 emoji | 💖 = 高亲密度(关系指数 ≥ 0.7),💛 = 中亲密度(关系指数 < 0.7 |
| 亲密度 emoji | 💖(>8.5) / 🧡(>7) / 💛(>5) / 💙(≤5) |
#### 数据源映射表
@@ -867,8 +867,8 @@ interface CoachBoardItem {
skills: Array<{ text: string; cls: string }>
/**
* Top 客户列表(含亲密度 emoji 前缀)
* 示例:['💖 王先生', '💖 李女士', '💛 赵总']
* 💖 = 高亲密度,💛 = 中亲密度
* 示例:['💖 王先生', '🧡 李女士', '💛 赵总', '💙 新客户']
* 💖(>8.5) / 🧡(>7) / 💛(>5) / 💙(≤5)
*/
topCustomers: string[]
@@ -922,7 +922,7 @@ interface CoachBoardItem {
| `avatarGradient` | string | 头像渐变色标识(`blue`/`green`/`pink`/`amber`/`violet`/`cyan` | 后端根据 ID 哈希分配 |
| `level` | string | 等级英文 key`star`/`senior`/`middle`/`junior` | `fdw_etl.v_dws_assistant_salary_calc.coach_level` |
| `skills` | `Array<{ text: string, cls: string }>` | 技能标签,`text` 为 emoji 或文字,`cls` 为前端样式类 | `biz.coach_skills` 或配置表 |
| `topCustomers` | `string[]` | Top 客户列表,含亲密度 emoji 前缀(💖/💛 | 后端按亲密度排序取 Top 3拼接 emoji + 姓名 |
| `topCustomers` | `string[]` | Top 客户列表,含亲密度 emoji 前缀(💖/🧡/💛/💙 | 后端按亲密度排序取 Top 3拼接 emoji + 姓名 |
##### perf 维度专属字段4 个)
@@ -978,7 +978,7 @@ interface CoachBoardItem {
| 助教费用拆分DWD-DOC 规则 2 | 工资计算中基础课 = `assistant_pd_money`,激励课 = `assistant_cx_money` |
| `levelClass` 前端计算 | 后端仅返回 `level`(英文 key前端通过 `LEVEL_CLASS` 映射生成样式类 |
| `perfGap` 后端计算 | 后端根据档位阈值计算差距描述字符串,已达标时不返回该字段 |
| `topCustomers` emoji 规则 | 💖 = 高亲密度(关系指数 ≥ 0.7),💛 = 中亲密度(关系指数 < 0.7 |
| `topCustomers` emoji 规则 | 💖(>8.5) / 🧡(>7) / 💛(>5) / 💙(≤5),基于 rs_display 值域 0-10 |
### BOARD-2: 客户看板
@@ -1856,10 +1856,12 @@ interface NoteItem {
---
## 八、对话模块
## 八、对话模块(已实现 ✅ — RNS1.4
> 数据源:`zqyy_app.chat_sessions`(对话会话)、`zqyy_app.chat_messages`(消息
> 路由前缀:`/api/xcx/chat`RNS1.4 从 `/api/ai/*` 迁移,旧路径已移除
> 数据源:`zqyy_app.biz.ai_conversations`(对话会话)、`zqyy_app.biz.ai_messages`(消息)
> 时间字段统一使用 `createdAt`camelCase替代前端 `timestamp` 和旧契约 `created_at`
> 所有端点需 JWTapproved 状态),使用 `require_approved()` 权限检查
### CHAT-1: 对话历史列表
@@ -1867,15 +1869,22 @@ interface NoteItem {
GET /api/xcx/chat/history?page=1&pageSize=20
```
#### 请求参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| `page` | number (query) | 否 | 页码,默认 1 |
| `pageSize` | number (query) | 否 | 每页条数,默认 20最大 100 |
#### 响应结构
```typescript
interface ChatHistoryResponse {
items: Array<{
id: string // 对话 ID
title: string // 对话标题(新增,需求 5.8.2
customerName?: string // 关联客户姓名(可选
lastMessage: string // 最后一条消息摘要
id: number // 对话 ID(即 chatId
title: string // 对话标题(自定义 > 客户姓名 > 首条消息前20字
customerName?: string // 关联客户姓名(仅 contextType=customer 时有值
lastMessage?: string // 最后一条消息摘要
timestamp: string // 最后消息时间ISO 8601
unreadCount: number // 未读消息数
}>
@@ -1885,33 +1894,55 @@ interface ChatHistoryResponse {
}
```
### CHAT-2: 对话消息
#### 排序规则
> 支持 `customerId` 查询参数(需求 5.8.5):后端根据 `customerId` 自动查找或创建对话,返回对应的 `chatId` 和消息列表
`last_message_at` 倒序(最新对话在前)。
### CHAT-2a: 通过 chatId 查询消息
```
GET /api/xcx/chat/{chatId}/messages?page=1&pageSize=50
GET /api/xcx/chat/messages?customerId={customerId}&page=1&pageSize=50
```
#### 响应结构
#### 请求参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| `chatId` | number (path) | 是 | 对话 ID |
| `page` | number (query) | 否 | 页码,默认 1 |
| `pageSize` | number (query) | 否 | 每页条数,默认 50最大 100 |
### CHAT-2b: 通过上下文查询消息
> 后端根据 `contextType` + `contextId` 自动查找或创建对话,返回对应的 `chatId` 和消息列表。
> 对话复用规则:`task` 入口同一 taskId 始终复用(无时限);`customer`/`coach` 入口 ≤ 3 天复用、> 3 天新建;`general` 入口始终新建。
```
GET /api/xcx/chat/messages?contextType={type}&contextId={id}&page=1&pageSize=50
```
#### 请求参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| `contextType` | string (query) | 是 | 上下文类型:`task` / `customer` / `coach` / `general` |
| `contextId` | string (query) | 是 | 上下文 IDtaskId / customerId / coachIdgeneral 时为空) |
| `page` | number (query) | 否 | 页码,默认 1 |
| `pageSize` | number (query) | 否 | 每页条数,默认 50最大 100 |
#### 响应结构CHAT-2a / CHAT-2b 共用)
```typescript
interface ChatMessagesResponse {
chatId: string // 对话 IDcustomerId 入口时由后端返回
chatId: number // 对话 ID上下文入口时由后端返回,供后续发送消息使用
items: Array<{
id: string
id: number
role: 'user' | 'assistant'
content: string
createdAt: string // 统一时间字段名(需求 5.8.1ISO 8601
createdAt: string // 统一时间字段名ISO 8601
// 引用卡片(可选,需求 5.8.3
referenceCard?: {
type: 'customer' | 'record' // 引用类型
title: string // 卡片标题,如 '张伟 — 消费概览'
summary: string // 摘要文字
data: Record<string, string> // 键值对详情,如 { '近30天消费': '¥2,380', '到店次数': '8次' }
}
// 引用卡片(可选,AI 回复涉及特定客户时附带
referenceCard?: ReferenceCard
}>
total: number
page: number
@@ -1919,42 +1950,89 @@ interface ChatMessagesResponse {
}
```
### CHAT-3: 发送消息
#### 排序规则
消息按 `created_at` 正序(最早的消息在前)。
#### referenceCard 结构定义
```typescript
interface ReferenceCard {
type: 'customer' | 'record' // 引用类型
title: string // 卡片标题,如 '张伟 — 消费概览'
summary: string // 摘要文字,如 '余额 ¥5,200近30天消费 ¥2,380'
data: Record<string, string> // 键值对详情
}
```
示例:
```json
{
"type": "customer",
"title": "张伟 — 消费概览",
"summary": "余额 ¥5,200近30天消费 ¥2,380",
"data": {
"余额": "¥5,200",
"近30天消费": "¥2,380",
"到店次数": "8次",
"最近到店": "3天前"
}
}
```
> referenceCard 中金额使用 `items_sum` 口径DWD-DOC 强制规则 1会员信息通过 `member_id` JOIN `dim_member` 获取DWD-DOC 规则 DQ-6
### CHAT-3: 发送消息(同步回复)
```
POST /api/xcx/chat/{chatId}/messages
Content-Type: application/json
Body: { content: string }
```
#### 请求参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|:----:|------|
| `chatId` | number (path) | 是 | 对话 ID归属验证不属于当前用户返回 403 |
| `content` | string (body) | 是 | 消息内容(不能为空) |
#### 响应结构
```typescript
interface SendMessageResponse {
userMessage: {
id: string
id: number
content: string
createdAt: string
createdAt: string // ISO 8601
}
aiReply: {
id: string
id: number
content: string
createdAt: string
createdAt: string // ISO 8601
}
}
```
### CHAT-4: SSE 流式端点(需求 5.8.4
#### AI 失败降级
> 用于 AI 流式回复,前端通过 SSE 接收逐 token 输出
AI 服务调用失败时,用户消息仍保存,`aiReply.content` 返回错误提示消息(如 `"抱歉AI 助手暂时无法回复,请稍后重试"`HTTP 状态码保持 200。
### CHAT-4: SSE 流式端点
> 用于 AI 流式回复,前端通过 SSE 接收逐 token 输出。
> chatId 归属验证在 SSE 流开始前完成,失败时返回普通 HTTP 403非 SSE
```
POST /api/xcx/chat/stream
Content-Type: application/json
Body: { chatId: string, content: string }
Body: { chatId: number, content: string }
Response: text/event-stream
```
#### SSE 事件格式
#### SSE 事件类型定义
三种事件类型:`message`(逐 token`done`(完成)、`error`(错误)。
```
event: message
@@ -1964,24 +2042,40 @@ event: message
data: {"token": "数据分析"}
event: done
data: {"messageId": "msg-xxx", "createdAt": "2026-03-05T14:30:00+08:00"}
data: {"messageId": 123, "createdAt": "2026-03-20T14:30:00+08:00"}
event: error
data: {"message": "AI 服务暂时不可用"}
```
- `event: message` — 逐 token 输出,`data.token` 为文本片段
- `event: done` — 流结束,`data.messageId` 为完整消息 ID
- `event: error` — 错误,`data.message` 为错误描述
| 事件类型 | data 结构 | 说明 |
|----------|----------|------|
| `message` | `{ "token": string }` | 逐 token 输出,`token` 为文本片段 |
| `done` | `{ "messageId": number, "createdAt": string }` | 流结束,返回完整消息 ID 和创建时间 |
| `error` | `{ "message": string }` | 错误,返回错误描述 |
> 注意SSE 端点的响应不经过 ResponseWrapperMiddleware 包装(`text/event-stream` 自动跳过
> 注意SSE 端点的响应不经过 ResponseWrapperMiddleware 包装(`text/event-stream` 自动跳过RNS1.0 已实现)。
#### 对话模块错误码
| HTTP 状态码 | 场景 | 响应示例 |
|:-----------:|------|---------|
| 403 | 用户未通过审核 | `{ "code": 403, "message": "用户未通过审核,无法访问此资源" }` |
| 403 | chatId 不属于当前用户 | `{ "code": 403, "message": "无权访问此对话" }` |
| 404 | 对话不存在 | `{ "code": 404, "message": "对话不存在" }` |
| 422 | 消息内容为空 | `{ "code": 422, "message": "消息内容不能为空" }` |
#### 字段说明
| 字段 | 类型 | 说明 |
|------|------|------|
| `CHAT-1.items[].title` | string | 对话标题,新增字段 |
| `CHAT-1.items[].title` | string | 对话标题(自定义 > 客户姓名 > 首条消息前20字 |
| `CHAT-1.items[].timestamp` | string | 最后消息时间ISO 8601 |
| `CHAT-2.items[].createdAt` | string | 统一时间字段名,替代 `created_at``timestamp` |
| `CHAT-2.items[].referenceCard` | object? | 引用卡片,从其他页面跳转时附带的上下文信息 |
| `CHAT-2` `customerId` 参数 | query | 支持通过客户 ID 查找/创建对话 |
| `CHAT-4` | SSE | 流式端点,`text/event-stream` 格式,不经过响应包装 |
| `CHAT-2.items[].referenceCard` | ReferenceCard? | 引用卡片AI 回复涉及客户时附带结构化上下文数据 |
| `CHAT-2b` `contextType` 参数 | query | 上下文类型:`task` / `customer` / `coach` / `general` |
| `CHAT-2b` `contextId` 参数 | query | 上下文 IDtaskId / customerId / coachId |
| `CHAT-4` SSE | text/event-stream | 三种事件:`message`token/ `done`(完成)/ `error`(错误) |
---
@@ -2017,6 +2111,6 @@ Response: {
| board-coach | BOARD-1, CONFIG-1 | P2 |
| board-customer | BOARD-2 | P2 |
| board-finance | BOARD-3 | P2 |
| chat-history | CHAT-1 | P2 |
| chat | CHAT-2, CHAT-3 | P2 |
| chat-history | CHAT-1 | ✅ 后端已实现 |
| chat | CHAT-2a, CHAT-2b, CHAT-3, CHAT-4 | ✅ 后端已实现 |
| my-profile | AUTH-3 | ✅ 已实现(读 globalData |

View File

@@ -79,7 +79,7 @@
| `name` | 客户姓名 |
| `initial` | 姓氏首字(头像占位) |
| `avatarGradient` | 头像渐变色 key |
| `heartEmoji` | 爱心 emoji❤️/💛/🤍 |
| `heartEmoji` | 爱心 emoji💖/🧡/💛/💙 |
| `score` | 亲密度评分 |
| `scoreColor` | 评分颜色 key |
| `serviceCount` | 服务次数 |

View File

@@ -286,15 +286,102 @@ async def build_page_text(
- 主观信息需在 Prompt 中标注【来源XXX请甄别信息真实性】
- reference 从 `ai_cache` 获取 app8 最新 + 最近 2 套历史
### 3.6 应用 1页面上下文文本化
### 3.6 应用 1biz_params 权限隔离(已实现,需确认)
应用 1 的 `_build_page_context()``_build_source_context()` 需要根据 `source_page` 类型,调用对应的数据获取函数并格式化为文本
> ✅ RNS1.4 已在 `app1_chat.py` 的 `_build_system_prompt()` 中实现 biz_params 拼接
实现要点
- 每个页面类型对应一个文本化函数
- 文本化输出为结构化中文描述(非 JSON便于 AI 理解
应用 1 通过 `biz_params.user_prompt_params` 向百炼 AI 传入用户身份信息,实现数据查询隔离
```python
system_content = {
"task": "你是台球门店的 AI 助手...",
"biz_params": {
"user_prompt_params": {
"User_ID": str(user_id),
"Role": role, # "助教" | "管理者"
"Nickname": nickname,
},
},
}
```
权限控制规则(百炼平台侧执行):
- 助教身份:仅允许查询与该助教相关的数据,禁止查询其他助教业绩/工资/客户关系等敏感数据,禁止查询门店级财务数据
- 管理者身份:可查询该门店下所有数据
- 所有查询必须包含 `site_id` 过滤
NS2 需确认事项:
- 前端调用 `POST /api/chat/conversations/:id/messages` 时,是否正确传入 `user_id`/`role`/`nickname`
- 后端从 JWT token 中提取用户信息并传入 `_build_system_prompt()`,确保不可伪造
### 3.7 应用 1页面上下文传递与文本化
#### 当前问题P5 PRD 与 RNS1.4 实现的差异)
P5 PRD 原始设计:
```
前端跳转 chat.html 时传入 source_page + page_context + screen_content
→ 后端接收后文本化 → 拼接为首条 user message
```
RNS1.4 当前实现:
```
前端跳转 chat 页面时仅传入 contextType + contextId如 task/12345
→ 后端 _build_page_context() 为占位实现page_context 和 screen_content 为空
→ AI 无法获得页面上下文
```
#### 解决方案:后端根据 contextType 自动获取页面上下文
采用方案 B简化前端后端自动查询与 RNS1.4 已有的 `contextType`/`contextId` 参数兼容:
```
前端传入 contextType="task-detail" + contextId="12345"
→ 后端 build_page_text("task-detail", 12345, site_id)
→ 后端从数据库获取任务详情、客户信息、备注、AI 分析等
→ 格式化为结构化中文文本
→ 拼接为首条 user message 的 page_context 字段
```
#### contextType 映射表
| 前端 contextType | 后端数据获取 | 文本化内容 |
|-----------------|-------------|-----------|
| `task-detail` | coach_tasks + member + notes + ai_cache | 任务信息 + 客户信息 + 备注 + AI 分析 |
| `customer-detail` | member + consumption + clues | 客户全信息 + 消费记录 + 维客线索 |
| `coach-detail` | assistant + tasks + notes | 助教信息 + 任务统计 + 备注 |
| `board-finance` | finance DWS 汇总(按当前筛选条件) | 财务数据摘要 |
| `board-customer` | 当前筛选维度 top 列表 | 客户排名摘要 |
| `board-coach` | 当前筛选维度排名 | 助教排名摘要 |
| `performance` | salary_calc + daily_detail | 绩效数据摘要 |
| `my-profile` | user info + assistant binding | 个人信息摘要 |
| `task-list` | 长按的任务摘要 + 客户-助教关系 | 任务概要 |
| `customer-service-records` | 服务记录列表 | 服务记录摘要 |
#### 前端需要补充的参数
当前前端仅传 `contextType` + `contextId`,对于看板类页面需要额外传入筛选条件:
| contextType | 必传参数 | 可选参数 |
|-------------|---------|---------|
| `task-detail` | contextIdtaskId | — |
| `customer-detail` | contextIdmemberId | — |
| `coach-detail` | contextIdassistantId | — |
| `board-finance` | — | `timeDimension`(时间维度)、`areaFilter`(区域筛选) |
| `board-customer` | — | `dimension`(排序维度)、`typeFilter`(类型筛选) |
| `board-coach` | — | `dimension``projectFilter``timeDimension` |
| `performance` | — | `timeDimension` |
| `task-list` | contextIdtaskId | — |
| `my-profile` | — | — |
#### 实现要点
- 每个页面类型对应一个文本化函数,在 `data_fetchers/page_context.py` 中实现
- 文本化输出为结构化中文描述(非 JSON便于 AI 理解上下文
- 数据量控制:每个页面上下文不超过 2000 字符,避免 token 浪费
- 敏感信息脱敏:不传入 member_phone 等断档字段
- 看板类页面:若前端未传筛选条件,使用默认值(如 board-finance 默认"本月"
- 错误降级数据获取失败时page_context 传入"页面上下文获取失败",不阻断对话
---
@@ -348,12 +435,18 @@ async def build_page_text(
```
用户点击 AI 入口
→ 前端传入 source_page + context_data
→ 后端 build_page_text(source_page, context_data)
拼接为首条 user message
→ 前端传入 contextType + contextId+ 可选筛选参数)
→ 后端 build_page_text(contextType, contextId, site_id, filters?)
从数据库获取对应页面数据,格式化为结构化中文文本
→ 拼接为首条 user message 的 page_context 字段
→ 注入 biz_paramsUser_ID/Role/Nickname到 system prompt
→ SSE 流式调用百炼 API
```
> ⚠️ 与 P5 PRD 原始设计的差异P5 设计为前端传入 `source_page` + `page_context` + `screen_content`
> RNS1.4 实际实现为前端传入 `contextType` + `contextId`,后端自动查询数据库获取上下文。
> NS2 沿用 RNS1.4 方案(后端自动获取),不要求前端传入原始页面数据。
---
## 六、参考文档
@@ -361,12 +454,16 @@ async def build_page_text(
| 文档 | 路径 | 用途 |
|------|------|------|
| P5 AI 集成 spec | `docs/prd/specs/P5-miniapp-ai-integration.md` | Prompt JSON 结构定义、调用链时序 |
| AI 应用 Prompt | `docs/prd/ai-app-prompts.md` | 8 个应用的 System Prompt、biz_params 定义 |
| AI 需求文档 | `docs/prd/AI需求2.md` | 8 个应用的详细需求表、传参格式 |
| AI 应用→页面映射 | `docs/reports/2026-03-20__ai_app_page_mapping.md` | 各应用输出内容在哪个页面的什么位置展示 |
| API 契约 | `docs/miniprogram-dev/API-contract.md` | 接口响应格式(决定数据结构) |
| DWD-DOC 标杆 | `docs/reports/DWD-DOC/` | 金额口径、字段语义权威参考 |
| AI 应用骨架代码 | `apps/backend/app/ai/apps/app*.py` | 当前骨架实现 |
| AI 应用骨架代码 | `apps/backend/app/ai/apps/app*.py` | 当前骨架实现(含 biz_params 已实现) |
| 百炼客户端 | `apps/backend/app/ai/bailian_client.py` | API 调用封装 |
| 缓存服务 | `apps/backend/app/ai/cache_service.py` | ai_cache 读写 |
| NS1 接口设计 | `docs/prd/Neo_Specs/NS1-xcx-backend-api.md` | 后端数据结构参考 |
| RNS1.4 任务计划 | `.kiro/specs/rns1-chat-integration/tasks.md` | 当前 chat 模块实施进度 |
---
@@ -413,7 +510,12 @@ async def build_page_text(
- [ ] T7完善 `app6_note.py``build_prompt()`(备注+客户数据 → 备注分析)
- [ ] T8完善 `app7_customer.py``build_prompt()`(客户全量数据 → 运营策略)
### Batch C应用 1 页面文本化 + 联调
### Batch C应用 1 页面文本化 + 前端配合
- [ ] T9实现各页面类型的文本化函数task-detail/customer-detail/board-*/performance 等)
- [ ] T10补充 `app1_chat.py``_build_page_context()` 调用文本化函数
- [ ] T11端到端联调(触发事件 → Prompt 拼接 → 百炼调用 → 缓存写入 → 前端展示
- [ ] T10补充 `app1_chat.py``_build_page_context()` 调用文本化函数,根据 `contextType` 路由到对应文本化函数
- [ ] T11前端补充看板类页面的筛选参数传递board-finance/board-customer/board-coach 跳转 chat 时传入当前筛选条件
- [ ] T12确认 biz_params 端到端正确性(前端 JWT → 后端提取 user_id/role/nickname → system prompt 注入 → 百炼权限隔离生效)
### Batch D联调与验证
- [ ] T13端到端联调触发事件 → Prompt 拼接 → 百炼调用 → 缓存写入 → 前端展示)
- [ ] T14应用 1 页面上下文联调(各入口页面 → contextType/contextId → 后端文本化 → AI 对话验证上下文感知)

View File

@@ -8,13 +8,13 @@
## 拆分总览
| 子 Spec | 名称 | 任务数 | 依赖 | 交付物 |
|---------|------|:------:|------|--------|
| RNS1.0 | 基础设施与契约重写 | 6 | 无 | 全局中间件 + 重写后的 API 契约 + 前端跨页面参数修复 |
| RNS1.1 | 任务与绩效接口 | 6 | RNS1.0 | TASK-1 扩展 + TASK-2 + PERF-1 + PERF-2 + 前端适配 |
| RNS1.2 | 客户与助教接口 | 6 | RNS1.0 | CUST-1 + CUST-2 + COACH-1 + 前端适配 |
| RNS1.3 | 三看板接口 | 7 | RNS1.0 | BOARD-1 + BOARD-2 + BOARD-3 + CONFIG-1 + 前端筛选修复 |
| RNS1.4 | CHAT 对齐与联调收尾 | 5 | RNS1.1-1.3 | CHAT 迁移 + FDW 验证 + 前后端联调 |
| 子 Spec | 名称 | 任务数 | 依赖 | 交付物 | 状态 |
|---------|------|:------:|------|--------|------|
| RNS1.0 | 基础设施与契约重写 | 6 | 无 | 全局中间件 + 重写后的 API 契约 + 前端跨页面参数修复 | ✅ 已完成 |
| RNS1.1 | 任务与绩效接口 | 6 | RNS1.0 | TASK-1 扩展 + TASK-2 + PERF-1 + PERF-2 + 前端适配 | ✅ 已完成 |
| RNS1.2 | 客户与助教接口 | 6 | RNS1.0 | CUST-1 + CUST-2 + COACH-1 + 前端适配 | ✅ 已完成 |
| RNS1.3 | 三看板接口 | 7 | RNS1.0 | BOARD-1 + BOARD-2 + BOARD-3 + CONFIG-1 + 前端筛选修复 | ✅ 已完成 |
| RNS1.4 | CHAT 对齐与联调收尾 | 5 | RNS1.1-1.3 | CHAT 迁移 + FDW 验证 + 前后端联调 | 🔧 实施中 |
总计30 个任务5 个子 spec。

View File

@@ -145,7 +145,7 @@
| TopCustomer: `name` | string | 客户姓名 |
| TopCustomer: `initial` | string | 姓名首字 |
| TopCustomer: `avatarGradient` | string | 头像渐变色 |
| TopCustomer: `heartEmoji` | string | 爱心 emoji❤️/💛/🤍 |
| TopCustomer: `heartEmoji` | string | 爱心 emoji💖/🧡/💛/💙 |
| TopCustomer: `score` | string | 关系指数 |
| TopCustomer: `scoreColor` | string | 分数颜色success/warning/gray |
| TopCustomer: `serviceCount` | number | 服务次数 |

View File

@@ -602,7 +602,7 @@ AbandonedTask 字段:
| `name` | string | 客户名 | ✅ |
| `initial` | string | 姓名首字 | ❌ |
| `avatarGradient` | string | 头像渐变色 | ❌ |
| `heartEmoji` | string | 爱心 emoji `❤️/💛/🤍` | ❌ |
| `heartEmoji` | string | 爱心 emoji `💖/🧡/💛/💙` | ❌ |
| `score` | string | 关系指数 `'9.5'` | ❌ 契约有 `total_spend`/`visit_count` 但无 score |
| `scoreColor` | string | 指数颜色 | ❌ |
| `serviceCount` | number | 服务次数 | ⚠️ 契约有 `visit_count` |

View File

@@ -250,7 +250,6 @@
| `dws.dws_member_visit_detail` | 客户到店明细 |
| `dws.dws_member_winback_index` | WBI 指数 |
| `dws.dws_member_newconv_index` | NCI 指数 |
| `dws.dws_member_recall_index` | 召回指数 |
| `dws.dws_member_spending_power_index` | SPI 指数(新建后映射) |
| `dws.dws_member_assistant_relation_index` | RS/OS/MS/ML 指数 |
| `dws.dws_member_assistant_intimacy` | 亲密度 |