用户通知设置开关分组补齐 (2026-09-22)

需求背景

  • 来源:2026-09-22 通知开关分组评审(pipipen-api 通知系统全量盘点),批准方案「新增 2 个开关 + 节点确认并入既有开关」。
  • 动机:取消/退款协商、退款进度、节点确认等场景此前未登记到用户通知开关,用户无法自定义渠道(关闭或开启);设置页开关粒度也与后端实际场景不匹配。
  • 范围:纯增量——新增 2 个开关枚举 case、work_task.confirm_stage 语义扩展,并在默认清单新增 4 个 key(响应数组新增 key);本次不改 HTTP 方法/路径/请求参数/错误码,也不改任何场景的发送条件与通知模板。
  • 面向读者:前端(用户端 + 画师端)、联调、测试、运营。
  • 术语对照:commission = WorkTask(委托);Open Call = Project(对外展示名)。

更新记录

  • 2026-09-22 首次发布:SystemNotificationSwitchScene 新增 commission_cancellation.updated / commission_refund.updated,work_task.confirm_stage 语义扩展为节点确认全生命周期(开发阶段契约,后端未发版)。
  • 2026-09-23 修订:字段表去除 emoji 前缀(按在途同域迭代规则,展示形式调整,契约无变化)。

建议阅读顺序:第 1 章(一分钟上手)→ 第 2 章(接口变更总览)→ 第 3 章(接口示例)→ 第 4 章(场景登记口径)→ 第 5 章(兼容性与前端跟进清单)。遇到「某个场景到底归哪个开关」先看第 4 章登记清单。

目录

章内容什么时候看
1. 一分钟上手开关→通道决策图 + 存量合并图 + 七条对接要点刚拿到文档
2. 接口变更通知设置两个接口的变更摘要找接口
3. 接口示例请求参数、响应示例与错误语义对接具体接口
4. 接口/字段补充说明登记 / 不登记 / 暂缓场景口径与映射去重查具体归属
5. 兼容性说明兼容结论与前端跟进清单发布前确认

1. 一分钟上手

先看两张图理解「用户开关如何决定一条通知走哪些通道」,再读七条对接要点即可开始对接。 本次是纯增量:新增 3 个开关 key(2 个新开关 + 1 个既有开关补入客户侧),没有新增/删除/改名的 HTTP 接口,也没有改动任何场景的发送条件。

1.1 用户开关如何决定通知通道

登记开关只增加「可自定义」能力:登记前后「用户未设置」都走默认渠道,行为一致;无兼容性突变。

1.2 存量用户读取时如何合并出新 key

1.3 对接要点

  1. 本次无 HTTP 签名变更:POST /api/notification_setting/list、POST /api/notification_setting/update 的路径、参数、错误码全部不变;只是响应里的开关数组多了元素,属向后兼容的纯增量。
  2. 4 个新增 key 必须显式渲染:user 角色新增 commission_cancellation.updated、commission_refund.updated、work_task.confirm_stage;artist 角色新增 commission_cancellation.updated。当前前端设置页的开关清单是前端硬编码(pages/account/inform.vue 的 user_list / artist_list),只渲染本地清单里存在的 key——后端响应里有、前端清单里没有的 key 不会自动出现。前端需按第 5 章清单补齐。
  3. 新 key 默认三通道全开:commission_cancellation.updated / commission_refund.updated / work_task.confirm_stage 的 email / site_msg / sms 默认均为 true;存量用户无需重新保存,读取响应即包含这些 key 与默认值。
  4. work_task.confirm_stage 语义扩展为「节点确认」:该开关现覆盖交稿待确认、到期提醒、自动确认(共 3 个新场景)与既有节点确认场景;前端文案 set.inform.confirm_stage 需由「客户确认节点」改为「节点确认」,并在客户(user)角色的设置页新增渲染该开关(此前只在画师侧展示)。
  5. 退款开关只在客户侧展示:commission_refund.updated 的两个场景都只发客户,artist 角色的默认清单不含该 key,前端画师侧不要渲染。
  6. 只有已登记的 key 会出现在响应里:本次登记 9(取消与退款协商)+ 2(退款进度)+ 3(节点确认并入既有开关)个场景,其余场景仍不可自定义(见第 4 章),前端不要为未登记场景预留 UISwitch。
  7. 设置更新只影响已登记场景:用户关闭某开关站内信后,只有归属该开关的场景不再落库;未登记场景不受任何开关影响,仍走默认渠道。

2. 接口变更

user

  • POST /api/notification_setting/list
    • 功能:读取当前用户的 user(客户)/ artist(画师)两套通知开关设置
    • 变更:✨ 响应的开关数组新增 key(user 3 个、artist 1 个),存量用户读取时自动按默认清单合并出新 key
  • POST /api/notification_setting/update
    • 功能:更新当前用户的 user / artist 通知开关设置
    • 变更:✨ 请求/响应结构与校验规则不变;响应的开关数组同样包含新增 key,可正常回写这些 key

两个接口共用同一套 NotificationChannelSetting(key / email / site_msg / sms)响应结构,只是 list 为只读、update 为写后回读。


