chore: 初始开源发布
iotlcm-admin-svr:管理后台服务。 - Go module:iotlcm → git.5iot.com/iotlcm-public/iotlcm-admin-svr(729 文件) - 凭据与地址脱敏:JWT signingKey(含代码里的 MustGet 默认值)、 MySQL DSN、Redis 口令、告警邮件发件人邮箱全部改为占位; 私网 IP → 文档地址段;库名 xiujie_admin → iotlcm - 真实手机号/邮箱 → 合法 11 位编造号 / example.com - 移除内部资产:CI 触发器、过时 DDL(sql/db_iotlcm.sql)、 私有依赖 go get 行、明文 registry 登录口令 - 启动 banner 统一 IOTLCM 方块字 + 社区惯例署名 - Dockerfile 改用公共基础镜像 - 拼错的 RADEME.md → README.md,README 重写 - 需要真实数据库的用例改为可用 IOTLCM_TEST_WITH_DB=1 显式启用 - 补 Apache-2.0 LICENSE,LF 行尾 + .gitattributes 数据库 schema 不随本仓发布(生产 DDL 已过时),README 已说明。
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
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")
|
||||
}
|
||||
Reference in New Issue
Block a user