# 君鸿卡管系统 - Fiber 中间件集成

基于 Go + Fiber 框架的 HTTP 服务，集成了认证、限流、结构化日志和嵌入式配置功能。

## 系统简介

物联网卡 + 号卡全生命周期管理平台，支持代理商体系和分佣结算。

**技术栈**：Fiber + GORM + Viper + Zap + Lumberjack.v2 + Validator + sonic JSON + Asynq + PostgreSQL

---

## 核心业务说明

### 业务模式概览

君鸿卡管系统是一个物联网卡和号卡的全生命周期管理平台，支持三种客户类型和两种组织实体的多租户管理。

### 三种客户类型

| 客户类型 | 业务特点 | 典型场景 | 钱包归属 |
|---------|---------|---------|---------|
| **企业客户** | B端大客户，公对公支付 | 企业购买大量卡/设备用于业务运营 | ❌ 无钱包（后台直接分配套餐） |
| **个人客户** | C端用户，微信登录 | 个人购买单卡或设备（含1-4张卡） | ✅ 钱包归属**卡/设备**（支持转手） |
| **代理商** | 渠道分销商，层级管理 | 预存款采购套餐，按成本价+加价销售 | ✅ 钱包归属**店铺**（多账号共享） |

### 个人客户业务流程

```
┌─────────────────────────────────────────────────────────────────┐
│                    个人客户使用流程                              │
└──────────────────────────┬──────────────────────────────────────┘
                           │
                ┌──────────▼──────────┐
                │ 1. 获得卡/设备      │
                │ - 单卡：ICCID       │
                │ - 设备：设备号/IMEI │
                └──────────┬──────────┘
                           │
                ┌──────────▼──────────┐
                │ 2. 微信扫码登录     │
                │ - 输入ICCID/IMEI    │
                │ - 首次需绑定手机号  │
                └──────────┬──────────┘
                           │
                ┌──────────▼──────────┐
                │ 3. 查看卡/设备信息  │
                │ - 流量使用情况      │
                │ - 套餐有效期        │
                │ - 钱包余额          │
                └──────────┬──────────┘
                           │
                ┌──────────▼──────────┐
                │ 4. 钱包充值         │
                │ - 微信支付          │
                │ - 支付宝支付        │
                └──────────┬──────────┘
                           │
                ┌──────────▼──────────┐
                │ 5. 购买套餐         │
                │ - 单卡套餐          │
                │ - 设备套餐（共享）  │
                └──────────┬──────────┘
                           │
                ┌──────────▼──────────┐
                │ 6. 卡/设备转手      │
                │ - 新用户扫码登录    │
                │ - 钱包余额跟着走    │
                └─────────────────────┘
```

### 钱包归属设计

#### 为什么钱包绑定资源（卡/设备）而非用户？

**问题场景**：
```
个人客户 A 购买单卡 → 充值 100 元 → 使用 50 元 → 转手给个人客户 B
```

**如果钱包绑定用户**：
- ❌ 个人客户 B 登录后看不到余额（钱包还在 A 账号下）
- ❌ 需要手动转账或退款，体验极差

**钱包绑定资源（当前设计）**：
- ✅ 个人客户 B 登录后看到剩余 50 元（钱包跟着卡走）
- ✅ 无需任何额外操作，自然流转

#### 钱包归属规则

```go
// 钱包模型
type Wallet struct {
    ResourceType  string  // iot_card | device | shop
    ResourceID    uint    // 资源ID
    Balance       int64   // 余额（分）
    // ...
}

// 场景1：个人客户的单卡钱包
resource_type = "iot_card"
resource_id = 101  // 卡ID

// 场景2：个人客户的设备钱包（3张卡共享）
resource_type = "device"
resource_id = 1001  // 设备ID

// 场景3：代理商店铺钱包（多账号共享）
resource_type = "shop"
resource_id = 10  // 店铺ID
```

### 设备套餐业务规则

#### 设备级套餐购买

```
设备绑定 3 张 IoT 卡
├── 卡1：ICCID-001
├── 卡2：ICCID-002
└── 卡3：ICCID-003

用户购买套餐：399 元/年，每月 3000G 流量
├── 套餐分配：3 张卡都获得该套餐
├── 流量共享：3000G/月 在 3 张卡之间共享（总共 3000G）
├── 用户支付：399 元（一次性）
└── 代理分佣：100 元（只分一次，不按卡数倍增）
```

**关键点**：
- ✅ 套餐自动分配到设备的所有卡
- ✅ 流量是**设备级别共享**（非每卡独立）
- ✅ 分佣**只计算一次**（防止重复分佣）

### 标签系统多租户隔离

#### 三级隔离模型

