feat: add phone frequency controls and modularize codebase
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
# R0 渐进式拆分发布与回滚门禁
|
||||
|
||||
更新日期:2026-07-30
|
||||
|
||||
## 1. 单版本所有权
|
||||
|
||||
- 一个拆分版本只能指定一个“结构修改会话”。
|
||||
- 其他会话可以查阅、测试和报告问题,但不得同时移动同一职责域的文件。
|
||||
- 开始前记录:负责人、目标域、基线提交、允许修改的文件、明确不修改的文件。
|
||||
- 发现重叠未提交修改时立即停止移动,先由修改所有者确认边界。
|
||||
|
||||
## 2. 开始前检查
|
||||
|
||||
- [ ] `git status --short --branch`
|
||||
- [ ] `git diff`
|
||||
- [ ] `git fetch`
|
||||
- [ ] 分别核对 `HEAD` 和 `origin/main`
|
||||
- [ ] 完整阅读 `docs/testing-progress.md` 最新记录
|
||||
- [ ] 标记其他会话修改、未跟踪文件和构建产物
|
||||
- [ ] 选择一个业务域,列出稳定门面、调用者、表、队列和副作用
|
||||
- [ ] 运行目标域定向特征测试并保存基线结果
|
||||
- [ ] 确认该版本不混入业务规则修改
|
||||
- [ ] 确认回滚只需回到本版本前精确提交,不依赖手工修库
|
||||
|
||||
## 3. 实施中检查
|
||||
|
||||
- [ ] 原公开类、方法、导出名和控制器调用保持兼容
|
||||
- [ ] 纯移动不同时重命名、格式化或改写逻辑
|
||||
- [ ] 原事务仍由同一顶层用例持有
|
||||
- [ ] 子服务接收同一 `Prisma.TransactionClient`
|
||||
- [ ] 数据库锁顺序、唯一约束、幂等键未改变
|
||||
- [ ] Redis Stream 名称、消费者组和 JSON 字段未改变
|
||||
- [ ] CMPP Sequence_Id、Msg_Id、版本和分片规则未改变
|
||||
- [ ] 错误码、中文提示、金额精度和上海时区口径未改变
|
||||
- [ ] 新模块依赖单向,不新增 `forwardRef` 循环
|
||||
- [ ] 每完成一个委托点即运行对应定向测试
|
||||
|
||||
## 4. 提交前检查
|
||||
|
||||
- [ ] `node tools/quality/verify-refactor-r0.mjs`
|
||||
- [ ] `node tools/spike/validate-gateway-queue-contract.mjs`
|
||||
- [ ] 目标域定向测试
|
||||
- [ ] API 全量测试
|
||||
- [ ] Prisma format、validate、generate 和 migrate status
|
||||
- [ ] API TypeScript 正式构建
|
||||
- [ ] 前端 TypeScript 与 Vite 生产构建
|
||||
- [ ] Gateway `go test ./...` 和 `go vet ./...`
|
||||
- [ ] 依赖安全门禁
|
||||
- [ ] `git diff --check`
|
||||
- [ ] 逐文件人工核对 diff,确认只有目标域
|
||||
- [ ] 更新需求、测试用例和 `docs/testing-progress.md`
|
||||
|
||||
构建缓存、`outputs/` 和临时文件不能因为“提交全部代码”而混入提交。
|
||||
|
||||
## 5. 发布与观察
|
||||
|
||||
- [ ] 只使用精确 Git 提交制作发布包
|
||||
- [ ] 部署前备份 PostgreSQL、运行源码和环境文件
|
||||
- [ ] 校验本地和服务器发布包 SHA-256
|
||||
- [ ] 只使用 `tools/deploy/production-deploy.sh`
|
||||
- [ ] Gateway 先于 API 重启
|
||||
- [ ] 检查 migration、服务、端口、health、Redis、Stream pending/lag
|
||||
- [ ] 检查供应商通道和下游客户连接,不修改真实凭据或状态
|
||||
- [ ] 检查 API/Gateway error 日志
|
||||
- [ ] 不为重构验收发送、重投或补发真实短信
|
||||
- [ ] 观察至少一个完整发布周期后再开始同一门面的下一阶段
|
||||
|
||||
## 6. 立即停止条件
|
||||
|
||||
出现任一情况,停止继续拆分并回到诊断:
|
||||
|
||||
- 测试数量减少但没有明确删除用例的依据;
|
||||
- 同一输入的 API/队列/CMPP 契约发生变化;
|
||||
- 事务被拆成多个独立提交;
|
||||
- 账务重复、漏记或余额出现非预期变化;
|
||||
- 并发测试出现重复提交、重复补发、重复 Deliver 或重复退款;
|
||||
- Gateway 重连、ACK、分片或 Msg_Id 映射行为变化;
|
||||
- 页面需要靠 mock、静态数据或 localStorage 才能展示;
|
||||
- diff 同时跨越两个业务域且无法逐段人工复核。
|
||||
|
||||
## 7. 回滚记录模板
|
||||
|
||||
```text
|
||||
版本:
|
||||
目标域:
|
||||
结构修改提交:
|
||||
发布前提交:
|
||||
数据库 migration:无 / 列表
|
||||
发布前备份目录:
|
||||
触发回滚的证据:
|
||||
是否涉及业务数据修复:
|
||||
回滚命令/发布包:
|
||||
回滚后服务与端口:
|
||||
回滚后 Redis Stream pending/lag:
|
||||
回滚后通道连接:
|
||||
回滚后错误日志:
|
||||
验证人和时间:
|
||||
```
|
||||
|
||||
纯结构拆分原则上不新增 migration。若必须改 schema,应拆成独立业务版本,不与文件移动同批发布。
|
||||
@@ -0,0 +1,144 @@
|
||||
# R0 大文件职责与副作用索引
|
||||
|
||||
更新日期:2026-07-30
|
||||
基线提交:`0af671b4ed4713912e703defd08791f164d4eb25`
|
||||
|
||||
## 1. 使用方式
|
||||
|
||||
本索引是后续拆分版本的行为地图,不是目标架构。开始任何 R1~R11 拆分前,必须:
|
||||
|
||||
1. 重新核对 Git 与部署基线,不能直接沿用本文提交号。
|
||||
2. 找到目标方法所属的职责组、调用者、数据表和副作用。
|
||||
3. 先运行该职责组的特征测试,再移动代码。
|
||||
4. 保留原门面、公开方法、事务边界、幂等键和消息结构。
|
||||
5. 如果实际代码与本文不一致,先更新索引再开始拆分。
|
||||
|
||||
## 2. 核心文件总览
|
||||
|
||||
| 文件 | 稳定门面 | 直接调用者 | 主要外部副作用 | 拆分优先级 |
|
||||
|---|---|---|---|---|
|
||||
| `api/src/send-chain/send-chain.service.ts` | `SendChainService` | Admin/Client SendChain Controller、GatewayEvents、RiskReview、Operations、OpenAPI | PostgreSQL、Redis Stream、Gateway 控制 API、账务与下游回执 | 最高 |
|
||||
| `gateway/internal/inbound/server.go` | `inbound.Server` | Gateway 启动入口、控制面 | CMPP TCP、NestJS Gateway API、Redis 在线状态、ACK 定时器 | 最高 |
|
||||
| `gateway/internal/upstream/manager.go` | `upstream.Manager` | Submit Worker、控制面 | 供应商 CMPP TCP、连接池、重连、回执和上行事件 | 最高 |
|
||||
| `api/src/channels/channels.service.ts` | `ChannelsService` | Channels Controller、Report/SmsConfig 间接调用 | PostgreSQL、Gateway 连接控制、导入导出文件 | 高 |
|
||||
| `api/src/sms-config/sms-config.service.ts` | `SmsConfigService` | Admin/Client SmsConfig Controller、GatewayEvents | PostgreSQL、Gateway 下游连接状态、审核日志 | 高 |
|
||||
| `api/src/operations/operations.service.ts` | `OperationsService` | Admin/Client Operations Controller | PostgreSQL 聚合查询、CSV 导出、恢复控制 | 中高 |
|
||||
| `src/api/adminApi.ts` | `adminApi` | 运营端页面和组件 | HTTP、文件上传下载、认证失败跳转 | 中高 |
|
||||
| `api/src/report-materials/report-materials.service.ts` | `ReportMaterialsService` | ReportMaterials Controller | PostgreSQL、XLSX/CSV、MinIO/FileObject | 中高 |
|
||||
| `src/styles/global.css` | 全局 class 名和 CSS 变量 | 全部页面 | 全局级联、响应式覆盖 | 高 |
|
||||
|
||||
## 3. SendChainService
|
||||
|
||||
### 3.1 公开职责组
|
||||
|
||||
| 职责组 | 公开入口(同组方法不得在一次拆分中漏项) | 主要表 | 队列/外部调用 | 不变量 |
|
||||
|---|---|---|---|---|
|
||||
| 客户批量发送 | 创建、预览导入、确认导入、取消、任务/号码分页 | `SmsBatchTask`、`SmsMessageRecord`、`SmsSendTask` | Redis 任务队列 | 去重、非法号码不计费、任务进度与真实记录一致 |
|
||||
| HTTP/OpenAPI 发送 | API 请求受理、查询请求结果 | `SmsApiRequest`、`SmsMessageRecord` | Redis 任务队列 | 请求幂等、签名模板校验、同步错误不进入计费 |
|
||||
| CMPP 入站 | 鉴权、Submit、长短信分片、重启恢复 | `CmppInboundLongMessage`、`SmsBatchTask`、`SmsMessageRecord` | 下游 SubmitResp/Deliver | 一个目标号码一条真实记录;原 `Msg_Id` 可恢复;冲突分片拒绝 |
|
||||
| 审核恢复 | 审核通过/驳回、聚合任务展开 | `SmsSendTask`、`SmsMessageRecord` | Redis 任务队列、下游 Deliver | 每条消息只恢复一次;驳回必须释放冻结 |
|
||||
| 路由与提交 | 入队、选择通道、创建提交记录 | `ChannelRouteRule`、`SmsChannel`、`SmsSubmitRecord` | `gateway.submit.commands` | 运营商/省份快照;仅走已报备通道;重试不重复扣费 |
|
||||
| SubmitResult | 接收分片/聚合结果、提交异常重排 | `SmsSubmitRecord`、`SmsMessageRecord`、`GatewaySubmitDeadLetter` | Redis 重排 | 优先按 `submitId`;模糊匹配必须拒绝;相同幂等键恢复 |
|
||||
| 最终回执 | 回执持久化、匹配、分片聚合、补发 | `UpstreamReceiptInbox`、`SmsReceiptRecord`、`SmsMessageSegmentAudit` | 重试提交、下游 Deliver | terminal 状态不可被旧事件降级;并发失败只产生一次补发 |
|
||||
| 账务 | 冻结、扣费、释放、退款 | `SmsBillingRecord`、`AccountTransaction` | PostgreSQL 事务锁 | 同一业务键只记账一次;失败/拒绝路径释放或退款 |
|
||||
| 下游投递 | 创建 Deliver、ACK、人工重排、超时扫描 | `CmppDownstreamDelivery`、`CmppDownstreamDeliveryAttempt` | Gateway control API、HTTP webhook | `Result=0` 且非零 `Msg_Id` 才是业务 ACK;并发重排只执行一次 |
|
||||
| 上行短信 | 保存、候选匹配、人工认领 | `SmsUplinkMessage`、`SmsUplinkMatchCandidate` | 下游 Deliver/Webhook | 共享接入号歧义不得自动错配 |
|
||||
| 恢复与扫描 | 定时任务、过期回执、长短信恢复、恢复状态 | 多表 | Redis/Gateway | 扫描必须可重复;抢占超时可恢复;不得覆盖已完成状态 |
|
||||
|
||||
### 3.2 事务、锁与幂等边界
|
||||
|
||||
- 顶层发送用例持有 Prisma 事务;未来子服务只能接收同一个 `TransactionClient`。
|
||||
- 定时任务、补发、下游人工重排使用数据库原子抢占;禁止改回“先查再写”。
|
||||
- `SmsSubmitRecord`、`SmsBillingRecord`、`CmppDownstreamDelivery` 和队列消息之间的关联 ID 是跨进程恢复依据,不能只保存在内存。
|
||||
- 最终回执以当前尝试和分片审计聚合;旧尝试不得覆盖新尝试的终态。
|
||||
- 发送链拆分必须运行第 8 节中 SendChain、Billing、Gateway queue 和 Gateway protocol 四组门禁。
|
||||
|
||||
## 4. Gateway 入站 Server
|
||||
|
||||
| 职责组 | 当前入口/处理器 | 外部状态 | 不变量 |
|
||||
|---|---|---|---|
|
||||
| Bind 与鉴权 | CMPP 2.0/3.0 CONNECT、账号鉴权 | NestJS API、Redis presence | disabled 新提交与 receipt-draining 状态必须区分 |
|
||||
| Submit | 报文归一化、长短信字段解析、调用 API、返回 SubmitResp | NestJS `/inbound/submit` | Sequence_Id 原样响应;API 结果与协议状态一致 |
|
||||
| 会话与心跳 | ACTIVE_TEST、连接注册/移除 | Redis、API connection events | 心跳超时不保留伪在线连接 |
|
||||
| 下游 Deliver | 选择会话、写报文、注册 ACK | 内存 ACK registry、API | CMPP 2.0/3.0 报文分别正确;非零 Msg_Id |
|
||||
| ACK | DELIVER_RESP 匹配、成功/失败回调、超时 | ACK timer、API | 连接/Sequence_Id/Msg_Id 不得串线;超时只回调一次 |
|
||||
| 恢复 | 账号在线状态与历史待投递恢复 | Redis recovery lock | 锁、退避和 stale owner 不得互相覆盖 |
|
||||
| 协议日志 | 入站/出站报文安全日志 | API protocol log | 方向、命令、Sequence_Id 正确;不记录明文密码 |
|
||||
|
||||
## 5. Gateway 上游 Manager
|
||||
|
||||
| 职责组 | 当前入口/处理器 | 外部状态 | 不变量 |
|
||||
|---|---|---|---|
|
||||
| 连接池 | connect/disconnect、desired connections | 供应商 TCP、状态回调 | 连接数量与状态回调一致 |
|
||||
| 重连 | supervisor、退避、鉴权失败分类 | timer、连接状态 | 网络退避封顶;鉴权失败慢速重试;人工停用不自动拉起 |
|
||||
| Submit | 窗口、Sequence_Id、长短信分片 | CMPP TCP、tracker | 窗口不超限;每分片映射独立;结果可跨重启关联 |
|
||||
| 回执 | DELIVER receipt 解析与映射 | API/Redis Stream | 通道、目标号码、Msg_Id 联合约束,歧义不误配 |
|
||||
| 上行 | MO 解析与事件发布 | API/Redis Stream | 内容编码、源/目的号码和协议版本保持 |
|
||||
| 心跳 | ACTIVE_TEST 和断线判定 | timer、连接池 | 心跳超时只触发一次连接丢失处理 |
|
||||
|
||||
## 6. 其余大服务
|
||||
|
||||
### ChannelsService
|
||||
|
||||
- 公开职责:通道/通道组 CRUD、组成员、路由规则、连接状态、通道测试、健康指标、报备字段、报备任务/记录、回执导入、删除治理。
|
||||
- 主要表:`SmsChannel`、`SmsChannelGroup`、`SmsChannelGroupItem`、`ChannelRouteRule`、`CmppConnectionState`、`ChannelHealthMetric`、`ChannelReportField`、`ChannelSignatureReportTask/Record`。
|
||||
- 副作用:Gateway connect/disconnect、通道测试真实提交、报备文件导入导出。
|
||||
- 禁区:连接控制与数据库状态不可只完成一侧;通道测试不得混入业务计费;删除前依赖检查必须保留。
|
||||
|
||||
### SmsConfigService
|
||||
|
||||
- 公开职责:应用、签名、模板、引流信息、通用资料字段、客户安全视图、下游连接事件与停用生命周期。
|
||||
- 主要表:`SmsApplication`、`SmsSignature`、`SmsTemplate`、`SmsDrainageInfo`、`SignatureMaterial`、`SignatureReportMaterial`、`CmppDownstreamConnection/Delivery`、`AuditRecord`。
|
||||
- 副作用:审核/操作日志、Gateway 连接生命周期。
|
||||
- 禁区:客户端响应必须继续使用专用安全选择字段;审核状态与通道报备状态不得混为一列。
|
||||
|
||||
### OperationsService
|
||||
|
||||
- 公开职责:短信/上行/任务分页与导出、运营看板、发送质量、签名通道质量、操作日志、死信、下游投递与恢复、分片审计、全链路追踪、对账。
|
||||
- 主要表:短信、提交、回执、账务、连接、审计及企业配置相关读模型。
|
||||
- 副作用:CSV 输出、下游恢复查询/操作;其余应保持只读。
|
||||
- 禁区:分页总数必须来自数据库;上海时区边界不可依赖数据库会话时区;客户端查询不得泄露通道和内部路由。
|
||||
|
||||
### ReportMaterialsService
|
||||
|
||||
- 公开职责:官方模板、待生成资料分页/导出、导入配置、导入预览/提交、逐行/批量/整批审核、生成报备批次、批次导出和状态更新。
|
||||
- 主要表:`ReportMaterialImportProfile/Batch/Item`、`ReportMaterialBatch/Item`、`ReportExportFile/Item`、签名/引流/通道报备表。
|
||||
- 副作用:文件解析、导出文件、对象存储。
|
||||
- 禁区:导入不得自动审核通过;逐行、勾选和整批审核使用同一状态机;批次生成必须幂等。
|
||||
|
||||
## 7. 前端门面与样式
|
||||
|
||||
### adminApi.ts
|
||||
|
||||
`adminApi` 继续作为稳定导出。其方法按以下域归档,R1 拆分时只能在内部委托,不批量修改页面 import:
|
||||
|
||||
- auth/users/roles/permissions
|
||||
- tenants/applications/certification
|
||||
- channels/channelGroups/routes/connections/tests
|
||||
- signatures/templates/drainage/reportMaterials
|
||||
- riskReview/blacklist/phoneFrequency
|
||||
- operations/dashboard/messages/uplink/tracing
|
||||
- reports/reconciliation/profit/quality
|
||||
- billing/accounts/recharges
|
||||
- files/audit/deletion governance
|
||||
|
||||
HTTP 基址、认证头、错误转换、下载和上传只能由 core 层统一实现;业务域文件不得各写一套。
|
||||
|
||||
### global.css
|
||||
|
||||
R3 前只允许新增带页面或组件命名空间的规则。拆分时按 token、基础组件、布局、页面域、响应式五层迁移;每批迁移后做桌面和窄屏截图对比。禁止一边迁移一边重新设计页面。
|
||||
|
||||
## 8. R0 固定特征测试矩阵
|
||||
|
||||
| 门禁 | 命令 | 覆盖 |
|
||||
|---|---|---|
|
||||
| 队列契约 | `node tools/spike/validate-gateway-queue-contract.mjs` | Redis Stream 四类消息 |
|
||||
| R0 清单 | `node tools/quality/verify-refactor-r0.mjs` | 核心门面、契约样例、关键测试名称 |
|
||||
| 发送链 | `npm --prefix api test -- --runInBand --forceExit send-chain.service.spec.ts` | 并发、幂等、路由、回执、下游 |
|
||||
| 账务 | `npm --prefix api test -- --runInBand --forceExit billing.service.spec.ts` | 冻结、扣费、释放、退款、并发重放 |
|
||||
| Gateway | `go test ./...`(`gateway` 目录) | CMPP、ACK、重连、队列和 tracker |
|
||||
| Gateway 静态检查 | `go vet ./...`(`gateway` 目录) | Go 静态错误 |
|
||||
| API 全量 | `npm --prefix api test -- --runInBand --forceExit` | NestJS 全量行为 |
|
||||
| 构建 | `npm --prefix api run build`、`npm run build` | API/前端类型及生产产物 |
|
||||
|
||||
R0 本身不移动生产代码;后续版本若修改了上述不变量,必须先把它作为独立 Bug/需求处理,不得伪装为“纯拆分”。
|
||||
Reference in New Issue
Block a user