docs(02-01): 完成 PERM+FIN+POLL 9项修复计划 PERM-01/02 FIN-01/02/03 POLL-01/02/03/04
This commit is contained in:
@@ -21,14 +21,14 @@
|
||||
|
||||
### PERM — 企业端权限完善(方案 B)
|
||||
|
||||
- [ ] **PERM-01**: 移除 Resolve 接口对企业账号的错误拦截 — 企业账号应能查询被授权的资产
|
||||
- [ ] **PERM-02**: Refresh 接口新增企业账号拦截 — 企业只读,不允许主动触发运营商刷新
|
||||
- [x] **PERM-01**: 移除 Resolve 接口对企业账号的错误拦截 — 企业账号应能查询被授权的资产
|
||||
- [x] **PERM-02**: Refresh 接口新增企业账号拦截 — 企业只读,不允许主动触发运营商刷新
|
||||
|
||||
### FIN — 财务/提现流程完善(方案 C)
|
||||
|
||||
- [ ] **FIN-01**: 提现审批人字段补全 — `Approve()` 写入 `approved_by` / `approved_at`(当前永远为空)
|
||||
- [ ] **FIN-02**: 提现拒绝 remark 必填校验 — `Reject()` 在 BodyParser 后补充 `validator.Validate()`
|
||||
- [ ] **FIN-03**: 激活配置并发保护 — 提现配置和微信配置激活时,两步 UPDATE 前加 `FOR UPDATE` 行锁
|
||||
- [x] **FIN-01**: 提现审批人字段补全 — `Approve()` 写入 `approved_by` / `approved_at`(当前永远为空)
|
||||
- [x] **FIN-02**: 提现拒绝 remark 必填校验 — `Reject()` 在 BodyParser 后补充 `validator.Validate()`
|
||||
- [x] **FIN-03**: 激活配置并发保护 — 提现配置和微信配置激活时,两步 UPDATE 前加 `FOR UPDATE` 行锁
|
||||
|
||||
### REALNAME — 实名激活架构重构(方案 G)
|
||||
|
||||
@@ -40,10 +40,10 @@
|
||||
|
||||
### POLL — 轮询系统小修(方案 H)
|
||||
|
||||
- [ ] **POLL-01**: `polling:protect` 接入调度 — `scheduler.go` 补充 processManualQueue + processTimedQueue 对 protect 任务的调度
|
||||
- [ ] **POLL-02**: gatewayClient=nil 时停复机返回错误 — `stopCardWithRetry` 和 `resumeCardWithRetry` 两处改为返回业务错误而非 nil
|
||||
- [ ] **POLL-03**: packages 接口新增分页 — `GetPackages()` 默认 page=1, pageSize=50,最大 100
|
||||
- [ ] **POLL-04**: Series 删除前检查关联套餐 — `package_series/service.go:Delete()` 前置 `CountBySeriesID()` 检查
|
||||
- [x] **POLL-01**: `polling:protect` 接入调度 — `scheduler.go` 补充 processManualQueue + processTimedQueue 对 protect 任务的调度
|
||||
- [x] **POLL-02**: gatewayClient=nil 时停复机返回错误 — `stopCardWithRetry` 和 `resumeCardWithRetry` 两处改为返回业务错误而非 nil
|
||||
- [x] **POLL-03**: packages 接口新增分页 — `GetPackages()` 默认 page=1, pageSize=50,最大 100
|
||||
- [x] **POLL-04**: Series 删除前检查关联套餐 — `package_series/service.go:Delete()` 前置 `CountBySeriesID()` 检查
|
||||
|
||||
### DEVICE — 设备体系完善(方案 D)
|
||||
|
||||
@@ -128,20 +128,20 @@
|
||||
| CRITICAL-06 | Phase 1 | Complete |
|
||||
| CRITICAL-07 | Phase 1 | Complete |
|
||||
| CRITICAL-08 | Phase 1 | Complete |
|
||||
| PERM-01 | Phase 2 | Pending |
|
||||
| PERM-02 | Phase 2 | Pending |
|
||||
| FIN-01 | Phase 2 | Pending |
|
||||
| FIN-02 | Phase 2 | Pending |
|
||||
| FIN-03 | Phase 2 | Pending |
|
||||
| PERM-01 | Phase 2 | Complete |
|
||||
| PERM-02 | Phase 2 | Complete |
|
||||
| FIN-01 | Phase 2 | Complete |
|
||||
| FIN-02 | Phase 2 | Complete |
|
||||
| FIN-03 | Phase 2 | Complete |
|
||||
| REALNAME-01 | Phase 2 | Complete |
|
||||
| REALNAME-02 | Phase 2 | Complete |
|
||||
| REALNAME-03 | Phase 2 | Complete |
|
||||
| REALNAME-04 | Phase 2 | Complete |
|
||||
| REALNAME-05 | Phase 2 | Complete |
|
||||
| POLL-01 | Phase 2 | Pending |
|
||||
| POLL-02 | Phase 2 | Pending |
|
||||
| POLL-03 | Phase 2 | Pending |
|
||||
| POLL-04 | Phase 2 | Pending |
|
||||
| POLL-01 | Phase 2 | Complete |
|
||||
| POLL-02 | Phase 2 | Complete |
|
||||
| POLL-03 | Phase 2 | Complete |
|
||||
| POLL-04 | Phase 2 | Complete |
|
||||
| DEVICE-01 | Phase 3 | Pending |
|
||||
| DEVICE-02 | Phase 3 | Pending |
|
||||
| DEVICE-03 | Phase 3 | Pending |
|
||||
|
||||
@@ -56,7 +56,7 @@ Plans:
|
||||
**Plans**: 2 plans
|
||||
|
||||
Plans:
|
||||
- [ ] 02-01-PLAN.md — PERM-01/02 + FIN-01/02/03 + POLL-01/02/03/04:权限/财务/轮询小修合并(Wave 1)
|
||||
- [x] 02-01-PLAN.md — PERM-01/02 + FIN-01/02/03 + POLL-01/02/03/04:权限/财务/轮询小修合并(Wave 1)
|
||||
- [x] 02-02-PLAN.md — REALNAME-01~05:实名激活架构重构,原子执行(Wave 1,parallel)
|
||||
|
||||
---
|
||||
|
||||
@@ -3,12 +3,12 @@ gsd_state_version: 1.0
|
||||
milestone: v1.0
|
||||
milestone_name: milestone
|
||||
status: unknown
|
||||
last_updated: "2026-03-28T02:41:33.151Z"
|
||||
last_updated: "2026-03-28T02:45:27.917Z"
|
||||
progress:
|
||||
total_phases: 6
|
||||
completed_phases: 1
|
||||
completed_phases: 2
|
||||
total_plans: 7
|
||||
completed_plans: 6
|
||||
completed_plans: 7
|
||||
---
|
||||
|
||||
# Project State — 君鸿卡管系统
|
||||
@@ -77,6 +77,7 @@ Progress: [ 1 ]──[ 2 ]──[ 3 ]──[ 4 ]──[ 5 ]──[ 6 ]
|
||||
| Phase 01-p0 P02 | 3min | 1 tasks | 2 files |
|
||||
| Phase 01-p0 P03 | 8min | 2 tasks | 4 files |
|
||||
| Phase 02-perm-fin-realname-poll P02 | 25min | 2 tasks | 8 files |
|
||||
| Phase 02-perm-fin-realname-poll P01 | 10min | 2 tasks | 14 files |
|
||||
|
||||
## Accumulated Context
|
||||
|
||||
|
||||
179
.planning/codebase/ARCHITECTURE.md
Normal file
179
.planning/codebase/ARCHITECTURE.md
Normal 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*
|
||||
173
.planning/codebase/CONCERNS.md
Normal file
173
.planning/codebase/CONCERNS.md
Normal file
@@ -0,0 +1,173 @@
|
||||
# Codebase Concerns
|
||||
|
||||
**Analysis Date:** 2026-03-27
|
||||
|
||||
## Tech Debt
|
||||
|
||||
**配置与凭据管理(严重级别:Critical):**
|
||||
- Issue: 仓库内存在已提交的生产/测试环境敏感配置与直连外部基础设施的写法,配置边界没有被代码、脚本和部署文件统一约束。
|
||||
- Files: `docker-compose.prod.yml`, `Makefile`, `pkg/config/defaults/config.yaml`, `scripts/verify_migration/main.go`, `scripts/verify_indexes/main.go`, `docs/deployment/deployment-guide.md`
|
||||
- Impact: 凭据轮换成本高;泄漏后影响数据库、Redis、对象存储、短信网关、Gateway 与部署链路;开发、测试、生产边界容易串用。
|
||||
- Supporting evidence:
|
||||
- `docker-compose.prod.yml` 直接写入数据库、Redis、JWT、对象存储、Gateway、短信服务配置。
|
||||
- `Makefile` 的 `DB_URL` 内嵌远程数据库连接串。
|
||||
- `pkg/config/defaults/config.yaml` 为 `gateway.app_id`、`gateway.app_secret` 提供非空默认值,服务在未显式覆盖时仍会初始化 Gateway 客户端,见 `cmd/api/main.go:366-383`。
|
||||
- `scripts/verify_migration/main.go` 与 `scripts/verify_indexes/main.go` 直接写死远程 PostgreSQL DSN。
|
||||
- `docs/deployment/deployment-guide.md` 把 Registry 凭据与数据库连接示例直接写进文档。
|
||||
- Fix approach: 把所有密钥迁移到部署平台 Secret/环境注入;默认配置只保留空值;脚本统一改为读取环境变量;把示例文档改成占位符而不是真实值。
|
||||
- Suggested follow-up investigation topics:
|
||||
- 盘点 `openspec/`, `docs/`, `scripts/` 中所有历史凭据残留。
|
||||
- 审查 Gitea Secrets、Docker Registry、数据库、Redis、对象存储、短信网关、Gateway 的轮换计划。
|
||||
- 检查 `.env`、`.env.local` 是否已在团队机器与 Runner 上扩散。
|
||||
|
||||
**支付与微信配置迁移未闭环(严重级别:High):**
|
||||
- Issue: 设计已经切到 `tb_wechat_config` + `payment_config_id`,但代码与文档仍保留环境变量和单例支付实现,处于半迁移状态。
|
||||
- Files: `docs/wechat-config-management/功能总结.md`, `internal/service/order/service.go`, `internal/service/recharge/service.go`, `internal/handler/callback/payment.go`, `docs/environment-variables.md`, `scripts/verify-wechat.sh`, `docker-compose.prod.yml`
|
||||
- Impact: 多支付配置切换、旧订单验签、客户端支付发起、OAuth 配置一致性存在行为不一致风险;运维很难判断哪些配置源仍然生效。
|
||||
- Supporting evidence:
|
||||
- `docs/wechat-config-management/功能总结.md:232-238` 明确写着客户端支付发起仍是留桩,OAuth 仍从环境变量读取,且旧 `JUNHONG_WECHAT_PAYMENT_*` “可清理”。
|
||||
- `internal/service/order/service.go:2107,2163,2356,2362` 与 `internal/service/recharge/service.go:271`、`internal/handler/callback/payment.go:66,143` 仍保留 TODO/留桩。
|
||||
- `docs/environment-variables.md:36-69` 与 `scripts/verify-wechat.sh` 仍把微信支付环境变量当成必填启动前置条件。
|
||||
- `docker-compose.prod.yml` 注释称“微信配置已迁移至数据库”,但部署文件仍维持部分旧式环境变量与其他支付相关外部配置模式。
|
||||
- Fix approach: 明确单一配置源;把“已迁移”“留桩”“仍依赖环境变量”的边界写入运维文档;在代码层补齐动态加载或删除旧入口。
|
||||
- Suggested follow-up investigation topics:
|
||||
- 核实当前线上 OAuth 与支付分别读哪一套配置。
|
||||
- 核查 `payment_config_id` 相关回调链路是否覆盖订单、资产充值、代理充值三条路径。
|
||||
- 为未实现的客户端支付入口建立显式禁用清单。
|
||||
|
||||
**核心服务体量过大(严重级别:High):**
|
||||
- Issue: 部分核心服务文件已经远超团队规范中的函数/文件规模要求,维护成本和回归风险高。
|
||||
- Files: `internal/service/order/service.go`, `AGENTS.md`
|
||||
- Impact: 改动难以局部验证;支付、幂等、库存/余额、回调逻辑耦合;新需求更容易引入回归。
|
||||
- Supporting evidence:
|
||||
- `AGENTS.md:371-375` 要求函数长度 ≤ 100 行,核心逻辑建议 ≤ 50 行。
|
||||
- `internal/service/order/service.go` 的 TODO 已出现在 2107、2163、2356、2362 行,说明文件至少超过 2362 行。
|
||||
- Fix approach: 按支付发起、支付回调、订单状态流转、幂等与锁、钱包扣款等职责拆分子服务/辅助组件。
|
||||
- Suggested follow-up investigation topics:
|
||||
- 统计 `internal/service/` 中超长文件与超长函数。
|
||||
- 识别最常改动的热点段落与共享依赖。
|
||||
|
||||
## Known Bugs
|
||||
|
||||
**健康检查接口对故障返回 200 成功包裹(严重级别:High):**
|
||||
- Symptoms: PostgreSQL 或 Redis 故障时,`/health` 仍通过 `response.Success` 返回成功包裹,只在 `data.status` 中标记 `degraded`。
|
||||
- Files: `internal/handler/health.go`, `docker-compose.prod.yml`, `Dockerfile.api`, `debug-deployment.sh`
|
||||
- Trigger: 数据库或 Redis Ping 失败。
|
||||
- Workaround: 监控端必须解析响应体里的 `data.status` 与 `services.*.status`,不能只看 HTTP 状态码。
|
||||
- Supporting evidence:
|
||||
- `internal/handler/health.go:101-111` 注释与实现都明确“端点本身能响应即视为成功”。
|
||||
- `docker-compose.prod.yml` 与 `Dockerfile.api` 的健康检查只执行 `wget --spider http://127.0.0.1:3000/health`,不会校验 JSON 内容。
|
||||
- Suggested follow-up investigation topics:
|
||||
- 确认当前容器健康检查、反向代理、告警系统是否只看 200。
|
||||
- 评估是否需要新增 `readyz`/`livez` 区分活性与依赖可用性。
|
||||
|
||||
**API 路径文档与实际路由不一致(严重级别:Medium):**
|
||||
- Symptoms: 文档仍混用 `/api/v1`、`/api/auth`、`/api/c/v1`,读者很难判断当前真实入口。
|
||||
- Files: `README.md`, `internal/routes/routes.go`, `scripts/check-comment-paths.sh`
|
||||
- Trigger: 新人按 README 试运行、按旧注释实现客户端、或依据文档回归接口。
|
||||
- Workaround: 以 `internal/routes/routes.go` 为准核对真实挂载路径。
|
||||
- Supporting evidence:
|
||||
- `internal/routes/routes.go:21-39` 当前真实路由为 `/api/auth`、`/api/admin`、`/api/c/v1`、`/api/callback`。
|
||||
- `README.md:577-583` 仍展示 `v1 := app.Group("/api/v1")` 的旧限流示例;`README.md:588-624` 仍以 `/api/v1/users` 讲解请求流。
|
||||
- `scripts/check-comment-paths.sh` 只检查 `internal/handler/` 中残留 `/api/v1`,并不覆盖 `README.md`、`docs/`、`openspec/`。
|
||||
- Suggested follow-up investigation topics:
|
||||
- 扫描所有 `docs/`, `README.md`, `openspec/`, `specs/` 中的旧路径。
|
||||
- 明确哪些旧路径仍需要兼容,哪些应统一下线。
|
||||
|
||||
## Security Considerations
|
||||
|
||||
**部署流水线绕过 TLS 校验(严重级别:High):**
|
||||
- Risk: 工作流检出步骤通过 `GIT_SSL_NO_VERIFY=1` 跳过证书校验,供应链完整性依赖内网信任而不是证书验证。
|
||||
- Files: `.gitea/workflows/deploy.yaml`
|
||||
- Current mitigation: 注释说明是“内网自签名证书”。
|
||||
- Recommendations: 为 Gitea/Registry 配置受信 CA;移除跳过校验;至少把跳过范围限制在明确的单一主机与受控 Runner。
|
||||
- Supporting evidence:
|
||||
- `.gitea/workflows/deploy.yaml:23-27` 设置 `GIT_SSL_NO_VERIFY=1` 后执行 `git clone`。
|
||||
|
||||
**对外/回调错误信息泄漏底层细节(严重级别:Medium):**
|
||||
- Risk: 健康检查与富友回调把底层错误或内部状态直接返回给调用方,不符合统一错误暴露规范。
|
||||
- Files: `internal/handler/health.go`, `internal/handler/callback/payment.go`, `AGENTS.md`
|
||||
- Current mitigation: 有统一错误处理规范,但没有覆盖这两个特殊出口。
|
||||
- Recommendations: 对外返回统一错误码/固定消息,把底层错误仅写日志。
|
||||
- Supporting evidence:
|
||||
- `AGENTS.md:124-129` 明确禁止直接暴露底层错误。
|
||||
- `internal/handler/health.go:50-60,82-85` 直接返回 `err.Error()`。
|
||||
- `internal/handler/callback/payment.go:178-191` 直接把 `err.Error()` 拼入富友回调失败响应。
|
||||
|
||||
## Performance Bottlenecks
|
||||
|
||||
**默认限流仍使用内存存储且默认关闭(严重级别:Medium):**
|
||||
- Problem: 默认配置既不开启限流,也默认使用 `memory` 存储;多实例部署时无法形成统一配额。
|
||||
- Files: `pkg/config/defaults/config.yaml`, `cmd/api/main.go`, `README.md`
|
||||
- Cause: 配置更偏向本地开发便利,缺少生产默认值与环境级约束。
|
||||
- Improvement path: 明确生产必须启用 Redis 存储限流;在部署文档里给出环境变量模板;增加启动日志或自检提示当前限流模式。
|
||||
- Supporting evidence:
|
||||
- `pkg/config/defaults/config.yaml:87-93` 默认 `enable_rate_limiter: false`、`storage: memory`。
|
||||
- `cmd/api/main.go:236-278` 只有显式开启时才挂载限流,并根据配置选择 Redis 或内存存储。
|
||||
|
||||
## Fragile Areas
|
||||
|
||||
**OpenAPI 文档生成依赖手工双点维护(严重级别:Medium):**
|
||||
- Files: `AGENTS.md`, `docs/api-documentation-guide.md`, `cmd/api/docs.go`, `cmd/gendocs/main.go`
|
||||
- Why fragile: 新增 Handler 必须同时更新两个文件;流程靠人工记忆,历史上已经出现清单不一致并专门做过修复。
|
||||
- Safe modification: 每次新增 Handler 时同时检查 `cmd/api/docs.go`、`cmd/gendocs/main.go` 与生成产物 `docs/admin-openapi.yaml`/`logs/openapi.yaml`。
|
||||
- Test coverage: 未发现自动化校验这两份清单一致性的 CI 步骤。
|
||||
- Supporting evidence:
|
||||
- `AGENTS.md:53-65` 明确要求手工更新两个文件。
|
||||
- `docs/api-documentation-guide.md` 多处把“忘记在两个文件中添加新 Handler”列为最常见问题。
|
||||
- `.gitea/workflows/deploy.yaml` 中未见 `go run cmd/gendocs/main.go`、diff 校验或文档一致性检查。
|
||||
|
||||
**部署脚本与运行时约定已漂移(严重级别:Medium):**
|
||||
- Files: `debug-deployment.sh`, `docker-compose.prod.yml`, `README.md`, `docs/deployment/deployment-guide.md`
|
||||
- Why fragile: 诊断脚本仍假定旧环境变量名与外置配置文件存在,文档与当前嵌入式配置实现不完全一致。
|
||||
- Safe modification: 先以 `docker-compose.prod.yml`、`pkg/config/defaults/config.yaml`、`cmd/api/main.go` 为准修正运维脚本,再统一更新部署文档。
|
||||
- Test coverage: 未发现对诊断脚本、部署文档、健康检查语义的自动验证。
|
||||
- Supporting evidence:
|
||||
- `debug-deployment.sh:52-57` 仍 grep `DB_|CONFIG_ENV`,并尝试读取 `/app/configs/config.yaml`;当前配置前缀实际是 `JUNHONG_`,配置默认嵌入二进制,见 `README.md:651-689`、`pkg/config/defaults/config.yaml`。
|
||||
- `docs/deployment/deployment-guide.md:302-305` 仍建议从 `/app/.env` 拼接迁移命令,但容器实际入口用环境变量构造 DSN,见 `docker/entrypoint-api.sh:8-17`。
|
||||
|
||||
## Scaling Limits
|
||||
|
||||
**CI/CD 质量闸门过窄(严重级别:High):**
|
||||
- Current capacity: 当前工作流只做 clone、build、push、main 分支部署。
|
||||
- Limit: 文档一致性、脚本检查、编译前规范、迁移安全、健康语义等问题都可能直接进入镜像与部署环境。
|
||||
- Scaling path: 在 `.gitea/workflows/deploy.yaml` 前置独立质量作业,至少执行 `bash scripts/check-all.sh`、`go build ./...`、OpenAPI 生成/校验,并在部署前验证 compose 文件与镜像健康策略。
|
||||
- Supporting evidence:
|
||||
- `README.md:941-957` 声称脚本检查会在 CI/CD 自动执行。
|
||||
- `.gitea/workflows/deploy.yaml` 未出现 `scripts/check-all.sh`、`go test`、`go vet`、`go build ./...`、OpenAPI 校验等步骤。
|
||||
|
||||
## Dependencies at Risk
|
||||
|
||||
**Go 版本与运行基础不统一(严重级别:Medium):**
|
||||
- Risk: 版本声明分散在多个位置,升级与排障时容易出现“本地能过、容器不一致”的问题。
|
||||
- Impact: 构建环境复现、依赖升级与 bug 排查成本上升。
|
||||
- Migration plan: 约定单一权威版本源(如 `go.mod` + `.tool-versions`/`.go-version`),并让 Dockerfile、README、部署文档同步引用。
|
||||
- Supporting evidence:
|
||||
- `go.mod:3` 使用 `go 1.25.0`。
|
||||
- `Dockerfile.api:4` 与 `Dockerfile.worker:4` 使用 `golang:1.25.6-alpine`。
|
||||
- `README.md:884` 写的是 `Go 1.25.1`。
|
||||
|
||||
## Missing Critical Features
|
||||
|
||||
**缺少可执行的自动化质量/回归入口(严重级别:High):**
|
||||
- Problem: README、历史设计与测试文档大量提到 `go test`、`IntegrationTestEnv`、`tests/integration/`,但仓库当前未发现对应测试文件或 `tests/` 目录。
|
||||
- Blocks: 无法快速验证重构、支付回调、权限过滤、部署脚本变更;文档中的测试命令无法直接落地。
|
||||
- Supporting evidence:
|
||||
- `AGENTS.md:324-354` 当前规范明确禁止自动化测试,和 `README.md:712-756`、`docs/testing/test-connection-guide.md` 的测试要求形成正面冲突。
|
||||
- 读取仓库根目录未发现 `tests/` 目录;`glob` 搜索 `tests/**/*_test.go` 与 `internal/**/*_test.go` 未返回结果。
|
||||
- `docs/testing/test-connection-guide.md` 仍宣称“204 个测试总耗时 ~10.5 秒”并要求所有新测试遵循该规范。
|
||||
|
||||
## Test Coverage Gaps
|
||||
|
||||
**部署、配置迁移、运维脚本与文档一致性未见自动验证(优先级:High):**
|
||||
- What's not tested: `docker-compose.prod.yml`、`debug-deployment.sh`、`scripts/migrate.sh`、OpenAPI 文档生成器双清单、README/部署文档与代码的同步性。
|
||||
- Files: `.gitea/workflows/deploy.yaml`, `docker-compose.prod.yml`, `debug-deployment.sh`, `scripts/migrate.sh`, `cmd/api/docs.go`, `cmd/gendocs/main.go`, `README.md`
|
||||
- Risk: 配置漂移、凭据泄漏、文档错误、健康检查误报、迁移行为变化都可能在上线后才暴露。
|
||||
- Priority: High
|
||||
- Suggested follow-up investigation topics:
|
||||
- 设计最小化“文档/脚本一致性检查”而不是恢复完整自动化测试体系。
|
||||
- 评估是否要为部署文件与文档生成引入静态检查或 smoke check。
|
||||
|
||||
---
|
||||
|
||||
*Concerns audit: 2026-03-27*
|
||||
168
.planning/codebase/CONVENTIONS.md
Normal file
168
.planning/codebase/CONVENTIONS.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# Coding Conventions
|
||||
|
||||
**Analysis Date:** 2026-03-27
|
||||
|
||||
## Naming Patterns
|
||||
|
||||
**Files:**
|
||||
- Go 源码使用 `snake_case.go` 或按领域拆分的普通小写命名;路由文件按模块命名放在 `internal/routes/*.go`,Handler 按域放在 `internal/handler/admin/*.go`、`internal/handler/auth/*.go`、`internal/handler/app/*.go`。
|
||||
- OpenAPI 与规范文档使用明确用途命名,例如 `docs/api-documentation-guide.md`、`docs/admin-openapi.yaml`、`cmd/api/docs.go`、`cmd/gendocs/main.go`。
|
||||
- 规范脚本使用动词前缀命名,例如 `scripts/check-all.sh`、`scripts/check-service-errors.sh`、`scripts/check-comment-paths.sh`、`scripts/verify_migration/main.go`。
|
||||
|
||||
**Functions:**
|
||||
- 导出函数/方法使用 Go 的 `PascalCase`,例如 `NewAccountHandler()`、`Create()`、`Success()`、`RedisOrderIdempotencyKey()`,证据见 `internal/handler/admin/account.go`、`pkg/response/response.go`、`pkg/constants/redis.go`。
|
||||
- 未导出帮助函数使用 `camelCase`,例如 `generateOpenAPIDocs()`、`generateAdminDocs()`、`handleError()`、`safeLogWithLevel()`,证据见 `cmd/api/docs.go`、`cmd/gendocs/main.go`、`pkg/errors/handler.go`。
|
||||
|
||||
**Variables:**
|
||||
- 局部变量使用英文 `camelCase`,例如 `outputPath`、`httpStatus`、`roleID`、`groupPath`,证据见 `cmd/api/docs.go`、`pkg/errors/handler.go`、`internal/handler/admin/account.go`。
|
||||
- 错误变量统一使用 `err`,成功返回数据常用业务名,例如 `account`、`accounts`、`roles`,证据见 `internal/handler/admin/account.go`。
|
||||
|
||||
**Types:**
|
||||
- 结构体和接口使用 `PascalCase`,接口语义化命名,配合 `-er` 后缀规则;项目规范明确要求接口名使用 `-er`,见 `AGENTS.md`。
|
||||
- 路由文档元数据类型使用语义名,如 `RouteSpec`、`FileUploadField`,证据见 `internal/routes/registry.go`。
|
||||
|
||||
## Code Style
|
||||
|
||||
**Formatting:**
|
||||
- 使用 `gofmt`;项目在 `AGENTS.md` 明确要求“使用 gofmt 格式化”。
|
||||
- 代码风格遵循 Go 惯用法,避免 Java 式过度抽象;证据见 `AGENTS.md` 的“Go 惯用法 vs Java 风格”。
|
||||
|
||||
**Linting / Scripted Checks:**
|
||||
- 仓库未检测到 `.golangci.yml`、`golangci-lint`、`eslint`、`prettier` 之类自动 lint 配置;当前质量门主要依赖 shell 脚本和人工审查。
|
||||
- `scripts/check-service-errors.sh`:扫描 `internal/service/**/*.go` 中的 `fmt.Errorf`,要求改用 `errors.New()` / `errors.Wrap()`。
|
||||
- `scripts/check-comment-paths.sh`:扫描 `internal/handler/` 中残留的 `/api/v1` 注释,要求改成真实路径 `/api/admin`、`/api/h5`、`/api/c/v1`。
|
||||
- `scripts/check-all.sh`:串行执行以上两个检查。
|
||||
- 当前仓库与脚本规则存在冲突:`internal/service/polling/alert_service.go` 仍存在 4 处 `fmt.Errorf(...)`,按 `scripts/check-service-errors.sh` 的规则属于违规现状。
|
||||
|
||||
## Language & Comment Requirements
|
||||
|
||||
**语言要求:**
|
||||
- 用户可见内容、日志、注释、文档、提交信息都使用中文;变量名、函数名、类型名使用英文,证据见 `AGENTS.md` 的“语言要求”。
|
||||
|
||||
**注释要求:**
|
||||
- 导出包、结构体、接口、函数、方法、常量、变量必须有中文文档注释,证据见 `AGENTS.md`。
|
||||
- Handler 方法注释必须包含 HTTP 方法与真实路径;实际示例见 `internal/handler/admin/account.go`、`internal/handler/callback/payment.go`。
|
||||
- 注释解释“为什么”,不要复述代码;复杂逻辑必须有实现注释,证据见 `AGENTS.md`。
|
||||
- 常量必须带中文注释;实际示例见 `pkg/constants/constants.go`、`pkg/constants/redis.go`。
|
||||
|
||||
## Import Organization
|
||||
|
||||
**Order:**
|
||||
1. Go 标准库,例如 `strconv`、`time`、`regexp`。
|
||||
2. 第三方库,例如 `github.com/gofiber/fiber/v2`、`go.uber.org/zap`。
|
||||
3. 项目内包,例如 `github.com/break/junhong_cmp_fiber/pkg/errors`、`internal/service/account`。
|
||||
|
||||
**Pattern:**
|
||||
- 多数组件按“标准库 → 项目包/第三方包”分组,并保留空行分隔,证据见 `internal/handler/admin/account.go`、`pkg/errors/handler.go`、`cmd/gendocs/main.go`。
|
||||
- 项目使用完整 module import path,未体现短别名路径;必要时用语义别名,例如 `apphandler`、`accountService`,证据见 `cmd/gendocs/main.go`、`internal/handler/admin/account.go`。
|
||||
|
||||
**Path Aliases:**
|
||||
- 未检测到 TS/JS 式路径别名;Go 代码通过 module path `github.com/break/junhong_cmp_fiber/...` 引用。
|
||||
|
||||
## Layering Rules
|
||||
|
||||
**Required Architecture:**
|
||||
- 必须遵循 `Handler → Service → Store → Model`,证据见 `AGENTS.md` 与 `README.md`。
|
||||
|
||||
**How to apply it:**
|
||||
- Handler 只做参数解析、上下文读取、调用 service、返回统一响应;示例见 `internal/handler/admin/account.go`。
|
||||
- Service 承载业务逻辑,并向下调用 Store;项目规范在 `AGENTS.md` 明确禁止在 Handler 中写业务逻辑。
|
||||
- Store 负责数据访问和事务;规范说明见 `AGENTS.md`、`README.md`。
|
||||
- Model/DTO 定义请求、响应和持久化结构;OpenAPI 文档也依赖 DTO 元数据,证据见 `docs/api-documentation-guide.md`。
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Centralized package:**
|
||||
- 所有错误码与应用错误类型集中在 `pkg/errors/`,证据见 `pkg/errors/codes.go`、`pkg/errors/errors.go`、`pkg/errors/handler.go`、`AGENTS.md`。
|
||||
|
||||
**Rules to follow:**
|
||||
- Handler 层不要把底层错误直接暴露给客户端;参数校验失败统一返回 `errors.New(errors.CodeInvalidParam)` 或其中文变体,证据见 `AGENTS.md`、`internal/handler/admin/account.go`。
|
||||
- Service 层对外不要返回 `fmt.Errorf(...)`,要返回 `errors.New(...)` 或 `errors.Wrap(...)`;脚本 `scripts/check-service-errors.sh` 专门检查这一点。
|
||||
- 全局错误处理使用 `pkg/errors/handler.go` 的 `SafeErrorHandler()` / `handleError()`,统一输出 `{code, data, msg, timestamp}`,并按错误码映射 HTTP 状态码与日志级别。
|
||||
- 5xx 响应统一走通用中文消息,避免泄露敏感信息,证据见 `pkg/errors/handler.go`。
|
||||
|
||||
**Error code system:**
|
||||
- `pkg/errors/codes.go` 定义 1000-1999 客户端错误、2000-2999 服务端错误。
|
||||
- `pkg/errors/codes.go` 在 `init()` 校验全部错误码都必须映射消息,新增错误码时要同步维护 `allErrorCodes` 和 `errorMessages`。
|
||||
|
||||
## Response Format
|
||||
|
||||
**Envelope:**
|
||||
- 所有 API 成功响应统一使用 `pkg/response/response.go`:`{code, data, msg, timestamp}`。
|
||||
- `Success()` 返回 `msg: "success"`;`SuccessWithMessage()` 允许覆盖消息;`SuccessWithPagination()` 将分页数据包进 `PaginationData`。
|
||||
|
||||
**How handlers should respond:**
|
||||
- Handler 成功路径直接调用 `response.Success()` / `response.SuccessWithPagination()`;实际示例见 `internal/handler/admin/account.go`、`internal/handler/admin/iot_card.go`、`internal/handler/admin/shop.go`。
|
||||
- 错误路径直接 `return err` 交给全局 ErrorHandler,不在 Handler 内手动拼接 JSON,证据见 `internal/handler/admin/account.go`、`pkg/errors/handler.go`。
|
||||
|
||||
## Constant Management
|
||||
|
||||
**Location:**
|
||||
- 所有常量统一放在 `pkg/constants/`,证据见 `AGENTS.md` 与 `pkg/constants/constants.go`、`pkg/constants/redis.go`。
|
||||
|
||||
**Rules to follow:**
|
||||
- 禁止硬编码字符串与 magic numbers;公共业务值放进 `pkg/constants/constants.go`。
|
||||
- Redis Key 必须通过函数生成,而不是手写字符串,命名格式使用 `Redis{Module}{Purpose}Key(...)`,证据见 `pkg/constants/redis.go` 与 `AGENTS.md`。
|
||||
- 常量要配中文注释;`pkg/constants/constants.go` 中的用户类型、任务类型、分页上限、默认管理员信息都按此方式组织。
|
||||
|
||||
## API / OpenAPI Conventions
|
||||
|
||||
**Route registration:**
|
||||
- 所有 HTTP 路由都应在 `internal/routes/*.go` 中通过 `Register()` 注册,不要直接 `router.Get/Post/...`,证据见 `docs/api-documentation-guide.md`、`internal/routes/registry.go`。
|
||||
- `Register()` 同时负责 Fiber 路由注册和 OpenAPI 生成;当 `doc != nil` 时,会把 `/:id` 转为 OpenAPI 的 `/{id}`,证据见 `internal/routes/registry.go`。
|
||||
|
||||
**RouteSpec requirements:**
|
||||
- 每个接口需要提供中文 `Summary`,可选 Markdown `Description`,并声明 `Input`、`Output`、`Tags`、`Auth`;证据见 `internal/routes/registry.go`、`docs/api-documentation-guide.md`。
|
||||
- DTO 字段必须使用 `description` 标签,不依赖行尾注释;枚举字段要在 `description` 中列出可选值,证据见 `docs/api-documentation-guide.md`。
|
||||
|
||||
**Handler + docs generator sync:**
|
||||
- 新增 Handler 时,除业务接线外,还必须同步更新 `internal/bootstrap/types.go`、`internal/bootstrap/handlers.go`、`internal/routes/admin.go`、`cmd/api/docs.go`、`cmd/gendocs/main.go`,证据见 `docs/api-documentation-guide.md` 与 `AGENTS.md`。
|
||||
- 文档生成命令使用 `go run cmd/gendocs/main.go` 或 `make docs`;代码证据见 `cmd/gendocs/main.go`、`Makefile`。
|
||||
|
||||
## Documentation Expectations
|
||||
|
||||
**Required docs workflow:**
|
||||
- 每个功能应在 `docs/{feature-id}/` 创建总结文档,并同步更新 `README.md`,证据见 `AGENTS.md`。
|
||||
- 文档和说明统一使用中文。
|
||||
- API 文档规范集中在 `docs/api-documentation-guide.md`;开发总规范集中在 `AGENTS.md`。
|
||||
|
||||
**Repository reality:**
|
||||
- `README.md` 仍保留旧的自动化测试与覆盖率章节、旧项目结构中的 `tests/` 目录描述、以及“CI/CD 自动执行检查”的表述。
|
||||
- 当前仓库未发现任何 `*_test.go` 文件,也未发现 `tests/` 目录内容;因此后续文档和执行应优先遵循 `AGENTS.md` 的现行规则,而不是 `README.md` 中这些过期段落。
|
||||
|
||||
## Operational Scripts
|
||||
|
||||
**Quality / convention scripts:**
|
||||
- `bash scripts/check-service-errors.sh`:检查 Service 层是否违规使用 `fmt.Errorf`。
|
||||
- `bash scripts/check-comment-paths.sh`:检查 Handler 注释路径是否残留 `/api/v1`。
|
||||
- `bash scripts/check-all.sh`:运行上述两个检查。
|
||||
|
||||
**Documentation script:**
|
||||
- `make docs` → `go run cmd/gendocs/main.go`,用于生成 `docs/admin-openapi.yaml`,证据见 `Makefile`、`cmd/gendocs/main.go`。
|
||||
|
||||
**Migration verification helper:**
|
||||
- `go run scripts/verify_migration/main.go`:这是一个面向数据库迁移结果的人工验证脚本,会直接连接数据库并查询字段,不是自动测试框架,证据见 `scripts/verify_migration/main.go`。
|
||||
|
||||
**Caution:**
|
||||
- `Makefile` 的 `test` 目标仍定义为 `go test -v ./...`,但这反映的是旧工具入口,不代表当前团队允许自动化测试。
|
||||
|
||||
## Module Design
|
||||
|
||||
**Exports:**
|
||||
- 构造函数使用 `NewXxx...`,例如 `NewAccountHandler()`、`NewGenerator()`。
|
||||
- Handler、response、errors、constants 都以小包单职责方式导出少量明确入口,证据见 `internal/handler/admin/account.go`、`pkg/response/response.go`、`pkg/errors/errors.go`。
|
||||
|
||||
**Barrel Files:**
|
||||
- 未检测到 JS/TS 式 barrel file;Go 通过包目录与导出符号组织模块。
|
||||
|
||||
## Prescriptive Summary
|
||||
|
||||
- 写新 Handler 时:在 `internal/handler/...` 保持中文注释 + 英文标识符,只解析请求并调用 Service,成功统一走 `pkg/response/response.go`,失败直接返回 `error`。
|
||||
- 写新业务错误时:先在 `pkg/errors/codes.go` 注册错误码与中文消息,再用 `errors.New()` / `errors.Wrap()`。
|
||||
- 写新常量或 Redis Key 时:放入 `pkg/constants/`,并补中文注释与 Key 生成函数。
|
||||
- 写新 API 时:在 `internal/routes/*.go` 用 `Register()` + `RouteSpec` 注册,并同步更新 `cmd/api/docs.go` 与 `cmd/gendocs/main.go` 的文档 Handler 装配。
|
||||
- 运行规范检查时:优先使用 `scripts/check-*.sh` 与 `make docs`;不要把 `README.md` 中的测试/覆盖率描述当成当前执行标准。
|
||||
|
||||
---
|
||||
|
||||
*Convention analysis: 2026-03-27*
|
||||
215
.planning/codebase/INTEGRATIONS.md
Normal file
215
.planning/codebase/INTEGRATIONS.md
Normal file
@@ -0,0 +1,215 @@
|
||||
# External Integrations
|
||||
|
||||
**Analysis Date:** 2026-03-27
|
||||
|
||||
## APIs & External Services
|
||||
|
||||
**Database / Cache Infrastructure:**
|
||||
- PostgreSQL - 主业务数据库,GORM 通过 DSN 建连并配置连接池,见 `pkg/database/postgres.go`
|
||||
- Client: `gorm.io/gorm` + `gorm.io/driver/postgres`
|
||||
- Config: `pkg/config/config.go` 的 `database.*`
|
||||
- Redis - Token、缓存、Asynq 后端、限流存储、微信配置缓存,见 `pkg/database/redis.go`、`pkg/queue/client.go`、`cmd/api/main.go`、`internal/service/wechat_config/service.go`
|
||||
- Client: `github.com/redis/go-redis/v9`
|
||||
- Config: `pkg/config/config.go` 的 `redis.*`
|
||||
|
||||
**Object Storage:**
|
||||
- S3-compatible object storage - 文件直传、下载、导入任务临时落盘,见 `pkg/storage/s3.go`、`pkg/storage/service.go`
|
||||
- SDK/Client: `github.com/aws/aws-sdk-go`
|
||||
- Config: `storage.provider`、`storage.s3.*`、`storage.presign.*` in `pkg/config/config.go`
|
||||
- Used by: `internal/handler/admin/storage.go`、`internal/routes/storage.go`、`internal/task/iot_card_import.go`、`internal/task/device_import.go`
|
||||
|
||||
**WeChat ecosystem:**
|
||||
- WeChat Official Account OAuth - 公众号登录与用户信息拉取,见 `pkg/wechat/official_account.go`、`internal/service/client_auth/service.go`
|
||||
- SDK/Client: `github.com/ArtisanCloud/PowerWeChat/v3`
|
||||
- Config source: `tb_wechat_config` via `internal/model/wechat_config.go`
|
||||
- WeChat Mini Program login - `code2session` 换取 openid/session_key,见 `pkg/wechat/miniapp.go`、`internal/service/client_auth/service.go`
|
||||
- Transport: 标准 `net/http`
|
||||
- Config source: `tb_wechat_config`
|
||||
- WeChat Pay - JSAPI/H5 下单、查单、关单、支付回调验签,见 `pkg/wechat/payment.go`、`pkg/wechat/config.go`、`internal/handler/callback/payment.go`
|
||||
- SDK/Client: `github.com/ArtisanCloud/PowerWeChat/v3`
|
||||
- Config source: `tb_wechat_config`
|
||||
|
||||
**Payment gateway:**
|
||||
- Fuiou - 预下单、回调解析、签名与验签 SDK 已存在,见 `pkg/fuiou/client.go`、`pkg/fuiou/wxprecreate.go`、`pkg/fuiou/notify.go`
|
||||
- SDK/Client: repo-local `pkg/fuiou`
|
||||
- Config source: `tb_wechat_config` provider=`fuiou`,字段位于 `internal/model/wechat_config.go`
|
||||
- Callback endpoint: `internal/handler/callback/payment.go`
|
||||
- Current state: 回调处理存在 `TODO`,当前路径解析 XML 但未按配置创建客户端做验签,见 `internal/handler/callback/payment.go`
|
||||
|
||||
**SMS gateway:**
|
||||
- SMS HTTP gateway - 验证码/消息发送,见 `pkg/sms/client.go`、`cmd/api/main.go`
|
||||
- Client: repo-local `pkg/sms`
|
||||
- Config: `sms.gateway_url`、`sms.username`、`sms.password`、`sms.signature` in `pkg/config/config.go`
|
||||
|
||||
**Business Gateway / IoT upstream:**
|
||||
- Gateway API - 设备信息、卡状态、流量、实名链接、设备限速/切卡等外部接口,见 `internal/gateway/client.go`、`internal/gateway/device.go`、`internal/gateway/flow_card.go`
|
||||
- Auth: appId + appSecret,自定义 AES-128-ECB 加密 + MD5 签名,见 `internal/gateway/client.go`
|
||||
- Config: `gateway.base_url`、`gateway.app_id`、`gateway.app_secret`、`gateway.timeout` in `pkg/config/config.go`
|
||||
- Used by: `internal/service/iot_card/gateway_service.go`、`internal/service/device/gateway_service.go`、`internal/task/polling_handler.go`
|
||||
|
||||
## Data Storage
|
||||
|
||||
**Databases:**
|
||||
- PostgreSQL
|
||||
- Connection keys: `JUNHONG_DATABASE_HOST`, `JUNHONG_DATABASE_PORT`, `JUNHONG_DATABASE_USER`, `JUNHONG_DATABASE_PASSWORD`, `JUNHONG_DATABASE_DBNAME`, `JUNHONG_DATABASE_SSLMODE`
|
||||
- Connection code: `pkg/database/postgres.go`
|
||||
- Runtime wiring: `cmd/api/main.go`, `cmd/worker/main.go`, `docker-compose.prod.yml`
|
||||
|
||||
**Redis:**
|
||||
- Redis is shared across auth, cache, rate limiting, WeChat config cache, and Asynq
|
||||
- Connection keys: `JUNHONG_REDIS_ADDRESS`, `JUNHONG_REDIS_PORT`, `JUNHONG_REDIS_PASSWORD`, `JUNHONG_REDIS_DB`
|
||||
- Connection code: `pkg/database/redis.go`
|
||||
- Asynq reuse: `pkg/queue/client.go`, `pkg/queue/server.go`
|
||||
- Config cache key example: `wechat:config:active` in `internal/service/wechat_config/service.go`
|
||||
|
||||
**File Storage:**
|
||||
- S3-compatible bucket storage
|
||||
- Provider implementation: `pkg/storage/s3.go`
|
||||
- Upload URL API: `internal/routes/storage.go`
|
||||
- Import consumers download to temp files before parsing: `internal/task/iot_card_import.go`, `internal/task/device_import.go`
|
||||
|
||||
**Local filesystem:**
|
||||
- Logs are persisted to `/app/logs` inside containers and mounted by Compose, see `pkg/config/defaults/config.yaml`, `Dockerfile.api`, `Dockerfile.worker`, `docker-compose.prod.yml`
|
||||
- OpenAPI runtime export writes to `logs/openapi.yaml`, see `cmd/api/main.go`, `cmd/api/docs.go`
|
||||
|
||||
## Authentication & Identity
|
||||
|
||||
**B-side auth:**
|
||||
- Redis-backed token management and JWT manager initialization occur in `cmd/api/main.go`
|
||||
- Components: `pkg/auth`, Redis token manager, JWT manager
|
||||
|
||||
**C-side identity via WeChat:**
|
||||
- 公众号 OAuth login path uses active config from `tb_wechat_config`, see `internal/service/client_auth/service.go`
|
||||
- 小程序 login path uses WeChat `code2session`, see `internal/service/client_auth/service.go`, `pkg/wechat/miniapp.go`
|
||||
|
||||
## Monitoring & Observability
|
||||
|
||||
**Application logs:**
|
||||
- Zap + Lumberjack app/access loggers, see `pkg/logger/logger.go`
|
||||
|
||||
**Request tracing:**
|
||||
- Access logger captures request metadata and request/response bodies, see `pkg/logger/middleware.go`
|
||||
|
||||
**SQL observability:**
|
||||
- GORM logger sends query errors and slow queries to Zap, see `pkg/database/postgres.go`
|
||||
|
||||
**Container health:**
|
||||
- API health endpoint is used by Docker health checks and Compose dependency ordering, see `Dockerfile.api`, `docker-compose.prod.yml`
|
||||
|
||||
## Queue, Scheduling, and Runtime Integrations
|
||||
|
||||
**Asynq runtime:**
|
||||
- Worker server consumes Redis-backed queues configured in `queue.*`, see `pkg/queue/server.go`, `cmd/worker/main.go`
|
||||
- Task submission is wrapped in `pkg/queue/client.go`
|
||||
|
||||
**Registered task domains:**
|
||||
- Email, data sync, SIM status sync, IoT card import, device import, commission stats, commission calculation, polling, package activation, order expiration, alert check, data cleanup, see `pkg/queue/handler.go`
|
||||
|
||||
**Schedulers:**
|
||||
- Worker process starts an internal polling scheduler plus an Asynq Scheduler for recurring jobs, see `cmd/worker/main.go`
|
||||
- Recurring jobs include order expiration, alert checks, and nightly cleanup, see `cmd/worker/main.go`
|
||||
|
||||
## Docs Generation Integrations
|
||||
|
||||
**OpenAPI generation:**
|
||||
- Repo-local generator wraps `swaggest/openapi-go` and exports YAML, see `pkg/openapi/generator.go`
|
||||
- Runtime generation during API boot writes `logs/openapi.yaml`, see `cmd/api/main.go`, `cmd/api/docs.go`
|
||||
- Manual generation command writes `docs/admin-openapi.yaml`, see `Makefile`, `cmd/gendocs/main.go`
|
||||
- Route registration for docs reuses production route registration, see `internal/routes/routes.go`, `pkg/openapi/handlers.go`
|
||||
|
||||
## CI/CD & Deployment
|
||||
|
||||
**Hosting / Runtime:**
|
||||
- Dockerized API and Worker services are the deployment target, see `Dockerfile.api`, `Dockerfile.worker`, `docker-compose.prod.yml`
|
||||
|
||||
**Registry:**
|
||||
- Images are pushed to a private registry declared in `.gitea/workflows/deploy.yaml`
|
||||
|
||||
**CI Pipeline:**
|
||||
- Gitea workflow builds both images, pushes tag + SHA tags, copies `docker-compose.prod.yml`, and executes `docker compose pull/up -d`, see `.gitea/workflows/deploy.yaml`
|
||||
|
||||
**Migration tooling:**
|
||||
- API image bundles `golang-migrate` and migration files, see `Dockerfile.api`
|
||||
- Local migration shortcuts are defined in `Makefile`
|
||||
|
||||
## Environment Configuration
|
||||
|
||||
**Required env vars:**
|
||||
- Database: `JUNHONG_DATABASE_HOST`, `JUNHONG_DATABASE_PORT`, `JUNHONG_DATABASE_USER`, `JUNHONG_DATABASE_PASSWORD`, `JUNHONG_DATABASE_DBNAME`
|
||||
- Redis: `JUNHONG_REDIS_ADDRESS` plus optional Redis port/password/db tuning vars
|
||||
- JWT: `JUNHONG_JWT_SECRET_KEY`
|
||||
|
||||
**Optional env vars by integration:**
|
||||
- Storage: `JUNHONG_STORAGE_PROVIDER`, `JUNHONG_STORAGE_S3_*`
|
||||
- SMS: `JUNHONG_SMS_*`
|
||||
- Gateway: `JUNHONG_GATEWAY_*`
|
||||
- Logging: `JUNHONG_LOGGING_*`
|
||||
|
||||
**Secrets location:**
|
||||
- Default app config is embedded from `pkg/config/defaults/config.yaml`
|
||||
- Most runtime secrets are expected through environment variables bound in `pkg/config/loader.go`
|
||||
- WeChat / payment provider secrets are stored in database table `tb_wechat_config`, see `internal/model/wechat_config.go`
|
||||
- Deployment wiring exists in `docker-compose.prod.yml` and `.gitea/workflows/deploy.yaml`; these files should be treated as sensitive and are not reproduced here
|
||||
|
||||
## Webhooks & Callbacks
|
||||
|
||||
**Incoming:**
|
||||
- WeChat Pay callback: `POST /api/callback/wechat-pay`, handler in `internal/handler/callback/payment.go`
|
||||
- Fuiou Pay callback: `POST /api/callback/fuiou-pay`, handler in `internal/handler/callback/payment.go`
|
||||
- Alipay callback stub: `POST /api/callback/alipay`, handler in `internal/handler/callback/payment.go`
|
||||
|
||||
**Outgoing:**
|
||||
- WeChat OAuth and payment API calls from `pkg/wechat/*.go`
|
||||
- Gateway API calls from `internal/gateway/*.go`
|
||||
- SMS API calls from `pkg/sms/client.go`
|
||||
- S3 object storage calls from `pkg/storage/s3.go`
|
||||
|
||||
## Evidence
|
||||
|
||||
- `go.mod`
|
||||
- `README.md`
|
||||
- `cmd/api/main.go`
|
||||
- `cmd/api/docs.go`
|
||||
- `cmd/worker/main.go`
|
||||
- `cmd/gendocs/main.go`
|
||||
- `pkg/config/config.go`
|
||||
- `pkg/config/loader.go`
|
||||
- `pkg/config/defaults/config.yaml`
|
||||
- `pkg/database/postgres.go`
|
||||
- `pkg/database/redis.go`
|
||||
- `pkg/queue/client.go`
|
||||
- `pkg/queue/server.go`
|
||||
- `pkg/queue/handler.go`
|
||||
- `pkg/logger/logger.go`
|
||||
- `pkg/openapi/generator.go`
|
||||
- `pkg/openapi/handlers.go`
|
||||
- `pkg/storage/s3.go`
|
||||
- `pkg/storage/service.go`
|
||||
- `pkg/wechat/config.go`
|
||||
- `pkg/wechat/official_account.go`
|
||||
- `pkg/wechat/miniapp.go`
|
||||
- `pkg/wechat/payment.go`
|
||||
- `pkg/fuiou/client.go`
|
||||
- `pkg/fuiou/wxprecreate.go`
|
||||
- `pkg/fuiou/notify.go`
|
||||
- `pkg/sms/client.go`
|
||||
- `internal/gateway/client.go`
|
||||
- `internal/gateway/device.go`
|
||||
- `internal/gateway/flow_card.go`
|
||||
- `internal/model/wechat_config.go`
|
||||
- `internal/service/wechat_config/service.go`
|
||||
- `internal/service/client_auth/service.go`
|
||||
- `internal/service/client_order/service.go`
|
||||
- `internal/routes/storage.go`
|
||||
- `internal/handler/callback/payment.go`
|
||||
- `internal/task/iot_card_import.go`
|
||||
- `internal/task/device_import.go`
|
||||
- `Makefile`
|
||||
- `Dockerfile.api`
|
||||
- `Dockerfile.worker`
|
||||
- `docker-compose.prod.yml`
|
||||
- `.gitea/workflows/deploy.yaml`
|
||||
|
||||
---
|
||||
|
||||
*Integration audit: 2026-03-27*
|
||||
177
.planning/codebase/STACK.md
Normal file
177
.planning/codebase/STACK.md
Normal file
@@ -0,0 +1,177 @@
|
||||
# Technology Stack
|
||||
|
||||
**Analysis Date:** 2026-03-27
|
||||
|
||||
## Languages
|
||||
|
||||
**Primary:**
|
||||
- Go 1.25.x - 应用代码、API、Worker、OpenAPI 生成和基础设施脚本,依据 `go.mod`、`cmd/api/main.go`、`cmd/worker/main.go`、`cmd/gendocs/main.go`
|
||||
|
||||
**Secondary:**
|
||||
- YAML - 配置与部署编排,见 `pkg/config/defaults/config.yaml`、`docker-compose.prod.yml`、`.gitea/workflows/deploy.yaml`
|
||||
- Dockerfile - API/Worker 容器构建,见 `Dockerfile.api`、`Dockerfile.worker`
|
||||
- Makefile - 本地构建、文档生成、迁移命令,见 `Makefile`
|
||||
|
||||
## Runtime
|
||||
|
||||
**Environment:**
|
||||
- Go runtime;API 服务基于 Fiber HTTP 服务器,见 `cmd/api/main.go`
|
||||
- 独立 Worker 进程处理异步任务与调度,见 `cmd/worker/main.go`
|
||||
- 生产容器基础镜像为 Alpine,构建镜像使用 Go 1.25.6 Alpine,见 `Dockerfile.api`、`Dockerfile.worker`
|
||||
|
||||
**Package Manager:**
|
||||
- Go Modules,见 `go.mod`
|
||||
- Lockfile: `go.sum` present
|
||||
|
||||
## Frameworks
|
||||
|
||||
**Core:**
|
||||
- Fiber v2.52.9 - HTTP 框架与路由宿主,见 `go.mod`、`cmd/api/main.go`
|
||||
- GORM v1.31.1 + `gorm.io/driver/postgres` v1.6.0 - PostgreSQL ORM 与连接层,见 `go.mod`、`pkg/database/postgres.go`
|
||||
- Viper v1.21.0 - 嵌入式默认配置 + 环境变量覆盖,见 `go.mod`、`pkg/config/loader.go`
|
||||
|
||||
**Queue / Scheduling:**
|
||||
- Asynq v0.25.1 - 任务提交、Worker 消费、定时任务,见 `go.mod`、`pkg/queue/client.go`、`pkg/queue/server.go`、`cmd/worker/main.go`
|
||||
|
||||
**Serialization / Validation:**
|
||||
- sonic v1.14.2 - Fiber JSON 编解码与任务载荷序列化,见 `go.mod`、`cmd/api/main.go`、`pkg/queue/client.go`
|
||||
- validator/v10 v10.28.0 - DTO 校验依赖,见 `go.mod`
|
||||
|
||||
**Logging:**
|
||||
- zap v1.27.1 - 应用日志与 SQL 日志,见 `go.mod`、`pkg/logger/logger.go`、`pkg/database/postgres.go`
|
||||
- lumberjack.v2 v2.2.1 - 日志轮转,见 `go.mod`、`pkg/logger/logger.go`
|
||||
|
||||
**Docs tooling:**
|
||||
- swaggest/openapi-go v0.2.60 - OpenAPI 3.0.3 文档生成,见 `go.mod`、`pkg/openapi/generator.go`
|
||||
|
||||
## Build / Dev / Deploy
|
||||
|
||||
**Build:**
|
||||
- `Makefile` 定义 `build`、`run`、`run-worker`、`docs`、`migrate-*` 目标,见 `Makefile`
|
||||
- API 与 Worker 使用多阶段 Docker 构建,编译产物分别来自 `./cmd/api` 与 `./cmd/worker`,见 `Dockerfile.api`、`Dockerfile.worker`
|
||||
|
||||
**Deploy:**
|
||||
- 生产编排使用 Docker Compose,运行 `api` 与 `worker` 两个服务,见 `docker-compose.prod.yml`
|
||||
- Gitea Actions 工作流构建镜像、推送私有镜像仓库并在主分支执行 `docker compose up -d`,见 `.gitea/workflows/deploy.yaml`
|
||||
|
||||
**Docs generation:**
|
||||
- API 启动时会生成 `logs/openapi.yaml`,见 `cmd/api/main.go`、`cmd/api/docs.go`
|
||||
- 手动文档命令 `make docs` 运行 `cmd/gendocs/main.go`,输出 `docs/admin-openapi.yaml`,见 `Makefile`、`cmd/gendocs/main.go`
|
||||
|
||||
## Storage, Data, Messaging
|
||||
|
||||
**Database:**
|
||||
- PostgreSQL 是唯一检测到的主数据库;连接、连接池、GORM logger、Ping 校验位于 `pkg/database/postgres.go`
|
||||
|
||||
**Cache / KV:**
|
||||
- Redis 用于认证、缓存、队列后端、限流存储和配置缓存,见 `pkg/database/redis.go`、`cmd/api/main.go`、`internal/service/wechat_config/service.go`
|
||||
|
||||
**Messaging / Queue:**
|
||||
- Asynq 直接复用 Redis 连接配置作为消息后端,见 `pkg/queue/client.go`、`pkg/queue/server.go`
|
||||
- Worker 内注册订单超时、告警检查、数据清理等调度任务,见 `cmd/worker/main.go`
|
||||
|
||||
**Object storage:**
|
||||
- S3 兼容对象存储通过 AWS SDK v1 实现,支持上传、下载、预签名 URL、临时文件下载,见 `pkg/storage/s3.go`、`pkg/storage/service.go`
|
||||
|
||||
## Configuration
|
||||
|
||||
**Loading model:**
|
||||
- 默认配置从嵌入文件 `pkg/config/defaults/config.yaml` 读取,见 `pkg/config/embedded.go`
|
||||
- 运行时使用 `JUNHONG_` 前缀环境变量覆盖,见 `pkg/config/loader.go`
|
||||
- 必填配置校验包括数据库、Redis、JWT,见 `pkg/config/config.go`
|
||||
|
||||
**Key config areas:**
|
||||
- 服务与超时:`server.*`,见 `pkg/config/config.go`
|
||||
- PostgreSQL:`database.*`,见 `pkg/config/config.go`
|
||||
- Redis:`redis.*`,见 `pkg/config/config.go`
|
||||
- Asynq:`queue.*`,见 `pkg/config/config.go`
|
||||
- 日志:`logging.*`,见 `pkg/config/config.go`
|
||||
- 短信:`sms.*`,见 `pkg/config/config.go`
|
||||
- 对象存储:`storage.*`,见 `pkg/config/config.go`
|
||||
- 外部 Gateway:`gateway.*`,见 `pkg/config/config.go`
|
||||
|
||||
**WeChat config exception:**
|
||||
- 微信公众号/小程序/支付配置不走 Viper 主配置,业务侧从数据库表 `tb_wechat_config` 读取并缓存到 Redis,见 `internal/model/wechat_config.go`、`internal/service/wechat_config/service.go`、`pkg/wechat/config.go`
|
||||
|
||||
## Logging & Observability
|
||||
|
||||
**Application logs:**
|
||||
- `pkg/logger/logger.go` 初始化 app/access 双 logger;生产模式写 JSON,开发模式 app logger 同时输出控制台
|
||||
|
||||
**Access logs:**
|
||||
- `pkg/logger/middleware.go` 记录 method、path、query、status、duration、request_id、ip、user_agent、user_id、请求/响应 body
|
||||
|
||||
**SQL logs:**
|
||||
- `pkg/database/postgres.go` 自定义 GORM logger,记录错误、慢查询和普通 SQL trace
|
||||
|
||||
**Health checks:**
|
||||
- `Dockerfile.api` 与 `docker-compose.prod.yml` 均通过 `GET /health` 做容器健康检查
|
||||
|
||||
## Notable Dependencies
|
||||
|
||||
**HTTP / App:**
|
||||
- `github.com/gofiber/fiber/v2` - API 服务核心框架,见 `go.mod`、`cmd/api/main.go`
|
||||
- `github.com/google/uuid` - 请求 ID 生成,见 `cmd/api/main.go`
|
||||
|
||||
**Persistence / Cache:**
|
||||
- `gorm.io/gorm`、`gorm.io/driver/postgres` - ORM 与 PostgreSQL 驱动,见 `go.mod`、`pkg/database/postgres.go`
|
||||
- `github.com/redis/go-redis/v9` - Redis 客户端,见 `go.mod`、`pkg/database/redis.go`
|
||||
|
||||
**Queue / Async:**
|
||||
- `github.com/hibiken/asynq` - 队列与调度器,见 `go.mod`、`pkg/queue/*.go`、`cmd/worker/main.go`
|
||||
|
||||
**Serialization / File processing:**
|
||||
- `github.com/bytedance/sonic` - JSON 编解码,见 `cmd/api/main.go`、`pkg/queue/client.go`
|
||||
- `github.com/xuri/excelize/v2` - Excel 导入处理依赖,见 `go.mod`; 任务消费方在 `internal/task/iot_card_import.go`、`internal/task/device_import.go` 调用 Excel 解析
|
||||
|
||||
**External SDKs:**
|
||||
- `github.com/ArtisanCloud/PowerWeChat/v3` - 微信公众号与微信支付 SDK,见 `go.mod`、`pkg/wechat/official_account.go`、`pkg/wechat/payment.go`
|
||||
- `github.com/aws/aws-sdk-go` - S3 兼容对象存储客户端,见 `go.mod`、`pkg/storage/s3.go`
|
||||
|
||||
## Platform Requirements
|
||||
|
||||
**Development:**
|
||||
- 需要 PostgreSQL、Redis、JWT 密钥与 `JUNHONG_` 环境变量;`config.Load()` 在启动时强校验,见 `pkg/config/config.go`
|
||||
- `make docs` 需要 Go 环境;`migrate-*` 目标需要系统安装 `migrate` 命令,见 `Makefile`
|
||||
|
||||
**Production:**
|
||||
- 目标运行形态是 Docker 化 API + Worker 双进程部署,见 `Dockerfile.api`、`Dockerfile.worker`、`docker-compose.prod.yml`
|
||||
- 镜像发布到私有仓库 `registry.boss160.cn`,见 `.gitea/workflows/deploy.yaml`
|
||||
|
||||
## Evidence
|
||||
|
||||
- `go.mod`
|
||||
- `README.md`
|
||||
- `cmd/api/main.go`
|
||||
- `cmd/api/docs.go`
|
||||
- `cmd/worker/main.go`
|
||||
- `cmd/gendocs/main.go`
|
||||
- `pkg/config/config.go`
|
||||
- `pkg/config/loader.go`
|
||||
- `pkg/config/embedded.go`
|
||||
- `pkg/config/defaults/config.yaml`
|
||||
- `pkg/database/postgres.go`
|
||||
- `pkg/database/redis.go`
|
||||
- `pkg/queue/client.go`
|
||||
- `pkg/queue/server.go`
|
||||
- `pkg/queue/handler.go`
|
||||
- `pkg/logger/logger.go`
|
||||
- `pkg/logger/middleware.go`
|
||||
- `pkg/openapi/generator.go`
|
||||
- `pkg/openapi/handlers.go`
|
||||
- `pkg/storage/s3.go`
|
||||
- `pkg/storage/service.go`
|
||||
- `pkg/wechat/config.go`
|
||||
- `pkg/wechat/official_account.go`
|
||||
- `pkg/wechat/payment.go`
|
||||
- `pkg/sms/client.go`
|
||||
- `internal/gateway/client.go`
|
||||
- `Makefile`
|
||||
- `Dockerfile.api`
|
||||
- `Dockerfile.worker`
|
||||
- `docker-compose.prod.yml`
|
||||
- `.gitea/workflows/deploy.yaml`
|
||||
|
||||
---
|
||||
|
||||
*Stack analysis: 2026-03-27*
|
||||
250
.planning/codebase/STRUCTURE.md
Normal file
250
.planning/codebase/STRUCTURE.md
Normal file
@@ -0,0 +1,250 @@
|
||||
# Codebase Structure
|
||||
|
||||
**Analysis Date:** 2026-03-27
|
||||
|
||||
## Directory Layout
|
||||
|
||||
```text
|
||||
junhong_cmp_fiber/
|
||||
├── cmd/ # API、Worker、文档生成入口
|
||||
├── internal/ # 核心业务代码(handler/service/store/model/routes/bootstrap/task)
|
||||
├── pkg/ # 可复用基础设施与跨模块工具
|
||||
├── migrations/ # SQL 迁移脚本
|
||||
├── docs/ # 业务文档、接入指南、架构说明
|
||||
├── openspec/ # 当前能力规格与变更档案
|
||||
├── specs/ # 早期 Speckit 规格文档
|
||||
├── scripts/ # 检查与初始化脚本
|
||||
├── docker/ # 部署相关辅助文件
|
||||
├── logs/ # 运行期日志输出目录
|
||||
└── .planning/codebase/ # 当前代码地图产物
|
||||
```
|
||||
|
||||
## Directory Purposes
|
||||
|
||||
**`cmd/`:**
|
||||
- Purpose: 进程级入口和独立命令。
|
||||
- Contains: `cmd/api/main.go`, `cmd/api/docs.go`, `cmd/worker/main.go`, `cmd/gendocs/main.go`
|
||||
- Key files: `cmd/api/main.go`, `cmd/worker/main.go`
|
||||
|
||||
**`internal/bootstrap/`:**
|
||||
- Purpose: 依赖装配中心。
|
||||
- Contains: API/Worker 的 stores、services、handlers、middlewares 初始化。
|
||||
- Key files: `internal/bootstrap/bootstrap.go`, `internal/bootstrap/services.go`, `internal/bootstrap/worker.go`, `internal/bootstrap/types.go`
|
||||
|
||||
**`internal/routes/`:**
|
||||
- Purpose: 路由域划分与 OpenAPI 绑定。
|
||||
- Contains: 域入口 `admin.go`, `auth.go`, `personal.go` 以及各模块路由文件。
|
||||
- Key files: `internal/routes/routes.go`, `internal/routes/registry.go`, `internal/routes/admin.go`, `internal/routes/personal.go`
|
||||
|
||||
**`internal/handler/`:**
|
||||
- Purpose: HTTP Handler 层。
|
||||
- Contains: `admin/`、`app/`、`auth/`、`callback/` 四个子域。
|
||||
- Key files: `internal/handler/admin/account.go`, `internal/handler/auth/handler.go`, `internal/handler/callback/payment.go`
|
||||
|
||||
**`internal/service/`:**
|
||||
- Purpose: 业务逻辑层。
|
||||
- Contains: 按业务模块拆分的子目录,每个模块通常有一个 `service.go`。
|
||||
- Key files: `internal/service/account/service.go`, `internal/service/order/service.go`, `internal/service/package/service.go`, `internal/service/polling/*.go`
|
||||
|
||||
**`internal/store/`:**
|
||||
- Purpose: 数据访问层。
|
||||
- Contains: 基础抽象 `store.go`、查询选项 `options.go`、以及 `postgres/` 下的具体实现。
|
||||
- Key files: `internal/store/store.go`, `internal/store/options.go`, `internal/store/postgres/account_store.go`, `internal/store/postgres/order_store.go`
|
||||
|
||||
**`internal/model/`:**
|
||||
- Purpose: 持久化模型与 DTO。
|
||||
- Contains: 业务实体模型、基础模型、DTO 子目录。
|
||||
- Key files: `internal/model/account.go`, `internal/model/order.go`, `internal/model/shop.go`, `internal/model/dto/account_dto.go`
|
||||
|
||||
**`internal/task/`:**
|
||||
- Purpose: Asynq 任务处理逻辑。
|
||||
- Contains: 轮询、导入、订单超时、邮件、佣金等任务处理器。
|
||||
- Key files: `internal/task/polling_handler.go`, `internal/task/order_expire.go`, `internal/task/device_import.go`
|
||||
|
||||
**`internal/polling/`:**
|
||||
- Purpose: 轮询调度器与专用运行时组件。
|
||||
- Contains: `Scheduler`、激活处理器、流量重置处理器。
|
||||
- Key files: `internal/polling/scheduler.go`
|
||||
|
||||
**`internal/gateway/`:**
|
||||
- Purpose: 对外 Gateway HTTP 客户端封装。
|
||||
- Contains: 请求签名、设备/卡查询与操作客户端。
|
||||
- Key files: `internal/gateway/client.go`(由 `cmd/api/main.go`、`cmd/worker/main.go` 初始化)
|
||||
|
||||
**`pkg/`:**
|
||||
- Purpose: 通用基础设施。
|
||||
- Contains: 配置、数据库、日志、认证、错误、响应、OpenAPI、队列、对象存储、微信、短信、中间件辅助。
|
||||
- Key files: `pkg/config/config.go`, `pkg/config/loader.go`, `pkg/database/postgres.go`, `pkg/middleware/auth.go`, `pkg/middleware/data_scope.go`, `pkg/openapi/generator.go`
|
||||
|
||||
**`migrations/`:**
|
||||
- Purpose: 数据库结构演进。
|
||||
- Contains: 成对的 `*.up.sql` / `*.down.sql` 与少量补数 SQL。
|
||||
- Key files: `migrations/000000_create_legacy_tables.up.sql`, `migrations/000055_package_system_upgrade.up.sql`, `migrations/000088_rename_card_wallet_id_to_asset_wallet_id.up.sql`
|
||||
|
||||
**`docs/`:**
|
||||
- Purpose: 面向开发和业务的说明文档。
|
||||
- Contains: 功能总结、架构说明、接入指南、运维说明、第三方资料。
|
||||
- Key files: `docs/api-documentation-guide.md`, `docs/auth-architecture.md`, `docs/polling-system/README.md`
|
||||
|
||||
**`openspec/`:**
|
||||
- Purpose: 规范驱动开发资产。
|
||||
- Contains: `config.yaml`、当前 specs、历史归档变更。
|
||||
- Key files: `openspec/config.yaml`, `openspec/specs/**/spec.md`, `openspec/changes/archive/**`
|
||||
|
||||
## Key File Locations
|
||||
|
||||
**Entry Points:**
|
||||
- `cmd/api/main.go`: API 服务主入口。
|
||||
- `cmd/worker/main.go`: Worker 服务主入口。
|
||||
- `cmd/gendocs/main.go`: 离线 OpenAPI 生成命令。
|
||||
|
||||
**Configuration:**
|
||||
- `pkg/config/config.go`: 配置结构与校验规则。
|
||||
- `pkg/config/loader.go`: 嵌入配置 + 环境变量覆盖加载。
|
||||
- `openspec/config.yaml`: 规范生成器上下文与规则。
|
||||
|
||||
**Core Logic:**
|
||||
- `internal/service/`: 所有业务服务主实现。
|
||||
- `internal/store/postgres/`: 所有 PostgreSQL 访问实现。
|
||||
- `internal/bootstrap/`: 依赖注入和装配。
|
||||
- `internal/polling/scheduler.go`: 轮询核心调度逻辑。
|
||||
|
||||
**API Surface:**
|
||||
- `internal/routes/`: 路由分组与注册。
|
||||
- `internal/handler/admin/`, `internal/handler/app/`, `internal/handler/auth/`, `internal/handler/callback/`: 传输层处理器。
|
||||
|
||||
**Data Definitions:**
|
||||
- `internal/model/*.go`: 表模型。
|
||||
- `internal/model/dto/*.go`: 请求/响应 DTO。
|
||||
|
||||
**Documentation / Planning:**
|
||||
- `docs/`: 功能文档。
|
||||
- `openspec/specs/`: 现行规格。
|
||||
- `openspec/changes/archive/`: 历史变更记录。
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
**Files:**
|
||||
- 模块按业务名命名:`internal/service/account/service.go`, `internal/handler/admin/account.go`
|
||||
- 大部分 Store 文件采用 `{resource}_store.go`:`internal/store/postgres/account_store.go`
|
||||
- DTO 文件采用 `{module}_dto.go`:`internal/model/dto/account_dto.go`
|
||||
- 路由文件按模块命名:`internal/routes/account.go`, `internal/routes/polling_config.go`
|
||||
|
||||
**Directories:**
|
||||
- 分层目录使用小写单数语义:`internal/service/`, `internal/store/`, `internal/model/`
|
||||
- 传输域用子目录表达边界:`internal/handler/admin/`, `internal/handler/app/`, `internal/handler/callback/`
|
||||
- 规格目录按能力名组织:`openspec/specs/account-management/`, `openspec/specs/order-payment/`
|
||||
|
||||
## Ownership / Responsibility Guide
|
||||
|
||||
**如果改接口路径或加新 API:**
|
||||
- 先看 `internal/routes/registry.go` 与对应模块路由文件。
|
||||
- 再同步检查 `docs/api-documentation-guide.md`、`cmd/api/docs.go`、`cmd/gendocs/main.go`、`pkg/openapi/handlers.go`。
|
||||
|
||||
**如果改业务规则:**
|
||||
- 主要落点在 `internal/service/{module}/service.go`。
|
||||
- 如涉及跨模块,先看 `internal/bootstrap/services.go` 中该服务依赖谁。
|
||||
|
||||
**如果改查询或权限过滤:**
|
||||
- 主要落点在 `internal/store/postgres/*.go`。
|
||||
- 数据范围辅助逻辑集中在 `pkg/middleware/data_scope.go` 与 `pkg/middleware/permission_helper.go`。
|
||||
|
||||
**如果改表结构或模型:**
|
||||
- SQL 先改 `migrations/`。
|
||||
- Go 模型同步改 `internal/model/*.go`。
|
||||
- DTO 变更再改 `internal/model/dto/*.go`。
|
||||
|
||||
**如果改异步任务:**
|
||||
- 任务实现放 `internal/task/*.go`。
|
||||
- 注册入口在 `pkg/queue/handler.go`。
|
||||
- Worker 依赖拼装在 `internal/bootstrap/worker*.go`。
|
||||
|
||||
**如果改轮询系统:**
|
||||
- 调度逻辑看 `internal/polling/scheduler.go`。
|
||||
- 执行逻辑看 `internal/task/polling_handler.go`。
|
||||
- 配置/监控/清理相关服务看 `internal/service/polling/*.go`。
|
||||
|
||||
## Where to Add New Code
|
||||
|
||||
**New Admin API Feature:**
|
||||
- Primary code: `internal/handler/admin/{module}.go`, `internal/service/{module}/service.go`, `internal/store/postgres/{module}_store.go`, `internal/routes/{module}.go`
|
||||
- Models / DTOs: `internal/model/{module}.go`, `internal/model/dto/{module}_dto.go`
|
||||
- Wiring: `internal/bootstrap/services.go`, `internal/bootstrap/handlers.go`, `internal/bootstrap/types.go`, `internal/routes/admin.go`
|
||||
|
||||
**New Personal Client Feature:**
|
||||
- Primary code: `internal/handler/app/{feature}.go`, `internal/service/{feature}/service.go` 或复用现有服务
|
||||
- Routes: `internal/routes/personal.go`
|
||||
- Auth-sensitive DTOs: `internal/model/dto/` 下新增对应 DTO 文件
|
||||
|
||||
**New Worker Task:**
|
||||
- Implementation: `internal/task/{task_name}.go`
|
||||
- Registration: `pkg/queue/handler.go`
|
||||
- Dependencies: `internal/bootstrap/worker_services.go` 或 `internal/bootstrap/worker_stores.go`
|
||||
- Constants: `pkg/constants/` 中对应任务类型定义
|
||||
|
||||
**New Shared Utility:**
|
||||
- Shared helpers: `pkg/{subsystem}/`
|
||||
- 仅限跨多个业务模块复用的基础能力,不要把业务逻辑塞进 `pkg/`
|
||||
|
||||
**New Business Spec / Plan Artifact:**
|
||||
- Current capability spec: `openspec/specs/{capability}/spec.md`
|
||||
- Historical change proposal: `openspec/changes/...`
|
||||
- Developer-facing long-form doc: `docs/{feature-id}/` 或现有专题目录
|
||||
|
||||
## Practical Navigation Guide
|
||||
|
||||
**从请求入口追代码:**
|
||||
1. 在 `internal/routes/*.go` 找路径。
|
||||
2. 跳到对应 `internal/handler/...` 方法。
|
||||
3. 看该 Handler 注入了哪个 Service。
|
||||
4. 到 `internal/service/...` 看业务编排。
|
||||
5. 再进入 `internal/store/postgres/...` 看 SQL 过滤与分页。
|
||||
6. 最后回到 `internal/model/` / `internal/model/dto/` 看字段定义。
|
||||
|
||||
**从一个模块反查装配方式:**
|
||||
1. 先找 `internal/bootstrap/services.go` 是否实例化该 Service。
|
||||
2. 再看 `internal/bootstrap/handlers.go` 是否把它交给 Handler。
|
||||
3. 再看 `internal/routes/admin.go` 或 `internal/routes/personal.go` 是否挂载了该模块。
|
||||
|
||||
**从一个异步任务反查来源:**
|
||||
1. 看 `pkg/queue/handler.go` 的 `registerXxxHandler()`。
|
||||
2. 跳到 `internal/task/{task}.go`。
|
||||
3. 若任务由调度器触发,再看 `internal/polling/scheduler.go` 或 `cmd/worker/main.go` 的 Asynq Scheduler 注册。
|
||||
|
||||
## Special Directories
|
||||
|
||||
**`logs/`:**
|
||||
- Purpose: 运行日志输出目录。
|
||||
- Generated: Yes
|
||||
- Committed: No(运行期产物)
|
||||
|
||||
**`migrations/`:**
|
||||
- Purpose: 数据库迁移脚本。
|
||||
- Generated: No
|
||||
- Committed: Yes
|
||||
|
||||
**`docs/第三方文档/`:**
|
||||
- Purpose: 外部供应商或接口资料归档。
|
||||
- Generated: No
|
||||
- Committed: Yes
|
||||
|
||||
**`openspec/changes/archive/`:**
|
||||
- Purpose: 已归档历史变更。
|
||||
- Generated: No
|
||||
- Committed: Yes
|
||||
|
||||
**`.planning/codebase/`:**
|
||||
- Purpose: 供规划/执行命令消费的代码地图文档。
|
||||
- Generated: Yes
|
||||
- Committed: Yes
|
||||
|
||||
## Structural Notes for Contributors
|
||||
|
||||
- 这个仓库的“物理结构”严格围绕 Go 后端单体展开;新增代码优先放进现有层级,不要新造平行架构。
|
||||
- `internal/bootstrap/handlers.go` 当前会为客户端能力额外 new 一批 Store,这意味着某些接口不是纯 Service 注入;新增客户端功能前先确认是否要复用现有 `services` 聚合,还是遵循当前适配方式。
|
||||
- OpenAPI 不是完全自动发现;新增 Handler、Route 之后,必须同步维护 `internal/bootstrap/types.go`、`internal/bootstrap/handlers.go`、`internal/routes/*.go`、`cmd/api/docs.go`、`cmd/gendocs/main.go`、`pkg/openapi/handlers.go`。
|
||||
- 数据权限过滤不在统一 GORM 查询回调中自动生效;新增 Store 查询时,要主动选对 `ApplyShopFilter`、`ApplyEnterpriseFilter`、`ApplySellerShopFilter`、`ApplyOwnerShopFilter` 等函数。
|
||||
|
||||
---
|
||||
|
||||
*Structure analysis: 2026-03-27*
|
||||
182
.planning/codebase/TESTING.md
Normal file
182
.planning/codebase/TESTING.md
Normal file
@@ -0,0 +1,182 @@
|
||||
# Testing Patterns
|
||||
|
||||
**Analysis Date:** 2026-03-27
|
||||
|
||||
## Test Framework
|
||||
|
||||
**Runner:**
|
||||
- 当前仓库未检测到有效的 Go 自动化测试文件:`glob("**/*_test.go")` 返回空,`glob("tests/**")` 返回空。
|
||||
- 因此,仓库当前状态下不存在可执行的项目内单元测试、集成测试或 E2E 测试代码。
|
||||
|
||||
**Assertion Library:**
|
||||
- 未检测到;因为当前仓库没有 `*_test.go` 文件。
|
||||
|
||||
**Configured / Documented commands:**
|
||||
```bash
|
||||
go test ./... # `README.md` 文档命令;当前仓库没有测试文件
|
||||
go test -cover ./... # `README.md` 文档命令;覆盖率描述与仓库现状不符
|
||||
go test -v ./... # `Makefile` 的 `test` 目标与 `README.md` 一致
|
||||
go test ./pkg/... # `README.md` 文档命令;当前没有对应测试文件
|
||||
go test ./tests/integration/... # `README.md` 文档命令;当前仓库不存在 `tests/` 目录内容
|
||||
go test -v ./internal/middleware -run TestKeyAuth # `README.md` 文档命令;当前未检测到对应测试
|
||||
```
|
||||
|
||||
## Policy Reality
|
||||
|
||||
**Current project rule (authoritative):**
|
||||
- `AGENTS.md` 明确规定:本项目不使用任何形式的自动化测试代码。
|
||||
- `AGENTS.md` 明确禁止单元测试、集成测试、验收测试、流程测试、E2E 测试,以及创建 `*_test.go` 文件;唯一例外是用户明确要求“请写测试”。
|
||||
|
||||
**Repository reality:**
|
||||
- 当前仓库与该规则一致:未发现任何 `*_test.go` 文件,也未发现 `tests/` 目录内容。
|
||||
|
||||
**Stale documentation conflicts:**
|
||||
- `README.md` 仍包含完整“测试”章节,列出 `go test ./...`、覆盖率、集成测试、`tests/integration/`、以及 `docs/testing/test-connection-guide.md` 的说明。
|
||||
- `README.md` 的项目结构段仍展示 `tests/integration/auth_test.go`、`tests/integration/ratelimit_test.go`。
|
||||
- `README.md` 的 “Speckit / 宪章” 段仍写有 “70%+ 测试覆盖率,核心业务 90%+”。
|
||||
- 这些内容与 `AGENTS.md` 的现行禁令、以及仓库中真实文件状态冲突。后续执行应以 `AGENTS.md` + 实际文件现状为准。
|
||||
|
||||
## Verification Approach Actually Used
|
||||
|
||||
**Primary approach:**
|
||||
- 人工验证 + 脚本检查 + 文档生成检查 + 生产日志/监控。
|
||||
|
||||
**Evidence from policy:**
|
||||
- `AGENTS.md` 将替代方案明确写为:
|
||||
- 使用 PostgreSQL MCP 工具手动验证数据
|
||||
- 使用 Postman/curl 手动测试 API
|
||||
- 依赖生产环境日志和监控发现问题
|
||||
|
||||
**Repository scripts that support this model:**
|
||||
- `scripts/check-service-errors.sh`:静态扫描 Service 层错误处理规则。
|
||||
- `scripts/check-comment-paths.sh`:静态扫描 Handler 注释中的 API 路径是否过时。
|
||||
- `scripts/check-all.sh`:聚合上述脚本。
|
||||
- `scripts/verify_migration/main.go`:直接连接数据库查询表和字段,验证迁移结果;这是一种手工校验辅助程序,不是自动测试框架。
|
||||
- `cmd/gendocs/main.go` / `make docs`:通过重新生成 OpenAPI 文档来验证路由注册和文档接线是否完整。
|
||||
|
||||
## Test File Organization
|
||||
|
||||
**Location:**
|
||||
- 当前无自动化测试文件。
|
||||
|
||||
**Naming:**
|
||||
- 按 Go 常规应为 `*_test.go`,但本项目现行规则禁止新增,除非用户明确要求。
|
||||
|
||||
**Structure:**
|
||||
```text
|
||||
自动化测试目录:未检测到
|
||||
README 中声明的 `tests/integration/`:当前不存在实际文件
|
||||
```
|
||||
|
||||
## Manual Validation Patterns
|
||||
|
||||
**API / behavior validation:**
|
||||
- 使用 Postman、curl 或 Swagger/OpenAPI 文档进行接口人工验证,依据见 `AGENTS.md` 与 `docs/api-documentation-guide.md`。
|
||||
- 生成 OpenAPI 文档后检查路径是否出现:`go run cmd/gendocs/main.go`,再检查 `docs/admin-openapi.yaml`,依据见 `docs/api-documentation-guide.md`。
|
||||
|
||||
**Database validation:**
|
||||
- 涉及数据正确性时,使用 PostgreSQL MCP 工具做手动查询验证,依据见 `AGENTS.md` 的 `db-validation` 说明。
|
||||
- 涉及迁移结果时,可运行 `go run scripts/verify_migration/main.go` 检查表与字段;该脚本读取真实数据库 schema,不产出测试报告。
|
||||
|
||||
**Convention validation:**
|
||||
- 改动 Service 层错误处理后运行 `bash scripts/check-service-errors.sh`。
|
||||
- 改动 Handler 注释或路由后运行 `bash scripts/check-comment-paths.sh`。
|
||||
- 需要一次性检查时运行 `bash scripts/check-all.sh`。
|
||||
|
||||
**Documentation / OpenAPI validation:**
|
||||
- 新增 Handler 后必须同步更新 `cmd/api/docs.go` 与 `cmd/gendocs/main.go`,然后重新生成文档并检查目标路径,依据见 `docs/api-documentation-guide.md`。
|
||||
|
||||
## Mocking
|
||||
|
||||
**Framework:**
|
||||
- Not applicable;当前没有自动化测试代码。
|
||||
|
||||
**Patterns:**
|
||||
```text
|
||||
未检测到 mock 框架、测试替身、fixture helper 或 testutil 包的实际使用。
|
||||
```
|
||||
|
||||
**What to Mock:**
|
||||
- 当前仓库没有现行自动化测试实践可供复用。
|
||||
|
||||
**What NOT to Mock:**
|
||||
- 当前仓库没有现行自动化测试实践可供复用。
|
||||
|
||||
## Fixtures and Factories
|
||||
|
||||
**Test Data:**
|
||||
```text
|
||||
未检测到 fixture/factory 测试数据目录或 `_test.go` 中的构造模式。
|
||||
```
|
||||
|
||||
**Reality check:**
|
||||
- `README.md` 仍引用 `docs/testing/test-connection-guide.md` 和 `testutils.NewTestTransaction(...)` 示例,但当前仓库没有对应测试文件作为实际落点。
|
||||
|
||||
## Coverage
|
||||
|
||||
**Requirements:**
|
||||
- 当前现行项目规则不要求覆盖率,且原则上禁止自动化测试,依据见 `AGENTS.md`。
|
||||
|
||||
**Conflict to document clearly:**
|
||||
- `README.md` 和其中引用的 Speckit 宪章仍声称需要覆盖率目标;这与 `AGENTS.md` 的测试禁令冲突,且与当前“零测试文件”现状不符。
|
||||
|
||||
**View Coverage:**
|
||||
```bash
|
||||
go test -cover ./... # 仅为 README 中遗留命令,不代表现行流程
|
||||
```
|
||||
|
||||
## Test Types
|
||||
|
||||
**Unit Tests:**
|
||||
- 当前未使用。
|
||||
|
||||
**Integration Tests:**
|
||||
- 当前未使用。
|
||||
- `README.md` 里提到的 `tests/integration/` 是过期描述,不是当前仓库事实。
|
||||
|
||||
**E2E Tests:**
|
||||
- 当前未使用。
|
||||
|
||||
## CI Reality
|
||||
|
||||
**Actual workflow present:**
|
||||
- `.gitea/workflows/deploy.yaml` 只有检出、Docker 构建、推送镜像、部署到测试环境的步骤。
|
||||
|
||||
**What is not in CI:**
|
||||
- 未发现 `go test`。
|
||||
- 未发现 `bash scripts/check-all.sh`。
|
||||
- 未发现 `bash scripts/check-service-errors.sh`。
|
||||
- 未发现 `bash scripts/check-comment-paths.sh`。
|
||||
- 未发现独立的质量门或覆盖率上传步骤。
|
||||
|
||||
**Conflict to document clearly:**
|
||||
- `README.md` 写着“这些检查会在 CI/CD 流程中自动执行”,但 `.gitea/workflows/deploy.yaml` 中没有相应步骤。当前 CI 现实是“构建/部署优先,无显式质量校验门”。
|
||||
|
||||
## Common Verification Commands
|
||||
|
||||
```bash
|
||||
bash scripts/check-service-errors.sh # 校验 Service 错误处理规则
|
||||
bash scripts/check-comment-paths.sh # 校验 Handler 注释路径是否过时
|
||||
bash scripts/check-all.sh # 运行当前已存在的全部静态脚本检查
|
||||
go run cmd/gendocs/main.go # 重新生成 OpenAPI 文档,验证路由接线
|
||||
make docs # 等价于生成 OpenAPI 文档
|
||||
go run scripts/verify_migration/main.go # 人工验证数据库迁移结果
|
||||
```
|
||||
|
||||
## Current Gaps and Caveats
|
||||
|
||||
- `Makefile` 仍保留 `test:` 目标并执行 `go test -v ./...`,这属于历史遗留入口,不应被误解为现行团队要求。
|
||||
- `README.md` 的测试章节、目录结构和覆盖率目标明显滞后于当前 `AGENTS.md` 规则。
|
||||
- `scripts/check-service-errors.sh` 虽然存在,但当前仓库 `internal/service/polling/alert_service.go` 仍有 `fmt.Errorf(...)` 违规点,说明现有脚本检查并未通过 CI 强制执行。
|
||||
- `scripts/verify_migration/main.go` 内含直接数据库连接字符串,反映的是临时人工验证脚本,而不是可复用、无环境依赖的测试体系。
|
||||
|
||||
## Prescriptive Summary
|
||||
|
||||
- 讨论“测试”时,先区分三件事:`README.md` 的历史文档、`AGENTS.md` 的现行规则、仓库当前真实文件状态。
|
||||
- 在当前仓库中,默认不要新增 `*_test.go`;只有用户明确要求时才编写测试代码。
|
||||
- 日常验证优先使用:静态检查脚本、OpenAPI 文档再生成、数据库手查、Postman/curl、人肉回归。
|
||||
- 判断 CI 是否有质量门时,以 `.gitea/workflows/deploy.yaml` 为准,而不是 `README.md` 的说明。
|
||||
|
||||
---
|
||||
|
||||
*Testing analysis: 2026-03-27*
|
||||
36
.planning/config.json
Normal file
36
.planning/config.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"model_profile": "balanced",
|
||||
"commit_docs": true,
|
||||
"parallelization": true,
|
||||
"search_gitignored": false,
|
||||
"brave_search": false,
|
||||
"firecrawl": false,
|
||||
"exa_search": false,
|
||||
"git": {
|
||||
"branching_strategy": "none",
|
||||
"phase_branch_template": "gsd/phase-{phase}-{slug}",
|
||||
"milestone_branch_template": "gsd/{milestone}-{slug}",
|
||||
"quick_branch_template": null
|
||||
},
|
||||
"workflow": {
|
||||
"research": true,
|
||||
"plan_check": true,
|
||||
"verifier": true,
|
||||
"nyquist_validation": true,
|
||||
"auto_advance": false,
|
||||
"node_repair": true,
|
||||
"node_repair_budget": 2,
|
||||
"ui_phase": false,
|
||||
"ui_safety_gate": false,
|
||||
"text_mode": false,
|
||||
"research_before_questions": false,
|
||||
"discuss_mode": "discuss",
|
||||
"skip_discuss": false,
|
||||
"_auto_chain_active": false
|
||||
},
|
||||
"hooks": {
|
||||
"context_warnings": true
|
||||
},
|
||||
"agent_skills": {},
|
||||
"resolve_model_ids": "omit"
|
||||
}
|
||||
148
.planning/phases/02-perm-fin-realname-poll/02-01-SUMMARY.md
Normal file
148
.planning/phases/02-perm-fin-realname-poll/02-01-SUMMARY.md
Normal file
@@ -0,0 +1,148 @@
|
||||
---
|
||||
phase: 02-perm-fin-realname-poll
|
||||
plan: "01"
|
||||
subsystem: perm-fin-poll
|
||||
tags:
|
||||
- permissions
|
||||
- finance
|
||||
- polling
|
||||
- enterprise
|
||||
- concurrency
|
||||
dependency_graph:
|
||||
requires: []
|
||||
provides:
|
||||
- PERM-01
|
||||
- PERM-02
|
||||
- FIN-01
|
||||
- FIN-02
|
||||
- FIN-03
|
||||
- POLL-01
|
||||
- POLL-02
|
||||
- POLL-03
|
||||
- POLL-04
|
||||
affects:
|
||||
- internal/handler/admin/asset.go
|
||||
- internal/service/commission_withdrawal/service.go
|
||||
- internal/handler/admin/commission_withdrawal.go
|
||||
- internal/service/commission_withdrawal_setting/service.go
|
||||
- internal/store/postgres/wechat_config_store.go
|
||||
- internal/polling/scheduler.go
|
||||
- internal/service/iot_card/stop_resume_service.go
|
||||
- internal/service/asset/service.go
|
||||
- internal/store/postgres/package_store.go
|
||||
- internal/service/package_series/service.go
|
||||
tech_stack:
|
||||
added: []
|
||||
patterns:
|
||||
- gorm.io/gorm/clause FOR UPDATE 行锁防并发
|
||||
- go-playground/validator 参数校验
|
||||
- 函数签名扩展(分页参数)
|
||||
key_files:
|
||||
created: []
|
||||
modified:
|
||||
- internal/handler/admin/asset.go
|
||||
- internal/service/commission_withdrawal/service.go
|
||||
- internal/handler/admin/commission_withdrawal.go
|
||||
- internal/service/commission_withdrawal_setting/service.go
|
||||
- internal/store/postgres/wechat_config_store.go
|
||||
- internal/polling/scheduler.go
|
||||
- internal/service/iot_card/stop_resume_service.go
|
||||
- internal/service/asset/service.go
|
||||
- internal/model/dto/asset_dto.go
|
||||
- internal/store/postgres/package_store.go
|
||||
- internal/service/package_series/service.go
|
||||
- internal/bootstrap/handlers.go
|
||||
- internal/bootstrap/services.go
|
||||
- pkg/openapi/handlers.go
|
||||
decisions:
|
||||
- 企业账号权限边界:Resolve 可查(删除拦截),Refresh 不可主动刷新(新增拦截)
|
||||
- GetPackages 分页在内存中排序后切片,避免改动 Store 层查询逻辑
|
||||
- Series 删除检查在 Service 层实现(非 DB 约束),符合项目禁止外键规范
|
||||
- CommissionWithdrawalHandler 新增 validator 字段注入,与其他 Handler 保持一致
|
||||
metrics:
|
||||
duration: "约 10 分钟"
|
||||
completed_date: "2026-03-28"
|
||||
tasks: 2
|
||||
files: 14
|
||||
---
|
||||
|
||||
# Phase 02 Plan 01: PERM + FIN + POLL 9项独立修复 Summary
|
||||
|
||||
企业账号权限边界(Resolve 可查/Refresh 只读)、提现流程数据完整性(审批人字段/remark 必填/激活并发锁)、轮询调度和停复机安全修复,全部为独立小修,共 9 个需求 8 个 commit。
|
||||
|
||||
## 执行结果
|
||||
|
||||
所有 9 个需求(PERM-01/02, FIN-01/02/03, POLL-01/02/03/04)均已修复,`go build ./...` 零新增错误(已有两个预存在的 `service/package` 字段缺失错误不在本 plan 范围内)。
|
||||
|
||||
## Task 1: PERM + FIN 修复
|
||||
|
||||
### PERM-01 (commit: 2a92ab6)
|
||||
- **文件**: `internal/handler/admin/asset.go`
|
||||
- **变更**: 删除 `Resolve` 方法中对企业账号的错误拦截(企业账号应可查询资产)
|
||||
|
||||
### PERM-02 (commit: 2a92ab6)
|
||||
- **文件**: `internal/handler/admin/asset.go`
|
||||
- **变更**: `Refresh` 方法开头新增企业账号拦截(企业账号只读,不允许主动触发运营商刷新)
|
||||
|
||||
### FIN-01 (commit: 8f62496)
|
||||
- **文件**: `internal/service/commission_withdrawal/service.go`
|
||||
- **变更**: `Approve` 的 `updates` map 补充 `approved_by` 和 `approved_at` 字段
|
||||
|
||||
### FIN-02 (commit: 9ee2f0b)
|
||||
- **文件**: `internal/handler/admin/commission_withdrawal.go`, `internal/bootstrap/handlers.go`, `pkg/openapi/handlers.go`
|
||||
- **变更**: `CommissionWithdrawalHandler` 新增 `validator` 字段;`RejectWithdrawal` 补充 `validator.Struct` 参数验证,确保 `remark` 必填
|
||||
|
||||
### FIN-03 (commit: bdac4ab)
|
||||
- **文件**: `internal/service/commission_withdrawal_setting/service.go`, `internal/store/postgres/wechat_config_store.go`
|
||||
- **变更**: 激活配置事务内首步加 `clause.Locking{Strength: "UPDATE"}` 行锁,防止并发时两个事务同时置 `is_active=true`
|
||||
|
||||
## Task 2: POLL 修复
|
||||
|
||||
### POLL-01 (commit: 922a3d0)
|
||||
- **文件**: `internal/polling/scheduler.go`
|
||||
- **变更**: `processSchedule` 新增 `TaskTypePollingProtect` 的 `processManualQueue` 和 `processTimedQueue` 调用
|
||||
|
||||
### POLL-02 (commit: 8a53123)
|
||||
- **文件**: `internal/service/iot_card/stop_resume_service.go`
|
||||
- **变更**: `stopCardWithRetry` 和 `resumeCardWithRetry` 中 `gatewayClient==nil` 时返回 `errors.New(CodeInternalError, ...)` 而非 `nil`(原来假装成功会误更新卡状态)
|
||||
|
||||
### POLL-03 (commit: 8553a46)
|
||||
- **文件**: `internal/model/dto/asset_dto.go`, `internal/service/asset/service.go`, `internal/handler/admin/asset.go`, `internal/handler/app/client_asset.go`
|
||||
- **变更**: 新增 `AssetPackagesResult` DTO;`GetPackages` 签名新增 `page/pageSize` 参数(默认 page=1, pageSize=50, max=100);admin handler 从 Query 参数读取;client_asset.go 调用处同步更新
|
||||
|
||||
### POLL-04 (commit: 47aa5f8)
|
||||
- **文件**: `internal/store/postgres/package_store.go`, `internal/service/package_series/service.go`, `internal/bootstrap/services.go`
|
||||
- **变更**: `PackageStore` 新增 `CountBySeriesID` 方法;`PackageSeries.Service` 注入 `packageStore` 字段;`Delete` 前检查关联套餐数量,有则拒绝删除
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 3 - Blocking] pkg/openapi/handlers.go 需同步更新签名**
|
||||
- **Found during**: Task 1 FIN-02 完成后最终编译验证
|
||||
- **Issue**: `CommissionWithdrawalHandler` 构造函数新增了 `validator` 参数,`pkg/openapi/handlers.go` 中文档生成器调用处仍使用旧签名导致编译失败
|
||||
- **Fix**: `admin.NewCommissionWithdrawalHandler(nil)` 改为 `admin.NewCommissionWithdrawalHandler(nil, nil)`
|
||||
- **Files modified**: `pkg/openapi/handlers.go`
|
||||
- **Commit**: 754bbcd
|
||||
|
||||
## Known Stubs
|
||||
|
||||
无 — 所有修复均为实际业务逻辑,无留桩或占位符。
|
||||
|
||||
## Commits
|
||||
|
||||
| Commit | Message |
|
||||
|--------|---------|
|
||||
| 2a92ab6 | fix(perm): 修复企业账号资产权限边界 PERM-01/02 |
|
||||
| 8f62496 | fix(fin): 补全提现审批人字段 FIN-01 |
|
||||
| 9ee2f0b | fix(fin): 提现拒绝补 remark 必填校验 FIN-02 |
|
||||
| bdac4ab | fix(fin): 激活配置并发行锁保护 FIN-03 |
|
||||
| 922a3d0 | fix(poll): polling:protect 接入调度器 POLL-01 |
|
||||
| 8a53123 | fix(poll): gatewayClient=nil 时停复机返回错误而非成功 POLL-02 |
|
||||
| 8553a46 | fix(poll): GetPackages 接口新增分页 POLL-03 |
|
||||
| 47aa5f8 | fix(poll): Series 删除前检查关联套餐 POLL-04 |
|
||||
| 754bbcd | fix(fin): 同步更新 openapi handlers 中 CommissionWithdrawalHandler 签名 |
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
所有关键文件存在,所有 commits 均可在 git log 中查到。
|
||||
Reference in New Issue
Block a user