# 看板全面排查修复计划 > 排查日期:2026-04-07 > 范围:客户看板(BOARD-2)8 维度 + 助教看板(BOARD-1)4 维度 > 状态:待实施(新对话执行) --- ## 一、已完成的修复 | # | 问题 | 修复内容 | 文件 | |---|------|----------|------| | ✅ | SPI 消费口径用 `pay_amount` 而非 `items_sum` | 3 处 settlement_head 查询改为 `items_sum`(DWD 规则 #1) | `apps/etl/connectors/feiqiu/tasks/dws/index/spending_power_index_task.py` | | ✅ | 潜力标签阈值 60 但数据范围 0-10 | 阈值 60→6,返回 `[{text, theme}]` 对象数组 | `apps/backend/app/services/fdw_queries.py` `_derive_potential_tags` | --- ## 二、待实施修复(共 10 项) ### P1 — 功能性 Bug #### Fix-1:客户看板项目筛选崩溃(C-1) - **问题**:`_project_filter_clause()` 硬编码 `vd.member_id`,但 6/8 维度主表别名不是 `vd`,选择项目筛选时后端 SQL 500 - **影响维度**:recall / balance / recharge / spend60 / freq60 / loyal - **不受影响**:recent(别名 `vd`)、potential(自写子查询) - **修复方案**:统一改为独立子查询模式 `member_id IN (SELECT member_id FROM app.v_dws_member_project_tag WHERE category_code = %s AND is_tagged = true)`,不依赖外层别名(参考 potential 维度已有写法) - **文件**:`apps/backend/app/services/fdw_queries.py` — `_project_filter_clause()` 函数及 6 个维度查询函数 #### Fix-2:助教看板 task 维度 callback 映射错误(A-3) - **问题**:callback 统计映射到 `relationship_building`,但业务上"回访"应对应 `follow_up_visit` - **修复**:`board_service.py` `_query_coach_tasks()` 中 callback 的 task_type 从 `relationship_building` 改为 `follow_up_visit` - **文件**:`apps/backend/app/services/board_service.py` ### P2 — 数据口径错误 #### Fix-3:recall 维度 visits_30d 实为 14d(C-2) - **问题**:`dws_member_winback_index` 无 `visits_30d` 字段,后端用 `visits_14d` 近似,前端显示"30天到店" - **修复**: 1. DDL:`dws.dws_member_winback_index` 加列 `visits_30d INTEGER DEFAULT 0` 2. ETL:WBI 计算任务增加 30 天到店次数统计 3. RLS 视图:`dws.v_dws_member_winback_index` 和 `app.v_dws_member_winback_index` 加新列 4. 后端:查询改用新字段 5. DDL 文档同步更新 - **文件**: - `db/etl_feiqiu/schemas/dws.sql`(DDL) - `apps/etl/connectors/feiqiu/tasks/dws/index/winback_index_task.py`(ETL) - `db/etl_feiqiu/schemas/app.sql`(RLS 视图) - `apps/backend/app/services/fdw_queries.py`(查询) - `docs/database/ddl/etl_feiqiu__dws.sql` / `etl_feiqiu__app.sql`(DDL 文档) #### Fix-4:balance 维度月均消耗和可用月数偏差 2 倍(C-3 + C-4) - **问题**: - `monthlyConsume`:直接用 `consume_amount_60d`(60天总额),标签"月均消耗"应为 `consume_amount_60d / 2` - `availableMonths`:`balance / consume_amount_60d`,应为 `balance / (consume_amount_60d / 2)` 即 `2 * balance / consume_amount_60d` - **示例**:余额 49780 / 60天消费 14521 → 当前显示 3.4 个月,实际应 6.9 个月 - **文件**:`apps/backend/app/services/fdw_queries.py` — `get_customer_board_balance()` #### Fix-5:freq60 柱状图数据源不一致(C-7) - **问题**:汇总数据来自 `v_dws_member_consumption_summary`(消费维度),但 8 周柱状图来自 `v_dwd_assistant_service_log`(助教服务维度),口径不一致 - **修复**:柱状图改为从消费汇总或结算维度获取周数据,与汇总口径一致 - **文件**:`apps/backend/app/services/fdw_queries.py` — freq60 相关查询 #### Fix-6:助教看板 sv 维度不响应时间筛选(A-4) - **问题**:函数接收 `start_date/end_date` 但 SQL 未使用,"消耗"始终是固定 60 天 - **修复**:`sv_consume` 的查询加入 `start_date/end_date` 过滤,使其随时间筛选联动 - **文件**:`apps/backend/app/services/fdw_queries.py` — `get_coach_sv_data()` ### P3 — 标签/文案修正 #### Fix-7:loyal 维度标签"近60天"→"近90天" - **问题**:ETL 关系指数实际使用 90 天窗口(`lookback_days: 90`),非 60 天 - **修复**:下拉选项文本 `最专一 近60天` → `最专一 近90天` - **文件**:`apps/miniprogram/miniprogram/pages/board-customer/board-customer.ts` — `DIMENSION_OPTIONS` #### Fix-8:potential 维度"月均到店"→"近30天到店" - **问题**:实际是 30 天到店天数(同一天多次只算一天),非"月均到店次数" - **位置**:board-customer 页面 → 最大消费潜力 tab → 4 列网格第 2 格 - **文件**:`apps/miniprogram/miniprogram/pages/board-customer/board-customer.wxml` #### Fix-9:recharge 维度"充值"→"累计充值" - **问题**:实际是 `SUM(pay_amount)` 历史累计充值总额,标签仅"充值"易误解为单次 - **位置**:board-customer 页面 → 最近充值 tab → 4 列网格第 2 格 - **文件**:`apps/miniprogram/miniprogram/pages/board-customer/board-customer.wxml` #### Fix-10:freq60 维度标签确认 - 此维度标签"最频繁 近60天"与实际口径一致(`visit_count_60d`),无需修改 --- ## 三、DWD 合规检查结果(通过) | 检查项 | 客户看板 | 助教看板 | |--------|----------|----------| | consume_money 禁止直接使用 | ✅ 全部走 DWS items_sum | ✅ | | settle_type IN (1,3) | ✅ 不直查结算表 | ✅ | | DQ-6 会员姓名通过 dim_member | ✅ | ✅ | | DQ-7 会员卡通过 dim_member_card_account | ✅ | ✅ | | 助教费用拆分 pd/cx | N/A | ✅ | --- ## 四、已知但不修的项 | 项 | 原因 | |----|------| | A-2:sv 维度客户重复计算 | 业务定义确认:每个助教看"我的客户总余额",允许跨助教重叠,作为催促消耗的参考值 | | A-5:后端 `dim_type` 冗余 | 功能无影响,两端映射一致 | --- ## 五、recall_detector 修复 + 任务统计需求(同期实施) > 需求确认日期:2026-04-07 ### 背景 `biz.coach_tasks` 当前 255 条记录,completed = 0。排查确认: - 系统完成逻辑设计正确(`recall_detector.py` 匹配服务记录→标记 completed) - 但两个技术问题导致完成检测不可靠 - 缺少"助教主动标记完成"能力 - 任务完成统计需要三个维度 ### 当前架构(调研结果) | 服务 | 文件 | 职责 | 调度方式 | |------|------|------|----------| | `recall_detector` | `app/services/recall_detector.py` | 检测客户到店→标记召回任务完成→生成回访任务 | event: `etl_data_updated`(当前无人触发) | | `task_generator` | `app/services/task_generator.py` | 根据 WBI/NCI/RS 指数生成召回/关系维护任务 | cron: `0 4 * * *`(每日凌晨 4 点) | 生成规则(四级漏斗): - `max(WBI, NCI) > 7` → `high_priority_recall` - `max(WBI, NCI) > 5` → `priority_recall` - `1 < RS < 6` → `relationship_building` - 不满足 → 不生成;`session_count > 0` 无任务时保底补充 `relationship_building` 回溯逻辑(已有):`recall_detector` 检测到新服务记录时,如匹配到活跃的 `follow_up_visit`,关闭旧任务(`superseded_by_new_visit`)并创建新回访。 ### Fix-11:recall_detector 增量时序 Bug + 编排顺序 **问题**:`recall_detector` 用 `create_time > last_run_at` 全局增量指针。任务创建与客户到店时序不一致时漏匹配。 **示例时间线**: 1. 4/5 08:00 — `recall_detector` 运行,`last_run_at = 08:00` 2. 4/5 14:00 — 客户张三到店,服务记录 `create_time = 14:00` 3. 4/6 02:00 — `task_generator` 为张三创建召回任务 4. 4/6 08:00 — `recall_detector` 再次运行,但 `last_run_at` 已更新,张三 4/5 的记录在上轮已扫描过(当时无任务)→ 永远不会重新匹配 **修复方案**: 1. 废弃 `last_run_at` 增量指针,改为:对所有活跃任务,检查 `dwd_settlement_head` 中是否有 `pay_time > task.created_at` 的结算记录(`settle_type IN (1,3)`),匹配 `(site_id, assistant_id, member_id)` 2. 合并运行顺序:ETL 完成后,统一编排器按顺序执行: ``` ETL 完成 → HTTP callback → 后端编排器: Step 1: recall_detector.run() # 先检查完成(含回溯) Step 2: task_generator.run() # 再生成新任务 ``` 3. 保留 `task_generator` 的每日 cron(`0 4 * * *`)作为兜底 4. 两个服务保持独立文件,仅在调度层串联 **文件**:`apps/backend/app/services/recall_detector.py`、调度编排器 ### Fix-12:ETL 完成后自动触发(HTTP callback) **问题**:`etl_data_updated` 事件无调用方,recall_detector 仅靠手动触发。 **业务影响**:ETL 每小时同步完新的飞球数据后,系统不会自动检查召回完成情况。客户回店后,助教看不到任务完成,必须有人手动在管理后台触发。 **修复方案**:ETL `api_full` pipeline 完成后,通过 HTTP callback 通知后端。 实现: 1. 后端新增 API:`POST /api/internal/etl-completed`(内部接口,仅限本机调用) 2. 该接口触发统一编排:`recall_detector.run()` → `task_generator.run()` 3. ETL orchestrator 在 pipeline 完成后调用此接口 4. 安全:校验来源 IP 或 shared secret **文件**: - `apps/backend/app/routers/internal.py`(新增) - `apps/etl/connectors/feiqiu/orchestration/` — pipeline 完成回调 - `apps/backend/app/services/trigger_scheduler.py` — 编排逻辑 ### Fix-13:任务完成统计三维度 **需求确认**:任务完成分三个统计维度: | 统计维度 | 触发条件 | 是否需已有任务 | 判定数据源 | |----------|----------|---------------|-----------| | 召回完成(广义) | 关联客户来店 + 有结算单 | 不需要,只要是 `dws_member_assistant_relation_index` 中的关联客户 | `dwd_settlement_head`(`settle_type IN (1,3)`) | | 优先/高优先召回完成 | 已有 `priority_recall` / `high_priority_recall` 任务 + 客户到店 | 需要 | 同上 + `coach_tasks.status = 'completed'` | | 回访完成 | 完成 `follow_up_visit` 任务(含回溯完成) | 需要 | `coach_tasks` + 回溯检测 | 关键定义: - **"关联客户"**:`dws_member_assistant_relation_index` 中有关系记录的客户 - **"来店"判定**:`dwd_settlement_head` 中有结算单(`settle_type IN (1,3)`) - **时间窗口**:不限间隔,只要来了就算一次 - **统计时间范围**:全量(任务系统上线至今) 完成类型标记(两种都要记录): | 完成类型 | 触发方式 | 当前状态 | |----------|----------|----------| | 自动完成 | 客户到店,系统匹配结算记录 | 逻辑存在但有 Bug(Fix-11 + Fix-12) | | 手动完成 | 助教在小程序中主动标记 | 当前不存在,需新建 | 手动完成需要: - 小程序端:任务卡片增加"标记完成"按钮 - 后端 API:`POST /api/xcx/tasks/{task_id}/complete`,body: `{type: "manual", note?: string}` - 数据库:`coach_tasks` 增加 `completion_type` 字段(`auto` / `manual`) - `coach_task_history` 增加 `action='manual_completed'` ### Fix-14:助教看板 task 维度展示 **展示位置**:微信小程序 board-coach 页面 task_desc 排序维度 **统计口径**(按助教/按月): - 召回完成数:`coach_task_history` 中 `action='completed'` 的任务数(优先+高优先) - 回访完成数:`follow_up_visit` 类型的完成数(含自动+手动+回溯) - 广义召回(关联客户来店):从 `dwd_settlement_head` + 关系表统计 **数据来源**:`biz.coach_tasks` + `biz.coach_task_history` + ETL 关系/结算表 **时间范围**:全量 **后续**:更详细的趋势报表作为独立需求 ### 实施依赖关系 ``` Fix-11(时序Bug+编排顺序)──┐ ├→ Fix-14(看板展示) Fix-12(ETL 自动触发)────────┤ │ Fix-13(手动完成+三维度统计)─┘ ``` 建议实施顺序:Fix-11 → Fix-12 → Fix-13 → Fix-14