migrate from github

This commit is contained in:
2026-07-15 22:42:31 +08:00
commit 3ad559f05e
262 changed files with 21637 additions and 0 deletions
@@ -0,0 +1,237 @@
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