Constraint: 在线热修前必须保存当前迭代分支全部有效代码进展 Confidence: medium Scope-risk: broad Directive: 后续修改需保持审计事件与业务事务边界一致 Tested: git diff --cached --check Not-tested: 未运行全量测试,提交用于切换分支前保存既有工作
74 lines
2.7 KiB
Go
74 lines
2.7 KiB
Go
package audit
|
|
|
|
import (
|
|
"context"
|
|
"strconv"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/break/junhong_cmp_fiber/internal/model"
|
|
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
|
pkgerrors "github.com/break/junhong_cmp_fiber/pkg/errors"
|
|
"github.com/break/junhong_cmp_fiber/pkg/middleware"
|
|
)
|
|
|
|
// PollingInput 描述轮询配置、规则或人工任务的审计事实。
|
|
type PollingInput struct {
|
|
EventID string
|
|
ActionCode string
|
|
Summary string
|
|
ResourceType string
|
|
ResourceID uint
|
|
ResourceKey string
|
|
DisplayName string
|
|
OperatorID uint
|
|
IdentitySnapshot map[string]any
|
|
BeforeData map[string]any
|
|
AfterData map[string]any
|
|
Metadata map[string]any
|
|
Cards []*model.IotCard
|
|
Result string
|
|
ErrorCode string
|
|
ErrorSummary string
|
|
}
|
|
|
|
// WritePolling 将轮询配置、规则或人工任务转换为统一 Audit Event。
|
|
func (w *Writer) WritePolling(ctx context.Context, tx *gorm.DB, input PollingInput) error {
|
|
if input.OperatorID == 0 || input.ResourceType == "" || input.ResourceKey == "" {
|
|
return pkgerrors.New(pkgerrors.CodeInvalidParam, "轮询审计资源或操作者不完整")
|
|
}
|
|
resourceID := optionalResourceID(input.ResourceID)
|
|
resources := []ResourceInput{{
|
|
Type: input.ResourceType, ID: resourceID, Key: input.ResourceKey, DisplayName: input.DisplayName,
|
|
Relation: constants.AuditResourceRelationPrimary, Role: constants.AuditResourceRolePollingTarget,
|
|
IdentitySnapshot: input.IdentitySnapshot, BeforeData: input.BeforeData, AfterData: input.AfterData,
|
|
SubjectVisibility: constants.AuditSubjectInternalOnly,
|
|
}}
|
|
for index, card := range input.Cards {
|
|
if card == nil || card.ID == 0 {
|
|
continue
|
|
}
|
|
resources = append(resources, ResourceInput{
|
|
Type: constants.AuditResourceIotCard, ID: optionalResourceID(card.ID),
|
|
Key: iotCardResourceKey(card), DisplayName: card.ICCID,
|
|
Relation: constants.AuditResourceRelationReference, Role: constants.AuditResourceRolePollingCard,
|
|
IdentitySnapshot: iotCardIdentity(card), SubjectVisibility: constants.AuditSubjectInternalOnly,
|
|
SortOrder: index + 1,
|
|
})
|
|
}
|
|
result := input.Result
|
|
if result == "" {
|
|
result = constants.AuditResultSuccess
|
|
}
|
|
return w.Append(ctx, tx, AppendInput{
|
|
EventID: input.EventID, ActionCode: input.ActionCode, Summary: input.Summary,
|
|
Actor: ActorInput{
|
|
Kind: constants.AuditActorAccount, ID: strconv.FormatUint(uint64(input.OperatorID), 10),
|
|
Name: middleware.GetUsernameFromContext(ctx),
|
|
},
|
|
Source: constants.AuditSourceAdminAPI, ScopeType: constants.AuditScopePlatform,
|
|
Result: result, ErrorCode: input.ErrorCode, ErrorSummary: input.ErrorSummary,
|
|
Metadata: input.Metadata, Resources: resources,
|
|
})
|
|
}
|