Files
junhong_cmp_fiber/.scratch/iot-risk-import-voucher-remark/PRD.md
break c0b9604515
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
完成
2026-06-23 11:05:58 +09:00

8.6 KiB
Raw Blame History

PRDIoT 卡风险状态限制 / 导入任务操作人与批量失效 / 凭证多图 / 预充值备注

Status: ready-for-agent


Problem Statement

运营团队在日常工作中遇到四个独立痛点:

  1. 风险停机/已销户卡无法有效拦截:运营商侧将某些卡置为"风险停机"或"已销户"状态后,系统仍允许对这些卡发起复机操作,且轮询系统仍持续轮询这些卡,浪费资源并可能产生错误数据。

  2. 导入任务无操作人信息IoT 卡导入任务和设备导入任务的列表中没有"操作人"字段,无法追溯是谁发起的批量操作。同时缺少批量失效订单套餐的操作入口,运营只能逐条手动处理。

  3. 凭证只能上传一张:后台创建订单和发起退款时,只允许上传一张凭证图片,无法满足多张凭证的业务场景(如多笔转账记录、多页合同等)。

  4. 代理预充值缺少备注字段:代理预充值操作无法附加运营备注,导致事后无法追溯充值的业务原因。


Solution

  1. 在复机接口和轮询系统中增加对 gateway_extend 字段的检查,当独立卡处于"风险停机"或"已销户"状态时,拒绝复机并停止轮询。

  2. 在所有导入任务中快照操作人姓名;新增"批量失效订单套餐"导入任务,支持通过 CSV 批量将订单下的套餐置为失效状态。

  3. 将订单、退款、代理预充值的凭证字段改为支持最多5张的 jsonb 数组存储。

  4. 在代理预充值记录上新增 remark 备注字段,供运营创建时填写,并在列表/详情接口中返回。


User Stories

  1. 作为运营人员,当我尝试对"风险停机"状态的独立卡发起复机时,我希望系统拒绝并提示原因,以免产生无效操作。
  2. 作为运营人员,当我尝试对"已销户"状态的独立卡发起复机时,我希望系统拒绝并提示原因,以免对已注销的卡发起无意义请求。
  3. 作为运营人员,我希望"风险停机"和"已销户"的独立卡不再参与系统轮询,以免浪费轮询资源并产生噪音数据。
  4. 作为运营人员,当轮询系统检测到某张独立卡的网关状态变为"风险停机"或"已销户"时,我希望系统自动将该卡的轮询关闭,而不需要手动干预。
  5. 作为运营人员,我希望在 IoT 卡导入任务列表中看到"操作人"姓名,以便追溯每次批量导入是谁发起的。
  6. 作为运营人员,我希望在设备导入任务列表中看到"操作人"姓名,以便追溯每次批量导入是谁发起的。
  7. 作为运营人员,我希望通过上传 CSV 文件批量将一批订单的套餐置为失效,以便快速处理异常订单。
  8. 作为运营人员我希望在创建批量失效任务时能填写备注和上传凭证最多5张以便记录操作原因。
  9. 作为运营人员,我希望批量失效任务完成后能看到成功数、失败数,以及失败原因(如订单号不存在),以便核查处理结果。
  10. 作为运营人员,我希望批量失效套餐只影响套餐记录本身,不触发退款和其他业务流程,让轮询系统自行根据套餐状态处理停机。
  11. 作为运营人员我希望在创建后台订单时可以上传最多5张凭证以便完整记录线下付款证明。
  12. 作为运营人员我希望在发起退款申请时可以上传最多5张凭证以便完整记录退款证明材料。
  13. 作为运营人员,我希望在代理预充值列表中看到备注内容,以便了解每笔预充值的业务背景。
  14. 作为运营人员,我希望在创建代理预充值订单时填写备注,以便记录本次充值的原因或说明。
  15. 作为开放 API 调用方,当我通过 Open API 对"风险停机"或"已销户"的独立卡调用复机接口时,我希望收到明确的错误响应。

Implementation Decisions

