aliyun-deepseek-r1

This commit is contained in:
zhangkun9038@dingtalk.com 2025-02-20 11:29:57 +08:00
parent 998e9d5412
commit 375b305ab4
30 changed files with 705 additions and 3357 deletions

221
README.md
View File

@ -1,221 +0,0 @@
### 认证相关路由
1. **注册用户**
```sh
curl -X POST http://localhost:8080/auth/register \
-H "Content-Type: application/json" \
-d '{"username": "testuser", "password": "testpass", "email": "test@example.com"}'
```
2. **登录用户**
```sh
curl -X POST http://localhost:8080/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "testuser", "password": "testpass"}'
```
### 需要认证的路由
假设登录后返回的token为`your_jwt_token_here`
#### 用户管理
3. **获取所有用户**
```sh
curl -X GET http://localhost:8080/api/users \
-H "Authorization: Bearer your_jwt_token_here"
```
4. **获取单个用户**
```sh
curl -X GET http://localhost:8080/api/users/1 \
-H "Authorization: Bearer your_jwt_token_here"
```
5. **更新用户**
```sh
curl -X PUT http://localhost:8080/api/users/1 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your_jwt_token_here" \
-d '{"name": "updated_name", "email": "updated_email@example.com"}'
```
6. **删除用户**
```sh
curl -X DELETE http://localhost:8080/api/users/1 \
-H "Authorization: Bearer your_jwt_token_here"
```
#### 角色管理
7. **创建新角色**
```sh
curl -X POST http://localhost:8080/api/roles \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your_jwt_token_here" \
-d '{"name": "admin", "description": "Administrator role"}'
```
8. **获取所有角色**
```sh
curl -X GET http://localhost:8080/api/roles \
-H "Authorization: Bearer your_jwt_token_here"
```
9. **获取单个角色**
```sh
curl -X GET http://localhost:8080/api/roles/1 \
-H "Authorization: Bearer your_jwt_token_here"
```
10. **更新角色**
```sh
curl -X PUT http://localhost:8080/api/roles/1 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your_jwt_token_here" \
-d '{"name": "updated_role_name", "description": "Updated description"}'
```
11. **删除角色**
```sh
curl -X DELETE http://localhost:8080/api/roles/1 \
-H "Authorization: Bearer your_jwt_token_here"
```
#### 权限管理
12. **创建新权限**
```sh
curl -X POST http://localhost:8080/api/permissions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your_jwt_token_here" \
-d '{"name": "create_user", "description": "Create user permission", "resource": "user", "action": "create"}'
```
13. **获取所有权限**
```sh
curl -X GET http://localhost:8080/api/permissions \
-H "Authorization: Bearer your_jwt_token_here"
```
14. **获取单个权限**
```sh
curl -X GET http://localhost:8080/api/permissions/1 \
-H "Authorization: Bearer your_jwt_token_here"
```
15. **更新权限**
```sh
curl -X PUT http://localhost:8080/api/permissions/1 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your_jwt_token_here" \
-d '{"name": "updated_permission_name", "description": "Updated description", "resource": "user", "action": "update"}'
```
16. **删除权限**
```sh
curl -X DELETE http://localhost:8080/api/permissions/1 \
-H "Authorization: Bearer your_jwt_token_here"
> 请用curl语句实现如下操作:
>
> 创建一个账号:
> fullname: 李晓,
> 用户名: lixiao
> 密码 abcd@1234,
> 邮箱 lixiao@zjmud.xyz
> 创建2个权限:
> dbGroup1Reader:
> resource: db1,db2
> action: read
> dbGroup1Writer:
> resource: db1,db2
> action: write
> 创建1个角色:
> db1RW, 包含Permissions: "dbGroup1Reader, dbGroup1Writer"
> 把 账号lixiao绑定角色 db1RW
为了实现这些操作,我们需要使用 `curl` 命令来发送 HTTP 请求到你的 API。假设你的 API 端点是 `http://localhost:8080/api`,以下是具体的 `curl` 命令:
### 1. 创建账号
```sh
curl -X POST http://localhost:8080/auth/register \
-H "Content-Type: application/json" \
-d '{
"fullname": "李晓",
"username": "lixiao",
"password": "abcd@1234",
"email": "lixiao@zjmud.xyz"
}'
```
### 2. 创建权限 `dbGroup1Reader`
```sh
curl -X POST http://localhost:8080/api/permissions \
-H "Content-Type: application/json" \
-d '{
"name": "dbGroup1Reader",
"description": "Read access to db1 and db2",
"resource": "db1,db2",
"action": "read"
}'
```
### 3. 创建权限 `dbGroup1Writer`
```sh
curl -X POST http://localhost:8080/api/permissions \
-H "Content-Type: application/json" \
-d '{
"name": "dbGroup1Writer",
"description": "Write access to db1 and db2",
"resource": "db1,db2",
"action": "write"
}'
```
### 4. 创建角色 `db1RW` 并包含权限 `dbGroup1Reader``dbGroup1Writer`
首先,我们需要获取 `dbGroup1Reader``dbGroup1Writer` 的 ID。假设我们已经知道这些 ID例如 `readerID``writerID`),我们可以直接使用它们。如果不知道,可以通过查询权限列表来获取。
#### 查询权限列表以获取 ID可选
```sh
curl -X GET http://localhost:8080/api/permissions
```
#### 创建角色 `db1RW` 并关联权限
假设 `readerID` 是 1`writerID` 是 2
```sh
curl -X POST http://localhost:8080/api/roles \
-H "Content-Type: application/json" \
-d '{
"name": "db1RW",
"description": "Read and Write access to db1 and db2"
}'
# 获取新创建的角色 ID假设为 roleID
curl -X GET http://localhost:8080/api/roles
# 为角色 `db1RW` 分配权限 `dbGroup1Reader``dbGroup1Writer`
curl -X POST http://localhost:8080/api/roles/{roleID}/permissions \
-H "Content-Type: application/json" \
-d '[
{"permission_id": 1},
{"permission_id": 2}
]'
```
### 5. 将账号 `lixiao` 绑定角色 `db1RW`
假设 `lixiao` 的用户 ID 是 1`db1RW` 的角色 ID 是 1
```sh
curl -X POST http://localhost:8080/api/users/{userID}/roles \
-H "Content-Type: application/json" \
-d '[
{"role_id": 1}
]'
```
请根据实际情况替换 `{userID}``{roleID}` 为实际的 ID 值。
如果你需要自动化这个过程,可以编写一个脚本来执行这些命令,并处理响应以获取必要的 ID。
*

View File

