Commission 取消退款通知与页面事件 (2026-09-21)

需求背景

  • 来源:docs/pipipen/feature/COMMISSION_REFUND_PRD.md §3.1 延期项补齐。
  • 动机:取消协商与退款执行过程中,双方缺少对应的站内信告知与待办入口,用户无从得知对方是否已响应、退款何时可选去向。
  • 范围:纯增量——新增 2 个页面事件 type、8 个站内信场景与 1 个通知模板 seeder;本次无 HTTP 接口、无对外字段与错误码变更,也不改既有通知与未付款直接取消路径。
  • 面向读者:前端(用户端 + 画师端)、联调、测试、运营。
  • 术语对照:commission = WorkTask(委托);退款债权 = commission_refunds 记录(obligation)。

更新记录

  • 2026-09-21 首次发布:新增 2 个页面事件 type、8 个站内信场景与 CommissionRefundNotificationTemplateSeeder(开发阶段契约,后端未发版)。
  • 2026-09-23 修订:字段表去除 emoji 前缀,echo / event 详情改为表格(按在途同域迭代规则,展示形式调整,契约无变化)。
  • 2026-09-28 勘误:修正「新场景不登记用户通知设置开关」的错误描述。8 个新场景已登记进 NotificationSettingService::getMapSwitchToScene():commission_cancellation.* 归属开关组 commission_cancellation.updated,commission_refund.* 归属开关组 commission_refund.updated(用户默认 email / site_msg / sms 均开启,画师默认仅开启 commission_cancellation.updated);因此通知渠道按用户在这两个开关组上的设置选择,而不是「不读用户自定义渠道」。

建议阅读顺序:第 1 章(一分钟上手)→ 第 2 章(接口变更总览)→ 第 3 章(落库形态)→ 第 4 章(type / 场景 / meta 对照)→ 第 5 章(兼容性与上线)。遇到「这条通知什么时候发」先看第 1 章流程图与第 4 章对照表。

目录

章内容什么时候看
1. 一分钟上手通知 / 页面事件流转图 + 六条对接要点刚拿到文档
2. 接口变更变更总览(无 HTTP 接口,仅事件与通知)确认改了什么
3. 接口示例页面事件与站内信落库形态写绑定 / 核对数据
4. 接口/字段补充说明type、场景值、meta 键对照查具体取值
5. 兼容性说明兼容项与上线部署步骤发布前确认

1. 一分钟上手

先看流程图理解「哪个动作发哪条站内信、开/关哪条页面事件」,再读六条对接要点即可开始对接。 本次没有新增或变更任何 HTTP 接口,前端只需消费新增的页面事件 type、识别新增的站内信场景。

1.1 取消协商与退款的完整流转

未付款的直接取消走另一条既有链路:只发既有的 worktask.user_canceled / worktask.artist_canceled,不叠加 commission_cancellation.completed,也不写 cancellation_request 页面事件。

1.2 取消协商状态机

本图只画与通知相关的路径:结算阶段失败会进入 failed(运营侧处理,不发站内信与页面事件),failed 恢复后回到对应处理中状态;完整状态机与失败语义见 2026-09-09_commission_cancellation_and_refunds.md。

1.3 退款债权状态机

退款执行失败、人工复核、恢复耗尽只写运营侧日志,不通知用户(本次产品决策 D2);这些状态由退款池接口呈现。

1.4 对接要点

  1. 本次零接口改动:没有任何 HTTP path、请求参数或响应字段变更,联调成本为零;新逻辑只体现在页面事件与站内信两类数据里。
  2. 页面事件新增两个 type:cancellation_request(取消申请待办,to 为对方)与 refund_awaiting_destination(退款待选择去向,to 为 user)。未知 type 按忽略处理即可,不要按旧枚举抛错或崩溃。
  3. 页面事件是否关闭只看 is_close:cancellation_request 在拒绝 / 撤回 / 接受(含条款变更转撤回)时由后端置 is_close = true;refund_awaiting_destination 在用户选择去向或退款执行完成时置 is_close = true。前端不要自行推断关闭。
  4. 页面事件只有待办语义:cancellation_request 与 refund_awaiting_destination 是仅有的两条新待办事件;退款完成、协商取消完成只发站内信、不写页面事件(决策 D4),不要为它们预留新的待办 UI 状态。
  5. 站内信新增 8 个场景值(commission_cancellation.* 6 个 + commission_refund.* 2 个),见第 4 章;commission_cancellation.* 与 commission_refund.* 的 receiver 由后端 notifiable 类型决定(User → user,Artist → artist),前端按 meta.to 分支即可。
  6. 站内信文案来自 notification_templates 表:上线必须执行 seeder 填充初始模板(见 上线部署步骤),否则站内信会以空内容落库(接口不报错,只在用户侧可见)。

2. 接口变更