| 标签类型 | 创建者 | 可见范围 | 名称唯一性 | 示例 |
|---------|-------|---------|-----------|------|
| 平台全局标签 | 平台管理员 | 所有用户 | 全局唯一 | "VIP"、"重要客户" |
| 企业标签 | 企业用户 | 仅该企业 | 企业内唯一 | 企业A的"测试标签" |
| 店铺标签 | 代理商 | 该店铺及下级 | 店铺内唯一 | 店铺10的"华东区" |

#### 隔离规则

```
企业 A 创建标签 "测试标签"
├── enterprise_id = 5, shop_id = NULL
├── 企业 A 的用户可见
└── 企业 B 的用户不可见

企业 B 创建标签 "测试标签"（允许）
├── enterprise_id = 8, shop_id = NULL
├── 企业 B 的用户可见
└── 与企业 A 的 "测试标签" 相互隔离

平台创建标签 "VIP"
├── enterprise_id = NULL, shop_id = NULL
└── 所有用户可见
```

#### 数据权限自动过滤

```go
// GORM Callback 自动注入过滤条件
switch userType {
case UserTypeAgent:
    // 代理用户：只看到自己店铺及下级店铺的标签
    db.Where("shop_id IN (?) OR (enterprise_id IS NULL AND shop_id IS NULL)", subordinateShopIDs)
    
case UserTypeEnterprise:
    // 企业用户：只看到自己企业的标签
    db.Where("enterprise_id = ? OR (enterprise_id IS NULL AND shop_id IS NULL)", enterpriseID)
    
default:
    // 个人客户：只看到全局标签
    db.Where("enterprise_id IS NULL AND shop_id IS NULL")
}
```

---

## 核心功能

- **认证中间件**：基于 Redis 的 Token 认证
- **限流中间件**：基于 IP 的限流，支持可配置的限制和存储后端
- **结构化日志**：使用 Zap 的 JSON 日志和自动日志轮转
- **嵌入式配置**：配置嵌入二进制文件，通过环境变量覆盖，简化 Docker 部署
- **请求 ID 追踪**：UUID 跨日志的请求追踪
- **Panic 恢复**：优雅的 panic 处理和堆栈跟踪日志
- **统一错误处理**：全局 ErrorHandler 统一处理所有 API 错误，返回一致的 JSON 格式（包含错误码、消息、时间戳）；Panic 自动恢复防止服务崩溃；错误分类处理（客户端 4xx、服务端 5xx）和日志级别控制；敏感信息自动脱敏保护
- **数据持久化**：GORM + PostgreSQL 集成，提供完整的 CRUD 操作、事务支持和数据库迁移能力
- **异步任务处理**：Asynq 任务队列集成，支持任务提交、后台执行、自动重试和幂等性保障，实现邮件发送、数据同步等异步任务
- **RBAC 权限系统**：完整的基于角色的访问控制，支持账号、角色、权限的多对多关联和层级关系；基于店铺层级的自动数据权限过滤，实现多租户数据隔离；使用 PostgreSQL WITH RECURSIVE 查询下级店铺并通过 Redis 缓存优化性能；完整的权限检查功能支持路由级别的细粒度权限控制，支持平台过滤（web/h5/all）和超级管理员自动跳过（详见 [功能总结](docs/004-rbac-data-permission/功能总结.md)、[使用指南](docs/004-rbac-data-permission/使用指南.md) 和 [权限检查使用指南](docs/permission-check-usage.md)）
- **商户管理**：完整的商户（Shop）和商户账号管理功能，支持商户创建时自动创建初始坐席账号、删除商户时批量禁用关联账号、账号密码重置等功能（详见 [使用指南](docs/shop-management/使用指南.md) 和 [API 文档](docs/shop-management/API文档.md)）
- **B 端认证系统**：完整的后台和 H5 认证功能，支持基于 Redis 的 Token 管理和双令牌机制（Access Token 24h + Refresh Token 7天）；包含登录、登出、Token 刷新、用户信息查询和密码修改功能；通过用户类型隔离确保后台（SuperAdmin、Platform、Agent）和 H5（Agent、Enterprise）的访问控制；**登录响应包含菜单树和按钮权限**（menus/buttons），前端无需二次处理直接渲染侧边栏和控制按钮显示；详见 [API 文档](docs/api/auth.md)、[使用指南](docs/auth-usage-guide.md)、[架构说明](docs/auth-architecture.md) 和 [菜单权限使用指南](docs/login-menu-button-response/使用指南.md)
- **B 端认证系统**：完整的后台和 H5 认证功能，支持基于 Redis 的 Token 管理和双令牌机制（Access Token 24h + Refresh Token 7天）；包含登录、登出、Token 刷新、用户信息查询和密码修改功能；通过用户类型隔离确保后台（SuperAdmin、Platform、Agent）和 H5（Agent、Enterprise）的访问控制；详见 [API 文档](docs/api/auth.md)、[使用指南](docs/auth-usage-guide.md) 和 [架构说明](docs/auth-architecture.md)
- **生命周期管理**：物联网卡/号卡的开卡、激活、停机、复机、销户
- **代理商体系**：层级管理和分佣结算
- **批量同步**：卡状态、实名状态、流量使用情况
- **分佣验证指引**：对代理分佣的冻结、解冻、提现校验流程进行了结构化说明与流程图，详见 [分佣逻辑正确与否验证](docs/优化说明/分佣逻辑正确与否验证.md)
- **对象存储**：S3 兼容的对象存储服务集成（联通云 OSS），支持预签名 URL 上传、文件下载、临时文件处理；用于 ICCID 批量导入、数据导出等场景；详见 [使用指南](docs/object-storage/使用指南.md) 和 [前端接入指南](docs/object-storage/前端接入指南.md)
- **微信集成**：完整的微信公众号 OAuth 认证和微信支付功能（JSAPI + H5），使用 PowerWeChat v3 SDK；支持个人客户微信授权登录、账号绑定、微信内支付和浏览器 H5 支付；支付回调自动验证签名和幂等性处理；详见 [使用指南](docs/wechat-integration/使用指南.md) 和 [API 文档](docs/wechat-integration/API文档.md)

