Files
kra-new/internal/data/business/binding_repo.go
T

162 lines
5.9 KiB
Go

package business
import (
"context"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"kra/internal/biz/business"
)
type bindingLockPO struct {
SourceDB string `gorm:"column:source_db;primaryKey;size:50"`
PlatformBusierID uint64 `gorm:"primaryKey;autoIncrement:false"`
}
func (bindingLockPO) TableName() string { return "sys_business_binding_locks" }
type bindingRepo struct{ data Provider }
func NewBindingRepo(data Provider) business.BindingRepo { return &bindingRepo{data: data} }
type bindingTxKey struct{}
func (r *bindingRepo) db(ctx context.Context) *gorm.DB {
if tx, ok := ctx.Value(bindingTxKey{}).(*gorm.DB); ok {
return tx.WithContext(ctx)
}
return r.data.DB().WithContext(ctx)
}
func (r *bindingRepo) WithPlatformLock(ctx context.Context, key business.PlatformKey, fn func(context.Context) error) error {
err := r.data.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
// 单独的键锁覆盖“尚无绑定记录”的并发场景,不修改平台数据库。
lock := bindingLockPO{SourceDB: key.SourceDB, PlatformBusierID: key.BusierID}
if err := tx.Clauses(clause.OnConflict{DoNothing: true}).Create(&lock).Error; err != nil {
return business.ErrStorage
}
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("source_db = ? AND platform_busier_id = ?", key.SourceDB, key.BusierID).First(&lock).Error; err != nil {
return business.ErrStorage
}
return fn(context.WithValue(ctx, bindingTxKey{}, tx))
})
return storageError(err)
}
func toBizBinding(po bindingPO) *business.Binding {
return &business.Binding{UserID: po.UserID, PlatformKey: business.PlatformKey{SourceDB: po.SourceDB, BusierID: po.PlatformBusierID}, Account: po.PlatformBusierAccount,
Status: po.Status, RejectReason: po.RejectReason, CreatedAt: po.CreateTime, ReviewTime: po.ReviewTime, ReviewAccount: po.ReviewAccount}
}
func newBinding(b *business.Binding) bindingPO {
return bindingPO{UserID: b.UserID, SourceDB: b.SourceDB, PlatformBusierID: b.BusierID, PlatformBusierAccount: b.Account, Status: b.Status,
RejectReason: b.RejectReason, CreateTime: b.CreatedAt, ReviewTime: b.ReviewTime, ReviewAccount: b.ReviewAccount}
}
func (r *bindingRepo) ForPlatform(ctx context.Context, key business.PlatformKey) ([]*business.Binding, error) {
var pos []bindingPO
if err := r.db(ctx).Where("source_db = ? AND platform_busier_id = ?", key.SourceDB, key.BusierID).Find(&pos).Error; err != nil {
return nil, business.ErrStorage
}
rows := make([]*business.Binding, 0, len(pos))
for _, po := range pos {
rows = append(rows, toBizBinding(po))
}
return rows, nil
}
func (r *bindingRepo) Save(ctx context.Context, b *business.Binding) error {
po := newBinding(b)
err := r.db(ctx).Clauses(clause.OnConflict{Columns: []clause.Column{{Name: "user_id"}, {Name: "source_db"}, {Name: "platform_busier_id"}},
DoUpdates: clause.AssignmentColumns([]string{"platform_busier_account", "status", "reject_reason", "create_time", "review_time", "review_account"})}).Create(&po).Error
if err != nil {
return business.ErrStorage
}
return nil
}
func (r *bindingRepo) Delete(ctx context.Context, userID uint, key business.PlatformKey) error {
result := r.db(ctx).Where("user_id = ? AND source_db = ? AND platform_busier_id = ?", userID, key.SourceDB, key.BusierID).Delete(&bindingPO{})
if result.Error != nil {
return business.ErrStorage
}
if result.RowsAffected == 0 {
return business.ErrBindingNotFound
}
return nil
}
func (r *bindingRepo) List(ctx context.Context, scope business.UserScope, q business.BindingFilter, options ...business.ListOption) ([]*business.Binding, int64, error) {
db := r.db(ctx).Table("sys_user_bind_platform b").Joins("JOIN sys_users u ON u.id=b.user_id AND u.deleted_at IS NULL")
db = applyUserScope(db, scope, "u.")
if q.UserID != 0 {
db = db.Where("b.user_id = ?", q.UserID)
}
if len(q.SourceDBs) > 0 {
db = db.Where("b.source_db IN ?", q.SourceDBs)
}
if q.Account != "" {
db = db.Where("b.platform_busier_account LIKE ?", "%"+q.Account+"%")
}
if q.Status != "" {
db = db.Where("b.status = ?", q.Status)
}
var total int64
if err := db.Count(&total).Error; err != nil {
return nil, 0, business.ErrStorage
}
o := business.NewListOptions(options...)
var pos []struct {
Binding bindingPO `gorm:"embedded"`
UserName string
}
if err := db.Select("b.*,u.username AS user_name").Order("b.create_time DESC").Order("b.source_db,b.platform_busier_id,b.user_id").Offset(o.Offset).Limit(o.Limit).Scan(&pos).Error; err != nil {
return nil, 0, business.ErrStorage
}
rows := make([]*business.Binding, 0, len(pos))
keys := make(map[business.PlatformKey]bool)
for _, po := range pos {
b := toBizBinding(po.Binding)
zero := int64(0)
b.PromoteCount = &zero
b.UserName = po.UserName
rows = append(rows, b)
if b.Status == "0" {
keys[b.PlatformKey] = true
}
}
if len(keys) == 0 {
return rows, total, nil
}
available, err := businessTableAvailable(r.db(ctx), "tab_promote")
if err != nil {
return nil, 0, err
}
if !available {
for _, b := range rows {
if b.Status == "0" {
b.PromoteCount = nil
}
}
return rows, total, nil
}
query := r.db(ctx).Table("tab_promote").Where("promote_level = ?", 1)
pairs := r.db(ctx).Session(&gorm.Session{NewDB: true})
for key := range keys {
pairs = pairs.Or("(source_db = ? AND busier_id = ?)", key.SourceDB, key.BusierID)
}
var counts []struct {
SourceDB string `gorm:"column:source_db"`
BusierID uint64
Total int64
}
if err := query.Where(pairs).Select("source_db,busier_id,COUNT(*) AS total").Group("source_db,busier_id").Scan(&counts).Error; err != nil {
return nil, 0, business.ErrStorage
}
byKey := make(map[business.PlatformKey]int64, len(counts))
for _, c := range counts {
byKey[business.PlatformKey{SourceDB: c.SourceDB, BusierID: c.BusierID}] = c.Total
}
for _, b := range rows {
if b.Status == "0" {
count := byKey[b.PlatformKey]
b.PromoteCount = &count
}
}
return rows, total, nil
}