feat: simplify SOP to immediate-effect configuration
This commit is contained in:
@@ -2,6 +2,8 @@ package audit
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.iwork-ai.com/xdc/iqudo-top1/internal/auth"
|
||||
"git.iwork-ai.com/xdc/iqudo-top1/internal/model"
|
||||
@@ -11,10 +13,6 @@ import (
|
||||
)
|
||||
|
||||
func Record(db *gorm.DB, principal auth.Principal, action, resource string, resourceID uint64, payload interface{}) error {
|
||||
data, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fields := []zap.Field{
|
||||
zap.Uint64("tenant_id", principal.TenantID),
|
||||
zap.Uint64("user_id", principal.UserID),
|
||||
@@ -22,7 +20,9 @@ func Record(db *gorm.DB, principal auth.Principal, action, resource string, reso
|
||||
zap.String("resource", resource),
|
||||
zap.Uint64("resource_id", resourceID),
|
||||
}
|
||||
if err := db.Create(&model.AuditLog{TenantID: principal.TenantID, UserID: principal.UserID, Action: action, Resource: resource, ResourceID: resourceID, Payload: datatypes.JSON(data)}).Error; err != nil {
|
||||
if err := db.Transaction(func(tx *gorm.DB) error {
|
||||
return RecordTx(tx, principal, action, resource, resourceID, payload)
|
||||
}); err != nil {
|
||||
zap.L().Error("persist business event", append(fields, zap.Error(err))...)
|
||||
return err
|
||||
}
|
||||
@@ -30,6 +30,43 @@ func Record(db *gorm.DB, principal auth.Principal, action, resource string, reso
|
||||
return nil
|
||||
}
|
||||
|
||||
// RecordTx persists the audit entry and its projection event in the caller's
|
||||
// transaction. Callers that mutate business data should use this function so
|
||||
// the mutation and Outbox event cannot diverge.
|
||||
func RecordTx(tx *gorm.DB, principal auth.Principal, action, resource string, resourceID uint64, payload interface{}) error {
|
||||
data, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
entry := model.AuditLog{TenantID: principal.TenantID, UserID: principal.UserID, Action: action, Resource: resource, ResourceID: resourceID, Payload: datatypes.JSON(data)}
|
||||
if err := tx.Create(&entry).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if !isProjected(resource, action) {
|
||||
return nil
|
||||
}
|
||||
auditID := entry.ID
|
||||
outbox := model.MultiTableOutbox{TenantID: principal.TenantID, AuditLogID: &auditID, Resource: resource, ResourceID: resourceID, Action: action, Payload: datatypes.JSON(data), DedupeKey: fmt.Sprintf("audit:%d", entry.ID), Status: "pending", AvailableAt: time.Now()}
|
||||
return tx.Create(&outbox).Error
|
||||
}
|
||||
|
||||
func isProjected(resource, action string) bool {
|
||||
switch resource {
|
||||
case "scenario":
|
||||
return action == "create" || action == "update" || action == "archive"
|
||||
case "scenario_field":
|
||||
return action == "create" || action == "update" || action == "delete"
|
||||
case "sop":
|
||||
return action == "publish" || action == "offline"
|
||||
case "scenario_rule", "knowledge_item", "knowledge_relation":
|
||||
return action == "create" || action == "update" || action == "delete" || action == "archive"
|
||||
case "sop_run":
|
||||
return action == "start" || action == "finish" || action == "feedback"
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func logBusinessEvent(fields []zap.Field) {
|
||||
zap.L().Info("business event", fields...)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user