1872 lines
60 KiB
Go
1872 lines
60 KiB
Go
package upstreamimpl
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"fmt"
|
||
"strings"
|
||
"sync/atomic"
|
||
"time"
|
||
"git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/consts"
|
||
"git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/model"
|
||
"git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/service"
|
||
sharedtoken "git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/token"
|
||
|
||
"github.com/gogf/gf/v2/errors/gerror"
|
||
"github.com/gogf/gf/v2/frame/g"
|
||
"github.com/gogf/gf/v2/os/gtime"
|
||
"github.com/gogf/gf/v2/util/gconv"
|
||
)
|
||
|
||
const (
|
||
gUrlBase = "https://api.iot.10086.cn/v5/"
|
||
)
|
||
|
||
type ydDataUsageResponse struct {
|
||
Status string `json:"status"`
|
||
Message string `json:"message"`
|
||
Result []struct {
|
||
DataAmount string `json:"dataAmount"`
|
||
ApnUseAmountList []struct {
|
||
ApnName string `json:"apnName"`
|
||
ApnUseAmount string `json:"apnUseAmount"`
|
||
PccCodeUseAmountList []any `json:"pccCodeUseAmountList"`
|
||
} `json:"apnUseAmountList"`
|
||
} `json:"result"`
|
||
}
|
||
|
||
func decodeYdDataUsageResponse(body string) (ydDataUsageResponse, error) {
|
||
var response ydDataUsageResponse
|
||
err := json.Unmarshal([]byte(body), &response)
|
||
return response, err
|
||
}
|
||
|
||
type sSimYD struct {
|
||
channelConfig *model.SimChannelConfigItemCache
|
||
seqId atomic.Int64
|
||
tokenProvider ydTokenProvider
|
||
apiConfig *oneLinkAPIConfig
|
||
}
|
||
|
||
func NewSimYD() *sSimYD {
|
||
return &sSimYD{}
|
||
}
|
||
|
||
func init() {
|
||
s := NewSimYD()
|
||
service.UpstreamAPI().RegisterTemp("sim_yd_v5", s)
|
||
service.RegisterSimYD(s)
|
||
}
|
||
|
||
func (s *sSimYD) NewTemp(ctx context.Context, channelConfig *model.SimChannelConfigItemCache) model.SimCardChannelI {
|
||
apiConfig := parseOneLinkTemplateAPI(ctx, channelConfig)
|
||
if apiConfig == nil {
|
||
apiConfig = &oneLinkAPIConfig{BaseURL: gUrlBase, Timeout: loadDefaultUpstreamHTTPTimeout(ctx)}
|
||
}
|
||
return &sSimYD{
|
||
channelConfig: channelConfig,
|
||
apiConfig: apiConfig,
|
||
}
|
||
}
|
||
|
||
// GetSharedToken exposes the shared token for channel types that support it.
|
||
// Shared-token channels carry a sharedTokenManagerProvider; other channel
|
||
// types return an error.
|
||
func (s *sSimYD) GetSharedToken(ctx context.Context) (sharedtoken.OneLinkToken, error) {
|
||
provider, ok := s.tokenProvider.(interface {
|
||
GetSharedToken(ctx context.Context) (sharedtoken.OneLinkToken, error)
|
||
})
|
||
if !ok {
|
||
return sharedtoken.OneLinkToken{}, fmt.Errorf("通道不支持共享Token")
|
||
}
|
||
return provider.GetSharedToken(ctx)
|
||
}
|
||
|
||
func (s *sSimYD) getUrl(ctx context.Context, serviceUrl string) string {
|
||
baseURL := gUrlBase
|
||
if s.apiConfig != nil && s.apiConfig.BaseURL != "" {
|
||
baseURL = s.apiConfig.BaseURL
|
||
}
|
||
if strings.HasSuffix(baseURL, "/") {
|
||
return baseURL + serviceUrl
|
||
}
|
||
return baseURL + "/" + serviceUrl
|
||
}
|
||
|
||
func (s *sSimYD) refreshToken(ctx context.Context) string {
|
||
chanKey := s.channelConfig.Channel.ChanKey
|
||
tokenPath := "ec/get/token"
|
||
if s.apiConfig != nil && s.apiConfig.TokenPath != "" {
|
||
tokenPath = strings.TrimPrefix(s.apiConfig.TokenPath, "/")
|
||
}
|
||
url := s.getUrl(ctx, tokenPath)
|
||
client := g.Client()
|
||
client.SetTimeout(s.httpTimeout())
|
||
// {"status":"0","message":"正确","result":[{"token":"50af5334a72a5a51ad8fe72d5c0e77a3e00a7a5588d301be143479310d32b609","ttl":3600}]}
|
||
type ydTokenResp struct {
|
||
Status string `json:"status"`
|
||
Message string `json:"message"`
|
||
Result []struct {
|
||
Token string `json:"token"`
|
||
Ttl int `json:"ttl"`
|
||
} `json:"result"`
|
||
}
|
||
now := gtime.Now()
|
||
oldToken := s.channelConfig.Channel.AccessToken
|
||
// TODO: 暂时不刷新,影响老系统,实际上需要刷新的
|
||
// FIX: 暂时不刷新,影响老系统,实际上需要刷新的
|
||
params := g.Map{
|
||
"appid": s.channelConfig.Channel.AppKey,
|
||
"password": s.channelConfig.Channel.AppSecret,
|
||
"transid": s.getTransId(ctx),
|
||
"refresh": "0", // 0: 不刷新, 1: 刷新
|
||
}
|
||
resp, err := client.Get(ctx, url, params)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "refreshToken get error %+v, chanKey %s", err, chanKey)
|
||
return ""
|
||
}
|
||
|
||
body := resp.ReadAllString()
|
||
var ydResp ydTokenResp
|
||
err = json.Unmarshal([]byte(body), &ydResp)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "refreshToken unmarshal error %+v, chanKey %s", err, chanKey)
|
||
return ""
|
||
}
|
||
if ydResp.Status != "0" {
|
||
g.Log().Warningf(ctx, "refreshToken upstream error status=%s message=%s chanKey=%s", ydResp.Status, ydResp.Message, chanKey)
|
||
return ""
|
||
}
|
||
token := ""
|
||
ttl := 0
|
||
if len(ydResp.Result) > 0 {
|
||
token = ydResp.Result[0].Token
|
||
ttl = ydResp.Result[0].Ttl
|
||
}
|
||
if token == "" {
|
||
g.Log().Warningf(ctx, "refreshToken empty token in result chanKey=%s", chanKey)
|
||
return ""
|
||
}
|
||
now2 := gtime.New(now)
|
||
accessTokenExpire := now2.Add(time.Duration(ttl) * time.Second)
|
||
err = service.ChannelUpdate().UpdateAccessToken(ctx, &model.UpdateAccessTokenInput{
|
||
ChanKey: s.channelConfig.Channel.ChanKey,
|
||
AccessToken: token,
|
||
AccessTokenCreated: now,
|
||
AccessTokenExpire: accessTokenExpire,
|
||
RefreshToken: "",
|
||
RefreshTokenExpire: nil,
|
||
})
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "refreshToken update access token error %+v, chanKey %s, ", err, chanKey)
|
||
return ""
|
||
}
|
||
s.channelConfig.Channel.AccessToken = token
|
||
s.channelConfig.Channel.AccessTokenExpire = accessTokenExpire
|
||
updateYdTokenState(chanKey, token, now.Time, accessTokenExpire.Time)
|
||
g.Log().Infof(ctx, "refreshToken success, tokenChanged %v, ttl %d, expire %s, chanKey %s", oldToken != token, ttl, accessTokenExpire.Format("2006-01-02 15:04:05"), chanKey)
|
||
return token
|
||
}
|
||
|
||
func (s *sSimYD) getToken(ctx context.Context) string {
|
||
if s.tokenProvider != nil {
|
||
token, err := s.tokenProvider.GetToken(ctx)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "shared token get error: %v", err)
|
||
return ""
|
||
}
|
||
return token
|
||
}
|
||
chanKey := s.channelConfig.Channel.ChanKey
|
||
policy := loadYdTokenRefreshPolicy(ctx)
|
||
return withYdTokenGuard(chanKey, func() string {
|
||
now := time.Now()
|
||
seedYdTokenState(s.channelConfig.Channel)
|
||
state := ydTokenStateFor(chanKey)
|
||
state.mu.RLock()
|
||
current, expiresAt := state.token, state.expiresAt
|
||
state.mu.RUnlock()
|
||
usable := current != "" && !expiresAt.IsZero() && now.Before(expiresAt)
|
||
if policy.mode == ydTokenRefreshModeProactive && usable {
|
||
usable = now.Before(expiresAt.Add(-time.Duration(policy.skewSecond) * time.Second))
|
||
}
|
||
if usable {
|
||
s.channelConfig.Channel.AccessToken = current
|
||
s.channelConfig.Channel.AccessTokenExpire = gtime.New(expiresAt)
|
||
return current
|
||
}
|
||
g.Log().Debugf(ctx, "getToken refresh needed, mode %s, chanKey %s", policy.mode, chanKey)
|
||
return s.refreshToken(ctx)
|
||
})
|
||
}
|
||
|
||
func (s *sSimYD) recoverToken(ctx context.Context, rejectedToken string) (string, error) {
|
||
if s.tokenProvider != nil {
|
||
token, err := s.tokenProvider.RecoverToken(ctx, rejectedToken)
|
||
if err != nil {
|
||
return token, err
|
||
}
|
||
if token == "" {
|
||
return "", fmt.Errorf("shared token recovery returned an empty token")
|
||
}
|
||
if token == rejectedToken {
|
||
return token, errYdTokenUnchanged
|
||
}
|
||
return token, nil
|
||
}
|
||
return recoverYdToken(ctx, s.channelConfig.Channel.ChanKey, s.channelConfig.Channel, rejectedToken, s.refreshToken)
|
||
}
|
||
|
||
func (s *sSimYD) requestYd(ctx context.Context, operation, url string, params g.Map) (*ydHTTPResponse, error) {
|
||
client := g.Client()
|
||
client.SetTimeout(s.httpTimeout())
|
||
return doYdRequest(ctx, operation, s.channelConfig.Channel.ChanKey, s.getToken, s.recoverToken, params, func(requestParams g.Map) (string, error) {
|
||
if t, _ := requestParams["token"].(string); strings.TrimSpace(t) == "" {
|
||
// Sending an empty token yields OneLink 11000 "请求参数不正确",
|
||
// which hides the real cause (token acquisition failure).
|
||
return "", gerror.New("token is empty, skip upstream request")
|
||
}
|
||
if s.tokenProvider != nil {
|
||
// transid is protocol-owned and must be regenerated for a replay.
|
||
requestParams["transid"] = s.getTransId(ctx)
|
||
if provider, ok := s.tokenProvider.(interface {
|
||
GetDeviceToken(context.Context) (string, error)
|
||
}); ok {
|
||
deviceToken, deviceErr := provider.GetDeviceToken(ctx)
|
||
if deviceErr != nil {
|
||
return "", fmt.Errorf("shared device token get error: %w", deviceErr)
|
||
}
|
||
if strings.TrimSpace(deviceToken) != "" {
|
||
requestParams["token_device"] = deviceToken
|
||
}
|
||
}
|
||
}
|
||
resp, err := client.Get(ctx, url, requestParams)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
return resp.ReadAllString(), nil
|
||
})
|
||
}
|
||
|
||
func (s *sSimYD) getTransId(ctx context.Context) string {
|
||
prefix := s.channelConfig.Channel.AppKey
|
||
if s.apiConfig != nil && s.apiConfig.transIDPrefix != "" {
|
||
prefix = s.apiConfig.transIDPrefix
|
||
}
|
||
return prefix + time.Now().Format("20060102150405") + fmt.Sprintf("%08d", s.seqId.Add(1))
|
||
}
|
||
|
||
func (s *sSimYD) httpTimeout() time.Duration {
|
||
if s != nil && s.apiConfig != nil && s.apiConfig.Timeout > 0 {
|
||
return s.apiConfig.Timeout
|
||
}
|
||
return defaultUpstreamHTTPTimeout
|
||
}
|
||
|
||
// 单卡基本信息查询
|
||
// http://api.iot.10086.cn/v5/ec/query/sim-basic-info?transid=REPLACE_WITH_VALUE&token=REPLACE_WITH_VALUE&iccid=89860000000000000004
|
||
func (s *sSimYD) QuerySimBasicInfo(ctx context.Context, input *model.SimBasicInfoInput) (model.SimBasicInfoOutput, error) {
|
||
// {
|
||
// "status": "0",
|
||
// "message": "正确",
|
||
// "result": [
|
||
// {
|
||
// "msisdn": "1440000000002",
|
||
// "imsi": "460000000000002",
|
||
// "iccid": "89860000000000000004",
|
||
// "activeDate": "2023-05-11 15:07:06", // 激活日期
|
||
// "openDate": "2023-05-11 14:53:24", // 开卡日期
|
||
// "remark": ""
|
||
// }
|
||
// ]
|
||
// }
|
||
type ydResp struct {
|
||
Status string `json:"status"`
|
||
Message string `json:"message"`
|
||
Result []struct {
|
||
Msisdn string `json:"msisdn"`
|
||
Imsi string `json:"imsi"`
|
||
Iccid string `json:"iccid"`
|
||
ActiveDate string `json:"activeDate"`
|
||
OpenDate string `json:"openDate"`
|
||
Remark string `json:"remark"`
|
||
} `json:"result"`
|
||
}
|
||
|
||
url := s.getUrl(ctx, "ec/query/sim-basic-info")
|
||
|
||
items := make(model.SimBasicInfoOutput, 0)
|
||
for _, simKey := range input.SimKeys {
|
||
params := g.Map{
|
||
"transid": s.getTransId(ctx),
|
||
"iccid": simKey.ICCID,
|
||
}
|
||
resp, err := s.requestYd(ctx, "QuerySimBasicInfo", url, params)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimBasicInfo get error %+v,url %s, params %+v, ", err, url, params)
|
||
items = append(items, &model.SimBaseRes[model.SimBasicInfoItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
ResData: model.SimBasicInfoItem{
|
||
ActiveDate: "",
|
||
OpenDate: "",
|
||
Remark: "",
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
body := resp.ReadAllString()
|
||
|
||
var ydResp ydResp
|
||
err = json.Unmarshal([]byte(body), &ydResp)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimBasicInfo unmarshal error %+v,url %s, params %+v, body %s, ", err, url, params, body)
|
||
items = append(items, &model.SimBaseRes[model.SimBasicInfoItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
ResData: model.SimBasicInfoItem{
|
||
ActiveDate: "",
|
||
OpenDate: "",
|
||
Remark: "",
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
if ydResp.Status != "0" {
|
||
g.Log().Warningf(ctx, "QuerySimBasicInfo error %s,url %s, params %+v, body %s, ", ydResp.Message, url, params, body)
|
||
items = append(items, &model.SimBaseRes[model.SimBasicInfoItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
ResData: model.SimBasicInfoItem{
|
||
ActiveDate: "",
|
||
OpenDate: "",
|
||
Remark: "",
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
if len(ydResp.Result) == 0 {
|
||
items = append(items, &model.SimBaseRes[model.SimBasicInfoItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: firstNonEmpty(ydResp.Message, "上游未返回SIM卡信息"),
|
||
ResData: model.SimBasicInfoItem{
|
||
ActiveDate: "",
|
||
OpenDate: "",
|
||
Remark: "",
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
hasIdentity := false
|
||
for _, session := range ydResp.Result {
|
||
if ydBasicInfoHasIdentity(session.Iccid, session.Msisdn, session.Imsi) {
|
||
hasIdentity = true
|
||
break
|
||
}
|
||
}
|
||
if !hasIdentity {
|
||
message := strings.TrimSpace(ydResp.Message)
|
||
if message == "" || message == "正常" || message == "正确" {
|
||
message = "上游未返回三码信息"
|
||
}
|
||
items = append(items, &model.SimBaseRes[model.SimBasicInfoItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: message,
|
||
ResData: model.SimBasicInfoItem{
|
||
ActiveDate: "",
|
||
OpenDate: "",
|
||
Remark: "",
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
for _, session := range ydResp.Result {
|
||
items = append(items, &model.SimBaseRes[model.SimBasicInfoItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
ResData: model.SimBasicInfoItem{
|
||
ICCID: session.Iccid,
|
||
IMSI: session.Imsi,
|
||
MSISDN: session.Msisdn,
|
||
ActiveDate: session.ActiveDate,
|
||
OpenDate: session.OpenDate,
|
||
Remark: session.Remark,
|
||
},
|
||
})
|
||
}
|
||
}
|
||
return items, nil
|
||
}
|
||
|
||
// 5.7.2 CMIOT_API25M01-单卡在线信息实时查询
|
||
func (s *sSimYD) QuerySimSession(ctx context.Context, input *model.SimSessionInput) (model.SimSessionOutput, error) {
|
||
// {
|
||
// "status": "0",
|
||
// "message": "正确",
|
||
// "result": [
|
||
// {
|
||
// "simSessionList":[
|
||
// {
|
||
// "apnId":"CMIOT",
|
||
// "status":"01",
|
||
// "ip":"127.0.0.1",
|
||
// "ipv6Prefix":"",
|
||
// "ipv6":"",
|
||
// "createDate":"2018-08-23 17:17:27",
|
||
// "rat":"6"
|
||
// }
|
||
// ]
|
||
// }
|
||
// ]
|
||
// }
|
||
|
||
type ydResp struct {
|
||
Status string `json:"status"`
|
||
Message string `json:"message"`
|
||
Result []struct {
|
||
InfoList []struct {
|
||
Apn string `json:"apn"`
|
||
SessionStatus string `json:"sessionStatus"`
|
||
Ipv4 string `json:"ipv4"`
|
||
Ipv6Prefix string `json:"ipv6Prefix"`
|
||
Ipv6 string `json:"ipv6"`
|
||
CreateTime string `json:"createTime"`
|
||
AccessType string `json:"accessType"`
|
||
} `json:"infoList"`
|
||
SimSessionList []struct {
|
||
ApnId string `json:"apnId"`
|
||
Status string `json:"status"`
|
||
Ip string `json:"ip"`
|
||
Ipv6Prefix string `json:"ipv6Prefix"`
|
||
Ipv6 string `json:"ipv6"`
|
||
CreateDate string `json:"createDate"`
|
||
Rat string `json:"rat"`
|
||
} `json:"simSessionList"`
|
||
} `json:"result"`
|
||
}
|
||
|
||
url := s.getUrl(ctx, "ec/query/sim-apn-session-info")
|
||
|
||
items := make(model.SimSessionOutput, 0)
|
||
for _, simKey := range input.SimKeys {
|
||
params := g.Map{
|
||
"transid": s.getTransId(ctx),
|
||
}
|
||
paramName, paramValue := pickSimQueryParam(simKey)
|
||
if paramName == "" {
|
||
items = append(items, &model.SimBaseRes[model.SimSessionItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: "missing iccid/msisdn/imsi",
|
||
ResData: model.SimSessionItem{
|
||
Status: "-2",
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
params[paramName] = paramValue
|
||
resp, err := s.requestYd(ctx, "QuerySimSession", url, params)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimSession get error %+v,url %s, params %+v, ", err, url, params)
|
||
items = append(items, &model.SimBaseRes[model.SimSessionItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: err.Error(),
|
||
ResData: model.SimSessionItem{
|
||
Status: "-2",
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
body := resp.ReadAllString()
|
||
|
||
var ydResp ydResp
|
||
err = json.Unmarshal([]byte(body), &ydResp)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimSession unmarshal error %+v,url %s, params %+v, body %s, ", err, url, params, body)
|
||
items = append(items, &model.SimBaseRes[model.SimSessionItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
ResData: model.SimSessionItem{
|
||
Status: "-2",
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
if ydResp.Status != "0" {
|
||
g.Log().Warningf(ctx, "QuerySimSession error %s,url %s, params %+v, body %s, ", ydResp.Message, url, params, body)
|
||
items = append(items, &model.SimBaseRes[model.SimSessionItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
ResData: model.SimSessionItem{
|
||
Status: "-2",
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
if len(ydResp.Result) == 0 || (len(ydResp.Result[0].InfoList) == 0 && len(ydResp.Result[0].SimSessionList) == 0) {
|
||
items = append(items, &model.SimBaseRes[model.SimSessionItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: ydResp.Message,
|
||
ResData: model.SimSessionItem{
|
||
Status: "-2",
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
for _, session := range ydResp.Result[0].InfoList {
|
||
items = append(items, &model.SimBaseRes[model.SimSessionItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
ResData: model.SimSessionItem{
|
||
ApnId: session.Apn,
|
||
Rat: session.AccessType,
|
||
Ipv4: session.Ipv4,
|
||
Ipv6: session.Ipv6,
|
||
Ipv6Prefix: session.Ipv6Prefix,
|
||
Status: convertYdSessionStatus(ctx, session.SessionStatus),
|
||
CreateTime: session.CreateTime,
|
||
},
|
||
})
|
||
}
|
||
for _, session := range ydResp.Result[0].SimSessionList {
|
||
items = append(items, &model.SimBaseRes[model.SimSessionItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
ResData: model.SimSessionItem{
|
||
ApnId: session.ApnId,
|
||
Rat: session.Rat,
|
||
Ipv4: session.Ip,
|
||
Ipv6: session.Ipv6,
|
||
Ipv6Prefix: session.Ipv6Prefix,
|
||
Status: convertYdSessionStatus(ctx, session.Status),
|
||
CreateTime: session.CreateDate,
|
||
},
|
||
})
|
||
}
|
||
}
|
||
return items, nil
|
||
}
|
||
|
||
// 5.1.6 单卡状态查询
|
||
// https://api.iot.10086.cn/v5/ec/query/sim-status?transid=REPLACE_WITH_VALUE&token=REPLACE_WITH_VALUE&iccid=89860000000000000004
|
||
func (s *sSimYD) QuerySimCardStatus(ctx context.Context, input *model.SimCardStatusInput) (model.SimCardStatusOutput, error) {
|
||
url := s.getUrl(ctx, "ec/query/sim-status")
|
||
|
||
// {
|
||
// "status":"0",
|
||
// "message":"正确",
|
||
// "result":[
|
||
// {
|
||
// "cardStatus":"1",
|
||
// "lastChangeDate":"2018-04-21 08:00:00"
|
||
// }
|
||
// ]
|
||
// }
|
||
|
||
// {"status":"0","message":"正确","result":[{"cardStatus":"2","lastChangeDate":"2023-05-11 15:07:08"}]}
|
||
type ydResp struct {
|
||
Status string `json:"status"`
|
||
Message string `json:"message"`
|
||
Result []struct {
|
||
CardStatus string `json:"cardStatus"`
|
||
LastChangeDate string `json:"lastChangeDate"`
|
||
} `json:"result"`
|
||
}
|
||
items := make(model.SimCardStatusOutput, 0)
|
||
for _, simKey := range input.SimKeys {
|
||
params := g.Map{
|
||
"transid": s.getTransId(ctx),
|
||
"iccid": simKey.ICCID,
|
||
}
|
||
|
||
resp, err := s.requestYd(ctx, "QuerySimCardStatus", url, params)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimCardStatus get error %+v,url %s, params %+v, ", err, url, params)
|
||
items = append(items, &model.SimBaseRes[model.SimCardStatusItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: err.Error(),
|
||
})
|
||
continue
|
||
}
|
||
body := resp.ReadAllString()
|
||
var ydResp ydResp
|
||
err = json.Unmarshal([]byte(body), &ydResp)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimCardStatus unmarshal error %+v,url %s, params %+v, body %s, ", err, url, params, body)
|
||
items = append(items, &model.SimBaseRes[model.SimCardStatusItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: err.Error(),
|
||
})
|
||
continue
|
||
}
|
||
if ydResp.Status != "0" {
|
||
g.Log().Warningf(ctx, "QuerySimCardStatus error %s,url %s, params %+v, body %s, ", ydResp.Message, url, params, body)
|
||
items = append(items, &model.SimBaseRes[model.SimCardStatusItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
ResData: model.SimCardStatusItem{
|
||
CardStatus: 0,
|
||
LastChangeDate: "",
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
if len(ydResp.Result) == 0 {
|
||
items = append(items, &model.SimBaseRes[model.SimCardStatusItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: ydResp.Message,
|
||
ResData: model.SimCardStatusItem{
|
||
CardStatus: 0,
|
||
LastChangeDate: "",
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
if len(ydResp.Result) > 0 {
|
||
result := ydResp.Result[0]
|
||
items = append(items, &model.SimBaseRes[model.SimCardStatusItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
ResData: model.SimCardStatusItem{
|
||
CardStatus: gconv.Int(result.CardStatus),
|
||
LastChangeDate: result.LastChangeDate,
|
||
},
|
||
})
|
||
} else {
|
||
items = append(items, &model.SimBaseRes[model.SimCardStatusItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: ydResp.Message,
|
||
ResData: model.SimCardStatusItem{
|
||
CardStatus: 0,
|
||
LastChangeDate: "",
|
||
},
|
||
})
|
||
}
|
||
}
|
||
return items, nil
|
||
}
|
||
|
||
// 物联网卡IMEI查询
|
||
// https://api.iot.10086.cn/v5/ec/query/sim-imei?transid=REPLACE_WITH_VALUE&token=REPLACE_WITH_VALUE&iccid=89860000000000000005
|
||
func (s *sSimYD) QuerySimStopReason(ctx context.Context, input *model.SimStopReasonInput) (model.SimStopReasonOutput, error) {
|
||
url := s.getUrl(ctx, "ec/query/sim-stop-reason")
|
||
|
||
type ydResp struct {
|
||
Status string `json:"status"`
|
||
Message string `json:"message"`
|
||
Result []struct {
|
||
Msisdn string `json:"msisdn"`
|
||
PlatformType string `json:"platformType"`
|
||
StopReason string `json:"stopReason"`
|
||
} `json:"result"`
|
||
}
|
||
|
||
items := make(model.SimStopReasonOutput, 0, len(input.SimKeys))
|
||
for _, simKey := range input.SimKeys {
|
||
paramKey, paramValue := pickSimQueryParam(simKey)
|
||
if paramKey == "" {
|
||
items = append(items, &model.SimBaseRes[model.SimStopReasonItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: "missing iccid/msisdn/imsi",
|
||
ResData: model.SimStopReasonItem{},
|
||
})
|
||
continue
|
||
}
|
||
|
||
params := g.Map{
|
||
"transid": s.getTransId(ctx),
|
||
paramKey: paramValue,
|
||
}
|
||
resp, err := s.requestYd(ctx, "QuerySimStopReason", url, params)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimStopReason get error %+v,url %s, params %+v, ", err, url, params)
|
||
items = append(items, &model.SimBaseRes[model.SimStopReasonItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: err.Error(),
|
||
ResData: model.SimStopReasonItem{},
|
||
})
|
||
continue
|
||
}
|
||
body := resp.ReadAllString()
|
||
|
||
var ydResp ydResp
|
||
if err = json.Unmarshal([]byte(body), &ydResp); err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimStopReason unmarshal error %+v,url %s, params %+v, body %s, ", err, url, params, body)
|
||
items = append(items, &model.SimBaseRes[model.SimStopReasonItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: err.Error(),
|
||
ResData: model.SimStopReasonItem{},
|
||
})
|
||
continue
|
||
}
|
||
if ydResp.Status != "0" {
|
||
g.Log().Warningf(ctx, "QuerySimStopReason error %s,url %s, params %+v, body %s, ", ydResp.Message, url, params, body)
|
||
items = append(items, &model.SimBaseRes[model.SimStopReasonItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
ResData: model.SimStopReasonItem{},
|
||
})
|
||
continue
|
||
}
|
||
|
||
stopReason := "未停机"
|
||
platformType := ""
|
||
if len(ydResp.Result) > 0 {
|
||
stopReason = mapYdStopReasonText(ydResp.Result[0].StopReason)
|
||
if stopReason == "" {
|
||
stopReason = "未停机"
|
||
}
|
||
platformType = strings.TrimSpace(ydResp.Result[0].PlatformType)
|
||
}
|
||
|
||
items = append(items, &model.SimBaseRes[model.SimStopReasonItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
ResData: model.SimStopReasonItem{
|
||
StopReason: stopReason,
|
||
PlatformType: platformType,
|
||
},
|
||
})
|
||
}
|
||
return items, nil
|
||
}
|
||
|
||
func (s *sSimYD) QuerySimFrozenState(ctx context.Context, input *model.SimFrozenStateInput) (model.SimFrozenStateOutput, error) {
|
||
url := s.getUrl(ctx, "ec/query/sim-frozen-state-info")
|
||
|
||
type ydResp struct {
|
||
Status string `json:"status"`
|
||
Message string `json:"message"`
|
||
Result []struct {
|
||
Msisdn string `json:"msisdn"`
|
||
FrozenStatus string `json:"frozenStatus"`
|
||
FrozenReason string `json:"frozenReason"`
|
||
OperTime string `json:"operTime"`
|
||
} `json:"result"`
|
||
}
|
||
|
||
items := make(model.SimFrozenStateOutput, 0, len(input.SimKeys))
|
||
for _, simKey := range input.SimKeys {
|
||
paramKey, paramValue := pickSimQueryParam(simKey)
|
||
if paramKey == "" {
|
||
items = append(items, &model.SimBaseRes[model.SimFrozenStateItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: "missing iccid/msisdn/imsi",
|
||
ResData: model.SimFrozenStateItem{},
|
||
})
|
||
continue
|
||
}
|
||
|
||
params := g.Map{
|
||
"transid": s.getTransId(ctx),
|
||
paramKey: paramValue,
|
||
}
|
||
resp, err := s.requestYd(ctx, "QuerySimFrozenState", url, params)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimFrozenState get error %+v,url %s, params %+v, ", err, url, params)
|
||
items = append(items, &model.SimBaseRes[model.SimFrozenStateItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: err.Error(),
|
||
ResData: model.SimFrozenStateItem{},
|
||
})
|
||
continue
|
||
}
|
||
body := resp.ReadAllString()
|
||
|
||
var ydResp ydResp
|
||
if err = json.Unmarshal([]byte(body), &ydResp); err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimFrozenState unmarshal error %+v,url %s, params %+v, body %s, ", err, url, params, body)
|
||
items = append(items, &model.SimBaseRes[model.SimFrozenStateItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: err.Error(),
|
||
ResData: model.SimFrozenStateItem{},
|
||
})
|
||
continue
|
||
}
|
||
if ydResp.Status != "0" {
|
||
g.Log().Warningf(ctx, "QuerySimFrozenState error %s,url %s, params %+v, body %s, ", ydResp.Message, url, params, body)
|
||
items = append(items, &model.SimBaseRes[model.SimFrozenStateItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
ResData: model.SimFrozenStateItem{},
|
||
})
|
||
continue
|
||
}
|
||
|
||
frozenStatus := "未冻结"
|
||
frozenReason := ""
|
||
operTime := ""
|
||
if len(ydResp.Result) > 0 {
|
||
frozenStatus = mapYdFrozenStatusText(ydResp.Result[0].FrozenStatus)
|
||
if frozenStatus == "" {
|
||
frozenStatus = "未冻结"
|
||
}
|
||
frozenReason = strings.TrimSpace(ydResp.Result[0].FrozenReason)
|
||
operTime = strings.TrimSpace(ydResp.Result[0].OperTime)
|
||
}
|
||
|
||
items = append(items, &model.SimBaseRes[model.SimFrozenStateItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
ResData: model.SimFrozenStateItem{
|
||
FrozenStatus: frozenStatus,
|
||
FrozenReason: frozenReason,
|
||
OperTime: operTime,
|
||
},
|
||
})
|
||
}
|
||
return items, nil
|
||
}
|
||
|
||
func (s *sSimYD) QuerySimImei(ctx context.Context, input *model.SimImeiInput) (model.SimImeiOutput, error) {
|
||
url := s.getUrl(ctx, "ec/query/sim-terminal-device-info")
|
||
// {"status":"0","message":"正确","result":[{"imei":"865466070817587"}]}
|
||
type ydResp struct {
|
||
Status string `json:"status"`
|
||
Message string `json:"message"`
|
||
Result []struct {
|
||
Imei string `json:"imei"`
|
||
TerminalDeviceImei string `json:"terminalDeviceImei"`
|
||
} `json:"result"`
|
||
}
|
||
items := make(model.SimImeiOutput, 0)
|
||
for _, simKey := range input.SimKeys {
|
||
key, value := pickSimQueryParam(simKey)
|
||
if key == "" {
|
||
items = append(items, &model.SimBaseRes[model.SimImeiItem]{SimKey: simKey, Code: consts.ApiStatusError, Message: "missing iccid, msisdn or imsi"})
|
||
continue
|
||
}
|
||
params := g.Map{
|
||
"transid": s.getTransId(ctx),
|
||
key: value,
|
||
}
|
||
|
||
resp, err := s.requestYd(ctx, "QuerySimImei", url, params)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimImei get error %+v,url %s, params %+v, ", err, url, params)
|
||
items = append(items, &model.SimBaseRes[model.SimImeiItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: err.Error(),
|
||
})
|
||
continue
|
||
}
|
||
body := resp.ReadAllString()
|
||
var ydResp ydResp
|
||
err = json.Unmarshal([]byte(body), &ydResp)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimImei unmarshal error %+v,url %s, params %+v, body %s, ", err, url, params, body)
|
||
items = append(items, &model.SimBaseRes[model.SimImeiItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: err.Error(),
|
||
})
|
||
continue
|
||
}
|
||
if ydResp.Status != "0" {
|
||
g.Log().Warningf(ctx, "QuerySimImei error %s,url %s, params %+v, body %s, ", ydResp.Message, url, params, body)
|
||
items = append(items, &model.SimBaseRes[model.SimImeiItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
})
|
||
continue
|
||
}
|
||
if len(ydResp.Result) == 0 {
|
||
items = append(items, &model.SimBaseRes[model.SimImeiItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
})
|
||
continue
|
||
}
|
||
if len(ydResp.Result) == 1 {
|
||
result := ydResp.Result[0]
|
||
items = append(items, &model.SimBaseRes[model.SimImeiItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
ResData: model.SimImeiItem{
|
||
Imei: firstNonEmpty(result.TerminalDeviceImei, result.Imei),
|
||
},
|
||
})
|
||
} else {
|
||
items = append(items, &model.SimBaseRes[model.SimImeiItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
})
|
||
}
|
||
}
|
||
return items, nil
|
||
}
|
||
|
||
// 单卡本月流量累计使用量查询
|
||
// https://api.iot.10086.cn/v5/ec/query/sim-data-usage-inpool?transid=REPLACE_WITH_VALUE&token=REPLACE_WITH_VALUE&msisdn=1440000000003
|
||
func (s *sSimYD) QuerySimDataUsageInPool(ctx context.Context, input *model.SimDataUsageInPoolInput) (model.SimDataUsageInPoolOutput, error) {
|
||
url := s.getUrl(ctx, "ec/query/sim-data-usage-inpool")
|
||
// {"status":"0","message":"正确","result":[{"apnList":[{"apnName":"CMIOT","apnUseAmount":"5994479616"}]}]}
|
||
type ydResp struct {
|
||
Status string `json:"status"`
|
||
Message string `json:"message"`
|
||
Result []struct {
|
||
ApnList []struct {
|
||
ApnName string `json:"apnName"`
|
||
ApnUseAmount string `json:"apnUseAmount"`
|
||
} `json:"apnList"`
|
||
} `json:"result"`
|
||
}
|
||
items := make(model.SimDataUsageInPoolOutput, 0)
|
||
for _, simKey := range input.SimKeys {
|
||
params := g.Map{
|
||
"transid": s.getTransId(ctx),
|
||
"iccid": simKey.ICCID,
|
||
}
|
||
|
||
resp, err := s.requestYd(ctx, "QuerySimDataUsageInPool", url, params)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimDataUsageInPool get error %+v,url %s, params %+v, ", err, url, params)
|
||
items = append(items, &model.SimBaseRes[model.SimDataUsageInPoolItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: err.Error(),
|
||
})
|
||
continue
|
||
}
|
||
body := resp.ReadAllString()
|
||
var ydResp ydResp
|
||
err = json.Unmarshal([]byte(body), &ydResp)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimDataUsageInPool unmarshal error %+v,url %s, params %+v, body %s, ", err, url, params, body)
|
||
items = append(items, &model.SimBaseRes[model.SimDataUsageInPoolItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: err.Error(),
|
||
})
|
||
continue
|
||
}
|
||
if ydResp.Status != "0" {
|
||
g.Log().Warningf(ctx, "QuerySimDataUsageInPool error %s,url %s, params %+v, body %s, ", ydResp.Message, url, params, body)
|
||
items = append(items, &model.SimBaseRes[model.SimDataUsageInPoolItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
})
|
||
continue
|
||
}
|
||
if len(ydResp.Result) == 0 {
|
||
items = append(items, &model.SimBaseRes[model.SimDataUsageInPoolItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
ResData: model.SimDataUsageInPoolItem{
|
||
DataUsage: 0.0,
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
if len(ydResp.Result) == 1 {
|
||
result := ydResp.Result[0]
|
||
dataUsage := 0.0
|
||
if len(result.ApnList) != 0 {
|
||
for _, apn := range result.ApnList {
|
||
dataUsage += gconv.Float64(apn.ApnUseAmount)
|
||
}
|
||
}
|
||
items = append(items, &model.SimBaseRes[model.SimDataUsageInPoolItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
ResData: model.SimDataUsageInPoolItem{
|
||
DataUsage: dataUsage / 1024,
|
||
},
|
||
})
|
||
} else {
|
||
items = append(items, &model.SimBaseRes[model.SimDataUsageInPoolItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
ResData: model.SimDataUsageInPoolItem{
|
||
DataUsage: 0.0,
|
||
},
|
||
})
|
||
}
|
||
}
|
||
return items, nil
|
||
}
|
||
|
||
// 单卡本月流量累计使用量查询
|
||
// https://api.iot.10086.cn/v5/ec/query/sim-data-usage?transid=REPLACE_WITH_VALUE&token=REPLACE_WITH_VALUE&msisdn=1440000000003
|
||
func (s *sSimYD) QuerySimDataUsage(ctx context.Context, input *model.SimDataUsageInput) (model.SimDataUsageOutput, error) {
|
||
url := s.getUrl(ctx, "ec/query/sim-data-usage")
|
||
// {"status":"0","message":"正确","result":[{"dataAmount":"710569.00","apnUseAmountList":[{"apnName":"CMIOT","apnUseAmount":"710569.00","pccCodeUseAmountList":[]}]}]}
|
||
items := make(model.SimDataUsageOutput, 0)
|
||
for _, simKey := range input.SimKeys {
|
||
params := g.Map{
|
||
"transid": s.getTransId(ctx),
|
||
"iccid": simKey.ICCID,
|
||
}
|
||
resp, fetchErr := s.requestYd(ctx, "QuerySimDataUsage", url, params)
|
||
if fetchErr != nil {
|
||
g.Log().Errorf(ctx, "QuerySimDataUsage request failed, chanKey:%s, err:%+v", simKey.ChanKey, fetchErr)
|
||
items = append(items, &model.SimBaseRes[model.SimDataUsageItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: fetchErr.Error(),
|
||
})
|
||
continue
|
||
}
|
||
body := resp.ReadAllString()
|
||
ydResp, decodeErr := decodeYdDataUsageResponse(body)
|
||
if fetchErr = decodeErr; fetchErr != nil {
|
||
g.Log().Errorf(ctx, "QuerySimDataUsage unmarshal error, chanKey:%s, err:%+v", simKey.ChanKey, fetchErr)
|
||
items = append(items, &model.SimBaseRes[model.SimDataUsageItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: fetchErr.Error(),
|
||
})
|
||
continue
|
||
}
|
||
if ydResp.Status != "0" {
|
||
g.Log().Warningf(ctx, "QuerySimDataUsage upstream error, chanKey:%s, status:%s, message:%s", simKey.ChanKey, ydResp.Status, ydResp.Message)
|
||
items = append(items, &model.SimBaseRes[model.SimDataUsageItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
ErrCode: ydResp.Status,
|
||
})
|
||
continue
|
||
}
|
||
if len(ydResp.Result) == 0 {
|
||
items = append(items, &model.SimBaseRes[model.SimDataUsageItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
ResData: model.SimDataUsageItem{
|
||
DataUsage: 0.0,
|
||
},
|
||
})
|
||
continue
|
||
} else {
|
||
items = append(items, &model.SimBaseRes[model.SimDataUsageItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
ResData: model.SimDataUsageItem{
|
||
DataUsage: gconv.Float64(ydResp.Result[0].DataAmount) / 1024,
|
||
},
|
||
})
|
||
}
|
||
}
|
||
return items, nil
|
||
}
|
||
|
||
// 物联卡单月 GPRS 流量使用量批量查询,单批最多 100 张卡
|
||
func (s *sSimYD) QuerySimDataUsageMonthly(ctx context.Context, input *model.SimDataUsageMonthlyInput) (model.SimDataUsageMonthlyOutput, error) {
|
||
url := s.getUrl(ctx, "ec/query/sim-data-usage-monthly/batch")
|
||
type YdResp struct {
|
||
Status string `json:"status"`
|
||
Message string `json:"message"`
|
||
Result []struct {
|
||
DeadLine string `json:"deadLine"`
|
||
DataAmountList []struct {
|
||
DataAmount string `json:"dataAmount"`
|
||
Iccid string `json:"iccid"`
|
||
Imsi string `json:"imsi"`
|
||
Msisdn string `json:"msisdn"`
|
||
} `json:"dataAmountList"`
|
||
} `json:"result"`
|
||
}
|
||
items := make(model.SimDataUsageMonthlyOutput, 0, len(input.SimKeys))
|
||
const batchSize = 100
|
||
for start := 0; start < len(input.SimKeys); start += batchSize {
|
||
end := start + batchSize
|
||
if end > len(input.SimKeys) {
|
||
end = len(input.SimKeys)
|
||
}
|
||
batch := input.SimKeys[start:end]
|
||
iccids := make([]string, 0, len(batch))
|
||
simKeyMap := make(map[string]*model.SimKey, len(batch)*3)
|
||
for _, simKey := range batch {
|
||
if simKey == nil {
|
||
iccids = append(iccids, "")
|
||
continue
|
||
}
|
||
iccids = append(iccids, strings.TrimSpace(simKey.ICCID))
|
||
if value := strings.TrimSpace(simKey.ICCID); value != "" {
|
||
simKeyMap["iccid:"+strings.ToUpper(value)] = simKey
|
||
}
|
||
if value := strings.TrimSpace(simKey.IMSI); value != "" {
|
||
simKeyMap["imsi:"+strings.ToUpper(value)] = simKey
|
||
}
|
||
if value := strings.TrimSpace(simKey.MSISDN); value != "" {
|
||
simKeyMap["msisdn:"+value] = simKey
|
||
}
|
||
}
|
||
params := g.Map{
|
||
"transid": s.getTransId(ctx),
|
||
"iccids": strings.Join(iccids, "_"),
|
||
"queryDate": input.ReqData.QueryDate,
|
||
}
|
||
resp, err := s.requestYd(ctx, "QuerySimDataUsageMonthly", url, params)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimDataUsageMonthly get error %+v,url %s, params %+v", err, url, params)
|
||
for _, simKey := range batch {
|
||
items = append(items, &model.SimBaseRes[model.SimDataUsageMonthlyOutputItem]{SimKey: simKey, Code: consts.ApiStatusError, Message: err.Error()})
|
||
}
|
||
continue
|
||
}
|
||
body := resp.ReadAllString()
|
||
var ydResp YdResp
|
||
if err = json.Unmarshal([]byte(body), &ydResp); err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimDataUsageMonthly unmarshal error %+v,url %s, params %+v, body %s", err, url, params, body)
|
||
for _, simKey := range batch {
|
||
items = append(items, &model.SimBaseRes[model.SimDataUsageMonthlyOutputItem]{SimKey: simKey, Code: consts.ApiStatusError, Message: err.Error()})
|
||
}
|
||
continue
|
||
}
|
||
if ydResp.Status != "0" {
|
||
g.Log().Warningf(ctx, "QuerySimDataUsageMonthly error %s,url %s, params %+v, body %s", ydResp.Message, url, params, body)
|
||
for _, simKey := range batch {
|
||
items = append(items, &model.SimBaseRes[model.SimDataUsageMonthlyOutputItem]{SimKey: simKey, Code: gconv.Int(ydResp.Status), Message: ydResp.Message})
|
||
}
|
||
continue
|
||
}
|
||
|
||
resultMap := make(map[*model.SimKey]struct {
|
||
DeadLine string
|
||
DataAmount string
|
||
}, len(batch))
|
||
for _, result := range ydResp.Result {
|
||
for _, dataAmount := range result.DataAmountList {
|
||
var simKey *model.SimKey
|
||
if value := strings.TrimSpace(dataAmount.Iccid); value != "" {
|
||
simKey = simKeyMap["iccid:"+strings.ToUpper(value)]
|
||
}
|
||
if simKey == nil {
|
||
if value := strings.TrimSpace(dataAmount.Imsi); value != "" {
|
||
simKey = simKeyMap["imsi:"+strings.ToUpper(value)]
|
||
}
|
||
}
|
||
if simKey == nil {
|
||
if value := strings.TrimSpace(dataAmount.Msisdn); value != "" {
|
||
simKey = simKeyMap["msisdn:"+value]
|
||
}
|
||
}
|
||
if simKey == nil && len(batch) == 1 {
|
||
simKey = batch[0]
|
||
}
|
||
if simKey != nil {
|
||
resultMap[simKey] = struct {
|
||
DeadLine string
|
||
DataAmount string
|
||
}{DeadLine: result.DeadLine, DataAmount: dataAmount.DataAmount}
|
||
}
|
||
}
|
||
}
|
||
for _, simKey := range batch {
|
||
result, ok := resultMap[simKey]
|
||
if !ok {
|
||
items = append(items, &model.SimBaseRes[model.SimDataUsageMonthlyOutputItem]{SimKey: simKey, Code: gconv.Int(ydResp.Status), Message: "查询信息为空"})
|
||
continue
|
||
}
|
||
items = append(items, &model.SimBaseRes[model.SimDataUsageMonthlyOutputItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
ResData: model.SimDataUsageMonthlyOutputItem{
|
||
DeadLine: result.DeadLine,
|
||
DataAmount: gconv.Float64(strings.TrimSpace(result.DataAmount)) / 1024,
|
||
},
|
||
})
|
||
}
|
||
}
|
||
return items, nil
|
||
}
|
||
|
||
// 物联卡单日 GPRS 流量使用量批量查询,单批最多 100 张卡
|
||
// 上游返回 dataAmount 单位为 KB,对外统一转换为 MB
|
||
func (s *sSimYD) QuerySimDataUsageDaily(ctx context.Context, input *model.SimDataUsageDailyInput) (model.SimDataUsageDailyOutput, error) {
|
||
url := s.getUrl(ctx, "ec/query/sim-data-usage-daily/batch")
|
||
type YdResp struct {
|
||
Status string `json:"status"`
|
||
Message string `json:"message"`
|
||
Result []struct {
|
||
QueryDate string `json:"queryDate"`
|
||
DeadLine string `json:"deadLine"`
|
||
DataAmount string `json:"dataAmount"`
|
||
DataAmountList []struct {
|
||
QueryDate string `json:"queryDate"`
|
||
DeadLine string `json:"deadLine"`
|
||
Msisdn string `json:"msisdn"`
|
||
Iccid string `json:"iccid"`
|
||
Imsi string `json:"imsi"`
|
||
DataAmount string `json:"dataAmount"`
|
||
} `json:"dataAmountList"`
|
||
} `json:"result"`
|
||
}
|
||
items := make(model.SimDataUsageDailyOutput, 0, len(input.SimKeys))
|
||
const batchSize = 100
|
||
for start := 0; start < len(input.SimKeys); start += batchSize {
|
||
end := start + batchSize
|
||
if end > len(input.SimKeys) {
|
||
end = len(input.SimKeys)
|
||
}
|
||
batch := input.SimKeys[start:end]
|
||
iccids := make([]string, 0, len(batch))
|
||
simKeyMap := make(map[string]*model.SimKey, len(batch)*3)
|
||
for _, simKey := range batch {
|
||
if simKey == nil {
|
||
iccids = append(iccids, "")
|
||
continue
|
||
}
|
||
iccids = append(iccids, strings.TrimSpace(simKey.ICCID))
|
||
if value := strings.TrimSpace(simKey.ICCID); value != "" {
|
||
simKeyMap["iccid:"+strings.ToUpper(value)] = simKey
|
||
}
|
||
if value := strings.TrimSpace(simKey.IMSI); value != "" {
|
||
simKeyMap["imsi:"+strings.ToUpper(value)] = simKey
|
||
}
|
||
if value := strings.TrimSpace(simKey.MSISDN); value != "" {
|
||
simKeyMap["msisdn:"+value] = simKey
|
||
}
|
||
}
|
||
params := g.Map{
|
||
"transid": s.getTransId(ctx),
|
||
"iccids": strings.Join(iccids, "_"),
|
||
"queryDate": input.ReqData.QueryDate,
|
||
}
|
||
resp, err := s.requestYd(ctx, "QuerySimDataUsageDaily", url, params)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimDataUsageDaily get error %+v,url %s, params %+v", err, url, params)
|
||
for _, simKey := range batch {
|
||
items = append(items, &model.SimBaseRes[model.SimDataUsageDailyOutputItem]{SimKey: simKey, Code: consts.ApiStatusError, Message: err.Error()})
|
||
}
|
||
continue
|
||
}
|
||
body := resp.ReadAllString()
|
||
var ydResp YdResp
|
||
if err = json.Unmarshal([]byte(body), &ydResp); err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimDataUsageDaily unmarshal error %+v,url %s, params %+v, body %s", err, url, params, body)
|
||
for _, simKey := range batch {
|
||
items = append(items, &model.SimBaseRes[model.SimDataUsageDailyOutputItem]{SimKey: simKey, Code: consts.ApiStatusError, Message: err.Error()})
|
||
}
|
||
continue
|
||
}
|
||
if ydResp.Status != "0" {
|
||
g.Log().Warningf(ctx, "QuerySimDataUsageDaily error %s,url %s, params %+v, body %s", ydResp.Message, url, params, body)
|
||
for _, simKey := range batch {
|
||
items = append(items, &model.SimBaseRes[model.SimDataUsageDailyOutputItem]{SimKey: simKey, Code: gconv.Int(ydResp.Status), Message: ydResp.Message, ResData: model.SimDataUsageDailyOutputItem{QueryDate: input.ReqData.QueryDate}})
|
||
}
|
||
continue
|
||
}
|
||
|
||
resultMap := make(map[*model.SimKey]struct {
|
||
QueryDate string
|
||
DeadLine string
|
||
DataAmount string
|
||
}, len(batch))
|
||
for _, result := range ydResp.Result {
|
||
if len(result.DataAmountList) == 0 {
|
||
if len(batch) == 1 {
|
||
resultMap[batch[0]] = struct {
|
||
QueryDate string
|
||
DeadLine string
|
||
DataAmount string
|
||
}{QueryDate: result.QueryDate, DeadLine: result.DeadLine, DataAmount: result.DataAmount}
|
||
}
|
||
continue
|
||
}
|
||
for _, dataAmount := range result.DataAmountList {
|
||
var simKey *model.SimKey
|
||
if value := strings.TrimSpace(dataAmount.Iccid); value != "" {
|
||
simKey = simKeyMap["iccid:"+strings.ToUpper(value)]
|
||
}
|
||
if simKey == nil {
|
||
if value := strings.TrimSpace(dataAmount.Imsi); value != "" {
|
||
simKey = simKeyMap["imsi:"+strings.ToUpper(value)]
|
||
}
|
||
}
|
||
if simKey == nil {
|
||
if value := strings.TrimSpace(dataAmount.Msisdn); value != "" {
|
||
simKey = simKeyMap["msisdn:"+value]
|
||
}
|
||
}
|
||
if simKey == nil && len(batch) == 1 {
|
||
simKey = batch[0]
|
||
}
|
||
if simKey != nil {
|
||
queryDate := dataAmount.QueryDate
|
||
if queryDate == "" {
|
||
queryDate = dataAmount.DeadLine
|
||
}
|
||
if queryDate == "" {
|
||
queryDate = result.QueryDate
|
||
}
|
||
if queryDate == "" {
|
||
queryDate = result.DeadLine
|
||
}
|
||
resultMap[simKey] = struct {
|
||
QueryDate string
|
||
DeadLine string
|
||
DataAmount string
|
||
}{QueryDate: queryDate, DeadLine: dataAmount.DeadLine, DataAmount: dataAmount.DataAmount}
|
||
}
|
||
}
|
||
}
|
||
for _, simKey := range batch {
|
||
result, ok := resultMap[simKey]
|
||
if !ok {
|
||
items = append(items, &model.SimBaseRes[model.SimDataUsageDailyOutputItem]{SimKey: simKey, Code: gconv.Int(ydResp.Status), Message: "查询信息为空", ResData: model.SimDataUsageDailyOutputItem{QueryDate: input.ReqData.QueryDate}})
|
||
continue
|
||
}
|
||
queryDate := result.QueryDate
|
||
if queryDate == "" {
|
||
queryDate = result.DeadLine
|
||
}
|
||
if queryDate == "" {
|
||
queryDate = input.ReqData.QueryDate
|
||
}
|
||
items = append(items, &model.SimBaseRes[model.SimDataUsageDailyOutputItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
ResData: model.SimDataUsageDailyOutputItem{
|
||
QueryDate: queryDate,
|
||
DataAmount: gconv.Float64(strings.TrimSpace(result.DataAmount)) / 1024,
|
||
},
|
||
})
|
||
}
|
||
}
|
||
return items, nil
|
||
}
|
||
|
||
// 单卡已开通APN信息查询
|
||
// https://api.iot.10086.cn/v5/ec/query/apn-info?transid=REPLACE_WITH_VALUE&token=REPLACE_WITH_VALUE&iccid=89860000000000000003
|
||
func (s *sSimYD) QuerySimApnInfo(ctx context.Context, input *model.SimApnInfoInput) (model.SimApnInfoOutput, error) {
|
||
url := s.getUrl(ctx, "ec/query/apn-info")
|
||
// {"status":"0","message":"正确","result":[{"apnList":[{"apnName":"CMIOT","status":"1"}]}]}
|
||
type YdResp struct {
|
||
Status string `json:"status"`
|
||
Message string `json:"message"`
|
||
Result []struct {
|
||
ApnList []struct {
|
||
ApnName string `json:"apnName"`
|
||
Status string `json:"status"`
|
||
} `json:"apnList"`
|
||
} `json:"result"`
|
||
}
|
||
items := make(model.SimApnInfoOutput, 0)
|
||
for _, simKey := range input.SimKeys {
|
||
params := g.Map{
|
||
"transid": s.getTransId(ctx),
|
||
"iccid": simKey.ICCID,
|
||
}
|
||
|
||
resp, err := s.requestYd(ctx, "QuerySimApnInfo", url, params)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimApnInfo get error %+v,url %s, params %+v, ", err, url, params)
|
||
items = append(items, &model.SimBaseRes[model.SimApnInfoItemList]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: err.Error(),
|
||
ResData: model.SimApnInfoItemList{
|
||
ApnList: make([]model.SimApnInfoItem, 0),
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
body := resp.ReadAllString()
|
||
var ydResp YdResp
|
||
err = json.Unmarshal([]byte(body), &ydResp)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimApnInfo unmarshal error %+v,url %s, params %+v, body %s, ", err, url, params, body)
|
||
items = append(items, &model.SimBaseRes[model.SimApnInfoItemList]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: err.Error(),
|
||
ResData: model.SimApnInfoItemList{
|
||
ApnList: make([]model.SimApnInfoItem, 0),
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
if ydResp.Status != "0" {
|
||
g.Log().Warningf(ctx, "QuerySimApnInfo error %s,url %s, params %+v, body %s, ", ydResp.Message, url, params, body)
|
||
items = append(items, &model.SimBaseRes[model.SimApnInfoItemList]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
ResData: model.SimApnInfoItemList{
|
||
ApnList: make([]model.SimApnInfoItem, 0),
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
if len(ydResp.Result) == 0 {
|
||
items = append(items, &model.SimBaseRes[model.SimApnInfoItemList]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: "查询信息为空",
|
||
ResData: model.SimApnInfoItemList{
|
||
ApnList: make([]model.SimApnInfoItem, 0),
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
if len(ydResp.Result) == 1 {
|
||
result := ydResp.Result[0]
|
||
if len(result.ApnList) != 0 {
|
||
for _, apn := range result.ApnList {
|
||
items = append(items, &model.SimBaseRes[model.SimApnInfoItemList]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: "",
|
||
ResData: model.SimApnInfoItemList{
|
||
ApnList: []model.SimApnInfoItem{
|
||
{
|
||
ApnName: apn.ApnName,
|
||
Status: convertYdApnStatus(ctx, apn.Status),
|
||
},
|
||
},
|
||
},
|
||
})
|
||
}
|
||
} else {
|
||
g.Log().Errorf(ctx, "SimYD.QuerySimApnInfo, Error in returning information,ydResp: %+v, input: %+v, ", ydResp, input)
|
||
items = append(items, &model.SimBaseRes[model.SimApnInfoItemList]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: "查询信息为空",
|
||
ResData: model.SimApnInfoItemList{
|
||
ApnList: make([]model.SimApnInfoItem, 0),
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
} else {
|
||
g.Log().Errorf(ctx, "SimYD.QuerySimApnInfo, Error in returning information,ydResp: %+v, input: %+v, ", ydResp, input)
|
||
items = append(items, &model.SimBaseRes[model.SimApnInfoItemList]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: "查询信息为空",
|
||
ResData: model.SimApnInfoItemList{
|
||
ApnList: make([]model.SimApnInfoItem, 0),
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
}
|
||
return items, nil
|
||
}
|
||
|
||
// 单卡状态变更
|
||
// 平台 operType: 0=申请停机(已激活转已停机), 1=申请复机(已停机转已激活)
|
||
// https://api.iot.10086.cn/v5/ec/change/sim-status?transid=REPLACE_WITH_VALUE&token=REPLACE_WITH_VALUE&msisdn=1440000000003&operType=0
|
||
func (s *sSimYD) ChangeSimStatus(ctx context.Context, input *model.SimCardStatusChangeInput) (model.SimCardStatusChangeOutput, error) {
|
||
operType, ok := normalizeCardStatusChangeOperType(ctx, input.ReqData.OperType)
|
||
if !ok {
|
||
return newInvalidCardStatusChangeOutput(input.SimKeys), nil
|
||
}
|
||
|
||
url := s.getUrl(ctx, "ec/change/sim-status")
|
||
// {"status":"0","message":"正确","result":[{"iccid":"89860000000000000004","orderNum":"995000011515720993"}]}
|
||
type YdResp struct {
|
||
Status string `json:"status"`
|
||
Message string `json:"message"`
|
||
Result []struct {
|
||
Iccid string `json:"iccid"`
|
||
OrderNum string `json:"orderNum"`
|
||
} `json:"result"`
|
||
}
|
||
items := make(model.SimCardStatusChangeOutput, 0, len(input.SimKeys))
|
||
for _, simKey := range input.SimKeys {
|
||
params := g.Map{
|
||
"transid": s.getTransId(ctx),
|
||
"iccid": simKey.ICCID,
|
||
"operType": operType,
|
||
}
|
||
|
||
resp, err := s.requestYd(ctx, "ChangeSimStatus", url, params)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "ChangeSimStatus get error %+v,url %s, params %+v, ", err, url, params)
|
||
items = append(items, &model.SimBaseRes[model.SimCardStatusChangeItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: err.Error(),
|
||
ResData: model.SimCardStatusChangeItem{
|
||
OrderNum: "",
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
body := resp.ReadAllString()
|
||
var ydResp YdResp
|
||
err = json.Unmarshal([]byte(body), &ydResp)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "ChangeSimStatus unmarshal error %+v,url %s, params %+v, body %s, ", err, url, params, body)
|
||
items = append(items, &model.SimBaseRes[model.SimCardStatusChangeItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: err.Error(),
|
||
ResData: model.SimCardStatusChangeItem{
|
||
OrderNum: "",
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
if ydResp.Status != "0" {
|
||
g.Log().Warningf(ctx, "ChangeSimStatus error %s,url %s, params %+v, body %s, ", ydResp.Message, url, params, body)
|
||
items = append(items, &model.SimBaseRes[model.SimCardStatusChangeItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: ydResp.Message,
|
||
ResData: model.SimCardStatusChangeItem{
|
||
OrderNum: "",
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
if len(ydResp.Result) == 0 {
|
||
items = append(items, &model.SimBaseRes[model.SimCardStatusChangeItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: "查询信息为空",
|
||
ResData: model.SimCardStatusChangeItem{
|
||
OrderNum: "",
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
if len(ydResp.Result) == 1 {
|
||
result := ydResp.Result[0]
|
||
if len(result.OrderNum) != 0 {
|
||
items = append(items, &model.SimBaseRes[model.SimCardStatusChangeItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: "变更成功",
|
||
ResData: model.SimCardStatusChangeItem{
|
||
OrderNum: result.OrderNum,
|
||
},
|
||
})
|
||
}
|
||
} else {
|
||
items = append(items, &model.SimBaseRes[model.SimCardStatusChangeItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(ydResp.Status),
|
||
Message: "查询信息为空",
|
||
ResData: model.SimCardStatusChangeItem{
|
||
OrderNum: "",
|
||
},
|
||
})
|
||
continue
|
||
}
|
||
}
|
||
return items, nil
|
||
}
|
||
|
||
// 开通/停用移动数据(APN)功能(开通数据 dataStart / 停用数据 dataStop / 达量断网 dataCut)。
|
||
// 2026 接口改版:已从旧的 ec/operate/sim-apn-function 迁移到
|
||
// 7.2.1 单卡 sim-communication-service 与 7.2.2 批量 sim-communication-function/batch,
|
||
// 具体分流与编码转换见 sim_yd_apn_communication.go。
|
||
func (s *sSimYD) ChangeSimApnFunction(ctx context.Context, input *model.SimCardOperateChangeApnInput) (model.SimCardOperateChangeApnOutput, error) {
|
||
return changeSimApnFunctionYD(ctx, s, input)
|
||
}
|
||
|
||
// 单卡机卡绑定状态查询
|
||
// https://api.iot.10086.cn/v5/ec/query/card-bind-status
|
||
// 文档位置:中移物联网OneLink能力调用参考文档V25.11.1(CT)- 6.1.1 CMIOT_API23A04
|
||
// 请求参数:msisdn、testType
|
||
// 上游返回 result 中的 bindStatus/cardBindStatus 作为机卡绑定状态
|
||
func (s *sSimYD) QuerySimCardBindStatus(ctx context.Context, input *model.SimCardBindStatusInput) (model.SimCardBindStatusOutput, error) {
|
||
url := s.getUrl(ctx, "ec/query/card-bind-status")
|
||
|
||
type ydResp struct {
|
||
Status string `json:"status"`
|
||
Message string `json:"message"`
|
||
Result []map[string]any `json:"result"`
|
||
}
|
||
|
||
items := make(model.SimCardBindStatusOutput, 0, len(input.SimKeys))
|
||
for _, simKey := range input.SimKeys {
|
||
msisdn := strings.TrimSpace(simKey.MSISDN)
|
||
if msisdn == "" {
|
||
g.Log().Warningf(ctx, "QuerySimCardBindStatus missing msisdn,url %s, simKey %+v, reqData %+v", url, simKey, input.ReqData)
|
||
items = append(items, &model.SimBaseRes[model.SimCardBindStatusItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: "missing msisdn",
|
||
})
|
||
continue
|
||
}
|
||
|
||
testType := 1
|
||
if input.ReqData.TestType != nil {
|
||
testType = *input.ReqData.TestType
|
||
}
|
||
|
||
params := g.Map{
|
||
"transid": s.getTransId(ctx),
|
||
"msisdn": msisdn,
|
||
"testType": testType,
|
||
}
|
||
|
||
resp, err := s.requestYd(ctx, "QuerySimCardBindStatus", url, params)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimCardBindStatus get error %+v,url %s, params %+v", err, url, params)
|
||
items = append(items, &model.SimBaseRes[model.SimCardBindStatusItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: err.Error(),
|
||
})
|
||
continue
|
||
}
|
||
body := resp.ReadAllString()
|
||
|
||
var respData ydResp
|
||
if err = json.Unmarshal([]byte(body), &respData); err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimCardBindStatus unmarshal error %+v,url %s, params %+v, body %s", err, url, params, body)
|
||
items = append(items, &model.SimBaseRes[model.SimCardBindStatusItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: err.Error(),
|
||
})
|
||
continue
|
||
}
|
||
if respData.Status != "0" {
|
||
g.Log().Warningf(ctx, "QuerySimCardBindStatus error %s,url %s, params %+v, body %s", respData.Message, url, params, body)
|
||
items = append(items, &model.SimBaseRes[model.SimCardBindStatusItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(respData.Status),
|
||
Message: respData.Message,
|
||
})
|
||
continue
|
||
}
|
||
|
||
bindStatus := ""
|
||
var errorCode string
|
||
var errorDes string
|
||
if len(respData.Result) > 0 {
|
||
bindStatus = pickYdCardBindStatus(respData.Result[0])
|
||
errorCode = pickYdCardBindStatusErrorCode(respData.Result[0])
|
||
errorDes = pickYdCardBindStatusErrorDes(respData.Result[0])
|
||
}
|
||
if bindStatus == "" {
|
||
g.Log().Warningf(ctx, "QuerySimCardBindStatus empty bind status,url %s, params %+v, body %s", url, params, body)
|
||
items = append(items, &model.SimBaseRes[model.SimCardBindStatusItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: "sim card bind status is empty",
|
||
})
|
||
continue
|
||
}
|
||
|
||
items = append(items, &model.SimBaseRes[model.SimCardBindStatusItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(respData.Status),
|
||
Message: respData.Message,
|
||
ResData: model.SimCardBindStatusItem{
|
||
BindStatus: bindStatus,
|
||
ErrorCode: errorCode,
|
||
ErrorDes: errorDes,
|
||
},
|
||
})
|
||
}
|
||
return items, nil
|
||
}
|
||
|
||
// 单卡设备状态查询
|
||
// https://api.iot.10086.cn/v5/ec/query/sim-operating-status?transid=REPLACE_WITH_VALUE&token=REPLACE_WITH_VALUE&iccid=xxx
|
||
// 上游返回 result 中的 operatingStatus/onOffStatus/status 作为设备状态
|
||
func (s *sSimYD) QuerySimDeviceStatus(ctx context.Context, input *model.SimDeviceStatusInput) (model.SimDeviceStatusOutput, error) {
|
||
url := s.getUrl(ctx, "ec/query/sim-operating-status")
|
||
|
||
type ydResp struct {
|
||
Status string `json:"status"`
|
||
Message string `json:"message"`
|
||
Result []map[string]any `json:"result"`
|
||
}
|
||
|
||
items := make(model.SimDeviceStatusOutput, 0, len(input.SimKeys))
|
||
for _, simKey := range input.SimKeys {
|
||
params := g.Map{
|
||
"transid": s.getTransId(ctx),
|
||
}
|
||
paramName, paramValue := pickSimQueryParam(simKey)
|
||
if paramName == "" {
|
||
items = append(items, &model.SimBaseRes[model.SimDeviceStatusItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: "missing imsi/iccid/msisdn",
|
||
})
|
||
continue
|
||
}
|
||
params[paramName] = paramValue
|
||
|
||
resp, err := s.requestYd(ctx, "QuerySimDeviceStatus", url, params)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimDeviceStatus get error %+v,url %s, params %+v", err, url, params)
|
||
items = append(items, &model.SimBaseRes[model.SimDeviceStatusItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: err.Error(),
|
||
})
|
||
continue
|
||
}
|
||
body := resp.ReadAllString()
|
||
|
||
var respData ydResp
|
||
if err = json.Unmarshal([]byte(body), &respData); err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimDeviceStatus unmarshal error %+v,url %s, params %+v, body %s", err, url, params, body)
|
||
items = append(items, &model.SimBaseRes[model.SimDeviceStatusItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: err.Error(),
|
||
})
|
||
continue
|
||
}
|
||
if respData.Status != "0" {
|
||
items = append(items, &model.SimBaseRes[model.SimDeviceStatusItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(respData.Status),
|
||
Message: respData.Message,
|
||
})
|
||
continue
|
||
}
|
||
|
||
// sim-operating-status only reports the carrier terminal power state.
|
||
// It does not provide the unified device health state, so keep the two
|
||
// dimensions separate instead of treating shutdown as device failure.
|
||
deviceStatus := consts.CardSessionStatusUnknown
|
||
operatingStatus := consts.CardSessionStatusUnknown
|
||
if len(respData.Result) > 0 {
|
||
result := respData.Result[0]
|
||
rawStatus := firstNonEmpty(
|
||
gconv.String(result["operatingStatus"]),
|
||
gconv.String(result["onOffStatus"]),
|
||
gconv.String(result["status"]),
|
||
)
|
||
operatingStatus = mapYdOperatingStatus(rawStatus)
|
||
}
|
||
items = append(items, &model.SimBaseRes[model.SimDeviceStatusItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(respData.Status),
|
||
Message: respData.Message,
|
||
ResData: model.SimDeviceStatusItem{
|
||
DeviceStatus: deviceStatus,
|
||
OperatingStatus: operatingStatus,
|
||
},
|
||
})
|
||
}
|
||
return items, nil
|
||
}
|
||
|
||
// 单卡地域限制状态查询
|
||
// https://api.iot.10086.cn/v5/ec/query/region-limit-status
|
||
// 上游返回 result 中的 regionLimitStatus/status 作为地域限制状态
|
||
func (s *sSimYD) QuerySimRegionLimitStatus(ctx context.Context, input *model.SimRegionLimitStatusInput) (model.SimRegionLimitStatusOutput, error) {
|
||
url := s.getUrl(ctx, "ec/query/region-limit-status")
|
||
|
||
type ydResp struct {
|
||
Status string `json:"status"`
|
||
Message string `json:"message"`
|
||
Result []map[string]any `json:"result"`
|
||
}
|
||
|
||
items := make(model.SimRegionLimitStatusOutput, 0, len(input.SimKeys))
|
||
for _, simKey := range input.SimKeys {
|
||
params := g.Map{
|
||
"transid": s.getTransId(ctx),
|
||
}
|
||
switch {
|
||
case strings.TrimSpace(simKey.ICCID) != "":
|
||
params["iccid"] = simKey.ICCID
|
||
case strings.TrimSpace(simKey.IMSI) != "":
|
||
params["imsi"] = simKey.IMSI
|
||
case strings.TrimSpace(simKey.MSISDN) != "":
|
||
params["msisdn"] = simKey.MSISDN
|
||
default:
|
||
items = append(items, &model.SimBaseRes[model.SimRegionLimitStatusItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: "missing iccid/imsi/msisdn",
|
||
})
|
||
continue
|
||
}
|
||
|
||
resp, err := s.requestYd(ctx, "QuerySimRegionLimitStatus", url, params)
|
||
if err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimRegionLimitStatus get error %+v,url %s, params %+v", err, url, params)
|
||
items = append(items, &model.SimBaseRes[model.SimRegionLimitStatusItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: err.Error(),
|
||
})
|
||
continue
|
||
}
|
||
body := resp.ReadAllString()
|
||
|
||
var respData ydResp
|
||
if err = json.Unmarshal([]byte(body), &respData); err != nil {
|
||
g.Log().Errorf(ctx, "QuerySimRegionLimitStatus unmarshal error %+v,url %s, params %+v, body %s", err, url, params, body)
|
||
items = append(items, &model.SimBaseRes[model.SimRegionLimitStatusItem]{
|
||
SimKey: simKey,
|
||
Code: consts.ApiStatusError,
|
||
Message: err.Error(),
|
||
})
|
||
continue
|
||
}
|
||
if respData.Status != "0" {
|
||
items = append(items, &model.SimBaseRes[model.SimRegionLimitStatusItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(respData.Status),
|
||
Message: respData.Message,
|
||
})
|
||
continue
|
||
}
|
||
|
||
regionLimitStatus := ""
|
||
if len(respData.Result) > 0 {
|
||
result := respData.Result[0]
|
||
regionLimitStatus = gconv.String(result["regionLimitStatus"])
|
||
if regionLimitStatus == "" {
|
||
regionLimitStatus = gconv.String(result["status"])
|
||
}
|
||
}
|
||
items = append(items, &model.SimBaseRes[model.SimRegionLimitStatusItem]{
|
||
SimKey: simKey,
|
||
Code: gconv.Int(respData.Status),
|
||
Message: respData.Message,
|
||
ResData: model.SimRegionLimitStatusItem{
|
||
RegionLimitStatus: regionLimitStatus,
|
||
},
|
||
})
|
||
}
|
||
return items, nil
|
||
}
|