Files
junhong_cmp_fiber/.planning/codebase/ARCHITECTURE.md

180 lines
11 KiB
Markdown
Raw Permalink 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.
# 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*