feat: integrate analytics and fragment receipt improvements

This commit is contained in:
hectorzhao
2026-08-03 15:34:41 +08:00
parent 3357ace7e1
commit 530a65de80
89 changed files with 1964 additions and 324 deletions
+47 -8
View File
@@ -1447,6 +1447,7 @@
- `awaiting_ack` 记录在前端不可选且后端拒绝并发重投,不能仅依赖按钮禁用。
- 确认弹窗明确展示投递类型、消息 ID 和重复处理风险;取消时不得请求后端,确认提交期间操作按钮禁用并显示处理中状态。
- 单条成功弹窗展示真实返回的当前状态和 `manualRetryCount`;接口失败时弹窗展示错误,并提醒先刷新核对人工次数再决定是否重试,不能静默失败或诱导重复提交。
- 筛选区使用平台共享查询控件宽度;桌面端空间不足时条件按完整控件自然换行,不得把关键字、日期、状态、类型、应用和操作按钮挤压在同一行;移动端条件整行展示,查询与重置按钮清晰可操作。
### TC-GW-015 下游投递指数退避
@@ -1598,6 +1599,7 @@
- 页面刷新后恢复状态仍然存在,可继续用于生产排查。
- 页面解释恢复状态与逐条下游投递记录的用途差异;恢复状态和下游投递记录默认均选择近 7 天。
- 恢复状态按更新时间区间筛选,摘要、失败分类、列表和 CSV 导出使用同一时间口径;列表标题与外框保持正常内边距,最后错误/跳过原因列具备可读宽度。
- 筛选区复用平台共享查询控件宽度;关键字、日期、状态、失败分类、应用和操作按钮在桌面端按可用空间自然换行,移动端条件整行展示,不出现控件压缩、重叠、截断或按钮混入字段的问题。
### TC-GW-025 多 Gateway 恢复抢占协调
@@ -3445,7 +3447,7 @@ npm run verify:phase8
| TC-BILLING-010 | 余额不足发送失败,人工充值后重试发送并模拟 delivered。 | 充值前不扣费;充值后发送成功;冻结、扣费、短信计费记录完整;reconciliation diff 为 0。 |
| TC-BILLING-011 | 分别准备 `余额+授信` 为正数、0 和负数的账户,使用相同短信费用发起发送。 | 和为正数时允许发送;和为 0 或负数时提示余额不足。判断公式为 `balanceCents + creditCents > 0`,与本次费用和套餐无关。 |
| TC-BILLING-012 | 已扣费短信收到最终失败回执;另一个消息在提交前失败并释放冻结;另准备一笔任务冻结转扣费时的批次级释放。 | 最终失败只生成一条 `refunded` 并计入“今日返还”,重复回执不重复退款;提交前失败生成 `released + relatedType=sms_message_record` 并计入“今日返还”;冻结转扣费的 `released + relatedType=sms_batch_task` 属于内部转换,不计入“今日返还”;客户端和运营端当日金额一致且保留三位小数。 |
| TC-BILLING-013 | 准备已提交扣费但 72 小时完全无回执的 `submitted` 短信,以及有 `UNKNOWN` 回执且超过 72 小时的短信;启动 API 定时扫描并模拟重复扫描。 | 两类短信都转为 timeout 并退款;任务进度刷新;同一短信只退款一次;定时扫描默认启用且每 5 分钟执行。 |
| TC-BILLING-013 | 准备已提交扣费但 72 小时完全无回执的 `submitted` 短信,以及有 `UNKNOWN` 回执且超过 72 小时的短信;分别覆盖HTTP提交、CMPP短短信和多分片长短信,启动 API 定时扫描并模拟投递建单失败后重复扫描。 | 两类短信都转为 timeout、写入`undelivered/EXPIRED/RECEIPT_TIMEOUT`并只退款一次;HTTP产生一个明确失败Webhook,CMPP对每个请求回执的原始分片产生失败状态报告且使用各自SubmitResp Msg_Id;建单未完成时`timeoutReceiptQueuedAt`保持空并由后续扫描补齐,成功建单后不重复;任务进度刷新。 |
| TC-BILLING-014 | 在运营端充值记录中分别打开整数金额、含1至4位有效小数、负数冲正以及缺少可追溯余额的真实订单回执。 | 每行提供“查看回执”;弹窗左上只使用系统真实Logo;企业、订单号、时间、备注与数据库订单一致;可追溯订单的入账前余额等于入账后余额减本次变动;无快照时前后余额不得伪造;主金额整数不显示小数,非整数仅显示有效小数,余额仍显示四位精度;正数显示已入账,负数显示已冲正。 |
| TC-SEC-006 | 安装 API 生产依赖并执行 `npm audit`;使用缺文件、多文件、超大文件、超量字段和正常单文件调用认证后的 multipart 上传接口。 | NestJS/Multer/Hono 已升级或锁定到修复版本,生产依赖 audit 为 0;接口只接受一个不超过 20MB 的文件,并限制字段、part、字段名、字段值和 header pair 数量;异常请求返回受控 4xx,正常文件仍写入真实 MinIO 和 `FileObject`。 |
@@ -3706,7 +3708,7 @@ npm run verify:phase8
| --- | --- | --- |
| TC-USER-REUSE-001 | 新建用户名`zhaohui`,逻辑删除后再次使用同一用户名、邮箱或手机号新建用户。 | 新用户创建成功且主键与旧用户不同;旧用户及其OperationLog、审核关联保持原用户主键;登录只命中新用户。 |
| TC-USER-REUSE-002 | 两个未删除用户并发提交相同用户名、邮箱或手机号。 | PostgreSQL仅允许一个请求成功,另一个返回HTTP 409、`USER_DUPLICATE`、冲突字段和中文提示,不产生两个活动账号。 |
| TC-USER-FILTER-001 | 在运营端分别及组合填写用户姓名、登录账号、所属企业、用户角色和状态,点击查询,再点击重置。 | 每次操作请求真实`GET /api/admin/users`;条件分别生效,组合使用AND,登录账号匹配用户名/邮箱/手机号;重置返回全部未删除用户。 |
| TC-USER-FILTER-001 | 在运营端分别及组合填写用户姓名、登录账号、所属企业、用户角色和状态,点击查询,再点击重置;分别使用桌面和移动视口检查筛选布局。 | 每次操作请求真实`GET /api/admin/users`;条件分别生效,组合使用AND,登录账号匹配用户名/邮箱/手机号;重置返回全部未删除用户。五组条件按共享宽度自然换行,不被压缩或截断;移动端条件整行展示,查询/重置与“新增用户”分区清晰且均可操作。 |
| TC-USER-FILTER-002 | 在客户端分别及组合填写用户姓名、登录账号和状态。 | 请求真实`GET /api/client/users`;只返回当前企业管理员,无法通过查询参数跨租户或查询平台管理员。 |
| TC-USER-CONTINUITY-UI-001 | 删除、禁用或降权最后一个平台管理员,再删除或停用某企业最后一个启用管理员。 | 平台管理员操作由后端权威拦截并在确认弹窗显示建议;企业管理员操作成功且可归零;按钮结束忙碌状态,浏览器无未处理Promise。 |
| TC-USER-CONTINUITY-UI-002 | 为相同范围增加另一名启用管理员后重复删除或禁用。 | 操作成功、弹窗关闭、列表按当前已应用查询条件刷新,并写入对应OperationLog。 |
@@ -3851,9 +3853,9 @@ npm run verify:phase8
- `TC-PROTOCOL-LOG-008`:一条真实短短信取得成功状态报告后,按同一平台消息号查询应恰好看到四个供应商侧真实业务报文:`平台→通道/CMPP_SUBMIT``通道→平台/CMPP_SUBMIT_RESP``通道→平台/CMPP_DELIVER``平台→通道/CMPP_DELIVER_RESP`;每个报文只出现一条,箭头与抓包传输方向一致,长短信则按实际分片分别记录Submit/SubmitResp。
- `TC-PROTOCOL-LOG-009`:企业应用提交短信时,入站Submit显示“企业应用→平台”,每个实际返回的SubmitResp显示“平台→企业应用”;供应商侧统一显示“平台→供应商通道/供应商通道→平台”,不得再使用含义模糊的客户/通道箭头。
- `TC-RECEIPT-SHARED-010`:供应商账号、Gateway主机、端口、协议和CMPP版本均相同的两个物理通道连接中,回执从副连接进入、原连接存在唯一`gatewayMessageId + DestTerminalId`分片候选时,应写入原提交逻辑通道;账号或端点任一不同、或候选超过一条时不得自动匹配。
- `TC-RECEIPT-LONG-011`:两分片长短信仅收到第一片`DELIVRD`时,`SmsMessageRecord`保持`submitted`且不创建企业应用最终回执;第二片到达后两条分片审计均为`delivered`,主记录只聚合一次为`delivered`,重复回执不得重复投递、扣费或退款。
- `TC-RECEIPT-LONG-011`:两分片长短信仅收到第一片`DELIVRD`时,`SmsMessageRecord`保持`submitted`且不创建企业应用最终回执;第二片到达后两条分片审计均为`delivered`,主记录只聚合一次为`delivered`CMPP按两个原始分片各创建一条`DELIVRD`且分别使用两个SubmitResp Msg_IdHTTP只创建一个最终事件;重复回执不得重复投递、扣费或退款。
- `TC-PROTOCOL-LOG-012`:供应商长短信每个真实分片分别产生一条`平台→供应商通道/CMPP_SUBMIT`和一条`供应商通道→平台/CMPP_SUBMIT_RESP`;内部`submit-result`聚合回调不得额外落协议日志。
- `TC-RECEIPT-LONG-013`:两分片长短信主记录保存首片上游消息号,第二片返回`YL:1014`等任意非成功状态且首片未回执;系统通过第二片审计识别当前提交尝试,整条短信进入失败/补发或退款终态并只投递一次最终失败回执,不再卡在`submitted`
- `TC-RECEIPT-LONG-013`:两分片长短信主记录保存首片上游消息号,第二片返回`YL:1014`等任意非成功状态且首片未回执;系统通过第二片审计识别当前提交尝试,整条短信进入失败/补发或退款终态,不再卡在`submitted`;最终不再补发时,对两个请求回执的原始客户分片分别投递失败状态报告,Msg_Id与各自SubmitResp一致
- `TC-DELIVERY-AUTO-014`:分别配置仅CMPP、仅HTTP、CMPP+HTTP、两者均关闭四种应用状态;回执与上行分别只产生CMPP下游记录、HTTP Webhook事件、两者各一条、均不产生。修改历史手工投递模式不得改变自动计算结果。
- `TC-HTTP-WEBHOOK-015`:运营端关闭HTTP接口后,回执和上行Webhook地址输入框仍显示且可保存;任一地址保存为空时删除对应有效端点,后续不推送该类HTTP事件,另一非空地址不受影响。
- `TC-PROTOCOL-LOG-016`:在线企业应用收到回执或上行 `CMPP_DELIVER` 并返回 `CMPP_DELIVER_RESP`;通讯日志各出现一条“平台→企业应用/DELIVER”和“企业应用→平台/DELIVER_RESP”,结果、消息号、序列号和投递记录一致,下游投递记录仍独立展示发送、ACK和重试状态。
@@ -3934,10 +3936,10 @@ npm run verify:phase8
|---|---|---|
| TC-RETRY-RACE-001 | 三分片长短信的三个失败回执并发进入API,备用通道可用 | 三个回执和通讯报文全部保存;来源提交记录只关联一个补发记录,只发布一个Gateway命令、三个补发分片 |
| TC-RETRY-RACE-002 | 三个线程在唯一补发记录提交前后交错执行 | 只有一个线程取得`retryOfSubmitRecordId`唯一关系;其他线程返回同一下一跳`submitId`并写复用日志,不退款、不生成最终回执 |
| TC-RETRY-RACE-003 | 三个失败回执并发处理且没有可用备用通道 | 主记录最终失败;只产生一笔退款交易和一次余额增量,只生成一个CMPP最终失败回执及一个HTTP回调事件 |
| TC-RETRY-RACE-003 | 三个失败回执并发处理且没有可用备用通道 | 主记录最终失败;只产生一笔退款交易和一次余额增量;HTTP只生成一个最终失败事件,CMPP对三个原始客户分片各生成一条失败回执且每片只生成一次 |
| TC-BILLING-IDEM-004 | 三个线程使用同一短信退款幂等键并发退款 | 三次调用返回同一交易ID,`AccountTransaction`只有一条,账户余额只增加一次 |
| TC-BILLING-ATOMIC-005 | 同一企业同时发生扣费、退款和充值 | 账户级事务锁串行化余额变更,使用数据库原子增量;每条流水`balanceAfter`连续且最终余额与流水一致 |
| TC-DOWNSTREAM-IDEM-006 | 同一短信终态被重复处理,企业同时启用CMPP和HTTP | CMPP有一条`CmppDownstreamDelivery`且只发送一次;HTTP只有一个稳定事件和一条端点投递 |
| TC-DOWNSTREAM-IDEM-006 | 同一短信终态被重复处理,企业同时启用CMPP和HTTP | CMPP每个请求回执的原始客户分片各有一条稳定`CmppDownstreamDelivery`只发送一次;HTTP只有一个稳定事件和一条端点投递 |
| TC-MIGRATION-IDEM-007 | 在含历史重复最终回执的预生产数据上执行migration | 历史行全部保留;每个短信只给最早一条历史回执设置唯一键,其余保持空键;新数据开始强制唯一 |
## 2026-07-27 通道报备发送统计用例
@@ -4251,9 +4253,9 @@ npm run verify:phase8
| TC-REFACTOR-R10-005 | 分片未收齐、全部成功、明确失败或全部未知 | 未收齐不提前成功;全部成功才最终成功;明确失败优先;未知按既有超时和失败口径处理 |
| TC-REFACTOR-R10-006 | 两个执行者并发抢占同一失败短信补发 | 来源提交唯一关系、事务条件更新和P2002唯一冲突处理保证最多创建一个新提交尝试 |
| TC-REFACTOR-R10-007 | 重复执行成功扣费、失败退款或预占释放 | 账务使用原稳定幂等键,同一业务事件只产生一次账户流水,余额和预占不重复变化 |
| TC-REFACTOR-R10-008 | 创建、认领、发送并ACK最终CMPP/HTTP回执 | 每短信只有一最终回执语义;下游去重、attempt记录、ACK确认、超时恢复和失败分类保持 |
| TC-REFACTOR-R10-008 | 创建、认领、发送并ACK最终CMPP/HTTP回执 | 内部每短信只有一最终业务结论;CMPP按原始客户分片投递并逐片去重,HTTP按消息投递并去重;attempt记录、ACK确认、超时恢复和失败分类保持 |
| TC-REFACTOR-R10-009 | 人工重排失败下游投递或恢复陈旧认领 | 使用稳定重排键和原状态条件,已完成或正由其他执行者处理的记录不得重复投递 |
| TC-REFACTOR-R10-010 | 执行回执超时扫描 | 仅处理满足既有时间和状态条件的当前记录;扫描并发保护和终态聚合保持 |
| TC-REFACTOR-R10-010 | 执行回执超时扫描 | 仅处理满足既有时间和状态条件的当前记录;转为明确`EXPIRED`失败并为CMPP逐分片、HTTP逐消息建立可重试下游回执,建单标记未完成时后续扫描可恢复;扫描并发保护和内部终态聚合保持 |
| TC-REFACTOR-R10-011 | 检查七个完成链领域的持久化操作 | 不存在删除历史事故记录的`deleteMany`路径;提交、回执、attempt、死信和账务历史继续保留 |
| TC-REFACTOR-R10-012 | 对真实本地PostgreSQL查询R10八类表 | 查询前后计数完全一致;无分片或attempt样本时如实记为0,不造数、不发短信、不触发补发或重投 |
| TC-REFACTOR-R10-013 | 执行SendChain定向、API全量、前后端构建、Gateway及R0R10门禁 | 112项定向和389项API测试通过;schema、migration、事务语义、Redis契约、CMPP协议及其他业务不因R10改变 |
@@ -4348,3 +4350,40 @@ npm run verify:phase8
| TC-REFACTOR-R11-084 | 打开签名、发送、企业认证、发送详情和模板代表页面 | 各单页面样式仍由原global规则托管,页面结构、真实接口和交互不因第九步改变 |
| TC-REFACTOR-R11-085 | 在375×812视口检查首页、账单及一个单页面代表 | 标题、卡片、表格和操作区无新增横向溢出,控制台无相关warning/error |
| TC-REFACTOR-R11-086 | 执行前端生产构建、API/Gateway全量、Prisma、安全门禁、全部R0~R11结构门禁和`git diff --check` | 29个API套件/389项测试、API构建、Gateway测试/vet及全部门禁通过;R11九步完成且业务行为不因client共享CSS迁移改变 |
## 2026-08-03 工作台金额与审核筛选回归用例
| 用例编号 | 操作 | 预期结果 |
| --- | --- | --- |
| TC-CLIENT-DASHBOARD-012 | 使用客户端企业登录短信服务工作台,对比 Dashboard API、数据库当天消费聚合和页面金额卡 | “今日消费金额”位于“今日返还金额”之前,展示当前企业北京时间当天真实消费金额;刷新后与 API/数据库一致,不使用静态值或浏览器缓存计算 |
| TC-AUDIT-FILTER-014 | 依次打开企业认证、短信、模板、签名、引流信息五个审核页面,选择同一提交日期区间并查询 | 五页均显示日期区间控件;请求携带开始/结束日期,列表只返回 PostgreSQL 中提交时间位于北京时间闭区间内的记录 |
| TC-AUDIT-FILTER-015 | 在签名和引流信息审核页切换到“导入批次审核”,按提交时间查询并翻页 | 导入批次真实分页 API 同时携带日期条件,当前页和总数均受日期范围约束,不在前端仅过滤当前页 |
| TC-AUDIT-FILTER-016 | 分别只选择开始日、只选择结束日、选择跨日范围,再输入非法日期或开始日晚于结束日直接调用 API | 单边范围可正确查询;完整范围包含开始日 00:00:00 和结束日 23:59:59.999;非法或倒置范围返回受控 400,不执行无界误查询 |
| TC-QUERY-LAYOUT-009 | 在风控规则页检查规则范围、平台白名单和号码频次触发记录查询区,并执行查询与重置 | 普通控件使用统一标准宽度,条件可换行但不占满整行;查询/重置按钮等宽;重置后白名单恢复全部有效记录,触发记录恢复“拦截中”并重新请求真实后端 |
| TC-QUERY-LAYOUT-010 | 在五个审核页面及导入审核页签检查普通控件、日期区间和查询/重置按钮,并切换桌面与375×812视口 | 普通控件宽度一致,日期区间明显更宽,查询/重置按钮宽度一致;窄屏转为整行且无横向溢出、遮挡或弹层裁切 |
## 2026-08-03 引流信息识别与统计用例
| 用例编号 | 操作 | 预期结果 |
| --- | --- | --- |
| TC-DRAINAGE-DETECT-001 | 在引流识别规则页新增、编辑、停用规则并刷新 | 所有操作调用真实后端并持久化到 PostgreSQL;版本递增、状态生效并留下操作日志,刷新后不丢失 |
| TC-DRAINAGE-DETECT-002 | 分别测试协议 URL、裸域名、短链接、IP:端口/路径及中文标点相邻链接 | 均识别为含引流,命中类型为 URL,保存原始内容位置;检测不会改写短信原文 |
| TC-DRAINAGE-DETECT-003 | 输入被空格、中文句号拆开的域名以及普通邮箱地址 | 规避域名仍命中;完整或带空格规避的邮箱不被当作引流 URL |
| TC-DRAINAGE-DETECT-004 | 测试`+86 138 0013 8000``138-0013-8000`及中文标点拆分手机号 | 均识别为手机号引流,原文片段可在短信记录中正确高亮 |
| TC-DRAINAGE-DETECT-005 | 测试`0108888-8888 转 123`等固话 | 区号括号、分隔符和分机号均可识别为固定电话引流 |
| TC-DRAINAGE-SEND-001 | 使用待审核、驳回或未报备的既有引流资料分别创建客户端批次和 CMPP 入站任务 | 不产生`DRAINAGE_NOT_APPROVED`,不因引流资料状态拒绝或转人工;其他发送校验仍正常执行,禁止用真实短信完成自动测试 |
| TC-DRAINAGE-RECORD-001 | 查询含引流、不含引流和未检测三类短信记录并导出 CSV | PostgreSQL 分页和总数按筛选值返回;含引流原文使用提示色和片段高亮;CSV“是否含引流”与数据库一致 |
| TC-DRAINAGE-STATS-001 | 打开签名发送质量明细并切换“整体统计/按引流切分” | 整体矩阵显示全部通道×运营商真实提交;切分矩阵分别显示含引流、不含引流、未检测,分组计数之和等于整体计数 |
| TC-DRAINAGE-DASHBOARD-001 | 对比运营看板两个签名统计表与数据库当天数据 | “今日签名发送统计”包含全部短信;“含引流”表只包含`hasDrainageContent=true`,历史 null 不计入含引流 |
| TC-DRAINAGE-SAFETY-001 | 保存超长、非法标志、后行断言、反向引用或嵌套量词规则 | API 返回受控参数错误,不保存可能在发送入口造成灾难性回溯的规则 |
## 2026-08-03 CMPP逐分片回执与超时失败回执用例
| 用例编号 | 操作 | 预期结果 |
| --- | --- | --- |
| TC-RECEIPT-FRAGMENT-001 | 客户提交三分片长短信,每片`Registered_Delivery=1`,供应商三片分别返回成功、成功、失败 | 内部主记录只形成一个最终业务结论;下游产生3条CMPP状态报告,各自Msg_Id等于对应SubmitResp Msg_Id,状态/原始码/时间来自对应上游分片;HTTP只产生1个消息级最终事件 |
| TC-RECEIPT-FRAGMENT-002 | 三分片中第二片`Registered_Delivery=0`,其余两片为1 | 数据库保留三片的真实请求值;下游只为第一、三片建立CMPP状态报告,第二片不生成;不影响内部整条短信状态和HTTP最终事件 |
| TC-RECEIPT-FRAGMENT-003 | 对同一长短信终态并发处理三次并重放重复上游回执 | 每个原始客户分片最多一条`CmppDownstreamDelivery`,唯一键分别含分片序号;已建立分片不重复发,HTTP稳定事件不重复,退款和补发仍只有一次 |
| TC-RECEIPT-FRAGMENT-004 | 客户在线时分别向同一长短信的两个分片推送回执 | Gateway发送的两个`CMPP_DELIVER`回执内容分别携带两个不同的原SubmitResp Msg_Id,不因在线会话按业务消息查找而都复用第一片Msg_Id |
| TC-RECEIPT-TIMEOUT-005 | HTTP消息超过72小时无明确回执,首次Webhook建单失败,下一轮扫描恢复 | 主记录只转一次`timeout`并只退款一次;失败时`timeoutReceiptQueuedAt`为空,后续扫描补建`undelivered/EXPIRED/RECEIPT_TIMEOUT`事件后写入标记,HTTP最终事件只有一个 |
| TC-RECEIPT-TIMEOUT-006 | CMPP长短信超过72小时无明确回执,其中部分片已有真实回执,其余片缺失 | 已有结果的分片保留自己的最终状态;缺失片收到明确`EXPIRED`失败回执;每片使用各自原SubmitResp Msg_Id并可分别ACK,重复扫描不重复发送 |