package adminuser import ( "context" "errors" "regexp" "strings" ) var ( ErrDependencyUnavailable = errors.New("dependency unavailable") ErrInvalidUser = errors.New("invalid user") ErrInvalidRealnameInput = errors.New("invalid realname input") ErrRealnameAlreadyVerified = errors.New("realname already verified") ErrRealnameNotRevocable = errors.New("realname not revocable") ErrInvalidWalletDirection = errors.New("invalid wallet direction") ErrInvalidWalletAmount = errors.New("invalid wallet amount") ErrInvalidWalletReason = errors.New("invalid wallet reason") ErrInsufficientBalance = errors.New("insufficient balance") ) // 单笔人工调账上限:10 万元,防止误填。 const maxWalletAdjustAmountCent int64 = 10_000_000 var manualRealnameIDPattern = regexp.MustCompile(`^\d{17}[\dXx]$`) type Service struct { repo *Repository } func NewService(repo *Repository) *Service { return &Service{repo: repo} } func (s *Service) List(ctx context.Context, page, pageSize int, query ListQuery) (*PaginatedResult, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } return s.repo.List(ctx, page, pageSize, query) } func (s *Service) Freeze(ctx context.Context, adminID uint64, userID uint64, req FreezeRequest, meta AuditMeta) (*UserDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } if userID == 0 { return nil, ErrInvalidUser } return s.repo.Freeze(ctx, adminID, userID, req, meta) } func (s *Service) Unfreeze(ctx context.Context, adminID uint64, userID uint64, meta AuditMeta) (*UserDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } if userID == 0 { return nil, ErrInvalidUser } return s.repo.Unfreeze(ctx, adminID, userID, meta) } func (s *Service) SetDepositFreeQuota(ctx context.Context, adminID uint64, userID uint64, req DepositFreeQuotaRequest, meta AuditMeta) (*UserDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } if userID == 0 || req.AmountCent < 0 { return nil, ErrInvalidUser } return s.repo.SetDepositFreeQuota(ctx, adminID, userID, req, meta) } func (s *Service) RevokeRealname(ctx context.Context, adminID uint64, userID uint64, req RevokeRealnameRequest, meta AuditMeta) (*UserDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } if userID == 0 { return nil, ErrInvalidUser } return s.repo.RevokeRealname(ctx, adminID, userID, req, meta) } func (s *Service) ManualRealname(ctx context.Context, adminID uint64, userID uint64, req ManualRealnameRequest, meta AuditMeta) (*UserDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } if userID == 0 { return nil, ErrInvalidUser } if !validManualRealname(req.Name, req.IDNo) { return nil, ErrInvalidRealnameInput } return s.repo.ManualRealname(ctx, adminID, userID, req, meta) } func (s *Service) AdjustWallet(ctx context.Context, adminID uint64, userID uint64, req WalletAdjustRequest, meta AuditMeta) (*UserDTO, error) { if s.repo == nil { return nil, ErrDependencyUnavailable } if userID == 0 { return nil, ErrInvalidUser } direction := strings.TrimSpace(strings.ToLower(req.Direction)) if direction != "in" && direction != "out" { return nil, ErrInvalidWalletDirection } if req.AmountCent <= 0 || req.AmountCent > maxWalletAdjustAmountCent { return nil, ErrInvalidWalletAmount } if reasonLen := len([]rune(strings.TrimSpace(req.Reason))); reasonLen < 2 || reasonLen > 200 { return nil, ErrInvalidWalletReason } req.Direction = direction req.Reason = strings.TrimSpace(req.Reason) req.ReferenceNo = strings.TrimSpace(req.ReferenceNo) return s.repo.AdjustWallet(ctx, adminID, userID, req, meta) } func validManualRealname(name string, idNo string) bool { name = strings.TrimSpace(name) idNo = strings.TrimSpace(idNo) nameLen := len([]rune(name)) return nameLen >= 2 && nameLen <= 30 && manualRealnameIDPattern.MatchString(idNo) }