Files
ai-customer-service/internal/store/memory/ticket_workflow.go
Your Name cf46b27610 fix: P0-1 RateLimiter并发写安全 + P0-2工单操作错误码区分 + P1 rows.Close修复
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()
2026-05-01 20:56:25 +08:00

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")
}