暂存
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m32s

This commit is contained in:
2026-08-06 09:35:00 +08:00
parent 8659dfc658
commit 88cc5e96ec
75 changed files with 6520 additions and 513 deletions

View File

@@ -10,6 +10,7 @@ import (
"gorm.io/gorm"
"github.com/break/junhong_cmp_fiber/internal/model"
retentionquery "github.com/break/junhong_cmp_fiber/internal/query/retention"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/pkg/errors"
"github.com/break/junhong_cmp_fiber/pkg/middleware"
@@ -39,10 +40,17 @@ type EventFilter struct {
// EventPage 是平台全局事件稳定分页结果。
type EventPage struct {
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
Items []EventView `json:"items"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
Items []EventView `json:"items"`
Retention retentionquery.Info `json:"retention"`
}
// EventDetail 是单个审计事件及在线留存边界。
type EventDetail struct {
EventView
Retention retentionquery.Info `json:"retention"`
}
// EventView 是不暴露 GORM Model 的审计事件投影。
@@ -147,6 +155,14 @@ func (q *Query) List(ctx context.Context, filter EventFilter) (*EventPage, error
if err := q.authorize(ctx); err != nil {
return nil, err
}
retention, err := retentionquery.Load(ctx, q.db, retentionquery.SourceAudit)
if err != nil {
return nil, err
}
filter.CreatedFrom, filter.CreatedTo, err = retentionquery.NormalizeRange(retention, filter.CreatedFrom, filter.CreatedTo)
if err != nil {
return nil, err
}
if !validEventFilter(filter) {
return nil, errors.New(errors.CodeInvalidParam)
}
@@ -156,16 +172,33 @@ func (q *Query) List(ctx context.Context, filter EventFilter) (*EventPage, error
if err := query.Count(&total).Error; err != nil {
return nil, errors.Wrap(errors.CodeDatabaseError, err, "统计审计事件失败")
}
rows := make([]model.AuditEvent, 0, filter.PageSize)
if err := query.Order("occurred_at DESC, id DESC").
Offset((filter.Page - 1) * filter.PageSize).Limit(filter.PageSize).Find(&rows).Error; err != nil {
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询审计事件失败")
rows, err := q.loadEventPage(ctx, query, filter.Page, filter.PageSize)
if err != nil {
return nil, err
}
items, err := q.project(ctx, rows)
if err != nil {
return nil, err
}
return &EventPage{Total: total, Page: filter.Page, PageSize: filter.PageSize, Items: items}, nil
return &EventPage{Total: total, Page: filter.Page, PageSize: filter.PageSize, Items: items, Retention: retention}, nil
}
// loadEventPage 先分页主键,再批量读取事件宽行,避免排序阶段加载 JSON 字段。
func (q *Query) loadEventPage(ctx context.Context, query *gorm.DB, page, pageSize int) ([]model.AuditEvent, error) {
ids := make([]uint, 0, pageSize)
if err := query.Select("id").Order("occurred_at DESC, id DESC").
Offset((page-1)*pageSize).Limit(pageSize).Pluck("id", &ids).Error; err != nil {
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询审计事件分页ID失败")
}
rows := make([]model.AuditEvent, 0, len(ids))
if len(ids) == 0 {
return rows, nil
}
if err := q.db.WithContext(ctx).Where("id IN ?", ids).
Order("occurred_at DESC, id DESC").Find(&rows).Error; err != nil {
return nil, errors.Wrap(errors.CodeDatabaseError, err, "批量投影审计事件失败")
}
return rows, nil
}
func validEventFilter(filter EventFilter) bool {
@@ -192,15 +225,19 @@ func validOptionalValue(value string, allowed ...string) bool {
}
// Get 查询平台范围的单个稳定审计事件详情。
func (q *Query) Get(ctx context.Context, eventID string) (*EventView, error) {
func (q *Query) Get(ctx context.Context, eventID string) (*EventDetail, error) {
if err := q.authorize(ctx); err != nil {
return nil, err
}
if eventID == "" {
return nil, errors.New(errors.CodeInvalidParam)
}
retention, err := retentionquery.Load(ctx, q.db, retentionquery.SourceAudit)
if err != nil {
return nil, err
}
var row model.AuditEvent
if err := q.db.WithContext(ctx).Where("event_id = ?", eventID).First(&row).Error; err != nil {
if err := q.db.WithContext(ctx).Where("event_id = ? AND occurred_at >= ?", eventID, retention.OnlineFrom.UTC()).First(&row).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, errors.New(errors.CodeNotFound, "审计事件不存在")
}
@@ -210,7 +247,7 @@ func (q *Query) Get(ctx context.Context, eventID string) (*EventView, error) {
if err != nil {
return nil, err
}
return &items[0], nil
return &EventDetail{EventView: items[0], Retention: retention}, nil
}
func (q *Query) authorize(ctx context.Context) error {