11 条有业务交付改判 verified(#6397/6903/6904/6905/6950/6979/6986/7013/7029/7036/7066,owner=mmg+对应业务 commit ref+交付日 verified_at); 6 条实证零改动改判 not_required(#6014/6016/6140/6938/6842/7087,仅翻 frontend_status 不填 owner/ref)。 #5935 挂起待后端补字段,保持 pending 不动。sync-log 均已记账。
14 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 | 6986 | 团期物资支持从备品库选择配置 | admin | jw(GIT) | 新增接口 | deployed | verified | verified | mmg | 4d82bdb5 | 2026-09-03 | PR #6987 已合入 dev-v3;2026-09-03 经测试环境网关实测,10 条正负向用例全部通过。前端尚未接入 | 2026-09-03 | dev-v3 |
团期物资: 新增备品库候选列表接口,并放宽 suppliesName 必填约束
服务: hl-order-service-v3 + hl-resource-service(跨两个服务) PR: #6987 Issue: #6986 影响范围: 管理后台「团期详情 → 物资清单 → 从备品库选择」
⚠️ 关键变化
新增物资行 POST /v3/admin/order/group-batch/:groupBatchId/supplies 的 suppliesName 不再无条件必填。
- 前端以前以为:
suppliesName是@NotBlank,任何情况下都得填。 - 实际现在是:改为
@AssertTrue二选一——传了suppliesResourceId就可以不填名称 (名称、分类、计费方式从备品库取,入参传了也会被库值覆盖);不传suppliesResourceId时名称仍必填。 - 这解决的是原先「从备品库选一条加进来,却仍被迫手填名字」与「名称以库为准」自相矛盾的问题。
部署要求:本次跨 order-v3 与 resource-service 两个服务,必须同时部署。
只发 order-v3 会调不到资源域新增的 /internal/supplies/list-available,候选列表直接报 589518。
一、背景
物资清单原先只能手工逐条录入,名称、分类、计费方式、单价全靠人填,既慢又容易与备品库口径不一致。 本次接入资源域备品库:团期管理员从库里勾选,基础信息由库带出。
资源域改动:新增 GET /internal/supplies/list-available(内部接口,不对外)。
order-v3 侧通过 Feign 调用,并带降级工厂。
二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 团期物资候选列表 | GET | /v3/admin/order/group-batch/:groupBatchId/supplies/candidates |
新增接口 | 从备品库拉可选备品,标记本团期已加入项 |
| 2 | 新增团期物资行 | POST | /v3/admin/order/group-batch/:groupBatchId/supplies |
请求体校验放宽 | suppliesName 由必填改为与 suppliesResourceId 二选一 |
三、接口详情
1. 团期物资候选列表 GET /v3/admin/order/group-batch/:groupBatchId/supplies/candidates
VO: SuppliesCandidateRespVO
使用场景
「物资清单 → 从备品库选择」弹窗打开时调用,渲染可选备品列表。
added=true 的项应显示为已勾选,其 batchSuppliesId 即清单中对应行的 ID,可直接用于删除。
入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
groupBatchId |
Path | Long | ✅ | — | 团期主订单 ID |
categoryCode |
Query | String | ❌ | 字典 supplies_category |
分类筛选。传不存在的分类返回空数组,不报错 |
keyword |
Query | String | ❌ | — | 关键字,匹配备品名称或副标题。中文需 URL 编码 |
出参 Result<List<SuppliesCandidateRespVO>>
| 字段 | 类型 | 说明 |
|---|---|---|
suppliesResourceId |
Long | 备品资源 ID。加入清单时回传本值 |
suppliesName |
String | 备品名称(来自库) |
subtitle |
String | 副标题 |
category |
String | 分类编码 |
billingType |
String | 计费方式,已归一为产品域词表 PER_PERSON / PER_QUANTITY |
hasCost |
Boolean | 是否计费(资源域 isCharged=1 时为 true) |
unitPrice |
BigDecimal | 基础价,加入清单时作为 unitPrice 默认值 |
unit |
String | 计量单位 |
added |
Boolean | 是否已加入本团期清单,true 时前端应显示为已勾选 |
batchSuppliesId |
Long | 已加入时对应的清单行 ID;未加入为 null |
请求示例
GET /v3/admin/order/group-batch/1/supplies/candidates?categoryCode=personal_gear
响应示例
{
"code": 200,
"message": "成功",
"success": true,
"data": [
{
"suppliesResourceId": "2023438566624866305",
"suppliesName": "定制遮阳帽",
"subtitle": "呼籁旅行专属防晒鸭舌帽",
"category": "personal_gear",
"billingType": "PER_PERSON",
"hasCost": true,
"unitPrice": 15.00,
"unit": "顶",
"added": false,
"batchSuppliesId": null
}
]
}
空数据 / 降级响应
备品库无匹配项(含分类不存在、关键字无命中)时返回空数组,不报错:
{
"code": 200,
"message": "成功",
"success": true,
"data": []
}
资源域不可达时不静默降级为空,而是显式报 589518,避免前端误判为「库里没有备品」。
错误响应
备品库查询失败(资源域未部署或不可达):
{
"code": 589518,
"message": "备品库查询失败,请稍后重试",
"success": false,
"data": null
}
业务边界
- 只读接口,不产生任何写入,可安全重复调用
- 资源域只返回可用备品,已下架的不在候选池
billingType在 order-v3 侧做过归一,前端不必再兼容资源域原始词表added依据suppliesResourceId匹配;纯手填(无 resourceId)的清单行不会影响任何候选项的勾选态- 同一备品在清单中重复存在时,
batchSuppliesId取首条
2. 新增团期物资行 POST /v3/admin/order/group-batch/:groupBatchId/supplies
VO: AddSuppliesReqVO
使用场景
两种来源共用本接口:从备品库勾选加入(传 suppliesResourceId),或手工录入临时物资(传 suppliesName)。
入参
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
groupBatchId |
Path | Long | ✅ | — | 团期主订单 ID |
suppliesResourceId |
Body | Long | ❌ | 与 suppliesName 二选一 |
备品资源 ID。传了则名称/分类/计费方式从库取 |
suppliesName |
Body | String | ❌ | 与 suppliesResourceId 二选一 |
变更前 @NotBlank 无条件必填;变更后传了 resourceId 时可空,且传了也会被库值覆盖 |
quantity |
Body | Integer | ✅ | @NotNull @Min(1) |
数量 |
category |
Body | String | ❌ | — | 分类,传 resourceId 时以库为准 |
hasCost |
Body | Boolean | ❌ | — | 是否计费 |
billingType |
Body | String | ❌ | — | 计费方式 |
unitPrice |
Body | BigDecimal | ❌ | — | 单价,缺省取库基础价 |
sortOrder |
Body | Integer | ❌ | — | 排序 |
remark |
Body | String | ❌ | — | 备注 |
出参 Result<Long>
| 字段 | 类型 | 说明 |
|---|---|---|
data |
Long | 新建清单行 ID(batchSuppliesId),可直接用于删除或调整数量 |
请求示例
{
"suppliesResourceId": 2023438566624866305,
"quantity": 2
}
响应示例
{
"code": 200,
"message": "成功",
"success": true,
"data": "2095411540751519745"
}
空数据 / 降级响应
本接口必然返回行 ID 或错误,无空数据形态。校验失败时 data 为 null 且 success=false。
错误响应
名称与备品库 ID 都没传(本次新增的二选一校验):
{
"code": 400,
"message": "备品名称不能为空(未指定备品库 ID 时必填)",
"success": false,
"data": null
}
所选备品不存在或已下架:
{
"code": 589519,
"message": "所选备品不存在或已下架,请重新选择",
"success": false,
"data": null
}
业务边界
- 二选一校验在
@AssertTrue阶段完成,失败时数据库无任何变更 - 传了
suppliesResourceId时,入参里的suppliesName/category/billingType会被库值覆盖,不是「以入参优先」 quantity最小为 1,传 0 或负数返回 400- 同一备品可重复加入,接口不做去重
- 纯手填行(无
suppliesResourceId)不会出现在候选列表的added判定中
四、契约约束与正确调用方式
- 加入备品库物资时,只需传
suppliesResourceId和quantity。不要再从候选列表把名称回填进请求体—— 传了也会被库值覆盖,徒增不一致风险。 - 手工录入临时物资时,
suppliesName仍然必填。二选一不等于两个都可以不传。 - 勾选态用
added判断,删除用batchSuppliesId。候选列表已经把行 ID 回填好,无需再查一次清单。 keyword含中文必须 URL 编码,否则查询条件丢失(实测未编码时请求异常)。- 589518 与「空数组」含义不同:前者是资源域挂了,后者是库里确实没有匹配项。前端不应把 589518 展示成「暂无备品」。
- 589518 / 589519 是业务码,HTTP 状态仍为 200,判断成败要读响应体的
code。
五、数据库行为
仅描述外部可观察行为:
- 接口 1(GET candidates)只读,不产生任何写入。
- 接口 2(POST supplies)新增一条团期物资清单行并返回其 ID;传
suppliesResourceId时, 名称、分类、计费方式、单价默认值取自备品库快照,落库后不再随备品库变动。 - 二选一校验与数量校验均发生在写入之前,校验失败时数据库无任何变更(已实测复核)。
- 本次变更不涉及表结构调整,也不对存量物资行做迁移或回填。
六、边界行为
- 资源域未部署 / 不可达 → 589518,不降级为空数组
- 备品库无匹配 → 返回
[],不报错 categoryCode传不存在的分类 → 返回[]- 备品库 ID 不存在或已下架 → 589519
- 既无
suppliesName又无suppliesResourceId→ 400 quantity为 0、负数或缺失 → 400- 未登录 → 401(网关拦截)
六.5、枚举 / 数据字典
billingType(已归一为产品域词表)
| 取值 | 含义 |
|---|---|
PER_PERSON |
按人计费 |
PER_QUANTITY |
按数量计费 |
order-v3 侧已对资源域原始词表做过归一,前端直接消费这两个值即可。
category(字典 supplies_category)
分类编码由字典维护,如 personal_gear(个人装备)、camping_equipment(露营装备)等。
本接口不校验分类是否存在,传未知值返回空数组。
六.6、错误码
段位 589500-589599,owner hl-order-service-v3(GroupBatchErrorCode)。
| 码 | 符号 | 消息 | 触发 |
|---|---|---|---|
589518 |
SUPPLIES_LIBRARY_FETCH_FAILED |
备品库查询失败,请稍后重试 | 本次新增。资源域 list-available 返回失败或空结果对象 |
589519 |
SUPPLIES_RESOURCE_NOT_AVAILABLE |
所选备品不存在或已下架,请重新选择 | 本次新增。suppliesResourceId 在库中查不到 |
400 |
— | 备品名称不能为空(未指定备品库 ID 时必填) | 本次新增。二选一校验未通过 |
七、不影响范围
- 仅影响:管理后台团期详情的「物资清单」模块
- 零影响:
- 已有的物资清单查询、调整数量、删除行、确认物料四个接口的出参
- 存量团期物资行数据(不迁移,新校验只在下次新增时触发)
- 资源域备品库自身的管理接口(本次只新增一个内部只读端点)
- 团期状态机与物料确认闸门
八、测试环境已验证
✅ 已验证。 2026-09-03 于测试环境网关实测,真实鉴权(管理端 admin 账号)。
- 网关:
https://api.test.1814.love:9443,分支dev-v3 - 前置确认:order-v3 与 resource-service 两侧均已部署(候选列表能返回资源域真实数据即为证)
| # | 用例 | 期望 | 实测 |
|---|---|---|---|
| 1 | GET candidates 无筛选 | 200,返回候选 | ✅ 200,15 条 |
| 2 | ?categoryCode=personal_gear |
200,按分类收窄 | ✅ 200,5 条 |
| 3 | ?keyword=帽(URL 编码) |
200,按名称匹配 | ✅ 200,1 条「定制遮阳帽」 |
| 4 | ?categoryCode 传不存在分类 |
200 空数组 | ✅ 200,0 条 |
| 5 | GET candidates 无 Authorization | 401 | ✅ 401 缺少有效的 Authorization 头 |
| 6 | POST 既无名称也无 resourceId | 400 | ✅ 400 备品名称不能为空(未指定备品库 ID 时必填) |
| 7 | POST quantity=0 |
400 | ✅ 400 数量至少为 1 |
| 8 | POST 缺 quantity |
400 | ✅ 400 数量不能为空 |
| 9 | POST suppliesResourceId 不存在 |
589519 | ✅ 589519 所选备品不存在或已下架,请重新选择 |
| 10 | POST 只传 resourceId 不传名称 | 放行,名称取库值 | ✅ 200,落库 suppliesName="定制遮阳帽" 与库一致,unitPrice=15.00、billingType=PER_PERSON |
行为变更核对:用例 10 是本次核心——入参未传 suppliesName 仍成功建行,名称由备品库带出。
联动核对:加入后重查候选列表,该项 added=true、batchSuppliesId 回填为新建行 ID;删除后回到 added=false。
未落库核对:用例 6–9 执行后复查清单仍为 0 条,证明校验失败不产生写入。
测试数据还原:用例 10 建的行已 DELETE 删除,清单由测试前 0 条恢复为 0 条。
本地单元测试(提交信息记载):GroupBatchSuppliesServiceTest 新增 208 行用例。
十、相关文档
- 团期需求文档:
docs/group/(dev-v3 分支) - 实施单 AC-TD-13(
ad747212d已标记 #6986 落地并勘误)
关联 / 联系人
- Issue: #6986
- PR: #6987(合并提交
bcf9332bd,落入dev-v3) - 后续修正:
8806315e3(备品去重,同时关联 #6950) - 服务: hl-order-service-v3 + hl-resource-service
- 后端: jw
- 前端: 待认领(
frontend_status: pending)