- Fix MaskMap to properly handle []string sensitive fields - Add missing slice handling in sanitizer - Add comprehensive tests for GetMetrics and CreateEventsBatch - Improve audit/handler coverage from 49.8% to 68.8% - Fix test expectations to match actual sanitizer behavior - All tests pass
122 lines
3.3 KiB
Go
122 lines
3.3 KiB
Go
package service
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
|
||
"lijiaoqiao/supply-api/internal/audit/model"
|
||
"lijiaoqiao/supply-api/internal/audit/repository"
|
||
)
|
||
|
||
// DatabaseAuditService 数据库-backed审计服务
|
||
type DatabaseAuditService struct {
|
||
repo repository.AuditRepository
|
||
}
|
||
|
||
// NewDatabaseAuditService 创建数据库-backed审计服务
|
||
func NewDatabaseAuditService(repo repository.AuditRepository) *DatabaseAuditService {
|
||
return &DatabaseAuditService{repo: repo}
|
||
}
|
||
|
||
// Ensure interface
|
||
var _ AuditStoreInterface = (*DatabaseAuditService)(nil)
|
||
|
||
// Emit 发送审计事件
|
||
func (s *DatabaseAuditService) Emit(ctx context.Context, event *model.AuditEvent) error {
|
||
// 验证事件
|
||
if event == nil {
|
||
return ErrInvalidInput
|
||
}
|
||
if event.EventName == "" {
|
||
return ErrMissingEventName
|
||
}
|
||
|
||
// 检查幂等键
|
||
if event.IdempotencyKey != "" {
|
||
existing, err := s.repo.GetByIdempotencyKey(ctx, event.IdempotencyKey)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if existing != nil {
|
||
// 幂等键已存在,检查payload是否一致
|
||
if isSamePayload(existing, event) {
|
||
return repository.ErrDuplicateIdempotencyKey
|
||
}
|
||
return ErrIdempotencyConflict
|
||
}
|
||
}
|
||
|
||
// 发送事件
|
||
if err := s.repo.Emit(ctx, event); err != nil {
|
||
if errors.Is(err, repository.ErrDuplicateIdempotencyKey) {
|
||
return repository.ErrDuplicateIdempotencyKey
|
||
}
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// EmitBatch 批量发送审计事件
|
||
func (s *DatabaseAuditService) EmitBatch(ctx context.Context, events []*model.AuditEvent) error {
|
||
if len(events) == 0 {
|
||
return nil
|
||
}
|
||
|
||
// 验证所有事件
|
||
for _, event := range events {
|
||
if event == nil {
|
||
return ErrInvalidInput
|
||
}
|
||
if event.EventName == "" {
|
||
return ErrMissingEventName
|
||
}
|
||
}
|
||
|
||
// 调用仓储批量发送
|
||
return s.repo.EmitBatch(ctx, events)
|
||
}
|
||
|
||
// Query 查询审计事件
|
||
func (s *DatabaseAuditService) Query(ctx context.Context, filter *EventFilter) ([]*model.AuditEvent, int64, error) {
|
||
if filter == nil {
|
||
filter = &EventFilter{}
|
||
}
|
||
// 转换 filter 类型
|
||
repoFilter := &repository.EventFilter{
|
||
TenantID: filter.TenantID,
|
||
Category: filter.Category,
|
||
EventName: filter.EventName,
|
||
Limit: filter.Limit,
|
||
Offset: filter.Offset,
|
||
}
|
||
if !filter.StartTime.IsZero() {
|
||
repoFilter.StartTime = &filter.StartTime
|
||
}
|
||
if !filter.EndTime.IsZero() {
|
||
repoFilter.EndTime = &filter.EndTime
|
||
}
|
||
return s.repo.Query(ctx, repoFilter)
|
||
}
|
||
|
||
// GetByIdempotencyKey 根据幂等键获取事件
|
||
func (s *DatabaseAuditService) GetByIdempotencyKey(ctx context.Context, key string) (*model.AuditEvent, error) {
|
||
return s.repo.GetByIdempotencyKey(ctx, key)
|
||
}
|
||
|
||
// GetByEventID 根据事件ID获取事件
|
||
func (s *DatabaseAuditService) GetByEventID(ctx context.Context, eventID string) (*model.AuditEvent, error) {
|
||
return s.repo.GetByEventID(ctx, eventID)
|
||
}
|
||
|
||
// NewDatabaseAuditServiceWithPool 从数据库连接池创建审计服务
|
||
func NewDatabaseAuditServiceWithPool(pool interface {
|
||
Query(ctx context.Context, sql string, args ...interface{}) (interface{}, error)
|
||
Exec(ctx context.Context, sql string, args ...interface{}) (interface{}, error)
|
||
}) *DatabaseAuditService {
|
||
// 注意:这里需要一个适配器来将通用的pool接口转换为pgxpool.Pool
|
||
// 在实际使用中,应该直接使用 NewDatabaseAuditService(repo)
|
||
// 这个函数仅用于类型兼容性
|
||
return nil
|
||
}
|