P0-1 (limits.go): Allow()方法改为全程使用写锁保护counters map读写,避免RLock写入时的data race P0-2 (ticket_workflow.go+ticket_handler.go): Assign/Resolve/Close操作先查询ticket存在性和状态,返回明确的CS_TICKET_4001/CS_TKT_4002/CS_TICKET_4092/CS_TICKET_4093错误码,handler根据错误前缀路由HTTP状态码 P1-1 (ticket_store.go): 移除GetStats中3处手动rows.Close(),只保留defer Close()
76 lines
2.1 KiB
Go
76 lines
2.1 KiB
Go
package memory
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/bridge/ai-customer-service/internal/domain/ticket"
|
|
)
|
|
|
|
func (s *TicketStore) ListOpen(_ context.Context, limit int) ([]ticket.Ticket, error) {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
if limit <= 0 || limit > len(s.tickets) {
|
|
limit = len(s.tickets)
|
|
}
|
|
items := make([]ticket.Ticket, 0, limit)
|
|
for _, item := range s.tickets {
|
|
if item.Status == ticket.StatusOpen || item.Status == ticket.StatusAssigned || item.Status == ticket.StatusProcessing {
|
|
items = append(items, item)
|
|
if len(items) == limit {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
func (s *TicketStore) Assign(_ context.Context, ticketID, agentID, _, _ string, now time.Time) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
for i := range s.tickets {
|
|
if s.tickets[i].ID == ticketID && s.tickets[i].Status == ticket.StatusOpen {
|
|
s.tickets[i].AssignedTo = agentID
|
|
s.tickets[i].Status = ticket.StatusAssigned
|
|
s.tickets[i].UpdatedAt = now
|
|
return nil
|
|
}
|
|
}
|
|
return fmt.Errorf("ticket not assignable")
|
|
}
|
|
|
|
func (s *TicketStore) Resolve(_ context.Context, ticketID, resolution, _, _ string, now time.Time) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
for i := range s.tickets {
|
|
if s.tickets[i].ID == ticketID {
|
|
resolvedAt := now
|
|
s.tickets[i].Resolution = resolution
|
|
s.tickets[i].Status = ticket.StatusResolved
|
|
s.tickets[i].ResolvedAt = &resolvedAt
|
|
s.tickets[i].UpdatedAt = now
|
|
return nil
|
|
}
|
|
}
|
|
return fmt.Errorf("ticket not resolvable")
|
|
}
|
|
|
|
func (s *TicketStore) Close(_ context.Context, ticketID, resolution, _, _ string, now time.Time) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
for i := range s.tickets {
|
|
if s.tickets[i].ID == ticketID && (s.tickets[i].Status == ticket.StatusResolved || s.tickets[i].Status == ticket.StatusAssigned || s.tickets[i].Status == ticket.StatusProcessing) {
|
|
resolvedAt := now
|
|
s.tickets[i].Resolution = resolution
|
|
s.tickets[i].Status = ticket.StatusClosed
|
|
if s.tickets[i].ResolvedAt == nil {
|
|
s.tickets[i].ResolvedAt = &resolvedAt
|
|
}
|
|
s.tickets[i].UpdatedAt = now
|
|
return nil
|
|
}
|
|
}
|
|
return fmt.Errorf("ticket not closable")
|
|
}
|