package account import ( "context" "fmt" "github.com/break/junhong_cmp_fiber/internal/model" "github.com/break/junhong_cmp_fiber/internal/store" "github.com/break/junhong_cmp_fiber/internal/store/postgres" "github.com/break/junhong_cmp_fiber/pkg/constants" "github.com/break/junhong_cmp_fiber/pkg/errors" "github.com/break/junhong_cmp_fiber/pkg/middleware" "golang.org/x/crypto/bcrypt" "gorm.io/gorm" ) // Service 账号业务服务 type Service struct { accountStore *postgres.AccountStore roleStore *postgres.RoleStore accountRoleStore *postgres.AccountRoleStore } // New 创建账号服务 func New(accountStore *postgres.AccountStore, roleStore *postgres.RoleStore, accountRoleStore *postgres.AccountRoleStore) *Service { return &Service{ accountStore: accountStore, roleStore: roleStore, accountRoleStore: accountRoleStore, } } // Create 创建账号 func (s *Service) Create(ctx context.Context, req *model.CreateAccountRequest) (*model.Account, error) { // 获取当前用户 ID currentUserID := middleware.GetUserIDFromContext(ctx) if currentUserID == 0 { return nil, errors.New(errors.CodeUnauthorized, "未授权访问") } // 验证非 root 用户必须提供 parent_id if req.UserType != constants.UserTypeRoot && req.ParentID == nil { return nil, errors.New(errors.CodeParentIDRequired, "非 root 用户必须提供上级账号") } // 检查用户名唯一性 existing, err := s.accountStore.GetByUsername(ctx, req.Username) if err == nil && existing != nil { return nil, errors.New(errors.CodeUsernameExists, "用户名已存在") } // 检查手机号唯一性 existing, err = s.accountStore.GetByPhone(ctx, req.Phone) if err == nil && existing != nil { return nil, errors.New(errors.CodePhoneExists, "手机号已存在") } // 验证 parent_id 存在(如果提供) if req.ParentID != nil { parent, err := s.accountStore.GetByID(ctx, *req.ParentID) if err != nil || parent == nil { return nil, errors.New(errors.CodeInvalidParentID, "上级账号不存在或无效") } } // bcrypt 哈希密码 hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) if err != nil { return nil, fmt.Errorf("密码哈希失败: %w", err) } // 创建账号 account := &model.Account{ Username: req.Username, Phone: req.Phone, Password: string(hashedPassword), UserType: req.UserType, ShopID: req.ShopID, ParentID: req.ParentID, Status: constants.StatusEnabled, } if err := s.accountStore.Create(ctx, account); err != nil { return nil, fmt.Errorf("创建账号失败: %w", err) } // 清除父账号的下级 ID 缓存 if account.ParentID != nil { _ = s.accountStore.ClearSubordinatesCacheForParents(ctx, *account.ParentID) } return account, nil } // Get 获取账号 func (s *Service) Get(ctx context.Context, id uint) (*model.Account, error) { account, err := s.accountStore.GetByID(ctx, id) if err != nil { if err == gorm.ErrRecordNotFound { return nil, errors.New(errors.CodeAccountNotFound, "账号不存在") } return nil, fmt.Errorf("获取账号失败: %w", err) } return account, nil } // Update 更新账号 func (s *Service) Update(ctx context.Context, id uint, req *model.UpdateAccountRequest) (*model.Account, error) { // 获取当前用户 ID currentUserID := middleware.GetUserIDFromContext(ctx) if currentUserID == 0 { return nil, errors.New(errors.CodeUnauthorized, "未授权访问") } // 获取现有账号 account, err := s.accountStore.GetByID(ctx, id) if err != nil { if err == gorm.ErrRecordNotFound { return nil, errors.New(errors.CodeAccountNotFound, "账号不存在") } return nil, fmt.Errorf("获取账号失败: %w", err) } // 更新字段 if req.Username != nil { // 检查新用户名唯一性 existing, err := s.accountStore.GetByUsername(ctx, *req.Username) if err == nil && existing != nil && existing.ID != id { return nil, errors.New(errors.CodeUsernameExists, "用户名已存在") } account.Username = *req.Username } if req.Phone != nil { // 检查新手机号唯一性 existing, err := s.accountStore.GetByPhone(ctx, *req.Phone) if err == nil && existing != nil && existing.ID != id { return nil, errors.New(errors.CodePhoneExists, "手机号已存在") } account.Phone = *req.Phone } if req.Password != nil { hashedPassword, err := bcrypt.GenerateFromPassword([]byte(*req.Password), bcrypt.DefaultCost) if err != nil { return nil, fmt.Errorf("密码哈希失败: %w", err) } account.Password = string(hashedPassword) } if req.Status != nil { account.Status = *req.Status } account.Updater = currentUserID if err := s.accountStore.Update(ctx, account); err != nil { return nil, fmt.Errorf("更新账号失败: %w", err) } return account, nil } // Delete 软删除账号 func (s *Service) Delete(ctx context.Context, id uint) error { // 检查账号存在 account, err := s.accountStore.GetByID(ctx, id) if err != nil { if err == gorm.ErrRecordNotFound { return errors.New(errors.CodeAccountNotFound, "账号不存在") } return fmt.Errorf("获取账号失败: %w", err) } if err := s.accountStore.Delete(ctx, id); err != nil { return fmt.Errorf("删除账号失败: %w", err) } // 清除该账号和所有上级的下级 ID 缓存 _ = s.accountStore.ClearSubordinatesCacheForParents(ctx, id) // 如果有上级,也需要清除上级的缓存 if account.ParentID != nil { _ = s.accountStore.ClearSubordinatesCacheForParents(ctx, *account.ParentID) } return nil } // List 查询账号列表 func (s *Service) List(ctx context.Context, req *model.AccountListRequest) ([]*model.Account, int64, error) { opts := &store.QueryOptions{ Page: req.Page, PageSize: req.PageSize, OrderBy: "id DESC", } if opts.Page == 0 { opts.Page = 1 } if opts.PageSize == 0 { opts.PageSize = constants.DefaultPageSize } filters := make(map[string]interface{}) if req.Username != "" { filters["username"] = req.Username } if req.Phone != "" { filters["phone"] = req.Phone } if req.UserType != nil { filters["user_type"] = *req.UserType } if req.Status != nil { filters["status"] = *req.Status } return s.accountStore.List(ctx, opts, filters) } // AssignRoles 为账号分配角色 func (s *Service) AssignRoles(ctx context.Context, accountID uint, roleIDs []uint) ([]*model.AccountRole, error) { // 获取当前用户 ID currentUserID := middleware.GetUserIDFromContext(ctx) if currentUserID == 0 { return nil, errors.New(errors.CodeUnauthorized, "未授权访问") } // 检查账号存在 _, err := s.accountStore.GetByID(ctx, accountID) if err != nil { if err == gorm.ErrRecordNotFound { return nil, errors.New(errors.CodeAccountNotFound, "账号不存在") } return nil, fmt.Errorf("获取账号失败: %w", err) } // 验证所有角色存在 for _, roleID := range roleIDs { _, err := s.roleStore.GetByID(ctx, roleID) if err != nil { if err == gorm.ErrRecordNotFound { return nil, errors.New(errors.CodeRoleNotFound, fmt.Sprintf("角色 %d 不存在", roleID)) } return nil, fmt.Errorf("获取角色失败: %w", err) } } // 创建关联 var ars []*model.AccountRole for _, roleID := range roleIDs { // 检查是否已分配 exists, _ := s.accountRoleStore.Exists(ctx, accountID, roleID) if exists { continue // 跳过已存在的关联 } ar := &model.AccountRole{ AccountID: accountID, RoleID: roleID, Status: constants.StatusEnabled, Creator: currentUserID, Updater: currentUserID, } if err := s.accountRoleStore.Create(ctx, ar); err != nil { return nil, fmt.Errorf("创建账号-角色关联失败: %w", err) } ars = append(ars, ar) } return ars, nil } // GetRoles 获取账号的所有角色 func (s *Service) GetRoles(ctx context.Context, accountID uint) ([]*model.Role, error) { // 检查账号存在 _, err := s.accountStore.GetByID(ctx, accountID) if err != nil { if err == gorm.ErrRecordNotFound { return nil, errors.New(errors.CodeAccountNotFound, "账号不存在") } return nil, fmt.Errorf("获取账号失败: %w", err) } // 获取角色 ID 列表 roleIDs, err := s.accountRoleStore.GetRoleIDsByAccountID(ctx, accountID) if err != nil { return nil, fmt.Errorf("获取账号角色 ID 失败: %w", err) } if len(roleIDs) == 0 { return []*model.Role{}, nil } // 获取角色详情 return s.roleStore.GetByIDs(ctx, roleIDs) } // RemoveRole 移除账号的角色 func (s *Service) RemoveRole(ctx context.Context, accountID, roleID uint) error { // 检查账号存在 _, err := s.accountStore.GetByID(ctx, accountID) if err != nil { if err == gorm.ErrRecordNotFound { return errors.New(errors.CodeAccountNotFound, "账号不存在") } return fmt.Errorf("获取账号失败: %w", err) } // 删除关联 if err := s.accountRoleStore.Delete(ctx, accountID, roleID); err != nil { return fmt.Errorf("删除账号-角色关联失败: %w", err) } return nil } // ValidatePassword 验证密码 func (s *Service) ValidatePassword(plainPassword, hashedPassword string) bool { err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(plainPassword)) return err == nil }