## 用户体系设计

系统支持四种用户类型和两种组织实体,实现分层级的多租户管理：

### 用户类型

1. **平台用户**：平台管理员,具有最高权限,可分配多个角色
2. **代理账号**：店铺（代理商）员工账号,归属于特定店铺,权限相同
3. **企业账号**：企业客户账号,归属于特定企业,一企业一账号
4. **个人客户**：个人用户,独立表存储,支持微信绑定,不参与 RBAC 体系

### 组织实体

1. **店铺（Shop）**：代理商组织实体,支持最多 7 级层级关系
   - 一级代理直接归属于平台
   - 下级代理归属于上级店铺（通过 `parent_id` 字段）
   - 一个店铺可以有多个账号（代理员工）

2. **企业（Enterprise）**：企业客户组织实体
   - 可归属于店铺（通过 `owner_shop_id` 字段）
   - 可归属于平台（`owner_shop_id = NULL`）
   - 一个企业目前只有一个账号

### 核心设计决策

- **层级关系在店铺之间维护**：代理上下级关系通过 `Shop.parent_id` 维护,而非账号之间
- **数据权限基于店铺归属**：数据过滤使用 `shop_id IN (当前店铺及下级店铺)`
- **递归查询+Redis缓存**：使用 `GetSubordinateShopIDs()` 递归查询下级店铺ID,结果缓存30分钟
- **GORM 自动过滤**：通过 GORM Callback 自动应用数据权限过滤,无需在每个查询手动添加条件
- **禁止外键约束**：遵循项目原则,表之间通过ID字段关联,关联查询在代码层显式执行
- **GORM字段显式命名**：所有模型字段必须显式指定 `gorm:"column:field_name"` 标签

### 表结构

```
tb_shop (店铺表)
├── id, created_at, updated_at, deleted_at
├── creator, updater
├── shop_name, shop_code
├── parent_id (上级店铺ID)
├── level (层级 1-7)
├── contact_name, contact_phone
├── province, city, district, address
└── status

tb_enterprise (企业表)
├── id, created_at, updated_at, deleted_at
├── creator, updater
├── enterprise_name, enterprise_code
├── owner_shop_id (归属店铺ID)
├── legal_person, contact_name, contact_phone
├── business_license
├── province, city, district, address
└── status

tb_personal_customer (个人客户表)
├── id, created_at, updated_at, deleted_at
├── phone (唯一标识)
├── nickname, avatar_url
├── wx_open_id, wx_union_id
└── status

tb_account (账号表 - 已修改)
├── id, created_at, updated_at, deleted_at
├── creator, updater
├── username, phone, password
├── user_type (1=超级管理员 2=平台用户 3=代理账号 4=企业账号)
├── shop_id (代理账号必填)
├── enterprise_id (企业账号必填) ← 新增
└── status
```

详细设计文档参见：
- [设计文档](openspec/changes/add-user-organization-model/design.md)
- [提案文档](openspec/changes/add-user-organization-model/proposal.md)

## 数据权限模型

系统采用基于用户类型的自动数据权限过滤策略，通过 GORM Callback 自动应用，无需在每个查询中手动添加过滤条件。

### 过滤规则

