fix: 系统性修复安全问题、性能问题和错误处理

安全问题修复:
- X-Forwarded-For越界检查(auth.go)
- checkTokenStatus Context参数传递(auth.go)
- Type Assertion安全检查(auth.go)

性能问题修复:
- TokenCache过期清理机制
- BruteForceProtection过期清理
- InMemoryIdempotencyStore过期清理

错误处理修复:
- AuditStore.Emit返回error
- domain层emitAudit辅助方法
- List方法返回空slice而非nil
- 金额/价格负数验证

架构一致性:
- 统一使用model.RoleHierarchyLevels

新增功能:
- Alert API完整实现(CRUD+Resolve)
- pkg/error错误码集中管理
This commit is contained in:
Your Name
2026-04-07 07:41:25 +08:00
parent 12ce4913cd
commit d5b5a8ece0
21 changed files with 2321 additions and 83 deletions

View File

@@ -0,0 +1,249 @@
package service
import (
"context"
"sync"
"testing"
"time"
"lijiaoqiao/supply-api/internal/audit/model"
)
// TestBatchBuffer_BatchSize 测试50条/批刷新
func TestBatchBuffer_BatchSize(t *testing.T) {
const batchSize = 50
buffer := NewBatchBuffer(batchSize, 100*time.Millisecond) // 100ms超时防止测试卡住
ctx := context.Background()
err := buffer.Start(ctx)
if err != nil {
t.Fatalf("Start failed: %v", err)
}
defer buffer.Close()
// 收集器:接收批量事件
var receivedBatches [][]*model.AuditEvent
var mu sync.Mutex
buffer.SetFlushHandler(func(events []*model.AuditEvent) error {
mu.Lock()
receivedBatches = append(receivedBatches, events)
mu.Unlock()
return nil
})
// 添加50条事件应该触发一次批量刷新
for i := 0; i < batchSize; i++ {
event := &model.AuditEvent{
EventID: "batch-test-001",
EventName: "TEST-EVENT",
}
if err := buffer.Add(event); err != nil {
t.Errorf("Add failed: %v", err)
}
}
// 等待刷新完成
time.Sleep(50 * time.Millisecond)
// 验证:应该收到恰好一个批次
mu.Lock()
if len(receivedBatches) != 1 {
t.Errorf("expected 1 batch, got %d", len(receivedBatches))
}
if len(receivedBatches) > 0 && len(receivedBatches[0]) != batchSize {
t.Errorf("expected batch size %d, got %d", batchSize, len(receivedBatches[0]))
}
mu.Unlock()
}
// TestBatchBuffer_TimeoutFlush 测试5ms超时刷新
func TestBatchBuffer_TimeoutFlush(t *testing.T) {
const batchSize = 100 // 大于我们添加的数量
const flushInterval = 5 * time.Millisecond
buffer := NewBatchBuffer(batchSize, flushInterval)
ctx := context.Background()
err := buffer.Start(ctx)
if err != nil {
t.Fatalf("Start failed: %v", err)
}
defer buffer.Close()
// 收集器
var receivedBatches [][]*model.AuditEvent
var mu sync.Mutex
buffer.SetFlushHandler(func(events []*model.AuditEvent) error {
mu.Lock()
receivedBatches = append(receivedBatches, events)
mu.Unlock()
return nil
})
// 只添加3条事件不满50条
for i := 0; i < 3; i++ {
event := &model.AuditEvent{
EventID: "batch-test-002",
EventName: "TEST-TIMEOUT",
}
if err := buffer.Add(event); err != nil {
t.Errorf("Add failed: %v", err)
}
}
// 等待5ms超时刷新
time.Sleep(20 * time.Millisecond)
// 验证应该收到一个批次包含3条事件
mu.Lock()
defer mu.Unlock()
if len(receivedBatches) != 1 {
t.Errorf("expected 1 batch (timeout flush), got %d", len(receivedBatches))
}
if len(receivedBatches) > 0 && len(receivedBatches[0]) != 3 {
t.Errorf("expected 3 events in batch, got %d", len(receivedBatches[0]))
}
}
// TestBatchBuffer_ConcurrentAccess 测试并发安全性
func TestBatchBuffer_ConcurrentAccess(t *testing.T) {
const batchSize = 50
const numGoroutines = 10
const eventsPerGoroutine = 100
buffer := NewBatchBuffer(batchSize, 10*time.Millisecond)
ctx := context.Background()
err := buffer.Start(ctx)
if err != nil {
t.Fatalf("Start failed: %v", err)
}
defer buffer.Close()
var totalReceived int
var mu sync.Mutex
buffer.SetFlushHandler(func(events []*model.AuditEvent) error {
mu.Lock()
totalReceived += len(events)
mu.Unlock()
return nil
})
// 并发添加事件
var wg sync.WaitGroup
for g := 0; g < numGoroutines; g++ {
wg.Add(1)
go func(goroutineID int) {
defer wg.Done()
for i := 0; i < eventsPerGoroutine; i++ {
event := &model.AuditEvent{
EventID: "batch-test-concurrent",
EventName: "TEST-CONCURRENT",
}
if err := buffer.Add(event); err != nil {
t.Errorf("Add failed: %v", err)
}
}
}(g)
}
wg.Wait()
time.Sleep(50 * time.Millisecond) // 等待所有刷新完成
mu.Lock()
defer mu.Unlock()
expectedTotal := numGoroutines * eventsPerGoroutine
if totalReceived != expectedTotal {
t.Errorf("expected %d total events, got %d", expectedTotal, totalReceived)
}
}
// TestBatchBuffer_Close 测试关闭
func TestBatchBuffer_Close(t *testing.T) {
buffer := NewBatchBuffer(50, 10*time.Millisecond)
ctx := context.Background()
err := buffer.Start(ctx)
if err != nil {
t.Fatalf("Start failed: %v", err)
}
// 添加一些事件
for i := 0; i < 5; i++ {
event := &model.AuditEvent{
EventID: "batch-test-close",
EventName: "TEST-CLOSE",
}
if err := buffer.Add(event); err != nil {
t.Errorf("Add failed: %v", err)
}
}
// 关闭缓冲区
err = buffer.Close()
if err != nil {
t.Errorf("Close failed: %v", err)
}
// 关闭后添加应该失败
event := &model.AuditEvent{
EventID: "batch-test-after-close",
EventName: "TEST-AFTER-CLOSE",
}
if err := buffer.Add(event); err == nil {
t.Errorf("Add after Close should fail")
}
}
// TestBatchBuffer_FlushNow 测试手动刷新
func TestBatchBuffer_FlushNow(t *testing.T) {
const batchSize = 100 // 足够大,不会自动触发
buffer := NewBatchBuffer(batchSize, 100*time.Millisecond) // 100ms才自动刷新
ctx := context.Background()
err := buffer.Start(ctx)
if err != nil {
t.Fatalf("Start failed: %v", err)
}
defer buffer.Close()
var receivedBatches [][]*model.AuditEvent
var mu sync.Mutex
buffer.SetFlushHandler(func(events []*model.AuditEvent) error {
mu.Lock()
receivedBatches = append(receivedBatches, events)
mu.Unlock()
return nil
})
// 添加少量事件
for i := 0; i < 3; i++ {
event := &model.AuditEvent{
EventID: "batch-test-manual",
EventName: "TEST-MANUAL",
}
if err := buffer.Add(event); err != nil {
t.Errorf("Add failed: %v", err)
}
}
// 立即手动刷新
err = buffer.FlushNow()
if err != nil {
t.Errorf("FlushNow failed: %v", err)
}
time.Sleep(10 * time.Millisecond)
mu.Lock()
defer mu.Unlock()
if len(receivedBatches) != 1 {
t.Errorf("expected 1 batch after FlushNow, got %d", len(receivedBatches))
}
}