changelog: #8339 团期确认联动子订单确认 / #8341 团期核单结算同步子订单
changelog-filename-gate / validate (push) Failing after 1s
changelog-filename-gate / validate (push) Failing after 1s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
这个提交包含在:
@@ -0,0 +1,818 @@
|
||||
---
|
||||
schema: "hl-changelog/v2"
|
||||
ticket: "8339"
|
||||
title: "团期人工确认联动子订单确认行程:确认门加逐户预检(589556 逐户列订单号 + 未满足项);团期单在团期确认前单户确认行程返新码 581065;时间线新增 BATCH_SUB_ORDER_CONFIRM_FAILED;合同保险出具门补出行完毕"
|
||||
consumer: "admin"
|
||||
author: "jw(GIT)"
|
||||
change_type: "修改接口"
|
||||
backend_status: "deployed"
|
||||
gateway_status: "verified"
|
||||
frontend_status: "pending"
|
||||
frontend_owner: ""
|
||||
frontend_ref: ""
|
||||
target_release: ""
|
||||
verified_at: "2026-09-24"
|
||||
status_note: "六节点定案(SRS §0.27.7):团期人工确认时,团期单子订单一并确认行程(定制中 → 待出行),团期单不再允许单户自行确认,合同保险只由逐户确认链路出具。POST /v3/admin/order/group-batch/{groupBatchId}/confirm 的确认门在四项 ready + 物资已确认之外加逐户预检(待支付 / 定制中的活跃户须已付订金、出行人齐全且有成人手机号、户级房车完成、合同模板已设、有主报账人),不满足仍返 589556,message 由「、」连接的团期级清单改为「;」分段:首段团期级未满足项(有才出现),其后每户一段「订单 {订单号}:{未满足项}」;确认成功后事务提交后系统异步逐户确认行程,合同保险由逐户链路出具(#8268 的确认后全团补发下线),单户失败不回滚团期确认,写团期时间线新事件 BATCH_SUB_ORDER_CONFIRM_FAILED(extra 带 orderId / orderNo / reason)。POST /v3/admin/order/{id}/confirm-itinerary 与 POST /v3/admin/order/{id}/transition(eventCode=CONFIRM)对团期单在团期确认前返新码 581065「团期单随团期确认,不可单独确认」,团期确认后仍在定制中的户放行作补确认,散客单不受影响。合同保险出具门可出具状态集补 TRIP_FINISHED(出行完毕):GET .../contracts 的 issuable 在出行完毕为 true,contracts/issue、contracts/reissue 在出行完毕不再返 589548。前端需:589556 按「;」分段逐户展示;团期单订单详情在团期确认前隐藏或置灰「确认行程」并处理 581065;时间线显示新事件(eventTypeName 后端已给)。"
|
||||
updated_at: "2026-09-24"
|
||||
base: "dev-v3"
|
||||
---
|
||||
|
||||
# 团期确认: 确认门加逐户预检、子订单随团期确认行程、团期单禁单户确认(管理后台)
|
||||
|
||||
> **服务**: hl-order-service-v3(端口 8086/8186)
|
||||
> **PR**: #8346(主体)、#8349(TEST 验收缺陷修复:主报账人副本补齐、逐户确认失败留痕)
|
||||
> **Issue**: #8339
|
||||
> **日期**: 2026-09-24
|
||||
> **影响范围**: 管理后台团期详情页「确认」按钮的 589556 提示、团期时间线、「合同保险」页签的可出具判断;订单详情页「确认行程」按钮(团期单)
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 关键变化
|
||||
|
||||
1. **团期确认会连带确认子订单行程**。确认成功后,系统在后台逐户把「定制中」的户推到「待出行」,每户的合同与保险随之自动出具;前端不需要再逐户点「确认行程」。
|
||||
2. **589556 的 message 格式变了**:由「、」连接的一串,变成「;」分隔的若干段,每个不满足的户一段「订单 {订单号}:{未满足项}」。
|
||||
3. **团期单在团期确认前不能单独「确认行程」**:订单详情 `confirm-itinerary` 与通用 `transition`(CONFIRM)返新码 **581065**。团期确认之后仍在定制中的户(逐户确认失败 / 确认后加入的户)可以单户补确认。
|
||||
4. **团期时间线新增事件 `BATCH_SUB_ORDER_CONFIRM_FAILED`**(子订单确认行程失败),逐户确认失败时写入,带订单号与原因。
|
||||
5. **出行完毕(`TRIP_FINISHED`)也可出具合同保险**:面板 `issuable=true`,手动开 / 重开不再返 589548。
|
||||
|
||||
---
|
||||
|
||||
## 一、背景
|
||||
|
||||
#8268 把「配置 → 确认」改成人工确认,但子订单仍需运营逐户点「确认行程」,而合同保险由团期确认后的「全团补发」与逐户确认链路两条路出具,存在并发重复出具风险。SRS §0.27.7 定案:
|
||||
|
||||
| 项 | 改前 | 改后 |
|
||||
|---|---|---|
|
||||
| 子订单确认行程 | 运营逐户点 | 团期确认提交后系统逐户确认(每户独立事务,单户失败隔离) |
|
||||
| 团期确认门 | 四项 ready + 物资已确认 | 另加逐户预检,任一户不满足整团不可确认 |
|
||||
| 合同保险出具 | 确认后全团补发 + 逐户确认链路 | 只剩逐户确认链路 |
|
||||
| 团期单单户确认 | 可以 | 团期确认前 581065;确认后可补确认 |
|
||||
|
||||
---
|
||||
|
||||
## 二、变更接口清单
|
||||
|
||||
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||
|---|------|------|------|----------|------|
|
||||
| 1 | 团期确认 | POST | `/v3/admin/order/group-batch/{groupBatchId}/confirm` | 修改(门条件 + 错误 message + 副作用) | 逐户预检;589556 逐户列订单号与未满足项;成功后逐户确认行程 |
|
||||
| 2 | 确认行程 | POST | `/v3/admin/order/{id}/confirm-itinerary` | 修改(新增错误码) | 团期单在团期确认前 581065 |
|
||||
| 3 | 状态变更操作(内部通用) | POST | `/v3/admin/order/{id}/transition` | 修改(新增错误码) | `eventCode=CONFIRM` 与 #2 同一守卫,581065 |
|
||||
| 4 | 团期状态流水 | GET | `/v3/admin/order/group-batch/{groupBatchId}/status-logs` | 修改(枚举新增值) | 新事件 `BATCH_SUB_ORDER_CONFIRM_FAILED` |
|
||||
| 5 | 合同保险面板(GB-ADM-030) | GET | `/v3/admin/order/group-batch/{groupBatchId}/contracts` | 修改(出参取值) | `issuable` 在 `TRIP_FINISHED` 为 true |
|
||||
| 6 | 手动开合同 / 保险(GB-ADM-031) | POST | `/v3/admin/order/group-batch/{groupBatchId}/contracts/issue` | 修改(前置门放宽) | `TRIP_FINISHED` 不再 589548 |
|
||||
| 7 | 作废重开合同 / 保险(GB-ADM-031) | POST | `/v3/admin/order/group-batch/{groupBatchId}/contracts/reissue` | 修改(前置门放宽) | 同上 |
|
||||
|
||||
网关无改动(均在既有 `/v3/admin/order` 前缀下);无新增权限码;无 DB schema 变更。
|
||||
|
||||
---
|
||||
|
||||
## 三、接口详情
|
||||
|
||||
### 1. 团期确认 `POST /v3/admin/order/group-batch/{groupBatchId}/confirm`
|
||||
|
||||
**VO**: `Result<Void>`(无请求体)
|
||||
|
||||
#### 使用场景
|
||||
|
||||
团期详情页「配置」节点的「确认」按钮。本次起点一次确认 = 团期进入「确认」+ 全团定制中的户确认行程 + 逐户出合同保险。门不满足时,运营从 589556 的 message 里直接看到是哪几户、差什么。
|
||||
|
||||
#### 入参
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| groupBatchId | Path | Long | ✅ | 团期主键 | 不存在返 589500(不变) |
|
||||
|
||||
无请求体(不变)。
|
||||
|
||||
#### 出参
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| data | Void | 成功返回 null(不变);团期进入 `MATERIAL_PREPARING`,子订单逐户确认在后台异步进行 |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```http
|
||||
POST /v3/admin/order/group-batch/2097250563497385985/confirm HTTP/1.1
|
||||
Host: api.test.1814.love:9443
|
||||
Authorization: Bearer <admin token>
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": null,
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 空数据 / 降级响应
|
||||
|
||||
接口立即返回 200,不等逐户确认完成。逐户确认在团期确认事务提交后异步执行、每户独立事务:
|
||||
|
||||
- 单户失败(如出具外的副作用异常)不回滚团期确认、不影响其他户,该户留在「定制中」;
|
||||
- 失败户写团期时间线 `BATCH_SUB_ORDER_CONFIRM_FAILED`(见接口 4),可在订单详情单户补确认;
|
||||
- 已是待出行 / 出行中的户跳过,不重复确认、不重复出具。
|
||||
|
||||
```json
|
||||
{ "code": 200, "message": "成功", "data": null, "success": true }
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
|
||||
门不满足(团期级 + 逐户,「;」分段):
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 589556,
|
||||
"message": "团期尚不满足确认条件:物资未确认;订单 HL2609240001:未付订金、未指定主报账人;订单 HL2609240002:未提交用车需求",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
只有逐户不满足时,没有团期级那一段:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 589556,
|
||||
"message": "团期尚不满足确认条件:订单 HL2609240003:待支付(须先付订金或取消)",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
状态不是「配置」(含重复确认):
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 589501,
|
||||
"message": "团期状态不允许当前操作",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
#### 业务边界
|
||||
|
||||
- 判定顺序不变:589500 → 589501 → 确认门 589556 → CAS 推进;权限码 `group-batch:confirm`(不变,589507)。
|
||||
- 团期级与逐户预检**一并求值、一次列全**,不会改一项点一次。
|
||||
- 逐户预检范围:待支付、定制中的活跃户(已取消不在内);待出行 / 出行中 / 已完成的户跳过。
|
||||
- 逐户未满足项(同一户内以「、」连接):
|
||||
- `待支付(须先付订金或取消)`:订单仍在待支付;
|
||||
- `未付订金`:付款项未过——团期口径**付订金即可**,`DEPOSIT_PAID` / `FULLY_PAID` 都算满足;
|
||||
- 出行人:`未添加出行人` / `出行人信息未完善 {已完成}/{总数}(部分待补全)` / `至少需要一名成人出行人填写手机号用于合同签署`;
|
||||
- 户级房:`未提交用房需求` / `用房需求未完成(当前: {状态})`;
|
||||
- 户级车:`未提交用车需求` 或用车清单项给出的原因(整团免车 / 团车完成来源视为满足);
|
||||
- `产品未配置合同方案`;
|
||||
- `未指定主报账人`(与单户确认 581062 同一判据)。
|
||||
- 预检前系统会按团期人员配置补齐各户的团期人员副本(PR #8349),先设主报账人后下单的户不会再被误报「未指定主报账人」。
|
||||
- 团期级未满足项取值不变:`房未配齐` / `车未配齐` / `导游领队未配齐` / `摄影未配齐` / `物资未确认`。
|
||||
- 任一户不满足整团不可确认,团期与子订单状态均不变。
|
||||
|
||||
---
|
||||
|
||||
### 2. 确认行程 `POST /v3/admin/order/{id}/confirm-itinerary`
|
||||
|
||||
**VO**: `ConfirmItineraryReqVO` → `OrderTransitionRespVO`
|
||||
|
||||
#### 使用场景
|
||||
|
||||
订单详情页「确认行程」按钮(定制中 → 待出行)。散客单用法不变;团期单改为随团期确认,只在团期确认后作为补确认入口使用。
|
||||
|
||||
#### 入参
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| id | Path | Long | ✅ | 订单主键 | 不变 |
|
||||
| reporterAssignmentId | Body | Long | 否 | 本单人员 assignmentId | 新的主报账人;不传沿用当前主报账人(不变);请求体整体可省略 |
|
||||
|
||||
#### 出参
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| success | Boolean | 是否成功(不变) |
|
||||
| oldStatus / newStatus | String | 变更前 / 后粗状态,如 `CUSTOMIZING` → `PENDING_DEPARTURE`(不变) |
|
||||
| oldFlowStatus / newFlowStatus | String | 变更前 / 后细状态(不变) |
|
||||
| triggeredEvents | List<String> | 副作用事件,如 `ASYNC_CONTRACT_GENERATE` / `ASYNC_INSURANCE_ISSUE`(不变) |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```json
|
||||
{
|
||||
"reporterAssignmentId": "2072930844657283074"
|
||||
}
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"success": true,
|
||||
"oldStatus": "CUSTOMIZING",
|
||||
"newStatus": "PENDING_DEPARTURE",
|
||||
"oldFlowStatus": "待确认",
|
||||
"newFlowStatus": "待出行",
|
||||
"triggeredEvents": ["ASYNC_CONTRACT_GENERATE", "ASYNC_INSURANCE_ISSUE"]
|
||||
},
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 空数据 / 降级响应
|
||||
|
||||
无列表出参。请求体可整体不传(不变):
|
||||
|
||||
```json
|
||||
{}
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
|
||||
团期单、所属团期尚未确认(招募中 / 配置 / 已流团等):
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 581065,
|
||||
"message": "团期单随团期确认,不可单独确认",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
其余既有码不变,例如清单未通过:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 581036,
|
||||
"message": "确认订单前置校验未通过,请先补全所有必填项",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
#### 业务边界
|
||||
|
||||
- 581065 只在「定制中 + CONFIRM」这一跳判,且在五项清单(581036)、主报账人(581062)之前;散客单不判。
|
||||
- 判「团期单」走统一归团解析(`group_batch_id` 或按 `product_batch_id` 反查),与团期详情口径一致。
|
||||
- 团期已确认(`MATERIAL_PREPARING` 及之后,与合同保险出具门同一状态集)时放行:用于逐户确认失败后的重试、确认后转入 / 加入的户。
|
||||
- 团期管理员角色(`GROUP_BATCH_MANAGER`)调本接口仍被既有写守卫 581008「无权查看此订单」拒绝(不变),补确认需由有订单写权限的角色操作。
|
||||
- CONFIRM 落库改为带原状态条件的 CAS:团期驱动的逐户确认与人工补确认同时打到同一户时只有一个成功,输家报错回滚,不会重复出具合同保险。
|
||||
|
||||
---
|
||||
|
||||
### 3. 状态变更操作(内部通用) `POST /v3/admin/order/{id}/transition`
|
||||
|
||||
**VO**: `OrderTransitionReqVO` → `OrderTransitionRespVO`
|
||||
|
||||
#### 使用场景
|
||||
|
||||
通用状态机入口(Swagger 隐藏)。本次只影响 `eventCode=CONFIRM`:与「确认行程」落到同一道 Service 层守卫,团期单在团期确认前 581065。其它事件不变。
|
||||
|
||||
#### 入参
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| id | Path | Long | ✅ | 订单主键 | 不变 |
|
||||
| eventCode | Body | String | ✅ | `PAY_DEPOSIT` / `PAY_FULL` / `CANCEL` / `CONFIRM` / `DEPART` / `FINISH` / `TERMINATE` | 本次只有 `CONFIRM` 行为变化 |
|
||||
| reason | Body | String | 否 | - | 操作原因(不变) |
|
||||
| payload | Body | Map | 否 | - | CONFIRM 可传 `reporterAssignmentId`(不变) |
|
||||
|
||||
#### 出参
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| success | Boolean | 不变 |
|
||||
| oldStatus / newStatus / oldFlowStatus / newFlowStatus | String | 不变 |
|
||||
| triggeredEvents | List<String> | 不变 |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```json
|
||||
{
|
||||
"eventCode": "CONFIRM",
|
||||
"reason": "定制师确认所有前置条件已完成"
|
||||
}
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"success": true,
|
||||
"oldStatus": "CUSTOMIZING",
|
||||
"newStatus": "PENDING_DEPARTURE",
|
||||
"oldFlowStatus": "待确认",
|
||||
"newFlowStatus": "待出行",
|
||||
"triggeredEvents": ["ASYNC_CONTRACT_GENERATE", "ASYNC_INSURANCE_ISSUE"]
|
||||
},
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 空数据 / 降级响应
|
||||
|
||||
无列表出参;非 CONFIRM 事件行为不变。
|
||||
|
||||
```json
|
||||
{ "code": 200, "message": "成功", "data": { "success": true }, "success": true }
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 581065,
|
||||
"message": "团期单随团期确认,不可单独确认",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
#### 业务边界
|
||||
|
||||
- 与接口 2 完全同一判据与放行条件;散客单不受影响。
|
||||
- 非 CONFIRM 事件的落库仍是原写法;CONFIRM 落库改为 CAS(同接口 2)。
|
||||
|
||||
---
|
||||
|
||||
### 4. 团期状态流水 `GET /v3/admin/order/group-batch/{groupBatchId}/status-logs`
|
||||
|
||||
**VO**: `List<GroupBatchStatusLogItemVO>`
|
||||
|
||||
#### 使用场景
|
||||
|
||||
团期详情「操作记录 / 时间线」。本次新增一个事件值:团期确认后逐户确认行程失败时每户一条。
|
||||
|
||||
#### 入参
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| groupBatchId | Path | Long | ✅ | 团期主键 | 不变 |
|
||||
|
||||
#### 出参
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| [].eventType | String | **新增取值** `BATCH_SUB_ORDER_CONFIRM_FAILED` |
|
||||
| [].eventTypeName | String | 新值对应「子订单确认行程失败」 |
|
||||
| [].changeType | String | 新事件为 `DATA`(团期状态不变,`fromStatus == toStatus`) |
|
||||
| [].content | String | 「订单 {订单号} 随团期确认行程失败:{原因}(该户留在定制中,可在订单详情单户补确认)」,原因最长 300 字截断 |
|
||||
| [].operatorType | String | 新事件为 `SYSTEM`,`operatorId` 为 null |
|
||||
| [].extra | Object | 新事件带 `orderId` / `orderNo` / `reason` |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```http
|
||||
GET /v3/admin/order/group-batch/2097250563497385985/status-logs HTTP/1.1
|
||||
Host: api.test.1814.love:9443
|
||||
Authorization: Bearer <admin token>
|
||||
```
|
||||
|
||||
无请求体。
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": [
|
||||
{
|
||||
"changeType": "DATA",
|
||||
"eventType": "BATCH_SUB_ORDER_CONFIRM_FAILED",
|
||||
"eventTypeName": "子订单确认行程失败",
|
||||
"fromStatus": "MATERIAL_PREPARING",
|
||||
"toStatus": "MATERIAL_PREPARING",
|
||||
"content": "订单 HL2609240001 随团期确认行程失败:[581062] 请先指定本单主报账人(尾款代收人)(该户留在定制中,可在订单详情单户补确认)",
|
||||
"operatorType": "SYSTEM",
|
||||
"operatorId": null,
|
||||
"extra": {
|
||||
"orderId": "2103023889187799042",
|
||||
"orderNo": "HL2609240001",
|
||||
"reason": "[581062] 请先指定本单主报账人(尾款代收人)"
|
||||
}
|
||||
}
|
||||
],
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 空数据 / 降级响应
|
||||
|
||||
逐户全部成功时不写这类事件;无流水返回空数组(不变):
|
||||
|
||||
```json
|
||||
{ "code": 200, "message": "成功", "data": [], "success": true }
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 589500,
|
||||
"message": "团期不存在",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
#### 业务边界
|
||||
|
||||
- `eventTypeName` 由后端给,前端直接显示即可(不变的约定)。
|
||||
- `reason` 是原始失败信息:业务异常为「[错误码] 文案」,非业务异常为「异常类名: 消息」,可能含技术细节。
|
||||
- 同一原因同时写入该户 `order_main.last_confirm_error`(最长 500 字),单户补确认成功后清空;该列当前**没有接口出参**。
|
||||
- 同一户多次失败会各写一条,不去重。
|
||||
|
||||
---
|
||||
|
||||
### 5. 合同保险面板 `GET /v3/admin/order/group-batch/{groupBatchId}/contracts`
|
||||
|
||||
**VO**: `GroupBatchContractBoardVO`
|
||||
|
||||
#### 使用场景
|
||||
|
||||
「合同保险」页签首屏;`issuable` 决定出具按钮是否可点。本次只补 `TRIP_FINISHED`(出行完毕)为可出具。
|
||||
|
||||
#### 入参
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| groupBatchId | Path | Long | ✅ | 团期主键 | 不变 |
|
||||
|
||||
#### 出参
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| issuable | Boolean | **取值变化**:`TRIP_FINISHED` 由 false 改为 true;其余状态不变 |
|
||||
| batchStatus | String | 团期状态存储值(不变) |
|
||||
| totalCount / contractIssuedCount / contractSignedCount / insuranceIssuedCount | Integer | 不变 |
|
||||
| items | List | 逐户明细(不变) |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```http
|
||||
GET /v3/admin/order/group-batch/2097250563497385985/contracts HTTP/1.1
|
||||
Host: api.test.1814.love:9443
|
||||
Authorization: Bearer <admin token>
|
||||
```
|
||||
|
||||
无请求体。
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"groupBatchId": "2097250563497385985",
|
||||
"issuable": true,
|
||||
"batchStatus": "TRIP_FINISHED",
|
||||
"totalCount": 4,
|
||||
"contractIssuedCount": 4,
|
||||
"contractSignedCount": 3,
|
||||
"insuranceIssuedCount": 4,
|
||||
"items": []
|
||||
},
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 空数据 / 降级响应
|
||||
|
||||
无活跃子订单时 `totalCount=0`、`items` 为空数组(不变):
|
||||
|
||||
```json
|
||||
{ "code": 200, "message": "成功", "data": { "issuable": true, "batchStatus": "TRIP_FINISHED", "totalCount": 0, "items": [] }, "success": true }
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 589500,
|
||||
"message": "团期不存在",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
#### 业务边界
|
||||
|
||||
- 可出具状态集:`MATERIAL_PREPARING` / `PENDING_DEPARTURE` / `TRAVELLING` / **`TRIP_FINISHED`(新增)** / `REVIEWING` / `SETTLED`。
|
||||
- 同一状态集也用于接口 2 / 3 的「团期已确认、放行补确认」判断,保证确认得了的户也出具得了。
|
||||
|
||||
---
|
||||
|
||||
### 6. 手动开合同 / 保险 `POST /v3/admin/order/group-batch/{groupBatchId}/contracts/issue`
|
||||
|
||||
**VO**: `GroupBatchContractIssueReqVO` → `GroupBatchIssueResultVO`
|
||||
|
||||
#### 使用场景
|
||||
|
||||
「合同保险」页签逐户开合同 / 保险。本次只放宽前置门:出行完毕的团(返团当天补签 / 补开)不再被 589548 挡住。入参出参结构不变。
|
||||
|
||||
#### 入参
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| groupBatchId | Path | Long | ✅ | 团期主键 | 不变 |
|
||||
| orderIds | Body | List<Long> | 否 | - | 为空 = 本期全部尚未出具的户(不变) |
|
||||
| target | Body | String | ✅ | `CONTRACT` / `INSURANCE` / `BOTH` | 不变 |
|
||||
|
||||
#### 出参
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| totalCount / successCount / failCount / skipCount | Integer | 不变 |
|
||||
| results[].orderId / target / outcome / message | String | 不变 |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```json
|
||||
{ "orderIds": ["2103023889187799042"], "target": "CONTRACT" }
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"totalCount": 1,
|
||||
"successCount": 1,
|
||||
"failCount": 0,
|
||||
"skipCount": 0,
|
||||
"results": [
|
||||
{ "orderId": "2103023889187799042", "target": "CONTRACT", "outcome": "SUCCESS", "message": null }
|
||||
]
|
||||
},
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 空数据 / 降级响应
|
||||
|
||||
已出具的户逐项 `SKIPPED`(不变):
|
||||
|
||||
```json
|
||||
{ "code": 200, "message": "成功", "data": { "totalCount": 1, "successCount": 0, "failCount": 0, "skipCount": 1, "results": [] }, "success": true }
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
|
||||
团期尚未确认(`RECRUITING` / `RESOURCE_PREPARING` 等,不变):
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 589548,
|
||||
"message": "团期确认后才能出具合同与保险",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
#### 业务边界
|
||||
|
||||
- `TRIP_FINISHED` 改前 589548,改后放行;其余状态口径不变。
|
||||
- 自动出具只剩逐户确认链路;本接口仍是人工补出的通路(逐户确认失败户补确认后会自动出,一般无需手动)。
|
||||
|
||||
---
|
||||
|
||||
### 7. 作废重开合同 / 保险 `POST /v3/admin/order/group-batch/{groupBatchId}/contracts/reissue`
|
||||
|
||||
**VO**: `GroupBatchContractIssueReqVO` → `GroupBatchIssueResultVO`
|
||||
|
||||
#### 使用场景
|
||||
|
||||
「开错」态的补救通路。前置门与手动开一致,本次同样放宽到出行完毕。
|
||||
|
||||
#### 入参
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| groupBatchId | Path | Long | ✅ | 团期主键 | 不变 |
|
||||
| orderIds | Body | List<Long> | 否 | - | 不变 |
|
||||
| target | Body | String | ✅ | `CONTRACT` / `INSURANCE` / `BOTH` | 不变 |
|
||||
| reason | Body | String | 否 | - | 作废原因(不变) |
|
||||
|
||||
#### 出参
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| totalCount / successCount / failCount / skipCount | Integer | 不变 |
|
||||
| results[] | List | 逐户结果(不变) |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```json
|
||||
{ "orderIds": ["2103023889187799042"], "target": "CONTRACT", "reason": "方案选错" }
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"totalCount": 1,
|
||||
"successCount": 1,
|
||||
"failCount": 0,
|
||||
"skipCount": 0,
|
||||
"results": [
|
||||
{ "orderId": "2103023889187799042", "target": "CONTRACT", "outcome": "SUCCESS", "message": null }
|
||||
]
|
||||
},
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 空数据 / 降级响应
|
||||
|
||||
作废失败的户记 `FAILED`,不影响其他户(不变):
|
||||
|
||||
```json
|
||||
{ "code": 200, "message": "成功", "data": { "totalCount": 1, "successCount": 0, "failCount": 1, "skipCount": 0, "results": [] }, "success": true }
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 589548,
|
||||
"message": "团期确认后才能出具合同与保险",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
#### 业务边界
|
||||
|
||||
- 与手动开共用同一出具门;`TRIP_FINISHED` 放行。
|
||||
|
||||
---
|
||||
|
||||
## 四、契约约束与正确调用方式
|
||||
|
||||
### ✅ 正确 / ❌ 错误调用对照
|
||||
|
||||
| 场景 | 调用 |
|
||||
|------|------|
|
||||
| ✅ 团期单确认行程 | 补齐各户资料 → 团期 `confirm` → 系统逐户确认,合同保险自动出 |
|
||||
| ❌ 团期确认前逐户点确认行程 | `confirm-itinerary` → 581065 |
|
||||
| ✅ 团期确认后某户失败 | 看时间线 `BATCH_SUB_ORDER_CONFIRM_FAILED` 的原因 → 补资料 → 订单详情 `confirm-itinerary` 补确认 |
|
||||
| ❌ 确认后等「全团补发」合同 | 已下线;合同保险只随逐户确认自动出具 |
|
||||
|
||||
### 589556 的解析
|
||||
|
||||
message 冒号之后按「;」切段:不以「订单 」开头的是团期级(「、」分隔的五种之一),以「订单 {订单号}:」开头的是逐户段,冒号后按「、」切出未满足项。可直接逐段展示,无需解析。
|
||||
|
||||
### 581065 的处理
|
||||
|
||||
提示「请到团期详情点确认」,不要引导运营去补本单资料(那是 581036 的出路)。
|
||||
|
||||
---
|
||||
|
||||
## 五、数据库行为
|
||||
|
||||
| 前端动作 | 外部可观察的写入 |
|
||||
|----------|------------------|
|
||||
| `confirm` 被拒(589556 等) | 零写入(预检前的人员副本补齐随事务一并回滚) |
|
||||
| `confirm` 成功(团期事务) | 团期状态 CAS 推进 + 时间线 `BATCH_CONFIRM`;各户团期人员副本(`order_staff_assignment`)按团期配置补齐 |
|
||||
| `confirm` 成功(提交后逐户,每户独立事务) | 子订单 `CUSTOMIZING → PENDING_DEPARTURE`、写 `confirmed_at`、订单日志 CONFIRM「随团期确认行程」、应付台账推送;随后逐户生成合同 / 保单 |
|
||||
| 逐户确认失败 | 该户不变;团期时间线 `BATCH_SUB_ORDER_CONFIRM_FAILED` + `order_main.last_confirm_error` |
|
||||
| 单户补确认成功 | 同上逐户写入,并清空 `last_confirm_error` |
|
||||
| `confirm-itinerary` / `transition` 被 581065 拒 | 零写入 |
|
||||
|
||||
无 schema 变更(`last_confirm_error` 为既有列)。
|
||||
|
||||
---
|
||||
|
||||
## 六、边界行为
|
||||
|
||||
- 未登录 → 401(网关拦截);团期不存在 → 589500。
|
||||
- 待支付户、未付订金户会挡住整团确认:须先付订金或取消该户。
|
||||
- 已是待出行 / 出行中的户在团期确认时跳过,不重复确认、不重复出具。
|
||||
- 逐户确认使用系统身份,订单日志 / 合同创建人为 SYSTEM;确认人记在团期时间线 `BATCH_CONFIRM`。
|
||||
- 定案前已单独确认但未出合同的存量户,需在「合同保险」页签手动补出。
|
||||
- 建单时团期人员副本连同报账人等级一起抄(PR #8349),后挂团的户订单层主报账人不再停在 NONE。
|
||||
|
||||
---
|
||||
|
||||
## 六.5、枚举 / 数据字典
|
||||
|
||||
### 时间线事件(GroupBatchLogEventType,新增一值)
|
||||
|
||||
**所属字段**: 团期状态流水 `eventType` / `eventTypeName` | **类型**: `String`
|
||||
|
||||
| 值 | 中文 | 说明 |
|
||||
|----|------|------|
|
||||
| `BATCH_SUB_ORDER_CONFIRM_FAILED` | 子订单确认行程失败 | 本次新增;`changeType=DATA`,`operatorType=SYSTEM`,extra 带 orderId / orderNo / reason |
|
||||
|
||||
### 错误码
|
||||
|
||||
| 码 | 文案 | 说明 |
|
||||
|----|------|------|
|
||||
| 581065 | 团期单随团期确认,不可单独确认 | 本次新增 |
|
||||
| 589556 | 团期尚不满足确认条件:{0} | 码值不变,{0} 改为「;」分段、含逐户段 |
|
||||
|
||||
## 六.6、修改前后对比
|
||||
|
||||
### 字段级对比
|
||||
|
||||
| 字段 | 改前 | 改后 |
|
||||
|------|------|------|
|
||||
| 589556 message `{0}` | 团期级未满足项,「、」连接 | 「;」分段:团期级一段(有才出现)+ 每户一段「订单 {订单号}:{未满足项}」 |
|
||||
| `confirm-itinerary` / `transition(CONFIRM)` 错误码 | 无 581065 | 团期单在团期确认前 581065 |
|
||||
| 时间线 `eventType` | 无该值 | 新增 `BATCH_SUB_ORDER_CONFIRM_FAILED` |
|
||||
| 合同保险面板 `issuable`(`TRIP_FINISHED`) | false | true |
|
||||
| `contracts/issue` / `reissue`(`TRIP_FINISHED`) | 589548 | 放行 |
|
||||
|
||||
### 行为级对比
|
||||
|
||||
| 行为 | 改前 | 改后 |
|
||||
|------|------|------|
|
||||
| 团期确认后子订单 | 仍在定制中,需逐户点确认行程 | 系统逐户确认 → 待出行 |
|
||||
| 合同保险出具 | 确认后全团补发 + 逐户确认链路 | 只剩逐户确认链路 |
|
||||
| 团期确认门 | 四项 ready + 物资 | 另加逐户预检(付订金即可) |
|
||||
| 子订单 CONFIRM 落库 | 按主键更新 | 带原状态条件 CAS |
|
||||
|
||||
## 六.7、影响评估
|
||||
|
||||
- **是否破坏向后兼容**: 是。按「、」切 589556 message 的前端会把逐户段切碎;团期单的「确认行程」在团期确认前会收到 581065。
|
||||
- **前端是否必须同步上线**: 建议同步。后端不依赖前端即可工作(团期确认即完成子订单确认);前端未改时只是提示不友好、按钮可点但报 581065。
|
||||
- **前端 workaround 清理点**: 团期单订单详情「确认行程」按钮在团期确认前隐藏或置灰;589556 改按「;」分段展示;时间线对新事件无需映射(后端给中文名)。
|
||||
|
||||
---
|
||||
|
||||
## 七、不影响范围
|
||||
|
||||
- **仅影响**: 团期确认门与确认后的子订单联动、团期单 CONFIRM 守卫、团期时间线新事件、合同保险出具门的状态集。
|
||||
- **零影响**:
|
||||
- 散客单的确认行程与 transition 各事件
|
||||
- 团期确认的权限码、路径、入参、成功出参
|
||||
- `GET /v3/admin/order/{id}/confirm-checklist` 的入参出参(逐户预检复用其判据,接口本身不变)
|
||||
- 进入待出发的七项硬门
|
||||
- 小程序端
|
||||
|
||||
---
|
||||
|
||||
## 八、测试环境已验证
|
||||
|
||||
部署:hl-order-service-v3 = dev-v3 @ f55840cb5(PR #8346,第一轮 2026-09-24 15:30–16:16),复验 dev-v3 @ 9ffb3c2de(PR #8349,2026-09-24)的 TEST 环境实测(证据见工单 #8339 验收评论);工单 #8339 已验收关单。
|
||||
|
||||
| # | 场景 | 结果 |
|
||||
|---|---|---|
|
||||
| 1 | 全户满足,调团期 `confirm` | 200;子订单 定制中 → 待出行,写 `confirmed_at`,订单日志 CONFIRM「随团期确认行程」;合同 1 份 SIGNED、保险 2 条 INSURED / 户,无重复(两轮均验) |
|
||||
| 2 | 某户仅付订金(DEPOSIT_PAID) | 视为满足付款项 |
|
||||
| 3 | 待支付 / 户级房未完成 / 无主报账人(第一轮);产品未配置合同方案 / 成人出行人无手机号 / 未提交用车需求(复验,SQL 构造已恢复) | 589556 整团拒,逐户列订单号 + 未满足项,团期与子订单状态不变 |
|
||||
| 4 | 已待出行户 | 跳过,不重复确认、不重复出具 |
|
||||
| 5 | CHECK 约束注入使一户逐户确认失败 | 团期确认不回滚、其他户正常;时间线 `BATCH_SUB_ORDER_CONFIRM_FAILED`(含订单号 + 原因),`last_confirm_error` 有值;删约束后单户补确认成功,`last_confirm_error` 清空 |
|
||||
| 6 | 团期单 `confirm-itinerary` / `transition` CONFIRM(团期未确认) | 581065;散客单不受影响 |
|
||||
| 7 | `TRIP_FINISHED`(附带 `REVIEWING`)调 `GET /contracts` | `issuable=true` |
|
||||
| 8 | 先设 PRIMARY 后下单的户 / 存量 NONE 副本(PR #8349) | 建单即抄为 PRIMARY;确认预检前补齐,不再误报「未指定主报账人」 |
|
||||
|
||||
本地:788 例 + 合入后 606 例(含 6 个 Docker IT,唯一红为 dev-v3 既有基线 `ConfirmChecklistGuardIT` 581062);PR #8349 相关 360 例(含 Docker IT)0 失败。
|
||||
|
||||
---
|
||||
|
||||
## 九、相关历史 PR
|
||||
|
||||
| PR | Issue | 说明 | 是否仍有效 |
|
||||
|----|-------|------|------------|
|
||||
| #8302 | #8268 | 团期人工确认端点;确认后全团补发合同保险 | ⚠️ 确认端点有效;「确认后全团补发」被本单下线 |
|
||||
| **本 PR #8346** | **#8339** | 确认门逐户预检、子订单随团期确认、581065 | ✅ 最新 |
|
||||
| **本 PR #8349** | **#8339** | 人员副本补齐、逐户确认失败留痕 | ✅ 最新 |
|
||||
|
||||
---
|
||||
|
||||
## 十、相关文档
|
||||
|
||||
- 关联 Issue: [wx/HL#8339](https://git.1814.love/wx/HL/issues/8339)
|
||||
- 关联 PR: [wx/HL#8346](https://git.1814.love/wx/HL/pulls/8346)、[wx/HL#8349](https://git.1814.love/wx/HL/pulls/8349)
|
||||
- 定案文档:SRS §0.27.7(PR #8338)
|
||||
- 同批条目:团期核单 / 结算 / 反结算同步子订单(#8341);团期人工确认端点(#8268)
|
||||
|
||||
## 关联 / 联系人
|
||||
|
||||
### 链接
|
||||
|
||||
- **Issue**: [#8339](https://git.1814.love/wx/HL/issues/8339)
|
||||
- **PR**: [#8346](https://git.1814.love/wx/HL/pulls/8346)、[#8349](https://git.1814.love/wx/HL/pulls/8349)
|
||||
- **Merge commit**: [f55840cb5](https://git.1814.love/wx/HL/commit/f55840cb5dca1d2f3a81078548c7a50cc813ca7e)、[9ffb3c2de](https://git.1814.love/wx/HL/commit/9ffb3c2de31ccf2ba7b16c2922f6f493f2025269)
|
||||
|
||||
### 联系人
|
||||
|
||||
- **后端负责人**: @jw
|
||||
@@ -0,0 +1,504 @@
|
||||
---
|
||||
schema: "hl-changelog/v2"
|
||||
ticket: "8341"
|
||||
title: "团期核单 / 结算 / 反结算同步子订单:进入核单时待核单户进核单中;团期结算即整团财务复核(逐户结算 + 报账单),有逐户核单未提交的户返 589568 并列出订单 ID;反结算时已结算户退回待结算"
|
||||
consumer: "admin"
|
||||
author: "jw(GIT)"
|
||||
change_type: "修改接口"
|
||||
backend_status: "deployed"
|
||||
gateway_status: "verified"
|
||||
frontend_status: "pending"
|
||||
frontend_owner: ""
|
||||
frontend_ref: ""
|
||||
target_release: ""
|
||||
verified_at: "2026-09-24"
|
||||
status_note: "六节点定案(SRS §0.27.7):团期单子订单的核单结算状态由团期驱动,与团期状态变更同一事务、全有全无。① 团期进入核单(POST .../review/start 或首次 POST .../settlement/cost 把团期从 TRIP_FINISHED 推到 REVIEWING):在团户中 flow=PENDING_REVIEW 的户同步为 review IN_PROGRESS / flow REVIEWING,已在核单中或已提交的户不动,幂等命中(alreadyStarted=true / 第二笔成本)不同步。② 团期结算 POST .../settle = 整团财务复核:待结算户逐户做与「财务复核确认结算」相同的写入(settlement COMPLETED / flow SETTLED / settled_at、SETTLEMENT_CONFIRM 日志、推送 BZ 报账单),已结算户跳过;有逐户核单未提交(或定稿后被逐户反确认)的户,整团拒绝 589568「整团核单当前状态不允许该操作:结算需要所有在团订单逐户核单已提交,未提交订单:{订单 ID,「、」分隔}」,零写入。③ 团期反结算 POST .../settle/reopen:已结算户退回 flow PENDING_SETTLE / settlement PENDING、清 settled_at,review 与逐户核单快照不动,已生成的报账单不撤(重新结算按 settlementId 幂等命中)。路径、入参、成功出参均不变;已取消户不受影响。前端需:处理团期结算的 589568 新原因(提示先去对应订单提交逐户核单);团期结算后不必再引导运营逐户点「结算确认」。"
|
||||
updated_at: "2026-09-24"
|
||||
base: "dev-v3"
|
||||
---
|
||||
|
||||
# 团期结算: 核单 / 结算 / 反结算同步子订单(管理后台)
|
||||
|
||||
> **服务**: hl-order-service-v3(端口 8086/8186)
|
||||
> **PR**: #8345
|
||||
> **Issue**: #8341
|
||||
> **日期**: 2026-09-24
|
||||
> **影响范围**: 管理后台团期详情「核单 / 结算」节点的发起核单、录共享成本、结算、反结算按钮;子订单列表与订单详情里的核单 / 结算状态展示
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 关键变化
|
||||
|
||||
1. **团期结算 = 整团财务复核**。点团期「结算」后,在团待结算的户一并变为「已结算」并各生成一张报账单,运营**不再逐户点结算确认**。
|
||||
2. **团期结算新增一种 589568 拒绝原因**:有户的逐户核单还没提交时,整团拒绝,message 列出这些户的**订单 ID**。
|
||||
3. **团期反结算会把子订单一并退回待结算**;已生成的报账单保留。
|
||||
4. **团期进入核单时,待核单的户自动进入核单中**,不必等逐户第一次录核单明细。
|
||||
|
||||
以上四个接口的路径、入参、成功出参都不变。
|
||||
|
||||
---
|
||||
|
||||
## 一、背景
|
||||
|
||||
改前团期的核单、结算、反结算只改团期自身,子订单仍要逐户推进:团期「已结算」而子订单还「待结算」、财务侧没有报账单的情况会出现。SRS §0.27.7 定案子订单跟随团期:
|
||||
|
||||
| 团期动作 | 子订单同步(同一事务) |
|
||||
|---|---|
|
||||
| 进入核单(`TRIP_FINISHED → REVIEWING`) | `flow PENDING_REVIEW` 的户 → review `IN_PROGRESS` / flow `REVIEWING` |
|
||||
| 逐户核单明细录入与提交 | 照旧(提交后 flow `PENDING_SETTLE`、review `COMPLETED`、settlement `PENDING`) |
|
||||
| 结算(`REVIEWING → SETTLED`) | 待结算户逐户财务复核 → settlement `COMPLETED` / flow `SETTLED` / `settled_at` + 报账单 |
|
||||
| 反结算(`SETTLED → REVIEWING`) | 已结算户 → flow `PENDING_SETTLE` / settlement `PENDING`,清 `settled_at` |
|
||||
|
||||
---
|
||||
|
||||
## 二、变更接口清单
|
||||
|
||||
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||
|---|------|------|------|----------|------|
|
||||
| 1 | 结算归档 | POST | `/v3/admin/order/group-batch/{groupBatchId}/settle` | 修改(新增拒绝原因 + 副作用) | 逐户结算 + 报账单;有逐户核单未提交的户 589568 |
|
||||
| 2 | 结算反确认 | POST | `/v3/admin/order/group-batch/{groupBatchId}/settle/reopen` | 修改(副作用) | 已结算户退回待结算 |
|
||||
| 3 | 发起核单 | POST | `/v3/admin/order/group-batch/{groupBatchId}/review/start` | 修改(副作用) | 首次推进时待核单户进核单中 |
|
||||
| 4 | 录入团期共享成本 | POST | `/v3/admin/order/group-batch/{groupBatchId}/settlement/cost` | 修改(副作用 + 事务) | 首笔成本推进团期时同上同步;整方法改为单事务 |
|
||||
|
||||
网关无改动;无新增权限码;无新增错误码;无 DB schema 变更。
|
||||
|
||||
---
|
||||
|
||||
## 三、接口详情
|
||||
|
||||
### 1. 结算归档 `POST /v3/admin/order/group-batch/{groupBatchId}/settle`
|
||||
|
||||
**VO**: `GroupBatchSettleReqVO` → `Result<Void>`
|
||||
|
||||
#### 使用场景
|
||||
|
||||
团期详情「核单」节点的「结算」按钮(`REVIEWING → SETTLED`)。本次起同时完成全团子订单的财务复核,逐户生成报账单。
|
||||
|
||||
#### 入参
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| groupBatchId | Path | Long | ✅ | 团期主键 | 不变 |
|
||||
| checkNote | Body | String | 否 | - | 结算意见(请求体整体可省略,不变);本次起也写进逐户财务复核日志的备注 |
|
||||
|
||||
#### 出参
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| data | Void | 成功返回 null(不变) |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```json
|
||||
{
|
||||
"checkNote": "成本已逐项核对"
|
||||
}
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": null,
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 空数据 / 降级响应
|
||||
|
||||
无列表出参。团期没有在团子订单时跳过子订单同步,团期照常结算:
|
||||
|
||||
```json
|
||||
{ "code": 200, "message": "成功", "data": null, "success": true }
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
|
||||
有在团户逐户核单未提交(本次新增的原因,码值与前缀不变):
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 589568,
|
||||
"message": "整团核单当前状态不允许该操作:结算需要所有在团订单逐户核单已提交,未提交订单:2103023889187799042",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
已结算归档(不变):
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 589555,
|
||||
"message": "该团期已结算归档,不可重复结算",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
#### 业务边界
|
||||
|
||||
- 判定顺序:判权(589507)→ 团期状态(589555 / 589501)→ 整团核单前置(589567 / 589568,不变)→ 团期 CAS → **子订单同步** → 时间线。
|
||||
- 在团户分三类:待结算(flow `PENDING_SETTLE` + settlement `PENDING` + review `COMPLETED`)逐户复核;已结算(flow `SETTLED` + settlement `COMPLETED`)跳过、不重复推报账单;其余一律视为「逐户核单未提交」→ 589568。
|
||||
- 589568 在任何逐户写入之前判,拒绝时零写入;列出的是**订单 ID**(不是订单号),以「、」分隔。
|
||||
- 逐户复核的写入与副作用与 `POST /v3/admin/order/{orderId}/settlement/confirm`(财务复核)同源;任一户复核失败,其业务码原样返回,整团回滚(含已推的报账单)。
|
||||
- 团期结算不对每户再做订单数据范围判定:财务角色结算整团不会被逐户 581008 挡住;判权仍是团期结算原有的角色判据。
|
||||
- 已取消户不在「在团户」内,全程不受影响。
|
||||
|
||||
---
|
||||
|
||||
### 2. 结算反确认 `POST /v3/admin/order/group-batch/{groupBatchId}/settle/reopen`
|
||||
|
||||
**VO**: `Result<Void>`(无请求体)
|
||||
|
||||
#### 使用场景
|
||||
|
||||
团期详情「结算」节点的「反结算」按钮(`SETTLED → REVIEWING`)。本次起已结算的子订单一并退回待结算。
|
||||
|
||||
#### 入参
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| groupBatchId | Path | Long | ✅ | 团期主键 | 不变 |
|
||||
|
||||
无请求体(不变)。
|
||||
|
||||
#### 出参
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| data | Void | 成功返回 null(不变) |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```http
|
||||
POST /v3/admin/order/group-batch/2097250563497385985/settle/reopen HTTP/1.1
|
||||
Host: api.test.1814.love:9443
|
||||
Authorization: Bearer <admin token>
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": null,
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 空数据 / 降级响应
|
||||
|
||||
无列表出参;没有已结算子订单时只退团期,不报错:
|
||||
|
||||
```json
|
||||
{ "code": 200, "message": "成功", "data": null, "success": true }
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
|
||||
团期当前不是已结算(不变):
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 589501,
|
||||
"message": "团期状态不允许当前操作",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
#### 业务边界
|
||||
|
||||
- 只退「财务复核」这一步:flow `SETTLED → PENDING_SETTLE`、settlement `COMPLETED → PENDING`、清 `settled_at`;review 保持 `COMPLETED`,逐户核单终态快照与核单汇总不动。
|
||||
- 不在已结算的户(未结算 / 已被逐户反确认)不命中,不写。
|
||||
- **已生成的报账单不撤**;重新结算时按核单汇总 ID 幂等命中既有报账单,不重复生成。反结算期间出纳仍可能处理这些报账单。
|
||||
- 本接口未新增错误码。
|
||||
|
||||
---
|
||||
|
||||
### 3. 发起核单 `POST /v3/admin/order/group-batch/{groupBatchId}/review/start`
|
||||
|
||||
**VO**: `GroupBatchReviewStartRespVO`
|
||||
|
||||
#### 使用场景
|
||||
|
||||
团期详情「出行完毕」节点的「发起核单」按钮(`TRIP_FINISHED → REVIEWING`)。本次起首次推进时同事务把待核单的户带进核单中。
|
||||
|
||||
#### 入参
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| groupBatchId | Path | Long | ✅ | 团期主键 | 不变 |
|
||||
|
||||
无请求体(不变)。
|
||||
|
||||
#### 出参
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| groupBatchId | String | 团期主订单 ID(不变) |
|
||||
| batchStatus | String | 恒为 `REVIEWING`(不变) |
|
||||
| batchStatusDesc | String | 恒为「核单中」(不变) |
|
||||
| alreadyStarted | Boolean | true = 幂等命中,零写入(不变;本次起幂等命中也不同步子订单) |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```http
|
||||
POST /v3/admin/order/group-batch/2097250563497385985/review/start HTTP/1.1
|
||||
Host: api.test.1814.love:9443
|
||||
Authorization: Bearer <admin token>
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": {
|
||||
"groupBatchId": "2097250563497385985",
|
||||
"batchStatus": "REVIEWING",
|
||||
"batchStatusDesc": "核单中",
|
||||
"alreadyStarted": false
|
||||
},
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 空数据 / 降级响应
|
||||
|
||||
重复点击返回 `alreadyStarted=true`,团期与子订单都不写(不变):
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": { "groupBatchId": "2097250563497385985", "batchStatus": "REVIEWING", "batchStatusDesc": "核单中", "alreadyStarted": true },
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
|
||||
团期不在出行完毕(不变):
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 589564,
|
||||
"message": "当前团期状态不可发起核单(须为「出行完毕」)",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
#### 业务边界
|
||||
|
||||
- 只推 flow `PENDING_REVIEW` 的户(review `PENDING` / `IN_PROGRESS`、settlement 为空或 `NONE`);已在核单中、已提交、已结算的户不动。
|
||||
- flow 仍早于待核单的户(如未完成出行同步的存量户)跳过并记日志,不阻断团期进入核单;这些户日后第一次录核单明细时仍会自己进入核单中。
|
||||
- 权限码 `group-batch:finance:advance` 与各错误码(589507 / 589564 / 589565 / 589566)不变。
|
||||
|
||||
---
|
||||
|
||||
### 4. 录入团期共享成本 `POST /v3/admin/order/group-batch/{groupBatchId}/settlement/cost`
|
||||
|
||||
**VO**: `RecordBatchCostReqVO` → `Result<Long>`
|
||||
|
||||
#### 使用场景
|
||||
|
||||
团期核单页录大巴 / 领队 / 摄影等共享成本。团期在出行完毕时录第一笔会顺带把团期推进核单中;本次起这一推进同时同步子订单(与接口 3 同一逻辑)。
|
||||
|
||||
#### 入参
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| groupBatchId | Path | Long | ✅ | 团期主键 | 不变 |
|
||||
| costType | Body | String | ✅ | `BUS` / `LEADER` / `PHOTOGRAPHER` / `OTHER` | 不变 |
|
||||
| amount | Body | BigDecimal | ✅ | ≥ 0 | 不变 |
|
||||
| source | Body | String | 否 | `MANUAL` / `FLEET_CALLBACK` | 默认 `MANUAL`(不变) |
|
||||
| sourceRefNo | Body | String | 否 | `FLEET_CALLBACK` 时必填 | 幂等键(不变) |
|
||||
| remark | Body | String | 否 | ≤ 255 字符 | 不变 |
|
||||
|
||||
#### 出参
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| data | Long | 新成本明细 ID(不变) |
|
||||
|
||||
#### 请求示例
|
||||
|
||||
```json
|
||||
{
|
||||
"costType": "BUS",
|
||||
"amount": 1200.00,
|
||||
"source": "MANUAL",
|
||||
"remark": "大巴费用"
|
||||
}
|
||||
```
|
||||
|
||||
#### 响应示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "成功",
|
||||
"data": 90412,
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 空数据 / 降级响应
|
||||
|
||||
第二笔及以后的成本(团期已在核单中)只插明细,不再同步子订单(不变的推进口径):
|
||||
|
||||
```json
|
||||
{ "code": 200, "message": "成功", "data": 90413, "success": true }
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
|
||||
团期不在可录成本的状态(不变):
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 589501,
|
||||
"message": "团期状态不允许当前操作",
|
||||
"success": false,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
#### 业务边界
|
||||
|
||||
- 本接口由「无事务」改为整方法单事务:团期推进、子订单同步、明细插入全有全无(改前 CAS 与 INSERT 各自提交,插入失败时团期可能已被推进)。
|
||||
- 权限码 `group-batch:finance:advance` 与错误码不变。
|
||||
|
||||
---
|
||||
|
||||
## 四、契约约束与正确调用方式
|
||||
|
||||
### ✅ 正确 / ❌ 错误调用对照
|
||||
|
||||
| 场景 | 调用 |
|
||||
|------|------|
|
||||
| ✅ 团期结算 | 各户提交逐户核单 → 整团核单提交核算(ALLOCATED)→ `settle`,子订单一并结算 |
|
||||
| ❌ 有户逐户核单未提交就结算 | `settle` → 589568「…未提交订单:{订单 ID}」 |
|
||||
| ❌ 团期结算后再逐户点「结算确认」 | 不再需要;已结算户逐户复核会被既有状态门拒绝 |
|
||||
| ✅ 结算后发现问题 | `settle/reopen` → 改整团核单 → 重新 `settle`(报账单幂等,不重复) |
|
||||
|
||||
### 589568 的区分
|
||||
|
||||
589568 码值同时用于「整团核单状态不对」与本次新增的「逐户核单未提交」。前端可直接展示 message;如要分支,message 含「未提交订单:」即为本次新增原因,出路是去对应订单提交逐户核单。
|
||||
|
||||
---
|
||||
|
||||
## 五、数据库行为
|
||||
|
||||
| 前端动作 | 外部可观察的写入(同一事务) |
|
||||
|----------|------------------|
|
||||
| `review/start` 首次成功 / 首笔 `settlement/cost` | 团期 `REVIEWING` + 待核单户 review `IN_PROGRESS` / flow `REVIEWING`(CAS,按行) |
|
||||
| `review/start` 幂等命中 | 零写入 |
|
||||
| `settle` 成功 | 核单 `CHECKED` + 团期 `SETTLED` + 待结算户 settlement `COMPLETED` / flow `SETTLED` / `settled_at`、订单日志 `SETTLEMENT_CONFIRM`(备注「团期结算(整团财务复核)[:结算意见]」)、每户一张 BZ 报账单 |
|
||||
| `settle` 被 589568 拒 | 零写入 |
|
||||
| `settle/reopen` 成功 | 团期 `REVIEWING` + 核单退回 + 已结算户 flow `PENDING_SETTLE` / settlement `PENDING` / 清 `settled_at`;报账单保留 |
|
||||
|
||||
无 schema 变更。
|
||||
|
||||
---
|
||||
|
||||
## 六、边界行为
|
||||
|
||||
- 未登录 → 401(网关拦截);团期不存在 → 589500。
|
||||
- 已取消户全程不受影响;重复触发不重复写(各写入均带状态 CAS)。
|
||||
- 二次结算(反结算后再结算)报账单不重复、`settlement_id` 不变。
|
||||
- 首次结算后逐户反确认会被 584330(报账单已进入出纳)拒绝(既有行为,不变)。
|
||||
- 逐户财务复核入口对团期单仍开放;已被逐户复核过的户在团期结算时跳过。
|
||||
|
||||
---
|
||||
|
||||
## 六.6、修改前后对比
|
||||
|
||||
### 字段级对比
|
||||
|
||||
| 字段 | 改前 | 改后 |
|
||||
|------|------|------|
|
||||
| `settle` 589568 message | 仅整团核单状态类原因 | 新增「结算需要所有在团订单逐户核单已提交,未提交订单:{订单 ID}」 |
|
||||
| 子订单 `settlement_status` / `flow_status` / `settled_at`(团期结算后) | 不变(待结算) | `COMPLETED` / `SETTLED` / 结算时间 |
|
||||
| 子订单 review / flow(团期进入核单后) | `PENDING` / `PENDING_REVIEW` | `IN_PROGRESS` / `REVIEWING` |
|
||||
|
||||
### 行为级对比
|
||||
|
||||
| 行为 | 改前 | 改后 |
|
||||
|------|------|------|
|
||||
| 团期结算 | 只改团期与整团核单 | 另做整团财务复核,逐户结算 + 报账单 |
|
||||
| 团期反结算 | 只改团期与整团核单 | 另把已结算户退回待结算 |
|
||||
| 团期进入核单 | 不碰子订单 | 待核单户进核单中 |
|
||||
| 录共享成本事务 | 无事务 | 单事务 |
|
||||
|
||||
## 六.7、影响评估
|
||||
|
||||
- **是否破坏向后兼容**: 部分。路径、入参、成功出参不变;`settle` 多一种 589568 原因,逐户核单未提交的团原先能结算、现在被拒。
|
||||
- **前端是否必须同步上线**: 否。后端已自行完成子订单同步;前端未改时 589568 按原样弹 message 即可理解。
|
||||
- **前端 workaround 清理点**: 团期结算后引导运营逐户「结算确认」的提示可移除;子订单列表刷新即可看到已结算。
|
||||
|
||||
---
|
||||
|
||||
## 七、不影响范围
|
||||
|
||||
- **仅影响**: 团期发起核单、首笔共享成本、结算、反结算四个动作对子订单的联动。
|
||||
- **零影响**:
|
||||
- 逐户核单明细录入与提交(`/v3/admin/order/{orderId}/settlement/**`)
|
||||
- 逐户财务复核 `POST /v3/admin/order/{orderId}/settlement/confirm` 的入参出参与判权
|
||||
- 整团核单(核算、分摊)各端点
|
||||
- 散客单的核单结算
|
||||
- 小程序端
|
||||
|
||||
---
|
||||
|
||||
## 八、测试环境已验证
|
||||
|
||||
部署:hl-order-service-v3 = dev-v3 @ f55840cb5(含 PR #8345),TEST 环境实测 2026-09-24 16:05–16:11(证据见工单 #8341 验收评论);工单 #8341 已验收关单。
|
||||
|
||||
| # | 场景 | 结果 |
|
||||
|---|---|---|
|
||||
| 1 | 团期进入核单 | 活跃户 review `IN_PROGRESS` / flow `REVIEWING`;重复调用状态不变 |
|
||||
| 2 | 逐户核单定稿 + 整团核单 ALLOCATED 后团期结算 | 四户 settlement `COMPLETED` / flow `SETTLED` / `settled_at`;报账单每户 1 张(BZ-202609240001~0004) |
|
||||
| 3 | 团期反结算 | 四户回 flow `PENDING_SETTLE` / settlement `PENDING`,`settled_at` 清空;报账单保留 |
|
||||
| 4 | 已取消户 / 二次结算 | 已取消户全程未动;二次结算报账单不重复、`settlement_id` 不变 |
|
||||
| 5 | 逐户核单未提交时整团结算(SQL 构造) | 589568「…未提交订单:2103023889187799042」,整团回滚 |
|
||||
|
||||
本地:相关单测 / H2 IT / ArchUnit 314 例 0 失败;Docker IT `GroupBatchAuditVersionCasMySqlIT` 10 例、`FlowStatusP1cIntegrationTest` 4 例 0 失败。
|
||||
|
||||
---
|
||||
|
||||
## 九、相关历史 PR
|
||||
|
||||
| PR | Issue | 说明 | 是否仍有效 |
|
||||
|----|-------|------|------------|
|
||||
| — | #7456 | 团期结算归档 / 反确认端点 | ✅ 端点不变,本单加子订单联动 |
|
||||
| — | #7527 | 发起核单端点 | ✅ 端点不变,本单加子订单联动 |
|
||||
| **本 PR #8345** | **#8341** | 核单 / 结算 / 反结算同步子订单 | ✅ 最新 |
|
||||
|
||||
---
|
||||
|
||||
## 十、相关文档
|
||||
|
||||
- 关联 Issue: [wx/HL#8341](https://git.1814.love/wx/HL/issues/8341)
|
||||
- 关联 PR: [wx/HL#8345](https://git.1814.love/wx/HL/pulls/8345)
|
||||
- 定案文档:SRS §0.27.7(PR #8338)
|
||||
- 同批条目:团期确认联动子订单确认行程(#8339)
|
||||
|
||||
## 关联 / 联系人
|
||||
|
||||
### 链接
|
||||
|
||||
- **Issue**: [#8341](https://git.1814.love/wx/HL/issues/8341)
|
||||
- **PR**: [#8345](https://git.1814.love/wx/HL/pulls/8345)
|
||||
- **Merge commit**: [454e2da4b](https://git.1814.love/wx/HL/commit/454e2da4ba347d45c25834f12d290d7689b85a3a)
|
||||
|
||||
### 联系人
|
||||
|
||||
- **后端负责人**: @jw
|
||||
在新工单中引用
屏蔽一个用户