package redemption import ( "context" "crypto/rand" "math/big" "github.com/perfect-panel/server/internal/model/redemption" "github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/types" "github.com/perfect-panel/server/pkg/logger" "github.com/perfect-panel/server/pkg/xerr" "github.com/pkg/errors" "gorm.io/gorm" ) type CreateRedemptionCodeLogic struct { logger.Logger ctx context.Context svcCtx *svc.ServiceContext } // Create redemption code func NewCreateRedemptionCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateRedemptionCodeLogic { return &CreateRedemptionCodeLogic{ Logger: logger.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } // generateUniqueCode generates a unique redemption code func (l *CreateRedemptionCodeLogic) generateUniqueCode() (string, error) { const charset = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" // Removed confusing characters like I, O, 0, 1 const codeLength = 16 maxRetries := 10 for i := 0; i < maxRetries; i++ { code := make([]byte, codeLength) for j := 0; j < codeLength; j++ { num, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset)))) if err != nil { return "", err } code[j] = charset[num.Int64()] } codeStr := string(code) // Check if code already exists _, err := l.svcCtx.RedemptionCodeModel.FindOneByCode(l.ctx, codeStr) if errors.Is(err, gorm.ErrRecordNotFound) { return codeStr, nil } else if err != nil { return "", err } // Code exists, try again } return "", errors.New("failed to generate unique code after maximum retries") } func (l *CreateRedemptionCodeLogic) CreateRedemptionCode(req *types.CreateRedemptionCodeRequest) error { // Check if subscribe plan is valid if req.SubscribePlan == 0 { l.Errorw("[CreateRedemptionCode] Subscribe plan cannot be empty") return errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), "subscribe plan cannot be empty") } // Verify subscribe plan exists _, err := l.svcCtx.SubscribeModel.FindOne(l.ctx, req.SubscribePlan) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { l.Errorw("[CreateRedemptionCode] Subscribe plan not found", logger.Field("subscribe_plan", req.SubscribePlan)) return errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), "subscribe plan not found") } l.Errorw("[CreateRedemptionCode] Database Error", logger.Field("error", err.Error())) return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find subscribe plan error: %v", err.Error()) } // Validate batch count if req.BatchCount < 1 { l.Errorw("[CreateRedemptionCode] Batch count must be at least 1") return errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), "batch count must be at least 1") } // Generate redemption codes in batch var createdCodes []string for i := int64(0); i < req.BatchCount; i++ { code, err := l.generateUniqueCode() if err != nil { l.Errorw("[CreateRedemptionCode] Failed to generate unique code", logger.Field("error", err.Error())) return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "generate unique code error: %v", err.Error()) } redemptionCode := &redemption.RedemptionCode{ Code: code, TotalCount: req.TotalCount, UsedCount: 0, SubscribePlan: req.SubscribePlan, UnitTime: req.UnitTime, Quantity: req.Quantity, Status: 1, // Default to enabled } err = l.svcCtx.RedemptionCodeModel.Insert(l.ctx, redemptionCode) if err != nil { l.Errorw("[CreateRedemptionCode] Database Error", logger.Field("error", err.Error())) return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "create redemption code error: %v", err.Error()) } createdCodes = append(createdCodes, code) } l.Infow("[CreateRedemptionCode] Successfully created redemption codes", logger.Field("count", len(createdCodes)), logger.Field("codes", createdCodes)) return nil }