package repository import ( "context" "errors" "fmt" "gorm.io/gorm" "pay-bridge/internal/model" ) // SequenceRepository 序列数据访问 type SequenceRepository struct { db *gorm.DB } func NewSequenceRepository(db *gorm.DB) *SequenceRepository { return &SequenceRepository{db: db} } // IncrAndGet 原子自增并返回新值(行级锁) func (r *SequenceRepository) IncrAndGet(ctx context.Context, appID string, seqType model.SeqType) (uint64, error) { var seq model.OrderSequence err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { // 加行锁读取 if err := tx.Set("gorm:query_option", "FOR UPDATE"). Where("app_id = ? AND seq_type = ?", appID, seqType). First(&seq).Error; err != nil { if !errors.Is(err, gorm.ErrRecordNotFound) { return err } // 自动初始化序列 prefix := defaultPrefix(seqType) seq = model.OrderSequence{ AppID: appID, SeqType: seqType, Prefix: prefix, CurrentValue: 0, Step: 1, } if err := tx.Create(&seq).Error; err != nil { return err } // 重新加锁读 return tx.Set("gorm:query_option", "FOR UPDATE"). Where("app_id = ? AND seq_type = ?", appID, seqType). First(&seq).Error } return nil }) if err != nil { return 0, err } // 自增 newVal := seq.CurrentValue + uint64(seq.Step) if err := r.db.WithContext(ctx).Model(&model.OrderSequence{}). Where("id = ?", seq.ID). Update("current_value", newVal).Error; err != nil { return 0, err } return newVal, nil } func defaultPrefix(t model.SeqType) string { switch t { case model.SeqTypeTrade: return "PAY" case model.SeqTypeRefund: return "REF" case model.SeqTypeSharing: return "SHA" default: return "ORD" } } // GetPrefix 获取序列前缀 func (r *SequenceRepository) GetPrefix(ctx context.Context, appID string, seqType model.SeqType) (string, error) { var seq model.OrderSequence err := r.db.WithContext(ctx).Where("app_id = ? AND seq_type = ?", appID, seqType).First(&seq).Error if errors.Is(err, gorm.ErrRecordNotFound) { return fmt.Sprintf("%s", defaultPrefix(seqType)), nil } if err != nil { return "", err } return seq.Prefix, nil }