Files
iotlcm/admin-svr/server/internal/logic/upstream/sim_data_diagnosis.go

356 lines
10 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package upstream
import (
"encoding/json"
"fmt"
"strings"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
)
type ydDataDiagnosisResp struct {
Status string `json:"status"`
Message string `json:"message"`
Result []ydDataDiagnosisResult `json:"result"`
}
type ydDataDiagnosisResult struct {
ApnList json.RawMessage `json:"apnList"`
}
type ydDataDiagnosisApnInfo struct {
ApnName string `json:"apnName"`
ApnStatus string `json:"apnStatus"`
ModifyTime string `json:"modifyTime"`
OverUseDetails string `json:"overUseDetails"`
ApnServiceUsageState string `json:"apnServiceUsageState"`
PccList json.RawMessage `json:"pccList"`
}
type ydDataDiagnosisPccInfo struct {
PccServiceCode string `json:"pccServiceCode"`
PccName string `json:"pccName"`
PccNameLower string `json:"pccname"`
PccPolicyStatus string `json:"pccPolicyStatus"`
PccServiceUsageState string `json:"pccServiceUsageState"`
ModifyTime string `json:"modifyTime"`
}
func parseYdDataDiagnosisBody(body json.RawMessage) (model.SimDataDiagnosisItem, error) {
var ydResp ydDataDiagnosisResp
if err := json.Unmarshal(body, &ydResp); err != nil {
return model.SimDataDiagnosisItem{}, err
}
return buildYdDataDiagnosisItem(ydResp.Result), nil
}
func buildYdDataDiagnosisItem(results []ydDataDiagnosisResult) model.SimDataDiagnosisItem {
apnInfos := make([]ydDataDiagnosisApnInfo, 0)
for _, result := range results {
apnInfos = append(apnInfos, decodeYdDataDiagnosisApnInfos(result.ApnList)...)
}
if len(apnInfos) == 0 {
return model.SimDataDiagnosisItem{
RateLimitStatus: "2",
RateLimitReason: "上游未返回达量限速明细",
}
}
triggered := false
reasons := make([]string, 0)
details := make([]string, 0, len(apnInfos))
apnDetails := make([]model.SimDataDiagnosisApnDetail, 0, len(apnInfos))
for _, apn := range apnInfos {
apnName := strings.TrimSpace(apn.ApnName)
displayApn := apnName
if displayApn == "" {
displayApn = "未命名APN"
}
if overUseText := mapYdOverUseDetailsText(apn.OverUseDetails); overUseText != "" {
triggered = true
reasons = append(reasons, fmt.Sprintf("%s:%s", displayApn, overUseText))
}
if stateText, limited := mapYdUsageStateText(apn.ApnServiceUsageState); limited {
triggered = true
reasons = append(reasons, fmt.Sprintf("%s:%s", displayApn, stateText))
}
pccInfos := decodeYdDataDiagnosisPccInfos(apn.PccList)
pccDetails := make([]model.SimDataDiagnosisPccDetail, 0, len(pccInfos))
pccText := make([]string, 0, len(pccInfos))
for _, pcc := range pccInfos {
pccName := firstNonEmpty(pcc.PccName, pcc.PccNameLower, pcc.PccServiceCode)
if pccName == "" {
pccName = "未命名PCC策略"
}
if stateText, limited := mapYdUsageStateText(pcc.PccServiceUsageState); limited {
triggered = true
reasons = append(reasons, fmt.Sprintf("%s/%s:%s", displayApn, pccName, stateText))
}
pccText = append(pccText, fmt.Sprintf("%s %s %s", pccName, mapYdPccPolicyStatusText(pcc.PccPolicyStatus), mapYdUsageStateTextForDetail(pcc.PccServiceUsageState)))
pccDetails = append(pccDetails, model.SimDataDiagnosisPccDetail{
PccServiceCode: strings.TrimSpace(pcc.PccServiceCode),
PccName: firstNonEmpty(pcc.PccName, pcc.PccNameLower),
PccPolicyStatus: strings.TrimSpace(pcc.PccPolicyStatus),
PccServiceUsageState: strings.TrimSpace(pcc.PccServiceUsageState),
ModifyTime: strings.TrimSpace(pcc.ModifyTime),
})
}
detailParts := []string{fmt.Sprintf("%s %s %s", displayApn, mapYdApnStatusText(apn.ApnStatus), mapYdUsageStateTextForDetail(apn.ApnServiceUsageState))}
if overUseText := mapYdOverUseDetailsText(apn.OverUseDetails); overUseText != "" {
detailParts = append(detailParts, "超停原因:"+overUseText)
}
if len(pccText) > 0 {
detailParts = append(detailParts, "PCC:"+strings.Join(pccText, ";"))
}
details = append(details, strings.Join(detailParts, ";"))
apnDetails = append(apnDetails, model.SimDataDiagnosisApnDetail{
ApnName: apnName,
ApnStatus: strings.TrimSpace(apn.ApnStatus),
ModifyTime: strings.TrimSpace(apn.ModifyTime),
OverUseDetails: strings.TrimSpace(apn.OverUseDetails),
ApnServiceUsageState: strings.TrimSpace(apn.ApnServiceUsageState),
PccDetails: pccDetails,
})
}
status := "1"
reason := "未发现达量限速或流量关停"
if triggered {
status = "0"
reason = strings.Join(uniqueNonEmptyStrings(reasons), ";")
}
return model.SimDataDiagnosisItem{
RateLimitStatus: status,
RateLimitReason: reason,
RateLimitDetail: strings.Join(uniqueNonEmptyStrings(details), ";"),
ApnDetails: apnDetails,
}
}
func decodeYdDataDiagnosisApnInfos(raw json.RawMessage) []ydDataDiagnosisApnInfo {
raw = trimJSONRaw(raw)
if len(raw) == 0 {
return nil
}
if raw[0] == '{' {
var wrapper struct {
ApnInfo json.RawMessage `json:"apnInfo"`
}
if json.Unmarshal(raw, &wrapper) == nil && len(trimJSONRaw(wrapper.ApnInfo)) > 0 {
return decodeYdDataDiagnosisApnInfoList(wrapper.ApnInfo)
}
var item ydDataDiagnosisApnInfo
if json.Unmarshal(raw, &item) == nil {
return []ydDataDiagnosisApnInfo{item}
}
return nil
}
if raw[0] == '[' {
var wrappers []struct {
ApnInfo json.RawMessage `json:"apnInfo"`
}
if json.Unmarshal(raw, &wrappers) == nil {
items := make([]ydDataDiagnosisApnInfo, 0)
for _, wrapper := range wrappers {
items = append(items, decodeYdDataDiagnosisApnInfoList(wrapper.ApnInfo)...)
}
if len(items) > 0 {
return items
}
}
return decodeYdDataDiagnosisApnInfoList(raw)
}
return nil
}
func decodeYdDataDiagnosisApnInfoList(raw json.RawMessage) []ydDataDiagnosisApnInfo {
raw = trimJSONRaw(raw)
if len(raw) == 0 {
return nil
}
if raw[0] == '[' {
var items []ydDataDiagnosisApnInfo
if json.Unmarshal(raw, &items) == nil && len(items) > 0 {
return items
}
}
if raw[0] == '{' {
var item ydDataDiagnosisApnInfo
if json.Unmarshal(raw, &item) == nil {
return []ydDataDiagnosisApnInfo{item}
}
}
return nil
}
func decodeYdDataDiagnosisPccInfos(raw json.RawMessage) []ydDataDiagnosisPccInfo {
raw = trimJSONRaw(raw)
if len(raw) == 0 {
return nil
}
if raw[0] == '{' {
var wrapper struct {
PccInfo json.RawMessage `json:"pccInfo"`
}
if json.Unmarshal(raw, &wrapper) == nil && len(trimJSONRaw(wrapper.PccInfo)) > 0 {
return decodeYdDataDiagnosisPccInfoList(wrapper.PccInfo)
}
var item ydDataDiagnosisPccInfo
if json.Unmarshal(raw, &item) == nil {
return []ydDataDiagnosisPccInfo{item}
}
return nil
}
if raw[0] == '[' {
var wrappers []struct {
PccInfo json.RawMessage `json:"pccInfo"`
}
if json.Unmarshal(raw, &wrappers) == nil {
items := make([]ydDataDiagnosisPccInfo, 0)
for _, wrapper := range wrappers {
items = append(items, decodeYdDataDiagnosisPccInfoList(wrapper.PccInfo)...)
}
if len(items) > 0 {
return items
}
}
return decodeYdDataDiagnosisPccInfoList(raw)
}
return nil
}
func decodeYdDataDiagnosisPccInfoList(raw json.RawMessage) []ydDataDiagnosisPccInfo {
raw = trimJSONRaw(raw)
if len(raw) == 0 {
return nil
}
if raw[0] == '[' {
var items []ydDataDiagnosisPccInfo
if json.Unmarshal(raw, &items) == nil && len(items) > 0 {
return items
}
}
if raw[0] == '{' {
var item ydDataDiagnosisPccInfo
if json.Unmarshal(raw, &item) == nil {
return []ydDataDiagnosisPccInfo{item}
}
}
return nil
}
func trimJSONRaw(raw json.RawMessage) json.RawMessage {
return json.RawMessage(strings.TrimSpace(string(raw)))
}
func firstNonEmpty(values ...string) string {
for _, value := range values {
if value = strings.TrimSpace(value); value != "" {
return value
}
}
return ""
}
func uniqueNonEmptyStrings(values []string) []string {
seen := make(map[string]struct{}, len(values))
result := make([]string, 0, len(values))
for _, value := range values {
value = strings.TrimSpace(value)
if value == "" {
continue
}
if _, ok := seen[value]; ok {
continue
}
seen[value] = struct{}{}
result = append(result, value)
}
return result
}
func mapYdOverUseDetailsText(value string) string {
parts := strings.FieldsFunc(strings.TrimSpace(value), func(r rune) bool {
return r == '|' || r == ',' || r == ';' || r == ',' || r == ';'
})
texts := make([]string, 0, len(parts))
for _, part := range parts {
switch strings.TrimSpace(part) {
case "12":
texts = append(texts, "流量用尽关停")
case "17":
texts = append(texts, "限额管控关停")
case "18":
texts = append(texts, "超量关停")
case "":
default:
texts = append(texts, strings.TrimSpace(part))
}
}
return strings.Join(uniqueNonEmptyStrings(texts), ";")
}
func mapYdUsageStateText(value string) (text string, limited bool) {
switch strings.TrimSpace(value) {
case "", "1":
return "正常速率", false
case "91":
return "限速至2Mbps(月初不自动恢复)", true
case "92":
return "限速至2Mbps(月初自动恢复)", true
case "95":
return "限速至1Mbps(月初不自动恢复)", true
case "96":
return "限速至1Mbps(月初自动恢复)", true
case "97":
return "达量限速2M", true
case "98":
return "达量限速1M", true
default:
value = strings.TrimSpace(value)
if value == "" {
return "", false
}
return value, value != "0"
}
}
func mapYdUsageStateTextForDetail(value string) string {
text, _ := mapYdUsageStateText(value)
return text
}
func mapYdApnStatusText(value string) string {
switch strings.TrimSpace(value) {
case "", "0", "1":
return "APN已停用"
case "2":
return "APN已激活"
case "3":
return "APN已删除"
case "4":
return "APN已暂停"
default:
return strings.TrimSpace(value)
}
}
func mapYdPccPolicyStatusText(value string) string {
switch strings.TrimSpace(value) {
case "", "0", "1":
return "策略已停用"
case "2":
return "策略已激活"
case "3":
return "策略已删除"
case "4":
return "策略已暂停"
default:
return strings.TrimSpace(value)
}
}