| 用户类型 | 过滤策略 | 示例 |
|---------|---------|------|
| 超级管理员（Super Admin） | 跳过过滤，查看所有数据 | - |
| 平台用户（Platform） | 跳过过滤，查看所有数据 | - |
| 代理账号（Agent） | 基于店铺层级过滤 | `WHERE shop_id IN (当前店铺及下级店铺)` |
| 企业账号（Enterprise） | 基于企业归属过滤 | `WHERE enterprise_id = 当前企业ID` |
| 个人客户（Personal Customer） | 基于创建者过滤 | `WHERE creator = 当前用户ID` |

### 工作机制

1. **认证中间件**设置完整用户上下文（`UserContextInfo`）到 `context` 中
2. **GORM Callback**在每次查询前自动注入过滤条件
3. **递归查询 + 缓存**：代理用户的下级店铺 ID 通过 `GetSubordinateShopIDs()` 递归查询，结果缓存 30 分钟
4. **跳过过滤**：特殊场景（如统计、后台任务）可使用 `SkipDataPermission(ctx)` 绕过过滤

### 使用示例

```go
// 1. 认证后 context 已自动包含用户信息
ctx := c.UserContext()

// 2. 所有 Store 层查询自动应用数据权限过滤
orders, err := orderStore.List(ctx)  // 自动过滤为当前用户可见的订单

// 3. 需要查询所有数据时，显式跳过过滤
ctx = gorm.SkipDataPermission(ctx)
allOrders, err := orderStore.List(ctx)  // 查询所有订单（仅限特殊场景）
```

详细说明参见：
- [数据权限清理总结](docs/remove-legacy-rbac-cleanup/清理总结.md)
- [RBAC 权限使用指南](docs/004-rbac-data-permission/使用指南.md)

## 快速开始

```bash
# 安装依赖
go mod tidy

# 启动 Redis（认证功能必需）
redis-server

# 运行 API 服务
go run cmd/api/main.go

# 运行 Worker 服务（可选）
go run cmd/worker/main.go
```

### 默认超级管理员账号

系统首次启动时会自动创建默认超级管理员账号，无需手动执行 SQL 或脚本。

**默认账号信息**：
- 用户名：`admin`
- 密码：`Admin@123456`
- 手机号：`13800000000`

**自定义配置**：

通过环境变量自定义默认管理员信息：

```bash
export JUNHONG_DEFAULT_ADMIN_USERNAME="自定义用户名"
export JUNHONG_DEFAULT_ADMIN_PASSWORD="自定义密码"
export JUNHONG_DEFAULT_ADMIN_PHONE="自定义手机号"
```

**注意事项**：
- 系统只在数据库无超级管理员账号时才创建
- 如果已存在超级管理员，启动时会跳过创建
- 建议首次登录后立即修改默认密码
- 初始化日志记录在 `logs/app.log` 中

详细设置和测试说明请参阅 [快速开始指南](specs/001-fiber-middleware-integration/quickstart.md)。

## 项目结构

```
junhong_cmp_fiber/
│
├── cmd/                          # 应用程序入口
│   ├── api/                      # HTTP API 服务
│   │   └── main.go               # API 服务主入口
│   └── worker/                   # Asynq 异步任务 Worker
│       └── main.go               # Worker 服务主入口
│
├── internal/                     # 私有业务代码
│   ├── handler/                  # HTTP 处理层
│   │   ├── user.go               # 用户处理器
│   │   └── health.go             # 健康检查处理器
│   ├── middleware/               # Fiber 中间件实现
│   │   ├── auth.go               # 认证中间件（keyauth）
│   │   ├── ratelimit.go          # 限流中间件
│   │   └── recover.go            # Panic 恢复中间件
│   ├── service/                  # 业务逻辑层（核心业务）
│   ├── store/                    # 数据访问层
│   │   └── postgres/             # PostgreSQL 实现
│   ├── model/                    # 数据模型（实体、DTO）
│   ├── task/                     # Asynq 任务定义和处理
│   ├── gateway/                  # Gateway 服务 HTTP 客户端
│   └── router/                   # 路由注册
│
├── pkg/                          # 公共工具库
│   ├── config/                   # 配置管理
│   │   ├── config.go             # 配置结构定义
│   │   ├── loader.go             # 配置加载（嵌入配置 + 环境变量覆盖）
│   │   ├── embedded.go           # go:embed 嵌入配置加载
│   │   └── defaults/config.yaml  # 默认配置（嵌入二进制）
│   ├── logger/                   # 日志基础设施
│   │   ├── logger.go             # Zap 日志初始化
│   │   └── middleware.go         # Fiber 日志中间件适配器
│   ├── response/                 # 统一响应处理
│   │   └── response.go           # 响应结构和辅助函数
│   ├── errors/                   # 错误码和类型
│   │   ├── codes.go              # 错误码常量
│   │   └── errors.go             # 自定义错误类型
│   ├── constants/                # 业务常量
│   │   ├── constants.go          # 上下文键、请求头名称
│   │   └── redis.go              # Redis Key 生成器
│   ├── validator/                # 验证服务
│   │   └── token.go              # Token 验证（Redis）
│   ├── database/                 # 数据库初始化
│   │   └── redis.go              # Redis 客户端初始化
│   └── queue/                    # 队列封装（Asynq）
│
├── tests/
│   └── integration/              # 集成测试
│       ├── auth_test.go          # 认证测试
│       └── ratelimit_test.go     # 限流测试
│
├── migrations/                   # 数据库迁移文件
├── scripts/                      # 脚本工具
├── docs/                         # 文档
│   └── rate-limiting.md          # 限流指南
└── logs/                         # 应用日志（自动创建）
    ├── app.log                   # 应用日志（JSON）
    └── access.log                # 访问日志（JSON）
```

