feat: add report material workflows and gateway safeguards
This commit is contained in:
@@ -1306,21 +1306,21 @@
|
||||
- 如果存在多条候选或无候选,则拒绝归因,不得误绑到其他短信。
|
||||
- 已经由新通道成功送达的短信,旧尝试迟到回执仍只记历史,不覆盖最终送达状态。
|
||||
|
||||
### TC-GW-012 SubmitCommand 死信入库与人工重入队
|
||||
### TC-GW-012 Gateway 提交异常入库与人工重新入队
|
||||
|
||||
- 优先级:P0
|
||||
- 前置条件:Gateway submit worker 已连接 Redis Stream `gateway.submit.commands`;准备一条会持续触发处理错误的 `SubmitCommand`;NestJS `/gateway/events/dead-letter` 和运营端 `/api/admin/operations/gateway-submit-dead-letters` 真实可用。
|
||||
- 前置条件:Gateway submit worker 已连接 Redis Stream `gateway.submit.commands`;准备一条会持续触发处理错误的 `SubmitCommand`;NestJS 内部异常上报接口和运营端 Gateway 提交异常 API 真实可用。
|
||||
- 步骤:
|
||||
1. 让同一条 `SubmitCommand` 连续处理失败,达到 Gateway 配置的死信阈值。
|
||||
1. 让同一条 `SubmitCommand` 连续处理失败,达到 Gateway 配置的异常转存阈值。
|
||||
2. 检查 Redis PEL 中该消息是否被 ack,不再无限 pending。
|
||||
3. 检查 NestJS 是否在真实数据库写入一条 `GatewaySubmitDeadLetter`,保存失败原因、尝试次数和原始命令载荷。
|
||||
4. 调用运营端真实接口查询死信列表。
|
||||
5. 调用人工重入队接口,将该死信重新写回 `gateway.submit.commands`。
|
||||
4. 从运营端“Gateway提交异常”页面查询异常列表并打开脱敏详情。
|
||||
5. 完成风险确认、原因和状态校验后,将该异常命令重新写回 `gateway.submit.commands`。
|
||||
- 预期结果:
|
||||
- 达到阈值后,Gateway 会把该消息转为死信,而不是永久卡在 PEL。
|
||||
- 死信记录来自真实数据库,包含 `streamMessageId`、`messageId/submitId`、失败原因、尝试次数和原始 `SubmitCommand`。
|
||||
- 人工重入队成功后,死信状态更新为 `requeued`,记录新的 Redis Stream 消息 ID,并写系统日志。
|
||||
- 重入队后如后续收到真实 `SubmitResult`,对应死信记录应自动转为 `resolved`。
|
||||
- 达到阈值后,Gateway 会把该消息转存为提交异常,而不是永久卡在 PEL。
|
||||
- 异常记录来自真实数据库,包含 `streamMessageId`、`messageId/submitId`、失败原因和尝试次数;浏览器端只收到脱敏命令,不收到原始 payload 或密码密钥。
|
||||
- 人工重新入队成功后,异常状态更新为 `requeued`,记录新的 Redis Stream 消息 ID、操作人和原因,并写系统日志。
|
||||
- 重新入队后如后续收到真实 `SubmitResult`,对应异常记录应自动转为 `resolved`。
|
||||
|
||||
### TC-GW-013 下游客户在线时周期补投与失败封顶
|
||||
|
||||
@@ -3213,6 +3213,15 @@ npm run test:gateway
|
||||
npm run verify:phase8
|
||||
```
|
||||
|
||||
## 2026-07-15 通道 TPS 归属与通道组成员字段清理
|
||||
|
||||
| 用例编号 | 场景 | 操作 | 预期结果 |
|
||||
| --- | --- | --- | --- |
|
||||
| TC-CHANNEL-TPS-001 | 单通道限速 | 通道 A 配置 100 TPS,持续提交超过 100 条/秒 | PostgreSQL 保存 `SmsChannel.rateLimitPerSecond=100`;NestJS 以通道 A 的 ID 建立 Redis 限速桶;单个自然秒放行不超过 100 条。 |
|
||||
| TC-CHANNEL-TPS-002 | 多通道独立限速 | 通道 A、B 均配置 100 TPS,并发向两个通道提交 | A、B 使用不同通道 ID 的 Redis 限速桶,各自最多 100 TPS;平台不存在共享的 100 TPS 总额度。 |
|
||||
| TC-CHANNEL-TPS-003 | 多通道组共享同一物理通道 | 两个通道组同时选中通道 A 并产生发送 | 两组发送共享通道 A 的同一个 100 TPS 限速桶,合计不超过通道 A 配置;通道组成员接口和页面均无单独流速字段。 |
|
||||
| TC-CHANNEL-TPS-004 | 数据库结构清理 | 执行 Prisma migration 并读取 `SmsChannelGroupItem` 结构 | `SmsChannelGroupItem.rateLimitPerSecond` 已删除,通道自身的 `SmsChannel.rateLimitPerSecond` 保留。 |
|
||||
|
||||
测试环境必须具备 PostgreSQL、Redis、MinIO 或等价本地服务后,才能将 E2E smoke 和真实 API HTTP 测试记为系统功能通过;缺失时对应用例标记为阻塞或未执行。
|
||||
|
||||
## 17. 新增和更新用例细化执行清单
|
||||
@@ -3419,3 +3428,27 @@ npm run verify:phase8
|
||||
| TC-GW-ACK-005 | 客户 Submit 后由业务校验立即生成失败回执,并覆盖在线即时投递、Gateway 重启后恢复投递;另模拟客户端对 `Msg_Id=0` 返回 Result=0。 | 客户收到的第一个响应包必须是对应 `CMPP_SUBMIT_RESP`,之后 Deliver 的 `Msg_Id` 非 0 且与 SubmitResp 完全一致;重启后根据持久化 Submit Sequence_Id 重建同一 Msg_Id;Result=0/Msg_Id=0 不得写为 delivered。 |
|
||||
| TC-GW-ACK-006 | 对一条 Gateway 已丢失原消息映射且 payload 缺少 `submitSequenceId` 的历史状态回执执行人工重投;另对字段完整但客户离线的记录重投。 | 缺少序列号的记录由 Gateway 返回 `retryable=false/MISSING_SUBMIT_SEQUENCE_ID`,NestJS 立即终结为 `failed` 并保存明确原因;客户暂时离线的记录返回 `retryable=true/CLIENT_DISCONNECTED`,按次数上限和指数退避继续处理,不得无限保持 `retryCount=0`。 |
|
||||
| TC-GW-ACK-007 | 构造一条从未重投且 pending 超过 72 小时的记录,以及一条人工重投后尚未满 72 小时但原 `createdAt` 很早的记录,执行自动扫描并查看告警。 | 第一条自动转为 `failed/queue_timeout`;第二条仍保持 pending,终结时间和 10 分钟积压告警均从 `lastRetriedAt` 重新计算,刚重投后不立即告警。 |
|
||||
|
||||
### 17.12 签名与引流资料导入及统一通道报备
|
||||
|
||||
| 用例编号 | 操作 | 预期结果 |
|
||||
| --- | --- | --- |
|
||||
| TC-REPORT-MATERIAL-IMPORT-001 | 将含两行表头、文本列和营业执照/身份证等内嵌图片的 WPS 在线表格另存为 `.xlsx`,选择企业、应用和签名资料后解析。 | NestJS 读取真实工作表及图片锚点,返回列、组合表头、前十行和图片数预览;原文件写 MinIO,导入批次写 PostgreSQL;未确认前不改签名、不建通道任务。 |
|
||||
| TC-REPORT-MATERIAL-IMPORT-002 | 将源列分别映射到短信签名、签名用途和动态报备字段,调整数据类型/必填/转换规则,保存映射方案后确认导入;再用列顺序不同但表头相同的文件复用方案。 | 新签名或已存在签名的资料真实入库,内嵌图片拆出并写 MinIO 引用,材料版本递增且进入待报备池;映射方案持久化并可再次选择,源列顺序不影响目标字段。 |
|
||||
| TC-REPORT-MATERIAL-IMPORT-003 | 导入引流资料,将所属签名、站点、URL、备注和动态图片映射后确认;其中一行引用不存在或未审核签名。 | 合法行创建/更新真实 `SmsDrainageInfo` 并进入待报备池;非法行记录行号和原因,批次为部分失败,不因单行错误回滚其他合法行,也不自动创建报备任务。 |
|
||||
| TC-REPORT-CHANNEL-FIELD-001 | 在同一通道分别打开签名和引流字段配置,添加字段、修改通道表头、上下排序、设置必填/列宽/图片宽高后保存并刷新。 | 两类配置相互独立且完整持久化;刷新后字段池、映射表头和顺序一致;重复字段、停用字段和非法尺寸由 API 拒绝或归一化。 |
|
||||
| TC-REPORT-BATCH-001 | 一个应用配置两个生效通道,选择一个待报备签名创建统一批次。 | 系统从真实应用路由展开两个通道,生成两个独立通道任务和两个 `.xlsx`;每个文件表头名称、列顺序和列宽均来自对应通道配置,批次可下载两份文件。 |
|
||||
| TC-REPORT-BATCH-002 | 两个通道对同一标准字段配置不同表头和顺序,并包含图片列,生成批次后分别用 WPS 打开。 | 两份工作簿各自使用对应通道映射,图片直接显示在数据行内且尺寸按通道配置;文件不是 URL 清单,文本与图片属于同一材料快照。 |
|
||||
| TC-REPORT-BATCH-003 | 分别制造无生效路由、通道未配置字段、缺少通道必填图片,再创建批次。 | 对应资料不会清除待报备标记;有通道但资料不全时任务为 `waiting_material` 并记录原因;批次为部分失败,无任何假成功任务。 |
|
||||
| TC-REPORT-BATCH-004 | 同一签名修改资料后再次选择生成批次。 | 材料版本递增;复用同一签名/通道任务并重置到新一轮状态,批次项目保留当次版本和快照,历史导出文件仍可追溯。 |
|
||||
|
||||
### 17.13 Gateway 提交异常与通道级 TPS 限速
|
||||
|
||||
| 用例编号 | 操作 | 预期结果 |
|
||||
| --- | --- | --- |
|
||||
| TC-GW-SUBMIT-EXCEPTION-001 | 制造一条超过 Gateway 最大处理次数的真实 `SubmitCommand`,打开运营端“Gateway提交异常”,按状态、应用、通道和关键字筛选并查看详情。 | 记录写入 PostgreSQL,页面汇总、分页和详情来自 NestJS API;手机号脱敏,命令中的密码、密钥和原始 payload 不返回浏览器,页面不使用“死信”作为业务名称。 |
|
||||
| TC-GW-SUBMIT-EXCEPTION-002 | 对短信仍处于 pending/failed、通道 active 且 connected 的异常记录,输入 5~500 字原因,勾选“已确认上游未受理”并重新入队。 | 近期认证通过后服务端原子抢占记录、真实写入 Redis Stream;记录变为 requeued,人工次数、操作人、原因、Stream ID 和时间完整留痕,收到 SubmitResult 后变为 resolved。 |
|
||||
| TC-GW-SUBMIT-EXCEPTION-003 | 不勾选确认、原因过短、重复点击同一记录,或分别把短信置为 accepted/submitted/delivered/unknown、把通道置为停用/断开、人工重试达到 3 次后尝试重新入队。 | API 拒绝危险或重复操作,不产生额外 Stream 命令;页面显示可读原因,操作日志不伪造成功。 |
|
||||
| TC-GW-RATE-001 | 给通道 A 配置 10 TPS,连续投递 20 条;通道 B 同时配置 20 TPS 并投递,另让提交命令携带高于通道配置的数值。 | Gateway A 实际提交节奏不超过 10 TPS,B 独立按自身额度执行;消息值不能放大 A 的权威上限,同一通道跨通道组共享额度。 |
|
||||
| TC-GW-RATE-002 | 超过通道 TPS 后观察 Redis Stream consumer group,并在存在等待消息时重启 Gateway。 | 超流速消息保留在 Stream pending,不直接失败;重启后通过 PEL/XAUTOCLAIM 恢复并继续按通道 TPS 排队提交,不丢失、不重复 ACK。 |
|
||||
| TC-GW-RATE-003 | 启动两个共享同一 Redis 的 Gateway 消费实例,同时向同一通道发送,再向两个不同通道发送。 | 同一通道的两个实例共享 Redis 限速额度,总 TPS 不叠加;不同通道使用独立 key,不被合并成平台总 TPS。 |
|
||||
|
||||
Reference in New Issue
Block a user