fix(server): refactor server model methods to support transaction handling

This commit is contained in:
Chang lue Tsen 2025-05-06 13:21:27 +09:00
parent 38bcacc9e7
commit 3cadd9e621
3 changed files with 32 additions and 33 deletions

View File

@ -2,11 +2,11 @@ package server
import ( import (
"context" "context"
"github.com/perfect-panel/server/pkg/tool"
"github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/internal/types" "github.com/perfect-panel/server/internal/types"
"github.com/perfect-panel/server/pkg/logger" "github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/tool"
"github.com/perfect-panel/server/pkg/xerr" "github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors" "github.com/pkg/errors"
"gorm.io/gorm" "gorm.io/gorm"
@ -29,20 +29,23 @@ func NewDeleteNodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Delete
func (l *DeleteNodeLogic) DeleteNode(req *types.DeleteNodeRequest) error { func (l *DeleteNodeLogic) DeleteNode(req *types.DeleteNodeRequest) error {
err := l.svcCtx.DB.Transaction(func(tx *gorm.DB) error { err := l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
// Delete server // Delete server
err := l.svcCtx.ServerModel.Delete(l.ctx, req.Id) err := l.svcCtx.ServerModel.Delete(l.ctx, req.Id, tx)
if err != nil { if err != nil {
return err return err
} }
// Delete server to subscribe // Delete server to subscribe
subs, err := l.svcCtx.SubscribeModel.QuerySubscribeIdsByServerIdAndServerGroupId(l.ctx, req.Id, 0) subs, err := l.svcCtx.SubscribeModel.QuerySubscribeIdsByServerIdAndServerGroupId(l.ctx, req.Id, 0)
if err != nil { if err != nil {
l.Logger.Errorf("[DeleteNode] QuerySubscribeIdsByServerIdAndServerGroupId error: %v", err.Error())
return err return err
} }
for _, sub := range subs { for _, sub := range subs {
servers := tool.StringToInt64Slice(sub.Server) servers := tool.StringToInt64Slice(sub.Server)
newServers := tool.RemoveElementBySlice(servers, req.Id) newServers := tool.RemoveElementBySlice(servers, req.Id)
sub.Server = tool.Int64SliceToString(newServers) sub.Server = tool.Int64SliceToString(newServers)
if err = l.svcCtx.SubscribeModel.Update(l.ctx, sub); err != nil { if err = l.svcCtx.SubscribeModel.Update(l.ctx, sub, tx); err != nil {
l.Logger.Errorf("[DeleteNode] UpdateSubscribe error: %v", err.Error())
return err return err
} }
} }

View File

@ -23,10 +23,10 @@ type (
customServerLogicModel customServerLogicModel
} }
serverModel interface { serverModel interface {
Insert(ctx context.Context, data *Server) error Insert(ctx context.Context, data *Server, tx ...*gorm.DB) error
FindOne(ctx context.Context, id int64) (*Server, error) FindOne(ctx context.Context, id int64) (*Server, error)
Update(ctx context.Context, data *Server) error Update(ctx context.Context, data *Server, tx ...*gorm.DB) error
Delete(ctx context.Context, id int64) error Delete(ctx context.Context, id int64, tx ...*gorm.DB) error
Transaction(ctx context.Context, fn func(db *gorm.DB) error) error Transaction(ctx context.Context, fn func(db *gorm.DB) error) error
} }
@ -77,8 +77,11 @@ func (m *defaultServerModel) getCacheKeys(data *Server) []string {
return cacheKeys return cacheKeys
} }
func (m *defaultServerModel) Insert(ctx context.Context, data *Server) error { func (m *defaultServerModel) Insert(ctx context.Context, data *Server, tx ...*gorm.DB) error {
err := m.ExecCtx(ctx, func(conn *gorm.DB) error { err := m.ExecCtx(ctx, func(conn *gorm.DB) error {
if len(tx) > 0 {
conn = tx[0]
}
return conn.Create(&data).Error return conn.Create(&data).Error
}, m.getCacheKeys(data)...) }, m.getCacheKeys(data)...)
return err return err
@ -98,19 +101,21 @@ func (m *defaultServerModel) FindOne(ctx context.Context, id int64) (*Server, er
} }
} }
func (m *defaultServerModel) Update(ctx context.Context, data *Server) error { func (m *defaultServerModel) Update(ctx context.Context, data *Server, tx ...*gorm.DB) error {
old, err := m.FindOne(ctx, data.Id) old, err := m.FindOne(ctx, data.Id)
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return err return err
} }
err = m.ExecCtx(ctx, func(conn *gorm.DB) error { err = m.ExecCtx(ctx, func(conn *gorm.DB) error {
db := conn if len(tx) > 0 {
return db.Save(data).Error conn = tx[0]
}
return conn.Save(data).Error
}, m.getCacheKeys(old)...) }, m.getCacheKeys(old)...)
return err return err
} }
func (m *defaultServerModel) Delete(ctx context.Context, id int64) error { func (m *defaultServerModel) Delete(ctx context.Context, id int64, tx ...*gorm.DB) error {
data, err := m.FindOne(ctx, id) data, err := m.FindOne(ctx, id)
if err != nil { if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
@ -119,8 +124,10 @@ func (m *defaultServerModel) Delete(ctx context.Context, id int64) error {
return err return err
} }
err = m.ExecCtx(ctx, func(conn *gorm.DB) error { err = m.ExecCtx(ctx, func(conn *gorm.DB) error {
db := conn if len(tx) > 0 {
return db.Delete(&Server{}, id).Error conn = tx[0]
}
return conn.Delete(&Server{}, id).Error
}, m.getCacheKeys(data)...) }, m.getCacheKeys(data)...)
return err return err
} }

View File

@ -52,33 +52,26 @@ func (*Server) TableName() string {
func (s *Server) BeforeDelete(tx *gorm.DB) error { func (s *Server) BeforeDelete(tx *gorm.DB) error {
logger.Debugf("[Server] BeforeDelete") logger.Debugf("[Server] BeforeDelete")
if err := tx.Exec("UPDATE `server` SET sort = sort - 1 WHERE sort > ?", s.Sort).Error; err != nil { if err := tx.Exec("UPDATE `server` SET sort = sort - 1 WHERE sort > ?", s.Sort).Error; err != nil {
return err return err
} }
// 删除后重新排序,防止因 sort 缺口导致问题
if err := reorderSort(tx); err != nil {
return err
}
return nil return nil
} }
func (s *Server) BeforeUpdate(tx *gorm.DB) error { func (s *Server) BeforeUpdate(tx *gorm.DB) error {
logger.Debugf("[Server] BeforeUpdate") logger.Debugf("[Server] BeforeUpdate")
var count int64 var count int64
if err := tx.Model(&Server{}).Where("sort = ? AND id != ?", s.Sort, s.Id).Count(&count).Error; err != nil { if err := tx.Set("gorm:query_option", "FOR UPDATE").Model(&Server{}).
Where("sort = ? AND id != ?", s.Sort, s.Id).Count(&count).Error; err != nil {
return err return err
} }
if count > 0 { if count > 0 {
logger.Debugf("[Server] Duplicate sort found, reordering...") var maxSort int64
if err := reorderSort(tx); err != nil { if err := tx.Model(&Server{}).Select("MAX(sort)").Scan(&maxSort).Error; err != nil {
return err return err
} }
s.Sort = maxSort + 1
} }
return nil return nil
} }
@ -191,17 +184,13 @@ func (RuleGroup) TableName() string {
} }
func reorderSort(tx *gorm.DB) error { func reorderSort(tx *gorm.DB) error {
var servers []*Server var servers []Server
if err := tx.Model(&Server{}).Order("sort ASC").Find(&servers).Error; err != nil { if err := tx.Order("sort, id").Find(&servers).Error; err != nil {
return err return err
} }
for i, server := range servers { for i, server := range servers {
newSort := int64(i + 1) if server.Sort != int64(i)+1 {
if server.Sort != newSort { if err := tx.Exec("UPDATE `server` SET sort = ? WHERE id = ?", i+1, server.Id).Error; err != nil {
if err := tx.Model(&Server{}).
Where("id = ?", server.Id).
Update("sort", newSort).Error; err != nil {
return err return err
} }
} }