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"
type Event struct {
ID string `json:"id"`
SessionID string `json:"session_id,omitempty"`
TicketID string `json:"ticket_id,omitempty"`
Type string `json:"type"`
Action string `json:"action,omitempty"`
Channel string `json:"channel,omitempty"`
OpenID string `json:"open_id,omitempty"`
ActorID string `json:"actor_id,omitempty"`
SourceIP string `json:"source_ip,omitempty"`
Payload map[string]any `json:"payload,omitempty"`
BeforeState map[string]any `json:"before_state,omitempty"`
AfterState map[string]any `json:"after_state,omitempty"`
CreatedAt time.Time `json:"created_at"`
ID string `json:"id"`
SessionID string `json:"session_id,omitempty"`
TicketID string `json:"ticket_id,omitempty"`
Type string `json:"type"`
Action string `json:"action,omitempty"`
Channel string `json:"channel,omitempty"`
OpenID string `json:"open_id,omitempty"`
ActorID string `json:"actor_id,omitempty"`
SourceIP string `json:"source_ip,omitempty"`
Payload map[string]any `json:"payload,omitempty"`
BeforeState map[string]any `json:"before_state,omitempty"`
AfterState map[string]any `json:"after_state,omitempty"`
CreatedAt time.Time `json:"created_at"`
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,12 +2,12 @@ package ticketstats
// Stats represents aggregated ticket statistics for monitoring dashboards.
type Stats struct {
Total int `json:"total_tickets"`
Open int `json:"open"`
Resolved int `json:"resolved"`
Closed int `json:"closed"`
ByChannel map[string]int `json:"by_channel"`
ByPriority map[string]int `json:"by_priority"`
HandoffCount int `json:"handoff_count"`
AvgResolutionTimeMinutes float64 `json:"avg_resolution_time_minutes"`
Total int `json:"total_tickets"`
Open int `json:"open"`
Resolved int `json:"resolved"`
Closed int `json:"closed"`
ByChannel map[string]int `json:"by_channel"`
ByPriority map[string]int `json:"by_priority"`
HandoffCount int `json:"handoff_count"`
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) {
mock := &mockTicketStatsServiceForStats{
stats: ticketstats.Stats{
Total: 100,
Open: 30,
Resolved: 50,
Closed: 20,
ByChannel: map[string]int{"api": 40, "web": 60},
ByPriority: map[string]int{"P1": 10, "P2": 60, "P3": 30},
HandoffCount: 15,
Total: 100,
Open: 30,
Resolved: 50,
Closed: 20,
ByChannel: map[string]int{"api": 40, "web": 60},
ByPriority: map[string]int{"P1": 10, "P2": 60, "P3": 30},
HandoffCount: 15,
AvgResolutionTimeMinutes: 45.5,
},
err: nil,

View File

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

View File

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

View File

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

View File

@@ -7,10 +7,10 @@ import (
"time"
"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/session"
"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"
intentservice "github.com/bridge/ai-customer-service/internal/service/intent"
"github.com/bridge/ai-customer-service/internal/service/reply"

View File

@@ -160,4 +160,4 @@ func TestGenerate_ContextCancellation(t *testing.T) {
if result == "" {
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())
tests := []struct {
name string
content string
wantIntent string
wantHandoff bool
wantPriority string // empty if no handoff expected
wantReply bool // whether to check reply is non-empty
name string
content string
wantIntent string
wantHandoff bool
wantPriority string // empty if no handoff expected
wantReply bool // whether to check reply is non-empty
}{
{
name: "AC-02: 退款意图 → P1 handoff",
@@ -58,11 +58,11 @@ func TestDialogService_AC02_IntentMatrix(t *testing.T) {
wantReply: true,
},
{
name: "AC-02: 正常查询 → bot 回复无 handoff",
content: "查询额度",
wantIntent: "quota",
wantHandoff: false,
wantReply: true,
name: "AC-02: 正常查询 → bot 回复无 handoff",
content: "查询额度",
wantIntent: "quota",
wantHandoff: false,
wantReply: true,
},
}
@@ -70,9 +70,9 @@ func TestDialogService_AC02_IntentMatrix(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
result, err := svc.Process(context.Background(), &message.UnifiedMessage{
MessageID: "m_" + tc.name,
Channel: "widget",
OpenID: "u_" + tc.name,
Content: tc.content,
Channel: "widget",
OpenID: "u_" + tc.name,
Content: tc.content,
})
if err != nil {
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.
type ticketStatsResponse struct {
Total int `json:"total_tickets"`
Open int `json:"open"`
Resolved int `json:"resolved"`
Closed int `json:"closed"`
ByChannel map[string]int `json:"by_channel"`
ByPriority map[string]int `json:"by_priority"`
HandoffCount int `json:"handoff_count"`
AvgResolutionTimeMinutes float64 `json:"avg_resolution_time_minutes"`
Total int `json:"total_tickets"`
Open int `json:"open"`
Resolved int `json:"resolved"`
Closed int `json:"closed"`
ByChannel map[string]int `json:"by_channel"`
ByPriority map[string]int `json:"by_priority"`
HandoffCount int `json:"handoff_count"`
AvgResolutionTimeMinutes float64 `json:"avg_resolution_time_minutes"`
}
// TestTicketStats_Success verifies the stats endpoint returns correct
// counts when the store has tickets.
func TestTicketStats_Success(t *testing.T) {
stats := ticketstats.Stats{
Total: 100,
Open: 30,
Resolved: 50,
Closed: 20,
ByChannel: map[string]int{"api": 40, "web": 60},
ByPriority: map[string]int{"P1": 10, "P2": 60, "P3": 30},
HandoffCount: 15,
Total: 100,
Open: 30,
Resolved: 50,
Closed: 20,
ByChannel: map[string]int{"api": 40, "web": 60},
ByPriority: map[string]int{"P1": 10, "P2": 60, "P3": 30},
HandoffCount: 15,
AvgResolutionTimeMinutes: 45.5,
}
@@ -114,13 +114,13 @@ func TestTicketStats_Success(t *testing.T) {
// TestTicketStats_Empty verifies that an empty store returns all-zero stats.
func TestTicketStats_Empty(t *testing.T) {
stats := ticketstats.Stats{
Total: 0,
Open: 0,
Resolved: 0,
Closed: 0,
ByChannel: map[string]int{},
ByPriority: map[string]int{},
HandoffCount: 0,
Total: 0,
Open: 0,
Resolved: 0,
Closed: 0,
ByChannel: map[string]int{},
ByPriority: map[string]int{},
HandoffCount: 0,
AvgResolutionTimeMinutes: 0,
}
@@ -163,14 +163,14 @@ func TestTicketStats_GroupedCounts(t *testing.T) {
Resolved: 10,
Closed: 5,
ByChannel: map[string]int{
"api": 8,
"web": 12,
"api": 8,
"web": 12,
"wechat": 5,
},
ByPriority: map[string]int{
"P1": 3,
"P1": 3,
"P2": 15,
"P3": 7,
"P3": 7,
},
HandoffCount: 6,
AvgResolutionTimeMinutes: 120.0,