package asset import ( "context" stderrors "errors" "github.com/break/junhong_cmp_fiber/internal/model" assetAuditSvc "github.com/break/junhong_cmp_fiber/internal/service/asset_audit" "github.com/break/junhong_cmp_fiber/internal/store/postgres" "github.com/break/junhong_cmp_fiber/pkg/constants" "github.com/break/junhong_cmp_fiber/pkg/errors" "gorm.io/gorm" ) var deactivatableAssetStatuses = []int{constants.AssetStatusInStock, constants.AssetStatusSold} // LifecycleService 资产生命周期服务 type LifecycleService struct { db *gorm.DB iotCardStore *postgres.IotCardStore deviceStore *postgres.DeviceStore assetAuditService assetAuditSvc.OperationLogger } // NewLifecycleService 创建资产生命周期服务 func NewLifecycleService( db *gorm.DB, iotCardStore *postgres.IotCardStore, deviceStore *postgres.DeviceStore, assetAuditService assetAuditSvc.OperationLogger, ) *LifecycleService { return &LifecycleService{ db: db, iotCardStore: iotCardStore, deviceStore: deviceStore, assetAuditService: assetAuditService, } } func (s *LifecycleService) logLifecycleAudit(ctx context.Context, p assetAuditSvc.BuildLogParams) { if s == nil || s.assetAuditService == nil { return } if p.Operator.Type == "" { p.Operator = assetAuditSvc.OperatorFromContext(ctx) } if p.OperationType == "" { p.OperationType = constants.AssetAuditOpAssetDeactivate } s.assetAuditService.LogOperation(ctx, assetAuditSvc.BuildLog(ctx, p)) } // DeactivateIotCard 手动停用 IoT 卡 func (s *LifecycleService) DeactivateIotCard(ctx context.Context, id uint) error { card, err := s.iotCardStore.GetByID(ctx, id) if err != nil { if stderrors.Is(err, gorm.ErrRecordNotFound) { appErr := errors.New(errors.CodeIotCardNotFound) errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(appErr) s.logLifecycleAudit(ctx, assetAuditSvc.BuildLogParams{ AssetType: constants.AssetTypeIotCard, AssetID: id, OperationDesc: "统一入口停用IoT卡失败", ResultStatus: constants.AssetAuditResultFailed, ErrorCode: errorCode, ErrorMsg: errorMsg, }) return appErr } appErr := errors.Wrap(errors.CodeDatabaseError, err, "查询IoT卡失败") errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(appErr) s.logLifecycleAudit(ctx, assetAuditSvc.BuildLogParams{ AssetType: constants.AssetTypeIotCard, AssetID: id, OperationDesc: "统一入口停用IoT卡失败", ResultStatus: constants.AssetAuditResultFailed, ErrorCode: errorCode, ErrorMsg: errorMsg, }) return appErr } beforeData := map[string]any{ "asset_status": card.AssetStatus, "iccid": card.ICCID, "virtual_no": card.VirtualNo, } if !canDeactivateAsset(card.AssetStatus) { appErr := errors.New(errors.CodeForbidden, "当前状态不允许停用") errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(appErr) s.logLifecycleAudit(ctx, assetAuditSvc.BuildLogParams{ AssetType: constants.AssetTypeIotCard, AssetID: card.ID, AssetIdentifier: card.ICCID, OperationDesc: "统一入口停用IoT卡被拒绝", ResultStatus: constants.AssetAuditResultDenied, ErrorCode: errorCode, ErrorMsg: errorMsg, BeforeData: beforeData, }) return appErr } result := s.db.WithContext(ctx).Model(&model.IotCard{}). Where("id = ? AND asset_status IN ?", id, deactivatableAssetStatuses). Update("asset_status", constants.AssetStatusDeactivated) if result.Error != nil { appErr := errors.Wrap(errors.CodeDatabaseError, result.Error, "停用IoT卡失败") errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(appErr) s.logLifecycleAudit(ctx, assetAuditSvc.BuildLogParams{ AssetType: constants.AssetTypeIotCard, AssetID: card.ID, AssetIdentifier: card.ICCID, OperationDesc: "统一入口停用IoT卡失败", ResultStatus: constants.AssetAuditResultFailed, ErrorCode: errorCode, ErrorMsg: errorMsg, BeforeData: beforeData, AfterData: map[string]any{ "asset_status": constants.AssetStatusDeactivated, "iccid": card.ICCID, "virtual_no": card.VirtualNo, }, }) return appErr } if result.RowsAffected == 0 { appErr := errors.New(errors.CodeConflict, "状态已变更,请刷新后重试") errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(appErr) s.logLifecycleAudit(ctx, assetAuditSvc.BuildLogParams{ AssetType: constants.AssetTypeIotCard, AssetID: card.ID, AssetIdentifier: card.ICCID, OperationDesc: "统一入口停用IoT卡失败", ResultStatus: constants.AssetAuditResultFailed, ErrorCode: errorCode, ErrorMsg: errorMsg, BeforeData: beforeData, }) return appErr } s.logLifecycleAudit(ctx, assetAuditSvc.BuildLogParams{ AssetType: constants.AssetTypeIotCard, AssetID: card.ID, AssetIdentifier: card.ICCID, OperationDesc: "统一入口停用IoT卡", ResultStatus: constants.AssetAuditResultSuccess, BeforeData: beforeData, AfterData: map[string]any{ "asset_status": constants.AssetStatusDeactivated, "iccid": card.ICCID, "virtual_no": card.VirtualNo, }, }) return nil } // DeactivateDevice 手动停用设备 func (s *LifecycleService) DeactivateDevice(ctx context.Context, id uint) error { device, err := s.deviceStore.GetByID(ctx, id) if err != nil { if stderrors.Is(err, gorm.ErrRecordNotFound) { appErr := errors.New(errors.CodeNotFound, "设备不存在") errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(appErr) s.logLifecycleAudit(ctx, assetAuditSvc.BuildLogParams{ AssetType: constants.AssetTypeDevice, AssetID: id, OperationDesc: "统一入口停用设备失败", ResultStatus: constants.AssetAuditResultFailed, ErrorCode: errorCode, ErrorMsg: errorMsg, }) return appErr } appErr := errors.Wrap(errors.CodeDatabaseError, err, "查询设备失败") errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(appErr) s.logLifecycleAudit(ctx, assetAuditSvc.BuildLogParams{ AssetType: constants.AssetTypeDevice, AssetID: id, OperationDesc: "统一入口停用设备失败", ResultStatus: constants.AssetAuditResultFailed, ErrorCode: errorCode, ErrorMsg: errorMsg, }) return appErr } beforeData := map[string]any{ "asset_status": device.AssetStatus, "virtual_no": device.VirtualNo, } if !canDeactivateAsset(device.AssetStatus) { appErr := errors.New(errors.CodeForbidden, "当前状态不允许停用") errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(appErr) s.logLifecycleAudit(ctx, assetAuditSvc.BuildLogParams{ AssetType: constants.AssetTypeDevice, AssetID: device.ID, AssetIdentifier: device.VirtualNo, OperationDesc: "统一入口停用设备被拒绝", ResultStatus: constants.AssetAuditResultDenied, ErrorCode: errorCode, ErrorMsg: errorMsg, BeforeData: beforeData, }) return appErr } result := s.db.WithContext(ctx).Model(&model.Device{}). Where("id = ? AND asset_status IN ?", id, deactivatableAssetStatuses). Update("asset_status", constants.AssetStatusDeactivated) if result.Error != nil { appErr := errors.Wrap(errors.CodeDatabaseError, result.Error, "停用设备失败") errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(appErr) s.logLifecycleAudit(ctx, assetAuditSvc.BuildLogParams{ AssetType: constants.AssetTypeDevice, AssetID: device.ID, AssetIdentifier: device.VirtualNo, OperationDesc: "统一入口停用设备失败", ResultStatus: constants.AssetAuditResultFailed, ErrorCode: errorCode, ErrorMsg: errorMsg, BeforeData: beforeData, AfterData: map[string]any{ "asset_status": constants.AssetStatusDeactivated, "virtual_no": device.VirtualNo, }, }) return appErr } if result.RowsAffected == 0 { appErr := errors.New(errors.CodeConflict, "状态已变更,请刷新后重试") errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(appErr) s.logLifecycleAudit(ctx, assetAuditSvc.BuildLogParams{ AssetType: constants.AssetTypeDevice, AssetID: device.ID, AssetIdentifier: device.VirtualNo, OperationDesc: "统一入口停用设备失败", ResultStatus: constants.AssetAuditResultFailed, ErrorCode: errorCode, ErrorMsg: errorMsg, BeforeData: beforeData, }) return appErr } s.logLifecycleAudit(ctx, assetAuditSvc.BuildLogParams{ AssetType: constants.AssetTypeDevice, AssetID: device.ID, AssetIdentifier: device.VirtualNo, OperationDesc: "统一入口停用设备", ResultStatus: constants.AssetAuditResultSuccess, BeforeData: beforeData, AfterData: map[string]any{ "asset_status": constants.AssetStatusDeactivated, "virtual_no": device.VirtualNo, }, }) return nil } func canDeactivateAsset(assetStatus int) bool { return assetStatus == constants.AssetStatusInStock || assetStatus == constants.AssetStatusSold }