This commit is contained in:
58
internal/service/iot_card/audit.go
Normal file
58
internal/service/iot_card/audit.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package iot_card
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
assetAuditSvc "github.com/break/junhong_cmp_fiber/internal/service/asset_audit"
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
)
|
||||
|
||||
// AssetAuditService 资产审计服务接口。
|
||||
type AssetAuditService interface {
|
||||
LogOperation(ctx context.Context, log *model.AssetOperationLog)
|
||||
}
|
||||
|
||||
func (s *Service) logCardAudit(ctx context.Context, p assetAuditSvc.BuildLogParams) {
|
||||
if s == nil || s.assetAuditService == nil {
|
||||
return
|
||||
}
|
||||
if p.Operator.Type == "" {
|
||||
p.Operator = assetAuditSvc.OperatorFromContext(ctx)
|
||||
}
|
||||
if p.AssetType == "" {
|
||||
p.AssetType = constants.AssetTypeIotCard
|
||||
}
|
||||
s.assetAuditService.LogOperation(ctx, assetAuditSvc.BuildLog(ctx, p))
|
||||
}
|
||||
|
||||
func (s *StopResumeService) logCardAudit(ctx context.Context, p assetAuditSvc.BuildLogParams) {
|
||||
if s == nil || s.assetAuditService == nil {
|
||||
return
|
||||
}
|
||||
if p.Operator.Type == "" {
|
||||
p.Operator = assetAuditSvc.OperatorFromContext(ctx)
|
||||
}
|
||||
if p.AssetType == "" {
|
||||
p.AssetType = constants.AssetTypeIotCard
|
||||
}
|
||||
s.assetAuditService.LogOperation(ctx, assetAuditSvc.BuildLog(ctx, p))
|
||||
}
|
||||
|
||||
func cardSnapshot(card *model.IotCard) map[string]any {
|
||||
if card == nil {
|
||||
return nil
|
||||
}
|
||||
return map[string]any{
|
||||
"id": card.ID,
|
||||
"iccid": card.ICCID,
|
||||
"shop_id": card.ShopID,
|
||||
"status": card.Status,
|
||||
"series_id": card.SeriesID,
|
||||
"enable_polling": card.EnablePolling,
|
||||
"realname_policy": card.RealnamePolicy,
|
||||
"real_name_status": card.RealNameStatus,
|
||||
"network_status": card.NetworkStatus,
|
||||
"stop_reason": card.StopReason,
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/break/junhong_cmp_fiber/internal/gateway"
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
"github.com/break/junhong_cmp_fiber/internal/model/dto"
|
||||
assetAuditSvc "github.com/break/junhong_cmp_fiber/internal/service/asset_audit"
|
||||
"github.com/break/junhong_cmp_fiber/internal/store"
|
||||
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
@@ -64,6 +65,7 @@ type Service struct {
|
||||
deviceSimBindingStore *postgres.DeviceSimBindingStore
|
||||
redis *redis.Client
|
||||
assetIdentifierStore *postgres.AssetIdentifierStore
|
||||
assetAuditService AssetAuditService
|
||||
}
|
||||
|
||||
func New(
|
||||
@@ -76,6 +78,7 @@ func New(
|
||||
packageSeriesStore *postgres.PackageSeriesStore,
|
||||
gatewayClient *gateway.Client,
|
||||
logger *zap.Logger,
|
||||
assetAuditService AssetAuditService,
|
||||
) *Service {
|
||||
return &Service{
|
||||
db: db,
|
||||
@@ -87,6 +90,7 @@ func New(
|
||||
packageSeriesStore: packageSeriesStore,
|
||||
gatewayClient: gatewayClient,
|
||||
logger: logger,
|
||||
assetAuditService: assetAuditService,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,11 +345,37 @@ func (s *Service) toStandaloneResponse(card *model.IotCard, shopMap map[uint]str
|
||||
|
||||
func (s *Service) AllocateCards(ctx context.Context, req *dto.AllocateStandaloneCardsRequest, operatorID uint, operatorShopID *uint) (*dto.AllocateStandaloneCardsResponse, error) {
|
||||
if err := s.validateDirectSubordinate(ctx, operatorShopID, req.ToShopID); err != nil {
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(err)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardAllocate,
|
||||
OperationDesc: "单卡分配被拒绝",
|
||||
ResultStatus: constants.AssetAuditResultDenied,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AfterData: map[string]any{
|
||||
"to_shop_id": req.ToShopID,
|
||||
"selection_type": req.SelectionType,
|
||||
"requested_count": len(req.ICCIDs),
|
||||
},
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cards, err := s.getCardsForAllocation(ctx, req, operatorShopID)
|
||||
if err != nil {
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(err)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardAllocate,
|
||||
OperationDesc: "单卡分配执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AfterData: map[string]any{
|
||||
"to_shop_id": req.ToShopID,
|
||||
"selection_type": req.SelectionType,
|
||||
"requested_count": len(req.ICCIDs),
|
||||
},
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -401,6 +431,19 @@ func (s *Service) AllocateCards(ctx context.Context, req *dto.AllocateStandalone
|
||||
}
|
||||
|
||||
if len(cardIDs) == 0 {
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardAllocate,
|
||||
OperationDesc: "单卡分配被拒绝",
|
||||
ResultStatus: constants.AssetAuditResultDenied,
|
||||
ErrorMsg: "无可分配卡",
|
||||
BatchTotal: len(cards),
|
||||
FailCount: len(failedItems),
|
||||
AfterData: map[string]any{
|
||||
"to_shop_id": req.ToShopID,
|
||||
"failed_items": failedItems,
|
||||
"selection_type": req.SelectionType,
|
||||
},
|
||||
})
|
||||
return &dto.AllocateStandaloneCardsResponse{
|
||||
TotalCount: len(cards),
|
||||
SuccessCount: 0,
|
||||
@@ -426,6 +469,21 @@ func (s *Service) AllocateCards(ctx context.Context, req *dto.AllocateStandalone
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(err)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardAllocate,
|
||||
OperationDesc: "单卡分配执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
BatchTotal: len(cards),
|
||||
SuccessCount: len(cardIDs),
|
||||
FailCount: len(failedItems),
|
||||
AfterData: map[string]any{
|
||||
"to_shop_id": req.ToShopID,
|
||||
"failed_items": failedItems,
|
||||
},
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -436,6 +494,32 @@ func (s *Service) AllocateCards(ctx context.Context, req *dto.AllocateStandalone
|
||||
}
|
||||
}
|
||||
|
||||
beforeData := make([]map[string]any, 0, len(cards))
|
||||
for _, card := range cards {
|
||||
beforeData = append(beforeData, map[string]any{
|
||||
"id": card.ID,
|
||||
"iccid": card.ICCID,
|
||||
"shop_id": card.ShopID,
|
||||
"status": card.Status,
|
||||
})
|
||||
}
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardAllocate,
|
||||
OperationDesc: "单卡分配",
|
||||
ResultStatus: constants.AssetAuditResultSuccess,
|
||||
BatchTotal: len(cards),
|
||||
SuccessCount: len(cardIDs),
|
||||
FailCount: len(failedItems),
|
||||
BeforeData: map[string]any{
|
||||
"cards": beforeData,
|
||||
},
|
||||
AfterData: map[string]any{
|
||||
"to_shop_id": req.ToShopID,
|
||||
"new_status": newStatus,
|
||||
"failed_items": failedItems,
|
||||
},
|
||||
})
|
||||
|
||||
return &dto.AllocateStandaloneCardsResponse{
|
||||
TotalCount: len(cards),
|
||||
SuccessCount: len(cardIDs),
|
||||
@@ -449,6 +533,18 @@ func (s *Service) RecallCards(ctx context.Context, req *dto.RecallStandaloneCard
|
||||
// 1. 查询卡列表
|
||||
cards, err := s.getCardsForRecall(ctx, req)
|
||||
if err != nil {
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(err)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardRecall,
|
||||
OperationDesc: "单卡回收执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AfterData: map[string]any{
|
||||
"selection_type": req.SelectionType,
|
||||
"requested_count": len(req.ICCIDs),
|
||||
},
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -535,6 +631,18 @@ func (s *Service) RecallCards(ctx context.Context, req *dto.RecallStandaloneCard
|
||||
}
|
||||
|
||||
if len(cardIDs) == 0 {
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardRecall,
|
||||
OperationDesc: "单卡回收被拒绝",
|
||||
ResultStatus: constants.AssetAuditResultDenied,
|
||||
ErrorMsg: "无可回收卡",
|
||||
BatchTotal: len(cards),
|
||||
FailCount: len(failedItems),
|
||||
AfterData: map[string]any{
|
||||
"failed_items": failedItems,
|
||||
"selection_type": req.SelectionType,
|
||||
},
|
||||
})
|
||||
return &dto.RecallStandaloneCardsResponse{
|
||||
TotalCount: len(cards),
|
||||
SuccessCount: 0,
|
||||
@@ -570,6 +678,20 @@ func (s *Service) RecallCards(ctx context.Context, req *dto.RecallStandaloneCard
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(err)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardRecall,
|
||||
OperationDesc: "单卡回收执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
BatchTotal: len(cards),
|
||||
SuccessCount: len(cardIDs),
|
||||
FailCount: len(failedItems),
|
||||
AfterData: map[string]any{
|
||||
"failed_items": failedItems,
|
||||
},
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -580,6 +702,32 @@ func (s *Service) RecallCards(ctx context.Context, req *dto.RecallStandaloneCard
|
||||
}
|
||||
}
|
||||
|
||||
beforeData := make([]map[string]any, 0, len(successCards))
|
||||
for _, card := range successCards {
|
||||
beforeData = append(beforeData, map[string]any{
|
||||
"id": card.ID,
|
||||
"iccid": card.ICCID,
|
||||
"shop_id": card.ShopID,
|
||||
"status": card.Status,
|
||||
})
|
||||
}
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardRecall,
|
||||
OperationDesc: "单卡回收",
|
||||
ResultStatus: constants.AssetAuditResultSuccess,
|
||||
BatchTotal: len(cards),
|
||||
SuccessCount: len(cardIDs),
|
||||
FailCount: len(failedItems),
|
||||
BeforeData: map[string]any{
|
||||
"cards": beforeData,
|
||||
},
|
||||
AfterData: map[string]any{
|
||||
"to_shop_id": newShopID,
|
||||
"new_status": newStatus,
|
||||
"failed_items": failedItems,
|
||||
},
|
||||
})
|
||||
|
||||
return &dto.RecallStandaloneCardsResponse{
|
||||
TotalCount: len(cards),
|
||||
SuccessCount: len(cardIDs),
|
||||
@@ -750,10 +898,35 @@ func (s *Service) buildRecallRecords(successCards []*model.IotCard, toShopID *ui
|
||||
func (s *Service) BatchSetSeriesBinding(ctx context.Context, req *dto.BatchSetCardSeriesBindngRequest, operatorShopID *uint) (*dto.BatchSetCardSeriesBindngResponse, error) {
|
||||
cards, err := s.iotCardStore.GetByICCIDs(ctx, req.ICCIDs)
|
||||
if err != nil {
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(err)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardSeriesBinding,
|
||||
OperationDesc: "卡系列绑定执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
BatchTotal: len(req.ICCIDs),
|
||||
AfterData: map[string]any{
|
||||
"series_id": req.SeriesID,
|
||||
"iccids": req.ICCIDs,
|
||||
},
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(cards) == 0 {
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardSeriesBinding,
|
||||
OperationDesc: "卡系列绑定被拒绝",
|
||||
ResultStatus: constants.AssetAuditResultDenied,
|
||||
ErrorMsg: "卡不存在",
|
||||
BatchTotal: len(req.ICCIDs),
|
||||
FailCount: len(req.ICCIDs),
|
||||
AfterData: map[string]any{
|
||||
"series_id": req.SeriesID,
|
||||
"iccids": req.ICCIDs,
|
||||
},
|
||||
})
|
||||
return &dto.BatchSetCardSeriesBindngResponse{
|
||||
SuccessCount: 0,
|
||||
FailCount: len(req.ICCIDs),
|
||||
@@ -772,12 +945,53 @@ func (s *Service) BatchSetSeriesBinding(ctx context.Context, req *dto.BatchSetCa
|
||||
packageSeries, err = s.packageSeriesStore.GetByID(ctx, req.SeriesID)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, errors.New(errors.CodeNotFound, "套餐系列不存在或已禁用")
|
||||
denyErr := errors.New(errors.CodeNotFound, "套餐系列不存在或已禁用")
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(denyErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardSeriesBinding,
|
||||
OperationDesc: "卡系列绑定被拒绝",
|
||||
ResultStatus: constants.AssetAuditResultDenied,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
BatchTotal: len(req.ICCIDs),
|
||||
AfterData: map[string]any{
|
||||
"series_id": req.SeriesID,
|
||||
"iccids": req.ICCIDs,
|
||||
},
|
||||
})
|
||||
return nil, denyErr
|
||||
}
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(err)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardSeriesBinding,
|
||||
OperationDesc: "卡系列绑定执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
BatchTotal: len(req.ICCIDs),
|
||||
AfterData: map[string]any{
|
||||
"series_id": req.SeriesID,
|
||||
"iccids": req.ICCIDs,
|
||||
},
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
if packageSeries.Status != 1 {
|
||||
return nil, errors.New(errors.CodeInvalidParam, "套餐系列不存在或已禁用")
|
||||
denyErr := errors.New(errors.CodeInvalidParam, "套餐系列不存在或已禁用")
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(denyErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardSeriesBinding,
|
||||
OperationDesc: "卡系列绑定被拒绝",
|
||||
ResultStatus: constants.AssetAuditResultDenied,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
BatchTotal: len(req.ICCIDs),
|
||||
AfterData: map[string]any{
|
||||
"series_id": req.SeriesID,
|
||||
"iccids": req.ICCIDs,
|
||||
},
|
||||
})
|
||||
return nil, denyErr
|
||||
}
|
||||
}
|
||||
|
||||
@@ -836,10 +1050,67 @@ func (s *Service) BatchSetSeriesBinding(ctx context.Context, req *dto.BatchSetCa
|
||||
seriesIDPtr = &req.SeriesID
|
||||
}
|
||||
if err := s.iotCardStore.BatchUpdateSeriesID(ctx, successCardIDs, seriesIDPtr); err != nil {
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(err)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardSeriesBinding,
|
||||
OperationDesc: "卡系列绑定执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
BatchTotal: len(req.ICCIDs),
|
||||
SuccessCount: len(successCardIDs),
|
||||
FailCount: len(failedItems),
|
||||
AfterData: map[string]any{
|
||||
"series_id": req.SeriesID,
|
||||
"success_ids": successCardIDs,
|
||||
"failed_items": failedItems,
|
||||
},
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
resultStatus := constants.AssetAuditResultSuccess
|
||||
operationDesc := "批量设置卡系列绑定"
|
||||
errorMsg := ""
|
||||
if len(successCardIDs) == 0 && len(failedItems) > 0 {
|
||||
resultStatus = constants.AssetAuditResultDenied
|
||||
operationDesc = "卡系列绑定被拒绝"
|
||||
errorMsg = "无可操作卡"
|
||||
}
|
||||
beforeCards := make([]map[string]any, 0, len(successCardIDs))
|
||||
successSet := make(map[uint]struct{}, len(successCardIDs))
|
||||
for _, id := range successCardIDs {
|
||||
successSet[id] = struct{}{}
|
||||
}
|
||||
for _, card := range cards {
|
||||
if _, ok := successSet[card.ID]; !ok {
|
||||
continue
|
||||
}
|
||||
beforeCards = append(beforeCards, map[string]any{
|
||||
"id": card.ID,
|
||||
"iccid": card.ICCID,
|
||||
"series_id": card.SeriesID,
|
||||
})
|
||||
}
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardSeriesBinding,
|
||||
OperationDesc: operationDesc,
|
||||
ResultStatus: resultStatus,
|
||||
ErrorMsg: errorMsg,
|
||||
BatchTotal: len(req.ICCIDs),
|
||||
SuccessCount: len(successCardIDs),
|
||||
FailCount: len(failedItems),
|
||||
BeforeData: map[string]any{
|
||||
"cards": beforeCards,
|
||||
},
|
||||
AfterData: map[string]any{
|
||||
"series_id": req.SeriesID,
|
||||
"success_ids": successCardIDs,
|
||||
"failed_items": failedItems,
|
||||
},
|
||||
})
|
||||
|
||||
return &dto.BatchSetCardSeriesBindngResponse{
|
||||
SuccessCount: len(successCardIDs),
|
||||
FailCount: len(failedItems),
|
||||
@@ -1052,19 +1323,74 @@ func (s *Service) UpdatePollingStatus(ctx context.Context, cardID uint, enablePo
|
||||
card, err := s.iotCardStore.GetByID(ctx, cardID)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return errors.New(errors.CodeNotFound, "IoT卡不存在")
|
||||
denyErr := errors.New(errors.CodeNotFound, "IoT卡不存在")
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(denyErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardPollingStatus,
|
||||
OperationDesc: "更新卡轮询状态被拒绝",
|
||||
ResultStatus: constants.AssetAuditResultDenied,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: cardID,
|
||||
AssetIdentifier: "",
|
||||
AfterData: map[string]any{
|
||||
"enable_polling": enablePolling,
|
||||
},
|
||||
})
|
||||
return denyErr
|
||||
}
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(err)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardPollingStatus,
|
||||
OperationDesc: "更新卡轮询状态执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: cardID,
|
||||
AfterData: map[string]any{
|
||||
"enable_polling": enablePolling,
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// 检查是否需要更新
|
||||
if card.EnablePolling == enablePolling {
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardPollingStatus,
|
||||
OperationDesc: "更新卡轮询状态",
|
||||
ResultStatus: constants.AssetAuditResultSuccess,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: map[string]any{
|
||||
"enable_polling": card.EnablePolling,
|
||||
},
|
||||
AfterData: map[string]any{
|
||||
"enable_polling": enablePolling,
|
||||
},
|
||||
})
|
||||
return nil // 状态未变化
|
||||
}
|
||||
|
||||
// 更新数据库
|
||||
card.EnablePolling = enablePolling
|
||||
if err := s.iotCardStore.Update(ctx, card); err != nil {
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(err)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardPollingStatus,
|
||||
OperationDesc: "更新卡轮询状态执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: map[string]any{
|
||||
"enable_polling": !enablePolling,
|
||||
},
|
||||
AfterData: map[string]any{
|
||||
"enable_polling": enablePolling,
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1082,6 +1408,20 @@ func (s *Service) UpdatePollingStatus(ctx context.Context, cardID uint, enablePo
|
||||
}
|
||||
}
|
||||
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardPollingStatus,
|
||||
OperationDesc: "更新卡轮询状态",
|
||||
ResultStatus: constants.AssetAuditResultSuccess,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: map[string]any{
|
||||
"enable_polling": !enablePolling,
|
||||
},
|
||||
AfterData: map[string]any{
|
||||
"enable_polling": enablePolling,
|
||||
},
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1093,6 +1433,21 @@ func (s *Service) BatchUpdatePollingStatus(ctx context.Context, cardIDs []uint,
|
||||
|
||||
// 批量更新数据库
|
||||
if err := s.iotCardStore.BatchUpdatePollingStatus(ctx, cardIDs, enablePolling); err != nil {
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(err)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardPollingStatus,
|
||||
OperationDesc: "批量更新卡轮询状态执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
BatchTotal: len(cardIDs),
|
||||
FailCount: len(cardIDs),
|
||||
AfterData: map[string]any{
|
||||
"card_ids": cardIDs,
|
||||
"enable_polling": enablePolling,
|
||||
"trigger_source": "batch",
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1112,6 +1467,19 @@ func (s *Service) BatchUpdatePollingStatus(ctx context.Context, cardIDs []uint,
|
||||
}
|
||||
}
|
||||
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardPollingStatus,
|
||||
OperationDesc: "批量更新卡轮询状态",
|
||||
ResultStatus: constants.AssetAuditResultSuccess,
|
||||
BatchTotal: len(cardIDs),
|
||||
SuccessCount: len(cardIDs),
|
||||
AfterData: map[string]any{
|
||||
"card_ids": cardIDs,
|
||||
"enable_polling": enablePolling,
|
||||
"trigger_source": "batch",
|
||||
},
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1120,12 +1488,42 @@ func (s *Service) DeleteCard(ctx context.Context, cardID uint) error {
|
||||
card, err := s.iotCardStore.GetByID(ctx, cardID)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return errors.New(errors.CodeNotFound, "IoT卡不存在")
|
||||
denyErr := errors.New(errors.CodeNotFound, "IoT卡不存在")
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(denyErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardDelete,
|
||||
OperationDesc: "删除卡被拒绝",
|
||||
ResultStatus: constants.AssetAuditResultDenied,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: cardID,
|
||||
})
|
||||
return denyErr
|
||||
}
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(err)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardDelete,
|
||||
OperationDesc: "删除卡执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: cardID,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.iotCardStore.Delete(ctx, cardID); err != nil {
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(err)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardDelete,
|
||||
OperationDesc: "删除卡执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: cardSnapshot(card),
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1139,6 +1537,18 @@ func (s *Service) DeleteCard(ctx context.Context, cardID uint) error {
|
||||
s.pollingCallback.OnCardDeleted(ctx, cardID)
|
||||
}
|
||||
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardDelete,
|
||||
OperationDesc: "删除卡",
|
||||
ResultStatus: constants.AssetAuditResultSuccess,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: cardSnapshot(card),
|
||||
AfterData: map[string]any{
|
||||
"deleted": true,
|
||||
},
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1147,9 +1557,39 @@ func (s *Service) BatchDeleteCards(ctx context.Context, cardIDs []uint) error {
|
||||
if len(cardIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
cards, queryErr := s.iotCardStore.GetByIDs(ctx, cardIDs)
|
||||
if queryErr != nil {
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(queryErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardBatchDelete,
|
||||
OperationDesc: "批量删除卡执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
BatchTotal: len(cardIDs),
|
||||
FailCount: len(cardIDs),
|
||||
AfterData: map[string]any{
|
||||
"card_ids": cardIDs,
|
||||
},
|
||||
})
|
||||
return queryErr
|
||||
}
|
||||
|
||||
// 批量软删除
|
||||
if err := s.iotCardStore.BatchDelete(ctx, cardIDs); err != nil {
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(err)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardBatchDelete,
|
||||
OperationDesc: "批量删除卡执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
BatchTotal: len(cardIDs),
|
||||
FailCount: len(cardIDs),
|
||||
AfterData: map[string]any{
|
||||
"card_ids": cardIDs,
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1161,6 +1601,24 @@ func (s *Service) BatchDeleteCards(ctx context.Context, cardIDs []uint) error {
|
||||
s.pollingCallback.OnCardDeleted(ctx, cardID)
|
||||
}
|
||||
}
|
||||
beforeCards := make([]map[string]any, 0, len(cards))
|
||||
for _, card := range cards {
|
||||
beforeCards = append(beforeCards, cardSnapshot(card))
|
||||
}
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardBatchDelete,
|
||||
OperationDesc: "批量删除卡",
|
||||
ResultStatus: constants.AssetAuditResultSuccess,
|
||||
BatchTotal: len(cardIDs),
|
||||
SuccessCount: len(cardIDs),
|
||||
BeforeData: map[string]any{
|
||||
"cards": beforeCards,
|
||||
},
|
||||
AfterData: map[string]any{
|
||||
"card_ids": cardIDs,
|
||||
"deleted": true,
|
||||
},
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1171,19 +1629,75 @@ func (s *Service) UpdateRealnamePolicy(ctx context.Context, cardID uint, realnam
|
||||
card, err := s.iotCardStore.GetByID(ctx, cardID)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return errors.New(errors.CodeNotFound, "IoT卡不存在")
|
||||
denyErr := errors.New(errors.CodeNotFound, "IoT卡不存在")
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(denyErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardRealnamePolicy,
|
||||
OperationDesc: "更新卡实名认证策略被拒绝",
|
||||
ResultStatus: constants.AssetAuditResultDenied,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: cardID,
|
||||
AfterData: map[string]any{
|
||||
"realname_policy": realnamePolicy,
|
||||
},
|
||||
})
|
||||
return denyErr
|
||||
}
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "查询IoT卡失败")
|
||||
wrapErr := errors.Wrap(errors.CodeDatabaseError, err, "查询IoT卡失败")
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(wrapErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardRealnamePolicy,
|
||||
OperationDesc: "更新卡实名认证策略执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: cardID,
|
||||
AfterData: map[string]any{
|
||||
"realname_policy": realnamePolicy,
|
||||
},
|
||||
})
|
||||
return wrapErr
|
||||
}
|
||||
|
||||
// 幂等检查
|
||||
if card.RealnamePolicy == realnamePolicy {
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardRealnamePolicy,
|
||||
OperationDesc: "更新卡实名认证策略",
|
||||
ResultStatus: constants.AssetAuditResultSuccess,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: map[string]any{
|
||||
"realname_policy": card.RealnamePolicy,
|
||||
},
|
||||
AfterData: map[string]any{
|
||||
"realname_policy": realnamePolicy,
|
||||
},
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// 更新数据库
|
||||
if err := s.iotCardStore.UpdateRealnamePolicy(ctx, cardID, realnamePolicy); err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "更新实名认证策略失败")
|
||||
wrapErr := errors.Wrap(errors.CodeDatabaseError, err, "更新实名认证策略失败")
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(wrapErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardRealnamePolicy,
|
||||
OperationDesc: "更新卡实名认证策略执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: map[string]any{
|
||||
"realname_policy": card.RealnamePolicy,
|
||||
},
|
||||
AfterData: map[string]any{
|
||||
"realname_policy": realnamePolicy,
|
||||
},
|
||||
})
|
||||
return wrapErr
|
||||
}
|
||||
|
||||
s.logger.Info("更新卡实名认证策略",
|
||||
@@ -1191,6 +1705,20 @@ func (s *Service) UpdateRealnamePolicy(ctx context.Context, cardID uint, realnam
|
||||
zap.String("realname_policy", realnamePolicy),
|
||||
)
|
||||
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardRealnamePolicy,
|
||||
OperationDesc: "更新卡实名认证策略",
|
||||
ResultStatus: constants.AssetAuditResultSuccess,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: map[string]any{
|
||||
"realname_policy": card.RealnamePolicy,
|
||||
},
|
||||
AfterData: map[string]any{
|
||||
"realname_policy": realnamePolicy,
|
||||
},
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1199,15 +1727,54 @@ func (s *Service) UpdateRealnamePolicy(ctx context.Context, cardID uint, realnam
|
||||
func (s *Service) ManualUpdateRealnameStatus(ctx context.Context, cardID uint, realNameStatus int) (*model.IotCard, error) {
|
||||
if realNameStatus != constants.RealNameStatusNotVerified &&
|
||||
realNameStatus != constants.RealNameStatusVerified {
|
||||
return nil, errors.New(errors.CodeInvalidParam, "无效的实名状态")
|
||||
denyErr := errors.New(errors.CodeInvalidParam, "无效的实名状态")
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(denyErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardRealnameStatus,
|
||||
OperationDesc: "手动更新卡实名状态被拒绝",
|
||||
ResultStatus: constants.AssetAuditResultDenied,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: cardID,
|
||||
AfterData: map[string]any{
|
||||
"real_name_status": realNameStatus,
|
||||
},
|
||||
})
|
||||
return nil, denyErr
|
||||
}
|
||||
|
||||
card, err := s.iotCardStore.GetByID(ctx, cardID)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, errors.New(errors.CodeNotFound, "IoT卡不存在")
|
||||
denyErr := errors.New(errors.CodeNotFound, "IoT卡不存在")
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(denyErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardRealnameStatus,
|
||||
OperationDesc: "手动更新卡实名状态被拒绝",
|
||||
ResultStatus: constants.AssetAuditResultDenied,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: cardID,
|
||||
AfterData: map[string]any{
|
||||
"real_name_status": realNameStatus,
|
||||
},
|
||||
})
|
||||
return nil, denyErr
|
||||
}
|
||||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询IoT卡失败")
|
||||
wrapErr := errors.Wrap(errors.CodeDatabaseError, err, "查询IoT卡失败")
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(wrapErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardRealnameStatus,
|
||||
OperationDesc: "手动更新卡实名状态执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: cardID,
|
||||
AfterData: map[string]any{
|
||||
"real_name_status": realNameStatus,
|
||||
},
|
||||
})
|
||||
return nil, wrapErr
|
||||
}
|
||||
|
||||
oldStatus := card.RealNameStatus
|
||||
@@ -1227,12 +1794,46 @@ func (s *Service) ManualUpdateRealnameStatus(ctx context.Context, cardID uint, r
|
||||
}
|
||||
|
||||
if err := s.iotCardStore.UpdateFields(ctx, cardID, fields); err != nil {
|
||||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "更新卡实名状态失败")
|
||||
wrapErr := errors.Wrap(errors.CodeDatabaseError, err, "更新卡实名状态失败")
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(wrapErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardRealnameStatus,
|
||||
OperationDesc: "手动更新卡实名状态执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: map[string]any{
|
||||
"real_name_status": oldStatus,
|
||||
},
|
||||
AfterData: map[string]any{
|
||||
"real_name_status": realNameStatus,
|
||||
},
|
||||
})
|
||||
return nil, wrapErr
|
||||
}
|
||||
|
||||
freshCard, err := s.iotCardStore.GetByID(ctx, cardID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询更新后的IoT卡失败")
|
||||
wrapErr := errors.Wrap(errors.CodeDatabaseError, err, "查询更新后的IoT卡失败")
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(wrapErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardRealnameStatus,
|
||||
OperationDesc: "手动更新卡实名状态执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: map[string]any{
|
||||
"real_name_status": oldStatus,
|
||||
},
|
||||
AfterData: map[string]any{
|
||||
"real_name_status": realNameStatus,
|
||||
},
|
||||
})
|
||||
return nil, wrapErr
|
||||
}
|
||||
|
||||
if statusChanged {
|
||||
@@ -1249,6 +1850,22 @@ func (s *Service) ManualUpdateRealnameStatus(ctx context.Context, cardID uint, r
|
||||
zap.Int("new_status", realNameStatus),
|
||||
zap.Uint("operator_id", middleware.GetUserIDFromContext(ctx)))
|
||||
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardRealnameStatus,
|
||||
OperationDesc: "手动更新卡实名状态",
|
||||
ResultStatus: constants.AssetAuditResultSuccess,
|
||||
AssetID: freshCard.ID,
|
||||
AssetIdentifier: freshCard.ICCID,
|
||||
BeforeData: map[string]any{
|
||||
"real_name_status": oldStatus,
|
||||
"first_realname_at": card.FirstRealnameAt,
|
||||
},
|
||||
AfterData: map[string]any{
|
||||
"real_name_status": freshCard.RealNameStatus,
|
||||
"first_realname_at": freshCard.FirstRealnameAt,
|
||||
},
|
||||
})
|
||||
|
||||
return freshCard, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/break/junhong_cmp_fiber/internal/gateway"
|
||||
"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"
|
||||
@@ -36,6 +37,7 @@ type StopResumeService struct {
|
||||
deviceSimBindingStore *postgres.DeviceSimBindingStore
|
||||
gatewayClient *gateway.Client
|
||||
logger *zap.Logger
|
||||
assetAuditService AssetAuditService
|
||||
|
||||
maxRetries int
|
||||
retryInterval time.Duration
|
||||
@@ -49,6 +51,7 @@ func NewStopResumeService(
|
||||
deviceSimBindingStore *postgres.DeviceSimBindingStore,
|
||||
gatewayClient *gateway.Client,
|
||||
logger *zap.Logger,
|
||||
assetAuditService AssetAuditService,
|
||||
) *StopResumeService {
|
||||
return &StopResumeService{
|
||||
redis: redis,
|
||||
@@ -57,6 +60,7 @@ func NewStopResumeService(
|
||||
deviceSimBindingStore: deviceSimBindingStore,
|
||||
gatewayClient: gatewayClient,
|
||||
logger: logger,
|
||||
assetAuditService: assetAuditService,
|
||||
maxRetries: 3,
|
||||
retryInterval: 2 * time.Second,
|
||||
}
|
||||
@@ -363,6 +367,16 @@ func (s *StopResumeService) ResumeCardIfStopped(ctx context.Context, carrierType
|
||||
func (s *StopResumeService) resumeSingleCard(ctx context.Context, cardID uint) error {
|
||||
card, err := s.iotCardStore.GetByID(ctx, cardID)
|
||||
if err != nil {
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(err)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
Operator: assetAuditSvc.SystemOperator("系统任务"),
|
||||
OperationType: constants.AssetAuditOpCardAutoStart,
|
||||
OperationDesc: "自动复机执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: cardID,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -407,6 +421,21 @@ func (s *StopResumeService) resumeSingleCard(ctx context.Context, cardID uint) e
|
||||
zap.Uint("card_id", cardID),
|
||||
zap.String("iccid", card.ICCID),
|
||||
zap.Error(err))
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(err)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
Operator: assetAuditSvc.SystemOperator("系统任务"),
|
||||
OperationType: constants.AssetAuditOpCardAutoStart,
|
||||
OperationDesc: "自动复机执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: map[string]any{
|
||||
"network_status": card.NetworkStatus,
|
||||
"stop_reason": card.StopReason,
|
||||
},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -420,6 +449,25 @@ func (s *StopResumeService) resumeSingleCard(ctx context.Context, cardID uint) e
|
||||
zap.Uint("card_id", cardID),
|
||||
zap.String("iccid", card.ICCID),
|
||||
zap.Error(err))
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(err)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
Operator: assetAuditSvc.SystemOperator("系统任务"),
|
||||
OperationType: constants.AssetAuditOpCardAutoStart,
|
||||
OperationDesc: "自动复机执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: map[string]any{
|
||||
"network_status": constants.NetworkStatusOffline,
|
||||
"stop_reason": card.StopReason,
|
||||
},
|
||||
AfterData: map[string]any{
|
||||
"network_status": constants.NetworkStatusOnline,
|
||||
"stop_reason": "",
|
||||
},
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -429,13 +477,52 @@ func (s *StopResumeService) resumeSingleCard(ctx context.Context, cardID uint) e
|
||||
zap.Uint("card_id", cardID),
|
||||
zap.String("iccid", card.ICCID))
|
||||
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
Operator: assetAuditSvc.SystemOperator("系统任务"),
|
||||
OperationType: constants.AssetAuditOpCardAutoStart,
|
||||
OperationDesc: "自动复机",
|
||||
ResultStatus: constants.AssetAuditResultSuccess,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: map[string]any{
|
||||
"network_status": constants.NetworkStatusOffline,
|
||||
"stop_reason": card.StopReason,
|
||||
},
|
||||
AfterData: map[string]any{
|
||||
"network_status": constants.NetworkStatusOnline,
|
||||
"stop_reason": "",
|
||||
},
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// stopCardWithRetry 调用运营商停机接口(带重试机制),并更新 DB 停机原因
|
||||
func (s *StopResumeService) stopCardWithRetry(ctx context.Context, card *model.IotCard, stopReason string) error {
|
||||
operator := assetAuditSvc.SystemOperator("系统任务")
|
||||
operationType := constants.AssetAuditOpCardAutoStop
|
||||
operationDesc := "自动停卡"
|
||||
if stopReason == constants.StopReasonManual {
|
||||
operator = assetAuditSvc.OperatorFromContext(ctx)
|
||||
operationType = constants.AssetAuditOpCardManualStop
|
||||
operationDesc = "手动停卡"
|
||||
}
|
||||
|
||||
if s.gatewayClient == nil {
|
||||
return errors.New(errors.CodeInternalError, "Gateway 未配置,停复机操作不可用")
|
||||
failErr := errors.New(errors.CodeInternalError, "Gateway 未配置,停复机操作不可用")
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(failErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
Operator: operator,
|
||||
OperationType: operationType,
|
||||
OperationDesc: operationDesc + "执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: cardSnapshot(card),
|
||||
})
|
||||
return failErr
|
||||
}
|
||||
|
||||
s.logger.Info("调用网关停机",
|
||||
@@ -470,9 +557,44 @@ func (s *StopResumeService) stopCardWithRetry(ctx context.Context, card *model.I
|
||||
zap.Uint("card_id", card.ID),
|
||||
zap.String("iccid", card.ICCID),
|
||||
zap.Error(updateErr))
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(updateErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
Operator: operator,
|
||||
OperationType: operationType,
|
||||
OperationDesc: operationDesc + "执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: map[string]any{
|
||||
"network_status": card.NetworkStatus,
|
||||
"stop_reason": card.StopReason,
|
||||
},
|
||||
AfterData: map[string]any{
|
||||
"network_status": constants.NetworkStatusOffline,
|
||||
"stop_reason": stopReason,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
s.invalidatePollingCache(ctx, card.ID)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
Operator: operator,
|
||||
OperationType: operationType,
|
||||
OperationDesc: operationDesc,
|
||||
ResultStatus: constants.AssetAuditResultSuccess,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: map[string]any{
|
||||
"network_status": card.NetworkStatus,
|
||||
"stop_reason": card.StopReason,
|
||||
},
|
||||
AfterData: map[string]any{
|
||||
"network_status": constants.NetworkStatusOffline,
|
||||
"stop_reason": stopReason,
|
||||
},
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -483,6 +605,26 @@ func (s *StopResumeService) stopCardWithRetry(ctx context.Context, card *model.I
|
||||
zap.Error(err))
|
||||
}
|
||||
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(lastErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
Operator: operator,
|
||||
OperationType: operationType,
|
||||
OperationDesc: operationDesc + "执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: map[string]any{
|
||||
"network_status": card.NetworkStatus,
|
||||
"stop_reason": card.StopReason,
|
||||
},
|
||||
AfterData: map[string]any{
|
||||
"network_status": constants.NetworkStatusOffline,
|
||||
"stop_reason": stopReason,
|
||||
},
|
||||
})
|
||||
|
||||
return lastErr
|
||||
}
|
||||
|
||||
@@ -529,11 +671,33 @@ func (s *StopResumeService) resumeCardWithRetry(ctx context.Context, card *model
|
||||
func (s *StopResumeService) ManualStopCard(ctx context.Context, iccid string) error {
|
||||
card, err := s.iotCardStore.GetByICCID(ctx, iccid)
|
||||
if err != nil {
|
||||
return errors.New(errors.CodeNotFound, "卡不存在")
|
||||
denyErr := errors.New(errors.CodeNotFound, "卡不存在")
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(denyErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardManualStop,
|
||||
OperationDesc: "手动停卡被拒绝",
|
||||
ResultStatus: constants.AssetAuditResultDenied,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetIdentifier: iccid,
|
||||
})
|
||||
return denyErr
|
||||
}
|
||||
|
||||
if card.RealNameStatus != constants.RealNameStatusVerified {
|
||||
return errors.New(errors.CodeForbidden, "卡未实名,无法操作")
|
||||
denyErr := errors.New(errors.CodeForbidden, "卡未实名,无法操作")
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(denyErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardManualStop,
|
||||
OperationDesc: "手动停卡被拒绝",
|
||||
ResultStatus: constants.AssetAuditResultDenied,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: cardSnapshot(card),
|
||||
})
|
||||
return denyErr
|
||||
}
|
||||
|
||||
// 检查绑定设备是否在复机保护期
|
||||
@@ -542,10 +706,37 @@ func (s *StopResumeService) ManualStopCard(ctx context.Context, iccid string) er
|
||||
if bindErr == nil && binding != nil {
|
||||
exists, _ := s.redis.Exists(ctx, constants.RedisDeviceProtectKey(binding.DeviceID, "start")).Result()
|
||||
if exists > 0 {
|
||||
return errors.New(errors.CodeForbidden, "设备复机保护期内,禁止停机")
|
||||
denyErr := errors.New(errors.CodeForbidden, "设备复机保护期内,禁止停机")
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(denyErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardManualStop,
|
||||
OperationDesc: "手动停卡被拒绝",
|
||||
ResultStatus: constants.AssetAuditResultDenied,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: cardSnapshot(card),
|
||||
AfterData: map[string]any{
|
||||
"device_id": binding.DeviceID,
|
||||
},
|
||||
})
|
||||
return denyErr
|
||||
}
|
||||
} else if bindErr != nil && !stderrors.Is(bindErr, gorm.ErrRecordNotFound) {
|
||||
return errors.Wrap(errors.CodeInternalError, bindErr, "查询卡绑定关系失败")
|
||||
wrapErr := errors.Wrap(errors.CodeInternalError, bindErr, "查询卡绑定关系失败")
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(wrapErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardManualStop,
|
||||
OperationDesc: "手动停卡执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: cardSnapshot(card),
|
||||
})
|
||||
return wrapErr
|
||||
}
|
||||
}
|
||||
|
||||
@@ -560,11 +751,33 @@ func (s *StopResumeService) ManualStopCard(ctx context.Context, iccid string) er
|
||||
func (s *StopResumeService) ManualStartCard(ctx context.Context, iccid string) error {
|
||||
card, err := s.iotCardStore.GetByICCID(ctx, iccid)
|
||||
if err != nil {
|
||||
return errors.New(errors.CodeNotFound, "卡不存在")
|
||||
denyErr := errors.New(errors.CodeNotFound, "卡不存在")
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(denyErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardManualStart,
|
||||
OperationDesc: "手动复机被拒绝",
|
||||
ResultStatus: constants.AssetAuditResultDenied,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetIdentifier: iccid,
|
||||
})
|
||||
return denyErr
|
||||
}
|
||||
|
||||
if card.RealNameStatus != constants.RealNameStatusVerified {
|
||||
return errors.New(errors.CodeForbidden, "卡未实名,无法操作")
|
||||
denyErr := errors.New(errors.CodeForbidden, "卡未实名,无法操作")
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(denyErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardManualStart,
|
||||
OperationDesc: "手动复机被拒绝",
|
||||
ResultStatus: constants.AssetAuditResultDenied,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: cardSnapshot(card),
|
||||
})
|
||||
return denyErr
|
||||
}
|
||||
|
||||
// 检查绑定设备是否在停机保护期
|
||||
@@ -573,15 +786,54 @@ func (s *StopResumeService) ManualStartCard(ctx context.Context, iccid string) e
|
||||
if bindErr == nil && binding != nil {
|
||||
exists, _ := s.redis.Exists(ctx, constants.RedisDeviceProtectKey(binding.DeviceID, "stop")).Result()
|
||||
if exists > 0 {
|
||||
return errors.New(errors.CodeForbidden, "设备停机保护期内,禁止复机")
|
||||
denyErr := errors.New(errors.CodeForbidden, "设备停机保护期内,禁止复机")
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(denyErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardManualStart,
|
||||
OperationDesc: "手动复机被拒绝",
|
||||
ResultStatus: constants.AssetAuditResultDenied,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: cardSnapshot(card),
|
||||
AfterData: map[string]any{
|
||||
"device_id": binding.DeviceID,
|
||||
},
|
||||
})
|
||||
return denyErr
|
||||
}
|
||||
} else if bindErr != nil && !stderrors.Is(bindErr, gorm.ErrRecordNotFound) {
|
||||
return errors.Wrap(errors.CodeInternalError, bindErr, "查询卡绑定关系失败")
|
||||
wrapErr := errors.Wrap(errors.CodeInternalError, bindErr, "查询卡绑定关系失败")
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(wrapErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardManualStart,
|
||||
OperationDesc: "手动复机执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: cardSnapshot(card),
|
||||
})
|
||||
return wrapErr
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.resumeCardWithRetry(ctx, card); err != nil {
|
||||
return errors.Wrap(errors.CodeGatewayError, err, "调用运营商复机失败,请稍后重试")
|
||||
wrapErr := errors.Wrap(errors.CodeGatewayError, err, "调用运营商复机失败,请稍后重试")
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(wrapErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardManualStart,
|
||||
OperationDesc: "手动复机执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: cardSnapshot(card),
|
||||
})
|
||||
return wrapErr
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
@@ -590,11 +842,46 @@ func (s *StopResumeService) ManualStartCard(ctx context.Context, iccid string) e
|
||||
"resumed_at": now,
|
||||
"stop_reason": "",
|
||||
}); err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "更新卡状态失败")
|
||||
wrapErr := errors.Wrap(errors.CodeDatabaseError, err, "更新卡状态失败")
|
||||
errorCode, errorMsg := assetAuditSvc.BuildErrorInfo(wrapErr)
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardManualStart,
|
||||
OperationDesc: "手动复机执行失败",
|
||||
ResultStatus: constants.AssetAuditResultFailed,
|
||||
ErrorCode: errorCode,
|
||||
ErrorMsg: errorMsg,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: map[string]any{
|
||||
"network_status": card.NetworkStatus,
|
||||
"stop_reason": card.StopReason,
|
||||
},
|
||||
AfterData: map[string]any{
|
||||
"network_status": constants.NetworkStatusOnline,
|
||||
"stop_reason": "",
|
||||
},
|
||||
})
|
||||
return wrapErr
|
||||
}
|
||||
|
||||
s.invalidatePollingCache(ctx, card.ID)
|
||||
|
||||
s.logCardAudit(ctx, assetAuditSvc.BuildLogParams{
|
||||
OperationType: constants.AssetAuditOpCardManualStart,
|
||||
OperationDesc: "手动复机",
|
||||
ResultStatus: constants.AssetAuditResultSuccess,
|
||||
AssetID: card.ID,
|
||||
AssetIdentifier: card.ICCID,
|
||||
BeforeData: map[string]any{
|
||||
"network_status": card.NetworkStatus,
|
||||
"stop_reason": card.StopReason,
|
||||
},
|
||||
AfterData: map[string]any{
|
||||
"network_status": constants.NetworkStatusOnline,
|
||||
"stop_reason": "",
|
||||
},
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user