收口审计治理与套餐任务进展

Constraint: 在线热修前必须保存当前迭代分支全部有效代码进展
Confidence: medium
Scope-risk: broad
Directive: 后续修改需保持审计事件与业务事务边界一致
Tested: git diff --cached --check
Not-tested: 未运行全量测试,提交用于切换分支前保存既有工作
This commit is contained in:
2026-08-05 14:30:54 +08:00
parent b3499adfca
commit 5e552d99bc
178 changed files with 16797 additions and 5674 deletions

View File

@@ -5,6 +5,7 @@ import (
"strconv"
"github.com/break/junhong_cmp_fiber/internal/gateway"
"github.com/break/junhong_cmp_fiber/internal/infrastructure/audit"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/internal/model/dto"
"github.com/break/junhong_cmp_fiber/pkg/constants"
@@ -51,73 +52,31 @@ func (s *Service) GatewayGetSlotInfo(ctx context.Context, identifier string) (*g
func (s *Service) GatewaySetWiFi(ctx context.Context, identifier string, req *dto.SetWiFiRequest) error {
device, imei, err := s.getGatewayDevice(ctx, identifier)
if err != nil {
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceSetWiFi,
"设备设置WiFi失败",
constants.AssetAuditResultFailed,
nil,
nil,
map[string]any{
"identifier": identifier,
"ssid": req.SSID,
"enabled": req.Enabled,
"password": req.Password,
},
0,
0,
0,
err,
)
return err
}
observation := s.captureDeviceControlObservation(ctx, device.ID, 0, "")
if err = s.gatewayClient.SetWiFi(ctx, &gateway.WiFiReq{
CardNo: imei,
Params: gateway.WiFiParams{
SSIDName: req.SSID,
SSIDPassword: req.Password,
err = s.executeDeviceGatewayCommand(ctx, device, deviceGatewayCommand{
ActionCode: constants.AuditActionDeviceWiFiSet,
Summary: "设置设备 Wi-Fi",
Operation: constants.IntegrationOperationGatewaySetWiFi,
Scene: constants.CardObservationSceneDeviceSetWiFi,
RequestSummary: map[string]any{
"device_id": device.ID, "imei": imei, "ssid": req.SSID,
"enabled_requested": req.Enabled, "credentials_configured": req.Password != "",
},
}); err != nil {
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceSetWiFi,
"设备设置WiFi失败",
constants.AssetAuditResultFailed,
device,
map[string]any{"device": deviceSnapshot(device)},
map[string]any{
"imei": imei,
"ssid": req.SSID,
"enabled": req.Enabled,
"password": req.Password,
},
0,
0,
0,
err,
)
Metadata: map[string]any{
"ssid": req.SSID, "enabled_requested": req.Enabled, "credentials_configured": req.Password != "",
},
Call: func(callCtx context.Context) error {
return s.gatewayClient.SetWiFi(callCtx, &gateway.WiFiReq{
CardNo: imei,
Params: gateway.WiFiParams{SSIDName: req.SSID, SSIDPassword: req.Password},
})
},
})
if err != nil {
return err
}
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceSetWiFi,
"设备设置WiFi",
constants.AssetAuditResultSuccess,
device,
map[string]any{"device": deviceSnapshot(device)},
map[string]any{
"imei": imei,
"ssid": req.SSID,
"enabled": req.Enabled,
"password": req.Password,
},
0,
0,
0,
nil,
)
s.dispatchDeviceControlObservation(ctx, device.ID, constants.CardObservationSceneDeviceSetWiFi, observation, false)
return nil
}
@@ -126,58 +85,92 @@ func (s *Service) GatewaySetWiFi(ctx context.Context, identifier string, req *dt
func (s *Service) GatewaySwitchCard(ctx context.Context, identifier string, req *dto.SwitchCardRequest) error {
device, imei, err := s.getGatewayDevice(ctx, identifier)
if err != nil {
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceSwitchCard,
"设备切卡失败",
constants.AssetAuditResultFailed,
nil,
nil,
map[string]any{
"identifier": identifier,
"target_iccid": req.TargetICCID,
},
0,
0,
0,
err,
)
return err
}
state, err := loadDeviceBindingState(ctx, s.db, device.ID, req.TargetICCID, false)
metadata := map[string]any{"target_iccid": req.TargetICCID}
if err != nil {
s.recordDeviceBindingAuditFailure(ctx, constants.AuditActionDeviceCurrentCardSwitched, "设备切卡失败", constants.AuditResultFailed,
device, nil, nil, metadata, err)
return err
}
if state.target == nil {
appErr := errors.New(errors.CodeForbidden, "目标卡未绑定到当前设备")
s.recordDeviceBindingAuditFailure(ctx, constants.AuditActionDeviceCurrentCardSwitched, "设备切卡被拒绝", constants.AuditResultDenied,
device, map[string]any{"current_iot_card_id": currentCardID(state)}, switchCardAuditItems(state), metadata, appErr)
return appErr
}
targetCard := state.cards[state.target.IotCardID]
if targetCard == nil {
appErr := errors.New(errors.CodeNotFound, "目标卡资产不存在或无权限访问")
s.recordDeviceBindingAuditFailure(ctx, constants.AuditActionDeviceCurrentCardSwitched, "设备切卡失败", constants.AuditResultFailed,
device, map[string]any{"current_iot_card_id": currentCardID(state)}, switchCardAuditItems(state), metadata, appErr)
return appErr
}
metadata["target_iot_card_id"] = targetCard.ID
metadata["target_slot_position"] = state.target.SlotPosition
observation := s.captureDeviceControlObservation(ctx, device.ID, 0, req.TargetICCID)
if err = s.gatewayClient.SwitchCard(ctx, &gateway.SwitchCardReq{
deviceID := strconv.FormatUint(uint64(device.ID), 10)
observer := &deviceGatewayAttemptObserver{
service: s, operation: constants.IntegrationOperationGatewaySwitchCard,
scene: constants.CardObservationSceneDeviceSwitchCard, seriesKey: deviceCommandSeriesKey(ctx),
resource: deviceGatewayResource{
Type: constants.AuditResourceDevice, ID: deviceID, Key: audit.DeviceResourceKey(device), ExternalID: imei,
RequestSummary: map[string]any{
"device_id": device.ID, "imei": imei, "target_iot_card_id": targetCard.ID,
"target_iccid": targetCard.ICCID, "target_slot_position": state.target.SlotPosition,
},
},
}
if err = s.gatewayClient.SwitchCard(gateway.WithAttemptObserver(ctx, observer), &gateway.SwitchCardReq{
CardNo: imei,
ICCID: req.TargetICCID,
}); err != nil {
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceSwitchCard,
"设备切卡失败",
constants.AssetAuditResultFailed,
device,
map[string]any{"device": deviceSnapshot(device)},
map[string]any{"target_iccid": req.TargetICCID},
0,
0,
0,
err,
)
result, summary := constants.AuditResultFailed, "设备切卡失败"
if observer.unknown {
result, summary = constants.AuditResultUnknown, "设备切卡结果未知"
}
metadata["integration_id"] = observer.integration
s.recordDeviceBindingAuditFailure(ctx, constants.AuditActionDeviceCurrentCardSwitched, summary, result,
device, map[string]any{"current_iot_card_id": currentCardID(state)}, switchCardAuditItems(state), metadata, err)
return err
}
metadata["integration_id"] = observer.integration
if err := observer.completeSuccess(ctx, false); err != nil {
appErr := errors.Wrap(errors.CodeDatabaseError, err, "终结设备切卡 Integration Log 失败")
s.recordDeviceBindingAuditFailure(ctx, constants.AuditActionDeviceCurrentCardSwitched, "设备切卡结果未知", constants.AuditResultUnknown,
device, map[string]any{"current_iot_card_id": currentCardID(state)}, switchCardAuditItems(state), metadata, appErr)
return appErr
}
err = s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
lockedState, err := loadDeviceBindingState(ctx, tx, device.ID, targetCard.ICCID, true)
if err != nil {
return err
}
if lockedState.target == nil {
return errors.New(errors.CodeConflict, "切卡期间目标卡绑定关系已变化")
}
if err := tx.WithContext(ctx).Model(&model.DeviceSimBinding{}).
Where("device_id = ? AND bind_status = ?", device.ID, constants.BindStatusBound).
Update("is_current", false).Error; err != nil {
return err
}
if err := tx.WithContext(ctx).Model(&model.DeviceSimBinding{}).
Where("id = ? AND bind_status = ?", lockedState.target.ID, constants.BindStatusBound).
Update("is_current", true).Error; err != nil {
return err
}
return s.appendDeviceBindingAudit(ctx, tx, constants.AuditActionDeviceCurrentCardSwitched, "切换设备当前卡", constants.AuditResultSuccess,
device,
map[string]any{"current_iot_card_id": currentCardID(lockedState)},
map[string]any{"current_iot_card_id": lockedState.target.IotCardID},
switchCardAuditItems(lockedState), metadata, nil)
})
if err != nil {
s.recordDeviceBindingAuditFailure(ctx, constants.AuditActionDeviceCurrentCardSwitched, "设备切卡结果未知", constants.AuditResultUnknown,
device, map[string]any{"current_iot_card_id": currentCardID(state)}, switchCardAuditItems(state), metadata, err)
return err
}
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceSwitchCard,
"设备切卡",
constants.AssetAuditResultSuccess,
device,
map[string]any{"device": deviceSnapshot(device)},
map[string]any{"target_iccid": req.TargetICCID},
0,
0,
0,
nil,
)
s.dispatchDeviceControlObservation(ctx, device.ID, constants.CardObservationSceneDeviceSwitchCard, observation, true)
return nil
}
@@ -186,54 +179,21 @@ func (s *Service) GatewaySwitchCard(ctx context.Context, identifier string, req
func (s *Service) GatewayRebootDevice(ctx context.Context, identifier string) error {
device, imei, err := s.getGatewayDevice(ctx, identifier)
if err != nil {
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceReboot,
"设备重启失败",
constants.AssetAuditResultFailed,
nil,
nil,
map[string]any{"identifier": identifier},
0,
0,
0,
err,
)
return err
}
observation := s.captureDeviceControlObservation(ctx, device.ID, 0, "")
if err = s.gatewayClient.RebootDevice(ctx, &gateway.DeviceOperationReq{
DeviceID: imei,
}); err != nil {
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceReboot,
"设备重启失败",
constants.AssetAuditResultFailed,
device,
map[string]any{"device": deviceSnapshot(device)},
nil,
0,
0,
0,
err,
)
err = s.executeDeviceGatewayCommand(ctx, device, deviceGatewayCommand{
ActionCode: constants.AuditActionDeviceRebooted, Summary: "重启设备",
Operation: constants.IntegrationOperationGatewayReboot, Scene: constants.CardObservationSceneDeviceReboot,
RequestSummary: map[string]any{"device_id": device.ID, "imei": imei},
Metadata: map[string]any{"requested_action": "reboot"},
Call: func(callCtx context.Context) error {
return s.gatewayClient.RebootDevice(callCtx, &gateway.DeviceOperationReq{DeviceID: imei})
},
})
if err != nil {
return err
}
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceReboot,
"设备重启",
constants.AssetAuditResultSuccess,
device,
map[string]any{"device": deviceSnapshot(device)},
nil,
0,
0,
0,
nil,
)
s.dispatchDeviceControlObservation(ctx, device.ID, constants.CardObservationSceneDeviceReboot, observation, false)
return nil
}
@@ -242,54 +202,21 @@ func (s *Service) GatewayRebootDevice(ctx context.Context, identifier string) er
func (s *Service) GatewayResetDevice(ctx context.Context, identifier string) error {
device, imei, err := s.getGatewayDevice(ctx, identifier)
if err != nil {
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceReset,
"设备恢复出厂失败",
constants.AssetAuditResultFailed,
nil,
nil,
map[string]any{"identifier": identifier},
0,
0,
0,
err,
)
return err
}
observation := s.captureDeviceControlObservation(ctx, device.ID, 0, "")
if err = s.gatewayClient.ResetDevice(ctx, &gateway.DeviceOperationReq{
DeviceID: imei,
}); err != nil {
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceReset,
"设备恢复出厂失败",
constants.AssetAuditResultFailed,
device,
map[string]any{"device": deviceSnapshot(device)},
nil,
0,
0,
0,
err,
)
err = s.executeDeviceGatewayCommand(ctx, device, deviceGatewayCommand{
ActionCode: constants.AuditActionDeviceReset, Summary: "恢复设备出厂设置",
Operation: constants.IntegrationOperationGatewayReset, Scene: constants.CardObservationSceneDeviceReset,
RequestSummary: map[string]any{"device_id": device.ID, "imei": imei},
Metadata: map[string]any{"requested_action": "factory_reset"},
Call: func(callCtx context.Context) error {
return s.gatewayClient.ResetDevice(callCtx, &gateway.DeviceOperationReq{DeviceID: imei})
},
})
if err != nil {
return err
}
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceReset,
"设备恢复出厂",
constants.AssetAuditResultSuccess,
device,
map[string]any{"device": deviceSnapshot(device)},
nil,
0,
0,
0,
nil,
)
s.dispatchDeviceControlObservation(ctx, device.ID, constants.CardObservationSceneDeviceReset, observation, false)
return nil
}
@@ -303,244 +230,77 @@ func (s *Service) GatewaySwitchMode(ctx context.Context, identifier string, req
device, imei, err := s.getGatewayDevice(ctx, identifier)
if err != nil {
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceSwitchMode,
"设备切卡模式切换失败",
constants.AssetAuditResultFailed,
nil,
nil,
map[string]any{
"identifier": identifier,
"switch_mode": switchMode,
"iot_card_id": req.IotCardID,
},
0,
0,
0,
err,
)
return err
}
recordRejected := func(summary, result string, businessErr error, targetCard *model.IotCard) error {
s.recordDeviceCommandAudit(ctx, constants.AuditActionDeviceSwitchModeSet, summary, result,
device, targetCard, nil, nil,
map[string]any{"requested_switch_mode": switchMode, "iot_card_id": req.IotCardID}, businessErr)
return businessErr
}
if req.SwitchMode == nil {
appErr := errors.New(errors.CodeInvalidParam, "切卡模式不能为空")
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceSwitchMode,
"设备切卡模式切换被拒绝",
constants.AssetAuditResultDenied,
device,
map[string]any{"device": deviceSnapshot(device)},
map[string]any{"iot_card_id": req.IotCardID},
0,
0,
0,
appErr,
)
return appErr
return recordRejected("设置设备切卡模式被拒绝", constants.AuditResultDenied, appErr, nil)
}
if switchMode != 0 && switchMode != 1 {
appErr := errors.New(errors.CodeInvalidParam, "切卡模式仅支持0或1")
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceSwitchMode,
"设备切卡模式切换被拒绝",
constants.AssetAuditResultDenied,
device,
map[string]any{"device": deviceSnapshot(device)},
map[string]any{"switch_mode": switchMode, "iot_card_id": req.IotCardID},
0,
0,
0,
appErr,
)
return appErr
return recordRejected("设置设备切卡模式被拒绝", constants.AuditResultDenied, appErr, nil)
}
if req.IotCardID == 0 {
appErr := errors.New(errors.CodeInvalidParam, "目标卡资产ID不能为空")
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceSwitchMode,
"设备切卡模式切换被拒绝",
constants.AssetAuditResultDenied,
device,
map[string]any{"device": deviceSnapshot(device)},
map[string]any{"switch_mode": switchMode, "iot_card_id": req.IotCardID},
0,
0,
0,
appErr,
)
return appErr
return recordRejected("设置设备切卡模式被拒绝", constants.AuditResultDenied, appErr, nil)
}
targetCard, err := s.iotCardStore.GetByID(ctx, req.IotCardID)
if err != nil {
if err == gorm.ErrRecordNotFound {
appErr := errors.New(errors.CodeNotFound, "目标卡资产不存在或无权限访问")
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceSwitchMode,
"设备切卡模式切换被拒绝",
constants.AssetAuditResultDenied,
device,
map[string]any{"device": deviceSnapshot(device)},
map[string]any{"switch_mode": switchMode, "iot_card_id": req.IotCardID},
0,
0,
0,
appErr,
)
return appErr
return recordRejected("设置设备切卡模式被拒绝", constants.AuditResultDenied, appErr, nil)
}
appErr := errors.Wrap(errors.CodeDatabaseError, err, "查询目标卡资产失败")
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceSwitchMode,
"设备切卡模式切换失败",
constants.AssetAuditResultFailed,
device,
map[string]any{"device": deviceSnapshot(device)},
map[string]any{"switch_mode": switchMode, "iot_card_id": req.IotCardID},
0,
0,
0,
appErr,
)
return appErr
return recordRejected("设置设备切卡模式失败", constants.AuditResultFailed, appErr, nil)
}
if _, err = s.deviceSimBindingStore.GetByDeviceAndCard(ctx, device.ID, targetCard.ID); err != nil {
if err == gorm.ErrRecordNotFound {
appErr := errors.New(errors.CodeForbidden, "目标卡未绑定到当前设备,禁止设置切卡模式")
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceSwitchMode,
"设备切卡模式切换被拒绝",
constants.AssetAuditResultDenied,
device,
map[string]any{"device": deviceSnapshot(device)},
map[string]any{"switch_mode": switchMode, "iot_card_id": req.IotCardID, "iccid": targetCard.ICCID},
0,
0,
0,
appErr,
)
return appErr
return recordRejected("设置设备切卡模式被拒绝", constants.AuditResultDenied, appErr, targetCard)
}
appErr := errors.Wrap(errors.CodeDatabaseError, err, "查询设备卡绑定关系失败")
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceSwitchMode,
"设备切卡模式切换失败",
constants.AssetAuditResultFailed,
device,
map[string]any{"device": deviceSnapshot(device)},
map[string]any{"switch_mode": switchMode, "iot_card_id": req.IotCardID, "iccid": targetCard.ICCID},
0,
0,
0,
appErr,
)
return appErr
return recordRejected("设置设备切卡模式失败", constants.AuditResultFailed, appErr, targetCard)
}
if targetCard.ICCID == "" {
appErr := errors.New(errors.CodeConflict, "目标卡资产缺少ICCID无法设置切卡模式")
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceSwitchMode,
"设备切卡模式切换被拒绝",
constants.AssetAuditResultDenied,
device,
map[string]any{"device": deviceSnapshot(device)},
map[string]any{"switch_mode": switchMode, "iot_card_id": req.IotCardID},
0,
0,
0,
appErr,
)
return appErr
return recordRejected("设置设备切卡模式被拒绝", constants.AuditResultDenied, appErr, targetCard)
}
if targetCard.NetworkStatus != constants.NetworkStatusOnline {
appErr := errors.New(errors.CodeForbidden, "目标卡状态异常,仅正常状态的卡允许设置切卡模式")
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceSwitchMode,
"设备切卡模式切换被拒绝",
constants.AssetAuditResultDenied,
device,
map[string]any{"device": deviceSnapshot(device)},
map[string]any{
"switch_mode": switchMode,
"iot_card_id": req.IotCardID,
"iccid": targetCard.ICCID,
"network_status": targetCard.NetworkStatus,
"real_name_status": targetCard.RealNameStatus,
},
0,
0,
0,
appErr,
)
return appErr
return recordRejected("设置设备切卡模式被拒绝", constants.AuditResultDenied, appErr, targetCard)
}
if targetCard.RealNameStatus != constants.RealNameStatusVerified {
appErr := errors.New(errors.CodeForbidden, "目标卡未实名,禁止设置切卡模式")
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceSwitchMode,
"设备切卡模式切换被拒绝",
constants.AssetAuditResultDenied,
device,
map[string]any{"device": deviceSnapshot(device)},
map[string]any{
"switch_mode": switchMode,
"iot_card_id": req.IotCardID,
"iccid": targetCard.ICCID,
"network_status": targetCard.NetworkStatus,
"real_name_status": targetCard.RealNameStatus,
},
0,
0,
0,
appErr,
)
return appErr
return recordRejected("设置设备切卡模式被拒绝", constants.AuditResultDenied, appErr, targetCard)
}
observation := s.captureDeviceControlObservation(ctx, device.ID, targetCard.ID, targetCard.ICCID)
if err = s.gatewayClient.SwitchMode(ctx, &gateway.SwitchModeReq{
CardNo: imei,
SwitchMode: strconv.Itoa(switchMode),
ICCID: targetCard.ICCID,
}); err != nil {
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceSwitchMode,
"设备切卡模式切换失败",
constants.AssetAuditResultFailed,
device,
map[string]any{"device": deviceSnapshot(device)},
map[string]any{"switch_mode": switchMode, "iot_card_id": req.IotCardID, "iccid": targetCard.ICCID},
0,
0,
0,
err,
)
err = s.executeDeviceGatewayCommand(ctx, device, deviceGatewayCommand{
ActionCode: constants.AuditActionDeviceSwitchModeSet, Summary: "设置设备切卡模式",
Operation: constants.IntegrationOperationGatewaySwitchMode, Scene: constants.CardObservationSceneDeviceSwitchMode,
RequestSummary: map[string]any{
"device_id": device.ID, "imei": imei, "switch_mode": switchMode,
"iot_card_id": targetCard.ID, "iccid": targetCard.ICCID,
},
Metadata: map[string]any{
"requested_switch_mode": switchMode, "iot_card_id": targetCard.ID, "iccid": targetCard.ICCID,
},
TargetCard: targetCard,
Call: func(callCtx context.Context) error {
return s.gatewayClient.SwitchMode(callCtx, &gateway.SwitchModeReq{
CardNo: imei, SwitchMode: strconv.Itoa(switchMode), ICCID: targetCard.ICCID,
})
},
})
if err != nil {
return err
}
s.logDeviceOperation(
ctx,
constants.AssetAuditOpDeviceSwitchMode,
"设备切卡模式切换",
constants.AssetAuditResultSuccess,
device,
map[string]any{"device": deviceSnapshot(device)},
map[string]any{"switch_mode": switchMode, "iot_card_id": req.IotCardID, "iccid": targetCard.ICCID},
0,
0,
0,
nil,
)
s.dispatchDeviceControlObservation(ctx, device.ID, constants.CardObservationSceneDeviceSwitchMode, observation, true)
return nil
}