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

@@ -1,4 +1,9 @@
# AI_CHANGELOG
# - 2026-03-20 | Prompt: R3 项目类型筛选接口重建 | get_skill_types() 从虚构的 v_cfg_skill_type
# 改为查询 app.v_cfg_area_category真实 RLS 视图),头部插入"不限"选项;
# get_all_assistants() 移除 _skill_to_category/_category_to_skill 映射字典,
# 改为 _valid_categories set 直接比较_project_filter_clause() 移除 _project_to_category
# 映射字典,直接用 category_code。
# - 2026-03-20 | Prompt: RNS1.3 FDW 列名修正 | 修正 17 处列名映射design.md 理想名 → 实际视图列名),
# gift_rows 每个 cell 改为 GiftCell dict 避免 Pydantic 校验失败,
# v_dws_member_spending_power_index 降级为空列表skill_filter 暂不生效
@@ -525,15 +530,22 @@ def get_relation_index(
return records
# AI_CHANGELOG
# - 2026-03-20: R1 修复 — 5 列(table_charge_money, goods_money, assistant_pd_money,
# assistant_cx_money, settle_type)从 sl(service_log) 改为 sh(settlement_head)
# 添加 LEFT JOIN v_dwd_settlement_headWHERE settle_type 引用也改为 sh。
# 原因:这些字段属于结算单头表,不在助教服务日志视图中。
# 验证MCP 端到端查询通过。
def get_consumption_records(
conn: Any, site_id: int, member_id: int, limit: int, offset: int
) -> list[dict]:
"""
查询客户消费记录CUST-1 consumptionRecords 用)。
来源: app.v_dwd_assistant_service_log + v_dim_assistant。
⚠️ DWD-DOC 规则 1: totalAmount 使用 ledger_amount。
⚠️ DWD-DOC 规则 2: coaches fee 使用 assistant_pd_money / assistant_cx_money。
来源: app.v_dwd_assistant_service_log + v_dwd_settlement_head + v_dim_assistant。
⚠️ DWD-DOC 规则 1: totalAmount 使用 ledger_amount(来自 service_log
⚠️ DWD-DOC 规则 2: coaches fee 使用 assistant_pd_money / assistant_cx_money(来自 settlement_head
⚠️ 费用拆分字段table_charge_money, goods_money, settle_type来自 settlement_head。
⚠️ 废单排除: is_delete = 0。
⚠️ 正向交易: settle_type IN (1, 3)。
⚠️ DQ-6: 助教姓名通过 v_dim_assistant 获取。
@@ -553,18 +565,25 @@ def get_consumption_records(
sl.site_assistant_id AS assistant_id,
COALESCE(da.real_name, da.nickname, '') AS assistant_name,
da.level AS assistant_level,
sl.table_charge_money,
sl.goods_money,
sl.assistant_pd_money,
sl.assistant_cx_money,
sl.settle_type
sh.table_charge_money,
sh.goods_money,
sh.assistant_pd_money,
sh.assistant_cx_money,
sh.settle_type
FROM app.v_dwd_assistant_service_log sl
LEFT JOIN app.v_dim_assistant da
ON sl.site_assistant_id = da.assistant_id
AND da.scd2_is_current = 1
LEFT JOIN app.v_dwd_settlement_head sh
ON sl.order_settle_id = sh.order_settle_id
-- CHANGE 2026-03-20 | R1 修复: 费用拆分字段来自 settlement_head 而非 service_log
-- intent: table_charge_money/goods_money/assistant_pd_money/assistant_cx_money/settle_type
-- 属于结算单头表(dwd_settlement_head),通过 order_settle_id 关联
-- assumption: 每条 service_log 对应一条 settlement_head1:1 或 1:0
-- verify: SELECT count(*) FROM v_dwd_assistant_service_log WHERE order_settle_id IS NULL
WHERE sl.tenant_member_id = %s
AND sl.is_delete = 0
AND sl.settle_type IN (1, 3)
AND sh.settle_type IN (1, 3)
ORDER BY sl.create_time DESC
LIMIT %s OFFSET %s
""",
@@ -963,23 +982,17 @@ def get_coach_service_records(
def get_all_assistants(
conn: Any, site_id: int, skill_filter: str = "all"
conn: Any, site_id: int, skill_filter: str = "ALL"
) -> list[dict]:
"""
查询门店全部助教列表BOARD-1 用)。
CHANGE 2026-03-19 | P1 修复:通过 LEFT JOIN v_dws_assistant_project_tag 获取技能标签,
支持 skill_filter 筛选chinese/snooker/mahjong/karaoke/all
category_code 映射BILLIARD→chinese, SNOOKER→snooker, MAHJONG→mahjong, KTV→karaoke。
CHANGE 2026-03-20 | R3 修复:skill_filter 直接接收 category_code
BILLIARD/SNOOKER/MAHJONG/KTV/ALL去掉 chinese→BILLIARD 映射层
"""
# CHANGE 2026-03-19 | feiqiu-data-rules 规则 6: 等级名称从配置表动态读取
_skill_to_category = {
"chinese": "BILLIARD",
"snooker": "SNOOKER",
"mahjong": "MAHJONG",
"karaoke": "KTV",
}
_category_to_skill = {v: k for k, v in _skill_to_category.items()}
# CHANGE 2026-03-20 | R3 修复:去掉 _skill_to_category 映射,直接用 category_code
_valid_categories = {"BILLIARD", "SNOOKER", "MAHJONG", "KTV"}
level_map = get_level_map(conn, site_id)
records: list[dict] = []
@@ -987,7 +1000,7 @@ def get_all_assistants(
# 筛选条件:如果指定了技能,只返回被标记的助教
filter_clause = ""
params: tuple = ()
if skill_filter != "all" and skill_filter in _skill_to_category:
if skill_filter != "ALL" and skill_filter in _valid_categories:
filter_clause = """
AND da.assistant_id IN (
SELECT apt.assistant_id
@@ -995,7 +1008,7 @@ def get_all_assistants(
WHERE apt.category_code = %s AND apt.is_tagged = true
)
"""
params = (_skill_to_category[skill_filter],)
params = (skill_filter,)
cur.execute(
f"""
@@ -1015,11 +1028,11 @@ def get_all_assistants(
)
for row in cur.fetchall():
skill_codes = row[3] if row[3] else []
skill_labels = [_category_to_skill.get(c, c) for c in skill_codes if c]
# CHANGE 2026-03-20 | R3 修复:直接返回 category_code不再反向映射为旧值
records.append({
"assistant_id": row[0],
"name": row[1] or "",
"skill": ",".join(skill_labels) if skill_labels else "",
"skill": ",".join(c for c in skill_codes if c) if skill_codes else "",
"level": level_map.get(row[2], "") if row[2] else "",
})
return records
@@ -1190,19 +1203,13 @@ def _project_filter_clause(project: str) -> tuple[str, tuple]:
"""
生成项目筛选 SQL 片段(用于 BOARD-2 会员维度查询)。
CHANGE 2026-03-19 | P1 修复:通过 v_dws_member_project_tag 子查询实现项目筛选。
project 参数映射chinese→BILLIARD, snooker→SNOOKER, mahjong→MAHJONG, karaoke→KTV
CHANGE 2026-03-20 | R3 修复:project 参数直接接收 category_code
BILLIARD/SNOOKER/MAHJONG/KTV/ALL去掉 chinese→BILLIARD 映射层
返回 (sql_fragment, params)sql_fragment 以 AND 开头,可直接拼入 WHERE 子句。
"""
_project_to_category = {
"chinese": "BILLIARD",
"snooker": "SNOOKER",
"mahjong": "MAHJONG",
"karaoke": "KTV",
}
if project == "all" or project not in _project_to_category:
_valid_categories = {"BILLIARD", "SNOOKER", "MAHJONG", "KTV"}
if project == "ALL" or project not in _valid_categories:
return "", ()
category_code = _project_to_category[project]
clause = """
AND vd.member_id IN (
SELECT mpt.member_id
@@ -1210,7 +1217,7 @@ def _project_filter_clause(project: str) -> tuple[str, tuple]:
WHERE mpt.category_code = %s AND mpt.is_tagged = true
)
"""
return clause, (category_code,)
return clause, (project,)
def get_customer_board_recall(
@@ -2314,25 +2321,31 @@ def get_finance_coach_analysis(
def get_skill_types(conn: Any, site_id: int) -> list[dict]:
"""
CONFIG-1: 查询技能类型配置。
CONFIG-1: 查询项目类型筛选器配置。
来源: ETL cfg 表app.v_cfg_skill_type 或类似配置视图)。
来源: app.v_cfg_area_category基于 dws.cfg_area_category 去重,
排除 SPECIAL/OTHER。返回列表头部插入"不限"选项。
查询失败时由调用方降级返回空数组。
"""
# CHANGE 2026-03-20 | R3 修复:原查询虚构的 v_cfg_skill_type 视图不存在,
# 改为查询 v_cfg_area_category项目类型配置value 直接用 category_code
# BILLIARD/SNOOKER/MAHJONG/KTV前端枚举同步修改。
# 假设cfg_area_category 的 category_code 是稳定的业务标识,不会频繁变动。
with _fdw_context(conn, site_id) as cur:
cur.execute(
"""
SELECT skill_key, skill_label, emoji, css_cls
FROM app.v_cfg_skill_type
SELECT category_code, display_name, short_name
FROM app.v_cfg_area_category
ORDER BY sort_order
"""
)
items = []
# 头部插入"不限"选项(后端生成,不存数据库)
items: list[dict] = [{"key": "ALL", "label": "不限", "emoji": "", "cls": ""}]
for row in cur.fetchall():
items.append({
"key": row[0] or "",
"label": row[1] or "",
"emoji": row[2] or "",
"cls": row[3] or "",
"cls": "",
})
return items