--- schema: "hl-changelog/v2" ticket: "7287" title: "团期达门槛自动成团 + 未成团动作闸 589552/589553;团期详情新增成团口径三件套并改取实时门槛" consumer: "multiple" author: "jw(GIT)" change_type: "修改接口" backend_status: "deployed" gateway_status: "verified" frontend_status: "verified" frontend_owner: "mmg" frontend_ref: "7f775213" target_release: "" verified_at: "2026-09-08" status_note: "前端三项待办已交付:①成团弹窗「已售」与距标准差改读 formingRooms(thresholdSource=SNAPSHOT_FALLBACK 显回退提示不阻断);②班期表单(编辑+批量创建)对称补 minToForm/minParticipants,编辑传了才覆盖、0=不限不丢、BatchPanel 卡片回显成团门槛;③589552/589553 由拦截器透后端 message(本系统无中央错误码字典,无需补录)。BatchPricingStep 已有 minToForm 不在范围,提交体不含 minParticipants 后端「没传保留旧值」不丢。ref 7f775213,checkpoint 全绿含 Vitest 全量+生产构建。" updated_at: "2026-09-08" base: "dev-v3" --- # 团期: 达门槛自动成团 + 未成团动作闸 + 门槛链路实时化 > **服务**: hl-order-service-v3、hl-product-service-v2、hl-user-service(**必须一起部署**,次序 product-v2 → order-v3 → user-service) > **PR**: #7307 > **Issue**: #7287 > **日期**: 2026-09-08 > **影响范围**: 团期运营台成团、团期详情、班期维护(新增/编辑/批量创建)、房务与车务与导摄的资源动作入口 --- ## ⚠️ 关键变化 **已有接口的出入参没有删改,只有新增字段。** 但有四件事必须知道: 1. **系统会自动成团了**。达到最低成团户数**或**最低成团人数任一门槛的团期, 由定时任务每 5 分钟自动推进到「资源准备中」。**任务落地即暂停**,需运营在用户中心手动开启。 2. **团期详情新增三个字段**:`formingRooms` / `formingPeople` / `thresholdSource`。 ⚠️ **成团弹窗的「已售」必须改读 `formingRooms`**,不能继续读 `enrolledRooms`—— 两者口径不同(见下方对照表),继续读旧字段会出现「弹窗显示未达标、系统却自动成团了」。 3. **未成团不能做资源动作了**:招募中的团期调派房务 / 派车务 / 配导摄 / 设报账人一律拒绝, 新增错误码 **589552**(团期尚未成团)与 **589553**(团期尚未创建)。 4. **班期表单要加一个字段**:`minParticipants`(最低成团人数)。 ⚠️ **编辑班期时必须把两个门槛都回传**,否则会被清零——这是本次修掉的一个既有缺陷, 但前端表单若不加这个字段,用户就没法设置人数门槛。 --- ## 一、背景 现在团期无论卖到多少户都停在「招募中」,必须有人盯着看板手动点成团; 而成团是后面所有资源动作的起点(派房务、配车、配导摄、出合同保险、备物料), 漏点一次整条链路就停摆。 另一头是相反的风险:团还没成,房务 / 车务 / 导摄配置就能被提前发起, 资源成本先于成团发生,团若流掉这些成本收不回。 同时门槛链路本身有两个洞:**最低成团人数在产品域根本没有落库映射**; **团期详情读的是建团那天的冻结快照**——产品域把门槛从 6 改成 3 后, 系统按实时值 3 判定成团(判定是对的),弹窗却显示「满 6 户 · 未达标」, 成团流水还会落库一条「未达标(满6户)」的错误审计记录。 --- ## 二、变更接口清单 | # | 接口 | 方法 | 路径 | 变更类型 | 说明 | |---|------|------|------|----------|------| | 1 | 团期详情 | GET | `/v3/admin/order/group-batch/:groupBatchId` | 新增字段 | 新增 formingRooms / formingPeople / thresholdSource;minToForm、minGroupPeople 改取实时值 | | 2 | 新增/编辑班期 | PUT | `/admin/product/item/:id/schedule` | 新增字段 | 新增 minParticipants;两个门槛改为「传了才覆盖、没传保留旧值」 | | 3 | 批量创建班期 | POST | `/admin/product/item/:id/schedule/batch-create` | 新增字段 | 新增 minParticipants,逐期落库 | | 4 | 班期列表 | GET | `/admin/product/item/:id/schedule/list` | 新增字段 | 响应新增 minParticipants 回显 | | 5 | 逐单提交房务需求 | POST | `/v3/admin/order/:id/hotel-requirement/dispatch` | 行为变化 | 未成团拒 589552 / 未建团拒 589553 | | 6 | 逐单提交车务需求 | POST | `/v3/admin/order/:id/vehicle-requirement/dispatch` | 行为变化 | 同上 | | 7 | 团期导摄配置保存 | PUT | `/v3/admin/group-batch/:productBatchId/staff` | 行为变化 | 同上 | --- ## 三、接口详情 ### 1. 团期详情 `GET /v3/admin/order/group-batch/:groupBatchId` **VO**: `GroupBatchDetailRespVO` #### 使用场景 管理后台团期详情页 / 成团弹窗。调用方 hl-ui 团期运营台。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | groupBatchId | Path | Long | ✅ | — | 团期主订单 ID;**入参一个都没变** | #### 出参 `Result` | 字段 | 类型 | 说明 | |------|------|------| | formingRooms | Integer | **新增**。成团判定用户数 = 线上**已付款**活跃订单数 + 产品域线下占位。**成团弹窗的「已售」请读这个** | | formingPeople | Integer | **新增**。成团判定用人数 = 线上已付款活跃订单总人数 + 产品域线下报名人数 | | thresholdSource | String | **新增**。本次响应里两个门槛的来源:`PRODUCT_REALTIME`(产品域实时值,正常)/ `SNAPSHOT_FALLBACK`(产品域暂不可达,回退建团快照) | | minToForm | Integer | **口径变化**:改为产品域**实时值**(取不到才回退快照)。0/null = 不限 | | minGroupPeople | Integer | **口径变化**:历史上恒为 null 的字段,现已启用,含义是「最低成团人数」,与 minToForm 对称 | | enrolledRooms / enrolledPeople | Integer | **未变**,仍是库存口径(含未付款、不含线下占位)。⚠️ 与 formingRooms/formingPeople 不是一回事 | | 其余全部字段 | — | **完全不变** | #### 请求示例 ```http GET /v3/admin/order/group-batch/2097209881592266753 HTTP/1.1 Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "success": true, "data": { "groupBatchId": "2097209881592266753", "minToForm": 3, "minGroupPeople": 3, "formingRooms": 6, "formingPeople": 18, "thresholdSource": "PRODUCT_REALTIME", "enrolledRooms": 1, "enrolledPeople": 2 } } ``` 注意 `formingRooms=6` 与 `enrolledRooms=1` 同时出现且**都不是错的**—— 前者含线下占位、不含未付款;后者是库存口径,正好相反。 #### 空数据 / 降级响应 产品域暂时取不到时,两个门槛**一起**回退建团快照,`thresholdSource` 标成 `SNAPSHOT_FALLBACK`, 线下占位按 0 计。**接口仍返 200**,不会因为产品域抖动而整个详情页打不开。 前端可据此提示「门槛可能非最新」。两个门槛要么都实时、要么都回退,不会一个实时一个快照。 ```json { "code": 200, "message": "成功", "success": true, "data": { "groupBatchId": "2097209881592266753", "minToForm": 4, "minGroupPeople": 7, "formingRooms": 0, "formingPeople": 0, "thresholdSource": "SNAPSHOT_FALLBACK" } } ``` #### 错误响应 ```json { "code": 589500, "message": "团期不存在", "success": false, "data": null } ``` **错误码集合无新增**。 #### 业务边界 - **判定、成团弹窗、成团流水三处共用同一份计数与门槛**,不会出现「弹窗说未达标、系统却成团了」。 - **`enrolledRooms` 的语义没变**,超卖闸、对账任务、调名额校验仍旧读它,前端沿用旧口径的地方不受影响。 - **0 或 null 表示「不限」**,两个门槛都为 0 时系统不会自动成团,只能人工成团。 ### 2. 新增/编辑班期 `PUT /admin/product/item/:id/schedule` **VO**: `ScheduleSaveReqVO` #### 使用场景 管理后台产品维护 → 班期新增 / 编辑。调用方 hl-ui 产品班期表单。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | id | Path | Long | ✅ | — | 产品 ID | | batchId | Body | Long | 编辑必填 | — | 班期 ID;新增时不传 | | departureDate | Body | LocalDate | ✅ | `yyyy-MM-dd` | 出发日期 | | singleRoomDiff | Body | BigDecimal | ✅ | ≥0 | 单房差 | | minToForm | Body | Integer | ❌ | ≥0 | 最低成团户数,0/不传视场景见下 | | minParticipants | Body | Integer | ❌ | ≥0 | **新增字段**:最低成团人数,0=不限 | | maxRooms / maxParticipants | Body | Integer | ❌ | ≥0 | 满团房数 / 人数,未变 | | 其余字段 | — | — | — | — | **完全不变** | #### 出参 `Result` | 字段 | 类型 | 说明 | |------|------|------| | data | Long | 班期 ID;**响应结构完全不变** | #### 请求示例 ```http PUT /admin/product/item/2056947670512971778/schedule HTTP/1.1 Authorization: Bearer Content-Type: application/json { "batchId": 2097209609465864195, "departureDate": "2026-11-11", "singleRoomDiff": "0", "adultPrice": "3000.00", "childPrice": "2500.00", "maxParticipants": 30, "maxRooms": 9, "minToForm": 6, "minParticipants": 10 } ``` #### 响应示例 ```json { "code": 200, "message": "成功", "success": true, "data": "2097209609465864195" } ``` #### 空数据 / 降级响应 保存成功后会尽最大努力把新门槛推给订单域刷新展示兜底值。 **这一步失败不影响保存**——接口仍返 200,只记警告日志,班期本身已经存好了。 ```json { "code": 200, "message": "成功", "success": true, "data": "2097209609465864195" } ``` #### 错误响应 ```json { "code": 410105, "message": "成人售价(1000.00元)必须大于产品订金(1000.00元/人),请调整定价或修改基础信息中的订金", "success": false, "data": null } ``` **错误码集合无新增**。 #### 业务边界 - ⚠️ **编辑时两个门槛都要回传**:语义是「传了才覆盖、没传保留旧值」。 改前只要表单不回传就会被**静默清零**(自动成团随之关闭且页面看不出异常),本次已修。 但前端仍应把两个字段放进表单并原样回传,否则用户改不了门槛。 - **显式传 0 是「不限」**,不会被旧值覆盖回去。 - **新增班期时不传按 0(不限)兜底**。 ### 3. 批量创建班期 `POST /admin/product/item/:id/schedule/batch-create` **VO**: `ScheduleBatchCreateReqVO` #### 使用场景 管理后台按重复规则一次性建多期班期。调用方 hl-ui 产品班期批量创建弹窗。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | id | Path | Long | ✅ | — | 产品 ID | | startDate / endDate | Body | LocalDate | ✅ | `yyyy-MM-dd` | 日期区间 | | repeatMode | Body | String | ✅ | WEEKLY / BIWEEKLY / MONTHLY | 重复模式 | | dayOfWeek | Body | Integer | 按模式 | 1-7 | 周几 | | minToForm | Body | Integer | ❌ | ≥0 | 最低成团户数,逐期落库 | | minParticipants | Body | Integer | ❌ | ≥0 | **新增字段**:最低成团人数,**逐期落库** | | 其余字段 | — | — | — | — | **完全不变** | #### 出参 `Result` | 字段 | 类型 | 说明 | |------|------|------| | data | Integer | 创建成功的期数;**响应结构完全不变** | #### 请求示例 ```http POST /admin/product/item/2056947670512971778/schedule/batch-create HTTP/1.1 Authorization: Bearer Content-Type: application/json { "startDate": "2027-01-21", "endDate": "2027-02-05", "repeatMode": "WEEKLY", "dayOfWeek": 4, "adultPrice": "3000.00", "childPrice": "2500.00", "singleRoomDiff": "0", "maxParticipants": 30, "maxRooms": 9, "minToForm": 2, "minParticipants": 6 } ``` #### 响应示例 ```json { "code": 200, "message": "成功", "success": true, "data": 3 } ``` #### 空数据 / 降级响应 日期区间内没有匹配重复规则的日期时返回 `data: 0`,不是错误。 ```json { "code": 200, "message": "成功", "success": true, "data": 0 } ``` #### 错误响应 ```json { "code": 410107, "message": "批量创建日期跨度超过上限", "success": false, "data": null } ``` **错误码集合无新增**。 #### 业务边界 - **`minParticipants` 会落到每一期**。改前该字段不存在,批量建出来的班期人数门槛**静默落 0**。 - 单期上限与既有的跨度校验未变。 ### 4. 班期列表 `GET /admin/product/item/:id/schedule/list` **VO**: `ScheduleRespVO` #### 使用场景 管理后台产品详情 → 班期列表。调用方 hl-ui 产品班期表格。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | id | Path | Long | ✅ | — | 产品 ID;**入参一个都没变** | #### 出参 `Result>` | 字段 | 类型 | 说明 | |------|------|------| | minParticipants | Integer | **新增**:最低成团人数,0=不限 | | minToForm | Integer | 最低成团户数,未变 | | 其余全部字段 | — | **完全不变** | #### 请求示例 ```http GET /admin/product/item/2056947670512971778/schedule/list HTTP/1.1 Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "success": true, "data": [ { "batchId": "2097221229113991170", "departureDate": "2027-01-21", "minToForm": 2, "minParticipants": 6 } ] } ``` #### 空数据 / 降级响应 产品下无班期时返回空数组,不是错误。 ```json { "code": 200, "message": "成功", "success": true, "data": [] } ``` #### 错误响应 ```json { "code": 410001, "message": "产品不存在", "success": false, "data": null } ``` **错误码集合无新增**。 #### 业务边界 - 该字段与班期表单的 `minParticipants` 同源,用于表格回显与编辑回填。 ### 5. 逐单提交房务需求 `POST /v3/admin/order/:id/hotel-requirement/dispatch` **VO**: `HotelRequirementRespVO` #### 使用场景 管理后台把某户的房型需求派给房务。调用方 hl-ui 团期子订单操作区。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | id | Path | Long | ✅ | — | 子订单 ID;**入参一个都没变** | #### 出参 `Result` | 字段 | 类型 | 说明 | |------|------|------| | requirementId | Long | 需求 ID;**响应结构完全不变** | | status | String | 派发后为 PENDING | | 其余全部字段 | — | **完全不变** | #### 请求示例 ```http POST /v3/admin/order/2097223133588041729/hotel-requirement/dispatch HTTP/1.1 Authorization: Bearer Content-Type: application/json {} ``` #### 响应示例 ```json { "code": 200, "message": "成功", "success": true, "data": { "requirementId": "2097223397887913985", "status": "PENDING" } } ``` #### 空数据 / 降级响应 该单尚未提交过房需求时返 582031「订单无有效需求行」,属业务前置条件不满足,未变。 ```json { "code": 582031, "message": "订单无有效需求行", "success": false, "data": null } ``` #### 错误响应 ```json { "code": 589552, "message": "团期尚未成团,请先完成成团后再操作", "success": false, "data": null } ``` **新增错误码**:589552(团期尚未成团)、589553(团期尚未创建,即该班期还没有任何订单)。 #### 业务边界 - **定制师提交 / 修改房需求不受影响**:招募中照常放行,只有「派给房务」这一步被拦。 - **未建团与未成团分开报码**,让运营能区分「团建了但没成」与「团根本还没建」—— 后者的下一步动作完全不同。 - **存量需求也被拦**:上线前已经放行到房务的需求,其后续配房 / 单日确认动作在团期回到 招募中时同样返 589552。 ### 6. 逐单提交车务需求 `POST /v3/admin/order/:id/vehicle-requirement/dispatch` **VO**: `VehicleRequirementRespVO` #### 使用场景 管理后台把某户的用车需求派给车务。调用方 hl-ui 团期子订单操作区。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | id | Path | Long | ✅ | — | 子订单 ID;**入参一个都没变** | #### 出参 `Result` | 字段 | 类型 | 说明 | |------|------|------| | requirementId | Long | 需求 ID;**响应结构完全不变** | | status | String | 派发后状态 | | 其余全部字段 | — | **完全不变** | #### 请求示例 ```http POST /v3/admin/order/2097223133588041729/vehicle-requirement/dispatch HTTP/1.1 Authorization: Bearer Content-Type: application/json {} ``` #### 响应示例 ```json { "code": 200, "message": "成功", "success": true, "data": { "requirementId": "2097223397887913986" } } ``` #### 空数据 / 降级响应 该单尚未提交过车需求时返 582031「订单无有效需求行」,未变。 ```json { "code": 582031, "message": "订单无有效需求行", "success": false, "data": null } ``` #### 错误响应 ```json { "code": 589552, "message": "团期尚未成团,请先完成成团后再操作", "success": false, "data": null } ``` **新增错误码**:589552 / 589553,同房务。 #### 业务边界 - 与房务同一套判据、同一个方法,不会出现两边口径漂移。 - 定制师提交 / 修改车需求同样不受影响。 ### 7. 团期导摄配置保存 `PUT /v3/admin/group-batch/:productBatchId/staff` **VO**: `GroupBatchStaffConfigRespVO` #### 使用场景 管理后台团期详情 → 导游 / 摄影师配置整体保存。调用方 hl-ui 团期资源配置页。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | productBatchId | Path | Long | ✅ | — | 产品侧班期 ID;**入参一个都没变** | | guides | Body | Array | ❌ | — | 导游列表 | | photographers | Body | Array | ❌ | — | 摄影师列表 | #### 出参 `Result` | 字段 | 类型 | 说明 | |------|------|------| | guides / photographers | Array | 保存后的配置;**响应结构完全不变** | | 其余全部字段 | — | **完全不变** | #### 请求示例 ```http PUT /v3/admin/group-batch/2097223022807400450/staff HTTP/1.1 Authorization: Bearer Content-Type: application/json {"guides":[{"staffId":1,"staffName":"张导"}],"photographers":[]} ``` #### 响应示例 ```json { "code": 200, "message": "成功", "success": true, "data": { "guides": [{ "staffId": "1" }] } } ``` #### 空数据 / 降级响应 两个列表都传空表示清空配置,返 200,不是错误。 ```json { "code": 200, "message": "成功", "success": true, "data": { "guides": [], "photographers": [] } } ``` #### 错误响应 ```json { "code": 589553, "message": "团期尚未创建(该班期还没有任何订单),请先建团并完成成团后再操作", "success": false, "data": null } ``` **新增错误码**:589552 / 589553。设置报账人等级 `PUT /v3/admin/group-batch/:productBatchId/staff/:staffId/reporter-rank` 同样受这两个码约束。 #### 业务边界 - **未成团时保存被拒且不留副作用**:实测被拒后导游就绪标记仍为未就绪。 - **成团后同一请求即可保存成功**,就绪标记随之置位。 --- ## 四、契约约束与正确调用方式 - **成团弹窗的「已售」请读 `formingRooms`**,不要读 `enrolledRooms`。 前者是成团口径(线上已付款 + 线下占位),后者是库存口径(含未付款、不含线下占位)。 继续读旧字段会出现「弹窗说未达标、系统却自动成团了」。 - **门槛请读 `minToForm` / `minGroupPeople`**,并用 `thresholdSource` 判断要不要给用户提示 「门槛可能非最新」。两个门槛的来源恒定一致,不会一个实时一个快照。 - **编辑班期时两个门槛都要回传**,不传等于「不改」,传 0 等于「不限」。 - **589552 / 589553 是可恢复的拒绝**,提示运营「请先完成成团」/「请先建团」, 不要提示「系统繁忙」。 --- ## 五、数据库行为 - 本次**无表结构变更**;最低成团人数复用班期已有的列,团期侧复用既有的「最低成团人数」列 (该列此前恒为空,本次开始写入)。 - 新增一条定时任务配置(团期达门槛自动成团,每 5 分钟),**落地即暂停**,需人工开启。 - 自动成团会写团期状态流水一条,操作人记为「系统」。 --- ## 六、边界行为 - **两个门槛都为 0 / 未设**:不自动成团,只能人工成团。 - **未付款的户不计入达标判定**;**产品域线下占位计入**。 - **自动成团后退单掉回门槛以下**:保持成团,不退回招募中;是否流团由运营人工判断。 - **产品域暂时不可达**:本轮跳过该产品名下团期,**绝不误成团**;下一轮自动重试。 - **产品域班期已取消 / 已过出发日**:不自动成团;产品域显示「已满额」的班期**照常自动成团** (满员正是该成团的状态)。 - **纯线下、零线上订单的班期**不在扫描范围(订单域还没有这个团),需运营先建团。 - **人工成团仍然保留**:未达门槛也可提前成团,流水会记「未达标(满N户)」与理由。 ## 六.6、修改前后对比 | 场景 | 改前 | 改后 | |------|------|------| | 团期达到最低成团户数 / 人数 | 一直停在「招募中」,等人工点成团 | 定时任务自动推进到「资源准备中」(任务需先开启) | | 团期详情的成团门槛 | 读建团那天的**冻结快照**;产品域改了门槛也不变 | 读产品域**实时值**;取不到才回退快照并标 `SNAPSHOT_FALLBACK` | | 团期详情的「已售」 | 只有 `enrolledRooms`(含未付款、不含线下占位) | 新增 `formingRooms`(成团口径),`enrolledRooms` 保留不变 | | 最低成团人数 | 产品域**没有这个字段**,无法设置 | 班期表单 / 批量创建 / 列表回显全链路可用 | | 编辑班期不回传门槛 | 门槛被**静默清零**,自动成团随之关闭且页面看不出异常 | 保留旧值;显式传 0 才是「不限」 | | 批量创建带最低成团人数 | 字段不存在,逐期**静默落 0** | 逐期正确落库 | | 招募中派房务 / 车务 / 配导摄 / 设报账人 | **放行**,资源成本先于成团发生 | 拒 **589552**(未建团拒 **589553**) | | 定制师提交 / 修改房车需求 | 放行 | **仍然放行**,未收紧 | ## 六.7、影响评估 - **前端有三项必做**: 1. 团期详情消费三个新字段,**成团弹窗的「已售」改读 `formingRooms`**; 2. 班期表单 / 批量创建弹窗新增 `minParticipants`,且编辑时两个门槛都要回传; 3. 错误码字典补录 589552 / 589553。 - **不做会怎样**:接口不会报错,但①成团弹窗的数字会与系统判定对不上; ②用户无法设置最低成团人数;③被拒时只看到默认错误提示。 - **对运营的净效果**:不用再盯看板手动点成团;未成团时不会误配资源。 - **需要运营做一件事**:到用户中心把「团期达门槛自动成团」任务从暂停改为启用。 - **风险**:任务开启后会对存量招募中团期批量成团。两个门槛都为 0 的存量班期不受影响 (不自动成团),但已设门槛且已达标的团期会在开启后的第一轮全部推进——**建议先在低峰期开启并观察一轮**。 --- ## 七、不影响范围 - 人工成团入口与权限未变(未达标仍可提前成团)。 - 满团名额调整的校验规则未变(仍按含未付款的库存口径判)。 - 团期看板列表、超卖闸、名额对账任务未变。 - 下单、支付、退款链路未变。 - 定制师提交 / 修改房车需求的权限未收紧。 --- ## 八、测试环境已验证 三服务按次序一起滚:product-v2 → order-v3 → user-service。验收后已滚回主干,造数已清理。 | 验证项 | 结果 | |--------|------| | 未付款不计入 | 2 单都不付款 → 不成团;付款后再跑 → 成团 | | 线下占位计入 | 门槛 6、线上已付 1 户 + 线下占位 5 → 自动成团 | | 人数维度 | 户数门槛 0 / 人数门槛 4,1 单 3 人 + 线下 1 人 → 自动成团 | | 两个门槛都为 0 | 3 单全付款、连跑两轮 → 仍「招募中」 | | 自动成团留痕 | 操作人「系统」,文案「系统自动成团 · 已售 6/9 户(线上已付 1 + 线下 5) · 达标(满6户)」 | | 判定与展示同源 | 详情 `formingRooms=6` 与流水记录完全一致;`enrolledRooms` 保持库存口径不受影响 | | 幂等 | 对已成团团期连跑两轮,成团流水仍只有 1 条 | | 人工成团文案 | 「手动成团 · 已售 1/9 户 · 未达标(满3户) · 理由:客户催促」,门槛取的是实时值 | | 权限未被削弱 | 无权限账号手动成团被拒;同一团期由任务自动成团仍成功 | | 退单不回退 | 已成团团期退掉已付款户后仍「资源准备中」 | | 产品域不可达 | 门槛已达标的团期**不误成团**;恢复后同一团期同一任务立即成团 | | 详情门槛实时化 | 产品域 6→3 且不下新单 → 详情返 3,来源标 `PRODUCT_REALTIME` | | 详情降级 | 产品域取不到 → 仍 200,两个门槛一起回退快照,来源标 `SNAPSHOT_FALLBACK` | | 门槛变更回推 | 产品域改门槛且不下新单 → 团期侧展示兜底值同步更新 | | 编辑不再洗掉门槛 | 只改备注 → 仍 6/10;显式传 0/0 → 0/0 | | 批量创建 | 3 期全部落 `minParticipants=6`,列表回显一致 | | 未成团闸 | 招募中派房务 / 派车务 / 配导摄均 589552;成团后房务与导摄同请求返 200 | | 未建团闸 | 无任何订单的班期配导摄 / 设报账人均 589553 | | 定制师未被收紧 | 招募中提交房需求返 200 | | 候选排除 | 产品域已取消 / 已过出发日 → 不成团;产品域「已满额」→ 正常成团 | | 取消成团后 | 团期回到招募中,再配房返 589552 | | 单元测试 | product-v2 1620 例、order-v3 9060+ 例、user-service 3828 例,均 0 失败 0 错误 | --- ## 十、相关文档 - Issue #7287、PR #7307 - 关联工单:#7158(人工成团与户数门槛)、#7178(满团名额调整)、#7196(流团审批) - 设计文档已同步:团期订单设计说明中「系统不自动成团」的表述已改写, 「未达标也可提前成团」保留 --- ## 关联 / 联系人 - 后端:jw - 前端:mmg(hl-ui) - 前端待办(三项):①团期详情消费 formingRooms / formingPeople / thresholdSource, **成团弹窗「已售」改读 formingRooms**;②班期表单与批量创建新增 minParticipants, 编辑时两个门槛都回传;③错误码字典补录 589552 / 589553。 - 运营待办:到用户中心开启「团期达门槛自动成团」定时任务(落地即暂停)。