docs(changelog): #7534 成团通知接通通知中心 + 补发端点 GB-ADM-004N(新增接口)
changelog-filename-gate / validate (push) Failing after 2s

新增补发端点 notify-formed;成团(含自动成团)后事务外投递 GROUP_BATCH_FORMED。
backend=deployed / gateway=verified(TEST 网关三组实测:补发成功 recipientCount=4 /
冷却 589593 {0} 渲染 / 成团契约 589537 未变)。错误码 589593/594/595(589592 被 #7533 占顺延)。
这个提交包含在:
jw
2026-09-14 20:03:47 +08:00
父节点 0b2853224e
当前提交 3572fc7a0d
@@ -0,0 +1,215 @@
---
schema: "hl-changelog/v2"
ticket: "7534"
title: "成团通知接通通知中心 + 补发端点(GB-ADM-004N)"
consumer: "admin"
author: "jw(GIT)"
change_type: "新增接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "pending"
frontend_owner: "mmg"
frontend_ref: ""
target_release: ""
verified_at: "2026-09-14"
status_note: "新增补发成团通知端点 notify-formed;成团端点 group 契约一字未变,但成团成功后现在会真的给全部活跃子订单客户投递 GROUP_BATCH_FORMED 事件(此前不发任何通知)。前端需:①成团弹窗「通知客户」勾选框去掉或改纯说明(后端无对应入参,勾不勾都发);②团期详情/看板加「补发成团通知」按钮接 notify-formed。通知模板与占位符全在通知中心 notification_event_config 侧配置,文案定了不需再改 order-v3。eventCode 暂定 GROUP_BATCH_FORMED,最终取值待 jw/运营在通知中心确认;配置未建时 dispatcher 走 config 未找到分支、链路照通、零渠道触发(不阻塞)。零 DDL、零网关路由改动(/v3/admin/** 既有覆盖)。"
updated_at: "2026-09-14"
base: "dev-v3"
---
# order-v3: 成团通知接通通知中心 + 补发端点(GB-ADM-004N)
**服务**: hl-order-service-v3
**PR**: #7705
**Issue**: #7534
---
## ⚠️ 关键变化
🟢 **新增一个补发端点 + 一处成团行为变更(成团端点契约零改动)**:
1. **新增** `POST .../group-batch/:groupBatchId/notify-formed`:对**已成团**团期,向全部活跃子订单客户**补发**一次 `GROUP_BATCH_FORMED` 通知。无请求体、无查询参数。300s/团 冷却(Redis SETNX),冷却期内返 589593。
2. **行为变更(契约不变)** `POST .../group-batch/:groupBatchId/group`:请求体 / 响应 / 错误码**一字未变**,但成团成功后**现在会真的给客户发通知**了——成团(含自动成团)后在**事务外**向全部活跃子订单客户投递 `GROUP_BATCH_FORMED`;投递失败只记 `log.error`,**接口响应与错误码不受影响**。
3. **通道**:走 `MqNotificationEventProducer` 异步投递(afterCommit + producer 可空降级 + 异常只 log.error),**不**走同步 Feign。**一户一条**消息带 C 端 `userId`,`bizType=GROUP_BATCH`、`bizId=groupBatchId`、`extras` 含团期号 / 产品名 / 出发日期 / 成团时间 / orderId 供模板占位。
**无 DDL、无网关路由改动、成团端点结构一字未改。**
## 一、背景
团期「成团」此前**不发任何客户通知**;且成团当下通知失败(MQ 不可用 / 模板未配 / 手机号缺)后运营**无任何补发手段**——既有团期动作端点只有成团 / 取消成团 / 流团 / 调名额 / 预支 / 退单 / 转订单七个,无通知相关端点。本单第 1 步:①成团后接通通知中心;②新增补发口。第 2 步(转订单同类缺口)另开单。事件码与短信 / 公众号模板由 jw / 运营在通知中心 `notification_event_config` 侧配置,order-v3 只负责把 `extras` 业务字段塞全。
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 补发成团通知 | POST | `/v3/admin/order/group-batch/:groupBatchId/notify-formed` | 新增 | 对已成团团期向全部活跃子订单客户补发 GROUP_BATCH_FORMED;300s/团 冷却,冷却期返 589593;无收件人 589594;MQ/Redis 不可用 589595 |
| 2 | 成团 | POST | `/v3/admin/order/group-batch/:groupBatchId/group` | 行为变更(契约不变) | 请求体/响应/错误码一字未变;成团(含自动成团)成功后事务外投递 GROUP_BATCH_FORMED,失败只记日志不影响响应 |
## 三、接口详情
### 1. 补发成团通知 `POST /v3/admin/order/group-batch/:groupBatchId/notify-formed`
**VO**: `Result<GroupBatchNotifyRespVO>`(path: groupBatchId)
#### 使用场景
团期管理台「团期详情 / 团期看板」的「补发成团通知」按钮。成团当下通知失败或客户没收到时,运营对**已成团**团期主动补发一次。沿用既有鉴权(`group-batch:manage`,与成团同权限)与网关路由(`/v3/admin/**` 已覆盖)。冷却 300s/团,防连点与「没收到再点一次」。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| groupBatchId | path | string(Long) | 是 | 雪花 ID | 团期主订单 ID |
无请求体、无查询参数(`X-Admin-Id` 由网关注入,客户端传值忽略)。
#### 出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| groupBatchId | string(Long) | 团期主订单 ID(字符串防精度丢失) |
| eventCode | string | 本次投递的事件码,如 `GROUP_BATCH_FORMED` |
| recipientCount | int | 本次投递的收件人数(= 活跃子订单户数) |
| enqueued | boolean | 是否成功入队(false 时接口已抛 589595,本字段恒 true,保留供排查) |
| notifiedAt | string(datetime) | 本次补发时间 |
| cooldownSeconds | int | 下次可补发前的冷却秒数(固定 300) |
#### 请求示例
```http
POST /v3/admin/order/group-batch/2096412454643802114/notify-formed
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{
"code": 200,
"msg": "成功",
"data": {
"groupBatchId": "2096412454643802114",
"eventCode": "GROUP_BATCH_FORMED",
"recipientCount": 6,
"enqueued": true,
"notifiedAt": "2026-09-14T18:00:00",
"cooldownSeconds": 300
}
}
```
#### 空数据 / 降级响应
`notification_event_config` 未建 `GROUP_BATCH_FORMED` 行时,消息仍成功入队(本端点仍返 200 + recipientCount),dispatcher 侧走「config 未找到」分支、零渠道触发;客户手机号 / userId 缺失时 dispatcher 侧走「无接收人」日志降级,order-v3 不预校验。
#### 错误响应
```json
{ "code": 589593, "msg": "成团通知补发过于频繁,请 180 秒后再试", "data": null }
```
冷却期内重复补发 → **589593**(`{0}` 渲染为剩余秒数);团期无活跃子订单 → **589594**(并释放冷却键);MQ producer 不可用 / 投递失败 / Redis 不可用 → **589595**(并释放冷却键);团期不存在 → 589500;仍招募中或已流团(CANCELLED)→ 589552;无 `group-batch:manage` → 589507。
#### 业务边界
- 编排顺序严格:权限 → 存在性 → 状态门(∈ RESOURCE_PREPARING 及其后续已成团态;RECRUITING / CANCELLED 拒)→ 冷却 SETNX → 收件人 → 投递。
- 冷却键 `order:gb:notify-formed:{groupBatchId}` 必设 TTL 300s;**投递阶段失败(589594/589595)主动删冷却键**——冷却是防轰炸不是惩罚失败,让运营可立刻重试。Redis 不可用时按 589595 处理(宁可拒绝,不在防轰炸失效时放行)。
- 收件人 = 活跃(非 COMPLETED / CANCELLED)子订单户,一户一条消息带其 `userId`;`recipientCount` 为户数。
- 全程无事务:只读 + Redis 写 + MQ 发,不写任何业务表;发送日志由通知中心 `notification_send_log` 记录(user-service 职责)。
### 2. 成团 `POST /v3/admin/order/group-batch/:groupBatchId/group`
**VO**: `Result<Void>`(path: groupBatchId;body: `GroupBatchFormReqVO`,整体选填)
#### 使用场景
团期成团弹窗。**本单不改契约**——请求体 `GroupBatchFormReqVO`(`formedNote` 选填)、响应 `Result<Void>`、错误码(589500/589501/589507/589537)全部不变。改的只是行为:成团成功后现在会给客户发成团通知。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| groupBatchId | path | string(Long) | 是 | 雪花 ID | 团期主订单 ID |
| formedNote | body | string | 否 | `@Size(max=256)` | 提前成团理由(本单不动此字段) |
#### 出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| data | null | `Result<Void>`,成团成功 data 恒 null(契约不变) |
#### 请求示例
```http
POST /v3/admin/order/group-batch/2096412454643802114/group
Authorization: Bearer <admin token>
Content-Type: application/json
{}
```
#### 响应示例
```json
{ "code": 200, "msg": "成功", "data": null }
```
#### 空数据 / 降级响应
成团后通知投递为**事务外**动作:无活跃子订单只 log.warn、MQ producer 不可用只 log.warn、单条投递异常只 log.error——**一律不冒泡**,接口响应恒为 `{"code":200,"data":null}`。
#### 错误响应
```json
{ "code": 589500, "msg": "团期不存在", "data": null }
```
错误码**一字未变**(589500 团期不存在 / 589501 / 589507 无权限 / 589537)。通知失败被吞,**不产生任何新错误码**。
#### 业务边界
- 成团原子事务由 `GroupBatchFormTxService#formAtomically` 开,返回即已提交;通知调用插在 `doGroup` 末尾(该方法第 3/4 步本就在事务外),故通知天然在事务外投递。
- **自动成团**(达门槛定时任务)与手动成团共用 `doGroup`,同获通知行为。
- 通知内部再走 afterCommit 注册 + 无活动事务立即执行 的双路兜底,将来若有人把 doGroup 折进事务语义仍正确。
## 四、契约约束与正确调用方式
- notify-formed 无请求体;`groupBatchId` 走路径。重复补发受 300s 冷却约束,前端应对 589593 展示「请 N 秒后再试」(N 从 message 取)。
- 成团弹窗若有「通知客户」勾选框:后端无对应入参,勾不勾都发——请去掉或改为纯说明文案。
- 金额无涉;`groupBatchId` 字符串回传防 JS 大整数精度丢失。
## 五、数据库行为
无 DDL、无迁移、无新表新列。冷却状态只在 Redis(带 TTL,不持久化)。通知发送记录由通知中心侧 `notification_send_log` 记录(user-service 职责,本单不改 user-service 一行)。
## 六、边界行为
- 逐户容错与通知顺序:成团状态已 CAS 提交、通知已发,个别户子订单推进失败——短信内容仍为真(团确已成团),逐户失败有日志且下一轮 job 重扫。
- 历史脏数据:2026-09-11 前已成团团不自动补发(本单不做回溯),运营需要就点补发端点。
- 300s 冷却跨实例:Redis 共享天然一致。
## 七、不影响范围
- 成团 / 取消成团 / 流团 / 调名额 / 预支 / 退单 / 转订单等既有端点结构与错误码零改动。
- user-service 一行未改;通知中心 publish / dispatch 端点复用不改。
- 无网关路由新增(`/v3/admin/**` 前缀既有覆盖)。
## 八、测试环境已验证
TEST 网关(`api.test.1814.love:9443`)实测,特性分支 `feat/7534-formed-notify`(HEAD `3fc5d7d6b`)已部署两实例(8086/8186):
1. **补发端点成功**:`POST .../group-batch/2089713737135964161/notify-formed` → `{"code":200,"data":{"groupBatchId":"2089713737135964161","eventCode":"GROUP_BATCH_FORMED","recipientCount":4,"enqueued":true,"notifiedAt":"2026-09-14 19:59:11","cooldownSeconds":300}}`(4 活跃户逐户投递,`enqueued=true` 证 TEST RocketMQ 真实入队)。
2. **冷却期内 589593**:同团 13 秒后再次调用 → `{"code":589593,"message":"成团通知补发过于频繁,请 287 秒后再试"}`(`{0}` 已渲染为剩余秒数 287,非字面 `{0}`)。
3. **成团端点契约未变**:`POST .../group-batch/2089713737135964161/group`(已成团团)→ `{"code":589537,"message":"团期已成团,不可重复成团"}`(既有码、`Result<Void>` 信封一字未变)。
**AC-8 通知配置行**:TEST `hl_user_service.notification_event_config` 查无 `GROUP_BATCH_FORMED` 行——属 jw / 运营在通知中心管理后台的配置动作(eventCode 最终取值亦待 jw 确认);未建时 dispatcher 走「config 未找到」分支、链路照通、零渠道触发(口径第 5 条不阻塞代码)。上线前须先建该行否则等于没发。
## 十、相关文档
- 工单 #7534;本单为「成团通知」链路第 1 步,转订单同类缺口第 2 步另开单。
## 关联 / 联系人
- 后端 jw;前端 mmg(成团弹窗勾选框调整 + 团期详情/看板「补发成团通知」按钮)。
- 通知中心配置(`notification_event_config` 建 `GROUP_BATCH_FORMED` 行 + 配模板 + ≥1 渠道 enabled):jw / 运营。