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