Files
lislgosms/docs/testing-progress.md
T

2566 lines
364 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 第一版系统化测试进度
> 环境命名:当前 `8.160.169.106:12026`Web/API)和 `8.160.169.106:17890`CMPP 入站)实例统一定义为“预发布环境”。历史记录中涉及该实例的验证、部署和业务页面均按预发布环境理解;`production-deploy.sh`、`NODE_ENV=production` 及正式生产安全/备份规范保留原有技术语义,不代表该实例为正式生产。
## 2026-07-16 客户端签名与引流信息页面重做(已提交、已部署)
- 客户端“签名与引流信息”按运营端信息结构重做为签名父级、引流信息子级的可展开工作台,增加真实后端状态统计、关键字/应用/状态筛选、已交资料数、修改说明、新增/修改/删除确认;客户端文案不再出现通道和内部报备概念。
- 新增客户端专用安全视图和工作台 API。NestJS 查询仅选择客户需要的签名、应用、材料和引流字段;动态资料字段移除通道来源,签名响应移除通道、路由、运营商汇总、报备任务和内部要求快照,避免只靠前端隐藏造成泄露。
- 客户端签名更新补充企业归属校验并重新进入审核;签名列表的顶部统计由 PostgreSQL 状态分组通过真实 API 返回,不使用 mock、localStorage 或前端临时统计冒充。
- Prisma validate/generate/migrate status 通过,本地 PostgreSQL 共 52 条 migration 且无待执行项;SmsConfig 定向 1 suite/37 项、API 全量 20 suites/209 项、API build、前端 build、Gateway `go test ./...``git diff --check` 均通过。Jest 延续既有 open-handle 提示,使用同一全量用例加 `--forceExit` 复核退出码为 0;前端仅有既有 Vite chunk size warning。
- 应用内浏览器可加载最新本地构建,但目标路由因无现成客户端登录态跳转至真实图形验证码登录页;Chrome 也没有可复用的平台登录页。本轮未绕过验证码,因此没有把登录页误记为目标页面视觉通过,登录后的展开、筛选和弹窗交互仍建议补一次可见复测。
## 2026-07-15 短信模板签名自动填充与真实校验(已提交、已部署)
- 客户端和运营端短信模板表单将签名改为必选;选择签名时自动在模板内容开头填入规范 `【签名】`,切换签名只替换原前缀并保留正文,清空选择时移除自动前缀。
- 两套内容输入框均明确提示“模板内容必须以所选签名开头”,字符数、变量识别和计费条数继续基于包含签名的完整内容计算。
- NestJS 在模板创建、编辑和提交审核时校验真实签名归属及完整内容前缀,阻止绕过页面提交缺失或不匹配签名的模板。SmsConfig 定向 1 suite/33 项、API 全量 18 suites/194 项、Prisma validate/generate/migrate status47 条 migration 已应用)、API build、前端 build、Gateway `go test ./...``git diff --check` 均通过;Jest 仍有既有 open-handle 提示,相同全量测试加 `--forceExit` 复核退出码为 0。浏览器交互结果待完成后回填。
- 应用内浏览器可正常加载最新本地构建,页面标题、登录表单和控制台均正常;本地会话已过期并跳转图形验证码登录页,未绕过验证码进入模板表单,因此没有把目标表单交互误记为浏览器通过。自动填入/替换逻辑由共享纯函数、前端生产构建和真实后端定向/全量测试覆盖,仍建议登录后补一次可见交互复测。
## 2026-07-15 运营列表排序、短信批量驳回与通道成本展示(已提交、已部署)
- 短信审核页增加“驳回已选”,统一填写非空原因后调用真实 `/admin/risk-review/tasks/batch/reject`;NestJS 对 id 去重、限制单批最多 100 条并逐项执行现有风控拒绝和发送链路拒绝处理,不使用前端本地状态冒充完成。
- 企业管理接口按 PostgreSQL 当日短信消费 `todaySpendCents` 降序返回;企业应用管理接口按 PostgreSQL 当日短信记录 `sentToday` 降序返回;两者相同指标均以名称和 id 稳定排序。
- 通道列表成本继续读取真实 `unitPrice`,仅将展示格式调整为“X.XX 分”,不修改计费或成本快照精度。
- 功能与接入号改动提交 `3e114ee7294e502ab8a65f84891fd2e1258a00c1` 已 push 并部署生产;发布包本地/服务器 SHA-256 均为 `8d84c15d2d95c79c38a57af8e74588f1ef52f5550e6eb54d881eb2cb74ac7d1d`
- 部署前生产 PostgreSQL、运行源码和环境文件备份目录为 `/opt/cmpp-platform/backups/releases/20260715-154713`;三份文件均非空并通过 `sha256sum -c`,数据库、源码、环境 SHA-256 分别为 `12c372c9ccc65a815444b59cd5856864128b25f137a5fc141bec91084b2f0921``8100e7f88275e87fe7ccca89ceeb76443488335972076a767c6c29d64b944712``87ab2efd509b39849b0ee22ead1b4cf0568fba4ab8525c59b5c78da51c73bc86`
- 本地定向 3 suites/46 项、API 全量 18 suites/192 项、API build、前端 build、Gateway 全量 Go 测试、Prisma validate/generate/migrate status 和 `git diff --check` 均通过。Jest 仍有既有 open-handle 退出提示,使用相同全量测试集加 `--forceExit` 复核退出码为 0。
- 生产第 47 条 migration 已成功应用且无待执行项;API、Gateway、MinIO、Nginx、PostgreSQL、Redis 均 active`12026/17890/8090/3000/9000` 监听,内外 health、首页、运营登录页、Redis 和 PostgreSQL 均通过。真实 PostgreSQL 当日企业消费和应用发送量查询按降序返回,产物包含批量拒绝路由、“驳回已选”和接入号填充交互,废弃设计文档已从运行源码删除,部署后 API/Gateway 无新增 error。
- 应用内浏览器确认生产运营登录页标题、表单和验证码正常渲染,无框架错误覆盖或控制台错误;点击验证码后题目由 `43 + 8` 更新为 `46 + 6`。因浏览器无现成运营登录态且图形验证码需要人工确认,未执行登录后批量拒绝的破坏性真实任务操作,相关闭环由真实 API/数据库/产物及后端测试验收。
## 2026-07-15 企业应用接入号填充与扩展码(已提交、已部署)
- 已删除未落地且容易被误作实现依据的 `docs/access-number-upstream-downstream-design.md`,正式口径改为企业应用级“应用扩展码 + 可选客户接入号填充前缀”。
- 企业应用新增真实 PostgreSQL 配置:应用扩展码、填充开关、填充前缀和全局唯一客户侧 `Src_Id`。管理端仅在开启填充时展示可编辑前缀,并实时只读预览客户侧接入号。
- 配置扩展码后,NestJS 在任务、计费和入队前严格校验客户 CMPP `Src_Id`;新短信保存客户侧号码与应用扩展码快照。上游 Submit 只使用“通道基础号 + 应用扩展码”,客户填充前缀不进入上游。未配置扩展码的历史应用保持原通道基础号行为。
- 本地 PostgreSQL 已应用第 47 条 migrationPrisma format/validate/generate/migrate status 通过。SmsConfig/SendChain 定向 2 suites/88 项、API 全量 18 suites/188 项、API build、前端 build、Gateway `go test ./...` 均通过;前端仅有既有 Vite chunk size warning。Jest 全量用例全部通过但存在既有 open handle,使用 `--forceExit` 取得明确退出码 0。应用内浏览器确认本地站点、运营登录页和控制台正常,但真实登录及图形验证码阻断表单页,未将条件显示交互误记为浏览器已验收。
## 2026-07-15 报表与通用报备字段生产发布
- 功能提交 `8c3336600e900667d8bb77fd6e0fb5fe40986c11` 已 push 到 `origin/main` 并部署生产,覆盖对账单、利润报表、发送质量报表和通用报备字段/签名资料动态化全部工作区改动。发布包本地与服务器 SHA-256 均为 `94f59561f1a5a2255bb188007a6ce73fd1549ba33ca9241d05ba3ac4118f2d89`
- 部署前备份 PostgreSQL 为 `/opt/cmpp-platform/backups/cmpp-20260715-142351.sql`(约 65MBSHA-256 `7cedd56e15c402c9e6cc03f0d7b03bc232206361704988e7bfd8a0c8c5ca4cf6`),运行源码为 `/opt/cmpp-platform/backups/cmpp-source-20260715-142351.tar.gz`(约 1.3MBSHA-256 `fb4e7a6e75a68496c4a4bee68db1760dbafdc53dac2357a965ded6d6843f463e`),环境文件备份为 `/opt/cmpp-platform/backups/cmpp-env-20260715-142351`(权限 600SHA-256 `87ab2efd509b39849b0ee22ead1b4cf0568fba4ab8525c59b5c78da51c73bc86`)。
- 生产成功应用第 4446 条 migrationPrisma 确认 46 条 migration 全部齐全。日报启动任务已按北京时间重算 T-4~T-1,生成对账 3 行、利润 4 行、质量 11 行,质量日期范围为 2026-07-11 至 2026-07-14;生产现有通用报备字段为 0,未为验收伪造配置。
- `cmpp-api``cmpp-gateway``cmpp-minio`、Nginx、PostgreSQL、Redis 均为 activeAPI/Gateway/MinIO health、Redis `PONG`、PostgreSQL readiness、`12026/17890/8090/3000/9000` 监听均通过。外部首页、运营端登录页和 API health 均为 HTTP 200CMPP 入站 `17890` 可连接,部署后 API/Gateway journal 无 error,生产根目录与 API `npm audit --omit=dev` 均为 0 vulnerabilities。未发送或重投客户短信。
## 2026-07-15 发送质量报表(已提交、已部署)
- “报表对账”新增“发送质量报表”,包含企业应用、通道、签名、引流信息四个真实 Tab;统一展示日发送条数、成功条数、成功率、平均到达时长、生成时间,并由 API 按发送条数降序分页。
- 新增 `DailyQualityReport` 和第 46 条 migration;质量日报与现有日报任务共同按北京时间 T+1 生成并每日重算 T-4 至 T-1。企业应用/签名/引流按短信最终状态聚合,通道按 accepted submit 与对应 Gateway delivered 回执聚合。
- 平均到达时长使用成功短信的 `deliveredAt - submittedAt`,按日期和维度计算 PostgreSQL `PERCENTILE_CONT(0.95)`,只对小于等于 P95 的样本求平均;无有效成功时间时保存 null。
- `SmsMessageRecord` 新增真实 `drainageInfoId`。新短信在同签名 approved 引流信息中按正文 URL 精确匹配并取唯一最长 URL;migration 对历史短信用相同规则回填,歧义或未命中统一进入“未关联引流信息”,不重复展开。
- 本地真实 PostgreSQL 已应用 46 条 migration,并实际执行四个质量维度的 T-4~T-1 生成 SQL;现有真实短信成功生成企业应用、未关联签名和未关联引流信息质量行,未关联行按企业应用隔离并可正常筛选。Prisma validate/generate/migrate status、报表与发送链路定向 2 suites/58 项、API 全量 18 suites/183 项、API build、前端 build、Gateway 全量 Go 测试、`npm audit` 0 漏洞和 `git diff --check` 通过;前端仅有既有 Vite chunk size warning。
## 2026-07-15 通用报备字段与签名资料动态化(已提交、已部署)
- 新增真实 PostgreSQL `CommonReportField` 配置表,字段库可将字段分别配置为通用签名报备资料或通用引流信息报备资料,并支持真实新增、删除;删除配置不删除字段定义或历史材料。字段被通道或通用配置引用时,字段库定义均不能直接删除。
- 应用报备资料字段统一合并“通用字段 + 当前应用生效通道组/通道字段”,按字段库 ID 去重并合并必填规则。未绑定应用的签名和引流信息仍返回、展示并由 NestJS 强制校验通用字段;通用字段仍保留在通道报备字段的字段库选择项中。
- 运营端企业签名添加/编辑弹窗移除固定签名依据、资质凭证、企业信息和责任人信息,所有资料改由动态字段生成。运营端和客户端的签名、引流信息添加/编辑均加载对应通用字段;客户端新增签名不再走旧固定材料上传,改为把动态值写入真实签名资料 payload,文件继续上传 MinIO/FileObject。
- migration `20260715170000_add_common_report_fields` 已在本地 PostgreSQL 成功应用,当前 45 条 migration 全部齐全。Prisma format/generate/validate、API 定向 2 suites/39 项、API 全量 18 suites/181 项、API build、前端 build 和 `git diff --check` 通过;前端仅有既有 Vite chunk size warning。该功能已随 `8c333660` 提交、push 并部署生产。
## 2026-07-15 报表对账与利润报表(已提交、已部署)
- 在“数据详单”后新增“报表对账”一级菜单及“对账单”“利润报表”二级菜单;两页调用真实 `/api/admin/reports/reconciliation``/api/admin/reports/profit`,提供日期、企业、企业应用、通道、统计维度和服务端分页。
- 新增 `DailyReconciliationReport``DailyProfitReport` 真实 PostgreSQL 表和第 44 条 migration。报表按北京时间 T+1 生成,API 启动后及每日任务重算 T-4 至 T-1;每个日期在独立事务内删除旧聚合并重建,覆盖 72 小时回执更新窗口。
- 发送量和成功量按 `billingUnits` 统计;成功取最终 delivered。企业应用利润的消费取当前 charged 账单,退款不计收入;成本累计所有 accepted 上游提交,因此补发成本不会漏算。通道利润按实际 accepted 尝试及对应 Gateway delivered 回执汇总,收入只归属最终提交。
- `SmsSubmitRecord` 新增成本单价和金额快照,创建提交记录时写入;migration 按当时现有通道价回填历史记录。SubmitResult 更新同时收窄为优先按 `submitId` 更新,避免一次补发结果覆盖同一短信的其他尝试并污染成本。
- 本地真实 PostgreSQL 已成功应用 migration 并实际执行 2026-07-11 至 2026-07-14 的 T-4T-1 生成 SQLPrisma schema validate/generate 和 migrate status 通过,44 条 migration 全部齐全。报表与发送链路定向 2 suites/56 项、API 全量 18 suites/176 项、API build、前端 build、Gateway 全量 Go 测试和 `git diff --check` 全部通过;前端仅有既有 Vite chunk size warning。本批未修改依赖,`npm audit` 延续上一批 0 漏洞锁文件;本轮在线复查因 npm registry TLS 建链连续两次失败,未将网络失败误记为 audit 成功。
## 2026-07-15 依赖安全与今日返还修复(已提交、已部署)
- 生产数据库只读核查确认目标企业当天共有两笔真实返还:提交前路由失败产生消息级 `released=5` 分,最终失败产生 `refunded=5` 分,正确合计为 10 分(页面应显示 `¥0.100`)。原 Dashboard 和企业列表只聚合 `refunded`,因此少算前一笔并显示 5 分。
- 客户端 Dashboard 与运营端企业列表统一按当天 `refunded``released + relatedType=sms_message_record` 汇总;排除任务冻结转扣费时的 `released + relatedType=sms_batch_task`,避免把内部账务转换误当成返还。
- API 依赖升级到 NestJS `11.1.28`、Multer `2.2.0`,并通过 override 将 `@hono/node-server` 固定为 `1.19.13``npm --prefix api audit --json` 已由 3 个 moderate、2 个 high 降为 0。
- multipart 文件上传补充单文件 20MB、字段数、part 数、字段名/值长度和 header pair 限制,继续使用认证后的真实 NestJS API、MinIO 与 `FileObject` 链路。定向回归 3 suites/21 项通过;完整 API 17 suites/173 项、API build、前端 build、Gateway 全量 Go 测试、Prisma validate/migrate status 和依赖 audit 均通过,43 条 migration 全部齐全。前端仅有既有 Vite chunk size warning。
- 功能提交 `a758672436b3ebcd78aab91ba4a8e3cf68cbdfb0` 已 push 并部署。生产两次 `npm ci` 均为 0 vulnerabilities;真实 PostgreSQL 按新口径聚合目标企业当天返还为 10 分,前端产物包含“今日返还金额”。
## 2026-07-15 下游人工重投无限排队修复(已提交、已部署)
- 根因是 Gateway 以空的 `DownstreamSendResult` 同时表达“客户暂时离线”和“原消息映射丢失且缺少 Submit Sequence_Id”,NestJS 收到 `sent=false` 后又不登记失败,导致历史状态回执永久保持 `pending/retryCount=0`
- Gateway 控制结果新增 `retryable/reasonCode/errorMessage`:缺少原 `submitSequenceId` 且无法命中内存映射时返回不可恢复的 `MISSING_SUBMIT_SEQUENCE_ID`NestJS 立即终结为 `failed`;字段完整但客户离线时返回可恢复的 `CLIENT_DISCONNECTED`,失败回调进入真实次数上限与指数退避。
- 新状态回执入队补存 `SmsMessageRecord.cmppSubmitSequenceId`,Gateway 控制面允许在当前连接存在时立即恢复原 `Msg_Id`;不生成伪造或为 0 的 Msg_Id。
- 自动扫描为 pending 增加默认 72 小时绝对终点(`CMPP_DOWNSTREAM_PENDING_TIMEOUT_HOURS` 可覆盖),从 `createdAt` 或最近 `lastRetriedAt` 计算,超时写 `failed/queue_timeout` 和失败操作日志。积压告警同样以最近人工重投时间重新计时,避免刚重投就因旧创建时间立即告警。
- 回归已通过:API 定向 `send-chain/operations` 2 suites、68 项,API 全量 17 suites、173 项,Gateway 全量 Go 测试、Prisma validate、API build、前端 build 和 `git diff --check`;前端仅有既有 Vite chunk size warning。部署后生产原 3 条历史 pending 均已真实终结为 `failed/manualRetryCount=1/retryCount=1`,失败原因明确为 `MISSING_SUBMIT_SEQUENCE_ID`,当前下游投递 pending 为 0。
## 2026-07-15 待审核通知移除下游投递告警(已提交、已部署)
- 生产只读核查确认当前“下游投递告警 3”全部为历史状态回执记录:三条均在人工重投后成为 `pending/manualRetryCount=1/retryCount=0`,因为告警仍按原 `createdAt` 超过 10 分钟判断,立即计入 `stalled_pending`;它们不是待审核任务。
- 右上角“待审核任务”移除下游投递告警菜单项及其数字,通知总数只汇总企业认证、短信、模板、签名和引流信息五类审核。运营看板和下游投递记录页面继续保留独立告警展示。
- “人工重投排队中”表示记录已由人工重投重置为 `pending` 并累计人工重投次数,正在等待 Gateway 对在线客户写出 Deliver 并取得 `CMPP_DELIVER_RESP`,不代表重投已经成功。
- 进一步核查三条历史记录的 `payload.submitSequenceId` 均为空;虽然账号 `910887` 当前 connected 且持续心跳,Gateway 重启后没有原 Submit 的消息映射,也无法安全重建非 0 Msg_Id,因此修复前人工重投没有写出 Deliver 并持续保持 pending。前端 build 与 `git diff --check` 通过;应用内 Browser 连接因插件运行时初始化失败,未使用独立 Playwright 或 mock 页面替代。
- 本批随 `a7586724` 部署,生产源码确认右上角待审核区域不再包含“下游投递告警”;运营看板与下游投递页面仍保留独立告警入口。
## 2026-07-15 `a7586724` 生产发布记录
- 发布前确认本地 `main` 与最新 `origin/main` 无分叉;PostgreSQL、运行源码、环境文件分别备份为 `/opt/cmpp-platform/backups/cmpp-20260715-120751.sql`65MB)、`/opt/cmpp-platform/backups/source-20260715-120751.tar.gz`104MB)、`/opt/cmpp-platform/backups/cmpp-platform-20260715-120751.env`,均非空、权限 `600` 并完成 SHA-256 校验。
- 发布包本地与服务器 SHA-256 均为 `e1f14706389e6c42e89161422b07e6a409213dd54b9ae39b6bcc50dbba53f61e`;生产 `.deployed-commit=a758672436b3ebcd78aab91ba4a8e3cf68cbdfb0`43 条 migration 全部齐全且无待执行项。
- `cmpp-api``cmpp-gateway`、MinIO、Nginx、PostgreSQL、Redis 均 active`12026/17890/8090/3000/9000` 正常监听;API、Gateway、MinIO、Redis、PostgreSQL、外部首页、运营登录页和外部 API health 均通过,HTTP 返回 200。真实 CMPP 账号 `910887` 保持 connected,发布后 API/Gateway journal 无 error 级日志;未主动发送或重投计费短信。
## 2026-07-15 当前工作区汇总发布
- 当前各对话产生的 34 个文件变更已统一提交为 `e47432bc631bf37f4c6a6dcb3576ce3c86370b45` 并 push 到 `origin/main`;提交明确排除 `api/tsconfig.build.tsbuildinfo``logs/`。发布前确认本地 `main` 与最新 `origin/main` 无分叉,API 全量 17 suites/169 项、API build、前端 build、Gateway 全量 Go 测试、Prisma validate/migrate status 和 `git diff --check` 全部通过,前端仅有既有 Vite chunk size warning。
- 部署前生产 PostgreSQL、运行源码和环境文件分别备份为 `/opt/cmpp-platform/backups/cmpp-20260715-112406.sql`65MB)、`/opt/cmpp-platform/backups/source-20260715-112406.tar.gz`20MB)和 `/opt/cmpp-platform/backups/cmpp-platform-20260715-112406.env`;三份文件均非空、权限 `600` 并完成 SHA-256 校验。
- 发布包本地与服务器 SHA-256 均为 `c26d2414df13e535f3ce69d838d299d80680f23576599f264b7043ad1eea713c`。生产成功应用 `20260715090000_track_downstream_manual_retries``20260715153000_remove_disconnected_downstream_sessions`,共 43 条 migration 全部齐全;非 connected 历史连接行已清为 0,下游人工重投字段已可查询。
- 生产 `.deployed-commit=e47432bc631bf37f4c6a6dcb3576ce3c86370b45``cmpp-api``cmpp-gateway`、MinIO、Nginx、PostgreSQL、Redis 均 active`12026/17890/8090/3000/9000` 监听,API/Gateway health、Redis、PostgreSQL、外部首页、运营登录页和外部 API health 均通过。真实 CMPP2.0 账号 `910887` 已重新登录并保持 connected,部署后 API/Gateway 新增 error 为 0。
- 部署未重投 10:52:58 的历史失败短信,也未主动发送新的计费短信。生产 `npm ci` 报告现有依赖 3 个 moderate、2 个 high audit 风险,未阻断本次构建和启动,后续需在独立依赖升级任务中评估处理。
## 2026-07-15 CMPP 变量模板与 direct_send 修复
- 生产只读诊断确认:10:52:58 账号 `910887``18821203795` 提交验证码短信,应用已于 10:52:36 保存 `templateMismatchMode=direct_send`,且存在审核通过的 `${code}` 变量模板;原实现用正文精确相等查询,实际验证码无法匹配占位符,同时模板为空时仅识别 `manual_review`,导致 `direct_send` 错误落入 `TEMPLATE/REJECTD`
- `resolveInboundTemplateCandidate` 先保留精确匹配,再对同应用变量模板执行固定正文全量匹配,提取非空变量值;同名变量重复出现必须取值一致。匹配成功后保存真实 `templateId`,并将变量值传给真实风控评估。
- 新增 `direct_send` 分支:模板不匹配时识别并保存已审核完整括号签名,继续执行风控、余额冻结、真实队列、通道组路由和具体通道签名报备校验;只跳过模板要求,不做无条件放行。`reject/manual_review` 行为保持不变。
- 新增变量模板验证码和 `direct_send` 两项回归;SendChainService 1 suite/49 项通过,API 全量 17 suites/169 项通过,Prisma validate、API build、前端 build 和 `git diff --check` 通过,前端仅有既有 Vite chunk size warning。本修复已随 `e47432bc` 部署,未重投 10:52:58 的短信。
## 2026-07-15 运营端细节修复批次
- 企业签名引流字段统一为“引流信息”并隐藏提交时间;人工充值弹窗移除操作人;待审核通知中的 0 使用黑字灰底。
- 企业应用表单补充每任务号码上限的整任务拒绝说明;企业代码控件不可编辑并跟随 CMPP 6 位账号,NestJS 创建/更新也强制持久化两者相等。
- 手机号段新增真实 DELETE;报备字段 API 返回真实通道引用数,未引用可删除,引用数大于 0 时前端禁用且后端返回 400。
- 客户 CMPP 连接详情只展示已连接会话;Gateway 上报断开时删除活跃连接行,心跳超时扫描也删除,新增 migration 清理旧非 connected 行,断开操作日志仍保留。
- 短信审核新增逐行/全选勾选,批量按钮只处理已选任务;下游投递列表与 Dashboard 增加统一创建日期范围参数并下推 Prisma/PostgreSQL。
- 运营端和客户端短信模板变量均插入当前光标/选区位置;短信记录前端改为自适应卡片和分组详情,失败原因独立警示,不改变短信记录后端接口与业务语义。
- 定向 API 测试 `dictionaries/sms-config/operations` 为 3 suites、49 项通过;API 全量 17 suites、167 项通过,Prisma validate、API build、前端 build、Gateway 全量 Go 测试和 `git diff --check` 均通过,前端仅有既有 chunk size warning。本地真实 PostgreSQL 已应用连接清理 migration43 条 migration 全部齐全。
- 应用内浏览器已验证本地构建的运营端登录路由标题为“聆界短信管理平台”、DOM 非空且无框架错误覆盖;受 HttpOnly 会话和图形验证码限制,未绕过认证进入受保护页面。后续浏览器连接因桌面插件版本热更新失败,未以独立 Playwright 或 mock 页面替代。本批已随 `e47432bc` 部署,外部首页和运营登录页均 HTTP 200。
## 2026-07-15 下游人工重投状态追踪修复
- 根因确认:人工重投会将 `CmppDownstreamDelivery` 重置为 `pending/retryCount=0`,前端又将所有 pending 固定翻译为“待首次投递”,导致已人工重投的记录被误展示为从未投递。
- 新增 `CmppDownstreamDelivery.manualRetryCount/lastRetriedAt` 及真实 Prisma migration;人工重投时递增人工次数、保存时间,并在 `OperationLog` 中记录重投前状态、原自动重试次数和新人工次数。自动重试次数仍可为新一轮重置为 0,但不再丢失人工重投轨迹。
- 列表和详情改为基于真实字段显示:初始 pending 为“待首次投递”,仅自动失败为“等待自动重试”,存在人工重投时为“人工重投排队中”;分开展示自动/人工次数和最近人工时间。
- 后端新增 `awaiting_ack` 并发重投拦截,避免绕过前端禁用直接调 API 造成重复投递。并发修改最终已合并且编译问题已修正;汇总回归 API 17 suites/169 项、API/前端 build、Prisma validate/status 和 Gateway 全量 Go 测试均通过。本修复已随 `e47432bc` 部署,生产人工重投 migration 已应用且 43 条 migration 全部齐全。
## 2026-07-15 下游投递告警口径统一
- 修复前存在三套口径:侧栏/首页只统计超阈值 pending 和最近 failed;详情页额外统计超时 awaiting_ack 与最近 unconfirmed/rejected;应用排行则将全部 pending 和所有历史失败累加为告警,造成同一时刻数量不一致。
- 统一为“超阈值 pending + 超过 `ackDeadlineAt` 的 awaiting_ack + 最近窗口内 failed/unconfirmed/rejected”;默认积压阈值 10 分钟、最近失败窗口 1 小时,继续支持环境变量覆盖。
- `OperationsService.dashboard()``downstreamDeliveryDashboard()` 复用同一时间窗生成逻辑,首页/侧栏补齐 `stalledAck` 与三种最终异常状态;应用告警排行改为单独按统一告警 where 聚合,不再将普通 pending 和历史失败永久累加。
- 已补实 OperationsService 定向单元测试,覆盖首页三类告警条件和应用排行统一条件。API 完整 17 suites、163 项通过,API build 和前端 build 通过;前端仅有既有 Vite chunk size warning。本批已随 `e47432bc` 部署。
## 2026-07-14 生产发送 Worker 配置缺失修复
- 生产号码 `18821203795` 的最新短信于 18:24:59 审核通过后恢复为 queuedBullMQ 已生成 job,但一直停留在 `bull:sms.send.queue:prioritized`,无通道、`submitId``SmsSubmitRecord`
- 生产 API 进程环境缺少 `API_ENABLE_SEND_WORKER=true`,而 `SendChainService.onModuleInit()` 只在该值严格为 `true` 时启动 BullMQ WorkerGateway 健康,但任务尚未进入 Gateway。
- 修复为生产初始化默认写入 `API_ENABLE_SEND_WORKER=true` 和并发数 50,发布脚本在构建、迁移和重启前强制校验开关与正整数并发数,避免再次带病发布。
- 与本批工作区改动合并回归:Prisma validate/generate 通过,API 完整 17 suites、163 项通过,API build、前端 build 和 Gateway 全量 Go 测试通过;前端仅有既有 Vite chunk size warning。
- 功能提交 `c1a17699` 已 push 并部署生产。部署前 PostgreSQL 备份为 `/opt/cmpp-platform/backups/cmpp-20260714-184827.sql`(约 65 MB),运行源码备份为 `/opt/cmpp-platform/backups/source-20260714-184827.tar.gz`(约 23 MB),环境文件备份为 `/opt/cmpp-platform/backups/cmpp-platform-20260714-184827.env`;发布包本地和服务器 SHA-256 均为 `920e01406560214ad5e2ce9c4cbef6c4d627590a7da135be6416ed5f555f4ea5`
- 生产 API 进程已实际加载 `API_ENABLE_SEND_WORKER=true``API_SEND_WORKER_CONCURRENCY=50`Redis `prioritized/active` 均为 0。原排队短信于 18:51:11 经“赛邮行业-王斯评中转”通道获得 `SUBMIT_RESP accepted`,证明 Worker、路由、Gateway 和上游提交链路已恢复。18:57:00 上游回执原始状态 `DB:9032`,平台按 `undelivered` 最终失败处理,5 分扣费已全额退回,账户余额从 95 分恢复为 100 分。
- 生产迁移 `20260714190000_restore_negative_credit_limit` 成功应用,41 条 migration 全部齐全;`.deployed-commit=c1a17699db29b7fcbde63c0b716cbf1abceb0cdf`。API、Gateway、Nginx、PostgreSQL、Redis、MinIO 均为 active`12026/17890/8090/3000` 监听,API/Gateway health、Redis 和外部首页/运营登录页 HTTP 200,部署后 API/Gateway 无新增 error。
## 2026-07-14 恢复授信额度并补齐 72 小时无回执退款
- 产品口径修正:套餐和短信余量继续移除,但企业账户保留授信额度;授信额度可为正数、负数或 0。
- 发送校验公式调整为 `balanceCents + creditCents > 0`,和大于 0 才允许发送,和小于等于 0 时禁止发送;不扣除本次预估费用后再判断。
- 新增授信调整真实 API、操作日志、企业新建/编辑页输入和运营端企业列表展示,并新增后续迁移恢复 `TenantAccount.creditCents`;客户端账户页不展示授信额度字段,只展示现金余额和合并后的可用发送额度。
- 退款时点审查:明确失败回执在补发不可用或耗尽后,于同一次回执处理内同步退款;Submit 拒绝/超时发生在扣费前,只释放冻结而非退款。已修复原 72 小时接口无自动调度且漏掉 `submitted` 的缺口:API 启动 60 秒后首次扫描,之后默认每 5 分钟扫描 `submitted/unknown`,超过 72 小时即转 timeout 并退款,单条条件更新避免并发扫描重复处理。
- CMPP 2.0/3.0 的 `Stat` 定义包含标准值 `UNKNOWN`,不是平台自造状态;当前 Gateway 对 `DELIVRD` 以外的非空最终状态(包括 `UNKNOWN`)统一映射为 `undelivered`,因此会进入失败补发,无法补发时直接失败退款。只有空 `Stat` 才映射为平台 `unknown`,并由 72 小时扫描兜底。
- 本地验证通过:新增迁移已应用于本地 PostgreSQLPrisma validate/generate、完整 API 17 suites/163 项、API build、前端 build 和 Go Gateway 全量测试通过;前端仅保留既有 Vite chunk size warning。
## 2026-07-14 计费收敛为现金余额、失败退款展示
- 生产只读核查确认 17:01 的人工充值已将目标企业现金余额从 0 增加到 100 分;17:04 发送预估费用仅 5 分却被拒绝,根因是旧逻辑同时要求 `TenantAccount.smsUnits >= billingUnits`,充值只增加现金时短信余量仍为 0。
- 发送前账户检查已收敛为只判断 `TenantAccount.balanceCents >= amountCents`,错误文案统一为“企业账户余额不足”;冻结、提交成功扣费、最终失败退款均只变更现金余额。短信 `billingUnits` 继续作为 70/67 拆分和费用计算字段,不再充当套餐额度。
- 删除 `BillingPlan`、账户短信余量、授信额度及充值订单套餐字段,并增加 Prisma 迁移;移除管理端套餐接口和客户端套餐购买入口,客户端改为展示真实现金余额与人工充值记录,运营端人工充值只录入金额。
- 企业管理列表隐藏企业 ID 和透支限额,增加“今日返还”;后端只聚合当日 `AccountTransaction.transactionType=refunded` 的实际退款,普通冻结释放 `released` 不计入返还。
- 失败退款链路复核:提交拒绝或超时且未实际扣费时只释放冻结;已提交扣费短信收到最终失败回执后才退款;已有 `SmsBillingRecord.billingStatus=refunded` 的消息不会重复退款。新增 `TC-BILLING-011/012` 覆盖余额唯一判断和退款口径。
- 同步更新第一版需求和系统功能测试用例;Prisma validate/generate、本地 PostgreSQL migration deploy、完整 API 17 suites/162 项、API build、前端 build 和 Go Gateway 全量测试均通过。前端仅保留既有 Vite chunk size warning。
- 功能与日志治理提交 `28dad93e` 已 push 并部署生产。部署前 PostgreSQL 备份为 `/opt/cmpp-platform/backups/pre-billing-20260714-173949.sql.gz`(约 4.1 MB),运行源码备份为 `/opt/cmpp-platform/deploy-backups/pre-billing-20260714-173949.tar.gz`(约 26 MB),发布包 SHA-256 为 `6253aa50bdea9d5bd35786b7414286b72b56737df3fa533b3555544983537e19`
- 生产两条迁移成功应用,40 条 migration 无待执行项;`BillingPlan` 表和 `TenantAccount.smsUnits/creditCents` 字段已移除,目标企业 `CMPP生产对接测试企业A` 的现金余额保持 100 分。生产 `.deployed-commit=28dad93e3ed7adcc7791a92723200ffe19509999`API、Gateway、Nginx、PostgreSQL、Redis、MinIO 均 activeAPI/Gateway health、`12026/17890/8090/3000` 监听和外部首页/运营入口 HTTP 200;前端产物包含“今日返还”“账户余额”且不含“充值套餐”“透支限额”。未擅自重发计费短信。
## 2026-07-14 系统日志增长治理、分页索引与归档
- 生产只读评估确认 `OperationLog` 已有约 3 万条、总占用约 17 MB,当前查询尚未形成性能事故;其中 `cmpp_downstream_connection.heartbeat` 约 2.4 万条、`gateway.downstream_recovery_status_sync` 约 5700 条,两类周期事件约占全部日志 99.5%。
- `SmsConfigService.recordDownstreamConnectionEvent` 调整为 heartbeat/Submit/Deliver 只更新 `CmppDownstreamConnection` 当前状态和时间,只有 connected/disconnected 写系统日志。
- `SendChainService.recordGatewayDownstreamRecoveryStatus` 在 upsert 前读取审计状态,只在 state、gatewayInstanceId、lockOwner、failureCategory、lastError 或 lastSkipReason 真实变化时写 `gateway.downstream_recovery_status_changed`;仅尝试次数、重试时间和锁过期时间变化不再重复写日志。
- Prisma 为 `OperationLog` 新增 `createdAt``resource+createdAt` 索引;系统日志 level 条件移入 PostgreSQL 查询后再 count/分页,排序增加 id 稳定次序。`/admin/operations/audit-logs``/admin/operation-logs` 均改为分页响应并限制 pageSize 最大 100。
- 新增 `OperationLogArchive` 和定时归档服务:在线日志默认保留 180 天,每日最多 20 批、每批 1000 条,使用单条 PostgreSQL CTE、`FOR UPDATE SKIP LOCKED` 和“归档存在后才删除源记录”保证并发与失败安全;归档记录按 `archiveMonth=YYYY-MM` 标记且不自动删除。
- 同步需求与用例:`TC-LOG-010` 覆盖高频运行事件不写永久审计,`TC-LOG-011` 覆盖数据库侧分页、接口上限、归档完整性和失败不丢数据。
- 验证通过:Prisma schema validate、Prisma Client 生成、本地 PostgreSQL migration deploy;本地真实 PostgreSQL 归档 smoke 验证过期日志进入 `archiveMonth=2000-01` 且仅在归档成功后删除在线源记录;数据库级 error 筛选真实查询通过。相关 5 suites/87 项和完整 API 17 suites/162 项测试全部通过;API build、前端 build 通过。前端仅保留既有 Vite chunk size warning。
## 2026-07-09 运营端通道测试短信闭环修复
- 生产验证发现运营端通道“短信测试”弹窗仅关闭页面,未调用后端;`POST /api/admin/channels/:id/test` 仍返回 phase-4 placeholder,不创建 `SmsMessageRecord/SmsSubmitRecord`,也不写入 Gateway SubmitCommand,因此短信记录页面无记录。
- 已修复为真实链路:前端提交手机号、内容和可选接入号;NestJS 校验通道 active 且存在在线 CMPP 连接后,创建独立 `SmsMessageRecord``SmsSubmitRecord` 和操作日志,并向 BullMQ `gateway.submit.queue` 与 Redis Stream `gateway.submit.commands` 写入真实 `SubmitCommand`。通道测试不绑定企业、企业应用或 `SmsBatchTask` 发送任务。
- 测试口径同步:`TC-ADMIN-003` 增加通道测试短信闭环要求,必须能从页面/API 发起真实测试短信,短信记录页面可查询到对应记录,Gateway submit worker 按通道真实 CMPP 配置消费发送。
- 已执行:`npm --prefix api test -- channels.service.spec.ts --runInBand``npm --prefix api run build``npm run build`。待生产部署后用指定号码做一次真实发送验证,并回查 DB/短信记录。
- 2026-07-09 追加:按产品边界收窄通道测试短信,`SmsMessageRecord/SmsSubmitRecord/SmsReceiptRecord/SmsMessageSegmentAudit` 支持 `tenantId/batchTaskId` 为空;通道测试只记录短信、提交结果和回执,不进入企业账务、发送任务进度、客户侧 Deliver 推送或业务补发。
- 2026-07-09 追加:生产验证短信进入 Gateway 后出现 `SUBMIT_TIMEOUT/context deadline exceeded`,上游平台疑似内容乱码;排查确认 API、数据库和 Redis Stream 中中文内容正常,`SubmitCommand.cmpp.msgFmt=8`,根因为 Gateway 在 CMPP 2.0 通道上仍固定构造 `Cmpp3SubmitReqPkt`。已修复为按通道 `cmppVersion` 分别构造 `Cmpp2SubmitReqPkt`/`Cmpp3SubmitReqPkt`,并同时处理 CMPP 2.0/3.0 SubmitResp。新增 `submit_packet_test.go` 覆盖 CMPP 2.0 中文 UCS2 Submit 包类型和长度。
- 2026-07-09 追加:生产验证 Submit 已 accepted 后未见 `SmsReceiptRecord`,结合上游为 CMPP 2.0 排查 Gateway readLoop,确认只处理 `Cmpp3DeliverReqPkt`CMPP 2.0 回执 Deliver 即使到达连接也不会 ACK 或进入 receipt 事件链路。已修复为同时处理 `Cmpp2DeliverReqPkt`/`Cmpp3DeliverReqPkt`,分别返回 `Cmpp2DeliverRspPkt`/`Cmpp3DeliverRspPkt`,并统一 Deliver 解码、回执和上行处理。新增 `deliver_test.go` 覆盖 CMPP 2.0 `DELIVRD` 回执写入事件链路。
- 2026-07-09 追加:运营端短信记录“发送详情 - 通道发送与回执”的“回执码”必须展示通道原始回执码 `SmsReceiptRecord.rawStatus`,用于和上游平台核对;该字段不再回退展示平台映射状态 `receiptStatus` 或提交状态。
- 2026-07-09 追加:生产验证 `17317959177` 在 DB 中已有 `rawStatus=DELIVRD`,但页面仍显示空。排查确认页面调用 `/admin/send/messages` 时生产返回缺少 `submitRecords/receiptRecords` 明细,而 `/admin/operations/messages` 返回完整通道发送与回执记录;短信记录页已切换到完整明细接口,并将 UTC ISO 时间按 `Asia/Shanghai` 展示,避免 16 点多页面显示 8 点多。
## 2026-07-09 企业应用短信接口开关和接口类型
- 按设计基线恢复企业应用添加/编辑页“短信接口 开通/关闭”和“接口类型 CMPP/HTTP”;当前第一版只允许 CMPP2.0,HTTP 在页面中禁用,后端拒绝未实现的 `interfaceType=http`
- Prisma `SmsApplication` 新增 `interfaceEnabled``interfaceType` 持久化字段;企业应用创建、编辑、列表和 CMPP 参数接口均返回真实数据库值。
- 发送链路新增硬校验:`interfaceEnabled=false` 时客户端/API 发送、Gateway bind/login 鉴权和 Gateway submit 入站都会拒绝,不进入任务、计费或 Gateway 上游提交。
- 测试口径同步:`TC-ADMIN-018A` 覆盖表单保存、CMPP2.0 only、HTTP 禁用和接口关闭后的发送/Gateway 拒绝;`TC-GW-006` 覆盖 Gateway 鉴权与 submit 对接口开关的校验。
- 已执行:`npm --prefix api run prisma:generate``npm --prefix api test -- sms-config.service.spec.ts send-chain.service.spec.ts``npm --prefix api run build``npm run build``git diff --check``npm --prefix api run prisma:migrate:deploy`
## 2026-07-09 通道复制默认停用与真实连接池状态回写修复
- 生产验证发现当前 3 个赛邮行业通道中有 2 个由首个 active 通道复制而来;数据库 `CmppConnectionState` 曾显示 3 条通道都为 `connected/currentConnections=1`,但生产机 `ss/netstat` 与上游平台都只能看到 1 条真实 TCP 长连接。
- 根因一:`POST /api/admin/channels/:id/copy` 会继承源通道 `status=active`,复制后的通道创建完成后立即参与 Gateway 连接流程,和“复制只是拷贝配置,不应自动上线”的产品预期不符。
- 根因二:Gateway `/connections/connect` 之前只做一次性 `DialCMPP` 探测,探测成功后就把 `connected/currentConnections=desiredConnections` 回写给 NestJS;该探测连接随后立即断开,导致页面状态与真实上游连接池不一致。
- 已修复:
- 通道复制后统一保存为 `disabled`,复制日志补充 `sourceStatus``copiedStatus`,避免 active 通道副本自动占用上游连接。
- Gateway `ConnectChannel` 改为直接建立/复用真实上游连接池,并由连接池回写 `connected/failed/disconnected``currentConnections`、最近错误和断开时间;连接丢失时状态随真实连接数变化更新。
- 测试口径同步:
- `TC-ADMIN-003` 增加“连接状态必须来自真实上游连接池回写”的要求。
- `TC-ADMIN-016` 明确复制 active 通道后副本默认 `disabled`,且不会立即触发上游真实连接。
- 已执行:`npm --prefix api test -- channels.service.spec.ts --runInBand``npm --prefix api run build``go test ./internal/control ./internal/upstream``go build -o ..\\dist\\cmpp-gateway .\\cmd\\gateway`
## 2026-07-09 线上通道 CMPP 版本修复
- 线上生产验证发现 3 个赛邮行业通道配置均指向 `121.40.172.212:7890`,其中 2 个已触发 Gateway 真实连接并失败,`CmppConnectionState.lastError``packetWriter.ReadBytes error: ReadBytes reads 14 bytes, not equal to 16 we expected`
- 生产机到上游 `121.40.172.212:7890` TCP 可连接,失败不是 API/Gateway 服务不可用,也不是网络完全不通;结合上游确认参数为 CMPP 2.0,根因定位为通道创建默认 CMPP 3.0 且运营端没有版本选择入口。
- 已修复通道创建/编辑:运营端新增 CMPP 2.0/3.0 版本选择,默认 2.0NestJS 通道 API 默认 `cmppVersion=2.0`,并只允许 2.0 或 3.0Prisma `SmsChannel.cmppVersion` 默认值同步改为 2.0。
- 测试口径同步:`TC-ADMIN-003` 要求通道协议默认 CMPP,CMPP 版本默认 2.0,且可选择 2.0 或 3.0;Gateway 连接命令必须携带真实通道版本。
- 待复测:部署迁移后将线上赛邮通道 `cmppVersion` 调整为 2.0,并重新触发 Gateway 连接验证。
## 2026-07-07 企业应用通用下拉与优先队列需求补充
- 已补充需求文档,明确运营端企业应用新增时选择企业必须使用通用 Select/下拉控件,企业选项来自真实企业 API,支持加载、空态和错误态,不允许静态数组或 localStorage 兜底。
- 已补充应用发送队列等级需求:短信应用必须保存普通队列/优先队列配置,默认普通队列;优先队列用于验证码、登录确认、交易通知等高时效短信。
- 已补充发送链路要求:发送入队必须按应用队列等级分流,优先队列在同等业务校验、通道组路由、通道限速和 Gateway 连接条件下插队消费;普通队列不能永久饿死。
- 已补充测试计划和系统功能用例:
- 企业应用新增表单企业选择与队列等级真实保存。
- 优先队列插队发送。
- SubmitCommand 队列等级契约校验。
- 混合优先级队列性能 smoke。
- 当前状态:仅完成需求和测试口径补充,前后端、Prisma、BullMQ/Send Worker、Gateway 队列契约尚未实现,不能记为功能验收通过。
- 2026-07-07 追加:Prisma 与 NestJS 企业应用 API 已增加 `queuePriority` 持久化字段,创建/编辑支持 normal/priority 校验,列表/详情随真实应用数据返回;前端表单、发送入队、Send Worker 调度和 Gateway 队列契约仍待后续步骤实现。
- 2026-07-07 追加:运营端企业应用新增第一步企业选择已改为通用 `Select`;企业应用新增/编辑表单已按设计锚点恢复“发送队列”单选项,并在保存时真实提交 `queuePriority`。发送入队、Send Worker 调度和 Gateway 队列契约仍待后续步骤实现。
- 2026-07-07 追加:发送链路已将应用 `queuePriority` 固化到 `SmsMessageRecord`BullMQ 入队按 priority/normal 写入不同 job prioritySubmitCommand 契约、示例和 Go Gateway 队列结构已增加 `queuePriority`;当前实现覆盖优先队列插队的基础能力,持续高优先级流量下普通队列防饥饿策略仍需后续压测和调度增强。
- 2026-07-07 追加:Gateway 队列契约第 8 步已独立校验,`SubmitCommand` schema/example 要求 `queuePriority`Go Gateway `SubmitCommand` 结构可反序列化该字段,并通过 `npm run spike:contracts``npm run spike:gateway`
- 2026-07-07 追加:第 9 步收口验证通过:`npm --prefix api run prisma:generate``npm --prefix api test -- sms-config.service.spec.ts send-chain.service.spec.ts --runInBand``npm run spike:contracts``npm run spike:gateway``npm --prefix api run build``npm run build` 均通过;前端 build 仅保留既有 Vite chunk size warning。
## 2026-07-06 企业管理列表字段回归
- 按设计锚点 `131f344a^` 恢复运营端企业管理列表字段:企业 ID、企业名称、当前余额、透支限额、今日消费、企业状态、操作。
- 新增真实后端接口 `GET /api/admin/tenants/management-list`,由 NestJS/Prisma 聚合租户、企业账户和当天短信消息金额;前端不再用静态字段或本地假数拼出今日消费。
- 当前余额来自 `TenantAccount.balanceCents`,透支限额来自 `TenantAccount.creditCents`,今日消费来自当天 `SmsMessageRecord.amountCents` 汇总。
- 已执行:
- `npm --prefix api test -- tenants.service.spec.ts --runInBand`
- `npm --prefix api run build`
- `npm run build`
- 验证结果:API 单测、API build、前端 build 均通过;前端 build 仅保留既有 Vite chunk size warning。
## 2026-07-06 企业编辑页和上传链路修复
- 按设计锚点 `131f344a^` 恢复运营端企业新建/编辑页字段:企业照片、企业名称、统一社会信用代码、省/直辖市、市/区、通讯地址、联系人姓名、身份证号、手机号、电子邮箱。
- 运营端企业新建/编辑页移除偏离锚点的企业编码、企业状态字段;后端 `POST /api/admin/tenants` 支持不传企业编码,并按信用代码/企业名生成真实唯一企业编码。
- 修复营业执照/企业照片上传 500:
- 启动脚本在 MinIO 不可用时启用本地对象存储 `.local-data/object-storage`,文件仍通过真实 NestJS 上传接口写入对象存储目录并创建 `FileObject` 数据库记录。
- 文件上传接口缺少 multipart 文件时返回 400。
- `FileObject.sizeBytes` 返回前转换为字符串,避免 Prisma `BigInt` JSON 序列化 500。
- 已执行:
- `npm --prefix api test -- tenants.service.spec.ts files.service.spec.ts --runInBand`
- `npm --prefix api run build`
- `npm run build`
- `POST http://localhost:3000/api/admin/files/upload` multipart smoke
- 验证结果:API 单测、API build、前端 build 和真实上传 smoke 均通过;前端 build 仅保留既有 Vite chunk size warning。
## 2026-07-06 本地 MinIO 启动脚本补充
- `tools/start-local.ps1` 补充本地 MinIO 启动流程:Docker Compose 优先;无 Docker 时查找 `C:\cmpp-platform-local\minio.exe``C:\cmpp-platform-local\minio\minio.exe` 或 PATH 中的 `minio.exe`,使用 `C:\cmpp-platform-local\minio-data` 作为数据目录,监听 `9000/9001`
- `package.json` 新增 `npm run start:local:minio`,用于单独启动本地 MinIO。
- MinIO 不可用时,脚本仍会明确启用 `.local-data/object-storage` fallback;启动完成提示会区分 MinIO 是否真实运行。
- MinIO 模式下对象存储服务会在上传/预签名前自动确认并创建 `cmpp-platform` bucket。
- 已执行:
- `npm --prefix api run build`
- `npm run start:local -- -SkipApi -SkipWeb -SkipMigrate`
- 验证结果:API build 和启动脚本 smoke 通过;当前机器未发现 `minio.exe`,脚本按预期提示并启用本地对象存储 fallback。
## 2026-07-06 应用级 CMPP 连接和签名/引流表单基线
- CMPP 连接状态从企业/租户级聚合改为应用级独立连接:
- `CmppConnectionState` 新增 `applicationId` 并关联 `SmsApplication`
- 企业应用列表和连接详情只读取当前应用的 `CmppConnectionState`
- 运营端断开连接只操作当前应用下的连接。
- Gateway 连接上报 `POST /api/admin/gateway/connections` 支持 `applicationId`,新连接可按应用独立记录。
- 运营端添加/编辑短信签名页面按设计锚点 `131f344a^` 补齐字段:签名依据、短信签名、资质凭证、公司名称、统一社会信用代码、法人姓名、法人身份证号、法人身份证照片、责任人姓名、责任人手机号、责任人身份证号、责任人身份证照片、三网报备状态。
- 运营端添加/编辑引流信息页面按设计锚点 `131f344a^` 补齐字段:引流信息、字段名称 1-10、文件上传、三网报备状态、提交时间、备注。
- 签名和引流表单仍使用真实 `enterprise-signatures` 后端接口保存;扩展字段写入 `SmsSignature.drainageInfo` JSON,文件上传走真实 `admin/files/upload` 并保存 `FileObject` 引用。
- 已执行:
- `npm --prefix api run prisma:generate`
- `npm --prefix api test -- sms-config.service.spec.ts channels.service.spec.ts --runInBand`
- `npm run build`
- `npm --prefix api run build`
- `npm --prefix api run prisma:migrate:deploy`
- 验证结果:Prisma Client 生成、API 针对测试、API build、前端 build 和本地 PostgreSQL migration deploy 均通过;前端 build 仅保留既有 Vite chunk size warning。
## 2026-07-01
### 新增测试基础
- API 引入 Jest + ts-jest。
- API 新增脚本:
- `npm --prefix api test`
- `npm --prefix api test -- <spec>`
- 根目录新增脚本:
- `npm run test:api`
- `npm run test:gateway`
### 新增 API 测试
| 测试文件 | 覆盖范围 |
| --- | --- |
| `api/src/risk-review/risk-review.service.spec.ts` | 最大号码数、重复率、非法号码率、黑名单率、模板变量异常、非工作时间营销大批量、短时间频控、直接拒绝、人工审核。 |
| `api/src/billing/billing.service.spec.ts` | 70/67 费用预估、余额/授信/套餐检查、充值、冻结、扣费、释放、退款、调整、短信计费记录。 |
| `api/src/send-chain/send-chain.service.spec.ts` | 批量任务创建、手机号去重拆分、发送入队、Gateway SubmitCommand 投递、submit result、receipt、uplink、72 小时未知转超时。 |
| `api/src/channels/channels.service.spec.ts` | 通道创建、路由规则、报备材料 upsert、报备任务创建、导出、回执导入、签名报备状态同步。 |
| `api/src/operations/operations.service.spec.ts` | 发送记录查询过滤、dashboard、statistics、trace、reconciliation。 |
### 新增 Gateway 测试
- `gateway/internal/tracker/tracker_test.go` 增加并发 SEQID/MSGID/GatewayMessageID 映射测试。
- 既有 Gateway 测试继续覆盖:
- tracker 基础映射和未知 submit resp。
- reconnector 重连和最终错误返回。
- health handler。
- gocmpp adapter。
- spike simulator。
### 已执行命令
```bash
npm --prefix api test -- risk-review.service.spec.ts
npm --prefix api test -- billing.service.spec.ts
npm --prefix api test -- send-chain.service.spec.ts
npm --prefix api test -- channels.service.spec.ts
npm --prefix api test -- operations.service.spec.ts
npm run test:api
npm run verify:phase8
npm run build
npm run spike:gateway
npm --prefix api test
npm run test:gateway
node tools/smoke/real-env-smoke.mjs
$env:API_PORT='3101'; npm --prefix api run start:dev
node <inline step4 precheck smoke>
$env:API_PORT='3101'; $env:API_ENABLE_SEND_WORKER='true'; npm --prefix api run start:dev
node <inline step5 send-chain smoke>
node <inline step5 schedule probe>
$env:API_PORT='3101'; npm --prefix api run start:dev
node <inline step6 billing/reconciliation smoke>
node <inline step6 auto-billing probe>
npm run spike:contracts
npm run test:gateway
$env:API_PORT='3101'; $env:API_ENABLE_SEND_WORKER='true'; npm --prefix api run start:dev
node <inline step7 channel/cmpp status smoke>
npm run verify:phase8
npm run test:api
npm run test:gateway
```
### 当前结果
- API Jest5 个 test suite 通过,22 个测试通过。
- Gateway`npm run spike:gateway` 通过。
- 阶段 8 完整验证:`npm run verify:phase8` 通过,其中 BullMQ spike 15000 条消息、并发 500、端到端 705.65 TPS,满足 500 TPS。
- 前端 build 通过,仍存在既有 Vite chunk size warning。
- API Jest 使用 mock 依赖的结果仅代表单元/轻集成测试通过;系统功能验收仍要求 PostgreSQL/Redis/MinIO 和真实 API smoke 通过。
- 真实 PostgreSQL/Redis/MinIO smoke 通过:
- PostgreSQL `localhost:5432`、Redis `localhost:6379`、MinIO `localhost:9000/9001` 端口均连通。
- `npm --prefix api run prisma:migrate:deploy` 通过,无待应用迁移。
- API 以真实 `.env` 启动,`GET http://127.0.0.1:3101/api/health` 返回 `ok`
- `tools/smoke/real-env-smoke.mjs` 已补充最小幂等 seed,并验证登录、企业/客户、人工充值、余额检查、发送任务、MinIO 预签名上传、运营日志、发送链路追踪和账务对账聚合。
- 第 4 步发送前置校验 smoke 通过:
- `TC-TEMPLATE-001``TC-TEMPLATE-002``TC-TEMPLATE-003 / TC-RISK-005``TC-RISK-001``TC-RISK-002``TC-RISK-003``TC-RISK-004``TC-BILLING-001 / TC-TEMPLATE-005``TC-BILLING-PRECHECK-001` 均通过。
- 记录两个接口健壮性问题:不存在的 `reviewerId`、不存在的 `createdById` 会触发数据库外键 500。
- 客户侧导入发送、非法字符展示、敏感词接入发送前风控当前缺少完整入口,已在 `docs/testing-execution-step-4.md` 记录为阻塞缺口。
- 第 5 步发送链路 smoke 通过:
- `TC-SEND-001 / TC-SEND-002`、旧版 `TC-SEND-003 / TC-SEND-004``TC-SEND-005``TC-SEND-006``TC-SEND-007``TC-SEND-008`、旧版 `TC-SEND-009 / TC-SEND-010` 均通过。
- 2026-07-03 新增的通道组真实路由用例 `TC-SEND-010``TC-SEND-018` 尚未开发和执行,不能沿用旧 smoke 通过结论。
- 使用真实 Redis/BullMQ 和 `API_ENABLE_SEND_WORKER=true` 验证了任务创建、号码去重拆分、自动入队、Worker 消费、submit record、submit result、receipt、uplink、72 小时 unknown 转 timeout、客户端/运营端任务查看。
- 定时发送探测发现 `scheduledAt/sendMode=scheduled` 会被接口忽略,任务直接变为 `queued`,已在 `docs/testing-execution-step-5.md` 记录为功能缺口。
- 第 6 步计费和对账 smoke 通过:
- `TC-BILLING-001``TC-BILLING-002 / TC-RECHARGE-001``TC-BILLING-003``TC-BILLING-004 / TC-BILLING-005 / TC-BILLING-006 / TC-BILLING-007``TC-BILLING-008 / TC-RECON-001 seed``TC-RECON-001``TC-DASHBOARD-001 / TC-STAT-001``TC-TRACE-001``TC-BILLING-009` 均通过。
- 验证了费用预估、人工充值、余额检查、冻结、扣费、释放、退款、短信计费记录、账务流水、dashboard/statistics、trace 和 reconciliation。
- 自动计费探测发现发送任务不会自动生成短信计费记录、账户交易流水,消息金额默认为 0,已在 `docs/testing-execution-step-6.md` 记录为发送计费集成缺口。
- 第 7 步客户/通道 CMPP 连接状态和 Gateway smoke 通过:
- `TC-GW-CONTRACT-001``TC-GW-001``TC-GW-002``TC-GW-003``TC-GW-004``TC-CMPP-STATUS-001 / TC-CHANNEL-001``TC-CHANNEL-ROUTE-001``TC-CHANNEL-METRIC-001``TC-CMPP-SESSION-001 / TC-CONNECTION-COUNT-001``TC-CMPP-STATUS-002 / TC-OPERATIONS-MONITOR-001``TC-GATEWAY-EVENT-001` 均通过。
- 验证了 Gateway 队列契约、Go Gateway health/重连/tracker/gocmpp 模拟器、通道创建、路由、metrics、submit session、通道维度监控和 Gateway submit event trace。
- 客户级 CMPP 连接状态、客户连接数量、通道连接列表和 Gateway health 聚合到运营 dashboard 尚缺少一等 API,已在 `docs/testing-execution-step-7.md` 记录。
- 第 8 步最终回归通过:
- `npm run verify:phase8` 通过,BullMQ 15000 条消息、并发 500、端到端 TPS 668.71,满足 500 TPSPrisma generate、API build、前端 build 均通过。
- `npm run test:api` 通过,5 个 test suite、22 个 tests 全部通过。
- `npm run test:gateway` 通过,Go Gateway health、connection、tracker、cmpp、spike 测试全部通过。
- 最终归档见 `docs/testing-execution-step-8.md`
### 已知缺口
- 暂未新增前端测试框架,前端仍以 `npm run build` 作为 smoke。
- BullMQ 真实链路仍由 `npm run spike:bullmq` 覆盖,不在 Jest 内启动 Redis。
- Gateway 未接真实运营商 SMSC;真实 CMPP 互通需要运营商测试环境后补充联调记录。
- 客户侧导入发送、短信内容非法字符展示和敏感词发送前拦截尚未形成完整业务入口。
- 定时短信发送缺少计划发送时间字段、scheduled 状态、到点触发和取消接口。
- 发送链路尚未自动接入计费闭环,发送任务不会自动冻结/扣费/生成短信计费记录。
- 客户/通道 CMPP 连接状态和连接数量尚未形成运营端/客户端一等接口,Gateway health 未聚合到 NestJS dashboard。
## 2026-07-01 缺口修复复测
### 本轮修复范围
- P0 定时短信发送闭环:
- `SmsBatchTask` 增加 `scheduledAt``canceledAt` 字段和 `status/scheduledAt` 索引。
- `CreateBatchTaskDto` 支持 `sendMode=scheduled``scheduledAt`
- 新增定时任务取消和到点触发入口:`POST /api/client/send/batch-tasks/:id/cancel``POST /api/admin/send/scheduled/dispatch-due`
- 到点触发前重新校验企业状态、认证状态、应用状态、模板审核状态、签名审核/报备状态和账户余额。
- P0 发送链路自动计费闭环:
- 发送创建时按通道单价写入消息计费条数、单价和金额。
- 立即发送创建时执行账户检查和冻结;定时发送创建时检查余额,到点再冻结。
- submit accepted 后生成/更新 `SmsBillingRecord`、写入 charged 流水;submit rejected/timeout 释放冻结;失败回执和 72 小时超时执行退款。
- P0 发送前内容校验:
- 风控评估接入敏感词字典和控制字符扫描。
- `variableIssues` 扩展为 `{ variables, content }`,同时保留变量异常和内容异常证据。
- emoji/UCS2、多空格和换行不直接拒绝,继续通过 70/67 字符长度影响计费。
- P1/P2 补齐:
- 新增企业认证模型/API,提交、审核通过、驳回会同步 `Tenant.certificationStatus`,发送前强制认证通过。
- 新增客户侧导入预览/确认入口,覆盖 CSV/TXT 文本解析、20MB 限制、重复/非法/黑名单/变量缺失提示。
- 新增客户/通道 CMPP 连接状态模型/APIGateway 或本地 Gateway 模拟器可通过真实 API 回写连接状态,运营 dashboard 聚合连接状态。
- 新增应用密钥重置、应用/签名/模板状态变化、通道启停接口,并写入系统日志。
- 无效 `createdById``reviewerId` 改为明确 400,不再冒泡数据库外键 500。
### 新增/更新测试
| 测试文件 | 新增覆盖 |
| --- | --- |
| `api/src/send-chain/send-chain.service.spec.ts` | TC-SCHEDULE-001 到 006、TC-BILLING-AUTO-001 到 004、TC-IMPORT-001 到 006 子集、企业认证/状态阻断。 |
| `api/src/risk-review/risk-review.service.spec.ts` | TC-CONTENT-001 到 004 子集、敏感词和控制字符拦截、无效 createdById。 |
| `api/src/certification/certification.service.spec.ts` | TC-CERT-001 到 004 子集,认证提交、审核、驳回和租户状态同步。 |
| `api/src/channels/channels.service.spec.ts` | 通道启停日志、通道连接状态 upsert/list、客户连接状态 list。 |
| `api/src/sms-config/sms-config.service.spec.ts` | 无效 reviewerId 400,避免审核外键 500。 |
| `api/src/operations/operations.service.spec.ts` | Dashboard Gateway 连接状态聚合。 |
### 已执行命令
```bash
npm run prisma:generate
npm --prefix api test -- send-chain.service.spec.ts risk-review.service.spec.ts sms-config.service.spec.ts
npm --prefix api test
npm --prefix api run build
npm --prefix api run prisma:migrate:deploy
npm run verify:phase8
npm run test:api
npm run test:gateway
```
### 当前结果
- Prisma Client 生成通过。
- 新增迁移已应用到真实 PostgreSQL
- `20260701103000_add_scheduled_sms_fields`
- `20260701110000_add_certification_and_connection_state`
- API build 通过。
- API Jest7 个 test suite 通过,34 个测试通过。
- 最终回归通过:
- `npm run verify:phase8` 通过,BullMQ 15000 条消息、并发 500、端到端 TPS 608.93,满足 500 TPSPrisma generate、API build、前端 build 均通过。
- `npm run test:api` 通过,7 个 test suite、34 个 tests 全部通过。
- `npm run test:gateway` 通过,Go Gateway health、connection、tracker、cmpp、spike 测试全部通过。
### 剩余说明
- 客户侧导入当前提供 API 级文本预览/确认闭环;浏览器端真实文件选择、GBK 二进制转码和错误文件下载仍需前端/E2E 后续覆盖。
- Gateway 连接状态通过 NestJS API 支持 Go Gateway 或本地模拟器回写;真实运营商 SMSC 联调仍需运营商测试环境。
## 2026-07-02 运营端优化转真实后端补齐
### 本轮修复范围
- 修正测试策略说明:mock 仅作为单元/轻集成测试替身,不能作为业务完成标准;新增页面能力必须接 NestJS API、Prisma/PostgreSQL 和必要操作日志。
- 通道管理补齐真实 API
- `POST /api/admin/channels/:id/copy`:复制通道配置、通道报备字段和该通道签名报备材料,写入操作日志。
- `DELETE /api/admin/channels/:id`:软删除通道,避免破坏历史发送/报备外键。
- `GET /api/admin/channels/:id/connection-logs`:基于 `OperationLog``CmppConnectionState` 查询连接日志;保留 `/link-logs` 兼容旧前端。
- 安全控制补齐真实 API:敏感词、全局黑名单、企业黑名单支持 keyword/status 查询、创建、启停/软删除,并写操作日志。
- 企业黑名单修正为企业应用级黑名单:Prisma `EnterpriseBlacklist` 新增 `applicationId` 并改为 `applicationId + phoneNumber` 唯一;运营端页面按企业和短信应用新增/搜索;发送预览和风控只命中当前应用的 active 黑名单。
- 模板审核补齐真实查询:运营端模板列表支持 keyword/status,并返回企业、应用、签名信息;前端模板审核页已改为调用真实 API。
- 运营端企业模板管理新增短信模板时,后端直接写入 `auditStatus=approved`;短信模板审核页展示为“已通过”,客户端自行提交模板仍保留审核流。
- 企业认证审核补齐真实查询:列表支持 keyword/status,详情返回企业信息和认证 materials;前端企业认证审核页已改为调用真实 API。
- 前端新增 `/api` Vite 代理和 `src/api/adminApi.ts`,通道管理、模板审核、企业认证审核应调用真实 API;API 不可用时页面应展示错误态或空态,静态兜底不能作为验收通过依据。
### 新增/更新测试
| 测试文件 | 新增覆盖 |
| --- | --- |
| `api/src/channels/channels.service.spec.ts` | 通道复制、软删除、连接状态日志写入、连接日志查询。 |
| `api/src/dictionaries/dictionaries.service.spec.ts` | 敏感词、全局黑名单、企业黑名单查询、创建、软删除和操作日志。 |
### 已执行命令
```bash
npm --prefix api run build
npm --prefix api test
npm run build
```
### 当前结果
- API build 通过。
- API Jest8 个 test suite 通过,38 个测试通过。
- 前端 build 通过,仍存在既有 Vite chunk size warning。
### 文档同步
- 已将今天的客户端和运营端优化要求补入 `docs/first-version-development-requirements.md`
- 去除客户端独立账号设置菜单,改为头像下拉承载退出登录和修改密码。
- 明确模板审核搜索、企业认证详情审核、企业应用 CMPP 连接数/连接详情/参数复制、通道复制、通道软删除、通道连接日志、安全控制 CRUD、系统日志分页等均需要真实后端 API 支撑。
- 补充客户端用户、运营端企业认证、通道、连接、字典、安全控制、系统日志等接口范围。
- 修正 Codex 执行模板,明确 mock、localStorage 或静态数据不得作为真实开发完成标准。
- 已将今天的验收点补入 `docs/system-functional-test-cases.md`
- 新增 TC-CLIENT-010 到 TC-CLIENT-011。
- 新增 TC-ADMIN-014 到 TC-ADMIN-022。
## 2026-07-02 新增浏览器和业务闭环用例执行
### 执行环境
- API`npm --prefix api run start:dev`,监听 `http://localhost:3000/api`
- 前端:`npm run build` 后使用 `npm run preview -- --port 4173`,访问 `http://localhost:4173`
- Browser 插件:可连接本地 tab,但对 Vite dev 页 `Page.navigate` 超时;改用临时目录 Playwright 包加本机 Chrome 执行浏览器 smoke,未修改项目依赖。
- PostgreSQL:本地 `localhost:5432` 可用,API smoke 使用真实数据库。
### 已执行命令
```bash
npm run build
# 临时目录 C:\Users\hectorzhao\AppData\Local\Temp\cmpp-pw-smoke
npm init -y
npm install playwright --no-save
node <browser-and-api-smoke>
```
### 通过用例
| 用例 | 结果 | 覆盖点 |
| --- | --- | --- |
| TC-DASHBOARD-CLIENT-UI | UI-SMOKE PASS / BACKEND GAP | 客户端 Dashboard 可渲染账户余额、今日发送、账户状态,但页面数据仍需确认全部来自真实 API。 |
| TC-DASHBOARD-ADMIN-UI | UI-SMOKE PASS / BACKEND GAP | 运营端 Dashboard 可渲染今日发送总量、总体成功率、企业消费排行、通道运行,但当前源码仍存在 mock 数据路径。 |
| TC-BILLING-MANUAL-UI | UI-SMOKE PASS / BACKEND GAP | 运营端人工充值弹窗填写后,前端表格新增企业、金额、操作人和备注;该页面当前未调用真实充值 API。 |
| TC-LOG-ADMIN-UI | UI-SMOKE PASS / BACKEND GAP | 运营端系统日志页面可展示人工充值、账户计费等记录;页面数据仍需接真实日志 API。 |
| TC-LOG-CLIENT-UI | UI-SMOKE PASS / BACKEND GAP | 客户端系统日志页面可渲染并展示客户侧日志记录;页面数据仍需接真实日志 API。 |
| TC-CMPP-STATUS-UI | UI-SMOKE PASS / BACKEND GAP | 企业应用管理可展示 CMPP 状态和连接数量并打开连接详情;页面当前仍有本地初始数据路径。 |
| TC-FRONTEND-CONSOLE | PASS | 关键页面无相关 console error/pageerror;仅忽略 favicon 404。 |
| TC-BILLING-MANUAL-API | PASS | 人工充值无需审批:确认后账户余额、短信条数、充值单、账户流水和 Dashboard transactions 聚合同步更新。 |
| TC-CMPP-STATUS-API | PASS | 通道创建、Gateway 连接状态回写、按通道/客户查询、连接日志和 Dashboard gatewayConnections 聚合通过。 |
### 发现和说明
- `npm run dev` 在本机 5173 被占用后切换到 5174Vite 首次依赖 bundling 长时间未完成,浏览器看到白屏;生产构建和 preview 渲染正常。
- 运营端人工充值页面当前是前端本地状态 smoke,不能作为系统功能通过;真实入账闭环通过 `POST /api/admin/billing/manual-recharges` 验证。
- 人工充值不需要审批,测试口径已同步修正为“有权限确认即入账,不产生 pending 审批态”。
### 真实后端缺口和 Bug 清单
| 编号 | 严重级别 | 问题 | 证据 | 期望修复 |
| --- | --- | --- | --- | --- |
| BUG-FE-001 | P0 | 运营端人工充值页面未调用真实后端,提交后只更新前端本地表格状态。 | `src/apps/admin/AdminRechargeRecordsPage.tsx` 使用 `rechargeRecordsSeed``useState``submitManualRecharge``setRecords`。 | 页面提交调用 `POST /api/admin/billing/manual-recharges`,成功后刷新真实充值记录、账户余额、流水和日志。 |
| BUG-FE-002 | P0 | 运营端 Dashboard 仍使用 mock service 和静态排行,不能证明真实统计准确。 | `src/apps/admin/AdminHome.tsx` 引用 `adminService``hourlySendTrend``auditTrend`,指标从前端数组计算。 | 接入 `GET /api/admin/operations/dashboard/statistics` 或拆分真实统计接口,所有卡片和排行从 API 返回。 |
| BUG-FE-003 | P0 | 客户端 Dashboard 仍使用 mock service,余额、发送量、最近充值等不是实时后端数据。 | `src/apps/client/ClientHome.tsx` 使用 `clientService.getOverview()` 和客户端 mock 数据。 | 接入客户端真实 dashboard、账户、任务、充值流水 API,点击明细继承真实筛选条件。 |
| BUG-FE-004 | P0 | 客户端和运营端系统日志页面仍有静态数据路径,无法验证真实日志、分页、筛选和租户隔离。 | `src/apps/admin/AdminSystemLogsPage.tsx``src/apps/client/ClientSystemLogsPage.tsx` 页面 smoke 可展示,但未证明调用真实日志 API。 | 接入真实日志 API,支持分页、筛选、详情、租户隔离,失败动作也可查。 |
| BUG-FE-005 | P0 | 企业应用 CMPP 状态和连接详情页面仍使用本地初始数据,未读取真实连接状态 API。 | `src/apps/admin/AdminEnterpriseApplicationsPage.tsx` 使用 `initialSmsApps``setSmsApps`,连接删除也是本地状态变更。 | 接入企业应用、连接状态、连接详情、连接删除/断开真实 API 或 Gateway 回写接口。 |
| BUG-API-001 | P1 | 通道创建参数缺失时返回 Prisma 500,而不是业务 400。 | 浏览器 smoke 第一轮 `POST /api/admin/channels` 缺少 `code/gatewayHost/gatewayPort/account/passwordCipher/srcId`API 返回 Internal server error。 | 为通道创建 DTO 增加校验,缺失必填字段返回 400 和可读错误,并写失败日志。 |
| BUG-DEV-001 | P1 | `npm run dev` 在 5173 被占用后切到 5174Vite 依赖 bundling 长时间未完成,浏览器看到白屏。 | 本轮浏览器测试中 5174 HTTP 后续可达,但首次打开截图为空白;生产 build/preview 正常。 | 检查 Vite dev 依赖预构建和端口占用问题,确保开发模式可稳定渲染。 |
| BUG-SEND-001 | P0 | 发送路由规则允许直接绑定单个通道,违反“规则只能绑定通道组”的业务约束。 | `SendChainService.selectChannel()` 当前存在 `route?.channel ?? route?.group...` 路径;`ChannelRouteRule` 模型也保留 `channelId` 字段。 | 路由规则只能表达应用到通道组的绑定关系;发送链路必须从企业应用绑定的运营商通道组内选路,不允许规则直接指定单个通道。 |
| BUG-SEND-002 | P0 | 未命中路由规则时会 fallback 到全局第一个 `active` 通道,可能把短信发到未配置给该企业/应用的通道。 | `SendChainService.selectChannel()` 未找到 route 后执行 `smsChannel.findFirst({ where: { status: 'active' } })`。 | 企业应用没有配置对应运营商通道组或无可用通道时,短信直接 failed;不得进入 pending/delayed,不得 fallback 到其他 active 通道,需记录 trace/日志。 |
| BUG-SEND-003 | P0 | 发送选路只判断通道业务状态 `active`,不判断 CMPP 真实连接状态。 | `selectChannel()` 只检查 `SmsChannel.status`,未查询 `CmppConnectionState.status/currentConnections/lastHeartbeatAt/lastError`。 | 选路必须跳过离线、认证失败、心跳超时、重连中或 `currentConnections=0` 的通道;至少 1 条连接 connected 且心跳正常才可发送,连续 3 次心跳失败进入重连且不可选。 |
| BUG-SEND-004 | P0 | 通道组主通道提交失败、超时或回执失败后不会切换到下一个通道补发。 | `handleSubmitResult()``handleReceipt()` 只更新状态、释放/退款和刷新进度,没有重新选路或创建补发记录;`retry.maxAttempts` 目前未形成业务补发闭环。 | 除 unknown、超过 72 小时、超过通道组补发时间上限或通道组关闭补发外,submit rejected/timeout、连接断开、未提交成功、receipt failed 均需补发;省网失败后立即走全国通道,全国通道按优先级继续补发,最终成功只按企业应用客户费率扣一次。 |
| BUG-SEND-005 | P0 | 通道组省网/全国路由没有接入真实发送链路,手机号段库也未参与归属地识别。 | `SmsChannelGroupItem``ChannelRouteRule` 虽有 `carrier/province` 字段,`PhoneSegment``prefix/carrier/province/city`,但 `SendChainService.selectChannel()` 未读取 message.phoneNumber、未查询 `phoneSegment`,只按优先级取第一个 active 通道;前端 `AdminChannelGroupFormPage` 的省网/全国配置仍为本地 `useState`。 | 发送前按可配置号码前缀正则识别运营商,识别失败走移动通道组;按手机号段库识别省份和城市,省份识别失败走对应运营商全国通道;通道需支持移动/联通/电信/三网和全国/单省发送地区,三网作为通配。 |
| BUG-SEND-006 | P0 | 企业应用缺少按运营商绑定多个通道组和保存校验的真实闭环。 | 当前发送链路只按 `tenantId/applicationId` 查询单一路由规则;未体现一个应用分别绑定移动、联通、电信通道组,也未强制至少绑定一个通道组后才能保存。 | 企业应用可分别绑定移动、联通、电信通道组;一个都不绑定时 UI 不允许保存,发送时直接 failed;移动、联通、电信短信按识别结果进入对应通道组。 |
## 2026-07-03 通道组真实路由和补发修复
### 本轮修复范围
- BUG-SEND-001:后端 `createRouteRule` 禁止直接绑定单通道,路由规则只能绑定应用、运营商和通道组;发送链路不再读取 `route.channel`
- BUG-SEND-002:发送链路未找到企业应用对应运营商通道组或无可用在线通道时,短信直接标记 `failed`,不再 fallback 到全局第一个 active 通道。
- BUG-SEND-003:发送选路加入 CMPP 连接状态过滤,通道必须业务 `active`、连接 `connected``desiredConnections > 0``currentConnections > 0` 才可选;`online/open` 仅作为旧 Gateway 回写兼容词入库归一化。
- BUG-CMPP-STATUS-001:新建/启用通道后若 Gateway 连接请求长时间无回写,API 后台兜底任务会将超过 30 秒的 `connecting` 连接标记为 `failed`,写入超时原因和连接日志,避免页面长期停留“连接中”。
- BUG-SEND-004submit rejected、submit timeout、回执 failed 等失败场景会在补发开启且未超过时间限制时,排除已尝试通道并切换到同一通道组全国通道继续提交;unknown、超过 72 小时、超过通道组补发上限或关闭补发时不补发。
- BUG-SEND-005:新增 `PhoneCarrierRule` 运营商前缀正则配置,发送前先识别运营商,识别失败默认移动;手机号段库用于识别省份,省份识别失败走对应运营商全国通道;通道新增 `sendRegion`,支持全国或单省。
- BUG-SEND-006:企业应用创建页面可分别选择移动、联通、电信通道组,一个都不选时 UI 阻止保存;创建应用成功后写入真实通道组路由规则。
- 前端配置补充:通道创建支持移动、联通、电信、三网和发送地区;手机号段库新增“运营商区分规则”tab。
### 新增/更新测试
| 测试文件 | 新增覆盖 |
| --- | --- |
| `api/src/send-chain/send-chain.service.spec.ts` | 应用运营商通道组路由、在线连接过滤、失败不 fallback、补发关闭时释放/退款。 |
| `api/src/channels/channels.service.spec.ts` | 通道发送地区默认值、通道组补发配置、禁止单通道路由规则。 |
### 已执行命令
```bash
npm --prefix api run prisma:generate
npm --prefix api test -- send-chain.service.spec.ts channels.service.spec.ts --runInBand
npm --prefix api test
npm --prefix api run build
npm run build
npm run verify:phase8 # 阻塞:BullMQ spike endToEndTps 未达到 500
npm run spike:bullmq # 复跑仍未达到 500
```
### 当前结果
- Prisma Client generate:通过。
- API Jest10 个 test suite 通过,50 个测试通过。
- API build:通过。
- 前端 build:通过,仍存在既有大 chunk warning。
- `npm run verify:phase8`:未通过,阻塞在 `spike:bullmq` 性能阈值;第一次 endToEndTps=464.58,复跑 `npm run spike:bullmq` endToEndTps=495.97,第三次 endToEndTps=477.17,均低于 500 TPS 阈值。
- 尚未执行真实 PostgreSQL/Redis/Gateway 端到端 smoke;需在生产验证或本地真实服务环境中覆盖 `TC-SEND-010``TC-SEND-018``TC-CMPP-STATUS-008A`
## 2026-07-02 真实后端缺口修复
### 本轮修复范围
- BUG-FE-001:运营端充值记录页移除 `rechargeRecordsSeed` 验收路径,加载真实租户、人工充值记录、账户余额和账户流水;确认人工充值调用 `POST /api/admin/billing/manual-recharges`,成功后刷新真实记录、账户、流水,并由后端写 `billing.manual_recharge` 操作日志,不产生 pending 审批态。
- BUG-FE-002:运营端 Dashboard 移除 `adminService`、静态趋势和前端排行计算,改为调用 `GET /api/admin/operations/dashboard/statistics`、真实通道 API 和真实账户聚合。
- BUG-FE-003:客户端 Dashboard 移除 `clientService`、静态趋势和本地 mock,改为调用 `GET /api/client/operations/dashboard`、客户端账务/任务聚合,并通过 `x-tenant-id` 限定当前租户。
- BUG-FE-004:运营端和客户端系统日志页移除静态 `logsSeed`,接入真实日志 API,支持分页、关键字、级别、模块和时间范围;长详情使用详情卡展示 JSON 摘要。
- BUG-FE-005:企业应用管理短信应用 tab 接入真实企业应用、租户连接状态、连接详情和 CMPP 参数 API;断开连接调用真实后端并写系统日志,变更后刷新列表。彩信 tab 仍为第一版待开发路径,不作为短信验收依据。
- BUG-API-001:通道创建在 Service 层校验 `code/name/gatewayHost/gatewayPort/account/passwordCipher/srcId`,缺失或端口非法返回 400,不再让 Prisma validation error 冒泡成 500。
- BUG-DEV-001:复现 Vite 8 dev server 在端口切换后依赖/模块转换请求超时,导致白屏;根 `npm run dev` 改为先 `npm run build``vite preview --host 0.0.0.0`,确保本地打开稳定。`vite.config.ts` 保留 `optimizeDeps.noDiscovery`,避免自动扫描引发的预构建卡住。
### 新增/更新测试
| 测试文件 | 新增覆盖 |
| --- | --- |
| `api/src/channels/channels.service.spec.ts` | 通道创建缺少必填字段时返回可读 400。 |
| `api/src/billing/billing.service.spec.ts` | 人工充值写入 `billing.manual_recharge` 操作日志。 |
| `api/src/operations/operations.service.spec.ts` | Dashboard 新增今日统计、账户/充值/待审核聚合和系统日志分页详情。 |
| `api/src/sms-config/sms-config.service.spec.ts` | 企业应用列表聚合真实 CMPP 连接状态、CMPP 参数读取、断开连接写日志。 |
### 已执行命令和 Smoke
```bash
npm --prefix api test
npm --prefix api run build
npm run build
npm run dev
# API HTTP smoke on API_PORT=3101
GET /api/health
POST /api/admin/channels # 缺必填字段返回 400
GET /api/admin/operations/dashboard/statistics
GET /api/admin/system-logs?page=1&pageSize=2
```
### 当前结果
- API Jest8 个 test suite 通过,43 个测试通过。
- API build:通过。
- 前端 build:通过,仍存在既有大 chunk warning。
- `npm run dev`:通过,当前会 build 后启动 Vite preview,实际可访问 `http://localhost:4173/`,避免 Vite 8 dev optimizer/transform 白屏。
- 浏览器 smoke 通过:
- 运营端 Dashboard 渲染真实聚合指标,无相关 console error。
- 运营端人工充值页渲染真实记录,人工充值弹窗展示真实企业下拉和确认入口。
- 运营端系统日志页渲染真实日志,长详情以卡片展示。
- 企业应用管理页渲染真实应用和 CMPP 状态,连接详情弹窗和 CMPP 参数弹窗可打开。
- 客户端 Dashboard 和客户端系统日志页按当前租户渲染,无相关 console error。
- API HTTP smoke`/api/health` 返回 ok;通道缺参返回 400 和可读错误;dashboard/statistics、system-logs 返回真实数据。
### 剩余说明
-`npm run dev` 为稳定预览模式,不提供 Vite HMR;保留原因是 Vite 8/Rolldown dev transform 在当前 Windows + 中文路径工作区下会阻塞模块请求并造成白屏。开发时如需热更新,可另行评估降级 Vite 或迁移工作区路径后恢复原生 dev server。
## 2026-07-02 登录与用户管理闭环补充
### 本轮修复范围
- 新增用户登录字段和 fail2ban 持久化字段:`email``phone``failedLoginCount``lockedUntil``lastLoginAt``deletedAt`
- 登录入口拆分为 `/client/login``/admin/login`,两端均调用真实验证码和登录 API。
- 用户登录入口和用户表单统一文案为“用户名/登录账号”,提示可用用户名、邮箱或手机号登录。
- 运营端登录仅允许 `platform_admin`;客户端登录仅允许已关联企业的 `enterprise_admin`
- 运营端用户管理接入真实 `/api/admin/users`,支持平台管理员和企业管理员的新增、编辑、启用/禁用、删除、改密;企业管理员必须关联企业。
- 客户端用户管理接入真实 `/api/client/users`,所有操作继承当前登录企业 `tenantId`
- 启用/禁用、删除均通过确认弹窗执行;用户删除采用软删除,不破坏历史日志和业务记录。
- 连续 5 次登录失败后锁定 24 小时;登录成功清空失败次数和锁定状态。
### 已执行测试
```bash
npm --prefix api test -- users.service.spec.ts auth.service.spec.ts
npm --prefix api run prisma:generate
npm --prefix api run build
npm run build
```
### 当前结果
- `users.service.spec.ts``auth.service.spec.ts`:通过,覆盖用户类型约束、企业关联约束、操作日志、端登录隔离和失败次数累计。
- API build:通过。
- 前端 build:通过,仍有既有大 chunk warning。
- `tools/smoke/real-env-smoke.mjs` 已同步企业管理员邮箱/手机号、角色 seed、验证码登录和 CMPP 端口 `17890`
- 真实数据库迁移、浏览器端登录 smoke 需要在预发布环境执行 `prisma migrate deploy` 后补充记录。
## 2026-07-02 非彩信纯 mock 菜单真实化
### 本轮修复范围
- 客户端:充值套餐、账单流水、批量任务、短信发送、短信签名、短信模板改为调用真实 API;签名材料使用真实文件元数据和材料关联接口;发送任务调用真实批量任务接口。
- 运营端:数据统计、账务账户、发送监控、敏感词、全局黑名单、企业黑名单、手机号段库、报备字段库、通道组、通道报备字段、报备任务、报备记录、短信审核、短信记录改为真实 API。
- 企业管理:客户列表、客户表单、客户详情由 `adminEnterpriseMock`/localStorage 改为真实租户、账户、应用、签名、模板接口;后端补充租户编辑、状态变更和删除接口。
- 通道管理:删除静态通道兜底,API 失败展示错误态。
- 企业认证审核:删除静态认证兜底,API 失败展示错误态。
- 企业签名/企业模板运营端列表只展示真实短信配置数据;彩信相关菜单继续作为待开发边界,不计入第一版短信验收。
### 已执行命令
```bash
npm --prefix api test
npm --prefix api run build
npm run build
npm run verify:phase8
```
### 当前结果
- API Jest10 个 test suite 通过,49 个测试通过。
- API build 通过。
- 前端 build 通过,仍存在既有大 chunk warning。
- `npm run verify:phase8` 通过:
- Gateway 队列契约 4 个示例通过。
- Go Gateway 测试通过。
- BullMQ 15000 条消息、并发 500、端到端 TPS 681.47,满足 500 TPS。
- Prisma generate、API build、前端 build 均通过。
- 源码搜索剩余静态业务数据集中在彩信待开发页面、彩信审核页面、企业应用彩信 tab,以及 `src/api/session.ts` 的登录 session 持久化;非彩信主菜单的 `clientService`/`adminService` 业务路径已清理。
### 待复测
- 浏览器 smoke 和真实文件上传 smoke 需要在预发布环境补跑,重点复测客户端发送、签名材料上传、短信审核、短信记录、客户管理和报备任务。
## 2026-07-03 单运营商通道组、应用级费率和回执幂等
### 本轮修复范围
- 通道组规则:
- `SmsChannelGroup.carrier` 固化为移动、联通、电信三选一,禁止三网通道组。
- `SmsChannelGroupItem.carrier` 保留并参与发送,必须等于通道组运营商。
- 三网只作为通道本体能力 `SmsChannel.carrier=all`,放入某个通道组后只服务该组运营商。
- 同一通道组内同一省份只能配置一个通道;省份 item 必须引用发送地区一致的通道。
- 全国通道允许多个,但同一通道组内全国通道优先级禁止重复;本期不做权重分流。
- 路由规则必须绑定应用、运营商、通道组,且 route carrier 必须等于 group carrier。
- 发送和计费规则:
- 运营商以号码前缀正则为准;手机号段库只提供省份/城市,carrier 仅作后台提示或校验。
- 发送前校验最终选中通道的签名报备任务为 approved,补发切换通道时重新校验。
- 企业应用新增 `customerUnitPrice`,客户扣费按应用级客户费率;通道成本只作内部成本。
- 迟到旧通道 failed receipt 不覆盖新通道 delivered 最终状态;历史回执仍入库可查。
- 重复 submit/receipt 回调不得重复扣费、释放冻结或退款。
- 补发使用触发时当前通道组配置;本期不考虑人工重发。
- 前端和真实 smoke
- 企业应用表单增加客户单价输入,保存时写入真实应用 API。
- 企业应用按移动、联通、电信分别选择通道组,选项按通道组 carrier 过滤。
- 真实 smoke seed 补充应用客户单价、三网通道放入移动组、签名-通道 approved 报备。
### 新增/更新测试
| 测试文件 | 新增覆盖 |
| --- | --- |
| `api/src/channels/channels.service.spec.ts` | 单运营商通道组、通道组 item carrier 校验、通道 carrier 兼容、省份与发送地区一致、同省唯一、全国优先级唯一、route carrier 与 group carrier 一致。 |
| `api/src/send-chain/send-chain.service.spec.ts` | 通道组 item carrier 参与发送、最终通道签名报备校验、应用级客户费率、迟到旧 failed receipt 不覆盖 delivered、重复账务动作幂等。 |
| `api/src/sms-config/sms-config.service.spec.ts` | 应用配置与列表在新增客户费率字段后继续通过。 |
### 已执行命令
```bash
npm --prefix api run prisma:generate
npm --prefix api test -- channels.service.spec.ts send-chain.service.spec.ts sms-config.service.spec.ts --runInBand
npm --prefix api test
npm --prefix api run build
npm run build
npm --prefix api run prisma:migrate:deploy
$env:API_PORT='3101'; $env:API_ENABLE_SEND_WORKER='true'; npm --prefix api run start:dev
node tools/smoke/real-env-smoke.mjs
node <inline channel-group rule HTTP smoke>
npm run spike:contracts
npm run test:gateway
npm run verify:phase8
```
### 当前结果
- Prisma Client 生成通过。
- 新增迁移已应用到真实 PostgreSQL
- `20260703143000_add_channel_group_carrier`
- `20260703152000_add_application_customer_rate`
- API Jest10 个 test suite 通过,56 个测试通过。
- API build 通过。
- 前端 build 通过,仍存在既有 Vite chunk size warning。
- 真实 API smoke 通过:
- `tools/smoke/real-env-smoke.mjs` 通过,验证真实 API、Prisma/PostgreSQL、Redis/BullMQ、登录、充值、发送任务、worker 入队、文件上传元数据和操作日志。
- inline 通道组规则 HTTP smoke 通过,覆盖三网组拒绝、item carrier 不匹配拒绝、通道 carrier 不兼容拒绝、省份/发送地区不匹配拒绝、同省重复拒绝、全国优先级重复拒绝、route carrier/group carrier 不匹配拒绝。
- Gateway 队列契约通过,4 个示例均验证通过。
- `npm run test:gateway` 通过,Go Gateway health、connection、tracker、cmpp、spike 测试全部通过。
- `npm run verify:phase8` 未通过,仍阻塞在已知 BullMQ spike 性能阈值:
- 15000 条消息、并发 500。
- enqueue TPS 3139.43。
- end-to-end TPS 479.02,低于 500 TPS。
### 剩余说明
- `verify:phase8` 当前失败点是独立 BullMQ 性能阈值,不是本轮通道组、计费、报备、回执业务逻辑测试失败。
- 浏览器端完整手工回归仍建议补跑企业应用创建、通道组配置、短信记录详情弹窗中的历史回执展示。
## 2026-07-07 Gateway 下游 CMPP 入站第一阶段补齐
### 本轮修复
- Go Gateway 启动时同时监听 `GATEWAY_CMPP_ADDR`,默认生产端口 `0.0.0.0:17890`,不再只是 HTTP `/health` 控制服务。
- 新增企业应用独立 6 位 `cmppAccount`Prisma 迁移 `20260707162000_add_application_cmpp_account` 会为存量应用生成账号;客户端/运营端 CMPP 参数接口返回该应用独立账号。
- Gateway 下游 CMPP bind 使用真实 gocmpp 协议解析 `Source_Addr/AuthSource/Timestamp`,调用 NestJS `/api/gateway/events/inbound/authenticate`,由真实数据库校验应用账号、应用 CMPP 密码、企业状态、企业认证状态、应用状态和 IP 白名单。
- Gateway 下游 CMPP submit 解码 CMPP 3.0 `SubmitReq`,调用 NestJS `/api/gateway/events/inbound/submit`NestJS 按 `sourceType=cmpp` 创建发送记录并复用模板/签名/风控/余额/运营商识别/通道组路由/队列优先级链路。
- Go Gateway 新增入站集成测试,覆盖本地 CMPP 客户端 connect/login、UCS2 submit 和 API 回调。
### 验证状态
- `npm --prefix api run prisma:generate`:通过。
- `npm --prefix api test -- sms-config.service.spec.ts send-chain.service.spec.ts --runInBand`:通过。
- `npm --prefix api run build`:通过。
- `go test ./...`Gateway):通过。
### 剩余缺口
- 下游连接状态回写、连接数上限、断开/心跳历史日志仍需继续产品化。
- 下游 submit 当前通过 `sourceType=cmpp` 的系统批次兼容承载,尚未完全拆成独立单条发送模型。
- 客户侧最终 Deliver Receipt 投递、客户侧上行 Deliver 推送、上游真实 SMSC submit worker、上游 receipt/uplink 生产解析仍未完成。
## 2026-07-11 Gateway 客户侧 Submit 日志完善
### 本轮修复
- Gateway 入站 Submit 日志增加 `submit_received``submit_accepted``submit_rejected` 结构化事件,同时记录 CONNECT 声明的客户协议版本与 Go 实际解包类型,并记录账号、客户 IP、sequenceId、号码、srcId、编码、分片、CMPP result、平台 messageId、CMPP Msg_Id 和处理耗时。
- Gateway HTTP 回调在 NestJS 返回非 2xx 时保留最多 64KB 响应体,客户 Submit 失败日志可直接显示模板不匹配、IP 白名单、余额或路由等真实业务原因,不再只显示 HTTP 状态码。
- 日志不记录明文短信正文,仅记录字符数和 MD5 哈希,便于比对同一内容且避免日志泄露。
- 将当前 gocmpp 版本固定为仓库内小型 fork,仅在 server 循环补充底层诊断:包在进入业务 handler 之前发生长度、命令字、包体读取或 Unpack 失败时,记录 `read/unpack packet failed`、远端地址、库解析协议模式、Go 错误类型和原始错误;正常 EOF 断开不记为解包失败。
- 生产复现确认 CMPP2.0 客户 Submit 被固定 CMPP3.0 解包导致 `MsgSrc/手机号/内容` 错位为空。现在 gocmpp server 按 CONNECT `Version` 将每条连接切换到 CMPP2.0/2.1/3.0 解包模式,并返回同版本 ConnectResp、SubmitResp 和 Deliver。
- Gateway 使用 bind 时已鉴权会话账号调用 NestJS 入站接口,Submit `MsgSrc` 改为与鉴权返回的应用级 `cmppEnterpriseCode` 独立比对,不再将企业代码误当登录账号。
### 验证状态
- `go test ./internal/inbound -count=1`:通过。
- `go test ./... -count=1`:通过。
- `go build ./cmd/gateway`:通过。
- 真实 TCP 非法包用例:向入站端口写入非法 `total_length`,确认业务 handler 未执行时仍产生 `read/unpack packet failed` 日志。
- CMPP2.0 真实集成用例:客户使用与登录账号不同的 `MsgSrc=SP0001`,完成 V20 ConnectResp、Cmpp2SubmitReq/Resp 和 Cmpp2Deliver ReceiptNestJS 收到的 account 仍为 bind 账号:通过。
- 已将合并后提交 `bb4992f0` 部署到预发布环境;Prisma 无待执行迁移,前端/API/Gateway 构建和标准健康检查通过,12026/3000/8090/17890 监听正常。生产账号 `910887` 重连日志确认 `requested_version=0x20 response_version=0x20`;该测试应用的 `cmppEnterpriseCode` 已通过真实运营 API 同步为 `910887`
## 2026-07-07 Gateway 上游提交与下游 Deliver 闭环补齐
### 本轮修复
- `SubmitCommand` 契约、示例和 Go 结构增加 `upstream.gatewayHost/gatewayPort/account/passwordCipher/cmppVersion`,API 发送链路在真实业务校验通过后保留 BullMQ 审计投递,同时写入 Redis Stream `gateway.submit.commands` 主命令流。
- Go Gateway 新增上游提交管理器,按通道建立/复用 gocmpp 客户端连接,发送真实 CMPP Submit,接收 SubmitResp,并回调 NestJS `SubmitResult`
- Go Gateway 上游读循环开始处理 deliver receipt 和普通 deliver 上行:receipt 解析后回调 NestJS `/gateway/events/receipt`,普通上行解码后回调 `/gateway/events/uplink`
- Gateway 下游入站服务记录客户 Submit 对应的 messageId 到在线客户连接映射;NestJS 收到最终 receipt/uplink 并入库后调用 Gateway `/downstream/receipt``/downstream/uplink`Gateway 向在线客户下发 CMPP Deliver Receipt 或普通 Deliver。
- `api/src/send-chain/send-chain.service.spec.ts` 覆盖 SubmitCommand 上游配置和 Redis Stream 发布;`gateway/internal/inbound/server_test.go` 覆盖客户 submit 后平台下发 Deliver ReceiptGateway 契约示例覆盖新 upstream 字段。
### 验证状态
- `npm --prefix api test -- send-chain.service.spec.ts`:通过。
- `npm --prefix api test`:通过,12 个 suites、82 个 tests。
- `npm --prefix api run build`:通过。
- `go test ./...`Gateway):通过。
- `npm run spike:contracts`:通过,4 个 Gateway 队列契约示例通过。
### 剩余缺口
- Gateway 控制面 `/upstream/submit` 仅保留为调试/补偿入口;生产主链路由 Go Gateway submit worker 消费 Redis Stream `gateway.submit.commands` 触发。worker 当前覆盖新消息 `>` 消费和 ack,pending 历史消息扫描与精细重试治理放入后续在途恢复阶段。
- 客户侧 Deliver Receipt/上行 Deliver 当前依赖 Gateway 内存在线连接映射;客户断线、Gateway 重启或映射丢失时尚未实现持久化缓存、重试和投递失败审计。
- 普通上行只有能关联 messageId 的事件可推送给客户;仅按接入号、手机号、应用和时间窗口匹配客户连接仍待产品化。
- 长短信拆分/重组、多连接窗口、窗口满、在途消息恢复、断线重连后的状态补偿仍待后续实现和压测。
## 2026-07-07 阶段 1Gateway SubmitCommand 独立消费
### 本轮修复
- NestJS SendChain 取消主链路同步调用 Gateway `/upstream/submit`;真实业务校验通过后创建 SmsSubmitRecord、保留 BullMQ `gateway.submit.queue` 审计/兼容投递,并向 Redis Stream `gateway.submit.commands` 写入 `SubmitCommand`
- Go Gateway 新增 `submitworker`,启动时默认创建/复用 consumer group `cmpp-gateway`,独立消费 Redis Stream 中的 `SubmitCommand`,调用同一个上游提交管理器真实 submit 到上游 SMSC。
- Gateway `/upstream/submit` 保留为调试/运维补偿接口,不作为 API 主发送路径。
- Gateway worker 支持环境变量:`REDIS_URL``GATEWAY_SUBMIT_STREAM``GATEWAY_SUBMIT_GROUP``GATEWAY_SUBMIT_CONSUMER``GATEWAY_SUBMIT_WORKER_DISABLED=true`
### 验收口径
- API 入队后不再因为 Gateway 控制面短暂不可达而自己生成 timeoutSubmitResult 必须由 Gateway worker 真实消费和提交后回调。
- Gateway 停止时,SubmitCommand 留在 Redis StreamGateway 恢复后由 consumer group 继续消费新消息。
- BullMQ `gateway.submit.queue` 仅作为审计/兼容,不再是唯一主提交通道。
### 剩余边界
- 当前 worker 先覆盖新消息 `>` 消费和 ackpending 历史消息扫描、claim、重试退避和死信审计放到在途恢复阶段继续做。
## 2026-07-07 阶段 2/3:客户侧 Deliver 持久化重投与普通上行匹配
### 本轮修复
- Prisma 新增 `CmppDownstreamDelivery`,用于保存客户侧待投递 Deliver Receipt 和普通 Deliver 上行;状态覆盖 pending/delivered,记录 retryCount、nextRetryAt、lastError、payload、message/application 关联。
- `SmsUplinkMessage` 增加 `applicationId``messageRecordId``matchStatus``matchReason`,并建立应用和匹配下发记录关系。
- NestJS 收到最终 receipt 后,先写平台回执和消息状态,再创建客户侧待投递记录,尝试调用 Gateway `/downstream/receipt`;成功标记 delivered,客户不在线或 Gateway 不可达时保留 pending 并记录失败原因。
- NestJS 收到普通上行后执行匹配:messageId 精确匹配优先;无 messageId 时按接入号匹配应用路由;仍无唯一应用时按手机号和最近下发时间窗口匹配;多候选标记 ambiguous,未匹配标记 unmatched,但均真实入库。
- Gateway 下游客户 bind/login 成功后保存账号级在线连接,并调用 NestJS `/gateway/events/downstream/pending` 拉取 pending 投递;补发成功后回调 `/gateway/events/downstream/delivered`,失败回调 `/gateway/events/downstream/failed`
- 运营/客户端上行查询 include 应用和匹配下发记录,便于页面展示 matchStatus/matchReason。
### 验证状态
- `npm --prefix api run prisma:generate`:通过。
- `npm --prefix api test -- send-chain.service.spec.ts`:通过。
- `npm --prefix api test`:通过,12 个 suites、82 个 tests。
- `npm --prefix api run build`:通过。
- `go test ./...`Gateway):通过。
- `npm run spike:contracts`:通过。
- `npm run build`:通过,仅既有 Vite chunk size warning。
### 剩余边界
- 待投递 pending 目前在客户 bind/login 时拉取补发;后台周期扫描、指数退避、过期策略、死信队列和运营端失败审计页面仍待后续实现。
- 上行匹配已覆盖 messageId、接入号和手机号时间窗口;共享接入号、多应用多候选时不会误推,但人工认领/改派流程尚未实现。
- 客户连接断开检测和应用级连接数状态回写仍需继续产品化。
## 2026-07-08 阶段 4Gateway 长短信拆分与长上行重组
### 本轮修复
- Go Gateway 上游 Submit 支持长短信第一版拆分:超过 140 字节的短信按 CMPP 标准 6 字节 UDH 生成分片,每片总长度不超过 140 字节,并设置 `PkTotal/PkNumber/TpUdhi` 后逐包发送到上游 SMSC。
- 同一平台 `SubmitCommand` 的多个 accepted 分片 `MsgId` 均登记到 Gateway 映射表,后续任一分片 receipt 可回溯到原 `messageId/submitId/channelId`
- Go Gateway 上游普通 Deliver 支持长上行第一版重组:收到 `TpUdhi=1` 且携带标准 UDH 的分片时,按通道、主叫、被叫、引用号和总片数缓存;分片齐全后只回传一条完整 `UplinkEvent` 给 NestJS。
- 新增 `gateway/internal/upstream/long_message_test.go`,覆盖 UCS2 长短信拆分、短短信不分片、长上行乱序重组。
### 验证状态
- `go test ./...`Gateway):通过。
### 剩余边界
- 阶段 4 后长短信仍按单条平台消息记录展示,尚未提供运营端分片级提交明细、分片级补发审计和部分分片失败后的精细补偿;该审计缺口已在阶段 23 补齐第一版。
- 长上行分片缓存当前为 Gateway 进程内内存;Gateway 重启、跨连接分片漂移或超过缓存 TTL 的残片不会恢复,后续在“在途消息恢复/状态补偿”阶段继续做。
## 2026-07-08 阶段 5Gateway 多连接窗口与窗口满控制
### 本轮修复
- `SubmitCommand.upstream` 契约、示例、Go 结构和 NestJS 生产者增加 `desiredConnections/windowSize`,字段来自通道真实配置;未配置时默认 `desiredConnections=1``windowSize=16`
- Go Gateway 上游提交管理器从单连接升级为通道级连接池:同一通道按 `desiredConnections` 建立多条 CMPP 客户端连接,每条连接独立维护 submit pending、receipt/uplink 映射和长上行分片缓存。
- 每条上游连接增加窗口令牌;提交前必须获得窗口,SubmitResp、reject 或 timeout 后释放窗口;所有连接窗口均满时等待可用窗口,超过提交超时时返回 `WINDOW_TIMEOUT`
- 长短信分片也复用连接池窗口调度,同一条平台消息的多个 accepted 分片仍映射回原 `messageId/submitId/channelId`
- 新增 `gateway/internal/upstream/pool_test.go`,覆盖连接池跨连接获取窗口、窗口满拒绝继续占用、释放后可重新获取。
### 验证状态
- `go test ./...`Gateway):通过。
- `npm --prefix api test -- send-chain.service.spec.ts`:通过。
### 剩余边界
- 当前窗口状态为 Gateway 进程内控制,尚未把连接级窗口占用、等待队列长度、submit latency 等指标回写到 NestJS 或运营端页面。
- 当前阶段只处理窗口容量和多连接发送;Gateway 重启、上游连接断开时的在途 submit 恢复、pending claim、状态补偿和死信审计仍在下一阶段处理。
## 2026-07-08 阶段 6CMPP 配置入口补齐
### 本轮修复
- 运营端通道创建/编辑表单新增上游 `desiredConnections``windowSize` 输入,真实提交到 NestJS 通道 API,并规范化写入 `SmsChannel.config`
- NestJS `ChannelsService``desiredConnections/windowSize` 增加正整数校验;通道激活后的 `ConnectChannel` 请求和发送链路 `SubmitCommand.upstream` 均复用该真实配置。
- Prisma 为 `SmsApplication` 新增 `cmppMaxConnections``cmppWindowSize` 字段;运营端短信应用创建/编辑表单新增 `cmppAccount` 和客户最大连接数输入,客户提交窗口暂不展示给运营配置,保留后端默认值。
- 企业应用 `cmppAccount` 现在支持两种真实路径:显式填写 6 位数字账号,或留空由后端自动生成唯一账号;重复账号和非法格式会被后端拒绝。
- 企业应用 CMPP 参数接口改为从应用真实字段返回 `enterpriseCode/account/passwordCipher/maxConnections/windowSize`,不再借用任意通道企业代码或默认值拼装客户参数。
- 应用级 `cmppEnterpriseCode` 新建/编辑可自定义;接口密码新建默认随机 16 位 UUID 片段,编辑留空不覆盖、填写 16 位后更新。`AppID` 仅作为平台应用标识展示,不作为 CMPP 协议认证参数。
### 验证状态
- `npm --prefix api run prisma:generate`:通过。
- `npm --prefix api test -- sms-config.service.spec.ts channels.service.spec.ts`:通过,2 个 suites、34 个 tests。
- `npm --prefix api run build`:通过。
- `npm run build`:通过,仅既有 Vite chunk size warning。
### 说明
- `desiredConnections/windowSize` 不是 CMPP 协议标准字段,也不是 gocmpp 的原生配置项;它们是本平台对上游通道连接池和提交窗口的运行参数。
- `cmppAccount` 是客户侧应用接入账号;当前已支持真实生成、真实保存和显式配置。
## 2026-07-08 阶段 7SubmitCommand 在途恢复第一步
### 本轮修复
- Go Gateway `submitworker` 在正常消费新消息前新增 pending 恢复流程:对 Redis Stream consumer group 中空闲超过阈值的消息执行 `XAUTOCLAIM`,将滞留在 PEL 的 `SubmitCommand` 认领到当前 consumer。
- 被认领的 pending 命令复用现有 `handleMessage -> Upstream.Submit -> XAck` 成功路径处理;成功后 ack,失败时保留在 PEL,留给后续重试/死信治理。
- `submitworker` 增加可注入 `Submit` 函数,便于单测覆盖消息处理路径;新增单测覆盖 injected submit 和默认 `minIdle` 阈值。
### 验证状态
- `go test ./...`Gateway):通过。
### 剩余边界
- 当前恢复能力只覆盖 Redis Stream PEL 中“已被读走但未 ack”的 pending 命令;尚未实现恢复次数上限、死信队列、失败审计页面和人工补偿入口。
- Gateway 重启时上游连接内已经发出但尚未收到 submit resp 的 in-flight CMPP 请求,仍未完成状态补偿;这部分继续放在后续“断线重连后的消息状态处理”阶段。
## 2026-07-08 阶段 8:上游连接断开时 pending submit 补偿
### 本轮修复
- Go Gateway 上游连接读循环开始区分“空读超时”和“真实连接断开”;空读超时继续等待,真实断开则进入连接丢失处理。
- 某条上游连接断开时,Gateway 会把该连接上所有等待 submit resp 的 pending submit 立即唤醒,返回 `timeout` + `CONNECTION_LOST`,不再机械等待固定 `SUBMIT_TIMEOUT`
- 连接池在再次分配连接前会重新执行 `ensureConnected()`;旧连接断开后,后续新消息可重新建立物理连接继续提交。
- 新增 `gateway/internal/upstream/connection_loss_test.go`,覆盖 pending submit 被唤醒和临时读超时识别。
### 验证状态
- `go test ./...`Gateway):通过。
- `npm --prefix api test -- send-chain.service.spec.ts`:通过。
### 剩余边界
- 当前补偿只覆盖“连接断开且 submit resp 尚未返回”的场景;尚未覆盖“上游其实已受理,但 submit resp 在断线前后丢失”的二次确认和幂等回查。
- submit 结果死信队列、失败审计、恢复次数上限和人工补偿入口仍在后续阶段。
## 2026-07-08 阶段 9receipt 驱动的保守二次归因
### 本轮修复
- Gateway 上游 receipt 事件补充 `phoneNumber`,即使无法从内存 tracker 中精确恢复平台 `messageId`,也会把运营商回执手机号带回 NestJS。
- NestJS `handleReceipt` 新增保守归因:如果 receipt 无法按平台 `messageId/gatewayMessageId` 精确命中,只在“同通道、同手机号、72 小时窗口内、且仅存在 1 条 `timeout + gatewayMessageId=null` 的 submit 记录”时才接收该回执。
- 归因成功后会先回填该次 `sms_submit_record.gatewayMessageId/sequenceId`,再写入真实 `sms_receipt_record` 并按既有逻辑更新 `sms_message_record`、下游客户回执推送和幂等保护。
- 新增 SendChainService 单测,覆盖唯一候选归因成功和多候选拒绝归因两种场景。
### 验证状态
- `go test ./...`Gateway):待本轮统一回归。
- `npm --prefix api test -- send-chain.service.spec.ts`:待本轮统一回归。
### 剩余边界
- 当前只做“唯一候选才归因”的保守版本,仍未实现面向运营商或供应商的 submit 结果主动回查。
- 如果同通道同手机号在窗口内存在多条 timeout 候选,系统会拒绝归因,后续仍需人工补偿或更强的协议级关联键。
## 2026-07-08 阶段 10SubmitCommand 死信治理第一版
### 本轮修复
- Prisma 新增真实表 `GatewaySubmitDeadLetter`,保存 Gateway SubmitCommand 死信的消息 ID、租户/应用/通道、失败原因、尝试次数、原始命令载荷、人工重入队状态和解决状态。
- Go Gateway `submitworker` 新增失败次数治理:同一条 Stream 消息处理失败达到阈值后,调用 NestJS `/gateway/events/dead-letter` 入库死信,并对原消息执行 ack,避免它无限滞留在 PEL。
- Gateway 对非法 `SubmitCommand` 载荷也会直接转死信,防止 poison message 持续阻塞消费。
- NestJS 新增真实死信接口:Gateway 可上报死信;运营端后端可分页查询 `/api/admin/operations/gateway-submit-dead-letters`;可通过 `/api/admin/operations/gateway-submit-dead-letters/:id/requeue` 将原始 `SubmitCommand` 重新写回 Redis Stream。
- NestJS 在收到同一 `submitId/messageId` 的后续真实 `SubmitResult` 时,会把对应死信自动标记为 `resolved`
### 验证状态
- `npm --prefix api run prisma:generate`:通过。
- `npm --prefix api test -- send-chain.service.spec.ts operations.service.spec.ts`:通过,2 个 suites、26 个测试通过。
- `npm --prefix api run build`:通过。
- `go test ./...`Gateway):通过。
### 剩余边界
- 当前死信治理只提供“达到阈值后入库 + 人工重入队”的第一版,尚未实现后台自动重放、重放节流、过期清理和专门的前端运营页面。
- 非法载荷死信如果缺少完整 `SubmitCommand`,当前不可人工重放,只能用于审计和人工排查。
## 2026-07-08 阶段 11:下游客户在线时周期补投与失败封顶
### 本轮修复
- 明确责任边界:客户系统负责断线后的重新 bind;平台负责客户不在线或临时投递失败时的消息不丢、待投递保存和补投。
- Go Gateway 下游入站服务新增在线账号周期补投:除客户 bind 成功后立即拉取 pending 外,Gateway 还会按周期为当前在线账号再次调用 `/gateway/events/downstream/pending`,继续补发未投递成功的 Deliver Receipt/上行 Deliver。
- Gateway 向下游发送 Deliver 失败时会清理失效的内存会话映射,避免对已失效连接无休止重复尝试。
- NestJS `markDownstreamDeliveryFailed` 新增失败上限:未超过阈值时继续 `pending` 并推进 `retryCount/nextRetryAt`;达到阈值后转为 `failed`,停止无限重试,并写 `gateway.downstream_delivery_failed` 系统日志。
### 验证状态
- `npm --prefix api test -- send-chain.service.spec.ts`:通过,1 个 suite、21 个测试通过。
- `go test ./internal/inbound ./internal/control ./...`Gateway):通过。
### 剩余边界
- 当前周期补投只针对“Gateway 认为客户在线”的账号;尚未实现下游投递失败专门列表、人工重投页面和跨 Gateway 实例共享的客户在线状态。
- `CmppDownstreamDelivery` 目前仍使用固定重试间隔,尚未实现指数退避、不同消息类型差异化策略和过期归档。
## 2026-07-08 阶段 12:下游投递失败审计与人工重投
### 本轮修复
- 运营端新增真实下游投递查询接口 `/api/admin/operations/downstream-deliveries`,支持按 `tenantId/applicationId/deliveryType/status/keyword` 筛选并分页返回真实 `CmppDownstreamDelivery` 数据。
- NestJS 新增 `/api/admin/operations/downstream-deliveries/:id/requeue`,可对单条下游投递记录执行人工重投,真实调用 Gateway `/downstream/receipt``/downstream/uplink`,并写 `gateway.downstream_delivery_requeue` 系统日志。
- 运营端新增“下游投递记录”页面,列表、详情、筛选和重投均接真实后端,不使用 mock、本地状态或静态数组。
### 验证状态
- `npm --prefix api test -- send-chain.service.spec.ts operations.service.spec.ts`:通过,2 个 suites、29 个测试通过。
- `npm --prefix api run build`:通过。
- `npm run build`:通过,仅有既有 Vite chunk size warning。
### 剩余边界
- 当前人工重投仍是单条操作,尚未提供批量重投、失败聚合告警和专门的下游投递 Dashboard。
- 页面侧暂未做自动轮询刷新,需要手动查询或重进页面观察状态变化。
## 2026-07-08 阶段 13:下游投递自动退避第一版
### 本轮修复
- `CmppDownstreamDelivery` 的失败重试从固定 60 秒改为指数退避:基础间隔来自 `CMPP_DOWNSTREAM_RETRY_DELAY_MS`,每次失败按 2 倍递增,并受 `CMPP_DOWNSTREAM_RETRY_MAX_DELAY_MS` 上限约束。
- 这样在客户长时间离线或网络持续抖动时,平台不会每分钟机械重试同一条下游投递,能更温和地消耗 API、Gateway 和连接资源。
- 总重试次数上限逻辑保持不变,超过 `CMPP_DOWNSTREAM_MAX_RETRIES` 后仍转 `failed` 并写失败审计。
### 验证状态
- `npm --prefix api test -- send-chain.service.spec.ts`:通过,新增指数退避单测。
### 剩余边界
- 当前退避策略还没有加入随机抖动,多个记录在同一时间失败时,后续重试时刻仍可能比较集中。
- 退避参数当前是全局环境变量,尚未细分到 receipt/uplink 或不同客户应用级别。
## 2026-07-08 阶段 14:下游投递批量重投
### 本轮修复
- 运营端下游投递记录页新增勾选和“批量重投”操作,仅允许对当前页的 `pending/failed` 记录执行批量重投。
- NestJS 新增真实批量接口 `/api/admin/operations/downstream-deliveries/requeue`,逐条调用既有单条重投逻辑,返回成功/失败汇总,不用前端自行拼结果。
- SendChainService 新增批量重投结果汇总与空选择拦截单测。
### 验证状态
- `npm --prefix api test -- send-chain.service.spec.ts`:通过,新增批量重投单测。
- `npm --prefix api run build`:通过。
- `npm run build`:待本轮统一回归。
### 剩余边界
- 当前批量重投只支持“勾选当前页记录”,还不支持“按筛选条件全量重投”或后台异步大批量任务。
- 批量结果当前以内联提示为主,尚未做专门的批量执行历史与导出。
## 2026-07-08 阶段 15:下游投递告警第一版
### 本轮修复
- `OperationsService.dashboard()` 新增真实下游投递告警聚合 `downstreamDeliverySummary`,统计 `pending/failed/delivered` 总量,以及“积压过久的 pending”和“最近失败”两类告警计数。
- 运营端右上角通知新增“下游投递告警”,数量直接来自真实 Dashboard 聚合。
- 运营看板新增下游投递告警摘要卡片,帮助运营从总览页直接感知当前下游投递异常。
### 验证状态
- `npm --prefix api test -- operations.service.spec.ts`:随定向测试通过。
- `npm --prefix api run build`:通过。
- `npm run build`:通过,仅有既有 Vite chunk size warning。
### 剩余边界
- 当前告警仍是站内聚合提醒,尚未接短信、邮件、企业微信等外部告警通道。
- 告警口径当前采用全局阈值环境变量,尚未按客户应用、消息类型或时间段细分。
## 2026-07-08 阶段 16:下游投递 Dashboard 第一版
### 本轮修复
- 新增真实接口 `/api/admin/operations/downstream-deliveries/dashboard`,直接按 `CmppDownstreamDelivery` 聚合返回 `summary/typeBreakdown/retryBuckets/topApplications`
- 运营端“下游投递记录”页面顶部补上真实 Dashboard 区域,展示投递总量、待投递、已投递、告警、类型分布、重试压力和应用告警排行。
- Dashboard 筛选范围与页面应用/类型筛选保持一致,不允许由前端只根据当前页列表数据临时拼装。
### 验证状态
- `npm --prefix api test -- operations.service.spec.ts`:通过。
- `npm --prefix api run build`:通过。
- `npm run build`:通过,仅有既有 Vite chunk size warning。
- `git diff --check`:无空白错误,仅 Windows LF/CRLF 提示。
### 剩余边界
- 当前 Dashboard 仍偏运营处置视角,尚未补时间趋势、按客户/账号维度的更细颗粒聚合。
- 应用告警排行当前以 `pending + failed` 为主排序,尚未加入更复杂的权重和 SLA 指标。
## 2026-07-08 阶段 17:下游在线账号 Presence 持久化底座
### 本轮修复
- Gateway inbound 新增 Redis presence store,客户 `cmppAccount` 在 bind 成功、submit 建链和下游回执/上行投递时,会把在线账号状态写入 Redis。
- presence 数据至少包含 `account/srcId/remoteIp/gatewayInstanceId/state/connectedAt/updatedAt`,并按 TTL 自动过期,避免该状态只存在单进程内存中。
- Gateway 发送失败触发连接清理时,会同步移除该账号的 Redis presence 记录。
- 该阶段先完成“在线状态外部化”,尚未宣称“Gateway 重启后 pending 投递自动恢复”已完成;恢复逻辑在后续阶段继续补。
### 验证状态
- `go test ./internal/inbound/...`:通过。
- `go test ./cmd/gateway/...`:通过。
### 剩余边界
- 当前 presence 主要服务于后续恢复能力,Gateway 还未在启动时主动根据 Redis presence 扫描并恢复 pending 投递。
- 连接断开当前主要依赖发送失败清理和 TTL 过期兜底,尚未建立更完整的显式断线回收机制。
## 2026-07-08 阶段 18Gateway 恢复候选视图
### 本轮修复
- Gateway 启动时会读取 Redis presence,并输出恢复候选账号加载日志。
- 新增控制面接口 `GET /downstream/recovery-candidates`,返回 Redis presence 与当前内存在线账号合并后的恢复候选视图。
- 候选视图当前用于后续恢复逻辑和运维排查,不直接触发 pending 下游投递补发。
### 验证状态
- `go test ./internal/inbound/...`:通过。
- `go test ./internal/control/...`:通过。
- `go test ./cmd/gateway/...`:通过。
### 剩余边界
- 当前只是“识别谁值得恢复”,还没有执行“把这些账号的 pending 回执/上行自动继续补投”。
- 候选视图默认按 Redis TTL 和最近活跃时间保留,尚未叠加更复杂的健康判定和跨实例去重策略。
## 2026-07-08 阶段 19Gateway pending 恢复执行第一版
### 本轮修复
- Gateway 启动时会立即按恢复候选账号执行一次 pending 下游投递恢复扫描。
- 后续每轮补投周期除扫描当前内存在线账号外,也会继续扫描恢复候选账号,尝试恢复 `CmppDownstreamDelivery.pending`
- 当前恢复策略是“能投就投,投不了继续 pending”:若账号尚无可用下游连接,Gateway 不会把记录误标成失败,而是等待客户重连后的后续恢复机会。
### 验证状态
- `go test ./internal/inbound/...`:通过。
- `go test ./internal/control/...`:通过。
- `go test ./cmd/gateway/...`:通过。
### 剩余边界
- 当前恢复仍按固定扫描周期触发,尚未做更细的按账号退避、恢复批次追踪和恢复告警。
- 仍未覆盖更复杂的长短信分片恢复、跨实例抢占协调和恢复中的重复投递防抖。
## 2026-07-08 阶段 20Gateway 恢复退避、锁与状态审计
### 本轮修复
- Gateway 新增账号级恢复锁,避免同一 `cmppAccount` 被并发重复恢复。
- 恢复失败、等待连接和部分成功场景会写入真实恢复状态,并按指数退避计算下一次可恢复时间,减少无意义高频重试。
- 控制面新增 `GET /downstream/recovery-statuses`,可查看账号最近恢复状态、尝试次数、下一次重试时间和错误原因。
### 验证状态
- `go test ./internal/inbound/...`:通过。
- `go test ./internal/control/...`:通过。
- `go test ./cmd/gateway/...`:通过。
### 剩余边界
- 当前恢复状态审计仍停留在 Gateway 控制面和 Redis,尚未同步到运营端页面或 NestJS 持久化审计表。
- 恢复退避当前按账号统一处理,尚未细分到回执/上行类型、失败类别或跨实例抢占优先级。
## 2026-07-08 阶段 21Gateway 恢复总览与链路缺口收口
### 本轮修复
- 控制面新增 `GET /downstream/recovery-overview`,一次性返回恢复候选账号和恢复状态,便于生产联调与排查。
- 需求文档已按当前真实代码重新梳理 CMPP 端到端链路剩余缺口,明确区分“已能验收的真实链路能力”和“尚未产品化完成的恢复审计/指标/复杂补偿能力”。
- 系统测试用例新增恢复总览接口口径,便于后续生产验证直接对照。
### 验证状态
- `go test ./internal/control/...`:通过。
- `go test ./internal/inbound/...`:通过(延续前一阶段验证结果,本轮未改动 inbound 核心分支逻辑)。
- `go test ./cmd/gateway/...`:通过。
### 阶段 21 后剩余真实缺口
- 恢复状态仍未写回 NestJS/Prisma/PostgreSQL,运营端暂无真实恢复状态页面。
- 多 Gateway 实例下更强的恢复抢占协调、分片级补偿审计、共享接入号上行人工认领仍未完成。
- 连接级窗口利用率、恢复吞吐、恢复失败分布等运营指标仍未进入真实后台页面。
## 2026-07-08 阶段 22:恢复状态回流 NestJS 与运营端展示
### 本轮修复
- NestJS 新增真实恢复状态接收接口 `/api/gateway/events/downstream/recovery-status`
- Prisma/PostgreSQL 新增 `GatewayDownstreamRecoveryStatus` 表,按 `cmppAccount` 持久化恢复状态、尝试次数、下一次恢复时间、错误原因及应用/企业关联。
- 运营端新增独立“恢复状态管理”页面,支持真实恢复状态列表、分页、详情接口和当前筛选结果 CSV 导出。
- 原“下游投递记录”页面仅保留投递记录与重投能力,不再混放恢复状态列表。
- 恢复状态新增 `failureCategory` 失败分类字段,Gateway 回传、NestJS 兜底归类并落库,运营端支持分类筛选、分布统计、详情展示和导出。
- 多 Gateway 恢复抢占协调补强:恢复锁升级为 Redis token 租约,恢复完成时通过 Lua 原子校验 token 后才写状态和释放锁;迟到旧实例不能误删新实例锁。
- `GatewayDownstreamRecoveryStatus` 新增 `lockOwner/lockExpiresAt`Gateway 回传并由 NestJS 入库,运营端恢复状态列表和详情可查看锁持有实例。
- 本地启动脚本补充 `.local-tools\minio.exe` 查找路径,并已验证本机 MinIO 可通过 `npm run start:local:minio` 启动。
### 验证状态
- `npm --prefix api run prisma:generate`:通过。
- `npm --prefix api test -- operations.service.spec.ts send-chain.service.spec.ts`:通过。
- `npm --prefix api run build`:通过。
- `go test ./internal/inbound/... ./internal/control/... ./cmd/gateway/...`:通过。
- `npm run build`:通过,仅有既有 Vite chunk size warning。
- `npm --prefix api run prisma:migrate:deploy`:通过,已应用 `20260708213000_add_recovery_lock_observability`
- `npm run start:local:minio`:通过,MinIO API `http://localhost:9000`、Console `http://localhost:9001` 已监听。
### 阶段 22 后剩余真实缺口
- 恢复状态已回流 NestJS,并已具备独立运营页、详情、导出和第一版失败分类分布;后续仍缺少恢复吞吐、耗时趋势、连续失败账号等更细指标。
- 多 Gateway 账号级恢复抢占协调已具备 token 租约和完成校验;分片级补偿审计、共享接入号上行人工认领仍未完成。
- 连接级窗口利用率、连接级心跳、恢复吞吐和恢复耗时等运营指标仍未进入真实后台页面。
## 2026-07-08 阶段 23:长短信分片级补偿审计
### 本轮修复
- Prisma/PostgreSQL 新增 `SmsMessageSegmentAudit`,按短信记录、submitId、分片序号保存真实分片提交、回执和补偿归因。
- Go Gateway 上游提交结果 `SubmitResult` 增加 `segments[]`,逐片回传 `segmentTotal/segmentIndex/sequenceId/gatewayMessageId/submitStatus/submittedAt`,长短信不再只暴露首个分片结果。
- NestJS `handleSubmitResult` 写入分片提交审计,`handleReceipt` 按上游 `gatewayMessageId` 回填分片回执状态;重投或补偿产生的新 submitId 与历史 submitId 可并存追踪。
- 运营端短信记录详情新增“分片补偿审计”列表,从真实 API 查询 `SmsMessageSegmentAudit`,展示分片、submitId、通道、Sequence、MsgId、提交状态、回执状态、补偿类型和错误信息。
- 契约文档和示例补充 `SubmitResult.segments[]`,系统测试用例新增 `TC-GW-026 长短信分片补偿审计`
### 验证状态
- `npm --prefix api run prisma:generate`:通过。
- `go test ./internal/upstream/... ./internal/queue/... ./internal/submitworker/...`:通过。
- `npm --prefix api test -- send-chain.service.spec.ts operations.service.spec.ts`:通过。
- `npm --prefix api run build`:通过。
- `npm run build`:通过,仅有既有 Vite chunk size warning。
### 阶段 23 后剩余真实缺口
- 长短信分片级提交、回执和补偿归因已具备真实审计;后续仍需补按单个分片自动重投、分片级人工重投和更细的补偿指标。
- 共享接入号、多候选普通上行的人工认领流程仍未完成。
- 连接级窗口利用率、连接级心跳、恢复吞吐、恢复耗时趋势和连续失败账号等运营指标仍未进入真实后台页面。
## 2026-07-08 阶段 24:共享接入号上行人工认领
### 本轮修复
- Prisma/PostgreSQL 新增 `SmsUplinkMatchCandidate`,用于保存普通上行 ambiguous 场景下的候选企业、应用、下发短信、候选来源、置信度、认领状态和认领时间。
- NestJS 上行匹配逻辑增强:接入号匹配多个应用、或手机号时间窗口匹配多条下发时,不误推客户;上行记录标记 `ambiguous`,并真实写入候选表。
- 运营端“短信上行记录”详情新增候选认领区,展示候选企业、候选应用、候选来源、置信度、候选下发短信和候选原因,支持“认领并推送”。
- 新增 `POST /admin/operations/uplink-messages/:id/claim`:认领后更新 `SmsUplinkMessage``matched`,选中候选置为 `claimed`,其他候选置为 `rejected`,写入操作日志,并创建真实 `CmppDownstreamDelivery(deliveryType=uplink)` 走客户侧下游投递链路。
- 系统测试用例新增 `TC-GW-027 共享接入号上行人工认领`
### 验证状态
- `npm --prefix api run prisma:generate`:通过。
- `npm --prefix api run prisma:migrate:deploy`:通过,已应用 `20260708233000_add_uplink_match_candidates`
- `npm --prefix api test -- send-chain.service.spec.ts operations.service.spec.ts`:通过,40 个测试。
- `npm --prefix api run build`:通过。
- `npm run build`:通过,仅有既有 Vite chunk size warning。
### 当前剩余真实缺口
- 共享接入号上行已具备候选记录、人工认领和认领后下游投递第一版;后续仍需补批量认领、认领复核和认领准确率/积压指标。
- 长短信分片级提交、回执和补偿归因已具备真实审计;后续仍需补按单个分片自动重投、分片级人工重投和更细的补偿指标。
- 连接级窗口利用率、连接级心跳、恢复吞吐、恢复耗时趋势和连续失败账号等运营指标仍未进入真实后台页面。
## 2026-07-03 阶段 9:运营端报备回执导入真实上传/解析
### 本轮修复
- 运营端报备任务导入弹窗改为真实选择 CSV/TSV/TXT 文件。
- 前端先调用 `/api/admin/files/upload` 保存文件对象,再提交 `fileObjectId`、文件名和文本内容到 `/api/admin/report-tasks/{id}/receipt-import`
- 后端导入接口解析文本回执,识别 `status/result/状态/结果` 列,统计成功行、失败行,并保存行级解析结果。
- 报备任务状态由后端按解析结果派生:全成功为 `completed`,有成功有失败为 `partial`,全失败或空文件为 `failed`
- 未识别的运营商状态按失败处理,避免把未知回执误判为通过。
### 已执行命令
```bash
npm --prefix api test -- channels.service.spec.ts
npm --prefix api test
npm --prefix api run build
npm run build
git diff --check
```
### 当前结果
- `api/src/channels/channels.service.spec.ts` 新增文本回执解析和任务状态派生覆盖。
- API Jest12 个 test suite 通过,73 个测试通过。
- API build 通过。
- 前端 build 通过,仍存在既有 Vite chunk size warning。
- `git diff --check` 无空白错误,仅 Windows 工作区 LF/CRLF 提示。
- 本地服务已重启:`http://localhost:3000/``http://localhost:5173/` 均监听,`/api/admin/report-tasks``/admin/report-tasks` HTTP smoke 返回 200。
## 2026-07-03 全菜单真实后端、上传和列宽回归
### 本轮修复
- 客户端企业认证从纯前端状态机改为真实 `GET/POST /api/client/enterprise-certification` 驱动。
- 客户端企业认证营业执照上传接入 `/api/admin/files/upload`,提交时保存 `licenseFileObjectId` 等材料字段。
- 运营端企业表单“企业照片”从禁用占位按钮改为真实上传,保存时写入 `photoFileObjectId`
- 客户端账号设置、运营端系统配置无真实保存接口,已移除路由并删除纯前端页面。
- 彩信待开发菜单路由统一指向占位页,不再进入静态 mock 演示页面。
- 客户端短信发送详情、批量任务表格中明显偏窄的中文字段列已加宽。
### 已执行命令
```bash
npm --prefix api test
npm --prefix api run build
npm run build
npm run spike:contracts
npm run test:gateway
$env:API_BASE_URL='http://127.0.0.1:3000/api'; node tools/smoke/real-env-smoke.mjs
npm run verify:phase8
git diff --check
```
### 当前结果
- API Jest12 个 test suite 通过,73 个测试通过。
- API build 通过。
- 前端 build 通过,仍存在既有 Vite chunk size warning。
- Gateway 队列契约通过,4 个示例均验证通过。
- `npm run test:gateway` 通过。
- 真实 API smoke 通过,覆盖真实 PostgreSQL/Redis/MinIO/API 主链路和文件上传对象写入。
- 浏览器抽检通过:客户端真实登录后,企业认证页面无“纯前端原型”文案,资料页出现真实上传入口;彩信待开发入口显示占位页而非静态表单。
- `npm run verify:phase8` 仍未通过,失败点仍是已知 BullMQ spike 性能阈值:15000 条消息、并发 500、end-to-end TPS 469.76,低于 500。
## 2026-07-03 企业列表列宽和新建应用交互回归
### 本轮修复
- 通用 `Table` 组件增加 `colgroup`、列最小宽度和表格最小宽度计算,显式配置的业务列不再被容器强行压窄,超出区域横向滚动。
- 企业管理列表加宽企业 ID、企业名称、企业编码、统一社会信用代码、联系人、联系电话、余额、短信余量、状态和操作列。
- 企业模板管理列表加宽企业、应用、签名、模板内容、审核状态、更新时间和操作列,模板内容列保留两行展示。
- 运营端短信任务进度、短信审核、短信记录、报备任务、用户、系统日志、安全控制、充值记录等列表中的状态/操作/数量等易挤压列统一加宽。
- 新建企业应用入口弹窗改为先选择真实企业,再进入应用参数、客户单价、IP 白名单和三网通道组配置;未选择企业时“下一步”禁用。
- 新建短信应用表单把移动、联通、电信通道组配置改为独立卡片区,显示已配置数量和无可用通道组提示;未填写应用名称或未选择任一运营商通道组时禁止保存。
- 补齐基础弹窗居中、遮罩、最大宽度和正文滚动样式,避免 1280px 视口下弹窗偏移或被截断。
### 已执行命令和浏览器验证
```bash
npm run build
git diff --check
```
- 前端 build 通过,仍存在既有 Vite chunk size warning。
- `git diff --check` 无空白错误,仅 Windows 工作区 LF/CRLF 提示。
- 窄列扫描仅剩短字段列:报备字段“必填”90px、运营看板排名72px、短信上行选择框72px。
- 浏览器使用真实运营端登录 `admin@example.com` 抽检通过:
- 企业管理表格最小宽度 1920px,统一社会信用代码列 220px,联系人列 160px,联系电话列 150px,横向滚动生效。
- 企业模板管理表格最小宽度 1820px,模板内容列 420px,横向滚动生效。
- 新建企业应用弹窗在 1280px 视口下未截断,未选择企业时“下一步”禁用。
- 新建短信应用页显示三网通道组卡片、已配置数量和无可用通道组提示,初始状态“创建应用”禁用。
## 2026-07-06 文件上传预览和下载回归
### 本轮修复
- 文件服务新增真实下载接口 `GET /api/admin/files/:id/download`,从 MinIO 或本地对象存储读取真实文件对象,支持 `inline` 预览和 `attachment` 下载。
- 运营端企业照片、企业签名材料、引流材料、报备回执导入均在真实上传成功后显示下载入口;图片类型文件显示点击预览入口。
- 客户端企业认证营业执照上传成功后显示下载入口,图片类型文件显示点击预览入口;提交认证时保存文件类型信息。
- 客户端签名列表对已保存签名材料显示下载入口,图片材料按文件名或类型显示预览入口。
- 客户端短信发送导入号码文件为前端解析文件,未生成后端文件对象;页面仅提供本地原始文件下载,不标记为真实后端归档。
### 已执行命令
```bash
npm --prefix api test -- files.service.spec.ts
npm --prefix api run build
npm run build
git diff --check
```
### 当前结果
- 文件服务单测通过:1 个 test suite、2 个测试通过。
- API build 通过。
- 前端 build 通过,仍存在既有 Vite chunk size warning。
- `git diff --check` 无空白错误,仅 Windows 工作区 LF/CRLF 提示。
## 2026-07-06 企业列表人工充值入口
### 本轮修复
- 运营端企业管理列表新增“充值”按钮。
- 点击“充值”打开企业人工充值弹窗,展示企业名称、当前余额,并支持录入充值金额、操作人和备注;充值金额允许负数冲正,0 金额不允许提交;企业列表入口不要求填写短信条数。
- 提交后调用现有真实接口 `POST /api/admin/billing/manual-recharges`,成功后重新拉取企业管理列表,余额来自真实账户接口聚合结果。
- 该入口不使用前端本地状态模拟充值入账;充值订单、账户余额、账户流水和操作日志仍由后端 `BillingService.createManualRecharge` 负责。
### 已执行命令
```bash
npm run build
git diff --check
```
### 当前结果
- 前端 build 通过,仍存在既有 Vite chunk size warning。
- `git diff --check` 无空白错误,仅 Windows 工作区 LF/CRLF 提示。
## 2026-07-07 手机号段 Tab 和通道组补发上限
### 本轮修复
- 运营端手机号段库页面移除自定义卡片式 Tab,改用通用 `Tabs` 控件,与企业应用管理页面“短信应用/彩信应用”交互一致。
- 通道组添加/编辑页面新增“补发时间上限(小时)”输入控件,编辑时回填 `retryTimeLimitHours`,保存时写入真实通道组接口。
- 补发时间上限按后端现有校验限制为 1 到 72 小时。
### 已执行命令
```bash
npm run build
git diff --check
```
### 当前结果
- 前端 build 通过,仍存在既有 Vite chunk size warning。
- `git diff --check` 无空白错误,仅 Windows 工作区 LF/CRLF 提示。
## 2026-07-07 账单流水页面移除和列表分页
### 本轮修复
- 删除客户端账单流水页面和运营端账单流水页面,移除对应路由、菜单、占位映射和首页跳转入口。
- 移除公开交易查询/创建接口:`GET/POST /api/admin/billing/transactions``GET /api/client/billing/transactions`
- 保留内部 `AccountTransaction` 写入能力,人工充值、扣费、释放、退款等真实计费动作仍可写入内部账务记录;本期不作为独立账单流水页面验收。
- 通用 `Table` 组件新增内置分页,默认每页 10 条;服务端分页页面关闭内置分页,避免双分页。
- 补齐手写列表和卡片列表分页:通道管理、通道组、充值记录、客户端应用、客户端充值套餐、客户端签名、客户端模板、客户端批量任务、客户端发送详情、运营端短信任务进度、运营端企业签名。
### 已执行命令
```bash
npm run build
npm --prefix api run build
npm --prefix api test -- billing.service.spec.ts --runInBand
git diff --check
```
### 当前结果
- 前端 build 通过,仍存在既有 Vite chunk size warning。
- API build 通过。
- BillingService 单测通过:1 个 test suite、6 个测试通过。
- `git diff --check` 无空白错误,仅 Windows 工作区 LF/CRLF 提示。
## 2026-07-07 通道组表格和分钟级补发上限
### 本轮修复
- 通道组添加/编辑页的省网分流、全国通道配置从卡片改为通用表格展示,行内保留编辑、删除操作。
- 通道状态文案改为设计锚点口径“链接正常/通道停用”;“链接正常”必须来自真实 CMPP 连接状态 connected 且当前连接数大于 0,新建但未连接的 active 通道不再显示为链接正常。
- 通道组补发时间上限从整小时升级为分钟级配置,页面交互为“小时 + 分钟”,默认 12 小时 0 分钟;后端新增 `retryTimeLimitMinutes` 持久化字段,并保留 `retryTimeLimitHours` 兼容旧调用。
- 发送链路按分钟级上限判断是否继续补发,超过配置分钟数、超过 72 小时或关闭补发时均不再补发。
### 已执行命令
```bash
npm --prefix api run prisma:generate
npm --prefix api test -- channels.service.spec.ts send-chain.service.spec.ts --runInBand
npm --prefix api run build
npm run build
git diff --check
```
### 当前结果
- Prisma Client 已根据新 schema 生成。
- ChannelsService 和 SendChainService 定向单测通过:2 个 test suites、35 个测试通过。
- API build 通过。
- 前端 build 通过,仍存在既有 Vite chunk size warning。
- `git diff --check` 无空白错误,仅 Windows 工作区 LF/CRLF 提示。
## 2026-07-10 手机号段库大数据分页
### 本轮修复
- `GET /api/admin/dictionaries/phone-segments` 从固定返回前 200 条改为按唯一 `prefix` 游标分页,支持服务端按号段、运营商、省份和城市搜索。
- API 每页多读取 1 条计算 `hasMore/nextCursor`,不执行 50 万级号段表的 `COUNT(*)`
- 运营端手机号段页面使用真实服务端分页,移除号段总数卡片、Tab 数字和分页总数,只显示当前页码及上一页/下一页。
- 生产手机号段数据已从 `dannyhu926/phone_location` 2026 年 4 月数据导入;源数据 516470 条,过滤 253 条非 7 位异常记录,最终有效 7 位号段 516217 条。
### 验证口径
- API 定向单测覆盖游标、搜索、每页多取 1 条和不查询总数。
- 前端 build 和 API build 必须通过。
- 生产验证应覆盖首尾翻页、关键词搜索、API/Gateway/PostgreSQL 健康状态和典型号段归属地查询。
### 已执行命令与结果
```bash
npm --prefix api test -- --runTestsByPath src/dictionaries/dictionaries.service.spec.ts
npm --prefix api run build
npm run build
git diff --check
```
- DictionariesService 定向单测通过:1 个 test suite、3 个测试通过。
- API build 和前端 build 通过;前端仍有既有 chunk size warning。
- 生产 API 实测 `pageSize=2`:第一页返回 `1300000/1300001``nextCursor=1300001`,下一页返回 `1300002/1300003`,响应无 `total` 字段。
- 生产 API 搜索 `1882120` 返回“中国移动/上海/上海”;搜索“上海”首屏响应约 80ms。
- 生产 `cmpp-api``cmpp-gateway`、PostgreSQL、Nginx 均为 activeAPI health 正常。
- 隔离部署后曾因 `dist/assets` 被保留为 `700 root:root` 导致 Nginx 无权读取 JS/CSS、admin 页面空白;线上已修正为目录 `755`、文件 `644`,正式生产部署脚本同步固化权限。
- 正式部署发现已有生产管理员且未配置 `PROD_ADMIN_PASSWORD` 时,`upsert.create` 仍会对空密码执行哈希;已拆分 create/update 密码变量,已有账号不改密码,新建账号才生成临时密码。
## 2026-07-10 Batch 0 飞书瑕疵台账与分批策略
来源:飞书《短信平台第一版瑕疵》。本表仅记录问题路由和验收边界;除 Batch 1 外,其他项目仍须先在预发布环境只读复现并核对真实代码、API、PostgreSQL、Redis、MinIO 或 Gateway 状态,不能根据页面现象直接修改。
| 飞书项 | 初步分类 | 真实链路/风险 | 计划批次 | 当前状态 |
| --- | --- | --- | --- | --- |
| 1.1-1.5 企业-充值流程 | UI + API/DB | 对象存储预览、人工充值、充值订单、账户流水 | Batch 1 | 已完成并部署;历史余额取 AccountTransaction 快照 |
| 2.1 通道密码展示/修改 | UI + API/DB + 安全 | 密码密文、权限、审计、上游连接配置 | Batch 2 | 已完成:密码不回显,留空不覆盖,填写新值才更新 |
| 2.2 扩展位数和通道流速 | UI + API/DB + Gateway | 通道配置持久化、Gateway submit 限速 | Batch 2 | 已完成:真实持久化并下发 Gateway SubmitCommand |
| 2.3 通道组名称为空提示 | UI 校验 | 服务端字段校验与前端错误提示一致 | Batch 2 | 已验证:既有前端提示会在真实 API 调用前中断保存 |
| 2.4 发送记录详情弹窗 | UI + API | 详情、回执、提交记录必须来自真实接口 | Batch 2 | 已完成:真实状态、提交和回执信息分层展示 |
| 2.5 连接日志优化 | UI + API + Gateway | 连接状态回写、操作日志、分页筛选 | Batch 2 | 已完成:展示真实连接状态摘要并支持日志关键词筛选 |
| 2.6 通道测试 | API/DB + Gateway/CMPP | 测试 submit、Redis Stream、上游响应、审计 | Batch 2 | 已完成:提交结果展示真实测试流水和提交记录 |
| 2.7 短信记录页面 | UI + API/DB + Gateway | 短信、submit、回执、分片审计真实查询 | Batch 2 | 已完成:筛选下推 PostgreSQL,详情/审计为真实接口 |
| 3.1 报备配置无返回 | UI 导航 | 返回后筛选/表单状态不丢失 | Batch 3 | 已完成:返回通道列表 |
| 3.2 通道组添加通道弹窗 | UI + API | 通道组成员真实保存和回填 | Batch 3 | 已完成:真实候选、状态展示、重复项限制和错误提示 |
| 4.1 创建用户 | UI + API/DB | 用户、角色、企业关联、审计 | Batch 4 | 已完成:真实表单校验、提交状态和错误提示 |
| 4.2 禁用/删除/改密后踢下线 | API/DB + 会话 | Token/session 失效、跨浏览器验证、审计 | Batch 4 | 已完成:数据库会话版本使旧 token 失效 |
| 4.3 禁用按钮颜色 | UI | 仅样式,保持通用危险操作语义 | Batch 4 | 已完成:使用 warning 语义色 |
| 4.4 个人改密缺失 | UI + API/会话 | 当前用户校验、密码更新、旧会话失效 | Batch 4 | 已完成:右上角真实当前密码校验与改密 |
| 5.1 待审核任务数不准 | API/DB 聚合 | 审核状态口径与任务明细一致 | Batch 5 | 已完成:风险审核改按 SmsSendTask.pending_review 统计 |
| 5.2 任务进度 | UI + API/DB + Gateway | 状态机、发送/回执计数、分页 | Batch 5 | 已完成:未知/超时不重复累计,已处理数不超过总号码数 |
| 5.3 企业应用 | UI + API/DB | 短信应用真实 CRUD/审核;彩信仅占位 | Batch 5 | 已完成:停用使用 warning 色,启用使用 success 色,搜索区宽度协调 |
| 5.4 企业模板 | UI + API/DB | 模板材料、审核状态、真实筛选 | Batch 5 | 已完成:审核状态以中文展示,draft 显示为草稿 |
| 5.5 企业签名 | UI + API/DB + MinIO | 资质文件、签名审核、对象存储预览 | Batch 5 | 已完成:左边框按三网真实报备结果展示,编辑页不允许手工改报备状态 |
| 5.6 引流信息 | UI + API/DB | 字典字段、签名/模板关联、审核口径 | Batch 5 | 已完成:列表改为引流信息、长链接不跳转且省略展示、操作列可见,编辑页不允许手工改报备状态 |
| 6.1 手机号段库 Tab | UI | 使用通用 Tabs,不改变真实号段数据路径 | Batch 6 | 已完成:Tab 按内容宽度展示 |
| 6.2 运营商区分规则分页 | UI + API/DB | 服务端分页、筛选与总数口径 | Batch 6 | 已完成:PostgreSQL 分页、总数、25 条每页 |
| 7.1 敏感词页 | UI + API/DB | 敏感词 CRUD、生效范围、发送校验 | Batch 6 | 已完成:状态 Tag 清晰展示,添加弹窗扩展,保留真实 CRUD |
| 8.1 系统日志 IP 为空 | API/DB + Nginx | 转发头、请求上下文、OperationLog 落库、历史数据边界 | Batch 6 | 已完成:真实 HTTP 操作日志记录 Nginx 转发的客户端 IP;后台任务保持空值 |
| 8.2 客户端标题 | UI | 客户端产品名称与运营端区分 | Batch 6 | 已完成:短信平台客户端 |
| 8.3 通用输入框/文本框样式 | UI | 去除内层填充色,保留边框和焦点状态 | 回归复查 | 已完成:含 Chromium 自动填充背景 |
| 8.4 精确时间格式 | UI | 所有精确时间统一 `YYYY-MM-DD HH:mm:ss` | Batch 6 | 已完成:统一 helper 覆盖日志、用户、充值、任务与配置展示 |
| 8.5 中文图片文件名乱码 | API/DB + MinIO + UI | multipart 编码、对象存储文件名、历史展示兼容 | 回归复查 | 已完成:新上传正确入库,历史展示兼容解码 |
| 8.6 全局分页控件 | UI + API/DB | 总页数、首页/末页、指定页跳转与服务端分页口径 | Batch 6 | 已完成:统一控件支持首页、末页、页码跳转;真实服务端分页页传入总页数 |
| 9.1 客户端菜单顺序 | UI | 签名与引流信息菜单位于模板管理之前 | Batch 6 | 已完成 |
### 执行约束
- 每个 Batch 先只读复现并记录页面、API、DB、Gateway 分类,再做最小真实修复。
- 纯 UI 项也必须确认页面数据源不是 mock、localStorage 或静态数组;未实现后端的彩信仅保留待开发占位。
- 每批结束执行相关 API 测试、API build、前端 build;涉及 Gateway 时追加 Go 测试和生产 Gateway health/CMPP 验证。
- 完成后更新本文件;未经明确要求不提交或推送代码。
## 2026-07-10 Batch 1 企业充值流程瑕疵
### 本轮修复
- 企业新建/编辑页的图片“预览”改为站内弹窗展示,不再跳转或新开页面;下载仍走真实对象存储文件接口。
- 通用 `Input``Select``Textarea` 根据 `required` 属性显示必填标识;企业资料和人工充值弹窗不再依赖页面散落的文案约定。
- 运营端充值记录列表的“充值后余额”改为真实订单关联 `AccountTransaction.balanceAfter`;不再用当前 `TenantAccount` 余额冒充历史快照。没有可追溯流水的历史记录显示 `-`
- 人工充值弹窗补齐非零校验、提交中禁用和 API 失败提示;提交仍调用 `POST /api/admin/billing/manual-recharges`,成功后刷新真实记录。
- 企业名称与统一社会信用代码已经使用同一双列栅格,本轮复现未见对齐问题,不做无效样式改动。
### 验证口径
- `GET /api/admin/billing/manual-recharges` 必须基于 Prisma/PostgreSQL 的 `RechargeOrder` 和关联 `AccountTransaction` 返回余额快照。
- `TC-BILLING-006` 增加断言:充值记录“充值后余额”等于关联账务流水的 `balanceAfter`,与后续充值或消费后的当前余额无关。
### 已执行命令与结果
```bash
npm --prefix api test
npm --prefix api run build
npm run build
git diff --check
```
- API 全量单测通过:12 个 test suites、113 个测试通过;新增 BillingService 覆盖两笔人工充值分别返回其历史余额。
- API build 和前端 build 通过;前端仍有既有 Vite chunk size warning。
- `git diff --check` 无空白错误,仅 Windows 工作区 LF/CRLF 提示。
- 已按正式发布标准脚本部署到预发布服务器 `8.160.169.106`Prisma migration deploy 无待执行迁移,`cmpp-api``cmpp-gateway`、Nginx、MinIO 均为 activeAPI/Gateway health、Redis 均通过。
- 生产管理员真实登录后只读调用 `GET /api/admin/billing/manual-recharges` 成功返回 2 条记录,响应包含真实 `balanceAfterCents`10000、1000)。
## 2026-07-10 Batch 2 通道配置真实链路
### 本轮修复
- 运营端通道编辑/新建页的“通道流速”不再固定提交 `100`;输入值按 `1-2000 TPS` 校验后写入 `SmsChannel.rateLimitPerSecond`,发送链路和通道测试继续从该真实字段生成 Gateway `SubmitCommand.route.rateLimitPerSecond`
- “扩展位数”仅允许 `0/2/4/6`,持久化到 `SmsChannel.config.extensionDigits`;编辑页回填该值,普通发送和通道测试均将其放入 Gateway `SubmitCommand.cmpp.extensionDigits`
- NestJS 更新通道时修正 `config` 合并行为:传入的配置会与既有 JSON 配置合并,不会再被 `desiredConnections/windowSize` 规范化过程静默丢弃。
- 网关密码保持安全策略:编辑时不回显已配置密码,留空不覆盖;输入新密码才更新真实通道配置。
### 已执行命令与结果
```bash
npm --prefix api test -- channels.service.spec.ts --runInBand
npm --prefix api run build
npm run build
go test ./internal/queue ./internal/upstream
git diff --check
```
- ChannelsService 和 SendChainService 定向测试通过:2 个 test suites、55 个测试通过;ChannelsService 单独测试 23 项,覆盖流速、扩展位数持久化和非法配置拒绝。
- API build、前端 build、Gateway queue/upstream 测试通过;前端仍有既有 Vite chunk size warning。
- 已重新部署预发布环境;Prisma migration deploy 无待执行迁移,`cmpp-api``cmpp-gateway`、Nginx、MinIO 均为 activeAPI/Gateway health 正常。生产运行源码已确认包含流速校验、扩展位数持久化及 Gateway 队列字段。
- 通道组名称为空时已有前端提示“请输入通道组名称”,保存会在调用真实创建/更新 API 前中断;本轮复核后不重复改动。
- 通道编辑密码保持掩码且不回显:编辑时明确提示“留空保持不变,填写新密码才更新”;新建通道仍要求填写密码。
- 上述密码交互调整已于 2026-07-10 生产验证部署后再次核验:`cmpp-api``cmpp-gateway`、Nginx、MinIO 均为 active,内外部 health/HTTP 检查通过。
- 短信记录列表修复:企业、应用、手机号、状态之外的提交日期、短信内容、通道名称筛选改为传给 `GET /api/admin/operations/messages`NestJS 通过 Prisma/PostgreSQL 执行内容、关联通道名和上海自然日范围查询,页面不再仅筛选已加载的前 500 条记录。
- 生产只读复现确认:短信记录 9 条均有真实 `SmsSubmitRecord`,其中 4 条已有真实 `SmsReceiptRecord`3 个通道均有 `CmppConnectionState``OperationLog` 连接日志。按一条生产记录的日期、内容、通道关键词组合查询,9 条中仅返回 1 条且条件均匹配。
- `OperationsService` 定向测试 12 项、API build、前端 build 均通过;已部署生产验证,`cmpp-api``cmpp-gateway`、Nginx、MinIO 均为 activeAPI/Gateway health 正常。
- 发送详情弹窗重组为真实状态摘要、短信内容、通道提交/回执轨迹、状态信息和分片补偿审计;提交轨迹新增真实 `submitStatus`,不再只展示时间和回执码。
- 连接日志弹窗新增 `CmppConnectionState` 摘要(连接 ID、状态、当前/期望连接数、最近心跳、最近错误),日志内容以真实 `OperationLog.detail` 可读格式呈现,并仅对已返回日志做关键词筛选。
- 通道测试成功后展示 API 返回的真实 `testNo`、提交数量、手机号与 `SmsSubmitRecord.submitId`,禁用重复提交,并提供跳转至短信记录入口;没有虚构“发送成功”或模拟回执。
## 2026-07-10 Batch 3 通道报备与通道组配置
- 通道报备配置页新增返回通道列表入口,沿用现有 `/admin/channels/:channelId/reports` 路由的来源页面,避免运营人员进入配置页后没有回退路径。
- 通道组“添加通道”弹窗不再使用固定省份数组:省份和候选通道均来自 `GET /api/admin/channels`,按真实运营商、地区和已绑定通道过滤;选中后展示通道代码、地区和真实连接状态。
- 弹窗在省份、优先级或通道未选择时提供表单错误提示;没有符合条件的候选时显示可读空态。前端仅做交互约束,最终仍由 NestJS `ChannelsService` 校验运营商/地区兼容性、重复通道和优先级规则,并持久化到 `SmsChannelGroupItem`
- 已执行 `npm --prefix api test -- channels.service.spec.ts --runInBand`23 项通过)、`npm run build``git diff --check`;前端保留既有 Vite chunk size warning。
- 已部署生产验证:Prisma migration deploy 无待执行迁移,`cmpp-api``cmpp-gateway`、Nginx、MinIO 均为 activeAPI/Gateway health 正常;生产只读接口返回 3 个真实通道(其中 2 个启用)、1 个真实通道组和 2 个组成员。
## 2026-07-10 Batch 4 用户管理与会话失效
- `User.sessionVersion` 真实持久化到 PostgreSQL;登录 token 携带该版本。浏览器携带 token 请求时,NestJS 会话中间件校验用户状态、删除状态和版本;禁用、删除、管理员改密和个人改密都会递增版本,使原会话在下一次请求被 401 拒绝,前端清理本地会话并跳回对应登录页。
- 为避免破坏 Gateway 与现有服务间无浏览器会话链路,中间件仅校验带 `Authorization` 的浏览器 token;未携带该 header 的既有内部请求保持原行为。
- 右上角“修改密码”补齐真实 `POST /api/auth/password`:要求当前密码、新密码(至少 6 位)和确认密码一致;成功后当前会话立即失效并回到登录页,写入操作日志。
- 运营端和客户端用户新建补齐姓名、至少一个联系方式、初始密码/企业关联等前端校验,提交中禁用按钮并展示 API 错误;用户禁用操作改用通用 warning 语义色。
- 已执行 `npm --prefix api run prisma:generate``npm --prefix api test -- auth.service.spec.ts session-validation.middleware.spec.ts users.service.spec.ts --runInBand`3 suites、8 项通过)、`npm --prefix api run build``npm run build``git diff --check`;前端保留既有 Vite chunk size warning。
- 已部署生产验证:第 25 条 Prisma migration `20260710153000_add_user_session_version` 成功应用;`cmpp-api``cmpp-gateway`、Nginx、MinIO 均为 activeAPI/Gateway health 正常。生产管理员新登录 token 为版本格式且可读取真实用户列表;伪造旧版本 token 被 `401` 拒绝,验证会话版本失效生效。
## 2026-07-10 Batch 5 审核与企业配置
- 修复 Dashboard 待审核聚合:短信审核的真实状态存储在 `SmsSendTask.status=pending_review`,原逻辑错误统计 `SmsBatchTask.auditStatus=pending`。聚合现统一模板、签名、企业认证和风险审核的真实待审状态。
- 生产 PostgreSQL 与对应 API 在部署后均显示四类待审为 0,Dashboard 也为 0,当前数据口径一致;无非零待审样本,未将该 0 值当作非零场景的充分验收。
- 任务进度、企业应用、企业模板、企业签名与引流字段页面均使用真实 NestJS API;生产只读接口成功返回任务、应用、模板、签名和引流字段数据,不存在 mock/localStorage 回退。
- 已根据下载的瑕疵文档修复 5.2-5.6:任务进度不重复累计未知/超时,企业应用启停语义色与搜索区,模板中文审核状态,签名三网状态驱动边框且移除人工状态选择,引流信息标题、链接展示、操作列和人工状态选择。
- 已执行 `npm --prefix api test -- operations.service.spec.ts --runInBand`(12 项通过)、前端 build 和 `git diff --check`;已部署生产验证,`cmpp-api``cmpp-gateway`、Nginx、MinIO 均为 activeAPI/Gateway health 正常。
## 2026-07-10 瑕疵回归复查
- 逐图复查下载的《短信平台第一版瑕疵》后,确认中文图片文件名乱码仍真实存在:生产 `FileObject.fileName` 中可见 UTF-8 被按 Latin-1 解释后的值。上传链路现先恢复 multipart 文件名编码;历史记录由前端展示层兼容解码,避免签名材料和企业认证页继续显示乱码。
- 通用输入框、文本框去除内部填充色;同时覆盖 Chromium 自动填充产生的蓝色内层背景。企业认证页此前额外写死的灰色输入背景已移除。
- 2026-07-11 回归发现此前金额展示验收不充分:充值记录和多处金额页面仍混用整数、两位或四位小数。现统一金额展示为人民币元三位小数,并新增输入框聚焦底色与文本选中高亮;需求和 `TC-BILLING-006` 已同步。
- 已执行 FilesService 定向单测(3 项通过)、API build、前端 build 和 `git diff --check`;生产部署后四个服务均为 activeAPI/Gateway health 正常。通过真实 `POST /api/admin/files/upload` 上传 `营业执照-编码回归.png`,响应和 `FileObject` 持久化文件名均为正常中文。
- Batch 6 的 6.1、6.2、7.1、8.1 仍为待修,不得因之前的前端构建通过而标记完成;其余 8.x 与客户端菜单顺序将继续按原始文档逐项复核。
## 2026-07-11 文档瑕疵二次闭环
- 基于本地《短信平台第一版瑕疵.docx》重新逐项复查,撤销“代码已改即已验收”的旧口径;本轮必须以源码、真实 NestJS API 返回、测试和生产页面复核共同作为完成条件。
- 通道扩展位数改为真实 API 与页面共同约束 `0-20` 的整数;流速仍由 NestJS 约束为 `1-2000 TPS` 并继续下发 Gateway。
- Dashboard API 新增四类真实待审明细:企业认证、短信审核、模板、签名;运营首页和右上角通知分别展示并跳转到各自真实审核入口。
- 手机号段接口从 cursor-only 响应升级为带 `total/page/pageSize` 的 PostgreSQL 分页,页面获得总页数、首页、末页和跳转能力;短信记录与通用 Table 同步补齐完整分页控制。
- 统一修复充值记录不展示操作人、客户端标题、引流详情名称、用户初始密码显示/隐藏与随机生成、时间秒级格式、输入框无填充焦点状态,以及发送页真实应用单价三位小数显示。
- 已执行:`channels.service.spec.ts`24 项通过)、`dictionaries.service.spec.ts`4 项通过)、`operations.service.spec.ts`12 项通过)、API build、前端 build 与 `git diff --check` 均通过;前端仍仅有既有 chunk size warning。
- 已部署生产验证:`cmpp-api``cmpp-gateway`、Nginx、MinIO 均为 activeAPI/Gateway health 均通过。真实认证 API 返回四类待审明细并与总数一致;手机号段第 2 页返回 25 条、总数 516217、`page=2/pageSize=25`,证明页面分页不再依赖 cursor 猜测总页数。
- 浏览器自动化在登录页连接阶段超时,未使用 CAPTCHA 绕过或修改生产数据;登录后页面视觉验收需在下一轮以人工登录或可用浏览器会话补充截图。其余项目以源码、真实 API 和构建结果验收,不能将该未完成的视觉截图记录成已完成。
## 2026-07-11 运营端查询控件与手机号段库重做
- 企业应用、企业签名、企业模板管理页的查询与重置统一为通用 `Button` 操作组:查询提交当前条件到真实 NestJS 列表 API,重置清空条件后重新加载真实列表,不再依赖输入即筛选或状态更新竞态。
- 系统管理的用户管理、手机号段库、报备字段库、系统日志均增加查询和重置;用户与报备字段使用已加载真实数据的显式筛选,系统日志使用已提交筛选条件请求真实日志 API。
- 手机号段库重做为概览、关键词筛选、号段/运营商规则双视图和统一分页工作台。号段和规则均使用 PostgreSQL 返回的 `total/page/pageSize`,不使用静态数组或 cursor 猜测总页数。
- 已执行前端 `npm run build``git diff --check`;已部署生产验证,`cmpp-api``cmpp-gateway`、Nginx、MinIO 均为 activeAPI/Gateway health 通过。生产源码和已构建静态资源均包含新的查询操作组与手机号段工作台样式;手机号段 API 继续返回真实 `total/page/pageSize`
## 2026-07-11 CMPP 业务失败回执闭环
- 修复下游 CMPP 入站的审计缺口:客户已完成 bind、账号可识别且手机号参数合法后,NestJS 会先创建真实 `SmsBatchTask``SmsApiRequest``SmsMessageRecord`,再执行模板、签名/报备、风控和余额校验;不再因模板未报备等业务失败而直接丢弃客户 Submit。
- 协议、鉴权、源 IP 和手机号参数错误仍由 Gateway/NestJS 返回非零 SubmitResp,且不创建短信记录。其余业务失败返回成功 SubmitResp 与平台 Msg_Id,并创建真实 `SmsReceiptRecord(rawStatus=REJECTD)``CmppDownstreamDelivery`,客户通过 Deliver Receipt 获得 `undelivered` 结果。
- 同一回执策略覆盖最终通道签名报备失败、无可用路由,以及上游 Submit rejected/timeout 在补发耗尽后的终态失败;失败记录、错误码和错误原因均可在运营端真实短信记录链路查询。
- Gateway 在客户 Submit 成功并建立 messageId-连接映射后立即冲刷该账号 pending 下游投递,避免 API 先创建失败回执时只能等待周期补投。
- 已执行 `npm --prefix api test -- --runInBand send-chain.service.spec.ts`34 项通过)、`npm --prefix api run build``go test ./...`(Gateway 全量通过)。待本轮全量 API/前端构建及生产验证完成后补充最终部署结果。
## 2026-07-11 企业应用下游 CMPP 连接状态修复
- 修复企业应用列表误用上游 `CmppConnectionState` 的问题。新增 PostgreSQL `CmppDownstreamConnection`,一条记录对应一个已鉴权的客户 CMPP TCP bind 会话,按应用保存账号、企业代码、客户端 IP、协议版本、建立时间、最近心跳、最近 Submit、最近 Deliver、断开时间和错误原因。
- Gateway 在客户 bind 成功、`ACTIVE_TEST`、Submit 和下游 Deliver 写入成功/失败时通过真实 NestJS API 回写连接事件;运营端只以该表的 `connected` 会话统计当前连接数,不再把上游通道连接数显示为企业客户连接数。
- API 列表/详情查询会将最近心跳超过 `CMPP_DOWNSTREAM_HEARTBEAT_TIMEOUT_MS`(默认 90 秒)的会话标记为 `heartbeat_timeout`;运营端展示真实客户端 IP、企业代码与最近心跳。移除了不能真正关闭 TCP 连接的运营端“删除连接”伪操作。
- 已执行 `sms-config.service.spec.ts`(17 项通过)、API 全量测试(13 suites、121 项)、Gateway 全量 `go test ./...`、API build 和前端 build;前端仅有既有 chunk size warning。
- 已部署生产验证:第 26 条 Prisma migration `20260711193000_add_cmpp_downstream_connections` 已成功应用,`CmppDownstreamConnection` 表存在。`cmpp-api``cmpp-gateway`、Nginx、MinIO 均为 active`12026/17890/8090` 监听及 API/Gateway health 均通过。部署时没有保持在线的客户 bind 会话,故新表初始为 0 条;下一次真实 CMPP bind 将作为生产数据验收样本写入该表。
## 2026-07-11 短信号码运营商与省份持久化
- 确认发送链路在进入应用通道组路由后,会先按真实 `PhoneCarrierRule` 正则识别号码运营商,再按 `PhoneSegment` 号段库识别省份。本轮不扩展地市字段。
- `SmsMessageRecord` 新增 `carrier/province`,路由阶段完成号码识别后立即持久化;即使后续缺少通道组或无在线通道而失败,短信记录仍保留识别结果。选中通道时与 `channelId/submitId` 再次同步写入;Prisma migration 使用真实号段库和生效运营商规则回填已有短信记录。
- 运营端短信记录列表、发送详情和 CSV 导出展示记录上的号码省份/运营商,不再以通道发送地区或通道本体 carrier 冒充号码归属。
- 短信任务进度详情中的“号码运营商分布”和“号码省份分布”均直接聚合任务内真实短信记录的 `carrier/province`
- 已执行 `npm --prefix api test -- --runInBand send-chain.service.spec.ts`35 项通过)、`npm --prefix api run build``npm run build``git diff --check`;前端仅有既有 chunk size warning。
- 已将 `7b8424d9` 部署生产,migration `20260711210000_add_message_route_identity` 成功应用。`cmpp-api``cmpp-gateway`、Nginx、MinIO 均为 active`12026/17890/8090/3000` 监听、API/Gateway health 及外部 `12026` HTTP 均通过。生产 11 条已有短信已全部回填运营商和省份:移动 9 条、电信 1 条、联通 1 条,省份均为上海。
## 2026-07-12 全页面固定条数截断与总数口径整改
- 生产核查确认企业管理只返回前 100 条,但 PostgreSQL 实际有 106 个企业(未删除 105 个);企业应用只返回前 100 条,实际有 204 条。页面将截断后的数组长度展示为“企业总数/共找到”,属于真实数据口径 Bug。
- 已系统审计页面主列表 API,移除会把前 `100/200/500` 条直接作为页面全量的固定截断。覆盖企业、企业应用、企业认证、用户/角色/权限、签名、模板、通道/通道组/路由规则、报备字段/材料/任务/记录、账务、文件、审计、风控、敏感词/黑名单/引流字段、短信任务/记录/提交/回执/上行等真实列表。
- 企业应用的“今日发送/到达率”不再每应用加载最多 1000 条 `SmsMessageRecord` 后用数组长度计算,改为 PostgreSQL 按 `applicationId/status``groupBy` 全量聚合。
- 短信任务进度不再为每个任务最多加载 100000 条短信后统计,改为 PostgreSQL 按 `batchTaskId/carrier/province/status` 聚合总数、成功数和计费条数,任务运营商/省份分布不再受 10 万条截断影响。
- 保留的固定条数均属于明确的近期日志/监控窗口、超时扫描单批、导出保护、单消息重试尝试或唯一候选判定,这些响应不被页面展示为业务总数。
- 已执行 API 全量测试(13 suites、122 项通过),及受影响服务定向测试(5 suites、91 项通过)、API build、前端 build 与 `git diff --check`;前端仅有既有 chunk size warning。
- 已将 `390b9700` 部署生产,部署前完成 PostgreSQL 和当前发布源码备份,Prisma 确认 27 条 migration 均已应用。`cmpp-api``cmpp-gateway`、Nginx、MinIO 均为 active`12026/17890/8090/3000` 监听、API/Gateway health 和外部 `12026` HTTP 均通过。使用生产平台管理员会话调用真实受保护 API:企业管理返回 105 条未删除企业,企业应用返回 204 条,不再封顶 100。
## 2026-07-12 客户批量任务与 CMPP 内部批次隔离
- 短信任务进度的产品口径回归为“客户在客户端提交的批量发送任务”。运营端和客户端任务列表统一强制 `SmsBatchTask.sourceType=client`,不再展示每条 CMPP Submit 创建的 `sourceType=cmpp` 内部批次或通道测试批次。
- CMPP 内部批次仍保留在 PostgreSQL,继续承载模板/签名/报备校验、风控、冻结/计费、队列、补发和 Deliver Receipt 关联;所有 CMPP 号码仍进入短信记录。
- 客户端任务详情、任务短信明细和取消接口同时校验当前 `tenantId``sourceType=client`,不能通过内部批次 ID 读取或操作 CMPP 内部任务。
- 生产现状只读核对:`sourceType=cmpp` 2 个、`sourceType=admin_channel_test` 1 个、`sourceType=client` 0 个。部署后任务进度应显示 0 个客户批量任务,但不删除现有内部批次数据。
- 已执行 `send-chain.service.spec.ts + operations.service.spec.ts`2 suites、50 项通过)、API 全量测试(13 suites、125 项通过)、API build 和前端 build;前端仅有既有 chunk size warning。已随 `8b6ec92f` 部署生产,受保护任务进度 API 返回 0 个客户批量任务,未将现有 2 个 CMPP 内部批次和 1 个通道测试批次误展示。
## 2026-07-12 CMPP 模板不匹配短窗口聚合审核
- 只有企业应用配置 `templateMismatchMode=manual_review` 时,CMPP 模板不匹配短信才进入人工审核。`reject` 继续逐条失败并下发 `REJECTD`;其他模式不被聚合逻辑接管。
- 新增 `SmsSendTask.sourceType/aggregationKey/contentHash/windowStartedAt/windowEndsAt`,以应用、CMPP 账号、规范化内容 SHA-256 和默认 10 秒窗口生成唯一聚合键。`SmsMessageRecord.reviewTaskId/signatureId` 保留每条成员与审核任务、真实签名的关联。
- 聚合前仍校验签名审核/报备、风控直接拒绝和账户余额,并按每条短信独立冻结。审核通过后逐条恢复到各自 `sourceType=cmpp` 内部批次并入队;驳回后逐条释放冻结、写入失败记录并生成客户侧 Deliver Receipt。
- 运营端短信审核页新增“审核来源”和“聚合号码数”,区分 CMPP 模板不匹配聚合与普通风控审核。
- 聚合窗口关闭前,任务不返回到待审列表且审核接口拒绝提前操作,避免窗口内后到短信加入已完成任务。
- Prisma migration`20260712113000_add_cmpp_review_aggregation`。已执行 API 全量测试(13 suites、129 项通过)、API build、前端 build、Prisma validate 和 `git diff --check`
- 已将 `8b6ec92f` 部署生产,部署前完成 PostgreSQL 和发布源码备份,migration 已成功应用。`SmsSendTask` 5 个聚合字段、`SmsMessageRecord.reviewTaskId/signatureId` 均已存在;生产应用中 `manual_review` 3 个、`reject` 201 个。审核 API 返回 200,当前无待审样本,未为验收人工注入短信或修改业务数据。`cmpp-api``cmpp-gateway`、Nginx、MinIO 均为 active`12026/17890/8090/3000` 监听、API/Gateway health 和外部 `12026` HTTP 均通过。
## 2026-07-12 报备字段库到企业签名/引流资料完整链路
- `ChannelReportField` 通过 `drainageFieldId` 真实关联报备字段库,并增加 `reportType=signature/drainage/both`;通道报备配置页改为选择字段库字段和报备用途,不再在通道内手工复制字段编码、名称和类型。
- 新增企业应用报备字段解析 API,按生效的应用路由规则遍历通道组及组内通道,以字段库 ID 求合集;同字段任一通道必填即整体必填,并返回全部来源通道。
- 企业签名与引流信息弹窗根据所选企业应用动态加载字段合集。签名只展示签名/共用字段,引流项只展示引流/共用字段;文件字段继续走真实 MinIO/对象存储上传,文本值和文件对象 ID 均提交 NestJS API。
- API 在写签名前执行必填校验,防止绕过前端;保存后同步写入各目标通道的 `SignatureReportMaterial` 和新增的 `DrainageReportMaterial`,删除引流项时同步清理旧材料。
- 为避免运营人员面对动态资料时无法理解来源,签名和引流编辑页增加“通道组数/通道数/字段数/必填数”摘要、字段级来源说明和“为什么需要这些资料”解释弹窗;弹窗按企业应用、通道组、通道逐级展示字段用途及必填口径。每次保存同时在签名 JSON 中固化 `reportRequirementSnapshot`,记录当时的字段与来源通道,供配置变化后的历史追溯。
- Prisma migration`20260712150000_link_report_field_library`。已执行 Prisma generate/validate、`channels.service.spec.ts + sms-config.service.spec.ts`2 suites、44 项通过)、API build 和前端 build;前端仅有既有 chunk size warning。
- 已提交并 push `87ae4a20`,随后以该提交生成发布快照并部署生产;部署前备份 PostgreSQL 和运行源码,migration `20260712150000_link_report_field_library` 已成功应用。生产 `.deployed-commit=87ae4a20``cmpp-api``cmpp-gateway`、Nginx、MinIO 均为 active`12026/17890/8090/3000` 监听,API/Gateway health 和外部 HTTP 均通过。
- 生产数据库已确认 `ChannelReportField.drainageFieldId/reportType``DrainageReportMaterial` 存在。当前生产 `DrainageField=0``ChannelReportField=0`,因此不会凭空展示动态资料区;需要先按真实业务配置创建字段库和通道字段后再做页面来源弹窗的有数据验收。
- 浏览器可打开生产登录路由并识别页面标题“CMPP 短信平台”,但读取 DOM/控制台时浏览器连接连续超时,未将解释弹窗点击交互标记为已通过;待生产产生真实字段配置后补测。
- 2026-07-12 追加:报备字段库字段类型收窄为字符串、图片、文件三种;前端筛选和新增弹窗移除整数、网址、电话、日期,API 严格拒绝三种之外的类型。migration `20260712170000_normalize_report_field_types` 将历史其他类型及其通道字段副本统一归并为字符串。
- 2026-07-12 追加:按设计基线 `131f344a^` 恢复“通道列表 → 报备详情”页面结构,不再把报备详情错误简化为字段配置表。页面按当前通道查询真实 `ChannelSignatureReportTask/ChannelSignatureReportRecord/SmsSignature.drainageInfo`,展示签名任务、报备状态和时间,签名下引流信息默认收起并可展开;查看详情使用真实企业、应用和动态资料。发送统计没有数据库事实时明确显示“暂无统计”,不复用基线演示百分比。签名报备字段和引流信息字段配置保留为页面顶部两个入口,均从真实报备字段库选择。
- 本地验证:通道/字典定向测试 2 suites、31 项通过,API 全量 13 suites、134 项通过,API build、前端 build、`git diff --check` 通过。浏览器确认本地构建可加载且无框架错误覆盖,但本地未启动真实 API,认证验证码请求返回 502 并停留登录页,因此未把目标报备页面的登录后视觉交互标记为通过;没有绕过认证或注入 mock 数据。
- 2026-07-12 追加:报备状态改为通道任务唯一事实来源。新增统一批量状态变更 API,企业签名按应用当前通道组展示具体通道矩阵,通道详情修改当前任务,报备任务页人工修正任务;三个入口统一更新/创建 `ChannelSignatureReportTask`、写 `ChannelSignatureReportRecord`,并重算三网汇总和 `SmsSignature.reportStatus`。回执导入不再直接覆盖全局状态,同样调用汇总算法;新增但无任务的目标通道按未报备计入汇总分母。
- 已执行相关定向测试 2 suites、46 项及 API 全量测试 13 suites、135 项,API build、前端 build、`git diff --check` 均通过;前端仅有既有 Vite chunk size warning。
- 已提交并 push `eab05958` 后部署生产,部署前完成 PostgreSQL 与运行源码备份;migration `20260712170000_normalize_report_field_types` 成功应用。生产 `.deployed-commit=eab05958``cmpp-api``cmpp-gateway`、Nginx、MinIO 均为 active`12026/17890/8090/3000` 监听,API/Gateway health 和外部 HTTP 200。新统一状态接口对空 items 返回预期 400,证明路由已注册。生产当前 `DrainageField/ChannelReportField/ChannelSignatureReportTask/ChannelSignatureReportRecord` 均为 0,未为验收注入虚假字段、任务或人工状态;运营人员可从企业签名通道矩阵对真实目标通道首次设置状态并创建任务。
## 2026-07-13 部分通道报备通过的发送路由
- 修复发送链路先以 `SmsSignature.reportStatus=approved` 一票否决、导致部分通道通过仍无法发送的问题。全局状态改为运营汇总展示;签名审核仍必须通过。
- 首次发送和失败补发均在路由阶段解析真实签名,只保留对应 `ChannelSignatureReportTask.status=approved` 的候选通道,再结合运营商、省份、优先级、通道状态和实时连接选择通道。主通道未报备而备用通道已报备时允许走备用通道;没有已报备通过且在线的候选通道时明确失败。
- Gateway 提交前继续保留最终通道报备任务二次校验,覆盖路由完成后任务状态发生变化的竞态。
- 已执行 `send-chain.service.spec.ts` 40 项通过,覆盖全局 reporting 可发、主通道未通过而备用通道通过时选备用,以及所有候选均未通过时拒绝;API build 和前端 build 通过。
- 已提交并 push `ade06058` 后部署生产;部署前完成 PostgreSQL 与 `eab05958` 运行源码备份,无待执行 migration。生产 `.deployed-commit=ade06058``cmpp-api``cmpp-gateway`、Nginx、MinIO 均为 active`12026/17890/8090/3000` 监听,API/Gateway health 与外部 HTTP 200。生产源码已确认包含 approved 通道候选过滤;【安徽航天信息】当前仍为全局 reporting、一个通道 approved、一个通道 pending,未主动发送计费短信,交由用户使用真实业务流量验证只走 approved 通道。
## 2026-07-13 企业签名审核与通道报备状态展示分离
- 生产核查【安徽航天信息】确认状态保存成功:两个 `carrier=all` 通道分别为 approved、reporting,三网真实汇总均为 reporting 且 1/2 通过;Bug 在于前端将 reporting 转换成旧 pending,再显示成“审核中”,并丢失通过数/总数。
- 企业签名列表和报备详情改为直接使用 API `carrierReportSummary.status/approved/total`:展示未报备、报备中、部分通过(x/y)、全部通过(x/y)、资料待补充、报备失败和不适用;签名 `auditStatus` 另列显示草稿、待审核、已通过、已驳回。详情同时列出每个目标通道的真实状态,不再读取 `drainageInfo.carrierStatus` 冒充当前汇总。
- 按用户授权,本次将工作区其他会话的字段代码校验、Select 搜索、应用列表及样式等改动一并测试和发布;API 全量 13 suites、137 项通过,API build、前端 build、`git diff --check` 通过。
- 已将完整工作区提交 `344b2afe` 部署生产,部署前完成 PostgreSQL 与 `ade06058` 运行源码备份,无待执行 migration。生产 `.deployed-commit=344b2afe`,四项服务 active`12026/17890/8090/3000` 监听,API/Gateway health 与外部 HTTP 200;发布产物已确认包含“部分通过(x/y)”“全部通过(x/y)”“签名审核”。生产【安徽航天信息】仍为 auditStatus=draft、reportStatus=reporting、1/2 通道 approved,刷新后应展示签名审核“草稿”和三网“部分通过(1/2)”。
## 2026-07-13 企业页面可用性与报备展示修正
- 企业短信模板卡片改为自适应列宽,卡片和操作区允许换行,避免中等视口下固定三列造成水平滚动。
- 企业应用管理列表隐藏 AppID 列;共用 `Select` 对标签中包含“企业”或“应用”的下拉自动提供名称搜索,选项仍来自各页已接入的真实 API。
- 报备字段代码在前端和 NestJS API 双层限制为 `A-Z/a-z/0-9`;API 会拒绝下划线、中文、空白等非法代码,防止绕过页面写入 PostgreSQL。
- 运营端浏览器标题更新为“聆界短信管理平台”。通道报备详情和签名详情弹窗在展示前剔除数据中已存在的外层中/英文括号,统一只渲染一层中括号。
- 本地验证:API 全量 13 suites、137 项通过,API build、前端 build 和 `git diff --check` 通过;前端仅有既有 chunk size warning。应用内浏览器确认本地运营端登录路由、非空 DOM、无框架错误覆盖,且标题为“聆界短信管理平台”;因本地未启动真实 API,验证码请求返回 502,无法进入登录后页面完成模板响应式、下拉搜索和签名实数据的视觉点击验收,未注入 mock 或绕过认证。截图阶段应用内浏览器页面挂载超时,未将截图标记为通过。
## 2026-07-13 运营端企业模板与企业签名布局优化
- 确认上一次自适应修正落在客户端 `/client/templates`,而运营端 `/admin/enterprise-templates` 仍使用累计最小宽度约 1820px 的表格,操作列必须水平滚动才能看到。
- 企业模板管理改为响应式列表行:将模板名称/审核状态、企业/应用/签名归属、两行内容摘要/变量数、更新时间和操作分区展示。在 1180px 以下重排为三列,860px 以下变为单列,预览/编辑/删除始终保留在当前视口。分页仍基于真实 API 结果,每页 10 条。
- 企业签名管理将三网报备标签与 `approved/total` 数量拆开,容器可换行但文字和数量各自保持完整;报备详情/报备状态/编辑/删除固定为两列两行。同时修正签名摘要网格少一列导致“引流信息”和操作区被挤到下一行的布局问题。
- 本地验证:API 全量 13 suites、137 项通过,API build、前端 build、`git diff --check` 通过;前端仅有既有 chunk size warning。Chrome 当前无已登录生产标签,部署前未绕过图形验证码或注入 mock 数据。
- 已将功能提交 `19d47b45` push 到 `origin/main` 并部署生产。部署前备份 PostgreSQL 为 `/opt/cmpp-platform/backups/cmpp-20260713-103205.sql`(约 61MB),备份运行源码为 `/opt/cmpp-platform/backups/source-20260713-103205.tar.gz`(约 63MB);发布快照本地/服务器 SHA-256 一致。生产无待执行 migration,`.deployed-commit=19d47b45``cmpp-api``cmpp-gateway`、Nginx、MinIO 均为 active`12026/17890/8090/3000` 监听,API/Gateway health 和外部 HTTP 200。生产 `index.html` 已引用新资源 `index-HtuLnrXL.js/index-C5Tf8YiT.css`,源码已确认包含企业模板响应式行和签名操作区两列布局。
- 生产浏览器确认路由可加载、标题为“聆界短信管理平台”、DOM 非空、无 console error/warn 和框架错误覆盖。由于浏览器没有已登录运营端会话,访问受保护页面按预期落到登录界面;未经用户确认不代解图形验证码,因此登录后真实数据截图和点击交互留待用户刷新生产页面验收。
## 2026-07-13 签名审核与引流信息通道报备闭环
- 生产只读核查确认:运营端新建的【安徽航天信息】仍为 `auditStatus=draft`;引流资料保存在 `SmsSignature.drainageInfo.links`,动态字段可写 `DrainageReportMaterial`,但缺少引流项维度的真实通道报备任务和记录。
-`/admin/signatures` 从占位页替换为真实签名审核页,支持状态查询、资质/动态资料详情、通过和带原因驳回,调用现有 NestJS 审核 API 并写 `AuditRecord`。运营端新建签名直接写 `auditStatus=approved`,同时写 `admin_create_approved` 审核记录;客户端提交审核流不变。
- `ChannelSignatureReportTask` 增加 `reportType=signature/drainage``drainageItemId`。保存引流资料时,根据企业应用的真实路由通道及其 `drainage/both` 字段自动生成 pending 任务和 create 记录;删除引流项时同步清理对应材料和任务。
- 企业签名引流列表、通道报备详情、报备任务页均按引流项和通道读取/修改同一任务,共享报备记录、导出和回执链路。企业签名三网状态改为引流任务真实汇总,不再使用 `drainageInfo.links.mobile/unicom/telecom` 静态值。
- 短信发送选路和最终通道二次校验明确增加 `reportType=signature`,引流通过任务不会误放行未报备签名。Prisma migrations`20260713153000_add_drainage_report_tasks``20260713154000_backfill_drainage_report_tasks``20260713155000_unique_report_task_scope`;后两者分别回填已有引流材料的 pending 任务/记录,并保证同一报备对象和通道仅一个任务。
- 本地真实 PostgreSQL 已成功应用 3 条新 migrationAPI 全量 13 suites、139 项通过,Gateway 全量 Go 测试、Prisma validate、API build、前端 build 和 `git diff --check` 通过,前端仅有既有 chunk size warning。应用内浏览器确认本地运营端登录路由标题正确、DOM 非空、无框架错误覆盖且 console 无 error/warn;真实图形验证码阻止进入受保护页,未代解验证码、未绕过认证、未注入 mock。
- 功能提交 `551b99cb` 已 push 并部署生产。部署前 PostgreSQL 备份为 `/opt/cmpp-platform/backups/cmpp-20260713-174148.sql`,运行源码备份为 `/opt/cmpp-platform/deploy-backups/full-19d47b45-20260713-174148`;三条 migration 成功应用。生产 `.deployed-commit=551b99cb`,四项服务 active`12026/17890/8090/3000` 监听,API/Gateway health 和外部 HTTP 200;前端产物已包含“短信签名审核”“按通道修改引流信息报备状态”“签名与引流信息报备任务”。生产现有 4 条任务均已回填为 `reportType=signature``DrainageReportMaterial=0`,因此没有伪造引流任务,待真实引流字段资料保存时自动生成。
- 部署后交付复核发现历史运营端新建的 draft 签名虽可在审核页筛选,但操作按钮只对 pending 开放。已改为 draft/pending 都可由运营直接通过或驳回,用于处理【安徽航天信息】等存量草稿;新增签名仍按新规则自动通过。修正提交 `ff3e5607` 已部署,二次备份时间戳 `20260713-174512`;生产 `.deployed-commit=ff3e5607`,四项服务、端口、API/Gateway health 和外部 HTTP 200 再次验证通过,部署后 API stderr 无新错误。
## 2026-07-13 引流信息独立审核与报备任务门禁
- 生产只读核查确认当前 `DrainageReportMaterial=0``reportType=drainage` 任务为 0、包含引流数组的签名为 0,因此本轮可安全引入规范化模型,不需要改写活跃引流业务数据。生产运行代码仍为 `ff3e5607`,本轮暂未部署。
- 新增独立 PostgreSQL 实体 `SmsDrainageInfo`,客户端在已审核签名下新建或修改引流信息均进入 pending,并写 `AuditRecord(targetType=sms_drainage_info)`;运营端新增/修改自动通过。运营端审核中心新增“引流信息审核”页,支持真实 API 查询、详情、通过和带原因驳回,首页和侧栏待审数量同步纳入引流信息。
- 通道报备增加审核门禁:审核通过前不创建新任务或可导出材料;已通过引流信息再次修改时删除旧材料并将已有任务冻结为 waiting_review。审核通过后按应用当前生效路由及通道引流字段重建材料,创建或重置 `ChannelSignatureReportTask(reportType=drainage)`,并写 `audit_approved_create/reset` 报备记录。
- 企业端签名页面补充引流信息新建、修改、删除、动态字段和真实对象存储上传;运营端企业签名页改为调用独立引流 API,并显示引流审核状态。报备任务和报备记录页增加类型筛选,直接关联真实引流实体显示站点、地址和所属签名。
- 本地真实 PostgreSQL 已成功应用 migration `20260713190000_add_drainage_audit_workflow`Prisma validate/migrate status 通过。真实 NestJS API 验收使用现有已审核签名、应用路由和通道临时增加报备字段:客户端创建后为 pending 且任务数 0;运营审核通过后生成 1 条 drainage 任务和报备记录;客户端修改后任务冻结为 waiting_review;驳回原因可从 API 返回。验收数据、临时报备字段、材料、任务、审核及报备记录已在本地数据库清理,不污染业务样本。
- API 全量测试 13 suites、141 项通过,API build、前端 build、`git diff --check` 通过;前端仅有既有 Vite chunk size warning。应用内浏览器确认本地构建标题、非空登录 DOM、无框架错误覆盖且 console 无 error/warn;访问 `/admin/drainage-audits` 按真实权限跳转登录页,因图形验证码未获授权代解,登录后的新增审核页点击和视觉验收未执行。该批改动纳入 2026-07-14 发布批次统一提交和部署。
## 2026-07-14 运营列表组合搜索与报备记录可追溯性
- 企业模板管理将企业名称、企业应用、模板名称、模板内容拆成独立查询参数;企业签名管理拆分企业名称、企业应用、签名名称/用途和引流信息。引流信息查询命中后按签名父级分组,并只展开显示命中的站点、URL 或备注。
- 企业应用管理新增应用名称和状态条件;企业黑名单搜索区重做为企业名称、企业应用、手机号码、入库原因、状态五个独立条件。上述条件均由 NestJS 接收并通过 Prisma AND 组合查询真实 PostgreSQL,不再拼接为单个模糊关键字。
- `ChannelSignatureReportRecord` 新增 `sourceEntry`,企业签名、报备任务、通道报备详情三个入口分别持久化 `enterprise_signature/report_task/channel_report`。报备记录列表展示真实通道名称、签名或引流信息主体、完整主体内容、中文动作和状态变化,并将入口翻译为“企业签名修改 / 报备任务修改 / 通道信息修改”。
- 历史 `manual_status_change` 记录无法从旧数据可靠反推出入口,迁移统一标记为 `legacy`,页面显示“历史记录(入口未记录)”,不得误标为系统自动处理或猜测入口。
- 充值记录、短信记录表头统一左对齐;搜索区使用自适应网格,增加条件后不挤压操作按钮。
- 本地真实 PostgreSQL 已应用 `20260714100000_add_report_record_source_entry`,并通过编译后的 NestJS 服务对现有应用、签名、模板执行真实组合查询。API 全量测试 13 suites、141 项通过;Prisma validate、API build、前端 build、Gateway 测试和 `git diff --check` 通过。应用内浏览器确认真实鉴权跳转、页面标题、非空 DOM、无框架错误覆盖及 console 无 error/warn;因图形验证码未获授权代解,登录后页面点击验收未执行。
- 功能提交 `64216712` 与历史入口修正提交 `70991478` 均已 push,生产最终运行代码为 `709914787196072b66d293ee9290d7cd41b46990`。首次备份为 `/opt/cmpp-platform/backups/cmpp-20260714-095958.sql``source-20260714-095958.tar.gz`,最终修正部署前备份为 `/opt/cmpp-platform/backups/cmpp-20260714-100658.sql``source-20260714-100658.tar.gz`;发布包本地与服务器 SHA-256 一致。
- 生产 36 条 migration 全部应用,四项服务 active,`12026/17890/8090/3000` 监听,API/Gateway health 和外部 HTTP 200,部署后 journal 无新 error。真实报备记录 API 返回 16 条且通道名称缺失数为 0;7 条系统记录为 `system`9 条旧人工记录为 `legacy`。用生产现有数据调用独立组合查询,企业应用、模板、签名和引流信息分组均各返回 1 个精确匹配结果;企业黑名单当前为 0 条,未注入演示数据。
## 2026-07-14 CMPP 入站完整括号签名与部分通道放行修复
- 生产只读核查手机号 `18821203795` 的最近一次提交:Gateway 已接收入站,但 NestJS 在路由前以 `SIGNATURE / 短信内容未识别到已审核且已报备的签名` 拒绝,未生成通道提交记录。实际短信前缀和签名库名称均为完整的 `【航天信息信诺网】`;签名审核已通过、全局报备状态为 reporting,主通道任务 approved、备用通道任务 pending。
- 根因是入站签名解析正则取捕获组后剥离了中括号,却用无括号名称查询保存完整括号的签名库;同时查询错误要求全局 `SmsSignature.reportStatus=approved`,与“部分通道通过即可发送、路由只选通过通道”的既定规则冲突。
- 修复为从短信开头提取完整 `【签名】` 并原样查询,仅在入站候选阶段校验 `auditStatus=approved`。全局 `reportStatus` 不再作为入口门禁,具体通道的 `ChannelSignatureReportTask(reportType=signature).status=approved` 仍由路由和最终提交二次校验。
- 新增真实故障形态回归用例:完整括号签名、审核通过、全局 reporting 时可进入模板不匹配人工审核聚合,并验证查询不再携带全局报备条件、不产生签名失败回执。定向 API 测试 1 suite、41 项通过;API 全量 13 suites、142 项通过,API build、前端 build、Gateway 全量 Go 测试和 `git diff --check` 通过,前端仅有既有 Vite chunk size warning。
- 功能提交 `f08a73e1` 已 push 并部署生产。部署前 PostgreSQL 备份为 `/opt/cmpp-platform/backups/cmpp-20260714-112012.sql`(约 64MB),运行源码备份为 `/opt/cmpp-platform/backups/source-20260714-112012.tar.gz`(约 15MB);发布包本地与服务器 SHA-256 均为 `38b3fa6282a06648dabc048fbebca7a6579a6a7ef1f8eee514ecf14fb9d69790`
- 生产 36 条 migration 无待执行项,`.deployed-commit=f08a73e19129f5249a5a9e0035c7ab63b80422d4``cmpp-api``cmpp-gateway`、Nginx、MinIO 均 active`12026/17890/8090/3000` 监听,API/Gateway health 和外部首页、运营端登录页 HTTP 200。线上源码确认按完整括号 `match[0]` 查询且不带全局 `reportStatus` 条件,部署后 API/Gateway 近期日志无 error。未擅自向客户号码重发短信;新提交将按应用现有模板不匹配人工审核策略进入真实审核与后续通道路由。
## 2026-07-14 服务端安全会话与自动锁定
- 将可预测的 `dev-token:userId:sessionVersion` 和 localStorage 访问令牌替换为 256 位随机会话标识;浏览器只通过 HttpOnly、SameSite Cookie 携带,Redis 使用会话标识 SHA-256 键保存真实状态。生产模式 Cookie 默认 `Secure`;本次按用户要求部署到现有 HTTP 预发布环境时显式配置 `SESSION_COOKIE_SECURE=false`,正式生产切换 HTTPS 后必须恢复为 `true`
- 运营端/客户端无操作阈值分别为 60/120 分钟,提前 5 分钟提醒;超时进入密码锁屏,4 小时内可用当前密码解锁并轮换会话标识,超过后完整登录。绝对会话时长 12 小时不可滑动续期;敏感操作最近密码认证窗口为 30 分钟。
- NestJS 中间件对运营端和客户端受保护 API 强制要求 Redis 会话,逐次校验用户状态和 `sessionVersion`Gateway 回调和 health 保持原内部链路,不被浏览器会话门禁拦截。自动轮询只有检测到近期真实浏览器操作时才携带活动标识,不能长期保活无人值守会话。
- 用户/权限、企业状态、应用密钥、通道和路由、报备状态、手工充值/退款/调整已接后端最近认证 Guard。前端收到 `RECENT_AUTHENTICATION_REQUIRED` 后要求当前密码,成功后自动重试;普通 JSON、Blob 和文件上传统一处理会话 401。会话创建、锁定、解锁、再认证和退出写 `OperationLog`,多标签页同步状态。
- API 自动化测试当前 15 suites、151 项通过;新增 Redis 会话空闲锁定、客户端独立阈值、4 小时恢复期限、解锁轮换旧标识失效、`sessionVersion`/角色权限变更撤销和敏感操作 Guard 用例。真实本地 PostgreSQL + Redis + NestJS 短阈值验收通过:登录响应无访问令牌且收到 Cookie、无 Cookie 访问返回 `401/SESSION_INVALID`、空闲后返回 `401/SESSION_LOCKED`、密码解锁后恢复查询、认证窗口过期后敏感操作返回 `403/RECENT_AUTHENTICATION_REQUIRED`、重新认证和登出成功;临时用户与会话已清理。该功能已随下游 ACK 批次提交并部署,生产 Redis `PONG`,携带无效 HttpOnly 会话 Cookie 访问真实会话接口返回预期 `401/SESSION_INVALID`
## 2026-07-14 下游 CMPP_DELIVER_RESP 精确确认与双重试开关
- 生产只读排查 11:40 两条余额失败短信确认:NestJS 已生成 `REJECTD` 回执,Gateway 对同一 CMPP2.0 会话写出后分别收到 Sequence_Id 37/38 的 `CMPP_DELIVER_RESP`。原 `CmppDownstreamDelivery.status=delivered` 仅代表 `SendPkt` 成功,无法证明客户确认,属于观测口径缺陷。
- Gateway 新增按连接和 Sequence_Id 跟踪投递,记录实际下发 Msg_Id;写出后回调 `downstream/sent`,收到 CMPP2.0/2.1/3.0 `DELIVER_RESP` 后校验 Sequence_Id、Msg_Id 和 Result,再回调 `downstream/acknowledged`。ACK 超时回调真实失败类型;重启后 NestJS 在客户恢复拉取 pending 时补偿处理过期 `awaiting_ack`
- Prisma `SmsApplication` 新增回执、上行两个自动重试开关,默认开启;`CmppDownstreamDelivery` 保存策略快照、写出/确认/截止时间、ACK Result/Sequence_Id/Msg_Id 和连接 ID。关闭开关后首次投递仍保留,已写出未确认或被拒绝的对应类型不自动重发;手工重投保留并增加重复处理二次确认。
- 运营端企业应用编辑页增加两个独立开关;下游投递页区分待首次投递、等待客户端确认、客户端已确认、未确认、拒绝和最终失败,Dashboard 和告警改为真实 ACK 口径。历史 7 条仅确认写出的记录迁移为 `unconfirmed`,不伪造 ACK。
- 新增 API ACK 状态与关闭重试策略单测、Gateway 真实 CMPP 会话 ACK 回调/超时单测;同步更新需求和 TC-GW-ACK-001004。API 全量 15 suites、153 项通过,Gateway 全量 Go 测试、Prisma validate 与本地真实 PostgreSQL migration、API build、前端 build 和 `git diff --check` 通过;前端仅有既有 Vite chunk size warning。应用内浏览器确认本地构建标题、登录 DOM、无框架覆盖及 console 无 error/warn;因真实图形验证码未获授权代解,受保护页面内的两个开关点击验收未执行。
- 功能提交 `8c03663f` 已 push 并部署生产。部署前 PostgreSQL 备份为 `/opt/cmpp-platform/backups/cmpp-20260714-142059.sql`(约 65MB),运行源码备份为 `/opt/cmpp-platform/backups/source-20260714-142059.tar.gz`(约 21MB);发布包本地和服务器 SHA-256 均为 `3e969aaf00e828da4167018bc349795413743414bc827d452859f0fd45776cd1`
- 生产 migration `20260714130000_add_downstream_delivery_ack_tracking` 成功应用,37 条 migration 全部齐全;历史 7 条 `delivered` 已按新口径迁移为 `unconfirmed`,204 个企业应用的回执和上行自动重试开关均为默认开启。生产 `.deployed-commit=8c03663f245c12cd5e6aab28ad9eeb68ea60ed57``cmpp-api``cmpp-gateway`、Nginx、MinIO 均 active`12026/17890/8090/3000` 监听,API/Gateway health、Redis、外部首页和运营端登录页均正常,部署后近期日志无新增 error;前端产物已确认包含“回执自动重试”和“客户端已确认”。未擅自发送或重投客户短信。
- 生产验证站点当前为 HTTP,已按发布约束显式设置 `SESSION_COOKIE_SECURE=false`,ACK 超时设置为 30 秒;无 Cookie 和无效 Cookie 访问受保护/会话接口均返回预期 401。服务器凭据文件对存量管理员只记录 `password=unchanged`,不是可用明文密码,因此未继续进行真实登录和开关点击,且已清除本次诊断产生的一次失败计数;登录后的页面交互由用户使用现有账号验收。
## 2026-07-14 下游回执 Msg_Id=0 与 SubmitResp 时序修复
- 生产核查 15:07、15:10 两条余额失败短信:`CmppDownstreamDelivery` 均记录 Result=0,但 ACK Msg_Id 都是 0Gateway 日志中的原 SubmitResp Msg_Id 分别为 `9677414932210841325``9687782471788644609`。这只能证明下游协议栈收到了 Deliver,不能证明下游平台已将状态回执关联到原短信,与下游页面仍显示“未回执”一致。
- 根因为 NestJS 在处理入站 Submit 时同步生成失败回执,而 Gateway 尚未建立 messageId 映射;原查找逻辑在精确消息未命中时回退账号会话,使用 bind 会话的零值 Msg_Id 提前写出 Deliver。上午 11:40 的回执手工重投后才显示,是因为重投时对应 Submit 映射已经存在,能携带正确 Msg_Id。
- 修复为:精确消息未登记时禁止回退账号会话;Gateway 仅在 ConnectResp/SubmitResp 成功写出后冲刷 pending;新建短信记录持久化原 CMPP Submit Sequence_Id,重启恢复时结合平台 MessageId 重建相同 Msg_Id;发送层拒绝任何 Msg_Id=0 的 DeliverNestJS 也不再把 Result=0/Msg_Id=0 标记为 delivered。
- migration `20260714153000_fix_downstream_receipt_message_id` 新增 `SmsMessageRecord.cmppSubmitSequenceId`,并把存量 `deliveryType=receipt/status=delivered/ackMessageId=0` 纠正为 `unconfirmed`,保留 ACK 证据但不冒充业务确认。
- 下游投递记录列表改为固定信息分组:投递类型/时间、企业/应用、消息 ID、状态、重试、最后错误和操作各自保持可读宽度,最后错误不再被挤成竖排。`delivered` 记录开放单条和批量手工重投,继续保留重复业务处理二次确认;`awaiting_ack` 仍禁止并发重投。
- 已新增 TC-GW-ACK-005 和 Gateway 回归:首个返回包必须是 SubmitResp,随后失败回执 Deliver Msg_Id 与 SubmitResp 完全相同;另覆盖精确映射不回退、持久化 Sequence_Id 恢复和 Msg_Id=0 拒绝。API 全量 15 suites、154 项、Gateway 全量 Go 测试、Prisma validate、真实本地 PostgreSQL migration、API build、前端 build 和 `git diff --check` 均通过;前端仅有既有 Vite chunk size warning。经用户授权在应用内浏览器完成一次本地图形验证码登录,使用真实 NestJS API、PostgreSQL 和临时投递记录在 1440×1000 视口验证列表无横向挤压、无 `NaN`、console 无 error/warn`delivered` 行的重投按钮可用且点击确实进入真实后端重投链路;因本地未运行 Gateway,请求按预期变为待重试而非伪造成功。临时投递记录和验收账号已清理。
- 功能提交 `1ce02ef2` 已 push 并部署生产。部署前 PostgreSQL 备份为 `/opt/cmpp-platform/backups/cmpp-20260714-163007.sql`(约 65MB),运行源码备份为 `/opt/cmpp-platform/backups/source-20260714-163007.tar.gz`(约 21MB);发布包本地与服务器 SHA-256 均为 `67a79f17bd2f61b5df3057ded500009d5d3ef95bf656728299f3ead0011a763a`
- 生产 migration `20260714153000_fix_downstream_receipt_message_id` 成功应用,38 条 migration 全部完成;错误的 `receipt/status=delivered/ackMessageId=0` 已降为 0,共 9 条历史记录按真实口径纠正为 `unconfirmed`。生产 `.deployed-commit=1ce02ef2066aa1ecd995b0a3b884304218adc008``cmpp-api``cmpp-gateway`、Nginx、MinIO 均 active`12026/17890/8090/3000` 监听,API/Gateway health、Redis、PostgreSQL 和外部首页/运营入口 HTTP 200;部署后 journal 无 error,真实 CMPP2.0 账号 `910887` 已重新连接并持续心跳。未擅自发送或重投客户短信,后续真实新提交用于验证 SubmitResp 与 Deliver 的 Msg_Id 关联。
## 2026-07-15 报表导出、模板规则与通用 Select 优化
- 运营端对账单、利润报表、发送质量报表增加真实服务端 CSV 导出,复用页面日期、企业、应用、通道和统计维度筛选,导出全部筛选结果且不受当前分页影响。
- 报备字段库重做为统计概览、签名/引流信息通用配置双栏和自适应字段卡片;继续使用真实字段库及通用字段 API,保留引用锁定删除规则。
- 运营端、客户端用户管理新增按钮统一调整为标准小尺寸。
- 本地真实 NestJS API 与 PostgreSQL 登录验收发现并修复两处仅构建无法暴露的布局问题:运营端新增用户按钮曾被 Grid 拉伸至 731px,现为 98×32px;报备字段库在 800px 视口的筛选区曾出现内部横向滚动,现已切换单列且页面与工具栏 `scrollWidth=clientWidth`。1280px 桌面下三类报表均加载真实聚合数据并显示唯一“导出报表”入口,页面无横向溢出、无 console error/warn。
- 将下拉裁剪修复收敛到项目通用 `Select`:所有下拉默认使用 `document.body` Portal 和 fixed 定位,最高展示 320px 选项,并随视口、页面滚动实时重定位,页面不再逐个配置专用下拉。应用内浏览器以企业签名真实本地数据验证企业搜索、企业选择及应用联动;831px 高视口下列表完整显示在弹窗上方层级,600px 高视口自动向上展开且 `top >= 0``bottom <= viewport`,控制台无 error/warn。
- 提交前整批验证:API 全量 18 suites、194 项通过,API build、前端 build、Gateway 全量 Go 测试、Prisma validate 和本地 47 条 migration status 均通过;通用 Select 另在利润报表普通筛选区确认 listbox 直接挂载于 `BODY`、使用 fixed 定位且完整处于视口内。前端仅保留既有 Vite chunk size warningJest 仍需 `--forceExit` 退出既有异步句柄。
- 工作区完整改动已提交并 push`a7a4e8d9f6aba00b8137e5b70c59bdef67ed3b57``feat: polish reporting templates and shared controls`)。部署前确认本地 `main``origin/main` 一致,并备份生产 PostgreSQL、运行源码和环境配置至 `/opt/cmpp-platform/backups/releases/20260715-163418`;三份备份均通过 SHA-256 复核和压缩包完整性检查,其中数据库备份 SHA-256 为 `a59d3b7f4538f99cdc73a1092ac31628efa45bc38d4adf20a05d6a1f075ec5da`,运行源码备份为 `305c529fb94c71fd6e49f6228717b2ce93f14fff84a84e477d1f202f3192ef58`
- 发布快照本地与服务器 SHA-256 均为 `489d6c969252403689dfdcca15b87e4f5a93b65187551fa6650451527366942e`。生产 `.deployed-commit=a7a4e8d9f6aba00b8137e5b70c59bdef67ed3b57`47 条 migration 全部齐全;`cmpp-api``cmpp-gateway`、MinIO、Nginx、PostgreSQL、Redis 均为 active`12026/17890/8090/3000/9000` 正常监听,API/Gateway health、Redis PONG、PostgreSQL readiness 和外部首页/运营端/API HTTP 均通过,部署后 API/Gateway 无 error 级日志。
- 生产浏览器确认登录页加载成功且标题为“聆界短信管理平台”。服务器凭据文件对存量管理员仅记录 `password=unchanged`,不是可用明文密码,因此未擅自重置生产密码;通用 Select 的企业搜索、企业与应用联动、弹窗越界和普通筛选区 Portal 交互已在部署前通过本地真实 NestJS API、PostgreSQL 数据和生产同构建验证,未使用 mock、localStorage 或静态数组。
## 2026-07-15 签名与引流资料批量导入及统一通道报备(已提交、已部署)
- 新增真实待报备资料工作台:WPS 表格另存 `.xlsx` 后由 NestJS + ExcelJS 解析多行表头、文本和内嵌图片,原文件及图片走 MinIO,导入批次、可复用映射方案、材料版本和待报备状态走 Prisma/PostgreSQL;导入只更新资料池,不自动生成通道任务。
- 新增统一报备批次:运营勾选新建/修改的签名与引流信息后,按企业应用当前生效路由展开全部通道,每通道生成一份内嵌图片的 `.xlsx`,并关联批次材料版本快照、文件行号和真实通道报备任务。无路由、未配置通道字段或缺必填资料不会清除待报备标记。
- 通道签名/引流字段配置按设计基线恢复为字段池和已选字段双栏,支持通道导出表头、顺序、必填、列宽、图片尺寸、缺省值和文本转换;导出严格使用各通道自己的映射名称与顺序。
- Prisma migrations `20260715190000_add_report_material_import_export_workflow``20260715193000_scope_channel_report_fields_by_type``20260715194000_initialize_existing_report_material_pending` 已在本地真实 PostgreSQL 成功应用,50 条 migration status 齐全;字段范围迁移将旧 `both` 配置拆成签名/引流两份,并允许同一标准字段在两类中使用不同表头和顺序;初始化迁移不把上线前所有历史签名误认成本次新建/修改资料。Prisma validate/generate、API 全量 19 suites/198 项、API build、前端 build、Gateway 全量 Go 测试、根目录与 API 生产依赖 audit、`git diff --check` 均通过;audit 为 0 漏洞,前端仅有既有 Vite chunk size warningJest 仍需 `--forceExit` 退出既有异步句柄。
- 应用内浏览器使用本地真实 NestJS API/PostgreSQL 会话验证 `/admin/report-materials`:真实待报备签名加载成功,勾选后“统一生成通道报备”由禁用变为可用;导入弹窗展示企业/应用、映射方案、表头行和 XLSX 文件控件。进入真实通道报备详情后,签名字段配置弹窗按字段池/导出字段双栏渲染,页面和两次交互均无 console error/warn、无框架错误覆盖。未点击统一生成、未上传客户文件、未写入烟测业务数据。
## 2026-07-15 通道 TPS 配置口径清理(已提交、已部署)
- 明确 `SmsChannel.rateLimitPerSecond` 是单个物理通道的 TPS 上限,不是平台总流速;Redis 限速键包含通道 ID,不同通道独立计数。
- 同一物理通道即使被多个通道组引用,也共享通道自身的同一限速桶,不按通道组重复获得额度。
- 删除未参与发送链路的 `SmsChannelGroupItem.rateLimitPerSecond`:Prisma 模型、通道组成员 DTO、真实 API 持久化和前端类型均已清理,并新增数据库迁移删除对应列;通道管理中的 `SmsChannel.rateLimitPerSecond` 和现有 NestJS Redis 限速链路保留。
- 本地真实 PostgreSQL 已应用 `20260715200000_drop_channel_group_item_rate_limit`Prisma validate/generate/migrate status 通过;通道服务定向测试 29 项、API 全量 19 suites/198 项、API build、前端 build、Gateway `go test ./...` 均通过。Jest 仍存在测试完成后异步句柄不自动退出的既有提示,前端仍只有既有 chunk size warning。
## 2026-07-15 Gateway 提交异常处理与最终流速限制(待部署)
- 运营端新增“Gateway提交异常”页面和真实 NestJS API:支持状态/应用/通道/关键字筛选、服务端汇总、脱敏详情及单条重新入队;浏览器响应不包含原始 payload、通道密码、密钥或鉴权字段,数据库和内部兼容接口暂保留 `GatewaySubmitDeadLetter` 技术命名。
- 重新入队增加近期认证、人工原因、明确确认上游未受理、短信状态、通道状态/真实连接状态、最多 3 次以及 pending→requeueing 原子抢占校验;写入 Redis Stream 失败会恢复 pending,成功记录操作人、原因、Stream ID 和时间,后续 SubmitResult 自动闭环 resolved。
- Go Gateway 新增 Redis 分布式单通道限速。连接命令保存权威通道 TPS,提交取权威值与消息值的较小者;同一通道在多实例和多个通道组间共享额度,不同通道独立。Stream 消息超速时保持未 ACK 并等待,不作为发送失败,重启后继续使用既有 pending 恢复机制;worker 对同批消息并发处理,避免低 TPS 通道等待阻塞其他通道。
- 定向验证已通过:Gateway `internal/ratelimit``internal/control``internal/submitworker`API `operations.service.spec.ts``send-chain.service.spec.ts` 共 73 项。最终本地门禁通过:Prisma validate/generate、51 条 migration status、API 19 suites/200 项、API build、前端 build、Gateway `go test ./...``git diff --check`;前端仅有既有 chunk size warningJest 仍需 `--forceExit` 退出既有异步句柄。应用内浏览器确认受保护新路由真实跳转运营端登录、标题和 DOM 正常、console 无 error/warn;因真实图形验证码未获授权代解,未绕过认证或注入会话。
- 首轮生产部署后发现 active 通道在数据库仍显示旧 connected,但新 Gateway 进程没有内存连接池且 Redis 权威 TPS key 为空。根因为发布脚本先重启 API、后重启 Gateway,并且 API 启动未重放 active 通道。已修复为先 Gateway 后 API,API 启动延迟 1 秒后从真实数据库重新下发全部 active 通道;复验结果随最终发布补录。
- 功能提交 `7091a8be` 已 push 并完成首轮部署;部署前备份 PostgreSQL、运行源码和环境文件至 `/opt/cmpp-platform/backups/releases/20260715-182455`,三份备份均通过完整性与 SHA-256 检查。首轮发布包本地/服务器 SHA-256 均为 `dc9fce751fce42bcf8ac14a4b9fd6a1d28a489469350904ee395db3ceb9dcab9`4 条 migration 成功应用,51 条齐全;首轮健康检查、端口、受保护异常 API 401 和静态资源均通过。由于随后发现并修复上述 Gateway 重启恢复缺口,最终运行提交和复验结果以修正发布记录为准。
- 启动恢复修正提交 `85ff0376` 已 push 并完成最终运行时发布;发布前第二次备份至 `/opt/cmpp-platform/backups/releases/20260715-183017`,数据库、源码、环境文件 SHA-256 分别为 `8d632d63db1afcad37889b445e3989819c15053d544f3a63239dca20cd0baecd``5ffc421046f436d5f8ed8178c7a884c4e476567826740d469f4560360304744d``87ab2efd509b39849b0ee22ead1b4cf0568fba4ab8525c59b5c78da51c73bc86`;最终运行发布包本地/服务器 SHA-256 均为 `930ac2a24bf6c1ee24cdfb2f90dff26d89bf7e1f9d84425aa800e66aa8ef4b9a`
- 生产 51 条 migration 全部齐全;`cmpp-api``cmpp-gateway`、MinIO、Nginx、PostgreSQL 和 Redis 正常,`12026/17890/8090/3000/9000` 监听,API/Gateway health、Redis PONG、外部首页和运营入口均通过,受保护异常列表无会话返回 401Redis Stream consumer group pending=0、lag=0,部署后近期 API/Gateway 无 error 级日志。
- 两个 active 上游通道在本次 Gateway/API 启动后均产生新的 `cmpp_connection.connect_requested``cmpp_connection.connected` 审计,currentConnections=1Redis 生成 `rate:gateway:channel:config:<channelId>` 两个独立 key,值均为各自真实配置 100,证明 Gateway 内存连接池与最终 TPS 上限已由当前进程恢复。生产前端资源包含“Gateway提交异常”;应用内浏览器访问新路由按真实鉴权跳转运营端登录,标题、DOM 正常且 console 无 error/warn,未绕过图形验证码。
## 2026-07-16 签名颜色、手机号段布局与跨入口登录失败隔离(已提交、已部署)
- 企业签名卡片建立审核优先、报备汇总次优先的统一颜色规则:全部目标通道通过为绿色、部分通过为蓝色、待审核/报备中/资料待补充为橙色、审核或报备失败为红色、草稿/未报备/不适用为灰色。三网标签将“报备中”从蓝色改为橙色,与“部分通过”明确区分;总体色条增加可访问状态说明。
- 手机号段库通用 Tab 移到页面标题下、搜索条件上;手机号段和运营商区分规则的统计卡分别放入各自 Tab,只展示当前类型的真实服务端总数。搜索、分页、新增和删除仍调用原 NestJS/Prisma API。
- 修复同浏览器跨入口错误登录误踢已有会话:`/admin/auth/login``/client/auth/login` 的 401 仅作为本次凭据失败返回,不再进入通用“现有会话失效”分支,不清理展示会话、不广播 logout、不跳转原入口。受保护 API 自身返回的 401 仍按 Redis 会话失效、锁定或撤销规则处理。
- 本地 PostgreSQL、Redis、正式构建的 NestJS API 和 Vite 生产构建已启动;API 19 suites、200 项全部通过,API build、前端 TypeScript/Vite build 和 `git diff --check` 通过,前端仅有既有 chunk size warning。应用内浏览器确认本地真实 API 登录页、标题、DOM 和验证码请求正常;因未获本轮图形验证码代填授权,未绕过 HttpOnly Cookie 或 localStorage 进入受保护页面,签名颜色、Tab 切换及“已有管理端会话 + 客户端错误密码”可视交互待授权后补测。
## 2026-07-16 HTTP 单条发送、回执/上行回调与上行查询(已提交、已部署)
- 已新增独立 HTTP 接入数据模型与 migration `20260716110000_add_http_open_api`:应用能力配置、独立 IP 白名单、AES-256-GCM 凭据、数据库幂等请求、Webhook 端点/事件/投递/尝试记录,以及应用内唯一 `clientMessageId`
- 已实现 HMAC-SHA256 机器鉴权、Redis nonce 防重放和应用级 QPS、单条发送、短信状态查询、上行游标查询/详情。单发复用真实 `SendChainService` 的模板、风控、余额、计费和 Redis 入队链路;相同幂等键同内容重放、不同内容冲突。
- 已将现有 Gateway 回执和上行落库后投递扩展为 `cmpp/http/both/none` 分流,HTTP Webhook 使用 BullMQ、事件 ID、签名、超时、尝试记录和退避重试;保存与投递前执行 HTTPS/SSRF 校验且不跟随重定向。
- 运营端企业应用编辑页已将 CMPP 和 HTTP 改为独立开关,并增加 HTTP 子能力、白名单、QPS、凭据数、投递模式和回调策略配置。客户端新增“接口对接”五个页签,企业应用卡片显示 HTTP 状态;上行短信页改为 NestJS/Prisma 服务端条件查询。
- 本地真实 PostgreSQL、Redis、MinIO 已启动,migration 成功应用,52 条 migration 全部齐全。临时企业应用通过真实 API 完成 HMAC/时间戳/IP、错误签名、签名后 nonce 防重放、单发业务拒绝、失败幂等重放和上行查询验收:错误签名 401 且不占 nonce,正确签名后同 nonce 重放 401,上行空集合 200;无审核签名的单发首次及相同幂等键重放均为 422,数据库未产生批次、短信、Submit 或回执,因此未向验收手机号发送。临时数据已清理。
- API 全量 20 suites、211 项通过,Prisma validate/migrate status、API build、前端 TypeScript/生产 build、Gateway 全量 Go 测试和 `git diff --check` 通过;前端仅有既有 chunk size warning。Browser 插件不可用,使用工作区 Playwright fallback:真实图形验证码登录客户端和运营端,客户端五个页签、真实凭据/请求日志、运营 HTTP 开关及能力字段均渲染并可交互;桌面 DOM 非空、无框架覆盖、console 无 error/warn。移动端仍受既有 AppShell 侧栏布局影响,本轮未扩展修改全局响应式框架。
- 工作区完整改动已提交并 push`dcb6162dcf93a70e9886c978b07fdccee19dd657``feat: add HTTP API and complete client workflows`)。部署前确认本地基线与最新 `origin/main` 一致;生产 PostgreSQL、运行源码和环境文件备份目录为 `/opt/cmpp-platform/backups/releases/20260716-113511`,三份备份均非空并通过 gzip/tar 完整性及 SHA-256 校验,SHA-256 分别为 `e384e1f041c0638943a0eee4da1877169b3aa89c4b0da95e55908be598ed31e0``1e28149b8b9d068f797a68d66ea074c88a0a43f0efd0b26f0b2d94afc259db3f``87ab2efd509b39849b0ee22ead1b4cf0568fba4ab8525c59b5c78da51c73bc86`
- 发布包本地与服务器 SHA-256 均为 `b49b51ef1acd2a0bb709ca91fbe0b98c16c80bc86f24b35b5dba141907cc7116`;生产原缺失的 `HTTP_API_MASTER_KEY` 已生成并以 `600` 权限保存,密钥内容未输出。migration `20260716110000_add_http_open_api` 已应用,52 条 migration 全部齐全且 schema 最新;生产运行提交为 `dcb6162dcf93a70e9886c978b07fdccee19dd657`
- `cmpp-api``cmpp-gateway`、MinIO、Nginx、PostgreSQL 和 Redis 正常,`12026/17890/8090/3000/9000` 监听,API/Gateway health、Redis PONG、PostgreSQL readiness 和外部首页、运营端、客户端、客户 Swagger 文档均通过。两个 Redis 通道权威 TPS key 均恢复为真实值 100`gateway.submit.commands` consumer group 为 `pending=0、lag=0`,数据库连接状态有 3 条 connected,部署后 10 分钟内 API/Gateway 无 error 级日志。
- 客户文档 JSON 仅包含约定的 4 个路径:单条发送、短信状态查询、上行列表和上行详情;未鉴权访问真实 HTTP 接口返回 401。Browser 插件不可用,使用现有 Playwright/Chromium 复核生产运营登录、客户端鉴权跳转和 Swagger 文档:页面非空、标题/表单/4 个接口正常,输入控件交互成功,无框架错误覆盖或 console error/warn;未重置账号、未绕过验证码、未发送真实短信。
## 2026-07-16 运营端与客户端 P1/P2 移动端适配(已提交、已部署)
- AppShell 在不大于 780px 的视口改为 56px 顶部栏与覆盖式左侧抽屉,抽屉默认关闭,支持遮罩、关闭按钮、Esc 和路由切换后自动收起;运营端长菜单和客户端菜单在抽屉内部独立滚动,不再作为 260px 高的页面顶部区域挤压业务内容。桌面折叠侧栏保持原行为。
- 修复两端登录面板宽度计算;运营端报表、企业查询和审核筛选、手机号段库、通道组、通道报备详情、企业签名报备目标、客户端 HTTP 凭据等多列/固定宽度区域在小屏下改为单列或可换行布局。
- 通用 `Table` 为每个单元格输出字段标签,小屏统一转为纵向记录卡片;客户端签名和引流信息列表、通道报备业务列表补专用卡片规则。状态、失败原因、密钥和操作按钮无需依赖横向滚动查看。
- 应用内浏览器使用本地真实 NestJS API/PostgreSQL 的既有客户端会话在 390×844 视口验证:关闭态 `document.scrollWidth=390`、侧栏完全移出视口;打开态抽屉约占 82% 宽度且菜单独立滚动;点击“签名与引流信息”后路由切换并自动关闭。签名列表由原 1050px 横向内容收敛为 343px 卡片,账户账单表格容器和表格均为 293px 且无内部横向滚动;客户端登录页正文和文档宽度均为 390px,登录面板边界为 16~374px。未注入 mock、未绕过认证、未写业务数据。
- 前端 TypeScript/Vite 生产构建、API build 和 `git diff --check` 均已通过;前端仅有既有 Vite chunk size warning。桌面规则位于 780px 媒体查询之外,既有侧栏折叠和桌面表格结构保持原行为。
- 客户端导航隐藏尚未完成真实后端闭环的整个“彩信服务”分组及六个彩信子菜单;内部占位路由暂时保留,避免把未完成能力暴露给客户。
- 运营端手机号段库按确认设计重做:复用平台通用 Breadcrumb、Button、Input、Tabs、Table、Tag、Pagination 和 Modal;标题区增加用途说明,Tab 下使用当前数据类型的紧凑真实总数信息带,搜索工具栏与数据表形成单一工作区,手机号段使用等宽强调,运营商使用语义标签,删除改为克制的文本危险操作。未增加批量导入、额外筛选或虚构覆盖统计。
- 应用内浏览器使用真实本地运营管理员会话和 NestJS API 验收手机号段库:1536×1024 下页面与表格 `scrollWidth=clientWidth=1230`1280px 下均为 959px,无横向滚动;390×844 下文档、面板、Tab、统计、筛选和表格容器均未超出 390px。已验证 Tab 切换同步“新增号段/新增规则”和当前统计,新增规则 Modal 可打开/关闭,关键词 `138` 查询后输入值保留、重置后清空,console 无 error/warn。设计图中的示例总数、31 省覆盖说明和示例行未照搬,页面只展示本地 PostgreSQL 的真实 0 条数据;分页和标题采用平台通用组件样式。
## 2026-07-16 企业应用 CMPP/HTTP 配置区域拆分(已提交、已部署)
- 运营端企业应用新增/编辑页将原来混排的“接口配置”拆成独立 CMPP 接入配置和 HTTP 接口配置区块。CMPP 区集中账号、扩展码、接入号、密码、连接数、CMPP 白名单和下游重试;HTTP 区集中能力、HTTP 白名单、QPS、凭据限制、投递方式及 Webhook 安全重试,保存仍沿用现有真实 NestJS API 字段。
- 两个协议使用独立开关和视觉标识;关闭时只收起本协议参数并展示关闭说明,不改变另一协议的开关或表单状态。HTTP 能力静态定义移出组件渲染,避免每次渲染重复创建配置数组。
- Browser 插件不可用,使用已有 Playwright/Chromium、真实本地 NestJS API、PostgreSQL、Redis 和临时平台管理员完成 1440×1000 验收:HTTP 从关闭切换为开启后只展开 HTTP 参数;CMPP 关闭后账号等字段消失但 HTTP QPS 保持可见;页面标题、DOM、控制台均正常。390×844 复核 `body.scrollWidth=viewportWidth=390`,无页面级横向滚动。临时管理员、登录操作日志和会话均已清理,未创建企业应用、未发送短信。
- 使用 Node.js 24 执行前端 TypeScript/Vite 生产构建通过,仅有既有 chunk size warning;默认 Node.js 14 不支持当前 Vite 的 `??=` 语法且会错误返回退出码 0,因此未将旧 Node 结果计为有效构建。
## 2026-07-16 全平台金额四位小数精度(已提交、已部署)
- 根因确认:企业应用编辑页原先按 `Math.round(元 × 100)` 保存,`0.0325 元`只能落为 3 分;PostgreSQL 的余额、流水、单价、计费和利润字段也均为 `Int` 分,无法表达万分之一元。现统一调整为 `1 元 = 10000 金额单位`,字段名中的 `Cents` 仅为兼容既有 API 保留。
- Prisma 金额列统一升级为 `BigInt`migration `20260716150000_expand_money_precision_to_four_decimals` 将历史整数分乘以 100。迁移前已备份本地真实 PostgreSQL;抽查 `AccountTransaction、SmsApplication、SmsMessageRecord、TenantAccount` 汇总,迁移后整数值均精确为迁移前 100 倍,按新除数换算后的人民币金额不变。53 条 migration 已全部应用,Prisma schema validate 和 migrate status 均通过。
- 企业应用客户价、通道成本价、授信和人工充值输入均允许最多 4 位小数并转换为整数金额单位;运营端与客户端的余额、授信、今日消费、今日返还、充值、短信详单、客户价、通道价和利润报表统一固定展示 4 位小数。利润 CSV 改为以“元”为表头并导出 4 位小数。
- NestJS 对客户价、通道价、充值、授信、计费规则和计费结果增加安全整数校验;Prisma `BigInt` 响应仅在 JavaScript 安全整数范围内序列化为 number,超限直接报错,避免静默精度损失。计费单测新增 `325 × 2 = 650` 金额单位,企业应用更新单测使用 `customerUnitPrice=325`
- 使用真实本地 NestJS API、PostgreSQL、Redis 和 Playwright/Chromium 编辑一条已配置通道组的企业应用:页面填写 `0.0325` 后保存,数据库核对 `SmsApplication.customerUnitPrice=325`,再次进入编辑页仍为 `0.0325`,控制台无 error;随后已恢复原单价并清理临时管理员、角色关联和操作日志。
- API 全量 20 个 Jest 测试套件通过,其中金额与企业应用目标套件 45 条用例通过;API TypeScript build、前端 TypeScript/Vite 生产 build、Gateway `go test ./...`、Prisma validate/status 和 `git diff --check` 均通过。
## 2026-07-16 企业应用参数复制与下游 CMPP 约束修复(已提交、已部署)
- 根因确认:运营端 CMPP 参数 API 原先误取最新上游 `SmsChannel.gatewayHost/gatewayPort`,不是客户接入平台的地址;页面直接调用 `navigator.clipboard.writeText`,在生产 HTTP 非安全上下文可能无提示失败。现改为读取 `CMPP_PUBLIC_HOST/CMPP_PUBLIC_PORT`,并增加 Clipboard API 失败后的 textarea 降级复制和错误提示。
- 运营端企业应用列表补充 HTTP 参数查看/复制;客户端接口对接页补充 HTTP 参数复制。客户端未开通 CMPP 时按钮禁用,客户端 CMPP 参数 API 同时返回 403,避免仅靠前端隐藏后仍可读取密码等接入参数。
- Gateway 登录响应接入真实 `cmppMaxConnections`,按应用活动 TCP 会话计数并拒绝超限 bind;底层连接关闭回调会清理会话和回写断开。API 连接事件再次校验应用状态、接口开关、IP/CIDR 白名单和连接数,存量连接在下一次心跳不再符合配置时由 Gateway 主动关闭。
- 生产只读核查发现应用 `715011` 最大连接数为 1,当前来源 IP 为 `183.194.97.158`,白名单后来改为 `2.2.2.2`;连接建立早于白名单修改,印证旧实现不会主动清理存量连接。核查期间未修改生产配置、数据库或进程。
- API 目标测试 2 个套件 97 条以及全量 20 个套件 213 条断言通过,API TypeScript build、Gateway `go test ./...`、前端 TypeScript/Vite build 和部署脚本语法检查通过;全量 Jest 完成后仍提示既有异步句柄未关闭,断言结果不受影响,测试进程已单独结束。使用真实本地 NestJS API、PostgreSQL 和 Playwright/Chromium 验证运营端两类参数复制、客户端未开通 CMPP 的页面禁用与 API 403,以及 HTTP 页面复制降级;临时数据已清理。
- 生产首轮发布复核发现 Gateway 进程重启时无法保证回写旧 TCP 会话断开,数据库陈旧连接可能占用应用连接名额,而原超时清理只在查询连接列表时触发。现将 90 秒陈旧连接清理前置到每次非断开连接事件校验,确保新 Gateway 无需等待运营人员打开页面即可自动恢复连接名额。
- 工作区完整功能提交 `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 buildPrisma 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 保持 submittedB 正确 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尚未发布。
## 2026-07-20 工作区汇总生产发布(`f02c33cb`)
- 按用户授权汇总提交全部有效源码、migration、测试和文档改动,共61个文件、1834行新增和281行删除;提交为`f02c33cbb7248410c189f75502d6e497fff7b355``feat: harden CMPP delivery and platform workflows`),已push至`origin/main`。按约束排除`api/tsconfig.build.tsbuildinfo``logs/`
- 提交前门禁:API全量21 suites/237 tests通过,API TypeScript build、前端TypeScript/Vite build、Gateway `go test ./...`、4份Gateway队列契约、Prisma generate/validate及本地57条migration status、真实PostgreSQL报表重算集成验证、部署脚本语法和`git diff --check`均通过。Jest断言完成后仍有既有异步句柄提示,测试残留进程已精确结束;前端仍有约1.9MB大chunk警告。项目未配置前端lint或组件测试脚本,未虚报通过。
- BullMQ 15000条/500并发门槛在本机共享Redis复测为429.50和404.43 TPS,临时独立Redis复测为379.39 TPS,未达到500 TPS;同一代码此前隔离复测达到907.93 TPS。本次不修改门槛、不伪造结果,按共享主机瞬时负载风险继续记录,后续应在固定规格、空载环境建立稳定基线。
- 部署前生产PostgreSQL、运行源码和环境配置备份至`/opt/cmpp-platform/backups/releases/20260720-180906`。三份备份均非空并通过gzip/tar完整性及SHA-256校验:数据库`824be663552da2d2ce99184ec464a6e72d2f25c0c16dee234bfc47c7d371f433`、源码`2598a9b6dd9b6a40002b877316cf23aef48447678915450381f76dcb99d14b54`、环境`189f4f67e9d52f3c1ce751d1005c08667b1b4efcd0c576aaa5ad3249c822e4e7`。发布包本地和服务器SHA-256均为`a6582da4de5c7161b480f4b5695099accb247071ec825e0b697c9f8d60762342`
- 使用`tools/deploy/production-deploy.sh`完成部署,成功应用`20260718130000_add_cmpp_submit_group_message_id``20260720110000_add_receipt_identity``20260720113000_add_report_business_metrics``20260720114500_add_receipt_phone_number`,生产57条migration齐全且schema最新;CMPP多号码分组、回执唯一身份/目的号码、报表失败退款指标等目标列均已存在。
- 预发布运行提交为`f02c33cbb7248410c189f75502d6e497fff7b355``cmpp-gateway``cmpp-api`、Nginx、PostgreSQL、Redis和MinIO均active`12026/17890/8090/3000/9000`监听;API/Gateway health、Redis PONG、PostgreSQL readiness、外部首页、运营登录、客户端登录、API health和Swagger JSON均通过,公网CMPP `8.160.169.106:17890`可连接。根目录/API运行依赖audit均为0漏洞。
- 两个active上游通道均为`connected/currentConnections=1`,权威TPS配置已恢复;Redis Stream `gateway.submit.commands` consumer group为`pending=0、lag=0`。部署后API/Gateway error级日志均为0。未发送或重投真实短信,未执行充值、审核、删除或生产业务数据修改。
- 本地Browser五视口LG2-P0-01证据已通过;生产浏览器烟测被企业网络策略禁止访问该公网HTTP地址,未使用其他浏览器或自动化方式绕过。生产外部HTTP/TCP、真实服务、数据库、Redis和运行产物均已核验,但本次不把生产浏览器交互标记为通过。
## 2026-07-21 北向连接名额、历史回执与报表回填复查(未提交、未部署)
- 生产只读复查确认北向普通/长短信无 `SUBMIT_RESP` 的直接阻塞发生在企业应用下游接入侧:账号 `695829` 的旧连接登记为 `connected`,但 TCP 和 Redis 均无对应在线连接;`lastHeartbeatAt=NULL` 使既有 `lastHeartbeatAt < cutoff` 条件永远不成立,陈旧记录持续占用 `cmppMaxConnections`,新连接在业务层登记时被关闭,Submit 因而未被 Gateway 读取。经用户授权只清理该条已确认无真实连接的陈旧登记,未修改账号、应用或短信数据,连接名额恢复为 0;仍须部署本轮代码后重新做生产普通、长短信和多号码端到端验证。
- `markTimedOutDownstreamConnections` 已增加安全的 NULL 心跳清理:仅当 `lastHeartbeatAt IS NULL``connectedAt` 也早于超时窗口时才删除,避免误清刚建立但首个心跳尚未到达的连接。回归测试先在旧实现上失败,修复后 `sms-config.service.spec.ts` 46 项全部通过。
- 生产历史数据只读核对找到 3 条“已有唯一 DELIVRD、主记录仍 submitted”的旧记录,均为同一上游账号复用两个通道时历史回执 `channelId` 归属反转;2026-07-21 新回执已按唯一提交记录正确归属并聚合,说明当前实时匹配代码已生效,遗留缺口是旧数据回填而不是继续发生的实时抢占。
- 新增 migration `20260721150000_backfill_misattributed_delivery_receipts`:仅在“同一内部消息、同一 Gateway Msg_Id、同一目的号码恰好只有一条提交记录”时修正回执通道并把 DELIVRD 聚合到短信主记录;零匹配或多匹配保持原样,避免猜测修复。迁移可重复执行;本地真实 PostgreSQL 构造同账号双通道错归属样本后应用及重放均通过,主记录变为 `delivered`,回执状态、原始码/文本、到达时间、通道和通道消息号一致。
- 在上述真实样本上执行既有 T-4 至 T-1 报表重算,发送数/成功数/失败数为 `1/1/0`,利润成功数为 1,质量成功率为 100%,平均到达时长为 5000ms;证明 7 月 18、19 日历史报表缺口应按“先迁移回填主记录,再重算对应日期”处理。生产尚未应用 migration 或重算,历史页面当前仍会保持旧结果。
- HTTP 正向发送旧结论经 2026-07-21 较新生产证据纠正:仅传 `mobile/content` 的合法请求已返回 202、自动关联正确签名和模板、返回 MessageId,并通过真实路由和计费;不能再归类为当前未修复。重复用户名生产接口也已返回 409。黑名单 P2002 映射代码原已存在,本轮补充全局/企业及逻辑删除占用的 `BLACKLIST_DUPLICATE` 409 回归测试。
- 完整验证:API 21 suites/240 tests、Gateway `go test ./...`、API build、前端 TypeScript/Vite build、Prisma generate/validate/status58 条 migrationschema 最新)均通过;前端仅有既有约 1.9MB chunk warningJest 仍需 `--forceExit` 结束既有异步句柄。`git diff --check` 在文档收尾后另行复核。
- `npm run verify:phase8` 的契约和 Gateway 阶段通过,但共享 Redis 的 BullMQ 15,000 条/500 并发结果为 enqueue 3577.89 TPS、端到端 431.29 TPS,未达到 500 TPS,因此完整命令未通过;临时隔离 Redis 同参数端到端为 567.37 TPS 并通过。该项按共享环境性能阻塞记录,不把共享 Redis 结果标为通过。
- 本地正式链路启动真实 NestJS API、PostgreSQL、Redis 和 Go GatewayAPI `/api/health`、Gateway `/health` 均为 `ok`Redis `PONG`Gateway 监听 `127.0.0.1:7890` 且恢复候选数为 0;验收后精确停止本轮 3000/8090/7890 端口进程。未向生产号码发送短信,未部署、未提交、未 push。
## 2026-07-21 UI/UX A1双门户会话隔离与深链恢复(未提交、未部署)
- 修复`LG2-P1-01/02/03/05`:前端会话存储、DOM事件和BroadcastChannel按`admin/client`命名空间隔离;NestJS使用`cmpp_admin_session/cmpp_client_session`(安全Cookie环境使用对应`__Host-`名称),中间件按目标门户路径只读取对应Cookie;所有touch/lock/unlock/reauthenticate/logout/password接口改为门户专用路径。
- 新增真实会话初始化边界和安全returnUrl:受保护路由先读取后端当前会话,未登录时保存同源、同门户白名单目标;登录后回原深链。当前会话接口只返回安全用户DTO和时序状态,不输出令牌。锁定刷新时暂停首次业务路由和运营看板请求,解锁后原URL重新挂载真实数据;当前标签已加载的路由不会被另一门户广播卸载。
- 自动化:认证会话2 suites/10 tests、API TypeScript build、前端TypeScript/Vite build及`git diff --check`通过;项目没有前端lint/组件测试脚本,未虚报。前端仍有既有约1.90MB大chunk警告。本批无Prisma schema变更。
- 真实链路:本地PostgreSQL建立隔离验收用户,Redis真实保存不透明会话;同一Cookie容器同时得到`cmpp_admin_session,cmpp_client_session`。锁定admin时client仍activeadmin解锁轮换Cookieclient退出后admin当前会话仍返回`a1_admin`
- 浏览器:客户端13/13受保护路由逐条打开和刷新均保持目标URL及客户端身份,console error/warn为0;同浏览器双门户并存、客户端退出后运营端刷新继续有效。运营用户页在1440×900、1366×768、768×1024、390×844、375×667实际视口均显示真实用户且console为0;390×844锁定、刷新、解锁后仍在`/admin/users`且真实用户重新出现,锁定阶段console为0。证据在测试项目`平台LG_UIUX二轮走查证据/A1会话隔离-20260721/`
- 未修改生产业务数据,未发送短信、充值、审核、删除或报备;代码未提交、未push、未部署,生产仍运行旧会话实现。生产发布后旧共享Cookie需要重新登录,故台账暂记“待验证”。本地临时API由本会话启动,收尾时精确停止;PostgreSQL/Redis/既有前端进程不属于本会话,不停止。
## 2026-07-21 UI/UX A2客户端安全上传与日志导出(未提交、未部署)
- `LG2-P1-04`改为客户端专用上传/下载接口:租户从当前会话用户反查,不再信任`x-tenant-id`;企业认证、签名报备、引流报备用途和目录白名单由后端强制,跨企业下载返回404。前端客户端预览/下载也不再调用admin端点。
- `LG2-P1-06`新增两端共用日志导出状态组件:提交中防重复、完成数量/截断提示/操作单号、CSV下载、失败原地重试,并按门户在`sessionStorage`仅保存瞬时恢复筛选。后端从PostgreSQL真实筛选导出;客户端租户由会话反查且CSV只保留时间、级别、模块、操作人、动作、资源ID,另有10000条上限和公式注入防护。
- 自动化通过:文件/运营服务2 suites、24 testsAPI TypeScript build,前端TypeScript/Vite buildPrisma validate/migrate status(本地58条、schema最新)和`git diff --check`。项目仍无前端lint/组件测试脚本;前端仍有既有约1.9MB chunk告警。
- 真实链路:本地NestJS、PostgreSQL、Redis和MinIO在线;客户端真实验证码登录后,携带伪造租户头上传仍落当前企业`a2-local-tenant`,MinIO对象经客户端接口读回55字节。客户端日志导出真实返回安全6列表头、5条记录;Browser在1440×900点击导出显示3条和操作单号`2aa2521d-111d-48a6-87c2-27cdbcf680be`。本轮未完成console专项读取,不虚报console通过。
- Browser上传因隐藏file input点击超时未完成页面级验收;1366×768、768×1024、390×844、375×667截图也尚未补齐,因此两项台账均保持“待验证”,不标记已通过。未改生产数据,未提交、未push、未部署;并行会话的短信配置、字典、回执migration及其文档未改动。
## 2026-07-21 UI/UX A3审核通过风险治理(未提交、未部署)
- 新增独立审核治理服务和`RiskAction`组件,签名/模板“通过”从直接终态调用改为“后端资格预检→对象/影响确认→提交中锁定→结构化结果”。签名预检覆盖应用绑定、公司/信用代码、法人、责任人/手机号和资质文件;模板覆盖内容、签名绑定及签名审核状态。
- 后端从当前会话写审核人,使用`pending + updatedAt`条件更新和Serializable事务阻止并发覆盖;AuditRecord ID作为操作单号,幂等键随审计持久化,同键重试返回原结果。旧批准路由也统一进入治理服务。未修改并行会话占用的`sms-config.service.ts/spec.ts`
- 自动化通过:审核治理1 suite/5 tests、API TypeScript build、前端TypeScript/Vite build、Prisma validate及`git diff --check`。前端仍有既有约1.91MB chunk告警;Jest断言通过后仍需`--forceExit`结束既有异步句柄。
- 真实本地API/PostgreSQL:完整签名预检为`approve,reject`且0阻断,首次批准写入操作单号`cmruefurl0002msyuftczvswt`并变为approved;相同幂等键重放返回同一单号和`replayed=true`。验收企业、应用、签名、审核记录、用户和日志已精确清理。
- Browser实际打开运营登录页并读取验证码,但登录后受A1未提交会话链的“登录会话已失效”恢复提示阻断,未打开A3确认层;console专项返回空数组。未改生产数据。驳回统一协议、有限撤销/双人复核和五视口页面证据未完成,因此台账记“部分通过”,代码未提交、未push、未部署。
## 2026-07-21 定时短信自动派发与多实例幂等(未提交、未部署)
- 根因:到期任务此前只有 `POST /api/admin/send/scheduled/dispatch-due` 手工入口,API 启动后没有自动扫描;派发前也没有数据库条件更新认领,多实例同时扫描会重复冻结和入队。
- API 启动后默认 1 秒首次扫描、每 5 秒继续扫描,可通过 `SMS_SCHEDULED_DISPATCH_SCAN_ENABLED``SMS_SCHEDULED_DISPATCH_SCAN_INTERVAL_MS` 配置;进程内重入保护避免同一实例扫描重叠。
- 派发使用 `id + status (+ stale updatedAt)``updateMany` 条件更新原子认领。正常任务进入 `scheduled_dispatching`;超过默认 2 分钟的陈旧认领在 `scheduled_dispatching/scheduled_recovering` 间交替认领,保证多实例仅一个恢复者胜出。
- 恢复时按 `tenantId + transactionType=frozen + relatedType=sms_batch_task + relatedId` 查询既有冻结流水,存在则不再冻结;BullMQ 继续使用消息记录 ID 作为 jobId。0 元任务在资源和余额检查通过后若 Redis 入队失败,也保留调度中状态等待恢复,不会被错误终结。
- 回归测试先覆盖旧代码失败,再完成实现;`send-chain.service.spec.ts` 共 67/67 通过,新增自动启动扫描、并发扫描唯一认领、陈旧认领恢复不重复冻结和 0 元任务入队失败可恢复用例。API TypeScript build 通过。
- 该 Jest 套件断言约 24 秒完成,但仍需 `--forceExit` 结束仓库既有 BullMQ/Redis 异步句柄;未把句柄问题标记为通过。本批没有 Prisma schema/migration 变化,尚未执行真实 PostgreSQL/Redis 双实例故障注入,留待本地链路总验收。
- 代码和文档均未提交、未 push、未部署;生产仍需发布后验证无需手工接口即可自动派发到期任务。
## 2026-07-21 下游人工重投与Gateway提交异常恢复(未提交、未部署)
- 根因一:`requeueDownstreamDelivery` 原先先读取再普通 `update`,没有状态版本条件;并发请求可重复递增人工次数并多次调用 Gateway。若直接把认领结果写为 pending,Gateway 恢复扫描还可能与同步控制面调用形成双发窗口。
- 下游人工重投改为 `id + status + updatedAt``updateMany` 原子认领,认领态为 `manual_requeueing`Gateway pending 查询不会选中该状态。成功写出后进入 `awaiting_ack`,失败走既有明确状态机;超过默认 2 分钟的陈旧认领自动恢复为 pending,阈值可通过 `CMPP_DOWNSTREAM_MANUAL_REQUEUE_STALE_MS` 配置。
- 根因二:Gateway提交异常虽然已有 pending→requeueing 抢占,但 Redis XADD 成功、数据库更新 requeued 失败时会永久停留 requeueing;直接重放又可能产生第二条 SubmitCommand。重复异常上报还会无条件把已解决记录重置为 pending。
- 提交异常重入队现在使用 `gateway:submit:requeue:{deadLetterId}:{nextAttempt}` 稳定键和 Redis Lua,原子执行幂等检查、XADD 和 30 天 Stream ID 保存;陈旧 requeueing 由后台扫描抢占为 requeue_recovering,并复用同一键完成数据库落账。SubmitResult 可从 pending/requeueing/requeue_recovering/requeued 任一在途状态直接闭环 resolved,恢复不会覆盖 resolved;重复上报只更新失败详情,不回退处理状态。
- 回归测试先在旧实现失败,修复后 `send-chain.service.spec.ts` 71/71 通过;新增重复异常上报不回退、陈旧提交重入队恢复、下游并发唯一认领和陈旧人工认领恢复用例。API TypeScript build 通过;Jest 仍需 `--forceExit` 结束仓库既有 BullMQ/Redis异步句柄。
- 真实 Redis 验证:同一幂等键并发发布两次返回相同 Stream IDStream entry 数为 1,幂等键 TTL 为 2592000 秒;测试 Stream 和 key 已删除。
- 真实 PostgreSQL 并发验证:两个请求在读取同一版本后同时重投,结果为 1 成功、1 冲突,Gateway 控制面仅调用 1 次,`manualRetryCount=1`,成功记录进入 awaiting_ack;陈旧 manual_requeueing 记录自动恢复为 pending。另以真实 PostgreSQL+Redis 验证陈旧提交异常恢复后 status=requeued、人工次数=1、重复幂等发布仍只有一条 Stream entry。所有本地临时企业、应用、投递、异常、日志、Stream 和 key 均已清理。
- 本批无 Prisma schema 或 migration 变化,没有修改 Go Gateway。未发送短信,未修改生产数据;代码未提交、未 push、未部署。
## 2026-07-21 UI/UX A4报备生成资格、幂等与结果治理(未提交、未部署)
- `LG2-P1-15`后端新增`POST /api/admin/report-materials/batches/preflight`,逐资料/通道检查审核与待报备状态、应用启用、版本、路由、通道状态、字段配置和必填值;非法资料ID返回可读400,0可生成目标在创建批次前返回`REPORT_BATCH_NOT_ELIGIBLE`
- 生成接口要求幂等键,通过PostgreSQL advisory transaction lock认领操作;资料类型/ID/版本/应用/通道/运营商形成持久化业务键。相同请求重放返回原操作单和结果,不同范围复用键返回409;成功响应包含成功、跳过、失败分项及每项阻断原因。
- 运营端列表由真实预检控制选择资格,明确显示“待补充”及首个阻断原因;生成前再次预检,确认层展示企业、应用、版本、预计通道、运营商和分项计数,提交中锁定,完成后显示批次与操作单号。
- 自动化:`report-materials.service.spec.ts` 7/7、API TypeScript build、前端TypeScript/Vite build、Prisma validate及migrate status(本地58条、schema最新)均通过,`git diff --check`通过。API build首次被并行会话尚未完成的`send-chain.service.ts`变量错误阻断,未修改该文件;对方完成后收尾重试已通过。项目没有独立前端lint/组件测试脚本;前端仍有既有约1.91MB单chunk告警。
- 真实API/PostgreSQL:临时已审核但未绑定应用的资料经真实管理员登录、待报备和预检接口返回`eligible=false`、0目标、“未绑定短信应用”;临时企业、用户、签名和日志清理后均为0。没有调用生成接口。
- Browser只打开到确认层并取消:完整临时应用、路由、通道和字段显示1个可生成组合;1440×900、1366×768、768×1024、390×844、375×667均无横向溢出且弹窗在视口内,console error/warn为0。截图位于测试项目`平台LG_UIUX二轮走查证据/A4_LG2-P1-15_报备预检_*.png`;所有临时数据清理为0。
- 未修改生产业务数据,未生成报备、发送短信、审核、充值或删除生产对象;代码未提交、未push、未部署,生产仍运行旧实现。
## 2026-07-21 UI/UX A5通道/签名/模板删除治理(未提交、未部署)
- `LG2-P1-20`新增统一`DeletionGovernanceService`、admin/client预检与删除接口及共享`DeleteRiskAction`。确认层展示对象名称/ID、活动依赖数量和对象摘要、阻断原因、影响范围、可恢复说明、原因、提交态及结构化操作结果;客户端查询由会话企业强制裁剪。
- 后端对通道组/路由/活动连接/报备、签名关联模板/引流/报备、模板发送/批量任务做真实依赖检查。删除使用`updatedAt`乐观锁、Serializable事务、幂等键、逻辑删除和OperationLog操作单,旧通道删除及旧签名/模板删除状态入口统一委托治理服务。
- 正向真实链路首次暴露`PrismaService.operationLog`代理属性不可配置导致事务客户端访问代理时报500;将属性改为可配置并新增`prisma.service.spec.ts`,修复后同一页面请求成功。真实PostgreSQL模板状态变为`deleted`并写入`governance.delete`日志、原因、依赖、影响和幂等键。
- 自动化通过:`deletion-governance.service.spec.ts``prisma.service.spec.ts`共2 suites / 7 tests、API TypeScript build、前端TypeScript/Vite build、Prisma migrate status(本地58条、schema最新)和`git diff --check`。项目没有独立前端lint/组件测试脚本;前端仍有既有约1.91MB单chunk告警。
- Browser真实页面:通道被活动组引用时,1440×900、1366×768、768×1024、390×844、375×667均展示1项引用和后端阻断原因,确认按钮断言disabled;无依赖模板填写原因后页面列表变空,数据库和审计同步落账。console error/warn为0。证据位于测试项目`平台LG_UIUX二轮走查证据/整改_A5_LG2-P1-20_20260721/`
- 本轮只写入并精确清理本地验收数据,未操作生产对象。代码未提交、未push、未部署;通用Dialog焦点/背景/dirty保护归A7,前端拆包与缓存归C阶段。
## 2026-07-21 UI/UX A6人工充值草稿、确认与幂等治理(未提交、未部署)
- `LG2-P1-21`将充值记录页和企业管理页两个入口收敛到共享`ManualRechargeDialog`。取消、右上角关闭和完成都会销毁金额、备注、预检、结果及幂等键;浏览器分别填写`123.45/88.88`和备注后取消/关闭,重开字段均为空。
- 新增`POST /api/admin/billing/manual-recharges/preflight`,从真实企业账户返回版本、现金余额、授信、方向和预计余额。确认层显示企业名称/编码/ID、方向、当前余额、变动、预计余额及备注,提交期间防重复,成功显示订单号、余额、操作单和幂等重放状态。
- 最终接口由当前会话注入操作者,要求8—128位幂等键和账户版本;advisory transaction lock串行同键请求,`updatedAt`条件更新阻止覆盖新余额。订单、余额增量、账户流水和OperationLog在Serializable事务中原子写入。
- 真实API/PostgreSQL:本地临时账户10.0000元,经页面充值1.2345元后为11.2345元;订单、流水、审计各1条。相同幂等键再次调用真实API返回同订单、同操作单、`replayed=true`,三个计数仍各1。充值记录页和企业管理页均回读11.2345元。
- Browser五视口1440×900、1366×768、768×1024、390×844、375×667通过;375短屏底部操作可达,console error/warn为0。截图和API日志位于测试项目`平台LG_UIUX二轮走查证据/整改_A6_LG2-P1-21_20260721/`
- 自动化通过:`billing.service.spec.ts` 1 suite / 11 tests、API TypeScript build、前端TypeScript/Vite build、Prisma migrate status58条、schema最新)及`git diff --check`。项目没有独立前端lint/组件测试脚本;约1.92MB单chunk告警归C阶段性能项。
- 仅修改并精确清理本地验收数据,未操作生产充值。代码未提交、未push、未部署;生产仍运行旧实现,通用Dialog焦点/背景隔离/dirty guard继续由A7处理。
## 2026-07-22 UI/UX A7公共Dialog整改(未提交、未部署)
- 修复`LG2-P1-23`:公共Modal新增初始焦点、顶层焦点栈、Tab/Shift+Tab约束、背景`inert`/`aria-hidden`、滚动锁、`aria-labelledby`和关闭后焦点恢复;遮罩由可聚焦button改为非交互div。
- 新增dirty关闭协议:右上角、取消、Escape和遮罩统一进入具名`alertdialog`;父弹窗暂时inert,继续编辑保留草稿并恢复原字段焦点,明确放弃后才关闭并销毁。人工充值、删除风险动作和两端模板表单接入该协议。
- 真实浏览器:本地NestJS/PostgreSQL/Redis会话分别登录运营与客户端;运营企业模板留存1440×900、1366×768、768×1024、390×844、375×667五张截图,客户端模板在1440×900和390×844完成DOM/键盘实测。五视口无横向溢出,主弹窗与确认层焦点循环、草稿保留、背景恢复、触发按钮焦点恢复均通过;两端console error/warn为空。
- 验证:前端TypeScript/Vite build、API TypeScript build、Prisma validate/migrate status和`git diff --check`通过。项目无独立前端lint/组件/axe脚本,未虚报;构建仍有既有约1.92MB单chunk告警。
- 未创建模板、未执行审核/删除/充值/发送或其他生产业务写入;本地临时用户、企业、日志和Redis会话已精确清理。代码未提交、未push、未部署,生产仍为旧Dialog实现。
## 2026-07-22 UI/UX A2/A3收口(未提交、未部署)
- `LG2-P1-04`通过真实Browser文件选择完成客户端企业认证材料上传;PostgreSQL FileObject归属当前会话企业,MinIO对象回读93字节。复验发现并修复手机端文件名、步骤条和省市选择的卡片内裁切,五视口重新截图后文件名完整且页面无横向溢出。
- `LG2-P1-06`在客户端系统日志页真实点击导出,五视口均显示5条和操作单号;独立客户端会话API再次导出7条,表头严格为`时间,级别,模块,操作人,动作,资源ID`,测试注入的详情/IP/供应商内部字段均未泄露。
- `LG2-P1-14`在运营签名和模板审核页分别打开通过确认层,仅取消时数据库状态仍为pending且审计为0。真实API随后首次批准签名并用相同幂等键重放,两次返回同一操作单`cmrvlgtrq000gakyumhm1iuiv`,重放标志为true且只写一次审核。
- 浏览器证据覆盖上传页和日志导出页五视口、签名确认层五视口截图,以及模板确认层1440×900截图和390×844 DOM尺寸测量;两端console error/warn为空。证据位于测试项目`平台LG_UIUX二轮走查证据/整改_A2_A3收口_20260722/`
- 自动化通过:files、operations、review-governance 3 suites / 31 tests;前端build、API build、Prisma validate/status58条、schema最新)和`git diff --check`。前端仍有约1.92MB单chunk告警,项目无独立前端lint/组件/axe脚本。
- 客户端日志列表仍展示内部详情/IP,继续归`LG2-P1-07`,未因导出安全而标记完成。临时数据、MinIO对象和Redis会话已清理;未触碰预发布环境,未提交、未push、未部署。
## 2026-07-22 工作区汇总提交与预发布发布(`0f223f7f`)
- 按用户授权汇总提交当前全部有效平台源码、migration、测试、依赖锁文件和项目文档,共80个文件、4959行新增和765行删除;功能提交为`0f223f7f91d1bd24e7a2cc0ce6ce9ae3e3258b10``feat: harden platform workflows and UI governance`),已push至`origin/main`。按仓库约束未提交`api/tsconfig.build.tsbuildinfo`和任何`.log`;独立的`outputs/phone-prefix-area-code-20260721/`为号码地区表生成交付物而非平台运行源码,未纳入生产提交。
- 发布前发现API锁文件中的Prisma 7.8开发依赖链产生6个审计漏洞,且`api/package.json`存在重复`overrides`键。已将`@prisma/client``@prisma/adapter-pg`和Prisma CLI同步升级至7.9.0,移除过时Hono强制版本并合并override;干净`npm ci --include=dev`后根目录和API的`npm audit --audit-level=low`均为0漏洞。Prisma Client 7.9.0生成、schema validate及本地58条migration status均通过。
- 本地发布门禁:API全量24 suites / 282 tests通过,API TypeScript build、前端TypeScript/Vite build、Gateway `go test ./...`、Prisma generate/validate/migrate status和`git diff --check`通过。Jest断言完成后仍需`--forceExit`结束既有异步句柄;前端仍有约1.92MB单chunk警告,继续归入后续性能整改,不虚报解决。
- 部署前预发布PostgreSQL、运行源码和环境配置备份至`/opt/cmpp-platform/backups/releases/20260722-142102`。数据库备份`postgresql.sql.gz`为4396893字节、SHA-256 `598e0b624b69c85f707a5c8f769a00fe2118f44e1491f872aa62e1f72d43890f`;源码备份`runtime-source.tar.gz`为28537401字节、SHA-256 `552633474c7888eb2c1a17902430fa385054ef7f5b5e654faf3e2934e94caec4`;环境备份`cmpp-platform.env`为850字节、SHA-256 `189f4f67e9d52f3c1ce751d1005c08667b1b4efcd0c576aaa5ad3249c822e4e7`。三份文件均非空、权限600,数据库gzip和源码tar完整性校验通过。
- 发布包由提交快照生成,本地与服务器SHA-256均为`4aa2188b943885d73558b8e04014fd559d421981071cbcddca72ba379780478e`。使用`tools/deploy/production-deploy.sh`完成部署,成功应用`20260721150000_backfill_misattributed_delivery_receipts`,预发布58条migration齐全且schema最新;脚本按Gateway在前、API在后的顺序重启并恢复运行状态。预发布`.deployed-commit=0f223f7f91d1bd24e7a2cc0ce6ce9ae3e3258b10`
- 发布后`cmpp-gateway``cmpp-api`、Nginx、PostgreSQL、Redis(实际unit为`redis`)和MinIO均active`12026/17890/8090/3000/9000/6379/5432`监听;API/Gateway health、Redis PONG、PostgreSQL readiness均通过。两个active上游通道`CH-1783566107506``CH-1783566107506-COPY-MRCXAK2W`均恢复为`connected/currentConnections=1`,Redis中7个通道权威TPS配置存在;`gateway.submit.commands``cmpp-gateway` consumer group为`pending=0、lag=0`
- 服务器本机及外部访问首页、运营登录、客户端登录和API health均返回HTTP 200,公网CMPP `8.160.169.106:17890` TCP连接成功。预发布根目录/API依赖audit均为0;部署后20分钟内API和Gateway journal error均为0API近期stderr错误匹配为0。
- Browser真实打开预发布客户端登录页`/#/client/login`,页面标题为“聆界短信管理平台”,Logo、用户名、密码、图形验证码和登录按钮完整渲染,DOM非空、无框架错误层,页面console error/warn为0并取得截图。空表单登录按钮交互在Browser控制层两次超时并触发连接重置,因此本次仅将预发布页面渲染烟测记为通过,不虚报登录交互通过;未输入或传输账号密码。
- 本次未发送或重投真实短信,未充值、审核、删除、禁用账号、改密或修改真实通道配置。预发布业务数据变更仅来自已审查并随发布执行的历史回执确定性回填migration。
## 2026-07-22 预发布发布中断窗口复盘
- 本次约1小时19分钟为端到端发布作业时长,并非业务持续中断时长;旧服务在依赖安装、构建、备份和上传期间持续运行,实际服务切换集中在`14:27:53`附近。
- systemd日志显示:Gateway从开始停止到重新启动约43毫秒,Nginx约126毫秒;API在`14:27:53.283`开始停止,Nest于`14:27:54`记录启动成功,API不可用窗口约1秒,按日志秒级精度保守上界小于1.72秒。
- Nginx访问日志在`14:20—14:32`共记录8次请求且均为HTTP 200、无5xx;但`14:27:45—14:28:10`没有请求样本,因此只能确认“未观察到HTTP失败”,不能据此声称HTTP业务零影响。
- Gateway重启会断开全部既有CMPP TCP会话。账号`910887`约3秒后重连;账号`991405`受客户端重试周期和连接名额暂未释放影响,首条可用连接约78秒后恢复,两条连接全部恢复约108秒后完成。若同样方式用于正式生产,应按受影响CMPP客户最长约1分48秒的业务中断评估,而不能只按Gateway监听端口的43毫秒计算。
- 该方式不是零停机发布。正式生产应采用API/前端蓝绿或滚动切换、Nginx reload、Gateway双实例与连接排空、受控关闭时及时释放连接名额,以及持续探针和健康门禁后再切流。
## 2026-07-22 应用日发送上限与HTTP参数复制修复(未提交、未部署)
- 根因:`SmsApplication.dailyLimit`原先仅保存/展示,发送链没有任何读取或拦截;HTTP开通默认值只改了前端表单,Prisma/数据库仍以`cmpp`为回执/上行默认投递模式,已有HTTP应用未回填;复制文本还漏了AppID和第六项“客户端自助密钥”。生产只读核查唯一已开通HTTP应用六项能力均为true,但两个投递模式均为`cmpp`,与现象一致。
- 新增`SmsApplicationDailyUsage(applicationId, usageDate, usedCount)`及唯一索引,按北京时间自然日使用PostgreSQL条件upsert原子抢占去重后号码配额。新建应用和历史NULL默认100000;迁移同时回填发布当日已创建的短信数,避免午间升级后额外获得一整份配额。
- 客户端/HTTP超限整批返回429和`DAILY_SEND_LIMIT_EXCEEDED`,不创建任务/记录/冻结;CMPP整包预留失败后每个号码仍创建可审计rejected记录与`DAILY_LIMIT`失败回执,不冻结或扣费。
- HTTP Schema和后端首次开通均默认六项能力为true、两个投递模式为`http`;迁移仅将“HTTP已开通+对应Webhook已开启+旧模式cmpp”的历史行定向回填为http。复制文本新增AppID和客户端自助密钥能力。
- 回归:目标SendChain/SmsConfig/OpenAPI 3 suites / 138 tests通过;API全量24 suites / 289 tests通过;API build、前端build、Gateway `go test ./...`、Prisma generate/validate通过。前端仍有既有1.92MB chunk警告;Jest断言完成后仍需`--forceExit`结束异步句柄。
- 独立临时PostgreSQL数据库从零应用60条migration并通过status;实链并发验证上限3条时两个2条请求仅1个成功,`usedCount=2`;新HTTP配置六项均true且投递模式为http。临时数据库已删除,共享本地库未应用本轮migration,避免触碰另一会话同时新增的签名迁移。
- `npm run verify:phase8`未通过:契约和Gateway通过,BullMQ完成15000条真实Redis消息,但本机端到端336.82 TPS低于脚本500 TPS门槛,命令链在此中止;未将环境性能不达标写成通过。
- Browser本地页面验收未完成:应用内Browser对`127.0.0.1`和工作站LAN地址均连接被拒,随后浏览器连接超时重置。未使用mock、生产旧页面或伪造截图冒充修复后交互通过。
## 2026-07-22 短信签名完整黑括号口径统一(未提交、未部署)
- 产品口径统一为:客户端和运营端新增、编辑签名时均填写完整中文黑括号名称,例如`【某某科技】`;缺少括号、英文方括号、重复括号、空括号或括号外文本均不得提交。所有列表、详情、审核、报备、模板和发送预览只显示一层完整括号。
- 客户端移除“无需填写【】”提示,两端表单按完整格式控制提交;NestJS新增、编辑API执行相同强制校验并保存完整名称,防止绕过前端。新增migration统一修复历史非规范名称,签名API读取时也按单层格式输出。
- 客户端发送预览改用共享签名替换函数,不再在已经带括号的数据库名称外再次拼接,避免`【【签名】】`。签名资料导入继续走同一后端校验,不能成为绕过入口。
- 验证结果:SmsConfig定向1 suite / 53项、报备资料导入1 suite / 7项、API全量24 suites / 290项通过;API TypeScript build、前端TypeScript/Vite build、Prisma validate通过。本地PostgreSQL已应用第59、60条migration且schema最新;第60条签名migration另在真实PostgreSQL事务内验证`测试``[英文]``【【重复】】`分别规范化为`【测试】``【英文】``【重复】`,随后回滚临时数据。Jest仍需`--forceExit`结束既有异步句柄,前端仍有既有约1.92MB单chunk警告。
- 应用内浏览器使用本地真实PostgreSQL临时企业、平台管理员和企业管理员,分别读取并计算真实算术验证码登录客户端`/#/client/signatures`和运营端`/#/admin/enterprise-signatures`。两端新增表单输入`某某科技`时均出现“必须填写完整中文黑括号签名”错误且提交按钮禁用,改为`【某某科技】`后按钮启用;未点击保存。另插入本地草稿`【编辑验收签名】`,客户端“修改”和运营端“编辑”弹窗均原样显示完整一层括号且提交可用,两端console error/warn均为0,并取得可见截图。
- 浏览器验收结束后已精确删除1条临时签名、2个临时用户、1个临时企业、4条登录操作日志和2个Redis会话;复核企业、用户、签名计数均为0。本会话启动的本地API和前端预览进程已停止,未留下测试业务数据或后台进程。
- 本轮未提交、未push、未部署;未写入预发布业务数据。工作区同时存在其他会话的应用日限额/HTTP默认配置修改,本轮仅在同一文件的签名逻辑区域增量修改并完整保留其改动。
## 2026-07-22 应用日限额与签名格式提交、推送及预发布发布(`a09036c6`)
- 合并功能提交`a09036c67bd24ce7e4b9372aa24918fec9d8386f``fix: enforce application limits and signature format`)首次由另一会话推送时遇到Git HTTP认证失败;本次重新执行`git fetch``git push origin main`成功,发布前`HEAD``origin/main`均为该提交。未提交`api/tsconfig.build.tsbuildinfo``outputs/`;发布期间另一会话新增的`api/src/send-chain/send-chain.service.spec.ts``gateway/internal/inbound/server_test.go`未提交修改也保持原样,未纳入本次发布快照。
- 发布门禁:API目标`SmsConfig/OpenAPI/SendChain` 3 suites / 139 tests通过,API TypeScript build、前端TypeScript/Vite build、Gateway `go test ./...`、Prisma validate/migrate status(本地60条、schema最新)和`git diff --check`通过。Jest断言后仍有既有异步句柄,使用`--forceExit`取得明确汇总;前端仍有约1.92MB单chunk告警。
- 迁移前只读预检:211个应用`dailyLimit`为空、发布日已接收短信为0;已启用HTTP配置中回执/上行仍使用`cmpp`的各1条;40个签名会被规范化。另发现规范化后6组同企业同名签名,但当前模型无名称唯一约束,不阻断迁移;按用户要求不继续清洗或合并历史签名。
- 部署前PostgreSQL、运行源码和环境配置备份至`/opt/cmpp-platform/backups/releases/20260722-165721`。数据库备份4396340字节、SHA-256 `dc4ffef70f04c800330ed8ab2b470ea6d6d1b1d4566c034bcafcc7b7c190cd9e`;源码备份28609359字节、SHA-256 `804bfff7a246ac73494a6195c3d9eb417203ede458722893fcdd52a3dd0425ca`;环境文件850字节、SHA-256 `189f4f67e9d52f3c1ce751d1005c08667b1b4efcd0c576aaa5ad3249c822e4e7`。三份文件权限均为600,数据库gzip和源码tar完整性校验通过。
- 发布包由`a09036c6`提交快照生成,本地与服务器SHA-256均为`1e1409250c24cd57c1682d39ac0bb4e54d140ea3c10f26ff47ff8544136fa8d9`。使用`tools/deploy/production-deploy.sh`完成部署,成功应用`20260722170000_enforce_application_daily_limit_and_http_defaults``20260722173000_normalize_sms_signature_brackets`,预发布60条migration齐全;脚本按Gateway在前、API在后的顺序重启。部署命令从16:58:04至16:58:38共34秒,Gateway、API和Nginx均在16:58:35进入active`.deployed-commit=a09036c67bd24ce7e4b9372aa24918fec9d8386f`
- 数据库发布后复核:应用空日限额降为0,日用量表当前0行/0条(发布日无已接收短信),目标HTTP配置的旧`cmpp`投递模式均降为0。49条历史签名中仍有21条不满足严格单层黑括号正则;用户明确要求忽略现有签名,本次未追加生产数据清洗、合并或删除。
- 发布后`cmpp-gateway``cmpp-api`、Nginx、PostgreSQL、Redis和MinIO均active`12026/17890/8090/3000/9000/6379/5432`监听;API/Gateway health、Redis PONG均通过。2个active上游通道均恢复`connected/currentConnections=1`,Redis中7个通道权威TPS配置存在;`gateway.submit.commands` consumer group为`pending=0、lag=0`,部署后API/Gateway error journal均为0。
- 外部首页、运营登录页、客户端登录页和API health均返回HTTP 200,公网CMPP `8.160.169.106:17890` TCP连接成功。应用内Browser两次在导航/DOM读取阶段控制超时并重置,因此只记录外部HTTP入口通过,不虚报浏览器DOM、交互或console验收通过。
- 本次业务数据变更仅来自两条已审查migration;未发送或重投短信,未充值、审核、删除、禁用账号、改密或修改真实通道配置。
## 2026-07-22 CMPP日限额同步整包拒绝口径修正(`55019443`已发布)
- 产品确认日额度按任务正式受理的北京时间自然日占用,待审核和定时任务计入受理日;后续审核拒绝、取消或发送失败不返还。号码基础校验不以号段库是否识别作为合法性条件,未知号段继续走全国或三网兼容通道。
- 修正CMPP整包日限额语义:API为每个目的号码建立`rejected/DAILY_LIMIT`审计主记录,但返回`accepted=false/result=8`Gateway同步返回唯一一个非0 `SUBMIT_RESP`且Msg_Id为0,不建立下游会话映射、不生成`SmsReceiptRecord`、不创建`CmppDownstreamDelivery`,不冻结或扣费。
- 新增API回归覆盖双号码整包超限、每号码审计记录及无回执/无冻结;新增Gateway真实CMPP2.0协议回归覆盖Bind后的`result=8`响应和不新增pending回执拉取。失败测试先在旧实现上分别暴露`accepted=true`和Gateway响应结构缺少业务结果码,修改后SendChain定向78项及Gateway inbound包通过。
- 功能提交`55019443c0015c65da94f0ad17ed274d77e6290a``fix: reject CMPP daily limit synchronously`)已push至`origin/main`。发布前API全量24 suites / 290项、API build、前端build、Gateway `go test ./...`、Prisma validate/migrate status60条、schema最新)和`git diff --check`通过;前端仅保留既有约1.92MB单chunk告警,Jest仍需`--forceExit`退出既有异步句柄。
- 部署前PostgreSQL、运行源码和环境配置备份至`/opt/cmpp-platform/backups/releases/20260722-171101`。数据库4396934字节、SHA-256 `305f3e5a95e0b850eacc8f4c0ff016880f496c343affac339f74e1a85ba3b8e1`;源码28617718字节、SHA-256 `223f0058982cd484c70312c1f3e03962a59311d79bf4ea59b560323b4c561790`;环境850字节、SHA-256 `189f4f67e9d52f3c1ce751d1005c08667b1b4efcd0c576aaa5ad3249c822e4e7`。三份文件权限均为600`sha256sum -c`、数据库gzip和源码tar完整性校验通过。
- 发布包本地与服务器SHA-256均为`191c41d068b0f418bf04ca44cb4dbd25bcac765961b7b259136102256ba3ba5c`。使用`tools/deploy/production-deploy.sh`完成发布,无待执行migration;脚本先重启Gateway再重启API`.deployed-commit=55019443c0015c65da94f0ad17ed274d77e6290a`
- 发布后`cmpp-gateway``cmpp-api`、Nginx、PostgreSQL、Redis和MinIO均active`12026/17890/8090/3000/9000/6379/5432`监听;API/Gateway health、Redis PONG、PostgreSQL readiness和60条migration状态通过。`gateway.submit.commands``pending=0、lag=0`,7个通道TPS权威配置存在,部署后API/Gateway error journal均无记录。
- 2条active上游通道均恢复`connected/currentConnections=1`。另1条disabled通道的状态行仍显示`connected/1`,但`updatedAt=2026-07-09 03:24:26.242`且本次启动无对应重连日志,确认为历史状态残留而非当前连接;5条deleted测试通道均为failed/0连接。本轮未修改或清理该历史数据。
- 外部首页、运营登录页、客户端登录页和API health均返回HTTP 200,公网CMPP `8.160.169.106:17890` TCP连接成功。生产源码确认API下发`result=8`且Gateway读取业务结果码;未通过真实短信制造超限条件,未发送、重投、充值、审核或修改生产业务数据。
## 2026-07-22 SQL_ASCII签名UTF-8损坏修复(`e28288f6`已发布)
- 预发布企业签名、报备任务/记录、待报备资料及部分模板读取接口返回500。Nginx与API stderr确认`SmsSignature.findMany()`等查询触发PostgreSQL `22021 invalid byte sequence for encoding UTF8: 0xe7 0xbd 0xe3`,不是前端或权限问题。
- 生产数据库编码为`SQL_ASCII``20260722173000_normalize_sms_signature_brackets`中的中文正则字符类按字节工作,把名称末尾“网”的UTF-8最后字节`0x91`误当成黑括号字节删除。49条签名中准确识别2条非法UTF-8;迁移前备份证明二者原值均为`【航天信息信诺网】`
- 新增补偿migration,仅当两个已确认ID及损坏hex同时匹配时恢复备份中的正确UTF-8,不覆盖之后的人工编辑;明确禁止回滚到非法字节,并补充SQL_ASCII与关联接口回归用例。
- 本地真实SQL_ASCII临时库从零应用61条migration后插入生产同款损坏hex,补偿SQL首次执行恢复两条正确UTF-8、第二次执行保持不变,随后删除临时库;本地共享库也已应用第61条migration。API全量24 suites / 290项、API build、前端build、Gateway `go test ./...`、Prisma generate/validate/status和`git diff --check`均通过。
- 功能提交`e28288f6911da002dce39e0e5208609a40bc0e30``fix: repair SQL_ASCII signature encoding`)已push至`origin/main`。首次push遇到Git Credential Manager瞬时认证失败,原命令重试后成功;未提交`api/tsconfig.build.tsbuildinfo``outputs/`
- 部署前PostgreSQL、运行源码和环境配置备份至`/opt/cmpp-platform/backups/releases/20260722-185147`。数据库备份4398661字节、SHA-256 `d0513ca2321bbd73859fd9e28d06380bda78e40383de50d919b13022fefc1976`;源码备份28618928字节、SHA-256 `aca65f5ac304063da476f56baaf1277a3c3487a0e1aefd6fa6f2d2dffebb87c7`;环境文件850字节、SHA-256 `189f4f67e9d52f3c1ce751d1005c08667b1b4efcd0c576aaa5ad3249c822e4e7`。三份文件权限均为600,gzip、tar及`sha256sum -c`完整性校验通过。
- 发布包本地与服务器SHA-256均为`74bc28ad2b9f48661eba133ccddc433a2e3201c1f88808fb8970b7d7c3cf7fd8`。使用`tools/deploy/production-deploy.sh`完成发布,成功应用`20260722190000_repair_sql_ascii_signature_utf8`,现有61条migration齐全且schema最新;脚本按Gateway在前、API在后的顺序重启,预发布`.deployed-commit=e28288f6911da002dce39e0e5208609a40bc0e30`
- 发布后以字节级扫描复核49条签名,非法UTF-8为0;两个目标ID均精确恢复为hex `e38090e888aae5a4a9e4bfa1e681afe4bfa1e8afbae7bd91e38091``【航天信息信诺网】`)。生产Prisma真实执行签名、报备任务、报备记录、待报备资料四类关联查询均成功,分别返回49、34、82、4条,不再触发P2039/22021。
- API/Gateway health、外部首页、运营登录页、客户端登录页和外部API health均返回HTTP 200Redis PONG`gateway.submit.commands``pending=0、lag=0`,7个通道TPS权威配置存在。两个active上游通道均为`connected/currentConnections=1`disabled通道的一条`connected/1`仍是2026-07-09历史状态残留,本次启动没有把它作为活动通道恢复。
- 部署后API日志新增P2039/22021为0,Nginx中企业签名及相关报备接口新增5xx为0。应用内Browser因Chrome标签被另一Codex会话占用且控制连接超时,未完成登录态页面交互验收;本次以真实NestJS所用Prisma关联查询、PostgreSQL字节扫描和Nginx/API日志作为后端修复证据,不虚报浏览器交互通过。未发送短信,未审核、删除、充值、改密或修改其他生产业务数据。
## 2026-07-23 预发布运营商区分规则同步(配置变更,未提交、未部署代码)
- 在线证据以华为云2026年2月《消息&短信》号码规则为主,并以工信部关于`190/197/196/192`公众移动通信网网号核发信息交叉核对。规则覆盖三大基础运营商、移动转售号段及必要的上网卡/物联网/卫星前缀;按用户明确要求将中国广电`192`归入中国移动路由。
- 变更前预发布`PhoneCarrierRule`有30条,存在`190`重复、移动`195`仅覆盖`1951—1952`、电信`191/193`等缺失。完整PostgreSQL及原规则CSV已备份至`/opt/cmpp-platform/backups/config/20260723-164357-phone-carrier-rules`;数据库备份SHA-256为`239c4c669d55e8ccf6bfc5458e92fab507e1af8cb928e7f4ace5cd7f53c1e6f1`,规则CSV SHA-256为`8e437138f0e1a526519b01c6ddc4e95ce58e1a76fb623dc2a3ab0f6616630e86`,两者权限600且gzip校验通过。
- 使用单个PostgreSQL事务锁定并原子替换规则,最终30条全部active:移动12条、联通9条、电信9条。`19200000000`唯一命中`mobile / ^19[2578]`,备注明确“含中国广电192,按业务要求归中国移动”。
- 真实管理员验证码登录成功(201),`GET /api/admin/dictionaries/phone-carrier-rules?page=1&pageSize=100`返回200和30条;对68个基础、转售及新号段代表号码按发送服务同款JavaScript正则验证,全部唯一命中、0个错配,随后真实登出成功。首次验证误把凭据文件的`password=unchanged`当作密码产生一次401,未锁定账号、未修改规则,修正为已授权密码后通过。
- 本次只修改预发布字典配置,不改代码、不重启服务、不发送短信,也不回写历史短信运营商。号段规则反映原始码号分配;携号转网后的当前签约运营商无法只靠前缀判断,若业务要求实时识别需另接MNP/HLR能力。
## 2026-07-23 下游 CMPP UDH 长短信持久化重组修复(发布前验证)
- 根因确认不是近期回归,而是既有下游入站链路从未实现重组:Gateway 将每个 CMPP Submit 的完整 `MsgContent`(包含 UDH)直接按 UCS2/GB18030 解码并逐片调用 NestJS,NestJS 因而把两片当成两条独立短信,控制字节污染首部签名匹配。此前台账通过的是“平台完整正文向上游拆分”和“上游 Deliver 长上行重组”,未覆盖“企业客户端已拆分的下游 Submit 重组”。
- Gateway 新增标准 8 位 `05 00 03`、16 位 `06 08 04` UDH 解析,校验 `PkTotal/PkNumber` 与 UDH 总片数/片序号一致,先剥离 UDH 再按 `MsgFmt` 解码,并向 NestJS 传递引用号、总片数、片序号和编码。真实 CMPP2.0 TCP 回归确认两片均获得成功 SubmitResp,API 收到的片正文不含 UDH。
- NestJS/Prisma 新增 `CmppInboundLongMessage``CmppInboundLongMessageSegment` 及 migration `20260723120000_add_cmpp_inbound_long_message_reassembly`。分组键包含应用、账号、Src_Id、目标号码、引用号、总片数和编码;使用 PostgreSQL advisory transaction lock 与同组同片唯一索引保证并发幂等。分片齐全前不创建批次/短信,齐全后按片序合并并只创建一条完整正文记录;持久化稳定 `messageId` 和第一片 `Sequence_Id`,支持乱序、重复片、冲突拒绝、进程重启恢复及超时转 expired。
- 本地真实 PostgreSQL 16 已应用 62 条 migrationschema 最新。事务验证成功写入2片并按序拼成 `【测试】第一片第二片正文`,同组同片重复索引被唯一约束拒绝,验证事务最终回滚为0条残留。真实本地 NestJS API + PostgreSQL + Redis 调用两次入站接口后,分组为 completed、持久化2片、只创建1条主记录,完整正文和第一片 `Sequence_Id` 均正确;未启动 Go Gateway 上游连接,未发送真实短信。
- 新增长短信相关 API 回归5项(合并、乱序/重复/冲突、处理中断恢复、主记录已落库后的幂等恢复、超时终止)和 Gateway 回归3项(8位UDH、16位UDH、真实CMPP2.0两片转发)。API发送链83/83、API全量24 suites/295项、Gateway `go test ./...`、API build、前端build、Prisma generate/validate/status均通过。`verify:phase8`首次与本地API并发时BullMQ为438.93 TPS而失败;关闭仅由本轮启动的API后单测为909.56 TPS,完整重跑为872.43 TPS并通过。前端仅保留既有约1.92MB单chunk告警。
- 预发布两次测试正文使用的 `【深圳市合正物业服务有限公司】` 在该应用签名库中不存在;本次修复能消除UDH污染并完整重组,但不会绕过签名审核。部署后复测前需先按正常产品流程为应用配置并审核该签名/模板,或改用应用已有的审核通过签名。数据库升级只新增两张重组表和外键/索引;如必须回滚,应先停止新版本 API/Gateway,再删除子表和父表,未完成分片审计会丢失,既有短信主记录不受影响。
- 发布前功能代码、迁移、回归测试和文档已完成并获用户授权提交、推送和部署;本节先保留发布前验证证据,实际提交、备份、migration、服务重启和发布后验收结果在部署完成后追加记录。
## 2026-07-23 下游 CMPP UDH 长短信持久化重组发布(`b29576fc`)
- 功能提交 `b29576fcd118bea04416be0c9fc1bc2a4213d830``fix: reassemble inbound CMPP long messages`)已推送至 `origin/main`。首次 `git push` 遇到远端 HTTP `Failed to authenticate user`,在不改写提交和工作区的情况下使用原命令重试成功;提交未包含 `api/tsconfig.build.tsbuildinfo``outputs/` 和未关联的 `docs/project-daily-log.md`
- 发布门禁通过:API 全量 24 suites / 295 tests、API TypeScript build、Gateway `go test ./...`、前端 TypeScript/Vite build、Prisma generate/validate/status、`git diff --check``npm run verify:phase8` 均成功;BullMQ 端到端 890.61 TPS,前端仅保留既有约 1.92 MB 单 chunk 告警。一次从仓库根目录直接调用 Prisma 因工作目录错误找不到 schema,随后从 `api` 目录使用本地可执行文件重跑 generate/validate/status 全部通过,不将错误命令计为验证通过。
- 部署前 PostgreSQL、当前运行源码和环境配置备份至 `/opt/cmpp-platform/backups/releases/20260723-210601`。数据库 4409540 字节、SHA-256 `c2af72f670f4352723c9ff9e259d52316ed63fe15b28d8aa5b705ed7cfd18dec`;源码 28610639 字节、SHA-256 `61679bdb6efb236d1739c2d1780e010bfb38e7e8d5fb07b52e56a555f8e4a3c2`;环境文件 850 字节、SHA-256 `189f4f67e9d52f3c1ce751d1005c08667b1b4efcd0c576aaa5ad3249c822e4e7`。三份文件权限为 600,数据库 gzip、源码 tar 和 `sha256sum -c` 完整性校验通过。
- 发布包由 `b29576fc` Git 快照生成,本地与服务器 SHA-256 均为 `6d8a70bee437d6583e2e719ee9c17f8f981a5775d8ed9a7e9cbd96ebd4f7874b`。使用 `tools/deploy/production-deploy.sh` 完成发布,依赖审计为 0 漏洞,成功应用 `20260723120000_add_cmpp_inbound_long_message_reassembly`,预发布现有 62 条 migration 齐全;`CmppInboundLongMessage``CmppInboundLongMessageSegment` 两张表存在,发布后尚无长短信分组数据。`.deployed-commit=b29576fcd118bea04416be0c9fc1bc2a4213d830`
- systemd 日志确认 21:07:16 先停止、启动 `cmpp-gateway`,随后停止、启动 `cmpp-api`。Gateway、API、Nginx、PostgreSQL、Redis 和 MinIO 实际运行,`12026/17890/8090/3000/6379/5432/9000` 均监听;本地 API/Gateway health 返回 okRedis PONG`gateway.submit.commands``pending=0、lag=0`10 个 `rate:gateway:channel:config:*` TPS 权威配置键存在,Prisma migration status 为最新。
- 5 条 active 上游通道在重启后的真实结果为 3 条 `connected/currentConnections=1``CH-1784797581833` 及其复制通道 `CH-1784797581833-COPY-MRXBBARL` 被上游明确返回 `connect response status: auth failed`。发布前数据库显示 5/5 connected 属于重启前状态,真实重连暴露了这两条通道的凭据/上游鉴权问题;本次长短信代码未修改上游通道鉴权,未擅自改密或停用通道,需由运营确认凭据后另行恢复。
- 外部首页、运营登录页、客户端登录页和 API health 均返回 HTTP 200,公网 `8.160.169.106:17890` TCP 连接成功。部署后 API/Gateway 未出现 Prisma、panic、fatal、Unhandled 或 Exception 程序错误,Nginx 仅有历史响应缓冲警告和本次正常重启 notice。未发送真实短信、未创建生产测试短信记录;由于目标测试正文的 `【深圳市合正物业服务有限公司】` 尚未配置为该应用的审核通过签名,仍需先完成签名/模板配置,再由用户进行真实企业 CMPP 长短信复测。
## 2026-07-23 供应商CMPP主动心跳与自动重连(本地未提交)
- 现状根因:活动通道仅在创建、启用和API启动时连接一次;Gateway在首次失败后删除连接池,运行中断链只上报状态且不重新拨号。供应商出站连接只响应对端`ACTIVE_TEST`,不主动检测静默半开连接。停用/删除只修改数据库状态,没有关闭Gateway连接;修改连接参数也不会立即替换旧池。
- Gateway新增每通道连接监督器、主动`ACTIVE_TEST`、按Sequence响应跟踪、连续未响应断链、网络退避和鉴权慢速重试。断链读循环立即退出,短信、回执响应和心跳共用串行写锁;连接监督器持续补足期望连接数。新增`DisconnectChannel`控制接口,停用/删除会取消监督器并关闭连接池。
- API新增30秒供应商连接协调任务,以Redis租约避免多实例重复下发;活动通道连接不足、心跳陈旧或失败到期时重新下发,非活动通道存在活动/连接中状态时强制断开。修改连接相关配置立即重建连接,BullMQ连接任务使用每次尝试唯一ID并保留受控历史。
- Prisma新增`lastReconnectAttemptAt``nextReconnectAt``lastErrorCategory``status + nextReconnectAt`索引,migration为`20260723220000_add_upstream_reconnect_schedule`;另以`20260723225000_enforce_supplier_connection_state_identity`清理潜在重复供应商状态并增加`applicationId IS NULL`部分唯一索引,P2002并发创建会复用既有状态。回滚需先停止新版API/Gateway,先删除部分唯一索引,再删除调度索引和三列后启动旧版本;回滚只丢失调度可观测字段,不影响短信、提交和回执记录。
- 运营端通道表单新增心跳间隔和失败阈值,连接日志展示重连次数和下次重连时间。默认30秒/3次,可按供应商通道覆盖。
- 当前验证:Gateway定向及`go test ./...`通过,并含真实CMPP服务端闭环“首次拒绝连接→端口恢复→自动重连→平台主动心跳收到响应”;合并并行工作区后API通道定向38项、API全量24 suites / 304项均以`--no-cache`通过,API TypeScript build、前端TypeScript/Vite build、Prisma generate/validate通过。本地升级前供应商重复状态组为0,两条自动重连migration均已应用;当前合并工作区66条migration全部应用且status最新。一次将`send-chain.service.spec.ts`与通道套件联合运行在184.8秒超时且无汇总,随后在Redis PONG环境取得明确全量通过汇总;Gateway race检测因本机CGO未启用而未执行。
- 本地真实协调验证使用临时通道贯通Prisma/PostgreSQL、Redis租约、BullMQ和HTTP控制请求,确认failed活动通道写为connecting并发送`ConnectChannel/automatic_reconnect`,改为disabled后写为disconnected/0连接并发送`DisconnectChannel/inactive_channel_reconcile`,Redis租约释放。首次执行还暴露BullMQ禁止含冒号的jobId,修正为合法唯一ID后复测通过。协调器同时命中两条既有本地陈旧活动通道;已依据验证前操作日志精确恢复二者为原`failed/0/connect_timeout`状态,删除3条本轮日志和2个本轮队列任务,临时通道、状态和日志也已清理。
- `npm run verify:phase8`中的Gateway和契约阶段通过,但BullMQ性能阶段两次分别为284.16和376.52 TPS,低于500 TPS门槛,因此完整phase8仍记为环境性能失败而非通过;本轮功能正确性不依赖降低该门槛。
- 本地前端预览可加载且无框架错误覆盖层,但访问运营端通道页因本地API未启动跳转登录页,验证码接口返回502;按安全规则未绕过验证码、未提交浏览器中已有凭据,因此新增心跳字段和连接日志的登录后页面交互验收仍未完成。预览进程及浏览器测试页已关闭。
- 本轮未提交、未push、未部署,也未修改预发布通道或发送真实短信。生产/预发布仍运行旧版本,不具备本节新增自动重连能力。
## 2026-07-23 通道、应用、详单与报表整改(未提交、未部署)
- 已实现:新建通道默认端口 7890、协议强制 CMPP、业务代码 `serviceId` 默认 SMS;短信测试弹窗白色顶部栏。
- 已实现:短信详情展示发送号码,分片审计改为响应式卡片;短信记录列表压缩间距并限制长内容为两行。
- 已实现:企业应用任务号码默认上限 10000;列表操作换行、客户连接状态改名、未开通参数按钮禁用并区分颜色、CMPP 参数弹窗移除冗余摘要框。
- 已实现:安全控制三个真实 API 默认和显式 deleted 查询均排除逻辑删除数据;添加/编辑二级路由保持所属菜单选中。
- 已实现:三张日报表及导出增加提交数、未知数,使用 `billingUnits` 统计长短信分片,并从发送数中排除平台拦截。
- 已实现:企业认证审核隐藏申请单号、短信审核隐藏任务编号、模板审核隐藏审核编号。
- 自动化验证:`prisma validate` 通过;相关 4 个 Jest 套件 108/108 通过;API build 通过;前端 TypeScript/Vite build 通过;`git diff --check` 通过。
- 真实本地链路验证:本地 PostgreSQL 已应用并核对全部 66 条 migration`prisma migrate status` 返回 schema up to date;使用真实登录/API 验证安全控制三个列表即使显式传入 `status=deleted` 也不返回已删除数据;重新生成 2026-06-30 至 2026-07-03 报表后,利润、对账、质量报表均满足 `发送数 = 未知数 + 成功数 + 失败数``提交数 >= 发送数`,三个真实报表 API 均返回新增字段。
- 浏览器验收:使用真实本地 API/PostgreSQL/Redis 页面完成 1440×900、1366×768、768×1024、390×844、375×667 验收。短信记录详情展示发送号码且弹窗无横向溢出;企业应用操作区换行、客户连接状态表头、接口参数按钮状态色及 CMPP 参数精简均生效;新增应用默认每任务 10000 个号码;三个报表新增提交数和未知数;三个审核页面不再展示内部编号;新增应用深链保持“企业应用”菜单选中。
- 验收边界:本地数据库暂无包含分片审计明细的短信记录,因此已验证真实空状态和各视口无横向溢出,分片卡片的有数据视觉仍需在存在真实分片记录的环境补验。当前修改保持未提交、未推送、未部署。
## 2026-07-24 合并发布前门禁与 TPS 隔离复测
- 本轮按用户授权合并供应商 CMPP 主动心跳/自动重连、通道与应用整改、详单与报表口径及全部并行工作区代码。`origin/main` 与本地基线提交 `2f781ebb8af656bdb2a0395fa742538961febf21` 一致,无远端新提交需要合并;`api/tsconfig.build.tsbuildinfo``outputs/` 按发布规则排除。
- 发布前 API 全量 24 suites / 304 tests、Gateway `go test ./...``go vet ./...`、API build、前端 TypeScript/Vite build、Prisma generate/validate/status、`git diff --check`均通过;本地 PostgreSQL 共 66 条 migration 且 schema up to date。
- 依赖审计发现新公告:前端 `react-router-dom/react-router 7.17.0` 存在中危开放重定向等问题,升级至 7.18.1API 的 Prisma CLI 间接依赖 `find-my-way 9.6.0` 存在高危 HTTP/2 DDoS 问题,以兼容覆盖固定为 9.7.0。升级后根项目与 API `npm audit` 均为 0 漏洞,构建和 Prisma 命令复测通过。
- 本机 3000 端口存在其他会话自 2026-07-23 23:02 起运行的 API 进程,若直接使用共享 Redis 压测会与业务服务争用事件循环、CPU 和 Redis 连接。为保留该会话进程并消除队列干扰,本轮在独立临时 Redis 6389 上运行完整 `npm run verify:phase8`15000 条消息入队 3679.70 TPS,提交结果与回执完整闭环 549.52 TPS,达到 500 TPS 门槛;临时 Redis 已停止。
- 预发布部署前只读基线:`.deployed-commit=b29576fcd118bea04416be0c9fc1bc2a4213d830`,服务器 4 核、7499MB 内存、可用内存 6519MB、负载 0Gateway/API/Nginx/PostgreSQL/MinIO 正常,Redis `PONG`,发送 Worker 并发 50`gateway.submit.commands``pending=0、lag=0`。实际发布、备份、迁移、服务重启和预发布 TPS 结果待部署后补记。
## 2026-07-24 供应商连接恢复、运营整改与安全依赖合并发布(`afd3c960`)
- 合并提交 `afd3c960709b1660c18d028499c811321983cb22``feat: improve channel resilience and operations`)包含 43 个文件、1969 行新增和 299 行删除,已推送至 `origin/main`。第一次 push 仍遇到 Git HTTP 鉴权失败,未改写提交或 remote,直接重试后成功;`api/tsconfig.build.tsbuildinfo``outputs/` 未纳入提交。
- 部署前 PostgreSQL、运行源码和环境配置备份至 `/opt/cmpp-platform/backups/releases/20260724-081857`。数据库备份 4414185 字节、SHA-256 `e69e1ea4c40871d2b213783cabe581f4f8697cac7c3d6e4fd62696508ad6a6e1`;源码备份 28621409 字节、SHA-256 `59d93f67be13d92d677b70ba6e0ee4b49797d7c1068304a548562a95e55defbc`;环境文件 850 字节、SHA-256 `189f4f67e9d52f3c1ce751d1005c08667b1b4efcd0c576aaa5ad3249c822e4e7`。三份文件权限均为 600,数据库 gzip、源码 tar 和 `sha256sum -c` 校验通过。
- 发布包由 `afd3c960` Git 快照生成,本地及服务器 SHA-256 均为 `a721b60750ff891ed9c43a203c2f82c113337f1b3ad34be5938705d643e39a59`,大小 1657178 字节。服务器未安装 `rsync`,首次同步在覆盖源码前安全停止,运行目录和服务未改变;随后按已核对的 Git 顶层路径精确替换源码,保留 `backups/node_modules/dist/logs/.deployed-commit` 等运行数据,再使用 `tools/deploy/production-deploy.sh` 发布。
- 发布成功应用 `20260723220000_add_upstream_reconnect_schedule``20260723223000_default_application_task_phone_limit``20260723224000_add_report_submission_unknown_units``20260723225000_enforce_supplier_connection_state_identity`,预发布 66 条 migration 全部应用且 schema up to date。部署脚本依次重启 Gateway 和 API`.deployed-commit=afd3c960709b1660c18d028499c811321983cb22`;根项目和 API 生产机 `npm audit` 均为 0 漏洞。
- Gateway、API、Nginx、PostgreSQL、Redis 和 MinIO 运行正常,`12026/17890/8090/3000/6379/5432/9000` 监听;本机 API/Gateway health、Redis PONG、公网页面、运营登录入口、客户端登录入口及 API health 均为 HTTP 200,公网 CMPP 17890 TCP 可连接。应用内浏览器连续两次在登录页加载阶段控制超时,因此不虚报 DOM、控制台或登录后页面验收。
- 5 条 active 供应商通道均恢复为 `connected/currentConnections=1/desiredConnections=1`,主动心跳持续刷新且无 `lastError/lastErrorCategory`;禁用通道为 disconnected。发布后 API/Gateway 的 panic、fatal、unhandled、exception、error 匹配均为 0`gateway.submit.commands` 保持 `pending=0、lag=0`,压测专用 BullMQ 键清理后为 0。
- 预发布在真实 API/Gateway 保持运行的条件下,使用不触碰业务 Stream、不访问供应商且最终清理的专用 BullMQ 队列连续执行 3 轮 15000 条测试:完整提交结果与回执闭环分别为 3570.33、3650.92、3587.31 TPS,平均 3602.85 TPS;入队平均 18781.28 TPS。该指标只表示 Redis/BullMQ 与 Node Worker 的内部队列能力,不包含 Prisma 业务事务、计费、路由或真实 CMPP 网络。
- 既往 284.16、376.52、438.93 TPS 的下降主要是测试环境争用而非已证实的代码回归:本机 3000 端口有另一会话从 2026-07-23 23:02 起运行的 API,压测与其争用 Redis 和 CPU;此前停止 API 后曾恢复至约 872—910 TPS。本轮未终止其他会话进程,改用独立 Redis 后完整 Phase 8 为 549.52 TPS并通过。预发布同一代码三轮约 3603 TPS且方差很小,进一步说明旧低值不能作为平台容量结论。
- 当前真实发送配置的上限不是 3603 TPS:5 条 active 通道各配置 100 TPSGateway 总配置上限为 500 TPS;每条通道 1 个连接、窗口 16API Send Worker 并发 50。真实持续吞吐还受供应商授权 TPS、网络往返、回执速度、数据库和计费事务影响,因此当前预发布应按“内部队列约 3600 TPS、配置发送上限 500 TPS、真实供应商持续能力仍需协议测试环境或供应商配合压测”理解。本次未发送真实短信、未修改生产业务数据。
## 2026-07-24 回执缺失复核与通讯交互日志(本地未提交、未部署)
- 预发布只读复核确认当前仍部署`afd3c960709b1660c18d028499c811321983cb22``13127620092`今日两次提交经“赛邮行业-王斯评中转”获得上游Msg_Id后停留submitted;该通道最后一条回执为2026-07-21 17:39:58(北京时间)。`18821203795`今日09:26“富泷物业-移动”测试提交上游Msg_Id `736025035345047554`5分32秒抓包期间只有SubmitResp和心跳,没有供应商DELIVER。
- 回执链路并非全局失效:同一“富泷物业-移动”通道在7月23 17:02收到DELIVRD,在本次`afd3c960`部署并重连后仍于7月24 08:49收到`UT:0010`失败回执;其他通道7月23、24也有回执落库。因此“7月24心跳/重连代码导致所有回执不能处理”与事实不符。Gateway的`handleDeliver`核心解析、匹配和API转发自7月8/9以来未改;最强证据仍指向特定提交或特定供应商账号未下发DELIVER,赛邮账号则需重点核查重启后的回执会话绑定。
- 新增独立`ProtocolInteractionLog`及migration `20260724113000_add_protocol_interaction_logs`,记录CMPP/HTTP业务交互的协议、方向、事件、状态、消息/请求标识、脱敏手机号、结果码、耗时和安全详情。默认500ms/100条批量异步写入、10000条缓冲上限和30天保留;不逐包记录心跳,不保存正文、密码、密钥、Token、鉴权签名或完整请求体。
- NestJS Gateway事件入口记录收到、成功和失败;公开HTTP发送记录受理/失败;HTTP Webhook记录成功、重试或失败。Go Gateway对DELIVER回执解包失败、上行解码失败、收到事件及转发API结果输出结构化日志,消除原先静默丢失盲点。
- 运营端系统日志新增“通讯交互日志”页签,支持协议、方向、事件、结果、关键字和时间筛选,分页展示并提供安全详情弹窗;手机号脱敏说明可见,详情操作列固定。
- 本地真实PostgreSQL已应用67条migration并为最新。使用新建的本地平台管理员、真实算术验证码和真实NestJS API提交无效CMPP认证,接口按预期返回400,查询API从PostgreSQL读到同一`connect`事件的`received``failed`两条记录,失败耗时17ms;未发送短信。
- 自动化验证已通过:API全量25 suites / 306 tests(含通讯日志脱敏/过滤和公开HTTP回归)、Prisma generate/validate、API TypeScript build、前端TypeScript/Vite build、Gateway `go test ./...`。全量Jest使用`--forceExit`结束并保留既有异步句柄提示;前端仍有既有约1.93MB单chunk/578.69KB gzip警告。
- 浏览器使用真实本地账号和验证码登录运营端,两个日志页签切换、真实数据表、筛选控件、分页和详情入口可见;桌面控制台0条error/warn,截图确认页面正常。最终构建在390×844和375×667复验均无页面级横向溢出,详情操作区分别完整位于`x=18..372``x=18..357`,控制台均为0条error/warn;截图保存在`outputs/protocol-interaction-logs/`
- 当前改动保持未提交、未推送、未部署。预发布尚无新通讯日志表和页面,后续发布前必须执行备份、migration、Gateway先于API重启及发布后真实回执链路观察。
## 2026-07-24 通讯交互日志预发布发布(`0bfeb083`)
- 功能提交`0bfeb0839ee6b67611e13796f46c19fc05b88de9``feat: add protocol interaction observability`)已推送至`origin/main`。首次推送被内部Git服务以`Failed to authenticate user`拒绝,第二次因网络超时且远端未更新,第三次重试成功;提交未包含`api/tsconfig.build.tsbuildinfo``outputs/`
- 发布门禁通过:API全量25 suites / 306 tests、API TypeScript build、前端TypeScript/Vite build、Gateway `go test ./...``go vet ./...`、Prisma generate/validate/status、`git diff --check`。本机根项目审计为0漏洞;本机API审计因npm Registry TLS建连失败未取得结果,预发布部署机`npm ci`随后对根项目59个包和API 717个包审计均为0漏洞。
- 部署前PostgreSQL、运行源码和环境配置备份至`/opt/cmpp-platform/backups/releases/20260724-105310`。数据库备份4422054字节、SHA-256 `bb48d3f2fcd4b291398700e66db6078130b36c519e06de007095e23f52ed7618`;源码备份28656320字节、SHA-256 `11085039b8f305883ec8082a9a45ff69f3f65919ad09bbe7a29f4ef6631f2be2`;环境文件850字节、SHA-256 `189f4f67e9d52f3c1ce751d1005c08667b1b4efcd0c576aaa5ad3249c822e4e7`。三份文件权限均为600,数据库gzip、源码tar和`sha256sum -c`全部通过。
- 发布包由`0bfeb083`精确Git快照生成,大小1667341字节,本地与服务器SHA-256均为`c3db81b662167fa23a46cee65828265d94e89057e8234a9b97443daf4d86748b`,tar完整性通过。精确替换运行源码时保留`backups/node_modules/dist/logs/.deployed-commit`,随后使用`tools/deploy/production-deploy.sh`完成发布。
- 标准脚本成功应用`20260724113000_add_protocol_interaction_logs`,预发布67条migration全部齐全且schema最新;前端、API和Gateway构建成功,脚本明确先重启Gateway、再重启API,最终`.deployed-commit=0bfeb0839ee6b67611e13796f46c19fc05b88de9`
- API、Gateway、Nginx、PostgreSQL、Redis和MinIO实际运行;`12026/17890/8090/3000/6379/5432/9000`监听,API/Gateway health为okRedis PONG。`gateway.submit.commands`消费者1、`pending=0``lag=0`10个`rate:gateway:channel:config:*` TPS配置键存在。
- Gateway重启后5条启用上游通道中3条立即连接,“富泷物业-联通/电信”共享账号`C59748`首次被供应商返回`auth failed`;自动重连按`nextReconnectAt=11:00:58`执行后两条均恢复。最终5/5通道全部`connected/currentConnections=1/desiredConnections=1`,最近心跳持续刷新,`lastError``nextReconnectAt`清空。
- 公网首页、运营登录、客户端登录和API health均HTTP 200,公网CMPP 17890 TCP连接成功。真实运营账号通过算术验证码登录,在“系统日志→通讯交互日志”看到预发布CMPP连接的`received/success`事件、耗时和固定详情列;浏览器控制台0条error/warn。
- 发布后API/Gateway日志未出现panic、fatal、unhandled、exception、通讯日志批量写入失败、回执解包失败或API转发失败;Nginx无error/crit/emerg。未发送真实短信、未充值、未审核、未删除或修改业务对象。
## 2026-07-24 通讯日志方向与重复记录修正(本地未提交、未部署)
- 复核预发布号码`18821203795`对应消息`MSG-TEST-1784863949078-b28e828d`确认:原页面四条记录全部显示“通道→平台”,不是四个完整协议报文,而是仅采集了Gateway转入NestJS的`CMPP_SUBMIT_RESP``CMPP_DELIVER`,并把每个报文各拆成`received/success`两行;真实下行`CMPP_SUBMIT``CMPP_DELIVER_RESP`此前未采集,因此箭头虽符合现有事件入口方向,但不足以表达完整交互并造成重复观感。
- 改为“一条数据库记录对应一个真实业务报文”:NestJS不再为同一入站报文分别写`received``success`,只落最终成功或失败结果;Go Gateway在真实`SendReqPkt(CMPP_SUBMIT)``SendRspPkt(CMPP_DELIVER_RESP)`写包完成后异步上报平台→通道事件。正常短短信完整成功闭环将显示`CMPP_SUBMIT → CMPP_SUBMIT_RESP → CMPP_DELIVER → CMPP_DELIVER_RESP`四条真实报文及实际传输方向;历史`received`行保留并标记为历史,不修改既有预发布数据。
- Gateway遥测使用独立goroutine和既有HTTP超时,不阻塞发送/回执读循环;API仅接受`cmpp + platform_to_channel + submit/deliver_resp + success/failed`白名单事件,不接收正文、密码或密钥。
- 本地使用真实NestJS API、真实算术验证码管理员会话和真实PostgreSQL写入两条安全的合成出站协议事件(未发送短信),再从运营端查询`MSG-DIRECTION-SMOKE`,页面正确显示“平台→通道 / CMPP_SUBMIT / 已发送”和“平台→通道 / CMPP_DELIVER_RESP / 已发送”;浏览器控制台0条error/warn。
- 自动化验证:新增Controller单元测试覆盖入站单报文单记录和Gateway白名单,新增Go测试覆盖出站遥测payload;API全量、API/前端构建、Prisma validate、Gateway全量测试及`git diff --check`结果见本次会话最终记录。
- 当前修改保持未提交、未推送、未部署;预发布仍运行`0bfeb0839ee6b67611e13796f46c19fc05b88de9`,发布前不得把本地验证结果误认为预发布已生效。
## 2026-07-24 跨连接回执、长短信聚合与通讯日志闭环修复(发布前)
- 对号码`13127620092`的预发布只读证据确认:两分片均已由供应商受理,第二片回执从同账号复制通道连接返回。Gateway内存映射按物理连接隔离,未找到原提交后上报了`receipt-736070230367350788`和当前连接通道;NestJS又要求`channelId + gatewayMessageId`严格一致,最终通讯日志显示`SMS message record not found`。首片`DELIVRD`同时提前把主记录改成`delivered`,没有等待第二片,是独立的长短信聚合缺陷。
- 回执匹配增加分片审计路径,并把“同供应商”固定为账号、Gateway主机、端口、协议和CMPP版本全部一致。只有`gatewayMessageId + DestTerminalId`在该供应商范围内唯一时才允许跨物理连接认领;回执、幂等键和主记录仍归属原提交逻辑通道。不同供应商或候选不唯一继续拒绝,避免串单。
- 长短信每片回执先写`SmsMessageSegmentAudit`。部分成功时主记录保持`submitted`且不向企业应用投递最终回执;全部分片成功后才聚合为`delivered`并投递一次。任一明确失败进入既有最终失败/补发路径,重复回执继续由逻辑通道回执键幂等。
- 通讯日志改为一条真实业务报文一条记录:供应商长短信每片真实`SUBMIT/SUBMIT_RESP`分别采集,内部`submit-result`聚合回调不重复落协议日志;Gateway补充供应商`SUBMIT``SUBMIT_RESP``DELIVER_RESP`和企业应用`SUBMIT_RESP`出入方向。运营端方向名称统一为“企业应用→平台、平台→供应商通道、供应商通道→平台、平台→企业应用”,回执成功处理后用真实主消息ID替换临时`receipt-*`标识。
- 新增回归覆盖:同供应商副连接唯一匹配、不同供应商拒绝、两分片未齐不提前送达、全部到齐一次聚合、内部回调不重复记录、入站服务真实SubmitResp遥测及安全白名单。API定向2 suites/90项、API全量26 suites/313项、Gateway全量`go test ./...``go vet ./...`、API/前端build、Prisma generate/validate/status(本地67条migration最新)、`git diff --check`均通过。
- 使用当前构建启动独立本地NestJS API,连接真实PostgreSQL和Redis执行`tools/smoke/receipt-cross-connection-smoke.mjs`:第二片从副连接进入后回执行的逻辑通道为原通道、主记录保持`submitted`;第一片补齐后主记录为`delivered`,恰好2条回执和2个已送达分片,脚本最终清理全部合成业务数据。
- `verify:phase8`首次使用共享Redis且本机已有API争用时为373.05 TPS,低于500阈值,明确记为失败;改用独立临时Redis完整重跑为764.69 TPS并通过,临时Redis随后停止。前端仅保留既有约1.93MB单chunk警告。
- 本次不新增Prisma模型或migration。发布后仍需只读确认服务、67条migration、Redis Stream、5条供应商通道重连、通讯日志新方向和近期错误;未经单独授权不发送真实短信,也不改写`13127620092`历史业务记录。
## 2026-07-24 跨连接回执、长短信聚合与通讯日志闭环发布(`ca12f14b`)
- 功能提交`ca12f14b0007ee75f727d18e66791a669838b66e``fix: reconcile shared-channel receipts and protocol logs`)已推送至`origin/main`,首次推送即成功。提交包含另一会话留下的通讯日志方向/去重改动及本轮回执聚合修复;未提交`api/tsconfig.build.tsbuildinfo``outputs/`
- 部署前备份位于`/opt/cmpp-platform/backups/releases/20260724-125251`PostgreSQL gzip 4426218字节、SHA-256 `0ae8714a29f753e4d313179e2471f6bff15d1d7dc996ef57d7052bb7ddd8a61d`;运行源码tar.gz 28655559字节、SHA-256 `40065d00c72273771abf63b430069332351586b02ac2462345eca045997a838c`;环境文件850字节、SHA-256 `189f4f67e9d52f3c1ce751d1005c08667b1b4efcd0c576aaa5ad3249c822e4e7`。三份文件权限均为600,gzip、tar和`sha256sum -c`全部通过。
- 精确Git归档大小1678722字节,本机与服务器SHA-256均为`ba9dc5f2f823eea810e2ef89798ae55e90f832e2e50a99d50295064eea4a31f6`,tar完整性通过。运行目录以该归档替换并保留`backups/node_modules/api/node_modules/dist/logs/.deployed-commit`;部署前目录额外保留在`/opt/cmpp-platform.previous-20260724-125251`作为短期恢复副本。
- `tools/deploy/production-deploy.sh`成功完成依赖安装、Prisma generate、migration deploy、前端/API/Gateway构建,并明确先重启Gateway、再重启API。根项目59个包和API 717个包审计均为0漏洞;67条migration全部齐全,无待应用migration`.deployed-commit=ca12f14b0007ee75f727d18e66791a669838b66e`
- Gateway、API、Nginx、PostgreSQL、Redis和MinIO均active`12026/17890/8090/3000/6379/5432/9000`监听,API/Gateway health为okRedis PONG。`gateway.submit.commands`消费者1、`pending=0``lag=0`10个通道TPS配置键存在。
- Gateway重启后5条活动供应商通道有4条立即在线,“富泷物业-联通”首次鉴权失败并按数据库`nextReconnectAt=2026-07-24 12:59:31+08`自动慢重试;到13:00只读复核时5/5均为`connected/currentConnections=1/desiredConnections=1`,最近心跳持续刷新、`nextReconnectAt``lastError`清空。
- 公网首页、运营登录、客户端登录和API health均HTTP 200,公网CMPP 17890 TCP连接成功。真实浏览器加载运营端登录页,标题正确、页面无横向溢出、控制台0条业务error/warn;因图形验证码保护,本轮未绕过登录,登录后通讯日志页面仍需下一次人工登录结合真实短信复测。
- 发布后API/Gateway日志未出现panic、fatal、unhandled、exception、通讯遥测失败或`SMS message record not found`。本次未发送真实短信、未修改或回填`13127620092`历史业务数据;跨连接真实供应商回执和长短信两片最终聚合仍需下一次授权测试短信或自然业务回执验证。
## 2026-07-24 运营端账户充值回执(本地未提交、未部署)
- 运营端充值记录每行新增“查看回执”操作,弹窗直接使用真实`RechargeOrder`、企业信息及订单关联的`balanceAfterCents`,据此计算入账前余额;历史记录缺少可追溯余额时显示`-`,不使用当前账户余额或前端假数据补齐。
- 回执左上只展示系统现有`/logo/logo1.png`真实Logo;展示入账状态、本次金额、企业名称和编码、订单号、入账时间、前后余额、入账方式与备注,适合客户截图留存。
- “本次充值金额”采用实际精度:整数不显示小数,存在小数时移除末尾无效零;前后余额继续显示平台统一四位精度。正数显示“已入账”,负数冲正显示“已冲正”。
- 金额边界验证结果:`10000.0000 → 10,000``10000.2500 → 10,000.25``10000.0001 → 10,000.0001`、负数冲正`-123.4500 → 123.45`,符合主金额按实际精度展示口径。
- 使用Node.js v24.14.0执行前端TypeScript和Vite生产构建通过,保留既有约1.93MB单chunk/579.51KB gzip警告;`git diff --check`通过。首次由系统旧Node执行时Vite不支持`??=`且错误返回0,已明确排除,未将其计为通过。
- 浏览器加载真实本地前端/API后进入运营登录页,页面标题正确、控制台0条error/warn;由于当前浏览器无有效会话且存在图形验证码,本轮未绕过验证码,登录后的“查看回执”点击与视觉验收仍需人工登录后补测。
- 本轮按要求保持未提交、未推送、未部署;预发布仍运行既有版本,不包含充值回执功能。
## 2026-07-24 长短信失败终态、自动双通道投递与企业侧通讯日志(发布前)
- 预发布只读复核号码`18821203795`的消息`MSG-e4ded553-8f08-4f7d-85ae-06a30b163e86`:主记录保存首片上游消息号`736078096474128384`,第二片`736078096490905600`收到`undelivered/YL:1014`,首片无回执。原逻辑只按主记录首片消息号查提交记录,导致第二片虽写入分片审计,但被误判为非当前尝试,主记录卡在`submitted`且未建立最终下游投递。
- 修复为优先通过`SmsMessageSegmentAudit.gatewayMessageId`取得该分片所属`submitRecordId/submitId/channelId`,再执行当前尝试判断。长短信任一分片明确失败即可沿既有补发、退款和最终回执链路使整条短信终态化,不等待缺失分片;供应商原始非成功码(包括`YL:1014`)保持原样,不增加码表。
- HTTP和CMPP投递改为按接口能力自动派生:CMPP开通即建CMPP下游投递,HTTP开通且对应Webhook地址有效即建HTTP事件,两者同时开通时双投。运营端不再提供回执/上行投递方式选择,HTTP关闭时仍显示并允许维护两个Webhook地址,保存空地址会删除对应端点并停止该类HTTP推送。
- Gateway补充企业侧真实`CMPP_DELIVER`发送成功/失败以及`CMPP_DELIVER_RESP`接收结果通讯日志,API白名单允许`platform_to_client + deliver_receipt/deliver_uplink``client_to_platform + deliver_resp`。通讯日志只描述真实协议报文;`CmppDownstreamDelivery`继续保存排队、发送、ACK、失败和重试业务状态,两者不合并。
- 新增migration`20260724143000_derive_application_delivery_modes`,按当前CMPP/HTTP开通状态回填历史配置的派生模式,避免旧人工模式继续影响展示或参数复制。
- 已完成定向回归:OpenAPI、短信配置和Gateway事件3 suites/67项通过;SendChain新增长短信非首片失败、仅HTTP投递和CMPP关闭3项通过;Gateway inbound全量通过并覆盖企业侧DELIVER/DELIVER_RESP日志。另一个会话的运营端账户充值回执源码和文档已一并纳入本发布分支,原工作区未覆盖。
## 2026-07-24 通道今日质量、看板签名统计与日期通道占比(本地未提交、未部署)
- 根因确认:运营端通道列表的`mapApiChannel`把今日总数、成功/未知/失败数量和比例全部固定为0,页面从未请求后端质量数据;预发布只读SQL确认当天实际已有3个通道共10条accepted提交,并非数据库无数据。
- 新增只读`GET /api/admin/operations/send-quality?date=YYYY-MM-DD`,按北京时间单日从真实`SmsSubmitRecord/SmsReceiptRecord`聚合通道提交质量,并从真实`SmsMessageRecord/SmsSignature/Tenant`聚合签名发送质量;日期无效返回受控400。
- 通道列表接入当天真实质量;运营看板移除通道运行表格、在线连接指标和平台连接健康度,恢复设计基线`2f3c274a`中的“不含引流/含引流”双签名统计结构,展示发送总数、成功、未知、失败、成功率和平均到达时长。
- 数据统计的通道占比默认北京时间当天,支持选择单个历史日期查询,图例使用真实通道名称而非内部ID。
- 预发布只读核验当天事实:富泷物业-移动5条、赛邮行业-王斯评中转4条、富泷物业-联通1条;另有4个签名存在当天发送记录,证明原页面全0为前端硬编码缺陷。
- Node.js v24.14.0下API Operations定向1 suite / 21项通过;整合远端最新回执链路后,API全量26 suites / 319项、API TypeScript build、前端TypeScript/Vite build、Gateway `go test ./...``go vet ./...`、Prisma generate/validate及`git diff --check`通过;前端保留既有约1.93MB单chunk/580.26KB gzip警告,Jest仍用`--forceExit`结束既有异步句柄。
- 本机PostgreSQL未监听,真实服务类本地集成因`ECONNREFUSED`未通过;应用内浏览器中的预发布管理员会话已安全锁定,未输入密码或绕过认证,因此未将修复后登录页面交互记为通过。新接口尚未部署,预发布只读SQL仅用于证明真实数据和根因。
- 本轮按要求不提交、不推送、不部署;工作区中另一会话的充值回执改动继续原样保留。
## 2026-07-24 通道今日质量、看板签名统计与日期通道占比发布(`e12bdf01`)
- 功能提交`e12bdf010e52584288effc626dc3b84a82dbc15f``fix: restore daily operations quality metrics`)已推送至`origin/main`。首次推送遇到内部Git服务瞬时`Failed to authenticate user`,原命令重试后成功;提交未包含`api/tsconfig.build.tsbuildinfo``outputs/`
- 发布前门禁通过:API全量26 suites / 319 tests、API TypeScript build、前端TypeScript/Vite build、Gateway `go test ./...``go vet ./...`、Prisma generate/validate及`git diff --check`。本机API审计因旧npm内部错误未取得有效结果;预发布部署机随后对根项目59个包和API 717个包完成审计,均为0漏洞。
- 部署前PostgreSQL、运行源码和环境配置备份至`/opt/cmpp-platform/backups/releases/20260724-181542-before-e12bdf01`。数据库备份SHA-256为`9d8c7e1699741d2e952598635b3d512a80a192443744be7a9cb86a3d9727d842`,源码备份SHA-256为`e2768949c92f568f36e487fd32159693895a0e77ec7f98203c971445112e5cce`,环境文件SHA-256为`189f4f67e9d52f3c1ce751d1005c08667b1b4efcd0c576aaa5ad3249c822e4e7`;权限、gzip、tar和`sha256sum -c`完整性校验通过。
- 发布包由`e12bdf01`精确Git快照生成,共567个条目、1690738字节,本机与服务器SHA-256均为`f3501053df9d6eec47f10059138dbb1de4e6c1d0badaa5af80ee3986e4265a6c`。精确替换受版本控制源码时保留备份、依赖、构建产物和日志目录,随后使用`tools/deploy/production-deploy.sh`完成依赖安装、Prisma generate/migrate、前端/API/Gateway构建及服务重启;68条migration齐全且无待执行项,最终`.deployed-commit=e12bdf010e52584288effc626dc3b84a82dbc15f`
- 发布后Gateway、API、Nginx、PostgreSQL、Redis和MinIO均active`12026/17890/3000/6379/5432/9000`监听,API health正常、Redis PONG。`gateway.submit.commands`消费者1、`pending=0``lag=0`3条active供应商通道均为`connected/currentConnections=1/desiredConnections=1`
- 新路由未登录访问返回受控401,证明路由已加载且认证保护有效;直接调用预发布已部署`OperationsService`并查询真实PostgreSQL返回日期`2026-07-24`、3行通道和4行签名统计。通道总量为10:富泷物业-移动5条(成功0、未知3、失败2),赛邮行业-王斯评中转4条(成功1、未知3、失败0),富泷物业-联通1条(成功1、未知0、失败0),修复后不再是前端硬编码全0。
- 公网首页、运营登录页、客户端登录页和API health均返回HTTP 200,公网CMPP 17890 TCP连接成功。发布以来API、Gateway和Nginx无warning及以上日志。服务器留存的旧管理员凭据已与当前账户密码不一致,接口验收首次登录返回一次401后停止重试,未触发锁定;本轮未绕过认证,登录后页面交互仍需持有当前密码的人工会话复核,未虚报通过。
## 2026-07-24 提交失败与送达失败口径拆分(本地未提交、未部署)
- 预发布只读核对确认三条上游结果均为`rejected / 103`:企业CMPP正式短信`18821203795`由平台生成`PLATFORM:MSG-* / REJECTD`失败回执并已完成企业侧投递;运营端通道测试`13127620092``15821447161`没有企业和应用归属,无需生成客户回执。三者业务结果统一归为“提交失败”,是否存在平台通知回执不再改变列表分类。
- 短信记录列表、CSV、详情和状态筛选按阶段拆分:`submit_failed``submitStatus=rejected/timeout`显示“提交失败”;只有供应商已受理且最终失败才显示“送达失败”。详情根据真实平台失败回执和`CmppDownstreamDelivery`状态显示“平台已生成失败回执并通知企业”,独立通道测试显示“运营端通道测试,无需生成客户回执”。
- 通道当天质量纳入`accepted/rejected/timeout`终态提交,“今日提交”包含全部实际提交结果,并新增“提交失败”;送达成功、回执未知、送达失败只以供应商已受理数为分母。分片回执优先按`SmsMessageSegmentAudit.submitRecordId`聚合,任一分片明确失败即归为送达失败,避免非首片失败被误算为未知。
- 通道页“查询”按钮绑定`loadChannels()`。风险复核确认该函数只调用通道列表、当天质量和连接状态三个GET接口,服务端均为只读`findMany`/聚合,不会连接、断开、重连通道,不会修改配置或写操作日志;不增加自动轮询。
- 新SQL在预发布真实库只读执行成功:会员营销-铁布衫为今日提交3、提交失败3、已受理0;富泷物业-移动为已受理5、送达成功0、回执未知2、送达失败3;富泷物业-联通为已受理1、送达成功0、回执未知1;赛邮行业-王斯评中转为已受理4、送达成功1、回执未知3。
- Node.js v24.14.0下API Operations定向1 suite / 22项、API全量26 suites / 320项、API TypeScript build、前端TypeScript/Vite build通过;全量测试仅出现既有Redis不可用容错告警并以`--forceExit`结束既有异步句柄。前端保留既有约1.93MB单chunk/580.73KB gzip告警。系统默认Node 14执行Jest/Vite时因不支持当前依赖语法却错误返回0,已排除该结果并使用工作区Node 24直接调用本地Jest/TypeScript/Vite可执行文件重跑。
- 本轮按用户要求仅修改代码并验证,未提交、未推送、未部署;`api/tsconfig.build.tsbuildinfo``outputs/`继续作为既有其他会话/构建产物保留。
## 2026-07-24 运营看板签名分页与单日企业应用统计(发布前)
- 运营看板“不含引流”和“含引流”两个今日签名发送统计区改为各占整行;每页展示10个签名,分别维护页码,并支持首页、末页、上一页、下一页和指定页跳转。跨页排名按完整结果集连续计算,不会在每页重新从1开始。
- 数据统计统一使用日期选择器指定的北京时间单日数据,默认当天;发送量、成功率、企业应用排行和通道占比均来自同一次只读`send-quality`后端快照。指标标题展示实际已加载日期,修改日期但尚未点击查询时不会把旧数据误标成新日期。
- 数据统计移除“待审核”。发送量和成功率从所选日期全部有效`SmsMessageRecord`聚合;企业排行改为企业应用维度,后端联表返回真实应用名称和所属企业名称,不再把企业ID或应用编号作为图表名称,也不混入历史累计记录。
- 后端新增单日汇总及企业应用统计回归数据,继续排除平台预校验`rejected`记录;已送达优先于失败判定,`submit_failed/failed/timeout`或失败回执计入失败,其余计入未知,满足`发送总数 = 成功 + 未知 + 失败`
- 发布前门禁:Node.js v24.14.0下API Operations定向1 suite / 22项、API全量26 suites / 320项、API TypeScript build、前端TypeScript/Vite生产构建、Prisma generate/validate、Gateway `go test ./...``go vet ./...``git diff --check`全部通过。Jest保留既有Redis不可用容错告警和`--forceExit`异步句柄提示;前端保留既有约1.93MB单chunk/580.79KB gzip告警。浏览器验收、提交、推送与预发布部署结果待完成后补记。
## 2026-07-24 提交失败口径、签名分页与单日企业应用统计发布(`53736c96`)
- 功能提交`53736c96e817d3310039bcf4cf1d17dfd775c9a4``fix: align daily operations statistics`)已推送至`origin/main`。第一次推送仍被内部Git服务以`Failed to authenticate user`拒绝,保持提交和工作区不变后直接重试成功;`api/tsconfig.build.tsbuildinfo``outputs/`未纳入提交。
- 部署前PostgreSQL、运行源码和环境配置备份至`/opt/cmpp-platform/backups/releases/20260724-220938-before-53736c96`。数据库、源码、环境文件SHA-256依次为`e48700dee4dfbf4b1e9f66f8eb6e5b9ede8698d6aeedd01d87e5638e35e9747e``eca2a1a33dccc6d5bac9cee5bda55a8bc342845831e8609067e4ee6462784000``189f4f67e9d52f3c1ce751d1005c08667b1b4efcd0c576aaa5ad3249c822e4e7`;权限、gzip、tar和`sha256sum -c`完整性校验通过。
- 发布包由`53736c96`精确Git快照生成,共567个条目、1695144字节,本机与服务器SHA-256均为`9148c94478dfdff0f1c3dfa0489d98ee98c20738897d9559611901fe99ef77d4`。旧运行目录保留在`/opt/cmpp-platform.previous-20260724-221057`,随后使用标准`tools/deploy/production-deploy.sh`完成安装、Prisma、三套构建、Gateway先于API重启和健康检查;68条migration齐全且无待执行项,`.deployed-commit=53736c96e817d3310039bcf4cf1d17dfd775c9a4`
- 标准部署脚本实测34秒,其中根项目`npm ci`约3秒、API`npm ci`约6秒;其余约25秒用于Prisma生成/迁移检查、前端/API/Gateway构建、管理员检查、systemd重启和固定3秒健康等待。当前每次发布都执行两次干净依赖安装、三套完整构建、备份/校验及发布后验收,因此用户感知的总耗时显著高于34秒的服务端脚本本身。
- 发布后Gateway、API、Nginx、PostgreSQL和MinIO均activeRedis PONG`12026/17890/8090/3000/6379/5432/9000`均监听,API/Gateway health正常。`gateway.submit.commands`消费者1、`pending=0``lag=0`3条active供应商通道均为`connected/currentConnections=1/desiredConnections=1`API、Gateway和Nginx自发布以来关键错误匹配为0。
- 直接调用已部署的真实`OperationsService`及PostgreSQL验证新SQL`2026-07-24`为总量13、成功2、未知6、失败5、成功率15.4%,返回4个真实企业应用名称、4个通道和5个签名;切换`2026-07-23`为总量8、成功3、未知1、失败4、成功率37.5%,返回2个企业应用、3个通道和1个签名,证明汇总、排行、通道和签名均随所选日期切换。
- 公网首页、运营登录页、客户端登录页和API health均HTTP 200,公网CMPP 17890 TCP连接成功。浏览器运营登录页标题正确、1280px视口无横向溢出、控制台0条error/warn;当前无已登录会话且页面存在图形验证码,未绕过验证码,因此登录后两张全宽签名表和统计图的最终视觉验收保留为人工登录复核项,不将源码/构建结果冒充登录后页面验收。
## 2026-07-25 上下游回执可靠性与逐次投递审计(发布前)
- 下游增加物理连接级 SubmitResp 写出屏障:从客户 Submit 开始处理到对应响应包真正写出前,同一连接产生的及时失败回执保持 pending;长短信在最终分片 SubmitResp 写出后立即补投,避免客户先收到 Deliver、后收到最后一片 SubmitResp。连接关闭会清理屏障,防止异常连接残留。
- 每次下游 Deliver 单独写入 `CmppDownstreamDeliveryAttempt`,保存投递次数、连接 ID、`Sequence_Id`、业务 `Msg_Id`、发送/ACK 时间、ACK Result、失败类别和错误;主记录继续承载当前状态和退避调度。运营端下游投递详情新增逐次投递记录,便于区分“平台写出、客户 ACK、ACK 超时和重投”。
- 上游长短信改为每片 `SubmitResp` 到达后立即回传 API 并写 `SmsMessageSegmentAudit`,最终聚合结果只作兜底;若早到供应商失败回执已经形成终态,后到 accepted 聚合不得把主记录倒退到 submitted,并对先扣后退场景保持幂等补偿。
- 供应商 Deliver Receipt 改为 API `UpstreamReceiptInbox` 幂等持久化成功后才返回 `CMPP_DELIVER_RESP Result=0`。异步工作器基于保存的通道端点身份匹配短信,失败指数退避,默认最多 30 次/72 小时;API 进程在 processing 中重启时,默认 2 分钟后可重新认领,不依赖单个 Gateway 连接的内存映射。
- 通讯日志覆盖供应商 Submit/SubmitResp、Deliver Receipt/DeliverResp、客户 Submit/SubmitResp、平台 Deliver/客户 DeliverResp;无法匹配当前 ACK tracker 的客户 `DELIVER_RESP` 也记录为失败通讯事件。内部逐分片 HTTP 回调失败另写 Gateway 结构化本地日志,不把内部回调伪装成 CMPP 报文。
- 新增 migration `20260725160000_add_reliable_receipt_delivery_tracking`,仅新增下游逐次投递表、上游回执收件箱及索引/外键,不改写既有短信、提交、回执或投递历史。回滚必须先停止新版本 API/Gateway,再删除两张新表;回滚会丢失新版本产生的逐次投递和待匹配收件箱证据。
- 发布前门禁通过:Prisma format/generate/validateAPI 定向 3 suites / 119 testsAPI 全量 26 suites / 325 testsAPI TypeScript build,前端 TypeScript/Vite生产构建及 Gateway `go test ./...`。API 全量仅保留既有 Redis 不可用容错告警和 `--forceExit` 异步句柄提示;前端保留既有约 1.94 MB 单 chunk 警告。`git diff --check`通过。
- 本节当前为发布前记录;提交、推送、数据库/源码/环境备份、migration、服务重启和预发布只读验收结果在发布完成后追加。未经额外授权不发送真实短信,不改写历史业务记录。
## 2026-07-26 npm 依赖安全整改(发布前)
- 根项目将 PostCSS 从 `8.5.15` 固定到已修复的 `8.5.23`,对应任意 `.map` 文件路径穿越公告已从审计中消失;平台没有接收用户 CSS 并交给 PostCSS 的运行时路径。
- React Router 保持并精确固定当前最新版 `7.18.1`。实测按审计建议降到 `7.11.0` 会重新引入多条 XSS、开放重定向、反序列化和 DoS 公告,因此未采用。当前唯一剩余 high 为实验性 RSC 模式 CSRF,项目实际使用 `HashRouter` 客户端 SPA;新增 `security:verify` 门禁递归扫描前端源码,禁止引入 RSC 服务端 API。
- API 将 ExcelJS 间接使用的 `brace-expansion` 替换为官方长度受限的 `5.0.8`,同时增加本地 CommonJS 兼容适配层,使旧版 minimatch 的函数调用方式继续有效。直接覆盖 5.0.8 曾被兼容性测试明确拦截(`expand is not a function`),未把审计清零冒充功能可用。
- 独立 Linux 临时目录从锁文件执行两次全新 `npm ci --ignore-scripts` 成功:根项目 59 个包、API 726 个包;旧版 minimatch 三条真实依赖链花括号匹配和长度上限均通过。整改后根项目审计只剩同一条不适用 RSC 公告的 2 个依赖节点,API high 从 26 降为 0,仅剩 Prisma CLI→Valibot 的 3 个 moderate;该工具链不处理 API 请求且上游暂无修复版本,不降级 Prisma 7。
- 功能门禁通过:报备 Excel 专项 1 suite / 7 testsAPI 全量 26 suites / 325 testsAPI TypeScript build,前端 TypeScript/Vite生产构建,Gateway `go test ./...``go vet ./...`,以及依赖安全门禁。标准部署脚本也会在干净安装后、Prisma migration 前强制执行该门禁,失败即停止发布。前端保留既有约 1.94 MB 单 chunk 提示;API 测试保留既有 Redis 容错告警和 `--forceExit` 异步句柄提示。
- Windows 本地完整 `npm ci` 因另一进程占用两个原生 `.node` 文件而无法清理旧目录,未结束未知会话进程;随后非破坏性 `npm install` 修复本地依赖。锁文件可重建性以隔离 Linux 干净安装结果为准。提交、推送和预发布部署结果待发布后补记。
## 2026-07-26 用户登录标识复用、组合查询与管理员提示(本地未提交、未部署)
- 用户逻辑删除仍保留原记录、主键、角色关联及历史审计;用户名、邮箱和手机号改为仅对`deletedAt IS NULL`记录唯一。新增migration删除原全表唯一索引并创建三个PostgreSQL部分唯一索引,允许新用户以新主键复用已删除用户的登录标识,不继承旧账号身份或权限;登录和用户名查询显式排除已删除记录。
- 运营端用户管理将用户姓名、登录账号、所属企业、用户角色和状态拆分;客户端拆分用户姓名、登录账号和状态。查询/重置调用真实用户列表API,后端按独立条件组合PostgreSQL查询,客户端条件由当前会话企业强制限域并固定企业管理员角色,不再下载全量用户后只在浏览器过滤。
- 新增/编辑用户的校验和重复登录标识错误改在当前表单弹窗内显示。删除、禁用或降权最后一个管理员继续由后端实时计数拦截;运营端和客户端确认弹窗捕获HTTP错误、保持打开、显示可访问红色提示和“先创建或启用另一名管理员”建议,请求期间禁用按钮,不再产生未处理Promise。操作成功后才关闭弹窗,再按已应用筛选条件刷新列表;刷新失败不会误报为删除失败。
- Prisma validate/generate通过;用户服务定向1 suite / 15 tests、API全量26 suites / 328 tests、API TypeScript build和前端TypeScript/Vite生产构建通过。全量测试只保留既有Redis不可用容错告警和`--forceExit`异步句柄提示,前端保留既有约1.94MB单chunk警告。
- 本地静态预览访问运营端、客户端用户管理路由时均由真实认证守卫重定向至对应图形验证码登录页;页面标题、登录表单、无框架覆盖和控制台0条error/warn通过。没有绕过验证码,因此分离筛选和管理员拦截弹窗的登录后可见交互仍保留为人工登录复核项。
- 本轮按用户要求保持未提交、未推送、未部署;未对预发布数据库执行migration或写入测试用户。`api/tsconfig.build.tsbuildinfo``outputs/`及未跟踪空文件`=`保持隔离,未纳入本次修改。
## 2026-07-26 企业应用停用回执清算与下游断连(本地未提交、未部署)
- 企业应用新增`disablingAt/autoDisableAt/disableReason`持久化字段和`disabling`状态;停用前统计等待供应商回执、等待推送、等待客户ACK、可重试失败、待推送上行和在线连接。
- 无待清算数据直接停用;有数据时运营端弹窗可选择“等待回执后停用”或“强制停用并断开连接”。停用中状态支持悬停/聚焦查看原因和数量,并可点击启用恢复。
- 停用中应用立即拒绝新Submit但允许回执清算连接;清算完成自动停用,进入停用中满72小时仍未完成时自动放弃剩余投递、标记`abandoned`并断开该账号全部下游CMPP连接。
- Gateway新增按客户账号关闭全部下游会话的控制接口;历史pending回执读取移除企业/应用当前启用状态限制,修复企业删除后回执已生成但Gateway持续收到400的投递死锁。
- 企业删除增加`active/disabling`应用拦截。应用停用后才到达的供应商回执继续更新真实短信终态,但下游投递直接留痕为`abandoned`且不再重试。
- 已通过Prisma format/generate/validate、API定向3 suites / 155 tests、API全量26 suites / 333 tests、API TypeScript build、前端TypeScript及Vite生产构建、Gateway `go vet ./...``go test ./...`。API全量仅保留既有Redis不可用容错告警和`--forceExit`异步句柄提示;前端保留既有约1.94MB单chunk提示。
- 应用内浏览器访问本地生产预览的企业应用管理路由,被真实认证守卫引导到运营登录页;页面标题和登录表单正常、无框架错误覆盖、控制台0条error/warn。当前没有已登录会话且存在图形验证码,未绕过认证,因此停用选择弹窗、停用中悬停详情和恢复启用的登录后视觉交互仍需持有有效会话后复核。
- 本轮按要求不提交、不推送、不部署;工作区原有用户管理、依赖安全整改、构建产物及`outputs/`等其他会话修改保持原样。
## 2026-07-26 风控规则与短信人工审核整改(发布前)
- 风控规则收敛为单任务号码上限、可自定义非工作时间营销批量、10分钟客户端任务频控三项;新增全局/企业应用级规则页和真实后端编辑接口。应用级规则稳定覆盖全局,客户端频控直接统计同应用`sourceType=client`批次,排除CMPP、HTTP、通道测试和预检。
- 删除企业应用`maxPhonesPerTask`字段和表单配置,不迁移测试应用旧值;重复号码、非法号码比例、黑名单比例和模板变量规则转`deleted`保留历史审计但不再生效。模板变量缺失/多传继续作为确定性提交拒绝。
- 手机号码基础校验放宽为`^1\d{10}$`,不依赖号段更新。客户端/HTTP混合批次逐号码把非法号码、平台黑名单和企业应用黑名单记为`submit_failed/rejected`且金额0,合法号码照常冻结、入队;CMPP混合多目的提交对被拦截号码生成平台`REJECTD`失败回执,合法号码继续处理。
- 短信审核页只返回待人工审核及人工处理记录,自动放行/拒绝不再混入;号码数量恢复设计基线的查看入口,真实接口仅返回手机号、归属地、运营商和短信状态并支持服务端搜索/分页。
- 新待审核短信同步保存`reviewTaskId`,审核决定同时按短信直连和批次`riskTaskId`查找,修复审核任务已通过而短信仍`pending_review`。遵照要求不改写现存历史异常数据、不补发历史短信。
- 用户登录标识改为“仅未删除记录唯一”后,部署管理员初始化与真实环境冒烟脚本同步从`username`唯一`upsert`改为先查询未删除用户、再按主键更新或创建,避免迁移后Prisma拒绝旧的唯一查询。
- 发布前门禁阶段结果:Prisma format/generate/validate通过;风险审核+发送链定向2 suites / 108 tests通过,随后补充应用覆盖、号码分页与逐号码拦截用例;API全量26 suites / 338 tests、API TypeScript build、前端TypeScript/Vite生产构建、Gateway `go test ./...`/`go vet ./...`、依赖安全门禁通过。API保留既有Redis不可用容错告警与`--forceExit`提示,前端保留约1.95MB单chunk/584.70KB gzip提示。
## 2026-07-26 发送批次号与任务号命名统一(本地未提交、未部署)
- 运营端短信任务进度、客户端批量任务、客户端首页及发送成功提示统一把`SmsBatchTask.taskNo`展示为“发送批次号”;同步修改查询标签、占位提示、详情标题和终止失败提示。
- 短信审核列表新增“审核任务号”列,筛选、详情和号码明细标题统一展示`SmsSendTask.taskNo`;不新增接口请求或浏览器派生数据。
- 报备任务和报备记录的列表、筛选及详情统一使用“报备任务号”。本次不修改数据库字段、编号格式、关联关系或协议消息ID。
- Node.js v24.14.0下前端TypeScript检查、Vite生产构建和`git diff --check`通过;构建保留既有约1.95MB单chunk/584.75KB gzip提示。应用内浏览器访问本地短信任务进度路由时,未登录会话由真实鉴权守卫引导到运营登录页,标题、表单交互和控制台0条error/warn通过;图形验证码阻止登录后页面验收,未绕过认证或把登录页冒充目标页面。
- 本轮按要求保持本地未提交、未推送、未部署;`api/tsconfig.build.tsbuildinfo``tsconfig.tsbuildinfo``outputs/`及未跟踪空文件`=`继续作为既有构建/临时产物隔离。
## 2026-07-26 企业删除拦截提示修复(本地未提交、未部署)
- 后端原本已在企业仍有`active/disabling`应用时返回具体数量和“先完成应用停用”提示;运营端此前仅把失败写入弹窗背后的页面级错误,用户几乎不可见。
- 企业删除/状态变更确认弹窗新增独立错误和请求中状态:失败保持弹窗并就地显示原因;请求期间禁用确认、取消及关闭;成功后才关闭并刷新列表。实现复用用户管理最后管理员拦截的交互口径。
- Node.js v24.14.0下前端TypeScript检查、Vite生产构建和`git diff --check`通过,保留既有约1.95MB单chunk/584.92KB gzip提示。应用内浏览器访问本地企业管理路由时由真实鉴权守卫引导到图形验证码登录页,页面完整、输入交互正常且控制台0条error/warn;未绕过认证,登录后的拦截弹窗仍需有效运营会话补充可见验收。
- 本节仅记录本地修改;未提交、未推送、未部署。
## 2026-07-26 运营页面细节与通道重连收敛(本地未提交、未部署)
- 企业签名及引流信息报备状态将运营商映射为中文,并使用目标通道真实名称替代内部通道编号;展示忠实反映实际通道关联,不根据签名名称猜测运营商。
- 短信上行内容列扩大到360px并最多展示三行,表格最小宽度同步增加;短信记录首次加载及重置默认查询北京时间昨天和今天。
- 下游投递详情把逐次投递宽表改为纵向时间线卡片,中文展示等待确认、已确认、拒绝和失败状态,并分组展示发送/ACK/截止时间、连接、Sequence_Id、Msg_Id、ACK Result和错误。
- Gateway提交异常列表新增带间距和分隔的标题区、结果总数及分页分隔,解决标题和边框紧贴的问题。
- 通道更新改为比较修改前后实际连接参数;仅网关地址、端口、账号/密码、CMPP版本、连接数、窗口和心跳参数变化时请求重连。名称、价格、运营商、地区、服务号、扩展位及TPS等非连接参数不触发连接控制,状态启停逻辑不变。
- 按最新要求取消日报T-4未知转失败,本轮未修改报表重算、未知口径或历史数据。
- Node.js v24.14.0下通道服务定向1 suite / 40 tests、API TypeScript build、前端TypeScript检查、Vite生产构建及`git diff --check`通过。Jest仅保留既有Redis不可用容错告警和`--forceExit`异步句柄提示,前端保留既有约1.95MB单chunk/585.35KB gzip提示。
- 应用内浏览器分别以默认桌面和390×844视口访问本地短信记录路由,真实认证守卫均引导至运营登录页;页面非空、无框架错误覆盖、控制台0条error/warn,输入框交互正常,移动端`scrollWidth=clientWidth=390`。未绕过图形验证码,因此六个登录后目标页面的最终视觉和真实数据验收仍需有效运营会话复核。
- 本轮按要求保持未提交、未推送、未部署;工作区原有用户管理、编号命名、依赖安全整改、构建产物及临时文件保持原样。
## 2026-07-26 通道补发归因与发送详情修复(发布前)
- 预生产只读核对`13901860234 / 17:53:46`:首轮富泷物业-移动两个分片均提交成功后返回`FLBLACK`,随后会员营销-铁布衫补发并返回`WL:FSNM`。详情把两次尝试都显示为铁布衫,是因为接口未返回提交/回执关联通道且前端使用短信主记录最终通道兜底;后续仅显示一个通道,是直接签名短信没有模板时补发签名解析失败并被空`catch`吞掉,与18:01通道顺序调整只是时间相关而非真实原因。
- 直接签名和模板短信统一从短信记录解析真实`signatureId`;补发开始、跳过、选中和失败均输出结构化日志,包含消息、通道组、尝试通道、限制时间和异常原因。
- Gateway聚合及逐分片提交结果携带原始`submitId`,API按提交记录主键精确更新。旧Gateway缺少`submitId`时只允许严格唯一候选兼容;歧义结果拒绝并记录,不再按短信批量覆盖多个尝试。迟到旧尝试结果只更新对应提交审计,不倒写当前短信尝试。
- 运营短信接口返回提交及回执关联通道;详情优先按分片审计的`submitId`重建逐次通道、发送时间、提交状态和分片回执,因此既有17:53记录可还原为富泷到铁布衫两次真实尝试。
- 通道组更新新增修改前后有序成员操作日志,包含通道编号、名称、运营商、地区、优先级、权重和主备,可审计顺序变化。
- 定向验证已通过发送链1 suite / 98 tests(含直接签名备用通道、聚合及分片歧义拒绝)、通道和运营接口专项以及Gateway消息透传专项。最终发布门禁通过:API全量26 suites / 343 tests、API TypeScript build、Prisma format/validate、前端TypeScript/Vite生产构建、Gateway `go test ./...`/`go vet ./...`、依赖安全门禁及`git diff --check`;前端仅保留既有约1.95MB单chunk提示。
- 功能及工作区既有UI修改提交`0857de09d82aec7233108fb6700c40a6183c21f8``fix: harden channel retry attribution and operations UI`)已推送至`origin/main`;首次推送被内部Git服务瞬时返回`Failed to authenticate user`,保持提交不变后重试成功。构建缓存`api/tsconfig.build.tsbuildinfo``tsconfig.tsbuildinfo`及临时`outputs/`、空文件`=`未提交。
- 部署前PostgreSQL、运行源码和环境配置备份至`/opt/cmpp-platform/backups/releases/20260726-213641-before-0857de09`SHA-256依次为`a9ec6f5c96f1dcd2f314e4c63e76b1d348f8320f8852667be5c2f7df933488c9``01140043ee1c640faa31b870b333771d9c57a6295ffdf0b54637db93321858dd``189f4f67e9d52f3c1ce751d1005c08667b1b4efcd0c576aaa5ad3249c822e4e7`gzip、tar和`sha256sum -c`均通过。精确Git归档438个文件、1736410字节,本机与服务器SHA-256均为`2d6be9044fa2c7070e7af7bb077c324f4e4eb99e03001be390f58875350ddafd`;旧运行目录保留在`/opt/cmpp-platform.previous-20260726-213641`
- 标准部署脚本完成两套干净依赖安装、安全门禁、Prisma生成/迁移检查、前端/API/Gateway构建和服务重启;72条migration齐全且无待执行项,`.deployed-commit=0857de09d82aec7233108fb6700c40a6183c21f8`。生产依赖审计仍报告前端2个high(未使用的React Router RSC路径)和API 3个moderatePrisma CLI工具链),自动安全门禁确认既定缓解继续有效,未执行破坏兼容性的`audit fix --force`
- 发布后API、Gateway、Nginx、PostgreSQL、MinIO均activeRedis`PONG``12026/17890/8090/3000/6379/5432/9000`均监听,内外健康页及运营/客户端入口HTTP 200,公网CMPP 17890可连接;Redis提交流消费者1、`pending=0``lag=0`,发布后API/Gateway无error级日志。
- Gateway重启时供应商曾对“会员营销-富泷”首次登录返回`auth failed`,系统按鉴权失败5分钟慢重试策略等待而未高频重连;21:42:17自动重试成功,最终4个启用通道全部`connected/currentConnections=1/desiredConnections=1`。历史17:53短信数据库只读验证仍为富泷两个`FLBLACK`分片、铁布衫两个`WL:FSNM`分片,证明部署未改写证据且新详情具备正确重建数据。
## 2026-07-26 `MSG-65a47514`长短信并发重复补发P0修复(发布前)
- 预生产只读证据确认`MSG-65a47514-7f30-49af-8113-b76747550199`正文408字、3个计费/协议分片。12:33:33经会员营销-铁布衫提交一次;12:58:17三个`WL:CGMT`失败回执在9毫秒内到达,三个处理线程分别触发整条补发,在约13毫秒内创建三个富泷`submitId`,形成“铁布衫1次+富泷3次”、共4次整条尝试和12个真实Submit分片。所有尝试均为失败回执,没有`DELIVRD`成功证据。
- 并发影响还包括三个已被客户`Result=0`确认的最终失败回执、三条各1053分的退款流水,以及非原子余额覆盖导致的实际多退。通道尝试记录成本为铁布衫993分、富泷3168分;是否形成供应商真实账单需另行对账。本轮不删除历史提交、回执、投递、ACK或账务证据,不自动冲正现有余额。
- 新migration`20260726223000_prevent_duplicate_retry_side_effects``SmsSubmitRecord`增加唯一`retryOfSubmitRecordId`自关联、为`AccountTransaction`增加唯一`idempotencyKey`、为`CmppDownstreamDelivery`增加唯一`dedupeKey`。历史重复下游回执只给每条短信最早的回执设置稳定键,其余历史行原样保留。
- 补发记录、提交会话计数和短信当前尝试改为同一数据库事务创建;相同来源提交记录的并发线程只有一个能取得下一跳,其他线程读取并复用已存在的补发,禁止再次向Gateway发布。缺少可审计来源提交记录时安全停止补发并写结构化错误。
- 企业账户通用余额变更改为企业级PostgreSQL事务锁加原子`increment`;短信扣费、提交成功释放冻结、最终释放和退款使用平台消息号稳定幂等键。三次并发退款专项用例返回同一交易ID,只创建一条流水并只改变一次余额。
- CMPP最终回执使用`receipt:{messageRecordId}`唯一键;HTTP回执事件使用`evt_receipt_{messageRecordId}`稳定事件号并复用事件/端点唯一投递。并发专项用例证明三个完成线程只向Gateway发送一次最终回执;所有抢占、复用和投递去重均写结构化日志。
- 当前专项门禁:发送链、账务和HTTP API共3 suites / 121 tests通过,包含三个长短信失败处理线程并发抢占、三次并发退款及三次最终回执创建。全量API回归26 suites / 346 tests通过,API TypeScript构建检查、Prisma schema校验、前端TypeScript/Vite/生产依赖安全门禁及Gateway Go测试/vet均通过;提交、推送和预生产部署结果待完成后补记。
- 功能提交`e0f6eed0d40e21fb06166e28dd54fefa7d0f09b0`已推送并由精确Git快照发布;快照585个条目、1741824字节,本地与服务器SHA-256均为`99ed2d4b86f91b09a87baca027441aa63bec4b0ccefae3a04be5c63d80878e3f`。部署前数据库备份为`/opt/cmpp-deploy-backups/cmpp-20260726-223344.sql`,旧运行目录保留为`/opt/cmpp-platform.previous-20260726-223344`
- 标准部署脚本成功应用`20260726223000_prevent_duplicate_retry_side_effects`,预发布73条migration齐全;三个目标字段、唯一索引及补发自关联外键均已落库,`.deployed-commit=e0f6eed0d40e21fb06166e28dd54fefa7d0f09b0`。API、Gateway、Nginx、PostgreSQL、MinIO均activeRedis`PONG``12026/17890/8090/3000/6379/5432/9000`监听,内外健康页、运营端和客户端入口HTTP 200,公网CMPP 17890可连接,发布后API/Gateway无error级日志。
- 四个启用通道均为`connected/currentConnections=1/desiredConnections=1`Redis提交流消费者1、`pending=0``lag=0`。事故短信只读复核仍为4条提交尝试、12条供应商回执、3条下游投递和3条`refunded`账务流水,证明发布未删除、重写或自动冲正历史证据;本轮未发送任何真实测试短信。
## 2026-07-27 HTTP环境前端随机ID兼容修复(发布前)
- 预发布运营端删除签名在发起删除预检前调用`crypto.randomUUID()`;公网入口为裸HTTP IP,浏览器非安全上下文中该方法不可用,导致点击后抛出`TypeError`且后端未收到删除请求。
- 新增统一随机ID工具:安全上下文优先使用原生`randomUUID()`HTTP等环境回退到`crypto.getRandomValues()`生成符合版本位和变体位要求的UUID v4;极旧环境无Web Crypto时保留随机字节兼容兜底。
- 删除签名/模板/通道、签名和模板审核通过、人工充值、报备批次生成统一改用兼容工具;企业应用密码随机生成也复用随机字节实现,避免同类问题从其他入口再次出现。
- Node.js v24.14.0下前端TypeScript检查、Vite生产构建及`git diff --check`通过;无`randomUUID()`和无Web Crypto两种模拟环境均生成格式正确的UUID v4,`getRandomValues()`回退同时生成16位十六进制应用密码。构建仅保留既有约1.95MB单chunk提示。
- 此修改已纳入用户授权的本轮统一提交、推送和预生产发布范围;工作区原有构建缓存、`outputs/`及空文件`=`继续作为非代码临时产物隔离。
## 2026-07-27 签名不可见字符校验与通道报备真实统计(发布前)
- 客户端与运营端签名新增/编辑统一禁止普通空格、Unicode空白、控制字符和默认不可见字符;非法键入或粘贴不覆盖已有受控输入,并即时显示错误。NestJS新增、编辑接口复用相同后端校验,防止绕过页面写入非法签名。
- 通道报备详情恢复设计基线的“提交报备时间、报备成功时间、上次发送成功时间、今日发送”列;报备成功时间取真实通过记录,上次成功时间取当前通道与签名/引流范围内最近一条最终成功短信。
- 今日发送统计由PostgreSQL真实提交记录、分片审计和供应商回执聚合,拆分成功、未知、回执失败和提交失败。提交拒绝/超时不混入已接受短信的回执失败率;签名任务汇总签名全部引流范围,具体引流任务严格隔离到自身。
- Node.js v24.14.0定向验证通过:通道与短信配置2 suites / 103 tests;本机临时Redis就绪后API全量26 suites / 353 tests通过,测试结束即停止临时Redis。Prisma schema校验、API TypeScript build、前端TypeScript/Vite生产构建、Gateway Go全量测试/vet和依赖安全门禁通过;前端仅保留既有约1.96MB单chunk提示。
- 预发布PostgreSQL按新口径只读执行聚合成功:当前报备任务范围覆盖512条通道提交尝试,其中北京时间今日324条、提交失败1条、最终成功169条,最近成功时间为`2026-07-27 11:18:39.04 UTC`;查询未写入或改写业务数据。提交、推送及预生产部署结果以本轮交付回执为准。
- 用户明确授权将工作区所有有效代码一并发布;`api/tsconfig.build.tsbuildinfo``tsconfig.tsbuildinfo``outputs/`及空文件`=`属于既有构建/临时产物,不纳入源码提交。
- 功能提交`94aeacd3a20b4c838c59d67ba86ef466ff49ce03`已推送并由精确Git快照发布;快照586个条目、1748983字节,本机与服务器SHA-256均为`563de7dbc2c6b85b5d120f37ba92f16c2933da1c4d5f6c7939d2c41a53ea3595`。首次推送被内部Git服务瞬时返回`Failed to authenticate user`,保持提交不变后重试成功。
- 部署前PostgreSQL和环境配置分别备份为`/opt/cmpp-deploy-backups/cmpp-20260727-205312.sql``/opt/cmpp-deploy-backups/cmpp-env-20260727-205312.env`,旧运行目录保留为`/opt/cmpp-platform.previous-20260727-205312`;标准脚本完成两套依赖安装、安全门禁、Prisma、前端/API/Gateway构建及Gateway先于API重启,73条migration齐全且无待执行项。
- 预发布`.deployed-commit=94aeacd3a20b4c838c59d67ba86ef466ff49ce03`API、Gateway、Nginx、PostgreSQL、MinIO均activeRedis`PONG``12026/17890/8090/3000/6379/5432/9000`监听,内外页面和健康接口HTTP 200CMPP 17890可连接,Redis Stream消费者1、`pending=0``lag=0`,发布后API/Gateway无error级日志。
- 部署后的真实`ChannelsService`查询53个报备任务,4个存在今日真实发送统计,返回成功、未知、回执失败、提交失败和最近成功时间;前端产物包含三项新交付文案。4个启用通道中3个立即恢复`connected 1/1`;“会员营销-富泷”首次鉴权失败后按5分钟慢重试策略于20:59:07自动恢复`connected 1/1`,未人工高频重连。本轮未发送真实测试短信。