3. 接口示例

user

POST /api/notification_setting/list

  • 功能说明:读取当前登录用户的 user(客户)与 artist(画师)两套通知开关设置。非画师用户的 artist 为 null。
  • 变更说明:✨ 每套设置的开关数组新增 key。user 新增 commission_cancellation.updated、commission_refund.updated、work_task.confirm_stage;artist 新增 commission_cancellation.updated;⚠️ 既有开关 work_task.confirm_stage 语义扩展为节点确认全生命周期。存量用户已保存的旧 key 值保持不变,新 key 以三通道全开的默认值补齐。

请求参数

无。

响应示例

{
  "data": {
    "user": [
      {
        "key": "service_request.artist_reply",
        "email": true,
        "site_msg": true,
        "sms": true
      },
      {
        "key": "commission_cancellation.updated",
        "email": true,
        "site_msg": true,
        "sms": true
      },
      {
        "key": "commission_refund.updated",
        "email": true,
        "site_msg": true,
        "sms": true
      },
      {
        "key": "work_task.confirm_stage",
        "email": true,
        "site_msg": true,
        "sms": true
      }
    ],
    "artist": [
      {
        "key": "commission_cancellation.updated",
        "email": true,
        "site_msg": true,
        "sms": true
      }
    ]
    // ...其余字段省略
  }
}

上面只截取部分 key 示意,完整清单以 NotificationSettingService::getDefaultSettings() 为准。非画师用户的 artist 返回 null。

错误响应

无新增,沿用原有错误语义(未登录为既有鉴权错误)。

POST /api/notification_setting/update

  • 功能说明:更新当前登录用户的 user / artist 通知开关设置,写入后回读最新设置。
  • 变更说明:✨ 请求/响应结构与校验规则不变;新增 key 可作为 user.*.key / artist.*.key 正常提交,未提交的新 key 在响应里保留默认值。

请求参数

字段类型必填说明
userarray否客户角色开关数组;仅提交需要修改的项即可
user.*.keystring是(有 user 时)开关 key,例如 commission_cancellation.updated
user.*.emailboolean否邮件通道开关
user.*.site_msgboolean否站内信通道开关
user.*.smsboolean否短信通道开关
artistarray否画师角色开关数组;仅画师用户生效
artist.*.keystring是(有 artist 时)开关 key,例如 commission_cancellation.updated
artist.*.emailboolean否邮件通道开关
artist.*.site_msgboolean否站内信通道开关
artist.*.smsboolean否短信通道开关

其他校验规则

  • key 不在默认清单内的项会被忽略,不写入存储;不会报错。
  • 非画师用户提交 artist 会被忽略,不影响 user 设置。
  • 未提交的既有 key 保留原值,未提交的新 key 保留默认值。

请求示例

{
  "user": [
    { "key": "commission_refund.updated", "email": true, "site_msg": false, "sms": true }
  ]
}

响应示例

{
  "data": {
    "user": [
      {
        "key": "commission_refund.updated",
        "email": true,
        "site_msg": false,
        "sms": true
      }
    ],
    "artist": null
  }
  // ...其余字段省略
}

响应为写后回读的完整设置(此处只截取被修改的 key);结构同 list。

错误响应

422:参数类型校验失败

{
  "message": "The user.0.key field is required."
}

无新增错误码。


4. 接口/字段补充说明

4.1 ✨ 本次登记的场景清单(14 个)

开关枚举 SystemNotificationSwitchScene 新增 2 个 case,映射 NotificationSettingService::getMapSwitchToScene() 新增 2 组、扩展 1 组。

开关 key展示角色聚合场景场景数
commission_cancellation.updateduser + artistcommission_cancellation.user_created / artist_created / rejected / withdrawn / accepted / completed;worktask.user_canceled / admin_canceled / artist_canceled9
commission_refund.updated仅 usercommission_refund.awaiting_destination / completed2
work_task.confirm_stage(既有开关,语义扩展)user + artist既有 worktask.user_confirm_stage / worktask.stage_finished;新增 worktask.stage_awaiting_confirmation / stage_auto_confirm_reminder / stage_auto_confirmed5(+3)

说明:

  • 协商取消 6 个场景双方都可能收到;直接取消 3 个场景中,用户取消 → 画师收、画师取消 → 用户收、管理员取消 → 双方收,故 commission_cancellation.updated 在客户与画师两个角色都展示。
  • 两个退款场景均只发客户,commission_refund.updated 不加入画师默认清单。
  • work_task.confirm_stage 并入节点确认 3 场景后覆盖节点确认全生命周期(画师交稿请求确认 → 到期提醒 → 自动确认 / 客户确认 → 节点完成),粒度与既有「稿酬变更」(一个开关聚合 10 个场景)一致;客户侧新增展示该开关。

4.2 不登记为可关的场景(7 个)

保持始终发送,用户设置页不展示对应开关:

