feat: harden CMPP delivery and platform workflows
This commit is contained in:
@@ -2008,3 +2008,44 @@ git diff --check
|
||||
- 工作区完整功能提交 `faa716b8d07ea77fae3ec41c858b52f6a341e6b9` 和重启恢复修正提交 `23a1f6fa15445dbe6d2e4738b10b545b7c657472` 已 push。两次发布前备份分别位于 `/opt/cmpp-platform/backups/releases/20260716-175500` 和 `/opt/cmpp-platform/backups/releases/20260716-180244`,PostgreSQL、运行源码和环境配置均通过 gzip/tar 完整性及 SHA-256 校验;最终发布包本地与服务器 SHA-256 均为 `c34c0fb627b77aa0c1a3d08169b3aff8d04587544ed9c1d83d8c0cd41b007272`。
|
||||
- 生产 migration `20260716150000_expand_money_precision_to_four_decimals` 已应用,53 条 migration 齐全,`SmsApplication.customerUnitPrice` 等金额列已为 `BIGINT`。生产 API/Gateway health、PostgreSQL、Redis PONG、MinIO、Nginx 及 `12026/17890/8090/3000/9000` 监听均正常;两个真实通道 TPS key 均为 100,`gateway.submit.commands` 为 `pending=0、lag=0`,部署后 API/Gateway 无 error 级日志。
|
||||
- 生产公网 CMPP 参数已配置为 `8.160.169.106:17890`。不符合白名单的 `715011 / 183.194.97.158` 陈旧连接在超时窗口后自动清除,合法账号 `910887` 由新 Gateway 建立新连接并持续更新心跳,证明白名单和重启后连接名额恢复逻辑真实生效。Chrome/Playwright 复核运营登录、390px 客户端登录和客户 Swagger 文档均为 200、无横向溢出、无 console/page error;未绕过验证码、未修改账号、未发送短信。
|
||||
|
||||
## 2026-07-18 CMPP 多号码 Submit 首号码静默丢弃修复(未提交、未部署)
|
||||
|
||||
- 生产只读复现确认:账号 `695829` 的 CMPP 3.0 Submit 日志记录 `dest_count=2`,Gateway 返回 `result=0`,但只将首号码 `188****3795` 传入 NestJS 并创建一条 `SmsMessageRecord`;随后单独提交第二个号码 `131****0092` 才产生第二条记录。核查期间未修改生产配置、数据或进程。
|
||||
- 根因为 Gateway 已完整解码 `DestTerminalId[]`,但处理时固定读取下标 `0`;Gateway→NestJS 契约和 `submitInboundMessage` 也只有单数 `phoneNumber`,NestJS 固定以 `phoneTotal=1` 创建内部批次和短信记录。当前行为属于“返回成功但静默丢失后续号码”,不是可接受的单号码范围限制。
|
||||
- Gateway 入站契约新增完整 `phoneNumbers`,保留首号码字段兼容;NestJS 在任何业务落库前校验全部目标号码,并以最多 10 个并发的有界批次逐号码复用现有真实模板/签名、风控、余额、计费、队列和失败回执链路。每个号码创建独立 `sourceType=cmpp` 内部批次和 `SmsMessageRecord`,API 返回全部内部 messageId 映射。
|
||||
- 一个客户 Submit 仍只返回一个 CMPP SubmitResp/Msg_Id。Gateway 将该 Msg_Id 同时关联到本包全部内部 messageId,后续每个号码的 Deliver Receipt 使用相同原 Submit Msg_Id,并通过自己的 `DestTerminalId` 区分;连接断开时同步清理同一连接下全部消息映射。新增 migration `20260718130000_add_cmpp_submit_group_message_id`,为每条拆分记录持久化同一 `cmppSubmitGroupMessageId`;Gateway 重启恢复时结合该分组 ID 与原 Sequence_Id 重建相同 Msg_Id,不再按各内部 messageId 算出不同结果。
|
||||
- 新增 API 回归覆盖两号码分别落库、分别生成失败回执,以及任一号码非法时整包落库前拒绝;Gateway 真实 CMPP 3.0 TCP 集成用例改为一次提交两个号码,并验证第二个内部消息的 Deliver 携带第二个号码且 Msg_Id 与原 SubmitResp 相同,另覆盖重启恢复时两个内部 messageId 仍重建同一 Msg_Id。API 定向测试 1 suite/60 项、API 全量 20 suites/215 项、Gateway `internal/inbound` 定向测试、Gateway 全量 `go test ./...`、API TypeScript build、前端 TypeScript/Vite build、Prisma generate/validate 均通过;本地 PostgreSQL 已应用 54 条 migration 且 schema 最新,前端仅有既有 chunk size warning。
|
||||
- 使用真实本地 NestJS API、PostgreSQL 和 Redis 创建临时接口关闭应用,向 `/api/gateway/events/inbound/submit` 一次提交两个合法号码:API 返回 `phoneCount=2` 和两个内部 messageId,数据库真实生成 2 个 `sourceType=cmpp` 内部批次、2 条短信记录、2 条失败回执和 2 条下游投递;两条记录的 Sequence_Id 与 `cmppSubmitGroupMessageId` 分别一致,下游 payload 只有 1 个分组 ID。再提交“一个合法号码 + 一个非法号码”返回 HTTP 400,记录数保持不变。临时企业、应用、白名单、批次、短信、回执和投递已全部清理,未连接上游 Gateway、未发送短信。
|
||||
|
||||
## 2026-07-18 手工验收瑕疵修复(未提交、未部署)
|
||||
|
||||
- 已实现图片 2MB/其他文件 10MB 前端与 NestJS 双层校验,通用文件和报备材料上传不再允许 20MB/100MB;企业信用代码收紧为英文字母与数字,营业执照操作样式和长文件名换行已修复。
|
||||
- 企业应用 HTTP 开通时默认开启六项能力并使用 HTTP Webhook,参数复制改为中文业务文案;应用列表查询/重置每次重请真实 API,CMPP 连接详情改为无水平滚动的自适应卡片;桌面侧栏强制隐藏移动端关闭按钮。
|
||||
- 审核 API 从当前登录会话写入审核人,列表时间格式化,审核人/时间收入“更多信息”弹窗,审核成功即时刷新导航角标。短信记录查询/重置重请 API,详情新增客户提交接入号与上游发送接入号。
|
||||
- 引流列表移除重复的“引流信息”列并改名“引流url或号码”;运营商规则改为中文标签,新增真实 NestJS/Prisma DELETE 链路。成功登录由用户服务写入 `auth.login_success`,其他会话用户成功写操作由统一 `OperationLog` 中间件兜底,不记录请求体和密钥。
|
||||
- 定向验证通过:API TypeScript build;文件、企业、风控审核、用户登录与人工操作日志 5 个 Jest suites/27 项;前端 TypeScript/Vite build;Prisma validate;`git diff --check`。前端仅有既有 chunk size warning。API 全量 Jest 已尝试,21 suites 中 20 suites、219 项中 218 项通过;唯一失败是另一会话正在修改的 `send-chain.service.spec.ts` 用例在本地 Redis `127.0.0.1:6379` 未运行时连接超时,本次未改动该套件。未在生产写入业务数据,未发送短信。
|
||||
- 生产只读排查:`MSG-22a27cd0-b671-4ae8-8beb-6608eaf04517` 已有真实未达回执,但无 `CmppDownstreamDelivery`/HTTP Webhook 事件;该问题与另一会话正在修复的 CMPP 多号码 Submit/Msg_Id 分组链路相互耦合,本次未修改 send-chain、Gateway inbound、Prisma schema 及其 migration,避免覆盖并行修复。“今日返还”数据核查发现当前包含发送链在路由/模板校验前的冻结后释放,余额、日限、校验顺序与返还口径待并行发送链修复后再联合回归。
|
||||
|
||||
## 2026-07-20 LG 缺陷修复与本地真实链路验证(未提交、未部署)
|
||||
|
||||
- 保留并识别并行会话的 CMPP 多号码 Submit、分组 Msg_Id 和 Gateway 入站差异,本轮未覆盖或重写其 Gateway 文件;既有两号码、混合非法号码及重启恢复测试随 Gateway/API 全量测试通过。
|
||||
- 回执关联改为内部消息 ID 或 `channelId + gatewayMessageId + phoneNumber` 的唯一提交记录,新增稳定 `receiptKey` 数据库唯一约束和并发冲突兜底。主记录同步保存真实通道、通道消息号、原始状态、文本和到达时间,重复 DELIVRD 不再重复客户投递。
|
||||
- 对账、应用/通道利润和质量报表统一补充失败数、退款、真实成本、利润及到达时长,并由既有 T-4 至 T-1 重算覆盖延迟回执。真实 PostgreSQL 重算样本为发送 2、成功 1、失败 1、收入 352、退款 352、成本 400、利润 -48、平均到达 5000ms;重复执行一致,临时数据已清理。
|
||||
- 用户接口增加运营端/客户端安全 DTO,移除密码散列、会话版本和认证内部字段;后端阻止自删除/自停用、最后一个平台/企业管理员删除或降权,并将 Prisma 唯一冲突映射为 409。逻辑删除后的用户名继续保留,确保历史审计关联稳定。
|
||||
- HTTP 单发改为仅凭 `mobile/content` 自动识别已审核签名、模板和变量,复用 CMPP 发送规则;修复 API 来源批次创建后按客户端来源读取导致的 404,OpenAPI 增加稳定请求 Schema 和示例。客户端发送候选默认只返回 approved 签名/模板,变量拒绝未闭合、空、中文、重复、非法或超长名称。
|
||||
- 报备资料新增官方 XLSX 模板和按筛选导出接口,导入拒绝公式及公式注入单元格;分析、提交、导出日志保存当前操作人、文件名、筛选和成功/失败数。黑名单重复唯一冲突返回 409,逻辑删除记录按既定规则恢复。
|
||||
- 本地 Redis `PING=PONG`,真实 NestJS API health 返回 ok。使用真实 PostgreSQL、Redis 与 `/api/gateway/events/receipt` 验证同一通道 Msg_Id 双通道场景:A 保持 submitted,B 正确 delivered,重复回执返回同一消息且仅一条回执记录。回执目的号码已持久化并建立三字段匹配索引;Prisma 本地 57 条 migration 已应用且 schema 最新。
|
||||
- 验证通过:API 全量 Jest 21 suites/237 tests、回执与报备材料定向 2 suites/68 tests、用户安全与报备材料定向 2 suites/17 tests、API TypeScript build、Gateway `go test ./...`、前端 TypeScript/Vite build。Jest 仍有既有异步句柄退出提示,断言全部通过;前端仍有既有大 chunk 警告。生产未写入数据、未发送短信,代码未提交、未 push、未部署,生产仍需获批发布后复测。
|
||||
- `npm run verify:phase8` 两次完整尝试都在同一个 BullMQ 15,000 条性能门槛停止,分别为 375.00 TPS 和 404.53 TPS,未达到 500 TPS;同一脚本隔离复测为 907.93 TPS 并通过。因此功能与契约断言未失败,但完整阶段命令受本机共享 Redis/瞬时负载影响,按“环境性能不稳定”记录,不能标记为完整通过。
|
||||
- Browser 插件已打开真实本地运营端,登录页、验证码和前端到 NestJS API 代理正常;登录后报表页面验收受图形验证码安全约束阻塞,本轮未代解或绕过验证码,不能标记为页面通过。本地临时管理员、角色及关联数据已清理,临时 API/前端进程已关闭。
|
||||
- 另通过真实本地登录 API、HttpOnly 会话、PostgreSQL 与 NestJS 路由完成后端验收:`GET /admin/users` 找到临时用户且 `passwordHash/sessionVersion/failedLoginCount` 泄漏字段为空;当前用户自删返回 403;官方签名资料模板下载返回 200、XLSX 6952 字节;利润查询返回真实聚合行。数据库 `OperationLog` 记录 `report_material.template_downloaded`、正确操作人、文件名、成功数和非空 IP;所有临时用户、角色、关联和日志随后已清理。
|
||||
|
||||
## 2026-07-20 平台LG二轮UI/UX阶段A启动与LG2-P0-01修复(未提交、未部署)
|
||||
|
||||
- 已完整读取二轮最终报告、执行进度、基线、页面矩阵、设计规范、可访问性、五视口、性能稳定性、高风险、会话导航、角色边界,以及阶段A相关客户端组件/核心流程、运营核心流程、表单、列表和弹窗专项报告;查看了375/390手机阻断截图与1280桌面列宽截图。在测试文档目录建立45项唯一问题整改台账和阶段A源码/测试/风险映射。
|
||||
- 根因确认:公共Table在`≤780px`已经转为卡片,但客户端用户页四个动作继续使用不可换行`.inline-actions`,父卡片`overflow:hidden`把末尾删除裁掉。修复仅修改`ClientUsersPage.tsx`并新增页面专用CSS;手机/平板操作区变为2×2网格、按钮高度44px,未修改已有并发变更的公共`global.css`或用户后端。
|
||||
- 真实本地链路验收:启动NestJS、Vite preview、PostgreSQL和Redis,创建唯一临时租户/企业管理员,经真实验证码、登录会话、`/api/client/users`读取数据。375视口点击删除只打开含目标用户名的确认层并取消,记录未删除;随后退出并精确清理临时租户、用户、角色关联和操作日志,复核`cleaned=true`。未发送短信、未充值、未修改生产数据。
|
||||
- 五视口Browser验收:375×667、390×844和768×1024四项操作全部可见、中心可命中、高度44px且页面无横向溢出;1440×900全部首屏可见;1366×768保留既有55px表格内部横向滚动,滚动后删除完整可达,该桌面列宽问题继续归入LG2-P1-03/P2-04。控制台`error/warn=0`。截图保存在测试项目`平台LG_UIUX二轮走查证据/整改_20260720_LG2-P0-01/`。
|
||||
- 自动化:API用户服务1 suite/11 tests、API TypeScript build、前端TypeScript/Vite build、Prisma validate/migrate status、Redis PONG和`git diff --check`通过;本地共有56条migration且schema最新。项目当前没有前端lint或组件测试脚本,未虚报通过。首次前端build被另一会话新增`includeHistory:boolean`查询类型拦截,已在`adminApi.ts`做字符串序列化的最小兼容,不改变业务语义。
|
||||
- `LG2-P0-01`在整改台账标记“已通过”;代码未提交、未推送、未部署,生产仍运行旧版本,生产P0尚未发布。
|
||||
|
||||
Reference in New Issue
Block a user