refactor(ticket): simplify ticket number generation and remove SQLite lock
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/mlogclub/simple/sqls"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -21,16 +22,11 @@ type ticketNoSequenceService struct {
|
||||
ticketNoSQLiteMu sync.Mutex
|
||||
}
|
||||
|
||||
func (s *ticketNoSequenceService) Next(tx *gorm.DB, now time.Time) (string, error) {
|
||||
if tx == nil {
|
||||
return "", fmt.Errorf("ticket number transaction is required")
|
||||
}
|
||||
if tx.Dialector.Name() == "sqlite" {
|
||||
s.ticketNoSQLiteMu.Lock()
|
||||
defer s.ticketNoSQLiteMu.Unlock()
|
||||
return s.nextWithRetry(tx, now)
|
||||
}
|
||||
return s.nextWithRetry(tx, now)
|
||||
func (s *ticketNoSequenceService) Next(now time.Time) (string, error) {
|
||||
s.ticketNoSQLiteMu.Lock()
|
||||
defer s.ticketNoSQLiteMu.Unlock()
|
||||
|
||||
return s.nextWithRetry(sqls.DB(), now)
|
||||
}
|
||||
|
||||
func (s *ticketNoSequenceService) nextWithRetry(tx *gorm.DB, now time.Time) (string, error) {
|
||||
|
||||
@@ -183,7 +183,6 @@ func (s *ticketService) CreateTicket(req request.CreateTicketRequest, operator *
|
||||
return nil, err
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
ticket := &models.Ticket{
|
||||
Title: title,
|
||||
Description: description,
|
||||
@@ -195,27 +194,25 @@ func (s *ticketService) CreateTicket(req request.CreateTicketRequest, operator *
|
||||
CurrentAssigneeID: req.CurrentAssigneeID,
|
||||
AuditFields: utils.BuildAuditFields(operator),
|
||||
}
|
||||
ticket.UpdatedAt = now
|
||||
|
||||
if err := withSQLiteTicketCreateLock(sqls.DB(), func() error {
|
||||
return sqls.WithTransaction(func(ctx *sqls.TxContext) error {
|
||||
ticketNo, err := TicketNoSequenceService.nextWithRetry(ctx.Tx, now)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ticket.TicketNo = ticketNo
|
||||
if err := repositories.TicketRepository.Create(ctx.Tx, ticket); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := TicketTagService.ReplaceTicketTags(ctx.Tx, ticket.ID, tagIDs, operator); err != nil {
|
||||
return err
|
||||
}
|
||||
return repositories.TicketProgressRepository.Create(ctx.Tx, &models.TicketProgress{
|
||||
TicketID: ticket.ID,
|
||||
Content: "创建工单",
|
||||
AuthorID: operator.UserID,
|
||||
CreatedAt: now,
|
||||
})
|
||||
ticketNo, err := TicketNoSequenceService.Next(ticket.CreatedAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := sqls.WithTransaction(func(ctx *sqls.TxContext) error {
|
||||
ticket.TicketNo = ticketNo
|
||||
if err := repositories.TicketRepository.Create(ctx.Tx, ticket); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := TicketTagService.ReplaceTicketTags(ctx.Tx, ticket.ID, tagIDs, operator); err != nil {
|
||||
return err
|
||||
}
|
||||
return repositories.TicketProgressRepository.Create(ctx.Tx, &models.TicketProgress{
|
||||
TicketID: ticket.ID,
|
||||
Content: "创建工单",
|
||||
AuthorID: operator.UserID,
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
@@ -228,14 +225,6 @@ func (s *ticketService) CreateTicket(req request.CreateTicketRequest, operator *
|
||||
return s.Get(ticket.ID), nil
|
||||
}
|
||||
|
||||
func withSQLiteTicketCreateLock(db *gorm.DB, fn func() error) error {
|
||||
if db != nil && db.Dialector.Name() == "sqlite" {
|
||||
TicketNoSequenceService.ticketNoSQLiteMu.Lock()
|
||||
defer TicketNoSequenceService.ticketNoSQLiteMu.Unlock()
|
||||
}
|
||||
return fn()
|
||||
}
|
||||
|
||||
func (s *ticketService) CreateFromConversation(req request.CreateTicketFromConversationRequest, operator *dto.AuthPrincipal) (*models.Ticket, error) {
|
||||
if operator == nil {
|
||||
return nil, errorsx.Unauthorized("未登录或登录已过期")
|
||||
|
||||
@@ -424,21 +424,15 @@ func TestTicketServiceTicketNoNextConcurrent(t *testing.T) {
|
||||
errs := make(chan error, count)
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
for range count {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
err := sqls.WithTransaction(func(ctx *sqls.TxContext) error {
|
||||
ticketNo, err := services.TicketNoSequenceService.Next(ctx.Tx, time.Now())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
results <- ticketNo
|
||||
return nil
|
||||
})
|
||||
ticketNo, err := services.TicketNoSequenceService.Next(time.Now())
|
||||
if err != nil {
|
||||
errs <- err
|
||||
}
|
||||
results <- ticketNo
|
||||
}()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user