Files
2024-08-12 21:38:09 +08:00

169 lines
3.8 KiB
Go

package commit
import (
"context"
"errors"
"github.com/eolinker/go-common/utils"
"strings"
"time"
"github.com/eolinker/go-common/store"
"github.com/google/uuid"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
var (
_ ICommitStore[any] = (*Store[any])(nil)
)
type ICommitStore[H any] interface {
Save(ctx context.Context, key string, target string, h *H) error
Latest(ctx context.Context, key string, target ...string) ([]*Commit[H], error)
Get(ctx context.Context, uuid string) (*Commit[H], error)
List(ctx context.Context, uuids ...string) ([]*Commit[H], error)
}
type Store[H any] struct {
db store.IDB `autowired:""`
latestTableName string
commitTableName string
name string
}
func (h *Store[H]) List(ctx context.Context, uuids ...string) ([]*Commit[H], error) {
if len(uuids) == 0 {
return nil, errors.New("uuids is empty")
}
db := h.db.DB(ctx).Table(h.commitTableName)
if len(uuids) == 1 {
db = db.Where("`uuid` = ?", uuids[0])
} else {
db = db.Where("`uuid` in ?", uuids)
}
var commit = make([]*Commit[H], 0, len(uuids))
err := db.Find(&commit).Error
if err != nil {
return nil, err
}
return commit, nil
}
func NewCommitStore[H any](name string) *Store[H] {
return &Store[H]{
name: name,
latestTableName: name + "_latest",
commitTableName: name + "_commit",
}
}
func (h *Store[H]) OnComplete() {
onceMigrate(h.name, func() {
db := h.db.DB(context.Background())
err := db.Table(h.commitTableName).AutoMigrate(&Commit[H]{})
if err != nil {
panic(err)
}
err = db.Table(h.latestTableName).AutoMigrate(&Latest{})
if err != nil {
panic(err)
}
})
}
var (
latestUniques = []clause.Column{
{
Name: "target",
}, {
Name: "type",
},
}
)
func (h *Store[H]) Save(ctx context.Context, key string, target string, commit *H) error {
hid := uuid.NewString()
ho := &Commit[H]{
Id: 0,
UUID: hid,
Target: target,
Key: key,
Data: commit,
CreateAt: time.Now(),
}
lo := &Latest{
Id: 0,
Target: target,
Key: key,
Latest: hid,
}
return h.db.DB(ctx).Transaction(func(tx *gorm.DB) error {
err := tx.Table(h.commitTableName).Create(ho).Error
if err != nil {
return err
}
return tx.Table(h.latestTableName).Clauses(clause.OnConflict{
Columns: latestUniques,
UpdateAll: true,
}).Create(lo).Error
})
}
func (h *Store[H]) Latest(ctx context.Context, key string, target ...string) ([]*Commit[H], error) {
wheres := make([]string, 0, 2)
args := make([]interface{}, 0, 2)
if key == "" && len(target) == 0 {
return nil, errors.New("key or target is required")
}
if key != "" {
wheres = append(wheres, "`key` = ?")
args = append(args, key)
}
if len(target) > 0 {
if len(target) > 1 {
wheres = append(wheres, "`target` in ?")
args = append(args, target)
} else {
wheres = append(wheres, "`target` = ?")
args = append(args, target[0])
}
}
latest := make([]*Latest, 0)
err := h.db.DB(ctx).Debug().Table(h.latestTableName).Where(strings.Join(wheres, " and "), args...).Find(&latest).Error
if err != nil {
return nil, err
}
latestUUUID := utils.SliceToSlice(latest, func(l *Latest) string { return l.Latest })
ho := make([]*Commit[H], 0, len(latest))
if len(latestUUUID) == 0 {
return nil, nil
}
if len(latestUUUID) > 1 {
err = h.db.DB(ctx).Table(h.commitTableName).Where("`uuid` in ?", latestUUUID).Find(&ho).Error
} else {
err = h.db.DB(ctx).Table(h.commitTableName).Where("`uuid` = ?", latestUUUID[0]).Find(&ho).Error
}
if err != nil {
return nil, err
}
return ho, nil
}
func (h *Store[H]) Get(ctx context.Context, uuid string) (*Commit[H], error) {
var commit = new(Commit[H])
err := h.db.DB(ctx).Table(h.commitTableName).Where("`uuid` = ?", uuid).First(commit).Error
if err != nil {
return nil, err
}
return commit, nil
}