
---

# junhong_cmp_fiber 项目开发规范

**重要**: 本文件包含核心规范。详细规范已提取为 Skills，在特定任务时按需加载。

## 专项规范 Skills（按需加载）

以下规范在相关任务时**自动触发**，无需手动加载：

| 任务类型 | 触发 Skill | 说明 |
|---------|-----------|------|
| 创建/修改 DTO 文件 | `dto-standards` | description 标签、枚举字段、验证标签规范 |
| 创建/修改 Model 模型 | `model-standards` | GORM 模型结构、字段标签、TableName 规范 |
| 注册 API 路由 / **新增 Handler** | `api-routing` | Register() 函数、RouteSpec、**文档生成器更新** |
| 测试接口/验证数据 | `db-validation` | PostgreSQL MCP 使用方法和验证示例 |
| 数据库迁移 | `db-migration` | 迁移命令、文件规范、执行流程、失败处理 |
| 维护规范文档 | `doc-management` | 规范文档流程和维护规则 |
| 调试 bug / 排查异常 | `systematic-debugging` | 四阶段根因分析流程、逐层诊断、场景速查表 |

### ⚠️ 新增 Handler 时必须同步更新文档生成器

新增 Handler 后，接口不会自动出现在 OpenAPI 文档中。**必须手动更新以下两个文件**：

```go
// cmd/api/docs.go 和 cmd/gendocs/main.go
handlers := &bootstrap.Handlers{
    // ... 添加新 Handler
    NewHandler: admin.NewXxxHandler(nil),
}
```