## 中间件执行顺序

中间件按注册顺序执行。请求按顺序流经每个中间件：

```
┌─────────────────────────────────────────────────────────────────┐
│                         HTTP 请求                                │
└────────────────────────────────┬────────────────────────────────┘
                                 │
                    ┌────────────▼────────────┐
                    │  1. Recover 中间件      │
                    │  (panic 恢复)           │
                    └────────────┬────────────┘
                                 │
                    ┌────────────▼────────────┐
                    │  2. RequestID 中间件    │
                    │  (生成 UUID)            │
                    └────────────┬────────────┘
                                 │
                    ┌────────────▼────────────┐
                    │  3. Logger 中间件       │
                    │  (访问日志)             │
                    └────────────┬────────────┘
                                 │
                ┌────────────▼────────────┐
                │  4. 认证中间件          │
                │  (按路由组配置)         │ ─── 模块化路由注册
                └────────────┬────────────┘
                             │
                ┌────────────▼────────────┐
                │  5. RateLimiter 中间件  │
                    │  (限流)                 │ ─── 可选 (config: enable_rate_limiter)
                    └────────────┬────────────┘
                                 │
                    ┌────────────▼────────────┐
                    │  6. 路由处理器          │
                    │  (业务逻辑)             │
                    └────────────┬────────────┘
                                 │
┌────────────────────────────────▼────────────────────────────────┐
│                         HTTP 响应                                │
└─────────────────────────────────────────────────────────────────┘
```

### 中间件详情

#### 1. Recover 中间件（fiber.Recover）
- **用途**：捕获 panic 并防止服务器崩溃
- **行为**：
  - 捕获下游中间件/处理器中的任何 panic
  - 将 panic 及堆栈跟踪记录到 `logs/app.log`
  - 返回 HTTP 500 统一错误响应
  - 服务器继续处理后续请求
- **始终激活**：是

#### 2. RequestID 中间件（自定义）
- **用途**：生成请求追踪的唯一标识符
- **行为**：
  - 为每个请求生成 UUID v4
  - 存储在上下文中：`c.Locals(constants.ContextKeyRequestID)`
  - 添加 `X-Request-ID` 响应头
  - 用于所有日志条目以进行关联
- **始终激活**：是

#### 3. Logger 中间件（自定义 Fiber 适配器）
- **用途**：记录所有 HTTP 请求和响应
- **行为**：
  - 记录请求：方法、路径、IP、User-Agent、请求 ID
  - 记录响应：状态码、耗时、用户 ID（如果已认证）
  - 写入 `logs/access.log`（JSON 格式）
  - 结构化字段便于解析和分析
- **始终激活**：是
- **日志格式**：包含字段的 JSON：timestamp、level、method、path、status、duration_ms、request_id、ip、user_agent、user_id

#### 4. 认证中间件（pkg/middleware/auth.go 和 internal/middleware/）
- **用途**：使用 Token 验证对请求进行认证
- **行为**：
  - 从 `Authorization: Bearer {token}` 请求头提取 token
  - 通过 TokenValidator 函数验证 token（支持 JWT 和 Redis Token）
  - 如果缺失/无效 token 返回 401
  - 成功时将用户信息存储在上下文中（UserID、UserType、ShopID、EnterpriseID）
- **实现方式**：模块化路由注册（无全局配置）
  - `/api/admin/*`：后台认证（SuperAdmin、Platform、Agent）
  - `/api/h5/*`：H5 认证（Agent、Enterprise）
  - `/api/personal/*`：个人客户认证（JWT）
- **跳过路由**：各路由组可自行配置跳过路径（如 `/api/admin/login`）
- **错误码**：
  - 1001：缺失 token
  - 1002：无效或过期 token
  - 1003：权限不足

#### 5. RateLimiter 中间件（internal/middleware/ratelimit.go）
- **用途**：通过限制请求速率保护 API 免受滥用
- **行为**：
  - 按客户端 IP 地址追踪请求
  - 执行限制：`expiration` 时间窗口内 `max` 个请求
  - 如果超过限制返回 429
  - 每个 IP 地址独立计数器
