退款去向七天未选自动执行 (2026-09-22)

需求背景

  • 来源:docs/pipipen/feature/COMMISSION_REFUND_PRD.md §3.1 延期项补齐(退款去向自动选定)。
  • 动机:退款债权进入 awaiting_destination 后若用户长期不选,资金会无限期停在待退状态;需要提供「到期自动选定」兜底,同时让用户能提前看到自己的截止时间。
  • 范围:纯增量、无破坏性变更——退款池 / 债权 payload 新增只读字段 destination_deadline_at,新增 destination_source 列与两个 internal 配置接口;本次不改用户主动选择去向的接口与行为、不动汇率与 credit 币种逻辑。
  • 面向读者:前端(用户端)、联调、测试、运营。
  • 术语对照:commission = WorkTask(委托);退款债权 = commission_refunds 记录;credit = 站内 Credit 钱包余额。

更新记录

  • 2026-09-22 首次发布:新增只读字段 destination_deadline_at、destination_source 列与两个 internal 配置接口(开发阶段契约,后端未发版)。
  • 2026-09-22 勘误上线步骤权限口径:权限标识由前端菜单树自动生成,上线只需在角色管理里授权,原文「需先创建权限」表述作废。
  • 2026-09-23 修订:字段表去除 emoji 前缀(按在途同域迭代规则,展示形式调整,契约无变化)。

建议阅读顺序:第 1 章(一分钟上手)→ 第 2 章(接口变更总览)→ 第 3 章(接口示例)→ 第 4 章(字段与配置口径)→ 第 5 章(兼容性与上线)。遇到「这个债权什么时候被自动选定」先看第 1 章流程图与第 4 章取值口径。

目录

章内容什么时候看
1. 一分钟上手状态机 + 流程 + 优先级回落 + 六条对接要点刚拿到文档
2. 接口变更端点清单与变更摘要找接口
3. 接口示例每个端点的参数、示例与错误对接具体接口
4. 字段补充说明destination_deadline_at 与配置项口径查具体取值
5. 兼容性说明兼容项与上线部署步骤发布前确认

1. 一分钟上手

先看状态机与流程图理解「等待中的债权如何到期自动选定」,再读六条对接要点即可开始对接。 本次没有状态、枚举、错误码变更;既有「用户主动选择去向」的接口与行为完全不变。

1.1 退款债权状态机

自动选定与用户手动选择走同一条状态迁移(awaiting_destination → ready),因此并发时先落库的一方生效,另一方由既有状态校验安全跳过;不会出现中间态或新状态。

1.2 到期自动选定的完整流程

自动选定不发新站内信、没有到期前提醒(产品决策 D2);退款执行完成仍沿用既有 commission_refund.completed 通知与 refund_awaiting_destination 页面事件关闭规则。

1.3 前后端交互时序

1.4 自动去向优先级如何回落

配置示例:优先级 ['credit', 'original'] 时,债权可选 credit 就选 credit,不可选则回落 original;优先级 ['credit'] 且债权不可选 credit 时,列表耗尽回落 original(original 对有效债权恒可用)。

1.5 对接要点

  1. 没有新增状态与错误码:退款债权状态机、枚举、错误码全部沿用既有定义;本次只新增一个只读字段与两个 internal 配置接口。
  2. destination_deadline_at 是动态值:等于 action_required_at + 当前配置时限,不是创建时固化。运营改时限会即时改变存量等待中债权的该字段;关闭开关则返回 null,且不再自动选定。
  3. 只有等待中的债权有到期时间:destination_deadline_at 仅在 execution_status = awaiting_destination 且 action_required_at 非空时返回 ISO8601 字符串,其余情况(已选、已执行、关闭开关)一律 null。无需按字段是否存在推断债权状态。
  4. 退款池 summary 给的是最早到期时间:summary.destination_deadline_at 是所有等待中债权里最早的一个截止时间,用于展示「最近一笔即将自动选定」;逐条时间在 data[] 每笔债权的同名字段。
  5. 自动选定等同用户选定:destination_source = auto_default 表示到期自动选定,user 表示用户主动选择,preference 表示创建债权时应用了冻结偏好;如需区分展示读该字段即可,不要用它反推流程合法性。
  6. 上线后首次运行会处理历史积压:配置默认 enabled = true、expire_hours = 168;命令上线后第一次执行会把所有等待超过 7 天的债权一次性自动选向(见上线部署步骤)。

2. 接口变更

user

上述四个接口共用同一套债权 payload 投影,只列出一次示例标题(第 3 章),锚点相同即为设计意图。

自动选定命令本身无需新接口与 new seeder;commission_refund.completed 通知沿用既有场景与模板。


3. 接口示例

user

POST /api/commission_refunds/list

  • 功能说明:查询指定 commission 的退款债权列表与退款池汇总。
  • 变更说明:✨ 每笔债权与 summary 新增只读字段 destination_deadline_at。

请求参数

字段类型必填说明
work_task_idinteger是commission id,必须属于当前用户
pageinteger否页码,默认 1
sizeinteger否每页条数,1~50,默认 15

响应示例