@ -1,55 +1,49 @@
package api package api
import ( import (
"gitea.zjmud.xyz/phyer/rbac/controllers" "gitea.zjmud.com/phyer/rbac/controllers"
"gitea.zjmud.xyz/phyer/rbac/middleware" "gitea.zjmud.com/phyer/rbac/middleware"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
func SetupRoutes(r *gin.Engine) { func SetupRouter(db *gorm.DB, redisClient *redis.Client) *gin.Engine {
// 认证相关路由 router := gin.Default()
auth := r.Group("/auth")
// Initialize controllers
authController := controllers.NewAuthController(db, redisClient)
rbacController := controllers.NewRBACController(db, redisClient)
userController := controllers.NewUserController(db, redisClient)
// Public routes - No authentication required
authGroup := router.Group("/auth")
{ {
auth.POST("/register", controllers.Register) authGroup.POST("/login", authController.Login)
auth.POST("/login", controllers.Login) authGroup.POST("/register", authController.Register)
} }
// 需要认证的路由 // Protected routes - Require JWT authentication
api := r.Group("/api") apiGroup := router.Group("/api")
api.Use(middleware.JWTAuth()) apiGroup.Use(middleware.AuthMiddleware())
{ {
// 用户管理 // User management routes
users := api.Group("/users") userGroup := apiGroup.Group("/users")
{ {
users.GET("/", controllers.GetUsers) userGroup.GET("", userController.GetUsers)
users.GET("/:id", controllers.GetUser) userGroup.GET("/:id", userController.GetUserByID)
users.PUT("/:id", controllers.UpdateUser)
users.DELETE("/:id", controllers.DeleteUser)
} }
// 角色管理 // RBAC administration routes with additional authorization
roles := api.Group("/roles") rbacGroup := apiGroup.Group("/rbac")
rbacGroup.Use(middleware.RBACMiddleware())
{ {
roles.POST("/", controllers.CreateRole) rbacGroup.POST("/roles", rbacController.CreateRole)
roles.GET("/", controllers.GetRoles) rbacGroup.POST("/permissions", rbacController.CreatePermission)
roles.GET("/:id", controllers.GetRole) rbacGroup.POST("/resources", rbacController.CreateResource)
roles.PUT("/:id", controllers.UpdateRole) rbacGroup.POST("/resource-groups", rbacController.CreateResourceGroup)
roles.DELETE("/:id", controllers.DeleteRole) rbacGroup.POST("/actions", rbacController.CreateAction)
rbacGroup.POST("/assignments", rbacController.AssignRoleToUser)
} }
// 权限管理
permissions := api.Group("/permissions")
{
permissions.POST("", controllers.CreatePermission)
permissions.GET("", controllers.GetPermissions)
permissions.GET("/:id", controllers.GetPermission)
permissions.PUT("/:id", controllers.UpdatePermission)
permissions.DELETE("/:id", controllers.DeletePermission)
}
// Handle trailing slash redirect
api.GET("/permissions/", func(c *gin.Context) {
c.Redirect(301, "/api/permissions")
})
} }
return router
} }

View File

@ -1,61 +1,60 @@
package config package config
import ( import (
"fmt"
"log"
"os" "os"
"strconv"
"github.com/go-redis/redis/v8"
"gorm.io/driver/mysql"
"gorm.io/gorm"
) )
type DBConfig struct {
Host string
Port int
User string
Password string
Name string
}
func (db *DBConfig) DSN() string {
return db.User + ":" + db.Password + "@tcp(" + db.Host + ":" + strconv.Itoa(db.Port) + ")/" + db.Name + "?charset=utf8mb4&parseTime=True&loc=Local"
}
type Config struct { type Config struct {
DB DBConfig DBHost string
JWTSecret string DBPort string
RedisHost string DBUser string
RedisPort int DBPassword string
RedisPassword string DBName string
RedisDB int RedisAddr string
RedisPass string
RedisDB int
ServerPort string
} }
var AppConfig Config func LoadConfig() *Config {
return &Config{
func Init() { DBHost: os.Getenv("DB_HOST"),
AppConfig = Config{ DBPort: os.Getenv("DB_PORT"),
DB: DBConfig{ DBUser: os.Getenv("DB_USER"),
Host: getEnv("DB_HOST", "192.168.65.5"), DBPassword: os.Getenv("DB_PASSWORD"),
Port: getEnvAsInt("DB_PORT", 3306), DBName: os.Getenv("DB_NAME"),
User: getEnv("DB_USER", "root"), RedisAddr: os.Getenv("REDIS_ADDR"),
Password: getEnv("DB_PASSWORD", "d8db*#00oS"), RedisPass: os.Getenv("REDIS_PASS"),
Name: getEnv("DB_NAME", "rbac"), RedisDB: 0,
}, ServerPort: os.Getenv("SERVER_PORT"),
JWTSecret: getEnv("JWT_SECRET", "secret"),
RedisHost: getEnv("REDIS_HOST", "192.168.65.5"),
RedisPort: getEnvAsInt("REDIS_PORT", 6379),
RedisPassword: getEnv("REDIS_PASSWORD", ""),
RedisDB: getEnvAsInt("REDIS_DB", 2),
} }
} }
func getEnv(key, defaultValue string) string { func InitDB(cfg *Config) *gorm.DB {
if value, exists := os.LookupEnv(key); exists { dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
return value cfg.DBUser, cfg.DBPassword, cfg.DBHost, cfg.DBPort, cfg.DBName)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatalf("Failed to connect to database: %v", err)
} }
return defaultValue return db
} }
func getEnvAsInt(key string, defaultValue int) int { func InitRedis(cfg *Config) *redis.Client {
valueStr := getEnv(key, "") rdb := redis.NewClient(&redis.Options{
if value, err := strconv.Atoi(valueStr); err == nil { Addr: cfg.RedisAddr,
return value Password: cfg.RedisPass,
DB: cfg.RedisDB,
})
_, err := rdb.Ping(rdb.Context()).Result()
if err != nil {
log.Fatalf("Failed to connect to Redis: %v", err)
} }
return defaultValue return rdb
} }

View File

@ -1,42 +1,104 @@
package controllers package controllers
import ( import (
"gitea.zjmud.xyz/phyer/rbac/services" "fmt"
"net/http"
"gitea.zjmud.com/phyer/rbac/models"
"gitea.zjmud.com/phyer/rbac/repositories"
"gitea.zjmud.com/phyer/rbac/utils"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-redis/redis/v8"
"github.com/go-redis/redis/v8"
"gorm.io/gorm"
) )
func Register(c *gin.Context) { type AuthController struct {
var registerData struct { DB *gorm.DB
Username string `json:"username"` Redis *redis.Client
Fullname string `json:"fullname"`
Password string `json:"password"`
Email string `json:"email"`
}
if err := c.ShouldBindJSON(&registerData); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
user, err := services.RegisterUser(registerData.Username, registerData.Fullname, registerData.Password, registerData.Email)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(201, user)
} }
func Login(c *gin.Context) { func NewAuthController(db *gorm.DB, redis *redis.Client) *AuthController {
var loginData struct { return &AuthController{DB: db, Redis: redis}
Username string `json:"username"` }
Password string `json:"password"`
} func (ac *AuthController) Login(c *gin.Context) {
if err := c.ShouldBindJSON(&loginData); err != nil { type LoginRequest struct {
c.JSON(400, gin.H{"error": err.Error()}) Username string `json:"username" binding:"required"`
return Password string `json:"password" binding:"required"`
} }
token, err := services.Login(loginData.Username, loginData.Password)
if err != nil { var req LoginRequest
c.JSON(401, gin.H{"error": err.Error()}) if err := c.ShouldBindJSON(&req); err != nil {
return utils.Error(c, http.StatusBadRequest, "Invalid request format")
} return
c.JSON(200, gin.H{"token": token}) }
userRepo := repositories.NewUserRepository(ac.DB)
user, err := userRepo.FindByUsername(req.Username)
if err != nil {
utils.Error(c, http.StatusUnauthorized, "Invalid credentials")
return
}
hashedPassword, err := models.HashPassword(req.Password)
if err != nil {
utils.Error(c, http.StatusUnauthorized, "Invalid credentials")
return
}
hashedHex := fmt.Sprintf("%x", hashedPassword)
if hashedHex != user.Password {
if err != nil {
utils.Error(c, http.StatusUnauthorized, "Invalid credentials")
return
}
hashedHex := fmt.Sprintf("%x", hashedPassword)
if hashedHex != user.Password {
utils.Error(c, http.StatusUnauthorized, "Invalid credentials")
return
}
token, err := utils.GenerateToken(user.ID)
if err != nil {
utils.Error(c, http.StatusInternalServerError, "Failed to generate token")
return
}
utils.Success(c, gin.H{"token": token})
}
func (ac *AuthController) Register(c *gin.Context) {
type RegisterRequest struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
Email string `json:"email" binding:"required,email"`
}
var req RegisterRequest
if err := c.ShouldBindJSON(&req); err != nil {
utils.Error(c, http.StatusBadRequest, "Invalid request format")
return
}
hashedPassword, err := models.HashPassword(req.Password)
if err != nil {
utils.Error(c, http.StatusInternalServerError, "Failed to hash password")
return
}
newUser := &models.User{
Username: req.Username,
Password: string(hashedPassword),
Email: req.Email,
}
userRepo := repositories.NewUserRepository(ac.DB)
if err := userRepo.Create(newUser); err != nil {
utils.Error(c, http.StatusConflict, "User already exists")
return
}
utils.Success(c, newUser)
} }

