From 30b2574747f46ec452a9bfde6f6e34f3a2754ba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=93=D1=80=D0=B8=D0=B3=D0=BE=D1=80=D0=B8=D0=B9=20=D0=A1?= =?UTF-8?q?=D0=B0=D1=84=D1=80=D0=BE=D0=BD=D0=BE=D0=B2?= Date: Mon, 20 Jul 2026 21:32:14 +0000 Subject: [PATCH] Delete internal/storage/trigger.go --- internal/storage/trigger.go | 948 ------------------------------------ 1 file changed, 948 deletions(-) delete mode 100644 internal/storage/trigger.go diff --git a/internal/storage/trigger.go b/internal/storage/trigger.go deleted file mode 100644 index 6992db9..0000000 --- a/internal/storage/trigger.go +++ /dev/null @@ -1,948 +0,0 @@ -/* - * Copyright 2026 Safronov Grigorii - * - * Licensed under the CDDL, Version 1.0 (the "License"); - * you may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * https://opensource.org/licenses/CDDL-1.0 - */ - -// Файл: internal/storage/trigger.go -// Назначение: Реализация триггеров, похожих на MongoDB trigger syntax. -// Поддерживает события: INSERT, UPDATE, DELETE, REPLACE. -// Триггеры могут выполняться до или после события. - -package storage - -import ( - "fmt" - "regexp" - "strings" - "sync" - "time" - - "futriis/internal/log" -) - -// ============================================================================= -// КОНСТАНТЫ -// ============================================================================= - -const ( - MaxVersionsPerDoc = 100 - VersionRetentionDays = 7 - VisibilityMapSize = 1024 * 1024 -) - -// ============================================================================= -// ТИПЫ ТРИГГЕРОВ -// ============================================================================= - -// TriggerEvent определяет тип события для триггера -type TriggerEvent string - -const ( - TriggerBeforeInsert TriggerEvent = "BEFORE_INSERT" - TriggerAfterInsert TriggerEvent = "AFTER_INSERT" - TriggerBeforeUpdate TriggerEvent = "BEFORE_UPDATE" - TriggerAfterUpdate TriggerEvent = "AFTER_UPDATE" - TriggerBeforeDelete TriggerEvent = "BEFORE_DELETE" - TriggerAfterDelete TriggerEvent = "AFTER_DELETE" - TriggerBeforeReplace TriggerEvent = "BEFORE_REPLACE" - TriggerAfterReplace TriggerEvent = "AFTER_REPLACE" -) - -// TriggerAction определяет действие триггера -type TriggerAction string - -const ( - ActionAbort TriggerAction = "abort" // Прервать операцию - ActionSkip TriggerAction = "skip" // Пропустить операцию - ActionModify TriggerAction = "modify" // Модифицировать документ - ActionLog TriggerAction = "log" // Записать в лог - ActionNotify TriggerAction = "notify" // Отправить уведомление - ActionCustom TriggerAction = "custom" // Пользовательское действие -) - -// Trigger представляет триггер на коллекции -type Trigger struct { - Name string `msgpack:"name" json:"name"` - Collection string `msgpack:"collection" json:"collection"` - Event string `msgpack:"event" json:"event"` - Action string `msgpack:"action" json:"action"` - Condition *TriggerCondition `msgpack:"condition" json:"condition"` - Operations []TriggerOperation `msgpack:"operations" json:"operations"` - CreatedAt int64 `msgpack:"created_at" json:"created_at"` - UpdatedAt int64 `msgpack:"updated_at" json:"updated_at"` - Enabled bool `msgpack:"enabled" json:"enabled"` - Description string `msgpack:"description" json:"description"` - EventType TriggerEvent `msgpack:"-" json:"-"` - ActionType TriggerAction `msgpack:"-" json:"-"` - mu sync.RWMutex `msgpack:"-" json:"-"` -} - -// GetEventType возвращает типизированное событие -func (t *Trigger) GetEventType() TriggerEvent { - t.mu.RLock() - defer t.mu.RUnlock() - if t.EventType != "" { - return t.EventType - } - return TriggerEvent(t.Event) -} - -// GetActionType возвращает типизированное действие -func (t *Trigger) GetActionType() TriggerAction { - t.mu.RLock() - defer t.mu.RUnlock() - if t.ActionType != "" { - return t.ActionType - } - return TriggerAction(t.Action) -} - -// SetEventType устанавливает типизированное событие -func (t *Trigger) SetEventType(event TriggerEvent) { - t.mu.Lock() - defer t.mu.Unlock() - t.EventType = event - t.Event = string(event) -} - -// SetActionType устанавливает типизированное действие -func (t *Trigger) SetActionType(action TriggerAction) { - t.mu.Lock() - defer t.mu.Unlock() - t.ActionType = action - t.Action = string(action) -} - -// IsEnabled проверяет, включен ли триггер -func (t *Trigger) IsEnabled() bool { - t.mu.RLock() - defer t.mu.RUnlock() - return t.Enabled -} - -// GetEvent возвращает строковое представление события -func (t *Trigger) GetEvent() string { - t.mu.RLock() - defer t.mu.RUnlock() - return t.Event -} - -// GetAction возвращает строковое представление действия -func (t *Trigger) GetAction() string { - t.mu.RLock() - defer t.mu.RUnlock() - return t.Action -} - -// TriggerCondition определяет условие выполнения триггера -type TriggerCondition struct { - Field string `msgpack:"field" json:"field"` - Operator string `msgpack:"operator" json:"operator"` - Value interface{} `msgpack:"value" json:"value"` - Match string `msgpack:"match" json:"match"` -} - -// TriggerOperation определяет операцию, выполняемую триггером -type TriggerOperation struct { - Type string `msgpack:"type" json:"type"` - Field string `msgpack:"field" json:"field"` - Value interface{} `msgpack:"value" json:"value"` - Params map[string]interface{} `msgpack:"params" json:"params"` -} - -// TriggerExecution содержит контекст выполнения триггера -type TriggerExecution struct { - TriggerName string - Event TriggerEvent - Collection string - Database string - DocumentID string - OldDocument *Document - NewDocument *Document - Operation string - Timestamp time.Time - TimestampMs int64 `msgpack:"timestamp_ms"` - TimestampStr string `msgpack:"timestamp_str"` - User string - Role string - CustomData map[string]interface{} - ActionResult string `msgpack:"action_result"` - DurationMs int64 `msgpack:"duration_ms"` -} - -// ============================================================================= -// ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ ДЛЯ ЛОГА ВЫПОЛНЕНИЯ ТРИГГЕРОВ -// ============================================================================= - -// triggerExecutionLog - глобальный лог выполнения триггеров -var triggerExecutionLog []TriggerExecutionLogEntry -var triggerExecutionLogMu sync.RWMutex - -// TriggerExecutionLogEntry представляет запись выполнения триггера -type TriggerExecutionLogEntry struct { - Timestamp time.Time `json:"timestamp"` - TriggerName string `json:"trigger_name"` - Event string `json:"event"` - Collection string `json:"collection"` - DocumentID string `json:"document_id"` - Action string `json:"action"` - Success bool `json:"success"` - Error string `json:"error,omitempty"` -} - -// GetTriggerExecutionLog возвращает лог выполнения триггеров -func GetTriggerExecutionLog() []TriggerExecutionLogEntry { - triggerExecutionLogMu.RLock() - defer triggerExecutionLogMu.RUnlock() - result := make([]TriggerExecutionLogEntry, len(triggerExecutionLog)) - copy(result, triggerExecutionLog) - return result -} - -// LogTriggerExecution записывает выполнение триггера -func LogTriggerExecution(triggerName, event, collection, docID, action string, success bool, err error) { - triggerExecutionLogMu.Lock() - defer triggerExecutionLogMu.Unlock() - - entry := TriggerExecutionLogEntry{ - Timestamp: time.Now(), - TriggerName: triggerName, - Event: event, - Collection: collection, - DocumentID: docID, - Action: action, - Success: success, - } - if err != nil { - entry.Error = err.Error() - } - - triggerExecutionLog = append(triggerExecutionLog, entry) - if len(triggerExecutionLog) > 1000 { - triggerExecutionLog = triggerExecutionLog[len(triggerExecutionLog)-1000:] - } -} - -// ============================================================================= -// TRIGGER MANAGER -// ============================================================================= - -// TriggerManager управляет триггерами в СУБД -type TriggerManager struct { - triggers sync.Map // map[string]*Trigger (ключ: collection|event|name) - logger *log.Logger - mu sync.RWMutex - auditLog []*TriggerExecution - maxLogSize int -} - -var ( - globalTriggerManager *TriggerManager - triggerManagerOnce sync.Once -) - -// GetTriggerManager возвращает глобальный менеджер триггеров -func GetTriggerManager() *TriggerManager { - triggerManagerOnce.Do(func() { - globalTriggerManager = &TriggerManager{ - maxLogSize: 10000, - auditLog: make([]*TriggerExecution, 0), - } - }) - return globalTriggerManager -} - -// InitTriggerManager инициализирует менеджер триггеров с логгером -func InitTriggerManager(logger *log.Logger) { - tm := GetTriggerManager() - tm.logger = logger - if logger != nil { - logger.Info("Trigger manager initialized") - } -} - -// CreateTrigger создаёт новый триггер (синтаксис MongoDB-like) -func (tm *TriggerManager) CreateTrigger(database, collection, name string, event TriggerEvent, config map[string]interface{}) error { - tm.mu.Lock() - defer tm.mu.Unlock() - - key := tm.getTriggerKey(collection, event, name) - if _, exists := tm.triggers.Load(key); exists { - return fmt.Errorf("trigger '%s' already exists on %s for event %s", name, collection, event) - } - - now := time.Now().UnixMilli() - - trigger := &Trigger{ - Name: name, - Collection: collection, - Event: string(event), - EventType: event, - Enabled: true, - CreatedAt: now, - UpdatedAt: now, - Operations: make([]TriggerOperation, 0), - } - - // Парсим конфигурацию триггера - if action, ok := config["action"].(string); ok { - switch strings.ToLower(action) { - case "abort": - trigger.Action = string(ActionAbort) - trigger.ActionType = ActionAbort - case "skip": - trigger.Action = string(ActionSkip) - trigger.ActionType = ActionSkip - case "modify": - trigger.Action = string(ActionModify) - trigger.ActionType = ActionModify - case "log": - trigger.Action = string(ActionLog) - trigger.ActionType = ActionLog - case "notify": - trigger.Action = string(ActionNotify) - trigger.ActionType = ActionNotify - default: - trigger.Action = string(ActionCustom) - trigger.ActionType = ActionCustom - } - } - - // Парсим условие - if cond, ok := config["condition"].(map[string]interface{}); ok { - trigger.Condition = &TriggerCondition{} - if field, ok := cond["field"].(string); ok { - trigger.Condition.Field = field - } - if operator, ok := cond["operator"].(string); ok { - trigger.Condition.Operator = operator - } - if value, ok := cond["value"]; ok { - trigger.Condition.Value = value - } - if match, ok := cond["match"].(string); ok { - trigger.Condition.Match = match - } - } - - // Парсим операции - if ops, ok := config["operations"].([]interface{}); ok { - for _, opRaw := range ops { - opMap, ok := opRaw.(map[string]interface{}) - if !ok { - continue - } - operation := TriggerOperation{} - if opType, ok := opMap["type"].(string); ok { - operation.Type = opType - } - if field, ok := opMap["field"].(string); ok { - operation.Field = field - } - if value, ok := opMap["value"]; ok { - operation.Value = value - } - if params, ok := opMap["params"].(map[string]interface{}); ok { - operation.Params = params - } - trigger.Operations = append(trigger.Operations, operation) - } - } - - if desc, ok := config["description"].(string); ok { - trigger.Description = desc - } - - tm.triggers.Store(key, trigger) - - // Аудит создания триггера - LogAudit("CREATE", "TRIGGER", fmt.Sprintf("%s.%s.%s", database, collection, name), map[string]interface{}{ - "event": event, - "action": trigger.Action, - "description": trigger.Description, - }) - - if tm.logger != nil { - tm.logger.Info(fmt.Sprintf("Trigger '%s' created on %s.%s for event %s", name, database, collection, event)) - } - - return nil -} - -// DropTrigger удаляет триггер -func (tm *TriggerManager) DropTrigger(collection, event, name string) error { - key := tm.getTriggerKey(collection, TriggerEvent(event), name) - if _, exists := tm.triggers.LoadAndDelete(key); !exists { - return fmt.Errorf("trigger '%s' not found on %s for event %s", name, collection, event) - } - - // Аудит удаления триггера - LogAudit("DROP", "TRIGGER", fmt.Sprintf("%s.%s", collection, name), map[string]interface{}{ - "event": event, - }) - - if tm.logger != nil { - tm.logger.Info(fmt.Sprintf("Trigger '%s' dropped from %s for event %s", name, collection, event)) - } - return nil -} - -// GetTrigger возвращает триггер по имени -func (tm *TriggerManager) GetTrigger(collection, event, name string) (*Trigger, error) { - key := tm.getTriggerKey(collection, TriggerEvent(event), name) - if val, ok := tm.triggers.Load(key); ok { - return val.(*Trigger), nil - } - return nil, fmt.Errorf("trigger not found: %s", name) -} - -// ListTriggers возвращает список всех триггеров для коллекции -func (tm *TriggerManager) ListTriggers(collection string) []*Trigger { - triggers := make([]*Trigger, 0) - tm.triggers.Range(func(key, value interface{}) bool { - trigger := value.(*Trigger) - if collection == "" || trigger.Collection == collection { - triggers = append(triggers, trigger) - } - return true - }) - return triggers -} - -// ListTriggersByEvent возвращает триггеры для конкретного события -func (tm *TriggerManager) ListTriggersByEvent(collection string, event TriggerEvent) []*Trigger { - triggers := make([]*Trigger, 0) - tm.triggers.Range(func(key, value interface{}) bool { - trigger := value.(*Trigger) - triggerEvent := trigger.GetEventType() - if trigger.Collection == collection && triggerEvent == event && trigger.IsEnabled() { - triggers = append(triggers, trigger) - } - return true - }) - return triggers -} - -// EnableTrigger включает триггер -func (tm *TriggerManager) EnableTrigger(collection, event, name string) error { - key := tm.getTriggerKey(collection, TriggerEvent(event), name) - val, ok := tm.triggers.Load(key) - if !ok { - return fmt.Errorf("trigger not found: %s", name) - } - trigger := val.(*Trigger) - trigger.mu.Lock() - trigger.Enabled = true - trigger.UpdatedAt = time.Now().UnixMilli() - trigger.mu.Unlock() - tm.triggers.Store(key, trigger) - - // Аудит включения триггера - LogAudit("ENABLE", "TRIGGER", fmt.Sprintf("%s.%s", collection, name), map[string]interface{}{ - "event": event, - }) - - return nil -} - -// DisableTrigger выключает триггер -func (tm *TriggerManager) DisableTrigger(collection, event, name string) error { - key := tm.getTriggerKey(collection, TriggerEvent(event), name) - val, ok := tm.triggers.Load(key) - if !ok { - return fmt.Errorf("trigger not found: %s", name) - } - trigger := val.(*Trigger) - trigger.mu.Lock() - trigger.Enabled = false - trigger.UpdatedAt = time.Now().UnixMilli() - trigger.mu.Unlock() - tm.triggers.Store(key, trigger) - - // Аудит выключения триггера - LogAudit("DISABLE", "TRIGGER", fmt.Sprintf("%s.%s", collection, name), map[string]interface{}{ - "event": event, - }) - - return nil -} - -// ExecuteTriggers выполняет все триггеры для данного события -// Возвращает: modifiedDocument, shouldAbort, error -func (tm *TriggerManager) ExecuteTriggers(execCtx *TriggerExecution) (*Document, bool, error) { - triggers := tm.ListTriggersByEvent(execCtx.Collection, execCtx.Event) - - if len(triggers) == 0 { - return execCtx.NewDocument, false, nil - } - - currentDoc := execCtx.NewDocument - if currentDoc == nil && execCtx.OldDocument != nil { - currentDoc = execCtx.OldDocument.Clone() - } - - for _, trigger := range triggers { - if !trigger.IsEnabled() { - continue - } - - // Проверяем условие - if trigger.Condition != nil { - if !tm.evaluateCondition(execCtx, trigger.Condition) { - continue - } - } - - startTime := time.Now() - - // Получаем типизированное действие - action := trigger.GetActionType() - - // Выполняем действие триггера - switch action { - case ActionAbort: - tm.logExecution(execCtx, trigger, "aborted", startTime) - return currentDoc, true, fmt.Errorf("operation aborted by trigger: %s", trigger.Name) - - case ActionSkip: - tm.logExecution(execCtx, trigger, "skipped", startTime) - return currentDoc, true, nil - - case ActionModify: - if currentDoc != nil { - currentDoc = tm.applyOperations(currentDoc, trigger.Operations, execCtx) - } - tm.logExecution(execCtx, trigger, "modified", startTime) - - case ActionLog: - tm.logExecution(execCtx, trigger, "logged", startTime) - if tm.logger != nil { - tm.logger.Info(fmt.Sprintf("Trigger %s executed on %s.%s (event: %s, doc: %s)", - trigger.Name, execCtx.Database, execCtx.Collection, execCtx.Event, execCtx.DocumentID)) - } - - case ActionNotify: - tm.logExecution(execCtx, trigger, "notified", startTime) - // Здесь можно отправить уведомление через WebSocket или другой канал - } - } - - return currentDoc, false, nil -} - -// evaluateCondition проверяет условие триггера -func (tm *TriggerManager) evaluateCondition(execCtx *TriggerExecution, cond *TriggerCondition) bool { - var docToCheck *Document - if execCtx.NewDocument != nil { - docToCheck = execCtx.NewDocument - } else if execCtx.OldDocument != nil { - docToCheck = execCtx.OldDocument - } else { - return false - } - - fieldValue, err := docToCheck.GetField(cond.Field) - if err != nil { - if cond.Operator == "exists" { - if existsVal, ok := cond.Value.(bool); ok && !existsVal { - return true - } - } - return false - } - - switch cond.Operator { - case "eq": - return fmt.Sprintf("%v", fieldValue) == fmt.Sprintf("%v", cond.Value) - case "ne": - return fmt.Sprintf("%v", fieldValue) != fmt.Sprintf("%v", cond.Value) - case "gt": - return compareNumbers(fieldValue, cond.Value) > 0 - case "lt": - return compareNumbers(fieldValue, cond.Value) < 0 - case "gte": - return compareNumbers(fieldValue, cond.Value) >= 0 - case "lte": - return compareNumbers(fieldValue, cond.Value) <= 0 - case "in": - if arr, ok := cond.Value.([]interface{}); ok { - for _, v := range arr { - if fmt.Sprintf("%v", fieldValue) == fmt.Sprintf("%v", v) { - return true - } - } - } - return false - case "nin": - if arr, ok := cond.Value.([]interface{}); ok { - for _, v := range arr { - if fmt.Sprintf("%v", fieldValue) == fmt.Sprintf("%v", v) { - return false - } - } - } - return true - case "exists": - if existsVal, ok := cond.Value.(bool); ok { - return existsVal - } - return true - case "regex": - if pattern, ok := cond.Value.(string); ok { - matched, _ := regexp.MatchString(pattern, fmt.Sprintf("%v", fieldValue)) - return matched - } - return false - default: - return true - } -} - -// applyOperations применяет операции к документу -func (tm *TriggerManager) applyOperations(doc *Document, ops []TriggerOperation, execCtx *TriggerExecution) *Document { - if doc == nil { - return nil - } - - result := doc.Clone() - - for _, op := range ops { - switch op.Type { - case "set": - value := tm.resolveValue(op.Value, execCtx) - result.SetField(op.Field, value) - - case "unset": - result.DeleteField(op.Field) - - case "inc": - if incVal, ok := toFloat64(op.Value); ok { - if current, err := result.GetField(op.Field); err == nil { - if currVal, ok := toFloat64(current); ok { - result.SetField(op.Field, currVal+incVal) - } - } else { - result.SetField(op.Field, incVal) - } - } - - case "mul": - if mulVal, ok := toFloat64(op.Value); ok { - if current, err := result.GetField(op.Field); err == nil { - if currVal, ok := toFloat64(current); ok { - result.SetField(op.Field, currVal*mulVal) - } - } - } - - case "rename": - if newName, ok := op.Value.(string); ok { - if val, err := result.GetField(op.Field); err == nil { - result.SetField(newName, val) - result.DeleteField(op.Field) - } - } - - case "currentDate": - result.SetField(op.Field, time.Now().UnixMilli()) - } - } - - return result -} - -// resolveValue разрешает специальные значения типа $$NOW, $$USER -func (tm *TriggerManager) resolveValue(value interface{}, execCtx *TriggerExecution) interface{} { - if strVal, ok := value.(string); ok { - switch strVal { - case "$$NOW": - return time.Now().UnixMilli() - case "$$USER": - if execCtx.User != "" { - return execCtx.User - } - return "anonymous" - case "$$ROLE": - if execCtx.Role != "" { - return execCtx.Role - } - return "anonymous" - } - } - return value -} - -// logExecution логирует выполнение триггера -func (tm *TriggerManager) logExecution(execCtx *TriggerExecution, trigger *Trigger, result string, startTime time.Time) { - tm.mu.Lock() - defer tm.mu.Unlock() - - duration := time.Since(startTime) - now := time.Now() - nowMs := now.UnixMilli() - nowStr := now.Format("2006-01-02 15:04:05.000") - - execCtx.TriggerName = trigger.Name - execCtx.Timestamp = now - execCtx.TimestampMs = nowMs - execCtx.TimestampStr = nowStr - execCtx.ActionResult = result - execCtx.DurationMs = duration.Milliseconds() - - if len(tm.auditLog) >= tm.maxLogSize { - tm.auditLog = tm.auditLog[1:] - } - tm.auditLog = append(tm.auditLog, execCtx) - - // Аудит выполнения триггера - LogAudit("TRIGGER_EXECUTE", "TRIGGER", fmt.Sprintf("%s.%s", trigger.Collection, trigger.Name), map[string]interface{}{ - "event": execCtx.Event, - "result": result, - "duration_ms": duration.Milliseconds(), - "document_id": execCtx.DocumentID, - }) -} - -// GetTriggerExecutionLog возвращает лог выполнения триггеров -func (tm *TriggerManager) GetTriggerExecutionLog() []*TriggerExecution { - tm.mu.RLock() - defer tm.mu.RUnlock() - - result := make([]*TriggerExecution, len(tm.auditLog)) - copy(result, tm.auditLog) - return result -} - -// GetTriggerExecutionLogFiltered возвращает отфильтрованный лог выполнения триггеров -func (tm *TriggerManager) GetTriggerExecutionLogFiltered(triggerName, collection string, fromTime, toTime int64) []*TriggerExecution { - tm.mu.RLock() - defer tm.mu.RUnlock() - - result := make([]*TriggerExecution, 0) - for _, entry := range tm.auditLog { - if triggerName != "" && entry.TriggerName != triggerName { - continue - } - if collection != "" && entry.Collection != collection { - continue - } - if fromTime > 0 && entry.TimestampMs < fromTime { - continue - } - if toTime > 0 && entry.TimestampMs > toTime { - continue - } - result = append(result, entry) - } - return result -} - -// ClearTriggerLog очищает лог выполнения триггеров -func (tm *TriggerManager) ClearTriggerLog() { - tm.mu.Lock() - defer tm.mu.Unlock() - tm.auditLog = make([]*TriggerExecution, 0) - LogAudit("CLEAR", "TRIGGER_LOG", "all", nil) -} - -// GetTriggerExecutionStats возвращает статистику выполнения триггеров -func (tm *TriggerManager) GetTriggerExecutionStats() map[string]interface{} { - tm.mu.RLock() - defer tm.mu.RUnlock() - - triggerStats := make(map[string]map[string]interface{}) - for _, entry := range tm.auditLog { - if _, ok := triggerStats[entry.TriggerName]; !ok { - triggerStats[entry.TriggerName] = map[string]interface{}{ - "total_executions": 0, - "aborted": 0, - "skipped": 0, - "modified": 0, - "logged": 0, - "notified": 0, - "total_duration_ms": int64(0), - "avg_duration_ms": float64(0), - } - } - - stats := triggerStats[entry.TriggerName] - stats["total_executions"] = stats["total_executions"].(int) + 1 - stats["total_duration_ms"] = stats["total_duration_ms"].(int64) + entry.DurationMs - - switch entry.ActionResult { - case "aborted": - stats["aborted"] = stats["aborted"].(int) + 1 - case "skipped": - stats["skipped"] = stats["skipped"].(int) + 1 - case "modified": - stats["modified"] = stats["modified"].(int) + 1 - case "logged": - stats["logged"] = stats["logged"].(int) + 1 - case "notified": - stats["notified"] = stats["notified"].(int) + 1 - } - } - - // Вычисляем среднюю длительность - for _, stats := range triggerStats { - if totalExec, ok := stats["total_executions"].(int); ok && totalExec > 0 { - if totalDuration, ok := stats["total_duration_ms"].(int64); ok { - stats["avg_duration_ms"] = float64(totalDuration) / float64(totalExec) - } - } - } - - return map[string]interface{}{ - "total_trigger_executions": len(tm.auditLog), - "triggers_stats": triggerStats, - "log_size": len(tm.auditLog), - "max_log_size": tm.maxLogSize, - } -} - -// getTriggerKey возвращает ключ для хранения триггера -func (tm *TriggerManager) getTriggerKey(collection string, event TriggerEvent, name string) string { - return fmt.Sprintf("%s|%s|%s", collection, event, name) -} - -// compareNumbers сравнивает два числа -func compareNumbers(a, b interface{}) int { - aVal, aOk := toFloat64(a) - bVal, bOk := toFloat64(b) - - if aOk && bOk { - if aVal < bVal { - return -1 - } - if aVal > bVal { - return 1 - } - return 0 - } - return 0 -} - -// ============================================================================= -// TRIGGER CONFIG BUILDER -// ============================================================================= - -// MongoDBLikeTriggerConfig создаёт конфигурацию триггера в стиле MongoDB -func MongoDBLikeTriggerConfig() *TriggerConfigBuilder { - return &TriggerConfigBuilder{ - config: make(map[string]interface{}), - ops: make([]interface{}, 0), - } -} - -// TriggerConfigBuilder строитель конфигурации триггера -type TriggerConfigBuilder struct { - config map[string]interface{} - ops []interface{} -} - -// On устанавливает событие триггера -func (b *TriggerConfigBuilder) On(event string) *TriggerConfigBuilder { - b.config["event"] = event - return b -} - -// Condition добавляет условие -func (b *TriggerConfigBuilder) Condition(field, operator string, value interface{}) *TriggerConfigBuilder { - b.config["condition"] = map[string]interface{}{ - "field": field, - "operator": operator, - "value": value, - } - return b -} - -// ConditionRegex добавляет regex условие -func (b *TriggerConfigBuilder) ConditionRegex(field, pattern string) *TriggerConfigBuilder { - b.config["condition"] = map[string]interface{}{ - "field": field, - "operator": "regex", - "value": pattern, - } - return b -} - -// Set добавляет операцию установки поля -func (b *TriggerConfigBuilder) Set(field string, value interface{}) *TriggerConfigBuilder { - b.ops = append(b.ops, map[string]interface{}{ - "type": "set", - "field": field, - "value": value, - }) - return b -} - -// Unset добавляет операцию удаления поля -func (b *TriggerConfigBuilder) Unset(field string) *TriggerConfigBuilder { - b.ops = append(b.ops, map[string]interface{}{ - "type": "unset", - "field": field, - }) - return b -} - -// Inc добавляет операцию инкремента -func (b *TriggerConfigBuilder) Inc(field string, value float64) *TriggerConfigBuilder { - b.ops = append(b.ops, map[string]interface{}{ - "type": "inc", - "field": field, - "value": value, - }) - return b -} - -// Mul добавляет операцию умножения -func (b *TriggerConfigBuilder) Mul(field string, value float64) *TriggerConfigBuilder { - b.ops = append(b.ops, map[string]interface{}{ - "type": "mul", - "field": field, - "value": value, - }) - return b -} - -// Rename добавляет операцию переименования поля -func (b *TriggerConfigBuilder) Rename(oldName, newName string) *TriggerConfigBuilder { - b.ops = append(b.ops, map[string]interface{}{ - "type": "rename", - "field": oldName, - "value": newName, - }) - return b -} - -// CurrentDate добавляет операцию установки текущей даты -func (b *TriggerConfigBuilder) CurrentDate(field string) *TriggerConfigBuilder { - b.ops = append(b.ops, map[string]interface{}{ - "type": "currentDate", - "field": field, - }) - return b -} - -// Action устанавливает действие триггера -func (b *TriggerConfigBuilder) Action(action string) *TriggerConfigBuilder { - b.config["action"] = action - return b -} - -// Description устанавливает описание триггера -func (b *TriggerConfigBuilder) Description(desc string) *TriggerConfigBuilder { - b.config["description"] = desc - return b -} - -// Build собирает конфигурацию -func (b *TriggerConfigBuilder) Build() map[string]interface{} { - b.config["operations"] = b.ops - return b.config -}