package controller import ( "errors" "strconv" "strings" "time" "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/model" "github.com/gin-gonic/gin" "gorm.io/gorm" ) type createPromoCodeRequest struct { Code string `json:"code"` BonusAmount int `json:"bonus_amount"` MaxUses int `json:"max_uses"` MinRechargeAmount int `json:"min_recharge_amount"` ExpiresAt string `json:"expires_at"` Notes string `json:"notes"` } type updatePromoCodeRequest struct { Status *int `json:"status"` MaxUses *int `json:"max_uses"` MinRechargeAmount *int `json:"min_recharge_amount"` ExpiresAt *string `json:"expires_at"` Notes *string `json:"notes"` } func GetPromoCodes(c *gin.Context) { pageInfo := common.GetPageQuery(c) keyword := strings.TrimSpace(c.Query("keyword")) var status *int if statusText := strings.TrimSpace(c.Query("status")); statusText != "" { statusValue, err := strconv.Atoi(statusText) if err != nil { common.ApiErrorMsg(c, "status 参数格式不正确") return } status = &statusValue } promoCodes, total, err := model.GetPromoCodes(pageInfo.GetPage(), pageInfo.GetPageSize(), status, keyword) if err != nil { common.ApiError(c, err) return } pageInfo.SetTotal(int(total)) pageInfo.SetItems(promoCodes) common.ApiSuccess(c, pageInfo) } func CreatePromoCode(c *gin.Context) { var req createPromoCodeRequest if err := c.ShouldBindJSON(&req); err != nil { common.ApiError(c, err) return } code := normalizePromoCode(req.Code) if code == "" { common.ApiErrorMsg(c, "优惠码不能为空") return } if len(code) > 32 { common.ApiErrorMsg(c, "优惠码长度不能超过 32 个字符") return } if req.BonusAmount <= 0 { common.ApiErrorMsg(c, "赠送额度必须大于 0") return } if req.MaxUses < 0 { common.ApiErrorMsg(c, "最大使用次数不能小于 0") return } if req.MinRechargeAmount < 0 { common.ApiErrorMsg(c, "最低充值金额不能小于 0") return } if _, err := model.GetPromoCodeByCode(code); err == nil { common.ApiErrorMsg(c, "优惠码已存在") return } else if !errors.Is(err, gorm.ErrRecordNotFound) { common.ApiError(c, err) return } promoCode := &model.PromoCode{ Code: code, BonusAmount: req.BonusAmount, MaxUses: req.MaxUses, MinRechargeAmount: req.MinRechargeAmount, Notes: req.Notes, Status: 1, } if strings.TrimSpace(req.ExpiresAt) != "" { expiresAt, err := parsePromoCodeExpiresAt(req.ExpiresAt) if err != nil { common.ApiErrorMsg(c, "expires_at 必须是 RFC3339 格式") return } promoCode.ExpiresAt = expiresAt } if err := promoCode.Insert(); err != nil { common.ApiError(c, err) return } common.ApiSuccess(c, promoCode) } func GetPromoCode(c *gin.Context) { promoCode, ok := getPromoCodeFromParam(c) if !ok { return } common.ApiSuccess(c, promoCode) } func UpdatePromoCode(c *gin.Context) { promoCode, ok := getPromoCodeFromParam(c) if !ok { return } var req updatePromoCodeRequest if err := c.ShouldBindJSON(&req); err != nil { common.ApiError(c, err) return } if req.Status != nil { promoCode.Status = *req.Status } if req.MaxUses != nil { if *req.MaxUses < 0 { common.ApiErrorMsg(c, "最大使用次数不能小于 0") return } promoCode.MaxUses = *req.MaxUses } if req.MinRechargeAmount != nil { if *req.MinRechargeAmount < 0 { common.ApiErrorMsg(c, "最低充值金额不能小于 0") return } promoCode.MinRechargeAmount = *req.MinRechargeAmount } if req.ExpiresAt != nil { expiresAtText := strings.TrimSpace(*req.ExpiresAt) if expiresAtText == "" { promoCode.ExpiresAt = nil } else { expiresAt, err := parsePromoCodeExpiresAt(expiresAtText) if err != nil { common.ApiErrorMsg(c, "expires_at 必须是 RFC3339 格式") return } promoCode.ExpiresAt = expiresAt } } if req.Notes != nil { promoCode.Notes = *req.Notes } if err := promoCode.Update(); err != nil { common.ApiError(c, err) return } common.ApiSuccess(c, promoCode) } func DeletePromoCode(c *gin.Context) { promoCode, ok := getPromoCodeFromParam(c) if !ok { return } promoCode.Status = 0 if err := promoCode.Update(); err != nil { common.ApiError(c, err) return } common.ApiSuccess(c, nil) } func GetPromoCodeUsages(c *gin.Context) { promoCode, ok := getPromoCodeFromParam(c) if !ok { return } pageInfo := common.GetPageQuery(c) usages, total, err := model.GetPromoCodeUsages(pageInfo.GetPage(), pageInfo.GetPageSize(), promoCode.Id) if err != nil { common.ApiError(c, err) return } pageInfo.SetTotal(int(total)) pageInfo.SetItems(usages) common.ApiSuccess(c, pageInfo) } func getPromoCodeFromParam(c *gin.Context) (*model.PromoCode, bool) { id, err := strconv.Atoi(c.Param("id")) if err != nil { common.ApiError(c, err) return nil, false } promoCode, err := model.GetPromoCodeByID(id) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { common.ApiErrorMsg(c, "优惠码不存在") return nil, false } common.ApiError(c, err) return nil, false } return promoCode, true } func normalizePromoCode(code string) string { return strings.ToUpper(strings.TrimSpace(code)) } func parsePromoCodeExpiresAt(value string) (*time.Time, error) { expiresAt, err := time.Parse(time.RFC3339, strings.TrimSpace(value)) if err != nil { return nil, err } return &expiresAt, nil }