refactor(utils): 修复密码哈希比较逻辑错误 feat(user): 新增按状态筛选优惠券接口 docs: 添加虚拟发货与任务中心相关文档 fix(wechat): 修正Code2Session上下文传递问题 test: 补充订单折扣与积分转换测试用例 build: 更新配置文件与构建脚本 style: 清理多余的空行与注释
109 lines
3.3 KiB
Go
109 lines
3.3 KiB
Go
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
|
||
}
|