2026-08-26 · dev3 东京云机 · 长官令「先 verify(物理 grep)· 不漏证据 · 查清楚再继续」
基线:worktree 分支feature/org-insights-simplify@451d3125
原型最终版:org-insights-redesign-v16.html(v16 第二十六轮)
现网前端:web/app/(dashboard)/admin/org-insights/page.tsx(1168 行)
现网后端:app/services/org_insight_service.py(37,718 B)、app/api/admin_activity.py(31,253 B)
一句话结论:原型要的东西,后端 80% 已经现成;有 3 个硬缺口必须先定怎么办,其中 1 个是我上一轮判断失误留下的。
现网 GET /api/admin/org-insights 的 OrgInsightRow(admin_activity.py:146-177)已经返回:
| 原型要的 | 后端字段 | 证据 |
|---|---|---|
| 九种状态标签 | status / status_label / action / evidence |
org_insight_service.py:50-82 九个枚举 + 中文名 + 建议动作 |
| 使用频率折线图(30 天) | spark30: list[int] |
admin_activity.py:177 |
| 「近 N 天用满 N 天」 | active_days_7 |
admin_activity.py:169 |
| 来源构成(席位/链接/门店端) | src_counts |
admin_activity.py:138-144,含 miniapp 第四类 |
| 三窗次数 / 在用席位 | events_day|week|month、events_prev_week、seats_active_day|week|month |
:165-171 |
| 席位总数 / 从未激活 | seats_total、seats_never_activated |
:158-160 |
| 到期天数 | days_to_expiry、next_expiry_at、all_expired |
:161-163 |
| 建档天数 | created_days |
:149 |
| 值得深挖的分子分母 | reports、plans_confirmed、quoted_amount |
:173-175 |
| 数据不可信判定依据 | has_credential、trusted |
:176、org_insight_service.py:486 |
| 频率分档 | freq(high/mid/low/idle) |
:157 |
| 口径阈值 + 存得住 | thresholds + GET/PUT /org-insights/config |
admin_activity.py:746/755 |
| 席位明细(含三类操作计数) | GET /org-insights/{id}/seats |
admin_activity.py:793 |
| 弹窗「概览」三块 | GET /org-insights/{id}/usage → overview.actions / data_generated / reports |
org_insight_service.py:156-171 |
| 弹窗「搜过什么(原文)」 | 同上 → cases.searched |
org_insight_service.py:181-184 |
后端 org_insight_service.py:290-317 的判定链:
untrusted → churned → no_seat → renew_window → cliff → never_active → low_util → worth_digging → healthy
顺序、命中即停的逻辑、九个枚举名,与原型 calc() 完全对得上。
这意味着原型里的「九状态 + 每条注解 + 四个可调阈值」不需要动后端判定逻辑,只是前端展示层重做。
| 原型功能 | 做法 |
|---|---|
| 五类筛选框(要立刻处理/该我们跟进/…) | 后端只给 status,五类是 status → bucket 的映射,前端做一张映射表即可 |
| 分页(每页 15 条) | 后端一次返回完整 ranking 数组,前端切片,无需后端分页 |
| 状态标签悬浮注解 | 纯前端文案(长官 8-26 给的 9 条),数字从 thresholds 取 |
| 柱子改状态色 / 数字进柱 / 长尾保底宽度 | 纯样式 |
| 席位「席」字徽章 + 账号胶囊 | 纯样式 |
| 折线图 7/30 天切换 + 每天悬浮出次数 | spark30 已有 30 天数据,切 7 天 = 取数组后 7 位,前端算 |
原型「席位详情 → 问了什么」保留了 5 块:患者年龄段 / 近视度数档 / 防控方法 / 疗程长度 / 关注的结果。
物理 grep(带对照组):
org_insight_service.py 全 app/*.py
input_types 3 3 ← 对照组:后端确实有的词
top_questions 2 2 ← 对照组
searched 2 2 ← 对照组
patient_type 0 34
myopia_degree 0 31
outcome_tag 0 33
对照组证明查法有效。结论:这套 facet 词表在系统里确实存在
(b3_ingest_service.py / find_similar_service.py / b3_sticker.py 等 30+ 处),
但机构洞察的 usage 接口一处都没用到。
后端 build_usage_detail 的 questions 实际只返回:
total / input_types / top(高频词)/ samples(问题原文样本)—— 没有任何 facet 聚合。
为什么会留下这块:原型里这 5 块自带 ready:1(现成)标记,上一轮长官令「只保留现成的」时我照标记留了。
但那个标记的原意是「b3 的词表现成」,不等于「这个接口能返回」——
要做出来得先给每条提问打上 facet 标签再 GROUP BY,是新功能,不是改一行 select。
这是我的判断失误,如实报。
users.username)没被取出来org_insight_service.py:610:
select(User.id, User.experience_store_name, User.seat_expires_at)
只取了门店名,返回 seat_name = 门店名 or f"席位 {seat_id}"(:641)。
原型第九轮起席位那列显示的是登录账号(10 位数字,格式见 seat_service.py:122)。
users.username 对 org_seat 角色强制非空(user.py CheckConstraint),给得出,加一个字段即可。
admin_activity.py:720 / 796 / 807 三处都写死:
range: str = Query("7d", pattern="^(7d|30d|90d)$")
原型第十四轮做的「自定义起止日期」需要后端接受任意区间(或至少 from/to 两个参数)。
要改 3 个端点的入参 + 下游取数逻辑。
删:副标题 :307 · 顶部橙色警示条 :345-363 · 时间下拉 :312-323 ·
卡片 hint 与环比行 :368-377 · 排行副标题 :389-393 · 「共 N 家 · 正在看 N 家」:422 ·
使用频率分档整块 :459+
改:排行行加状态标签列(悬浮注解)· 柱子改状态单色 + 数字进柱 + 长尾保底 ·
展开层重排(来源构成条 → 折线图 → 席位行)· 席位账号胶囊 + 「席」徽章 ·
抽屉重构为九状态 + 注解 + 四滑块 · 弹窗按「只留现成」精简 · 「用来干什么」→「席位详情」
新增:数据分类筛选框 · 时间范围框 · 分页(每页 15)· 折线图交互(7/30 切换 + 每天悬浮)
接口调用点无需新增:现有 5 处(主数据 / config 读写 / seats / usage)已覆盖,
除非缺口 2、3 要动后端。
username?(改动小,1 行 select + 1 行返回)range 入参?还是先只做 7/30/90 三档(前端下拉给三个选项)?# 后端契约
sed -n '138,235p' app/api/admin_activity.py # 全部响应模型字段
grep -n '"untrusted"\|"churned"\|…' app/services/org_insight_service.py # 九状态
sed -n '/^async def build_usage_detail/,/^async def /p' app/services/org_insight_service.py
# 缺口对照组
for k in input_types top_questions searched patient_type myopia_degree outcome_tag; do
printf "%-16s service=%s 全app=%s\n" "$k" \
"$(grep -c "$k" app/services/org_insight_service.py)" \
"$(grep -rc "$k" app/ --include=*.py | awk -F: '{s+=$2}END{print s}')"
done
# 前端现状
grep -n "useState" "web/app/(dashboard)/admin/org-insights/page.tsx"
grep -n "apiFetch(" "web/app/(dashboard)/admin/org-insights/page.tsx"
没做的:云机没装 web/node_modules,tsc / vitest 跑不了;
后端 pytest 依赖链要到 faiss,本机也跑不了。这两条到 test 环境验。