package data import ( "context" "log/slog" "sync" "time" "gorm.io/gorm" ) const ( dataScopeAuditQueueSize = 1024 dataScopeAuditBatchSize = 100 dataScopeAuditInterval = 2 * time.Second ) // dataScopeAuditWriter keeps row-scope auditing best-effort: callbacks only // enqueue, while a background worker writes batches to the active database. type dataScopeAuditWriter struct { data *Data logger *slog.Logger queue chan dataAccessLogPO batchSize int interval time.Duration stop chan struct{} done chan struct{} closeOnce sync.Once } func newDataScopeAuditWriter(data *Data, logger *slog.Logger) *dataScopeAuditWriter { return newDataScopeAuditWriterWithOptions(data, logger, dataScopeAuditQueueSize, dataScopeAuditBatchSize, dataScopeAuditInterval) } func newDataScopeAuditWriterWithOptions(data *Data, logger *slog.Logger, queueSize, batchSize int, interval time.Duration) *dataScopeAuditWriter { w := &dataScopeAuditWriter{ data: data, logger: logger, queue: make(chan dataAccessLogPO, queueSize), batchSize: batchSize, interval: interval, stop: make(chan struct{}), done: make(chan struct{}), } go w.run() return w } func (d *Data) enqueueDataScopeAudit(record dataAccessLogPO) { if d == nil || d.auditLog == nil { return } d.auditLog.Enqueue(record) } func (w *dataScopeAuditWriter) Enqueue(record dataAccessLogPO) { if w == nil { return } select { case w.queue <- record: default: w.log().Warn("数据权限审计缓冲已满, 事件被丢弃", "mod", "data-scope", "event_type", record.EventType, "table", record.TargetTable) } } func (w *dataScopeAuditWriter) Close() { if w == nil { return } w.closeOnce.Do(func() { close(w.stop) }) <-w.done } func (w *dataScopeAuditWriter) run() { defer close(w.done) ticker := time.NewTicker(w.interval) defer ticker.Stop() batch := make([]dataAccessLogPO, 0, w.batchSize) flush := func() { if len(batch) == 0 || w.data == nil || w.data.gormDB == nil { return } db := w.data.gormDB.DB() if db == nil { return } if err := db.WithContext(context.Background()).Session(&gorm.Session{NewDB: true, SkipHooks: true}).Create(&batch).Error; err != nil { w.log().Warn("数据权限审计批量写入失败", "mod", "data-scope", "error", err) } batch = batch[:0] } for { select { case record := <-w.queue: batch = append(batch, record) if len(batch) >= w.batchSize { flush() } case <-ticker.C: flush() case <-w.stop: for { select { case record := <-w.queue: batch = append(batch, record) default: flush() return } } } } } func (w *dataScopeAuditWriter) log() *slog.Logger { if w != nil && w.logger != nil { return w.logger } return slog.Default() }