- **配置**：`middleware.enable_rate_limiter`（默认：false）
- **存储选项**：
  - `memory`：内存存储（单服务器，重启后重置）
  - `redis`：基于 Redis（分布式，持久化）
- **错误码**：1003（请求过于频繁）

#### 6. 路由处理器
- **用途**：执行端点的业务逻辑
- **可用上下文数据**：
  - 请求 ID：`c.Locals(constants.ContextKeyRequestID)`
  - 用户 ID：`c.Locals(constants.ContextKeyUserID)`（如果已认证）
  - 标准 Fiber 上下文方法：`c.Params()`、`c.Query()`、`c.Body()` 等

### 中间件注册（cmd/api/main.go）

```go
// 核心中间件（始终激活）
app.Use(recover.New())
app.Use(addRequestID())
app.Use(loggerMiddleware())

// 模块化路由注册（认证中间件按路由组配置）
routes.RegisterRoutes(app, handlers, middlewares)

// 可选：限流中间件
if config.GetConfig().Middleware.EnableRateLimiter {
    var storage fiber.Storage = nil
    if config.GetConfig().Middleware.RateLimiter.Storage == "redis" {
        storage = redisStorage // 使用 Redis 存储
    }
    v1 := app.Group("/api/v1")
    v1.Use(middleware.RateLimiter(
        config.GetConfig().Middleware.RateLimiter.Max,
        config.GetConfig().Middleware.RateLimiter.Expiration,
        storage,
    ))
}
```

### 请求流程示例

**场景**：已启用所有中间件的 `/api/v1/users` 认证请求

```
1. 请求到达：GET /api/v1/users
   请求头：token: abc123

2. Recover 中间件：准备捕获 panic
   → 传递到下一个中间件

3. RequestID 中间件：生成 UUID
   → 设置上下文：request_id = "550e8400-e29b-41d4-a716-446655440000"
   → 传递到下一个中间件

4. Logger 中间件：记录请求开始
   → 日志：{"method":"GET", "path":"/api/v1/users", "request_id":"550e8400-..."}
   → 传递到下一个中间件

5. KeyAuth 中间件：验证 token
   → 检查 Redis：GET "auth:token:abc123" → "user-789"
   → 设置上下文：user_id = "user-789"
   → 传递到下一个中间件

6. RateLimiter 中间件：检查限流
   → 检查计数器：GET "rate_limit:127.0.0.1" → "5"（低于限制 100）
   → 增加计数器：INCR "rate_limit:127.0.0.1" → "6"
   → 传递到下一个中间件

7. 处理器执行：listUsersHandler()
   → 从上下文获取 user_id："user-789"
   → 从数据库获取用户
   → 返回响应：{"code":0, "data":[...], "msg":"success"}

8. Logger 中间件：记录响应
   → 日志：{"status":200, "duration_ms":23.45, "user_id":"user-789"}

9. RequestID 中间件：添加响应头
   → 响应头：X-Request-ID: 550e8400-e29b-41d4-a716-446655440000

10. 响应发送给客户端
```

### 中间件中的错误处理

如果任何中间件返回错误，链停止并发送错误响应：

```
请求 → Recover → RequestID → Logger → [KeyAuth 失败] ✗
                                              ↓
                                         返回 401
                                         （不执行 RateLimiter 和 Handler）
```

示例：缺失 token
```
KeyAuth：Token 缺失
→ 返回 response.Error(c, 401, 1001, "缺失认证令牌")
→ Logger 记录：{"status":401, "duration_ms":1.23}
→ RequestID 添加响应头
→ 发送响应
```

## 配置

### 嵌入式配置机制

系统使用 go:embed 将默认配置嵌入二进制文件，通过环境变量进行覆盖：

- **默认配置**：`pkg/config/defaults/config.yaml`（编译时嵌入）
- **环境变量前缀**：`JUNHONG_`
- **格式转换**：配置路径中的 `.` 替换为 `_`

**环境变量覆盖示例**：

| 配置项 | 环境变量 |
|-------|---------|
| `database.host` | `JUNHONG_DATABASE_HOST` |
| `redis.address` | `JUNHONG_REDIS_ADDRESS` |
| `jwt.secret_key` | `JUNHONG_JWT_SECRET_KEY` |
| `logging.level` | `JUNHONG_LOGGING_LEVEL` |

### 必填配置

以下配置项必须通过环境变量设置（无默认值或需要覆盖）：

