155 lines
3.8 KiB
Go
155 lines
3.8 KiB
Go
package handler
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"silk-server-go/internal/middleware"
|
|
"silk-server-go/internal/model"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// RegisterUserRoutes 注册用户管理路由(均需 user:manage 权限)
|
|
func RegisterUserRoutes(rg *gin.RouterGroup, db *gorm.DB) {
|
|
perm := middleware.RequirePermission(db, "user:manage")
|
|
rg.GET("/users", perm, listUsers(db))
|
|
rg.GET("/users/:id", perm, getUser(db))
|
|
rg.PATCH("/users/:id", perm, updateUser(db))
|
|
}
|
|
|
|
// toUserPublic 将 User 转为公开信息(不含密码哈希)
|
|
func toUserPublic(u model.User) gin.H {
|
|
return gin.H{
|
|
"id": u.ID,
|
|
"username": u.Username,
|
|
"email": u.Email,
|
|
"fullName": u.FullName,
|
|
"role": u.Role,
|
|
"active": u.Active,
|
|
"createdAt": u.CreatedAt,
|
|
}
|
|
}
|
|
|
|
// listUsers 用户列表
|
|
func listUsers(db *gorm.DB) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
var users []model.User
|
|
db.Select("id", "username", "email", "full_name", "role", "active", "created_at").
|
|
Order("created_at DESC").Find(&users)
|
|
|
|
result := make([]gin.H, 0, len(users))
|
|
for _, u := range users {
|
|
result = append(result, toUserPublic(u))
|
|
}
|
|
c.JSON(http.StatusOK, result)
|
|
}
|
|
}
|
|
|
|
// getUser 用户详情
|
|
func getUser(db *gorm.DB) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
id := c.Param("id")
|
|
var user model.User
|
|
if db.Select("id", "username", "email", "full_name", "role", "active", "created_at").
|
|
Where("id = ?", id).First(&user).Error != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, toUserPublic(user))
|
|
}
|
|
}
|
|
|
|
// updateUser 更新用户(角色/active/fullName/email),记录审计日志
|
|
func updateUser(db *gorm.DB) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
id := c.Param("id")
|
|
|
|
// 先检查用户是否存在
|
|
var user model.User
|
|
if db.Where("id = ?", id).First(&user).Error != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
|
|
return
|
|
}
|
|
|
|
var body map[string]interface{}
|
|
if err := c.ShouldBindJSON(&body); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
// 只允许更新 fullName/email/role/active
|
|
updates := make(map[string]interface{})
|
|
if v, ok := body["fullName"]; ok {
|
|
updates["full_name"] = v
|
|
}
|
|
if v, ok := body["email"]; ok {
|
|
updates["email"] = v
|
|
}
|
|
if v, ok := body["role"]; ok {
|
|
roleStr, _ := v.(string)
|
|
valid := false
|
|
for _, r := range model.AllRoles {
|
|
if r == roleStr {
|
|
valid = true
|
|
break
|
|
}
|
|
}
|
|
if !valid {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的角色,可选:" + strings.Join(model.AllRoles, ", ")})
|
|
return
|
|
}
|
|
updates["role"] = v
|
|
}
|
|
if v, ok := body["active"]; ok {
|
|
updates["active"] = v
|
|
}
|
|
|
|
if len(updates) > 0 {
|
|
db.Model(&model.User{}).Where("id = ?", id).Updates(updates)
|
|
}
|
|
|
|
// 记录审计日志
|
|
var actorID, actorName *string
|
|
if u, ok := c.Get("user"); ok {
|
|
if userMap, ok := u.(map[string]interface{}); ok {
|
|
if sub, ok := userMap["sub"].(string); ok {
|
|
s := sub
|
|
actorID = &s
|
|
}
|
|
if uname, ok := userMap["username"].(string); ok {
|
|
s := uname
|
|
actorName = &s
|
|
}
|
|
}
|
|
}
|
|
action := "update"
|
|
description := "user updated"
|
|
if _, ok := body["role"]; ok {
|
|
action = "role_change"
|
|
if r, ok := body["role"].(string); ok {
|
|
description = "role changed to " + r
|
|
}
|
|
}
|
|
res := "users"
|
|
tid := id
|
|
metaBytes, _ := json.Marshal(body)
|
|
recordAudit(db, &model.AuditLog{
|
|
UserID: actorID,
|
|
Username: actorName,
|
|
Action: action,
|
|
Resource: &res,
|
|
TargetID: &tid,
|
|
Description: &description,
|
|
Metadata: metaBytes,
|
|
})
|
|
|
|
// 返回更新后的用户
|
|
db.Select("id", "username", "email", "full_name", "role", "active", "created_at").
|
|
Where("id = ?", id).First(&user)
|
|
c.JSON(http.StatusOK, toUserPublic(user))
|
|
}
|
|
}
|