Files
iotlcm/onelink-mock/internal/service/service.go
T

747 lines
22 KiB
Go

package service
import (
"encoding/json"
"fmt"
"math"
"net/http"
"strconv"
"strings"
"time"
)
type Service struct {
store *StoreManager
operationLogs *OperationLogStore
runtimeLogs *RuntimeLogStore
}
func NewService(dataPath string, runtimeLogStores ...*RuntimeLogStore) (*Service, error) {
store, err := NewStoreManager(dataPath)
if err != nil {
return nil, err
}
runtimeLogs := NewRuntimeLogStore()
if len(runtimeLogStores) > 0 && runtimeLogStores[0] != nil {
runtimeLogs = runtimeLogStores[0]
}
return &Service{store: store, operationLogs: NewOperationLogStore(), runtimeLogs: runtimeLogs}, nil
}
func (s *Service) Router() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/healthz", s.health)
mux.HandleFunc("/api/state", s.adminState)
mux.HandleFunc("/api/appids", s.adminAppIDs)
mux.HandleFunc("/api/cards", s.adminCards)
mux.HandleFunc("/api/card-groups", s.adminCardGroups)
mux.HandleFunc("/api/cards/export", s.adminCardsExport)
mux.HandleFunc("/api/settings", s.adminSettings)
mux.HandleFunc("/api/orders", s.adminOrders)
mux.HandleFunc("/api/operation-logs", s.adminOperationLogs)
mux.HandleFunc("/api/runtime-logs", s.adminRuntimeLogs)
mux.HandleFunc("/api/sync", s.adminSync)
mux.HandleFunc("/api/generate", s.adminGenerate)
mux.HandleFunc("/sim/", s.chanAPI)
mux.HandleFunc("/v5/", s.oneLink)
mux.HandleFunc("/ec/", s.oneLink)
return withCORS(s.withOperationLogging(mux))
}
func withCORS(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusNoContent)
return
}
next.ServeHTTP(w, r)
})
}
func (s *Service) health(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, map[string]string{"status": "ok"})
}
func (s *Service) oneLink(w http.ResponseWriter, r *http.Request) {
path := strings.TrimPrefix(r.URL.Path, "/v5/")
path = strings.TrimPrefix(path, "/")
switch path {
case "ec/get/token":
s.getToken(w, r)
case "ec/query/sim-basic-info":
s.withToken(w, r, s.queryBasicInfo)
case "ec/query/sim-session":
s.withToken(w, r, s.querySession)
case "ec/query/sim-status":
s.withToken(w, r, s.queryStatus)
case "ec/query/sim-stop-reason":
s.withToken(w, r, s.queryStopReason)
case "ec/query/sim-frozen-state-info":
s.withToken(w, r, s.queryFrozenState)
case "ec/query/sim-apn-session-info":
s.withToken(w, r, s.queryAPNSession)
case "ec/query/sim-activated-features-info":
s.withToken(w, r, s.queryActivatedFeatures)
case "ec/query/sim-operating-status":
s.withToken(w, r, s.queryOperatingStatus)
case "ec/query/sim-data-diagnosis":
s.withToken(w, r, s.queryDataDiagnosis)
case "ec/query/sim-imei":
s.withToken(w, r, s.queryIMEI)
case "ec/query/sim-data-usage-inpool":
s.withToken(w, r, s.queryDataUsageInPool)
case "ec/query/sim-data-usage":
s.withToken(w, r, s.queryDataUsage)
case "ec/query/sim-data-usage-monthly/batch":
s.withToken(w, r, s.queryDataUsageMonthlyBatch)
case "ec/query/sim-data-usage-daily/batch":
s.withToken(w, r, s.queryDataUsageDailyBatch)
case "ec/query/apn-info":
s.withToken(w, r, s.queryAPNInfo)
case "ec/query/card-bind-status":
s.withToken(w, r, s.queryCardBindStatus)
case "ec/query/on-off-status":
s.withToken(w, r, s.queryOnOffStatus)
case "ec/query/region-limit-status":
s.withToken(w, r, s.queryRegionLimitStatus)
case "ec/change/sim-status":
s.withToken(w, r, s.changeSIMStatus)
case "ec/operate/sim-apn-function":
s.withToken(w, r, s.operateSIMAPNFunction)
default:
writeOneLink(w, Response{Status: "14008", Message: "查询不到信息", Result: []interface{}{}})
}
}
func (s *Service) getToken(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query()
appid := q.Get("appid")
password := q.Get("password")
refresh := q.Get("refresh")
var result Response
err := s.store.SaveMutation(func(st *Store) error {
for i := range st.AppIDs {
cred := &st.AppIDs[i]
if cred.AppID != appid || !cred.Enabled {
continue
}
if cred.Password != password {
result = Response{Status: "12022", Message: "PASSWORD 鉴权不通过", Result: []interface{}{}}
return nil
}
now := time.Now()
if refresh == "0" {
if token, ok := st.Tokens[appid]; ok && token.ExpiresAt.After(now) {
ttl := int(time.Until(token.ExpiresAt).Seconds())
result = okResp(map[string]string{"token": token.Value, "ttl": strconv.Itoa(ttl)})
return nil
}
}
token := Token{
AppID: appid,
Value: randomHex(32),
ExpiresAt: now.Add(time.Hour),
}
st.Tokens[appid] = token
cred.LastToken = token.Value
cred.TokenExpires = token.ExpiresAt.Format("2006-01-02 15:04:05")
result = okResp(map[string]string{"token": token.Value, "ttl": "3600"})
return nil
}
result = Response{Status: "12021", Message: "认证无效", Result: []interface{}{}}
return nil
})
if err != nil {
writeOneLink(w, Response{Status: "19000", Message: err.Error(), Result: []interface{}{}})
return
}
writeOneLink(w, result)
}
func (s *Service) withToken(w http.ResponseWriter, r *http.Request, next func(http.ResponseWriter, *http.Request, Store)) {
token := r.URL.Query().Get("token")
st := s.store.Snapshot()
for _, t := range st.Tokens {
if t.Value == token && t.ExpiresAt.After(time.Now()) {
next(w, r, st)
return
}
}
writeOneLink(w, Response{Status: "12021", Message: "TOKEN 不存在或已过期,请重新获取", Result: []interface{}{}})
}
func (s *Service) queryBasicInfo(w http.ResponseWriter, r *http.Request, st Store) {
card, err := findSingleCard(r, st)
if err != nil {
writeOneLink(w, paramErr(err.Error()))
return
}
writeOneLink(w, okResp(map[string]string{
"msisdn": card.MSISDN, "imsi": card.IMSI, "iccid": card.ICCID,
"activeDate": card.ActiveDate, "openDate": card.OpenDate, "remark": card.Remark,
}))
}
func (s *Service) querySession(w http.ResponseWriter, r *http.Request, st Store) {
card, err := findSingleCard(r, st)
if err != nil {
writeOneLink(w, paramErr(err.Error()))
return
}
writeOneLink(w, okResp(map[string]interface{}{
"simSessionList": []map[string]string{{
"apnId": "CMIOT", "status": card.SessionStatus, "ip": card.IP,
"ipv6Prefix": card.IPv6Prefix, "ipv6": card.IPv6,
"createDate": card.LastChangeDate, "rat": card.RAT,
}},
}))
}
func (s *Service) queryStatus(w http.ResponseWriter, r *http.Request, st Store) {
card, err := findSingleCard(r, st)
if err != nil {
writeOneLink(w, paramErr(err.Error()))
return
}
writeOneLink(w, okResp(map[string]string{"cardStatus": card.CardStatus, "lastChangeDate": card.LastChangeDate}))
}
func (s *Service) queryStopReason(w http.ResponseWriter, r *http.Request, st Store) {
card, err := findSingleCard(r, st)
if err != nil {
writeOneLink(w, paramErr(err.Error()))
return
}
stopReason := card.StopReason
if stopReason == "" {
stopReason = defaultStopReason(card.CardStatus)
}
writeOneLink(w, okResp(map[string]string{"platformType": "OneLink-CT", "stopReason": stopReason}))
}
func (s *Service) queryFrozenState(w http.ResponseWriter, r *http.Request, st Store) {
card, err := findSingleCard(r, st)
if err != nil {
writeOneLink(w, paramErr(err.Error()))
return
}
frozen := card.FrozenStatus
if frozen == "" {
frozen = "0"
}
operTime := card.FrozenOperTime
if operTime == "" {
operTime = diagnosticTime(parseCardTime(card.LastChangeDate))
}
data := map[string]string{"frozenStatus": frozen, "operTime": operTime}
if frozen == "1" && card.FrozenReason != "" {
data["frozenReason"] = card.FrozenReason
}
writeOneLink(w, okResp(data))
}
func (s *Service) queryAPNSession(w http.ResponseWriter, r *http.Request, st Store) {
card, err := findSingleCard(r, st)
if err != nil {
writeOneLink(w, paramErr(err.Error()))
return
}
items := make([]map[string]string, 0, len(card.APNs))
for _, apn := range card.APNs {
status := card.SessionStatus
if status != "00" && status != "01" {
status = "00"
}
createTime := card.SessionCreateDate
if createTime == "" {
createTime = card.LastChangeDate
}
items = append(items, map[string]string{"apn": apn.Name, "sessionStatus": status, "ipv4": card.IP, "ipv6Prefix": card.IPv6Prefix, "ipv6": card.IPv6, "createTime": createTime, "accessType": accessType(card.RAT)})
}
if len(items) == 0 {
status := card.SessionStatus
if status != "00" && status != "01" {
status = "00"
}
createTime := card.SessionCreateDate
if createTime == "" {
createTime = card.LastChangeDate
}
items = append(items, map[string]string{"apn": "CMIOT", "sessionStatus": status, "ipv4": card.IP, "ipv6Prefix": card.IPv6Prefix, "ipv6": card.IPv6, "createTime": createTime, "accessType": accessType(card.RAT)})
}
writeOneLink(w, okResp(map[string]interface{}{"infoList": items}))
}
func (s *Service) queryActivatedFeatures(w http.ResponseWriter, r *http.Request, st Store) {
card, err := findSingleCard(r, st)
if err != nil {
writeOneLink(w, paramErr(err.Error()))
return
}
items := make([]map[string]string, 0, len(card.APNs))
for _, apn := range card.APNs {
openStatus := "0"
if apn.Status == "1" {
openStatus = "1"
}
changeTime := apn.ModifyTime
if changeTime == "" {
changeTime = diagnosticTime(parseCardTime(card.LastChangeDate))
}
items = append(items, map[string]string{"functionType": "11", "openStatus": openStatus, "apn": apn.Name, "changeTime": changeTime})
}
if len(items) == 0 {
items = append(items, map[string]string{"functionType": "11", "openStatus": "1", "apn": "CMIOT", "changeTime": diagnosticTime(parseCardTime(card.LastChangeDate))})
}
writeOneLink(w, okResp(map[string]interface{}{"communicationFunctionList": items}))
}
func (s *Service) queryOperatingStatus(w http.ResponseWriter, r *http.Request, st Store) {
card, err := findSingleCard(r, st)
if err != nil {
writeOneLink(w, paramErr(err.Error()))
return
}
writeOneLink(w, okResp(map[string]string{"operatingStatus": card.OnOffStatus}))
}
func (s *Service) queryDataDiagnosis(w http.ResponseWriter, r *http.Request, st Store) {
card, err := findSingleCard(r, st)
if err != nil {
writeOneLink(w, paramErr(err.Error()))
return
}
apnInfo := make([]map[string]interface{}, 0, len(card.APNs))
for _, apn := range card.APNs {
status := apn.DiagnosisStatus
if status == "" {
status = "2"
}
modifyTime := apn.ModifyTime
if modifyTime == "" {
modifyTime = diagnosticTime(parseCardTime(card.LastChangeDate))
}
usageState := apn.ServiceUsageState
if usageState == "" {
usageState = "1"
}
item := map[string]interface{}{"apnName": apn.Name, "apnStatus": status, "modifyTime": modifyTime, "apnServiceUsageState": usageState}
if status == "4" && apn.OverUseDetails != "" {
item["overUseDetails"] = apn.OverUseDetails
}
pccInfo := make([]map[string]string, 0, len(apn.PCCs))
for _, pcc := range apn.PCCs {
pccTime := pcc.ModifyTime
if pccTime == "" {
pccTime = modifyTime
}
pccInfo = append(pccInfo, map[string]string{"pccServiceCode": pcc.ServiceCode, "pccName": pcc.Name, "pccPolicyStatus": pcc.PolicyStatus, "pccServiceUsageState": pcc.ServiceUsageState, "modifyTime": pccTime})
}
item["pccList"] = map[string]interface{}{"pccInfo": pccInfo}
apnInfo = append(apnInfo, item)
}
if len(apnInfo) == 0 {
apnInfo = append(apnInfo, map[string]interface{}{"apnName": "CMIOT", "apnStatus": "2", "modifyTime": diagnosticTime(parseCardTime(card.LastChangeDate)), "apnServiceUsageState": "1", "pccList": map[string]interface{}{"pccInfo": []map[string]string{}}})
}
writeOneLink(w, okResp(map[string]interface{}{"apnList": map[string]interface{}{"apnInfo": apnInfo}}))
}
func accessType(rat string) string {
switch strings.ToUpper(rat) {
case "5G", "51", "SA":
return "51"
case "NSA", "10":
return "10"
case "REDCAP", "58":
return "58"
case "NB", "8":
return "8"
case "2G", "2":
return "2"
case "3G", "3":
return "1"
default:
return "6"
}
}
func (s *Service) queryIMEI(w http.ResponseWriter, r *http.Request, st Store) {
card, err := findSingleCard(r, st)
if err != nil {
writeOneLink(w, paramErr(err.Error()))
return
}
writeOneLink(w, okResp(map[string]string{"imei": card.IMEI}))
}
func (s *Service) queryDataUsageInPool(w http.ResponseWriter, r *http.Request, st Store) {
card, err := findSingleCard(r, st)
if err != nil {
writeOneLink(w, paramErr(err.Error()))
return
}
apns := make([]map[string]string, 0, len(card.APNs))
for _, apn := range card.APNs {
apns = append(apns, map[string]string{"apnName": apn.Name, "apnUseAmount": fmtAmount(apn.UsageBytes)})
}
writeOneLink(w, okResp(map[string]interface{}{"apnList": apns}))
}
func (s *Service) queryDataUsage(w http.ResponseWriter, r *http.Request, st Store) {
card, err := findSingleCard(r, st)
if err != nil {
writeOneLink(w, paramErr(err.Error()))
return
}
writeOneLink(w, okResp(map[string]interface{}{
"dataAmount": fmt.Sprintf("%.2f", card.MonthUsageKB),
"apnUseAmountList": apnUseAmountList(card),
}))
}
func (s *Service) queryDataUsageMonthlyBatch(w http.ResponseWriter, r *http.Request, st Store) {
cards, key, err := findBatchCards(r, st)
if err != nil {
writeOneLink(w, paramErr(err.Error()))
return
}
items := make([]map[string]interface{}, 0, len(cards))
for _, card := range cards {
amount := card.MonthUsageKB
if q := r.URL.Query().Get("queryDate"); len(q) == 6 && q != time.Now().Format("200601") {
amount = card.MonthlyBaseKB
}
items = append(items, batchUsageItem(card, key, amount))
}
writeOneLink(w, okResp(map[string]interface{}{
"deadLine": time.Now().AddDate(0, 0, -1).Format("20060102"),
"dataAmountList": items,
}))
}
func (s *Service) queryDataUsageDailyBatch(w http.ResponseWriter, r *http.Request, st Store) {
cards, key, err := findBatchCards(r, st)
if err != nil {
writeOneLink(w, paramErr(err.Error()))
return
}
items := make([]map[string]interface{}, 0, len(cards))
for _, card := range cards {
items = append(items, batchUsageItem(card, key, card.DailyBaseKB))
}
writeOneLink(w, okResp(map[string]interface{}{"dataAmountList": items}))
}
func (s *Service) queryAPNInfo(w http.ResponseWriter, r *http.Request, st Store) {
card, err := findSingleCard(r, st)
if err != nil {
writeOneLink(w, paramErr(err.Error()))
return
}
apns := make([]map[string]string, 0, len(card.APNs))
for _, apn := range card.APNs {
apns = append(apns, map[string]string{"apnName": apn.Name, "status": apn.Status})
}
writeOneLink(w, okResp(map[string]interface{}{"apnList": apns}))
}
func (s *Service) queryCardBindStatus(w http.ResponseWriter, r *http.Request, st Store) {
if r.URL.Query().Get("msisdn") == "" || r.URL.Query().Get("iccid") != "" || r.URL.Query().Get("imsi") != "" {
writeOneLink(w, paramErr("card-bind-status requires msisdn only"))
return
}
testType := r.URL.Query().Get("testType")
if testType != "0" && testType != "1" {
writeOneLink(w, paramErr("testType must be 0 or 1"))
return
}
card, err := findSingleCard(r, st)
if err != nil {
writeOneLink(w, paramErr(err.Error()))
return
}
result := card.CardBindStatus
if result != "0" && result != "1" && result != "2" {
result = "1"
}
data := map[string]string{"result": result}
if result == "2" {
data["errorCode"] = "MOCK_BIND_CHECK_FAILED"
data["errorDes"] = "mock bind check failed"
}
writeOneLink(w, okResp(data))
}
func (s *Service) queryOnOffStatus(w http.ResponseWriter, r *http.Request, st Store) {
card, err := findSingleCard(r, st)
if err != nil {
writeOneLink(w, paramErr(err.Error()))
return
}
writeOneLink(w, okResp(map[string]string{"status": card.OnOffStatus}))
}
func (s *Service) queryRegionLimitStatus(w http.ResponseWriter, r *http.Request, st Store) {
card, err := findSingleCard(r, st)
if err != nil {
writeOneLink(w, paramErr(err.Error()))
return
}
writeOneLink(w, okResp(map[string]string{"regionLimitStatus": card.RegionLimitStatus}))
}
func (s *Service) changeSIMStatus(w http.ResponseWriter, r *http.Request, st Store) {
q := r.URL.Query()
idKey, idVal, err := singleIdentifier(q)
if err != nil {
writeOneLink(w, paramErr(err.Error()))
return
}
operType := q.Get("operType")
orderNum := "ORD" + time.Now().Format("20060102150405") + randomHex(3)
var result map[string]string
err = s.store.SaveMutation(func(store *Store) error {
idx := findCardIndex(*store, idKey, idVal)
if idx < 0 {
return fmt.Errorf("查询不到信息")
}
card := &store.Cards[idx]
card.CardStatus = statusByOperType(operType, card.CardStatus)
card.LastChangeDate = time.Now().Format("2006-01-02 15:04:05")
store.Orders = append(store.Orders, Order{
OrderNum: orderNum, MSISDN: card.MSISDN, ICCID: card.ICCID, IMSI: card.IMSI,
Type: "sim-status", OperType: operType, Status: "completed", CreatedAt: card.LastChangeDate,
})
result = map[string]string{idKey: idVal, "orderNum": orderNum}
return nil
})
if err != nil {
writeOneLink(w, paramErr(err.Error()))
return
}
writeOneLink(w, okResp(result))
}
func (s *Service) operateSIMAPNFunction(w http.ResponseWriter, r *http.Request, st Store) {
q := r.URL.Query()
idKey, idVal, err := singleIdentifier(q)
if err != nil {
writeOneLink(w, paramErr(err.Error()))
return
}
apnName := q.Get("apnName")
operType := q.Get("operType")
if apnName == "" || (operType != "0" && operType != "1") {
writeOneLink(w, paramErr("apnName 与 operType 必填"))
return
}
orderNum := "APN" + time.Now().Format("20060102150405") + randomHex(3)
var result map[string]string
err = s.store.SaveMutation(func(store *Store) error {
idx := findCardIndex(*store, idKey, idVal)
if idx < 0 {
return fmt.Errorf("查询不到信息")
}
card := &store.Cards[idx]
status := "0"
if operType == "0" {
status = "1"
}
found := false
for i := range card.APNs {
if strings.EqualFold(card.APNs[i].Name, apnName) {
card.APNs[i].Status = status
found = true
}
}
if !found {
card.APNs = append(card.APNs, APN{Name: apnName, Status: status})
}
now := time.Now().Format("2006-01-02 15:04:05")
store.Orders = append(store.Orders, Order{
OrderNum: orderNum, MSISDN: card.MSISDN, ICCID: card.ICCID, IMSI: card.IMSI,
Type: "sim-apn-function", OperType: operType, APNName: apnName, Status: "completed", CreatedAt: now,
})
result = map[string]string{idKey: idVal, "orderNum": orderNum}
return nil
})
if err != nil {
writeOneLink(w, paramErr(err.Error()))
return
}
writeOneLink(w, okResp(result))
}
func okResp(item interface{}) Response {
return Response{Status: "0", Message: "正确", Result: []interface{}{item}}
}
func paramErr(message string) Response {
if strings.Contains(message, "查询不到") {
return Response{Status: "14008", Message: "查询不到信息", Result: []interface{}{}}
}
return Response{Status: "12001", Message: message, Result: []interface{}{}}
}
func writeOneLink(w http.ResponseWriter, resp Response) {
writeJSON(w, http.StatusOK, resp)
}
func writeJSON(w http.ResponseWriter, code int, v interface{}) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(code)
_ = json.NewEncoder(w).Encode(v)
}
func singleIdentifier(q mapLike) (string, string, error) {
found := [][2]string{}
for _, key := range []string{"msisdn", "iccid", "imsi"} {
if v := q.Get(key); v != "" {
found = append(found, [2]string{key, v})
}
}
if len(found) != 1 {
return "", "", fmt.Errorf("msisdn、iccid、imsi 必须有且只有一项")
}
return found[0][0], found[0][1], nil
}
type mapLike interface {
Get(string) string
}
func findSingleCard(r *http.Request, st Store) (SIMCard, error) {
key, value, err := singleIdentifier(r.URL.Query())
if err != nil {
return SIMCard{}, err
}
for _, card := range st.Cards {
if matches(card, key, value) {
return card, nil
}
}
return SIMCard{}, fmt.Errorf("查询不到信息")
}
func findBatchCards(r *http.Request, st Store) ([]SIMCard, string, error) {
q := r.URL.Query()
keys := []string{"msisdns", "iccids", "imsis"}
foundKey := ""
foundValue := ""
for _, key := range keys {
if v := q.Get(key); v != "" {
if foundKey != "" {
return nil, "", fmt.Errorf("msisdns、iccids、imsis 必须有且只有一项")
}
foundKey = key
foundValue = v
}
}
if foundKey == "" {
return nil, "", fmt.Errorf("msisdns、iccids、imsis 必须有且只有一项")
}
singular := strings.TrimSuffix(foundKey, "s")
ids := strings.Split(foundValue, "_")
var cards []SIMCard
for _, id := range ids {
id = strings.TrimSpace(id)
for _, card := range st.Cards {
if matches(card, singular, id) {
cards = append(cards, card)
break
}
}
}
if len(cards) == 0 {
return nil, "", fmt.Errorf("查询不到信息")
}
return cards, singular, nil
}
func findCardIndex(st Store, key, value string) int {
for i, card := range st.Cards {
if matches(card, key, value) {
return i
}
}
return -1
}
func matches(card SIMCard, key, value string) bool {
switch key {
case "msisdn":
return card.MSISDN == value
case "iccid":
return card.ICCID == value
case "imsi":
return card.IMSI == value
}
return false
}
func apnUseAmountList(card SIMCard) []map[string]interface{} {
out := make([]map[string]interface{}, 0, len(card.APNs))
for _, apn := range card.APNs {
out = append(out, map[string]interface{}{
"apnName": apn.Name,
"apnUseAmount": fmtAmount(apn.UsageBytes / 1024),
"pccCodeUseAmountList": []map[string]string{{
"pccCode": "default",
"pccCodeUseAmount": fmtAmount(apn.UsageBytes / 1024),
}},
})
}
return out
}
func batchUsageItem(card SIMCard, key string, amount float64) map[string]interface{} {
item := map[string]interface{}{
key: identifierValue(card, key),
"dataAmount": fmtAmount(amount),
}
if amount > 0 {
item["apnDataAmountList"] = []map[string]string{
{"apnName": "CMIOT", "apnDataAmount": fmtAmount(amount * 0.7)},
{"apnName": "CMMTM", "apnDataAmount": fmtAmount(amount * 0.3)},
}
}
return item
}
func identifierValue(card SIMCard, key string) string {
switch key {
case "iccid":
return card.ICCID
case "imsi":
return card.IMSI
default:
return card.MSISDN
}
}
func fmtAmount(v float64) string {
if math.Abs(v-math.Round(v)) < 0.001 {
return strconv.FormatInt(int64(math.Round(v)), 10)
}
return fmt.Sprintf("%.2f", v)
}
func statusByOperType(operType, current string) string {
switch operType {
case "0":
return "4"
case "1", "2", "5", "6":
return "2"
case "3", "8":
return "7"
case "4", "9":
return "1"
default:
return current
}
}