本次无 HTTP 接口变更:没有新增、删除或修改任何 API path、请求参数、响应字段与错误码。

前端在页面事件列表接口(POST /api/user/worktask_page_event_list/list、POST /api/artist_center/worktask_page_event_list/list 等既有入口)中可能收到新增 type;在站内信列表/详情中可能读到新增 scene 值。除此之外无需任何接口侧调整。

echo / event

  • ✨ 页面事件类型(worktask_page_event_list.type)新增 2 个:
取值to关联 id触发时机
cancellation_request对方(用户发起 → artist,画师发起 → user)data_id = 取消申请 id协商取消申请创建时写入
refund_awaiting_destinationuserdata_id = 退款债权 id退款债权创建且 execution_status = awaiting_destination 时写入
  • ✨ 系统通知场景(SystemNotificationScene)新增 8 个:
场景receiver(实际送达对象)触发时机
commission_cancellation.user_created对方画师用户发起协商取消申请
commission_cancellation.artist_created对方用户画师发起协商取消申请
commission_cancellation.rejected发起方(非操作方)协商取消申请被拒绝
commission_cancellation.withdrawn非操作方申请被撤回(主动撤回或接受时条款变更转撤回)
commission_cancellation.accepted双方对方接受协商取消申请
commission_cancellation.completed双方协商取消完成
commission_refund.awaiting_destination用户退款债权创建且去向未定
commission_refund.completed用户退款执行完成

receiver、meta 键与落库形态见 站内信场景与 meta 与 站内信落库形态;页面事件落库形态见 页面事件落库形态。

上述 8 个场景的站内信模板不在代码里,依赖 notification_templates 表中的模板行;初始数据由 CommissionRefundNotificationTemplateSeeder 提供(共 12 行),上线部署时需执行,见上线部署步骤。


3. 接口示例

本次无新增或变更的 HTTP 接口。以下为页面事件与站内信的落库形态,供前端对接页面事件消费、供运营核对站内信模板使用。

3.1 页面事件落库形态

取消申请待办

{
  "id": 9001,
  "work_task_id": 1001,
  "type": "cancellation_request",
  "data_id": 501,
  "to": "artist",
  "is_close": false,
  "data": null
  // ...其余字段省略
}

退款待选择去向

{
  "id": 9002,
  "work_task_id": 1001,
  "type": "refund_awaiting_destination",
  "data_id": 601,
  "to": "user",
  "is_close": false,
  "data": null
  // ...其余字段省略
}

字段说明

字段类型说明
typestring取 cancellation_request / refund_awaiting_destination
data_idintegercancellation_request 为取消申请 id;refund_awaiting_destination 为退款债权 id
tostringuser / artist;cancellation_request 为对方,refund_awaiting_destination 恒为 user
is_closeboolean待办是否已关闭;前端列表只展示 false
datanull两个新 type 没有关联模型,固定为 null,前端只按 type + data_id 绑定

关闭规则

type关闭时机
cancellation_request申请被拒绝、被撤回(主动撤回或接受时条款变更转撤回)、被接受时,后端在同事务内置 is_close = true
refund_awaiting_destination用户选择退款去向时,或退款执行完成(completed_at 首次置值)时,后端在同事务内置 is_close = true

3.2 站内信落库形态

以「用户发起取消、通知画师」为例(notifications 表 data 列,notifiable_type = artist):

{
  "scene": "commission_cancellation.user_created",
  "meta": {
    "work_task_id": 1001,
    "commission_cancellation_id": 501,
    "to": "artist"
  },
  "title": {
    "zh": "客户发起了取消申请",
    "en": "Cancellation Request Received",
    "ja": "キャンセル申請が届きました",
    "_lang": "en"
  },
  "content": {
    "zh": "客户发起了取消申请,请在委托详情中查看并选择接受或拒绝。",
    "en": "Your client started a cancellation request. Review it in the commission details and choose to accept or reject it.",
    "ja": "依頼者からキャンセル申請が届きました。コミッション詳細で確認し、承認または拒否を選択してください。",
    "_lang": "en"
  }
  // ...其余字段省略
}

title / content 由 SystemNotification::toArray() 按模板多语言对象落库(zh / en / ja + _lang 基准语言),不是纯字符串;前端按用户的语言偏好取对应字段。

退款侧场景的 meta 为 work_task_id / commission_refund_id / trigger_type / to:

{
  "scene": "commission_refund.awaiting_destination",
  "meta": {
    "work_task_id": 1001,
    "commission_refund_id": 601,
    "trigger_type": "cancellation",
    "to": "user"
  },
  "title": {
    "zh": "退款待选择去向",
    "en": "Choose Your Refund Destination",
    "ja": "返金先の選択が必要です",
    "_lang": "en"
  },
  "content": {
    "zh": "有一笔退款等待你选择去向,请尽快在委托详情中完成选择。",
    "en": "A refund is waiting for you to choose a destination. Please complete the choice in the commission details.",
    "ja": "返金先の選択待ちの返金があります。コミッション詳細からお早めに選択してください。",
    "_lang": "en"
  }
  // ...其余字段省略
}