```bash
# 数据库配置（必填）
export JUNHONG_DATABASE_HOST=localhost
export JUNHONG_DATABASE_PORT=5432
export JUNHONG_DATABASE_USER=postgres
export JUNHONG_DATABASE_PASSWORD=your_password
export JUNHONG_DATABASE_DBNAME=junhong_cmp

# Redis 配置（必填）
export JUNHONG_REDIS_ADDRESS=localhost

# JWT 密钥（必填，生产环境必须修改）
export JUNHONG_JWT_SECRET_KEY=your-secret-key-change-in-production
```

### Docker 部署

Docker 部署使用纯环境变量配置，无需挂载配置文件：

```yaml
# docker-compose.prod.yml 示例
services:
  api:
    image: registry.boss160.cn/junhong/cmp-fiber-api:latest
    environment:
      - JUNHONG_DATABASE_HOST=db-host
      - JUNHONG_DATABASE_PORT=5432
      - JUNHONG_DATABASE_USER=postgres
      - JUNHONG_DATABASE_PASSWORD=secret
      - JUNHONG_DATABASE_DBNAME=junhong_cmp
      - JUNHONG_REDIS_ADDRESS=redis
      - JUNHONG_JWT_SECRET_KEY=production-secret
    volumes:
      - ./logs:/app/logs  # 仅挂载日志目录
```

### 完整环境变量列表

详见 [环境变量配置文档](docs/environment-variables.md)

## 测试

### 运行所有测试

```bash
# 运行所有单元和集成测试
go test ./...

# 带覆盖率运行
go test -cover ./...

# 详细输出运行
go test -v ./...
```

### 运行特定测试套件

```bash
# 仅单元测试
go test ./pkg/...

# 仅集成测试
go test ./tests/integration/...

# 特定测试
go test -v ./internal/middleware -run TestKeyAuth
```

### 集成测试

集成测试需要 Redis 运行：

```bash
# 启动 Redis
redis-server

# 运行集成测试
go test -v ./tests/integration/...
```

如果 Redis 不可用，测试自动跳过。

### 测试连接管理

测试使用全局单例连接池，性能提升 6-7 倍。详见 [测试连接管理规范](docs/testing/test-connection-guide.md)。

**标准写法**:
```go
func TestXxx(t *testing.T) {
    tx := testutils.NewTestTransaction(t)  // 自动回滚的事务
    rdb := testutils.GetTestRedis(t)       // 全局 Redis 连接
    testutils.CleanTestRedisKeys(t, rdb)   // 自动清理 Redis 键
    
    store := postgres.NewXxxStore(tx, rdb)
    // 测试代码...
}
```

## 架构设计

### 分层架构
```
Handler (HTTP) → Service (业务逻辑) → Store (数据访问) → Model (数据模型)
```

### 双服务架构
- **API 服务**：处理 HTTP 请求，快速响应
- **Worker 服务**：处理异步任务（批量同步、分佣计算等），独立部署

### 核心模块
- **Service 层**：统一管理所有业务逻辑，支持跨模块调用
- **Store 层**：统一管理所有数据访问，支持事务
- **Task 层**：Asynq 任务处理器，支持定时任务和事件触发

## 框架优化历史

### 005-framework-cleanup-refactor（2025-11）

**背景**：清理技术债务，统一框架设计

**主要变更**：
1. **清理示例代码**：删除所有 user/order 示例业务代码，保持代码库整洁
2. **统一认证中间件**：合并两套 Auth 实现到 `pkg/middleware/auth.go`，统一错误处理格式
3. **简化错误结构**：删除 AppError 的 HTTPStatus 字段，避免字段冗余
4. **组件注册解耦**：创建 `internal/bootstrap/` 包实现自动化组件初始化
   - 按模块拆分：`stores.go`、`services.go`、`handlers.go`
   - main.go 简化为一行：`handlers, err := bootstrap.Bootstrap(deps)`
5. **数据权限自动化**：实现 GORM Callback 自动注入数据权限过滤
   - 基于 creator 字段自动过滤（普通用户只能看到自己和下级的数据）
   - root 用户自动跳过过滤
   - 支持通过 `gorm.SkipDataPermission(ctx)` 手动绕过
   - 删除未使用的 `scopes.go` 手动 Scope 函数

**设计原则**：
- 保持 Go 惯用模式，避免 Java 风格过度抽象
- 使用显式依赖注入，不引入复杂的 DI 框架
- 每个文件保持 < 100 行，职责单一
- 在关键扩展点添加 TODO 标记

**详细文档**：
- [变更提案](openspec/changes/refactor-framework-cleanup/proposal.md)
- [设计文档](openspec/changes/refactor-framework-cleanup/design.md)
- [任务清单](openspec/changes/refactor-framework-cleanup/tasks.md)

## 开发规范

### 依赖注入
通过 `Service` 和 `Store` 结构体统一管理依赖：
```go
// 初始化
st := store.New(db)
svc := service.New(st, queueClient, logger)

// 使用
svc.SIM.Activate(...)
svc.Commission.Calculate(...)
```

