changelog-filename-gate / validate (push) Failing after 1s
- 8598 派单保险隔离事件新增人工终结出口 DISCARDED(新增接口) - 8613+8614 605072 恢复动作文案订正 + 605002 座位强禁码下线口径澄清(修复) - 8593 待配车团期清单 transferPendingCount 由硬编码 0 改真值,取不到给 null(修改接口) 三份均经 validate-changelog-frontmatter.mjs 与 changelog_workflow.py lint 双门禁 PASS, 并做过双向串味自检(他域关键词命中 0、本域关键词命中非 0)。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
229 行
11 KiB
Markdown
229 行
11 KiB
Markdown
---
|
||
schema: "hl-changelog/v2"
|
||
ticket: "8598"
|
||
title: "派单保险隔离事件新增人工终结(DISCARDED)出口"
|
||
consumer: "admin"
|
||
author: "wx(GIT)"
|
||
change_type: "新增接口"
|
||
backend_status: "deployed"
|
||
gateway_status: "not_required"
|
||
frontend_status: "pending"
|
||
frontend_owner: ""
|
||
frontend_ref: ""
|
||
target_release: ""
|
||
verified_at: ""
|
||
status_note: "新增 POST /admin/fleet/insurance/assignment-events/{eventId}/discard,仅 SUPER_ADMIN 可调用,把处于 QUARANTINED 的派单保险隔离事件人工终结为新终态 DISCARDED,终结不可逆、无回退入口,也无批量接口,逐条操作且终结原因必填(非空、≤200字)。幂等窗口10秒(重复提交返100502),并发冲突返100503(CAS未命中)。DISCARDED 会让关联的行程短信状态查询/重发端点(GET及POST .../itinerary-sms[/retry])对该事件返回 status=FAILED、canRetry=false——这是已有取值组合,不引入新字段或新枚举值,前端已有的 FAILED 分支即可覆盖,不需要新增代码路径。gateway_status=not_required,复用既有 /admin/fleet/** 路由,未新增网关配置。backend_status=deployed:PR #8607(合并提交5afadf6c634)已合并,hl-fleet-service 已部署测试服并与本条目所依据的源码提交一致(deploy-status.sh 实测 COMMIT=d57498d38,STATE=ok,2026-09-30 05:34:07 部署);未登录态 curl 实测该路径已挂载并优先鉴权(返 200 信封 code=401,非 HTTP 401 状态码),路由与鉴权链路均已验证。"
|
||
updated_at: "2026-09-30"
|
||
base: "dev-v3"
|
||
---
|
||
|
||
# 车务保险: 隔离事件新增人工终结(DISCARDED)出口
|
||
|
||
> **存放目录**: 二期(order-v3)→ `changelogs-v2/2026-09/`
|
||
>
|
||
> **服务**: hl-fleet-service(端口 8087)
|
||
> **PR**: [#8607](https://git.1814.love:8443/wx/HL/pulls/8607)
|
||
> **Issue**: [#8598](https://git.1814.love:8443/wx/HL/issues/8598)
|
||
> **日期**: 2026-09-30
|
||
> **影响范围**: 超管对派单保险隔离 Outbox 事件的处置面板,新增一个终结动作;对既有重放端点与行程短信状态端点零结构变化
|
||
|
||
---
|
||
|
||
## ⚠️ 关键变化
|
||
|
||
隔离事件(`QUARANTINED`)此前**唯一的出口是重放**——重放会再隔离的事件(关联需求已删、内容审核不过、上游数据已按别的单清理),会让卡死告警永久为红,没有任何办法让它退出告警。本次新增一个**人工终结**动作,把这类确定性失败的事件显式标成新终态 `DISCARDED`,终结之后它退出卡死告警、不再被扫描器捞起、也不再阻塞同 `orderingKey` 的后继事件。**终结无回退入口,是单向不可逆操作**。
|
||
|
||
---
|
||
|
||
## 二、变更接口清单
|
||
|
||
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||
|---|------|------|------|----------|------|
|
||
| 1 | 终结已隔离的派单生命周期事件 | POST | `/admin/fleet/insurance/assignment-events/{eventId}/discard` | 新增接口 | 仅 SUPER_ADMIN,QUARANTINED → DISCARDED |
|
||
|
||
---
|
||
|
||
## 三、接口详情
|
||
|
||
### 1. 终结已隔离的派单生命周期事件 `POST /admin/fleet/insurance/assignment-events/{eventId}/discard`
|
||
|
||
**VO**: `AssignmentInsuranceOutboxDiscardReqVO → AssignmentInsuranceOutboxDiscardRespVO`
|
||
|
||
#### 使用场景
|
||
|
||
超管在派单保险 Outbox 卡死告警/隔离事件处置面板里,对一条已确认「重放多少次都会再隔离」的事件(例如关联需求已随团期撤销删除、内容审核不通过、上游数据已被另一张单清理)执行终结,承认这个业务动作确实不会再发生、也不再补,并把原因、操作人、时间留痕。与重放动作共用同一批隔离事件列表数据源,本次不新增查询端点。
|
||
|
||
#### 入参
|
||
|
||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||
|------|------|------|------|------|------|
|
||
| eventId | Path | Long | ✅ | - | Outbox 事件 ID |
|
||
| reason | Body | String | ✅ | 非空;≤200 字 | 确认不再重放的原因,须说明业务影响已如何处置 |
|
||
|
||
#### 出参 `Result<AssignmentInsuranceOutboxDiscardRespVO>`
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| eventId | String(Long 转字符串) | Outbox 事件 ID |
|
||
| previousStatus | String | 终结前状态,恒为 `QUARANTINED` |
|
||
| status | String | 终结后状态,恒为 `DISCARDED` |
|
||
| operatorId | String(Long 转字符串) | 操作人管理员 ID |
|
||
| discardedAt | String | 终结操作时间,格式 `yyyy-MM-dd HH:mm:ss` |
|
||
|
||
#### 请求示例
|
||
|
||
```json
|
||
{
|
||
"reason": "关联需求已随团期撤销删除,短信不再需要补发"
|
||
}
|
||
```
|
||
|
||
#### 响应示例
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "成功",
|
||
"data": {
|
||
"eventId": "1934567890123456789",
|
||
"previousStatus": "QUARANTINED",
|
||
"status": "DISCARDED",
|
||
"operatorId": "88",
|
||
"discardedAt": "2026-09-30 10:20:30"
|
||
},
|
||
"success": true
|
||
}
|
||
```
|
||
|
||
#### 空数据 / 降级响应
|
||
|
||
本接口是单事件的状态迁移动作,没有「空数据」或部分成功的中间态——调用结果只有「成功迁移」或下方错误响应里的某一种拒绝,不存在降级返回。
|
||
|
||
#### 错误响应
|
||
|
||
```json
|
||
{
|
||
"code": 403001,
|
||
"message": "无权限,仅超级管理员可终结隔离事件",
|
||
"success": false,
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
其余可能返回的错误码:
|
||
|
||
| code | 触发条件 | message |
|
||
|---|---|---|
|
||
| 400 | `reason` 为空白或超过 200 字(Bean Validation,先于业务逻辑拦截) | `终结原因不能为空` 或 `终结原因最多200字` |
|
||
| 100001 | `eventId` 对应事件不存在 | `参数非法: 隔离事件不存在` |
|
||
| 100001 | 事件当前状态不是 `QUARANTINED`(已是 SUCCESS/DISCARDED/PENDING/PROCESSING) | `参数非法: 仅允许终结 QUARANTINED 事件,当前状态为{实际状态}` |
|
||
| 100502 | 同一 `eventId` 10 秒幂等窗口内重复提交 | `隔离事件终结中,请勿重复提交` |
|
||
| 100503 | 并发命中 CAS 未命中(他人同时终结/重放,或处理器抢先处理) | `资源被占用,请稍后重试` |
|
||
| 401 | 未登录(网关统一信封,HTTP 状态码仍是 200,信封内 `code=401`) | `缺少有效的 Authorization 头` |
|
||
|
||
#### 业务边界
|
||
|
||
- 只接受当前状态为 `QUARANTINED` 的事件;其余状态一律 100001 拒绝。
|
||
- 终结是单向操作,没有「撤销终结」的接口。
|
||
- 无批量终结接口,只能逐条调用——设计上刻意如此:批量会把混在隔离事件里的真实业务缺口一次性静默抹掉。
|
||
- 幂等键为 `eventId`(10 秒窗口),并发保护为乐观锁 CAS;两者返回的错误码不同(100502 vs 100503),前端应分别处理:100502 提示稍候,100503 建议重新拉取该事件当前状态后再决定下一步。
|
||
- 终结成功后,该事件对应的行程短信状态查询/重发端点(`GET /admin/fleet/assignments/{assignmentId}/itinerary-sms`、`POST .../itinerary-sms/retry`)会返回 `status=FAILED, canRetry=false`——这是这两个端点已公开枚举值集合里已有的取值组合,不是新增字段或新增枚举值,前端已有的 `FAILED` 分支不需要改动即可正确渲染。
|
||
|
||
---
|
||
|
||
## 四、契约约束与正确调用方式
|
||
|
||
> 本节只写后端接受/拒绝 payload 的规则,不写 UI 渲染建议。
|
||
|
||
### ✅ 正确 / ❌ 错误 payload 对照
|
||
|
||
| 场景 | payload | 结果 |
|
||
|------|---------|------|
|
||
| ✅ 原因非空且 ≤200 字 | `{ "reason": "关联需求已随团期撤销删除,短信不再需要补发" }` | 200,事件迁移为 DISCARDED |
|
||
| ❌ 原因为空 | `{ "reason": "" }` 或 `{ "reason": " " }` | 400 `终结原因不能为空` |
|
||
| ❌ 原因超长 | `{ "reason": "<201 个字符>" }` | 400 `终结原因最多200字` |
|
||
| ❌ 对非 QUARANTINED 事件调用 | 任意合法 reason,但目标事件当前是 SUCCESS/DISCARDED/PENDING/PROCESSING | 100001,message 里点名当前状态 |
|
||
|
||
### 调用前置
|
||
|
||
调用前前端应确认目标事件当前处于「已隔离」状态(面板上通常是从隔离事件列表点进来),不要对已经终结过、已成功、或还在处理中的事件发起终结请求——这些情形不会被静默忽略,而是显式返回 100001。
|
||
|
||
---
|
||
|
||
## 五、数据库行为
|
||
|
||
- 终结成功后,该事件状态字段变为 `DISCARDED`;原因、操作人、操作时间会被记录(复用重放动作已有的三个字段承载,未新增列)。
|
||
- 终结成功后再对该事件调用既有的重放端点(`POST /admin/fleet/insurance/assignment-events/{eventId}/replay`),会返回 100001「仅允许重放 QUARANTINED 事件,当前状态为DISCARDED」。
|
||
- 终结不会产生任何下游消息重放或补发——它就是承认这件事不会再发生。
|
||
|
||
---
|
||
|
||
## 六、边界行为
|
||
|
||
- 未登录 → 网关统一信封 `code=401`(HTTP 状态码 200,非 HTTP 401)
|
||
- 非超管 → 403001
|
||
- `eventId` 不存在 → 100001
|
||
- 事件状态非 `QUARANTINED` → 100001,message 带当前实际状态
|
||
- `reason` 为空/超长 → 400(Bean Validation 先于业务逻辑拦截)
|
||
- 10 秒幂等窗口内重复提交同一 `eventId` → 100502
|
||
- 并发命中 CAS 未命中 → 100503
|
||
- 下游服务降级 → 不适用,本接口无下游读取,只做本域状态迁移
|
||
|
||
---
|
||
|
||
## 六.5、枚举 / 数据字典
|
||
|
||
### status / previousStatus(`AssignmentInsuranceOutboxStatusEnum`)
|
||
|
||
**所属字段**: `status` / `previousStatus`(`AssignmentInsuranceOutboxDiscardRespVO`) | **类型**: `String`
|
||
|
||
| 值 | 中文 | 说明 |
|
||
|----|------|------|
|
||
| `PENDING` | 待处理 | 尚未开始处理(本接口不接受此状态) |
|
||
| `PROCESSING` | 处理中 | 正在处理(本接口不接受此状态) |
|
||
| `QUARANTINED` | 已隔离 | 卡死告警状态,唯一可被本接口终结的状态;`previousStatus` 恒为此值 |
|
||
| `SUCCESS` | 成功 | 机器判定的成功终态(本接口不接受此状态) |
|
||
| `DISCARDED` | 已终结(本次新增) | 人工判定的放弃终态,只能由本接口产出,单向不可逆;`status` 恒为此值 |
|
||
|
||
---
|
||
|
||
## 七、不影响范围
|
||
|
||
- **仅影响**: 派单保险隔离 Outbox 事件处置面板,新增一个终结动作入口
|
||
- **零影响**:
|
||
- 既有重放端点 `POST /admin/fleet/insurance/assignment-events/{eventId}/replay` 的路径、参数、错误码
|
||
- 既有隔离事件列表/卡死告警统计查询
|
||
- 行程短信状态查询/重发端点的响应字段结构与已公开枚举值集合(新增的只是一条已有取值组合被触发的路径)
|
||
|
||
---
|
||
|
||
## 八、测试环境已验证
|
||
|
||
- `deploy-status.sh`(测试服现状表)实测:`hl-fleet-service` COMMIT=`d57498d38`、STATE=ok,2026-09-30 05:34:07 部署——与本条目所依据的源码提交(含 #8598 合并提交 `5afadf6c634`)一致。
|
||
- 测试服内网 `curl` 实测路由已挂载且鉴权前置生效:
|
||
|
||
```
|
||
POST http://127.0.0.1:8080/admin/fleet/insurance/assignment-events/1/discard (无 Authorization 头)
|
||
→ HTTP 200
|
||
{"code":401,"message":"缺少有效的 Authorization 头","data":null,"traceId":"f3e71e825f844525","success":false}
|
||
```
|
||
|
||
---
|
||
|
||
## 十、相关文档
|
||
|
||
- 关联 Issue: [wx/HL#8598](https://git.1814.love:8443/wx/HL/issues/8598)
|
||
- 关联 PR: [wx/HL#8607](https://git.1814.love:8443/wx/HL/pulls/8607)
|
||
|
||
## 关联 / 联系人
|
||
|
||
### 链接
|
||
|
||
- **Issue**: [#8598](https://git.1814.love:8443/wx/HL/issues/8598)
|
||
- **PR**: [#8607](https://git.1814.love:8443/wx/HL/pulls/8607)
|
||
- **Merge commit**: [`5afadf6c634`](https://git.1814.love:8443/wx/HL/commit/5afadf6c634997a31b0f912f80ab0b67c34a9c2c)
|
||
|
||
### 联系人
|
||
|
||
- **后端负责人**: @wx
|