465 lines
19 KiB
Markdown
465 lines
19 KiB
Markdown
# CMPP 平台代码质量持续优化整改方案
|
||
|
||
- 方案日期:2026-08-28
|
||
- 依据报告:`docs/code-quality-reassessment-20260828-v2.md`
|
||
- 当前代码基线:`3af145abe5eb8cae567bb18e29116b4940889258`
|
||
- 适用范围:React/Vite 客户端、NestJS API、依赖治理、测试与工程门禁
|
||
- 实施边界:先在本地完成代码和自动化验证;如需部署,只允许部署到测试环境 `100.93.204.60`,且部署前必须重新建立并校验恢复资产
|
||
- 禁止范围:未经新的明确授权,不得访问、部署、覆盖或回退预生产环境
|
||
|
||
## 1. 背景与当前结论
|
||
|
||
V2 复评确认上一轮两个 P0 已关闭,代码质量从 58 分提升至 82 分。当前不存在新的运行态 P0,但仍有以下持续优化空间:
|
||
|
||
1. 少量客户端写接口仍使用内联 TypeScript 类型,缺少运行时严格校验。
|
||
2. 前端没有自动化测试,登录、权限、懒加载失败和页面异常状态主要依赖人工验证。
|
||
3. API 全源覆盖率刚刚超过门槛,余量不足。
|
||
4. 客户端仍发送 `x-tenant-id` 并保留默认租户回退,与服务端可信会话租户的最终设计不一致。
|
||
5. 登录保护只有账号维度的主要限流,需要补充 IP 和验证码请求维度。
|
||
6. API 依赖树存在审计公告,需要按真实依赖路径和运行时可利用面逐项治理。
|
||
7. 当前 `lint` 不是完整 ESLint,格式检查也只依赖 `git diff --check`。
|
||
8. 两个 SendChain Service 仍然过大,但直接拆分具有较高业务回归风险。
|
||
9. 工作区存在双锁文件口径,可能继续误导安装和依赖审计。
|
||
|
||
本方案遵循“先补小范围安全缺口和测试,再治理供应链和工程门禁,最后拆分发送链”的顺序。
|
||
|
||
## 2. 整改目标
|
||
|
||
本轮持续优化完成后应达到:
|
||
|
||
- 所有客户端写接口具有运行时 DTO 校验,不再使用裸内联 body 类型。
|
||
- 客户端租户授权完全依赖服务端认证会话,浏览器不再负责选择或回退租户。
|
||
- 建立可持续运行的前端测试框架,并覆盖核心登录、权限、异步路由和异常状态。
|
||
- API 全源覆盖率形成合理缓冲,新增代码不能依靠全局低门槛掩盖未测试分支。
|
||
- 登录防护具备账号、IP、IP+账号和验证码请求多维度限制与指标。
|
||
- API 依赖公告具有逐项路径、影响、处理方式和回归证据。
|
||
- `lint` 和 `format:check` 成为真实、稳定、可逐步扩展的工程门禁。
|
||
- SendChain 重构有行为锁定测试,拆分过程中不改变事务、锁、幂等和队列语义。
|
||
- 仓库只保留一种正式包管理器和一种正式锁文件。
|
||
|
||
## 3. 优先级和最小充分范围
|
||
|
||
| 批次 | 内容 | 风险 | 预估工作量 | 是否建议下一轮立即实施 |
|
||
|---|---|---:|---:|---|
|
||
| C1 | 剩余客户端 DTO 和负向测试 | 低 | 0.5~1.5 人日 | 是 |
|
||
| C2 | 删除客户端租户头和默认租户回退 | 中低 | 0.5~1 人日 | 是 |
|
||
| C3 | 最小前端测试框架和核心用例 | 中 | 1~3 人日 | 是 |
|
||
| C4 | 覆盖率缓冲和增量覆盖门禁 | 低 | 0.5~1 人日 | 是,依赖 C1/C3 |
|
||
| C5 | IP/账号/验证码多维登录保护 | 中 | 1~2 人日 | 建议 |
|
||
| C6 | API 依赖公告治理矩阵 | 中 | 1~3 人日 | 建议先诊断后升级 |
|
||
| C7 | ESLint、Prettier 和真实格式门禁 | 中 | 1~2 人日 | 建议分阶段启用 |
|
||
| C8 | SendChain Service 职责拆分 | 高 | 5~10 人日 | 单独专项 |
|
||
| C9 | 唯一包管理器和锁文件 | 中 | 0.5~1 人日 | 需先确认文件归属 |
|
||
|
||
下一轮最小充分范围为 C1~C4。它们投入较小,能够实质关闭 `CQ-API-001` 的客户端部分并改善 `CQ-TEST-001`,不需要数据库迁移,也不需要修改发送链业务规则。
|
||
|
||
## 4. C1:补齐客户端写接口 DTO
|
||
|
||
### 4.1 当前缺口
|
||
|
||
首批至少覆盖:
|
||
|
||
- `api/src/open-api/client-open-api.controller.ts`
|
||
- 创建 HTTP API 凭据。
|
||
- 新增或更新 Webhook。
|
||
- `api/src/operations/client-operations.controller.ts`
|
||
- 系统日志导出。
|
||
- `api/src/files/client-files.controller.ts`
|
||
- Multipart 上传的 `purpose` 和 `prefix`。
|
||
|
||
### 4.2 实施要求
|
||
|
||
1. 新增独立 DTO class,不再使用运行时会被擦除的内联 TypeScript 类型。
|
||
2. 对上述接口启用现有 `strictValidationPipe`。
|
||
3. DTO 规则至少包含:
|
||
- 凭据名称长度和空白处理。
|
||
- `expiresAt` 使用 ISO 日期时间校验,并拒绝已过期时间。
|
||
- Webhook `eventType` 只允许平台支持的事件。
|
||
- Webhook URL 限制协议、长度和格式;继续由 Service 执行 SSRF 与 HTTPS 策略。
|
||
- Webhook `status` 使用明确枚举。
|
||
- 日志级别、模块、日期区间使用明确格式和长度限制。
|
||
- 上传 `purpose` 使用白名单;`prefix` 限制长度、字符集和路径穿越。
|
||
4. 将当前过宽的 `ClientStatusChangeDto` 按应用、签名、模板和引流信息拆成更窄的 DTO,避免无关字段被不同接口接受。
|
||
5. 对 `scheduledAt`、`requestedAt` 等时间字段使用日期格式校验。
|
||
6. 对 `variables`、`materials`、`reportValues` 等对象补充:
|
||
- 最大键数量。
|
||
- 最大嵌套深度。
|
||
- 键名和字符串值长度。
|
||
- 禁止原型污染相关键。
|
||
|
||
### 4.3 自动化测试
|
||
|
||
每个接口至少覆盖:
|
||
|
||
- 合法最小请求成功。
|
||
- 额外字段返回 400。
|
||
- 超长字符串返回 400。
|
||
- 非法 URL、日期和状态返回 400。
|
||
- 客户端提交 `createdById/operatorId/tenantId` 等身份字段不能覆盖服务端身份。
|
||
- 校验失败时 Service 不被调用。
|
||
- 文件 purpose/prefix 校验失败时 MinIO 不发生写入。
|
||
|
||
### 4.4 出口标准
|
||
|
||
- 客户端 POST/PUT/PATCH 中不再存在未评审的裸 `@Body()` 内联类型。
|
||
- 对应控制器和 DTO 负向测试通过。
|
||
- 错误请求稳定返回 400,不返回 500。
|
||
- 不改变现有合法前端请求协议。
|
||
|
||
## 5. C2:移除客户端租户头和默认租户回退
|
||
|
||
### 5.1 目标设计
|
||
|
||
客户端租户范围只能来自服务端认证会话:
|
||
|
||
```text
|
||
客户端请求
|
||
-> session cookie
|
||
-> 服务端查询会话用户
|
||
-> request.sessionTenantId
|
||
-> CurrentTenantId
|
||
-> 数据库 tenantId 复合条件
|
||
```
|
||
|
||
浏览器不再通过 localStorage、默认常量或 `x-tenant-id` 参与客户端授权。
|
||
|
||
### 5.2 实施内容
|
||
|
||
1. 修改客户端 HTTP 请求封装,客户端路由默认不发送 `x-tenant-id`。
|
||
2. 删除 `getSessionTenantId() ?? DEFAULT_CLIENT_TENANT_ID` 的客户端回退路径。
|
||
3. 清理客户端 API 方法中仅为请求头服务的 `tenantId` 参数。
|
||
4. 管理端明确跨企业查询所需的租户参数继续保留,并与客户端请求封装分离。
|
||
5. 服务端暂时保留“收到客户端租户头时做一致性检查”的兼容逻辑,经过一个测试版本确认没有旧客户端后再决定移除。
|
||
6. 质量门禁增加:
|
||
- `src/api/client/**` 不得设置 `x-tenant-id`。
|
||
- 客户端 API 不得引用 `DEFAULT_CLIENT_TENANT_ID`。
|
||
- 管理端的显式租户参数不受此规则影响。
|
||
|
||
### 5.3 必测用例
|
||
|
||
- 不带租户头的客户端全部核心页面和接口正常。
|
||
- 浏览器 localStorage 中 tenantId 缺失、错误或过期,不影响服务端租户识别。
|
||
- 运营端跨租户查询仍按权限正常工作。
|
||
- 旧客户端携带正确租户头仍兼容;错误租户头仍返回 403 并记录安全事件。
|
||
- 双租户真实 API 隔离矩阵继续通过。
|
||
|
||
### 5.4 出口标准
|
||
|
||
- 客户端代码不再选择租户授权范围。
|
||
- 不出现登录后初始化阶段因默认租户错误导致的 403。
|
||
- 服务端可信租户门禁继续通过。
|
||
|
||
## 6. C3:建立最小前端测试集
|
||
|
||
### 6.1 技术方案
|
||
|
||
建议采用:
|
||
|
||
- Vitest:测试运行器。
|
||
- React Testing Library:组件和页面行为测试。
|
||
- jsdom:DOM 环境。
|
||
- MSW:模拟真实 HTTP 协议的成功、空、失败和延迟响应。
|
||
|
||
测试必须围绕用户可见行为和真实请求契约,不以大量快照替代断言。
|
||
|
||
### 6.2 首批核心用例
|
||
|
||
1. 客户端登录:成功、密码错误、验证码错误、接口不可用。
|
||
2. 未登录访问客户端深层路由:跳转登录页。
|
||
3. 权限不足:显示稳定提示,不白屏。
|
||
4. `RouteLoadBoundary`:异步 chunk 加载失败、重试和重新加载。
|
||
5. 列表页:loading、empty、error、成功、分页和筛选。
|
||
6. 高风险操作:删除、重置密钥、创建凭据等确认流程。
|
||
7. API 返回 500:页面显示业务化错误,不直接显示裸 `Internal server error`。
|
||
8. 会话失效:401 后清理展示会话并回登录页。
|
||
|
||
### 6.3 工程门禁
|
||
|
||
新增命令建议:
|
||
|
||
```text
|
||
npm run test:frontend
|
||
npm run test:frontend:coverage
|
||
```
|
||
|
||
并将 `test:frontend` 纳入 `verify:quality`。首轮覆盖率门槛以核心模块为主,不为追求全局数字测试纯展示组件。
|
||
|
||
### 6.4 出口标准
|
||
|
||
- 至少 6~10 个核心行为用例稳定通过。
|
||
- 登录、权限、路由失败和列表异常状态均有自动化覆盖。
|
||
- 测试可以在全新依赖安装后重复执行。
|
||
- CI/质量门禁中前端测试失败会阻止合并。
|
||
|
||
## 7. C4:覆盖率缓冲和增量门禁
|
||
|
||
### 7.1 当前问题
|
||
|
||
当前全源覆盖率虽然通过,但接近门槛:
|
||
|
||
- statements 59.84%,门槛 59%。
|
||
- branches 51.29%,门槛 50%。
|
||
- functions 60.15%,门槛 60%。
|
||
- lines 62.60%,门槛 62%。
|
||
|
||
functions 仅有 0.15 个百分点余量,新增少量未测试函数就可能失败。
|
||
|
||
### 7.2 实施内容
|
||
|
||
1. 先通过 C1 和 C3 增加有业务价值的测试,不立即盲目提高门槛。
|
||
2. 建立修改文件或新增文件覆盖率门禁,建议初始目标:
|
||
- statements/lines 不低于 80%。
|
||
- branches 不低于 70%。
|
||
- functions 不低于 80%。
|
||
3. 全源门槛在覆盖率稳定后分批上调,每次不超过 2~3 个百分点。
|
||
4. 覆盖率报告明确使用全源 `collectCoverageFrom` 口径,避免再次混用“已加载源码”和“全部源码”。
|
||
|
||
### 7.3 出口标准
|
||
|
||
- 新增 DTO、校验器和安全逻辑具有负向分支测试。
|
||
- 修改文件覆盖率门禁能够阻止新增无测试逻辑。
|
||
- 文档和质量门禁使用同一统计口径。
|
||
|
||
## 8. C5:多维登录和验证码保护
|
||
|
||
### 8.1 实施内容
|
||
|
||
在现有 Redis 原子计数基础上增加:
|
||
|
||
- 账号维度失败窗口。
|
||
- 来源 IP 维度失败窗口。
|
||
- IP+账号组合维度。
|
||
- 验证码获取频率限制。
|
||
- 单 IP 随机账号扫描保护。
|
||
- key 数量、失败次数、锁定次数和 Redis 异常指标。
|
||
|
||
来源 IP 必须基于受信 Nginx/代理链解析,不能直接信任任意客户端 `X-Forwarded-For`。
|
||
|
||
### 8.2 验证
|
||
|
||
- 多 API 实例共享相同锁定状态。
|
||
- 同账号换 IP、同 IP 换账号均能触发对应保护。
|
||
- 正常用户偶发输错不会被过度锁定。
|
||
- Redis 不可用继续 fail closed,并产生监控告警。
|
||
- 随机账号和验证码请求压测后 Redis key 数量能随 TTL 回落。
|
||
|
||
## 9. C6:API 依赖公告治理
|
||
|
||
### 9.1 原则
|
||
|
||
依赖审计公告不直接等于业务可利用漏洞。不得直接执行 `npm audit fix --force`。应先建立矩阵:
|
||
|
||
| 字段 | 内容 |
|
||
|---|---|
|
||
| 公告编号 | GHSA/CVE |
|
||
| 依赖路径 | 直接依赖到受影响包的完整路径 |
|
||
| 实际锁定版本 | 以 `api/package-lock.json` 为准 |
|
||
| 环境 | 生产运行时、构建期、开发期、可选工具 |
|
||
| 不可信输入 | 是否处理客户输入、文件、URL、模板或命令行参数 |
|
||
| 当前缓解 | override、功能未启用、输入边界、兼容适配 |
|
||
| 修复方案 | 补丁升级、依赖替换、隔离或书面风险接受 |
|
||
| 回归范围 | 构建、Excel 导出、Swagger、Prisma、数据库和部署 |
|
||
|
||
### 9.2 优先顺序
|
||
|
||
1. 直接生产运行时且处理不可信输入的路径。
|
||
2. Excel 导出、压缩、YAML/Swagger 等数据处理路径。
|
||
3. Prisma 的生产客户端路径。
|
||
4. 仅 CLI、Studio、构建或可选工具链路径。
|
||
|
||
### 9.3 出口标准
|
||
|
||
- 7 项公告均有明确依赖路径和处置结论。
|
||
- 可安全升级的依赖完成升级并通过全量回归。
|
||
- 暂不能升级的项目有代码级缓解门禁和明确复查日期。
|
||
- 根项目和 API 子项目审计口径分别记录,不互相替代。
|
||
|
||
## 10. C7:真实 lint 和格式门禁
|
||
|
||
### 10.1 分阶段实施
|
||
|
||
第一阶段只检查新增和修改文件,启用高价值规则:
|
||
|
||
- 未使用变量和导入。
|
||
- 未处理 Promise。
|
||
- 不安全的 `any` 和类型断言。
|
||
- React Hooks 依赖和调用规则。
|
||
- 无效条件、重复分支和不可达代码。
|
||
- Node/NestJS 常见异步错误。
|
||
|
||
第二阶段再逐目录修复历史问题并扩大到全仓库。
|
||
|
||
Prettier 首轮只执行 `check`,不得在同一提交中格式化全部历史文件。大规模格式化必须独立提交,避免掩盖业务改动。
|
||
|
||
### 10.2 脚本语义
|
||
|
||
- `lint`:真实 ESLint。
|
||
- `typecheck`:前端 TypeScript。
|
||
- `quality:verify`:结构安全门禁。
|
||
- `format:check`:Prettier check + `git diff --check`。
|
||
|
||
不要继续用 `lint` 名称包装结构检查和 TypeScript,使开发者误判实际门禁能力。
|
||
|
||
## 11. C8:SendChain 超大服务专项拆分
|
||
|
||
### 11.1 前置条件
|
||
|
||
拆分前必须补齐行为锁定测试:
|
||
|
||
- 事务边界。
|
||
- advisory lock 和锁顺序。
|
||
- 消息、Submit 和 Outbox 幂等键。
|
||
- 主备路由和补发。
|
||
- 计费冻结、扣费、退款和释放。
|
||
- Inbox claim、租约、超时和恢复。
|
||
- Redis Stream 发布及重复消费。
|
||
- 回执、上行和下游投递。
|
||
|
||
### 11.2 建议拆分方向
|
||
|
||
`send-inbound-entry.service.ts`:
|
||
|
||
- 入站协议数据转换。
|
||
- Inbox 持久化和 claim。
|
||
- 长短信聚合。
|
||
- 企业分组微批。
|
||
- 业务校验和消息创建。
|
||
|
||
`send-gateway-submit.service.ts`:
|
||
|
||
- 路由选择。
|
||
- Gateway 命令构造。
|
||
- Submit Outbox。
|
||
- Submit 结果处理。
|
||
- 重试和恢复。
|
||
|
||
### 11.3 实施规则
|
||
|
||
- 每次只移动一个职责。
|
||
- 先机械提取,再优化逻辑。
|
||
- 不同时修改事务、SQL、幂等键或状态语义。
|
||
- 每个拆分提交都运行 API 全量、Gateway 全量、真实 PostgreSQL/Redis 契约和队列排空检查。
|
||
|
||
### 11.4 出口标准
|
||
|
||
- 原 Service 只保留编排职责。
|
||
- 行为测试、全链契约和性能基线不下降。
|
||
- 不新增重复查询、跨事务状态漂移或锁顺序变化。
|
||
|
||
## 12. C9:唯一包管理器和锁文件
|
||
|
||
当前正式跟踪文件为根目录和 API 的 `package-lock.json`,工作区另有未跟踪 `pnpm-lock.yaml`。该文件属于受保护的既有工作区内容,本方案不授权删除。
|
||
|
||
需要文件所有者明确选择:
|
||
|
||
### 方案 A:统一 npm(建议)
|
||
|
||
- 保留两个正式 `package-lock.json`。
|
||
- 确认后删除或归档旧 `pnpm-lock.yaml`。
|
||
- 部署、审计和 CI 全部使用 `npm ci`。
|
||
- 门禁禁止新增 pnpm/yarn 锁文件。
|
||
|
||
### 方案 B:统一 pnpm
|
||
|
||
- 重新生成受控 pnpm 锁文件。
|
||
- 重写本地、CI、部署和审计命令。
|
||
- 在全新目录验证安装、构建、Prisma、测试和生产部署。
|
||
- 完成后再移除 npm 锁文件。
|
||
|
||
不得长期同时维护两种锁文件,也不得只改锁文件而不改部署流程。
|
||
|
||
## 13. 不建议在下一轮实施的动作
|
||
|
||
- 不一次性为全部 API 开启全局严格 `ValidationPipe`。
|
||
- 不直接执行 `npm audit fix --force`。
|
||
- 不为了 Vite 的 500 KiB 原始体积警告替换整个 ECharts;当前 gzip 包体仍在预算内。
|
||
- 不在安全补丁提交中同时拆分 SendChain。
|
||
- 不以大量快照测试提高前端覆盖率。
|
||
- 不擅自删除 `=`, `pnpm-lock.yaml` 或其他侧边任务文件。
|
||
- 不把测试环境健康检查等同于登录后页面和 CMPP 全链验收。
|
||
|
||
## 14. 验证矩阵
|
||
|
||
| 层级 | 必须验证 |
|
||
|---|---|
|
||
| 静态 | TypeScript、ESLint、Prettier、结构安全门禁、`git diff --check` |
|
||
| API | DTO 正负向、租户隔离、密码迁移、登录限流、账务和发送幂等 |
|
||
| 前端 | 登录、权限、懒加载、loading/empty/error、确认操作、401/500 |
|
||
| 数据库 | Prisma validate、95 项 migration 一致性、真实 PostgreSQL 关键查询 |
|
||
| Redis | 验证码 TTL、登录锁定、多实例、Stream pending/lag |
|
||
| Gateway | `go test ./... -count=1`、`go vet ./...`、5 份队列契约 |
|
||
| 浏览器 | 登录后页面、控制台、请求、刷新、深层链接和移动端 |
|
||
| 发布后 | 部署 commit、服务、端口、健康、日志、队列和临时数据清理 |
|
||
|
||
## 15. 测试环境发布要求
|
||
|
||
如下一轮包含部署,必须:
|
||
|
||
1. 明确目标是测试环境 `100.93.204.60`,显式绕过 Clash/系统代理进行健康探测。
|
||
2. 发布前重新建立:
|
||
- PostgreSQL custom dump。
|
||
- 当前运行目录归档。
|
||
- 环境、systemd 和 Nginx 配置。
|
||
- Redis RDB 和关键 Stream/队列状态。
|
||
- MinIO 数据或业务对象清单。
|
||
- 当前部署 commit 和 95 项 migration 状态。
|
||
3. 校验 `pg_restore --list`、tar 目录、Redis RDB 和 SHA-256。
|
||
4. 发布后核对所有相关服务、API/Gateway health、Redis pending/lag 和 error 日志。
|
||
5. 清理临时测试账号、凭据、文件和脚本。
|
||
6. 测试环境通过不自动授权预生产发布。
|
||
|
||
## 16. 建议提交拆分
|
||
|
||
1. `security: validate remaining client write payloads`
|
||
2. `test: cover client dto rejection paths`
|
||
3. `security: remove client-controlled tenant headers`
|
||
4. `test: add frontend auth and route failure baseline`
|
||
5. `test: enforce changed-file coverage`
|
||
6. `security: add ip-aware login throttling`
|
||
7. `build: document and remediate api audit paths`
|
||
8. `build: add eslint and prettier checks`
|
||
9. `refactor: extract inbound workflow responsibilities`
|
||
10. `refactor: extract gateway submit responsibilities`
|
||
11. `chore: enforce the selected package manager`
|
||
|
||
每个提交只处理一个主题,不混入测试环境部署资产、历史文档改写或其他会话的工作区文件。
|
||
|
||
## 17. 最终关闭标准
|
||
|
||
| 项目 | 关闭证据 |
|
||
|---|---|
|
||
| 客户端 DTO | 所有写接口使用 class DTO 和严格管道;负向测试通过 |
|
||
| 租户头清理 | 客户端不再发送/回退 tenantId;双租户矩阵通过 |
|
||
| 前端测试 | 核心用例进入质量门禁并稳定运行 |
|
||
| 覆盖率 | 全源口径稳定且修改文件满足增量门槛 |
|
||
| 登录保护 | 账号/IP/组合/验证码限流和指标通过多实例测试 |
|
||
| 依赖公告 | 每项有路径、影响、处理和回归证据 |
|
||
| lint/format | ESLint 和 Prettier 真实执行,不再仅使用脚本别名 |
|
||
| SendChain | 行为锁定后完成职责拆分,全链语义和性能不下降 |
|
||
| 锁文件 | 唯一包管理器、唯一正式锁文件、全新安装可重复 |
|
||
| 测试环境 | 恢复资产、真实后端、浏览器和发布后状态证据齐全 |
|
||
|
||
## 18. 建议执行顺序
|
||
|
||
```text
|
||
C1 客户端 DTO
|
||
-> C2 租户头清理
|
||
-> C3 前端测试
|
||
-> C4 覆盖率缓冲
|
||
-> C5 登录保护
|
||
-> C6 依赖治理
|
||
-> C7 lint/format
|
||
-> C8 SendChain 专项拆分
|
||
-> C9 锁文件统一
|
||
-> 测试环境完整验收
|
||
```
|
||
|
||
其中 C1~C4 可作为下一轮独立交付;C8 必须保持为单独的高风险重构任务。
|
||
|
||
## 19. 2026-08-28 执行补充
|
||
|
||
- 文件所有者已明确要求完成其余未关闭项,并确认不接受任何 npm 或依赖降级。
|
||
- C9 继续采用既有 npm 10.9.4 基线,没有执行 npm、Node、Prisma或业务依赖降级;旧的未跟踪 `pnpm-lock.yaml` 已移除,正式安装仍使用根目录和API目录各自的 `package-lock.json` 与 `npm ci`。
|
||
- C8 本轮只形成专项拆分方案 `docs/sendchain-service-decomposition-plan-20260828.md`,没有实施高风险发送链代码重构。
|
||
- 登录后页面由用户按 `docs/code-quality-browser-acceptance-checklist-20260828.md` 人工验收,结果回填前不得标记浏览器验收关闭。
|