427 lines
13 KiB
Go
427 lines
13 KiB
Go
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
|
||
}
|