售后拆分重构(PR #4251 #4254)对外接口变更: - 小程序端(changelogs-v2-mp): 申诉新增 4 接口(发起/我的/详情/撤回)+ /v3/mp/aftersale/ticket* 下线 404 - 管理后台(changelogs-v2): 申诉新增 2 接口(分页/详情,关联展示来源+触发退款)+ /v3/admin/aftersale/ticket* 下线 404 - 申诉复用退款审批(无独立申诉审批);投诉仅移包路径不变 - 13 节自包含:全字段+枚举(appealType/appealStatus)+错误码 530601-609+示例(含工单 404 异常)
8.8 KiB
退款申诉(新增)+ 统一售后工单接口下线(小程序端)
- 端类型:小程序端
- 变更类型:新增接口(4)+ 删除接口(统一售后工单 mp 端)
- 关联 Issue:#4250 PR:#4251 #4254
- 日期:2026-06-23
① 接口背景
售后体系重构:原"投诉 + 申诉统一售后工单"方向作废,拆为投诉与申诉两套独立能力。
- 申诉:用户对"退款被拒"或"退款金额"不服时发起,小程序入口仍是「退款申诉」。申诉记录单独存(不再混入退款申请表)。
- 关键变化:申诉复用既有退款审批——发起申诉后系统自动建一笔 PENDING 退款申请,由管理员在退款审批里复核(通过则退款 / 驳回则结束);申诉本身不再有独立审批。
- 原统一售后工单接口(
/v3/mp/aftersale/ticket*)全部下线。
② 变更清单
| # | 方法 | 路径 | 类型 |
|---|---|---|---|
| 1 | POST | /v3/mp/refund/appeal |
🆕 新增(发起申诉) |
| 2 | GET | /v3/mp/refund/appeal/my |
🆕 新增(我的申诉,分页) |
| 3 | GET | /v3/mp/refund/appeal/{id} |
🆕 新增(申诉详情) |
| 4 | POST | /v3/mp/refund/appeal/{id}/withdraw |
🆕 新增(撤回申诉) |
| 5 | ALL | /v3/mp/aftersale/ticket* |
❌ 删除(统一售后工单下线,调用返回 404) |
统一响应包装 Result<T>:{ code, message, data, success },code=200 为成功。
③ 接口详情
1. 发起申诉 POST /v3/mp/refund/appeal
用户对一笔退款申请发起申诉。两种类型:REFUND_REJECTED(退款被拒,原退款 status=REJECTED 才可申诉)/ REFUND_AMOUNT_DISPUTE(退款金额异议,原退款 status=REFUNDED 求补差)。
发起成功后:① 落一条申诉记录(PENDING)② 自动建一笔 PENDING 退款申请走退款审批 ③ 订单进入「售后中」。
2. 我的申诉 GET /v3/mp/refund/appeal/my
当前登录用户的申诉分页列表。
3. 申诉详情 GET /v3/mp/refund/appeal/{id}
单条申诉详情,含关联的两笔退款(来源退款 + 申诉触发的退款)。
4. 撤回申诉 POST /v3/mp/refund/appeal/{id}/withdraw
仅 PENDING(审批中)状态可撤回;撤回会一并取消申诉触发的那笔 PENDING 退款申请。
④ 入参
接口1 create(@RequestBody,登录态 userId 由网关注入)
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| refundApplicationId | long | 是 | 来源退款申请 ID(被申诉的那笔退款) |
| appealType | string | 是 | 申诉类型:REFUND_REJECTED / REFUND_AMOUNT_DISPUTE |
| appealReason | string | 是 | 申诉理由(≤2000 字) |
| appealAmount | string(decimal) | 否 | 期望退款金额(金额异议时填) |
| mediaTraceIds | string[] | 否 | 凭证图片机审 traceId 列表(前端上传后透传,≤12) |
接口2 my
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| pageNo | int | 否 | 页码,默认 1 |
| pageSize | int | 否 | 每页条数,默认 20 |
接口3 detail / 接口4 withdraw
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
| id | path | long | 申诉 ID |
⑤ 出参
接口1 create Result<Long>
data = 新建申诉 ID(字符串化 Long)。
接口2 my Result<PageResult<AppealMpRespVO>>;接口3 detail Result<AppealMpRespVO>
PageResult:{ records[], total, page, pageSize }。AppealMpRespVO:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | string(Long) | 申诉 ID |
| orderId | string(Long) | 订单 ID |
| refundApplicationId | string(Long) | 来源退款申请 ID |
| appealType | string | 申诉类型(枚举值,见⑥) |
| appealTypeLabel | string | 申诉类型中文名 |
| appealReason | string | 申诉理由 |
| appealAmount | string(decimal) | 申诉退款金额 |
| appealStatus | string | 申诉状态(枚举值,见⑥) |
| appealStatusLabel | string | 申诉状态中文名 |
| applicantName | string | 申请人姓名 |
| reviewedAt | datetime | 审批完成时间(可空) |
| createTime | datetime | 创建时间 |
| sourceRefund | object | 来源退款信息(被申诉的原退款),见下 |
| triggeredRefund | object | 申诉触发的退款信息(申诉建单后自动生成的退款),见下 |
sourceRefund / triggeredRefund 结构(关联退款信息):
| 字段 | 类型 | 说明 |
|---|---|---|
| refundApplicationId | string(Long) | 退款申请 ID |
| status | string | 退款状态(PENDING/APPROVED/REJECTED/REFUNDING/REFUNDED/CANCELLED/ABNORMAL) |
| actualAmount | string(decimal) | 实际退款金额(可空) |
接口4 withdraw Result<Void>
data = null。
⑥ 枚举 / 数据字典
申诉类型 appealType:
| 值 | 含义 |
|---|---|
| REFUND_REJECTED | 退款被拒(原退款被驳回后申诉) |
| REFUND_AMOUNT_DISPUTE | 退款金额异议(已退款但金额有异议,求补差) |
申诉状态 appealStatus:
| 值 | 含义 |
|---|---|
| PENDING | 审批中(申诉触发的退款申请待退款审批) |
| REJECTED | 已驳回(退款审批驳回) |
| REFUNDED | 退款到账(申诉成功,退款已退) |
| WITHDRAWN | 已撤回 |
⑦ 错误码(段位 530600-530699)
| code | message |
|---|---|
| 530601 | 申诉必须关联一笔退款申请 |
| 530602 | 关联的退款申请不存在 |
| 530603 | 退款申请当前状态不满足申诉条件 |
| 530604 | 该退款申请已有处理中的申诉,请勿重复提交 |
| 530605 | 申诉退款金额超出可退余额 |
| 530606 | 申诉记录不存在 |
| 530607 | 无权操作此申诉 |
| 530608 | 当前申诉状态不允许撤回,仅待审核状态可撤回 |
| 530609 | 申诉类型无效 |
⑧ 示例
典型:发起申诉(退款被拒)
请求 POST /v3/mp/refund/appeal:
{ "refundApplicationId": 20001, "appealType": "REFUND_REJECTED",
"appealReason": "退款审核不合理,申请复核", "appealAmount": "300.00",
"mediaTraceIds": [] }
响应:{ "code":200, "message":"成功", "data":"40001", "success":true }
典型:我的申诉
请求 GET /v3/mp/refund/appeal/my?pageNo=1&pageSize=10,响应(节选一项):
{ "code":200, "success":true, "data": { "total":1, "records":[
{ "id":"40001", "orderId":"10001", "appealType":"REFUND_REJECTED", "appealTypeLabel":"退款被拒",
"appealStatus":"PENDING", "appealStatusLabel":"审批中", "appealAmount":"300.00",
"sourceRefund": { "refundApplicationId":"20001", "status":"REJECTED", "actualAmount":null },
"triggeredRefund": { "refundApplicationId":"20009", "status":"PENDING", "actualAmount":null } }
] } }
异常:重复申诉
对同一退款申请已有处理中申诉时再发起:
{ "code":530604, "message":"该退款申请已有处理中的申诉,请勿重复提交", "success":false }
异常:调用已下线的工单接口
请求 POST /v3/mp/aftersale/ticket(旧统一售后工单接口):
{ "code":404, "message":"Not Found" }
⑨ 业务边界
- 申诉复用退款审批:发起后自动建 PENDING 退款申请,管理员在退款审批里复核——前端不要再调用任何"申诉审批"接口(已无独立申诉审批)。
- 申诉状态跟随其触发的退款申请:退款到账→REFUNDED,退款审批驳回→REJECTED。
- 撤回仅 PENDING 可撤,且会取消触发的 PENDING 退款。
- 申诉发起即把订单标记「售后中」,终结(REFUNDED/REJECTED/WITHDRAWN 且无其他活跃售后)后回切。
⑩ 修改前后对比
| 修改前 | 修改后 | |
|---|---|---|
| 申诉入口 | 统一售后工单 POST /v3/mp/aftersale/ticket(category=APPEAL) |
POST /v3/mp/refund/appeal |
| 我的申诉 | /v3/mp/aftersale/ticket/my |
/v3/mp/refund/appeal/my |
| 详情/撤回 | /v3/mp/aftersale/ticket/{id} /{id}/withdraw |
/v3/mp/refund/appeal/{id} /{id}/withdraw |
| 审批 | 申诉独立 OA 审批 | 无(复用退款审批) |
⑪ 影响评估 / 回滚
- 破坏性:
/v3/mp/aftersale/ticket*已删,调用返回 404,前端涉及"退款申诉"的页面必须切到新申诉接口。 - 投诉接口不在此列(投诉走 C 端经 BFF 的现有通道,无变化)。
- 回滚:后端回滚 PR #4251 #4254。
⑫ 注意事项
- 金额字段(appealAmount / actualAmount)均为字符串,前端按字符串处理防精度丢失。
- Long 型 ID 均字符串化返回(防 JS 精度)。
- 上传凭证图片仍走既有 wx 内容安全机审通道(mediaTraceIds 透传)。
⑬ 关联 / 联系人
- Issue:wx/HL#4250
- PR:wx/HL#4251 ・ wx/HL#4254
- 后端负责人:腰苏图
- 已部署测试服并网关实调验证通过(申诉接口 200 / 工单接口 404)。