mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-29 22:06:38 +08:00
295 lines
9.1 KiB
Go
295 lines
9.1 KiB
Go
// Copyright 2026 Arctel.net
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package handler
|
|
|
|
import (
|
|
"Wavelet/core/contracts"
|
|
"Wavelet/pkg/ginutil"
|
|
"Wavelet/pkg/logger"
|
|
"Wavelet/pkg/response"
|
|
"Wavelet/plugins/domain/admin/errs"
|
|
"Wavelet/plugins/domain/admin/model"
|
|
"Wavelet/plugins/domain/admin/service"
|
|
"errors"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func parseUserID(c *gin.Context) (uint64, bool) {
|
|
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
|
if err != nil || id == 0 {
|
|
response.AbortBadRequest(c, errs.UserNotFound)
|
|
return 0, false
|
|
}
|
|
return id, true
|
|
}
|
|
|
|
// abortUserLogicError maps the user service outcome onto the unified response envelope.
|
|
func abortUserLogicError(c *gin.Context, err error, notFoundMsg string, forbiddenMsgs, badRequestMsgs []string) bool {
|
|
if err == nil {
|
|
return false
|
|
}
|
|
if errors.Is(err, errs.ErrUserServiceUnavailable) {
|
|
response.AbortInternal(c, err.Error())
|
|
return true
|
|
}
|
|
if errors.Is(err, errs.ErrUserNotFound) {
|
|
response.AbortNotFound(c, notFoundMsg)
|
|
return true
|
|
}
|
|
msg := err.Error()
|
|
for _, m := range badRequestMsgs {
|
|
if msg == m {
|
|
response.AbortBadRequest(c, msg)
|
|
return true
|
|
}
|
|
}
|
|
for _, m := range forbiddenMsgs {
|
|
if msg == m {
|
|
response.AbortForbidden(c, msg)
|
|
return true
|
|
}
|
|
}
|
|
logger.ErrorF(c.Request.Context(), "Admin user error: %v", err)
|
|
response.AbortInternal(c, errs.InternalServerError)
|
|
return true
|
|
}
|
|
|
|
// ListUsers 获取用户列表
|
|
// @Summary 获取用户列表
|
|
// @Description 分页返回用户列表,支持按用户 ID 和用户名筛选,需要管理员权限
|
|
// @Tags admin
|
|
// @Produce json
|
|
// @Security SessionCookie
|
|
// @Param request query model.ListUsersRequest true "查询参数"
|
|
// @Success 200 {object} response.Any{data=model.ListUsersResponse} "用户列表"
|
|
// @Failure 400 {object} response.Any "参数错误"
|
|
// @Failure 401 {object} response.Any "未登录"
|
|
// @Failure 403 {object} response.Any "无管理员权限"
|
|
// @Failure 500 {object} response.Any "内部错误"
|
|
// @Router /api/v1/admin/users [get]
|
|
func ListUsers(c *gin.Context) {
|
|
var req model.ListUsersRequest
|
|
if err := c.ShouldBindQuery(&req); err != nil {
|
|
response.AbortBadRequest(c, err.Error())
|
|
return
|
|
}
|
|
|
|
total, dtos, err := service.AdminListUsers(c.Request.Context(), contracts.AdminListUsersFilter{
|
|
Page: req.Page,
|
|
PageSize: req.PageSize,
|
|
UserID: req.UserID,
|
|
Username: req.Username,
|
|
Email: req.Email,
|
|
})
|
|
if err != nil {
|
|
response.AbortInternal(c, err.Error())
|
|
return
|
|
}
|
|
|
|
users := make([]model.UserResponse, 0, len(dtos))
|
|
for _, dto := range dtos {
|
|
users = append(users, service.ToUserResponse(dto))
|
|
}
|
|
|
|
c.JSON(http.StatusOK, response.OK(model.ListUsersResponse{
|
|
Users: users,
|
|
Total: total,
|
|
}))
|
|
}
|
|
|
|
// GetUser 获取用户详情
|
|
// @Summary 获取用户详情
|
|
// @Description 返回指定用户的完整个人资料和系统状态,需要管理员权限,不返回密码等敏感字段
|
|
// @Tags admin
|
|
// @Produce json
|
|
// @Security SessionCookie
|
|
// @Param id path int true "用户 ID"
|
|
// @Success 200 {object} response.Any{data=model.UserResponse} "用户详情"
|
|
// @Failure 400 {object} response.Any "参数错误"
|
|
// @Failure 401 {object} response.Any "未登录"
|
|
// @Failure 403 {object} response.Any "无管理员权限"
|
|
// @Failure 404 {object} response.Any "用户不存在"
|
|
// @Failure 500 {object} response.Any "内部错误"
|
|
// @Router /api/v1/admin/users/{id} [get]
|
|
func GetUser(c *gin.Context) {
|
|
id, ok := parseUserID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
targetUser, err := service.AdminGetUser(c.Request.Context(), id)
|
|
if abortUserLogicError(c, err, errs.UserNotFound, nil, nil) {
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, response.OK(service.ToUserResponse(targetUser)))
|
|
}
|
|
|
|
// UpdateUserStatus 更新用户状态(启用/禁用)
|
|
// @Summary 更新用户状态
|
|
// @Description 启用或禁用指定用户,管理员账号无法被禁用,需要管理员权限
|
|
// @Tags admin
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security SessionCookie
|
|
// @Param id path int true "用户 ID"
|
|
// @Param request body model.UpdateUserStatusRequest true "状态参数"
|
|
// @Success 200 {object} response.Any{data=string} "更新成功"
|
|
// @Failure 400 {object} response.Any "参数错误"
|
|
// @Failure 401 {object} response.Any "未登录"
|
|
// @Failure 403 {object} response.Any "无管理员权限或尝试禁用管理员"
|
|
// @Failure 404 {object} response.Any "用户不存在"
|
|
// @Failure 500 {object} response.Any "内部错误"
|
|
// @Router /api/v1/admin/users/{id}/status [put]
|
|
func UpdateUserStatus(c *gin.Context) {
|
|
var req model.UpdateUserStatusRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.AbortBadRequest(c, err.Error())
|
|
return
|
|
}
|
|
|
|
id, ok := parseUserID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
if err := service.AdminUpdateUserStatus(c.Request.Context(), id, req.IsActive); err != nil {
|
|
if abortUserLogicError(c, err, errs.UserNotFound, []string{errs.CannotDisable}, nil) {
|
|
return
|
|
}
|
|
response.AbortInternal(c, errs.UpdateUserFailed)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, response.OKNil())
|
|
}
|
|
|
|
// DeleteUser 删除用户
|
|
// @Summary 删除用户
|
|
// @Description 删除指定非管理员用户,需要管理员权限,不能删除当前登录用户
|
|
// @Tags admin
|
|
// @Produce json
|
|
// @Security SessionCookie
|
|
// @Param id path int true "用户 ID"
|
|
// @Success 200 {object} response.Any{data=string} "删除成功"
|
|
// @Failure 400 {object} response.Any "参数错误"
|
|
// @Failure 401 {object} response.Any "未登录"
|
|
// @Failure 403 {object} response.Any "无管理员权限、尝试删除管理员或当前用户"
|
|
// @Failure 404 {object} response.Any "用户不存在"
|
|
// @Failure 500 {object} response.Any "内部错误"
|
|
// @Router /api/v1/admin/users/{id} [delete]
|
|
func DeleteUser(c *gin.Context) {
|
|
id, ok := parseUserID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
currUser, _ := ginutil.GetFromContext[*contracts.UserDTO](c, contracts.AuthUserObjKey)
|
|
if currUser == nil {
|
|
response.AbortUnauthorized(c, errs.AdminRequired)
|
|
return
|
|
}
|
|
|
|
if err := service.AdminDeleteUser(c.Request.Context(), currUser.ID, id); err != nil {
|
|
if abortUserLogicError(c, err, errs.UserNotFound, []string{errs.CannotDelete, errs.CannotDeleteSelf}, nil) {
|
|
return
|
|
}
|
|
response.AbortInternal(c, errs.DeleteUserFailed)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, response.OKNil())
|
|
}
|
|
|
|
// CreateUser 创建用户
|
|
// @Summary 创建用户
|
|
// @Description 创建一个本地密码登录的新用户,需要管理员权限
|
|
// @Tags admin
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security SessionCookie
|
|
// @Param request body model.CreateUserRequest true "创建用户参数"
|
|
// @Success 200 {object} response.Any{data=model.UserResponse} "创建成功"
|
|
// @Failure 400 {object} response.Any "参数错误或用户名已存在"
|
|
// @Failure 401 {object} response.Any "未登录"
|
|
// @Failure 403 {object} response.Any "无管理员权限"
|
|
// @Failure 500 {object} response.Any "内部错误"
|
|
// @Router /api/v1/admin/users [post]
|
|
func CreateUser(c *gin.Context) {
|
|
var req model.CreateUserRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.AbortBadRequest(c, err.Error())
|
|
return
|
|
}
|
|
|
|
newUser, err := service.AdminCreateUser(c.Request.Context(), contracts.AdminCreateUserRequest{
|
|
Username: req.Username,
|
|
Password: req.Password,
|
|
Nickname: req.Nickname,
|
|
Email: req.Email,
|
|
IsActive: req.IsActive,
|
|
IsAdmin: req.IsAdmin,
|
|
})
|
|
if abortUserLogicError(c, err, "", nil, []string{errs.UsernameRequired, errs.EmailRequired, errs.PasswordTooShort, errs.UsernameExists, errs.EmailExists}) {
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, response.OK(service.ToUserResponse(newUser)))
|
|
}
|
|
|
|
// UpdateUser 更新用户信息
|
|
// @Summary 更新用户信息
|
|
// @Description 更新指定用户的昵称、邮箱、管理员权限,并可选重置密码,需要管理员权限
|
|
// @Tags admin
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security SessionCookie
|
|
// @Param id path int true "用户 ID"
|
|
// @Param request body model.UpdateUserRequest true "更新参数"
|
|
// @Success 200 {object} response.Any{data=string} "更新成功"
|
|
// @Failure 400 {object} response.Any "参数错误"
|
|
// @Failure 401 {object} response.Any "未登录"
|
|
// @Failure 403 {object} response.Any "无管理员权限或尝试修改自身权限"
|
|
// @Failure 404 {object} response.Any "用户不存在"
|
|
// @Failure 500 {object} response.Any "内部错误"
|
|
// @Router /api/v1/admin/users/{id} [put]
|
|
func UpdateUser(c *gin.Context) {
|
|
var req model.UpdateUserRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.AbortBadRequest(c, err.Error())
|
|
return
|
|
}
|
|
|
|
id, ok := parseUserID(c)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
currUser, _ := ginutil.GetFromContext[*contracts.UserDTO](c, contracts.AuthUserObjKey)
|
|
if currUser == nil {
|
|
response.AbortUnauthorized(c, errs.AdminRequired)
|
|
return
|
|
}
|
|
|
|
err := service.AdminUpdateUser(c.Request.Context(), currUser.ID, contracts.AdminUpdateUserRequest{
|
|
ID: id,
|
|
Nickname: req.Nickname,
|
|
Email: req.Email,
|
|
IsAdmin: req.IsAdmin,
|
|
Password: req.Password,
|
|
})
|
|
if err != nil {
|
|
if abortUserLogicError(c, err, errs.UserNotFound, []string{errs.CannotRevokeSelfAdmin}, []string{errs.EmailRequired, errs.EmailExists, errs.PasswordTooShort}) {
|
|
return
|
|
}
|
|
response.AbortInternal(c, errs.UpdateUserInfoFailed)
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, response.OKNil())
|
|
}
|