Files
iotlcm/internal/logic/upstreamimpl/sim_qianxun.go
T
2026-10-29 14:59:32 +08:00

881 lines
29 KiB
Go

package upstreamimpl
import (
"bytes"
"context"
"crypto/md5"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"strings"
"time"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
"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"
)
const (
qianxunTempKey = "sim_qianxun_v3"
qianxunSuccessCode = "000000"
qianxunTimeLayout = "20060102150405"
qianxunBatchLimit = 200
)
var qianxunEast8 = time.FixedZone("UTC+8", 8*60*60)
type sSimQianxun struct {
channelConfig *model.SimChannelConfigItemCache
baseURL string
appKey string
appSecret string
userName string
http *http.Client
}
var _ model.SimCardChannelI = (*sSimQianxun)(nil)
var _ model.SimCardBatchQueryI = (*sSimQianxun)(nil)
type qianxunResponse struct {
Status string `json:"status"`
Message string `json:"message"`
Result json.RawMessage `json:"result"`
}
type qianxunCardInfo struct {
ICCID string `json:"iccid"`
IMSI string `json:"imsi"`
IMEI string `json:"imei"`
MSISDN string `json:"msisdn"`
AccessNumber string `json:"accessNumber"`
CardNo string `json:"cardNo"`
CardStatus any `json:"cardStatus"`
Status any `json:"status"`
SimStatus any `json:"simStatus"`
RealStatus any `json:"realStatus"`
StatusName string `json:"statusName"`
OpenDate string `json:"openDate"`
OpenTime string `json:"openTime"`
ActiveDate string `json:"activeDate"`
ActiveTime string `json:"activeTime"`
ActivateTime string `json:"activateTime"`
PackageName string `json:"packageName"`
MainOfferName string `json:"mainOfferName"`
ProdName string `json:"prodName"`
Remark string `json:"remark"`
UsageTotal any `json:"usageTotal"`
UsedGprs any `json:"usedGprs"`
UsedFlow any `json:"usedFlow"`
UseFlow any `json:"useFlow"`
CardFlow any `json:"cardFlow"`
Flow any `json:"flow"`
TotalFlow any `json:"totalFlow"`
ProdTotal any `json:"prodTotal"`
PackageFlow any `json:"packageFlow"`
LeftFlow any `json:"leftFlow"`
LastChangeDate string `json:"lastChangeDate"`
UpdateTime string `json:"updateTime"`
}
type qianxunFlowHistory struct {
ICCID string `json:"iccid"`
BillingMonth string `json:"billingMonth"`
UsageTotal any `json:"usageTotal"`
Status string `json:"status"`
}
type qianxunBatchCardResult struct {
ICCID string `json:"iccid"`
ICCIDData json.RawMessage `json:"iccidData"`
ICCIDResult json.RawMessage `json:"iccidResult"`
}
func NewSimQianxun() *sSimQianxun {
return &sSimQianxun{}
}
func init() {
service.UpstreamAPI().RegisterTemp(qianxunTempKey, NewSimQianxun())
}
func (s *sSimQianxun) NewTemp(ctx context.Context, channelConfig *model.SimChannelConfigItemCache) model.SimCardChannelI {
baseURL := ""
if channelConfig != nil && channelConfig.Template != nil {
var templateConfig struct {
API struct {
BaseURL string `json:"baseUrl"`
} `json:"api"`
}
if err := json.Unmarshal([]byte(channelConfig.Template.TempConfig), &templateConfig); err != nil {
g.Log().Warningf(ctx, "parse Qianxun template config failed: %v", err)
} else {
baseURL = strings.TrimRight(strings.TrimSpace(templateConfig.API.BaseURL), "/")
}
}
var channelExtra struct {
UserName string `json:"userName"`
}
if channelConfig != nil && channelConfig.Channel != nil {
if err := json.Unmarshal([]byte(channelConfig.Channel.ChanConfig), &channelExtra); err != nil {
g.Log().Warningf(ctx, "parse Qianxun channel config failed, chanKey=%s err=%v", channelConfig.Channel.ChanKey, err)
}
}
appKey := ""
appSecret := ""
if channelConfig != nil && channelConfig.Channel != nil {
appKey = strings.TrimSpace(channelConfig.Channel.AppKey)
appSecret = strings.TrimSpace(channelConfig.Channel.AppSecret)
}
return &sSimQianxun{
channelConfig: channelConfig,
baseURL: baseURL,
appKey: appKey,
appSecret: appSecret,
userName: strings.TrimSpace(channelExtra.UserName),
http: &http.Client{Timeout: resolveUpstreamHTTPTimeout(ctx, templateConfigJSON(channelConfig))},
}
}
func qianxunSign(appKey, appSecret, userName, timestamp string) string {
plain := fmt.Sprintf("appKey=%s&timeStamp=%s&userName=%s&appSecret=%s", appKey, timestamp, userName, appSecret)
sum := md5.Sum([]byte(plain))
return hex.EncodeToString(sum[:])
}
func (s *sSimQianxun) authParams() (url.Values, error) {
if s == nil {
return nil, fmt.Errorf("Qianxun client is nil")
}
if strings.TrimSpace(s.baseURL) == "" {
return nil, fmt.Errorf("Qianxun baseUrl is not configured")
}
if strings.TrimSpace(s.appKey) == "" || strings.TrimSpace(s.appSecret) == "" || strings.TrimSpace(s.userName) == "" {
return nil, fmt.Errorf("Qianxun credentials are not configured")
}
timestamp := time.Now().In(qianxunEast8).Format(qianxunTimeLayout)
params := url.Values{}
params.Set("appKey", s.appKey)
params.Set("timeStamp", timestamp)
params.Set("userName", s.userName)
params.Set("sign", qianxunSign(s.appKey, s.appSecret, s.userName, timestamp))
return params, nil
}
func (s *sSimQianxun) post(ctx context.Context, endpoint string, params url.Values) (qianxunResponse, error) {
authParams, err := s.authParams()
if err != nil {
return qianxunResponse{}, err
}
for key, values := range params {
for _, value := range values {
authParams.Add(key, value)
}
}
target := strings.TrimRight(s.baseURL, "/") + "/" + strings.TrimLeft(endpoint, "/")
body := authParams.Encode()
req, err := http.NewRequestWithContext(ctx, http.MethodPost, target, bytes.NewBufferString(body))
if err != nil {
return qianxunResponse{}, fmt.Errorf("create Qianxun request %s: %w", endpoint, err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
client := s.http
if client == nil {
client = &http.Client{Timeout: defaultUpstreamHTTPTimeout}
}
resp, err := client.Do(req)
if err != nil {
return qianxunResponse{}, fmt.Errorf("Qianxun request %s: %w", endpoint, err)
}
defer resp.Body.Close()
responseBody, err := io.ReadAll(resp.Body)
if err != nil {
return qianxunResponse{}, fmt.Errorf("read Qianxun response %s: %w", endpoint, err)
}
rawResponse := strings.TrimSpace(string(responseBody))
if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
g.Log().Warningf(ctx, "Qianxun HTTP error endpoint=%s status=%d response=%s", endpoint, resp.StatusCode, rawResponse)
return qianxunResponse{}, fmt.Errorf("Qianxun HTTP status %d", resp.StatusCode)
}
var output qianxunResponse
if err := json.Unmarshal(responseBody, &output); err != nil {
g.Log().Warningf(ctx, "decode Qianxun response failed endpoint=%s response=%s err=%v", endpoint, rawResponse, err)
return qianxunResponse{}, fmt.Errorf("decode Qianxun response %s: %w", endpoint, err)
}
if !qianxunSuccess(output) {
g.Log().Warningf(ctx, "Qianxun business error endpoint=%s status=%s message=%s response=%s", endpoint, output.Status, output.Message, rawResponse)
} else {
g.Log().Infof(ctx, "Qianxun response endpoint=%s status=%s response=%s", endpoint, output.Status, rawResponse)
}
return output, nil
}
func qianxunSuccess(resp qianxunResponse) bool {
return strings.TrimSpace(resp.Status) == qianxunSuccessCode
}
func qianxunBusinessError[T any](simKey *model.SimKey, resp qianxunResponse) *model.SimBaseRes[T] {
return &model.SimBaseRes[T]{
SimKey: simKey,
Code: gconv.Int(resp.Status),
ErrCode: resp.Status,
Message: resp.Message,
}
}
func qianxunError[T any](simKey *model.SimKey, err error) *model.SimBaseRes[T] {
return &model.SimBaseRes[T]{SimKey: simKey, Code: consts.ApiStatusError, Message: err.Error()}
}
func qianxunUnsupported[T any](simKeys []*model.SimKey, message string) []*model.SimBaseRes[T] {
items := make([]*model.SimBaseRes[T], 0, len(simKeys))
for _, simKey := range simKeys {
items = append(items, &model.SimBaseRes[T]{
SimKey: simKey,
Code: consts.ApiInvokeException,
Message: message,
})
}
return items
}
func qianxunICCID(simKey *model.SimKey) (string, error) {
if simKey == nil || strings.TrimSpace(simKey.ICCID) == "" {
return "", fmt.Errorf("ICCID not found")
}
return strings.TrimSpace(simKey.ICCID), nil
}
func decodeQianxunData[T any](raw json.RawMessage, target *T) error {
if len(raw) == 0 || string(raw) == "null" {
return fmt.Errorf("Qianxun response result is empty")
}
if err := json.Unmarshal(raw, target); err != nil {
return fmt.Errorf("decode Qianxun response result: %w", err)
}
return nil
}
func decodeQianxunStringJSON(raw json.RawMessage, target any) error {
if len(raw) == 0 || string(raw) == "null" {
return fmt.Errorf("Qianxun batch item result is empty")
}
var text string
if err := json.Unmarshal(raw, &text); err == nil {
text = strings.TrimSpace(text)
if !strings.HasPrefix(text, "{") && !strings.HasPrefix(text, "[") {
return errors.New(text)
}
return json.Unmarshal([]byte(text), target)
}
return json.Unmarshal(raw, target)
}
func qianxunCardStatus(ctx context.Context, value any) int {
status := strings.TrimSpace(gconv.String(value))
switch status {
case "":
return 0
case "0", "10":
return 2
case "1":
return 1
case "3", "4", "8":
return 4
case "6":
return 6
case "7":
return 7
case "9":
return 9
default:
g.Log().Warningf(ctx, "Unknown Qianxun card status %s from upstream", status)
return 0
}
}
func qianxunUsageMB(values ...any) float64 {
for _, value := range values {
text := strings.TrimSpace(gconv.String(value))
if text != "" && text != "<nil>" {
return parseDataUsage(text)
}
}
return 0
}
func qianxunCardMessage(resp qianxunResponse) string {
return firstNonEmpty(resp.Message, "成功")
}
func qianxunCardToBasic(data qianxunCardInfo) model.SimBasicInfoItem {
status := strconv.Itoa(qianxunCardStatus(context.Background(), qianxunStatusValue(data)))
return model.SimBasicInfoItem{
ICCID: data.ICCID,
IMSI: data.IMSI,
MSISDN: firstNonEmpty(data.MSISDN, data.AccessNumber, data.CardNo),
ActiveDate: firstNonEmpty(data.ActiveDate, data.ActiveTime, data.ActivateTime),
OpenDate: firstNonEmpty(data.OpenDate, data.OpenTime),
Remark: data.Remark,
SimStatus: status,
MainOfferName: firstNonEmpty(data.MainOfferName, data.PackageName, data.ProdName),
}
}
func qianxunStatusValue(data qianxunCardInfo) string {
return firstNonEmpty(gconv.String(data.SimStatus), gconv.String(data.CardStatus), gconv.String(data.Status), gconv.String(data.RealStatus))
}
func (s *sSimQianxun) queryCardInfo(ctx context.Context, simKey *model.SimKey) (qianxunCardInfo, qianxunResponse, error) {
iccid, err := qianxunICCID(simKey)
if err != nil {
return qianxunCardInfo{}, qianxunResponse{}, err
}
params := url.Values{}
params.Set("iccid", iccid)
resp, err := s.post(ctx, "/api/v3/cardInfo", params)
if err != nil || !qianxunSuccess(resp) {
return qianxunCardInfo{}, resp, err
}
var data qianxunCardInfo
if err := decodeQianxunData(resp.Result, &data); err != nil {
return qianxunCardInfo{}, resp, err
}
return data, resp, nil
}
func (s *sSimQianxun) QuerySimBasicInfo(ctx context.Context, input *model.SimBasicInfoInput) (model.SimBasicInfoOutput, error) {
if input == nil {
return make(model.SimBasicInfoOutput, 0), nil
}
items := make(model.SimBasicInfoOutput, 0, len(input.SimKeys))
for _, simKey := range input.SimKeys {
data, resp, err := s.queryCardInfo(ctx, simKey)
if err != nil {
items = append(items, qianxunError[model.SimBasicInfoItem](simKey, err))
continue
}
if !qianxunSuccess(resp) {
items = append(items, qianxunBusinessError[model.SimBasicInfoItem](simKey, resp))
continue
}
items = append(items, &model.SimBaseRes[model.SimBasicInfoItem]{
SimKey: simKey,
Code: consts.ApiStatusSuccess,
Message: qianxunCardMessage(resp),
ResData: qianxunCardToBasic(data),
})
}
return items, nil
}
func (s *sSimQianxun) QuerySimCardStatus(ctx context.Context, input *model.SimCardStatusInput) (model.SimCardStatusOutput, error) {
if input == nil {
return make(model.SimCardStatusOutput, 0), nil
}
items := make(model.SimCardStatusOutput, 0, len(input.SimKeys))
for _, simKey := range input.SimKeys {
data, resp, err := s.queryCardInfo(ctx, simKey)
if err != nil {
items = append(items, qianxunError[model.SimCardStatusItem](simKey, err))
continue
}
if !qianxunSuccess(resp) {
items = append(items, qianxunBusinessError[model.SimCardStatusItem](simKey, resp))
continue
}
items = append(items, &model.SimBaseRes[model.SimCardStatusItem]{
SimKey: simKey,
Code: consts.ApiStatusSuccess,
Message: qianxunCardMessage(resp),
ResData: model.SimCardStatusItem{
CardStatus: qianxunCardStatus(ctx, qianxunStatusValue(data)),
LastChangeDate: firstNonEmpty(data.LastChangeDate, data.UpdateTime),
},
})
}
return items, nil
}
func (s *sSimQianxun) QuerySimDataUsage(ctx context.Context, input *model.SimDataUsageInput) (model.SimDataUsageOutput, error) {
if input == nil {
return make(model.SimDataUsageOutput, 0), nil
}
items := make(model.SimDataUsageOutput, 0, len(input.SimKeys))
for _, simKey := range input.SimKeys {
data, resp, err := s.queryCardInfo(ctx, simKey)
if err != nil {
items = append(items, qianxunError[model.SimDataUsageItem](simKey, err))
continue
}
if !qianxunSuccess(resp) {
items = append(items, qianxunBusinessError[model.SimDataUsageItem](simKey, resp))
continue
}
item := model.SimDataUsageItem{
DataUsage: qianxunUsageMB(data.UsedGprs, data.UsageTotal, data.UsedFlow, data.UseFlow, data.CardFlow, data.Flow),
}
total := qianxunUsageMB(data.ProdTotal, data.TotalFlow, data.PackageFlow)
if total > 0 {
item.TotalFlow = &total
}
items = append(items, &model.SimBaseRes[model.SimDataUsageItem]{
SimKey: simKey,
Code: consts.ApiStatusSuccess,
Message: qianxunCardMessage(resp),
ResData: item,
})
}
return items, nil
}
func (s *sSimQianxun) QuerySimDataUsageMonthly(ctx context.Context, input *model.SimDataUsageMonthlyInput) (model.SimDataUsageMonthlyOutput, error) {
if input == nil {
return make(model.SimDataUsageMonthlyOutput, 0), nil
}
queryMonth, err := qianxunMonth(input.ReqData.QueryDate)
if err != nil {
return nil, err
}
items := make(model.SimDataUsageMonthlyOutput, 0, len(input.SimKeys))
for _, simKey := range input.SimKeys {
iccid, err := qianxunICCID(simKey)
if err != nil {
items = append(items, qianxunError[model.SimDataUsageMonthlyOutputItem](simKey, err))
continue
}
params := url.Values{}
params.Set("iccid", iccid)
params.Set("billingMonth", queryMonth)
resp, err := s.post(ctx, "/api/v3/queryFlowHistory", params)
if err != nil {
items = append(items, qianxunError[model.SimDataUsageMonthlyOutputItem](simKey, err))
continue
}
if !qianxunSuccess(resp) {
items = append(items, qianxunBusinessError[model.SimDataUsageMonthlyOutputItem](simKey, resp))
continue
}
var data qianxunFlowHistory
if err := decodeQianxunData(resp.Result, &data); err != nil {
items = append(items, qianxunError[model.SimDataUsageMonthlyOutputItem](simKey, err))
continue
}
if strings.EqualFold(strings.TrimSpace(data.Status), "N") {
items = append(items, &model.SimBaseRes[model.SimDataUsageMonthlyOutputItem]{
SimKey: simKey,
Code: consts.ApiStatusError,
Message: "Qianxun monthly usage data not synced",
ResData: model.SimDataUsageMonthlyOutputItem{DeadLine: firstNonEmpty(data.BillingMonth, queryMonth)},
})
continue
}
items = append(items, &model.SimBaseRes[model.SimDataUsageMonthlyOutputItem]{
SimKey: simKey,
Code: consts.ApiStatusSuccess,
Message: qianxunCardMessage(resp),
ResData: model.SimDataUsageMonthlyOutputItem{
DeadLine: firstNonEmpty(data.BillingMonth, queryMonth),
DataAmount: qianxunUsageMB(data.UsageTotal),
},
})
}
return items, nil
}
func qianxunMonth(queryDate string) (string, error) {
queryDate = strings.TrimSpace(queryDate)
if queryDate == "" {
return "", fmt.Errorf("queryDate is required")
}
for _, layout := range []string{"200601", "2006-01", "20060102", "2006-01-02"} {
if parsed, err := time.Parse(layout, queryDate); err == nil {
return parsed.Format("200601"), nil
}
}
return "", fmt.Errorf("invalid queryDate: %s", queryDate)
}
func (s *sSimQianxun) QuerySimCardStatusBatch(ctx context.Context, input *model.SimCardStatusInput) (model.SimCardStatusOutput, error) {
if input == nil || len(input.SimKeys) == 0 {
return make(model.SimCardStatusOutput, 0), nil
}
cards, errs := s.queryCardInfoBatch(ctx, input.SimKeys)
items := make(model.SimCardStatusOutput, 0, len(input.SimKeys))
for _, simKey := range input.SimKeys {
iccid, err := qianxunICCID(simKey)
if err != nil {
items = append(items, qianxunError[model.SimCardStatusItem](simKey, err))
continue
}
if err = errs[iccid]; err != nil {
items = append(items, qianxunError[model.SimCardStatusItem](simKey, err))
continue
}
data, ok := cards[iccid]
if !ok {
items = append(items, &model.SimBaseRes[model.SimCardStatusItem]{SimKey: simKey, Code: consts.ApiStatusError, Message: "batch response missing card"})
continue
}
items = append(items, &model.SimBaseRes[model.SimCardStatusItem]{
SimKey: simKey,
Code: consts.ApiStatusSuccess,
ResData: model.SimCardStatusItem{
CardStatus: qianxunCardStatus(ctx, qianxunStatusValue(data)),
LastChangeDate: firstNonEmpty(data.LastChangeDate, data.UpdateTime),
},
})
}
return items, nil
}
func (s *sSimQianxun) QuerySimDataUsageMonthlyBatch(ctx context.Context, input *model.SimDataUsageMonthlyInput) (model.SimDataUsageMonthlyOutput, error) {
if input == nil || len(input.SimKeys) == 0 {
return make(model.SimDataUsageMonthlyOutput, 0), nil
}
queryMonth, err := qianxunMonth(input.ReqData.QueryDate)
if err != nil {
return nil, err
}
results := make(model.SimDataUsageMonthlyOutput, len(input.SimKeys))
for _, chunk := range qianxunChunkSimKeys(input.SimKeys, qianxunBatchLimit) {
iccids := make([]string, 0, len(chunk))
for _, item := range chunk {
iccid, err := qianxunICCID(item.simKey)
if err != nil {
results[item.index] = qianxunError[model.SimDataUsageMonthlyOutputItem](item.simKey, err)
continue
}
iccids = append(iccids, iccid)
}
if len(iccids) == 0 {
continue
}
params := url.Values{}
params.Set("iccids", strings.Join(iccids, "_"))
params.Set("billingMonth", queryMonth)
resp, err := s.post(ctx, "/api/v3/batchQueryFlowHistory", params)
if err != nil {
for _, item := range chunk {
if results[item.index] == nil {
results[item.index] = qianxunError[model.SimDataUsageMonthlyOutputItem](item.simKey, err)
}
}
continue
}
if !qianxunSuccess(resp) {
for _, item := range chunk {
if results[item.index] == nil {
results[item.index] = qianxunBusinessError[model.SimDataUsageMonthlyOutputItem](item.simKey, resp)
}
}
continue
}
history, itemErrs, err := decodeQianxunBatchHistory(resp.Result)
if err != nil {
for _, item := range chunk {
if results[item.index] == nil {
results[item.index] = qianxunError[model.SimDataUsageMonthlyOutputItem](item.simKey, err)
}
}
continue
}
for _, item := range chunk {
if results[item.index] != nil {
continue
}
iccid, _ := qianxunICCID(item.simKey)
if itemErrs[iccid] != nil {
results[item.index] = qianxunError[model.SimDataUsageMonthlyOutputItem](item.simKey, itemErrs[iccid])
continue
}
data, ok := history[iccid]
if !ok {
results[item.index] = &model.SimBaseRes[model.SimDataUsageMonthlyOutputItem]{SimKey: item.simKey, Code: consts.ApiStatusError, Message: "batch response missing card", ResData: model.SimDataUsageMonthlyOutputItem{DeadLine: queryMonth}}
continue
}
if strings.EqualFold(strings.TrimSpace(data.Status), "N") {
results[item.index] = &model.SimBaseRes[model.SimDataUsageMonthlyOutputItem]{SimKey: item.simKey, Code: consts.ApiStatusError, Message: "Qianxun monthly usage data not synced", ResData: model.SimDataUsageMonthlyOutputItem{DeadLine: firstNonEmpty(data.BillingMonth, queryMonth)}}
continue
}
results[item.index] = &model.SimBaseRes[model.SimDataUsageMonthlyOutputItem]{
SimKey: item.simKey,
Code: consts.ApiStatusSuccess,
Message: qianxunCardMessage(resp),
ResData: model.SimDataUsageMonthlyOutputItem{
DeadLine: firstNonEmpty(data.BillingMonth, queryMonth),
DataAmount: qianxunUsageMB(data.UsageTotal),
},
}
}
}
return compactMonthlyBatchResults(results), nil
}
type qianxunBatchKey struct {
index int
simKey *model.SimKey
}
func qianxunChunkSimKeys(simKeys []*model.SimKey, size int) [][]qianxunBatchKey {
if size <= 0 {
size = 1
}
items := make([]qianxunBatchKey, 0, len(simKeys))
for i, simKey := range simKeys {
items = append(items, qianxunBatchKey{index: i, simKey: simKey})
}
chunks := make([][]qianxunBatchKey, 0, (len(items)+size-1)/size)
for start := 0; start < len(items); start += size {
end := start + size
if end > len(items) {
end = len(items)
}
chunks = append(chunks, items[start:end])
}
return chunks
}
func (s *sSimQianxun) queryCardInfoBatch(ctx context.Context, simKeys []*model.SimKey) (map[string]qianxunCardInfo, map[string]error) {
cards := make(map[string]qianxunCardInfo, len(simKeys))
errs := make(map[string]error, len(simKeys))
for _, chunk := range qianxunChunkSimKeys(simKeys, qianxunBatchLimit) {
iccids := make([]string, 0, len(chunk))
for _, item := range chunk {
iccid, err := qianxunICCID(item.simKey)
if err != nil {
continue
}
iccids = append(iccids, iccid)
}
if len(iccids) == 0 {
continue
}
params := url.Values{}
params.Set("iccids", strings.Join(iccids, "_"))
resp, err := s.post(ctx, "/api/v3/cardInfos", params)
if err != nil {
for _, iccid := range iccids {
errs[iccid] = err
}
continue
}
if !qianxunSuccess(resp) {
for _, iccid := range iccids {
errs[iccid] = fmt.Errorf("Qianxun batch card info failed: code=%s message=%s", resp.Status, resp.Message)
}
continue
}
batchCards, itemErrs, err := decodeQianxunBatchCards(resp.Result)
if err != nil {
for _, iccid := range iccids {
errs[iccid] = err
}
continue
}
for iccid, data := range batchCards {
cards[iccid] = data
}
for iccid, itemErr := range itemErrs {
errs[iccid] = itemErr
}
}
return cards, errs
}
func decodeQianxunBatchCards(raw json.RawMessage) (map[string]qianxunCardInfo, map[string]error, error) {
cards := make(map[string]qianxunCardInfo)
errs := make(map[string]error)
var wrapped []qianxunBatchCardResult
if err := decodeQianxunData(raw, &wrapped); err == nil && qianxunHasWrappedData(wrapped) {
for _, item := range wrapped {
iccid := strings.TrimSpace(item.ICCID)
var card qianxunCardInfo
if err := decodeQianxunStringJSON(firstQianxunRaw(item.ICCIDData, item.ICCIDResult), &card); err != nil {
errs[iccid] = err
continue
}
if strings.TrimSpace(card.ICCID) == "" {
card.ICCID = iccid
}
cards[card.ICCID] = card
}
return cards, errs, nil
}
var list []qianxunCardInfo
if err := decodeQianxunData(raw, &list); err != nil {
return nil, nil, err
}
for _, item := range list {
iccid := strings.TrimSpace(item.ICCID)
if iccid == "" {
continue
}
cards[iccid] = item
}
return cards, errs, nil
}
func decodeQianxunBatchHistory(raw json.RawMessage) (map[string]qianxunFlowHistory, map[string]error, error) {
history := make(map[string]qianxunFlowHistory)
errs := make(map[string]error)
var wrapped []qianxunBatchCardResult
if err := decodeQianxunData(raw, &wrapped); err == nil && qianxunHasWrappedData(wrapped) {
for _, item := range wrapped {
iccid := strings.TrimSpace(item.ICCID)
var data qianxunFlowHistory
if err := decodeQianxunStringJSON(firstQianxunRaw(item.ICCIDResult, item.ICCIDData), &data); err != nil {
errs[iccid] = err
continue
}
if strings.TrimSpace(data.ICCID) == "" {
data.ICCID = iccid
}
history[data.ICCID] = data
}
return history, errs, nil
}
var list []qianxunFlowHistory
if err := decodeQianxunData(raw, &list); err != nil {
return nil, nil, err
}
for _, item := range list {
iccid := strings.TrimSpace(item.ICCID)
if iccid == "" {
continue
}
history[iccid] = item
}
return history, errs, nil
}
func qianxunHasWrappedData(items []qianxunBatchCardResult) bool {
for _, item := range items {
if len(item.ICCIDData) > 0 && string(item.ICCIDData) != "null" {
return true
}
if len(item.ICCIDResult) > 0 && string(item.ICCIDResult) != "null" {
return true
}
}
return false
}
func firstQianxunRaw(values ...json.RawMessage) json.RawMessage {
for _, value := range values {
if len(value) > 0 && string(value) != "null" {
return value
}
}
return nil
}
func (s *sSimQianxun) QuerySimSession(ctx context.Context, input *model.SimSessionInput) (model.SimSessionOutput, error) {
if input == nil {
return make(model.SimSessionOutput, 0), nil
}
return qianxunUnsupported[model.SimSessionItem](input.SimKeys, "千寻通道暂不支持在线信息查询"), nil
}
func (s *sSimQianxun) QuerySimStopReason(ctx context.Context, input *model.SimStopReasonInput) (model.SimStopReasonOutput, error) {
if input == nil {
return make(model.SimStopReasonOutput, 0), nil
}
return qianxunUnsupported[model.SimStopReasonItem](input.SimKeys, "千寻通道暂不支持停机原因查询"), nil
}
func (s *sSimQianxun) QuerySimFrozenState(ctx context.Context, input *model.SimFrozenStateInput) (model.SimFrozenStateOutput, error) {
if input == nil {
return make(model.SimFrozenStateOutput, 0), nil
}
return qianxunUnsupported[model.SimFrozenStateItem](input.SimKeys, "千寻通道暂不支持冻结状态查询"), nil
}
func (s *sSimQianxun) QuerySimCardBindStatus(ctx context.Context, input *model.SimCardBindStatusInput) (model.SimCardBindStatusOutput, error) {
if input == nil {
return make(model.SimCardBindStatusOutput, 0), nil
}
return qianxunUnsupported[model.SimCardBindStatusItem](input.SimKeys, "千寻通道暂不支持机卡绑定状态查询"), nil
}
func (s *sSimQianxun) QuerySimDeviceStatus(ctx context.Context, input *model.SimDeviceStatusInput) (model.SimDeviceStatusOutput, error) {
if input == nil {
return make(model.SimDeviceStatusOutput, 0), nil
}
return qianxunUnsupported[model.SimDeviceStatusItem](input.SimKeys, "千寻通道暂不支持设备状态查询"), nil
}
func (s *sSimQianxun) QuerySimRegionLimitStatus(ctx context.Context, input *model.SimRegionLimitStatusInput) (model.SimRegionLimitStatusOutput, error) {
if input == nil {
return make(model.SimRegionLimitStatusOutput, 0), nil
}
return qianxunUnsupported[model.SimRegionLimitStatusItem](input.SimKeys, "千寻通道暂不支持地域限制状态查询"), nil
}
func (s *sSimQianxun) QuerySimImei(ctx context.Context, input *model.SimImeiInput) (model.SimImeiOutput, error) {
if input == nil {
return make(model.SimImeiOutput, 0), nil
}
return qianxunUnsupported[model.SimImeiItem](input.SimKeys, "千寻通道暂不支持IMEI查询"), nil
}
func (s *sSimQianxun) QuerySimDataDiagnosis(ctx context.Context, input *model.SimDataDiagnosisInput) (model.SimDataDiagnosisOutput, error) {
if input == nil {
return make(model.SimDataDiagnosisOutput, 0), nil
}
return qianxunUnsupported[any](input.SimKeys, "千寻通道暂不支持达量限速档位查询"), nil
}
func (s *sSimQianxun) QuerySimDataUsageDaily(ctx context.Context, input *model.SimDataUsageDailyInput) (model.SimDataUsageDailyOutput, error) {
if input == nil {
return make(model.SimDataUsageDailyOutput, 0), nil
}
return qianxunUnsupported[model.SimDataUsageDailyOutputItem](input.SimKeys, "千寻通道暂不支持单日流量查询"), nil
}
func (s *sSimQianxun) QuerySimDataUsageDailyBatch(ctx context.Context, input *model.SimDataUsageDailyInput) (model.SimDataUsageDailyOutput, error) {
return s.QuerySimDataUsageDaily(ctx, input)
}
func (s *sSimQianxun) QuerySimApnInfo(ctx context.Context, input *model.SimApnInfoInput) (model.SimApnInfoOutput, error) {
if input == nil {
return make(model.SimApnInfoOutput, 0), nil
}
return qianxunUnsupported[model.SimApnInfoItemList](input.SimKeys, "千寻通道暂不支持APN信息查询"), nil
}
func (s *sSimQianxun) ChangeSimStatus(ctx context.Context, input *model.SimCardStatusChangeInput) (model.SimCardStatusChangeOutput, error) {
if input == nil {
return make(model.SimCardStatusChangeOutput, 0), nil
}
return qianxunUnsupported[model.SimCardStatusChangeItem](input.SimKeys, "千寻通道暂不支持卡状态变更"), nil
}
func (s *sSimQianxun) ChangeSimApnFunction(ctx context.Context, input *model.SimCardOperateChangeApnInput) (model.SimCardOperateChangeApnOutput, error) {
if input == nil {
return make(model.SimCardOperateChangeApnOutput, 0), nil
}
return qianxunUnsupported[model.SimCardOperateChangeApnItem](input.SimKeys, "千寻通道暂不支持APN功能开停"), nil
}