| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100 |
- import { Message } from './api/models';
- /**
- * 组员超时检测 —— 组长派活后组员一直不回。
- *
- * ## 为什么需要
- *
- * 组长制下组长会「限时轮询」等组员。但实测量到:
- *
- * - 组员忙的时候消息在排队(可能排 20 分钟)
- * - 组员可能压根没收到(曾出现同一消息别的容器收到、它没收到)
- *
- * 用户看到的是「组长一直在等」,但不知道是组员慢还是死了。
- *
- * ## 这个模块只做判断,不做动作
- *
- * | 状态 | 含义 | App 该做什么 |
- * |---|---|---|
- * | `pending` | 刚派活,正常等待 | 什么都不做 |
- * | `slow` | 等得有点久了 | 轻提示「组员正在处理」|
- * | `stuck` | 等太久了 | 提示「可能卡住了,建议问问组长」|
- *
- * 🔴 **不自动重发** —— 重发可能让组员做两遍,产生重复产出。
- */
- /** 多久算「有点久」 */
- export const SLOW_MS = 5 * 60_000;
- /** 多久算「卡住了」 */
- export const STUCK_MS = 15 * 60_000;
- export type MemberTaskState = 'idle' | 'pending' | 'slow' | 'stuck';
- export interface MemberTaskStatus {
- state: MemberTaskState;
- /** 已经等了多久(毫秒),没有在等时为 0 */
- waitedMs: number;
- /** 给用户看的提示 */
- hint: string;
- /** 卡住的组员名字(多组员时用户要知道找谁) */
- memberName?: string;
- }
- /**
- * 判断「组长派给组员的活」等多久了。
- *
- * @param messages 群消息(时间升序)
- * @param leaderMxId 组长的 Matrix ID(用于识别「组长派的活」)
- * @param targetMxId 组员的 Matrix ID(用于识别「组员回了没」)
- * @param now 当前时刻
- */
- export function memberTaskStatus(
- messages: Message[],
- leaderMxId: string,
- targetMxId: string,
- now = Date.now(),
- memberName?: string,
- ): MemberTaskStatus {
- const idle: MemberTaskStatus = { state: 'idle', waitedMs: 0, hint: '' };
- if (!leaderMxId || !targetMxId) return idle;
- // 从后往前找:最近一次「组长 @ 该组员」
- let assignedAt = 0;
- let assignedIdx = -1;
- for (let i = messages.length - 1; i >= 0; i--) {
- const m = messages[i];
- if (m.senderId !== leaderMxId) continue;
- if (!m.mentions?.includes(targetMxId)) continue;
- assignedAt = Date.parse(m.ts);
- assignedIdx = i;
- break;
- }
- if (assignedIdx < 0 || !Number.isFinite(assignedAt)) return idle;
- // 派活之后,该组员有没有说过话
- const replied = messages
- .slice(assignedIdx + 1)
- .some((m) => m.senderId === targetMxId && !!m.text?.trim());
- if (replied) return idle;
- const waitedMs = Math.max(0, now - assignedAt);
- // 多组员时带上名字,否则用户不知道该找谁
- const who = memberName ? `${memberName} ` : '';
- if (waitedMs >= STUCK_MS) {
- return {
- state: 'stuck',
- waitedMs,
- memberName,
- hint: `${who}已经等了 ${Math.round(waitedMs / 60_000)} 分钟还没动静,可能卡住了,建议问问组长`,
- };
- }
- if (waitedMs >= SLOW_MS) {
- return {
- state: 'slow',
- waitedMs,
- memberName,
- hint: `${who}还在处理组长派的活(已 ${Math.round(waitedMs / 60_000)} 分钟)`,
- };
- }
- return { state: 'pending', waitedMs, hint: '' };
- }
|