View File

@ -1,228 +1,53 @@
package controllers package controllers
import ( import (
// "log" "net/http"
"strconv" "gitea.zjmud.com/phyer/rbac/models"
"gitea.zjmud.xyz/phyer/rbac/services"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/sirupsen/logrus" "gorm.io/gorm"
) )
// CreateRole 创建新角色 type RBACController struct {
func CreateRole(c *gin.Context) { DB *gorm.DB
var roleData struct { Redis *redis.Client
}
func NewRBACController(db *gorm.DB, redis *redis.Client) *RBACController {
return &RBACController{DB: db, Redis: redis}
}
func (rc *RBACController) CreateRole(c *gin.Context) {
type CreateRoleRequest struct {
Name string `json:"name" binding:"required"` Name string `json:"name" binding:"required"`
Description string `json:"description"` Description string `json:"description"`
} }
if err := c.ShouldBindJSON(&roleData); err != nil {
c.JSON(400, gin.H{"error": err.Error()}) var req CreateRoleRequest
if err := c.ShouldBindJSON(&req); err != nil {
utils.Error(c, http.StatusBadRequest, "Invalid request format")
return return
} }
role, err := services.CreateRole(roleData.Name, roleData.Description) // 检查角色是否已存在
if err != nil { roleRepo := repositories.NewRoleRepository(rc.DB)
c.JSON(500, gin.H{"error": err.Error()}) exists, err := roleRepo.ExistsByNameExcludeID(req.Name, 0)
if err != nil || exists {
utils.Error(c, http.StatusConflict, "Role already exists")
return return
} }
c.JSON(201, role)
newRole := &models.Role{
Name: req.Name,
Description: req.Description,
}
if err := roleRepo.Create(newRole); err != nil {
utils.Error(c, http.StatusInternalServerError, "Failed to create role")
return
}
utils.Success(c, newRole)
} }
// GetRoles 获取所有角色 // 其他RBAC相关方法...
func GetRoles(c *gin.Context) {
roles, err := services.GetAllRoles()
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, roles)
}
// CreatePermission 创建新权限
func CreatePermission(c *gin.Context) {
logrus.Info("CreatePermission called")
var permissionData struct {
Name string `json:"name" binding:"required"`
Description string `json:"description"`
Resource string `json:"resource" binding:"required"`
Action string `json:"action" binding:"required"`
}
if err := c.ShouldBindJSON(&permissionData); err != nil {
logrus.Errorf("Error binding JSON: %v", err)
c.JSON(400, gin.H{"error": err.Error()})
return
}
permission, err := services.CreatePermission(permissionData.Name, permissionData.Description, permissionData.Resource, permissionData.Action)
if err != nil {
logrus.Errorf("Error creating permission: %v", err)
c.JSON(500, gin.H{"error": err.Error()})
return
}
logrus.Infof("Permission created: %+v", permission)
c.JSON(201, permission)
}
// AssignRoleToUser 为用户分配角色
func AssignRoleToUser(c *gin.Context) {
var assignmentData struct {
UserID uint `json:"user_id" binding:"required"`
RoleID uint `json:"role_id" binding:"required"`
}
if err := c.ShouldBindJSON(&assignmentData); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
err := services.AssignRoleToUser(assignmentData.UserID, assignmentData.RoleID)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"message": "Role assigned successfully"})
}
// AssignPermissionToRole 为角色分配权限
func AssignPermissionToRole(c *gin.Context) {
var assignmentData struct {
RoleID uint `json:"role_id" binding:"required"`
PermissionID uint `json:"permission_id" binding:"required"`
}
if err := c.ShouldBindJSON(&assignmentData); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
err := services.AssignPermissionToRole(assignmentData.RoleID, assignmentData.PermissionID)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"message": "Permission assigned successfully"})
}
// GetPermission 获取单个权限
func GetPermission(c *gin.Context) {
id := c.Param("id")
permissionID, err := strconv.ParseUint(id, 10, 64)
if err != nil {
c.JSON(400, gin.H{"error": "Invalid permission ID"})
return
}
permission, err := services.GetPermissionByID(uint(permissionID))
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, permission)
}
// UpdatePermission 更新权限
func UpdatePermission(c *gin.Context) {
id := c.Param("id")
permissionID, err := strconv.ParseUint(id, 10, 64)
if err != nil {
c.JSON(400, gin.H{"error": "Invalid permission ID"})
return
}
var updateData map[string]interface{}
if err := c.ShouldBindJSON(&updateData); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
permission, err := services.UpdatePermission(uint(permissionID), updateData)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, permission)
}
// DeletePermission 删除权限
func DeletePermission(c *gin.Context) {
id := c.Param("id")
permissionID, err := strconv.ParseUint(id, 10, 64)
if err != nil {
c.JSON(400, gin.H{"error": "Invalid permission ID"})
return
}
err = services.DeletePermission(uint(permissionID))
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"message": "Permission deleted successfully"})
}
// GetPermissions 获取所有权限
func GetPermissions(c *gin.Context) {
permissions, err := services.GetAllPermissions()
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, permissions)
}
// GetRole 获取单个角色
func GetRole(c *gin.Context) {
id := c.Param("id")
roleID, err := strconv.ParseUint(id, 10, 64)
if err != nil {
c.JSON(400, gin.H{"error": "Invalid role ID"})
return
}
role, err := services.GetRoleByID(uint(roleID))
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, role)
}
// UpdateRole 更新角色
func UpdateRole(c *gin.Context) {
id := c.Param("id")
roleID, err := strconv.ParseUint(id, 10, 64)
if err != nil {
c.JSON(400, gin.H{"error": "Invalid role ID"})
return
}
var updateData map[string]interface{}
if err := c.ShouldBindJSON(&updateData); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
role, err := services.UpdateRole(uint(roleID), updateData)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, role)
}
// DeleteRole 删除角色
func DeleteRole(c *gin.Context) {
id := c.Param("id")
roleID, err := strconv.ParseUint(id, 10, 64)
if err != nil {
c.JSON(400, gin.H{"error": "Invalid role ID"})
return
}
err = services.DeleteRole(uint(roleID))
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"message": "Role deleted successfully"})
}

View File

@ -1,50 +1,37 @@
package controllers package controllers
import ( import (
"gitea.zjmud.xyz/phyer/rbac/services" "net/http"
"gitea.zjmud.com/phyer/rbac/models"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"gorm.io/gorm"
) )
func GetUsers(c *gin.Context) { type UserController struct {
users, err := services.GetAllUsers() DB *gorm.DB
if err != nil { Redis *redis.Client
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, users)
} }
func GetUser(c *gin.Context) { func NewUserController(db *gorm.DB, redis *redis.Client) *UserController {
id := c.Param("id") return &UserController{DB: db, Redis: redis}
user, err := services.GetUserByID(id)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, user)
} }
func UpdateUser(c *gin.Context) { func (uc *UserController) GetUsers(c *gin.Context) {
id := c.Param("id") var users []models.User
var updateData map[string]interface{} if err := uc.DB.Find(&users).Error; err != nil {
if err := c.ShouldBindJSON(&updateData); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to fetch users"})
c.JSON(400, gin.H{"error": err.Error()})
return return
} }
user, err := services.UpdateUser(id, updateData) c.JSON(http.StatusOK, users)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, user)
} }
func DeleteUser(c *gin.Context) { func (uc *UserController) GetUserByID(c *gin.Context) {
id := c.Param("id") id := c.Param("id")
err := services.DeleteUser(id) var user models.User
if err != nil { if err := uc.DB.First(&user, id).Error; err != nil {
c.JSON(500, gin.H{"error": err.Error()}) c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
return return
} }
c.JSON(200, gin.H{"message": "User deleted successfully"}) c.JSON(http.StatusOK, user)
} }

18
go.mod
View File

