11 KiB
11 KiB
售后工单接入 wx 内容安全机审 — 修改接口(小程序端)
1. 接口背景
用户在小程序发起售后工单(投诉/申诉)时,若附带图片或视频,前端需先通过微信内容安全 msgSecCheck 接口对媒体文件做内容审核,得到微信返回的 traceId 后,连同创建工单请求一并透传给后端。后端不直接调用微信安全接口,只存储 traceId,等待微信异步回调后更新审核状态。无图/无视频的工单跳过机审,直接置为 APPROVED。
2. 变更清单
| 变更类型 | 接口 | 字段 | 说明 |
|---|---|---|---|
| ⚠️ 入参新增 | POST /v3/mp/aftersale/ticket |
mediaTraceIds |
新增可选字段,机审 traceId 数组,最多 12 个 |
| ✨ 出参新增 | GET /v3/mp/aftersale/ticket/{id} |
auditStatus |
工单 wx 内容安全机审状态 |
| ✨ 出参新增 | GET /v3/mp/aftersale/ticket/list |
auditStatus |
工单列表每条记录新增机审状态 |
3. 接口详情
3.1 发起售后工单
| 属性 | 值 |
|---|---|
| 方法 + 路径 | POST /v3/mp/aftersale/ticket |
| 接口描述 | 用户发起售后工单(投诉/申诉),支持附件 + 机审 traceId 透传 |
| 认证 | 需要用户 JWT(小程序登录 token) |
| 幂等性 | 非幂等,每次调用创建一条新工单 |
| 限流 | 无独立限流(受网关全局限流) |
3.2 工单详情
| 属性 | 值 |
|---|---|
| 方法 + 路径 | GET /v3/mp/aftersale/ticket/{id} |
| 接口描述 | 获取单条售后工单详情 |
| 认证 | 需要用户 JWT,仅可查自己的工单 |
| 限流 | 无独立限流 |
3.3 工单列表
| 属性 | 值 |
|---|---|
| 方法 + 路径 | GET /v3/mp/aftersale/ticket/list |
| 接口描述 | 查询当前用户的售后工单列表(分页) |
| 认证 | 需要用户 JWT |
| 限流 | 无独立限流 |
4. 接口入参
4.1 路径参数 / Query 参数
GET /v3/mp/aftersale/ticket/{id}
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | Long | 是 | 工单 ID |
4.2 请求体字段(POST /v3/mp/aftersale/ticket)
| 字段名 | 类型 | 必填 | 校验 | 说明 |
|---|---|---|---|---|
| orderId | Long | 是 | 不能为空 | 关联订单 ID |
| category | String | 是 | 枚举:COMPLAINT / APPEAL | 工单分类:投诉 / 申诉 |
| type | String | 是 | 枚举,见 §6 | 具体类型(如 HOTEL / GUIDE / PRICE) |
| title | String | 是 | max=100 | 工单标题 |
| description | String | 是 | max=2000 | 问题描述 |
| attachments | List<String> | 否 | max=10 | 附件 URL 列表(图片/视频) |
| mediaTraceIds | List<String> | 否 | max=12 | 微信内容安全 traceId 数组(前端透传,后端只存) |
mediaTraceIds与attachments一一对应关系由前端维护。后端不校验对应关系,仅存储用于异步机审回流。无附件时可不传或传空数组。
5. 出参字段
TicketMpRespVO(工单详情/列表单条)
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | Long | 工单 ID |
| orderId | Long | 关联订单 ID |
| orderNo | String | 订单编号 |
| category | String | 工单分类枚举:COMPLAINT / APPEAL |
| type | String | 工单类型 |
| title | String | 标题 |
| description | String | 描述 |
| status | String | 工单状态:SUBMITTED / PROCESSING / RESOLVED / CLOSED / WITHDRAWN |
| statusName | String | 工单状态中文名 |
| canWithdraw | Boolean | 是否可撤回 |
| auditStatus | String | wx 内容安全机审状态,取值见 §6 |
| attachments | List<String> | 附件 URL |
| createdAt | String | 创建时间(ISO 8601) |
| updatedAt | String | 更新时间(ISO 8601) |
6. 枚举 / 数据字典
auditStatus — wx 内容安全机审状态
| 值 | 含义 | 触发条件 |
|---|---|---|
PENDING |
机审中 | 创建工单时有 mediaTraceIds(异步等待微信回调) |
APPROVED |
已通过 | 无 mediaTraceIds 的工单直接置此(免审);微信回调结果合规也置此 |
MANUAL_REVIEW |
人工复审 | 微信机审认为需人工介入 |
REJECTED |
已驳回 | 微信机审判定内容违规 |
重要:
PENDING状态为异步,工单创建成功后微信回调可能在数秒到数分钟内到达。前端建议在工单详情页轮询或基于页面刷新展示最新状态,不要把PENDING当作错误。
category — 工单分类
| 值 | 含义 |
|---|---|
COMPLAINT |
投诉 |
APPEAL |
申诉 |
7. 错误码
| 错误码 | HTTP 状态 | 含义 | 触发场景 |
|---|---|---|---|
| 581001 | 400 | 工单关联订单不存在 | orderId 无效 |
| 581002 | 400 | 工单已存在,不可重复提交 | 同一订单同分类已有活跃工单 |
| 581003 | 400 | 订单状态不允许发起售后 | 订单未处于可售后状态 |
| 200-001 | 401 | 未授权 | JWT 缺失或过期 |
| 200-002 | 403 | 无权操作 | 工单不属于当前用户 |
8. 示例
8.1 典型成功——带图片机审
请求
POST /v3/mp/aftersale/ticket
Authorization: Bearer <user-jwt>
Content-Type: application/json
{
"orderId": 1234567890,
"category": "COMPLAINT",
"type": "HOTEL",
"title": "酒店降级安排",
"description": "预订了四星酒店,实际安排了三星,差价未退。",
"attachments": [
"https://oss.example.com/media/hotel_photo_1.jpg",
"https://oss.example.com/media/hotel_photo_2.jpg"
],
"mediaTraceIds": [
"trace_abc123def456",
"trace_xyz789uvw012"
]
}
响应
{
"code": 200,
"msg": "success",
"data": {
"id": 987654321,
"orderId": 1234567890,
"orderNo": "HL20260622001",
"category": "COMPLAINT",
"type": "HOTEL",
"title": "酒店降级安排",
"description": "预订了四星酒店,实际安排了三星,差价未退。",
"status": "SUBMITTED",
"statusName": "已提交",
"canWithdraw": true,
"auditStatus": "PENDING",
"attachments": [
"https://oss.example.com/media/hotel_photo_1.jpg",
"https://oss.example.com/media/hotel_photo_2.jpg"
],
"createdAt": "2026-06-22T10:30:00",
"updatedAt": "2026-06-22T10:30:00"
}
}
auditStatus初始为PENDING,等待微信异步回调后更新为APPROVED/MANUAL_REVIEW/REJECTED。
8.2 边界情况——无附件工单(直接 APPROVED)
请求
POST /v3/mp/aftersale/ticket
Authorization: Bearer <user-jwt>
Content-Type: application/json
{
"orderId": 1234567890,
"category": "APPEAL",
"type": "PRICE",
"title": "价格异议",
"description": "行程中临时要求加收费用,合同未约定。",
"attachments": [],
"mediaTraceIds": []
}
响应
{
"code": 200,
"msg": "success",
"data": {
"id": 987654322,
"orderId": 1234567890,
"orderNo": "HL20260622001",
"category": "APPEAL",
"type": "PRICE",
"title": "价格异议",
"description": "行程中临时要求加收费用,合同未约定。",
"status": "SUBMITTED",
"statusName": "已提交",
"canWithdraw": true,
"auditStatus": "APPROVED",
"attachments": [],
"createdAt": "2026-06-22T10:35:00",
"updatedAt": "2026-06-22T10:35:00"
}
}
无 mediaTraceIds 或传空数组时,
auditStatus直接为APPROVED,无需等待回调。
8.3 业务失败——订单已有活跃工单
请求(同一订单同分类已存在未关闭工单)
POST /v3/mp/aftersale/ticket
Authorization: Bearer <user-jwt>
Content-Type: application/json
{
"orderId": 1234567890,
"category": "COMPLAINT",
"type": "GUIDE",
"title": "导游服务差",
"description": "导游态度恶劣。",
"mediaTraceIds": []
}
响应
{
"code": 581002,
"msg": "工单已存在,不可重复提交",
"data": null
}
9. 业务边界
适用场景:
- 订单处于可售后状态(已出行、出行中、已完成等,具体由后端校验)
- 有图片/视频附件时前端必须先完成
wx.msgSecCheck得到 traceId 再调此接口 - 无附件或不需要机审时,
mediaTraceIds可省略或传空数组
不适用场景:
- 已取消订单不可发起售后
- 同一订单同分类已有活跃(非 CLOSED/WITHDRAWN)工单时,不可重复发起
特殊边界:
mediaTraceIds最多 12 个,超出返回参数校验错误auditStatus为PENDING时工单照常流转(客服可正常处理),机审结果不阻塞工单流程auditStatus为REJECTED时,仅作展示标记,工单处理流程由客服决定是否关闭
10. 修改前后对比
字段级对比
CreateTicketReqVO(入参)
| 字段 | 变更前 | 变更后 |
|---|---|---|
| mediaTraceIds | 不存在 | 新增,List<String>,可选,max=12 |
TicketMpRespVO(出参)
| 字段 | 变更前 | 变更后 |
|---|---|---|
| auditStatus | 不存在 | 新增,String,wx 机审状态 |
行为级对比
| 场景 | 变更前 | 变更后 |
|---|---|---|
| 带图片发起工单 | 无机审,无审核状态 | 存 traceId,初始 auditStatus=PENDING,微信异步回调更新 |
| 无图片发起工单 | 无机审状态字段 | auditStatus 直接置 APPROVED |
11. 影响评估 / 回滚
| 维度 | 结论 |
|---|---|
| 破坏兼容性 | 否(入参新增可选字段,旧版不传兼容;出参新增字段,旧版忽略) |
| 前端同步上线 | 建议同步:入参传 mediaTraceIds 发挥机审能力;出参展示 auditStatus |
| 回滚方案 | 后端回滚 PR #4203 即可,无 DDL 数据损失风险(DDL 只加列不删) |
12. 注意事项
mediaTraceIds由前端在调用微信wx.msgSecCheck后获得,后端不直接调微信安全接口,只做存储和转发。auditStatus=PENDING是异步状态,不代表工单异常,客服后台可正常处理。- 历史工单(此 PR 上线前创建的)
auditStatus字段为null,前端展示时建议视null等同于APPROVED处理。 - 微信机审结果
REJECTED不自动关闭工单,仅打标记,由客服决定后续处理。