edit | blame | history | raw

ADDED Requirements

Requirement: 智能外呼自动触发

系统在首页加载案件数据完成后,SHALL 自动发起 AI 智能外呼,无需用户手动操作。外呼请求应基于案件数据(mediationId、caseId)构建,并确保幂等性(页面刷新时不重复触发)。

Scenario: 首次进入首页自动触发外呼

  • GIVEN 用户首次进入首页,localStorage 中无活跃外呼任务
  • WHEN CaseDataContext.loadCaseData() 完成并返回 timelineData
  • THEN 系统自动调用 OutboundBotAPIService.makeCallV2({ mediationId, caseId, callAuto: 0, callPersonId: null })
  • AND 成功响应后,从 response.data 中提取所有 errorCode === 0 的记录
  • AND 将每条记录的 jobIdcallStatuspersonIdmediationId 存储到 localStorage(键名:outbound_call_jobs
  • AND 右下角智能外呼气泡组件自动弹出,展示"正在与XX电话沟通中..."

Scenario: 页面刷新时检测活跃任务

  • GIVEN localStorage 中存在活跃状态的外呼任务(callStatusSchedulingExecutingPausedDrafted
  • WHEN 用户刷新页面,CaseDataContext.loadCaseData() 执行
  • THEN 系统检测到活跃任务,跳过发起新外呼
  • AND 继续轮询查询现有任务的通话状态
  • AND 右下角气泡组件根据 localStorage 中的 jobId 恢复显示

Scenario: 外呼发起失败处理

  • GIVEN 用户进入首页,数据加载完成
  • WHEN 调用 makeCallV2 失败(如网络错误、API 返回 500)
  • THEN 系统展示 Ant Design 错误提示:message.error('发起外呼失败,请稍后重试')
  • AND 在浏览器控制台输出详细错误日志:console.error('makeCallV2 failed:', err)
  • AND 不阻塞页面其他功能,用户可正常使用首页

Scenario: API 返回部分成功记录

  • GIVEN makeCallV2 响应包含多条记录,其中部分 errorCode === 0(成功),部分 errorCode !== 0(失败)
  • WHEN 系统解析响应数据
  • THEN 仅提取并存储 errorCode === 0 的记录到 localStorage
  • AND 对于失败记录(如"外呼次数已达上限"),在控制台输出警告日志
  • AND 右下角气泡仅展示成功发起的外呼任务

Requirement: 外呼状态实时监控

系统 SHALL 对活跃外呼任务进行定时轮询,查询通话状态并实时更新右下角气泡组件显示。轮询策略应平衡实时性与服务器压力,采用 10 秒间隔、最大 2 小时轮询时长、失败重试 10 次的机制。

Scenario: 定时轮询查询通话状态

  • GIVEN localStorage 中存在活跃状态的 jobId(callStatusSchedulingExecutingPausedDrafted
  • WHEN OutboundCallWidget 组件挂载后启动轮询定时器(间隔 10 秒)
  • THEN 遍历所有活跃 jobId,调用 OutboundBotAPIService.getCallStatus({ jobId })
  • AND 解析每个响应的 callStatus,更新组件内部 calls 状态数组
  • AND 气泡组件实时显示通话人姓名、通话状态(中文)、通话时长(格式:MM:SS)

Scenario: 检测终态状态并停止轮询

  • GIVEN 轮询过程中某个 jobId 的 callStatus 变为 Succeeded(成功)
  • WHEN 系统检测到终态状态
  • THEN 立即从 localStorage 的 outbound_call_jobs 中移除该 jobId
  • AND 更新 calls 状态数组,移除对应记录
  • AND 如果所有 jobId 均为终态,右下角气泡自动隐藏(isVisible=false
  • AND 停止该 jobId 的后续轮询

Scenario: 轮询超时自动停止

  • GIVEN 某个 jobId 的轮询已持续 2 小时(从 pollStartTime 开始计时)
  • WHEN 系统检测到轮询时长超过 7200 秒
  • THEN 自动停止该 jobId 的轮询,从 localStorage 中移除
  • AND 在控制台输出警告日志:console.warn('轮询超时,jobId: xxx')
  • AND 不展示错误提示,避免打扰用户

Scenario: 单次查询失败重试机制

  • GIVEN 调用 getCallStatus 失败(如网络抖动、API 返回 500)
  • WHEN 系统检测到单次查询失败
  • THEN 累加该 jobId 的 retryCount 计数器
  • AND 保留该 jobId 在 localStorage 中,下次轮询继续重试
  • AND 在控制台输出日志:console.warn('查询失败,重试次数: X/10, jobId: xxx')

Scenario: 连续失败 10 次后移除任务

  • GIVEN 某个 jobId 的 retryCount 已达到 10
  • WHEN 第 10 次查询失败
  • THEN 展示 Ant Design 错误提示:message.error('获取通话状态失败,请检查网络连接')
  • AND 从 localStorage 中移除该 jobId
  • AND 在控制台输出错误日志:console.error('重试次数超限,jobId: xxx')
  • AND 更新气泡组件,移除该任务显示

Requirement: 多任务并行支持

系统 SHALL 支持同时展示多个外呼任务的状态(如申请人、被申请人同时外呼),气泡组件采用纵向堆叠布局,每个任务独立显示通话人和状态信息。

Scenario: 同时展示多个外呼任务

  • GIVEN makeCallV2 响应返回 2 条成功记录(2 个 jobId)
  • WHEN 系统存储到 localStorage 并启动轮询
  • THEN 右下角气泡组件纵向堆叠展示 2 个气泡
  • AND 每个气泡独立显示:通话人姓名、通话状态、通话时长
  • AND 第一个气泡位于下方,第二个气泡位于上方(堆叠顺序)

Scenario: 单个任务结束后部分气泡消失

  • GIVEN 存在 2 个活跃外呼任务
  • WHEN 其中 1 个任务状态变为 Succeeded(成功)
  • THEN 对应气泡立即消失
  • AND 另一个任务的气泡继续显示并轮询
  • AND localStorage 中仅保留 1 个活跃 jobId

Scenario: 所有任务结束后气泡完全隐藏

  • GIVEN 所有外呼任务均已结束(callStatusSucceededFailedCancelled
  • WHEN 系统检测到 localStorage 中无活跃 jobId
  • THEN 右下角气泡完全隐藏(包括最小化的 AI 客服图标)
  • AND 停止轮询定时器

Requirement: 组件生命周期管理

系统 SHALL 在组件卸载或用户离开页面时正确清理定时器和资源,避免内存泄漏和控制台报错。

Scenario: 组件卸载时清理定时器

  • GIVEN OutboundCallWidget 组件已挂载并启动轮询定时器
  • WHEN 用户导航到其他页面,组件卸载
  • THENuseEffect 的清理函数中调用 clearInterval(intervalId)
  • AND 停止所有正在进行的轮询
  • AND 不抛出任何错误或警告到控制台

Scenario: 路由切换时保留 localStorage 状态

  • GIVEN 用户在首页查看外呼气泡,然后切换到其他页面
  • WHEN OutboundCallWidget 组件卸载
  • THEN localStorage 中的 outbound_call_jobs 数据保持不变
  • AND 用户返回首页时,组件重新挂载并从 localStorage 恢复状态
  • AND 继续轮询之前的活跃任务

Scenario: 避免组件卸载后的异步回调执行

  • GIVEN 组件发起了 getCallStatus 异步请求
  • WHEN 请求尚未返回时,组件被卸载
  • THEN 使用 useRef 记录 isMounted 状态
  • AND 异步回调中检查 isMounted,如果组件已卸载则不执行状态更新
  • AND 不抛出"Can't perform a React state update on an unmounted component"警告

Requirement: 通话状态中文映射

系统 SHALL 将 API 返回的英文通话状态映射为中文,便于用户理解当前外呼进度。

Scenario: 状态中文映射规则

  • GIVEN API 返回的 callStatus 为英文值
  • WHEN 气泡组件渲染通话状态
  • THEN 按照以下规则显示中文:
  • Scheduling → "拨号中"
  • Executing → "通话中"
  • Paused → "已暂停"
  • Drafted → "草稿"
  • Succeeded → "已完成"
  • Failed → "失败"
  • Cancelled → "已取消"
  • AND 未知状态显示原始值

Scenario: 通话时长计算

  • GIVEN API 响应包含 startTime 字段(时间戳或 ISO 格式字符串)
  • WHEN 气泡组件显示通话时长
  • THEN 计算当前时间与 startTime 的差值(秒)
  • AND 格式化为"MM:SS"格式(如"02:35"表示 2 分 35 秒)
  • AND 每次轮询更新时重新计算并刷新显示

Requirement: 错误提示与日志记录

系统 SHALL 在关键操作失败时提供友好的错误提示,并在浏览器控制台输出详细日志,便于开发调试。

Scenario: makeCallV2 失败提示

  • GIVEN 调用 makeCallV2 失败(如网络错误、API 返回 500)
  • WHEN 捕获到异常
  • THEN 展示 Ant Design 错误提示:message.error('发起外呼失败,请稍后重试')
  • AND 在控制台输出错误日志:console.error('makeCallV2 failed:', err)

Scenario: getCallStatus 重试超限提示

  • GIVEN 某个 jobId 的查询连续失败 10 次
  • WHEN 第 10 次失败后
  • THEN 展示 Ant Design 错误提示:message.error('获取通话状态失败,请检查网络连接')
  • AND 在控制台输出错误日志:console.error('重试次数超限,jobId: xxx')

Scenario: 关键步骤日志记录

  • GIVEN 系统执行外呼触发、轮询、状态更新等关键步骤
  • WHEN 每个步骤完成
  • THEN 在控制台输出相应日志:
  • console.log('发起外呼,mediationId: X, caseId: Y')
  • console.log('存储 jobId: X, callStatus: Y')
  • console.log('轮询查询 jobId: X')
  • console.log('检测到终态,移除 jobId: X')