diff --git a/changelogs-v2/2026-09/07_7196_团期流团改为申请与审批-修改接口-管理后台.md b/changelogs-v2/2026-09/07_7196_团期流团改为申请与审批-修改接口-管理后台.md new file mode 100644 index 00000000..2abe9f4a --- /dev/null +++ b/changelogs-v2/2026-09/07_7196_团期流团改为申请与审批-修改接口-管理后台.md @@ -0,0 +1,530 @@ +--- +schema: "hl-changelog/v2" +ticket: "7196" +title: "团期流团改为申请与审批" +consumer: "admin" +author: "jw(GIT)" +change_type: "修改接口" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "pending" +frontend_owner: "" +frontend_ref: "" +target_release: "" +verified_at: "" +status_note: "流团由「点完就退款、不可撤销」改为「先申请、批准后才执行」。发起流团端点语义变更且理由改为必填,新增审批中心列表、申请详情、同意、拒绝四个端点。后端已部署 TEST 并实测 R1-R10 全过;前端待接。" +updated_at: "2026-09-07" +base: "dev-v3" +--- + +# 团期流团:改为「申请 + 审批」,批准后才退定金 + +> **影响范围**:管理后台「团期详情 → 发起流团」与「订单管理 → 审批中心」。 +> 当前状态:后端已部署 TEST 并实测;前端待接入(**发起流团端点语义已变更**)。 + +## ⚠️ 关键变化 + +1. **流团不再是点完就执行**。原先点一下就把团期置为已取消、批量退各户定金、释放配车占用, + **不可撤销**;现在只提交一条待审批的申请,**团期、子订单、名额、钱四项零变动**, + **批准后才真正执行**。 +2. **发起流团端点语义变更**:`POST .../disband` 由「立即流团」改为「提交流团申请」, + 且**流团理由由可空改为必填**(原先允许不带请求体直接流团)。 +3. **新增四个端点**:审批中心列表、申请详情、**同意**、**拒绝**(拒绝原因必填)。 +4. **新增权限校验**:发起流团需要团期运营写权限(此前该接口无任何权限校验)。 +5. **新增阶段限制**:出行后不可发起流团。 + +## 一、背景 + +流团意味着整期解散并退还各户定金,是不可逆的重动作,此前却没有任何审批与权限把关, +点一下就生效。原型明确要求走公司管理审批:团期管理员发起 → 抄送定制师知会 → +负责人确认,**批准后**才自动原路退定金。本次把这条链路补齐。 + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +| --- | --- | --- | --- | --- | --- | +| 1 | 发起流团申请 | POST | `/v3/admin/order/group-batch/{groupBatchId}/disband` | 修改接口 | 由「立即流团」改为「提交申请」;理由改为必填;新增权限与阶段限制 | +| 2 | 审批中心列表 | GET | `/v3/admin/order/group-batch/approvals/page` | 新增接口 | 流团与退单户两类共用,可按类型筛选 | +| 3 | 流团申请详情 | GET | `/v3/admin/order/group-batch/disband/{approvalId}` | 新增接口 | 审批链、快照、批复信息 | +| 4 | 同意流团 | POST | `/v3/admin/order/group-batch/disband/{approvalId}/approve` | 新增接口 | 批准后才真正执行流团 | +| 5 | 拒绝流团 | POST | `/v3/admin/order/group-batch/disband/{approvalId}/reject` | 新增接口 | 只改申请单状态,团期继续正常招募 | + +## 三、接口详情 + +### 1. 发起流团申请 `POST /v3/admin/order/group-batch/{groupBatchId}/disband` + +**VO**: `DisbandSubmitReqVO` + +#### 使用场景 + +团期管理员在团期详情点「发起流团(走审批)」,填写流团理由后提交。 +提交只建一条待审批的申请单,**团期状态、子订单、名额、钱一律不动**,等审批结果。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +| --- | --- | --- | --- | --- | --- | +| groupBatchId | path | Long | 是 | 正整数 | 团期 ID | +| reason | body | String | 是 | 非空白,最长 512 字 | 流团理由。**本次由可空改为必填** | + +#### 出参 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| approvalId | String | 审批单 ID | +| approvalStatus | String | 固定 PENDING | +| affectedOrderCount | Integer | 影响子订单户数(提交时快照) | +| estimatedRefundAmount | BigDecimal | 退还定金合计(提交时快照,仅供展示) | +| applicantName | String | 发起人姓名 | +| ccUserNames | String[] | 抄送定制师姓名列表(去重) | + +#### 请求示例 + +```json +{ + "reason": "临近出团仍未达最低成团数(6 户)" +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "approvalId": "2096779558547087362", + "groupBatchId": "2096779382436651009", + "batchNo": "GT-26-0004", + "approvalStatus": "PENDING", + "reason": "临近出团仍未达最低成团数(6 户)", + "affectedOrderCount": 3, + "estimatedRefundAmount": 32900.00, + "applicantName": "卜丽颖", + "ccUserNames": ["李雯", "王浩"] + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +本期尚无子订单时也可发起:影响户数为 0、退还定金为 0、抄送名单为空数组,属正常情况。 + +#### 错误响应 + +```json +{ + "code": 589543, + "message": "该团期已有流团申请在审批中,请勿重复提交", + "data": null, + "success": false +} +``` + +#### 业务边界 + +- **提交零副作用**:团期状态、子订单、名额、钱四项均不变动。 +- **同期至多一条在途申请**,重复提交返回 589543。 +- **出行后不可发起**,返回 589544;可发起的阶段为招募中、资源准备中、物料准备中、待出发。 +- 需要团期运营写权限,无权限返回 589507。 +- 五秒内重复点击会先被幂等窗口拦下,返回提交中提示,属预期。 + +### 2. 审批中心列表 `GET /v3/admin/order/group-batch/approvals/page` + +**VO**: `GroupBatchApprovalItemRespVO` + +#### 使用场景 + +审批中心页面加载时调用,一次拿到流团与退单户两类申请,可按类型与状态筛选。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +| --- | --- | --- | --- | --- | --- | +| bizType | query | String | 否 | DISBAND / WITHDRAW | 业务类型;不传表示两类都要 | +| approvalStatus | query | String | 否 | PENDING / APPROVED / REJECTED | 审批状态;不传表示全部 | +| groupBatchId | query | Long | 否 | 正整数 | 按团期筛选 | +| pageNum | query | Integer | 否 | 从 1 开始 | 页码,默认 1 | +| pageSize | query | Integer | 否 | 1-100 | 每页条数,默认 20 | + +#### 出参 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| bizType | String | DISBAND 流团 / WITHDRAW 退单户 | +| bizTypeText | String | 类型中文名,服务端给出 | +| approvalStatus | String | 审批状态 | +| batchNo | String | 团期号 | +| orderId | String | 被退子订单 ID;流团为 null | +| affectedOrderCount | Integer | 影响户数;退单户为 null | +| estimatedRefundAmount | BigDecimal | 预计退款额 | +| applicantName | String | 发起人 | +| approvedByName | String | 批复人;未批复为 null | + +#### 请求示例 + +```http +GET /v3/admin/order/group-batch/approvals/page?bizType=DISBAND&approvalStatus=PENDING&pageNum=1&pageSize=20 +Authorization: Bearer +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "list": [ + { + "approvalId": "2096779558547087362", + "bizType": "DISBAND", + "bizTypeText": "流团", + "batchNo": "GT-26-0004", + "approvalStatus": "PENDING", + "orderId": null, + "affectedOrderCount": 3, + "estimatedRefundAmount": 32900.00, + "applicantName": "卜丽颖", + "approvedByName": null + } + ], + "total": 4, + "page": 1, + "pageSize": 20 + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +无待办时返回空列表且 total 为 0,页面展示「暂无审批事项」。 + +#### 错误响应 + +```json +{ + "code": 589507, + "message": "无操作权限", + "data": null, + "success": false +} +``` + +#### 业务边界 + +- 两类申请共用一套列表;差异只在 `orderId`(退单户有值)与 `affectedOrderCount`(流团有值)。 +- 按提交时间倒序;每页最多 100 条。 + +### 3. 流团申请详情 `GET /v3/admin/order/group-batch/disband/{approvalId}` + +**VO**: `DisbandApprovalRespVO` + +#### 使用场景 + +审批人在审批中心点开一条流团申请,或团期详情的「流团审批中 / 已被驳回」横幅取详情。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +| --- | --- | --- | --- | --- | --- | +| approvalId | path | Long | 是 | 正整数 | 审批单 ID | + +#### 出参 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| approvalStatus | String | PENDING / APPROVED / REJECTED | +| reason | String | 流团理由 | +| affectedOrderCount | Integer | 影响户数(提交时快照) | +| estimatedRefundAmount | BigDecimal | 退还定金合计(提交时快照) | +| actualRefundAmount | BigDecimal | **实际退款合计**,批准执行后按真实取消结果回填 | +| applicantName | String | 发起人 | +| ccUserNames | String[] | 抄送定制师 | +| approvedByName | String | 批复人 | +| approveRemark | String | 批复备注 / 拒绝原因 | + +#### 请求示例 + +```http +GET /v3/admin/order/group-batch/disband/2096779558547087362 +Authorization: Bearer +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "approvalId": "2096779558547087362", + "approvalStatus": "APPROVED", + "reason": "临近出团仍未达最低成团数(6 户)", + "affectedOrderCount": 3, + "estimatedRefundAmount": 32900.00, + "actualRefundAmount": 32900.00, + "applicantName": "卜丽颖", + "ccUserNames": ["李雯", "王浩"], + "approvedByName": "刘涛", + "approveRemark": "确认无法成团,同意流团" + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +未批复时 `approvedByName` / `approveRemark` / `actualRefundAmount` 均为 null,前端按「审批中」渲染。 + +#### 错误响应 + +```json +{ + "code": 589545, + "message": "流团申请不存在", + "data": null, + "success": false +} +``` + +#### 业务边界 + +- 只认流团类申请;拿退单户的单号来查会返回 589545,两类端点互不串用。 + +### 4. 同意流团 `POST /v3/admin/order/group-batch/disband/{approvalId}/approve` + +**VO**: `ApproveDisbandReqVO` + +#### 使用场景 + +审批人在审批中心点「同意」。**只有走到这一步,流团才真正执行**: +团期置为已取消、按规则退各户定金、释放配车占用。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +| --- | --- | --- | --- | --- | --- | +| approvalId | path | Long | 是 | 正整数 | 审批单 ID | +| remark | body | String | 否 | 最长 512 字 | 批复备注,**可选** | + +#### 出参 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| approvalStatus | String | 变为 APPROVED | +| actualRefundAmount | BigDecimal | 实际退款合计,按执行后的真实取消结果回填 | +| approvedByName | String | 批复人姓名 | +| approveRemark | String | 批复备注 | + +#### 请求示例 + +```json +{ + "remark": "确认无法成团,同意流团" +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "approvalId": "2096779558547087362", + "approvalStatus": "APPROVED", + "actualRefundAmount": 32900.00, + "approvedByName": "刘涛", + "approveRemark": "确认无法成团,同意流团" + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +本期无有效子订单时实际退款合计为 0,流团照常完成。 + +#### 错误响应 + +```json +{ + "code": 589546, + "message": "该流团申请已处理,不可重复操作", + "data": null, + "success": false +} +``` + +#### 业务边界 + +- **只有待审批的申请可被处理**;已同意 / 已拒绝的再点返回 589546。 +- **并发下只有一人成功**:两名审批人同时点,另一人拿 589546,且**绝不会重复执行流团**。 +- 需要管理员角色,否则返回 589547。 +- **实际退款合计按执行后的真实取消结果统计**,不等于提交时的预计金额, + 两者不一致的常见原因见「不影响范围」的说明。 + +### 5. 拒绝流团 `POST /v3/admin/order/group-batch/disband/{approvalId}/reject` + +**VO**: `RejectDisbandReqVO` + +#### 使用场景 + +审批人认为该期还能继续招募,点「拒绝」并写明原因。 + +#### 入参 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +| --- | --- | --- | --- | --- | --- | +| approvalId | path | Long | 是 | 正整数 | 审批单 ID | +| remark | body | String | 是 | 非空白,最长 512 字 | **拒绝原因必填** | + +#### 出参 + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| approvalStatus | String | 变为 REJECTED | +| approvedByName | String | 批复人姓名 | +| approveRemark | String | 拒绝原因 | + +#### 请求示例 + +```json +{ + "remark": "已有两户确认报名,继续招募" +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "data": { + "approvalId": "2096779558547087362", + "approvalStatus": "REJECTED", + "approvedByName": "刘涛", + "approveRemark": "已有两户确认报名,继续招募" + }, + "success": true +} +``` + +#### 空数据 / 降级响应 + +拒绝不产生任何业务数据,响应中与执行相关的字段(实际退款合计)保持为空。 + +#### 错误响应 + +```json +{ + "code": 400, + "message": "拒绝原因不能为空", + "data": null, + "success": false +} +``` + +#### 业务边界 + +- **拒绝只改申请单状态**:团期、子订单、名额、钱四项一律不动,团期继续正常招募。 +- 拒绝后**可以再次发起**流团申请。 +- 拒绝原因会同时写进团期操作记录,在团期详情里可直接看到。 + +## 四、契约约束与正确调用方式 + +- **发起流团必须带理由**。原先允许不带请求体直接流团的调用方式**已失效**,会返回参数校验错误。 +- **提交 ≠ 已流团**。提交后团期仍在原状态,前端应把按钮切成「流团审批中」并置灰, + 同时展示「去审批中心」的入口;不要在提交成功后就把团期显示成已取消。 +- **同意与拒绝是两个端点**:同意的备注可选,拒绝的原因必填,前端拒绝弹窗需做必填校验。 +- **审批中心一个列表拿两类**,用 `bizType` 区分与筛选,不需要分别请求再合并。 +- **五秒幂等窗口**:连点会先返回「提交中,请勿重复提交」,这不是业务错误,前端做防抖即可。 +- 展示金额时请区分两个字段:`estimatedRefundAmount` 是提交时的预计值(用于弹窗展示), + `actualRefundAmount` 是批准执行后的实际结果(用于结果页与对账)。 + +## 五、数据库行为 + +- 发起流团**只新增一条待审批的申请单**,记录理由、影响户数、退还定金合计、 + 发起人与抄送定制师名单;**不改团期、不改子订单、不动名额与钱**。 +- 同意时**先把申请单落为已通过,再执行流团**;若该单已被他人处理,则整笔拒绝且**不执行流团**。 +- 执行流团后**回读实际取消结果**再回填实际退款合计——不拿「预计金额」冒充实退。 + 若存在已付款但未被取消的户,服务端会打出告警日志,便于人工捞出处理。 +- 拒绝**只改申请单状态与批复信息**,另写一条团期操作记录(含拒绝原因)。 +- 发起、拒绝、以及流团执行本身各写一条团期操作记录,均可查到操作人与时间。 +- 被拒绝或被拒收的请求(重复提交、阶段不允许、已处理、无权限)**一律零写入**。 + +## 六、边界行为 + +- 同期至多一条在途申请;驳回后可重新发起,判重只拦在审批中的那条。 +- 出行后不可发起流团;已流团的团期再次发起同样被拦。 +- 并发审批由串行锁与状态校验双重保证,同一条申请只会被处理一次,流团只会执行一次。 +- 本期无有效子订单时也可发起与批准,影响户数与金额均为 0。 + +## 六.6、修改前后对比 + +| 项 | 修改前 | 修改后 | +| --- | --- | --- | +| 发起流团的效果 | **点完即执行**:团期取消、批量退定金、释放配车,不可撤销 | **只建申请单**,四项零变动,批准后才执行 | +| 流团理由 | 可空,甚至可以不带请求体 | **必填**,为空拒绝 | +| 权限 | **无任何校验** | 需要团期运营写权限 | +| 阶段限制 | 只要不是已结算 / 已取消都能流 | **出行后不可发起** | +| 审批 | 无 | 同意 / 拒绝两个操作,拒绝原因必填 | +| 审批可见性 | 无 | 审批中心可见,流团与退单户两类共用列表 | +| 返回值 | 空 | 返回申请单快照(含影响户数、退还定金、抄送名单) | + +## 六.7、影响评估 + +- **发起流团端点不兼容**:语义由「立即执行」变为「提交申请」,且理由必填。 + 前端必须同步改造:提交后不能再当作已流团处理,需展示审批中状态; + 看板上旧的「确认流团」二次确认入口(文案写「点完即退款、不可撤销」)**应下线或改走同一条流**, + 否则与审批语义冲突。 +- **权限是行为变更**:此前任何登录管理员都能流团,现在需要团期运营写权限; + 该权限已在既有工单中建立并授予管理员与超级管理员,无需额外配置。 +- **阶段限制是行为收紧**:出行后不再允许流团。 +- 新增四个端点均为增量,不影响任何既有接口。 +- **审批人范围**:本期由管理员审批。原型里写的「部门负责人」角色目前系统中不存在, + 待该角色落地后可再收窄。 + +## 七、不影响范围 + +- 退单户(单户退团)的申请与审批流程完全不变,仅新增了一个可同时查看两类的列表。 +- 团期成团、调整满团名额、需求审核等其他动作不受影响。 +- 小程序端不受影响,改动全部在管理后台侧。 +- **本次不改动退款的执行口径**。需要特别说明一个既有限制: + 流团执行时,**只有尚未付款的户会被取消**,已付定金的户会被跳过、既不取消也不退款, + 与「自动原路退定金」的承诺不符。这是本次改造之前就存在的问题,本单未修, + 但已让实际退款合计如实反映真实结果、并对被跳过的已付款户打出告警,便于人工兜底。 + 该问题建议单独排期修复。 +- **批准后的客户通知(公众号 / 短信)本期不做**,仍需人工通知客户;抄送定制师目前只记录名单、不推送消息。 + +## 八、测试环境已验证 + +TEST 环境已部署订单服务并实测通过: + +- 发起流团申请成功,返回影响户数与退还定金快照,**团期状态保持不变**。 +- 流团理由为空被拒绝。 +- 重复发起被拒绝(同期至多一条在途)。 +- 拒绝时原因为空被拒绝;正常拒绝后**团期状态不变**,可再次发起。 +- 已处理的申请再次同意或拒绝均被拒绝。 +- **同意后团期才变为已取消**,未付款子订单同步取消,实际退款合计按真实结果回填。 +- 已流团的团期再次发起被阶段限制拦下。 +- 审批中心列表返回两类共 7 条,按类型筛选分别为流团 4 条、退单户 3 条,类型中文名正确。 +- 申请详情回读齐全:快照、抄送名单、批复人与批复备注。 + +## 十、相关文档 + +- 工单:HL#7196 +- 合并:HL PR#7201(已合入 dev-v3) +- 关联:HL#7100(团期退单户审批)——本单复用其审批单模型与审批范式; + HL#7158 / HL#7178——复用其建立的团期运营写权限与阶段限制写法 + +## 关联 / 联系人 + +- 后端:jw +- 前端待接:发起流团弹窗(必填理由、审批链展示、提交后切「审批中」)、 + 团期详情的审批中与已驳回两条横幅、审批中心列表与同意/拒绝操作(拒绝原因必填)、 + 看板旧「确认流团」入口下线或改走审批流