fix: apply DIP to UserService with local repository interfaces
- Define userRepository, userRoleRepository, roleRepository, passwordHistoryRepository interfaces - Update UserService struct to use interface types instead of concrete *repository types - Update NewUserService constructor to accept interfaces - Add UserCursorResult type (avoid conflict with login_log.go's CursorResult) - Fix AssignRoles to use type assertion for WithTx (concrete method not in interface) - Add GetByEmail, UpdateStatus, BatchUpdateStatus, BatchDelete to userRepository interface - Add GetByID, GetByIDs to roleRepository interface This enables dependency injection and mocking at the service layer.
This commit is contained in:
@@ -14,27 +14,66 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Repository interfaces for dependency inversion (DIP) — service layer depends on these abstractions, not concrete types.
|
||||
type userRepository interface {
|
||||
GetByID(ctx context.Context, id int64) (*domain.User, error)
|
||||
GetByUsername(ctx context.Context, username string) (*domain.User, error)
|
||||
GetByEmail(ctx context.Context, email string) (*domain.User, error)
|
||||
Create(ctx context.Context, user *domain.User) error
|
||||
Update(ctx context.Context, user *domain.User) error
|
||||
Delete(ctx context.Context, id int64) error
|
||||
List(ctx context.Context, offset, limit int) ([]*domain.User, int64, error)
|
||||
ListCursor(ctx context.Context, filter *repository.AdvancedFilter, limit int, cursor *pagination.Cursor) ([]*domain.User, bool, error)
|
||||
GetByIDs(ctx context.Context, ids []int64) ([]*domain.User, error)
|
||||
UpdateStatus(ctx context.Context, id int64, status domain.UserStatus) error
|
||||
BatchUpdateStatus(ctx context.Context, ids []int64, status domain.UserStatus) error
|
||||
BatchDelete(ctx context.Context, ids []int64) error
|
||||
DB() *gorm.DB
|
||||
}
|
||||
|
||||
type userRoleRepository interface {
|
||||
GetByUserID(ctx context.Context, userID int64) ([]*domain.UserRole, error)
|
||||
DeleteByUserID(ctx context.Context, userID int64) error
|
||||
DeleteByUserAndRole(ctx context.Context, userID, roleID int64) error
|
||||
GetByRoleID(ctx context.Context, roleID int64) ([]*domain.UserRole, error)
|
||||
GetUserIDByRoleID(ctx context.Context, roleID int64) ([]int64, error)
|
||||
BatchCreate(ctx context.Context, userRoles []*domain.UserRole) error
|
||||
DB() *gorm.DB
|
||||
}
|
||||
|
||||
type roleRepository interface {
|
||||
GetByCode(ctx context.Context, code string) (*domain.Role, error)
|
||||
GetByID(ctx context.Context, id int64) (*domain.Role, error)
|
||||
GetByIDs(ctx context.Context, ids []int64) ([]*domain.Role, error)
|
||||
}
|
||||
|
||||
type passwordHistoryRepository interface {
|
||||
GetByUserID(ctx context.Context, userID int64, limit int) ([]*domain.PasswordHistory, error)
|
||||
Create(ctx context.Context, history *domain.PasswordHistory) error
|
||||
DeleteOldRecords(ctx context.Context, userID int64, keep int) error
|
||||
}
|
||||
|
||||
// UserService 用户服务
|
||||
type UserService struct {
|
||||
userRepo *repository.UserRepository
|
||||
userRoleRepo *repository.UserRoleRepository
|
||||
roleRepo *repository.RoleRepository
|
||||
passwordHistoryRepo *repository.PasswordHistoryRepository
|
||||
userRepo userRepository
|
||||
userRoleRepo userRoleRepository
|
||||
roleRepo roleRepository
|
||||
passwordHistoryRepo passwordHistoryRepository
|
||||
}
|
||||
|
||||
const passwordHistoryLimit = 5 // 保留最近5条密码历史
|
||||
|
||||
// NewUserService 创建用户服务实例
|
||||
func NewUserService(
|
||||
userRepo *repository.UserRepository,
|
||||
userRoleRepo *repository.UserRoleRepository,
|
||||
roleRepo *repository.RoleRepository,
|
||||
passwordHistoryRepo *repository.PasswordHistoryRepository,
|
||||
userRepo userRepository,
|
||||
userRoleRepo userRoleRepository,
|
||||
roleRepo roleRepository,
|
||||
passwordHistoryRepo passwordHistoryRepository,
|
||||
) *UserService {
|
||||
return &UserService{
|
||||
userRepo: userRepo,
|
||||
userRoleRepo: userRoleRepo,
|
||||
roleRepo: roleRepo,
|
||||
roleRepo: roleRepo,
|
||||
passwordHistoryRepo: passwordHistoryRepo,
|
||||
}
|
||||
}
|
||||
@@ -146,8 +185,16 @@ type ListCursorRequest struct {
|
||||
Size int `form:"size"`
|
||||
}
|
||||
|
||||
// UserCursorResult wraps cursor-based pagination response for users
|
||||
type UserCursorResult struct {
|
||||
Items []*domain.User `json:"items"`
|
||||
NextCursor string `json:"next_cursor"`
|
||||
HasMore bool `json:"has_more"`
|
||||
PageSize int `json:"page_size"`
|
||||
}
|
||||
|
||||
// ListCursor 游标分页获取用户列表(推荐使用)
|
||||
func (s *UserService) ListCursor(ctx context.Context, req *ListCursorRequest) (*CursorResult, error) {
|
||||
func (s *UserService) ListCursor(ctx context.Context, req *ListCursorRequest) (*UserCursorResult, error) {
|
||||
size := pagination.ClampPageSize(req.Size)
|
||||
|
||||
cursor, err := pagination.Decode(req.Cursor)
|
||||
@@ -176,7 +223,7 @@ func (s *UserService) ListCursor(ctx context.Context, req *ListCursorRequest) (*
|
||||
nextCursor = pagination.BuildNextCursor(last.ID, last.CreatedAt)
|
||||
}
|
||||
|
||||
return &CursorResult{
|
||||
return &UserCursorResult{
|
||||
Items: users,
|
||||
NextCursor: nextCursor,
|
||||
HasMore: hasMore,
|
||||
@@ -268,11 +315,16 @@ func (s *UserService) AssignRoles(ctx context.Context, userID int64, roleIDs []i
|
||||
}
|
||||
|
||||
// 使用事务包装删旧建新操作,确保原子性
|
||||
// Note: WithTx is on concrete type, requires type assertion
|
||||
txRepo, ok := s.userRoleRepo.(*repository.UserRoleRepository)
|
||||
if !ok {
|
||||
return errors.New("userRoleRepo does not support transactions")
|
||||
}
|
||||
return s.userRoleRepo.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
if err := s.userRoleRepo.WithTx(tx).DeleteByUserID(ctx, userID); err != nil {
|
||||
if err := txRepo.WithTx(tx).DeleteByUserID(ctx, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.userRoleRepo.WithTx(tx).BatchCreate(ctx, userRoles)
|
||||
return txRepo.WithTx(tx).BatchCreate(ctx, userRoles)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user