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)) } }