Files
junhong_cmp_fiber/tests/testutils/setup.go
huang eaa70ac255 feat: 实现 RBAC 权限系统和数据权限控制 (004-rbac-data-permission)
主要功能:
- 实现完整的 RBAC 权限系统(账号、角色、权限的多对多关联)
- 基于 owner_id + shop_id 的自动数据权限过滤
- 使用 PostgreSQL WITH RECURSIVE 查询下级账号
- Redis 缓存优化下级账号查询性能(30分钟过期)
- 支持多租户数据隔离和层级权限管理

技术实现:
- 新增 Account、Role、Permission 模型及关联关系表
- 实现 GORM Scopes 自动应用数据权限过滤
- 添加数据库迁移脚本(000002_rbac_data_permission、000003_add_owner_id_shop_id)
- 完善错误码定义(1010-1027 为 RBAC 相关错误)
- 重构 main.go 采用函数拆分提高可读性

测试覆盖:
- 添加 Account、Role、Permission 的集成测试
- 添加数据权限过滤的单元测试和集成测试
- 添加下级账号查询和缓存的单元测试
- 添加 API 回归测试确保向后兼容

文档更新:
- 更新 README.md 添加 RBAC 功能说明
- 更新 CLAUDE.md 添加技术栈和开发原则
- 添加 docs/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:06 +08:00

102 lines
2.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package testutils
import (
"context"
"fmt"
"testing"
"time"
"github.com/redis/go-redis/v9"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"github.com/break/junhong_cmp_fiber/internal/model"
)
// SetupTestDB 设置测试数据库和 Redis
func SetupTestDB(t *testing.T) (*gorm.DB, *redis.Client) {
t.Helper()
// 连接测试数据库(使用远程数据库)
dsn := "host=cxd.whcxd.cn port=16159 user=erp_pgsql password=erp_2025 dbname=junhong_cmp_test sslmode=disable TimeZone=Asia/Shanghai"
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
})
if err != nil {
t.Skipf("跳过测试:无法连接测试数据库: %v", err)
}
// 自动迁移测试表
err = db.AutoMigrate(
&model.Account{},
&model.Role{},
&model.Permission{},
&model.AccountRole{},
&model.RolePermission{},
)
if err != nil {
t.Fatalf("数据库迁移失败: %v", err)
}
// 连接测试 Redis使用远程 Redis
redisClient := redis.NewClient(&redis.Options{
Addr: "cxd.whcxd.cn:16299",
Password: "cpNbWtAaqgo1YJmbMp3h",
DB: 15, // 使用测试数据库
})
ctx := context.Background()
if err := redisClient.Ping(ctx).Err(); err != nil {
t.Skipf("跳过测试:无法连接 Redis: %v", err)
}
// 清空 Redis 测试数据库
redisClient.FlushDB(ctx)
return db, redisClient
}
// TeardownTestDB 清理测试数据库
func TeardownTestDB(t *testing.T, db *gorm.DB, redisClient *redis.Client) {
t.Helper()
// 清空测试数据
ctx := context.Background()
db.Exec("TRUNCATE TABLE tb_account_role CASCADE")
db.Exec("TRUNCATE TABLE tb_role_permission CASCADE")
db.Exec("TRUNCATE TABLE tb_account CASCADE")
db.Exec("TRUNCATE TABLE tb_role CASCADE")
db.Exec("TRUNCATE TABLE tb_permission CASCADE")
// 清空 Redis
redisClient.FlushDB(ctx)
// 关闭连接
sqlDB, _ := db.DB()
if sqlDB != nil {
_ = sqlDB.Close()
}
_ = redisClient.Close()
}
// GenerateUsername 生成测试用户名
func GenerateUsername(prefix string, index int) string {
return fmt.Sprintf("%s_%d", prefix, index)
}
// GeneratePhone 生成测试手机号
func GeneratePhone(prefix string, index int) string {
return fmt.Sprintf("%s%08d", prefix, index)
}
// Now 返回当前时间
func Now() time.Time {
return time.Now()
}
// Since 返回从指定时间到现在的持续时间
func Since(t time.Time) time.Duration {
return time.Since(t)
}