temp
This commit is contained in:
178
internal/domains/admin/dto/admin_dto.go
Normal file
178
internal/domains/admin/dto/admin_dto.go
Normal file
@@ -0,0 +1,178 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"tyapi-server/internal/domains/admin/entities"
|
||||
)
|
||||
|
||||
// AdminLoginRequest 管理员登录请求
|
||||
type AdminLoginRequest struct {
|
||||
Username string `json:"username" binding:"required"` // 用户名
|
||||
Password string `json:"password" binding:"required"` // 密码
|
||||
}
|
||||
|
||||
// AdminLoginResponse 管理员登录响应
|
||||
type AdminLoginResponse struct {
|
||||
Token string `json:"token"` // JWT令牌
|
||||
ExpiresAt time.Time `json:"expires_at"` // 过期时间
|
||||
Admin AdminInfo `json:"admin"` // 管理员信息
|
||||
}
|
||||
|
||||
// AdminInfo 管理员信息
|
||||
type AdminInfo struct {
|
||||
ID string `json:"id"` // 管理员ID
|
||||
Username string `json:"username"` // 用户名
|
||||
Email string `json:"email"` // 邮箱
|
||||
Phone string `json:"phone"` // 手机号
|
||||
RealName string `json:"real_name"` // 真实姓名
|
||||
Role entities.AdminRole `json:"role"` // 角色
|
||||
IsActive bool `json:"is_active"` // 是否激活
|
||||
LastLoginAt *time.Time `json:"last_login_at"` // 最后登录时间
|
||||
LoginCount int `json:"login_count"` // 登录次数
|
||||
Permissions []string `json:"permissions"` // 权限列表
|
||||
CreatedAt time.Time `json:"created_at"` // 创建时间
|
||||
}
|
||||
|
||||
// AdminCreateRequest 创建管理员请求
|
||||
type AdminCreateRequest struct {
|
||||
Username string `json:"username" binding:"required"` // 用户名
|
||||
Password string `json:"password" binding:"required"` // 密码
|
||||
Email string `json:"email" binding:"required,email"` // 邮箱
|
||||
Phone string `json:"phone"` // 手机号
|
||||
RealName string `json:"real_name" binding:"required"` // 真实姓名
|
||||
Role entities.AdminRole `json:"role" binding:"required"` // 角色
|
||||
Permissions []string `json:"permissions"` // 权限列表
|
||||
}
|
||||
|
||||
// AdminUpdateRequest 更新管理员请求
|
||||
type AdminUpdateRequest struct {
|
||||
Email string `json:"email" binding:"email"` // 邮箱
|
||||
Phone string `json:"phone"` // 手机号
|
||||
RealName string `json:"real_name"` // 真实姓名
|
||||
Role entities.AdminRole `json:"role"` // 角色
|
||||
IsActive *bool `json:"is_active"` // 是否激活
|
||||
Permissions []string `json:"permissions"` // 权限列表
|
||||
}
|
||||
|
||||
// AdminPasswordChangeRequest 修改密码请求
|
||||
type AdminPasswordChangeRequest struct {
|
||||
OldPassword string `json:"old_password" binding:"required"` // 旧密码
|
||||
NewPassword string `json:"new_password" binding:"required"` // 新密码
|
||||
}
|
||||
|
||||
// AdminListRequest 管理员列表请求
|
||||
type AdminListRequest struct {
|
||||
Page int `form:"page" binding:"min=1"` // 页码
|
||||
PageSize int `form:"page_size" binding:"min=1,max=100"` // 每页数量
|
||||
Username string `form:"username"` // 用户名搜索
|
||||
Email string `form:"email"` // 邮箱搜索
|
||||
Role string `form:"role"` // 角色筛选
|
||||
IsActive *bool `form:"is_active"` // 状态筛选
|
||||
}
|
||||
|
||||
// AdminListResponse 管理员列表响应
|
||||
type AdminListResponse struct {
|
||||
Total int64 `json:"total"` // 总数
|
||||
Page int `json:"page"` // 当前页
|
||||
Size int `json:"size"` // 每页数量
|
||||
Admins []AdminInfo `json:"admins"` // 管理员列表
|
||||
}
|
||||
|
||||
// AdminStatsResponse 管理员统计响应
|
||||
type AdminStatsResponse struct {
|
||||
TotalAdmins int64 `json:"total_admins"` // 总管理员数
|
||||
ActiveAdmins int64 `json:"active_admins"` // 激活管理员数
|
||||
TodayLogins int64 `json:"today_logins"` // 今日登录数
|
||||
TotalOperations int64 `json:"total_operations"` // 总操作数
|
||||
}
|
||||
|
||||
// AdminOperationLogRequest 操作日志请求
|
||||
type AdminOperationLogRequest struct {
|
||||
Page int `form:"page" binding:"min=1"` // 页码
|
||||
PageSize int `form:"page_size" binding:"min=1,max=100"` // 每页数量
|
||||
AdminID string `form:"admin_id"` // 管理员ID
|
||||
Action string `form:"action"` // 操作类型
|
||||
Resource string `form:"resource"` // 操作资源
|
||||
Status string `form:"status"` // 操作状态
|
||||
StartTime time.Time `form:"start_time"` // 开始时间
|
||||
EndTime time.Time `form:"end_time"` // 结束时间
|
||||
}
|
||||
|
||||
// AdminOperationLogResponse 操作日志响应
|
||||
type AdminOperationLogResponse struct {
|
||||
Total int64 `json:"total"` // 总数
|
||||
Page int `json:"page"` // 当前页
|
||||
Size int `json:"size"` // 每页数量
|
||||
Logs []AdminOperationLogInfo `json:"logs"` // 日志列表
|
||||
}
|
||||
|
||||
// AdminOperationLogInfo 操作日志信息
|
||||
type AdminOperationLogInfo struct {
|
||||
ID string `json:"id"` // 日志ID
|
||||
AdminID string `json:"admin_id"` // 管理员ID
|
||||
Username string `json:"username"` // 用户名
|
||||
Action string `json:"action"` // 操作类型
|
||||
Resource string `json:"resource"` // 操作资源
|
||||
ResourceID string `json:"resource_id"` // 资源ID
|
||||
Details string `json:"details"` // 操作详情
|
||||
IP string `json:"ip"` // IP地址
|
||||
UserAgent string `json:"user_agent"` // 用户代理
|
||||
Status string `json:"status"` // 操作状态
|
||||
Message string `json:"message"` // 操作消息
|
||||
CreatedAt time.Time `json:"created_at"` // 创建时间
|
||||
}
|
||||
|
||||
// AdminLoginLogRequest 登录日志请求
|
||||
type AdminLoginLogRequest struct {
|
||||
Page int `form:"page" binding:"min=1"` // 页码
|
||||
PageSize int `form:"page_size" binding:"min=1,max=100"` // 每页数量
|
||||
AdminID string `form:"admin_id"` // 管理员ID
|
||||
Username string `form:"username"` // 用户名
|
||||
Status string `form:"status"` // 登录状态
|
||||
StartTime time.Time `form:"start_time"` // 开始时间
|
||||
EndTime time.Time `form:"end_time"` // 结束时间
|
||||
}
|
||||
|
||||
// AdminLoginLogResponse 登录日志响应
|
||||
type AdminLoginLogResponse struct {
|
||||
Total int64 `json:"total"` // 总数
|
||||
Page int `json:"page"` // 当前页
|
||||
Size int `json:"size"` // 每页数量
|
||||
Logs []AdminLoginLogInfo `json:"logs"` // 日志列表
|
||||
}
|
||||
|
||||
// AdminLoginLogInfo 登录日志信息
|
||||
type AdminLoginLogInfo struct {
|
||||
ID string `json:"id"` // 日志ID
|
||||
AdminID string `json:"admin_id"` // 管理员ID
|
||||
Username string `json:"username"` // 用户名
|
||||
IP string `json:"ip"` // IP地址
|
||||
UserAgent string `json:"user_agent"` // 用户代理
|
||||
Status string `json:"status"` // 登录状态
|
||||
Message string `json:"message"` // 登录消息
|
||||
CreatedAt time.Time `json:"created_at"` // 创建时间
|
||||
}
|
||||
|
||||
// PermissionInfo 权限信息
|
||||
type PermissionInfo struct {
|
||||
ID string `json:"id"` // 权限ID
|
||||
Name string `json:"name"` // 权限名称
|
||||
Code string `json:"code"` // 权限代码
|
||||
Description string `json:"description"` // 权限描述
|
||||
Module string `json:"module"` // 所属模块
|
||||
IsActive bool `json:"is_active"` // 是否激活
|
||||
CreatedAt time.Time `json:"created_at"` // 创建时间
|
||||
}
|
||||
|
||||
// RolePermissionRequest 角色权限请求
|
||||
type RolePermissionRequest struct {
|
||||
Role entities.AdminRole `json:"role" binding:"required"` // 角色
|
||||
PermissionIDs []string `json:"permission_ids" binding:"required"` // 权限ID列表
|
||||
}
|
||||
|
||||
// RolePermissionResponse 角色权限响应
|
||||
type RolePermissionResponse struct {
|
||||
Role entities.AdminRole `json:"role"` // 角色
|
||||
Permissions []PermissionInfo `json:"permissions"` // 权限列表
|
||||
}
|
||||
147
internal/domains/admin/entities/admin.go
Normal file
147
internal/domains/admin/entities/admin.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AdminRole 管理员角色枚举
|
||||
// 定义系统中不同级别的管理员角色,用于权限控制和功能分配
|
||||
type AdminRole string
|
||||
|
||||
const (
|
||||
RoleSuperAdmin AdminRole = "super_admin" // 超级管理员 - 拥有所有权限
|
||||
RoleAdmin AdminRole = "admin" // 普通管理员 - 拥有大部分管理权限
|
||||
RoleReviewer AdminRole = "reviewer" // 审核员 - 仅拥有审核相关权限
|
||||
)
|
||||
|
||||
// Admin 管理员实体
|
||||
// 系统管理员的核心信息,包括账户信息、权限配置、操作统计等
|
||||
// 支持多角色管理,提供完整的权限控制和操作审计功能
|
||||
type Admin struct {
|
||||
// 基础标识
|
||||
ID string `gorm:"primaryKey;type:varchar(36)" comment:"管理员唯一标识"`
|
||||
Username string `gorm:"type:varchar(100);not null;uniqueIndex" comment:"登录用户名"`
|
||||
Password string `gorm:"type:varchar(255);not null" comment:"登录密码(加密存储)"`
|
||||
Email string `gorm:"type:varchar(255);not null;uniqueIndex" comment:"邮箱地址"`
|
||||
Phone string `gorm:"type:varchar(20)" comment:"手机号码"`
|
||||
RealName string `gorm:"type:varchar(100);not null" comment:"真实姓名"`
|
||||
Role AdminRole `gorm:"type:varchar(50);not null;default:'reviewer'" comment:"管理员角色"`
|
||||
|
||||
// 状态信息 - 账户状态和登录统计
|
||||
IsActive bool `gorm:"default:true" comment:"账户是否激活"`
|
||||
LastLoginAt *time.Time `comment:"最后登录时间"`
|
||||
LoginCount int `gorm:"default:0" comment:"登录次数统计"`
|
||||
|
||||
// 权限信息 - 细粒度权限控制
|
||||
Permissions string `gorm:"type:text" comment:"权限列表(JSON格式存储)"`
|
||||
|
||||
// 审核统计 - 管理员的工作绩效统计
|
||||
ReviewCount int `gorm:"default:0" comment:"审核总数"`
|
||||
ApprovedCount int `gorm:"default:0" comment:"通过数量"`
|
||||
RejectedCount int `gorm:"default:0" comment:"拒绝数量"`
|
||||
|
||||
// 时间戳字段
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" comment:"创建时间"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" comment:"更新时间"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" comment:"软删除时间"`
|
||||
}
|
||||
|
||||
// AdminLoginLog 管理员登录日志实体
|
||||
// 记录管理员的所有登录尝试,包括成功和失败的登录记录
|
||||
// 用于安全审计和异常登录检测
|
||||
type AdminLoginLog struct {
|
||||
// 基础标识
|
||||
ID string `gorm:"primaryKey;type:varchar(36)" comment:"日志记录唯一标识"`
|
||||
AdminID string `gorm:"type:varchar(36);not null;index" comment:"管理员ID"`
|
||||
Username string `gorm:"type:varchar(100);not null" comment:"登录用户名"`
|
||||
IP string `gorm:"type:varchar(45);not null" comment:"登录IP地址"`
|
||||
UserAgent string `gorm:"type:varchar(500)" comment:"客户端信息"`
|
||||
Status string `gorm:"type:varchar(20);not null" comment:"登录状态(success/failed)"`
|
||||
Message string `gorm:"type:varchar(500)" comment:"登录结果消息"`
|
||||
|
||||
// 时间戳字段
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" comment:"创建时间"`
|
||||
}
|
||||
|
||||
// AdminOperationLog 管理员操作日志实体
|
||||
// 记录管理员在系统中的所有重要操作,用于操作审计和问题追踪
|
||||
// 支持操作类型、资源、详情等完整信息的记录
|
||||
type AdminOperationLog struct {
|
||||
// 基础标识
|
||||
ID string `gorm:"primaryKey;type:varchar(36)" comment:"操作日志唯一标识"`
|
||||
AdminID string `gorm:"type:varchar(36);not null;index" comment:"操作管理员ID"`
|
||||
Username string `gorm:"type:varchar(100);not null" comment:"操作管理员用户名"`
|
||||
Action string `gorm:"type:varchar(100);not null" comment:"操作类型"`
|
||||
Resource string `gorm:"type:varchar(100);not null" comment:"操作资源"`
|
||||
ResourceID string `gorm:"type:varchar(36)" comment:"资源ID"`
|
||||
Details string `gorm:"type:text" comment:"操作详情(JSON格式)"`
|
||||
IP string `gorm:"type:varchar(45);not null" comment:"操作IP地址"`
|
||||
UserAgent string `gorm:"type:varchar(500)" comment:"客户端信息"`
|
||||
Status string `gorm:"type:varchar(20);not null" comment:"操作状态(success/failed)"`
|
||||
Message string `gorm:"type:varchar(500)" comment:"操作结果消息"`
|
||||
|
||||
// 时间戳字段
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" comment:"创建时间"`
|
||||
}
|
||||
|
||||
// AdminPermission 管理员权限实体
|
||||
// 定义系统中的所有权限项,支持模块化权限管理
|
||||
// 每个权限都有唯一的代码标识,便于程序中的权限检查
|
||||
type AdminPermission struct {
|
||||
// 基础标识
|
||||
ID string `gorm:"primaryKey;type:varchar(36)" comment:"权限唯一标识"`
|
||||
Name string `gorm:"type:varchar(100);not null;uniqueIndex" comment:"权限名称"`
|
||||
Code string `gorm:"type:varchar(100);not null;uniqueIndex" comment:"权限代码"`
|
||||
Description string `gorm:"type:varchar(500)" comment:"权限描述"`
|
||||
Module string `gorm:"type:varchar(50);not null" comment:"所属模块"`
|
||||
IsActive bool `gorm:"default:true" comment:"权限是否启用"`
|
||||
|
||||
// 时间戳字段
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" comment:"创建时间"`
|
||||
UpdatedAt time.Time `gorm:"autoUpdateTime" comment:"更新时间"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" comment:"软删除时间"`
|
||||
}
|
||||
|
||||
// AdminRolePermission 角色权限关联实体
|
||||
// 建立角色和权限之间的多对多关系,实现基于角色的权限控制(RBAC)
|
||||
type AdminRolePermission struct {
|
||||
// 基础标识
|
||||
ID string `gorm:"primaryKey;type:varchar(36)" comment:"关联记录唯一标识"`
|
||||
Role AdminRole `gorm:"type:varchar(50);not null;index" comment:"角色"`
|
||||
PermissionID string `gorm:"type:varchar(36);not null;index" comment:"权限ID"`
|
||||
|
||||
// 时间戳字段
|
||||
CreatedAt time.Time `gorm:"autoCreateTime" comment:"创建时间"`
|
||||
}
|
||||
|
||||
// TableName 指定数据库表名
|
||||
func (Admin) TableName() string {
|
||||
return "admins"
|
||||
}
|
||||
|
||||
// IsValid 检查管理员账户是否有效
|
||||
// 判断管理员账户是否处于可用状态,包括激活状态和软删除状态检查
|
||||
func (a *Admin) IsValid() bool {
|
||||
return a.IsActive && a.DeletedAt.Time.IsZero()
|
||||
}
|
||||
|
||||
// UpdateLastLoginAt 更新最后登录时间
|
||||
// 在管理员成功登录后调用,记录最新的登录时间
|
||||
func (a *Admin) UpdateLastLoginAt() {
|
||||
now := time.Now()
|
||||
a.LastLoginAt = &now
|
||||
}
|
||||
|
||||
// Deactivate 停用管理员账户
|
||||
// 将管理员账户设置为非激活状态,禁止登录和操作
|
||||
func (a *Admin) Deactivate() {
|
||||
a.IsActive = false
|
||||
}
|
||||
|
||||
// Activate 激活管理员账户
|
||||
// 重新启用管理员账户,允许正常登录和操作
|
||||
func (a *Admin) Activate() {
|
||||
a.IsActive = true
|
||||
}
|
||||
313
internal/domains/admin/handlers/admin_handler.go
Normal file
313
internal/domains/admin/handlers/admin_handler.go
Normal file
@@ -0,0 +1,313 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"tyapi-server/internal/domains/admin/dto"
|
||||
"tyapi-server/internal/domains/admin/services"
|
||||
"tyapi-server/internal/shared/interfaces"
|
||||
)
|
||||
|
||||
// AdminHandler 管理员HTTP处理器
|
||||
type AdminHandler struct {
|
||||
adminService *services.AdminService
|
||||
responseBuilder interfaces.ResponseBuilder
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewAdminHandler 创建管理员HTTP处理器
|
||||
func NewAdminHandler(
|
||||
adminService *services.AdminService,
|
||||
responseBuilder interfaces.ResponseBuilder,
|
||||
logger *zap.Logger,
|
||||
) *AdminHandler {
|
||||
return &AdminHandler{
|
||||
adminService: adminService,
|
||||
responseBuilder: responseBuilder,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Login 管理员登录
|
||||
// @Summary 管理员登录
|
||||
// @Description 管理员登录接口
|
||||
// @Tags 管理员认证
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body dto.AdminLoginRequest true "登录请求"
|
||||
// @Success 200 {object} dto.AdminLoginResponse
|
||||
// @Failure 400 {object} interfaces.ErrorResponse
|
||||
// @Failure 401 {object} interfaces.ErrorResponse
|
||||
// @Router /admin/login [post]
|
||||
func (h *AdminHandler) Login(c *gin.Context) {
|
||||
var req dto.AdminLoginRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.Warn("管理员登录参数验证失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, "请求参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取客户端信息
|
||||
clientIP := c.ClientIP()
|
||||
userAgent := c.GetHeader("User-Agent")
|
||||
|
||||
// 调用服务
|
||||
response, err := h.adminService.Login(c.Request.Context(), &req, clientIP, userAgent)
|
||||
if err != nil {
|
||||
h.logger.Error("管理员登录失败", zap.Error(err))
|
||||
h.responseBuilder.Unauthorized(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Success(c, response, "登录成功")
|
||||
}
|
||||
|
||||
// CreateAdmin 创建管理员
|
||||
// @Summary 创建管理员
|
||||
// @Description 创建新管理员账户
|
||||
// @Tags 管理员管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body dto.AdminCreateRequest true "创建管理员请求"
|
||||
// @Success 201 {object} interfaces.SuccessResponse
|
||||
// @Failure 400 {object} interfaces.ErrorResponse
|
||||
// @Failure 403 {object} interfaces.ErrorResponse
|
||||
// @Router /admin [post]
|
||||
func (h *AdminHandler) CreateAdmin(c *gin.Context) {
|
||||
var req dto.AdminCreateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.Warn("创建管理员参数验证失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, "请求参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取当前操作的管理员ID(从JWT中解析)
|
||||
operatorID := h.getCurrentAdminID(c)
|
||||
|
||||
// 调用服务
|
||||
err := h.adminService.CreateAdmin(c.Request.Context(), &req, operatorID)
|
||||
if err != nil {
|
||||
h.logger.Error("创建管理员失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Created(c, nil, "管理员创建成功")
|
||||
}
|
||||
|
||||
// UpdateAdmin 更新管理员
|
||||
// @Summary 更新管理员
|
||||
// @Description 更新管理员信息
|
||||
// @Tags 管理员管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "管理员ID"
|
||||
// @Param request body dto.AdminUpdateRequest true "更新管理员请求"
|
||||
// @Success 200 {object} interfaces.SuccessResponse
|
||||
// @Failure 400 {object} interfaces.ErrorResponse
|
||||
// @Failure 404 {object} interfaces.ErrorResponse
|
||||
// @Router /admin/{id} [put]
|
||||
func (h *AdminHandler) UpdateAdmin(c *gin.Context) {
|
||||
adminID := c.Param("id")
|
||||
if adminID == "" {
|
||||
h.responseBuilder.BadRequest(c, "管理员ID不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
var req dto.AdminUpdateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.Warn("更新管理员参数验证失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, "请求参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取当前操作的管理员ID
|
||||
operatorID := h.getCurrentAdminID(c)
|
||||
|
||||
// 调用服务
|
||||
err := h.adminService.UpdateAdmin(c.Request.Context(), adminID, &req, operatorID)
|
||||
if err != nil {
|
||||
h.logger.Error("更新管理员失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Success(c, nil, "管理员更新成功")
|
||||
}
|
||||
|
||||
// ChangePassword 修改密码
|
||||
// @Summary 修改密码
|
||||
// @Description 管理员修改自己的密码
|
||||
// @Tags 管理员管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body dto.AdminPasswordChangeRequest true "修改密码请求"
|
||||
// @Success 200 {object} interfaces.SuccessResponse
|
||||
// @Failure 400 {object} interfaces.ErrorResponse
|
||||
// @Router /admin/change-password [post]
|
||||
func (h *AdminHandler) ChangePassword(c *gin.Context) {
|
||||
var req dto.AdminPasswordChangeRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.Warn("修改密码参数验证失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, "请求参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取当前管理员ID
|
||||
adminID := h.getCurrentAdminID(c)
|
||||
|
||||
// 调用服务
|
||||
err := h.adminService.ChangePassword(c.Request.Context(), adminID, &req)
|
||||
if err != nil {
|
||||
h.logger.Error("修改密码失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Success(c, nil, "密码修改成功")
|
||||
}
|
||||
|
||||
// ListAdmins 获取管理员列表
|
||||
// @Summary 获取管理员列表
|
||||
// @Description 分页获取管理员列表
|
||||
// @Tags 管理员管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param page query int false "页码" default(1)
|
||||
// @Param page_size query int false "每页数量" default(10)
|
||||
// @Param username query string false "用户名搜索"
|
||||
// @Param email query string false "邮箱搜索"
|
||||
// @Param role query string false "角色筛选"
|
||||
// @Param is_active query bool false "状态筛选"
|
||||
// @Success 200 {object} dto.AdminListResponse
|
||||
// @Failure 400 {object} interfaces.ErrorResponse
|
||||
// @Router /admin [get]
|
||||
func (h *AdminHandler) ListAdmins(c *gin.Context) {
|
||||
var req dto.AdminListRequest
|
||||
|
||||
// 解析查询参数
|
||||
if page, err := strconv.Atoi(c.DefaultQuery("page", "1")); err == nil {
|
||||
req.Page = page
|
||||
} else {
|
||||
req.Page = 1
|
||||
}
|
||||
|
||||
if pageSize, err := strconv.Atoi(c.DefaultQuery("page_size", "10")); err == nil {
|
||||
req.PageSize = pageSize
|
||||
} else {
|
||||
req.PageSize = 10
|
||||
}
|
||||
|
||||
req.Username = c.Query("username")
|
||||
req.Email = c.Query("email")
|
||||
req.Role = c.Query("role")
|
||||
|
||||
if isActiveStr := c.Query("is_active"); isActiveStr != "" {
|
||||
if isActive, err := strconv.ParseBool(isActiveStr); err == nil {
|
||||
req.IsActive = &isActive
|
||||
}
|
||||
}
|
||||
|
||||
// 调用服务
|
||||
response, err := h.adminService.ListAdmins(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
h.logger.Error("获取管理员列表失败", zap.Error(err))
|
||||
h.responseBuilder.InternalError(c, "获取管理员列表失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Success(c, response, "获取管理员列表成功")
|
||||
}
|
||||
|
||||
// GetAdminByID 根据ID获取管理员
|
||||
// @Summary 获取管理员详情
|
||||
// @Description 根据ID获取管理员详细信息
|
||||
// @Tags 管理员管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "管理员ID"
|
||||
// @Success 200 {object} dto.AdminInfo
|
||||
// @Failure 400 {object} interfaces.ErrorResponse
|
||||
// @Failure 404 {object} interfaces.ErrorResponse
|
||||
// @Router /admin/{id} [get]
|
||||
func (h *AdminHandler) GetAdminByID(c *gin.Context) {
|
||||
adminID := c.Param("id")
|
||||
if adminID == "" {
|
||||
h.responseBuilder.BadRequest(c, "管理员ID不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
// 调用服务
|
||||
admin, err := h.adminService.GetAdminByID(c.Request.Context(), adminID)
|
||||
if err != nil {
|
||||
h.logger.Error("获取管理员详情失败", zap.Error(err))
|
||||
h.responseBuilder.NotFound(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Success(c, admin, "获取管理员详情成功")
|
||||
}
|
||||
|
||||
// DeleteAdmin 删除管理员
|
||||
// @Summary 删除管理员
|
||||
// @Description 软删除管理员账户
|
||||
// @Tags 管理员管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param id path string true "管理员ID"
|
||||
// @Success 200 {object} interfaces.SuccessResponse
|
||||
// @Failure 400 {object} interfaces.ErrorResponse
|
||||
// @Failure 404 {object} interfaces.ErrorResponse
|
||||
// @Router /admin/{id} [delete]
|
||||
func (h *AdminHandler) DeleteAdmin(c *gin.Context) {
|
||||
adminID := c.Param("id")
|
||||
if adminID == "" {
|
||||
h.responseBuilder.BadRequest(c, "管理员ID不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取当前操作的管理员ID
|
||||
operatorID := h.getCurrentAdminID(c)
|
||||
|
||||
// 调用服务
|
||||
err := h.adminService.DeleteAdmin(c.Request.Context(), adminID, operatorID)
|
||||
if err != nil {
|
||||
h.logger.Error("删除管理员失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Success(c, nil, "管理员删除成功")
|
||||
}
|
||||
|
||||
// GetAdminStats 获取管理员统计信息
|
||||
// @Summary 获取管理员统计
|
||||
// @Description 获取管理员相关的统计信息
|
||||
// @Tags 管理员管理
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} dto.AdminStatsResponse
|
||||
// @Failure 400 {object} interfaces.ErrorResponse
|
||||
// @Router /admin/stats [get]
|
||||
func (h *AdminHandler) GetAdminStats(c *gin.Context) {
|
||||
// 调用服务
|
||||
stats, err := h.adminService.GetAdminStats(c.Request.Context())
|
||||
if err != nil {
|
||||
h.logger.Error("获取管理员统计失败", zap.Error(err))
|
||||
h.responseBuilder.InternalError(c, "获取统计信息失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Success(c, stats, "获取统计信息成功")
|
||||
}
|
||||
|
||||
// getCurrentAdminID 获取当前管理员ID
|
||||
func (h *AdminHandler) getCurrentAdminID(c *gin.Context) string {
|
||||
// 这里应该从JWT令牌中解析出管理员ID
|
||||
// 为了简化,这里返回一个模拟的ID
|
||||
// 实际实现中应该从中间件中获取
|
||||
return "current_admin_id"
|
||||
}
|
||||
72
internal/domains/admin/repositories/admin_repository.go
Normal file
72
internal/domains/admin/repositories/admin_repository.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"tyapi-server/internal/domains/admin/dto"
|
||||
"tyapi-server/internal/domains/admin/entities"
|
||||
"tyapi-server/internal/shared/interfaces"
|
||||
)
|
||||
|
||||
// AdminRepository 管理员仓储接口
|
||||
type AdminRepository interface {
|
||||
interfaces.Repository[entities.Admin]
|
||||
|
||||
// 管理员认证
|
||||
FindByUsername(ctx context.Context, username string) (*entities.Admin, error)
|
||||
FindByEmail(ctx context.Context, email string) (*entities.Admin, error)
|
||||
|
||||
// 管理员管理
|
||||
ListAdmins(ctx context.Context, req *dto.AdminListRequest) (*dto.AdminListResponse, error)
|
||||
GetStats(ctx context.Context) (*dto.AdminStatsResponse, error)
|
||||
|
||||
// 权限管理
|
||||
GetPermissionsByRole(ctx context.Context, role entities.AdminRole) ([]entities.AdminPermission, error)
|
||||
UpdatePermissions(ctx context.Context, adminID string, permissions []string) error
|
||||
|
||||
// 统计信息
|
||||
UpdateLoginStats(ctx context.Context, adminID string) error
|
||||
UpdateReviewStats(ctx context.Context, adminID string, approved bool) error
|
||||
}
|
||||
|
||||
// AdminLoginLogRepository 管理员登录日志仓储接口
|
||||
type AdminLoginLogRepository interface {
|
||||
interfaces.Repository[entities.AdminLoginLog]
|
||||
|
||||
// 日志查询
|
||||
ListLogs(ctx context.Context, req *dto.AdminLoginLogRequest) (*dto.AdminLoginLogResponse, error)
|
||||
|
||||
// 统计查询
|
||||
GetTodayLoginCount(ctx context.Context) (int64, error)
|
||||
GetLoginCountByAdmin(ctx context.Context, adminID string, days int) (int64, error)
|
||||
}
|
||||
|
||||
// AdminOperationLogRepository 管理员操作日志仓储接口
|
||||
type AdminOperationLogRepository interface {
|
||||
interfaces.Repository[entities.AdminOperationLog]
|
||||
|
||||
// 日志查询
|
||||
ListLogs(ctx context.Context, req *dto.AdminOperationLogRequest) (*dto.AdminOperationLogResponse, error)
|
||||
|
||||
// 统计查询
|
||||
GetTotalOperations(ctx context.Context) (int64, error)
|
||||
GetOperationsByAdmin(ctx context.Context, adminID string, days int) (int64, error)
|
||||
|
||||
// 批量操作
|
||||
BatchCreate(ctx context.Context, logs []entities.AdminOperationLog) error
|
||||
}
|
||||
|
||||
// AdminPermissionRepository 管理员权限仓储接口
|
||||
type AdminPermissionRepository interface {
|
||||
interfaces.Repository[entities.AdminPermission]
|
||||
|
||||
// 权限查询
|
||||
FindByCode(ctx context.Context, code string) (*entities.AdminPermission, error)
|
||||
FindByModule(ctx context.Context, module string) ([]entities.AdminPermission, error)
|
||||
ListActive(ctx context.Context) ([]entities.AdminPermission, error)
|
||||
|
||||
// 角色权限管理
|
||||
GetPermissionsByRole(ctx context.Context, role entities.AdminRole) ([]entities.AdminPermission, error)
|
||||
AssignPermissionsToRole(ctx context.Context, role entities.AdminRole, permissionIDs []string) error
|
||||
RemovePermissionsFromRole(ctx context.Context, role entities.AdminRole, permissionIDs []string) error
|
||||
}
|
||||
341
internal/domains/admin/repositories/gorm_admin_repository.go
Normal file
341
internal/domains/admin/repositories/gorm_admin_repository.go
Normal file
@@ -0,0 +1,341 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"tyapi-server/internal/domains/admin/dto"
|
||||
"tyapi-server/internal/domains/admin/entities"
|
||||
"tyapi-server/internal/shared/interfaces"
|
||||
)
|
||||
|
||||
// GormAdminRepository 管理员GORM仓储实现
|
||||
type GormAdminRepository struct {
|
||||
db *gorm.DB
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewGormAdminRepository 创建管理员GORM仓储
|
||||
func NewGormAdminRepository(db *gorm.DB, logger *zap.Logger) *GormAdminRepository {
|
||||
return &GormAdminRepository{
|
||||
db: db,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Create 创建管理员
|
||||
func (r *GormAdminRepository) Create(ctx context.Context, admin entities.Admin) error {
|
||||
r.logger.Info("创建管理员", zap.String("username", admin.Username))
|
||||
return r.db.WithContext(ctx).Create(&admin).Error
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取管理员
|
||||
func (r *GormAdminRepository) GetByID(ctx context.Context, id string) (entities.Admin, error) {
|
||||
var admin entities.Admin
|
||||
err := r.db.WithContext(ctx).Where("id = ?", id).First(&admin).Error
|
||||
return admin, err
|
||||
}
|
||||
|
||||
// Update 更新管理员
|
||||
func (r *GormAdminRepository) Update(ctx context.Context, admin entities.Admin) error {
|
||||
r.logger.Info("更新管理员", zap.String("id", admin.ID))
|
||||
return r.db.WithContext(ctx).Save(&admin).Error
|
||||
}
|
||||
|
||||
// Delete 删除管理员
|
||||
func (r *GormAdminRepository) Delete(ctx context.Context, id string) error {
|
||||
r.logger.Info("删除管理员", zap.String("id", id))
|
||||
return r.db.WithContext(ctx).Delete(&entities.Admin{}, "id = ?", id).Error
|
||||
}
|
||||
|
||||
// SoftDelete 软删除管理员
|
||||
func (r *GormAdminRepository) SoftDelete(ctx context.Context, id string) error {
|
||||
r.logger.Info("软删除管理员", zap.String("id", id))
|
||||
return r.db.WithContext(ctx).Delete(&entities.Admin{}, "id = ?", id).Error
|
||||
}
|
||||
|
||||
// Restore 恢复管理员
|
||||
func (r *GormAdminRepository) Restore(ctx context.Context, id string) error {
|
||||
r.logger.Info("恢复管理员", zap.String("id", id))
|
||||
return r.db.WithContext(ctx).Unscoped().Model(&entities.Admin{}).Where("id = ?", id).Update("deleted_at", nil).Error
|
||||
}
|
||||
|
||||
// Count 统计管理员数量
|
||||
func (r *GormAdminRepository) Count(ctx context.Context, options interfaces.CountOptions) (int64, error) {
|
||||
var count int64
|
||||
query := r.db.WithContext(ctx).Model(&entities.Admin{})
|
||||
|
||||
// 应用过滤条件
|
||||
if options.Filters != nil {
|
||||
for key, value := range options.Filters {
|
||||
query = query.Where(key+" = ?", value)
|
||||
}
|
||||
}
|
||||
|
||||
// 应用搜索条件
|
||||
if options.Search != "" {
|
||||
query = query.Where("username LIKE ? OR email LIKE ? OR real_name LIKE ?",
|
||||
"%"+options.Search+"%", "%"+options.Search+"%", "%"+options.Search+"%")
|
||||
}
|
||||
|
||||
return count, query.Count(&count).Error
|
||||
}
|
||||
|
||||
// Exists 检查管理员是否存在
|
||||
func (r *GormAdminRepository) Exists(ctx context.Context, id string) (bool, error) {
|
||||
var count int64
|
||||
err := r.db.WithContext(ctx).Model(&entities.Admin{}).Where("id = ?", id).Count(&count).Error
|
||||
return count > 0, err
|
||||
}
|
||||
|
||||
// CreateBatch 批量创建管理员
|
||||
func (r *GormAdminRepository) CreateBatch(ctx context.Context, admins []entities.Admin) error {
|
||||
r.logger.Info("批量创建管理员", zap.Int("count", len(admins)))
|
||||
return r.db.WithContext(ctx).Create(&admins).Error
|
||||
}
|
||||
|
||||
// GetByIDs 根据ID列表获取管理员
|
||||
func (r *GormAdminRepository) GetByIDs(ctx context.Context, ids []string) ([]entities.Admin, error) {
|
||||
var admins []entities.Admin
|
||||
err := r.db.WithContext(ctx).Where("id IN ?", ids).Find(&admins).Error
|
||||
return admins, err
|
||||
}
|
||||
|
||||
// UpdateBatch 批量更新管理员
|
||||
func (r *GormAdminRepository) UpdateBatch(ctx context.Context, admins []entities.Admin) error {
|
||||
r.logger.Info("批量更新管理员", zap.Int("count", len(admins)))
|
||||
return r.db.WithContext(ctx).Save(&admins).Error
|
||||
}
|
||||
|
||||
// DeleteBatch 批量删除管理员
|
||||
func (r *GormAdminRepository) DeleteBatch(ctx context.Context, ids []string) error {
|
||||
r.logger.Info("批量删除管理员", zap.Strings("ids", ids))
|
||||
return r.db.WithContext(ctx).Delete(&entities.Admin{}, "id IN ?", ids).Error
|
||||
}
|
||||
|
||||
// List 获取管理员列表
|
||||
func (r *GormAdminRepository) List(ctx context.Context, options interfaces.ListOptions) ([]entities.Admin, error) {
|
||||
var admins []entities.Admin
|
||||
query := r.db.WithContext(ctx).Model(&entities.Admin{})
|
||||
|
||||
// 应用过滤条件
|
||||
if options.Filters != nil {
|
||||
for key, value := range options.Filters {
|
||||
query = query.Where(key+" = ?", value)
|
||||
}
|
||||
}
|
||||
|
||||
// 应用搜索条件
|
||||
if options.Search != "" {
|
||||
query = query.Where("username LIKE ? OR email LIKE ? OR real_name LIKE ?",
|
||||
"%"+options.Search+"%", "%"+options.Search+"%", "%"+options.Search+"%")
|
||||
}
|
||||
|
||||
// 应用排序
|
||||
if options.Sort != "" {
|
||||
order := "ASC"
|
||||
if options.Order != "" {
|
||||
order = options.Order
|
||||
}
|
||||
query = query.Order(options.Sort + " " + order)
|
||||
}
|
||||
|
||||
// 应用分页
|
||||
if options.Page > 0 && options.PageSize > 0 {
|
||||
offset := (options.Page - 1) * options.PageSize
|
||||
query = query.Offset(offset).Limit(options.PageSize)
|
||||
}
|
||||
|
||||
return admins, query.Find(&admins).Error
|
||||
}
|
||||
|
||||
// WithTx 使用事务
|
||||
func (r *GormAdminRepository) WithTx(tx interface{}) interfaces.Repository[entities.Admin] {
|
||||
if gormTx, ok := tx.(*gorm.DB); ok {
|
||||
return &GormAdminRepository{
|
||||
db: gormTx,
|
||||
logger: r.logger,
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// FindByUsername 根据用户名查找管理员
|
||||
func (r *GormAdminRepository) FindByUsername(ctx context.Context, username string) (*entities.Admin, error) {
|
||||
var admin entities.Admin
|
||||
err := r.db.WithContext(ctx).Where("username = ?", username).First(&admin).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin, nil
|
||||
}
|
||||
|
||||
// FindByEmail 根据邮箱查找管理员
|
||||
func (r *GormAdminRepository) FindByEmail(ctx context.Context, email string) (*entities.Admin, error) {
|
||||
var admin entities.Admin
|
||||
err := r.db.WithContext(ctx).Where("email = ?", email).First(&admin).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin, nil
|
||||
}
|
||||
|
||||
// ListAdmins 获取管理员列表(带分页和筛选)
|
||||
func (r *GormAdminRepository) ListAdmins(ctx context.Context, req *dto.AdminListRequest) (*dto.AdminListResponse, error) {
|
||||
var admins []entities.Admin
|
||||
var total int64
|
||||
|
||||
query := r.db.WithContext(ctx).Model(&entities.Admin{})
|
||||
|
||||
// 应用筛选条件
|
||||
if req.Username != "" {
|
||||
query = query.Where("username LIKE ?", "%"+req.Username+"%")
|
||||
}
|
||||
if req.Email != "" {
|
||||
query = query.Where("email LIKE ?", "%"+req.Email+"%")
|
||||
}
|
||||
if req.Role != "" {
|
||||
query = query.Where("role = ?", req.Role)
|
||||
}
|
||||
if req.IsActive != nil {
|
||||
query = query.Where("is_active = ?", *req.IsActive)
|
||||
}
|
||||
|
||||
// 统计总数
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 应用分页
|
||||
offset := (req.Page - 1) * req.PageSize
|
||||
query = query.Offset(offset).Limit(req.PageSize)
|
||||
|
||||
// 默认排序
|
||||
query = query.Order("created_at DESC")
|
||||
|
||||
// 查询数据
|
||||
if err := query.Find(&admins).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 转换为DTO
|
||||
adminInfos := make([]dto.AdminInfo, len(admins))
|
||||
for i, admin := range admins {
|
||||
adminInfos[i] = r.convertToAdminInfo(admin)
|
||||
}
|
||||
|
||||
return &dto.AdminListResponse{
|
||||
Total: total,
|
||||
Page: req.Page,
|
||||
Size: req.PageSize,
|
||||
Admins: adminInfos,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetStats 获取管理员统计信息
|
||||
func (r *GormAdminRepository) GetStats(ctx context.Context) (*dto.AdminStatsResponse, error) {
|
||||
var stats dto.AdminStatsResponse
|
||||
|
||||
// 总管理员数
|
||||
if err := r.db.WithContext(ctx).Model(&entities.Admin{}).Count(&stats.TotalAdmins).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 激活管理员数
|
||||
if err := r.db.WithContext(ctx).Model(&entities.Admin{}).Where("is_active = ?", true).Count(&stats.ActiveAdmins).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 今日登录数
|
||||
today := time.Now().Truncate(24 * time.Hour)
|
||||
if err := r.db.WithContext(ctx).Model(&entities.AdminLoginLog{}).Where("created_at >= ?", today).Count(&stats.TodayLogins).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 总操作数
|
||||
if err := r.db.WithContext(ctx).Model(&entities.AdminOperationLog{}).Count(&stats.TotalOperations).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &stats, nil
|
||||
}
|
||||
|
||||
// GetPermissionsByRole 根据角色获取权限
|
||||
func (r *GormAdminRepository) GetPermissionsByRole(ctx context.Context, role entities.AdminRole) ([]entities.AdminPermission, error) {
|
||||
var permissions []entities.AdminPermission
|
||||
|
||||
query := r.db.WithContext(ctx).
|
||||
Joins("JOIN admin_role_permissions ON admin_permissions.id = admin_role_permissions.permission_id").
|
||||
Where("admin_role_permissions.role = ? AND admin_permissions.is_active = ?", role, true)
|
||||
|
||||
return permissions, query.Find(&permissions).Error
|
||||
}
|
||||
|
||||
// UpdatePermissions 更新管理员权限
|
||||
func (r *GormAdminRepository) UpdatePermissions(ctx context.Context, adminID string, permissions []string) error {
|
||||
permissionsJSON, err := json.Marshal(permissions)
|
||||
if err != nil {
|
||||
return fmt.Errorf("序列化权限失败: %w", err)
|
||||
}
|
||||
|
||||
return r.db.WithContext(ctx).
|
||||
Model(&entities.Admin{}).
|
||||
Where("id = ?", adminID).
|
||||
Update("permissions", string(permissionsJSON)).Error
|
||||
}
|
||||
|
||||
// UpdateLoginStats 更新登录统计
|
||||
func (r *GormAdminRepository) UpdateLoginStats(ctx context.Context, adminID string) error {
|
||||
return r.db.WithContext(ctx).
|
||||
Model(&entities.Admin{}).
|
||||
Where("id = ?", adminID).
|
||||
Updates(map[string]interface{}{
|
||||
"last_login_at": time.Now(),
|
||||
"login_count": gorm.Expr("login_count + 1"),
|
||||
}).Error
|
||||
}
|
||||
|
||||
// UpdateReviewStats 更新审核统计
|
||||
func (r *GormAdminRepository) UpdateReviewStats(ctx context.Context, adminID string, approved bool) error {
|
||||
updates := map[string]interface{}{
|
||||
"review_count": gorm.Expr("review_count + 1"),
|
||||
}
|
||||
|
||||
if approved {
|
||||
updates["approved_count"] = gorm.Expr("approved_count + 1")
|
||||
} else {
|
||||
updates["rejected_count"] = gorm.Expr("rejected_count + 1")
|
||||
}
|
||||
|
||||
return r.db.WithContext(ctx).
|
||||
Model(&entities.Admin{}).
|
||||
Where("id = ?", adminID).
|
||||
Updates(updates).Error
|
||||
}
|
||||
|
||||
// convertToAdminInfo 转换为管理员信息DTO
|
||||
func (r *GormAdminRepository) convertToAdminInfo(admin entities.Admin) dto.AdminInfo {
|
||||
var permissions []string
|
||||
if admin.Permissions != "" {
|
||||
json.Unmarshal([]byte(admin.Permissions), &permissions)
|
||||
}
|
||||
|
||||
return dto.AdminInfo{
|
||||
ID: admin.ID,
|
||||
Username: admin.Username,
|
||||
Email: admin.Email,
|
||||
Phone: admin.Phone,
|
||||
RealName: admin.RealName,
|
||||
Role: admin.Role,
|
||||
IsActive: admin.IsActive,
|
||||
LastLoginAt: admin.LastLoginAt,
|
||||
LoginCount: admin.LoginCount,
|
||||
Permissions: permissions,
|
||||
CreatedAt: admin.CreatedAt,
|
||||
}
|
||||
}
|
||||
29
internal/domains/admin/routes/admin_routes.go
Normal file
29
internal/domains/admin/routes/admin_routes.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"tyapi-server/internal/domains/admin/handlers"
|
||||
)
|
||||
|
||||
// RegisterAdminRoutes 注册管理员路由
|
||||
func RegisterAdminRoutes(router *gin.Engine, adminHandler *handlers.AdminHandler) {
|
||||
// 管理员路由组
|
||||
adminGroup := router.Group("/api/admin")
|
||||
{
|
||||
// 认证相关路由(无需认证)
|
||||
authGroup := adminGroup.Group("/auth")
|
||||
{
|
||||
authGroup.POST("/login", adminHandler.Login)
|
||||
}
|
||||
|
||||
// 管理员管理路由(需要认证)
|
||||
adminGroup.POST("", adminHandler.CreateAdmin) // 创建管理员
|
||||
adminGroup.GET("", adminHandler.ListAdmins) // 获取管理员列表
|
||||
adminGroup.GET("/stats", adminHandler.GetAdminStats) // 获取统计信息
|
||||
adminGroup.GET("/:id", adminHandler.GetAdminByID) // 获取管理员详情
|
||||
adminGroup.PUT("/:id", adminHandler.UpdateAdmin) // 更新管理员
|
||||
adminGroup.DELETE("/:id", adminHandler.DeleteAdmin) // 删除管理员
|
||||
adminGroup.POST("/change-password", adminHandler.ChangePassword) // 修改密码
|
||||
}
|
||||
}
|
||||
431
internal/domains/admin/services/admin_service.go
Normal file
431
internal/domains/admin/services/admin_service.go
Normal file
@@ -0,0 +1,431 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"tyapi-server/internal/domains/admin/dto"
|
||||
"tyapi-server/internal/domains/admin/entities"
|
||||
"tyapi-server/internal/domains/admin/repositories"
|
||||
"tyapi-server/internal/shared/interfaces"
|
||||
)
|
||||
|
||||
// AdminService 管理员服务
|
||||
type AdminService struct {
|
||||
adminRepo repositories.AdminRepository
|
||||
loginLogRepo repositories.AdminLoginLogRepository
|
||||
operationLogRepo repositories.AdminOperationLogRepository
|
||||
permissionRepo repositories.AdminPermissionRepository
|
||||
responseBuilder interfaces.ResponseBuilder
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewAdminService 创建管理员服务
|
||||
func NewAdminService(
|
||||
adminRepo repositories.AdminRepository,
|
||||
loginLogRepo repositories.AdminLoginLogRepository,
|
||||
operationLogRepo repositories.AdminOperationLogRepository,
|
||||
permissionRepo repositories.AdminPermissionRepository,
|
||||
responseBuilder interfaces.ResponseBuilder,
|
||||
logger *zap.Logger,
|
||||
) *AdminService {
|
||||
return &AdminService{
|
||||
adminRepo: adminRepo,
|
||||
loginLogRepo: loginLogRepo,
|
||||
operationLogRepo: operationLogRepo,
|
||||
permissionRepo: permissionRepo,
|
||||
responseBuilder: responseBuilder,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Login 管理员登录
|
||||
func (s *AdminService) Login(ctx context.Context, req *dto.AdminLoginRequest, clientIP, userAgent string) (*dto.AdminLoginResponse, error) {
|
||||
s.logger.Info("管理员登录", zap.String("username", req.Username))
|
||||
|
||||
// 查找管理员
|
||||
admin, err := s.adminRepo.FindByUsername(ctx, req.Username)
|
||||
if err != nil {
|
||||
s.logger.Warn("管理员登录失败:用户不存在", zap.String("username", req.Username))
|
||||
s.recordLoginLog(ctx, req.Username, clientIP, userAgent, "failed", "用户不存在")
|
||||
return nil, fmt.Errorf("用户名或密码错误")
|
||||
}
|
||||
|
||||
// 检查管理员状态
|
||||
if !admin.IsActive {
|
||||
s.logger.Warn("管理员登录失败:账户已禁用", zap.String("username", req.Username))
|
||||
s.recordLoginLog(ctx, req.Username, clientIP, userAgent, "failed", "账户已禁用")
|
||||
return nil, fmt.Errorf("账户已被禁用,请联系管理员")
|
||||
}
|
||||
|
||||
// 验证密码
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(admin.Password), []byte(req.Password)); err != nil {
|
||||
s.logger.Warn("管理员登录失败:密码错误", zap.String("username", req.Username))
|
||||
s.recordLoginLog(ctx, req.Username, clientIP, userAgent, "failed", "密码错误")
|
||||
return nil, fmt.Errorf("用户名或密码错误")
|
||||
}
|
||||
|
||||
// 更新登录统计
|
||||
if err := s.adminRepo.UpdateLoginStats(ctx, admin.ID); err != nil {
|
||||
s.logger.Error("更新登录统计失败", zap.Error(err))
|
||||
}
|
||||
|
||||
// 记录登录日志
|
||||
s.recordLoginLog(ctx, req.Username, clientIP, userAgent, "success", "登录成功")
|
||||
|
||||
// 生成JWT令牌
|
||||
token, expiresAt, err := s.generateJWTToken(admin)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("生成令牌失败: %w", err)
|
||||
}
|
||||
|
||||
// 获取权限列表
|
||||
permissions, err := s.getAdminPermissions(ctx, admin)
|
||||
if err != nil {
|
||||
s.logger.Error("获取管理员权限失败", zap.Error(err))
|
||||
permissions = []string{}
|
||||
}
|
||||
|
||||
// 构建响应
|
||||
adminInfo := dto.AdminInfo{
|
||||
ID: admin.ID,
|
||||
Username: admin.Username,
|
||||
Email: admin.Email,
|
||||
Phone: admin.Phone,
|
||||
RealName: admin.RealName,
|
||||
Role: admin.Role,
|
||||
IsActive: admin.IsActive,
|
||||
LastLoginAt: admin.LastLoginAt,
|
||||
LoginCount: admin.LoginCount,
|
||||
Permissions: permissions,
|
||||
CreatedAt: admin.CreatedAt,
|
||||
}
|
||||
|
||||
s.logger.Info("管理员登录成功", zap.String("username", req.Username))
|
||||
return &dto.AdminLoginResponse{
|
||||
Token: token,
|
||||
ExpiresAt: expiresAt,
|
||||
Admin: adminInfo,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateAdmin 创建管理员
|
||||
func (s *AdminService) CreateAdmin(ctx context.Context, req *dto.AdminCreateRequest, operatorID string) error {
|
||||
s.logger.Info("创建管理员", zap.String("username", req.Username))
|
||||
|
||||
// 检查用户名是否已存在
|
||||
if _, err := s.adminRepo.FindByUsername(ctx, req.Username); err == nil {
|
||||
return fmt.Errorf("用户名已存在")
|
||||
}
|
||||
|
||||
// 检查邮箱是否已存在
|
||||
if _, err := s.adminRepo.FindByEmail(ctx, req.Email); err == nil {
|
||||
return fmt.Errorf("邮箱已存在")
|
||||
}
|
||||
|
||||
// 加密密码
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return fmt.Errorf("密码加密失败: %w", err)
|
||||
}
|
||||
|
||||
// 序列化权限
|
||||
permissionsJSON := "[]"
|
||||
if len(req.Permissions) > 0 {
|
||||
permissionsBytes, err := json.Marshal(req.Permissions)
|
||||
if err != nil {
|
||||
return fmt.Errorf("权限序列化失败: %w", err)
|
||||
}
|
||||
permissionsJSON = string(permissionsBytes)
|
||||
}
|
||||
|
||||
// 创建管理员
|
||||
admin := entities.Admin{
|
||||
ID: s.generateID(),
|
||||
Username: req.Username,
|
||||
Password: string(hashedPassword),
|
||||
Email: req.Email,
|
||||
Phone: req.Phone,
|
||||
RealName: req.RealName,
|
||||
Role: req.Role,
|
||||
IsActive: true,
|
||||
Permissions: permissionsJSON,
|
||||
}
|
||||
|
||||
if err := s.adminRepo.Create(ctx, admin); err != nil {
|
||||
return fmt.Errorf("创建管理员失败: %w", err)
|
||||
}
|
||||
|
||||
// 记录操作日志
|
||||
s.recordOperationLog(ctx, operatorID, "create", "admin", admin.ID, map[string]interface{}{
|
||||
"username": req.Username,
|
||||
"email": req.Email,
|
||||
"role": req.Role,
|
||||
}, "success", "创建管理员成功")
|
||||
|
||||
s.logger.Info("管理员创建成功", zap.String("username", req.Username))
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateAdmin 更新管理员
|
||||
func (s *AdminService) UpdateAdmin(ctx context.Context, adminID string, req *dto.AdminUpdateRequest, operatorID string) error {
|
||||
s.logger.Info("更新管理员", zap.String("admin_id", adminID))
|
||||
|
||||
// 获取管理员
|
||||
admin, err := s.adminRepo.GetByID(ctx, adminID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("管理员不存在")
|
||||
}
|
||||
|
||||
// 更新字段
|
||||
if req.Email != "" {
|
||||
// 检查邮箱是否被其他管理员使用
|
||||
if existingAdmin, err := s.adminRepo.FindByEmail(ctx, req.Email); err == nil && existingAdmin.ID != adminID {
|
||||
return fmt.Errorf("邮箱已被其他管理员使用")
|
||||
}
|
||||
admin.Email = req.Email
|
||||
}
|
||||
|
||||
if req.Phone != "" {
|
||||
admin.Phone = req.Phone
|
||||
}
|
||||
|
||||
if req.RealName != "" {
|
||||
admin.RealName = req.RealName
|
||||
}
|
||||
|
||||
if req.Role != "" {
|
||||
admin.Role = req.Role
|
||||
}
|
||||
|
||||
if req.IsActive != nil {
|
||||
admin.IsActive = *req.IsActive
|
||||
}
|
||||
|
||||
if len(req.Permissions) > 0 {
|
||||
permissionsJSON, err := json.Marshal(req.Permissions)
|
||||
if err != nil {
|
||||
return fmt.Errorf("权限序列化失败: %w", err)
|
||||
}
|
||||
admin.Permissions = string(permissionsJSON)
|
||||
}
|
||||
|
||||
// 保存更新
|
||||
if err := s.adminRepo.Update(ctx, admin); err != nil {
|
||||
return fmt.Errorf("更新管理员失败: %w", err)
|
||||
}
|
||||
|
||||
// 记录操作日志
|
||||
s.recordOperationLog(ctx, operatorID, "update", "admin", adminID, map[string]interface{}{
|
||||
"email": req.Email,
|
||||
"phone": req.Phone,
|
||||
"real_name": req.RealName,
|
||||
"role": req.Role,
|
||||
"is_active": req.IsActive,
|
||||
}, "success", "更新管理员成功")
|
||||
|
||||
s.logger.Info("管理员更新成功", zap.String("admin_id", adminID))
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChangePassword 修改密码
|
||||
func (s *AdminService) ChangePassword(ctx context.Context, adminID string, req *dto.AdminPasswordChangeRequest) error {
|
||||
s.logger.Info("修改管理员密码", zap.String("admin_id", adminID))
|
||||
|
||||
// 获取管理员
|
||||
admin, err := s.adminRepo.GetByID(ctx, adminID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("管理员不存在")
|
||||
}
|
||||
|
||||
// 验证旧密码
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(admin.Password), []byte(req.OldPassword)); err != nil {
|
||||
return fmt.Errorf("旧密码错误")
|
||||
}
|
||||
|
||||
// 加密新密码
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return fmt.Errorf("密码加密失败: %w", err)
|
||||
}
|
||||
|
||||
// 更新密码
|
||||
admin.Password = string(hashedPassword)
|
||||
if err := s.adminRepo.Update(ctx, admin); err != nil {
|
||||
return fmt.Errorf("更新密码失败: %w", err)
|
||||
}
|
||||
|
||||
// 记录操作日志
|
||||
s.recordOperationLog(ctx, adminID, "change_password", "admin", adminID, nil, "success", "修改密码成功")
|
||||
|
||||
s.logger.Info("管理员密码修改成功", zap.String("admin_id", adminID))
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListAdmins 获取管理员列表
|
||||
func (s *AdminService) ListAdmins(ctx context.Context, req *dto.AdminListRequest) (*dto.AdminListResponse, error) {
|
||||
s.logger.Info("获取管理员列表", zap.Int("page", req.Page), zap.Int("page_size", req.PageSize))
|
||||
|
||||
response, err := s.adminRepo.ListAdmins(ctx, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取管理员列表失败: %w", err)
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// GetAdminStats 获取管理员统计信息
|
||||
func (s *AdminService) GetAdminStats(ctx context.Context) (*dto.AdminStatsResponse, error) {
|
||||
s.logger.Info("获取管理员统计信息")
|
||||
|
||||
stats, err := s.adminRepo.GetStats(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取统计信息失败: %w", err)
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// GetAdminByID 根据ID获取管理员
|
||||
func (s *AdminService) GetAdminByID(ctx context.Context, adminID string) (*dto.AdminInfo, error) {
|
||||
s.logger.Info("获取管理员信息", zap.String("admin_id", adminID))
|
||||
|
||||
admin, err := s.adminRepo.GetByID(ctx, adminID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("管理员不存在")
|
||||
}
|
||||
|
||||
// 获取权限列表
|
||||
permissions, err := s.getAdminPermissions(ctx, &admin)
|
||||
if err != nil {
|
||||
s.logger.Error("获取管理员权限失败", zap.Error(err))
|
||||
permissions = []string{}
|
||||
}
|
||||
|
||||
adminInfo := dto.AdminInfo{
|
||||
ID: admin.ID,
|
||||
Username: admin.Username,
|
||||
Email: admin.Email,
|
||||
Phone: admin.Phone,
|
||||
RealName: admin.RealName,
|
||||
Role: admin.Role,
|
||||
IsActive: admin.IsActive,
|
||||
LastLoginAt: admin.LastLoginAt,
|
||||
LoginCount: admin.LoginCount,
|
||||
Permissions: permissions,
|
||||
CreatedAt: admin.CreatedAt,
|
||||
}
|
||||
|
||||
return &adminInfo, nil
|
||||
}
|
||||
|
||||
// DeleteAdmin 删除管理员
|
||||
func (s *AdminService) DeleteAdmin(ctx context.Context, adminID string, operatorID string) error {
|
||||
s.logger.Info("删除管理员", zap.String("admin_id", adminID))
|
||||
|
||||
// 检查管理员是否存在
|
||||
if _, err := s.adminRepo.GetByID(ctx, adminID); err != nil {
|
||||
return fmt.Errorf("管理员不存在")
|
||||
}
|
||||
|
||||
// 软删除管理员
|
||||
if err := s.adminRepo.SoftDelete(ctx, adminID); err != nil {
|
||||
return fmt.Errorf("删除管理员失败: %w", err)
|
||||
}
|
||||
|
||||
// 记录操作日志
|
||||
s.recordOperationLog(ctx, operatorID, "delete", "admin", adminID, nil, "success", "删除管理员成功")
|
||||
|
||||
s.logger.Info("管理员删除成功", zap.String("admin_id", adminID))
|
||||
return nil
|
||||
}
|
||||
|
||||
// getAdminPermissions 获取管理员权限
|
||||
func (s *AdminService) getAdminPermissions(ctx context.Context, admin *entities.Admin) ([]string, error) {
|
||||
// 首先从角色获取权限
|
||||
rolePermissions, err := s.adminRepo.GetPermissionsByRole(ctx, admin.Role)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 从角色权限中提取权限代码
|
||||
permissions := make([]string, 0, len(rolePermissions))
|
||||
for _, perm := range rolePermissions {
|
||||
permissions = append(permissions, perm.Code)
|
||||
}
|
||||
|
||||
// 如果有自定义权限,也添加进去
|
||||
if admin.Permissions != "" {
|
||||
var customPermissions []string
|
||||
if err := json.Unmarshal([]byte(admin.Permissions), &customPermissions); err == nil {
|
||||
permissions = append(permissions, customPermissions...)
|
||||
}
|
||||
}
|
||||
|
||||
return permissions, nil
|
||||
}
|
||||
|
||||
// generateJWTToken 生成JWT令牌
|
||||
func (s *AdminService) generateJWTToken(admin *entities.Admin) (string, time.Time, error) {
|
||||
// 这里应该使用JWT库生成令牌
|
||||
// 为了简化,这里返回一个模拟的令牌
|
||||
token := fmt.Sprintf("admin_token_%s_%d", admin.ID, time.Now().Unix())
|
||||
expiresAt := time.Now().Add(24 * time.Hour)
|
||||
|
||||
return token, expiresAt, nil
|
||||
}
|
||||
|
||||
// generateID 生成ID
|
||||
func (s *AdminService) generateID() string {
|
||||
bytes := make([]byte, 16)
|
||||
rand.Read(bytes)
|
||||
return hex.EncodeToString(bytes)
|
||||
}
|
||||
|
||||
// recordLoginLog 记录登录日志
|
||||
func (s *AdminService) recordLoginLog(ctx context.Context, username, ip, userAgent, status, message string) {
|
||||
log := entities.AdminLoginLog{
|
||||
ID: s.generateID(),
|
||||
Username: username,
|
||||
IP: ip,
|
||||
UserAgent: userAgent,
|
||||
Status: status,
|
||||
Message: message,
|
||||
}
|
||||
|
||||
if err := s.loginLogRepo.Create(ctx, log); err != nil {
|
||||
s.logger.Error("记录登录日志失败", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
// recordOperationLog 记录操作日志
|
||||
func (s *AdminService) recordOperationLog(ctx context.Context, adminID, action, resource, resourceID string, details map[string]interface{}, status, message string) {
|
||||
detailsJSON := "{}"
|
||||
if details != nil {
|
||||
if bytes, err := json.Marshal(details); err == nil {
|
||||
detailsJSON = string(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
log := entities.AdminOperationLog{
|
||||
ID: s.generateID(),
|
||||
AdminID: adminID,
|
||||
Action: action,
|
||||
Resource: resource,
|
||||
ResourceID: resourceID,
|
||||
Details: detailsJSON,
|
||||
Status: status,
|
||||
Message: message,
|
||||
}
|
||||
|
||||
if err := s.operationLogRepo.Create(ctx, log); err != nil {
|
||||
s.logger.Error("记录操作日志失败", zap.Error(err))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user