package user import ( "context" "errors" "time" "bindbox-game/internal/repository/mysql/dao" "bindbox-game/internal/repository/mysql/model" "gorm.io/gorm/clause" ) // ConsumePoints 扣减用户积分用于订单支付,按有效期优先扣减,返回扣减流水ID func (s *service) ConsumePoints(ctx context.Context, userID int64, points int64, orderNo string) (int64, error) { if points <= 0 { return 0, nil } var ledgerID int64 err := s.writeDB.Transaction(func(tx *dao.Query) error { rows, err := tx.UserPoints.WithContext(ctx).Clauses(clause.Locking{Strength: "UPDATE"}).Where(tx.UserPoints.UserID.Eq(userID)).Order(tx.UserPoints.ValidEnd.Asc()).Find() if err != nil { return err } remain := points now := time.Now() for _, r := range rows { if !r.ValidEnd.IsZero() && r.ValidEnd.Before(now) { continue } if remain <= 0 { break } use := r.Points if use > remain { use = remain } _, err := tx.UserPoints.WithContext(ctx).Where(tx.UserPoints.ID.Eq(r.ID)).Updates(map[string]any{"points": r.Points - use}) if err != nil { return err } remain -= use } if remain > 0 { return errors.New("insufficient_points") } // 记录扣减流水(负数) led := &model.UserPointsLedger{UserID: userID, Action: "consume_order", Points: -points, RefTable: "orders", RefID: orderNo, Remark: "consume by lottery"} if err := tx.UserPointsLedger.WithContext(ctx).Create(led); err != nil { return err } ledgerID = led.ID return nil }) return ledgerID, err } // RefundPoints 退款积分(退回扣减),返回退款流水ID func (s *service) RefundPoints(ctx context.Context, userID int64, points int64, orderNo string, reason string) (int64, error) { var ledgerID int64 err := s.writeDB.Transaction(func(tx *dao.Query) error { // 累加到默认积分条目 existing, _ := tx.UserPoints.WithContext(ctx).Clauses(clause.Locking{Strength: "UPDATE"}).Where(tx.UserPoints.UserID.Eq(userID)).First() if existing == nil { if err := tx.UserPoints.WithContext(ctx).Omit(tx.UserPoints.ValidEnd).Create(&model.UserPoints{UserID: userID, Points: points}); err != nil { return err } } else { if _, err := tx.UserPoints.WithContext(ctx).Where(tx.UserPoints.ID.Eq(existing.ID)).Updates(map[string]any{"points": existing.Points + points}); err != nil { return err } } led := &model.UserPointsLedger{UserID: userID, Action: "refund_points", Points: points, RefTable: "orders", RefID: orderNo, Remark: reason} if err := tx.UserPointsLedger.WithContext(ctx).Create(led); err != nil { return err } ledgerID = led.ID return nil }) return ledgerID, err }