All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m10s
269 lines
9.0 KiB
Go
269 lines
9.0 KiB
Go
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
|
|
}
|