--- schema: "hl-changelog/v2" ticket: "8252" title: "订单列表新增团期关键词筛选,团单行新增团期名与期次展示字段" consumer: "admin" author: "wx(GIT)" change_type: "修改接口" backend_status: "deployed" gateway_status: "verified" frontend_status: "verified" frontend_owner: "mmg" frontend_ref: "5f27c0094745c60cde7de866bb3450eee61fd91d" target_release: "" verified_at: "2026-09-23" status_note: "GET /v3/admin/order 新增入参 groupBatchKeyword(长度上限 64),出参 OrderListItemRespVO 新增 groupBatchName 与 groupBatchLabel。前端需在订单列表增加团期关键词筛选,并在团单行产品名后渲染「第N期 团期名」,故 frontend_status 记 pending。前端已交付(2026-09-23):列表头新增「团期」关键词输入框原样透传 groupBatchKeyword(maxlength 64 输入侧截断;清空省略=不传;六页签计数同口径透传);ProductCell 产品名后渲染「第N期 团期名」,label/name 逐段 trim 判空省略、全空整行不渲染,判团单仍用 groupOrder/groupBatchId。新建 spec 8 例+存量 6 例回归全绿,checkpoint 全量含生产构建通过。" updated_at: "2026-09-23" base: "dev-v3" --- # 订单列表团期筛选与团单行团期展示(管理后台) > **服务**: hl-order-service-v3 > **PR**: #8261 > **Issue**: #8252 > **日期**: 2026-09-23 > **合并提交**: b6e84bdd9 > **影响范围**: 管理后台订单列表的筛选条件(新增团期关键词)与团单行展示(新增团期名 + 期次) --- ## 一、接口背景 订单列表原本只支持按 `groupBatchId` 筛团期,而那是 19 位雪花 ID:运营在团期看板上看到的是「第3期 10月8日出发团」这样的文案, 记不住、也没法粘贴到订单列表去筛。团期看板自己支持按团期名 / 期次搜索,订单列表不支持,同一句话在两个页面搜出不同结果。 本次给订单列表补上同样口径的团期关键词筛选,并把团期名与期次直接补到团单行上,运营从看板跳到订单列表后能直接看到自己在看哪个团期。 ## 二、变更接口清单 | # | 接口 | 方法 | 路径 | 变更类型 | 说明 | |---|---|---|---|---|---| | 1 | 管理后台订单列表 | GET | `/v3/admin/order` | 修改接口 | 入参新增 `groupBatchKeyword`;出参 `OrderListItemRespVO` 新增 `groupBatchName` 与 `groupBatchLabel`;其余入参、其余出参、权限码、错误码均未变 | > 同一个 Controller 方法同时挂载集合根与 `/v3/admin/order/list` 两条路径(`@GetMapping({"", "/list"})`), > 两个地址的入参、出参、错误码、数据权限完全一致,本条目统称「订单列表接口」。 ## 三、接口详情 ### 1. 管理后台订单列表 `GET /v3/admin/order` **VO**: `OrderListItemRespVO` #### 使用场景 管理后台「订单管理」列表页:首次进入、翻页、切换六个页签、修改任意筛选项时调用。 本次与该页相关的两件事:列表头部新增「团期」关键词输入框(按团期名 / 期次 / 团号搜)。 团单行的产品名后面渲染「第N期 团期名」,数据由本接口出参的 `groupBatchName` 与 `groupBatchLabel` 提供。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |---|---|---|---|---|---| | groupBatchKeyword | Query | String | 否 | 长度 ≤64;全角空格与 NBSP 归一后为空串等同不传;groupBatchId 非空时本参数被忽略 | **本次新增**。团期关键词:按「团号模糊 ∪ 团期名模糊 ∪ 期次精确」解析出命中的团期后,只返回挂在这些团期下的订单。支持照抄页面渲染的文案,如 `第3期 10月8日出发团`。非空时强制按团单口径筛(等价于 orderKind=GROUP) | | groupBatchId | Query | Long | 否 | 19 位正整数 | 运营团期 ID(既有字段,本次未改)。非空时本参数优先:groupBatchKeyword 被忽略,且强制按团单口径筛 | | orderKind | Query | String | 否 | ALL / GROUP / NORMAL;缺省 NORMAL | 订单归属类型(既有字段,本次未改)。传了 groupBatchId 或非空 groupBatchKeyword 时,本参数被后端的团期口径覆盖为 GROUP | | pageNo | Query | Integer | 否 | ≥1,缺省 1 | 页码(既有字段)。等价别名 page,两者写哪个都生效 | | pageSize | Query | Integer | 否 | 1~100,缺省 20 | 每页条数(既有字段) | | statusGroup | Query | String | 否 | ALL / BEFORE_TRIP / ON_TRIP / SETTLEMENT / ABNORMAL / AFTERSALE | 页签分组(既有字段)。与 orderStatus 下拉为 AND 关系 | | orderStatus | Query | String | 否 | PENDING_PAY / CUSTOMIZING / PENDING_DEPARTURE / TRAVELLING / COMPLETED / CANCELLED,多值用逗号 | 粗状态过滤(既有字段) | | flowStatus | Query | String | 否 | 细状态枚举,多值用逗号 | 细状态过滤(既有字段) | | keyword | Query | String | 否 | 无 | 通用关键词:团号 / 客户姓名 / 产品名 / 订单号 任一 LIKE(既有字段,与本次的团期关键词是两个独立参数) | | tagNames | Query | List<String> | 否 | 多标签 OR,任一命中即返 | 标签过滤(既有字段) | | consultantName | Query | String | 否 | LIKE 匹配 | 定制师姓名(既有字段) | | departureDateFrom | Query | Date | 否 | yyyy-MM-dd | 出发日期区间起(既有字段) | | departureDateTo | Query | Date | 否 | yyyy-MM-dd,早于起日返 100001 | 出发日期区间止(既有字段) | | createSource | Query | String | 否 | CONSULTANT / CUSTOMER / 其他来源枚举 | 订单来源过滤(既有字段) | | cancelled | Query | Boolean | 否 | 缺省 false | 是否包含已取消订单(既有字段) | #### 出参 `Result>` | 字段 | 类型 | 说明 | |---|---|---| | records | Array | 订单行列表,元素结构见下方「行内字段」 | | total | Integer | 命中总条数(不受 pageSize 影响) | | page | Integer | 当前页码 | | pageSize | Integer | 每页条数 | | records[].groupBatchName | String | **本次新增**。团期名称快照(取自下方「业务边界」所述的下单时快照口径)。非团单与团期已软删(解散)两种情况均为 null,不返空串 | | records[].groupBatchLabel | String | **本次新增**。期次序号裸数字串,如 `"3"`,不含「第」「期」二字,由前端拼成「第3期」。存量未刷新快照的团期可能为 null | | records[].groupBatchId | Long | 既有字段,本次未改。运营团期 ID(JSON 中为字符串)。非空即团单,这是判别团单的唯一依据 | | records[].groupOrder | Boolean | 既有字段,本次未改。是否团单,与 groupBatchId 同源(groupBatchId != null 的派生值) | | records[].productName | String | 既有字段,本次未改。产品名快照。「第N期 团期名」渲染在产品名之后 | | records[].orderNo | String | 既有字段,本次未改。订单号(创单瞬间生成,永不变) | | records[].id | Long | 既有字段,本次未改。订单 ID(JSON 中为字符串) | | records[].customerName | String | 既有字段,本次未改。客户姓名 | | records[].departureDate | Date | 既有字段,本次未改。出发日,未定日期时为 null | | records[].orderStatus / orderStatusName | String | 既有字段,本次未改。粗状态枚举值与中文名 | | records[].flowStatus / flowStatusName | String | 既有字段,本次未改。细状态枚举值与中文名 | | records[].totalAmount / payableAmount / paidAmount / refundAmount / balanceAmount | BigDecimal | 既有字段,本次未改。金额在 JSON 中为字符串 | | records[].consultantName | String | 既有字段,本次未改。定制师姓名 | > 单行 VO 本次从 44 个字段变为 46 个:新增 `groupBatchName`、`groupBatchLabel`,其余 44 个字段逐字未变。 #### 请求示例 ```http GET /v3/admin/order?pageNo=1&pageSize=20&orderKind=ALL&groupBatchKeyword=%E7%AC%AC3%E6%9C%9F+10%E6%9C%888%E6%97%A5%E5%87%BA%E5%8F%91%E5%9B%A2 HTTP/1.1 Host: api.test.1814.love Authorization: Bearer Accept: application/json # 无请求体。 # 关键词解码后为「第3期 10月8日出发团」;等价别名地址:GET /v3/admin/order/list(参数与响应逐字一致)。 # 只按团期名筛:GET /v3/admin/order?pageNo=1&pageSize=20&groupBatchKeyword=%E4%BA%91%E5%8D%97 ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "records": [ { "id": "2102309919002943489", "orderNo": "HL20260922161158291", "teamNo": "26-2355", "productName": "王骁测试团期产品", "productType": "GROUP", "productTypeName": "小蒙马", "productCoverImg": "https://hlgl-test.oss-cn-beijing.aliyuncs.com/test/material/2026/03/07/ed278d1da74c3827cb06b7f0cf6091fe.jpg", "tierName": "轻奢", "customerName": "王二麻子", "customerPhoneMasked": "185****0000", "peopleSummary": "4 大", "departureDate": "2026-10-08", "tripDays": 3, "tripNights": 2, "orderStatus": "CUSTOMIZING", "orderStatusName": "定制中", "flowStatus": "AWAITING_PROFILE", "flowStatusName": "待补全信息", "flowStep": 1, "flowStepTotal": 6, "flowStepCode": "PROFILE", "flowStepName": "补全信息", "currentSubFlows": null, "totalAmount": "15920.00", "payableAmount": "15920.00", "paidAmount": "2000.00", "refundAmount": "0.00", "balanceAmount": "13920.00", "consultantName": "刘畅", "createSource": "CONSULTANT", "createSourceLabel": "定制师创建", "tags": [], "createdAt": "2026-09-22 16:11:58", "depositAmount": "2000.00", "depositRatio": null, "paymentMode": "DEPOSIT", "singleRoomSurcharge": "0.00", "agencyId": "2051922156798779394", "refundPolicyId": "2047249965792579585", "productSubtitle": "发短信给对方搞定", "aftersaleStatus": "NONE", "aftersaleStatusName": "无售后", "groupBatchId": "2100856430494973953", "groupOrder": true, "groupBatchName": "10月8日出发团", "groupBatchLabel": "3" } ], "total": 2, "page": 1, "pageSize": 5 }, "traceId": null, "success": true } ``` #### 空数据 / 降级响应 两种空值形态语义不同,前端要分别处理。 其一:关键词没有任何命中团期时,接口返 200 与空页(`records: []`、`total: 0`),同时六个页签的计数一并归零——列表数字与页签数字不会各说各话。 ```json { "code": 200, "message": "成功", "data": { "records": [], "total": 0, "page": 1, "pageSize": 5 }, "traceId": null, "success": true } ``` 其二:行本身存在,但该行拿不到团期信息。非团单行、以及团期已软删(解散)的团单行,`groupBatchName` 与 `groupBatchLabel` 都是 `null`(不是空串)。 ```json { "code": 200, "message": "成功", "data": { "records": [ { "id": "2097270761474469890", "orderNo": "HL20260908182809530", "productName": "测试小蒙马-多档-固定金额", "orderStatus": "CUSTOMIZING", "groupBatchId": "2097270759599616002", "groupOrder": true, "groupBatchName": null, "groupBatchLabel": null } ], "total": 3, "page": 1, "pageSize": 5 }, "traceId": null, "success": true } ``` #### 错误响应 ```json { "code": 100001, "message": "团期关键词长度不能超过 64", "data": null, "traceId": null, "success": false } ``` | 错误码 | 触发条件 | |---|---| | 100001 | groupBatchKeyword 超过 64 个字符(注意:不是 HTTP 400,是业务码 100001,HTTP 状态仍为 200) | | 100001 | groupBatchId 传了非数字(既有行为,本次未改) | | 100001 | pageNo 小于 1、pageSize 超出 1~100、orderKind 传非法值、出发日区间倒置 | | 401 | 未登录(网关拦截) | | 403 | 缺少管理端订单列表访问权限 | #### 业务边界 - **团期名与期次都取自 order 侧快照**:`groupBatchName` / `groupBatchLabel` 读的是订单侧记录的团期快照,而团期看板展示的是 product 侧实时重算值;快照只在该团期有新订单进入时刷新,**两者可能不一致**。这是两个页面各自的既定口径,不是本接口的缺陷。 - **`groupBatchLabel` 可能为 null**:存量未刷新的团期拿不到期次,此时只渲染团期名,**不得渲染「第null期」「第期」**。`groupBatchName` 同理,为 null 时整段不渲染。 - **非团单与「团期已软删(解散)」两种情况下两字段均为 null**(不是空串);若团期名本身是空字符串的脏数据,则返回 `""`,前端需按空文案处理而不是渲染出「第N期 」这样带空格残缺文案。 - **软删团期的历史订单仍能按团单筛出来**(`groupOrder=true`、`groupBatchId` 非空),只是团期名与期次为 null——这是既定行为,前端不要据此把行判成普通订单。 - **本接口的 `groupBatchName` 是团期名快照,与下单接口(订单创建响应)里那个恒为 null 的 `groupBatchName` 不是同一个语义**,不要把两个接口的字段名合并成同一个前端模型复用。 - **关键词口径与团期看板一致**:团号 LIKE ∪ 团期名 LIKE ∪ 期次精确匹配,且支持「第3期 云南」这类复合输入(期次精确 AND 名称/团号模糊)。期次是精确匹配,「第1期」不会连带命中第 10、11、21 期。 - **命中团期超过 500 个时结果被截断**(只取前 500 个团期对应的订单),属公开的失败模式:关键词过宽时 total 会偏小。 - **命中为空时返回空页且六个页签计数同步为 0**;不传 `groupBatchKeyword` 时,本接口行为与本次改动前完全一致(含只传一个全角空格或 NBSP 的情况,逐项等同不传)。 - **权限码零变化**:定制师等非管理员角色仍只看本人名下订单,权限口径与本次改动前逐字一致,访问本接口不会出现 589507。 - **已知差异(契约边界)**:某些团期名本身形如「3期特惠」时,团期看板按整串匹配,而订单列表会把它解析成「期次 3 + 名称 特惠」,因此同一个输入在两个页面可能给出不同结果。 ## 四、契约约束与正确调用方式 ### ✅ 正确 / ❌ 错误 payload 对照 | 场景 | 查询串 | |---|---| | ✅ 按团期名筛 | `groupBatchKeyword=10月8日出发团`,返回挂在该团期下的订单 | | ✅ 按期次筛 | `groupBatchKeyword=第3期`,期次精确匹配,不含第 10、11、21 期 | | ✅ 复合输入(照抄页面文案) | `groupBatchKeyword=第3期 云南深度8日`,拆成「期次 = 3」且「名称含 云南深度8日」 | | ✅ 只填一个全角空格 | `groupBatchKeyword=%E3%80%80`,等同于不传,行为与改动前逐项一致 | | ✅ 精确跳转(从团期看板带 ID 跳过来) | `groupBatchId=2100856430494973953` | | ❌ 两个参数都传且互不匹配 | `groupBatchId=<团期ID>&groupBatchKeyword=不存在的团期名` → 以 ID 为准,关键词被忽略 | | ❌ 关键词超过 64 字符 | 65 个字符 → 100001「团期关键词长度不能超过 64」 | | ❌ 把期次写成中文数字 | `groupBatchKeyword=第三期` → 不是期次形态,按团期名/团号整串模糊匹配,通常零命中 | ### 前端渲染规则 - 团单行文案 = 产品名 + 「第{groupBatchLabel}期 {groupBatchName}」,任一段为 null 或空串时该段整段省略,不要留下多余空格。 - 判断一行是不是团单请继续读 `groupOrder` / `groupBatchId`,不要用 `groupBatchName != null` 反推(软删团期的团单两字段为 null)。 - 筛选框可以同时展示「团期名」与「第N期」两种写法;提交时把用户输入原样传给 `groupBatchKeyword` 即可,后端负责解析。 - 筛选框在提交前建议做一次前端长度校验(≤64),避免让用户提交后只拿到一个 100001。 ## 五、数据库行为 零数据库变更。本次不新增表、列、索引或数据迁移,也不写入任何数据: `groupBatchKeyword` 的匹配是对既有团期数据的只读查询,出参两字段是对既有订单侧快照的只读回填,整页一次批量查询,不产生写操作。 ## 六、边界行为 - 未登录 → 401(网关拦截)。 - 无管理端权限 → 403。 - `groupBatchKeyword` 超过 64 字符 → 业务码 100001,HTTP 状态仍是 200。 - `groupBatchId` 传非数字 → 业务码 100001(既有行为)。 - 关键词无命中团期 → 200 + 空页,且六个页签计数全为 0。 - 班期 / 团期数据在 order 侧无快照(非团单、软删团期)→ 两个新字段为 null,不返空串。 - 团期名本身是空字符串的脏数据 → 返回 `""`。 - 排期区间倒置(departureDateFrom > departureDateTo)→ 100001。 - pageSize 超过 100 → 100001。 ## 六.6、修改前后对比 ### 字段级对比 | 字段 | 改前 | 改后 | |---|---|---| | `OrderListReqVO.groupBatchKeyword` | 不存在 | 新增,String,≤64,Query 参数 | | `OrderListItemRespVO.groupBatchName` | 不存在(前端取到 undefined) | 返回团期名快照;非团单 / 软删团期为 null | | `OrderListItemRespVO.groupBatchLabel` | 不存在(前端取到 undefined) | 返回期次裸数字串,如 `"3"`;存量未刷新快照为 null | | 单行 VO 字段数 | 44 | 46 | | 其余 44 个出参字段 | — | 逐字未变 | | 其余 15 个入参字段 | — | 逐字未变(新增参数是新增项,不改既有字段语义) | ### 行为级对比 | 行为 | 改前 | 改后 | |---|---|---| | 按团期名 / 期次筛选 | 不支持,只能传 19 位 groupBatchId | 新增 groupBatchKeyword,口径与团期看板一致 | | 期次匹配方式 | — | 期次精确匹配(第1期不命中第 10、11、21 期) | | 两个团期参数同传 | 不存在第二个参数 | 以 groupBatchId 为准,关键词被忽略 | | 空白关键词 | — | 归一后为空等同于不传,行为与改动前逐项一致 | | 关键词无命中 | — | 空页 + 六页签计数全 0 | | 团单行展示 | 只有 groupBatchId / groupOrder | 追加 groupBatchName / groupBatchLabel 供渲染「第N期 团期名」 | | 权限码 / 数据权限 | 仅 ADMIN / SUPER_ADMIN 看全量,其余角色只看本人订单 | 逐字未变 | ## 六.7、影响评估 - **是否破坏向后兼容**:否。入参是新增的可选参数,出参是新增的可选字段;不传关键词、不读新字段的老调用方行为逐字未变。 - **前端是否必须同步上线**:否,但需要适配:列表要加团期关键词筛选入口,团单行要渲染团期名 + 期次。老前端不改也不会报错,只是看不到本次的两个能力。 - **性能**:团期关键词的匹配落在团期名 / 期次的模糊查询上,命中团期条数有 500 的上限,超过即截断;订单侧仍是既有的分页查询,团期名与期次按页一次批量回填,不会按行查询。 - **前端 workaround 清理点**:若前端此前为了显示团期名而在列表页额外调用团期接口或按 groupBatchId 反查,可改用本接口的两个新字段;无此类逻辑则无需改动。 - **回滚**:撤销 PR #8261 即可,无数据、无迁移、无配置残留。 - **未覆盖**:本次只证明后端契约可用。页面上的筛选框与团期文案是否落位属前端实现侧;两个页面对同一输入的差异见「业务边界」末条。 ## 七、不影响范围 - **仅影响**:管理后台订单列表(筛选条件 + 团单行字段)。 - **零影响**: - 团期看板的搜索口径与命中结果(本次只新增解析口,未改看板既有语义) - 订单详情的团期四字段(batchNo / batchName / groupBatchStatus / groupBatchId) - 下单接口(订单创建响应)的同名字段 - 六个页签计数的计算口径(关键词无命中时同步归零属既定一致行为) - 权限码与数据权限收缩逻辑 - 小程序端:本接口仅管理后台使用,未涉及 ## 八、测试环境已验证 部署:hl-order-service-v3 `dev-v3 @ b6e84bdd9`(PR #8261 合并提交),2026-09-23 16:23~16:24 滚动部署完成,两实例健康。 验证入口:`https://api.test.1814.love`(经网关调用,非直连服务端口)。 自动化验收脚本 19 项全部 PASS,摘要如下: | 用例 | 场景 | 结果 | |---|---|---| | AC-1 | 按团期名 `10月8日出发团` 筛 | 200,total=2,两行 groupBatchName 均为「10月8日出发团」 | | AC-2 | 按期次 `第3期` 筛 | 200,total=2,命中期次集合 = ['3'](精确,不含 10 / 13 / 23 / 30) | | AC-3 | 按期次 `第1期` 筛 | 200,total=13,命中期次集合 = ['1'](不含 10 / 11 / 21) | | AC-4 | 裸数字 `3` 筛 | 200,total=406;第3期的 2 单是其中子集 | | AC-5 | 复合输入 `第3期 10月8日出发团` | 200,total=2,期次 = ['3'] | | AC-6 | 无命中 `不存在的团期名ZZZ9` | 200,空页,total=0 | | AC-7 | 页签计数与列表同口径 | 命中时 ALL=2、其余页签 0;无命中时六个页签全 0 | | AC-8 | 只填空格 vs 完全不传 | total 同为 708,首行订单号逐字一致 | | AC-9 | 定制师身份(CUSTOMIZER) | 200 而非 589507;不带关键词时 total=19 且定制师列只含本人 | | AC-10 | 65 字符关键词 | 100001「团期关键词长度不能超过 64」 | | AC-11 | 只传 groupBatchId | 200,total=2 | | AC-12 | 两参同传 | 与只传 ID 同结果,orderNo 序列一致 | | AC-13 | 团期看板口径回归 | 三组关键词的命中团期集合与基线一致 | | AC-14 | 团单行展示字段 | orderNo=HL20260922161158291,groupBatchName=10月8日出发团,groupBatchLabel=3 → 可渲染「第3期 10月8日出发团」 | | AC-15 | 团单 300 行扫描 | 无空串残缺值;groupBatchLabel 为 null 的行数为 0 | | AC-17 | 普通单 220 行 | 两个新字段全部为 null(非空串) | | AC-18 | 同订单不同 pageSize 一致性 | pageSize=1 与 20 的 groupBatchName 一致;页级一次批量回填(单测 times(1),逐行查询 never) | | AC-19 | 深翻页 | 第 2、3 页各 20 行均带团期名 | | AC-20 | 订单详情回归 | 团期四字段与基线逐字一致 | 在此之上补一组经网关的真实请求取证,覆盖脚本未包含的软删场景: | 用例 | 场景 | 结果 | |---|---|---| | AC-16 | 团期已软删(解散)的历史订单,`groupBatchId=2097270759599616002` | 200,total=3;三行 groupOrder=true,groupBatchName 与 groupBatchLabel 均为 null | ## 十、相关文档 - 关联 Issue: [wx/HL#8252](https://git.1814.love:8443/wx/HL/issues/8252) - 关联 PR: [wx/HL#8261](https://git.1814.love:8443/wx/HL/pulls/8261) - 团期看板关键词口径出处:#7942 - 订单列表 groupBatchId 冻结契约出处:#7635 ## 关联 / 联系人 ### 链接 - **Issue**: [#8252](https://git.1814.love:8443/wx/HL/issues/8252) - **PR**: [#8261](https://git.1814.love:8443/wx/HL/pulls/8261) - **Merge commit**: [b6e84bdd9](https://git.1814.love:8443/wx/HL/commit/b6e84bdd9) ### 联系人 - **后端负责人**: @wx