场景理由
worktask.refresh前端实时刷新的技术广播,非用户通知
wallet.need_rebind / wallet.kyc / wallet.alipay_publish钱包安全与合规提醒,必须送达
activity.christmas_event运营活动推送,运营侧控制
admin.joined_group管理员入群的系统事件
worktask.artist_rejected死场景(全库无发送代码,仅枚举定义),另行立项清理

4.3 暂缓登记的历史场景(21 个,第二批评估)

本轮不动,避免设置页一次性膨胀:

  • Open Call 应征流程 9 个:project_request.created / user_choosen / artist_exited / user_canceled / artist_rejected / artist_accepted / user_paid,以及 project.user_canceled / project.artist_canceled
  • 服务申请取消 2 个:service_request.user_canceled / admin_canceled
  • 商品 2 个:product_order.paid、product.refresh
  • 评价审核 2 个:artist_rec_review.created、user_rec_review.created
  • 申请者结果 2 个:user_applicant.accepted / rejected
  • 群组 2 个:group.created、group.reminder
  • 其他 2 个:worktask.created、report.processed

上述未登记场景在 NotificationSettingService::getMapSwitchToScene() 中无映射,SystemNotification::via() 直接走默认渠道(站内信 + 邮件 + 短信 + 用户动作),行为与本次改动前一致。

4.4 映射去重说明(无行为变化)

  • 改动前 worktask.stage_finished 同时出现在 work_task.user_paid 与 work_task.confirm_stage 映射里,worktask.finished 同时出现在 work_task.user_paid 与 work_task.confirm_last_stage 里。
  • buildSceneToSwitchCache() 对同一场景后写覆盖,实际归属一直是 work_task.confirm_stage / work_task.confirm_last_stage。
  • 本次从 work_task.user_paid 列表移除这两个场景(去重),不改变实际归属与发送行为,仅消除歧义;work_task.user_paid 仍聚合 service_request.user_paid / worktask.wait_pay / worktask.working。

5. 兼容性说明

5.1 兼容性结论

  • 纯增量,无破坏性变更:只新增枚举 case、映射条目与默认清单 key,无表结构变更、无接口签名/参数/错误码变更。
  • 旧前端可平滑升级:设置响应新增 key 前,旧前端忽略未知 key 即可;不读取新增 key 时行为与现状一致。
  • 存量用户设置不受影响:getRoleSettings() 按默认清单合并,已保存的旧 key 值保持不变,新 key 以三通道全开补齐。
  • 发送行为无突变:登记开关只增加「可自定义」能力;用户从未设置到设置之间,未设置场景仍走默认渠道。已登记场景在用户主动关闭某通道后,对应通道不再发送(关闭站内信则不落库)——这是本需求的目的。
  • 发送侧代码零改动:SystemNotification.php 的 via() / getCustomChannels() 逻辑不变,未登记场景仍走默认渠道。
  • 模板与场景值不变:不改 notification_templates seeder 与任何场景值。

5.2 前端跟进清单(pipipen-front,本任务不实施)

当前设置页 pages/account/inform.vue 的 user_list / artist_list 为硬编码清单,fetch_list() 只把后端响应值回填到本地清单已有的 key 上。后端新增 key 后,前端必须显式补齐以下内容:

  1. 新增文案(zh / en / ja)
    • commission_cancellation.updated:「取消与退款协商」(建议 locale key set.inform.commission_cancellation)
    • commission_refund.updated:「退款进度」(建议 locale key set.inform.commission_refund)
  2. 修改文案(zh / en / ja)
    • set.inform.confirm_stage:中文「客户确认节点」→「节点确认」;同步调整英文(现为 Client confirmed a stage)与日文(现为 クライアントによるステージ確認),避免与并入后的语义不符。
  3. user_list(客户角色)新增 3 个 key
    • commission_cancellation.updated → 文案见第 1 条
    • commission_refund.updated → 文案见第 1 条
    • work_task.confirm_stage → 文案用改后的 set.inform.confirm_stage(此前只在画师侧渲染,客户侧需新增)
  4. artist_list(画师角色)新增 1 个 key
    • commission_cancellation.updated → 文案见第 1 条(退款开关不在画师侧展示,勿加)

新 key 默认三通道全开,前端无需为默认值做兼容处理;仅需补齐清单与文案。

上线部署步骤

  • 后端:无需迁移、无需 seeder,直接发版即可;新枚举、映射与默认清单随代码生效。
  • 前端:随前端发版补齐第 5.2 节文案与清单;前端未上线前,后端新增 key 已在响应中,旧前端忽略即可,无运行风险。
  • 无需执行服务器脚本;无配置项、无权限项、无通知模板变更。
  • 回滚方式:revert 对应后端提交即可;已保存的设置行不受影响,回滚后前端如已上线会因缺少对应 key 而跳过渲染(fetch_list 回填时需注意旧前端对缺失 key 的空值处理,见下条注意事项)。

注意事项:当前前端 fetch_list() 对本地清单里的每个 key 直接执行 data.user.find(...) 后取 item.site_msg,未做空值判断。若前端清单已加 key 而后端未上线(或回滚),会因 item 为 undefined 报错。发布顺序请保证后端先上线,或由前端补上空值兜底。