package query import ( "context" "fmt" "git.5iot.com/iotlcm-public/iotlcm-openapi/internal/consts" "git.5iot.com/iotlcm-public/iotlcm-openapi/internal/dao" "git.5iot.com/iotlcm-public/iotlcm-openapi/internal/model" "git.5iot.com/iotlcm-public/iotlcm-openapi/internal/service" "math" "strings" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" ) type sQuerySim struct { } func NewQuerySim() *sQuerySim { return &sQuerySim{} } func init() { service.RegisterQuerySim(NewQuerySim()) } func (s *sQuerySim) querySim(ctx context.Context, input *model.QuerySimInput) (output *model.QuerySimInfoCard, err error) { if input.Iccid == "" && input.Msisdn == "" && input.Imsi == "" { g.Log().Debugf(ctx, "QuerySim.QuerySimCard, Iccid, Msisdn, Imsi are all empty, input: %v", input) return nil, gerror.New("Iccid, Msisdn, Imsi 不能全为空") } field := "" value := "" if input.Iccid != "" { field = dao.TSimCardInfo.Columns().Iccid value = input.Iccid } else if input.Msisdn != "" { field = dao.TSimCardInfo.Columns().Msisdn value = input.Msisdn } else if input.Imsi != "" { field = dao.TSimCardInfo.Columns().Imsi value = input.Imsi } cardInfoTable := dao.TSimCardInfo.Table() sysDept := dao.SysDept.Table() flowPackage := dao.TSimFlowPackageInfo.Table() packageCategory := dao.TSimPackageCategory.Table() err = service.IotTSimCardInfo(). Model(ctx). Where(field, value). LeftJoin(sysDept, sysDept+"."+dao.SysDept.Columns().DeptId+"="+cardInfoTable+"."+dao.TSimCardInfo.Columns().DeptId). LeftJoin(flowPackage, flowPackage+"."+dao.TSimFlowPackageInfo.Columns().PackageCode+"="+cardInfoTable+"."+dao.TSimCardInfo.Columns().CurrentPackageCode). LeftJoin(packageCategory, packageCategory+"."+dao.TSimPackageCategory.Columns().CategoryCode+"="+flowPackage+"."+dao.TSimFlowPackageInfo.Columns().CategoryCode). Fields( cardInfoTable+".*", sysDept+"."+dao.SysDept.Columns().DeptName+" as DeptName", sysDept+"."+dao.SysDept.Columns().DeptKey+" as DeptKey", sysDept+"."+dao.SysDept.Columns().AreaCode+" as DeptAreaCode", flowPackage+"."+dao.TSimFlowPackageInfo.Columns().PackageName+" as PackageName", flowPackage+"."+dao.TSimFlowPackageInfo.Columns().CategoryCode+" as CategoryCode", packageCategory+"."+dao.TSimPackageCategory.Columns().CategoryName+" as CategoryName", ). Limit(1). Scan(&output) if err != nil { g.Log().Debugf(ctx, "QuerySim.QuerySimCard, List sim card info failed, input: %v, err: %v", input, err) return nil, gerror.New("查询sim卡信息失败,请确认ICCID,MSISDN,IMSI是否正确") } if output == nil { return nil, gerror.New("查询sim卡信息失败,请确认ICCID,MSISDN,IMSI是否正确") } return output, nil } func queryDate(t *gtime.Time) string { if t == nil || t.IsZero() { return "" } return t.Format("Y-m-d") } func queryDateTime(t *gtime.Time) string { if t == nil || t.IsZero() { return "" } return t.Format("Y-m-d H:i:s") } func queryRemaining(total, used float64) string { remaining := total - used if remaining < 0 { remaining = 0 } return fmt.Sprintf("%.1f", remaining) } func queryRemainingInt(total, used int) string { remaining := total - used if remaining < 0 { remaining = 0 } return fmt.Sprintf("%d", remaining) } func queryValidityPeriod(begin, end *gtime.Time) int { if begin == nil || begin.IsZero() || end == nil || end.IsZero() { return 0 } beginTime := begin.Time endTime := end.Time months := (endTime.Year()-beginTime.Year())*12 + int(endTime.Month()-beginTime.Month()) if endTime.Day() >= beginTime.Day()-1 { months++ } if months < 0 { return 0 } return months } func possibleRegionCodes(regionCode string) []string { seen := map[string]struct{}{} codes := make([]string, 0, 4) add := func(code string) { if code == "" { return } if _, ok := seen[code]; ok { return } seen[code] = struct{}{} codes = append(codes, code) } add(regionCode) if len(regionCode) == 12 { if regionCode[0:2] != "00" { add(regionCode[0:2] + "0000000000") } if regionCode[2:4] != "00" { add(regionCode[0:4] + "00000000") } if regionCode[4:6] != "00" { add(regionCode[0:6] + "000000") } } else { if len(regionCode) >= 4 { add(regionCode[:2] + "0000") } if len(regionCode) >= 6 { add(regionCode[:4] + "00") } } return codes } func (s *sQuerySim) queryLocation(ctx context.Context, regionCode string) string { regionCode = strings.TrimSpace(regionCode) if regionCode == "" { return "" } type division struct { RegionCode string `json:"regionCode"` RegionName string `json:"regionName"` RegionParent string `json:"regionParent"` } var divisions []*division err := dao.TDivisionCode.Ctx(ctx). WhereIn(dao.TDivisionCode.Columns().RegionCode, possibleRegionCodes(regionCode)). Scan(&divisions) if err != nil || len(divisions) == 0 { return regionCode } byCode := make(map[string]*division, len(divisions)) for _, item := range divisions { byCode[item.RegionCode] = item } item := byCode[regionCode] if item == nil { return regionCode } names := make([]string, 0, 4) for item != nil { if item.RegionName != "" { names = append([]string{item.RegionName}, names...) } if item.RegionParent == "" || item.RegionParent == "0" { break } item = byCode[item.RegionParent] } if len(names) == 0 { return regionCode } if names[0] != "中国" { names = append([]string{"中国"}, names...) } return strings.Join(names, "/") } func (s *sQuerySim) queryLocationByAreaCode(ctx context.Context, areaCode string) string { areaCode = strings.TrimSpace(areaCode) if areaCode == "" { return "" } type division struct { RegionCode string `json:"regionCode"` RegionName string `json:"regionName"` RegionParent string `json:"regionParent"` } byCode := make(map[string]*division) currentCode := "" for _, code := range possibleRegionCodes(areaCode) { var item *division err := dao.TDivisionCode.Ctx(ctx). Where(dao.TDivisionCode.Columns().RegionCode, code). Scan(&item) if err != nil { return areaCode } if item == nil { continue } byCode[item.RegionCode] = item currentCode = item.RegionCode break } if currentCode == "" { return areaCode } names := make([]string, 0, 4) for currentCode != "" { item := byCode[currentCode] if item == nil { err := dao.TDivisionCode.Ctx(ctx). Where(dao.TDivisionCode.Columns().RegionCode, currentCode). Scan(&item) if err != nil { return areaCode } if item == nil { break } byCode[item.RegionCode] = item } if item.RegionName != "" { names = append([]string{item.RegionName}, names...) } if item.RegionParent == "" || item.RegionParent == "0" { break } currentCode = item.RegionParent } if len(names) == 0 { return areaCode } if names[0] != "中国" { names = append([]string{"中国"}, names...) } return strings.Join(names, "/") } func (s *sQuerySim) QuerySimInfo(ctx context.Context, input *model.QuerySimInput) (output *model.QuerySimInfoOutput, err error) { g.Log().Error(ctx, input) simCard, err := s.querySim(ctx, input) if err != nil { return nil, err } output = &model.QuerySimInfoOutput{ Iccid: simCard.Iccid, Msisdn: simCard.Msisdn, Imsi: simCard.Imsi, Imei: simCard.Imei, Username: simCard.DeptName, Location: s.queryLocationByAreaCode(ctx, simCard.DeptAreaCode), PlanCategory: simCard.CategoryName, CurrentPlanName: simCard.PackageName, Plangroup: "", Planid: simCard.CurrentPackageCode, Appid: simCard.DeptKey, AllowPlanChange: 0, LastSyncTime: queryDateTime(simCard.StatusSyncTime), AccessType: consts.V3ApiSimAccessTypeNameMap[simCard.AccessType], CardStatus: consts.V3ApiSimBossCardStatusNameMap[simCard.BossCardStatus], CardType: consts.V3ApiSimCardTypeNameMap[simCard.CardType], NetworkType: consts.V3ApiSimNetModelNameMap[simCard.NetModel], ActivationDate: queryDate(simCard.OutStockTime), ActiveDate: queryDate(simCard.FlowPkgBeginAt), ValidityPeriod: queryValidityPeriod(simCard.FlowPkgBeginAt, simCard.FlowPkgEndAt), ExpiryDate: queryDate(simCard.FlowPkgEndAt), TestPeriod: 0, TestPeriodExpiryDate: nil, SilentPeriod: 0, SilentPeriodExpiryDate: nil, DataService: simCard.DataServiceStatus, PlanData: int(math.Round(simCard.PackageTotalFlowMb)), UsedData: simCard.UsedFlowMb, RemainingData: queryRemaining(simCard.PackageTotalFlowMb, simCard.UsedFlowMb), SmsService: simCard.SmsServiceStatus, PlanSMS: simCard.PackageTotalSms, UsedSMS: simCard.UsedSms, RemainingSMS: queryRemainingInt(simCard.PackageTotalSms, simCard.UsedSms), VoiceService: simCard.VoiceServiceStatus, PlanMinutes: simCard.PackageTotalVoice, UsedMinutes: simCard.UsedVoice, RemainingMinutes: queryRemainingInt(simCard.PackageTotalVoice, simCard.UsedVoice), InternationalRoamingService: simCard.RoamServiceStatus, PlanInternationalData: int(math.Round(simCard.PackageTotalRoamFlowMb)), UsedInternationalData: int(math.Round(simCard.UsedRoamFlowMb)), RemainingInternationalData: queryRemaining(simCard.PackageTotalRoamFlowMb, simCard.UsedRoamFlowMb), Description: nil, } return output, nil } func (s *sQuerySim) QuerySimLimit(ctx context.Context, input *model.QuerySimInput) (output *model.QuerySimLimitOutput, err error) { simCard, err := s.querySim(ctx, input) if err != nil { return nil, err } trafficCutoffName := "删除" if simCard.TrafficCutoffStatus == 1 { trafficCutoffName = "已开启" } else if simCard.TrafficCutoffStatus == 2 { trafficCutoffName = "已关闭" } output = &model.QuerySimLimitOutput{ TrafficCutoffStatus: simCard.TrafficCutoffStatus, TrafficCutoffName: trafficCutoffName, TrafficCutoffThreshold: simCard.TrafficCutoffThreshold, } return output, nil } func (s *sQuerySim) QuerySimImei(ctx context.Context, input *model.QuerySimInput) (output *model.QuerySimImeiOutput, err error) { simCard, err := s.querySim(ctx, input) if err != nil { return nil, err } if imei := strings.TrimSpace(simCard.Imei); imei != "" { return &model.QuerySimImeiOutput{Imei: imei}, nil } upstreamInput := &model.SimImeiInput{ SimKeys: []*model.SimKey{{ ChanKey: strings.TrimSpace(simCard.ChanKey), ICCID: strings.TrimSpace(simCard.Iccid), IMSI: strings.TrimSpace(simCard.Imsi), MSISDN: strings.TrimSpace(simCard.Msisdn), }}, } upstreamResults, err := service.UpstreamChan().QuerySimImei(ctx, upstreamInput) if err != nil { g.Log().Warningf(ctx, "QuerySim.QuerySimImei upstream query failed, iccid: %s, err: %v", simCard.Iccid, err) return &model.QuerySimImeiOutput{Imei: ""}, nil } for _, item := range upstreamResults { if item.Code != 0 { continue } if imei := strings.TrimSpace(item.ResData.Imei); imei != "" { columns := dao.TSimCardInfo.Columns() // imei_update_time 记录"值实际变更"的时间(单卡回填必然是从空到有值, // 故一定发生变更);imei_sync_time 专属于 worker 轮询链路, // 此处不写,避免污染"轮询覆盖度"指标。 if _, updateErr := service.IotTSimCardInfo(). Model(ctx). Where(columns.Id, simCard.Id). Data(g.Map{ columns.Imei: imei, columns.ImeiUpdateTime: gtime.Now(), }). Update(); updateErr != nil { g.Log().Warningf(ctx, "QuerySim.QuerySimImei persist upstream imei failed, card id: %d, err: %v", simCard.Id, updateErr) } return &model.QuerySimImeiOutput{Imei: imei}, nil } } output = &model.QuerySimImeiOutput{ Imei: "", } return output, nil } func (s *sQuerySim) QuerySimUsage(ctx context.Context, input *model.QuerySimInput) (output *model.QuerySimUsageOutput, err error) { simCard, err := s.querySim(ctx, input) if err != nil { return nil, err } output = &model.QuerySimUsageOutput{ UseUnit: "MB", UseAmount: simCard.UsedFlowMb, } return output, nil } // QuerySimStatus 查询SIM卡状态(纯读本地库,不调用上游通道) func (s *sQuerySim) QuerySimStatus(ctx context.Context, input *model.QuerySimInput) (output *model.QuerySimCardStatusOutput, err error) { simCard, err := s.querySim(ctx, input) if err != nil { return nil, err } statusName, ok := consts.V3ApiSimBossCardStatusNameMap[simCard.BossCardStatus] if !ok { statusName = "未知" } output = &model.QuerySimCardStatusOutput{ Iccid: simCard.Iccid, LifeCycle: simCard.BossCardStatus, LifeCycleName: statusName, } return output, nil }