docs(02-01): 完成 PERM+FIN+POLL 9项修复计划 PERM-01/02 FIN-01/02/03 POLL-01/02/03/04

This commit is contained in:
2026-03-28 10:46:10 +08:00
parent 754bbcdf3b
commit e1ec024904
18 changed files with 5548 additions and 28 deletions

View File

@@ -0,0 +1,179 @@
# Architecture
**Analysis Date:** 2026-03-27
## Pattern Overview
**Overall:** 双进程分层单体API + Worker核心代码按 `Handler → Service → Store → Model` 组织。
**Key Characteristics:**
- HTTP 入口与异步任务入口分离:`cmd/api/main.go` 负责 Fiber API`cmd/worker/main.go` 负责 Asynq Worker 与轮询调度。
- 依赖集中在 `internal/bootstrap/` 组装,再分发给 `internal/routes/``internal/handler/``internal/task/`
- 数据访问主要走 `internal/store/postgres/*.go`,模型与 DTO 分别位于 `internal/model/*.go``internal/model/dto/*.go`
- 路由注册统一经过 `internal/routes/registry.go``Register()`,同时驱动 Fiber 路由与 OpenAPI 元数据。
- 多租户与权限控制主要依赖认证上下文 + Store 层显式过滤函数,而不是数据库外键或 ORM 关联。
## Layers
**Entrypoint / Composition Layer:**
- Purpose: 进程启动、基础设施初始化、依赖装配、生命周期管理。
- Location: `cmd/api/main.go`, `cmd/worker/main.go`, `internal/bootstrap/*.go`
- Contains: 配置加载、日志、数据库、Redis、队列、对象存储、Gateway 客户端、Bootstrap 编排。
- Depends on: `pkg/config`, `pkg/database`, `pkg/logger`, `pkg/queue`, `pkg/storage`, `internal/bootstrap`
- Used by: 整个应用进程。
**Routing / Transport Layer:**
- Purpose: 路由域划分、认证挂载、HTTP 入参与响应封装、OpenAPI 文档注册。
- Location: `internal/routes/*.go`, `internal/handler/**/*.go`
- Contains: `RegisterRoutesWithDoc()` 总入口、Admin/Auth/Personal 域路由、Fiber Handler。
- Depends on: `internal/bootstrap.Handlers`, `internal/bootstrap.Middlewares`, `pkg/openapi`, `pkg/response`
- Used by: `cmd/api/main.go`
**Service Layer:**
- Purpose: 业务规则、权限判断、事务编排、幂等、异步任务提交、跨模块协作。
- Location: `internal/service/**/*.go`
- Contains: 账号、订单、套餐、轮询、设备、卡、认证、充值、佣金等服务。
- Depends on: `internal/store/postgres`, `pkg/errors`, `pkg/middleware`, `pkg/queue`, `pkg/wechat`, `gorm.DB`, `redis.Client`
- Used by: Handler 层、Worker Bootstrap、Task Handler。
**Store Layer:**
- Purpose: SQL 查询、分页、显式数据权限过滤、缓存辅助、事务基础能力。
- Location: `internal/store/store.go`, `internal/store/options.go`, `internal/store/postgres/*.go`
- Contains: 每个聚合的 PostgreSQL Store例如 `internal/store/postgres/account_store.go`, `internal/store/postgres/order_store.go`
- Depends on: `gorm`, `redis`, `pkg/middleware/data_scope.go`
- Used by: Service 层、部分 Handler 组装期专用读模型。
**Model Layer:**
- Purpose: 持久化模型、DTO、常量化状态值、表名映射。
- Location: `internal/model/*.go`, `internal/model/dto/*.go`
- Contains: `model.Account`, `model.Order`, `model.Shop`, `model.PollingConfig` 以及请求/响应 DTO。
- Depends on: GORM tags、标准库类型。
- Used by: Store、Service、Handler、OpenAPI 生成。
**Task / Worker Layer:**
- Purpose: Asynq 任务处理、轮询调度、定时任务、批处理导入。
- Location: `internal/task/*.go`, `internal/polling/*.go`, `pkg/queue/*.go`
- Contains: `pkg/queue/handler.go` 任务注册器、`internal/task/polling_handler.go``internal/task/order_expire.go``internal/polling/scheduler.go`
- Depends on: Worker bootstrap 产出的 stores/services、Redis、Asynq、Gateway。
- Used by: `cmd/worker/main.go`
## Data Flow
**API Request Flow:**
1. `cmd/api/main.go` 创建 Fiber 应用并注册 Recover、RequestID、访问日志、压缩中间件。
2. `internal/routes/routes.go` 将请求分发到 `/api/auth``/api/admin``/api/c/v1``/api/callback` 四个域。
3. `pkg/middleware/auth.go``internal/middleware/personal_auth` 将用户信息写入 Fiber `Locals` 和标准 `context.Context`
4. Handler 解析 DTO 并调用 Service例如 `internal/handler/admin/account.go``internal/service/account/service.go`
5. Service 调用一个或多个 Store必要时使用 `db.WithContext(ctx).Transaction(...)` 编排事务,例如 `internal/service/order/service.go`
6. Store 在查询中显式调用 `pkg/middleware/data_scope.go` 的过滤函数,例如 `internal/store/postgres/account_store.go``internal/store/postgres/order_store.go`
7. Handler 通过 `pkg/response/response.go` 返回统一响应;异常交由 `internal/middleware.ErrorHandler` 处理。
**Worker Flow:**
1. `cmd/worker/main.go` 初始化 PostgreSQL、Redis、Asynq Client、对象存储、Gateway。
2. `internal/bootstrap/worker.go` 调用 `initWorkerStores()``initWorkerServices()`,生成 `queue.WorkerBootstrapResult`
3. `pkg/queue/handler.go` 基于 Bootstrap 结果注册导入、轮询、佣金、超时取消、告警、清理等任务处理器。
4. `internal/polling/scheduler.go` 独立运行轮询调度循环:从 Redis Sorted Set / List 取卡片,再投递到 Asynq。
5. `cmd/worker/main.go` 还额外创建 Asynq Scheduler注册 `TaskTypeOrderExpire``TaskTypeAlertCheck``TaskTypeDataCleanup`
6. 具体任务处理器调用 Service 或直接使用 Store/DB 更新状态,例如 `internal/task/order_expire.go``order.Service.CancelExpiredOrders()`
**State Management:**
- HTTP 侧状态主要放在 PostgreSQL请求态身份与数据权限范围放在 `context.Context`
- 高速状态、Token、轮询队列、并发信号量、下级店铺缓存放在 Redis`pkg/auth/token.go``internal/polling/scheduler.go``internal/store/postgres/shop_store.go`
- 异步处理采用 Redis-backed Asynq 队列,由 `pkg/queue/client.go` 提交、`pkg/queue/server.go` 消费。
## Key Abstractions
**BootstrapResult / WorkerBootstrapResult:**
- Purpose: 启动期依赖编排结果。
- Examples: `internal/bootstrap/bootstrap.go`, `internal/bootstrap/worker.go`
- Pattern: 显式依赖注入,不使用外部 DI 框架。
**Handlers / Middlewares 容器:**
- Purpose: 路由注册时的装配清单。
- Examples: `internal/bootstrap/types.go`, `internal/bootstrap/handlers.go`, `internal/bootstrap/middlewares.go`
- Pattern: 结构体聚合所有已构造的 Handler 与 Middleware。
**RouteSpec + Register():**
- Purpose: 单次声明同时完成真实路由注册和 OpenAPI 元数据注册。
- Examples: `internal/routes/registry.go`, `internal/routes/account.go`, `internal/routes/personal.go`
- Pattern: 代码即文档DTO 驱动文档生成。
**QueryOptions:**
- Purpose: Store 通用分页与排序选项。
- Examples: `internal/store/options.go`, `internal/service/account/service.go`, `internal/service/order/service.go`
- Pattern: Service 先构造查询选项,再下传到 Store。
**UserContextInfo + Data Scope Filters:**
- Purpose: 承载用户身份与可管理店铺范围,并在 Store 查询时执行过滤。
- Examples: `pkg/middleware/auth.go`, `pkg/middleware/data_scope.go`, `internal/store/postgres/shop_store.go`
- Pattern: 中间件预计算 `SubordinateShopIDs`Store 按字段类型显式调用 `ApplyShopFilter` / `ApplyEnterpriseFilter` / `ApplySellerShopFilter`
## Entry Points
**API Main:**
- Location: `cmd/api/main.go`
- Triggers: `go run cmd/api/main.go`、API 容器启动。
- Responsibilities: 加载配置、初始化基础设施、执行 `bootstrap.Bootstrap()`、创建 Fiber、注册中间件和路由、生成运行时 OpenAPI 文件 `logs/openapi.yaml`
**Worker Main:**
- Location: `cmd/worker/main.go`
- Triggers: `go run cmd/worker/main.go`、Worker 容器启动。
- Responsibilities: 初始化 Worker 依赖、执行 `bootstrap.BootstrapWorker()`、启动 Asynq Worker、启动轮询调度器和 Asynq Scheduler、优雅关闭。
**OpenAPI Generation CLI:**
- Location: `cmd/gendocs/main.go`
- Triggers: 手动执行文档生成。
- Responsibilities: 通过 `openapi.BuildDocHandlers()` + `routes.RegisterRoutesWithDoc()` 输出 `docs/admin-openapi.yaml`
## Routing Strategy
**Domain Split:**
- `/api/auth`:统一后台/H5 认证,见 `internal/routes/auth.go`
- `/api/admin`:管理后台业务域,见 `internal/routes/admin.go`
- `/api/c/v1`:个人客户端域,见 `internal/routes/personal.go`
- `/api/callback`:支付回调,无认证,见 `internal/routes/order.go``internal/handler/callback/payment.go`
**Registration Style:**
- 所有 HTTP 接口使用 `internal/routes/registry.go``Register()`,文档规范见 `docs/api-documentation-guide.md`
- `internal/routes/admin.go` 以 handler 是否为 `nil` 决定是否挂载模块,便于文档生成和裁剪。
- `internal/routes/personal.go` 明确区分公开路由与认证路由,并依赖注册顺序确保公开接口不被 `Use()` 拦截。
## Error Handling
**Strategy:** 统一错误码 + 全局 Fiber ErrorHandler。
**Patterns:**
- Handler 层只做参数解析与转发,返回 `pkg/errors` 中的 `AppError`,如 `internal/handler/admin/account.go`
- Service 层封装业务错误,不直接向外泄露底层错误文本,如 `internal/service/account/service.go``internal/service/order/service.go`
- Worker 任务记录结构化日志,并将可重试与不可重试逻辑写在处理器内部,如 `internal/task/order_expire.go``internal/task/polling_handler.go`
## Cross-Cutting Concerns
**Logging:**
- `pkg/logger/logger.go` 初始化 App/Access 日志;`pkg/logger/middleware.go` 记录完整请求/响应摘要。
**Validation:**
- Fiber `BodyParser` / `QueryParser` + `validator.v10`,示例见 `internal/handler/auth/handler.go`
**Authentication:**
- 后台/H5 使用 Redis Token + `pkg/middleware/auth.go`;个人客户使用单独中间件,由 `internal/bootstrap/middlewares.go` 创建。
**OpenAPI:**
- 路由元数据写在 `RouteSpec`;文档生成器依赖 `cmd/api/docs.go``cmd/gendocs/main.go``pkg/openapi/handlers.go` 三处手工维护 Handler 清单。
**Persistence Rules:**
- 模型显式声明 `TableName()` 与字段列名,如 `internal/model/account.go`, `internal/model/order.go`, `internal/model/shop.go`
- 数据库结构通过 `migrations/*.sql` 管理,`pkg/database/postgres.go` 明确禁用自动建表。
## Architectural Deviations
**README / 规范与当前实现存在的偏差:**
- `README.md``openspec/config.yaml` 多处描述“GORM Callback 自动注入数据权限过滤”,但当前 `internal/bootstrap/bootstrap.go` 第 67 行明确写明“数据权限过滤已移至 Store 层显式调用 ApplyXxxFilter 函数”。当前保留的 GORM Callback 只有 `pkg/gorm/callback.go` 中的创建人/更新人自动填充。
- `internal/bootstrap/handlers.go` 为客户端场景直接新建多个 Store 和一个 `client_order` Service而不是完全只消费 `services` 聚合;这说明 Handler 装配阶段存在读模型/适配层级的额外拼装。
- `internal/task/polling_handler.go` 在首次实名激活流程中先构造 Asynq 任务,但实际通过 Redis List `RPush` 提交,文件内注释明确写出“实际应该通过依赖注入 asynq.Client”这是 Worker 流程中的特殊机制与待收敛点。
---
*Architecture analysis: 2026-03-27*