24 KiB
schema, ticket, title, consumer, author, change_type, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
| schema | ticket | title | consumer | author | change_type | backend_status | gateway_status | frontend_status | frontend_owner | frontend_ref | target_release | verified_at | status_note | updated_at | base |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| hl-changelog/v2 | 8684 | 预支相关 8 个接口出参新增 canRevoke:当前操作人点「撤回」能否成功,与撤回接口共用同一个判定 | admin | jw(GIT) | 修改接口 | deployed | verified | implemented | mmg | 41468f5297fc1d5c04b49772b76af73d2fd0c3bf | v2.1 | 2026-10-02 | 已合并 dev-v3(806058c66)并部署 TEST,自签低权限 token 经网关实测:三个列表逐角色取值、联表 created_by 探针、开关关闭→还原往返(md5 逐字节还原,渲染期间拒绝日志零新增)、按 canRevoke 抽样真实撤回。前端待改两处撤回按钮:订单详情预支弹窗 AdvanceModal.vue 与团期财务页签 FinanceTab.vue 的 v-if 改为 a.canRevoke;审批中心本来没有撤回按钮。前端已交付(2026-10-02):两处撤回按钮 v-if 均改读 a.canRevoke,不再自拼 status+角色;存量行无键 undefined 不误显;spec FinanceTab 3 例+AdvanceModal 新建 2 例全绿,提交 41468f52。 | 2026-10-02 | dev-v3 |
order-v3: 预支列表出参新增 canRevoke
服务: hl-order-service-v3
PR: #8723(已合入 dev-v3,合并提交 806058c66)
Issue: #8684
⚠️ 关键变化
🟢 8 个接口的出参新增一个字段 canRevoke(Boolean):当前操作人现在点「撤回」能不能成功。其余入参、出参、判权、错误码全部不变。
🔴 同一行,不同人看到的值不同。 不要缓存后跨账号复用,也不要拿它当这笔预支本身的属性。
🟢 前端只需把撤回按钮的显示条件改成 a.canRevoke,不用自己判断角色、申请人和开关。
一、背景
#8517 之后,撤回待审批预支(DELETE /v3/admin/order/advance/:advanceId)只放行申请人本人和超管、管理员,其余返回 585009。但列表只返回申请人姓名,没有申请人账号 ID,也没有「能不能撤」的标记,前端只能按「待审批」显示撤回按钮:非申请人也能看到,点了才提示 585009。
本单在出参里补 canRevoke,由后端按撤回接口的同一套规则算好。撤回接口本身的判权不变。
二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 本单预支列表 | GET | /v3/admin/order/:orderId/advances |
修改 | records[] 新增 canRevoke |
| 2 | 预支审批列表 | GET | /v3/admin/order/advance-approvals/page |
修改 | records[] 新增 canRevoke |
| 3 | 团期预支记录(财务页签) | GET | /v3/admin/order/group-batch/:groupBatchId/advances |
修改 | 每行新增 canRevoke |
| 4 | 发起订单级预支 | POST | /v3/admin/order/:orderId/advance |
修改 | 返回体新增 canRevoke |
| 5 | 预支审批通过 | PUT | /v3/admin/order/advance/:advanceId/approve |
修改 | 返回体新增 canRevoke(恒 false) |
| 6 | 预支审批驳回 | PUT | /v3/admin/order/advance/:advanceId/reject |
修改 | 返回体新增 canRevoke(恒 false) |
| 7 | 发起团期级预支 | POST | /v3/admin/order/group-batch/:groupBatchId/advance |
修改 | 返回体新增 canRevoke |
| 8 | 核单汇总快照 | GET | /v3/admin/order/:orderId/settlement/summary |
修改 | advanceSummary.records[] 新增 canRevoke(只含已通过,恒 false) |
三、接口详情
canRevoke 取值规则(8 个接口相同,按顺序判,命中即返回):
| # | 条件 | 取值 |
|---|---|---|
| 1 | 这笔预支不是待审批(status ≠ SUBMITTED) |
false |
| 2 | 没有请求上下文(定时任务、消息消费等) | false |
| 3 | 当前角色是团期管理员 GROUP_BATCH_MANAGER |
false(撤回接口先返回 581008,不受开关影响) |
| 4 | 当前角色是 SUPER_ADMIN / ADMIN |
true |
| 5 | 当前账号就是申请人(创建人账号 ID 相等) | true |
| 6 | 其余:开关 advance.acl.enforce.role-guard 为 true(默认) |
false |
| 6' | 其余:开关已关闭 | true(撤回接口回到 #8517 之前的行为) |
创建人账号 ID 为空的存量行,只有第 4 条能得到 true。
1. 本单预支列表 GET /v3/admin/order/:orderId/advances
VO: PageParam → Result<PageResult<OrderAdvanceRespVO>>
使用场景
订单详情的「预支」弹窗。前端据 canRevoke 决定每一行显不显示「撤回」按钮。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| orderId | Path | Long | ✅ | 订单 ID | 不变 |
| page | Query | Integer | ❌ | ≥1 | 不变 |
| pageSize | Query | Integer | ❌ | ≥1 | 不变 |
出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| records[].canRevoke | Boolean | 🆕 当前操作人点撤回能否成功,规则见上表 |
| 其余字段 | — | 不变(id / status / createdByName 等) |
请求示例
GET /v3/admin/order/2100743225424621570/advances?page=1&pageSize=20 HTTP/1.1
Authorization: Bearer <本单定制师 token>
响应示例
本单定制师看:自己申请的待审批为 true,管理员申请的待审批为 false,已通过的为 false。
{
"code": 200,
"message": "成功",
"data": {
"records": [
{
"id": "2105886435083206657",
"orderId": "2100743225424621570",
"teamNo": "26-8707",
"payeeName": "刘大山",
"payeeRole": "LEADER",
"payeeRoleText": "导游",
"advanceType": "TICKET",
"amount": 180.0,
"purpose": "呼伦贝尔大草原景区门票代垫",
"status": "SUBMITTED",
"statusText": "待审批",
"createdByName": "admin",
"canRevoke": true
},
{
"id": "2105886437624999938",
"orderId": "2100743225424621570",
"teamNo": "26-8707",
"payeeName": "刘大山",
"advanceType": "CATERING",
"amount": 120.0,
"purpose": "额尔古纳湿地午餐代垫",
"status": "SUBMITTED",
"statusText": "待审批",
"createdByName": "jw",
"canRevoke": false
}
],
"total": 2,
"page": 1,
"pageSize": 20
},
"success": true
}
空数据 / 降级响应
无预支时 records 为空数组(不变)。开关 advance.acl.enforce.role-guard 关闭时,非团期管理员看待审批行都为 true。
错误响应
判权不变:非本单定制师、财务等无权角色仍返回 581008。
{
"code": 581008,
"message": "无权查看此订单",
"data": null,
"success": false
}
业务边界
- 每行单独计算,同一页里可以有
true也有false。 - 车务管理员能看这个列表,但看所有行都是
false。
2. 预支审批列表 GET /v3/admin/order/advance-approvals/page
VO: AdvanceApprovalPageReqVO → Result<PageResult<AdvanceApprovalPageItemRespVO>>
使用场景
「财务管理 → 预支审批」列表。页面目前没有撤回按钮,字段供后续使用。只有超管、管理员、财务能进(不变)。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| status | Query | String | ❌ | SUBMITTED / APPROVED / REJECTED / PAID |
不变 |
| page | Query | Integer | ❌ | ≥1 | 不变 |
| pageSize | Query | Integer | ❌ | ≥1 | 不变 |
| 其余筛选项 | Query | — | ❌ | — | 不变 |
出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| records[].canRevoke | Boolean | 🆕 规则见上表;财务看别人申请的为 false |
| 其余字段 | — | 不变 |
请求示例
GET /v3/admin/order/advance-approvals/page?status=SUBMITTED&page=1&pageSize=20 HTTP/1.1
Authorization: Bearer <财务 token>
响应示例
{
"code": 200,
"message": "成功",
"data": {
"records": [
{
"id": "2105886435083206657",
"orderId": "2100743225424621570",
"orderNo": "HL20260918082629372",
"teamNo": "26-8707",
"scope": "ORDER",
"amount": 180.0,
"status": "SUBMITTED",
"createdByName": "admin",
"canRevoke": false
}
],
"total": 1,
"page": 1,
"pageSize": 20
},
"success": true
}
空数据 / 降级响应
无数据时 records 为空数组(不变)。开关关闭时财务看待审批行都为 true。
错误响应
判权不变:非超管 / 管理员 / 财务返回 585008。
{
"code": 585008,
"message": "仅财务或管理员可查看预支审批、审批或驳回预支",
"data": null,
"success": false
}
业务边界
- 本接口多查了一列申请人账号 ID 用于计算,不出参。
- 团期级行(
scope=GROUP_BATCH)同样按规则计算。
3. 团期预支记录(财务页签) GET /v3/admin/order/group-batch/:groupBatchId/advances
VO: List<GroupBatchAdvanceItemVO>(继承 OrderAdvanceRespVO)
使用场景
团期详情「财务」页签的预支记录。前端据 canRevoke 决定显不显示「撤回」。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| groupBatchId | Path | Long | ✅ | 团期 ID | 不变 |
出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| [].canRevoke | Boolean | 🆕 规则见上表;团期管理员恒为 false |
| 其余字段 | — | 不变(scope / orderNo / teamNo 等) |
请求示例
GET /v3/admin/order/group-batch/2104839654727618562/advances HTTP/1.1
Authorization: Bearer <管理员 token>
响应示例
{
"code": 200,
"message": "成功",
"data": [
{
"id": "2105589347598368769",
"scope": "GROUP_BATCH",
"scopeName": "团期级",
"orderId": null,
"orderNo": null,
"amount": 5000.0,
"status": "SUBMITTED",
"statusText": "待审批",
"createdByName": "金卫",
"canRevoke": true
}
],
"success": true
}
空数据 / 降级响应
无预支时返回空数组(不变)。开关关闭时,非团期管理员看待审批行都为 true,团期管理员仍为 false。
错误响应
判权不变。团期不存在:
{
"code": 589500,
"message": "团期不存在",
"data": null,
"success": false
}
业务边界
- 原来前端写的
a.status === 'SUBMITTED' && !isGroupBatchManager可以整体换成a.canRevoke。 - 子订单级行与团期级行规则相同。
4. 发起订单级预支 POST /v3/admin/order/:orderId/advance
VO: CreateAdvanceReqVO → Result<OrderAdvanceRespVO>
使用场景
订单详情「发起预支」。返回体里 canRevoke 对发起人本人为 true(刚建的待审批,自己可以撤)。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| orderId | Path | Long | ✅ | 订单 ID | 不变 |
| payeeStaffId | Body | Long | ✅ | 本单人员 | 不变 |
| advanceType | Body | String | ✅ | 字典 advance_type |
不变 |
| amount | Body | BigDecimal | ✅ | >0,不超可用上限 | 不变 |
| purpose | Body | String | ❌ | — | 不变 |
| voucherUrl | Body | String | ❌ | — | 不变 |
出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| data.canRevoke | Boolean | 🆕 发起人本人为 true |
| 其余字段 | — | 不变 |
请求示例
{
"payeeStaffId": "2100747615736897537",
"advanceType": "TICKET",
"amount": 180.00,
"purpose": "呼伦贝尔大草原景区门票代垫"
}
响应示例
{
"code": 200,
"message": "成功",
"data": {
"id": "2105886435083206657",
"orderId": "2100743225424621570",
"status": "SUBMITTED",
"statusText": "待审批",
"amount": 180.0,
"canRevoke": true
},
"success": true
}
空数据 / 降级响应
无。
错误响应
判权与校验不变。金额超可用上限:
{
"code": 585004,
"message": "预支金额超过可用余额上限",
"data": null,
"success": false
}
业务边界
- 只是返回体多一个字段,创建逻辑不变。
5. 预支审批通过 PUT /v3/admin/order/advance/:advanceId/approve
VO: Result<OrderAdvanceRespVO>(无请求体)
使用场景
审批中心「通过」。审批后状态是已通过,canRevoke 恒为 false。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| advanceId | Path | Long | ✅ | 预支 ID | 不变 |
出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| data.canRevoke | Boolean | 🆕 恒 false |
| 其余字段 | — | 不变 |
请求示例
PUT /v3/admin/order/advance/2105886444679774210/approve HTTP/1.1
Authorization: Bearer <财务 token>
响应示例
{
"code": 200,
"message": "成功",
"data": {
"id": "2105886444679774210",
"status": "APPROVED",
"statusText": "已通过",
"approvedBy": "yaosutu",
"canRevoke": false
},
"success": true
}
空数据 / 降级响应
无。
错误响应
判权不变。预支不存在(财务 / 管理员):
{
"code": 585000,
"message": "预支记录不存在",
"data": null,
"success": false
}
业务边界
- 审批逻辑与出纳待付款的生成不变。
6. 预支审批驳回 PUT /v3/admin/order/advance/:advanceId/reject
VO: RejectAdvanceReqVO → Result<OrderAdvanceRespVO>
使用场景
审批中心「驳回」。驳回后状态是已驳回,canRevoke 恒为 false。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| advanceId | Path | Long | ✅ | 预支 ID | 不变 |
| reason | Body | String | ✅ | 驳回原因 | 不变 |
出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| data.canRevoke | Boolean | 🆕 恒 false |
| 其余字段 | — | 不变 |
请求示例
{
"reason": "门票已由地接社统一采购,无需个人垫付"
}
响应示例
{
"code": 200,
"message": "成功",
"data": {
"id": "2104861622562627585",
"status": "REJECTED",
"statusText": "已驳回",
"rejectReason": "门票已由地接社统一采购,无需个人垫付",
"canRevoke": false
},
"success": true
}
空数据 / 降级响应
无。
错误响应
判权不变。预支不存在(财务 / 管理员):
{
"code": 585000,
"message": "预支记录不存在",
"data": null,
"success": false
}
业务边界
- 驳回逻辑不变。
7. 发起团期级预支 POST /v3/admin/order/group-batch/:groupBatchId/advance
VO: CreateGroupBatchAdvanceReqVO → Result<OrderAdvanceRespVO>
使用场景
团期财务页签「发起预支」。返回体里 canRevoke 对发起人本人为 true;团期管理员发起的为 false(团期管理员撤回会先被 581008 拦下)。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| groupBatchId | Path | Long | ✅ | 团期 ID | 不变 |
| payeeStaffId | Body | Long | ✅ | 团期人员 | 不变 |
| advanceType | Body | String | ✅ | 字典 advance_type |
不变 |
| amount | Body | BigDecimal | ✅ | >0,不超团期统一池上限 | 不变 |
| purpose | Body | String | ❌ | — | 不变 |
| voucherUrl | Body | String | ❌ | — | 不变 |
出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| data.canRevoke | Boolean | 🆕 发起人本人为 true,团期管理员为 false |
| 其余字段 | — | 不变 |
请求示例
{
"payeeStaffId": "2100747615736897537",
"advanceType": "CATERING",
"amount": 600.00,
"purpose": "满洲里套娃广场团餐代垫"
}
响应示例
示例值(发起人本人调用):
{
"code": 200,
"message": "成功",
"data": {
"id": "2105890177461317634",
"orderId": null,
"status": "SUBMITTED",
"statusText": "待审批",
"amount": 600.0,
"canRevoke": true
},
"success": true
}
空数据 / 降级响应
无。
错误响应
判权与校验不变。团期不存在:
{
"code": 589500,
"message": "团期不存在",
"data": null,
"success": false
}
业务边界
- 只是返回体多一个字段,创建逻辑与额度池不变。
8. 核单汇总快照 GET /v3/admin/order/:orderId/settlement/summary
VO: Result<SettlementSummaryRespVO>
使用场景
核单页的汇总快照。advanceSummary.records 与预支列表共用同一个 VO,所以一并带上 canRevoke。这里只列已通过的预支,恒为 false。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| orderId | Path | Long | ✅ | 订单 ID | 不变 |
出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| advanceSummary.records[].canRevoke | Boolean | 🆕 恒 false(只含已通过) |
| 其余字段 | — | 不变 |
请求示例
GET /v3/admin/order/2100743225424621570/settlement/summary HTTP/1.1
Authorization: Bearer <管理员 token>
响应示例
{
"code": 200,
"message": "成功",
"data": {
"settled": false,
"orderId": "2100743225424621570",
"teamNo": "26-8707",
"advanceSummary": {
"approvedAmount": "90.00",
"records": [
{
"id": "2105886444679774210",
"status": "APPROVED",
"statusText": "已通过",
"amount": 90.0,
"canRevoke": false
}
]
}
},
"success": true
}
空数据 / 降级响应
没有已通过的预支时 records 为空数组(不变)。
错误响应
本接口没有业务错误码,订单不存在时也返回 200 和 settled=false 的空壳(行为不变)。网关层未带 token(TEST 2026-10-02 实打):
{
"code": 401,
"message": "缺少有效的 Authorization 头",
"data": null,
"success": false
}
业务边界
- 前端不需要用这里的
canRevoke。
四、契约约束与正确调用方式
- 撤回按钮显示条件改成
a.canRevoke,不要再自己拼「待审批 + 角色 + 是否团期管理员」。 canRevoke跟着当前登录账号和当前角色变;切换角色后要重新拉列表。canRevoke=true只表示列表渲染那一刻能撤;点击时若已被别人审批,仍会返回585005(状态不允许),照常提示即可。- 撤回接口的判权与错误码不变:非申请人、非管理员
585009,团期管理员581008。
五、数据库行为
- 零 DDL、零数据迁移、零写入。
- 取值用预支记录既有的创建人账号 ID(提交时自动写入);审批中心的联表查询多 select 这一列,不出参。
- 发起、审批、驳回的写库逻辑不变。
六、边界行为
- 定时任务、消息消费等无请求上下文的场景算出来恒为
false(这些场景不渲染列表)。 - 计算
canRevoke不打ADVANCE_ACL_DENY日志;只有真调撤回被拒时才打。 - 缺角色的 token 若正好是申请人本人,
canRevoke=true,与撤回接口一致(#8517 有意的设计)。
六.6、修改前后对比
| 场景 | 改前 | 改后 |
|---|---|---|
| 非申请人、非管理员看待审批行 | 按钮显示,点了 585009 |
canRevoke=false,前端可隐藏 |
| 申请人本人 / 管理员看待审批行 | 按钮显示,能撤 | canRevoke=true |
| 团期管理员看团期页签 | 前端用 !isGroupBatchManager 自己藏 |
canRevoke=false |
| 非待审批行 | 前端按状态藏 | canRevoke=false |
| 开关关闭 | 前端不知道开关状态 | 非团期管理员都为 true,跟撤回接口一致 |
六.7、影响评估
- 是否破坏向后兼容:否,纯新增字段。
- 前端是否必须同步上线:否。不改前端时行为与改前相同(按钮照旧显示);改了才能隐藏点不了的按钮。
- 回滚:revert PR #8723 后重新部署 order-v3。
七、不影响范围
- 撤回接口
DELETE /v3/admin/order/advance/:advanceId的判权与返回:不变。 - 各接口的入参、判权、其余出参:不变。
- 出纳付款、核单计算:不变。
- 小程序端:无影响。
八、测试环境已验证
环境:TEST(https://api.test.1814.love) 验证时间:2026-10-02 13:03~13:30
构建身份:order-v3 部署 dev-v3 @ 806058c66(本单合并提交),12:57 完成。零写入判据:部署后连查 6 次订单级预支列表,每行都带 canRevoke 键(旧字节没有这个键)。
身份:自签 token 直打网关,用 TEST 真实账号 ID 配对应角色;TEST 上没有在用的财务账号,财务用 role=FINANCE 的自签 token。
8.1 造数
在待出发订单 HL20260918082629372(定制师 1001)上:C1 定制师申请门票 180(待审批)、C2 管理员 jw 申请餐费 120(待审批)、C3 定制师申请门票 90 后由财务审批(已通过)。另有该单既有的已付款、已驳回各一笔。
8.2 三个列表逐角色取值
| 列表 | 查看人 | C1(1001 申请,待审批) | C2(jw 申请,待审批) | 已通过 / 已付款 / 已驳回 |
|---|---|---|---|---|
| 订单级 | 本单定制师 1001 | true |
false |
均 false |
| 订单级 | 管理员 | true |
true |
均 false |
| 订单级 | 车务管理员 | false |
false |
均 false |
| 审批中心 | 财务 | false |
false |
已通过 false |
| 审批中心 | 管理员 | true |
true |
已通过 false |
团期财务页签(团期级待审批一笔,jw 申请):财务 false、团期管理员 false、管理员 true。
联表探针:财务角色、账号 ID 设为 1001 查审批中心,C1 为 true、C2 为 false,证明审批中心带出了创建人。
核单汇总:只含 C3,canRevoke=false。
8.3 按 canRevoke 抽样真实撤回
| 操作 | 结果 |
|---|---|
财务、其他定制师撤 C1;车务、定制师 1001 撤 C2(均为 false) |
均 585009,未删除 |
| 团期管理员撤 C1 | 581008 |
定制师 1001 撤 C1、管理员撤 C2(均为 true) |
均 200,已软删 |
8.4 nacos 回滚开关往返
| 态 | 订单级:定制师 / 车务看 C2 | 团期页签:财务 / 团期管理员 | 审批中心:财务看 C1、C2、团期级 |
|---|---|---|---|
| A 默认 | false / false |
false / false |
全 false |
| B 关闭(10.5 秒生效) | true / true |
true / false |
全 true |
| C 还原(7.7 秒生效) | false / false |
false / false |
全 false |
- 发布带
casMd5,还原写在finally里;还原后 md5 与原值同为c2206934960057f70b7173159046dc54。 - 两个实例的
ADVANCE_ACL_DENY计数在三态的列表渲染前后都是 0;随后 4 次被拒的真实撤回让计数各 +2,证明计数有效。
8.5 回归(零写入)
不存在的预支 ID 调审批 / 驳回 / 撤回:定制师、车务 585008 / 585008 / 585009;财务 585000 / 585000 / 585009;管理员三个 585000;团期管理员三个 581008。与 #8517 验收读数一致,前后表行数不变。
本地证据
| 项 | 读数 |
|---|---|
| 相关 16 个测试类定向 | 265/0/0/0 |
| 一致性矩阵 | canRevoke 与撤回守卫在 144 种角色 × 账号 × 创建人 × 开关组合下逐条一致 |
| 变异 | 删掉审批中心联表那一列 → 1 例红;让 canRevoke 对财务放宽 → 2 例红;已还原 |
| order-v3 全量(有 Docker,两半) | 1115 个可执行测试类全部有报告;红 2 个类、hl-finance 红 25 个类,在基底 3bad2ad27 上读数与用例名逐条一致,本单零新增 |
十、相关文档
- Issue
#8684;PR#8723 - 前置:Issue
#8517(撤回判权本身)
关联 / 联系人
链接
联系人
- 后端负责人: @jw