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) } }