@ -1,17 +1,17 @@
module gitea.zjmud.xyz/phyer/rbac module gitea.zjmud.com/phyer/rbac
go 1.21 go 1.20
require ( require (
github.com/gin-gonic/gin v1.9.1 github.com/gin-gonic/gin v1.9.1
github.com/go-redis/redis/v8 v8.11.5 github.com/go-redis/redis/v8 v8.11.5 // 使
github.com/golang-jwt/jwt/v5 v5.0.0 github.com/go-sql-driver/mysql v1.7.1 // indirect
github.com/sirupsen/logrus v1.9.3 gorm.io/driver/mysql v1.5.1
golang.org/x/crypto v0.9.0 gorm.io/gorm v1.25.4
gorm.io/driver/mysql v1.5.7
gorm.io/gorm v1.25.7
) )
require github.com/golang-jwt/jwt/v5 v5.2.1
require ( require (
github.com/bytedance/sonic v1.9.1 // indirect github.com/bytedance/sonic v1.9.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect
@ -22,7 +22,6 @@ require (
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect github.com/goccy/go-json v0.10.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
@ -36,6 +35,7 @@ require (
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect github.com/ugorji/go/codec v1.2.11 // indirect
golang.org/x/arch v0.3.0 // indirect golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.9.0 // indirect
golang.org/x/net v0.10.0 // indirect golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.8.0 // indirect golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect golang.org/x/text v0.9.0 // indirect

26
go.sum
View File

@ -12,7 +12,6 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
@ -20,7 +19,6 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
@ -29,12 +27,13 @@ github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@ -58,17 +57,12 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@ -92,7 +86,6 @@ golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -106,14 +99,13 @@ google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= gorm.io/driver/mysql v1.5.1 h1:WUEH5VF9obL/lTtzjmML/5e6VfFR/788coz2uaVCAZw=
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= gorm.io/driver/mysql v1.5.1/go.mod h1:Jo3Xu7mMhCyj8dlrb3WoCaRd1FhsVh+yMXb1jUInf5o=
gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A= gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw=
gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

32
init.sh Executable file
View File

@ -0,0 +1,32 @@
# 创建所有目录
mkdir -p api config controllers middleware models repositories server services utils
# 创建api目录下的文件
touch api/routes.go
# 创建config目录下的文件
touch config/config.go
# 创建controllers目录下的文件
touch controllers/auth.go controllers/rbac.go controllers/user.go
# 创建middleware目录下的文件
touch middleware/auth.go middleware/rbac.go
# 创建models目录下的文件
touch models/permission.go models/role.go models/user.go models/user_group.go
# 创建repositories目录下的文件
touch repositories/db.go repositories/permission.go repositories/role.go repositories/user.go repositories/user_group.go
# 创建server目录下的文件
touch server/server.go
# 创建services目录下的文件
touch services/auth.go services/rbac.go services/user.go
# 创建utils目录下的文件
touch utils/jwt.go utils/redis.go utils/response.go
# 创建其他文件
touch go.mod go.sum main.go sug.md tree.txt rbac/

30
main.go
View File

@ -1,18 +1,28 @@
package main package main
import ( import (
"gitea.zjmud.xyz/phyer/rbac/api" "log"
"gitea.zjmud.xyz/phyer/rbac/config"
"gitea.zjmud.xyz/phyer/rbac/repositories" "gitea.zjmud.com/phyer/rbac/api"
"gitea.zjmud.xyz/phyer/rbac/server" "gitea.zjmud.com/phyer/rbac/config"
"github.com/gin-gonic/gin"
) )
func main() { func main() {
config.Init() // 加载配置
if err := repositories.InitDB(); err != nil { cfg := config.LoadConfig()
panic("failed to initialize database: " + err.Error())
// 初始化数据库
db := config.InitDB(cfg)
// 初始化Redis
redisClient := config.InitRedis(cfg)
// 初始化路由
router := api.SetupRouter(db, redisClient)
// 启动服务
if err := router.Run(":" + cfg.ServerPort); err != nil {
log.Fatalf("Failed to start server: %v", err)
} }
r := server.NewServer()
api.SetupRoutes(r)
r.Run(":8080")
} }

View File

@ -1,31 +1,27 @@
package middleware package middlewares
import ( import (
"gitea.zjmud.xyz/phyer/rbac/utils"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net/http"
) )
// JWTAuth is an alias for AuthMiddleware
func JWTAuth() gin.HandlerFunc {
return AuthMiddleware()
}
func AuthMiddleware() gin.HandlerFunc { func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
token := c.GetHeader("Authorization") tokenString := c.GetHeader("Authorization")
if token == "" { if tokenString == "" {
c.AbortWithStatusJSON(401, gin.H{"error": "Unauthorized"}) utils.Error(c, http.StatusUnauthorized, "Authorization header required")
c.Abort()
return return
} }
claims, err := utils.ParseJWT(token) claims, err := utils.ParseToken(tokenString)
if err != nil { if err != nil {
c.AbortWithStatusJSON(401, gin.H{"error": "Invalid token"}) utils.Error(c, http.StatusUnauthorized, "Invalid token")
c.Abort()
return return
} }
userID := claims["user_id"].(string) c.Set("userID", claims.UserID)
c.Set("userID", userID)
c.Next() c.Next()
} }
} }

View File

@ -1,23 +1,31 @@
package middleware package middlewares
import ( import (
"gitea.zjmud.xyz/phyer/rbac/repositories"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net/http"
) )
func RBACMiddleware(permission string) gin.HandlerFunc { func RBACMiddleware() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
userID := c.GetString("userID") // 获取用户ID
if userID == "" { userID, exists := c.Get("userID")
c.AbortWithStatusJSON(401, gin.H{"error": "Unauthorized"}) if !exists {
utils.Error(c, http.StatusForbidden, "User authentication required")
c.Abort()
return return
} }
hasPermission, err := repositories.CheckUserPermission(userID, permission) // 获取请求方法和路径
if err != nil || !hasPermission { method := c.Request.Method
c.AbortWithStatusJSON(403, gin.H{"error": "Forbidden"}) path := c.FullPath()
return
} // 这里需要实现RBAC检查逻辑示例代码
// 实际应该查询用户的角色权限是否匹配当前请求
// if !checkPermission(userID, method, path) {
// utils.Error(c, http.StatusForbidden, "Access denied")
// c.Abort()
// return
// }
c.Next() c.Next()
} }

View File

@ -1,11 +1,16 @@
package models package models
import "gorm.io/gorm" import "time"
type Permission struct { type Permission struct {
gorm.Model ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"unique;not null"` Name string `gorm:"uniqueIndex;size:50" json:"name"`
Description string Description string `gorm:"size:255" json:"description"`
Resource string `gorm:"not null"` ResourceID uint `json:"resource_id"`
Action string `gorm:"not null"` ActionID uint `json:"action_id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Resource Resource `gorm:"foreignKey:ResourceID"`
Action Action `gorm:"foreignKey:ActionID"`
} }

View File

@ -1,9 +1,11 @@
package models package models
import "gorm.io/gorm" import "time"
type Role struct { type Role struct {
gorm.Model ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"unique;not null"` Name string `gorm:"uniqueIndex;size:50" json:"name"`
Permissions []Permission `gorm:"many2many:role_permissions;"` Description string `gorm:"size:255" json:"description"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
} }

View File

@ -1,18 +1,35 @@
package models package models
import ( import (
// "crypto/rand"
"crypto/sha256"
"fmt"
"io"
"time" "time"
"gorm.io/gorm"
) )
type User struct { type User struct {
gorm.Model ID uint `gorm:"primaryKey" json:"id"`
Username string `json:"username" gorm:"uniqueIndex;not null"` Username string `gorm:"uniqueIndex;size:50" json:"username"`
Fullname string `json:"fullname" gorm:"not null"` Email string `gorm:"uniqueIndex;size:100" json:"email"`
Password string `json:"-" gorm:"not null"` Password string `gorm:"size:255" json:"-"`
Email string `json:"email" gorm:"uniqueIndex;not null"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"`
Roles []Role `json:"roles" gorm:"many2many:user_roles;"` }
// HashPassword 迁移到 utils 包更合适(可选)
func HashPassword(password string) ([]byte, error) {
h := sha256.New()
if _, err := io.WriteString(h, password); err != nil { // 修正[]byte转换
return nil, err
}
return h.Sum(nil), nil
}
// Validate 建议移动到 service 层(可选)
func (u *User) Validate() error {
if u.Password == "" {
return fmt.Errorf("password cannot be empty")
}
return nil
} }

