package business import ( "context" "net/mail" "strconv" "strings" "time" "unicode/utf8" "kra/internal/biz/business" "kra/internal/service/dto" ) type AccountUsecase interface { List(context.Context, business.AccountFilter, ...business.ListOption) ([]*business.Account, int64, error) Find(context.Context, uint) (*business.Account, error) CreateChild(context.Context, *business.Account) (*business.Account, error) Update(context.Context, *business.AccountUpdate) error } type AccountService struct{ uc AccountUsecase } func NewAccountService(uc *business.AccountUsecase) *AccountService { return &AccountService{uc: uc} } func accountResponse(a *business.Account) *dto.BusinessAccountResponse { return &dto.BusinessAccountResponse{UserID: a.ID, Username: a.Username, NickName: a.NickName, RealName: a.RealName, Phone: a.Phone, Email: a.Email, QQNumber: a.QQNumber, Level: a.Level, ParentID: a.ParentID, IDPath: a.IDPath, ParentUserName: a.ParentUserName, Status: strconv.Itoa(a.Status), RejectReason: a.RejectReason, AuthorityID: a.AuthorityID, CreateTime: a.CreatedAt.Format("2006-01-02 15:04:05"), UpdateTime: a.UpdatedAt.Format("2006-01-02 15:04:05"), PromoteCount: a.PromoteCount, BindCount: a.BindCount} } func (s *AccountService) List(ctx context.Context, req *dto.BusinessAccountQuery) (*dto.BusinessAccountPage, error) { if req == nil { return nil, business.ErrAccountInput } page, size := req.Page, req.PageSize if page < 1 { page = 1 } if size < 1 { size = 10 } if size > 500 { size = 500 } if page > int(^uint(0)>>1)/size { return nil, business.ErrAccountInput } filter := business.AccountFilter{UserID: req.UserID, Username: req.Username, NickName: req.NickName, RealName: req.RealName, Phone: req.Phone} for _, field := range []struct { raw string target **int }{{req.Level, &filter.Level}, {req.Status, &filter.Status}} { if field.raw == "" { continue } n, err := strconv.Atoi(field.raw) if err != nil || n < 0 || n > 3 { return nil, business.ErrAccountInput } *field.target = &n } if (req.BeginTime == "") != (req.EndTime == "") { return nil, business.ErrAccountInput } if req.BeginTime != "" && req.EndTime != "" { begin, err := time.ParseInLocation("2006-01-02 15:04:05", req.BeginTime, time.Local) if err != nil { return nil, business.ErrAccountInput } end, err := time.ParseInLocation("2006-01-02 15:04:05", req.EndTime, time.Local) if err != nil || end.Before(begin) { return nil, business.ErrAccountInput } filter.BeginTime, filter.EndTime = &begin, &end } rows, total, err := s.uc.List(ctx, filter, business.ListOffset((page-1)*size), business.ListLimit(size), business.ListOrderBy(req.OrderByColumn, strings.EqualFold(req.IsAsc, "desc") || req.IsAsc == "descending")) if err != nil { return nil, err } out := &dto.BusinessAccountPage{Rows: make([]*dto.BusinessAccountResponse, 0, len(rows)), Total: total} for _, a := range rows { out.Rows = append(out.Rows, accountResponse(a)) } return out, nil } func (s *AccountService) Find(ctx context.Context, id uint) (*dto.BusinessAccountResponse, error) { if id == 0 { return nil, business.ErrAccountInput } a, err := s.uc.Find(ctx, id) if err != nil { return nil, err } return accountResponse(a), nil } func validateAccount(req *dto.BusinessAccountRequest, create bool) (*int, error) { if req == nil { return nil, business.ErrAccountInput } if create && req.HireDate == nil { return nil, business.ErrMemberInput } if req.Phone != nil && !validMemberPhone(*req.Phone) { return nil, business.ErrMemberInput } if req.HireDate != nil { if _, err := time.Parse("2006-01-02", *req.HireDate); err != nil { return nil, business.ErrMemberInput } } if create && (req.Username == nil || req.RealName == nil || strings.TrimSpace(*req.RealName) == "" || req.Password == "") { return nil, business.ErrAccountInput } if req.Username != nil && (utf8.RuneCountInString(*req.Username) < 2 || utf8.RuneCountInString(*req.Username) > 30 || strings.TrimSpace(*req.Username) == "") { return nil, business.ErrAccountInput } for _, field := range []struct { s *string max int }{{req.NickName, 30}, {req.RealName, 30}, {req.Phone, 20}, {req.Email, 50}, {req.QQNumber, 20}, {req.RejectReason, 200}} { if field.s != nil && (utf8.RuneCountInString(*field.s) > field.max || strings.ContainsAny(*field.s, "<>")) { return nil, business.ErrAccountInput } } if req.Username != nil && strings.ContainsAny(*req.Username, "<>") { return nil, business.ErrAccountInput } if create && (utf8.RuneCountInString(req.Password) < 5 || utf8.RuneCountInString(req.Password) > 50 || len(req.Password) > 72) { return nil, business.ErrAccountInput } if req.Email != nil && *req.Email != "" { if _, err := mail.ParseAddress(*req.Email); err != nil { return nil, business.ErrAccountInput } } if req.Status == nil { return nil, nil } status, err := strconv.Atoi(*req.Status) if err != nil || status < 0 || status > 3 { return nil, business.ErrAccountInput } return &status, nil } func stringValue(s *string) string { if s == nil { return "" } return *s } func (s *AccountService) Create(ctx context.Context, req *dto.BusinessAccountRequest) (*dto.BusinessAccountResponse, error) { status, err := validateAccount(req, true) if err != nil { return nil, err } a := &business.Account{Username: stringValue(req.Username), NickName: stringValue(req.NickName), RealName: stringValue(req.RealName), Phone: stringValue(req.Phone), Email: stringValue(req.Email), QQNumber: stringValue(req.QQNumber), Password: req.Password, RejectReason: stringValue(req.RejectReason)} a.HireDate = stringValue(req.HireDate) if status != nil { a.Status = *status } created, err := s.uc.CreateChild(ctx, a) if err != nil { return nil, err } return accountResponse(created), nil } func (s *AccountService) Update(ctx context.Context, req *dto.BusinessAccountRequest) error { status, err := validateAccount(req, false) if err != nil { return err } if req.UserID == 0 { return business.ErrAccountInput } return s.uc.Update(ctx, &business.AccountUpdate{ID: req.UserID, Username: req.Username, NickName: req.NickName, RealName: req.RealName, Phone: req.Phone, Email: req.Email, QQNumber: req.QQNumber, Status: status, RejectReason: req.RejectReason}) }