文件
hl-api-changelog/changelogs-v2/2026-09/16_7827_团期配置导游摄影候选分页与模糊搜索-新增接口-管理后台.md
T
2026-09-16 19:15:28 +08:00

17 KiB
原始文件 Blame 文件历史

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 7827 团期配置导游/摄影候选新增分页与模糊搜索端点(旧候选端点不变) admin jw(GIT) 新增接口 deployed verified pending 新增分页候选端点,已在测试服经真实网关验收(26 项全部通过,见第八节与工单 #7827 验收评论);旧候选端点不变。前端待办:① 配置导游 / 配置摄影弹窗切换到 GET .../staff/candidates/page,加搜索框与分页;② 每行显示 staffTypeName · staffName · staffPhone(电话保持脱敏);③ 已选名单以 GET .../staff 为准,保存时整份提交 PUT .../staff,不能只提交当前页勾选的人(见第四节)。前端切换后,后端另开工单下线旧候选端点。 2026-09-16 dev-v3

order-v3: 团期配置导游/摄影候选新增分页与模糊搜索端点

存放目录: changelogs-v2/{YYYY-MM}/(管理后台,二期 order-v3)

服务: hl-order-service-v3 (端口 8086)、hl-resource-service (端口 8082,仅新增内部接口) PR: #7831 Issue: #7827 日期: 2026-09-16 影响范围: 团期详情页「配置导游」「配置摄影」弹窗的人员候选列表


⚠️ 关键变化

  1. 新增分页端点 GET /v3/admin/group-batch/{productBatchId}/staff/candidates/page,支持 keyword 模糊搜索与 page / pageSize 分页,返回 PageResult。旧端点 GET .../staff/candidates 请求与响应完全不变,前端可按自己的节奏切换;切换完成后后端再另开工单下线旧端点。
  2. 分页后保存弹窗有一个必须处理的坑:PUT /v3/admin/group-batch/{productBatchId}/staff 是全量覆盖——请求里没有的人会被软删,并同步到全团活跃订单。前端不能只拿当前页的勾选去拼保存请求,否则其他页已选的人会被静默删掉。已选名单必须以 GET /v3/admin/group-batch/{productBatchId}/staff 的返回为准,候选页只负责增删,见第四节。
  3. 排序与旧端点不同:旧端点是「先导游、后领队」按类型拼接;分页端点把导游位的两类人放在一次查询里,统一按 sortOrder 升序、staffId 升序排列,所以导游和领队会穿插出现。
  4. 弹窗展示字段后端已齐,无需新增(jw 2026-09-16 口径):每行显示「角色名称 · 人员名称 · 联系电话」,分别取 staffTypeName、staffName、staffPhone。电话保持脱敏(前 3 后 4,如 138****1002),后端不提供明文。

一、背景

「配置导游」「配置摄影」弹窗此前调用的旧候选端点会一次性拉取资源库全部在职人员:没有分页,也不能搜索;导游位还要分两次拉取(导游、领队)再合并,头像也按全量人员去取。人员库一变大,弹窗就不好找人,接口也越来越慢。

本次新增分页端点:资源服务一次按类型集合分页查询,order-v3 只为当前页的人标记已选、取头像。

jw 2026-09-16 同时确认:按日期分段配置导游/摄影(例如第 1–3 天是 A、第 4–5 天是 B)本期不做。现有三张人员配置表都没有日期字段,一个人配上去就代表负责整个行程。


二、变更接口清单

# 接口 方法 路径 变更类型 说明
1 分页查询团期人员配置候选 GET /v3/admin/group-batch/{productBatchId}/staff/candidates/page 新增 带关键词与分页;条目结构与旧候选端点相同

资源服务同时新增了内部接口 GET /internal/staff/page-available,仅供 order-v3 通过 Feign 调用,网关不对外暴露,前端无需关注。


三、接口详情

1. 分页查询团期人员配置候选 GET /v3/admin/group-batch/{productBatchId}/staff/candidates/page

VO: Result<PageResult<StaffCandidateRespVO>>

使用场景

团期详情页点「配置导游」或「配置摄影」,弹窗打开时请求第 1 页;输入搜索词、翻页时再次请求。导游位传 role=GUIDE,摄影位传 role=PHOTOGRAPHER。

弹窗里「已选 N 人」和保存请求用的已选名单,必须来自 GET /v3/admin/group-batch/{productBatchId}/staff,不能由本接口各页的 assigned 拼出来。

入参

字段 位置 类型 必填 约束 说明
productBatchId Path Long ✅ 产品侧排期 ID 与旧候选端点、GET/PUT .../staff 用同一个 ID(group_tour_batch.batch_id),不是运营团期主键 groupBatchId
role Query String ✅ GUIDE / PHOTOGRAPHER 配置位。GUIDE 同时返回资源库里的导游(GUIDE)和领队(LEADER);PHOTOGRAPHER 只返回摄影(PHOTOGRAPHER);其他值返回 582113
keyword Query String 否 ≤50 字符;去掉首尾空格后为空视为不传 按姓名模糊匹配(包含即命中);纯数字时另按手机号尾号匹配(如 1002);%、_ 按普通字符处理
page Query Integer 否 缺省 1;<1 按 1 处理 页码,从 1 开始
pageSize Query Integer 否 缺省 20;<1 按 20 处理;>100 按 100 处理 每页条数

出参 Result<PageResult<StaffCandidateRespVO>>

字段 类型 说明
total Integer 符合条件的总人数(只统计在职且未删除的人)
page Integer 实际使用的页码(已按上面的规则处理)
pageSize Integer 实际使用的每页条数(已按上面的规则处理)
records List 当前页候选,字段如下;页码超出范围时为空数组
records[].staffId Long 人员 ID,保存时传给 PUT .../staff 的 staffList[].staffId
records[].staffName String 人员名称(弹窗显示)
records[].staffType String 资源库人员类型:GUIDE / LEADER / PHOTOGRAPHER
records[].staffTypeName String 角色名称(弹窗显示):导游 / 领队 / 摄影
records[].staffPhone String 联系电话(弹窗显示),前 3 后 4 脱敏,如 138****1002;资源库没有手机号时为 null
records[].avatarUrl String 头像 URL,没有头像或头像服务失败时为 null
records[].assigned Boolean 是否已选在本配置位(true 显示为已勾选)
records[].assignedRole String 该人在本团名册里的角色;不在名册时为 null。可能与本配置位不同(例如一个领队被配到了摄影位),可据此提示「已在摄影位」
records[].assignedRoleName String assignedRole 的中文名
records[].reporterRank String 本团名册里的报账人等级:PRIMARY / SECONDARY / NONE;不在名册时为 null(不是 NONE)
records[].reporterRankName String 主报账人 / 次报账人 / 非报账人
records[].productBatchId String 原样返回路径里的 productBatchId
records[].groupBatchId String 运营团期 ID;团期尚未创建时为 null,属正常情况,不要拿它去拼后续请求

请求示例

GET /v3/admin/group-batch/2099751575655247875/staff/candidates/page?role=GUIDE&keyword=李&page=1&pageSize=20
Authorization: Bearer <token>

响应示例

(测试服真实响应,2026-09-16,dev-v3 6ef9458ed)

{
  "code": 200,
  "message": "成功",
  "data": {
    "records": [
      {
        "productBatchId": "2099751575655247875",
        "groupBatchId": "2099751580084420609",
        "staffId": 1002,
        "staffName": "李雪梅",
        "staffPhone": "138****1002",
        "staffType": "GUIDE",
        "staffTypeName": "导游",
        "avatarUrl": null,
        "assigned": false,
        "assignedRole": null,
        "assignedRoleName": null,
        "reporterRank": null,
        "reporterRankName": null
      }
    ],
    "total": 1,
    "page": 1,
    "pageSize": 20
  },
  "success": true
}

空数据 / 降级响应

没有候选人、搜索无结果,或页码超出范围时,返回空的 records,total 为真实总数:

{
  "code": 200,
  "message": "成功",
  "data": { "records": [], "total": 7, "page": 99, "pageSize": 20 }
}

头像服务失败不会导致接口报错,只是对应的 avatarUrl 为 null。

错误响应

{ "code": 582113, "message": "人员配置位不合法,请检查配置位标识", "data": null }
code 触发条件
582113 role 不是 GUIDE / PHOTOGRAPHER(包括不传)
582103 资源服务查询失败(超时、不可用或返回失败)。不会把失败当成空列表返回
589507 当前账号没有团期查看权限(与旧候选端点同码)
非 200 参数校验码 keyword 超过 50 个字符
401 未登录(网关拦截)

业务边界

  • 判权:需要团期查看权限(group-batch:view),与旧候选端点、GET .../staff 一致;没有权限时不查询任何数据。
  • 只读接口,不写库,可重复调用。
  • 只返回资源库中在职(上架)且未删除的人员。导游位的导游和领队在一次查询里完成,同一个人不会重复出现。
  • 关键词只在当前配置位的候选范围内搜索:在导游位搜摄影师不会有结果。
  • 手机号只按尾号匹配:1002 能搜到尾号为 1002 的人,138 这种号段前缀搜不到。
  • assigned 按当前配置位判断,assignedRole / reporterRank 按整个团的名册判断:被配到另一个位的人,在本位里 assigned=false,但 assignedRole / reporterRank 仍有值。这是预期行为,不是 bug。
  • 分页不影响保存语义:PUT .../staff 仍是全量覆盖,见第四节。

四、契约约束与正确调用方式

✅ 正确 / ❌ 错误 调用对照

场景 调用
✅ 打开导游弹窗 GET .../staff/candidates/page?role=GUIDE(缺省第 1 页,每页 20 条)
✅ 按姓名搜索 GET .../staff/candidates/page?role=GUIDE&keyword=李雪
✅ 按手机尾号搜索 GET .../staff/candidates/page?role=GUIDE&keyword=1002
✅ 翻页 GET .../staff/candidates/page?role=GUIDE&keyword=李&page=2&pageSize=20
❌ 不传 role GET .../staff/candidates/page → 582113
❌ 用运营团期 ID 拼路径 GET .../group-batch/{groupBatchId}/staff/candidates/page → 路径参数必须是产品侧 productBatchId
❌ 用 records[].groupBatchId 拼后续请求 团期未创建时该字段为 null,会拼出 /group-batch/null/...

保存时已选名单的正确来源(必须处理)

PUT /v3/admin/group-batch/{productBatchId}/staff 的 staffList 是最终名单,不在里面的人会被删除。分页后请按下面的方式处理:

  1. 弹窗打开时调用 GET /v3/admin/group-batch/{productBatchId}/staff,得到全团已配置名单,作为「已选」初始值;弹窗里的「已选 N 人」按本配置位过滤后计数(导游位统计 staffRole 为 GUIDE / LEADER 的人,摄影位统计 PHOTOGRAPHER)。
  2. 翻页、搜索时,用这份已选名单回显勾选状态;records[].assigned 只能作参考,不能作为唯一依据。
  3. 用户勾选或取消勾选时,只在这份已选名单上增删对应的人。
  4. 保存时把完整的已选名单(包括其他配置位的人,例如在导游弹窗里保存时,也要带上已配置的摄影)一起传给 PUT .../staff。
场景 结果
✅ 已选名单来自 GET .../staff,保存时整份提交 其他页、其他配置位的人都保留
❌ 只提交当前页勾选的人 其他页已选的人被软删,并同步到全团活跃订单

弹窗展示建议字段

展示内容 字段 示例
角色名称 staffTypeName 导游 / 领队 / 摄影
人员名称 staffName 李雪梅
联系电话 staffPhone 138****1002

六、边界行为

  • 未登录 → 网关返回 401。
  • 没有团期查看权限 → 589507,不查询任何数据。
  • role 非法或缺失 → 582113,不调用资源服务。
  • 资源服务不可用 → 582103,不返回空页。
  • 头像服务不可用 → 接口正常返回,avatarUrl 为 null。
  • 页码超出范围 → records 为空数组,total 为真实总数。
  • page / pageSize 不合法(≤0 或过大)→ 按「缺省 1 / 缺省 20 / 最大 100」处理,不报错;实际使用的值在响应的 page / pageSize 里返回。
  • 团期尚未创建(还没有第一个订单成团)→ 接口正常返回,records[].groupBatchId 为 null。
  • 资源库中已下架或已删除的人员不会出现在候选里。

六.5、枚举 / 数据字典

role(配置位,请求参数)

所属字段: role(Query) | 类型: String

值 中文 说明
GUIDE 导游位 候选同时包含资源库的导游(GUIDE)和领队(LEADER)
PHOTOGRAPHER 摄影位 候选只包含资源库的摄影(PHOTOGRAPHER)

staffType(资源库人员类型)

所属字段: records[].staffType | 类型: String

值 中文(staffTypeName) 说明
GUIDE 导游 出现在导游位
LEADER 领队 出现在导游位
PHOTOGRAPHER 摄影 出现在摄影位

reporterRank(报账人等级)

所属字段: records[].reporterRank | 类型: String

值 中文(reporterRankName) 说明
PRIMARY 主报账人 本团名册中唯一
SECONDARY 次报账人 本团名册中唯一
NONE 非报账人 在名册中但不是报账人
null — 该人不在本团名册中

七、不影响范围

  • 仅影响: 新增的分页候选端点。
  • 零影响:
    • 旧候选端点 GET /v3/admin/group-batch/{productBatchId}/staff/candidates(请求和响应都不变)
    • GET /v3/admin/group-batch/{productBatchId}/staff(已配置名单)
    • PUT /v3/admin/group-batch/{productBatchId}/staff(保存,仍为全量覆盖,同步规则不变)
    • PUT .../staff/{staffId}/reporter-rank(报账人设置)
    • 资源服务既有的 /internal/staff/list-available(其他模块仍在使用)
    • 数据库:无表结构变更,无数据迁移

八、测试环境已验证

被测版本:hl-order-service-v3、hl-resource-service = dev-v3 6ef9458ed(2026-09-16 18:40 部署)。此前已先用特性分支 415f4d077 做过同一套验收。验收脚本两轮均 26/26 通过。

GET .../staff/candidates/page?role=GUIDE                        → 200,total=7(= 库中在职导游+领队人数),page=1,pageSize=20 ✓
GET .../staff/candidates/page?role=PHOTOGRAPHER                 → 200,total=1,只含 PHOTOGRAPHER ✓
GET .../staff/candidates/page?role=GUIDE&pageSize=3&page=1/2/3  → 3+3+1 条,拼起来等于全量且无重复 ✓
GET .../staff/candidates/page?role=GUIDE&page=99                → 200,records=[],total=7 ✓
GET .../staff/candidates/page?role=GUIDE&page=0&pageSize=0      → page=1,pageSize=20 ✓
GET .../staff/candidates/page?role=GUIDE&pageSize=500           → pageSize=100 ✓
keyword=李雪 → 李雪梅 ✓   keyword=1002 → 李雪梅(尾号)✓   keyword=10 → 只有尾号为 10 的乌日娜 ✓
keyword=138 → 0 条 ✓      keyword=% → 0 条 ✓      keyword=_ → 0 条 ✓      keyword=(空格) → 7 条 ✓
导游位 keyword=王强 → 0 条;摄影位 keyword=王强 → 王强 ✓
keyword 51 个字符                                               → 400「关键词不能超过50个字符」✓
role=DRIVER                                                     → 582113 ✓
CUSTOMIZER 角色调用新端点 / 旧端点                              → 589507 / 589507 ✓
未登录                                                          → 401 ✓
pageSize=100 与旧端点 GET .../staff/candidates 同一批人         → 7 条逐字段一致 ✓
名册中的刘大山 → assigned=true,assignedRole=LEADER,reporterRank=NONE ✓
Feign 超时注入(1ms)                                           → 582103(配置已还原)✓

验证团期:productBatchId=2099751575655247875(名册中有刘大山)、2099751542897733635(名册中有王强)。


十、相关文档

  • 关联 Issue: wx/HL#7827
  • 关联 PR: wx/HL#7831
  • 后续计划: 前端切换到分页端点后,另开工单下线旧候选端点 GET .../staff/candidates

关联 / 联系人

链接

联系人

  • 后端负责人: @jw