205 行
6.8 KiB
Markdown
205 行
6.8 KiB
Markdown
# 订单投诉模块 - 小程序发起 + 后台只读列表
|
||
|
||
- **变更日期**: 2026-04-23
|
||
- **PR**: #1221(主) + #1222/#1223/#1225/#1235(配套 hotfix)
|
||
- **Closes**: #1220
|
||
- **影响面**: 小程序端(新增 5 接口) + 管理端(新增 2 接口 + 1 菜单)
|
||
- **兼容性**: **纯新增**,无既有接口变更
|
||
|
||
---
|
||
|
||
## 🎯 业务背景
|
||
|
||
客户在行程中任意一天(含"整程")可发起投诉;后端自动通过**企业微信 + 短信**双链路通知订单负责的定制师(管理员)。管理后台侧边栏新增"投诉管理"一级菜单,定制师只读查看自己订单的投诉,超管/主管看全部。无审批流,客户本人可填/改解决备注。
|
||
|
||
---
|
||
|
||
## 🛒 小程序端接口(/mp/complaint/**)
|
||
|
||
统一前缀 `/mp/complaint`,userId 从网关注入的 token 取,前端无需传。
|
||
|
||
### 1. 发起投诉
|
||
|
||
```http
|
||
POST /mp/complaint/create
|
||
Authorization: Bearer <mp-token>
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"orderId": 99988,
|
||
"dayNumber": 2, // 0 或 null = "整程投诉"; 1..N = 第 N 天
|
||
"types": ["ITINERARY", "VEHICLE"], // 字典 complaint_type: ITINERARY / VEHICLE / HOTEL / OTHER
|
||
"content": "第二天酒店入住流程混乱...", // 必填 1~500 字
|
||
"imageUrls": ["https://oss/.../1.jpg"], // ≤9,前端 OSS 直传后给后端 URL
|
||
"videoUrl": "https://oss/.../1.mp4" // 可空 ≤1
|
||
}
|
||
|
||
返回:
|
||
{"code":200,"data":{"complaintId":1024},"success":true}
|
||
```
|
||
|
||
**错误码**:
|
||
| Code | 含义 | 附加字段 |
|
||
|------|------|------|
|
||
| 20301 | 10 分钟冷却中(同一单同一天) | `remainingSeconds` |
|
||
| 20302 | 订单不属于当前用户 | — |
|
||
| 20303 | dayNumber 超出已发生范围 | `maxDayNumber` |
|
||
|
||
### 2. 撤回投诉(10 分钟内本人可)
|
||
|
||
```http
|
||
POST /mp/complaint/{id}/cancel
|
||
返回: {"code":200,"data":{"success":true}}
|
||
|
||
错误:
|
||
- 20305 非本人投诉
|
||
- 20306 已超 10 分钟
|
||
```
|
||
|
||
### 3. 我的投诉列表
|
||
|
||
```http
|
||
GET /mp/complaint/my-page?pageNo=1&pageSize=10
|
||
|
||
返回: Page<MpComplaintSimpleRespVO>:
|
||
- id / orderNo / groupCode
|
||
- dayNumber / dayLabel("整程" 或 "第N天")
|
||
- types: [{code, label}]
|
||
- contentSnippet(前 50 字)
|
||
- imageCount / videoCount
|
||
- isResolved(resolutionNote 非空)
|
||
- resolutionEditCount / createdAt
|
||
```
|
||
|
||
### 4. 投诉详情
|
||
|
||
```http
|
||
GET /mp/complaint/{id}
|
||
|
||
返回 MpComplaintRespVO(在 SimpleRespVO 基础上 + content/imageUrls/videoUrl/resolutionNote/resolutionUpdatedAt/canCancel/canEditResolution)
|
||
|
||
错误: 20304 不存在或已撤回 / 20305 非本人
|
||
```
|
||
|
||
### 5. 填/改解决备注(最多 2 次)
|
||
|
||
```http
|
||
PUT /mp/complaint/{id}/resolution
|
||
{
|
||
"resolutionNote": "已与定制师沟通完毕"
|
||
}
|
||
|
||
返回: {"code":200,"data":{"editCountLeft":1}}
|
||
|
||
错误:
|
||
- 20305 非本人
|
||
- 20307 已达 2 次上限
|
||
```
|
||
|
||
---
|
||
|
||
## 🧑💼 管理端接口(/admin/complaint/**,只读)
|
||
|
||
### 1. 投诉列表(含权限过滤)
|
||
|
||
```http
|
||
GET /admin/complaint/page?pageNo=1&pageSize=10&keyword=&orderNo=&groupCode=&customizerNickname=&types=ITINERARY&isResolved=false&dateFrom=2026-04-01&dateTo=2026-04-23&dayScope=ALL
|
||
|
||
dayScope: ALL(默认) / WHOLE(整程=dayNumber=0) / SPECIFIC(具体天>=1)
|
||
|
||
返回 Page<ComplaintRespVO>:
|
||
- id / orderId / orderNo / groupCode
|
||
- customizerId / customizerNickname
|
||
- userId / userNickname
|
||
- contactName / contactPhoneMasked(列表脱敏后 4 位)
|
||
- dayNumber / dayLabel
|
||
- types / content / contentSnippet / imageUrls / videoUrl
|
||
- resolutionNote / resolutionEditCount / resolutionUpdatedAt / isResolved
|
||
- notifyWechatStatus / notifySmsStatus(PENDING/SUCCESS/FAIL/SKIP)
|
||
- status / createdAt / updatedAt
|
||
```
|
||
|
||
### 2. 投诉详情
|
||
|
||
```http
|
||
GET /admin/complaint/{id}
|
||
|
||
同 Page 返回结构 + contactPhoneDecrypted(明文,仅 complaint:detail 权限 + 非 :self 或本人订单才返)
|
||
```
|
||
|
||
### 3. 权限模型(R4)
|
||
|
||
| 按钮权限 | 效果 |
|
||
|---------|------|
|
||
| `complaint:list:self` | 定制师:强制 SQL WHERE customizer_id = currentAdminId |
|
||
| `complaint:list:all` | 超管/主管:无过滤 |
|
||
| `complaint:detail` | 查看详情(基础权限) |
|
||
|
||
已配菜单 + 角色绑定:
|
||
- SUPER_ADMIN(1) / ADMIN(2) → :all + :detail
|
||
- CUSTOMIZER(3) → :self + :detail
|
||
|
||
---
|
||
|
||
## 🔔 通知链路(后端自动,前端不关心)
|
||
|
||
投诉创建成功后 `afterCommit` → 异步双链路发给 `order.customizerId`:
|
||
|
||
- **企业微信**(已有 WechatMessageDTO + MQ 优先 / Feign 降级):
|
||
```
|
||
【客户投诉】
|
||
团号:NM20260422001
|
||
订单号:DD20260422001
|
||
联系人:张三 13800138000
|
||
第 2 天 · 行程/车
|
||
内容:第二天酒店入住流程混乱(50字摘要)
|
||
图片 3 视频 0
|
||
```
|
||
- **短信**(阿里云模板 `aliyun.sms.template.complaint-notify`,签名"呼籁旅行"):
|
||
```
|
||
【呼籁旅行】团号${groupCode}订单${orderNo}${day}收到客户投诉(${type}),请登录后台查看。
|
||
```
|
||
⚠️ 当前测试服 `SMS_MOCK_TBD`(不真发),运营去阿里云控制台申请模板后 Nacos 替换即生效。
|
||
|
||
---
|
||
|
||
## 📂 数据字典(已执行)
|
||
|
||
- `complaint_type`:ITINERARY=行程 / VEHICLE=车 / HOTEL=房 / OTHER=其他
|
||
- 菜单 `投诉管理`(1250 系列,侧边栏末尾压底)
|
||
|
||
---
|
||
|
||
## 🎨 前端对接清单
|
||
|
||
### 管理端(hl-ui admin)
|
||
- [ ] 侧边栏自动渲染"投诉管理"(后端 menu tree 已返回)
|
||
- [ ] 列表页:表格 + 筛选(6 条件 + dayScope)
|
||
- [ ] 详情页(只读):图/视频预览 + 完整文字 + 解决备注(客人本人写的)+ 通知状态
|
||
- [ ] 字段精度:id/orderId 全部 Long 序列化为 String(防 JS 精度丢失,沿用项目全局 Jackson)
|
||
- [ ] 按钮权限:根据登录人 roleCodes 走 `complaint:list:self` 或 `:all` 两套菜单,无管理操作按钮
|
||
|
||
### 小程序端(hl-mp)
|
||
- [ ] 订单详情页"整程投诉"入口(dayNumber=null/0)
|
||
- [ ] 每日行程卡片"本日投诉"入口(dayNumber=具体天)
|
||
- [ ] 投诉表单:类型多选 + 文字(1~500)+ OSS 直传图片(≤9)/ 视频(≤1)
|
||
- [ ] 冷却拦截:20301 时展示"10 分钟后再试,剩余 {remainingSeconds}s"
|
||
- [ ] "我的投诉"列表 + 详情 + canCancel 显示撤回按钮 + canEditResolution 显示"填写解决备注"
|
||
|
||
---
|
||
|
||
## ⚠️ 注意
|
||
|
||
- 投诉表 `order_complaint` 已在测试服 `hl_order_service_v2` 建好(29 字段 + UNIQUE 防刷 + 4 索引)
|
||
- 企微通知依赖 `order.customizerId → admin_user.enterprise_wechat_id → wechat_user.mobile/name`,定制师未绑企微则自动 SKIP 短信/企微两路
|
||
- 同一用户 + 同一订单 + 同一 dayNumber 10 分钟只能一次(Redis SETNX + DB UNIQUE 双层兜底)
|
||
|
||
---
|
||
|
||
## 🚨 需要后端重启的服务(已完成,此处仅记录)
|
||
|
||
- hl-user-service(Feign: getAdminNotifyProfile + sendComplaintSms + 字典菜单)
|
||
- hl-order-service-v2(投诉主逻辑 + admin controller + internal api)
|
||
- hl-mp-service(MpComplaintController + Feign client + VO)
|
||
- hl-gateway(admin-complaint-service 路由)
|