Commit ba12851e by luoqi

fix(分配): 团队状态六列全部按诊所收窄 —— 别家诊所的负荷串进本诊所工作台

杭州大厦主管工作台显示「23 位在岗 · 在手 53」,其中李欣 52 条。实查:

  杭州大厦自己的 followup_plans:active 17,938 / superseded 4,137 / **assigned 0**
  那 52 条的 target_clinic_id 全是上海世纪大道 —— 07-30 她在**那边**自己认领的
  (plan_event_logs: event=claim, actor=assignee=5679, 08:52~10:54)

⇒ 杭州大厦一条都没分出去,整栏是幻觉。

根因:**名册按诊所取、计数却按全租户取**。客服在哪家做过回访就进哪家名册
(名册从回访行为反推,宿主没给权威员工名单),跨诊所工作的人于是把别家的
在手/超期/完成全带了过来。`assignee_user_id IN (本诊所名册)` 挡不住 ——
它限的是**人**,不是**单**。李欣在杭州大厦有 3,225 条回访、在上海世纪大道
有 196 条,两家名册都有她。

六支查询全改, 不只改「在手」:同一屏上"在手 0 / 超期 41"这种自相矛盾
比全错更难查。plan_event_logs / plan_executions 没有诊所列,经 plan_id 反查
followup_plans.target_clinic_id(**归属**), 不是 executor_clinic_id(执行地)。

生产数据实测(只读):
  杭州大厦   修复前 5679→52 / 2998→1(合计 53)  修复后 **0 行** ✓
  上海世纪大道 修复后 5679→52 / 1928→2          ← 52 条归位 ✓

代价(已知并接受):跨诊所客服本屏不再显示他在别家的负荷。分配前想知道
"这人在别处忙不忙"需要另加一列(产品未定), 别为此把诊所过滤去掉。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
parent bbc530b9
......@@ -882,6 +882,26 @@ export class PlanAssignmentService {
* ⚠️ 退回率**两个分母都给**:「退回 3 / 已处置 21 = 14.3%,另有 2 条没动」。
* 没动的数量本身是信号,只给一个百分比会把它藏起来(文档「三个必须先定的口径」)。
*
* 🔴 **每一支都必须按 `target_clinic_id` 收窄到本诊所**(2026-08-20 修的线上 bug)。
* ── 现场 ──────────────────────────────────────────────────
* 杭州大厦主管工作台显示「23 位在岗 · 在手 53」,其中李欣 52 条。实查:
* · 杭州大厦自己的 followup_plans:active 17,938 / superseded 4,137 / **assigned 0**
* · 那 52 条的 target_clinic_id 全是上海世纪大道,07-30 她在**那边**自己认领的
* ⇒ 一条都不属于杭州大厦,整栏是幻觉。
*
* 根因是**名册按诊所取、计数却按全租户取**:客服在哪家做过回访就进哪家名册
* (名册从回访行为反推,宿主没给权威员工名单),跨诊所工作的人于是把别家的
* 在手/超期/完成全带了过来。`assignee_user_id IN (本诊所名册)` 挡不住这件事 ——
* 它限的是**人**,不是**单**。
*
* ⚠️ 六支全改,⛔ 不许只改「在手」:同一屏上"在手 0 / 超期 41"这种自相矛盾
* 比全错更难查。plan_event_logs / plan_executions 没有诊所列,经 plan_id
* 反查 `followup_plans.target_clinic_id`(**归属**),⛔ 不是 executor_clinic_id
* (那是执行地,同一条单可能在别家诊所打的电话)。
*
* ⚠️ 代价(已知并接受):跨诊所的客服,本屏不再显示他在别家的负荷。分配前想知道
* "这人在别处忙不忙",需要另加一列(产品未定),⛔ 别为此把诊所过滤去掉。
*
* ⛔ 名册**沿用 AgentRosterService**,不在这里另查一份:
* 分配时问「还能吃多少」、跟踪时问「手上压了多少」是同一份数据的两种读法(T10)。
* 另查一份必然口径漂移,而漂了不报错。
......@@ -911,6 +931,7 @@ export class PlanAssignmentService {
FROM followup_plans
WHERE host_id = ${scope.hostId}::uuid
AND tenant_id = ${scope.tenantId}
AND target_clinic_id = ${clinicId}
AND status = 'assigned'
AND superseded_at IS NULL
AND assignee_user_id IN (${Prisma.join(ids)})
......@@ -943,6 +964,7 @@ export class PlanAssignmentService {
FROM followup_plans
WHERE host_id = ${scope.hostId}::uuid
AND tenant_id = ${scope.tenantId}
AND target_clinic_id = ${clinicId}
AND status = 'assigned'
AND superseded_at IS NULL
AND assignment_expires_at IS NOT NULL
......@@ -965,6 +987,7 @@ export class PlanAssignmentService {
FROM followup_plans
WHERE host_id = ${scope.hostId}::uuid
AND tenant_id = ${scope.tenantId}
AND target_clinic_id = ${clinicId}
AND status = 'assigned'
AND superseded_at IS NULL
AND assignment_expires_at IS NOT NULL
......@@ -988,6 +1011,8 @@ export class PlanAssignmentService {
AND event = ${PlanEventType.RELEASE}
AND created_at >= ${since}
AND actor_user_id IN (${Prisma.join(ids)})
AND EXISTS (SELECT 1 FROM followup_plans p
WHERE p.id = plan_event_logs.plan_id AND p.target_clinic_id = ${clinicId})
GROUP BY actor_user_id`);
/**
......@@ -1002,6 +1027,8 @@ export class PlanAssignmentService {
AND tenant_id = ${scope.tenantId}
AND created_at >= ${since}
AND operator_user_id IN (${Prisma.join(ids)})
AND EXISTS (SELECT 1 FROM followup_plans p
WHERE p.id = plan_executions.plan_id AND p.target_clinic_id = ${clinicId})
GROUP BY operator_user_id`);
/**
......@@ -1031,6 +1058,8 @@ export class PlanAssignmentService {
AND e.event = ${PlanEventType.ASSIGN}
AND e.created_at >= ${since}
AND e.assignee_user_id IN (${Prisma.join(ids)})
AND EXISTS (SELECT 1 FROM followup_plans p
WHERE p.id = e.plan_id AND p.target_clinic_id = ${clinicId})
AND NOT EXISTS (
SELECT 1 FROM plan_event_logs r
WHERE r.plan_id = e.plan_id
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment