Files
pay-bridge/backend/internal/repository/sequence.go
2026-03-13 15:51:59 +08:00

91 lines
2.2 KiB
Go

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
}