This commit is contained in:
@@ -105,6 +105,9 @@ func NewRegistry() *Registry {
|
||||
iotCardRealnameCallbackSynced := iotCardAction(constants.AuditActionIotCardRealnameCallbackSynced, "运营商回调同步 IoT 卡实名状态", constants.AuditActorExternalSystem, constants.AuditSourceCallback)
|
||||
iotCardManualRefreshed := iotCardAction(constants.AuditActionIotCardManualRefreshed, "人工刷新 IoT 卡", constants.AuditActorAccount, constants.AuditSourceAdminAPI)
|
||||
iotCardPersonalRefreshed := iotCardAction(constants.AuditActionIotCardPersonalRefreshed, "个人客户刷新 IoT 卡", constants.AuditActorPersonalCustomer, constants.AuditSourcePersonalAPI)
|
||||
iotCardWorkerRealnameSynced := iotCardAction(constants.AuditActionIotCardWorkerRealnameSynced, "Worker 同步 IoT 卡实名事实", constants.AuditActorSystemTask, constants.AuditSourceWorker)
|
||||
iotCardWorkerTrafficSynced := iotCardAction(constants.AuditActionIotCardWorkerTrafficSynced, "Worker 同步 IoT 卡流量事实", constants.AuditActorSystemTask, constants.AuditSourceWorker)
|
||||
iotCardWorkerNetworkSynced := iotCardAction(constants.AuditActionIotCardWorkerNetworkSynced, "Worker 同步 IoT 卡网络事实", constants.AuditActorSystemTask, constants.AuditSourceWorker)
|
||||
iotCardManualStopped := iotCardAction(constants.AuditActionIotCardManualStopped, "人工停用 IoT 卡网络", constants.AuditActorAccount, constants.AuditSourceAdminAPI)
|
||||
iotCardManualStarted := iotCardAction(constants.AuditActionIotCardManualStarted, "人工恢复 IoT 卡网络", constants.AuditActorAccount, constants.AuditSourceAdminAPI)
|
||||
iotCardAutoStopped := iotCardAction(constants.AuditActionIotCardAutoStopped, "自动停用 IoT 卡网络", constants.AuditActorSystemTask, constants.AuditSourceWorker)
|
||||
@@ -131,6 +134,7 @@ func NewRegistry() *Registry {
|
||||
deviceCardBound := deviceAction(constants.AuditActionDeviceCardBound, "设备绑定 IoT 卡", constants.AuditActorAccount, constants.AuditSourceAdminAPI)
|
||||
deviceCardUnbound := deviceAction(constants.AuditActionDeviceCardUnbound, "设备解绑 IoT 卡", constants.AuditActorAccount, constants.AuditSourceAdminAPI)
|
||||
deviceCurrentCardSwitched := deviceExternalAction(constants.AuditActionDeviceCurrentCardSwitched, "切换设备当前卡", true)
|
||||
deviceWorkerObservationSynced := deviceAction(constants.AuditActionDeviceWorkerObservationSynced, "Worker 同步设备观测事实", constants.AuditActorSystemTask, constants.AuditSourceWorker)
|
||||
cardExchangeCreated := cardExchangeAction(constants.AuditActionCardExchangeCreated, "创建卡换货单", constants.AuditRiskNormal, false)
|
||||
cardExchangeShippingInfoSubmitted := cardExchangeAction(constants.AuditActionCardExchangeShippingInfoSubmitted, "提交卡换货收货信息", constants.AuditRiskHigh, true)
|
||||
cardExchangeShipped := cardExchangeAction(constants.AuditActionCardExchangeShipped, "卡换货发货", constants.AuditRiskNormal, false)
|
||||
@@ -205,6 +209,14 @@ func NewRegistry() *Registry {
|
||||
notificationReadAll.AllowedOrigins = []ActionOrigin{{Actor: constants.AuditActorPersonalCustomer, Source: constants.AuditSourcePersonalAPI}}
|
||||
notificationCleanup := notificationAction(constants.AuditActionNotificationCleanup, "清理过期通知", constants.AuditResourceNotificationCleanupBatch, constants.AuditActorSystemTask, constants.AuditSourceWorker)
|
||||
notificationCleanupItem := notificationAction(constants.AuditActionNotificationCleanupItem, "清理单条过期通知", constants.AuditResourceNotification, constants.AuditActorSystemTask, constants.AuditSourceWorker)
|
||||
retentionCleanup := ActionDefinition{
|
||||
Code: constants.AuditActionLogRetentionCleanup, Name: "清理已归档在线日志",
|
||||
Category: constants.AuditCategoryReliability, Risk: constants.AuditRiskHigh,
|
||||
PrimaryResource: constants.AuditResourceLogArchiveMonth, AllowedActor: constants.AuditActorSystemTask,
|
||||
Source: constants.AuditSourceWorker, RequireTransaction: true,
|
||||
DefaultVisibility: constants.AuditSubjectInternalOnly,
|
||||
AllowedVisibility: []string{constants.AuditSubjectInternalOnly},
|
||||
}
|
||||
pollingConfigCreated := pollingAction(constants.AuditActionPollingConfigCreated, "创建轮询配置", constants.AuditResourcePollingConfig, constants.AuditRiskHigh)
|
||||
pollingConfigUpdated := pollingAction(constants.AuditActionPollingConfigUpdated, "更新轮询配置", constants.AuditResourcePollingConfig, constants.AuditRiskHigh)
|
||||
pollingConfigDeleted := pollingAction(constants.AuditActionPollingConfigDeleted, "删除轮询配置", constants.AuditResourcePollingConfig, constants.AuditRiskHigh)
|
||||
@@ -407,6 +419,9 @@ func NewRegistry() *Registry {
|
||||
constants.AuditActionIotCardRealnameCallbackSynced: iotCardRealnameCallbackSynced,
|
||||
constants.AuditActionIotCardManualRefreshed: iotCardManualRefreshed,
|
||||
constants.AuditActionIotCardPersonalRefreshed: iotCardPersonalRefreshed,
|
||||
constants.AuditActionIotCardWorkerRealnameSynced: iotCardWorkerRealnameSynced,
|
||||
constants.AuditActionIotCardWorkerTrafficSynced: iotCardWorkerTrafficSynced,
|
||||
constants.AuditActionIotCardWorkerNetworkSynced: iotCardWorkerNetworkSynced,
|
||||
constants.AuditActionIotCardManualStopped: iotCardManualStopped,
|
||||
constants.AuditActionIotCardManualStarted: iotCardManualStarted,
|
||||
constants.AuditActionIotCardAutoStopped: iotCardAutoStopped,
|
||||
@@ -432,6 +447,7 @@ func NewRegistry() *Registry {
|
||||
constants.AuditActionDeviceCardBound: deviceCardBound,
|
||||
constants.AuditActionDeviceCardUnbound: deviceCardUnbound,
|
||||
constants.AuditActionDeviceCurrentCardSwitched: deviceCurrentCardSwitched,
|
||||
constants.AuditActionDeviceWorkerObservationSynced: deviceWorkerObservationSynced,
|
||||
constants.AuditActionCardExchangeCreated: cardExchangeCreated,
|
||||
constants.AuditActionCardExchangeShippingInfoSubmitted: cardExchangeShippingInfoSubmitted,
|
||||
constants.AuditActionCardExchangeShipped: cardExchangeShipped,
|
||||
@@ -478,6 +494,7 @@ func NewRegistry() *Registry {
|
||||
constants.AuditActionNotificationReadAll: notificationReadAll,
|
||||
constants.AuditActionNotificationCleanup: notificationCleanup,
|
||||
constants.AuditActionNotificationCleanupItem: notificationCleanupItem,
|
||||
constants.AuditActionLogRetentionCleanup: retentionCleanup,
|
||||
constants.AuditActionPollingConfigCreated: pollingConfigCreated,
|
||||
constants.AuditActionPollingConfigUpdated: pollingConfigUpdated,
|
||||
constants.AuditActionPollingConfigDeleted: pollingConfigDeleted,
|
||||
@@ -608,6 +625,10 @@ func NewRegistry() *Registry {
|
||||
"resource_type", "resource_id", "resource_key", "correlation_id",
|
||||
},
|
||||
},
|
||||
constants.AuditResourceLogArchiveMonth: {
|
||||
Type: constants.AuditResourceLogArchiveMonth, Name: "日志归档自然月",
|
||||
IdentityFields: []string{"month", "timezone", "range_start", "range_end"},
|
||||
},
|
||||
constants.AuditResourceDeviceBatchTask: {
|
||||
Type: constants.AuditResourceDeviceBatchTask, Name: "设备批量分配任务",
|
||||
IdentityFields: []string{"task_no", "operation_type"},
|
||||
|
||||
38
internal/infrastructure/audit/retention.go
Normal file
38
internal/infrastructure/audit/retention.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package audit
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
auditarchive "github.com/break/junhong_cmp_fiber/internal/application/auditarchive"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
)
|
||||
|
||||
// WriteRetentionCleanup 将月度物理清理结果写入当前在线月份的统一审计事件。
|
||||
func (w *Writer) WriteRetentionCleanup(ctx context.Context, tx *gorm.DB, input auditarchive.RetentionAudit) error {
|
||||
return w.Append(ctx, tx, AppendInput{
|
||||
EventID: input.EventID, ActionCode: constants.AuditActionLogRetentionCleanup,
|
||||
Summary: input.Summary,
|
||||
Actor: ActorInput{
|
||||
Kind: constants.AuditActorSystemTask, ID: constants.AuditActorIDRetentionWorker, Name: "日志留存清理任务",
|
||||
},
|
||||
Source: constants.AuditSourceWorker, ScopeType: constants.AuditScopePlatform,
|
||||
Result: input.Result, ErrorSummary: input.ErrorSummary,
|
||||
CorrelationID: "retention:" + input.Month,
|
||||
Metadata: map[string]any{
|
||||
"event_count": input.EventCount, "resource_count": input.ResourceCount,
|
||||
"integration_count": input.IntegrationCount, "manifest_keys": input.ManifestKeys,
|
||||
"duration_ms": input.DurationMS,
|
||||
},
|
||||
Resources: []ResourceInput{{
|
||||
Type: constants.AuditResourceLogArchiveMonth, Key: input.Month, DisplayName: input.Month,
|
||||
Relation: constants.AuditResourceRelationPrimary, Role: constants.AuditResourceRoleRetentionMonth,
|
||||
IdentitySnapshot: map[string]any{
|
||||
"month": input.Month, "timezone": constants.AuditArchiveTimezone,
|
||||
"range_start": input.RangeStart, "range_end": input.RangeEnd,
|
||||
},
|
||||
SubjectVisibility: constants.AuditSubjectInternalOnly,
|
||||
}},
|
||||
})
|
||||
}
|
||||
@@ -12,9 +12,11 @@ import (
|
||||
cardapp "github.com/break/junhong_cmp_fiber/internal/application/cardobservation"
|
||||
carddomain "github.com/break/junhong_cmp_fiber/internal/domain/cardobservation"
|
||||
"github.com/break/junhong_cmp_fiber/internal/gateway"
|
||||
"github.com/break/junhong_cmp_fiber/internal/infrastructure/audit"
|
||||
"github.com/break/junhong_cmp_fiber/internal/infrastructure/integrationlog"
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/auditcontext"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
apperrors "github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||
)
|
||||
@@ -77,11 +79,15 @@ type SeriesRunner struct {
|
||||
observation *cardapp.Service
|
||||
integration *integrationlog.Repository
|
||||
carrier *postgres.CarrierStore
|
||||
auditWriter *audit.Writer
|
||||
}
|
||||
|
||||
// NewSeriesRunner 创建卡观测序列 Gateway 执行器。
|
||||
func NewSeriesRunner(db *gorm.DB, gatewayClient *gateway.Client, observation *cardapp.Service, integration *integrationlog.Repository) *SeriesRunner {
|
||||
return &SeriesRunner{db: db, gateway: gatewayClient, observation: observation, integration: integration, carrier: postgres.NewCarrierStore(db)}
|
||||
func NewSeriesRunner(db *gorm.DB, gatewayClient *gateway.Client, observation *cardapp.Service, integration *integrationlog.Repository, auditWriter *audit.Writer) *SeriesRunner {
|
||||
return &SeriesRunner{
|
||||
db: db, gateway: gatewayClient, observation: observation, integration: integration,
|
||||
carrier: postgres.NewCarrierStore(db), auditWriter: auditWriter,
|
||||
}
|
||||
}
|
||||
|
||||
// Provider 返回用于请求互斥的运营商接入标识。
|
||||
@@ -188,7 +194,7 @@ func (r *SeriesRunner) runDeviceInfo(ctx context.Context, payload cardapp.Series
|
||||
response, runErr := r.gateway.SyncDeviceInfo(ctx, &gateway.SyncDeviceInfoReq{CardNo: deviceGatewayIdentifier(device)})
|
||||
result := cardapp.RunResult{}
|
||||
if runErr == nil {
|
||||
result.StateChanged, runErr = r.applyDeviceInfo(ctx, device, response)
|
||||
result.StateChanged, runErr = r.applyDeviceInfo(ctx, device, response, attempt.IntegrationID)
|
||||
}
|
||||
completionResult := constants.IntegrationResultSuccess
|
||||
if runErr != nil {
|
||||
@@ -222,7 +228,7 @@ func (r *SeriesRunner) startDeviceAttempt(ctx context.Context, payload cardapp.S
|
||||
})
|
||||
}
|
||||
|
||||
func (r *SeriesRunner) applyDeviceInfo(ctx context.Context, device *model.Device, response *gateway.SyncDeviceInfoResp) (bool, error) {
|
||||
func (r *SeriesRunner) applyDeviceInfo(ctx context.Context, device *model.Device, response *gateway.SyncDeviceInfoResp, integrationID string) (bool, error) {
|
||||
if response == nil {
|
||||
return false, apperrors.New(apperrors.CodeGatewayError, "Gateway 设备信息响应为空")
|
||||
}
|
||||
@@ -236,21 +242,19 @@ func (r *SeriesRunner) applyDeviceInfo(ctx context.Context, device *model.Device
|
||||
if lastOnlineTime := parseGatewayTime(response.LastOnlineTime); lastOnlineTime != nil {
|
||||
updates["last_online_time"] = lastOnlineTime
|
||||
}
|
||||
var currentBinding struct {
|
||||
SlotPosition int
|
||||
}
|
||||
currentSlotErr := r.db.WithContext(ctx).Model(&model.DeviceSimBinding{}).
|
||||
Select("slot_position").
|
||||
Where("device_id = ? AND bind_status = ? AND is_current = ?", device.ID, constants.BindStatusBound, true).
|
||||
Take(¤tBinding).Error
|
||||
if currentSlotErr != nil && currentSlotErr != gorm.ErrRecordNotFound {
|
||||
return false, apperrors.Wrap(apperrors.CodeDatabaseError, currentSlotErr, "读取设备当前槽位失败")
|
||||
}
|
||||
changed := device.OnlineStatus != int(response.OnlineStatus) ||
|
||||
deviceChanged := device.OnlineStatus != int(response.OnlineStatus) ||
|
||||
device.SoftwareVersion != string(response.SoftwareVersion) ||
|
||||
device.SwitchMode != string(response.SwitchMode) ||
|
||||
currentBinding.SlotPosition != int(response.CurrentSlotNo)
|
||||
device.SwitchMode != string(response.SwitchMode)
|
||||
changed := deviceChanged
|
||||
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
state, err := loadDeviceObservationState(ctx, tx, device.ID, int(response.CurrentSlotNo))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
beforeSlot := bindingSlot(state.current)
|
||||
afterSlot := bindingSlot(state.target)
|
||||
changed = deviceChanged || beforeSlot != int(response.CurrentSlotNo)
|
||||
auditChanged := deviceChanged || beforeSlot != afterSlot
|
||||
if err := tx.Model(&model.Device{}).Where("id = ?", device.ID).Updates(updates).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -259,19 +263,192 @@ func (r *SeriesRunner) applyDeviceInfo(ctx context.Context, device *model.Device
|
||||
Update("is_current", false).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if int(response.CurrentSlotNo) <= 0 {
|
||||
if int(response.CurrentSlotNo) > 0 {
|
||||
if err := tx.Model(&model.DeviceSimBinding{}).
|
||||
Where("device_id = ? AND slot_position = ? AND bind_status = ?", device.ID, int(response.CurrentSlotNo), constants.BindStatusBound).
|
||||
Update("is_current", true).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !auditChanged {
|
||||
return nil
|
||||
}
|
||||
return tx.Model(&model.DeviceSimBinding{}).
|
||||
Where("device_id = ? AND slot_position = ? AND bind_status = ?", device.ID, int(response.CurrentSlotNo), constants.BindStatusBound).
|
||||
Update("is_current", true).Error
|
||||
return r.appendDeviceObservationAudit(ctx, tx, device, state, beforeSlot, afterSlot, integrationID, map[string]any{
|
||||
"online_status": device.OnlineStatus, "software_version": device.SoftwareVersion,
|
||||
"switch_mode": device.SwitchMode, "current_slot": beforeSlot,
|
||||
}, map[string]any{
|
||||
"online_status": int(response.OnlineStatus), "software_version": string(response.SoftwareVersion),
|
||||
"switch_mode": string(response.SwitchMode), "current_slot": afterSlot,
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
return false, apperrors.Wrap(apperrors.CodeDatabaseError, err, "回写设备 Gateway 信息失败")
|
||||
wrapped := apperrors.Wrap(apperrors.CodeDatabaseError, err, "回写设备 Gateway 信息失败")
|
||||
r.recordDeviceObservationFailure(ctx, device, integrationID, wrapped)
|
||||
return false, wrapped
|
||||
}
|
||||
return changed, nil
|
||||
}
|
||||
|
||||
type deviceObservationState struct {
|
||||
bindings []model.DeviceSimBinding
|
||||
cards map[uint]*model.IotCard
|
||||
current *model.DeviceSimBinding
|
||||
target *model.DeviceSimBinding
|
||||
}
|
||||
|
||||
func loadDeviceObservationState(ctx context.Context, tx *gorm.DB, deviceID uint, targetSlot int) (*deviceObservationState, error) {
|
||||
state := &deviceObservationState{cards: make(map[uint]*model.IotCard)}
|
||||
if err := tx.WithContext(ctx).Where("device_id = ? AND bind_status = ?", deviceID, constants.BindStatusBound).
|
||||
Order("slot_position ASC").Find(&state.bindings).Error; err != nil {
|
||||
return nil, apperrors.Wrap(apperrors.CodeDatabaseError, err, "读取设备有效卡槽失败")
|
||||
}
|
||||
cardIDs := make([]uint, 0, len(state.bindings))
|
||||
for index := range state.bindings {
|
||||
binding := &state.bindings[index]
|
||||
cardIDs = append(cardIDs, binding.IotCardID)
|
||||
if binding.IsCurrent && state.current == nil {
|
||||
state.current = binding
|
||||
}
|
||||
if targetSlot > 0 && binding.SlotPosition == targetSlot {
|
||||
state.target = binding
|
||||
}
|
||||
}
|
||||
if len(cardIDs) == 0 {
|
||||
return state, nil
|
||||
}
|
||||
var cards []*model.IotCard
|
||||
if err := tx.WithContext(ctx).Where("id IN ?", cardIDs).Find(&cards).Error; err != nil {
|
||||
return nil, apperrors.Wrap(apperrors.CodeDatabaseError, err, "读取设备卡槽关联 IoT 卡失败")
|
||||
}
|
||||
for _, card := range cards {
|
||||
state.cards[card.ID] = card
|
||||
}
|
||||
return state, nil
|
||||
}
|
||||
|
||||
func bindingSlot(binding *model.DeviceSimBinding) int {
|
||||
if binding == nil {
|
||||
return 0
|
||||
}
|
||||
return binding.SlotPosition
|
||||
}
|
||||
|
||||
func (r *SeriesRunner) appendDeviceObservationAudit(
|
||||
ctx context.Context,
|
||||
tx *gorm.DB,
|
||||
device *model.Device,
|
||||
state *deviceObservationState,
|
||||
beforeSlot, afterSlot int,
|
||||
integrationID string,
|
||||
beforeData, afterData map[string]any,
|
||||
) error {
|
||||
if r.auditWriter == nil {
|
||||
return apperrors.New(apperrors.CodeInternalError, "设备观测统一审计能力未配置")
|
||||
}
|
||||
deviceID := strconv.FormatUint(uint64(device.ID), 10)
|
||||
resources := []audit.ResourceInput{{
|
||||
Type: constants.AuditResourceDevice, ID: &deviceID,
|
||||
Key: audit.DeviceResourceKey(device), DisplayName: device.VirtualNo,
|
||||
Relation: constants.AuditResourceRelationPrimary, Role: constants.AuditResourceRoleDeviceTarget,
|
||||
IdentitySnapshot: audit.DeviceIdentitySnapshot(device), BeforeData: beforeData, AfterData: afterData,
|
||||
SubjectVisibility: constants.AuditSubjectResult, SubjectSummary: "Worker 同步设备观测事实",
|
||||
}}
|
||||
if beforeSlot != afterSlot {
|
||||
resources = appendDeviceObservationBindingResources(resources, device, state.current, state.cards[stateCardID(state.current)], false,
|
||||
constants.AuditResourceRoleDeviceOldCurrentCard, constants.AuditResourceRoleDeviceOldCurrentBinding)
|
||||
resources = appendDeviceObservationBindingResources(resources, device, state.target, state.cards[stateCardID(state.target)], true,
|
||||
constants.AuditResourceRoleDeviceNewCurrentCard, constants.AuditResourceRoleDeviceNewCurrentBinding)
|
||||
}
|
||||
resources = append(resources, deviceObservationIntegrationResource(ctx, device, integrationID))
|
||||
return r.auditWriter.Append(ctx, tx, audit.AppendInput{
|
||||
ActionCode: constants.AuditActionDeviceWorkerObservationSynced,
|
||||
Summary: "Worker 同步设备观测事实", ScopeType: constants.AuditScopePlatform,
|
||||
Result: constants.AuditResultSuccess, Resources: resources,
|
||||
})
|
||||
}
|
||||
|
||||
func appendDeviceObservationBindingResources(
|
||||
resources []audit.ResourceInput,
|
||||
device *model.Device,
|
||||
binding *model.DeviceSimBinding,
|
||||
card *model.IotCard,
|
||||
afterCurrent bool,
|
||||
cardRole, bindingRole string,
|
||||
) []audit.ResourceInput {
|
||||
if binding == nil {
|
||||
return resources
|
||||
}
|
||||
if card != nil {
|
||||
cardID := strconv.FormatUint(uint64(card.ID), 10)
|
||||
resources = append(resources, audit.ResourceInput{
|
||||
Type: constants.AuditResourceIotCard, ID: &cardID,
|
||||
Key: audit.IotCardResourceKey(card), DisplayName: card.ICCID,
|
||||
Relation: constants.AuditResourceRelationAffected, Role: cardRole,
|
||||
IdentitySnapshot: audit.IotCardIdentitySnapshot(card),
|
||||
BeforeData: map[string]any{"is_current": binding.IsCurrent}, AfterData: map[string]any{"is_current": afterCurrent},
|
||||
SubjectVisibility: constants.AuditSubjectResult, SubjectSummary: "设备当前卡槽已同步",
|
||||
})
|
||||
}
|
||||
bindingID := strconv.FormatUint(uint64(binding.ID), 10)
|
||||
identity := map[string]any{
|
||||
"id": binding.ID, "device_id": binding.DeviceID, "device_virtual_no": device.VirtualNo,
|
||||
"slot_position": binding.SlotPosition, "iot_card_id": binding.IotCardID, "is_current": afterCurrent,
|
||||
}
|
||||
if card != nil {
|
||||
identity["iccid"] = card.ICCID
|
||||
identity["virtual_no"] = card.VirtualNo
|
||||
}
|
||||
return append(resources, audit.ResourceInput{
|
||||
Type: constants.AuditResourceDeviceSIMBinding, ID: &bindingID,
|
||||
Key: bindingID, DisplayName: device.VirtualNo,
|
||||
Relation: constants.AuditResourceRelationAffected, Role: bindingRole,
|
||||
IdentitySnapshot: identity,
|
||||
BeforeData: map[string]any{"is_current": binding.IsCurrent}, AfterData: map[string]any{"is_current": afterCurrent},
|
||||
SubjectVisibility: constants.AuditSubjectInternalOnly,
|
||||
})
|
||||
}
|
||||
|
||||
func stateCardID(binding *model.DeviceSimBinding) uint {
|
||||
if binding == nil {
|
||||
return 0
|
||||
}
|
||||
return binding.IotCardID
|
||||
}
|
||||
|
||||
func deviceObservationIntegrationResource(ctx context.Context, device *model.Device, integrationID string) audit.ResourceInput {
|
||||
deviceID := strconv.FormatUint(uint64(device.ID), 10)
|
||||
return audit.ResourceInput{
|
||||
Type: constants.AuditResourceIntegrationLog, Key: integrationID, DisplayName: integrationID,
|
||||
Relation: constants.AuditResourceRelationReference, Role: constants.AuditResourceRoleWorkerIntegration,
|
||||
IdentitySnapshot: map[string]any{
|
||||
"integration_id": integrationID, "provider": constants.IntegrationProviderGateway,
|
||||
"direction": constants.IntegrationDirectionOutbound, "operation": constants.IntegrationOperationGatewayDeviceInfo,
|
||||
"resource_type": constants.CardObservationResourceTypeDevice, "resource_id": deviceID,
|
||||
"resource_key": "device:" + deviceID, "correlation_id": auditcontext.From(ctx).CorrelationID,
|
||||
},
|
||||
SubjectVisibility: constants.AuditSubjectInternalOnly,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SeriesRunner) recordDeviceObservationFailure(ctx context.Context, device *model.Device, integrationID string, businessErr error) {
|
||||
deviceID := strconv.FormatUint(uint64(device.ID), 10)
|
||||
r.auditWriter.RecordFailure(ctx, r.db, audit.AppendInput{
|
||||
ActionCode: constants.AuditActionDeviceWorkerObservationSynced,
|
||||
Summary: "Worker 同步设备观测事实失败", ScopeType: constants.AuditScopePlatform,
|
||||
Resources: []audit.ResourceInput{{
|
||||
Type: constants.AuditResourceDevice, ID: &deviceID,
|
||||
Key: audit.DeviceResourceKey(device), DisplayName: device.VirtualNo,
|
||||
Relation: constants.AuditResourceRelationPrimary, Role: constants.AuditResourceRoleDeviceTarget,
|
||||
IdentitySnapshot: audit.DeviceIdentitySnapshot(device),
|
||||
BeforeData: map[string]any{
|
||||
"online_status": device.OnlineStatus, "software_version": device.SoftwareVersion,
|
||||
"switch_mode": device.SwitchMode,
|
||||
},
|
||||
SubjectVisibility: constants.AuditSubjectResult,
|
||||
}, deviceObservationIntegrationResource(ctx, device, integrationID)},
|
||||
}, businessErr)
|
||||
}
|
||||
|
||||
func (r *SeriesRunner) loadDevice(ctx context.Context, payload cardapp.SeriesTaskPayload) (*model.Device, error) {
|
||||
if payload.ResourceType != constants.CardObservationResourceTypeDevice {
|
||||
return nil, apperrors.New(apperrors.CodeInvalidParam, "设备信息观测资源类型无效")
|
||||
|
||||
Reference in New Issue
Block a user