style(go): apply gofmt to repository
Some checks failed
CI / verify (push) Failing after 24s

This commit is contained in:
Your Name
2026-05-06 12:22:41 +08:00
parent 43bcb499d0
commit 06eeb5776b
17 changed files with 132 additions and 133 deletions

View File

@@ -3,17 +3,17 @@ package audit
import "time" import "time"
type Event struct { type Event struct {
ID string `json:"id"` ID string `json:"id"`
SessionID string `json:"session_id,omitempty"` SessionID string `json:"session_id,omitempty"`
TicketID string `json:"ticket_id,omitempty"` TicketID string `json:"ticket_id,omitempty"`
Type string `json:"type"` Type string `json:"type"`
Action string `json:"action,omitempty"` Action string `json:"action,omitempty"`
Channel string `json:"channel,omitempty"` Channel string `json:"channel,omitempty"`
OpenID string `json:"open_id,omitempty"` OpenID string `json:"open_id,omitempty"`
ActorID string `json:"actor_id,omitempty"` ActorID string `json:"actor_id,omitempty"`
SourceIP string `json:"source_ip,omitempty"` SourceIP string `json:"source_ip,omitempty"`
Payload map[string]any `json:"payload,omitempty"` Payload map[string]any `json:"payload,omitempty"`
BeforeState map[string]any `json:"before_state,omitempty"` BeforeState map[string]any `json:"before_state,omitempty"`
AfterState map[string]any `json:"after_state,omitempty"` AfterState map[string]any `json:"after_state,omitempty"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
} }

View File

@@ -164,10 +164,10 @@ func TestEvent_TicketAndSessionFields(t *testing.T) {
// Session-scoped event // Session-scoped event
sessionEvent := Event{ sessionEvent := Event{
ID: "e2", ID: "e2",
SessionID: "s-1", SessionID: "s-1",
Type: "session", Type: "session",
Action: "message", Action: "message",
} }
if sessionEvent.SessionID != "s-1" { if sessionEvent.SessionID != "s-1" {

View File

@@ -79,8 +79,8 @@ func TestErrorMsg_UnknownCode(t *testing.T) {
func TestErrorMsg_SpecificCodes(t *testing.T) { func TestErrorMsg_SpecificCodes(t *testing.T) {
tests := []struct { tests := []struct {
code string code string
expectedMsg string expectedMsg string
}{ }{
{CS_SES_4001, "session not found"}, {CS_SES_4001, "session not found"},
{CS_SES_4002, "message rate limit exceeded"}, {CS_SES_4002, "message rate limit exceeded"},
@@ -102,38 +102,38 @@ func TestErrorMsg_SpecificCodes(t *testing.T) {
func TestErrorMsg_AllKnownCodesReturnNonEmpty(t *testing.T) { func TestErrorMsg_AllKnownCodesReturnNonEmpty(t *testing.T) {
// Verify all codes defined in the switch have non-empty messages // Verify all codes defined in the switch have non-empty messages
knownCodes := map[string]string{ knownCodes := map[string]string{
CS_SES_4001: "session not found", CS_SES_4001: "session not found",
CS_SES_4002: "message rate limit exceeded", CS_SES_4002: "message rate limit exceeded",
CS_SES_4003: "identity verification locked", CS_SES_4003: "identity verification locked",
CS_IDT_4001: "identity information mismatch", CS_IDT_4001: "identity information mismatch",
CS_IDT_4002: "verification code incorrect", CS_IDT_4002: "verification code incorrect",
CS_TICKET_4001: "ticket not found", CS_TICKET_4001: "ticket not found",
CS_TICKET_4002: "ticket already assigned", CS_TICKET_4002: "ticket already assigned",
CS_TICKET_4092: "ticket resolve conflict", CS_TICKET_4092: "ticket resolve conflict",
CS_TICKET_4093: "ticket close conflict", CS_TICKET_4093: "ticket close conflict",
CS_KB_4001: "knowledge-base entry not found", CS_KB_4001: "knowledge-base entry not found",
CS_KB_4002: "entry name already exists", CS_KB_4002: "entry name already exists",
CS_LLM_5001: "LLM service unavailable", CS_LLM_5001: "LLM service unavailable",
CS_LLM_5002: "LLM request timeout", CS_LLM_5002: "LLM request timeout",
CS_AUTH_4001: "access denied", CS_AUTH_4001: "access denied",
CS_AUTH_4031: "missing webhook signature", CS_AUTH_4031: "missing webhook signature",
CS_AUTH_4032: "invalid webhook timestamp", CS_AUTH_4032: "invalid webhook timestamp",
CS_AUTH_4033: "stale webhook request", CS_AUTH_4033: "stale webhook request",
CS_AUTH_4034: "invalid webhook signature", CS_AUTH_4034: "invalid webhook signature",
CS_HTTP_405: "method not allowed", CS_HTTP_405: "method not allowed",
CS_REQ_4001: "invalid JSON", CS_REQ_4001: "invalid JSON",
CS_REQ_4131: "request body too large", CS_REQ_4131: "request body too large",
CS_REQ_4002: "channel, open_id and content are required", CS_REQ_4002: "channel, open_id and content are required",
CS_REQ_4003: "content exceeds maximum length", CS_REQ_4003: "content exceeds maximum length",
CS_REQ_4004: "unable to read request body", CS_REQ_4004: "unable to read request body",
CS_REQ_4008: "channel is required", CS_REQ_4008: "channel is required",
CS_REQ_4005: "ticket_id and agent_id are required", CS_REQ_4005: "ticket_id and agent_id are required",
CS_REQ_4006: "ticket_id and resolution are required", CS_REQ_4006: "ticket_id and resolution are required",
CS_REQ_4007: "ticket_id and resolution are required", CS_REQ_4007: "ticket_id and resolution are required",
CS_REQ_4009: "feedback score must be between 1 and 5", CS_REQ_4009: "feedback score must be between 1 and 5",
CS_REQ_4010: "handoff reason is required", CS_REQ_4010: "handoff reason is required",
CS_SYS_5001: "internal server error", CS_SYS_5001: "internal server error",
CS_SYS_5002: "list tickets failed", CS_SYS_5002: "list tickets failed",
} }
for code, expectedMsg := range knownCodes { for code, expectedMsg := range knownCodes {
@@ -142,4 +142,4 @@ func TestErrorMsg_AllKnownCodesReturnNonEmpty(t *testing.T) {
t.Errorf("ErrorMsg(%q): expected %q, got %q", code, expectedMsg, msg) t.Errorf("ErrorMsg(%q): expected %q, got %q", code, expectedMsg, msg)
} }
} }
} }

View File

@@ -9,11 +9,11 @@ type Result struct {
} }
const ( const (
IntentQuota = "quota" IntentQuota = "quota"
IntentToken = "token" IntentToken = "token"
IntentError = "error" IntentError = "error"
IntentHandoff = "handoff" IntentHandoff = "handoff"
IntentGeneral = "general" IntentGeneral = "general"
IntentRefund = "refund" IntentRefund = "refund"
IntentSecurity = "security" IntentSecurity = "security"
) )

View File

@@ -5,10 +5,10 @@ import "time"
type Status string type Status string
const ( const (
StatusIdle Status = "idle" StatusIdle Status = "idle"
StatusProcessing Status = "processing" StatusProcessing Status = "processing"
StatusHandoff Status = "handoff" StatusHandoff Status = "handoff"
StatusClosed Status = "closed" StatusClosed Status = "closed"
) )
type MessageContext struct { type MessageContext struct {

View File

@@ -187,4 +187,4 @@ func TestSession_FullLifecycle(t *testing.T) {
if sess.Status != StatusClosed { if sess.Status != StatusClosed {
t.Error("failed to transition to Closed") t.Error("failed to transition to Closed")
} }
} }

View File

@@ -22,16 +22,16 @@ const (
) )
type Ticket struct { type Ticket struct {
ID string `json:"id"` ID string `json:"id"`
SessionID string `json:"session_id"` SessionID string `json:"session_id"`
UserID string `json:"user_id,omitempty"` UserID string `json:"user_id,omitempty"`
Priority Priority `json:"priority"` Priority Priority `json:"priority"`
Status Status `json:"status"` Status Status `json:"status"`
HandoffReason string `json:"handoff_reason"` HandoffReason string `json:"handoff_reason"`
AssignedTo string `json:"assigned_to,omitempty"` AssignedTo string `json:"assigned_to,omitempty"`
ContextSnapshot map[string]any `json:"context_snapshot"` ContextSnapshot map[string]any `json:"context_snapshot"`
Resolution string `json:"resolution,omitempty"` Resolution string `json:"resolution,omitempty"`
CreatedAt time.Time `json:"created_at"` CreatedAt time.Time `json:"created_at"`
ResolvedAt *time.Time `json:"resolved_at,omitempty"` ResolvedAt *time.Time `json:"resolved_at,omitempty"`
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"`
} }

