diff --git a/docs/superpowers/plans/2026-09-08-management-batch-a-verification.md b/docs/superpowers/plans/2026-09-08-management-batch-a-verification.md new file mode 100644 index 0000000..e378f1e --- /dev/null +++ b/docs/superpowers/plans/2026-09-08-management-batch-a-verification.md @@ -0,0 +1,163 @@ +# 批次A Task 0 核实记录:五项核实 + 回测名单 + +- **日期**:2026-09-09 +- **方式**:`docker exec -i yunke-mysql-1 mysql -uyunke -pyunke yunke -e ""`(容器名以 `docker ps` 实测为准,确认为 `yunke-mysql-1`) +- **用途**:本记录的全部结论直接约束批次A(数据底座)后续任务的实现细节;偏差处显式标注「影响 Task N」。任务编号引用 [2026-09-08-management-console-batch-a-data-foundation.md](./2026-09-08-management-console-batch-a-data-foundation.md)。 +- **数据快照范围**:`stat_daily_staff` / `msg_daily_stats` 覆盖 20251024 ~ 20260907;`phone_call.start_call_time` 覆盖 2025-10-18 ~ 2026-09-05。 + +--- + +## ① 员工主数据(staff 表) + +| 指标 | 实测 | +|---|---| +| staff 总数 | **41**(与计划假设 ≈41 一致) | +| `is_on_job=1` | 41(全部在职) | +| `wechat_account` DISTINCT user_id | 41 | +| `whatsapp_account` DISTINCT user_id | 20 | +| 微信幽灵账号(user_id 不在 staff) | **0** | +| WA 幽灵账号(补充核实) | **0** | +| `stat_daily_staff` DISTINCT user_id | 40(其 user_id 全部在 staff 内;员工 `FD685A5620184156BE75A8D73FB72F64` 无任何消息统计行) | + +**结论**:账号表 → staff 无幽灵映射,`known_user_ids` 过滤(v2 §1.3 #11)零损耗,不会意外丢行。两个渠道的 account_map 产出 user_id 均能落到 staff。**无偏差,Task 5/6 按计划实现即可。** + +--- + +## ② phone_call 列与单位 + +表结构关键列:`call_id`(PK)、`channel`、`start_call_time`(bigint)、`call_duration`(int)、`call_status`(int)、`incoming_call`(int)、`user_id`、`call_number`、`fetched_at`、`updated_at`。 + +### channel 分布 + +| channel | 行数 | +|---|---| +| `voice_call` | 2,504 | +| `wa_voice_call` | 686 | +| 合计 | **3,190**(与已知 3,190 条一致) | + +### call_duration 单位:**秒**(确认) + +| 指标 | 实测 | +|---|---| +| 总和 | 263,651 秒 = **73.24 小时**(对照已知约 73 小时,吻合) | +| 均值 | 82.65 秒(对照预期均值 ≈82 秒,吻合) | + +### call_status × duration 交叉 + +| call_status | 行数 | duration=0 | duration>0 | 语义判定 | +|---|---|---|---|---| +| 1 | 1,508 | 31 | 1,477 | **接通**(含 31 条 0 秒即挂) | +| 2 | 1,682 | 1,682 | 0 | **未接通**(未接/拒接/取消,与方向无关) | + +### incoming_call 分布 + +| incoming_call | 行数 | 语义 | +|---|---|---| +| 1 | 1,764 | 呼入 | +| 0 | 1,426 | 呼出 | + +### start_call_time:**毫秒 epoch**(确认) + +13 位整数;`FROM_UNIXTIME(x/1000)` 解出 max = 2026-09-05 16:00:55(合理近期),按秒解释则超范围。min = 2025-10-18。与 `message.timestamp` 毫秒同口径。 + +### call_number 形态(按 channel 完全可分) + +- `voice_call` → 微信 ID:`wxid_*` 或自定义微信号(`csf7169708`、`yuanye751380`、`Maple_1988` 等) +- `wa_voice_call` → WA JID:`2348182388888@s.whatsapp.net` 等 + +### 与计划默认假设的偏差(重要) + +1. **计划假设 #4「PhoneCall.channel 是通话类型(phone_call/voice_call)非消息渠道」不完全成立**:实测无 `phone_call`(蜂窝)值,且 `wa_voice_call` **能**从 channel 列判别 WA。计划 Task 5 注释「voice_call 行无法从列判别微信/WA」不成立。**影响 Task 5**:`_followup_exists` 可按渠道收窄——`voice_call` 只查该员工 wechat 账号的 1:1 消息、`wa_voice_call` 只查 whatsapp 账号(当前双渠道任一匹配的写法也不会错——wxid 不会撞 WA JID,仅多一次无效查询;收窄属优化非必需)。 +2. `call_duration` 单位=秒、`start_call_time`=毫秒 epoch:**与计划默认一致,Task 5 无需换算**(`day_bounds_ms` 直接用)。 +3. 接通判定:计划默认 `duration>0`。实测 `call_status=1 AND duration>0` = 1,477 行,`duration>0` 口径与之差 31 行(31 条接通但 0 秒,占 1.0%)。**影响 Task 5**:`answered=dur>0` 口径可用;若追求语义精确可改 `call_status=1`(含 0 秒接通),差异 ≤1%,实现时任选其一并在表注释写明。 + +--- + +## ③ customer 归属字段填充率 + +| 指标 | 实测 | +|---|---| +| 总行数 | 16,339 | +| `user_id` 填充 | 16,339(**100%**) | +| `owner_account_id` 填充 | 16,339(**100%**) | +| `delete_flag=1` | 53 | + +分渠道:wechat 7,040(owner 填充 7,040)/ whatsapp 9,299(owner 填充 9,299)。 + +### 两条归属路径一致性(全表校验,非抽样) + +`customer.user_id` vs `owner_account_id → wechat_account.wx_id / whatsapp_account.uid → user_id`: + +| 渠道 | 校验行数 | 一致 | +|---|---|---| +| whatsapp | 9,299 | 9,299 | +| wechat | 7,040 | 7,040 | + +**结论**:两条路径 100% 等价、零冲突。生命周期归属覆盖面完整。**影响 Task 6**:默认 `owner_account_id → account_map → user_id` 路径确认可用;`customer.user_id` 列可直接当冗余等价路径使用(如需省一次 join,实测安全)。 + +补充观察(**影响 Task 3/6 的 peer 对齐**):`customer.friend_id` 形态有三种——微信 `25984981693625561@openim`、WA `12022946448@s.whatsapp.net`、WA `108976926654482@lid`。与 `message.talker_id` 对齐时须注意后缀形态一致(Task 3 实现 peer 匹配时以此为准,不做猜测性截断)。 + +--- + +## ④ 群占比(msg_daily_stats,量化混合口径偏差) + +| channel | peer_kind | 行数 | 消息量(send+recv) | 会话线程(cust_threads) | +|---|---|---|---|---| +| wechat | friend | 38,343 | 269,074 | 82,879 | +| wechat | room | 16,234 | 609,089 | 26,220 | +| whatsapp | friend | 11,673 | 77,994 | 14,178 | +| whatsapp | room | 866 | 8,086 | 1,402 | +| **合计** | | **67,116** | **964,243** | **124,679** | + +占比汇总: + +| 维度 | room 占比 | 说明 | +|---|---|---| +| 微信消息量 | **69.4%**(609,089/878,163) | 微信消息近七成来自群 | +| WA 消息量 | 9.4%(8,086/86,080) | WA 几乎纯 1:1 | +| 总消息量 | **64.0%**(617,175/964,243) | | +| 总会话线程 | **22.2%**(27,622/124,679) | | + +**结论**:若现有看板首响/回复率不分 `peer_kind`,消息量维度被群污染 64%、线程维度 22%——污染主要来自微信侧(微信消息 69% 是群消息)。批次A `first_reply_fact` / `employee_metric_daily` 强制 `peer_kind='friend'` 过滤是必要的,不能沿用混合口径。 + +--- + +## ⑤ call_followup join 键(call_number ↔ message.talker_id,1:1 消息) + +| channel | 分母(call_number 非空) | 命中(talker_id 匹配且非群消息) | 命中率 | +|---|---|---|---| +| `voice_call` | 2,504 | 2,378 | **95.0%** | +| `wa_voice_call`(补充核实) | 686 | 681 | **99.3%** | + +**结论**:双渠道命中率均远超 50% 阈值,`call_followup_count` 一期**高置信**,无需降级标记、无需在口径中注记低置信。**影响 Task 5**:24h 跟进判定按计划实现;未命中的 5%多为客户删除/无文字往来的纯通话对象,属正常损耗。 + +--- + +## 回测员工名单 + +选取依据:消息量/WA 量/活跃群排名 + 低样本端(见文末各候选分渠道数据)。覆盖:微信为主、WA 为主、群运营为主、高负载、低样本(前三类与高负载有合并)。 + +| # | user_id | 名字 | 覆盖类型 | 关键数据(20251024~20260907) | +|---|---|---|---|---| +| 1 | `369D257EEC9A4357935FDE19DDD99CAA` | 王云青 | **高负载 + 群运营为主 + 微信为主**(合并) | 消息量 #1(162,740:微信 162,371 / WA 369);活跃群 #1(4,036);通话 431+9 | +| 2 | `9C0B195A2C234C829FF507E011B4B460` | 田瑞志 | **WA 为主** | WA 消息 9,986 / 总 11,597 = 86%;WA 通话 28 | +| 3 | `F99F6557C2904A7E8F4C5A3BD8A597A1` | 张士蓓 | **双通道混合**(WA 量 #1,群 #2) | 微信 91,936 + WA 12,529;活跃群 2,273;通话最多(471 微信 + 97 WA) | +| 4 | `DC48798A5EA24DBB8B31A89CB858E08B` | 15639270965 | **纯微信无 WA 行**(验证 WA 缺失路径) | 微信 78,680,`stat_daily_staff` 无 whatsapp 行;通话 3 | +| 5 | `946C08639BF84DFEBBF5EC2A607AE1FA` | 18539430257 | **低样本**(验证空数据路径) | 消息 8 条,无群、无通话、无 WA | + +> 名单中的 #1/#3 建议作为 Task 8「本地全量回填实测」的 2 名回测员工(数据最厚、双通道+通话+群全齐)。 + +--- + +## 偏差汇总(对后续任务的约束清单) + +| # | 偏差/确认 | 影响 | +|---|---|---| +| 1 | `phone_call.channel` 实测仅 `voice_call`(微信)/`wa_voice_call`(WA)两值,**无蜂窝 `phone_call` 值**;channel 可判别渠道,计划「无法从列判别微信/WA」结论修正 | Task 5(followup 可按渠道收窄,属优化非必需) | +| 2 | `call_duration` 单位=**秒**(263,651s=73.24h≈已知 73h);`start_call_time`=**毫秒 epoch** | Task 5(默认口径正确,无需换算) | +| 3 | 接通判定 `dur>0` vs `call_status=1` 差 31 行(1.0%),前者少计 31 条 0 秒接通 | Task 5(任选其一,注释写明) | +| 4 | `customer.owner_account_id` 填充 **100%**,与 `user_id` 列全表 100% 等价 | Task 6(默认 owner 路径确认;可直接用 user_id 列省 join) | +| 5 | `customer.friend_id` 有 `@openim` / `@lid` / `@s.whatsapp.net` 三种后缀形态 | Task 3/6(peer 对齐按实测形态匹配,勿截断) | +| 6 | 群消息占总消息量 64%(微信侧 69%) | Task 3/6(friend 过滤必须,佐证 spec 口径) | +| 7 | call_number↔talker_id 命中率 voice_call 95.0% / wa_voice_call 99.3% | Task 5(call_followup_count 高置信,不降级) | +| 8 | staff 41 行无幽灵账号;`stat_daily_staff` 仅 40 名员工有行 | Task 5/6(known_user_ids 过滤零损耗) |