View File

@ -1,18 +1,11 @@
package models package models
import ( import "time"
"time"
"gorm.io/gorm"
)
// UserGroup 表示用户组模型
type UserGroup struct { type UserGroup struct {
gorm.Model ID uint `gorm:"primaryKey" json:"id"`
Name string `json:"name" gorm:"uniqueIndex;not null"` // 用户组名称 Name string `gorm:"uniqueIndex;size:50" json:"name"`
Description string `json:"description"` // 用户组描述 Description string `gorm:"size:255" json:"description"`
CreatedAt time.Time `json:"created_at"` // 创建时间 CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"` // 更新时间 UpdatedAt time.Time `json:"updated_at"`
Users []User `json:"users" gorm:"many2many:user_group_users;"` // 关联的用户
Roles []Role `json:"roles" gorm:"many2many:user_group_roles;"` // 关联的角色
} }

6
models/user_role.go Normal file
View File

@ -0,0 +1,6 @@
package models
type UserRole struct {
UserID uint `gorm:"primaryKey" json:"user_id"`
RoleID uint `gorm:"primaryKey" json:"role_id"`
}

View File

@ -1,21 +1,53 @@
package repositories package repositories
import ( import (
"gitea.zjmud.xyz/phyer/rbac/config" "gitea.zjmud.com/phyer/rbac/models"
"gitea.zjmud.xyz/phyer/rbac/models"
"gorm.io/driver/mysql"
"gorm.io/gorm" "gorm.io/gorm"
// "strconv"
) )
var db *gorm.DB // BaseRepository 提供基础CRUD操作
type BaseRepository struct {
func InitDB() error { db *gorm.DB
var err error }
dsn := config.AppConfig.DB.DSN()
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) func NewBaseRepository(db *gorm.DB) *BaseRepository {
if err != nil { return &BaseRepository{db: db}
return err }
}
return db.AutoMigrate(&models.Permission{}, &models.Role{}, &models.UserGroup{}, &models.User{}) // 事务支持
func (r *BaseRepository) Begin() *gorm.DB {
return r.db.Begin()
}
func (r *BaseRepository) Commit(tx *gorm.DB) error {
return tx.Commit().Error
}
func (r *BaseRepository) Rollback(tx *gorm.DB) error {
return tx.Rollback().Error
}
// 基础CRUD操作
func (r *BaseRepository) Create(model interface{}) error {
return r.db.Create(model).Error
}
func (r *BaseRepository) Update(model interface{}) error {
return r.db.Save(model).Error
}
func (r *BaseRepository) Delete(model interface{}) error {
return r.db.Delete(model).Error
}
func (r *BaseRepository) FindByID(model interface{}, id uint) error {
return r.db.First(model, id).Error
}
func (r *BaseRepository) FindAll(models interface{}, preloads ...string) error {
tx := r.db
for _, preload := range preloads {
tx = tx.Preload(preload)
}
return tx.Find(models).Error
} }

View File

@ -1,55 +1,55 @@
package repositories package repositories
import ( import (
"gitea.zjmud.xyz/phyer/rbac/models" "gitea.zjmud.com/phyer/rbac/models"
"gorm.io/gorm" "gorm.io/gorm"
) )
func GetPermissionByID(id uint) (*models.Permission, error) { type PermissionRepository interface {
var permission models.Permission Create(permission *models.Permission) error
result := db.First(&permission, id) Update(permission *models.Permission) error
return &permission, result.Error Delete(permission *models.Permission) error
FindByID(id uint) (*models.Permission, error)
FindAll() ([]models.Permission, error)
FindByName(name string) (*models.Permission, error)
} }
func GetPermissions() ([]models.Permission, error) { type permissionRepository struct {
var permissions []models.Permission *BaseRepository
result := db.Find(&permissions)
return permissions, result.Error
} }
func UpdatePermission(id uint, updateData map[string]interface{}) (*models.Permission, error) { func NewPermissionRepository(db *gorm.DB) PermissionRepository {
var permission models.Permission return &permissionRepository{
result := db.Model(&permission).Where("id = ?", id).Updates(updateData) BaseRepository: NewBaseRepository(db),
if result.Error != nil {
return nil, result.Error
} }
return GetPermissionByID(id)
} }
func CreatePermission(permission *models.Permission) (*models.Permission, error) { func (r *permissionRepository) Create(permission *models.Permission) error {
result := db.Create(permission) return r.BaseRepository.Create(permission)
return permission, result.Error
} }
func DeletePermission(id uint) error { func (r *permissionRepository) Update(permission *models.Permission) error {
result := db.Delete(&models.Permission{}, id) return r.BaseRepository.Update(permission)
return result.Error
} }
func CheckPermissionExists(name string) (bool, error) { func (r *permissionRepository) Delete(permission *models.Permission) error {
var count int64 return r.BaseRepository.Delete(permission)
result := db.Model(&models.Permission{}).Where("name = ?", name).Count(&count)
return count > 0, result.Error
} }
func AssignRoleToUser(userID uint, roleID uint) error { func (r *permissionRepository) FindByID(id uint) (*models.Permission, error) {
user := &models.User{Model: gorm.Model{ID: userID}} var permission models.Permission
role := &models.Role{Model: gorm.Model{ID: roleID}} err := r.BaseRepository.FindByID(&permission, id)
return db.Model(user).Association("Roles").Append(role) return &permission, err
} }
func RemoveRoleFromUser(userID uint, roleID uint) error { func (r *permissionRepository) FindAll() ([]models.Permission, error) {
user := &models.User{Model: gorm.Model{ID: userID}} var permissions []models.Permission
role := &models.Role{Model: gorm.Model{ID: roleID}} err := r.BaseRepository.FindAll(&permissions)
return db.Model(user).Association("Roles").Delete(role) return permissions, err
}
func (r *permissionRepository) FindByName(name string) (*models.Permission, error) {
var permission models.Permission
err := r.db.Where("name = ?", name).First(&permission).Error
return &permission, err
} }

View File

