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

@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"log"
"net/netip"
"time"
@@ -141,6 +142,14 @@ func NewAccountService(store AccountStore, auditStore audit.AuditStore) AccountS
return &accountService{store: store, auditStore: auditStore}
}
// emitAudit 安全记录审计日志(失败只记录错误,不影响主流程)
func (s *accountService) emitAudit(ctx context.Context, event audit.Event) {
if err := s.auditStore.Emit(ctx, event); err != nil {
log.Printf("[AUDIT_ERROR] failed to emit audit event: %v, object_type=%s, object_id=%d, action=%s",
err, event.ObjectType, event.ObjectID, event.Action)
}
}
func (s *accountService) Verify(ctx context.Context, supplierID int64, provider Provider, accountType AccountType, credential string) (*VerifyResult, error) {
// 开发阶段:模拟验证逻辑
result := &VerifyResult{
@@ -181,7 +190,7 @@ func (s *accountService) Create(ctx context.Context, req *CreateAccountRequest)
}
// 记录审计日志
s.auditStore.Emit(ctx, audit.Event{
s.emitAudit(ctx, audit.Event{
TenantID: req.SupplierID,
ObjectType: "supply_account",
ObjectID: account.ID,
@@ -210,7 +219,7 @@ func (s *accountService) Activate(ctx context.Context, supplierID, accountID int
return nil, err
}
s.auditStore.Emit(ctx, audit.Event{
s.emitAudit(ctx, audit.Event{
TenantID: supplierID,
ObjectType: "supply_account",
ObjectID: accountID,
@@ -239,7 +248,7 @@ func (s *accountService) Suspend(ctx context.Context, supplierID, accountID int6
return nil, err
}
s.auditStore.Emit(ctx, audit.Event{
s.emitAudit(ctx, audit.Event{
TenantID: supplierID,
ObjectType: "supply_account",
ObjectID: accountID,
@@ -260,7 +269,7 @@ func (s *accountService) Delete(ctx context.Context, supplierID, accountID int64
return errors.New("SUP_ACC_4092: cannot delete active accounts")
}
s.auditStore.Emit(ctx, audit.Event{
s.emitAudit(ctx, audit.Event{
TenantID: supplierID,
ObjectType: "supply_account",
ObjectID: accountID,

View File

@@ -3,6 +3,7 @@ package domain
import (
"context"
"errors"
"log"
"net/netip"
"time"
@@ -132,6 +133,14 @@ func NewPackageService(store PackageStore, accountStore AccountStore, auditStore
}
}
// emitAudit 安全记录审计日志(失败只记录错误,不影响主流程)
func (s *packageService) emitAudit(ctx context.Context, event audit.Event) {
if err := s.auditStore.Emit(ctx, event); err != nil {
log.Printf("[AUDIT_ERROR] failed to emit audit event: %v, object_type=%s, object_id=%d, action=%s",
err, event.ObjectType, event.ObjectID, event.Action)
}
}
func (s *packageService) CreateDraft(ctx context.Context, supplierID int64, req *CreatePackageDraftRequest) (*Package, error) {
pkg := &Package{
SupplierID: supplierID,
@@ -154,7 +163,7 @@ func (s *packageService) CreateDraft(ctx context.Context, supplierID int64, req
return nil, err
}
s.auditStore.Emit(ctx, audit.Event{
s.emitAudit(ctx, audit.Event{
TenantID: supplierID,
ObjectType: "supply_package",
ObjectID: pkg.ID,
@@ -183,7 +192,7 @@ func (s *packageService) Publish(ctx context.Context, supplierID, packageID int6
return nil, err
}
s.auditStore.Emit(ctx, audit.Event{
s.emitAudit(ctx, audit.Event{
TenantID: supplierID,
ObjectType: "supply_package",
ObjectID: packageID,
@@ -212,7 +221,7 @@ func (s *packageService) Pause(ctx context.Context, supplierID, packageID int64)
return nil, err
}
s.auditStore.Emit(ctx, audit.Event{
s.emitAudit(ctx, audit.Event{
TenantID: supplierID,
ObjectType: "supply_package",
ObjectID: packageID,
@@ -237,7 +246,7 @@ func (s *packageService) Unlist(ctx context.Context, supplierID, packageID int64
return nil, err
}
s.auditStore.Emit(ctx, audit.Event{
s.emitAudit(ctx, audit.Event{
TenantID: supplierID,
ObjectType: "supply_package",
ObjectID: packageID,
@@ -275,7 +284,7 @@ func (s *packageService) Clone(ctx context.Context, supplierID, packageID int64)
return nil, err
}
s.auditStore.Emit(ctx, audit.Event{
s.emitAudit(ctx, audit.Event{
TenantID: supplierID,
ObjectType: "supply_package",
ObjectID: clone.ID,
@@ -292,6 +301,17 @@ func (s *packageService) BatchUpdatePrice(ctx context.Context, supplierID int64,
}
for _, item := range req.Items {
// 验证价格不能为负数
if item.PricePer1MInput < 0 || item.PricePer1MOutput < 0 {
resp.FailedCount++
resp.Failures = append(resp.Failures, BatchPriceFailure{
PackageID: item.PackageID,
ErrorCode: "SUP_PKG_4004",
Message: "price cannot be negative",
})
continue
}
pkg, err := s.store.GetByID(ctx, supplierID, item.PackageID)
if err != nil {
resp.FailedCount++

View File

@@ -3,6 +3,7 @@ package domain
import (
"context"
"errors"
"log"
"net/netip"
"time"
@@ -160,11 +161,24 @@ func NewSettlementService(store SettlementStore, earningStore EarningStore, audi
}
}
// emitAudit 安全记录审计日志(失败只记录错误,不影响主流程)
func (s *settlementService) emitAudit(ctx context.Context, event audit.Event) {
if err := s.auditStore.Emit(ctx, event); err != nil {
log.Printf("[AUDIT_ERROR] failed to emit audit event: %v, object_type=%s, object_id=%d, action=%s",
err, event.ObjectType, event.ObjectID, event.Action)
}
}
func (s *settlementService) Withdraw(ctx context.Context, supplierID int64, req *WithdrawRequest) (*Settlement, error) {
if req.SMSCode != "123456" {
return nil, errors.New("invalid sms code")
}
// 验证金额:必须为正数
if req.Amount <= 0 {
return nil, errors.New("SUP_SET_4003: withdraw amount must be positive")
}
balance, err := s.store.GetWithdrawableBalance(ctx, supplierID)
if err != nil {
return nil, err
@@ -192,7 +206,7 @@ func (s *settlementService) Withdraw(ctx context.Context, supplierID int64, req
return nil, err
}
s.auditStore.Emit(ctx, audit.Event{
s.emitAudit(ctx, audit.Event{
TenantID: supplierID,
ObjectType: "supply_settlement",
ObjectID: settlement.ID,
@@ -221,7 +235,7 @@ func (s *settlementService) Cancel(ctx context.Context, supplierID, settlementID
return nil, err
}
s.auditStore.Emit(ctx, audit.Event{
s.emitAudit(ctx, audit.Event{
TenantID: supplierID,
ObjectType: "supply_settlement",
ObjectID: settlementID,