### 事务处理
```go
store.Transaction(ctx, func(tx *store.Store) error {
    tx.SIM.UpdateStatus(...)
    tx.Commission.Create(...)
    return nil
})
```

### 异步任务
- 高频任务：批量状态同步、流量同步、实名检查
- 业务任务：分佣计算、生命周期变更通知
- 任务优先级：critical > default > low

### 常量和 Redis Key 管理
所有常量统一在 `pkg/constants/` 目录管理：
```go
// 业务常量
constants.SIMStatusActive
constants.SIMStatusInactive

// Redis Key 管理（统一使用 Key 生成函数）
constants.RedisSIMStatusKey(iccid)              // sim:status:{iccid}
constants.RedisAgentCommissionKey(agentID)      // agent:commission:{agentID}
constants.RedisTaskLockKey(taskName)            // task:lock:{taskName}
constants.RedisAuthTokenKey(token)              // auth:token:{token}

// 使用示例
key := constants.RedisSIMStatusKey("898600...")
rdb.Set(ctx, key, status, time.Hour)
```

## 文档

### 开发规范

- **[API 文档生成规范](docs/api-documentation-guide.md)**：路由注册规范、DTO 规范、OpenAPI 文档生成流程
- **[数据库验证规范](AGENTS.md#数据库验证规范)**：使用 PostgreSQL MCP 验证接口逻辑和业务数据的正确性
- **[开发规范总览](AGENTS.md)**：完整的项目开发规范（必读）

### 功能指南

- **[快速开始指南](specs/001-fiber-middleware-integration/quickstart.md)**：详细设置和测试说明
- **[限流指南](docs/rate-limiting.md)**：全面的限流配置和使用
- **[错误处理使用指南](docs/003-error-handling/使用指南.md)**：错误码参考、Handler 使用、客户端处理、最佳实践
- **[错误处理架构说明](docs/003-error-handling/架构说明.md)**：架构设计、性能优化、扩展性说明

### 架构设计

- **[实现计划](specs/001-fiber-middleware-integration/plan.md)**：设计决策和架构
- **[数据模型](specs/001-fiber-middleware-integration/data-model.md)**：配置结构和 Redis 架构

## 技术栈

- **Go**：1.25.1
- **Fiber**：v2.52.9（HTTP 框架）
- **Zap**：v1.27.0（结构化日志）
- **Lumberjack**：v2.2.1（日志轮转）
- **Viper**：v1.19.0（配置管理）
- **go-redis**：v9.7.0（Redis 客户端）
- **fsnotify**：v1.8.0（文件系统通知）
- **GORM**：（数据库 ORM）
- **sonic**：（高性能 JSON）
- **Asynq**：（异步任务队列）
- **Validator**：（参数验证）
- **PowerWeChat**：v3.4.38（微信SDK - 公众号 & 支付）

## 开发流程（Speckit）

本项目使用 Speckit 规范化功能开发流程，确保代码质量、测试覆盖和架构一致性。

### 项目宪章

项目遵循 `.specify/memory/constitution.md` 定义的核心原则：

1. **技术栈遵守**：严格使用 Fiber + GORM + Viper + Zap + Asynq，禁止原生调用快捷方式
2. **代码质量标准**：遵循 Handler → Service → Store → Model 分层架构
3. **测试标准**：70%+ 测试覆盖率，核心业务 90%+
4. **用户体验一致性**：统一 JSON 响应格式、RESTful API、双语错误消息
5. **性能要求**：API P95 < 200ms，P99 < 500ms，合理使用批量操作和异步任务

详细原则和规则请参阅宪章文档。

### Speckit 命令

```bash
# 创建功能规范
/speckit.specify "功能描述"

# 明确规范细节
/speckit.clarify

# 生成实现计划
/speckit.plan

# 生成任务列表
/speckit.tasks

# 执行实现
/speckit.implement

# 一致性分析
/speckit.analyze

# 生成自定义检查清单
/speckit.checklist "检查项要求"

# 更新项目宪章
/speckit.constitution "宪章更新说明"
```

## 代码规范检查

运行代码规范检查：

```bash
# 检查 Service 层错误处理
bash scripts/check-service-errors.sh

# 检查注释路径一致性
bash scripts/check-comment-paths.sh

# 运行所有检查
bash scripts/check-all.sh
```

这些检查会在 CI/CD 流程中自动执行。

## 设计原则

- **简单实用**：不过度设计，够用就好
- **直接实现**：避免不必要的接口抽象
- **统一管理**：依赖集中初始化，避免参数传递
- **职责分离**：API 和 Worker 独立部署，便于扩展

## 许可证

MIT License