@ -1,59 +1,53 @@
package repositories package repositories
import ( import (
"gitea.zjmud.xyz/phyer/rbac/models" "gitea.zjmud.com/phyer/rbac/models"
"gorm.io/gorm" "gorm.io/gorm"
) )
func CreateRole(role *models.Role) (*models.Role, error) { type RoleRepository struct {
result := db.Create(role) *BaseRepository
return role, result.Error
} }
func GetAllRoles() ([]models.Role, error) { func NewRoleRepository(db *gorm.DB) *RoleRepository {
var roles []models.Role return &RoleRepository{
result := db.Find(&roles) BaseRepository: NewBaseRepository(db),
return roles, result.Error
}
func GetRoleByID(id uint) (*models.Role, error) {
var role models.Role
result := db.First(&role, id)
return &role, result.Error
}
func CheckRoleExists(name string) (bool, error) {
var count int64
result := db.Model(&models.Role{}).Where("name = ?", name).Count(&count)
return count > 0, result.Error
}
func AddPermissionToRole(roleID, permissionID uint) error {
role := &models.Role{Model: gorm.Model{ID: roleID}}
permission := &models.Permission{Model: gorm.Model{ID: permissionID}}
return db.Model(role).Association("Permissions").Append(permission)
}
func GetRoles() ([]models.Role, error) {
var roles []models.Role
result := db.Preload("Permissions").Find(&roles)
return roles, result.Error
}
func UpdateRole(id uint, updateData map[string]interface{}) (*models.Role, error) {
var role models.Role
result := db.Model(&role).Where("id = ?", id).Updates(updateData)
if result.Error != nil {
return nil, result.Error
} }
return GetRoleByID(id)
} }
func DeleteRole(id uint) error { // 根据角色名称查找角色
result := db.Delete(&models.Role{}, id) func (r *RoleRepository) FindByName(role *models.Role, name string) error {
return result.Error return r.db.Where("name = ?", name).First(role).Error
} }
func RemovePermissionFromRole(roleID uint, permissionID uint) error { // 分页查询角色列表
return db.Model(&models.Role{Model: gorm.Model{ID: roleID}}).Association("Permissions").Delete(&models.Permission{Model: gorm.Model{ID: permissionID}}) func (r *RoleRepository) FindPaged(roles *[]models.Role, page, pageSize int, preloads ...string) error {
tx := r.db.Model(&models.Role{})
for _, preload := range preloads {
tx = tx.Preload(preload)
}
return tx.Scopes(func(db *gorm.DB) *gorm.DB {
return db.Offset((page - 1) * pageSize).Limit(pageSize)
}).Find(roles).Error
}
// 带权限关联的角色查询
func (r *RoleRepository) FindWithPermissions(role *models.Role, id uint) error {
return r.db.Preload("Permissions").First(role, id).Error
}
// 检查角色名称是否存在(排除当前角色)
func (r *RoleRepository) ExistsByNameExcludeID(name string, excludeID uint) (bool, error) {
var count int64
err := r.db.Model(&models.Role{}).
Where("name = ? AND id != ?", name, excludeID).
Count(&count).Error
return count > 0, err
}
// 同步角色权限关联
func (r *RoleRepository) SyncPermissions(role *models.Role, permissionIDs []uint) error {
return r.db.Model(role).Association("Permissions").Replace(permissionIDs)
} }

View File

@ -1,60 +1,128 @@
package repositories package repositories
import ( import (
"gitea.zjmud.xyz/phyer/rbac/models" "gitea.zjmud.com/phyer/rbac/models"
// "gorm.io/gorm" "gorm.io/gorm"
) )
func CheckUserPermission(userID string, permission string) (bool, error) { type UserRepository interface {
var user models.User Create(user *models.User) error
result := db.Preload("Roles.Permissions").Where("id = ?", userID).First(&user) Update(user *models.User) error
if result.Error != nil { Delete(user *models.User) error
return false, result.Error FindByID(id uint) (*models.User, error)
} FindAll() ([]models.User, error)
FindByUsername(username string) (*models.User, error)
for _, role := range user.Roles {
for _, perm := range role.Permissions {
if perm.Name == permission {
return true, nil
}
}
}
return false, nil
} }
func GetAllUsers() ([]models.User, error) { type userRepository struct {
*BaseRepository
}
func NewUserRepository(db *gorm.DB) UserRepository {
return &userRepository{
BaseRepository: NewBaseRepository(db),
}
}
func (r *userRepository) Create(user *models.User) error {
return r.BaseRepository.Create(user)
}
func (r *userRepository) Update(user *models.User) error {
return r.BaseRepository.Update(user)
}
func (r *userRepository) Delete(user *models.User) error {
return r.BaseRepository.Delete(user)
}
func (r *userRepository) FindByID(id uint) (*models.User, error) {
var user models.User
err := r.BaseRepository.FindByID(&user, id)
return &user, err
}
func (r *userRepository) FindAll() ([]models.User, error) {
var users []models.User var users []models.User
result := db.Find(&users) err := r.BaseRepository.FindAll(&users)
return users, result.Error return users, err
} }
func GetUserByID(id string) (*models.User, error) { func (r *userRepository) FindByUsername(username string) (*models.User, error) {
var user models.User var user models.User
result := db.First(&user, "id = ?", id) err := r.db.Where("username = ?", username).First(&user).Error
return &user, result.Error return &user, err
}
package repositories
import (
"gitea.zjmud.com/phyer/rbac/models"
"gorm.io/gorm"
)
type UserRepository interface {
Create(user *models.User) error
Update(user *models.User) error
Delete(user *models.User) error
FindByID(id uint) (*models.User, error)
FindAll() ([]models.User, error)
FindByUsername(username string) (*models.User, error)
GetUserRoles(userID uint) ([]models.Role, error)
AssignRole(userID uint, roleID uint) error
RemoveRole(userID uint, roleID uint) error
} }
func GetUserByUsername(username string) (*models.User, error) { type userRepository struct {
var user models.User *BaseRepository
result := db.First(&user, "username = ?", username)
return &user, result.Error
} }
func CreateUser(user *models.User) (*models.User, error) { func NewUserRepository(db *gorm.DB) UserRepository {
result := db.Create(user) return &userRepository{
return user, result.Error BaseRepository: NewBaseRepository(db),
}
func UpdateUser(id string, updateData map[string]interface{}) (*models.User, error) {
var user models.User
result := db.Model(&user).Where("id = ?", id).Updates(updateData)
if result.Error != nil {
return nil, result.Error
} }
return GetUserByID(id)
} }
func DeleteUser(id string) error { func (r *userRepository) Create(user *models.User) error {
result := db.Delete(&models.User{}, "id = ?", id) return r.BaseRepository.Create(user)
return result.Error
} }
func (r *userRepository) Update(user *models.User) error {
return r.BaseRepository.Update(user)
}
func (r *userRepository) Delete(user *models.User) error {
return r.BaseRepository.Delete(user)
}
func (r *userRepository) FindByID(id uint) (*models.User, error) {
var user models.User
err := r.db.Preload("Roles").First(&user, id).Error
return &user, err
}
func (r *userRepository) FindAll() ([]models.User, error) {
var users []models.User
err := r.BaseRepository.FindAll(&users, "Roles")
return users, err
}
func (r *userRepository) FindByUsername(username string) (*models.User, error) {
var user models.User
err := r.db.Where("username = ?", username).First(&user).Error
return &user, err
}
func (r *userRepository) GetUserRoles(userID uint) ([]models.Role, error) {
var roles []models.Role
err := r.db.Model(&models.User{ID: userID}).Association("Roles").Find(&roles)
return roles, err
}
func (r *userRepository) AssignRole(userID uint, roleID uint) error {
return r.db.Model(&models.User{ID: userID}).Association("Roles").Append(&models.Role{ID: roleID})
}
func (r *userRepository) RemoveRole(userID uint, roleID uint) error {
return r.db.Model(&models.User{ID: userID}).Association("Roles").Delete(&models.Role{ID: roleID})
}

View File

