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,132 @@
package error
import (
"fmt"
"strings"
)
// ErrorCode 错误码格式:{DOMAIN}_{CODE}
// 错误码命名规范:{模块}_{问题类型}_{序号}
//
// 示例:
// - SUP_ACC_4001 (Supplier Account - 业务错误 - 4001)
// - AUDIT_EVT_4041 (Audit Event - 资源不存在 - 4041)
//
// 错误码分类:
// - 4xxx: 业务逻辑错误
// - 5xxx: 系统/服务器错误
// - 9xxx: 内部/未知错误
// 预定义的错误码前缀
const (
PrefixSUP = "SUP" // Supplier 模块
PrefixIAM = "IAM" // Identity & Access Management 模块
PrefixAudit = "AUDIT" // Audit 模块
PrefixRepo = "REPO" // Repository 模块
PrefixSys = "SYS" // 系统级错误
)
// CodeError 带错误码的错误
type CodeError struct {
Code string // 错误码,如 "SUP_ACC_4001"
Message string // 错误消息
Err error // 底层错误(可选)
}
// Error 实现 error 接口
func (e *CodeError) Error() string {
if e.Err != nil {
return fmt.Sprintf("%s: %s (caused by: %v)", e.Code, e.Message, e.Err)
}
return fmt.Sprintf("%s: %s", e.Code, e.Message)
}
// Unwrap 获取底层错误
func (e *CodeError) Unwrap() error {
return e.Err
}
// NewCodeError 创建带错误码的错误
func NewCodeError(code, message string) *CodeError {
return &CodeError{
Code: code,
Message: message,
}
}
// WrapCodeError 包装已有错误
func WrapCodeError(err error, code, message string) *CodeError {
return &CodeError{
Code: code,
Message: message,
Err: err,
}
}
// IsCodeError 检查错误是否为 CodeError
func IsCodeError(err error) bool {
_, ok := err.(*CodeError)
return ok
}
// GetErrorCode 从错误中提取错误码
func GetErrorCode(err error) string {
var codeErr *CodeError
if As(err, &codeErr) {
return codeErr.Code
}
return ""
}
// As 类型断言辅助函数
func As(err error, target **CodeError) bool {
if err == nil {
return false
}
if e, ok := err.(*CodeError); ok {
*target = e
return true
}
if e, ok := err.(interface{ Unwrap() error }); ok {
return As(e.Unwrap(), target)
}
return false
}
// Common errors - 可以被各模块引用的通用错误
var (
// ErrNotFound 资源不存在
ErrNotFound = NewCodeError("SYS_4040", "resource not found")
// ErrInvalidInput 输入参数无效
ErrInvalidInput = NewCodeError("SYS_4000", "invalid input parameter")
// ErrUnauthorized 未授权
ErrUnauthorized = NewCodeError("SYS_4010", "unauthorized")
// ErrForbidden 禁止访问
ErrForbidden = NewCodeError("SYS_4030", "forbidden")
// ErrInternalServer 服务器内部错误
ErrInternalServer = NewCodeError("SYS_5000", "internal server error")
// ErrConcurrencyConflict 并发冲突
ErrConcurrencyConflict = NewCodeError("SYS_4090", "concurrency conflict")
)
// ValidateErrorCode 验证错误码格式是否合法
func ValidateErrorCode(code string) bool {
parts := strings.Split(code, "_")
if len(parts) < 2 {
return false
}
// 检查前缀是否为有效值
prefix := parts[0]
validPrefixes := []string{PrefixSUP, PrefixIAM, PrefixAudit, PrefixRepo, PrefixSys}
for _, p := range validPrefixes {
if prefix == p {
return true
}
}
return false
}

View File

@@ -0,0 +1,95 @@
package error
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewCodeError(t *testing.T) {
err := NewCodeError("TEST_4001", "test error message")
assert.Equal(t, "TEST_4001", err.Code)
assert.Equal(t, "test error message", err.Message)
assert.Nil(t, err.Err)
assert.Equal(t, "TEST_4001: test error message", err.Error())
}
func TestWrapCodeError(t *testing.T) {
originalErr := errors.New("original error")
err := WrapCodeError(originalErr, "TEST_4001", "wrapped error")
assert.Equal(t, "TEST_4001", err.Code)
assert.Equal(t, "wrapped error", err.Message)
assert.Equal(t, originalErr, err.Err)
assert.Contains(t, err.Error(), "caused by: original error")
}
func TestIsCodeError(t *testing.T) {
codeErr := NewCodeError("TEST_4001", "test")
assert.True(t, IsCodeError(codeErr))
stdErr := errors.New("standard error")
assert.False(t, IsCodeError(stdErr))
}
func TestGetErrorCode(t *testing.T) {
codeErr := NewCodeError("SUP_ACC_4001", "test")
assert.Equal(t, "SUP_ACC_4001", GetErrorCode(codeErr))
stdErr := errors.New("standard error")
assert.Equal(t, "", GetErrorCode(stdErr))
}
func TestUnwrap(t *testing.T) {
originalErr := errors.New("original")
wrapped := WrapCodeError(originalErr, "TEST_4001", "wrapped")
// 通过 Unwrap 获取原始错误
unwrapped := wrapped.Unwrap()
assert.Equal(t, originalErr, unwrapped)
}
func TestValidateErrorCode(t *testing.T) {
tests := []struct {
code string
expected bool
}{
{"SUP_ACC_4001", true},
{"IAM_ROLE_4040", true},
{"AUDIT_EVT_5000", true},
{"REPO_NOT_FOUND", true},
{"SYS_5000", true},
{"INVALID", false}, // 没有下划线分隔
{"BAD_CODE", false}, // 前缀不在白名单
{"X_4001", false}, // 前缀不在白名单
{"", false}, // 空字符串
{"TOOLONG_4001", false}, // 前缀太长
}
for _, tc := range tests {
t.Run(tc.code, func(t *testing.T) {
result := ValidateErrorCode(tc.code)
assert.Equal(t, tc.expected, result, "code: %s", tc.code)
})
}
}
func TestCommonErrors(t *testing.T) {
assert.Equal(t, "SYS_4040", ErrNotFound.Code)
assert.Equal(t, "resource not found", ErrNotFound.Message)
assert.Equal(t, "SYS_4000", ErrInvalidInput.Code)
assert.Equal(t, "invalid input parameter", ErrInvalidInput.Message)
assert.Equal(t, "SYS_4010", ErrUnauthorized.Code)
assert.Equal(t, "unauthorized", ErrUnauthorized.Message)
assert.Equal(t, "SYS_4030", ErrForbidden.Code)
assert.Equal(t, "forbidden", ErrForbidden.Message)
assert.Equal(t, "SYS_5000", ErrInternalServer.Code)
assert.Equal(t, "internal server error", ErrInternalServer.Message)
assert.Equal(t, "SYS_4090", ErrConcurrencyConflict.Code)
assert.Equal(t, "concurrency conflict", ErrConcurrencyConflict.Message)
}