🚀 主要功能: - 完善后端API服务层,实现完整的CRUD操作 - 开发管理后台所有核心页面 (仪表板、照片、分类、标签、用户、设置) - 完成前后端完全集成,所有API接口正常对接 - 配置完整的CI/CD流水线,支持自动化部署 🎯 后端完善: - 实现PhotoService, CategoryService, TagService, UserService - 添加完整的API处理器和路由配置 - 支持Docker容器化部署 - 添加数据库迁移和健康检查 🎨 管理后台完成: - 仪表板: 实时统计数据展示 - 照片管理: 完整的CRUD操作,支持批量处理 - 分类管理: 树形结构展示和管理 - 标签管理: 颜色标签和统计信息 - 用户管理: 角色权限控制 - 系统设置: 多标签配置界面 - 添加pre-commit代码质量检查 🔧 部署配置: - Docker Compose完整配置 - 后端CI/CD流水线 (Docker部署) - 管理后台CI/CD流水线 (静态文件部署) - 前端CI/CD流水线优化 - 自动化脚本: 部署、备份、监控 - 完整的部署文档和运维指南 ✅ 集成完成: - 所有API接口正常连接 - 认证系统完整集成 - 数据获取和状态管理 - 错误处理和用户反馈 - 响应式设计优化
409 lines
11 KiB
Go
409 lines
11 KiB
Go
package handlers
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"photography-backend/internal/models"
|
|
"photography-backend/internal/service"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
type UserHandler struct {
|
|
userService *service.UserService
|
|
logger *zap.Logger
|
|
}
|
|
|
|
func NewUserHandler(userService *service.UserService, logger *zap.Logger) *UserHandler {
|
|
return &UserHandler{
|
|
userService: userService,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// GetCurrentUser 获取当前用户信息
|
|
// @Summary 获取当前用户信息
|
|
// @Description 获取当前登录用户的详细信息
|
|
// @Tags users
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Success 200 {object} models.UserResponse
|
|
// @Failure 401 {object} models.ErrorResponse
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
// @Router /me [get]
|
|
func (h *UserHandler) GetCurrentUser(c *gin.Context) {
|
|
userID := c.GetUint("user_id")
|
|
|
|
user, err := h.userService.GetUserByID(c.Request.Context(), userID)
|
|
if err != nil {
|
|
h.logger.Error("Failed to get current user", zap.Error(err), zap.Uint("user_id", userID))
|
|
c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
|
Error: "Failed to get user information",
|
|
Message: err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
userResponse := &models.UserResponse{
|
|
ID: user.ID,
|
|
Username: user.Username,
|
|
Email: user.Email,
|
|
Role: user.Role,
|
|
IsActive: user.IsActive,
|
|
CreatedAt: user.CreatedAt,
|
|
UpdatedAt: user.UpdatedAt,
|
|
}
|
|
|
|
c.JSON(http.StatusOK, userResponse)
|
|
}
|
|
|
|
// UpdateCurrentUser 更新当前用户信息
|
|
// @Summary 更新当前用户信息
|
|
// @Description 更新当前登录用户的个人信息
|
|
// @Tags users
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param user body models.UpdateCurrentUserRequest true "用户信息"
|
|
// @Success 200 {object} models.UserResponse
|
|
// @Failure 400 {object} models.ErrorResponse
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
// @Router /me [put]
|
|
func (h *UserHandler) UpdateCurrentUser(c *gin.Context) {
|
|
userID := c.GetUint("user_id")
|
|
|
|
var req models.UpdateCurrentUserRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
h.logger.Error("Failed to bind JSON", zap.Error(err))
|
|
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
|
Error: "Invalid request body",
|
|
Message: err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
user, err := h.userService.UpdateCurrentUser(c.Request.Context(), userID, &req)
|
|
if err != nil {
|
|
h.logger.Error("Failed to update current user", zap.Error(err), zap.Uint("user_id", userID))
|
|
c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
|
Error: "Failed to update user information",
|
|
Message: err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
userResponse := &models.UserResponse{
|
|
ID: user.ID,
|
|
Username: user.Username,
|
|
Email: user.Email,
|
|
Role: user.Role,
|
|
IsActive: user.IsActive,
|
|
CreatedAt: user.CreatedAt,
|
|
UpdatedAt: user.UpdatedAt,
|
|
}
|
|
|
|
c.JSON(http.StatusOK, userResponse)
|
|
}
|
|
|
|
// GetUsers 获取用户列表 (管理员功能)
|
|
// @Summary 获取用户列表
|
|
// @Description 获取系统中所有用户列表
|
|
// @Tags admin
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param page query int false "页码"
|
|
// @Param limit query int false "每页数量"
|
|
// @Param search query string false "搜索关键词"
|
|
// @Success 200 {object} service.UserListResponse
|
|
// @Failure 403 {object} models.ErrorResponse
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
// @Router /admin/users [get]
|
|
func (h *UserHandler) GetUsers(c *gin.Context) {
|
|
var params service.UserListParams
|
|
|
|
// 解析查询参数
|
|
if err := c.ShouldBindQuery(¶ms); err != nil {
|
|
h.logger.Error("Failed to bind query params", zap.Error(err))
|
|
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
|
Error: "Invalid query parameters",
|
|
Message: err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
result, err := h.userService.GetUsers(c.Request.Context(), params)
|
|
if err != nil {
|
|
h.logger.Error("Failed to get users", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
|
Error: "Failed to get users",
|
|
Message: err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, result)
|
|
}
|
|
|
|
// GetUser 获取用户详情 (管理员功能)
|
|
// @Summary 获取用户详情
|
|
// @Description 根据ID获取用户详情
|
|
// @Tags admin
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param id path int true "用户ID"
|
|
// @Success 200 {object} models.UserResponse
|
|
// @Failure 400 {object} models.ErrorResponse
|
|
// @Failure 404 {object} models.ErrorResponse
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
// @Router /admin/users/{id} [get]
|
|
func (h *UserHandler) GetUser(c *gin.Context) {
|
|
idStr := c.Param("id")
|
|
id, err := strconv.ParseUint(idStr, 10, 32)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
|
Error: "Invalid user ID",
|
|
Message: "User ID must be a valid number",
|
|
})
|
|
return
|
|
}
|
|
|
|
user, err := h.userService.GetUserByID(c.Request.Context(), uint(id))
|
|
if err != nil {
|
|
if err.Error() == "user not found" {
|
|
c.JSON(http.StatusNotFound, models.ErrorResponse{
|
|
Error: "User not found",
|
|
Message: "The requested user does not exist",
|
|
})
|
|
return
|
|
}
|
|
|
|
h.logger.Error("Failed to get user", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
|
Error: "Failed to get user",
|
|
Message: err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
userResponse := &models.UserResponse{
|
|
ID: user.ID,
|
|
Username: user.Username,
|
|
Email: user.Email,
|
|
Role: user.Role,
|
|
IsActive: user.IsActive,
|
|
CreatedAt: user.CreatedAt,
|
|
UpdatedAt: user.UpdatedAt,
|
|
}
|
|
|
|
c.JSON(http.StatusOK, userResponse)
|
|
}
|
|
|
|
// CreateUser 创建用户 (管理员功能)
|
|
// @Summary 创建用户
|
|
// @Description 创建新用户
|
|
// @Tags admin
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param user body models.CreateUserRequest true "用户信息"
|
|
// @Success 201 {object} models.UserResponse
|
|
// @Failure 400 {object} models.ErrorResponse
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
// @Router /admin/users [post]
|
|
func (h *UserHandler) CreateUser(c *gin.Context) {
|
|
var req models.CreateUserRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
h.logger.Error("Failed to bind JSON", zap.Error(err))
|
|
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
|
Error: "Invalid request body",
|
|
Message: err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
// 验证请求数据
|
|
if err := h.validateCreateUserRequest(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
|
Error: "Invalid request data",
|
|
Message: err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
user, err := h.userService.CreateUser(c.Request.Context(), &req)
|
|
if err != nil {
|
|
h.logger.Error("Failed to create user", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
|
Error: "Failed to create user",
|
|
Message: err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
userResponse := &models.UserResponse{
|
|
ID: user.ID,
|
|
Username: user.Username,
|
|
Email: user.Email,
|
|
Role: user.Role,
|
|
IsActive: user.IsActive,
|
|
CreatedAt: user.CreatedAt,
|
|
UpdatedAt: user.UpdatedAt,
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, userResponse)
|
|
}
|
|
|
|
// UpdateUser 更新用户 (管理员功能)
|
|
// @Summary 更新用户
|
|
// @Description 更新用户信息
|
|
// @Tags admin
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param id path int true "用户ID"
|
|
// @Param user body models.UpdateUserRequest true "用户信息"
|
|
// @Success 200 {object} models.UserResponse
|
|
// @Failure 400 {object} models.ErrorResponse
|
|
// @Failure 404 {object} models.ErrorResponse
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
// @Router /admin/users/{id} [put]
|
|
func (h *UserHandler) UpdateUser(c *gin.Context) {
|
|
idStr := c.Param("id")
|
|
id, err := strconv.ParseUint(idStr, 10, 32)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
|
Error: "Invalid user ID",
|
|
Message: "User ID must be a valid number",
|
|
})
|
|
return
|
|
}
|
|
|
|
var req models.UpdateUserRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
h.logger.Error("Failed to bind JSON", zap.Error(err))
|
|
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
|
Error: "Invalid request body",
|
|
Message: err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
user, err := h.userService.UpdateUser(c.Request.Context(), uint(id), &req)
|
|
if err != nil {
|
|
if err.Error() == "user not found" {
|
|
c.JSON(http.StatusNotFound, models.ErrorResponse{
|
|
Error: "User not found",
|
|
Message: "The requested user does not exist",
|
|
})
|
|
return
|
|
}
|
|
|
|
h.logger.Error("Failed to update user", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
|
Error: "Failed to update user",
|
|
Message: err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
userResponse := &models.UserResponse{
|
|
ID: user.ID,
|
|
Username: user.Username,
|
|
Email: user.Email,
|
|
Role: user.Role,
|
|
IsActive: user.IsActive,
|
|
CreatedAt: user.CreatedAt,
|
|
UpdatedAt: user.UpdatedAt,
|
|
}
|
|
|
|
c.JSON(http.StatusOK, userResponse)
|
|
}
|
|
|
|
// DeleteUser 删除用户 (管理员功能)
|
|
// @Summary 删除用户
|
|
// @Description 删除用户
|
|
// @Tags admin
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param id path int true "用户ID"
|
|
// @Success 204 "No Content"
|
|
// @Failure 400 {object} models.ErrorResponse
|
|
// @Failure 404 {object} models.ErrorResponse
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
// @Router /admin/users/{id} [delete]
|
|
func (h *UserHandler) DeleteUser(c *gin.Context) {
|
|
idStr := c.Param("id")
|
|
id, err := strconv.ParseUint(idStr, 10, 32)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
|
Error: "Invalid user ID",
|
|
Message: "User ID must be a valid number",
|
|
})
|
|
return
|
|
}
|
|
|
|
// 防止删除自己
|
|
currentUserID := c.GetUint("user_id")
|
|
if uint(id) == currentUserID {
|
|
c.JSON(http.StatusBadRequest, models.ErrorResponse{
|
|
Error: "Cannot delete yourself",
|
|
Message: "You cannot delete your own account",
|
|
})
|
|
return
|
|
}
|
|
|
|
err = h.userService.DeleteUser(c.Request.Context(), uint(id))
|
|
if err != nil {
|
|
if err.Error() == "user not found" {
|
|
c.JSON(http.StatusNotFound, models.ErrorResponse{
|
|
Error: "User not found",
|
|
Message: "The requested user does not exist",
|
|
})
|
|
return
|
|
}
|
|
|
|
h.logger.Error("Failed to delete user", zap.Error(err))
|
|
c.JSON(http.StatusInternalServerError, models.ErrorResponse{
|
|
Error: "Failed to delete user",
|
|
Message: err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
|
|
// validateCreateUserRequest 验证创建用户请求
|
|
func (h *UserHandler) validateCreateUserRequest(req *models.CreateUserRequest) error {
|
|
if req.Username == "" {
|
|
return errors.New("username is required")
|
|
}
|
|
|
|
if req.Email == "" {
|
|
return errors.New("email is required")
|
|
}
|
|
|
|
if req.Password == "" {
|
|
return errors.New("password is required")
|
|
}
|
|
|
|
if req.Role == "" {
|
|
req.Role = "user"
|
|
}
|
|
|
|
// 验证角色
|
|
validRoles := []string{"user", "editor", "admin"}
|
|
isValidRole := false
|
|
for _, role := range validRoles {
|
|
if req.Role == role {
|
|
isValidRole = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !isValidRole {
|
|
return errors.New("invalid role value")
|
|
}
|
|
|
|
return nil
|
|
} |