原文写「菜单可见性授予 SUPER_ADMIN / ADMIN / VEHICLE_MANAGER 三个角色」「权限码 授权 VEHICLE_MANAGER / ADMIN / SUPER_ADMIN」,这两句在 PR #7514 之后已经过时: V20260911_007__fix_group_dispatch_admin_grants.sql 于 2026-09-11 14:04:26 在测试库 收回了 ADMIN 的菜单行(2 行)与权限码授权(1 行),部署后实测两项命中数均为 0。 收回的原因本文件八节末尾那条红字早就写着:/admin/fleet/** 的访问控制是 #5626 的 路径级粗粒度角色门禁,只放行 VEHICLE_MANAGER 与 SUPER_ADMIN,不看权限码。给 ADMIN 菜单等于造一个看得见、点进去必 403 的死入口。也就是说本文件自身前后矛盾——六.8 节 说 ADMIN 有菜单,八节说 ADMIN 调接口必 403。现已统一到后者。 接口契约、错误码与行为均未变,仅授权数据层的描述订正。frontmatter 补 status_note。 前端影响:不要按「ADMIN 能看到团期配车」设计任何菜单可见性或路由逻辑。
16 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 | 7440 | 团期车务派单只读入口——看板三端点 + 内部读接口 + GROUP_FLEET 双团会话 | admin | wx(GIT) | 新增接口 | deployed | verified | pending | 2026-09-11 | 2026-09-11 订正:六.8 节的角色授予范围已随 PR #7514(V20260911_007)收回 ADMIN,正文已更新为当前状态;接口契约、错误码与行为均未变。 | 2026-09-11 | dev-v3 |
fleet/order-v3/user: 团期车务派单只读入口
服务: hl-fleet-service / hl-order-service-v3 / hl-user-service
PR: #7506
Issue: #7440
⚠️ 关键变化
本单仅含只读入口,不含配车写入、不含派单进度回写(配车写入与派单进度回写在后续工单)。
一、背景
本单新增3个admin只读端点供fleet读取团期需求主数据(order-v3)与服务日,以及2个/internal/端点供fleet内部调用。
二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 待配车团期清单 | GET | /admin/fleet/group-dispatch/pending-batches |
新增 | 管理后台车务页首屏清单 |
| 2 | 团期配车总览 | GET | /admin/fleet/group-dispatch/batches/{groupBatchId}/overview |
新增 | 单团详情页派车总览 |
| 3 | 资源排班 | GET | /admin/fleet/group-dispatch/resource-schedule |
新增 | 派单编辑时查资源占用 |
| 4 | 打开团期配车会话 | POST | /admin/message/chat/open-group-fleet |
新增 | 车务↔团期管理员团级会话 |
| 内部 | 待配车候选分页 | POST | /v3/internal/group-batch/vehicle-dispatch-candidates |
新增 | 服务间接口,前端无需对接 |
| 内部 | 单团用车覆盖 | GET | /v3/internal/group-batch/{groupBatchId}/vehicle-coverage |
新增 | 服务间接口,前端无需对接 |
三、接口详情
1. 待配车团期清单 GET /admin/fleet/group-dispatch/pending-batches
VO: GroupDispatchPendingBatchPageReqVO → Result<PageResult<GroupDispatchPendingBatchRespVO>>
使用场景
管理后台车务-团期配车页面的首屏清单。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| departDateFrom | Query | LocalDate | ❌ | - | 出发日下界 |
| departDateTo | Query | LocalDate | ❌ | - | 出发日上界 |
| keyword | Query | String | ❌ | ≤50字符 | 团号/团名 |
| dispatchProgress | Query | String | ❌ | NOT_STARTED/PARTIAL/FULL | 进度过滤 |
| page | Query | Integer | ❌ | ≥1 | 页码(默认1) |
| pageSize | Query | Integer | ❌ | 1-100 | 每页条数(默认20) |
出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| groupBatchId | Long | 团期ID |
| batchNo | String | 团号 |
| batchName | String | 团名 |
| departDate | LocalDate | 出团日 |
| dispatchProgress | String | 配车进度 |
| unreadCount | Integer | 车务未读数 |
请求示例
{
"departDateFrom": "2026-09-01",
"departDateTo": "2026-09-30",
"page": 1,
"pageSize": 20
}
响应示例
{
"code": 200,
"data": {
"total": 15,
"records": [
{
"groupBatchId": 1934567890123456789,
"batchNo": "GB-26-0912-01",
"batchName": "额吉的故乡 9/12 团",
"departDate": "2026-09-12",
"dispatchProgress": "PARTIAL",
"unreadCount": 3
}
]
},
"success": true
}
空数据 / 降级响应
{
"code": 200,
"data": {"total": 0, "records": []},
"success": true
}
错误响应
{
"code": 600013,
"message": "排班查询参数非法: departDateFrom 不能晚于 departDateTo",
"success": false
}
{
"code": 600012,
"message": "团期配车基线不可达,请稍后重试",
"success": false
}
降级链路:600013 由本端点自身入参校验抛出(page<1/pageSize不在1-100/出发日区间倒挂/keyword超50字符/dispatchProgress枚举非法,五种情形共用同一个码)。600012 是本端点对外唯一的「上游不可达」码——内部经 Feign 调用 order-v3 团期用车候选口(POST /v3/internal/group-batch/vehicle-dispatch-candidates),该内部接口对同一批入参约束会独立抛 809401 查询参数非法(GroupBatchVehicleReadErrorCode.java:38),但本端点已先行校验挡在前面(不会把非法参数带给 Feign),且 fetchCandidatesOrFail(GroupDispatchQueryService.java:314-336)对 Feign 返回的任何非成功 Result(含 809401)一律失败关闭转成 600012——调用方看不到 809401 原始码。
业务边界
- 进度是fleet内存过滤,故先分页再过滤,单页可能少于pageSize
- unreadCount软依赖,user-service不可达时退0
2. 团期配车总览 GET /admin/fleet/group-dispatch/batches/{groupBatchId}/overview
VO: Long → Result<GroupDispatchOverviewRespVO>
使用场景
单团详情页的派车总览。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| groupBatchId | Path | Long | ✅ | - | 团期ID |
出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| groupBatchId | Long | 团期ID |
| serviceDates | List | 权威服务日 |
| days | List | 逐日派车状态 |
| transferPendingTotal | Integer | 全团接送机未配计数 |
| conversationKey | String | 会话键 |
请求示例
GET /admin/fleet/group-dispatch/batches/1934567890123456789/overview
响应示例
{
"code": 200,
"data": {
"groupBatchId": 1934567890123456789,
"serviceDates": ["2026-09-12", "2026-09-13", "2026-09-14"],
"days": [
{"serviceDate": "2026-09-12", "dispatched": true}
],
"transferPendingTotal": 3,
"conversationKey": "GROUP_FLEET:1934567890123456789"
},
"success": true
}
空数据 / 降级响应
无。
错误响应
{
"code": 600012,
"message": "团期配车基线不可达,请稍后重试",
"success": false
}
降级链路(🔴 本端点对外只有 600012 这一个码):内部经 Feign 调用 order-v3 单团用车覆盖口(GET /v3/internal/group-batch/{groupBatchId}/vehicle-coverage),团期不存在或已软删时该内部接口会抛 809400 团期不存在(GroupBatchVehicleReadErrorCode.java:28);但 fetchCoverageOrFail(GroupDispatchQueryService.java:526-537)把 Feign 返回的任何非成功 Result(含 809400)一律失败关闭转成 600012——本端点不透出 809400,调用方只会看到 600012,不要按「overview 会返 809400」处理。
业务边界
- serviceDates是权威口径,fleet不得自己铺行程日
3. 资源排班 GET /admin/fleet/group-dispatch/resource-schedule
VO: ResourceScheduleListReqVO → Result<List<ResourceScheduleRespVO>>
使用场景
派单编辑时查询车/司机的排班占用。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| resourceType | Query | String | ✅ | VEHICLE/DRIVER | 资源维度 |
| resourceIds | Query | List | ✅ | 1-50个 | 资源ID集合 |
| dateFrom | Query | LocalDate | ✅ | - | 起始日 |
| dateTo | Query | LocalDate | ✅ | ≤31天 | 结束日 |
出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| resourceType | String | 资源维度 |
| resourceId | Long | 资源ID |
| resourceLabel | String | 车牌/司机名 |
| days | List | 排班日历 |
请求示例
GET /admin/fleet/group-dispatch/resource-schedule?resourceType=VEHICLE&resourceIds=1&dateFrom=2026-09-12&dateTo=2026-09-30
响应示例
{
"code": 200,
"data": [
{
"resourceType": "VEHICLE",
"resourceId": 1,
"resourceLabel": "蒙A12345",
"days": []
}
],
"success": true
}
空数据 / 降级响应
返回空days列表。
错误响应
{
"code": 600014,
"message": "不支持的资源类型: BOAT",
"success": false
}
{
"code": 600013,
"message": "排班查询参数非法: 查询跨度不能超过 31 天",
"success": false
}
触发条件:600014 仅在 resourceType 不是 VEHICLE/DRIVER 时抛出(示例中 BOAT 为非法值占位)。600013 覆盖:resourceIds 为空 / 超 50 个、dateFrom/dateTo 为空、日期倒置、跨度超 31 天,五种情形共用同一个码。本端点只读本域两张业务表,不经 Feign 调用 order-v3,故与 809400/809401 无关。
业务边界
- 不分页,规模由参数上限封死
- 返回顺序必须与入参顺序相同
4. 打开团期配车会话 POST /admin/message/chat/open-group-fleet
VO: ChatOpenGroupFleetReqVO → Result<ChatOpenFullRespVO>
使用场景
派单看板打开团期的车务↔团期管理员会话。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| groupBatchId | Body | Long | ✅ | - | 团期ID |
出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| conversationKey | String | 会话键 |
| conversationId | Long | 会话ID |
| currentSide | String | GROUP_ADMIN / VEHICLE_TEAM |
请求示例
{
"groupBatchId": 1934567890123456789
}
响应示例
{
"code": 200,
"data": {
"conversationKey": "GROUP_FLEET:1934567890123456789",
"conversationId": 1934567890123456789,
"currentSide": "GROUP_ADMIN"
},
"success": true
}
空数据 / 降级响应
无。
错误响应
{
"code": 281018,
"message": "无权访问该团期配车会话",
"success": false
}
{
"code": 281019,
"message": "团期不存在、已结算或已取消,无法发起配车会话",
"success": false
}
分工:281018 = 有登录态但当前角色既非 VEHICLE_MANAGER(车务侧)也不持 group-batch:demand:confirm(团期管理员侧,超管天然通过)——准入完全按 token 当前角色判,与是否为会话成员无关(TeamChatAuthorizationService.java:158-169)。281019 = 团期本身不可联系(不存在/已软删/已结算/已取消),前置条件与 GROUP_HOUSE 用的 281017 同源(TeamChatModule.java:75)。281019 只卡 open(本端点)/ send,messages/read 仍放行(requirePrecondition 参数为 false,见 ChatMessageController.java 的 /{conversationKey}/messages)——终态团期的历史会话仍可审计读取,前端不要因为拿到过 281019 就连历史消息一起屏蔽。
281016「缺少团期上下文」(ChatErrorCode.java:76)存在但正常路径触发不到:groupBatchId 由请求体 VO 的 @NotNull + Controller 的 @Valid 先挡成 100001,281016 只在 internal / 单测直调 Service 时才会真抛(见 ChatManager.java:444-446 注释),前端按正常表单提交不会遇到它。
业务边界
- 双团队会话
- 前置:团期活跃
四、契约约束与正确调用方式
| 场景 | 结果 |
|---|---|
| 分页越界 | 返600013 |
| 日期倒置 | 返600013 |
| 关键词超长 | 返600013 |
五、数据库行为
本单仅含查询接口,无写操作。
六、边界行为
- 未登录 → 401
- 团期不存在 → 600012
六.5、枚举
dispatchProgress
| 值 | 中文 |
|---|---|
| NOT_STARTED | 未启动 |
| PARTIAL | 部分完成 |
| FULL | 已完成 |
六.6、修改前后对比
N/A
六.7、影响评估
- 是否破坏向后兼容: 否
- 前端是否必须同步上线: 否
六.8、菜单与权限
- 菜单挂载(
V20260910_006__add_group_batch_fleet_dispatch_menu.sql):在既有「车务管理」(path=/fleet)节点下新增二级目录「团期配车」(menu_type=D,path=/fleet/group-dispatch,icon=Van,sort_order=46),其下唯一三级菜单同名「团期配车」(menu_type=M,path=component=/fleet/group-dispatch);菜单可见性(sys_role_menu)当前授予SUPER_ADMIN/VEHICLE_MANAGER两个角色。⚠️ 2026-09-11 订正:本脚本原本还授予了ADMIN,已由V20260911_007__fix_group_dispatch_admin_grants.sql(PR #7514,14:04:26 部署)收回。原因见八节末尾那条红字——/admin/fleet/**的门禁只放行VEHICLE_MANAGER/SUPER_ADMIN,给ADMIN菜单等于造一个看得见、点进去必 403 的死入口。前端不要按「ADMIN能看到团期配车」设计任何逻辑。 - 权限标识(
V20260910_007__add_group_batch_fleet_permission.sql):新增权限码fleet:group-dispatch:view(resource_type=FLEET_GROUP_DISPATCH,action=VIEW),覆盖本单三个只读端点(待配车团期清单/团期配车总览/资源排班);三级菜单的permission_code字段(V20260910_006 第66行)写的也是这同一个码。admin_role_permission当前授权VEHICLE_MANAGER/SUPER_ADMIN(原本含ADMIN,同由V20260911_007收回,理由同上);团期管理员侧不在此授权范围内(他们看配车进度走团期管理页,不进车务页面)。
七、不影响范围
- 房务、C端、既有会话
八、测试环境已验证
环境与提交:测试服部署 hl-fleet-service / hl-order-service-v3 @ 861ebff0c,hl-user-service @ 30761683e(与前两者不同提交号是部署窗口内 dev-v3 尖端继续前移所致;已核实中间那 5 个提交对 hl-user-service/hl-fleet-service 零文件改动,且改动集中在 hl-common-feign,与本单改的 hl-common-core/dto/fleet 零重叠,契约不受影响)。
网关实测(6 个端点,每条两轮不同角色):
| 端点 | 角色 | 结果 |
|---|---|---|
GET /admin/fleet/group-dispatch/pending-batchesGET /admin/fleet/group-dispatch/batches/{id}/overviewGET /admin/fleet/group-dispatch/resource-schedule |
VEHICLE_MANAGER |
200,返回真实数据 |
| 同上三个 | ROOM_MANAGER / ADMIN |
403「无权限访问车务管理,请切换到车务角色」 |
POST /v3/internal/group-batch/vehicle-dispatch-candidatesGET /v3/internal/group-batch/{id}/vehicle-coverage |
任意(含带 token) | 403「接口不可访问」——网关 JwtAuthFilter 按 /v3/internal/** 路径前缀在鉴权之前就拦掉,与 token/角色无关,外部不可达(符合预期,这两个是服务间接口) |
POST /admin/message/chat/open-group-fleet |
VEHICLE_MANAGER |
200,两次调用 isNew 分别为 true/false(首开建会话、二次找回同一条,幂等生效) |
🔴 ADMIN 角色拿到的是 403,不是 200——fleet 三个只读端点的访问控制是 #5626 的粗粒度角色门禁(仅放行 VEHICLE_MANAGER / SUPER_ADMIN),不看权限码 fleet:group-dispatch:view(六.8 节新增的那个)。前端如果按「持有该权限码就能调」设计菜单可见性之外的接口调用逻辑,ADMIN 角色会在实调时翻车——必须同时满足角色门禁。
GET /admin/fleet/group-dispatch/pending-batches → 200 ✓(VEHICLE_MANAGER)
九、相关历史 PR
| PR | Issue | 说明 |
|---|---|---|
| #7506 | #7440 | 本单 |
十、相关文档
关联 / 联系人
链接
联系人
- 后端负责人: @wx