241 lines
7.0 KiB
Go
241 lines
7.0 KiB
Go
package handler
|
||
|
||
import (
|
||
"net/http"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"soul-api/internal/database"
|
||
"soul-api/internal/middleware"
|
||
"soul-api/internal/model"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
"golang.org/x/crypto/bcrypt"
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// AdminUsersList GET /api/admin/users 管理员用户列表(仅 super_admin)
|
||
func AdminUsersList(c *gin.Context) {
|
||
claims := middleware.GetAdminClaims(c)
|
||
if claims == nil || claims.Role != "super_admin" {
|
||
c.JSON(http.StatusForbidden, gin.H{"success": false, "error": "无权限"})
|
||
return
|
||
}
|
||
db := database.DB()
|
||
page, _ := strconv.Atoi(c.Query("page"))
|
||
if page < 1 {
|
||
page = 1
|
||
}
|
||
pageSize, _ := strconv.Atoi(c.Query("pageSize"))
|
||
if pageSize < 1 {
|
||
pageSize = 10
|
||
}
|
||
if pageSize > 100 {
|
||
pageSize = 100
|
||
}
|
||
search := strings.TrimSpace(c.Query("search"))
|
||
var total int64
|
||
q := db.Model(&model.AdminUser{})
|
||
if search != "" {
|
||
q = q.Where("username LIKE ? OR name LIKE ?", "%"+search+"%", "%"+search+"%")
|
||
}
|
||
if err := q.Count(&total).Error; err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||
return
|
||
}
|
||
var list []model.AdminUser
|
||
offset := (page - 1) * pageSize
|
||
if err := q.Order("id ASC").Offset(offset).Limit(pageSize).Find(&list).Error; err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||
return
|
||
}
|
||
records := make([]gin.H, 0, len(list))
|
||
for _, u := range list {
|
||
records = append(records, gin.H{
|
||
"id": u.ID,
|
||
"username": u.Username,
|
||
"role": u.Role,
|
||
"name": u.Name,
|
||
"status": u.Status,
|
||
"createdAt": u.CreatedAt,
|
||
"updatedAt": u.UpdatedAt,
|
||
})
|
||
}
|
||
totalPages := (int(total) + pageSize - 1) / pageSize
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"records": records,
|
||
"total": total,
|
||
"page": page,
|
||
"pageSize": pageSize,
|
||
"totalPages": totalPages,
|
||
})
|
||
}
|
||
|
||
// AdminUsersAction POST/PUT/DELETE /api/admin/users 管理员用户增删改(仅 super_admin)
|
||
func AdminUsersAction(c *gin.Context) {
|
||
claims := middleware.GetAdminClaims(c)
|
||
if claims == nil || claims.Role != "super_admin" {
|
||
c.JSON(http.StatusForbidden, gin.H{"success": false, "error": "无权限"})
|
||
return
|
||
}
|
||
db := database.DB()
|
||
switch c.Request.Method {
|
||
case http.MethodPost:
|
||
var body struct {
|
||
Username string `json:"username" binding:"required"`
|
||
Password string `json:"password" binding:"required"`
|
||
Name string `json:"name"`
|
||
Role string `json:"role"`
|
||
}
|
||
if err := c.ShouldBindJSON(&body); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "参数错误"})
|
||
return
|
||
}
|
||
username := trimSpace(body.Username)
|
||
if len(username) < 2 {
|
||
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "用户名至少 2 个字符"})
|
||
return
|
||
}
|
||
if len(body.Password) < 6 {
|
||
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "密码至少 6 位"})
|
||
return
|
||
}
|
||
role := trimSpace(body.Role)
|
||
if role != "super_admin" && role != "admin" {
|
||
role = "admin"
|
||
}
|
||
hash, err := bcrypt.GenerateFromPassword([]byte(body.Password), bcrypt.DefaultCost)
|
||
if err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "密码加密失败"})
|
||
return
|
||
}
|
||
u := model.AdminUser{
|
||
Username: username,
|
||
PasswordHash: string(hash),
|
||
Role: role,
|
||
Name: trimSpace(body.Name),
|
||
Status: "active",
|
||
}
|
||
if err := db.Create(&u).Error; err != nil {
|
||
if strings.Contains(err.Error(), "Duplicate") || strings.Contains(err.Error(), "1062") {
|
||
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "用户名已存在"})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"data": gin.H{
|
||
"id": u.ID,
|
||
"username": u.Username,
|
||
"role": u.Role,
|
||
"name": u.Name,
|
||
"status": u.Status,
|
||
"createdAt": u.CreatedAt,
|
||
},
|
||
})
|
||
case http.MethodPut:
|
||
var body struct {
|
||
ID uint `json:"id" binding:"required"`
|
||
Password string `json:"password"`
|
||
Name string `json:"name"`
|
||
Role string `json:"role"`
|
||
Status string `json:"status"`
|
||
}
|
||
if err := c.ShouldBindJSON(&body); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "参数错误"})
|
||
return
|
||
}
|
||
var u model.AdminUser
|
||
if err := db.First(&u, body.ID).Error; err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
c.JSON(http.StatusNotFound, gin.H{"success": false, "error": "用户不存在"})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||
return
|
||
}
|
||
updates := make(map[string]interface{})
|
||
updates["name"] = trimSpace(body.Name)
|
||
if body.Role == "super_admin" || body.Role == "admin" {
|
||
updates["role"] = body.Role
|
||
}
|
||
if body.Status == "active" || body.Status == "disabled" {
|
||
updates["status"] = body.Status
|
||
}
|
||
if body.Password != "" {
|
||
if len(body.Password) < 6 {
|
||
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "新密码至少 6 位"})
|
||
return
|
||
}
|
||
hash, err := bcrypt.GenerateFromPassword([]byte(body.Password), bcrypt.DefaultCost)
|
||
if err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "密码加密失败"})
|
||
return
|
||
}
|
||
updates["password_hash"] = string(hash)
|
||
}
|
||
if len(updates) > 0 {
|
||
if err := db.Model(&u).Updates(updates).Error; err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||
return
|
||
}
|
||
}
|
||
var updated model.AdminUser
|
||
_ = db.First(&updated, u.ID)
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"data": gin.H{
|
||
"id": updated.ID,
|
||
"username": updated.Username,
|
||
"role": updated.Role,
|
||
"name": updated.Name,
|
||
"status": updated.Status,
|
||
"updatedAt": updated.UpdatedAt,
|
||
},
|
||
})
|
||
case http.MethodDelete:
|
||
id := c.Query("id")
|
||
if id == "" {
|
||
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "缺少 id"})
|
||
return
|
||
}
|
||
uid64, err := strconv.ParseUint(strings.TrimSpace(id), 10, 32)
|
||
if err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "id 无效"})
|
||
return
|
||
}
|
||
uid := uint(uid64)
|
||
var u model.AdminUser
|
||
if err := db.First(&u, uid).Error; err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||
return
|
||
}
|
||
if u.Username == claims.Username {
|
||
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "不能删除当前登录账号"})
|
||
return
|
||
}
|
||
if u.Role == "super_admin" {
|
||
var cnt int64
|
||
db.Model(&model.AdminUser{}).Where("role = ?", "super_admin").Count(&cnt)
|
||
if cnt <= 1 {
|
||
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "至少保留一个超级管理员"})
|
||
return
|
||
}
|
||
}
|
||
if err := db.Delete(&u).Error; err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||
default:
|
||
c.JSON(http.StatusMethodNotAllowed, gin.H{"success": false, "error": "方法不支持"})
|
||
}
|
||
}
|