fix/status-review-sync-20260409 #1

Merged
long merged 65 commits from fix/status-review-sync-20260409 into main 2026-04-18 15:05:51 +00:00
Showing only changes of commit 73b0d5b8c0 - Show all commits

View File

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