笔记簿
ᴄᴏᴅɪɴɢ ɪs ᴀʀᴛ
首页
关于
登录
注册
客服排队算法 - 流程图 / 时序图
## 1. 主流程图 ```mermaid flowchart TD A[请求进入 CustomerServiceQueue] --> B[BindJSON 解析请求体] B --> C{解析是否成功?} C -- 否 --> C1[返回 200 + InvalidArgument] C -- 是 --> D[生成 trace logger] D --> E[记录入参日志] E --> F[调用 GetRoughSortStore 计算粗排分] F --> G[按 RoughSortScore 倒序排序] G --> H[调用 GetRR 获取 RR 等级和 UserGroup] H --> I[调用 doQueueBaseRR 做 RR 冒泡微调] I --> J[调用 doQueueBaseVip 做 VIP 冒泡微调] J --> K[调用 GetResult 组装响应] K --> L[记录结果日志] L --> M[返回成功响应] ``` --- ## 2. 粗排打分流程图 ```mermaid flowchart TD A[遍历 queue 中每个 person] --> B[初始化 RoughSortScore = 0] B --> C{NotConfirmNum >= 4?} C -- 是 --> D[分数保持 0 并 continue] C -- 否 --> E[加等待分 AlreadyWaitTime / 3600] E --> F{IsOnline == 1?} F -- 是 --> G[加 0.3] F -- 否 --> H[跳过在线加分] G --> I{IsComplaint == 1?} H --> I I -- 是 --> J[加 0.3] I -- 否 --> K[跳过投诉加分] J --> L[加 ConfirmNum * 0.1] K --> L L --> M[进入下一个 person] D --> M ``` ### 粗排公式 当 `NotConfirmNum < 4` 时: ```text RoughSortScore = AlreadyWaitTime / 3600 + (IsOnline == 1 ? 0.3 : 0) + (IsComplaint == 1 ? 0.3 : 0) + ConfirmNum * 0.1 ``` 当 `NotConfirmNum >= 4` 时: ```text RoughSortScore = 0 ``` --- ## 3. RR 微调流程图 ```mermaid flowchart TD A["进入 doQueueBaseRR"] --> B["外层循环 i = 0..size-1"] B --> C["内层循环 j = 0..size-1-i"] C --> D{"users[j] 是 R6-R9?"} D -- 否 --> C D -- 是 --> E{"users[j+1] 是 R1-R5?"} E -- 否 --> C E -- 是 --> F{"users[j].RR > users[j+1].RR?"} F -- 否 --> C F -- 是 --> G{"users[j].BeJump < 15?"} G -- 否 --> C G -- 是 --> H{"users[j+1].NotConfirmNum < 4?"} H -- 否 --> C H -- 是 --> I["交换 users[j] 和 users[j+1]"] I --> J["调用 DoJump 记录被插队信息"] J --> C ``` ### RR 微调触发条件 只有当以下条件同时成立时,后一个用户才能前移: 1. 前一个用户的 `RR ∈ [6, 9]` 2. 后一个用户的 `RR ∈ [1, 5]` 3. 前一个用户的 `RR > 后一个用户的 RR` 4. 前一个用户的 `BeJump < 15` 5. 后一个用户的 `NotConfirmNum < 4` ### RR 微调的副作用 - 发生交换后,原先在前面的用户会被视为“被插队”一次 - `DoJump` 会读取缓存 `lechat_jump_uids_
` - 如果同一个 `jumpUid` 已经插过队,则不重复增加 `BeJump` - 如果是新的插队者,则: - `BeJump += 1` - 将 `jumpUid` 写回缓存 --- ## 4. VIP 微调流程图 ```mermaid flowchart TD A["进入 doQueueBaseVip"] --> B["外层循环 i = 0..size-1"] B --> C["内层循环 j = 0..size-1-i"] C --> D{"users[j].NotConfirmNum < 4?"} D -- 否 --> C D -- 是 --> E{"users[j] 不是 SSVIP?"} E -- 否 --> C E -- 是 --> F{"users[j+1] 是 SSVIP?"} F -- 否 --> C F -- 是 --> G{"users[j+1].UserGroup >= users[j].UserGroup?"} G -- 否 --> C G -- 是 --> H["交换 users[j] 和 users[j+1]"] H --> C ``` ### VIP 微调触发条件 后一个用户能凭 SSVIP 身份前移,必须同时满足: 1. 当前前面的用户 `NotConfirmNum < 4` 2. 当前前面的用户不是 SSVIP 3. 当前后面的用户是 SSVIP 4. 当前后面的用户 `UserGroup >= 当前前面的用户 UserGroup` ### UserGroup 含义 - `RR <= 5` 时,`UserGroup = 2` - `RR > 5` 时,`UserGroup = 1` 所以这里的 `>=` 可以理解为: - 同组 SSVIP 可以前移 - 更高组的 SSVIP 也可以前移 - 更低组的 SSVIP 不能越过更高组用户 --- ## 5. 时序图 ```mermaid sequenceDiagram participant Client as 调用方 / 前端 participant Controller as CustomerServiceQueue participant Queue as Candidate Queue participant CacheRR as RR缓存 lechat_
participant CacheJump as 插队缓存 lechat_jump_uids_
Client->>Controller: POST 请求 + candidate 列表 Controller->>Controller: BindJSON(req) alt 请求非法 Controller-->>Client: 200 + InvalidArgument else 请求合法 Controller->>Queue: GetRoughSortStore(queue) loop 遍历每个用户 Queue->>Queue: 计算 RoughSortScore end Controller->>Queue: sort.Slice 按粗排分倒序 loop 遍历每个用户获取 RR Controller->>CacheRR: GET lechat_
alt 命中且 rr_1p5 可解析 CacheRR-->>Controller: {"rr_1p5":"R3"} Controller->>Queue: 设置 person.RR 和 UserGroup else 未命中 / 解析失败 CacheRR-->>Controller: 空或异常数据 Controller->>Queue: RR 默认为 9, UserGroup=1 end end Controller->>Queue: doQueueBaseRR(queue) loop 冒泡比较相邻用户 alt 命中 RR 交换条件 Queue->>Queue: 交换两个用户位置 Controller->>CacheJump: GET lechat_jump_uids_<被插队用户uid> alt 当前 jumpUid 未出现过 CacheJump-->>Controller: 历史 jumpUid 列表 Controller->>Queue: 被插队用户 BeJump += 1 Controller->>CacheJump: SET 更新 jumpUid 列表 else 已被同一用户插过队 CacheJump-->>Controller: 已包含当前 jumpUid end end end Controller->>Queue: doQueueBaseVip(queue) loop 冒泡比较相邻用户 alt 命中 VIP 交换条件 Queue->>Queue: 交换两个用户位置 end end Controller->>Controller: GetResult(req) Controller-->>Client: 200 + 最终 queue 结果 end ``` --- ## 6. 一句话总结 这段代码的执行顺序是: **请求校验 → 粗排打分 → 粗排排序 → RR 缓存补全 → RR 冒泡纠偏 → VIP 冒泡纠偏 → 返回最终队列**。 它不是一次性总分排序,而是“**先形成基础顺序,再通过 RR 和 VIP 规则做局部前移**”。