{
  "data": [
    {
      "id": 601,
      "work_task_id": 1001,
      "execution_status": "awaiting_destination",
      "cash_destination": null,
      "action_required_at": "2026-09-22T10:00:00+00:00",
      "destination_deadline_at": "2026-09-29T10:00:00+00:00"
    }
  ],
  "summary": {
    "open_count": 1,
    "action_required_count": 1,
    "action_required_refund_ids": [601],
    "destination_deadline_at": "2026-09-29T10:00:00+00:00",
    "bulk_allowed_destinations": ["original", "credit"]
  },
  "total": 1
  // ...其余字段省略
}

完整字段以既有文档为准;此处只列本次相关字段。已选定/已执行的债权 destination_deadline_at 为 null。

错误响应

无新增,沿用原有错误语义。POST /api/commission_refunds/info、POST /api/commission_refunds/select_destination、POST /api/commission_refunds/select_destinations 的请求参数、响应结构与错误码同样无新增,仅多出 destination_deadline_at 字段(select_* 选定后为 null)。


4. 字段补充说明

4.1 ✨destination_deadline_at 取值口径

场景返回值
execution_status = awaiting_destination 且 action_required_at 非空,开关启用action_required_at + expire_hours 的 ISO8601 字符串
开关关闭null(该字段不展示,命令也停止自动选定)
已选定 / 已执行 / 无 action_required_atnull

补充口径:

  • 该字段动态计算,不落库、不加列;运营修改时限后,存量等待中债权的到期时间立即随新时限变化(产品决策 D7)。
  • summary.destination_deadline_at 取所有等待中债权里最早的一个截止时间;没有任何等待中债权时为 null。
  • 前端展示倒计时请直接使用该字段,不要自行用 action_required_at 加时限推算(时限可能已被运营调整)。

4.2 ✨ 配置项与默认值

配置项默认值取值范围说明
enabledtrue布尔关闭后等待中的债权无限期等待,命令直接退出
expire_hours168(7 天)1~720等待时限;改小会加速处理存量债权
destination_priority["original"]非空去重列表,∈ {original, credit}到期按顺序在债权可选去向内回落,列表耗尽回落 original
  • 配置存储于 SystemSetting,key 为 commission_refund.auto_destination;更新带 expected_version 乐观锁与 SystemSettingChangeLog 审计。
  • 管理后台配置页:业务配置 → 退款去向自动选定(/business-settings/refund-auto-destination),页内提示「修改配置即时影响存量等待中债权」。
  • 权限 slug:页面 system-settings.refund-auto-destination、按钮 system-settings.refund-auto-destination.update(由前端菜单树自动生成,无需手动创建)。

4.3 ✨destination_source 来源标记

新增列 commission_refunds.destination_source(可空字符串),区分去向决定来源;不改变任何接口契约,供排查与统计使用:

取值含义
user用户主动选择(含退款池批量选择)
preference创建债权时应用了冻结去向偏好
auto_default到期由命令自动选定
null仍在等待选择去向(存量数据可能为 null)

5. 兼容性说明

  • 纯增量,无破坏性变更:新增可空列与回填迁移、新增配置服务/命令/控制器、新增只读响应字段;既有「用户主动选择去向」的接口签名与行为零改动。
  • 旧前端可平滑升级:destination_deadline_at 为新增字段,前端忽略即可;不读取该字段时行为与现状一致。
  • 开关默认与现状等价:默认 enabled = true, expire_hours = 168, destination_priority = ["original"],等价于「七天后自动原路退回」;即使后台配置页尚未上线,命令上线即安全。
  • 通知不变:到期自动选定不发新站内信、不做到期前提醒(决策 D2);退款执行完成仍触发既有 commission_refund.completed。
  • 无汇率/币种改动:不触碰汇率与 credit 币种逻辑(决策 D3/D5)。
  • 并发安全:自动选定复用 selectDestination 的行锁与状态校验,与用户手动选择的并发由既有机制互斥;命令对状态冲突安静跳过,单条失败不阻断批次。

上线部署步骤

  1. 数据库迁移:后端发版会执行两个迁移(新增 destination_source 列 + 回填存量)。回填规则:cash_destination 非空时,preference_applied = true 填 preference,否则填 user;等待中的债权保持 null。上线前建议在 testing 库确认回填行数符合预期。
  2. 配置默认值:无需 seeder,配置读取不到时自动使用默认值 enabled = true / expire_hours = 168 / destination_priority = ["original"]。
  3. 运维提醒(重要):命令注册为每小时调度(commission-refund:auto-apply-destination,withoutOverlapping)。上线后第一次执行会把所有 action_required_at 超过 7 天的等待中债权一次性自动选向并进入执行。若希望先人工确认,可先通过 internal 更新接口把 enabled 置为 false,或按需把 expire_hours 调大,待确认后再开启/恢复。
  4. 角色授权:权限标识由前端菜单树自动生成(menu-permissions.ts → permissions.ts),无需创建;上线后需在角色管理里把新权限 system-settings.refund-auto-destination 与 system-settings.refund-auto-destination.update 勾选授予相应角色,否则角色看不到菜单/页面 403。后台入口:业务配置 → 退款去向自动选定。
  5. 回滚方式:优先把配置 enabled 置为 false,命令立即停止自动选定(免回滚);如需彻底回滚,revert 对应提交即可,两个迁移可独立 down()(回填 down() 只清理由回填写入且 cash_destination 非空的行,不影响应用后续写入的标记)。