开发机迁移
This commit is contained in:
242
docs/prd/2026-04-07__board-audit-fix-plan.md
Normal file
242
docs/prd/2026-04-07__board-audit-fix-plan.md
Normal file
@@ -0,0 +1,242 @@
|
||||
# 看板全面排查修复计划
|
||||
|
||||
> 排查日期: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
|
||||
Reference in New Issue
Block a user