Files
lislgosms/docs/code-quality-reassessment-20260828-v2.md
T

15 KiB
Raw Blame History

CMPP 平台代码质量复评报告 V2

  • 报告日期:2026-08-28
  • 复评时间:2026-08-28 12:3812:57Asia/Shanghai
  • 复评基线:3af145abe5eb8cae567bb18e29116b4940889258
  • 对比基线:171c7d38e8f17de0dd83570603da316d47c0d015
  • 分支状态:main,本地 HEADorigin/main 一致
  • 变更规模:58 个文件,新增 1,953 行、删除 487 行
  • 复评范围:React/Vite 前端、NestJS API、Prisma/PostgreSQL、Redis、Go Gateway、自动化测试、依赖与仓库门禁、测试环境只读健康探测
  • 明确未执行:提交、推送、部署、服务重启、数据库写入、短信发送、压力测试、MinIO 业务对象写入、预生产或生产环境操作

1. 复评结论

本次采用两个相互独立的结论,避免把“代码整改完成”和“环境可发布”混为一谈:

  1. 代码整改:有条件通过。 上一版的两个 P0 已在源码、自动化测试和结构门禁层面关闭;前端分包、覆盖率门槛、Redis 状态存储、数据库配置 fail closed、mock 解耦等整改有效。
  2. 发布就绪:待完整验收。 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/filesadmin/operation-logsadmin/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-*.js 370.54 KiBgzip 107.38 KiB;图表异步包 553.06 KiBgzip 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 msTCP 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 / P1API 依赖树仍有 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 KiBsend-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. 发布建议

当前建议为:代码可进入下一轮验证,但暂停预生产发布。

最小充分关闭顺序:

  1. 使用显式绕过系统代理的方式复核测试环境,并在可用只读凭据下核对实际部署标记、内部服务和队列状态。
  2. 在测试环境重跑双租户隔离和旧密码透明迁移,不发送真实短信。
  3. 补齐剩余客户端写接口 DTO/严格校验,并增加负向测试。
  4. 建立最小前端测试集:登录、权限、路由懒加载失败、列表加载/空/错误、关键确认操作。
  5. 对 API 7 项公告建立升级兼容矩阵;优先处理直接运行时路径。
  6. 在单独任务中治理 SendChain 超大服务、ESLint/格式门禁和唯一锁文件。
  7. 只有在真实 PostgreSQL、Redis、MinIO、Gateway/CMPP 模拟器和登录后浏览器证据齐全后,再申请预生产发布授权。

8. 最终判定

  • 原 P0 代码问题:2/2 已关闭
  • 原 P12 项关闭或基本关闭,2 项部分关闭
  • 原 P23 项关闭,2 项部分关闭
  • 新增运行态阻断:0 项;此前 502 已确认是本地 Clash 代理干扰并撤销。
  • 自动化门禁:除 API 子树依赖审计和前端测试缺失外,其余本次执行项通过。
  • 代码质量:82/100,有条件通过
  • 当前发布就绪:待完整验收;无 502 运行态阻断

本报告是对固定提交的独立复评和一次当前测试环境只读探测,不替代完整功能验收、生产安全评估或预生产发布审批。