--- schema: "hl-changelog/v2" ticket: "8684" title: "预支相关 8 个接口出参新增 canRevoke:当前操作人点「撤回」能否成功,与撤回接口共用同一个判定" consumer: "admin" author: "jw(GIT)" change_type: "修改接口" backend_status: "deployed" gateway_status: "verified" frontend_status: "implemented" frontend_owner: "mmg" frontend_ref: "41468f5297fc1d5c04b49772b76af73d2fd0c3bf" target_release: "v2.1" verified_at: "2026-10-02" status_note: "已合并 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。" updated_at: "2026-10-02" base: "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>` #### 使用场景 订单详情的「预支」弹窗。前端据 `canRevoke` 决定每一行显不显示「撤回」按钮。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | orderId | Path | Long | ✅ | 订单 ID | **不变** | | page | Query | Integer | ❌ | ≥1 | **不变** | | pageSize | Query | Integer | ❌ | ≥1 | **不变** | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | records[].canRevoke | Boolean | 🆕 当前操作人点撤回能否成功,规则见上表 | | 其余字段 | — | **不变**(`id` / `status` / `createdByName` 等) | #### 请求示例 ```http GET /v3/admin/order/2100743225424621570/advances?page=1&pageSize=20 HTTP/1.1 Authorization: Bearer <本单定制师 token> ``` #### 响应示例 本单定制师看:自己申请的待审批为 `true`,管理员申请的待审批为 `false`,已通过的为 `false`。 ```json { "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`。 ```json { "code": 581008, "message": "无权查看此订单", "data": null, "success": false } ``` #### 业务边界 - 每行单独计算,同一页里可以有 `true` 也有 `false`。 - 车务管理员能看这个列表,但看所有行都是 `false`。 --- ### 2. 预支审批列表 `GET /v3/admin/order/advance-approvals/page` **VO**: `AdvanceApprovalPageReqVO` → `Result>` #### 使用场景 「财务管理 → 预支审批」列表。页面目前没有撤回按钮,字段供后续使用。只有超管、管理员、财务能进(不变)。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | status | Query | String | ❌ | `SUBMITTED` / `APPROVED` / `REJECTED` / `PAID` | **不变** | | page | Query | Integer | ❌ | ≥1 | **不变** | | pageSize | Query | Integer | ❌ | ≥1 | **不变** | | 其余筛选项 | Query | — | ❌ | — | **不变** | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | records[].canRevoke | Boolean | 🆕 规则见上表;财务看别人申请的为 `false` | | 其余字段 | — | **不变** | #### 请求示例 ```http GET /v3/admin/order/advance-approvals/page?status=SUBMITTED&page=1&pageSize=20 HTTP/1.1 Authorization: Bearer <财务 token> ``` #### 响应示例 ```json { "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`。 ```json { "code": 585008, "message": "仅财务或管理员可查看预支审批、审批或驳回预支", "data": null, "success": false } ``` #### 业务边界 - 本接口多查了一列申请人账号 ID 用于计算,**不出参**。 - 团期级行(`scope=GROUP_BATCH`)同样按规则计算。 --- ### 3. 团期预支记录(财务页签) `GET /v3/admin/order/group-batch/:groupBatchId/advances` **VO**: `List`(继承 `OrderAdvanceRespVO`) #### 使用场景 团期详情「财务」页签的预支记录。前端据 `canRevoke` 决定显不显示「撤回」。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | Path | Long | ✅ | 团期 ID | **不变** | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | [].canRevoke | Boolean | 🆕 规则见上表;团期管理员恒为 `false` | | 其余字段 | — | **不变**(`scope` / `orderNo` / `teamNo` 等) | #### 请求示例 ```http GET /v3/admin/order/group-batch/2104839654727618562/advances HTTP/1.1 Authorization: Bearer <管理员 token> ``` #### 响应示例 ```json { "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`。 #### 错误响应 判权不变。团期不存在: ```json { "code": 589500, "message": "团期不存在", "data": null, "success": false } ``` #### 业务边界 - 原来前端写的 `a.status === 'SUBMITTED' && !isGroupBatchManager` 可以整体换成 `a.canRevoke`。 - 子订单级行与团期级行规则相同。 --- ### 4. 发起订单级预支 `POST /v3/admin/order/:orderId/advance` **VO**: `CreateAdvanceReqVO` → `Result` #### 使用场景 订单详情「发起预支」。返回体里 `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` | | 其余字段 | — | **不变** | #### 请求示例 ```json { "payeeStaffId": "2100747615736897537", "advanceType": "TICKET", "amount": 180.00, "purpose": "呼伦贝尔大草原景区门票代垫" } ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "id": "2105886435083206657", "orderId": "2100743225424621570", "status": "SUBMITTED", "statusText": "待审批", "amount": 180.0, "canRevoke": true }, "success": true } ``` #### 空数据 / 降级响应 无。 #### 错误响应 判权与校验不变。金额超可用上限: ```json { "code": 585004, "message": "预支金额超过可用余额上限", "data": null, "success": false } ``` #### 业务边界 - 只是返回体多一个字段,创建逻辑不变。 --- ### 5. 预支审批通过 `PUT /v3/admin/order/advance/:advanceId/approve` **VO**: `Result`(无请求体) #### 使用场景 审批中心「通过」。审批后状态是已通过,`canRevoke` 恒为 `false`。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | advanceId | Path | Long | ✅ | 预支 ID | **不变** | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | data.canRevoke | Boolean | 🆕 恒 `false` | | 其余字段 | — | **不变** | #### 请求示例 ```http PUT /v3/admin/order/advance/2105886444679774210/approve HTTP/1.1 Authorization: Bearer <财务 token> ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "id": "2105886444679774210", "status": "APPROVED", "statusText": "已通过", "approvedBy": "yaosutu", "canRevoke": false }, "success": true } ``` #### 空数据 / 降级响应 无。 #### 错误响应 判权不变。预支不存在(财务 / 管理员): ```json { "code": 585000, "message": "预支记录不存在", "data": null, "success": false } ``` #### 业务边界 - 审批逻辑与出纳待付款的生成不变。 --- ### 6. 预支审批驳回 `PUT /v3/admin/order/advance/:advanceId/reject` **VO**: `RejectAdvanceReqVO` → `Result` #### 使用场景 审批中心「驳回」。驳回后状态是已驳回,`canRevoke` 恒为 `false`。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | advanceId | Path | Long | ✅ | 预支 ID | **不变** | | reason | Body | String | ✅ | 驳回原因 | **不变** | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | data.canRevoke | Boolean | 🆕 恒 `false` | | 其余字段 | — | **不变** | #### 请求示例 ```json { "reason": "门票已由地接社统一采购,无需个人垫付" } ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "id": "2104861622562627585", "status": "REJECTED", "statusText": "已驳回", "rejectReason": "门票已由地接社统一采购,无需个人垫付", "canRevoke": false }, "success": true } ``` #### 空数据 / 降级响应 无。 #### 错误响应 判权不变。预支不存在(财务 / 管理员): ```json { "code": 585000, "message": "预支记录不存在", "data": null, "success": false } ``` #### 业务边界 - 驳回逻辑不变。 --- ### 7. 发起团期级预支 `POST /v3/admin/order/group-batch/:groupBatchId/advance` **VO**: `CreateGroupBatchAdvanceReqVO` → `Result` #### 使用场景 团期财务页签「发起预支」。返回体里 `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` | | 其余字段 | — | **不变** | #### 请求示例 ```json { "payeeStaffId": "2100747615736897537", "advanceType": "CATERING", "amount": 600.00, "purpose": "满洲里套娃广场团餐代垫" } ``` #### 响应示例 示例值(发起人本人调用): ```json { "code": 200, "message": "成功", "data": { "id": "2105890177461317634", "orderId": null, "status": "SUBMITTED", "statusText": "待审批", "amount": 600.0, "canRevoke": true }, "success": true } ``` #### 空数据 / 降级响应 无。 #### 错误响应 判权与校验不变。团期不存在: ```json { "code": 589500, "message": "团期不存在", "data": null, "success": false } ``` #### 业务边界 - 只是返回体多一个字段,创建逻辑与额度池不变。 --- ### 8. 核单汇总快照 `GET /v3/admin/order/:orderId/settlement/summary` **VO**: `Result` #### 使用场景 核单页的汇总快照。`advanceSummary.records` 与预支列表共用同一个 VO,所以一并带上 `canRevoke`。这里只列已通过的预支,恒为 `false`。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | orderId | Path | Long | ✅ | 订单 ID | **不变** | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | advanceSummary.records[].canRevoke | Boolean | 🆕 恒 `false`(只含已通过) | | 其余字段 | — | **不变** | #### 请求示例 ```http GET /v3/admin/order/2100743225424621570/settlement/summary HTTP/1.1 Authorization: Bearer <管理员 token> ``` #### 响应示例 ```json { "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 实打): ```json { "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`(撤回判权本身) ## 关联 / 联系人 ### 链接 - **Issue**: [#8684](https://git.1814.love/wx/HL/issues/8684) - **PR**: [#8723](https://git.1814.love/wx/HL/pulls/8723) - **Merge commit**: [806058c66](https://git.1814.love/wx/HL/commit/806058c66) ### 联系人 - **后端负责人**: @jw