Add promo code models and APIs, persist selected promo codes on top-up orders, and apply bonus quota idempotently when payments complete.
97 lines
2.3 KiB
Go
97 lines
2.3 KiB
Go
package controller
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/QuantumNous/new-api/common"
|
|
"github.com/QuantumNous/new-api/model"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type validatePromoCodeRequest struct {
|
|
Code string `json:"code"`
|
|
Amount int `json:"amount"`
|
|
}
|
|
|
|
type validatePromoCodeResponse struct {
|
|
Valid bool `json:"valid"`
|
|
Reason string `json:"reason"`
|
|
PromoCodeId int `json:"promo_code_id"`
|
|
BonusAmount int `json:"bonus_amount"`
|
|
Code string `json:"code"`
|
|
}
|
|
|
|
func ValidatePromoCode(c *gin.Context) {
|
|
var req validatePromoCodeRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
common.ApiError(c, err)
|
|
return
|
|
}
|
|
|
|
code := normalizeUserPromoCode(req.Code)
|
|
if code == "" {
|
|
common.ApiErrorMsg(c, "优惠码不能为空")
|
|
return
|
|
}
|
|
|
|
promoCode, err := model.GetPromoCodeByCode(code)
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
common.ApiSuccess(c, newInvalidPromoCodeResponse(code, "not_found"))
|
|
return
|
|
}
|
|
common.ApiError(c, err)
|
|
return
|
|
}
|
|
|
|
if promoCode.Status != 1 {
|
|
common.ApiSuccess(c, newInvalidPromoCodeResponse(code, "disabled"))
|
|
return
|
|
}
|
|
if promoCode.ExpiresAt != nil && promoCode.ExpiresAt.Before(time.Now()) {
|
|
common.ApiSuccess(c, newInvalidPromoCodeResponse(code, "expired"))
|
|
return
|
|
}
|
|
if promoCode.MaxUses > 0 && promoCode.UsedCount >= promoCode.MaxUses {
|
|
common.ApiSuccess(c, newInvalidPromoCodeResponse(code, "max_uses_reached"))
|
|
return
|
|
}
|
|
if req.Amount < promoCode.MinRechargeAmount {
|
|
common.ApiSuccess(c, newInvalidPromoCodeResponse(code, "below_min_recharge"))
|
|
return
|
|
}
|
|
|
|
userId := c.GetInt("id")
|
|
if _, err := model.GetPromoCodeUsageByCodeAndUser(promoCode.Id, userId); err == nil {
|
|
common.ApiSuccess(c, newInvalidPromoCodeResponse(code, "already_used"))
|
|
return
|
|
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
common.ApiError(c, err)
|
|
return
|
|
}
|
|
|
|
common.ApiSuccess(c, validatePromoCodeResponse{
|
|
Valid: true,
|
|
Reason: "",
|
|
PromoCodeId: promoCode.Id,
|
|
BonusAmount: promoCode.BonusAmount,
|
|
Code: promoCode.Code,
|
|
})
|
|
}
|
|
|
|
func normalizeUserPromoCode(code string) string {
|
|
return strings.ToUpper(strings.TrimSpace(code))
|
|
}
|
|
|
|
func newInvalidPromoCodeResponse(code string, reason string) validatePromoCodeResponse {
|
|
return validatePromoCodeResponse{
|
|
Valid: false,
|
|
Reason: reason,
|
|
Code: code,
|
|
}
|
|
}
|