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:
@@ -2,7 +2,7 @@
|
||||
|
||||
> 本文档记录项目中所有文档资产的位置、类型和内容概要,方便快速定位。
|
||||
> 归档规则见末尾「文档归档规则」章节;程序输出路径规范见 `docs/deployment/EXPORT-PATHS.md`。
|
||||
> 最后更新:2026-03-19(RNS1.3 三看板接口文档补充)
|
||||
> 最后更新:2026-03-20(RNS1.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 Schema(7 枚举 + ~40 响应 Schema) |
|
||||
| `app/schemas/xcx_config.py` | 配置相关 Pydantic Schema(SkillTypeItem) |
|
||||
|
||||
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 Schema(ChatHistoryResponse、ChatMessagesResponse、ReferenceCard、SendMessageResponse、ChatStreamRequest 等) |
|
||||
|
||||
Monorepo 级属性测试(`tests/`):
|
||||
|
||||
| 路径 | 内容 |
|
||||
@@ -216,6 +224,12 @@ Monorepo 级属性测试(`tests/`):
|
||||
| `tests/test_rns12_properties.py` | RNS1.2 属性测试(14 个 Property,Hypothesis 框架) |
|
||||
| `tests/test_board_properties.py` | RNS1.3 属性测试(18 个测试函数,12 个 Property,Hypothesis 框架) |
|
||||
| `tests/test_board_service_unit.py` | RNS1.3 看板工具函数单元测试 |
|
||||
| `tests/test_rns1_chat_title_properties.py` | RNS1.4 属性测试:标题生成优先级(Property 4,Hypothesis 框架) |
|
||||
| `tests/test_rns1_chat_reuse_properties.py` | RNS1.4 属性测试:对话复用规则(Property 6) |
|
||||
| `tests/test_rns1_chat_reference_card_properties.py` | RNS1.4 属性测试:referenceCard round trip(Property 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 消费力指数 |
|
||||
|
||||
---
|
||||
|
||||
@@ -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 客户 + 四级 emoji(RNS1.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 |
|
||||
|
||||
@@ -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) |
|
||||
|
||||
@@ -1,24 +1,33 @@
|
||||
# BD_Manual:biz 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_conversations(8 字段)
|
||||
#### biz.ai_conversations(13 字段)
|
||||
|
||||
| 字段 | 类型 | 约束 | 说明 |
|
||||
|------|------|------|------|
|
||||
@@ -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 新增** — 关联上下文 ID:task 入口为 taskId,customer 入口为 customerId,coach 入口为 coachId,general 为 NULL |
|
||||
| `title` | VARCHAR(200) | 可空 | **RNS1.4 新增** — 对话标题:自定义 > 上下文名称 > 首条消息前20字 |
|
||||
| `last_message` | TEXT | 可空 | **RNS1.4 新增** — 最后一条消息内容摘要(截断至100字) |
|
||||
| `last_message_at` | TIMESTAMPTZ | 可空 | **RNS1.4 新增** — 最后消息时间,用于历史列表排序和对话复用时限判断 |
|
||||
|
||||
#### biz.ai_messages(6 字段)
|
||||
#### biz.ai_messages(7 字段)
|
||||
|
||||
| 字段 | 类型 | 约束 | 说明 |
|
||||
|------|------|------|------|
|
||||
@@ -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_cache(9 字段)
|
||||
|
||||
@@ -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 流式对话等功能 |
|
||||
| 后端 API(RNS1.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 迁移)
|
||||
|
||||
按逆序 DROP(CASCADE 处理外键依赖):
|
||||
|
||||
```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)
|
||||
```
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# BD_Manual:app 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 权限
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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包厢 BILLIARD(3台)
|
||||
('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'),
|
||||
('麻将房', 'M1', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, '麻将房-M1'),
|
||||
('麻将房', '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),
|
||||
('麻将房', 'M1', 'MAHJONG', '🀄 麻将/棋牌', '🀄 麻将/棋牌', '🀄', 'EXACT', 10, TRUE, '麻将房-M1', 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);
|
||||
|
||||
|
||||
-- =============================================================================
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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`
|
||||
> 所有端点需 JWT(approved 状态),使用 `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) | 是 | 上下文 ID(taskId / customerId / coachId,general 时为空) |
|
||||
| `page` | number (query) | 否 | 页码,默认 1 |
|
||||
| `pageSize` | number (query) | 否 | 每页条数,默认 50,最大 100 |
|
||||
|
||||
#### 响应结构(CHAT-2a / CHAT-2b 共用)
|
||||
|
||||
```typescript
|
||||
interface ChatMessagesResponse {
|
||||
chatId: string // 对话 ID(customerId 入口时由后端返回)
|
||||
chatId: number // 对话 ID(上下文入口时由后端返回,供后续发送消息使用)
|
||||
items: Array<{
|
||||
id: string
|
||||
id: number
|
||||
role: 'user' | 'assistant'
|
||||
content: string
|
||||
createdAt: string // 统一时间字段名(需求 5.8.1),ISO 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 | 上下文 ID(taskId / 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) |
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
| `name` | 客户姓名 |
|
||||
| `initial` | 姓氏首字(头像占位) |
|
||||
| `avatarGradient` | 头像渐变色 key |
|
||||
| `heartEmoji` | 爱心 emoji(❤️/💛/🤍) |
|
||||
| `heartEmoji` | 爱心 emoji(💖/🧡/💛/💙) |
|
||||
| `score` | 亲密度评分 |
|
||||
| `scoreColor` | 评分颜色 key |
|
||||
| `serviceCount` | 服务次数 |
|
||||
|
||||
@@ -286,15 +286,102 @@ async def build_page_text(
|
||||
- 主观信息需在 Prompt 中标注【来源:XXX,请甄别信息真实性】
|
||||
- reference 从 `ai_cache` 获取 app8 最新 + 最近 2 套历史
|
||||
|
||||
### 3.6 应用 1:页面上下文文本化
|
||||
### 3.6 应用 1:biz_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` | contextId(taskId) | — |
|
||||
| `customer-detail` | contextId(memberId) | — |
|
||||
| `coach-detail` | contextId(assistantId) | — |
|
||||
| `board-finance` | — | `timeDimension`(时间维度)、`areaFilter`(区域筛选) |
|
||||
| `board-customer` | — | `dimension`(排序维度)、`typeFilter`(类型筛选) |
|
||||
| `board-coach` | — | `dimension`、`projectFilter`、`timeDimension` |
|
||||
| `performance` | — | `timeDimension` |
|
||||
| `task-list` | contextId(taskId) | — |
|
||||
| `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_params(User_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 对话验证上下文感知)
|
||||
|
||||
@@ -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。
|
||||
|
||||
|
||||
@@ -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 | 服务次数 |
|
||||
|
||||
@@ -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` |
|
||||
|
||||
@@ -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` | 亲密度 |
|
||||
|
||||
Reference in New Issue
Block a user