From 5f1969aef47c3b2e4f6de1a62ead5fbf73fd25c2 Mon Sep 17 00:00:00 2001 From: API Changelog Bot Date: Tue, 4 Aug 2026 01:04:31 +0800 Subject: [PATCH] =?UTF-8?q?docs(changelog):=20#5444/#5446=20changelog=20?= =?UTF-8?q?=E7=A7=BB=E8=87=B3=20changelogs-v2=EF=BC=88v3=20=E5=B7=A5?= =?UTF-8?q?=E5=8D=95=E7=9B=AE=E5=BD=95=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ..._5446_HOLD-通知管理端状态查询与受控重试.md | 115 ------------------ ...tep2-canonical-full-snapshot-与稳定槽位.md | 82 ------------- 2 files changed, 197 deletions(-) delete mode 100644 changelogs/2026-08/2_5446_HOLD-通知管理端状态查询与受控重试.md delete mode 100644 changelogs/2026-08/3_5444_Step2-canonical-full-snapshot-与稳定槽位.md diff --git a/changelogs/2026-08/2_5446_HOLD-通知管理端状态查询与受控重试.md b/changelogs/2026-08/2_5446_HOLD-通知管理端状态查询与受控重试.md deleted file mode 100644 index 65a182e..0000000 --- a/changelogs/2026-08/2_5446_HOLD-通知管理端状态查询与受控重试.md +++ /dev/null @@ -1,115 +0,0 @@ ---- -schema: "hl-changelog/v2" -ticket: "5446" -title: "HOLD 通知管理端状态查询与受控重试" -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: "后端 PR #5447 已合并 dev-v3(merge 15d79a8d4);测试部署 task ba1b84af 成功;网关验证 5 项通过(参数校验/missing/stale 拒绝);管理端可领取适配。" -updated_at: "2026-08-03" -base: "dev-v3" -generated: "2026-08-03T23:19:24+08:00" ---- - -# HOLD 通知管理端状态查询与受控重试 - -## 关联 - -- Issue: [#5446](https://git.1814.love:8443/wx/HL/issues/5446) -- Backend tracking: [#5366 B10](https://git.1814.love:8443/wx/HL/issues/5366) -- PR: [#5447](https://git.1814.love:8443/wx/HL/pulls/5447)(merge commit 15d79a8d4) - -## 变更接口 - -| 方法 | 路径 | 来源 | -|---|---|---| -| `GET` | `/admin/fleet/assignments/hold-notification/status` | `hl-fleet-service/src/main/java/com/hulalv/fleet/messagetemplate/controller/AssignmentHoldNotificationAdminController.java` | -| `POST` | `/admin/fleet/assignments/hold-notification/retry` | 同上 | - -路径前缀 `/admin/fleet/**` 已由网关登录/角色校验与 `FleetAdminRoleGuardInterceptor`(VEHICLE_MANAGER / SUPER_ADMIN)收口,无需新增网关规则。 - -## 1. 状态查询 `GET /admin/fleet/assignments/hold-notification/status` - -请求参数(query): - -| 参数 | 类型 | 必填 | 说明 | -|---|---|---|---| -| `assignmentGroupId` | `String(Long)` | 是 | 派车组 ID | -| `notificationGeneration` | `String(Long)` | 是 | HOLD 通知代际 | -| `attemptId` | `String` | 否 | 调用方生成的 attempt 标识(幂等/审计定位),≤64 字符 | - -响应 `data`: - -| 字段 | JSON 类型 | 可空 | 说明 | -|---|---|---|---| -| `assignmentGroupId` | `String(Long)` | 否 | 回显 | -| `notificationGeneration` | `String(Long)` | 否 | 回显 | -| `assignmentStatus` | `String` | 是 | 派车状态;HOLDING=仍在待确认,其余=已离开 holding | -| `deliveryStatus` | `String` | 否 | `NOT_FOUND/PENDING/SENT/FAILED/AMBIGUOUS/INVALIDATED`;`SENT` 只表示系统发送成功,不表示通道送达回执 | -| `messageLogId` | `String(Long)` | 是 | 本地通知日志 ID | -| `outboxEventId` | `String(Long)` | 是 | 可靠通知 Outbox 事件 ID | -| `outboxStatus` | `String` | 是 | `PENDING/PROCESSING/QUARANTINED/SUCCESS` | -| `retryCount` | `Integer` | 是 | Outbox 累计处理重试次数 | -| `sentAt` | `String(date-time)` | 是 | 有可信发送成功事实时返回 | -| `dispatchAttemptedAt` | `String(date-time)` | 是 | 最近一次向通知中心发起分发的时间 | -| `lastError` | `String` | 是 | 最近失败/对账留痕(脱敏) | -| `canRetry` | `Boolean` | 否 | 是否允许管理端重试:仅明确失败且未发送成功且未取消时为 `true` | -| `lastReplayAttemptId` | `String` | 是 | 上次管理端重试 attemptId | -| `lastReplayReason` | `String` | 是 | 上次管理端重试原因 | -| `lastReplayedBy` | `String(Long)` | 是 | 上次重试操作人 | -| `lastReplayedAt` | `String(date-time)` | 是 | 上次重试时间 | -| `attemptStatus` | `String` | 是 | 本次请求 attempt 处理状态:`ACCEPTED/SUCCEEDED/REJECTED/AMBIGUOUS`(持久幂等:同一 attemptId 重复提交直接返回已记录结果) | -| `attemptResultNote` | `String` | 是 | 本次 attempt 处理结果摘要(脱敏) | - -错误码:400 参数校验 / `100001` 派车组不存在 / `100003` 通知代际已过期 / 401 未登录。 - -## 2. 受控重试 `POST /admin/fleet/assignments/hold-notification/retry` - -请求体: - -| 字段 | 类型 | 必填 | 说明 | -|---|---|---|---| -| `assignmentGroupId` | `String(Long)` | 是 | 派车组 ID | -| `notificationGeneration` | `String(Long)` | 是 | HOLD 通知代际 | -| `attemptId` | `String` | 是 | 本次重试 attempt 标识(幂等键组成部分 + 审计),≤64 字符 | -| `reason` | `String` | 是 | 核查失败原因后的重试说明,≤200 字 | - -行为约束: - -- 幂等键 = `assignmentGroupId:notificationGeneration:attemptId`,持久化于 `fleet_hold_notification_retry_attempt` 唯一索引,重复提交(含 Redis 防重窗口过期后)幂等返回已记录结果,不重复发送、不重复写 Outbox; -- 仅允许明确失败(`FAILED`,Outbox `QUARANTINED` 或 `PENDING`+错误)的 HOLD 通知重试;`QUARANTINED` 重置失败预算,`PENDING`+错误立即重试; -- `UNKNOWN/AMBIGUOUS`(dispatching)先按通知中心供应商发送日志对账:存在真实外部成功则补记发送事实并返回 `SENT`,仍无法判定则拒绝重试(fail closed); -- stale(代际过期)/missing(派车组或日志不存在)/wrong identity 一律拒绝; -- 重试复用原 Outbox 事件与供应商幂等键,不新建事件、不直接外呼,不改变派车状态; -- 操作人、原因、attemptId 记入 Outbox 审计列(`last_replay_*`)。 - -响应:同状态查询 `data`(重试后最新事实)。 - -错误码:400 参数校验 / `100001` 派车组或通知日志不存在 / `100003` 通知代际已过期或已取消 / `100503` 资源竞争(并发重试)/ 401 未登录。 - -## 契约影响文件 - -- `hl-fleet-service/src/main/java/com/hulalv/fleet/messagetemplate/controller/AssignmentHoldNotificationAdminController.java` -- `hl-fleet-service/src/main/java/com/hulalv/fleet/messagetemplate/service/AssignmentHoldNotificationAdminService.java` -- `hl-fleet-service/src/main/java/com/hulalv/fleet/messagetemplate/vo/AssignmentHoldNotificationStatusReqVO.java` -- `hl-fleet-service/src/main/java/com/hulalv/fleet/messagetemplate/vo/AssignmentHoldNotificationStatusRespVO.java` -- `hl-fleet-service/src/main/java/com/hulalv/fleet/messagetemplate/vo/AssignmentHoldNotificationRetryReqVO.java` -- `hl-fleet-service/src/main/java/db/migration/V20260803_008__add_outbox_hold_replay_attempt_id.java` - -## 前端/调用方动作 - -管理后台新增适配:派车详情/司机通知维度展示 `deliveryStatus` 与 `canRetry`;`canRetry=true` 时提供重试按钮,重试需携带调用方生成的 `attemptId` 与原因;`AMBIGUOUS` 不提供重试入口(等待自动对账)。所有 Long ID 按 String 消费。 - -## 验证证据 - -- 定向测试:`AssignmentHoldNotificationAdminServiceTest` 24 项 / `AssignmentHoldNotificationAdminControllerTest` 5 项 / `HoldNotificationRetryAttemptMapperTest` 2 / `WechatMessageLogMapperTest` 2 / `AssignmentInsuranceOutboxMapperTest` 18 / 迁移可重入测试 2(Fleet reactor verify 3047 项 0 failures,1 项 Docker 环境型 error 与本任务无关) -- Spotless: check 通过 -- 网关验证:经 api.test.1814.love:9443 实测 5 项(status 缺参 400 / 组不存在 100001 / 过期代际 100003 / retry 缺 attemptId 400 / retry 过期代际 100003),证据 sha256 c8759216 -- 兼容性结论:纯新增端点,无既有字段或行为变更 diff --git a/changelogs/2026-08/3_5444_Step2-canonical-full-snapshot-与稳定槽位.md b/changelogs/2026-08/3_5444_Step2-canonical-full-snapshot-与稳定槽位.md deleted file mode 100644 index dd6df71..0000000 --- a/changelogs/2026-08/3_5444_Step2-canonical-full-snapshot-与稳定槽位.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -schema: "hl-changelog/v2" -ticket: "5444" -title: "Step2 canonical full snapshot 与稳定槽位" -consumer: "admin" -author: "wx(GIT)" -change_type: "修改接口" -backend_status: "released" -gateway_status: "verified" -frontend_status: "pending" -frontend_owner: "" -frontend_ref: "" -target_release: "" -verified_at: "" -status_note: "" -updated_at: "2026-08-03" -base: "dev-v3" -generated: "2026-08-03T23:34:18+08:00" ---- - -# Step2 canonical full snapshot 与稳定槽位 - -> #5444(#5366 B08/B09 后端):派车弹窗 Step2 canonical full snapshot 与稳定槽位。 -> 后端实现已合入 PR #5448(merge 8ceb8c5);测试部署 task 80234589;网关验证通过。 - -## 关联 - -- Issue: #5444 -- PR: [#5448](https://git.1814.love:8443/wx/HL/pulls/5448) - -## 变更接口 - -| 方法 | 路径 | 来源 | -|---|---|---| -| `POST` | `/admin/fleet/assignments/candidates` | 响应新增 `canonicalSnapshot` 区块;请求新增可选同代校验参数(additive,向后兼容) | - -## 契约影响文件 - -- `hl-fleet-service/src/main/java/com/hulalv/fleet/assignment/vo/AssignmentCandidateReqVO.java` -- `hl-fleet-service/src/main/java/com/hulalv/fleet/assignment/vo/AssignmentCandidateRespVO.java` -- `hl-fleet-service/src/test/java/com/hulalv/fleet/assignment/controller/AssignmentControllerTest.java` - -## 请求新增(均可选) - -- `expectedPlanGeneration`(Long·String):Step2 幂等重试/同代校验期望计划代际;携带且与当前 canonical 快照不符时以 `605061` 拒绝(generation 漂移 fail-closed)。 -- `expectedSnapshotVersion`(Long·String):同上,期望快照版本。 - -## 响应新增 `canonicalSnapshot` 区块(additive) - -- `planGeneration`(Long·String):当前计划代际(不透明令牌);与 `snapshotVersion` 共同标识同一份 canonical 快照。 -- `snapshotVersion`(Long·String):当前快照版本;同代内容修订递增。 -- `retainedSlotIds`(Long·String[]):有序 RetainedSlotSet(稳定槽位集合);不重编号、不丢 protected/active slot、至少 1 个。 -- `editableServiceDates`(LocalDate[]):完整有序可编辑服务日。 -- `cells`:每个 `RetainedSlotSet × editableServiceDates` 笛卡尔积位置唯一 cell: - - `slotId`(Long·String) - - `serviceDate`(LocalDate) - - `used`:`USED` / `UNUSED` / `null`(无切片行=未编辑) - - `readOnly`(Boolean):只读 cell 不可被批量操作改写 - - `readOnlyReason`(String):只读原因;可编辑为 null(派单已完结 / 派单已取消 / 服务日期已过去 / 对账期已关账) - - `vehicleId` / `driverId`(Long·String,未派或不用车为 null) -- `selectedVehicle`(对象,未选或资源失效为 null):`vehicleId` / `plate` / `modelName` / `seats` -- `selectedDriver`(对象,未选或资源失效为 null):`driverId` / `name` / `maskedPhone`(司机域脱敏)/ `driverStatus` / `season` - -## 行为说明(B08/B09) - -- 首次进入 Step2 且不存在稳定槽位时,后端在 requirement 锁 + 独立新事务内原子生成至少 1 个稳定 `slotId` 并落 `fleet_assignment` unassigned 每日切片;同一 `planGeneration + snapshotVersion` 下重复读取/幂等重试不重复建槽、不重编号。 -- `selectedVehicle`/`selectedDriver` 为与 `snapshotVersion` 同代的已选资源展示快照,独立于候选分页/筛选;null/失效资源 fail-closed 不编造。 -- 未传 `requirementId` 或需求上下文不可用时 `canonicalSnapshot` 为 `null`,候选查询本身不受影响。 - -## 前端/调用方动作 - -- Step2 前端可消费 `canonicalSnapshot` 区块组织稳定槽位 UI;重复读取/幂等重试时回传 `expectedPlanGeneration`/`expectedSnapshotVersion` 做同代校验。 -- 不新增前端建槽 API 与二次批量查询 API;`selectedVehicle`/`selectedDriver` 直接取自现有 `selectedVehicleId`/`selectedDriverId` 入参。 -- 未消费新字段的既有调用保持兼容(additive)。 - -## 验证证据 - -- 定向测试:`Step2CanonicalSnapshotServiceTest`(11 用例:首次原子建槽/同代幂等不重编号/并发/漂移 fail-closed/脱敏/跨页过滤/null 失效/readOnly cell)、`AssignmentCandidateServiceTest`、`AssignmentControllerTest`(canonicalSnapshot 挂载 + 透传),全部通过。 -- Fleet 全量测试:3022/3023 通过;唯一失败 `FleetInsuranceTaskTeamNoMysqlTest` 为 Testcontainers 无 Docker 环境型失败(基线上同样失败)。 -- Spotless `spotless:check` 通过。 -- 网关验证:`https://api.test.1814.love:9443` POST /admin/fleet/assignments/candidates 真实订单 3 槽×3 天 9 cell;同代 expected 重试 snapshotVersion 不变;漂移 605061 fail-closed;无 requirement/未知需求快照 null。证据 `D:/evidence/5444-gateway-step2-snapshot.json`。 -- 兼容性结论:请求/响应均为 additive 扩展,向后兼容。