Files
soul-yongping/soul-api/internal/handler/admin_users.go

241 lines
7.0 KiB
Go
Raw Normal View History

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": "方法不支持"})
}
}