@ -1,61 +1,55 @@
package repositories package repositories
import ( import (
"gitea.zjmud.xyz/phyer/rbac/models" "gitea.zjmud.com/phyer/rbac/models"
"gorm.io/gorm" "gorm.io/gorm"
) )
func CreateUserGroup(userGroup *models.UserGroup) (*models.UserGroup, error) { type UserGroupRepository interface {
result := db.Create(userGroup) Create(group *models.UserGroup) error
return userGroup, result.Error Update(group *models.UserGroup) error
Delete(group *models.UserGroup) error
FindByID(id uint) (*models.UserGroup, error)
FindAll() ([]models.UserGroup, error)
FindByName(name string) (*models.UserGroup, error)
} }
func GetUserGroupByID(id uint) (*models.UserGroup, error) { type userGroupRepository struct {
var userGroup models.UserGroup *BaseRepository
result := db.Preload("Users").Preload("Roles").First(&userGroup, id)
return &userGroup, result.Error
} }
func GetUserGroups() ([]models.UserGroup, error) { func NewUserGroupRepository(db *gorm.DB) UserGroupRepository {
var userGroups []models.UserGroup return &userGroupRepository{
result := db.Preload("Users").Preload("Roles").Find(&userGroups) BaseRepository: NewBaseRepository(db),
return userGroups, result.Error
}
func UpdateUserGroup(id uint, updateData map[string]interface{}) (*models.UserGroup, error) {
var userGroup models.UserGroup
result := db.Model(&userGroup).Where("id = ?", id).Updates(updateData)
if result.Error != nil {
return nil, result.Error
} }
return GetUserGroupByID(id)
} }
func DeleteUserGroup(id uint) error { func (r *userGroupRepository) Create(group *models.UserGroup) error {
result := db.Delete(&models.UserGroup{}, id) return r.BaseRepository.Create(group)
return result.Error
} }
func AddUserToGroup(userGroupID uint, userID uint) error { func (r *userGroupRepository) Update(group *models.UserGroup) error {
return db.Model(&models.UserGroup{Model: gorm.Model{ID: userGroupID}}). return r.BaseRepository.Update(group)
Association("Users").
Append(&models.User{Model: gorm.Model{ID: userID}})
} }
func RemoveUserFromGroup(userGroupID uint, userID uint) error { func (r *userGroupRepository) Delete(group *models.UserGroup) error {
return db.Model(&models.UserGroup{Model: gorm.Model{ID: userGroupID}}). return r.BaseRepository.Delete(group)
Association("Users").
Delete(&models.User{Model: gorm.Model{ID: userID}})
} }
func AddRoleToGroup(userGroupID uint, roleID uint) error { func (r *userGroupRepository) FindByID(id uint) (*models.UserGroup, error) {
return db.Model(&models.UserGroup{Model: gorm.Model{ID: userGroupID}}). var group models.UserGroup
Association("Roles"). err := r.BaseRepository.FindByID(&group, id)
Append(&models.Role{Model: gorm.Model{ID: roleID}}) return &group, err
} }
func RemoveRoleFromGroup(userGroupID uint, roleID uint) error { func (r *userGroupRepository) FindAll() ([]models.UserGroup, error) {
return db.Model(&models.UserGroup{Model: gorm.Model{ID: userGroupID}}). var groups []models.UserGroup
Association("Roles"). err := r.BaseRepository.FindAll(&groups)
Delete(&models.Role{Model: gorm.Model{ID: roleID}}) return groups, err
}
func (r *userGroupRepository) FindByName(name string) (*models.UserGroup, error) {
var group models.UserGroup
err := r.db.Where("name = ?", name).First(&group).Error
return &group, err
} }

View File

@ -1,27 +1,16 @@
package server package server
import ( import (
"gitea.zjmud.xyz/phyer/rbac/api" "fmt"
// "gitea.zjmud.xyz/phyer/rbac/config" "net/http"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
// "gorm.io/driver/mysql"
// "gorm.io/gorm"
) )
// InitDB is now handled in repositories package func StartServer(router *gin.Engine, cfg *config.Config) {
addr := fmt.Sprintf(":%s", cfg.ServerPort)
// NewServer creates and returns a new Gin server fmt.Printf("Starting server on %s\n", addr)
func NewServer() *gin.Engine { if err := router.Run(addr); err != nil {
r := gin.Default() panic(err)
return r }
}
func Start() {
r := gin.Default()
// 初始化路由
api.SetupRoutes(r)
// 启动服务器
r.Run(":8080")
} }

View File

@ -1,27 +1,31 @@
package services package services
import ( import (
"errors" "gitea.zjmud.com/phyer/rbac/models"
// "gitea.zjmud.xyz/phyer/rbac/models" "gitea.zjmud.com/phyer/rbac/repositories"
"strconv" "gorm.io/gorm"
"gitea.zjmud.xyz/phyer/rbac/repositories"
"gitea.zjmud.xyz/phyer/rbac/utils"
"golang.org/x/crypto/bcrypt"
) )
func Login(username, password string) (string, error) { type AuthService struct {
user, err := repositories.GetUserByUsername(username) userRepo repositories.UserRepository
if err != nil { }
return "", errors.New("invalid credentials")
} func NewAuthService(db *gorm.DB) *AuthService {
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) return &AuthService{
if err != nil { userRepo: repositories.NewUserRepository(db),
return "", errors.New("invalid credentials") }
} }
token, err := utils.GenerateJWT(strconv.FormatUint(uint64(user.ID), 10))
if err != nil { func (s *AuthService) Authenticate(username, password string) (*models.User, error) {
return "", err user, err := s.userRepo.FindByUsername(username)
} if err != nil {
return token, nil return nil, err
}
hashedPassword, err := models.HashPassword(password)
if err != nil || string(hashedPassword) != user.Password {
return nil, ErrInvalidCredentials
}
return user, nil
} }

View File

@ -1,237 +0,0 @@
package services
import (
"errors"
"strconv"
"gitea.zjmud.xyz/phyer/rbac/models"
"gitea.zjmud.xyz/phyer/rbac/repositories"
)
// AssignRoleToUser 为用户分配角色
func AssignRoleToUser(userID uint, roleID uint) error {
// 检查用户是否存在
user, err := repositories.GetUserByID(strconv.FormatUint(uint64(userID), 10))
if err != nil {
return errors.New("user not found")
}
// 检查角色是否存在
role, err := repositories.GetRoleByID(roleID)
if err != nil {
return errors.New("role not found")
}
// 分配角色
return repositories.AssignRoleToUser(user.ID, role.ID)
}
// RemoveRoleFromUser 移除用户的角色
func RemoveRoleFromUser(userID uint, roleID uint) error {
// 检查用户是否存在
user, err := repositories.GetUserByID(strconv.FormatUint(uint64(userID), 10))
if err != nil {
return errors.New("user not found")
}
// 检查角色是否存在
role, err := repositories.GetRoleByID(roleID)
if err != nil {
return errors.New("role not found")
}
// 移除角色
return repositories.RemoveRoleFromUser(user.ID, role.ID)
}
// CreateRoleWithPermissions 创建角色并分配权限
func CreateRoleWithPermissions(roleName string, permissionIDs []uint) (*models.Role, error) {
// 检查角色是否已存在
exists, err := repositories.CheckRoleExists(roleName)
if err != nil {
return nil, err
}
if exists {
return nil, errors.New("role already exists")
}
// 创建角色
role := &models.Role{Name: roleName}
role, err = repositories.CreateRole(role)
if err != nil {
return nil, err
}
// 为角色分配权限
for _, permissionID := range permissionIDs {
err = repositories.AddPermissionToRole(role.ID, permissionID)
if err != nil {
return nil, err
}
}
return role, nil
}
// CreateRole 创建新角色
func CreateRole(name, description string) (*models.Role, error) {
// 检查角色是否已存在
exists, err := repositories.CheckRoleExists(name)
if err != nil {
return nil, err
}
if exists {
return nil, errors.New("role already exists")
}
// 创建角色
role := &models.Role{
Name: name,
}
return repositories.CreateRole(role)
}
// GetAllRoles 获取所有角色
func GetAllRoles() ([]models.Role, error) {
return repositories.GetAllRoles()
}
// CreatePermission 创建新权限
func CreatePermission(name, description, resource, action string) (*models.Permission, error) {
// 检查权限是否已存在
exists, err := repositories.CheckPermissionExists(name)
if err != nil {
return nil, err
}
if exists {
return nil, errors.New("permission already exists")
}
// 创建权限
permission := &models.Permission{
Name: name,
Resource: resource,
Action: action,
}
return repositories.CreatePermission(permission)
}
// AssignPermissionToRole 为角色分配权限
func AssignPermissionToRole(roleID, permissionID uint) error {
// 检查角色是否存在
_, err := repositories.GetRoleByID(roleID)
if err != nil {
return errors.New("role not found")
}
// 检查权限是否存在
_, err = repositories.GetPermissionByID(permissionID)
if err != nil {
return errors.New("permission not found")
}
// 分配权限
return repositories.AddPermissionToRole(roleID, permissionID)
}
// GetAllPermissions 获取所有权限
func GetAllPermissions() ([]models.Permission, error) {
return repositories.GetPermissions()
}
// GetRoleByID 根据ID获取角色
func GetRoleByID(roleID uint) (*models.Role, error) {
return repositories.GetRoleByID(roleID)
}
// UpdateRole 更新角色
func UpdateRole(roleID uint, updateData map[string]interface{}) (*models.Role, error) {
// 检查角色是否存在
_, err := repositories.GetRoleByID(roleID)
if err != nil {
return nil, errors.New("role not found")
}
// 更新角色
return repositories.UpdateRole(roleID, updateData)
}
// DeleteRole 删除角色
func DeleteRole(roleID uint) error {
// 检查角色是否存在
_, err := repositories.GetRoleByID(roleID)
if err != nil {
return errors.New("role not found")
}
// 删除角色
return repositories.DeleteRole(roleID)
}
// GetPermissionByID 根据ID获取权限
func GetPermissionByID(permissionID uint) (*models.Permission, error) {
return repositories.GetPermissionByID(permissionID)
}
// UpdatePermission 更新权限
func UpdatePermission(permissionID uint, updateData map[string]interface{}) (*models.Permission, error) {
// 检查权限是否存在
_, err := repositories.GetPermissionByID(permissionID)
if err != nil {
return nil, errors.New("permission not found")
}
// 更新权限
return repositories.UpdatePermission(permissionID, updateData)
}
// DeletePermission 删除权限
func DeletePermission(permissionID uint) error {
// 检查权限是否存在
_, err := repositories.GetPermissionByID(permissionID)
if err != nil {
return errors.New("permission not found")
}
// 删除权限
return repositories.DeletePermission(permissionID)
}
// CheckUserPermission 检查用户是否具有特定权限
// CheckUserPermission 检查用户是否具有特定权限
func CheckUserPermission(userID string, permission string) (bool, error) {
// 获取用户的所有角色
user, err := repositories.GetUserByID(userID)
if err != nil {
return false, err
}
// 检查每个角色的权限
for _, role := range user.Roles {
for _, perm := range role.Permissions {
if perm.Name == permission {
return true, nil
}
}
}
return false, nil
}
// GetUserPermissions 获取用户的所有权限
func GetUserPermissions(userID string) ([]string, error) {
var permissions []string
user, err := repositories.GetUserByID(userID)
if err != nil {
return nil, err
}
// 收集所有角色的权限
for _, role := range user.Roles {
for _, perm := range role.Permissions {
permissions = append(permissions, perm.Name)
}
}
return permissions, nil
}

