需求背景
docs/pipipen/feature/COMMISSION_REFUND_PRD.md §3.1 延期项补齐。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. 兼容性说明 | 兼容项与上线部署步骤 | 发布前确认 |
先看流程图理解「哪个动作发哪条站内信、开/关哪条页面事件」,再读六条对接要点即可开始对接。 本次没有新增或变更任何 HTTP 接口,前端只需消费新增的页面事件 type、识别新增的站内信场景。
未付款的直接取消走另一条既有链路:只发既有的
worktask.user_canceled/worktask.artist_canceled,不叠加commission_cancellation.completed,也不写cancellation_request页面事件。
本图只画与通知相关的路径:结算阶段失败会进入
failed(运营侧处理,不发站内信与页面事件),failed恢复后回到对应处理中状态;完整状态机与失败语义见 2026-09-09_commission_cancellation_and_refunds.md。
退款执行失败、人工复核、恢复耗尽只写运营侧日志,不通知用户(本次产品决策 D2);这些状态由退款池接口呈现。
cancellation_request(取消申请待办,to 为对方)与 refund_awaiting_destination(退款待选择去向,to 为 user)。未知 type 按忽略处理即可,不要按旧枚举抛错或崩溃。is_close:cancellation_request 在拒绝 / 撤回 / 接受(含条款变更转撤回)时由后端置 is_close = true;refund_awaiting_destination 在用户选择去向或退款执行完成时置 is_close = true。前端不要自行推断关闭。cancellation_request 与 refund_awaiting_destination 是仅有的两条新待办事件;退款完成、协商取消完成只发站内信、不写页面事件(决策 D4),不要为它们预留新的待办 UI 状态。commission_cancellation.* 6 个 + commission_refund.* 2 个),见第 4 章;commission_cancellation.* 与 commission_refund.* 的 receiver 由后端 notifiable 类型决定(User → user,Artist → artist),前端按 meta.to 分支即可。notification_templates 表:上线必须执行 seeder 填充初始模板(见 上线部署步骤),否则站内信会以空内容落库(接口不报错,只在用户侧可见)。本次无 HTTP 接口变更:没有新增、删除或修改任何 API path、请求参数、响应字段与错误码。
前端在页面事件列表接口(
POST /api/user/worktask_page_event_list/list、POST /api/artist_center/worktask_page_event_list/list等既有入口)中可能收到新增 type;在站内信列表/详情中可能读到新增 scene 值。除此之外无需任何接口侧调整。
worktask_page_event_list.type)新增 2 个:| 取值 | to | 关联 id | 触发时机 |
|---|---|---|---|
cancellation_request | 对方(用户发起 → artist,画师发起 → user) | data_id = 取消申请 id | 协商取消申请创建时写入 |
refund_awaiting_destination | user | data_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 行),上线部署时需执行,见上线部署步骤。
本次无新增或变更的 HTTP 接口。以下为页面事件与站内信的落库形态,供前端对接页面事件消费、供运营核对站内信模板使用。
| 字段 | 类型 | 说明 |
|---|---|---|
type | string | 取 cancellation_request / refund_awaiting_destination |
data_id | integer | cancellation_request 为取消申请 id;refund_awaiting_destination 为退款债权 id |
to | string | user / artist;cancellation_request 为对方,refund_awaiting_destination 恒为 user |
is_close | boolean | 待办是否已关闭;前端列表只展示 false |
data | null | 两个新 type 没有关联模型,固定为 null,前端只按 type + data_id 绑定 |
| type | 关闭时机 |
|---|---|
cancellation_request | 申请被拒绝、被撤回(主动撤回或接受时条款变更转撤回)、被接受时,后端在同事务内置 is_close = true |
refund_awaiting_destination | 用户选择退款去向时,或退款执行完成(completed_at 首次置值)时,后端在同事务内置 is_close = true |
以「用户发起取消、通知画师」为例(notifications 表 data 列,notifiable_type = artist):
title/content由SystemNotification::toArray()按模板多语言对象落库(zh/en/ja+_lang基准语言),不是纯字符串;前端按用户的语言偏好取对应字段。
退款侧场景的 meta 为 work_task_id / commission_refund_id / trigger_type / to:
| type | 待办对象 to | 写入时机 | 关闭时机 |
|---|---|---|---|
cancellation_request | 对方(用户发起 → artist,画师发起 → user) | 协商取消申请创建时写入 | 拒绝 / 撤回 / 接受(含条款变更转撤回) |
refund_awaiting_destination | user | 退款债权创建且 execution_status = awaiting_destination 时写入 | 用户选择去向 / 退款执行完成 |
同一取消申请 idempotency key 重放、同一退款债权重放(改价审批重放)不会重复写入页面事件;未付款的直接取消不写
cancellation_request。
场景值按 . 拆分为 type / event,notification_templates 按 type + event + receiver 定位模板行。
场景值 scene | type | event | receiver(模板行) | 实际送达对象 | meta 键 |
|---|---|---|---|---|---|
commission_cancellation.user_created | commission_cancellation | user_created | artist | 对方画师 | work_task_id、commission_cancellation_id |
commission_cancellation.artist_created | commission_cancellation | artist_created | user | 对方用户 | work_task_id、commission_cancellation_id |
commission_cancellation.rejected | commission_cancellation | rejected | user、artist | 发起方(非操作方) | work_task_id、commission_cancellation_id |
commission_cancellation.withdrawn | commission_cancellation | withdrawn | user、artist | 非操作方 | work_task_id、commission_cancellation_id |
commission_cancellation.accepted | commission_cancellation | accepted | user、artist | 双方 | work_task_id、commission_cancellation_id |
commission_cancellation.completed | commission_cancellation | completed | user、artist | 双方 | work_task_id、commission_cancellation_id |
commission_refund.awaiting_destination | commission_refund | awaiting_destination | user | 用户 | work_task_id、commission_refund_id、trigger_type |
commission_refund.completed | commission_refund | completed | user | 用户 | 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。commission_cancellation 10 行 + commission_refund 2 行),sms_template_id 留空,短信通道自动跳过。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)。SystemNotificationEvent 实时广播;取消/退款均为用户主动操作的响应,页面本身有反馈。后端发版后,需在服务器上执行一次 seeder,为 8 个新通知场景填充初始模板(缺失时站内信会以空内容落库):
说明:
updateOrInsert,按 type / event / receiver 定位),可重复执行,不会产生重复行。commission_cancellation 的 6 个 event(receiver 按第 4 章表,合计 10 行)+ commission_refund 的 2 个 event(均 user),文案为中 / 英 / 日初版。DatabaseSeeder,随 php artisan db:seed 一并执行。sms_template_id 留空,短信通道自动跳过;上线后由运营在后台二次修改文案,并按需关联短信模板。