huang 39d14ec093 Merge branch '004-rbac-data-permission': RBAC 权限系统和数据权限控制
完成 RBAC 权限系统和数据权限控制功能的开发和测试。
详见分支 004-rbac-data-permission 的提交记录。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 16:44:41 +08:00
2025-11-10 14:55:46 +08:00

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

基于 Go + Fiber 框架的 HTTP 服务,集成了认证、限流、结构化日志和配置热重载功能。

系统简介

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

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

核心功能

  • 认证中间件:基于 Redis 的 Token 认证
  • 限流中间件:基于 IP 的限流,支持可配置的限制和存储后端
  • 结构化日志:使用 Zap 的 JSON 日志和自动日志轮转
  • 配置热重载:运行时配置更新,无需重启服务
  • 请求 ID 追踪UUID 跨日志的请求追踪
  • Panic 恢复:优雅的 panic 处理和堆栈跟踪日志
  • 统一错误处理:全局 ErrorHandler 统一处理所有 API 错误,返回一致的 JSON 格式包含错误码、消息、时间戳Panic 自动恢复防止服务崩溃;错误分类处理(客户端 4xx、服务端 5xx和日志级别控制敏感信息自动脱敏保护
  • 数据持久化GORM + PostgreSQL 集成,提供完整的 CRUD 操作、事务支持和数据库迁移能力
  • 异步任务处理Asynq 任务队列集成,支持任务提交、后台执行、自动重试和幂等性保障,实现邮件发送、数据同步等异步任务
  • RBAC 权限系统:完整的基于角色的访问控制,支持账号、角色、权限的多对多关联和层级关系;基于 owner_id + shop_id 的自动数据权限过滤,实现多租户数据隔离;使用 PostgreSQL WITH RECURSIVE 查询下级账号并通过 Redis 缓存优化性能(详见 功能总结使用指南
  • 生命周期管理:物联网卡/号卡的开卡、激活、停机、复机、销户
  • 代理商体系:层级管理和分佣结算
  • 批量同步:卡状态、实名状态、流量使用情况

快速开始

# 安装依赖
go mod tidy

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

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

# 运行 Worker 服务(可选)
go run cmd/worker/main.go

详细设置和测试说明请参阅 快速开始指南

项目结构

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             # 配置加载与验证
│   │   └── watcher.go            # 配置热重载fsnotify
│   ├── 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
│
├── configs/                      # 配置文件
│   ├── config.yaml               # 默认配置
│   ├── config.dev.yaml           # 开发环境
│   ├── config.staging.yaml       # 预发布环境
│   └── config.prod.yaml          # 生产环境
│
├── 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. KeyAuth 中间件      │
                    │  (认证)                 │ ─── 可选 (config: enable_auth)
                    └────────────┬────────────┘
                                 │
                    ┌────────────▼────────────┐
                    │  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.logJSON 格式)
    • 结构化字段便于解析和分析
  • 始终激活:是
  • 日志格式:包含字段的 JSONtimestamp、level、method、path、status、duration_ms、request_id、ip、user_agent、user_id

4. KeyAuth 中间件internal/middleware/auth.go

  • 用途:使用 Token 验证对请求进行认证
  • 行为
    • token 请求头提取 token
    • 通过 Redis 验证 tokenauth:token:{token}
    • 如果缺失/无效 token 返回 401
    • 如果 Redis 不可用返回 503fail-closed 策略)
    • 成功时将用户 ID 存储在上下文中:c.Locals(constants.ContextKeyUserID)
  • 配置middleware.enable_auth默认true
  • 跳过路由/health(健康检查绕过认证)
  • 错误码
    • 1001缺失 token
    • 1002无效或过期 token
    • 1004认证服务不可用

5. RateLimiter 中间件internal/middleware/ratelimit.go

  • 用途:通过限制请求速率保护 API 免受滥用
  • 行为
    • 按客户端 IP 地址追踪请求
    • 执行限制:expiration 时间窗口内 max 个请求
    • 如果超过限制返回 429
    • 每个 IP 地址独立计数器
  • 配置middleware.enable_rate_limiter默认false
  • 存储选项
    • memory:内存存储(单服务器,重启后重置)
    • redis:基于 Redis分布式持久化
  • 错误码1003请求过于频繁

