Some checks failed
CI / Lint + Build (push) Has been cancelled
审计修复: - CSP: unsafe-inline移除, nonce替代; unsafe-eval保留供Vditor使用 - HSTS: 始终启用(不再依赖release模式) - Controller Exp: common.GetGinExp包装, 不再直接读context - UID解析: common.ParseUIDParam统一, follow/admin controller改用 重构: - router/api.go(198行)拆为7个域文件: auth/settings/posts/comments/reactions/social/studio - 管理后台站点设置: 单页拆为4个子页(brand/security/registration/content)+子菜单 - 前台用户设置页: 1201行拆为6个独立模板+6个独立JS文件 新增: - DB健康检测中间件(middleware/db_health.go): 5s ping+降级页面 - 时区配置: config.yaml server.timezone→time.Local初始化 - .gitea/workflows/ci.yml: strict模式CI流水线
152 lines
4.2 KiB
Go
152 lines
4.2 KiB
Go
// Package admin 提供管理后台的 HTTP 控制器:用户管理、审核、站点设置等。
|
||
package admin
|
||
|
||
import (
|
||
"errors"
|
||
"net/http"
|
||
"strconv"
|
||
|
||
"metazone.cc/mce/internal/common"
|
||
"metazone.cc/mce/internal/model"
|
||
"metazone.cc/mce/internal/service"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
// Note: service import kept for ListUsersParams / ListUsersResult DTOs
|
||
|
||
// AdminController 管理后台控制器
|
||
type AdminController struct {
|
||
adminService adminUseCase
|
||
}
|
||
|
||
// NewAdminController 构造函数
|
||
func NewAdminController(adminService adminUseCase) *AdminController {
|
||
return &AdminController{adminService: adminService}
|
||
}
|
||
|
||
// Dashboard 管理首页(欢迎页)
|
||
func (ac *AdminController) Dashboard(c *gin.Context) {
|
||
totalUsers, err := ac.adminService.CountUsers()
|
||
if err != nil {
|
||
totalUsers = 0
|
||
}
|
||
postStats, err := ac.adminService.GetPostStats()
|
||
if err != nil {
|
||
postStats = &service.PostStats{}
|
||
}
|
||
storeMetrics := ac.adminService.GetStoreMetrics()
|
||
c.HTML(http.StatusOK, "admin/dashboard/index.html", common.BuildAdminPageData(c, gin.H{
|
||
"Title": "管理首页",
|
||
"TotalUsers": totalUsers,
|
||
"PostTotal": postStats.Total,
|
||
"PostApproved": postStats.Approved,
|
||
"PostPending": postStats.Pending,
|
||
"StoreType": storeMetrics.Type,
|
||
"StoreHealthy": storeMetrics.Healthy,
|
||
"StoreDegraded": storeMetrics.Degraded,
|
||
"DegradedNote": storeMetrics.DegradedNote,
|
||
"ActiveSessions": storeMetrics.ActiveCount,
|
||
}))
|
||
}
|
||
|
||
// UsersPage 用户管理页面
|
||
func (ac *AdminController) UsersPage(c *gin.Context) {
|
||
c.HTML(http.StatusOK, "admin/users/index.html", common.BuildAdminPageData(c, gin.H{
|
||
"Title": "用户管理",
|
||
"ExtraCSS": "/admin/static/css/users.css",
|
||
"ExtraJS": "/admin/static/js/users.js",
|
||
"RoleNames": model.RoleDisplayNames,
|
||
"StatusNames": model.StatusDisplayNames,
|
||
}))
|
||
}
|
||
|
||
// ListUsers 用户列表 API(分页 + 搜索 + 筛选)
|
||
func (ac *AdminController) ListUsers(c *gin.Context) {
|
||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||
|
||
params := service.ListUsersParams{
|
||
Keyword: c.Query("keyword"),
|
||
Role: c.Query("role"),
|
||
Status: c.Query("status"),
|
||
Page: page,
|
||
PageSize: pageSize,
|
||
}
|
||
|
||
result, err := ac.adminService.ListUsers(params)
|
||
if err != nil {
|
||
common.Error(c, http.StatusInternalServerError, "查询失败")
|
||
return
|
||
}
|
||
|
||
common.Ok(c, result)
|
||
}
|
||
|
||
// UpdateUserStatus 封禁/解封用户
|
||
func (ac *AdminController) UpdateUserStatus(c *gin.Context) {
|
||
targetUID, err := parseUIDParam(c)
|
||
if err != nil {
|
||
common.Error(c, http.StatusBadRequest, "无效的用户 ID")
|
||
return
|
||
}
|
||
|
||
var req struct {
|
||
Status string `json:"status" binding:"required"`
|
||
}
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
common.Error(c, http.StatusBadRequest, "参数错误")
|
||
return
|
||
}
|
||
|
||
operatorUID := c.GetUint("uid")
|
||
if err := ac.adminService.UpdateUserStatus(operatorUID, targetUID, req.Status); err != nil {
|
||
handleServiceError(c, err)
|
||
return
|
||
}
|
||
|
||
action := "封禁"
|
||
switch req.Status {
|
||
case model.StatusActive:
|
||
action = "解封"
|
||
case model.StatusLocked:
|
||
action = "已锁定"
|
||
}
|
||
common.OkMessage(c, action+"成功")
|
||
}
|
||
|
||
// ResetToken 强制用户下线
|
||
func (ac *AdminController) ResetToken(c *gin.Context) {
|
||
targetUID, err := parseUIDParam(c)
|
||
if err != nil {
|
||
common.Error(c, http.StatusBadRequest, "无效的用户 ID")
|
||
return
|
||
}
|
||
|
||
operatorUID := c.GetUint("uid")
|
||
if err := ac.adminService.ResetUserToken(operatorUID, targetUID); err != nil {
|
||
handleServiceError(c, err)
|
||
return
|
||
}
|
||
|
||
common.OkMessage(c, "已强制下线")
|
||
}
|
||
|
||
// parseUIDParam 从 URL 路径参数解析 uid
|
||
func parseUIDParam(c *gin.Context) (uint, error) {
|
||
return common.ParseUIDParam(c, "uid")
|
||
}
|
||
|
||
// handleServiceError 统一处理 service 层返回的错误
|
||
func handleServiceError(c *gin.Context, err error) {
|
||
if errors.Is(err, common.ErrPermissionDenied) {
|
||
common.Error(c, http.StatusForbidden, "权限不足")
|
||
} else if errors.Is(err, common.ErrUserNotFound) {
|
||
common.Error(c, http.StatusNotFound, "用户不存在")
|
||
} else if errors.Is(err, common.ErrEmailExists) {
|
||
common.Error(c, http.StatusConflict, "该邮箱已被其他用户注册,无法解锁")
|
||
} else {
|
||
common.Error(c, http.StatusInternalServerError, "操作失败")
|
||
}
|
||
}
|