View File

@@ -132,8 +132,8 @@ func TestTicket_Fields(t *testing.T) {
func TestTicket_ResolvedAtOptional(t *testing.T) { func TestTicket_ResolvedAtOptional(t *testing.T) {
// Test that ResolvedAt can be nil (open ticket) // Test that ResolvedAt can be nil (open ticket)
tk := Ticket{ tk := Ticket{
ID: "open-ticket", ID: "open-ticket",
Status: StatusOpen, Status: StatusOpen,
ResolvedAt: nil, ResolvedAt: nil,
} }
if tk.ResolvedAt != nil { if tk.ResolvedAt != nil {
@@ -170,4 +170,4 @@ func TestTicket_StatusTransitions(t *testing.T) {
if tk.Status != StatusClosed { if tk.Status != StatusClosed {
t.Error("failed to transition to Closed") t.Error("failed to transition to Closed")
} }
} }

View File

@@ -2,12 +2,12 @@ package ticketstats
// Stats represents aggregated ticket statistics for monitoring dashboards. // Stats represents aggregated ticket statistics for monitoring dashboards.
type Stats struct { type Stats struct {
Total int `json:"total_tickets"` Total int `json:"total_tickets"`
Open int `json:"open"` Open int `json:"open"`
Resolved int `json:"resolved"` Resolved int `json:"resolved"`
Closed int `json:"closed"` Closed int `json:"closed"`
ByChannel map[string]int `json:"by_channel"` ByChannel map[string]int `json:"by_channel"`
ByPriority map[string]int `json:"by_priority"` ByPriority map[string]int `json:"by_priority"`
HandoffCount int `json:"handoff_count"` HandoffCount int `json:"handoff_count"`
AvgResolutionTimeMinutes float64 `json:"avg_resolution_time_minutes"` AvgResolutionTimeMinutes float64 `json:"avg_resolution_time_minutes"`
} }

View File

@@ -106,13 +106,13 @@ func TestHealthHandler_Health_ReturnsOK(t *testing.T) {
func TestTicketStatsHandler_Get_Success(t *testing.T) { func TestTicketStatsHandler_Get_Success(t *testing.T) {
mock := &mockTicketStatsServiceForStats{ mock := &mockTicketStatsServiceForStats{
stats: ticketstats.Stats{ stats: ticketstats.Stats{
Total: 100, Total: 100,
Open: 30, Open: 30,
Resolved: 50, Resolved: 50,
Closed: 20, Closed: 20,
ByChannel: map[string]int{"api": 40, "web": 60}, ByChannel: map[string]int{"api": 40, "web": 60},
ByPriority: map[string]int{"P1": 10, "P2": 60, "P3": 30}, ByPriority: map[string]int{"P1": 10, "P2": 60, "P3": 30},
HandoffCount: 15, HandoffCount: 15,
AvgResolutionTimeMinutes: 45.5, AvgResolutionTimeMinutes: 45.5,
}, },
err: nil, err: nil,

View File

@@ -173,4 +173,4 @@ func TestClientIP_NoPort(t *testing.T) {
if ip != "192.168.1.100" { if ip != "192.168.1.100" {
t.Errorf("clientIP() = %s, want 192.168.1.100", ip) t.Errorf("clientIP() = %s, want 192.168.1.100", ip)
} }
} }

View File

@@ -7,7 +7,6 @@ import (
"strconv" "strconv"
"testing" "testing"
"time" "time"
) )
// TestWebhookSecurity_InvalidTimestampFormat covers CS_AUTH_4032: // TestWebhookSecurity_InvalidTimestampFormat covers CS_AUTH_4032:

View File

@@ -143,4 +143,4 @@ func TestRateLimiter_WithRateLimit_XForwardedFor(t *testing.T) {
if rec.Code != http.StatusOK { if rec.Code != http.StatusOK {
t.Errorf("different IP: expected 200, got %d", rec.Code) t.Errorf("different IP: expected 200, got %d", rec.Code)
} }
} }

View File

@@ -7,10 +7,10 @@ import (
"time" "time"
"github.com/bridge/ai-customer-service/internal/domain/audit" "github.com/bridge/ai-customer-service/internal/domain/audit"
intentdomain "github.com/bridge/ai-customer-service/internal/domain/intent"
"github.com/bridge/ai-customer-service/internal/domain/message" "github.com/bridge/ai-customer-service/internal/domain/message"
"github.com/bridge/ai-customer-service/internal/domain/session" "github.com/bridge/ai-customer-service/internal/domain/session"
"github.com/bridge/ai-customer-service/internal/domain/ticket" "github.com/bridge/ai-customer-service/internal/domain/ticket"
intentdomain "github.com/bridge/ai-customer-service/internal/domain/intent"
"github.com/bridge/ai-customer-service/internal/service/handoff" "github.com/bridge/ai-customer-service/internal/service/handoff"
intentservice "github.com/bridge/ai-customer-service/internal/service/intent" intentservice "github.com/bridge/ai-customer-service/internal/service/intent"
"github.com/bridge/ai-customer-service/internal/service/reply" "github.com/bridge/ai-customer-service/internal/service/reply"

View File

@@ -160,4 +160,4 @@ func TestGenerate_ContextCancellation(t *testing.T) {
if result == "" { if result == "" {
t.Error("Generate with cancelled context should still return answer") t.Error("Generate with cancelled context should still return answer")
} }
} }

View File

@@ -26,12 +26,12 @@ func TestDialogService_AC02_IntentMatrix(t *testing.T) {
svc := dialog.NewService(sessions, audits, tickets, dedup, intentservice.NewService(), reply.NewService(knowledge), handoff.NewService()) svc := dialog.NewService(sessions, audits, tickets, dedup, intentservice.NewService(), reply.NewService(knowledge), handoff.NewService())
tests := []struct { tests := []struct {
name string name string
content string content string
wantIntent string wantIntent string
wantHandoff bool wantHandoff bool
wantPriority string // empty if no handoff expected wantPriority string // empty if no handoff expected
wantReply bool // whether to check reply is non-empty wantReply bool // whether to check reply is non-empty
}{ }{
{ {
name: "AC-02: 退款意图 → P1 handoff", name: "AC-02: 退款意图 → P1 handoff",
@@ -58,11 +58,11 @@ func TestDialogService_AC02_IntentMatrix(t *testing.T) {
wantReply: true, wantReply: true,
}, },
{ {
name: "AC-02: 正常查询 → bot 回复无 handoff", name: "AC-02: 正常查询 → bot 回复无 handoff",
content: "查询额度", content: "查询额度",
wantIntent: "quota", wantIntent: "quota",
wantHandoff: false, wantHandoff: false,
wantReply: true, wantReply: true,
}, },
} }
@@ -70,9 +70,9 @@ func TestDialogService_AC02_IntentMatrix(t *testing.T) {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
result, err := svc.Process(context.Background(), &message.UnifiedMessage{ result, err := svc.Process(context.Background(), &message.UnifiedMessage{
MessageID: "m_" + tc.name, MessageID: "m_" + tc.name,
Channel: "widget", Channel: "widget",
OpenID: "u_" + tc.name, OpenID: "u_" + tc.name,
Content: tc.content, Content: tc.content,
}) })
if err != nil { if err != nil {
t.Fatalf("Process() error = %v", err) t.Fatalf("Process() error = %v", err)

View File

@@ -41,27 +41,27 @@ func setupTicketStatsHandler(stats ticketstats.Stats) (*httptest.ResponseRecorde
// ticketStatsResponse mirrors the JSON shape of ticketstats.Stats. // ticketStatsResponse mirrors the JSON shape of ticketstats.Stats.
type ticketStatsResponse struct { type ticketStatsResponse struct {
Total int `json:"total_tickets"` Total int `json:"total_tickets"`
Open int `json:"open"` Open int `json:"open"`
Resolved int `json:"resolved"` Resolved int `json:"resolved"`
Closed int `json:"closed"` Closed int `json:"closed"`
ByChannel map[string]int `json:"by_channel"` ByChannel map[string]int `json:"by_channel"`
ByPriority map[string]int `json:"by_priority"` ByPriority map[string]int `json:"by_priority"`
HandoffCount int `json:"handoff_count"` HandoffCount int `json:"handoff_count"`
AvgResolutionTimeMinutes float64 `json:"avg_resolution_time_minutes"` AvgResolutionTimeMinutes float64 `json:"avg_resolution_time_minutes"`
} }
// TestTicketStats_Success verifies the stats endpoint returns correct // TestTicketStats_Success verifies the stats endpoint returns correct
// counts when the store has tickets. // counts when the store has tickets.
func TestTicketStats_Success(t *testing.T) { func TestTicketStats_Success(t *testing.T) {
stats := ticketstats.Stats{ stats := ticketstats.Stats{
Total: 100, Total: 100,
Open: 30, Open: 30,
Resolved: 50, Resolved: 50,
Closed: 20, Closed: 20,
ByChannel: map[string]int{"api": 40, "web": 60}, ByChannel: map[string]int{"api": 40, "web": 60},
ByPriority: map[string]int{"P1": 10, "P2": 60, "P3": 30}, ByPriority: map[string]int{"P1": 10, "P2": 60, "P3": 30},
HandoffCount: 15, HandoffCount: 15,
AvgResolutionTimeMinutes: 45.5, AvgResolutionTimeMinutes: 45.5,
} }
@@ -114,13 +114,13 @@ func TestTicketStats_Success(t *testing.T) {
// TestTicketStats_Empty verifies that an empty store returns all-zero stats. // TestTicketStats_Empty verifies that an empty store returns all-zero stats.
func TestTicketStats_Empty(t *testing.T) { func TestTicketStats_Empty(t *testing.T) {
stats := ticketstats.Stats{ stats := ticketstats.Stats{
Total: 0, Total: 0,
Open: 0, Open: 0,
Resolved: 0, Resolved: 0,
Closed: 0, Closed: 0,
ByChannel: map[string]int{}, ByChannel: map[string]int{},
ByPriority: map[string]int{}, ByPriority: map[string]int{},
HandoffCount: 0, HandoffCount: 0,
AvgResolutionTimeMinutes: 0, AvgResolutionTimeMinutes: 0,
} }
@@ -163,14 +163,14 @@ func TestTicketStats_GroupedCounts(t *testing.T) {
Resolved: 10, Resolved: 10,
Closed: 5, Closed: 5,
ByChannel: map[string]int{ ByChannel: map[string]int{
"api": 8, "api": 8,
"web": 12, "web": 12,
"wechat": 5, "wechat": 5,
}, },
ByPriority: map[string]int{ ByPriority: map[string]int{
"P1": 3, "P1": 3,
"P2": 15, "P2": 15,
"P3": 7, "P3": 7,
}, },
HandoffCount: 6, HandoffCount: 6,
AvgResolutionTimeMinutes: 120.0, AvgResolutionTimeMinutes: 120.0,