238 lines
7.7 KiB
Go
238 lines
7.7 KiB
Go
package invitation_service
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"time"
|
|
|
|
"github.com/XingfenD/yoresee_doc/internal/dto"
|
|
"github.com/XingfenD/yoresee_doc/internal/model"
|
|
"github.com/XingfenD/yoresee_doc/internal/repository"
|
|
"github.com/XingfenD/yoresee_doc/internal/repository/invitation_repo"
|
|
"github.com/XingfenD/yoresee_doc/internal/repository/user_repo"
|
|
"github.com/XingfenD/yoresee_doc/internal/status"
|
|
"github.com/sirupsen/logrus"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type InvitationService struct {
|
|
invitationRepo *invitation_repo.InvitationRepository
|
|
userRepo *user_repo.UserRepository
|
|
}
|
|
|
|
func NewInvitationService(repos *repository.Repositories) *InvitationService {
|
|
return &InvitationService{
|
|
invitationRepo: repos.Invitation,
|
|
userRepo: repos.User,
|
|
}
|
|
}
|
|
|
|
func (s *InvitationService) Generate(userID int64, maxUsedCnt *int64, expiresAt *time.Time, note *string) (*model.Invitation, error) {
|
|
bytes := make([]byte, 8)
|
|
if _, err := rand.Read(bytes); err != nil {
|
|
logrus.Errorf("[Service layer: InvitationService] generate random code failed, err=%+v", err)
|
|
return nil, status.GenErrWithCustomMsg(status.StatusServiceInternalError, "generate invitation code failed")
|
|
}
|
|
code := hex.EncodeToString(bytes)
|
|
|
|
invitation := &model.Invitation{
|
|
Code: code,
|
|
CreatedBy: userID,
|
|
ExpiresAt: expiresAt,
|
|
MaxUsedCnt: maxUsedCnt,
|
|
Note: note,
|
|
}
|
|
|
|
if err := s.invitationRepo.Create(invitation).Exec(); err != nil {
|
|
return nil, status.StatusWriteDBError
|
|
}
|
|
return invitation, nil
|
|
}
|
|
|
|
func (s *InvitationService) ListByCreator(userID int64) ([]model.Invitation, error) {
|
|
list, err := s.invitationRepo.List(&model.Invitation{CreatedBy: userID}).Exec()
|
|
if err != nil {
|
|
logrus.Errorf("[Service layer: InvitationService] ListByCreator failed, user_id=%d, err=%+v", userID, err)
|
|
return nil, status.GenErrWithCustomMsg(status.StatusReadDBError, "list invitations failed")
|
|
}
|
|
return list, nil
|
|
}
|
|
|
|
func (s *InvitationService) ListInvitations(req *dto.ListInvitationsReq) ([]model.Invitation, int64, error) {
|
|
|
|
list, total, err := s.invitationRepo.List(&model.Invitation{}).
|
|
WithCreatorID(req.CreatorID).
|
|
WithKeyword(req.Keyword).
|
|
WithMaxUsedCnt(req.MaxUsedCnt).
|
|
WithExpiresAtRange(req.ExpiresAtStart, req.ExpiresAtEnd).
|
|
WithCreatedAtRange(req.CreatedAtStart, req.CreatedAtEnd).
|
|
WithDisabled(req.Disabled).
|
|
WithSort(req.SortArgs.Field, req.SortArgs.Desc).
|
|
WithPagination(req.Pagination.Page, req.Pagination.PageSize).
|
|
ExecWithTotal()
|
|
if err != nil {
|
|
logrus.Errorf("[Service layer: InvitationService] ListInvitations failed, err=%+v", err)
|
|
return nil, 0, status.GenErrWithCustomMsg(status.StatusReadDBError, "list invitations failed")
|
|
}
|
|
return list, total, nil
|
|
}
|
|
|
|
func (s *InvitationService) CreateInvitation(req *dto.CreateInvitationRequest) (*model.Invitation, error) {
|
|
if req == nil || req.CreatorExternalID == "" {
|
|
return nil, status.StatusParamError
|
|
}
|
|
// TODO: normal user can't create invitation codes
|
|
userID, err := s.userRepo.GetIDByExternalID(req.CreatorExternalID).Exec()
|
|
if err != nil {
|
|
return nil, status.StatusUserNotFound
|
|
}
|
|
return s.Generate(userID, req.MaxUsedCnt, req.ExpiresAt, req.Note)
|
|
}
|
|
|
|
func (s *InvitationService) UpdateInvitation(req *dto.UpdateInvitationRequest) error {
|
|
if req == nil || req.Code == "" {
|
|
return status.StatusParamError
|
|
}
|
|
return s.UpdateCode(req.Code, req.ExpiresAt, req.MaxUsedCnt, req.Disabled, req.Note)
|
|
}
|
|
|
|
func (s *InvitationService) DeleteInvitation(req *dto.DeleteInvitationRequest) error {
|
|
if req == nil || req.Code == "" {
|
|
return status.StatusParamError
|
|
}
|
|
inv, err := s.invitationRepo.GetByCode(req.Code).Exec()
|
|
if err != nil {
|
|
return status.StatusInvitationInvalid
|
|
}
|
|
if err := s.invitationRepo.Delete(inv.ID).Exec(); err != nil {
|
|
return status.StatusWriteDBError
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *InvitationService) CreateInvitationRecord(record *model.InvitationRecord) error {
|
|
if record == nil || record.Code == "" || record.Status == "" {
|
|
return status.StatusParamError
|
|
}
|
|
if record.UsedAt.IsZero() {
|
|
record.UsedAt = time.Now()
|
|
}
|
|
if err := s.invitationRepo.CreateRecord(record).Exec(); err != nil {
|
|
return status.StatusWriteDBError
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *InvitationService) CreateInvitationRecordWithTx(tx *gorm.DB, record *model.InvitationRecord) error {
|
|
if record == nil || record.Code == "" || record.Status == "" {
|
|
return status.StatusParamError
|
|
}
|
|
if record.UsedAt.IsZero() {
|
|
record.UsedAt = time.Now()
|
|
}
|
|
if err := s.invitationRepo.CreateRecord(record).WithTx(tx).Exec(); err != nil {
|
|
return status.StatusWriteDBError
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *InvitationService) ListInvitationRecords(req *dto.ListInvitationRecordsRequest) ([]model.InvitationRecord, int64, error) {
|
|
if req == nil {
|
|
return nil, 0, status.StatusParamError
|
|
}
|
|
return s.invitationRepo.ListRecords().
|
|
WithCode(req.Code).
|
|
WithStatus(req.Status).
|
|
WithUsedAtRange(req.UsedAtStart, req.UsedAtEnd).
|
|
WithCreatorID(req.CreatorID).
|
|
WithKeyword(req.Keyword).
|
|
WithPagination(req.Pagination.Page, req.Pagination.PageSize).
|
|
ExecWithTotal()
|
|
}
|
|
|
|
func (s *InvitationService) ValidateAndUse(code string) error {
|
|
ok, err := s.invitationRepo.ValidateAndUse(code).Exec()
|
|
if err != nil {
|
|
return status.StatusWriteDBError
|
|
}
|
|
if ok {
|
|
return nil
|
|
}
|
|
|
|
invitation, err := s.invitationRepo.GetByCode(code).Exec()
|
|
if err != nil {
|
|
return status.StatusInvitationInvalid
|
|
}
|
|
|
|
if invitation.DeletedAt != nil {
|
|
return status.GenErrWithCustomMsg(status.StatusInvitationInvalid, "invitation code deleted")
|
|
}
|
|
if invitation.Disabled == true {
|
|
return status.GenErrWithCustomMsg(status.StatusInvitationInvalid, "invitation code disabled")
|
|
}
|
|
if invitation.ExpiresAt != nil && invitation.ExpiresAt.Before(time.Now()) {
|
|
return status.GenErrWithCustomMsg(status.StatusInvitationInvalid, "invitation code expired")
|
|
}
|
|
if invitation.MaxUsedCnt != nil && invitation.UsedCnt >= *invitation.MaxUsedCnt {
|
|
return status.GenErrWithCustomMsg(status.StatusInvitationInvalid, "invitation code used up")
|
|
}
|
|
|
|
return status.StatusInvitationInvalid
|
|
}
|
|
|
|
func (s *InvitationService) ValidateAndUseWithTx(tx *gorm.DB, code string) error {
|
|
ok, err := s.invitationRepo.ValidateAndUse(code).WithTx(tx).Exec()
|
|
if err != nil {
|
|
return status.StatusWriteDBError
|
|
}
|
|
if ok {
|
|
return nil
|
|
}
|
|
|
|
invitation, err := s.invitationRepo.GetByCode(code).WithTx(tx).Exec()
|
|
if err != nil {
|
|
return status.StatusInvitationInvalid
|
|
}
|
|
|
|
if invitation.DeletedAt != nil {
|
|
return status.GenErrWithCustomMsg(status.StatusInvitationInvalid, "invitation code deleted")
|
|
}
|
|
if invitation.Disabled == true {
|
|
return status.GenErrWithCustomMsg(status.StatusInvitationInvalid, "invitation code disabled")
|
|
}
|
|
if invitation.ExpiresAt != nil && invitation.ExpiresAt.Before(time.Now()) {
|
|
return status.GenErrWithCustomMsg(status.StatusInvitationInvalid, "invitation code expired")
|
|
}
|
|
if invitation.MaxUsedCnt != nil && invitation.UsedCnt >= *invitation.MaxUsedCnt {
|
|
return status.GenErrWithCustomMsg(status.StatusInvitationInvalid, "invitation code used up")
|
|
}
|
|
|
|
return status.StatusInvitationInvalid
|
|
}
|
|
|
|
func (s *InvitationService) UpdateCode(code string, newExpireTime *time.Time, newMaxUsedCnt *int64, isDisabled *bool, note *string) error {
|
|
invitation, err := s.invitationRepo.GetByCode(code).Exec()
|
|
if err != nil {
|
|
logrus.Errorf("[Service layer: InvitationService] GetByCode failed, code=%s, err=%+v", code, err)
|
|
return status.GenErrWithCustomMsg(status.StatusInvitationInvalid, "invitation code invalid")
|
|
}
|
|
if newExpireTime != nil {
|
|
invitation.ExpiresAt = newExpireTime
|
|
}
|
|
if newMaxUsedCnt != nil {
|
|
invitation.MaxUsedCnt = newMaxUsedCnt
|
|
}
|
|
if isDisabled != nil {
|
|
invitation.Disabled = *isDisabled
|
|
}
|
|
if note != nil {
|
|
invitation.Note = note
|
|
}
|
|
if err := s.invitationRepo.Update(invitation).Exec(); err != nil {
|
|
return status.StatusWriteDBError
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var InvitationSvc *InvitationService
|