Files
csctalon/docs/standards/人均效率算法说明.md
tanzk cab6599ee2 docs(governance): 文档性质标记规范 — 区分「需求」与「需求的需求」+ 全量打标 (#71)
用户: 「有的是需求,有的是需求的需求,都需要打上不同的标记」

## 核心洞察
三类文档(META/REQ/REF)长期混用同一套审查标准 → 用错标准 = 审查失效。

## 新增规范
docs/standards/文档性质标记规范.md (v1.0)
- 三分类定义 + 判定判据(判据是「约束范围」,不是文件名)
- ★ 三类规则差异表:
  |        | META | REQ | REF |
  |审查标准| 能否约束住别人 | 验收标准达成 | 与代码一致 |
  |变更成本| 高(全仓) | 中(局部) | 低(同步) |
  |架构锁定| ❌ | ✅ | ❌ |
- 三层落地:① 文档头标性质 ② README §六 索引 ③ META 强制类挂 AGENTS

## 全量打标 25/25
META 13 · REQ 5 · REF 7

## ★ 盘点发现(已修复)
- **11 份 META 中曾仅 4 份有强制入口** → 7 份「标记了但约束不住」
- docs/standards/ 目录名误导:18 份混着 4 REF + 3 REQ
- 易错点:名为"XX接入规范"但只约束一个集成 → 是 REQ 不是 META

## 强制链补齐(AGENTS.md 绝对禁止 8~11)
新增 4 条,把"待挂"META 接入强制链:
- 8. error-code.md        响应格式(成功 {code,data,message} / 错误走 HTTPException)
- 9. 数据采集合并去重机制  所有采集通道的去重键
- 10. 投诉渠道口径与对账   渠道口径权威定义(改动影响全部投诉数字)
- 11. 数据源接入与缓存加速 缓存铁律(写入后必须失效 pattern)

## 其它
- docs/README.md 新增 §六 文档性质索引(单一事实来源);修复历史表格粘连缺陷
- AGENTS.md 新增「文档性质纪律(强制)」章
- review-checklist.md 新增「文档性质」检查项(6 条子项)
- .github/instructions/doc-class-rationale.instructions.md(applyTo: docs/**)

---

## 附带修复:前端构建/编辑器报错(真实问题)

### ① Vue 3 模板被误报 compileError(根因:仓库根无 ESLint 配置)
现象:`v-model:preset="..."` 被编辑器标 [vue/no-v-model-argument] compileError
实测澄清:vite build / docker build / 命令行 eslint **全部通过**
根因:VS Code ESLint 扩展以**工作区根**为 cwd 启动;根目录无任何 ESLint 配置
      → 回退内置默认规则集(含 Vue 2 遗留规则 vue/no-v-model-argument)
修复:
- 新增 eslint.config.mjs(根级**桥接**文件,转发 web/eslint.config.js,单一事实来源)
- 新增 web/jsconfig.json(声明 vueCompilerOptions.target=3,Vue 语言服务)
- 新增 .vscode/settings.json(eslint.useFlatConfig=true 等)
- web/eslint.config.js 显式关闭 vue/no-v-model-argument 并注明原因
验证:两个 cwd 均解析出 118 条规则、该规则 = [0](一致)

### ② el-radio-button label → value 迁移(Element Plus 2.14 弃用)
实测告警:[el-radio] label act as value is about to be deprecated (3.0.0 移除)
项目实际装 element-plus 2.14.4(package.json 写 ^2.6.2)→ 告警真实存在
迁移 28 文件 / 158 处(脚本 scripts/migrate_radio_label_to_value.py,可复现)
验证:浏览器实测 el-radio 弃用告警**归零**

## 验证
- 前端 eslint 0 error(5 warning 均为既有 v-html)· vitest 52 例全绿
- vite build 成功 · docker compose build --no-cache 成功
- 文档性质标记覆盖 25/25 · AGENTS 强制链 9 份 META

关联工单: #71
2026-09-11 19:11:56 +08:00

8.2 KiB
Raw Permalink Blame History

人均效率算法说明

性质:参考(REF) —— 记录效率算法现状;⚠️ 内含业务口径(难度系数),存疑待定

版本:v1.0 · 日期:2026-08-17 关联代码:analytics.py today_staff_efficiency 函数(约 L4219) 关联模型:person.py / specialty.py

一、算法概述

人均效率是运营看板的核心指标,通过两个合成指标量化每个客服人员的产出:

指标 维度 含义 公式
工作量分 会话次数 反映忙闲强度(接了多少次) Σ(专项会话数 × 难度系数 × 本职系数)
效率分 SN 去重接待个数 反映服务覆盖面(接待了多少个不同客户) Σ(接待个数 × 难度系数 × 本职系数)

两个指标的差异:同一客户多次咨询算 1 个效率分但多次工作量分。

二、6 专项难度系数

专项 系数 数据来源表 匹配规则
咨询接待 ×1.0 cs_record + service_tickets problem_category 含 咨询/其他/综合;service_category = 咨询接待
套餐专项 ×1.8 cs_record + service_tickets problem_category 含 套餐/降成;service_category = 套餐疑问
退款处理 ×2.0 refunds + service_tickets refunds 表全部;service_category = 退款
退款挽回 ×2.2 cs_record problem_category 含 退款类接待(沟通挽留)
内部投诉 ×3.0 cs_record + service_tickets problem_category 含 投诉;service_category 含 商品/服务投诉
外部投诉 ×5.0 service_tickets + external_complaints + card_complaints service_item 含 12315/工信部/消协等外部关键词

设计原则:难度系数反映处理复杂度和客户情绪烈度。退款金额不计入工作量分,只计退款操作笔数。

三、本职/转接判定

3.1 判定逻辑

if 人员是管理岗 (主管/组长/副主管):
    所有专项系数 = 1.0  (管理岗跨类全系数豁免)
elif 该专项是该人员的本职专项 (从 person_specialty_assignments SCD Type 2 查询):
    本职系数 = 1.0
else:
    转接系数 = 0.2  (固定惩罚,不再乘以专项难度系数)

3.2 按天加权

查询范围为多日时,从 person_specialty_assignments (SCD Type 2) 取每天的专项分配,按天数加权平均:

# 示例:查询 8月10日-8月16日(7天)
# 8月10-12日:本职=咨询接待
# 8月13-16日:本职=套餐专项(调岗)
# 加权本职系数:
#   咨询接待本职:3/7天 = 0.429
#   套餐专项本职:4/7天 = 0.571

3.3 SCD 无数据回退

当 person_specialty_assignments 无记录时,回退到 Person.specialty 字段作为默认全天专项。

四、SN 跨表去重

4.1 设计目标

效率分按 SN(设备序列号/订单号)去重统计接待个数。同一客户多个渠道的记录需合并。

4.2 6 个子查询 UNION ALL

子查询 来源表 分类逻辑
cs_sns cs_record 按 problem_category 分 6 桶
rf_sns refunds → 退款处理
cm_ext_sns service_tickets 外部投诉(service_item 含外部关键词)
cm_int_sns service_tickets 内部投诉(其余投诉)
tk_sns service_tickets(非投诉) 按 service_category 分桶
tk_sns_linked service_tickets(handler 为空) SN 关联 cs_record 找最近操作人

4.3 handler 空值补全

工单的 handler 字段为空或为 SYS 时,通过 SN 关联 cs_record 表找最近一次操作人:

-- 使用 DISTINCT ON 替代 LATERAL JOIN(性能提升 115 倍)
SELECT DISTINCT ON (sn) sn, operator, ...
FROM cs_record
WHERE sn = ticket.sn
ORDER BY sn, create_time DESC

五、投诉分类逻辑

5.1 分类决策树

⚠️ 本决策树是绩效专项口径(用于给工作量算难度系数)。其中"微信投诉"在绩效专项里归入"内部投诉专项",但这不代表三源归类——在三源归类口径中微信投诉 = 商户号投诉(见下方注记)。

service_category 为空
  → 100% 投诉(已验证 7.30-8.1 共 883 条空类别,投诉占比 100%)

service_category 含 "投诉"
  → 投诉,再细分:
    service_item 含外部关键词 (12315/工信部/消协/监督局/公共服务平台等)
      → 外部投诉(绩效专项)
    service_item = 微信投诉
      → 绩效专项归「内部投诉专项」;⚠️ 三源归类中为「商户号投诉」
    service_item 含内部关键词 (商品投诉/服务投诉)
      → 内部投诉
    其余 → 内部投诉(默认)

service_category 不含 "投诉"
  → 保持原值(退款/套餐疑问/其他/换货/设备故障/网络故障等)

5.2 投诉来源三表合一

表 覆盖范围 时间字段
service_tickets BMS 工单,内部投诉+商户号投诉+部分外部 create_time / finish_time
external_complaints 12315/工信部/监督局等独立投诉 complaint_time / close_time
card_complaints 卡商群投诉 create_time

注(2026-09-01): 本算法为绩效专项口径(内部投诉专项 ×3.0 / 外部投诉专项 ×5.0), 与看板三源归类口径(内部/商户号/外部)是两套独立体系, 勿混用:

  • 绩效专项: 微信投诉归"内部投诉专项"; 卡商/315 归"外部投诉专项"数据源。
  • 三源归类: 微信投诉 = 商户号投诉; 卡商/315 = 外部投诉桶。

5.3 未闭环投诉列表

  • 先过滤已完成投诉(Python 层),再统一 LIMIT 200
  • SQL 不预先 LIMIT(避免过滤前被截断)
  • 微信商户投诉(service_item='微信投诉')单独统计

六、operator → person 匹配

6.1 三层匹配

层级 匹配方式 示例
1. 精确匹配 PersonAlias.alias = operator alias='刘思敏' = operator='刘思敏'
2. 前缀兜底 alias 去前缀后 = operator alias='生产-刘思敏' → '刘思敏' = operator
3. 部门前缀推断 从 alias 前缀推断部门 '企微-' → 企微, '店铺-' → 店铺, '研发-' → 研发

6.2 别名来源

PersonAlias 记录一个人的所有可能的操作员名称(BMS 账号名、SCRM 操作员名、工单处理人名等),用于跨表关联。

七、退款挽回分析

7.1 SN 级别追踪

  • 退款挽回团队处理的 SN → 有退款倾向
  • 该 SN 后续出现退款工单 → 挽回失败
  • 该 SN 被组长/主管处理 → 视为挽回失败

7.2 判定规则

SN 被退款挽回团队成员处理 → 标记为"有挽回处理"
  ↓
该 SN 是否出现在退款工单中?
  是 → 挽回失败
  否 → 挽回成功
  ↓
该 SN 是否被组长/主管处理?
  是 → 视为挽回失败(升级处理说明挽回未成功)

同一 SN 可同时计入挽回处理人和退款操作人的工作量(双重计入),用于绩效评估。

八、前端推断逻辑(inferSpecialty)

当后端返回的 current_specialty 为空时,前端按工作量自动推断主专项显示:

8.1 优先级

  1. 维表登记值:current_specialty / hr_category 有值 → 100% 信任,即使当天全是跨专项
  2. 工作量推断:合并投诉桶(内部+外部)后与其他 4 专项平级比较,最大桶胜出
  3. 管理岗兜底:position 含 组长/主管/副主管 → 显示"管理岗"
  4. 最终兜底:未分组显示"—",其他显示"综合"

8.2 投诉合并桶设计

避免投诉专员因转接了几笔咨询就被误判为"咨询接待":

  • 先把 内部投诉 + 外部投诉 合并成"投诉"桶
  • 与咨询/套餐/退款挽回/退款处理 4 桶平级比较
  • 投诉桶胜出后再细分内/外诉

8.3 双标签比例控制

  • 第二名工作量 ≥ 第一名的 40% → 显示"主专项+次专项"(如"咨询接待+内部投诉")
  • 否则只显示主专项

九、性能优化记录

优化点 原方案 优化后 效果
handler 空值补全 LATERAL JOIN DISTINCT ON 查询时间 115 倍提升
未闭环投诉 SQL LIMIT 后过滤 Python 先过滤再 LIMIT 避免微信投诉被截断
按天加权 子查询逐天 CTE 一次查询 减少 70% DB 往返