**完整检查清单**: 参见 [`docs/api-documentation-guide.md`](docs/api-documentation-guide.md#新增-handler-检查清单)

---

## 语言要求

**必须遵守:**

- 永远用中文交互
- 注释必须使用中文
- 文档必须使用中文
- 日志消息必须使用中文
- 用户可见的错误消息必须使用中文
- 变量名、函数名、类型名必须使用英文（遵循 Go 命名规范）
- GIT提交的commit必须使用中文

## 技术栈

**必须严格遵守，禁止替代方案:**

| 类型 | 技术 |
|------|------|
| HTTP 框架 | Fiber v2.x |
| ORM | GORM v1.25.x |
| 配置管理 | Viper |
| 日志 | Zap + Lumberjack.v2 |
| JSON 序列化 | sonic（优先），encoding/json（必要时） |
| 验证 | Validator |
| 任务队列 | Asynq v0.24.x |
| 数据库 | PostgreSQL 14+ |
| 缓存 | Redis 6.0+ |

**禁止:**

- 直接使用 `database/sql`（必须通过 GORM）
- 使用 `net/http` 替代 Fiber
- 使用 `encoding/json` 替代 sonic（除非必要）

## 架构分层

必须遵循以下分层架构：

```
Handler → Service → Store → Model
```

- **Handler**: 只处理 HTTP 请求/响应，不包含业务逻辑
- **Service**: 包含所有业务逻辑，支持跨模块调用
- **Store**: 统一管理所有数据访问，支持事务处理
- **Model**: 定义数据结构和 DTO

## 核心原则

### 错误处理

- 所有错误必须在 `pkg/errors/` 中定义
- 使用统一错误码系统
- Handler 层通过返回 `error` 传递给全局 ErrorHandler

#### 错误报错规范（必须遵守）

- Handler 层禁止直接返回/拼接底层错误信息给客户端（例如 `"参数验证失败: "+err.Error()`、`err.Error()`）
- 参数校验失败：对外统一返回 `errors.New(errors.CodeInvalidParam)`（详细校验错误写日志）
- Service 层禁止对外返回 `fmt.Errorf(...)`，必须返回 `errors.New(...)` 或 `errors.Wrap(...)`
- 约定用法：`errors.New(code[, msg])`、`errors.Wrap(code, err[, msg])`

### 响应格式

- 所有 API 响应使用 `pkg/response/` 的统一格式
- 格式: `{code, msg, data, timestamp}`

### 常量管理

- 所有常量定义在 `pkg/constants/`
- Redis key 使用函数生成: `Redis{Module}{Purpose}Key(params...)`
- 禁止硬编码字符串和 magic numbers
- **必须为所有常量添加中文注释**

### 注释规范

#### 基本原则

- **所有注释使用中文**（与语言要求一致）
- **导出符号必须有文档注释**（包、函数、方法、类型、接口、常量、变量）
- **复杂逻辑必须有实现注释**（解释"为什么"，而不是"做了什么"）
- **禁止废话注释**（不要用注释复述代码本身）
- **修改代码时必须同步更新注释**（过时的注释比没有注释更有害）

#### 包注释

每个包的入口文件（通常是主文件或 `doc.go`）必须有包注释：

```go
// Package account 提供账号管理的业务逻辑服务
// 包含账号创建、修改、删除、权限分配等功能
package account
```

#### 结构体注释

所有导出结构体必须有文档注释，说明该结构体代表什么：

```go
// Service 账号业务服务
// 负责账号的 CRUD、角色分配、密码管理等业务逻辑
type Service struct {
    store        *Store
    auditService AuditServiceInterface
}
```

#### 接口注释

导出接口必须注释接口用途，每个方法必须说明契约：

```go
// PermissionChecker 权限检查器接口
// 用于查询用户的权限列表
type PermissionChecker interface {
    // CheckPermission 检查用户是否拥有指定权限
    // userID: 用户ID
    // permCode: 权限编码（格式: module:action）
    // platform: 端口类型 (all/web/h5)
    CheckPermission(ctx context.Context, userID uint, permCode string, platform string) (bool, error)
}
```

#### 函数和方法注释

导出函数/方法必须以函数名开头，说明功能：

```go
// Create 创建账号
// POST /api/admin/accounts
func (h *AccountHandler) Create(c *fiber.Ctx) error {
```

**复杂方法**（超过 30 行或包含复杂业务逻辑）必须额外说明实现思路：

```go
// ActivateByRealname 首次实名激活套餐
// 当用户完成实名认证后，自动激活处于"囤货待实名"状态的套餐：
// 1. 查找该卡所有 status=3（待实名激活）的套餐
// 2. 按创建时间排序，第一个主套餐立即激活（status=1）
// 3. 其余主套餐进入排队状态（status=4）
// 4. 加油包如果绑定了已激活的主套餐则一并激活
func (s *UsageService) ActivateByRealname(ctx context.Context, cardID uint) error {
```

#### 未导出符号的注释

未导出（小写）的函数/方法：

- **简单逻辑**（< 15 行）：可以不加注释
- **复杂逻辑**（≥ 15 行）或 **非显而易见的算法**：必须加注释

```go
// buildPermissionTree 递归构建权限树
// 采用 map 索引 + 单次遍历算法，时间复杂度 O(n)
func (s *Service) buildPermissionTree(permissions []*model.Permission) []*dto.PermissionTreeNode {
```

#### 内联注释（实现逻辑注释）

以下场景**必须**添加内联注释：

| 场景 | 要求 |
|------|------|
| 复杂条件判断 | 解释判断的业务含义 |
| 多步骤业务流程 | 用编号注释标明每一步 |
| 非显而易见的设计决策 | 解释"为什么这样做"而不是"做了什么" |
| 缓存/事务/并发处理 | 说明策略和原因 |
| 临时方案/兼容逻辑 | 标注 TODO 或说明背景 |

**✅ 好的内联注释（解释为什么）**：

```go
// 使用 Redis 分布式锁防止并发重复创建，锁超时 10 秒
if !s.acquireLock(ctx, lockKey, 10*time.Second) {
    return errors.New(errors.CodeTooManyRequests, "操作过于频繁，请稍后重试")
}

// 先冻结佣金再扣款，保证资金安全（失败时佣金自动解冻）
if err := s.freezeCommission(ctx, tx, orderID); err != nil {
    return err
}
```

**❌ 废话注释（禁止）**：

```go
// 获取用户ID          ← 禁止：代码本身已经很清楚
userID := middleware.GetUserIDFromContext(ctx)

// 创建账号            ← 禁止：变量名已说明意图
account := &model.Account{}

// 返回错误            ← 禁止：return err 不需要注释
return err
```

#### 常量和枚举注释

分组常量必须有组注释，每个值必须有行内注释：

```go
// 用户类型常量
const (
    UserTypeSuperAdmin = 1 // 超级管理员
    UserTypePlatform   = 2 // 平台用户
    UserTypeAgent      = 3 // 代理账号
    UserTypeEnterprise = 4 // 企业账号
)
```

#### Handler 层特殊要求

Handler 方法的注释必须包含 HTTP 方法和路径：

```go
// Create 创建账号
// POST /api/admin/accounts
func (h *AccountHandler) Create(c *fiber.Ctx) error {
```

### Go 代码风格

- 使用 `gofmt` 格式化
- 遵循 [Effective Go](https://go.dev/doc/effective_go)
- 包名: 简短、小写、单数、无下划线
- 接口命名: 使用 `-er` 后缀（Reader、Writer、Logger）

## 数据库设计

**核心规则:**

- ❌ 禁止建立外键约束
- ❌ 禁止使用 GORM 关联关系标签（foreignKey、hasMany、belongsTo）
- ✅ 关联通过存储 ID 字段手动维护
- ✅ 关联数据在代码层面显式查询

## Go 惯用法 vs Java 风格

### ✅ Go 风格（推荐）

- 扁平化包结构（最多 2-3 层）
- 小而专注的接口（1-3 个方法）
- 直接访问导出字段（不用 getter/setter）
- 组合优于继承
- 显式错误返回和检查

### ❌ Java 风格（禁止）

- 过度抽象（不必要的接口、工厂）
- Getter/Setter 方法
- 深层继承层次
- 异常处理（panic/recover）
- 类型前缀（IService、AbstractBase、ServiceImpl）

## ⚠️ 测试禁令（强制执行）

**本项目不使用任何形式的自动化测试代码。**

**绝对禁止：**

- ❌ **禁止编写单元测试** - 无论任何场景
- ❌ **禁止编写集成测试** - 无论任何场景
- ❌ **禁止编写验收测试** - 无论任何场景
- ❌ **禁止编写流程测试** - 无论任何场景
- ❌ **禁止编写 E2E 测试** - 无论任何场景
- ❌ **禁止创建 `*_test.go` 文件** - 除非用户明确要求
- ❌ **禁止在任务中包含测试相关工作** - 规划和实现均不涉及测试
- ❌ **禁止在文档中提及测试要求** - 规范、设计文档均不讨论测试

**唯一例外：**

- ✅ **仅当用户明确要求**时才编写测试代码
- ✅ 用户必须主动说明"请写测试"或"需要测试"

**原因说明：**

- 业务系统的正确性通过人工验证和生产环境监控保证
- 测试代码的维护成本高于价值
- 快速迭代优先于测试覆盖率

**替代方案：**

- 使用 PostgreSQL MCP 工具手动验证数据
- 使用 Postman/curl 手动测试 API
- 依赖生产环境日志和监控发现问题

## 性能要求

- API P95 响应时间 < 200ms
- API P99 响应时间 < 500ms
- 数据库查询 < 50ms
- 列表查询必须分页（默认 20，最大 100）
- 避免 N+1 查询，使用批量操作

## 文档要求

- 每个功能在 `docs/{feature-id}/` 创建总结文档
- 文档文件名和内容使用中文
- 同步更新 README.md
- 为导出的函数、类型编写文档注释

## 函数复杂度

- 函数长度 ≤ 100 行（核心逻辑建议 ≤ 50 行）
- `main()` 函数只做编排，不含具体实现
- 遵循单一职责原则

## 访问日志

- 所有 HTTP 请求记录到 `access.log`
- 记录完整的请求/响应（限制 50KB）
- 包含: method, path, query, status, duration, request_id, ip, user_agent, user_id, bodies
- 使用 JSON 格式，配置自动轮转

## OpenSpec 工作流

创建提案前的检查清单：

1. ✅ 技术栈合规
2. ✅ 架构分层正确
3. ✅ 使用统一错误处理
4. ✅ 常量定义在 pkg/constants/
5. ✅ Go 惯用法（非 Java 风格）
6. ✅ 性能考虑
7. ✅ 文档更新计划
8. ✅ 中文优先

## Code Review 检查清单

### 错误处理

- [ ] Service 层无 `fmt.Errorf` 对外返回
- [ ] Handler 层参数校验不泄露细节
- [ ] 错误码使用正确（4xx vs 5xx）
- [ ] 错误日志完整（包含上下文）

### 代码质量

- [ ] 遵循 Handler → Service → Store → Model 分层
- [ ] 函数长度 ≤ 100 行（核心逻辑 ≤ 50 行）
- [ ] 常量定义在 `pkg/constants/`
- [ ] 使用 Go 惯用法（非 Java 风格）

### 文档和注释

- [ ] 所有注释使用中文
- [ ] 导出函数/类型有文档注释
- [ ] API 路径注释与真实路由一致

### 幂等性

- [ ] 创建类写操作有 Redis 业务键防重
- [ ] 状态变更使用条件更新（`WHERE status = expected`）
- [ ] 余额/库存变更使用乐观锁（version 字段）
- [ ] 分布式锁使用 `defer` 确保释放
- [ ] Redis Key 定义在 `pkg/constants/redis.go`

### 越权防护规范

**适用场景**：任何涉及跨用户、跨店铺、跨企业的资源访问

**三层防护机制**：

1. **路由层中间件**（粗粒度拦截）
   - 用于明显的权限限制（如企业账号禁止访问账号管理）
   - 示例：

     ```go
     group.Use(func(c *fiber.Ctx) error {
         userType := middleware.GetUserTypeFromContext(c.UserContext())
         if userType == constants.UserTypeEnterprise {
             return errors.New(errors.CodeForbidden, "无权限访问账号管理功能")
         }
         return c.Next()
     })
     ```

2. **Service 层业务检查**（细粒度验证）
   - 使用 `middleware.CanManageShop(ctx, targetShopID, shopStore)` 验证店铺权限
   - 使用 `middleware.CanManageEnterprise(ctx, targetEnterpriseID, enterpriseStore, shopStore)` 验证企业权限
   - 类型级权限检查（如代理不能创建平台账号）
   - 示例见 `internal/service/account/service.go`

3. **GORM Callback 自动过滤**（兜底）
   - 已有实现，自动应用到所有查询
   - 代理用户：`WHERE shop_id IN (自己店铺+下级店铺)`
   - 企业用户：`WHERE enterprise_id = 当前企业ID`
   - 无需手动调用

**统一错误返回**：

- 越权访问统一返回：`errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")`
- 不区分"不存在"和"无权限"，防止信息泄露

### 幂等性规范

**适用场景**：任何可能被重复触发的写操作

#### 必须实现幂等性的场景

| 场景 | 原因 | 实现策略 |
|------|------|----------|
| 订单创建 | 用户双击、网络重试 | Redis 业务键防重 + 分布式锁 |
| 支付回调 | 第三方平台重复通知 | 状态条件更新（`WHERE status = pending`） |
| 钱包扣款/充值 | 并发请求、消息重投 | 乐观锁（version 字段）+ 状态条件更新 |
| 套餐激活 | 异步任务重试 | Redis 分布式锁 + 已存在记录检查 |
| 异步任务处理 | Asynq 自动重试 | Redis 任务锁（`RedisTaskLockKey`） |
| 佣金计算 | 支付成功后触发 | 幂等任务入队 + 状态检查 |

#### 不需要幂等性的场景

- 纯查询接口（GET 请求天然幂等）
- 管理后台的配置修改（低频操作，人为确认）
- 日志记录、审计记录（允许重复写入）

#### 实现策略选择

根据场景特征选择合适的策略：

**策略 1：状态条件更新（首选，适用于有明确状态流转的操作）**

```go
// 通过 WHERE 条件确保只有预期状态才能更新，RowsAffected == 0 说明已被处理
result := tx.Model(&model.Order{}).
    Where("id = ? AND payment_status = ?", orderID, model.PaymentStatusPending).
    Updates(map[string]any{"payment_status": model.PaymentStatusPaid})

if result.RowsAffected == 0 {
    // 已被处理，检查当前状态决定返回成功还是错误
}
```

**策略 2：Redis 业务键防重 + 分布式锁（适用于创建类操作，无状态可依赖）**

```go
// 业务键 = 唯一标识请求意图的组合字段
// 示例：order:create:{buyer_type}:{buyer_id}:{carrier_type}:{carrier_id}:{sorted_package_ids}
idempotencyKey := buildBusinessKey(...)
redisKey := constants.RedisXxxIdempotencyKey(idempotencyKey)

// 第 1 层：Redis GET 快速检测
val, err := s.redis.Get(ctx, redisKey).Result()
if err == nil && val != "" {
    return existingResult  // 已创建，直接返回
}

// 第 2 层：分布式锁防止并发
lockKey := constants.RedisXxxLockKey(resourceType, resourceID)
locked, _ := s.redis.SetNX(ctx, lockKey, time.Now().String(), lockTTL).Result()
if !locked {
    return errors.New(errors.CodeTooManyRequests, "操作进行中，请勿重复提交")
}
defer s.redis.Del(ctx, lockKey)

// 第 3 层：加锁后二次检测
val, err = s.redis.Get(ctx, redisKey).Result()
if err == nil && val != "" {
    return existingResult
}

// 执行业务逻辑...

// 成功后标记
s.redis.Set(ctx, redisKey, resultID, idempotencyTTL)
```

**策略 3：乐观锁（适用于余额、库存等数值更新）**

```go
result := tx.Model(&model.Wallet{}).
    Where("id = ? AND balance >= ? AND version = ?", walletID, amount, currentVersion).
    Updates(map[string]any{
        "balance": gorm.Expr("balance - ?", amount),
        "version": gorm.Expr("version + 1"),
    })
if result.RowsAffected == 0 {
    return errors.New(errors.CodeInsufficientBalance, "余额不足或并发冲突")
}
```

#### Redis Key 命名规范

幂等性相关的 Redis Key 统一在 `pkg/constants/redis.go` 定义：

```go
// 幂等性检测键：Redis{Module}IdempotencyKey — TTL 通常 3~5 分钟
func RedisOrderIdempotencyKey(businessKey string) string

// 分布式锁键：Redis{Module}{Action}LockKey — TTL 通常 10~30 秒
func RedisOrderCreateLockKey(carrierType string, carrierID uint) string
```

#### 现有幂等性实现参考

| 模块 | 文件 | 策略 |
|------|------|------|
| 订单创建 | `internal/service/order/service.go` → `Create()` | 策略 2：Redis 业务键 + 分布式锁 |
| 钱包支付 | `internal/service/order/service.go` → `WalletPay()` | 策略 1：状态条件更新 |
| 支付回调 | `internal/service/order/service.go` → `HandlePaymentCallback()` | 策略 1：状态条件更新 |
| 套餐激活 | `internal/service/package/activation_service.go` → `ActivateQueuedPackage()` | 策略 2（简化版）：Redis 分布式锁 |
| 钱包扣款 | `internal/service/order/service.go` → `WalletPay()` | 策略 3：乐观锁（version 字段） |

### 审计日志规范

**适用场景**：任何敏感操作（账号管理、权限变更、数据删除等）

**使用方式**：

1. **Service 层集成审计日志**：

   ```go
   type Service struct {
       store       *Store
       auditService AuditServiceInterface
   }
   
   func (s *Service) SensitiveOperation(ctx context.Context, ...) error {
       // 1. 执行业务操作
       err := s.store.DoSomething(ctx, ...)
       if err != nil {
           return err
       }
       
       // 2. 记录审计日志（异步）
       s.auditService.LogOperation(ctx, &model.OperationLog{
           OperatorID:    middleware.GetUserIDFromContext(ctx),
           OperationType: "operation_type",
           OperationDesc: "操作描述",
           BeforeData:    beforeData,  // 变更前数据
           AfterData:     afterData,   // 变更后数据
           RequestID:     middleware.GetRequestIDFromContext(ctx),
           IPAddress:     middleware.GetIPFromContext(ctx),
           UserAgent:     middleware.GetUserAgentFromContext(ctx),
       })
       
       return nil
   }
   ```

2. **审计日志字段说明**：
   - `operator_id`, `operator_type`, `operator_name`: 操作人信息（必填）
   - `target_*`: 目标资源信息（可选）
   - `operation_type`: 操作类型（create/update/delete/assign_roles等）
   - `operation_desc`: 操作描述（中文，便于查看）
   - `before_data`, `after_data`: 变更数据（JSON 格式）
   - `request_id`, `ip_address`, `user_agent`: 请求上下文

3. **异步写入**：
   - 审计日志使用 Goroutine 异步写入
   - 写入失败不影响业务操作
   - 失败时记录 Error 日志，包含完整审计信息

**示例参考**：`internal/service/account/service.go`

---

### ⚠️ 任务执行规范（必须遵守）

**提案中的 tasks.md 是契约，不可擅自变更：**

| 规则 | 说明 |
|------|------|
| ❌ 禁止跳过任务 | 每个任务都是经过规划的，不能因为"简单"或"显而易见"而跳过 |
| ❌ 禁止简化任务 | 不能将多个任务合并或简化执行，除非获得明确许可 |
| ❌ 禁止自作主张优化 | 发现可以优化的地方，必须先询问是否可以调整 |
| ✅ 必须逐项完成 | 按照 tasks.md 中的顺序逐一执行并标记完成 |
| ✅ 必须询问后变更 | 如需调整任务（简化/跳过/合并/优化），先询问用户确认 |

**询问示例**：
> "我注意到任务 2.1 和 2.2 可以合并为一步完成，是否可以这样优化？"
> "任务 3.1 在当前实现中可能不需要，是否可以跳过？"

**详细规范和 OpenSpec 工作流请查看**: `@/openspec/AGENTS.md`

# English Learning Mode

  The user is learning English through practical use. Apply these rules in every conversation:

  1. **Always respond in Chinese** — regardless of whether the user writes in English or Chinese.

  2. **When the user writes in English**, append a one-line correction at the end of your response in this format:
     → `[natural version of what they wrote]`
     No explanation needed — just the corrected phrase.

  3. **When the user mixes Chinese into English** (e.g., "I want to 实现 dark mode"), translate the Chinese word/phrase inline and continue naturally. Do not make a
  big deal of it.

  4. **Never interrupt the flow** to give grammar lessons. Corrections are silent and brief — the user's focus is on the task, not the language.

<!-- GSD:project-start source:PROJECT.md -->
## Project

**君鸿卡管系统**

物联网卡（IoT SIM）+ 号卡 + 设备全生命周期管理平台，支持多级代理商体系和分佣结算。平台以 B 端代理商为核心分销渠道，兼顾 C 端个人客户和企业客户，覆盖从卡/设备采购、分配、激活、套餐购买到佣金结算的完整业务链路。当前处于 MVP 阶段，核心功能模块已完成，目标是修复所有已知业务链路断点、补全缺失功能，将系统调整至可生产上线状态。

**Core Value:** **业务链路完整可用**——代理能采购分配卡/设备，C端客户能充值购包激活套餐，佣金能正确计算并可提现。这条主链路必须端到端跑通。

### Constraints

- **Tech Stack**: 严格使用 Fiber v2 + GORM + Asynq + PostgreSQL + Redis，不引入替代框架
- **No Tests**: 项目不写自动化测试，人工验收（rg + go build + DBHub + 日志）
- **Language**: 所有注释/日志/错误消息/文档使用中文，变量/函数名使用英文
- **No Foreign Keys**: 禁止数据库外键约束，关联通过 ID 字段在代码层维护
- **方案 E 风险**: 流量体系改革涉及 DB 迁移 + Redis 架构，必须低峰期发布，单独规划
<!-- GSD:project-end -->

<!-- GSD:stack-start source:codebase/STACK.md -->
## Technology Stack

## Languages
- Go 1.25.x - 应用代码、API、Worker、OpenAPI 生成和基础设施脚本，依据 `go.mod`、`cmd/api/main.go`、`cmd/worker/main.go`、`cmd/gendocs/main.go`
- YAML - 配置与部署编排，见 `pkg/config/defaults/config.yaml`、`docker-compose.prod.yml`、`.gitea/workflows/deploy.yaml`
- Dockerfile - API/Worker 容器构建，见 `Dockerfile.api`、`Dockerfile.worker`
- Makefile - 本地构建、文档生成、迁移命令，见 `Makefile`
## Runtime
- Go runtime；API 服务基于 Fiber HTTP 服务器，见 `cmd/api/main.go`
- 独立 Worker 进程处理异步任务与调度，见 `cmd/worker/main.go`
- 生产容器基础镜像为 Alpine，构建镜像使用 Go 1.25.6 Alpine，见 `Dockerfile.api`、`Dockerfile.worker`
- Go Modules，见 `go.mod`
- Lockfile: `go.sum` present
## Frameworks
- 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`
- Asynq v0.25.1 - 任务提交、Worker 消费、定时任务，见 `go.mod`、`pkg/queue/client.go`、`pkg/queue/server.go`、`cmd/worker/main.go`
- sonic v1.14.2 - Fiber JSON 编解码与任务载荷序列化，见 `go.mod`、`cmd/api/main.go`、`pkg/queue/client.go`
- validator/v10 v10.28.0 - DTO 校验依赖，见 `go.mod`
- 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`
- swaggest/openapi-go v0.2.60 - OpenAPI 3.0.3 文档生成，见 `go.mod`、`pkg/openapi/generator.go`
## Build / Dev / Deploy
- `Makefile` 定义 `build`、`run`、`run-worker`、`docs`、`migrate-*` 目标，见 `Makefile`
- API 与 Worker 使用多阶段 Docker 构建，编译产物分别来自 `./cmd/api` 与 `./cmd/worker`，见 `Dockerfile.api`、`Dockerfile.worker`
- 生产编排使用 Docker Compose，运行 `api` 与 `worker` 两个服务，见 `docker-compose.prod.yml`
- Gitea Actions 工作流构建镜像、推送私有镜像仓库并在主分支执行 `docker compose up -d`，见 `.gitea/workflows/deploy.yaml`
- 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
- PostgreSQL 是唯一检测到的主数据库；连接、连接池、GORM logger、Ping 校验位于 `pkg/database/postgres.go`
- Redis 用于认证、缓存、队列后端、限流存储和配置缓存，见 `pkg/database/redis.go`、`cmd/api/main.go`、`internal/service/wechat_config/service.go`
- Asynq 直接复用 Redis 连接配置作为消息后端，见 `pkg/queue/client.go`、`pkg/queue/server.go`
- Worker 内注册订单超时、告警检查、数据清理等调度任务，见 `cmd/worker/main.go`
- S3 兼容对象存储通过 AWS SDK v1 实现，支持上传、下载、预签名 URL、临时文件下载，见 `pkg/storage/s3.go`、`pkg/storage/service.go`
## Configuration
- 默认配置从嵌入文件 `pkg/config/defaults/config.yaml` 读取，见 `pkg/config/embedded.go`
- 运行时使用 `JUNHONG_` 前缀环境变量覆盖，见 `pkg/config/loader.go`
- 必填配置校验包括数据库、Redis、JWT，见 `pkg/config/config.go`
- 服务与超时：`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`
- 微信公众号/小程序/支付配置不走 Viper 主配置，业务侧从数据库表 `tb_wechat_config` 读取并缓存到 Redis，见 `internal/model/wechat_config.go`、`internal/service/wechat_config/service.go`、`pkg/wechat/config.go`
## Logging & Observability
- `pkg/logger/logger.go` 初始化 app/access 双 logger；生产模式写 JSON，开发模式 app logger 同时输出控制台
- `pkg/logger/middleware.go` 记录 method、path、query、status、duration、request_id、ip、user_agent、user_id、请求/响应 body
- `pkg/database/postgres.go` 自定义 GORM logger，记录错误、慢查询和普通 SQL trace
- `Dockerfile.api` 与 `docker-compose.prod.yml` 均通过 `GET /health` 做容器健康检查
## Notable Dependencies
- `github.com/gofiber/fiber/v2` - API 服务核心框架，见 `go.mod`、`cmd/api/main.go`
- `github.com/google/uuid` - 请求 ID 生成，见 `cmd/api/main.go`
- `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`
- `github.com/hibiken/asynq` - 队列与调度器，见 `go.mod`、`pkg/queue/*.go`、`cmd/worker/main.go`
- `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 解析
- `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
- 需要 PostgreSQL、Redis、JWT 密钥与 `JUNHONG_` 环境变量；`config.Load()` 在启动时强校验，见 `pkg/config/config.go`
- `make docs` 需要 Go 环境；`migrate-*` 目标需要系统安装 `migrate` 命令，见 `Makefile`
- 目标运行形态是 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`
<!-- GSD:stack-end -->

<!-- GSD:conventions-start source:CONVENTIONS.md -->
## Conventions

## Naming Patterns
- 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`。
- 导出函数/方法使用 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`。
- 局部变量使用英文 `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`。
- 结构体和接口使用 `PascalCase`，接口语义化命名，配合 `-er` 后缀规则；项目规范明确要求接口名使用 `-er`，见 `AGENTS.md`。
- 路由文档元数据类型使用语义名，如 `RouteSpec`、`FileUploadField`，证据见 `internal/routes/registry.go`。
## Code Style
- 使用 `gofmt`；项目在 `AGENTS.md` 明确要求“使用 gofmt 格式化”。
- 代码风格遵循 Go 惯用法，避免 Java 式过度抽象；证据见 `AGENTS.md` 的“Go 惯用法 vs Java 风格”。
- 仓库未检测到 `.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
- 多数组件按“标准库 → 项目包/第三方包”分组，并保留空行分隔，证据见 `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`。
- 未检测到 TS/JS 式路径别名；Go 代码通过 module path `github.com/break/junhong_cmp_fiber/...` 引用。
## Layering Rules
- 必须遵循 `Handler → Service → Store → Model`，证据见 `AGENTS.md` 与 `README.md`。
- 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
- 所有错误码与应用错误类型集中在 `pkg/errors/`，证据见 `pkg/errors/codes.go`、`pkg/errors/errors.go`、`pkg/errors/handler.go`、`AGENTS.md`。
- 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`。
- `pkg/errors/codes.go` 定义 1000-1999 客户端错误、2000-2999 服务端错误。
- `pkg/errors/codes.go` 在 `init()` 校验全部错误码都必须映射消息，新增错误码时要同步维护 `allErrorCodes` 和 `errorMessages`。
## Response Format
- 所有 API 成功响应统一使用 `pkg/response/response.go`：`{code, data, msg, timestamp}`。
- `Success()` 返回 `msg: "success"`；`SuccessWithMessage()` 允许覆盖消息；`SuccessWithPagination()` 将分页数据包进 `PaginationData`。
- 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
- 所有常量统一放在 `pkg/constants/`，证据见 `AGENTS.md` 与 `pkg/constants/constants.go`、`pkg/constants/redis.go`。
- 禁止硬编码字符串与 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
- 所有 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`。
- 每个接口需要提供中文 `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 时，除业务接线外，还必须同步更新 `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
- 每个功能应在 `docs/{feature-id}/` 创建总结文档，并同步更新 `README.md`，证据见 `AGENTS.md`。
- 文档和说明统一使用中文。
- API 文档规范集中在 `docs/api-documentation-guide.md`；开发总规范集中在 `AGENTS.md`。
- `README.md` 仍保留旧的自动化测试与覆盖率章节、旧项目结构中的 `tests/` 目录描述、以及“CI/CD 自动执行检查”的表述。
- 当前仓库未发现任何 `*_test.go` 文件，也未发现 `tests/` 目录内容；因此后续文档和执行应优先遵循 `AGENTS.md` 的现行规则，而不是 `README.md` 中这些过期段落。
## Operational Scripts
- `bash scripts/check-service-errors.sh`：检查 Service 层是否违规使用 `fmt.Errorf`。
- `bash scripts/check-comment-paths.sh`：检查 Handler 注释路径是否残留 `/api/v1`。
- `bash scripts/check-all.sh`：运行上述两个检查。
- `make docs` → `go run cmd/gendocs/main.go`，用于生成 `docs/admin-openapi.yaml`，证据见 `Makefile`、`cmd/gendocs/main.go`。
- `go run scripts/verify_migration/main.go`：这是一个面向数据库迁移结果的人工验证脚本，会直接连接数据库并查询字段，不是自动测试框架，证据见 `scripts/verify_migration/main.go`。
- `Makefile` 的 `test` 目标仍定义为 `go test -v ./...`，但这反映的是旧工具入口，不代表当前团队允许自动化测试。
## Module Design
- 构造函数使用 `NewXxx...`，例如 `NewAccountHandler()`、`NewGenerator()`。
- Handler、response、errors、constants 都以小包单职责方式导出少量明确入口，证据见 `internal/handler/admin/account.go`、`pkg/response/response.go`、`pkg/errors/errors.go`。
- 未检测到 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` 中的测试/覆盖率描述当成当前执行标准。
<!-- GSD:conventions-end -->

<!-- GSD:architecture-start source:ARCHITECTURE.md -->
## Architecture

## Pattern Overview
- 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
- 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: 整个应用进程。
- 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`
- 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。
- 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 组装期专用读模型。
- 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 生成。
- 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
- 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
- Purpose: 启动期依赖编排结果。
- Examples: `internal/bootstrap/bootstrap.go`, `internal/bootstrap/worker.go`
- Pattern: 显式依赖注入，不使用外部 DI 框架。
- Purpose: 路由注册时的装配清单。
- Examples: `internal/bootstrap/types.go`, `internal/bootstrap/handlers.go`, `internal/bootstrap/middlewares.go`
- Pattern: 结构体聚合所有已构造的 Handler 与 Middleware。
- Purpose: 单次声明同时完成真实路由注册和 OpenAPI 元数据注册。
- Examples: `internal/routes/registry.go`, `internal/routes/account.go`, `internal/routes/personal.go`
- Pattern: 代码即文档，DTO 驱动文档生成。
- Purpose: Store 通用分页与排序选项。
- Examples: `internal/store/options.go`, `internal/service/account/service.go`, `internal/service/order/service.go`
- Pattern: Service 先构造查询选项，再下传到 Store。
- 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
- Location: `cmd/api/main.go`
- Triggers: `go run cmd/api/main.go`、API 容器启动。
- Responsibilities: 加载配置、初始化基础设施、执行 `bootstrap.Bootstrap()`、创建 Fiber、注册中间件和路由、生成运行时 OpenAPI 文件 `logs/openapi.yaml`。
- Location: `cmd/worker/main.go`
- Triggers: `go run cmd/worker/main.go`、Worker 容器启动。
- Responsibilities: 初始化 Worker 依赖、执行 `bootstrap.BootstrapWorker()`、启动 Asynq Worker、启动轮询调度器和 Asynq Scheduler、优雅关闭。
- Location: `cmd/gendocs/main.go`
- Triggers: 手动执行文档生成。
- Responsibilities: 通过 `openapi.BuildDocHandlers()` + `routes.RegisterRoutesWithDoc()` 输出 `docs/admin-openapi.yaml`。
## Routing Strategy
- `/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`
- 所有 HTTP 接口使用 `internal/routes/registry.go` 的 `Register()`，文档规范见 `docs/api-documentation-guide.md`。
- `internal/routes/admin.go` 以 handler 是否为 `nil` 决定是否挂载模块，便于文档生成和裁剪。
- `internal/routes/personal.go` 明确区分公开路由与认证路由，并依赖注册顺序确保公开接口不被 `Use()` 拦截。
## Error Handling
- 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
- `pkg/logger/logger.go` 初始化 App/Access 日志；`pkg/logger/middleware.go` 记录完整请求/响应摘要。
- Fiber `BodyParser` / `QueryParser` + `validator.v10`，示例见 `internal/handler/auth/handler.go`。
- 后台/H5 使用 Redis Token + `pkg/middleware/auth.go`；个人客户使用单独中间件，由 `internal/bootstrap/middlewares.go` 创建。
- 路由元数据写在 `RouteSpec`；文档生成器依赖 `cmd/api/docs.go`、`cmd/gendocs/main.go`、`pkg/openapi/handlers.go` 三处手工维护 Handler 清单。
- 模型显式声明 `TableName()` 与字段列名，如 `internal/model/account.go`, `internal/model/order.go`, `internal/model/shop.go`。
- 数据库结构通过 `migrations/*.sql` 管理，`pkg/database/postgres.go` 明确禁用自动建表。
## Architectural Deviations
- `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 流程中的特殊机制与待收敛点。
<!-- GSD:architecture-end -->

<!-- GSD:workflow-start source:GSD defaults -->
## GSD Workflow Enforcement

Before using Edit, Write, or other file-changing tools, start work through a GSD command so planning artifacts and execution context stay in sync.

Use these entry points:
- `/gsd:quick` for small fixes, doc updates, and ad-hoc tasks
- `/gsd:debug` for investigation and bug fixing
- `/gsd:execute-phase` for planned phase work

Do not make direct repo edits outside a GSD workflow unless the user explicitly asks to bypass it.
<!-- GSD:workflow-end -->

<!-- GSD:profile-start -->
## Developer Profile

> Profile not yet configured. Run `/gsd:profile-user` to generate your developer profile.
> This section is managed by `generate-claude-profile` -- do not edit manually.
<!-- GSD:profile-end -->
