150 lines
11 KiB
Markdown
150 lines
11 KiB
Markdown
# 系统架构地图
|
||
|
||
## 系统职责与非职责
|
||
|
||
本系统保存物联网卡、设备、套餐、订单、钱包、分佣、审批、通知和审计的本地业务事实,并协调支付、运营商、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)。
|
||
|
||
## 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/旧 Service;Application → Domain + Port;Infrastructure → 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 Log:HTTP 调试事实。
|
||
- 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 中的历史连接信息。
|