diff --git a/docs/superpowers/plans/2026-09-08-management-console-batch-a-data-foundation.md b/docs/superpowers/plans/2026-09-08-management-console-batch-a-data-foundation.md index 507076d..3de0d68 100644 --- a/docs/superpowers/plans/2026-09-08-management-console-batch-a-data-foundation.md +++ b/docs/superpowers/plans/2026-09-08-management-console-batch-a-data-foundation.md @@ -33,7 +33,7 @@ | `yunke-server/app/storage/employee_metrics.py` | 宽表 + 通话小表生成 | 新建 | | `yunke-server/app/sync/cli.py` | 两条回填命令 | 修改 | | `yunke-server/tests/conftest.py` | `make_session` fixture | 修改 | -| `yunke-server/tests/test_management_models.py` 等 5 个测试文件 | 判别性测试 | 新建 | +| `yunke-server/tests/` 下 4 个新测试文件(test_management_models / test_first_reply_facts / test_lifecycle / test_employee_metrics) | 判别性测试 | 新建 | | `docs/superpowers/plans/2026-09-08-management-batch-a-verification.md` | Task 0 核实记录 | 新建 | | `docs/DEPLOY.md` | 两条回填命令部署注记 | 修改 | @@ -110,15 +110,6 @@ WHERE p.channel='voice_call' AND p.call_number IS NOT NULL - [ ] **Step 7: 选定 3-5 名回测员工** -```sql --- 消息量 top(高负载) -SELECT s.user_id, s.name, SUM(m.msg_sent+m.msg_recv) AS msgs -FROM stat_daily_staff s JOIN staff st ON s.user_id=st.user_id -JOIN staff st2 ON st2.user_id=s.user_id, stat_daily_staff m -- (示意;实际写法见下) -``` - -实际用(更直接): - ```sql SELECT user_id, SUM(msg_sent+msg_recv) AS msgs FROM stat_daily_staff GROUP BY user_id ORDER BY msgs DESC LIMIT 5; SELECT user_id, SUM(msg_sent+msg_recv) AS msgs FROM stat_daily_staff WHERE channel='whatsapp' GROUP BY user_id ORDER BY msgs DESC LIMIT 3; @@ -292,7 +283,7 @@ cd yunke-server && .venv/Scripts/python.exe -m pytest tests/test_management_mode - [ ] **Step 4: models.py 追加 6 个类** -在 `SensitiveScanState` 类之后、`__all__` 之前追加(文件头部已有所需 import:`String/Integer/BigInteger/Boolean/JSON/Text/Index/UniqueConstraint/mapped_column/Mapped/sa/dt/_updated_at`——若缺 `Text` 则补): +在 `SensitiveScanState` 类之后、`__all__` 之前追加(文件头部已有所需 import:`String/Integer/BigInteger/Boolean/JSON/Text/Index/mapped_column/Mapped/sa/dt/_updated_at`;唯一约束与 Text 列走 `sa.UniqueConstraint`/`sa.Text`,无需新增顶层 import): ```python # ========================================================================== @@ -559,7 +550,7 @@ git commit -m "feat(management): 批次A六表模型——fact/宽表/通话小 cd yunke-server && .venv/Scripts/python.exe -m alembic heads ``` -期望 `e6f7a8b9c0d1`(若不同,把迁移文件的 `down_revision` 改成实际 head)。 +期望 `f7a8b9c0d1e2`(customer_account_link 是当前链尾;若执行时又有新迁移合入,把迁移文件的 `down_revision` 改成实际 head)。 - [ ] **Step 2: 写迁移文件** @@ -569,7 +560,7 @@ cd yunke-server && .venv/Scripts/python.exe -m alembic heads """经营管理后台批次A:6 张新表(fact/宽表/通话小表/评分配置/快照/辅导) Revision ID: b7c8d9e0f1a2 -Revises: e6f7a8b9c0d1 +Revises: f7a8b9c0d1e2 Create Date: 2026-09-08 spec: docs/superpowers/plans/2026-09-08-yunke-management-console-implementation-v2.md §3。 @@ -582,7 +573,7 @@ import sqlalchemy as sa from alembic import op revision: str = "b7c8d9e0f1a2" -down_revision: str | Sequence[str] | None = "e6f7a8b9c0d1" +down_revision: str | Sequence[str] | None = "f7a8b9c0d1e2" branch_labels: str | Sequence[str] | None = None depends_on: str | Sequence[str] | None = None @@ -737,7 +728,7 @@ def downgrade() -> None: cd yunke-server && .venv/Scripts/python.exe -m alembic upgrade head ``` -期望:输出包含 `Running upgrade ... -> b7c8d9e0f1a2`。再跑一次(幂等)应无操作。 +期望:输出包含 `Running upgrade f7a8b9c0d1e2 -> b7c8d9e0f1a2`。再跑一次(幂等)应无操作。 - [ ] **Step 4: 验证表存在 + downgrade 冒烟** @@ -1096,7 +1087,7 @@ cd yunke-server && .venv/Scripts/python.exe -m pytest tests/test_first_reply_fac cd yunke-server && .venv/Scripts/python.exe -m pytest tests/ -q ``` -期望:10 passed;全量绿。 +期望:9 passed;全量绿。 - [ ] **Step 5: 提交** @@ -1427,6 +1418,7 @@ async def recompute_call_days(session: AsyncSession, days) -> int: rev.setdefault(uid, []).append(owner) written = 0 + staff_ids = await known_user_ids(session) # 幽灵过滤(v2 §1.3 #11) for day in sorted(set(days)): lo, hi = day_bounds_ms(day) calls = ( @@ -1449,7 +1441,7 @@ async def recompute_call_days(session: AsyncSession, days) -> int: # duration 单位按 Task0 核实结论调整(默认秒;若毫秒在此换算) agg: dict[str, dict] = {} for uid, number, ts, dur in calls: - if not uid: + if not uid or uid not in staff_ids: continue a = agg.setdefault( uid, {"cnt": 0, "sec": 0, "answered": 0, "followup": 0} @@ -2057,7 +2049,7 @@ git commit -m "docs(management): 批次A回填实测+对账记录+DEPLOY部署 - [ ] 同源判别测试通过(threads_answered ≡ fact 行数;多日未回串分母不膨胀); - [ ] 幽灵员工过滤测试通过; - [ ] 对账抽查:消息计数列与 msg_daily_stats friend 聚合一致;串类列差异全部可用跨夜串解释; -- [ ] 全量测试套件绿(现有 ~575 + 新增 ~25); +- [ ] 全量测试套件绿(现有 ~575 + 新增 30:模型 8 + fact 9 + lifecycle 7 + 通话/宽表 6); - [ ] 核实文档完整(五项核实 + 回测名单 + 回填耗时 + 对账记录); - [ ] 写钩子零改动(`git diff` 确认 summary.py/repos.py 未动)。