需求一:风险停机/已销户卡限制

  • 判断字段tb_iot_card.gateway_extend 原文值 = "风险停机""已销户"。新增两个常量 GatewayCardExtendRiskStopGatewayCardExtendCancelled
  • 判断范围:仅 is_standalone = true 的独立卡(未绑定设备的卡)。绑定设备的卡不受此限制。
  • 复机拦截:在 ManualStartCard(后台管理员入口)和 ResumeCardOpen API 入口)两个函数中,调用网关前先读取 DB 中已落库的 gateway_extend,若命中则返回业务错误,不实时查询网关。
  • 轮询停止:轮询状态处理器(PollingCardStatusHandler)在将新的 gateway_extend 写入 DB 后,检查若命中风险状态且 is_standalone=true,则调用现有的 UpdatePollingStatus(false)enable_polling 写为 false,并跳过 requeueCard,终止该卡的轮询循环。
  • 错误码:复机被拒绝时返回业务错误,提示信息明确说明原因("风险停机"/"已销户")。

需求二:导入任务操作人与批量失效

操作人快照:

  • tb_iot_card_import_tasktb_device_import_task 表各新增 creator_name varchar 字段。
  • 新的批量失效任务表同样包含此字段。
  • 创建任务时从当前登录用户快照姓名写入,旧数据留空(不回填)。
  • 列表响应 DTO 新增 creator_name 字段。

批量失效订单套餐任务:

  • 新建模型、Store、Service、Handler参照现有 IoT 卡导入任务的结构CSV 上传 → 异步任务处理)。
  • CSV 格式:单列 order_no(无表头行约定沿用现有导入任务的处理方式)。
  • 创建接口额外支持:remark字符串备注和最多5张凭证jsonb 存储)。
  • 处理逻辑:按行读取 order_no → 查询 Order → 找到 PackageUsage WHERE order_id = ? AND status NOT IN (3, 4) → 批量更新 status = 4(已失效)。
  • 订单不存在记为失败fail继续处理后续行。
  • 订单存在但旗下套餐全部已是终态status 3 或 4记为成功success
  • 不触发退款,不直接操作 IoT 卡停机,由轮询系统根据套餐状态自行评估。
  • 任务表命名:tb_order_package_invalidate_task

需求三:凭证多图改造

  • tb_order.payment_voucher_keyvarchar 500jsonb,存储 []string最多5个元素。
  • tb_refund.refund_voucher_keyvarchar 500jsonb,存储 []string最多5个元素。
  • tb_agent_recharge_record.payment_voucher_keyvarchar 500jsonb,存储 []string最多5个元素。
  • 数据迁移(同一 Migration 语句):非空旧值 "some_key"["some_key"];空字符串 → []。生产环境由运营手动执行 Migration。
  • 所有涉及凭证的请求 DTO 字段由 string 改为 []stringvalidate 标签限制 max=5,每项 max=500
  • 所有涉及凭证的响应 DTO 字段改为 []string
  • 不限制文件类型(上传 URL 获取接口层面不变,仅存储 key 列表)。

需求四:代理预充值备注

  • tb_agent_recharge_record 新增 remark text 字段(可为空)。
  • CreateAgentRechargeRequest 新增 remark 字段(omitempty,无最大长度强限制,建议 1000 字符内)。
  • AgentRechargeResponse 新增 remark 字段(列表和详情接口均返回)。
  • 创建后不可修改,无需新增修改接口。

Testing Decisions

本项目禁止自动化测试。验证方式:

  • PostgreSQL MCP:核查 tb_iot_card.enable_polling 在风险状态卡被轮询后是否正确置为 false;核查 tb_package_usage.status 在批量失效任务执行后是否按预期更新;核查 jsonb 字段迁移结果。
  • curl / Postman:对风险停机卡调用复机接口,验证返回正确错误码;上传多张凭证创建订单,验证存储和返回正确;创建代理预充值时附带备注,验证列表返回。

Out of Scope

  • 已绑定设备的卡(is_standalone=false)不受"风险停机/已销户"限制约束。
  • 批量失效订单套餐不触发退款流程。
  • 批量失效不直接调用网关停机,依赖轮询系统自行评估。
  • 历史导入任务的 creator_name 不回填。
  • 凭证文件类型校验(文件类型限制不在本次范围内)。
  • 代理预充值备注创建后不可修改(无修改接口)。

Further Notes

  • 凭证字段改为 jsonb 后,前端需同步更新上传组件支持多选;本 PRD 只覆盖后端改造。
  • 批量失效任务的凭证和备注字段,与代理预充值备注字段、以及需求三的多凭证设计保持一致的数据形态,便于后续统一处理。
  • "风险停机"和"已销户"的 gateway_extend 原文值来自上游网关,若上游修改返回值需同步更新常量。