View File

@ -1,46 +0,0 @@
package services
import (
"gitea.zjmud.xyz/phyer/rbac/models"
"gitea.zjmud.xyz/phyer/rbac/repositories"
// "gitea.zjmud.xyz/phyer/rbac/utils"
"golang.org/x/crypto/bcrypt"
)
func GetAllUsers() ([]models.User, error) {
return repositories.GetAllUsers()
}
func GetUserByID(id string) (*models.User, error) {
return repositories.GetUserByID(id)
}
func UpdateUser(id string, updateData map[string]interface{}) (*models.User, error) {
if password, ok := updateData["password"]; ok {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password.(string)), bcrypt.DefaultCost)
if err != nil {
return nil, err
}
updateData["password"] = string(hashedPassword)
}
return repositories.UpdateUser(id, updateData)
}
func DeleteUser(id string) error {
return repositories.DeleteUser(id)
}
func RegisterUser(username, fullname, password, email string) (*models.User, error) {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return nil, err
}
user := &models.User{
Username: username,
Fullname: fullname,
Password: string(hashedPassword),
Email: email,
}
return repositories.CreateUser(user)
}

2062
sug.md

File diff suppressed because it is too large Load Diff

View File

@ -1,31 +1,38 @@
package utils package utils
import ( import (
"errors"
"time" "time"
"gitea.zjmud.xyz/phyer/rbac/config"
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
) )
func GenerateJWT(userID string) (string, error) { var JWTSecret = []byte("your-secret-key")
claims := jwt.MapClaims{
"user_id": userID, type Claims struct {
"exp": time.Now().Add(time.Hour * 24).Unix(), UserID uint `json:"user_id"`
} jwt.RegisteredClaims
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(config.AppConfig.JWTSecret))
} }
func ParseJWT(tokenString string) (jwt.MapClaims, error) { func GenerateToken(userID uint) (string, error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { expirationTime := time.Now().Add(24 * time.Hour)
return []byte(config.AppConfig.JWTSecret), nil claims := &Claims{
}) UserID: userID,
if err != nil { RegisteredClaims: jwt.RegisteredClaims{
return nil, err ExpiresAt: jwt.NewNumericDate(expirationTime),
},
} }
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(JWTSecret)
}
func ParseToken(tokenString string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
return JWTSecret, nil
})
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
return claims, nil return claims, nil
} }
return nil, errors.New("invalid token") return nil, err
} }

View File

@ -1,83 +0,0 @@
package utils
import (
"context"
"fmt"
"time"
"gitea.zjmud.xyz/phyer/rbac/config"
"github.com/go-redis/redis/v8"
)
var (
RedisClient *redis.Client
ctx = context.Background()
)
// InitRedis 初始化 Redis 连接
func InitRedis() error {
RedisClient = redis.NewClient(&redis.Options{
Addr: fmt.Sprintf("%s:%d", config.AppConfig.RedisHost, config.AppConfig.RedisPort),
Password: config.AppConfig.RedisPassword,
DB: config.AppConfig.RedisDB, // 使用默认数据库
})
// 测试连接
_, err := RedisClient.Ping(ctx).Result()
if err != nil {
return fmt.Errorf("failed to connect to Redis: %v", err)
}
return nil
}
// Set 设置键值对,带过期时间
func Set(key string, value interface{}, expiration time.Duration) error {
return RedisClient.Set(ctx, key, value, expiration).Err()
}
// Get 获取键值
func Get(key string) (string, error) {
return RedisClient.Get(ctx, key).Result()
}
// Delete 删除键
func Delete(key string) error {
return RedisClient.Del(ctx, key).Err()
}
// Exists 检查键是否存在
func Exists(key string) (bool, error) {
result, err := RedisClient.Exists(ctx, key).Result()
return result == 1, err
}
// Incr 自增键值
func Incr(key string) (int64, error) {
return RedisClient.Incr(ctx, key).Result()
}
// Decr 自减键值
func Decr(key string) (int64, error) {
return RedisClient.Decr(ctx, key).Result()
}
// HSet 设置哈希字段值
func HSet(key string, field string, value interface{}) error {
return RedisClient.HSet(ctx, key, field, value).Err()
}
// HGet 获取哈希字段值
func HGet(key string, field string) (string, error) {
return RedisClient.HGet(ctx, key, field).Result()
}
// HGetAll 获取哈希所有字段值
func HGetAll(key string) (map[string]string, error) {
return RedisClient.HGetAll(ctx, key).Result()
}
// Expire 设置键的过期时间
func Expire(key string, expiration time.Duration) error {
return RedisClient.Expire(ctx, key, expiration).Err()
}

View File

@ -1,44 +1,25 @@
package utils package utils
import ( import "github.com/gin-gonic/gin"
"github.com/gin-gonic/gin"
)
// SuccessResponse 成功响应结构 type Response struct {
type SuccessResponse struct {
Code int `json:"code"` Code int `json:"code"`
Message string `json:"message"` Message string `json:"message"`
Data interface{} `json:"data"` Data interface{} `json:"data"`
} }
// ErrorResponse 错误响应结构 func Success(c *gin.Context, data interface{}) {
type ErrorResponse struct { c.JSON(200, Response{
Code int `json:"code"` Code: 200,
Message string `json:"message"` Message: "success",
}
// RespondSuccess 返回成功响应
func RespondSuccess(c *gin.Context, code int, message string, data interface{}) {
c.JSON(code, SuccessResponse{
Code: code,
Message: message,
Data: data, Data: data,
}) })
} }
// RespondError 返回错误响应 func Error(c *gin.Context, code int, message string) {
func RespondError(c *gin.Context, code int, message string) { c.JSON(code, Response{
c.JSON(code, ErrorResponse{
Code: code, Code: code,
Message: message, Message: message,
}) Data: nil,
}
// RespondValidationError 返回验证错误响应
func RespondValidationError(c *gin.Context, errors map[string]string) {
c.JSON(400, gin.H{
"code": 400,
"message": "Validation error",
"errors": errors,
}) })
} }