Files
junhong_cmp_fiber/ARCHITECTURE.md
break e134552ec5
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m35s
更新
2026-08-13 12:31:47 +08:00

150 lines
12 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.
# 系统架构地图
## 系统职责与非职责
本系统保存物联网卡、设备、套餐、订单、钱包、分佣、审批、通知和审计的本地业务事实并协调支付、运营商、Gateway、企业微信、短信和对象存储。第三方内部状态、审批流配置、支付清算和运营商网络行为不是本系统事实外部契约见 [`docs/integrations/`](docs/integrations/)。
## Composition Roots 与运行单元
| 单元 | 入口 | 启动链与外部依赖 | 验证 |
| --- | --- | --- | --- |
| HTTP API | `cmd/api/main.go` | Viper 配置 → 日志目录 → PostgreSQL → Redis/Asynq Client → JWT/验证码 → Storage/Gateway → `internal/bootstrap.Bootstrap` → Fiber 中间件与路由;启动时写 `logs/openapi.yaml` | `go build ./cmd/api`;启动后请求 `/health``/ready` |
| Worker | `cmd/worker/main.go` | PostgreSQL、Redis、Storage、Gateway → `internal/bootstrap.BootstrapWorker` → Task Handler、Outbox Relay、任务救援角色决定单例调度模块 | `go build ./cmd/worker`;从应用日志核对角色、模块和任务注册 |
| OpenAPI 生成 | `cmd/gendocs/main.go` | 使用占位 Handler 复用 `internal/routes.RegisterRoutesWithDoc`,生成接口文档 | `go run cmd/gendocs/main.go`;它证明路由元数据,不单独证明真实 Bootstrap 可达性 |
| 治理 CLI | `cmd/foundation-check``cmd/audit-coverage``cmd/migration-finalize``cmd/audit-retention-simulate` | 读取源码、迁移或数据库状态并输出门禁/模拟结果 | 逐命令检查输入;`audit-coverage` 当前仍依赖已删除路径,见工程约束 BLOCKED 项 |
启动、隔离数据库、smoke 与日志命令见 [`README.md`](README.md)。生产为独立的 systemd 手工二进制发布拓扑,不以 Docker Compose 或 CI 工作流为准;运行事实与人工发布边界见 [`docs/deployment/production-runbook.md`](docs/deployment/production-runbook.md)。
## Worker 角色
`pkg/config/config.go` 接受 `all``leader``consumer`
- 三种角色都启动 Asynq 消费服务、Outbox Relay 和未完成导入任务救援。
- `leader``all` 额外启动轮询初始化器、轮询调度器和 Asynq Scheduler。
- `consumer` 不运行单例调度模块,适合横向扩展消费能力。
- 实际启停判断位于 `cmd/worker/main.go``buildWorkerModuleStatus``runsSingletonModules`
## HTTP 路由与信任边界
总入口是 `internal/routes/routes.go`
| 路由域 | 认证/信任边界 | 主要入口 | 当前 Spec 索引 |
| --- | --- | --- | --- |
| `/health``/ready` | 公开,只返回进程健康/就绪状态 | `internal/routes/health.go` | `openspec/specs/operations-audit/spec.md` |
| `/api/auth` | 后台账号登录、刷新、登出与当前身份;认证中间件在路由组装配 | `internal/routes/auth.go` | `openspec/specs/identity-access/spec.md` |
| `/api/admin` | 后台认证、角色权限、店铺/企业数据范围;业务层仍需资源级校验 | `internal/routes/admin.go` | `identity-access``asset-device``package-lifecycle``order-payment-wallet``operations-audit` |
| `/api/c/v1` | 个人客户 Token 与资产归属边界 | `internal/routes/personal.go` | `openspec/specs/personal-customer/spec.md` |
| `/api/open/v1` | 代理 Open API 独立认证/签名,不复用后台账号权限 | `internal/routes/open.go` | `openspec/specs/agent-open-api/spec.md` |
| `/api/callback` | 无登录认证;每类渠道必须在 Handler/Adapter 内验签、解密、校验金额或事件身份 | `internal/routes/order.go``wecom_callback.go` 及运营商回调注册 | `openspec/specs/external-integration/spec.md``order-payment-wallet` |
HTTP 入参、回调 Body/Header、上传文件和代理签名材料均是不可信输入。Handler 只做绑定、边界校验与响应;状态和金额不变量必须由业务用例重新判断。
## 分层与实际共存结构
- `internal/routes`:可达 HTTP 入口与 RouteSpec。
- `internal/handler`Fiber 信任边界、参数绑定和统一响应。
- `internal/application`新用例编排、事务脚本、Port 和可靠副作用边界。
- `internal/domain`:状态、金额与并发不变量;不依赖 Fiber、GORM、Redis、Asynq 或外部 SDK。
- `internal/query`:只读权限、联表、分页和 DTO 投影。
- `internal/service`:旧用例;允许直接使用 Store/GORM按完整用例渐进迁移。
- `internal/infrastructure`Port Adapter、GORM Repository、Outbox、审计与外部交互。
- `internal/store/postgres``internal/model`:旧持久化模型与显式查询。
- `internal/bootstrap`API/Worker 依赖装配,不承载业务规则。
- `pkg`:跨模块基础能力和第三方客户端。
- `migrations`Schema 演进事实。
允许方向Handler → Application/Query/旧 ServiceApplication → Domain + PortInfrastructure → Application/Domain Port旧 Service → Store/GORM。禁止 Domain 依赖框架或基础设施、Query 修改状态、Handler 拼装领域规则、Infrastructure 定义业务状态机。
```bash
! grep -RIlE 'gofiber|gorm.io|go-redis|hibiken/asynq' internal/domain --include='*.go'
```
## 关键业务模块导航
以下表是入口地图,不替代业务 Specs事务与副作用必须按具体调用链证明。
| 业务能力 | HTTP/异步入口 | 用例与领域 | 持久化/外部边界 | Spec |
| --- | --- | --- | --- | --- |
| 后台身份、账号、角色、权限、店铺 | `routes/auth.go``account.go``role.go``permission.go``shop.go` | `handler/auth|admin`;旧 `service/auth|account|role|permission|shop`;新 `application/role|shop|accessaudit|accountaudit` | `store/postgres`、RBAC 中间件、审计 Writer | [`identity-access`](openspec/specs/identity-access/spec.md) |
| 卡、设备、资产与企业授权 | `iot_card.go``device.go``asset*.go``enterprise_card.go``enterprise_device.go`;导入/轮询任务 | 旧 `service/iot_card|device|asset|enterprise_*``application/cardobservation``domain/cardobservation`;读取 `query/asset` | GORM、Gateway、运营商、Outbox | [`asset-device`](openspec/specs/asset-device/spec.md) |
| 套餐、系列、授权、订购与到期 | `package*.go`、批量分配/调价/订购路由;激活、到期和失效任务 | 旧 `service/package|package_series|shop_*``domain/package``application/packageexpiry` | GORM、Asynq、Outbox/通知 | [`package-lifecycle`](openspec/specs/package-lifecycle/spec.md) |
| 订单、支付、充值、钱包、退款、分佣与换货 | `order.go``agent_recharge.go``refund.go``commission.go``exchange.go`、支付回调 | 旧 `service/order|recharge|refund|commission_*|exchange``application/agentrecharge|wallet|refundapproval|approval|exchange``domain/agentrecharge|wallet|approval` | GORM、支付 Adapter、企微、Outbox、审计/资金事实 | [`order-payment-wallet`](openspec/specs/order-payment-wallet/spec.md) |
| 个人客户 | `routes/personal.go` 及 C 端子路由 | `handler/app``service/client_*|personal_customer|customer_binding`;通知 Application/Query | Token、资产绑定、钱包、支付、Gateway | [`personal-customer`](openspec/specs/personal-customer/spec.md) |
| 代理 Open API | `routes/open.go` | `handler/openapi``service/agent_open_api` | 独立认证、店铺数据范围、卡/套餐/钱包 Store | [`agent-open-api`](openspec/specs/agent-open-api/spec.md) |
| 轮询、通知、导出、配置与审计调查 | `polling_*.go``notification.go``export_task.go``system_config.go``audit.go`Scheduler/Task/Outbox | `application/notification|systemconfig|auditarchive|outbox``query/audit|notification|outbox|systemconfig`;旧 polling/export Service | Asynq、Outbox、审计库、Integration Log、对象存储 | [`operations-audit`](openspec/specs/operations-audit/spec.md) |
| 外部集成接点 | 支付/运营商/企微回调Gateway 调用,对象存储、短信 | `internal/gateway``internal/infrastructure/wecom|payment|carriercallback|integrationlog``pkg/alipay|wechat|fuiou|sms|storage` | 第三方网络和凭证;协议事实见 integrations | [`external-integration`](openspec/specs/external-integration/spec.md) |
任务 7 会把当前过大的 Spec 索引拆成完整业务能力;拆分后必须同步本表链接。
## 同步数据流
```text
HTTP Route
→ 认证/数据范围中间件
→ Handler绑定与信任边界
→ Application / Query / 旧 Service
→ Domain仅适用于已迁移复杂写
→ Repository / Store / GORM
→ pkg/response 或全局 ErrorHandler
```
并非所有写操作都在统一事务中:旧 Service 可能直接写 Store新 Application 可能使用事务脚本,复杂写可能通过 Domain + Port。审计、状态条件和事务范围必须从具体用例证明不能由这张地图推断。
## 异步与可靠事件
### Asynq Task
Worker 当前处理的任务族包括:卡/设备导入、导出分片与收尾、资产批量购包、订单套餐失效、自动充值后购包、佣金、订单过期、五类轮询、告警、数据/通知清理、套餐到期提醒、每日流量落盘、企微审批同步/恢复、代理充值恢复、审计/集成归档和 Outbox 投递。
发现真实注册与生产位置:
```bash
rg -o 'constants\.TaskType[A-Za-z0-9_]+' cmd/worker/main.go internal/task internal/bootstrap/worker.go internal/infrastructure | sort -u
```
直接 `EnqueueTask` 只证明已提交,不等于业务终态成功;最终状态由 Task Handler 的条件更新、记录或补偿决定。
### Outbox
当前事件族覆盖审批提交/终态、卡实名/流量/网络/系列、代理充值支付确认、代理主钱包扣款/预留/入账/退款,以及后台/个人通知。
```bash
rg -o 'constants\.OutboxEventType[A-Za-z0-9_]+' cmd/worker/main.go internal | sort -u
```
需要业务事实与可靠异步副作用原子提交的用例应同事务写 Outbox是否已经做到必须逐调用链核对。Relay 和消费者按至少一次投递设计,消费者必须容忍重复。
## 外部调用与回调
- 支付:创建本地支付事实后调用渠道;回调需要验签、金额/业务号/状态校验,重复回调不得重复入账。
- 企业微信:本地审批实例 → Outbox 提交 → 渠道审批;加密回调、轮询和人工同步进入统一状态同步语义。
- Gateway/运营商:查询与控制结果来自外部;超时或未知结果不得理想化为成功。
- 对象存储:上传/预签名 URL 是外部副作用,数据库只保存对象引用。
- SMS发送结果是外部通知结果不等同业务事务成功。
外部网络调用不应持有长数据库事务;确需提交后可靠执行时使用 Outbox/Task。实际例外按 As-Is Spec 记录。
## 事务、审计与日志边界
- Access LogHTTP 调试事实。
- Audit Event操作者、资源、动作、前后值和结果。
- Domain Ledger/钱包交易:金额与状态领域事实。
- Integration Log外部调用尝试和脱敏结果。
- Outbox提交后可靠副作用不替代前三者。
成功高风险操作是否与审计同事务必须逐用例核对。业务已回滚后的 failed/denied 审计使用独立短事务。不得用 Access Log 证明资金或状态事实。
## 结构与生成验证
```bash
go build ./cmd/api ./cmd/worker
go run cmd/gendocs/main.go
openspec doctor --json
openspec validate --all
./scripts/context-health.sh
! grep -RIlE 'gofiber|gorm.io|go-redis|hibiken/asynq' internal/domain --include='*.go'
rg 'Register[A-Za-z]*Routes|register[A-Za-z]*Routes' internal/routes
```
OpenAPI 生成器使用占位 Handler因此还要对照 `internal/bootstrap` 与真实路由装配。Schema 事实以 `migrations/` 和隔离数据库为准,禁止使用 Makefile 中的历史连接信息。