4. 接口/字段补充说明

4.1 页面事件 type 与触发关闭时机

type待办对象 to写入时机关闭时机
cancellation_request对方(用户发起 → artist,画师发起 → user)协商取消申请创建时写入拒绝 / 撤回 / 接受(含条款变更转撤回)
refund_awaiting_destinationuser退款债权创建且 execution_status = awaiting_destination 时写入用户选择去向 / 退款执行完成

同一取消申请 idempotency key 重放、同一退款债权重放(改价审批重放)不会重复写入页面事件;未付款的直接取消不写 cancellation_request。

4.2 站内信场景与 meta

场景值按 . 拆分为 type / event,notification_templates 按 type + event + receiver 定位模板行。

场景值 scenetypeeventreceiver(模板行)实际送达对象meta 键
commission_cancellation.user_createdcommission_cancellationuser_createdartist对方画师work_task_id、commission_cancellation_id
commission_cancellation.artist_createdcommission_cancellationartist_createduser对方用户work_task_id、commission_cancellation_id
commission_cancellation.rejectedcommission_cancellationrejecteduser、artist发起方(非操作方)work_task_id、commission_cancellation_id
commission_cancellation.withdrawncommission_cancellationwithdrawnuser、artist非操作方work_task_id、commission_cancellation_id
commission_cancellation.acceptedcommission_cancellationaccepteduser、artist双方work_task_id、commission_cancellation_id
commission_cancellation.completedcommission_cancellationcompleteduser、artist双方work_task_id、commission_cancellation_id
commission_refund.awaiting_destinationcommission_refundawaiting_destinationuser用户work_task_id、commission_refund_id、trigger_type
commission_refund.completedcommission_refundcompleteduser用户work_task_id、commission_refund_id、trigger_type

补充口径:

  • trigger_type 取值 cancellation(取消链路)或 price_change(降价改价链路);两条触发源共用 commission_refund.* 场景,模板文案不区分 trigger(决策 D3)。
  • rejected / withdrawn 的 receiver 模板行同时提供 user 与 artist 两行,是因为两端都可能作为发起方或被通知方,实际送达方由事件操作方决定。
  • 运行时 meta 会自动追加 to(user / artist),表示本次送达的接收方角色;模板行 meta 列只声明业务键,值填 null。
  • 模板共 12 行(commission_cancellation 10 行 + commission_refund 2 行),sms_template_id 留空,短信通道自动跳过。

5. 兼容性说明

  • 纯增量,无破坏性变更:新增枚举值、新增 seeder、新增服务方法与挂钩调用,无表结构变更、无对外接口契约变更。
  • 前端可平滑升级:页面事件新增 type 前,旧前端只需按「未知 type 忽略」处理即可;站内信新增 scene 值不影响既有场景。
  • 通知渠道:8 个新场景已登记用户通知设置开关(NotificationSettingService::getMapSwitchToScene())——commission_cancellation.* 归属开关组 commission_cancellation.updated,commission_refund.* 归属开关组 commission_refund.updated。通知渠道按用户在这两个开关组上的设置选择(默认 email / site_msg / sms 均开启;画师默认仅开启 commission_cancellation.updated,因此画师不收到 commission_refund.*);sms_template_id 为空时短信自动跳过。
  • 直接取消路径不变:未付款直接取消仍沿用既有 worktask.user_canceled / worktask.artist_canceled 通知,不叠加 commission_cancellation.completed,避免双重通知(决策 D5)。
  • 失败语义不变:退款执行失败、人工复核、恢复耗尽不通知用户,维持运营侧日志与后台重试入口(决策 D2)。
  • 无需实时广播:本次仅站内信落库,不发送 SystemNotificationEvent 实时广播;取消/退款均为用户主动操作的响应,页面本身有反馈。

上线部署步骤

后端发版后,需在服务器上执行一次 seeder,为 8 个新通知场景填充初始模板(缺失时站内信会以空内容落库):

php artisan db:seed --class=CommissionRefundNotificationTemplateSeeder

说明:

  • 该 seeder 幂等(updateOrInsert,按 type / event / receiver 定位),可重复执行,不会产生重复行。
  • 共写入 12 行模板:commission_cancellation 的 6 个 event(receiver 按第 4 章表,合计 10 行)+ commission_refund 的 2 个 event(均 user),文案为中 / 英 / 日初版。
  • 该 seeder 也已注册进 DatabaseSeeder,随 php artisan db:seed 一并执行。
  • sms_template_id 留空,短信通道自动跳过;上线后由运营在后台二次修改文案,并按需关联短信模板。
  • 回滚方式:revert 对应单次提交即可;seeder 行可单独删除,不影响既有模板行。