package postgres import ( "context" "github.com/break/junhong_cmp_fiber/internal/model" "github.com/redis/go-redis/v9" "gorm.io/gorm" ) type PaymentStore struct { db *gorm.DB redis *redis.Client } func NewPaymentStore(db *gorm.DB, redis *redis.Client) *PaymentStore { return &PaymentStore{ db: db, redis: redis, } } func (s *PaymentStore) Create(ctx context.Context, payment *model.Payment) error { return s.db.WithContext(ctx).Create(payment).Error } func (s *PaymentStore) CreateWithTx(ctx context.Context, tx *gorm.DB, payment *model.Payment) error { return tx.WithContext(ctx).Create(payment).Error } func (s *PaymentStore) GetByPaymentNo(ctx context.Context, paymentNo string) (*model.Payment, error) { var payment model.Payment err := s.db.WithContext(ctx). Where("payment_no = ?", paymentNo). First(&payment).Error if err != nil { return nil, err } return &payment, nil } func (s *PaymentStore) GetByID(ctx context.Context, id uint) (*model.Payment, error) { var payment model.Payment err := s.db.WithContext(ctx).First(&payment, id).Error if err != nil { return nil, err } return &payment, nil } func (s *PaymentStore) UpdateStatus(ctx context.Context, id uint, status int) error { return s.db.WithContext(ctx). Model(&model.Payment{}). Where("id = ?", id). Update("status", status).Error } func (s *PaymentStore) UpdateStatusWithTx(ctx context.Context, tx *gorm.DB, id uint, status int) error { return tx.WithContext(ctx). Model(&model.Payment{}). Where("id = ?", id). Update("status", status).Error } func (s *PaymentStore) UpdateStatusWithOptimisticLock(ctx context.Context, id uint, oldStatus *int, newStatus int, paidAt interface{}) error { return s.UpdateStatusWithOptimisticLockDB(ctx, s.db, id, oldStatus, newStatus, paidAt) } func (s *PaymentStore) UpdateStatusWithOptimisticLockDB(ctx context.Context, db *gorm.DB, id uint, oldStatus *int, newStatus int, paidAt interface{}) error { updates := map[string]interface{}{ "status": newStatus, } if paidAt != nil { updates["paid_at"] = paidAt } query := db.WithContext(ctx).Model(&model.Payment{}).Where("id = ?", id) if oldStatus != nil { query = query.Where("status = ?", *oldStatus) } result := query.Updates(updates) if result.Error != nil { return result.Error } if result.RowsAffected == 0 { return gorm.ErrRecordNotFound } return nil } func (s *PaymentStore) UpdatePaymentInfo(ctx context.Context, id uint, thirdPartyTradeNo string, paymentConfigID *uint) error { return s.UpdatePaymentInfoWithDB(ctx, s.db, id, thirdPartyTradeNo, paymentConfigID) } func (s *PaymentStore) UpdatePaymentInfoWithDB(ctx context.Context, db *gorm.DB, id uint, thirdPartyTradeNo string, paymentConfigID *uint) error { updates := map[string]interface{}{} if thirdPartyTradeNo != "" { updates["third_party_trade_no"] = thirdPartyTradeNo } if paymentConfigID != nil { updates["payment_config_id"] = *paymentConfigID } if len(updates) == 0 { return nil } result := db.WithContext(ctx).Model(&model.Payment{}).Where("id = ?", id).Updates(updates) if result.Error != nil { return result.Error } if result.RowsAffected == 0 { return gorm.ErrRecordNotFound } return nil } func (s *PaymentStore) ListByOrderID(ctx context.Context, orderID uint, orderType string) ([]*model.Payment, error) { var payments []*model.Payment err := s.db.WithContext(ctx). Where("order_id = ? AND order_type = ?", orderID, orderType). Order("created_at DESC"). Find(&payments).Error if err != nil { return nil, err } return payments, nil } func (s *PaymentStore) ListByOrderIDAndStatus(ctx context.Context, orderID uint, orderType string, status int) (*model.Payment, error) { var payment model.Payment err := s.db.WithContext(ctx). Where("order_id = ? AND order_type = ? AND status = ?", orderID, orderType, status). First(&payment).Error if err != nil { return nil, err } return &payment, nil } func (s *PaymentStore) GetByOrderIDAndStatus(ctx context.Context, orderID uint, status int) (*model.Payment, error) { var payment model.Payment err := s.db.WithContext(ctx). Where("order_id = ? AND status = ?", orderID, status). First(&payment).Error if err != nil { return nil, err } return &payment, nil } // FindLatestPendingByOrderAndMethod 按业务单、支付方式查找最新一条待支付记录 // 用于支付宝支付单复用:同一业务单同一支付方式只允许一个有效 pending 支付单 func (s *PaymentStore) FindLatestPendingByOrderAndMethod(ctx context.Context, orderID uint, orderType string, paymentMethod string) (*model.Payment, error) { var payment model.Payment err := s.db.WithContext(ctx). Where("order_id = ? AND order_type = ? AND payment_method = ? AND status = ?", orderID, orderType, paymentMethod, model.PaymentRecordStatusPending). Order("created_at DESC"). First(&payment).Error if err != nil { return nil, err } return &payment, nil }