6. 路由处理器

  • 用途:执行端点的业务逻辑
  • 可用上下文数据
    • 请求 IDc.Locals(constants.ContextKeyRequestID)
    • 用户 IDc.Locals(constants.ContextKeyUserID)(如果已认证)
    • 标准 Fiber 上下文方法:c.Params()c.Query()c.Body()

中间件注册cmd/api/main.go

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

// 可选:认证中间件
if config.GetConfig().Middleware.EnableAuth {
    tokenValidator := validator.NewTokenValidator(rdb, logger.GetAppLogger())
    app.Use(middleware.KeyAuth(tokenValidator, logger.GetAppLogger()))
}

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

// 路由
app.Get("/health", healthHandler)
app.Get("/api/v1/users", listUsersHandler)

请求流程示例

场景:已启用所有中间件的 /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
   → 检查 RedisGET "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

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

配置

环境特定配置

设置 CONFIG_ENV 环境变量以加载特定配置:

# 开发环境config.dev.yaml
export CONFIG_ENV=dev

# 预发布环境config.staging.yaml
export CONFIG_ENV=staging

# 生产环境config.prod.yaml
export CONFIG_ENV=prod

# 默认配置config.yaml
# 不设置 CONFIG_ENV

配置热重载

配置更改在 5 秒内自动检测并应用,无需重启服务器:

  • 监控文件:所有 configs/*.yaml 文件
  • 检测:使用 fsnotify 监视文件更改
  • 验证:应用前验证新配置
  • 行为
    • 有效更改:立即应用,记录到 logs/app.log
    • 无效更改:拒绝,服务器继续使用先前配置
  • 原子性:使用 sync/atomic 进行线程安全的配置更新

示例

# 在服务器运行时编辑配置
vim configs/config.yaml
# 将 logging.level 从 "info" 改为 "debug"

# 检查日志5 秒内)
tail -f logs/app.log | jq .
# {"level":"info","message":"配置文件已更改","file":"configs/config.yaml"}
# {"level":"info","message":"配置重新加载成功"}

测试

运行所有测试

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

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

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

运行特定测试套件

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

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

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

集成测试

集成测试需要 Redis 运行:

# 启动 Redis
redis-server

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

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

架构设计

分层架构

Handler (HTTP) → Service (业务逻辑) → Store (数据访问) → Model (数据模型)

双服务架构

  • API 服务:处理 HTTP 请求,快速响应
  • Worker 服务:处理异步任务(批量同步、分佣计算等),独立部署

核心模块

  • Service 层:统一管理所有业务逻辑,支持跨模块调用
  • Store 层:统一管理所有数据访问,支持事务
  • Task 层Asynq 任务处理器,支持定时任务和事件触发

开发规范

依赖注入

通过 ServiceStore 结构体统一管理依赖:

// 初始化
st := store.New(db)
svc := service.New(st, queueClient, logger)

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

事务处理

store.Transaction(ctx, func(tx *store.Store) error {
    tx.SIM.UpdateStatus(...)
    tx.Commission.Create(...)
    return nil
})

异步任务

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

常量和 Redis Key 管理

所有常量统一在 pkg/constants/ 目录管理:

// 业务常量
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)

文档

技术栈

  • Go1.25.1
  • Fiberv2.52.9HTTP 框架)
  • Zapv1.27.0(结构化日志)
  • Lumberjackv2.2.1(日志轮转)
  • Viperv1.19.0(配置管理)
  • go-redisv9.7.0Redis 客户端)
  • fsnotifyv1.8.0(文件系统通知)
  • GORM:(数据库 ORM
  • sonic:(高性能 JSON
  • Asynq:(异步任务队列)
  • Validator:(参数验证)

开发流程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 < 200msP99 < 500ms合理使用批量操作和异步任务

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

Speckit 命令

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

# 明确规范细节
/speckit.clarify

# 生成实现计划
/speckit.plan

# 生成任务列表
/speckit.tasks

# 执行实现
/speckit.implement

# 一致性分析
/speckit.analyze

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

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

设计原则

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

许可证

MIT License

Description
No description provided
Readme 1.6 MiB
Languages
Go 99.3%
Shell 0.6%
Makefile 0.1%