基础架构
This commit is contained in:
@@ -2,353 +2,36 @@ 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 管理员服务
|
||||
// AdminService 管理员领域服务
|
||||
type AdminService struct {
|
||||
adminRepo repositories.AdminRepository
|
||||
loginLogRepo repositories.AdminLoginLogRepository
|
||||
operationLogRepo repositories.AdminOperationLogRepository
|
||||
permissionRepo repositories.AdminPermissionRepository
|
||||
responseBuilder interfaces.ResponseBuilder
|
||||
logger *zap.Logger
|
||||
adminRepo repositories.AdminRepository
|
||||
permissionRepo repositories.AdminPermissionRepository
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewAdminService 创建管理员服务
|
||||
// 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,
|
||||
adminRepo: adminRepo,
|
||||
permissionRepo: permissionRepo,
|
||||
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) {
|
||||
// GetAdminPermissions 获取管理员权限
|
||||
func (s *AdminService) GetAdminPermissions(ctx context.Context, admin *entities.Admin) ([]string, error) {
|
||||
// 首先从角色获取权限
|
||||
rolePermissions, err := s.adminRepo.GetPermissionsByRole(ctx, admin.Role)
|
||||
if err != nil {
|
||||
@@ -371,61 +54,3 @@ func (s *AdminService) getAdminPermissions(ctx context.Context, admin *entities.
|
||||
|
||||
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