邹方成 45815bfb7d chore: 清理无用文件与优化代码结构
refactor(utils): 修复密码哈希比较逻辑错误
feat(user): 新增按状态筛选优惠券接口
docs: 添加虚拟发货与任务中心相关文档
fix(wechat): 修正Code2Session上下文传递问题
test: 补充订单折扣与积分转换测试用例
build: 更新配置文件与构建脚本
style: 清理多余的空行与注释
2025-12-18 17:35:55 +08:00

109 lines
3.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package user
import (
"context"
"errors"
"fmt"
"time"
"bindbox-game/internal/repository/mysql/model"
"gorm.io/gorm"
)
func (s *service) AddCoupon(ctx context.Context, userID int64, couponID int64) error {
tpl, err := s.readDB.SystemCoupons.WithContext(ctx).Where(s.readDB.SystemCoupons.ID.Eq(couponID)).First()
if err != nil {
return err
}
if tpl == nil || tpl.Status != 1 {
// 模板不存在或未启用
fmt.Printf("[发券] 模板不可用 coupon_id=%d status=%d\n", couponID, func() int32 {
if tpl != nil {
return tpl.Status
}
return -1
}())
return errors.New("coupon not found or disabled")
}
// 配额检查:若 TotalQuantity > 0 则限制发放总量
if tpl.TotalQuantity > 0 {
issued, ierr := s.readDB.UserCoupons.WithContext(ctx).Where(s.readDB.UserCoupons.CouponID.Eq(couponID)).Count()
if ierr != nil {
return ierr
}
if issued >= tpl.TotalQuantity {
fmt.Printf("[发券] 模板配额已满 coupon_id=%d issued=%d total=%d\n", couponID, issued, tpl.TotalQuantity)
return gorm.ErrInvalidData
}
}
// 允许用户重复持有相同模板的未使用优惠券
item := &model.UserCoupons{UserID: userID, CouponID: couponID, Status: 1}
if !tpl.ValidStart.IsZero() {
item.ValidStart = tpl.ValidStart
} else {
item.ValidStart = time.Now()
}
if !tpl.ValidEnd.IsZero() {
item.ValidEnd = tpl.ValidEnd
}
do := s.writeDB.UserCoupons.WithContext(ctx).Omit(s.readDB.UserCoupons.UsedAt, s.readDB.UserCoupons.UsedOrderID)
if tpl.ValidEnd.IsZero() {
do = do.Omit(s.writeDB.UserCoupons.ValidEnd)
}
if err := do.Create(item); err != nil {
return err
}
// 直减金额券初始化余额其他类型余额置0
if tpl.DiscountType == 1 && tpl.DiscountValue > 0 {
_, _ = s.writeDB.UserCoupons.WithContext(ctx).Where(s.writeDB.UserCoupons.ID.Eq(item.ID)).Updates(map[string]any{"balance_amount": tpl.DiscountValue})
} else {
_, _ = s.writeDB.UserCoupons.WithContext(ctx).Where(s.writeDB.UserCoupons.ID.Eq(item.ID)).Updates(map[string]any{"balance_amount": 0})
}
return nil
}
func (s *service) VoidUserCoupon(ctx context.Context, adminID int64, userID int64, userCouponID int64) error {
if userID <= 0 || userCouponID <= 0 {
return fmt.Errorf("invalid_params")
}
uc, err := s.readDB.UserCoupons.WithContext(ctx).
Where(s.readDB.UserCoupons.ID.Eq(userCouponID)).
Where(s.readDB.UserCoupons.UserID.Eq(userID)).
First()
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fmt.Errorf("not_found")
}
return err
}
if uc.Status != 1 {
return fmt.Errorf("invalid_status")
}
db := s.repo.GetDbW()
if uc.BalanceAmount > 0 {
nb := int64(0)
if err := db.Exec("UPDATE user_coupons SET status=?, balance_amount=?, updated_at=NOW(3) WHERE id=? AND user_id=? AND status=1", 3, nb, userCouponID, userID).Error; err != nil {
return err
}
ledger := &model.UserCouponLedger{
UserID: userID,
UserCouponID: userCouponID,
ChangeAmount: -uc.BalanceAmount,
BalanceAfter: nb,
OrderID: 0,
Action: "void_by_admin",
CreatedAt: time.Now(),
}
if err := db.Create(ledger).Error; err != nil {
return err
}
return nil
}
if err := db.Exec("UPDATE user_coupons SET status=?, updated_at=NOW(3) WHERE id=? AND user_id=? AND status=1", 3, userCouponID, userID).Error; err != nil {
return err
}
_ = adminID
return nil
}