15 KiB
CMPP 平台代码质量复评报告 V2
- 报告日期:2026-08-28
- 复评时间:2026-08-28 12:38~12:57(Asia/Shanghai)
- 复评基线:
3af145abe5eb8cae567bb18e29116b4940889258 - 对比基线:
171c7d38e8f17de0dd83570603da316d47c0d015 - 分支状态:
main,本地HEAD与origin/main一致 - 变更规模:58 个文件,新增 1,953 行、删除 487 行
- 复评范围:React/Vite 前端、NestJS API、Prisma/PostgreSQL、Redis、Go Gateway、自动化测试、依赖与仓库门禁、测试环境只读健康探测
- 明确未执行:提交、推送、部署、服务重启、数据库写入、短信发送、压力测试、MinIO 业务对象写入、预生产或生产环境操作
1. 复评结论
本次采用两个相互独立的结论,避免把“代码整改完成”和“环境可发布”混为一谈:
- 代码整改:有条件通过。 上一版的两个 P0 已在源码、自动化测试和结构门禁层面关闭;前端分包、覆盖率门槛、Redis 状态存储、数据库配置 fail closed、mock 解耦等整改有效。
- 发布就绪:待完整验收。 2026-08-28 12:50 在 Clash 虚拟网卡/代理开启状态下探测测试环境时曾连续返回 HTTP 502;关闭 Clash 后使用
curl --noproxy '*'绕过代理直连,12:56 连续 3 次访问/api/health均返回 HTTP 200、status=ok,TCP 12026 端口可达,连接耗时约 10 ms、总响应约 19~28 ms。此前 502 判定属于本地网络代理干扰,不是测试环境运行故障。
当前代码质量综合参考分:82/100,较上一版 58/100 提升 24 分。该分数只表示仓库代码治理水平,不等价于功能验收通过率。
测试环境外部 API 健康探测已经恢复为通过;但在重新核对部署版本、内部服务/队列状态并完成真实后端与登录后浏览器复验前,仍不建议进入预生产发布。
2. 质量评级
| 维度 | 上版 | 本次 | 复评结论 |
|---|---|---|---|
| 业务正确性与并发设计 | B | B+ | API/Gateway 回归通过,核心幂等与队列逻辑保持;未重跑真实 CMPP 全链。 |
| 安全性 | D | B+ | 可信租户上下文和强密码哈希已落地;重点写接口校验仍非全覆盖。 |
| 自动化测试 | C | B- | API 47 套 543 项通过并建立覆盖率阈值;前端测试仍为 0。 |
| 前端性能 | C- | B+ | 路由懒加载和 ECharts 按需加载生效,入口 gzip 降至 107.38 KiB。 |
| 可维护性 | C | C+ | mock 解耦和页面辅助逻辑拆分完成;两个 SendChain Service 仍超大,未建立 ESLint/Prettier。 |
| 数据库工程 | B | B+ | Prisma 校验通过,生产/测试缺少 DATABASE_URL 时 fail closed;本轮未做真实数据执行计划复测。 |
| 依赖与仓库卫生 | C- | B- | 根正式锁文件审计为 0;API 依赖树仍有 7 项公告,且未跟踪的旧 pnpm 锁文件会造成错误审计口径。 |
| 运行与发布就绪 | 未评 | B- | 绕过本地代理后测试环境外部 /api/health 连续 3 次返回 200;本次未独立核对内部服务、队列和登录后业务链。 |
3. 上一版问题关闭状态
| 编号 | 原级别 | 本次状态 | 判定依据 |
|---|---|---|---|
| CQ-SEC-001 客户端租户可伪造 | P0 | 已关闭(代码级) | 会话中间件从数据库用户写入 sessionTenantId;客户端装饰器只读取可信上下文;伪造 header 返回 CLIENT_TENANT_MISMATCH;相关租户隔离测试通过。 |
| CQ-SEC-002 无盐 SHA-256 密码 | P0 | 已关闭(代码级) | 使用随机 salt 的版本化 scrypt、恒定时间比较、参数上限解析;旧 SHA-256 只读兼容并在正确登录后透明升级;写路径和工具门禁已覆盖。 |
| CQ-API-001 缺少运行时输入校验 | P1 | 部分关闭 | 已新增严格 ValidationPipe 和重点客户端写 DTO;但未全局注册,部分凭据、Webhook、日志导出等写接口仍使用内联类型和裸 @Body()。 |
| CQ-AUTH-001 验证码/失败计数进程内 Map | P1 | 基本关闭 | 已迁移 Redis TTL、GETDEL 和 Lua 原子计数,Redis 异常 fail closed;仍建议补 IP+账号双维度限流与容量指标。 |
| CQ-FE-001 主包过大、无路由分包 | P1 | 已关闭 | 全部业务页采用懒加载;ECharts 按需注册;入口 gzip 107.38 KiB,低于 250 KiB 门槛,最大异步图表包 181.64 KiB,低于 190 KiB 门槛。 |
| CQ-TEST-001 测试与验收层级不足 | P1 | 部分关闭 | API 覆盖率阈值已建立且本轮通过;Gateway 测试通过;前端仍无测试,登录后浏览器和真实全链未完成。 |
| CQ-DEP-001 两个 high advisory | P2 | 原问题已关闭 | 正式 package-lock.json 使用 React Router 7.18.2、NanoID 3.3.18;按该锁文件重建审计结果为 0。 |
| CQ-MAINT-001 超大文件、无静态风格门禁 | P2 | 部分关闭 | 页面辅助逻辑已拆分,增加结构质量脚本;两个 SendChain Service 仍为 86,253/53,421 字节,lint 实际为结构检查加 TypeScript,不是 ESLint/格式门禁。 |
| CQ-CONFIG-001 数据库默认凭据 | P2 | 已关闭 | 非 development/test 环境缺少 DATABASE_URL 时启动失败,不再静默使用默认生产连接。 |
| CQ-ARCH-001 生产代码依赖 mock | P2 | 已关闭 | 类型已迁至 src/api/types,结构门禁禁止生产页面重新导入 mock。 |
| CQ-REPO-001 仓库产物与审计基线不稳定 | P2 | 部分关闭 | *.tsbuildinfo 已停止跟踪并加入忽略;但工作区仍有未跟踪的 = 和过期 pnpm-lock.yaml。 |
4. 关键整改证据
4.1 可信租户上下文
api/src/auth/session-validation.middleware.ts:70-72:客户端请求携带的租户头与登录用户租户不一致时返回 403,并将数据库用户租户写入request.sessionTenantId。api/src/auth/current-tenant-id.decorator.ts:10-13:只允许 client portal 读取可信租户上下文,不再回退到请求头。- 客户端认证、短信配置、账务、发送链、风险复核、用户等控制器已改用
@CurrentTenantId()。 - 管理端
@TenantId()仍存在于admin/files、admin/operation-logs、admin/billing等管理路由中,不属于客户端授权依据;结构门禁已禁止客户端控制器回退使用旧装饰器。
判定:原 P0 的根因已消除。测试环境真实双租户结果在整改记录中有描述;本次只复核外部健康接口,没有重跑双租户矩阵,因此仍按代码级关闭记录。
4.2 密码存储与迁移
api/src/auth/password-hasher.ts:1-82:版本化 scrypt、随机 salt、timingSafeEqual、参数边界和旧 SHA-256 识别均已实现。- 旧哈希兼容写窗口被限制为最长 2 小时;正常写路径默认生成新格式。
- 登录成功后通过条件更新透明迁移旧哈希,错误密码不迁移。
- 单测覆盖随机盐、错误密码、旧哈希验证/迁移、畸形参数和兼容窗口边界。
判定:满足上一版“强哈希、版本参数、恒定时间比较、透明迁移”的最小关闭标准。测试环境仍保留的历史 SHA-256 数量来自整改记录,本次未直接查询数据库复核。
4.3 登录状态与输入校验
api/src/auth/session.service.ts:129-163:验证码使用 Redis TTL,消费使用GETDEL;失败计数使用 Redis Lua 原子逻辑。api/src/common/strict-validation.pipe.ts:启用 transform、whitelist 和 forbidNonWhitelisted。- 重点客户端写接口已增加 class-validator DTO 和严格管道。
api/src/open-api/client-open-api.controller.ts:15-18的凭据/Webhook 写入,以及api/src/operations/client-operations.controller.ts:78-84的日志导出仍使用内联 body,未套用严格校验管道。
判定:验证码多实例一致性问题已关闭;输入校验只能判为阶段性完成,不能写成全 API 闭环。
4.4 前端性能
src/routes/AppRoutes.tsx:10-74:业务页面统一路由级懒加载。src/components/ui/Chart.tsx:3-8:只注册实际使用的 ECharts 图表、组件和 Canvas renderer。- 本次生产构建:入口
index-*.js370.54 KiB,gzip 107.38 KiB;图表异步包 553.06 KiB,gzip 181.64 KiB。 - 包体预算脚本通过:入口不高于 250 KiB gzip,异步包不高于 190 KiB gzip。
判定:原主包 626.18 KiB gzip 的问题已实质关闭。Vite 仍提示图表 chunk 的未压缩体积超过 500 KiB,但其 gzip 体积在当前预算内,作为后续优化项而非阻断项。
4.5 数据库与工程配置
- API 正式构建通过。
- Prisma Schema validate 通过;仓库存在 95 个 migration 目录。
- 非开发/测试环境缺少
DATABASE_URL时 fail closed。 - 本轮没有连接真实 PostgreSQL 执行
EXPLAIN (ANALYZE, BUFFERS),也没有独立核对测试库 95/95 migration;相关运行态数据仅见整改记录。
5. 本次独立复测结果
| 检查项 | 本次结果 |
|---|---|
| Git 基线 | HEAD == origin/main == 3af145a... |
| 工作区 | 仅发现既有未跟踪 =、pnpm-lock.yaml;未修改或删除 |
| 前端 TypeScript | 通过 |
| Vite production build | 通过 |
| Bundle budget | 通过;入口 107.38 KiB gzip,最大异步包 181.64 KiB gzip |
| API TypeScript build | 通过 |
| Prisma validate | 通过 |
| API Jest | 47/47 套、543/543 项通过 |
| API 全源覆盖率 | statements 59.84%,branches 51.29%,functions 60.15%,lines 62.60% |
| API 覆盖率门槛 | 通过;门槛分别为 59%、50%、60%、62% |
Gateway go test ./... -count=1 |
通过 |
Gateway go vet ./... |
通过 |
| Gateway 队列契约 | 5/5 通过 |
| 代码质量结构检查 | 通过 |
| 依赖缓解/安全部署检查 | 通过 |
| 根正式锁文件生产依赖审计 | 0 个公告 |
| API 正式锁文件生产依赖审计 | 7 个公告:2 moderate、5 high |
| 前端自动化测试 | 0 个测试文件,未通过验收出口标准 |
测试环境 /api/health |
通过;关闭 Clash 后绕过代理直连,连续 3 次 HTTP 200、status=ok |
| 测试环境 TCP 12026 | 通过;可连接 |
| 测试环境 SSH 只读核验 | 未完成;当前凭据认证失败 |
覆盖率说明:整改结果文档记录的 69.77%/53.47%/71.08%/73.05% 使用 Jest 默认“已加载源码”口径;本报告显式使用 collectCoverageFrom=src/**/*.ts 的全源口径,得到 59.84%/51.29%/60.15%/62.60%。两个结果不是同一统计口径,不应直接比较。本次全源结果仍满足已设门槛,并略高于上一版全源基线。
依赖审计说明:工作区未跟踪的 pnpm-lock.yaml 固定了旧 React Router 7.18.1 和 NanoID 3.3.16,直接运行 pnpm audit 会错误报出上一版两个 high。按正式、已跟踪的 package-lock.json 在临时目录重建锁文件后,根项目审计为 0。该旧锁文件仍应由文件所有者确认后清理或更新,避免 CI/开发者误用。
6. 当前阻断项与剩余风险
运行探测校正:此前 502 为本地代理干扰,不构成缺陷
证据链
- 2026-08-28 12:50,在 Clash 虚拟网卡/代理开启状态下,PowerShell Web 请求曾连续返回 HTTP 502。
- 用户关闭 Clash 后,首次 PowerShell
-NoProxy请求出现一次 10 秒超时,随后请求开始恢复。 - 2026-08-28 12:56,使用
curl --noproxy '*'明确绕过代理连续请求 3 次,均返回 HTTP 200 和{"status":"ok","service":"cmpp-platform-api"}。 - 三次直连总响应时间分别约 28 ms、23 ms、19 ms,TCP 12026 检测为可连接。
校正结论
此前新增的 CQ-OPS-001 / P0 撤销,不计入缺陷和发布阻断。测试环境外部 API 当前可用,原整改记录中的 API 健康结论没有被本次复评推翻。
仍保留的验证边界
本次 SSH 凭据认证失败,因此没有独立读取部署标记、11 项服务状态、内部 Gateway health、Redis Stream pending/lag、migration 或服务器日志。这些项目继续引用整改发布记录,预生产前仍应在可用只读凭据下重新核对。后续私网健康探测应显式绕过系统代理,避免 Clash 等本地网络工具造成误判。
CQ-API-001 / P1:严格输入校验尚未覆盖全部写接口
当前采取“重点接口逐步接入”而非全局管道。凭据创建、Webhook 更新和日志导出等入口仍使用运行时会被擦除的内联 TypeScript 类型。建议先覆盖所有 client POST/PUT/PATCH/DELETE,再评估 admin 高风险写接口;为字段长度、URL、枚举、数组大小和额外字段补负向测试。
CQ-TEST-001 / P1:前端与真实环境验收未闭环
前端测试文件仍为 0;本轮没有可用登录态,无法验证登录后页面、控制台、请求、加载/空/错误/刷新状态。本次仅确认外部 API health,未重新执行真实 PostgreSQL、Redis、MinIO、Gateway 和 CMPP 联动复验。
CQ-DEP-002 / P1:API 依赖树仍有 7 项生产审计公告
按 api/package-lock.json 重建审计后为 2 moderate、5 high,涉及 ExcelJS 间接 UUID、Prisma 可选工具链、brace-expansion、Swagger 间接 js-yaml 等。公告并不等于当前业务路径全部可利用,但应建立“依赖路径、运行时是否打包/调用、可升级版本、兼容回归”的矩阵,不能只以根项目审计为 0 宣告供应链闭环。
CQ-MAINT-001 / P2:超大服务和有风格门禁仍未治理
send-inbound-entry.service.ts 约 84.2 KiB,send-gateway-submit.service.ts 约 52.2 KiB;职责仍集中。建议先补行为锁定测试,再按入站持久化、关联/去重、重试路由、下游投递和计费拆分。另应增加 ESLint 与 format check;当前 npm run lint 名称实际只执行结构脚本和 TypeScript 检查。
CQ-REPO-001 / P2:双锁文件口径仍可能误导审计
正式锁文件是 package-lock.json,但未跟踪 pnpm-lock.yaml 仍存在且版本过期。应先确认其归属,再选择唯一包管理器和唯一锁文件;本报告没有擅自删除或更新用户保留文件。
7. 发布建议
当前建议为:代码可进入下一轮验证,但暂停预生产发布。
最小充分关闭顺序:
- 使用显式绕过系统代理的方式复核测试环境,并在可用只读凭据下核对实际部署标记、内部服务和队列状态。
- 在测试环境重跑双租户隔离和旧密码透明迁移,不发送真实短信。
- 补齐剩余客户端写接口 DTO/严格校验,并增加负向测试。
- 建立最小前端测试集:登录、权限、路由懒加载失败、列表加载/空/错误、关键确认操作。
- 对 API 7 项公告建立升级兼容矩阵;优先处理直接运行时路径。
- 在单独任务中治理 SendChain 超大服务、ESLint/格式门禁和唯一锁文件。
- 只有在真实 PostgreSQL、Redis、MinIO、Gateway/CMPP 模拟器和登录后浏览器证据齐全后,再申请预生产发布授权。
8. 最终判定
- 原 P0 代码问题:2/2 已关闭。
- 原 P1:2 项关闭或基本关闭,2 项部分关闭。
- 原 P2:3 项关闭,2 项部分关闭。
- 新增运行态阻断:0 项;此前 502 已确认是本地 Clash 代理干扰并撤销。
- 自动化门禁:除 API 子树依赖审计和前端测试缺失外,其余本次执行项通过。
- 代码质量:82/100,有条件通过。
- 当前发布就绪:待完整验收;无 502 运行态阻断。
本报告是对固定提交的独立复评和一次当前测试环境只读探测,不替代完整功能验收、生产安全评估或预生产发布审批。