396 lines
11 KiB
Go
396 lines
11 KiB
Go
package handler
|
||
|
||
import (
|
||
"errors"
|
||
"net/http"
|
||
"strconv"
|
||
|
||
"carrotskin/internal/container"
|
||
apperrors "carrotskin/internal/errors"
|
||
"carrotskin/internal/model"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
"go.uber.org/zap"
|
||
)
|
||
|
||
// AdminHandler 管理员处理器
|
||
type AdminHandler struct {
|
||
container *container.Container
|
||
}
|
||
|
||
// NewAdminHandler 创建管理员处理器
|
||
func NewAdminHandler(c *container.Container) *AdminHandler {
|
||
return &AdminHandler{container: c}
|
||
}
|
||
|
||
// SetUserRoleRequest 设置用户角色请求
|
||
type SetUserRoleRequest struct {
|
||
UserID int64 `json:"user_id" binding:"required"`
|
||
Role string `json:"role" binding:"required,oneof=user admin"`
|
||
}
|
||
|
||
// SetUserRole 设置用户角色
|
||
// @Summary 设置用户角色
|
||
// @Description 管理员设置指定用户的角色
|
||
// @Tags Admin
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param request body SetUserRoleRequest true "设置角色请求"
|
||
// @Success 200 {object} model.Response{data=map[string]interface{}} "更新成功"
|
||
// @Failure 400 {object} model.ErrorResponse "参数错误"
|
||
// @Failure 403 {object} model.ErrorResponse "无权操作"
|
||
// @Security BearerAuth
|
||
// @Router /api/v1/admin/users/role [put]
|
||
func (h *AdminHandler) SetUserRole(c *gin.Context) {
|
||
var req SetUserRoleRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
RespondBadRequest(c, "参数错误", err)
|
||
return
|
||
}
|
||
|
||
// 获取当前操作者ID(安全类型断言,防止中间件异常时 panic)
|
||
operatorIDVal, _ := c.Get("user_id")
|
||
operatorID, ok := operatorIDVal.(int64)
|
||
if !ok {
|
||
RespondServerError(c, "操作者ID类型错误", nil)
|
||
return
|
||
}
|
||
|
||
// 不能修改自己的角色
|
||
if req.UserID == operatorID {
|
||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||
model.CodeBadRequest,
|
||
"不能修改自己的角色",
|
||
nil,
|
||
))
|
||
return
|
||
}
|
||
|
||
// 检查目标用户是否存在(区分 DB 错误与"用户不存在")
|
||
targetUser, err := h.container.UserService.GetByID(c.Request.Context(), req.UserID)
|
||
if err != nil {
|
||
RespondServerError(c, "查询用户失败", err)
|
||
return
|
||
}
|
||
if targetUser == nil {
|
||
c.JSON(http.StatusNotFound, model.NewErrorResponse(
|
||
model.CodeNotFound,
|
||
"用户不存在",
|
||
nil,
|
||
))
|
||
return
|
||
}
|
||
|
||
// 更新用户角色
|
||
if err := h.container.UserService.SetRole(c.Request.Context(), req.UserID, req.Role); err != nil {
|
||
RespondServerError(c, "更新用户角色失败", err)
|
||
return
|
||
}
|
||
|
||
h.container.Logger.Info("管理员修改用户角色",
|
||
zap.Int64("operator_id", operatorID),
|
||
zap.Int64("target_user_id", req.UserID),
|
||
zap.String("new_role", req.Role),
|
||
)
|
||
|
||
c.JSON(http.StatusOK, model.NewSuccessResponse(gin.H{
|
||
"message": "用户角色更新成功",
|
||
"user_id": req.UserID,
|
||
"role": req.Role,
|
||
}))
|
||
}
|
||
|
||
// GetUserList 获取用户列表
|
||
// @Summary 获取用户列表
|
||
// @Description 管理员获取所有用户列表
|
||
// @Tags Admin
|
||
// @Produce json
|
||
// @Param page query int false "页码" default(1)
|
||
// @Param page_size query int false "每页数量" default(20)
|
||
// @Success 200 {object} model.Response{data=map[string]interface{}} "获取成功"
|
||
// @Failure 403 {object} model.ErrorResponse "无权操作"
|
||
// @Security BearerAuth
|
||
// @Router /api/v1/admin/users [get]
|
||
func (h *AdminHandler) GetUserList(c *gin.Context) {
|
||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||
|
||
if page < 1 {
|
||
page = 1
|
||
}
|
||
if pageSize < 1 || pageSize > 100 {
|
||
pageSize = 20
|
||
}
|
||
|
||
// 通过 Service 层查询用户列表
|
||
users, total, err := h.container.UserService.ListUsers(c.Request.Context(), page, pageSize)
|
||
if err != nil {
|
||
RespondServerError(c, "获取用户列表失败", err)
|
||
return
|
||
}
|
||
|
||
// 构建响应(隐藏敏感信息)
|
||
userList := make([]gin.H, len(users))
|
||
for i, u := range users {
|
||
userList[i] = gin.H{
|
||
"id": u.ID,
|
||
"username": u.Username,
|
||
"email": u.Email,
|
||
"avatar": u.Avatar,
|
||
"role": u.Role,
|
||
"status": u.Status,
|
||
"points": u.Points,
|
||
"last_login_at": u.LastLoginAt,
|
||
"created_at": u.CreatedAt,
|
||
}
|
||
}
|
||
|
||
c.JSON(http.StatusOK, model.NewSuccessResponse(gin.H{
|
||
"users": userList,
|
||
"total": total,
|
||
"page": page,
|
||
"page_size": pageSize,
|
||
}))
|
||
}
|
||
|
||
// GetUserDetail 获取用户详情
|
||
// @Summary 获取用户详情
|
||
// @Description 管理员获取指定用户的详细信息
|
||
// @Tags Admin
|
||
// @Produce json
|
||
// @Param id path int true "用户ID"
|
||
// @Success 200 {object} model.Response{data=map[string]interface{}} "获取成功"
|
||
// @Failure 404 {object} model.ErrorResponse "用户不存在"
|
||
// @Security BearerAuth
|
||
// @Router /api/v1/admin/users/{id} [get]
|
||
func (h *AdminHandler) GetUserDetail(c *gin.Context) {
|
||
userID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||
if err != nil {
|
||
RespondBadRequest(c, "无效的用户ID", err)
|
||
return
|
||
}
|
||
|
||
user, err := h.container.UserService.GetByID(c.Request.Context(), userID)
|
||
if err != nil || user == nil {
|
||
c.JSON(http.StatusNotFound, model.NewErrorResponse(
|
||
model.CodeNotFound,
|
||
"用户不存在",
|
||
nil,
|
||
))
|
||
return
|
||
}
|
||
|
||
c.JSON(http.StatusOK, model.NewSuccessResponse(gin.H{
|
||
"id": user.ID,
|
||
"username": user.Username,
|
||
"email": user.Email,
|
||
"avatar": user.Avatar,
|
||
"role": user.Role,
|
||
"status": user.Status,
|
||
"points": user.Points,
|
||
"properties": user.Properties,
|
||
"last_login_at": user.LastLoginAt,
|
||
"created_at": user.CreatedAt,
|
||
"updated_at": user.UpdatedAt,
|
||
}))
|
||
}
|
||
|
||
// SetUserStatusRequest 设置用户状态请求
|
||
type SetUserStatusRequest struct {
|
||
UserID int64 `json:"user_id" binding:"required"`
|
||
Status int16 `json:"status" binding:"required,oneof=1 0 -1"` // 1:正常, 0:禁用, -1:删除
|
||
}
|
||
|
||
// SetUserStatus 设置用户状态
|
||
// @Summary 设置用户状态
|
||
// @Description 管理员设置用户状态(启用/禁用)
|
||
// @Tags Admin
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param request body SetUserStatusRequest true "设置状态请求"
|
||
// @Success 200 {object} model.Response{data=map[string]interface{}} "更新成功"
|
||
// @Failure 400 {object} model.ErrorResponse "参数错误"
|
||
// @Security BearerAuth
|
||
// @Router /api/v1/admin/users/status [put]
|
||
func (h *AdminHandler) SetUserStatus(c *gin.Context) {
|
||
var req SetUserStatusRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
RespondBadRequest(c, "参数错误", err)
|
||
return
|
||
}
|
||
|
||
operatorIDVal, _ := c.Get("user_id")
|
||
operatorID, ok := operatorIDVal.(int64)
|
||
if !ok {
|
||
RespondServerError(c, "操作者ID类型错误", nil)
|
||
return
|
||
}
|
||
|
||
// 不能修改自己的状态
|
||
if req.UserID == operatorID {
|
||
c.JSON(http.StatusBadRequest, model.NewErrorResponse(
|
||
model.CodeBadRequest,
|
||
"不能修改自己的状态",
|
||
nil,
|
||
))
|
||
return
|
||
}
|
||
|
||
// 检查目标用户是否存在(区分 DB 错误与"用户不存在")
|
||
targetUser, err := h.container.UserService.GetByID(c.Request.Context(), req.UserID)
|
||
if err != nil {
|
||
RespondServerError(c, "查询用户失败", err)
|
||
return
|
||
}
|
||
if targetUser == nil {
|
||
c.JSON(http.StatusNotFound, model.NewErrorResponse(
|
||
model.CodeNotFound,
|
||
"用户不存在",
|
||
nil,
|
||
))
|
||
return
|
||
}
|
||
|
||
// 更新用户状态
|
||
if err := h.container.UserService.SetStatus(c.Request.Context(), req.UserID, req.Status); err != nil {
|
||
RespondServerError(c, "更新用户状态失败", err)
|
||
return
|
||
}
|
||
|
||
statusText := map[int16]string{1: "正常", 0: "禁用", -1: "删除"}[req.Status]
|
||
|
||
h.container.Logger.Info("管理员修改用户状态",
|
||
zap.Int64("operator_id", operatorID),
|
||
zap.Int64("target_user_id", req.UserID),
|
||
zap.Int16("new_status", req.Status),
|
||
)
|
||
|
||
c.JSON(http.StatusOK, model.NewSuccessResponse(gin.H{
|
||
"message": "用户状态更新成功",
|
||
"user_id": req.UserID,
|
||
"status": req.Status,
|
||
"status_text": statusText,
|
||
}))
|
||
}
|
||
|
||
// DeleteTexture 管理员删除材质
|
||
// @Summary 管理员删除材质
|
||
// @Description 管理员可以删除任意材质(用于审核不当内容)
|
||
// @Tags Admin
|
||
// @Produce json
|
||
// @Param id path int true "材质ID"
|
||
// @Success 200 {object} model.Response{data=map[string]interface{}} "删除成功"
|
||
// @Failure 404 {object} model.ErrorResponse "材质不存在"
|
||
// @Security BearerAuth
|
||
// @Router /api/v1/admin/textures/{id} [delete]
|
||
func (h *AdminHandler) DeleteTexture(c *gin.Context) {
|
||
textureID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||
if err != nil {
|
||
RespondBadRequest(c, "无效的材质ID", err)
|
||
return
|
||
}
|
||
|
||
operatorIDVal, _ := c.Get("user_id")
|
||
operatorID, ok := operatorIDVal.(int64)
|
||
if !ok {
|
||
RespondServerError(c, "操作者ID类型错误", nil)
|
||
return
|
||
}
|
||
|
||
// 通过 Service 删除材质(Service 会检查存在性)
|
||
if err := h.container.TextureService.AdminDelete(c.Request.Context(), textureID); err != nil {
|
||
if errors.Is(err, apperrors.ErrTextureNotFound) {
|
||
c.JSON(http.StatusNotFound, model.NewErrorResponse(
|
||
model.CodeNotFound,
|
||
"材质不存在",
|
||
nil,
|
||
))
|
||
return
|
||
}
|
||
RespondServerError(c, "删除材质失败", err)
|
||
return
|
||
}
|
||
|
||
h.container.Logger.Info("管理员删除材质",
|
||
zap.Int64("operator_id", operatorID),
|
||
zap.Int64("texture_id", textureID),
|
||
)
|
||
|
||
c.JSON(http.StatusOK, model.NewSuccessResponse(gin.H{
|
||
"message": "材质删除成功",
|
||
"texture_id": textureID,
|
||
}))
|
||
}
|
||
|
||
// GetTextureList 管理员获取材质列表
|
||
// @Summary 管理员获取材质列表
|
||
// @Description 管理员获取所有材质列表(用于审核)
|
||
// @Tags Admin
|
||
// @Produce json
|
||
// @Param page query int false "页码" default(1)
|
||
// @Param page_size query int false "每页数量" default(20)
|
||
// @Success 200 {object} model.Response{data=map[string]interface{}} "获取成功"
|
||
// @Security BearerAuth
|
||
// @Router /api/v1/admin/textures [get]
|
||
func (h *AdminHandler) GetTextureList(c *gin.Context) {
|
||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||
|
||
if page < 1 {
|
||
page = 1
|
||
}
|
||
if pageSize < 1 || pageSize > 100 {
|
||
pageSize = 20
|
||
}
|
||
|
||
// 通过 Service 层查询材质列表
|
||
textures, total, err := h.container.TextureService.ListForAdmin(c.Request.Context(), page, pageSize)
|
||
if err != nil {
|
||
RespondServerError(c, "获取材质列表失败", err)
|
||
return
|
||
}
|
||
|
||
// 构建响应
|
||
textureList := make([]gin.H, len(textures))
|
||
for i, t := range textures {
|
||
uploaderName := ""
|
||
if t.Uploader != nil {
|
||
uploaderName = t.Uploader.Username
|
||
}
|
||
textureList[i] = gin.H{
|
||
"id": t.ID,
|
||
"name": t.Name,
|
||
"type": t.Type,
|
||
"hash": t.Hash,
|
||
"uploader_id": t.UploaderID,
|
||
"uploader_name": uploaderName,
|
||
"is_public": t.IsPublic,
|
||
"download_count": t.DownloadCount,
|
||
"created_at": t.CreatedAt,
|
||
}
|
||
}
|
||
|
||
c.JSON(http.StatusOK, model.NewSuccessResponse(gin.H{
|
||
"textures": textureList,
|
||
"total": total,
|
||
"page": page,
|
||
"page_size": pageSize,
|
||
}))
|
||
}
|
||
|
||
// GetPermissions 获取权限列表
|
||
// @Summary 获取权限列表
|
||
// @Description 管理员获取所有Casbin权限规则
|
||
// @Tags Admin
|
||
// @Produce json
|
||
// @Success 200 {object} model.Response{data=map[string]interface{}} "获取成功"
|
||
// @Security BearerAuth
|
||
// @Router /api/v1/admin/permissions [get]
|
||
func (h *AdminHandler) GetPermissions(c *gin.Context) {
|
||
// 获取所有权限规则
|
||
policies, _ := h.container.Casbin.GetEnforcer().GetPolicy()
|
||
c.JSON(http.StatusOK, model.NewSuccessResponse(gin.H{
|
||
"policies": policies,
|
||
}))
|
||
}
|