package simwarning import ( "context" "fmt" "strconv" "strings" "time" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/consts" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/dao" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/do" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" ) const ( warnActionDataCut = "data_cut" warnDataServiceStatusEnabled = 1 warnDataServiceStatusDisabled = 0 ) func (s *sSimWarning) handleAutoAction(ctx context.Context, hit warningHit, warnLogID int64, runTime time.Time) (bool, error) { if !autoDataCutEnabled(hit) { return false, nil } allowed, reason := autoDataCutEligibility(hit) if !allowed { g.Log().Infof(ctx, "SimWarning auto data cut skipped, reason:%s, configId:%d, cardId:%d, warnLogId:%d", reason, hit.Config.Id, hit.Card.Id, warnLogID) return false, nil } acquired, actionKey, err := acquireWarningActionDedupe(ctx, hit, warnActionDataCut, runTime) if err != nil { return false, err } if !acquired { g.Log().Infof(ctx, "SimWarning auto data cut duplicated, action:%s, configId:%d, cardId:%d, warnLogId:%d", warnActionDataCut, hit.Config.Id, hit.Card.Id, warnLogID) return false, nil } eligible, reason, err := s.ensureDataCutEligible(ctx, hit) if err != nil { releaseWarningDedupe(ctx, actionKey) return false, err } if !eligible { releaseWarningDedupe(ctx, actionKey) g.Log().Infof(ctx, "SimWarning auto data cut skipped, reason:%s, configId:%d, cardId:%d, warnLogId:%d", reason, hit.Config.Id, hit.Card.Id, warnLogID) return false, nil } orderNum, err := s.disableCardDataService(ctx, hit) if err != nil { releaseWarningDedupe(ctx, actionKey) return false, err } g.Log().Infof(ctx, "SimWarning auto data cut success, action:%s, configId:%d, cardId:%d, warnLogId:%d, orderNum:%s", warnActionDataCut, hit.Config.Id, hit.Card.Id, warnLogID, orderNum) return true, nil } func autoDataCutEnabled(hit warningHit) bool { return hit.Config.ItemType == warnItemTypeCard && (isExpireKind(hit.Kind) || isTrafficKind(hit.Kind)) && hit.WarnStage == warnStageThird && hit.Config.AutoAction == warnAutoActionDataCut } func autoDataCutEligibility(hit warningHit) (bool, string) { if hit.Card.BossCardStatus == consts.V3ApiSimBossCardStatusStopped { return false, "\u5361\u5df2\u505c\u673a\uff0c\u81ea\u52a8\u65ad\u7f51\u672a\u6267\u884c" } if hit.Card.BossCardStatus != consts.V3ApiSimBossCardStatusActivated { return false, "\u5f53\u524d\u8fd0\u8425\u5546\u72b6\u6001\u4e3a" + bossCardStatusText(hit.Card.BossCardStatus) + "\uff0c\u4ec5\u5df2\u6fc0\u6d3b\u5361\u53ef\u4ee5\u81ea\u52a8\u65ad\u7f51" } if hit.Card.DataServiceStatus == warnDataServiceStatusDisabled { return false, "\u6570\u636e\u670d\u52a1\u5df2\u505c\u7528\uff0c\u81ea\u52a8\u65ad\u7f51\u672a\u6267\u884c" } if hit.Card.DataServiceStatus != warnDataServiceStatusEnabled { return false, "\u6570\u636e\u670d\u52a1\u72b6\u6001\u65e0\u6548\uff0c\u81ea\u52a8\u65ad\u7f51\u672a\u6267\u884c" } if strings.TrimSpace(hit.Card.ChanKey) == "" { return false, "\u5361\u7f3a\u5c11\u901a\u9053\u6807\u8bc6\uff0c\u81ea\u52a8\u65ad\u7f51\u672a\u6267\u884c" } if strings.TrimSpace(hit.Card.Iccid) == "" && strings.TrimSpace(hit.Card.Msisdn) == "" && strings.TrimSpace(hit.Card.Imsi) == "" { return false, "\u5361\u7f3a\u5c11 ICCID/MSISDN/IMSI\uff0c\u81ea\u52a8\u65ad\u7f51\u672a\u6267\u884c" } return true, "\u5df2\u914d\u7f6e\u7b2c\u4e09\u6863\u65ad\u7f51\uff0c\u7cfb\u7edf\u5c06\u5728\u72b6\u6001\u6821\u9a8c\u901a\u8fc7\u540e\u53d1\u8d77\u6570\u636e\u670d\u52a1\u505c\u7528" } func (s *sSimWarning) ensureDataCutEligible(ctx context.Context, hit warningHit) (bool, string, error) { simKey, err := dataCutSimKey(hit) if err != nil { return false, "", err } output, err := service.UpstreamChan().QuerySimCardStatus(ctx, &model.SimCardStatusInput{ SimKeys: []*model.SimKey{simKey}, }) if err != nil { return false, "", err } if len(output) == 0 { return false, "", gerror.New("upstream card status is empty") } status := output[0].ResData.CardStatus if status == consts.V3ApiSimBossCardStatusStopped { return false, "\u4e0a\u6e38\u5361\u5df2\u505c\u673a\uff0c\u81ea\u52a8\u65ad\u7f51\u672a\u6267\u884c", nil } if status != consts.V3ApiSimBossCardStatusActivated { return false, fmt.Sprintf("\u4e0a\u6e38\u5f53\u524d\u72b6\u6001\u4e3a%s\uff0c\u4ec5\u5df2\u6fc0\u6d3b\u5361\u53ef\u4ee5\u81ea\u52a8\u65ad\u7f51", bossCardStatusText(status)), nil } return true, "\u4e0a\u6e38\u72b6\u6001\u5df2\u786e\u8ba4\uff0c\u53ef\u6267\u884c\u6570\u636e\u670d\u52a1\u505c\u7528", nil } func bossCardStatusText(status int) string { if name := consts.V3ApiSimBossCardStatusNameMap[status]; name != "" { return name } return "\u672a\u77e5\u72b6\u6001" } func (s *sSimWarning) disableCardDataService(ctx context.Context, hit warningHit) (string, error) { simKey, err := dataCutSimKey(hit) if err != nil { return "", err } output, err := service.UpstreamChan().ChangeSimApnFunction(ctx, &model.SimCardOperateChangeApnInput{ SimKeys: []*model.SimKey{simKey}, ReqData: model.SimCardOperateChangeApnInputItem{ OperType: strconv.Itoa(consts.SimCardDataOperTypeStop), ApnName: consts.SimCardDataApnName, }, }) if err != nil { return "", err } if len(output) == 0 { return "", gerror.New("upstream data service stop response is empty") } item := output[0] if item.Code != 0 { message := strings.TrimSpace(item.Message) if message == "" { message = "upstream data service stop failed" } return "", gerror.New(message) } if err = s.updateLocalDataServiceDisabled(ctx, hit); err != nil { return item.ResData.OrderNum, err } return item.ResData.OrderNum, nil } func dataCutSimKey(hit warningHit) (*model.SimKey, error) { chanKey := strings.TrimSpace(hit.Card.ChanKey) if chanKey == "" { return nil, gerror.New("card chan key is empty") } if strings.TrimSpace(hit.Card.Iccid) == "" && strings.TrimSpace(hit.Card.Msisdn) == "" && strings.TrimSpace(hit.Card.Imsi) == "" { return nil, gerror.New("card identity is empty") } return &model.SimKey{ ChanKey: chanKey, ICCID: strings.TrimSpace(hit.Card.Iccid), MSISDN: strings.TrimSpace(hit.Card.Msisdn), IMSI: strings.TrimSpace(hit.Card.Imsi), }, nil } func (s *sSimWarning) updateLocalDataServiceDisabled(ctx context.Context, hit warningHit) error { columns := dao.TSimCardInfo.Columns() result, err := dao.TSimCardInfo.Ctx(ctx). Where(columns.Id, hit.Card.Id). Where(columns.TenantId, hit.Config.TenantId). Where(columns.DataServiceStatus, warnDataServiceStatusEnabled). Data(do.TSimCardInfo{ DataServiceStatus: warnDataServiceStatusDisabled, UpdatedBy: hit.Config.CreatedBy, }). Update() if err != nil { return err } affected, err := result.RowsAffected() if err != nil { return err } if affected == 1 { return nil } count, err := dao.TSimCardInfo.Ctx(ctx). Where(columns.Id, hit.Card.Id). Where(columns.TenantId, hit.Config.TenantId). Where(columns.DataServiceStatus, warnDataServiceStatusDisabled). Count() if err != nil { return err } if count > 0 { return nil } return gerror.New("local data service status changed before update") }