Files
iotlcm/server/internal/logic/app/app_diagnosis_rate_limit.go
T

382 lines
11 KiB
Go
Raw 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 app
import (
"encoding/json"
"fmt"
"strings"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model"
)
type appYdDataDiagnosisResp struct {
Status string `json:"status"`
Message string `json:"message"`
Result []appYdDataDiagnosisResult `json:"result"`
}
type appYdDataDiagnosisResult struct {
ApnList json.RawMessage `json:"apnList"`
}
type appYdDataDiagnosisApnInfo 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 appYdDataDiagnosisPccInfo 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 appParseYdDataDiagnosisBody(body json.RawMessage) (appYdDataDiagnosisItem, error) {
var ydResp appYdDataDiagnosisResp
if err := json.Unmarshal(body, &ydResp); err != nil {
return appYdDataDiagnosisItem{}, err
}
return appBuildYdDataDiagnosisItem(ydResp.Result), nil
}
func appBuildYdDataDiagnosisItem(results []appYdDataDiagnosisResult) appYdDataDiagnosisItem {
apnInfos := make([]appYdDataDiagnosisApnInfo, 0)
for _, result := range results {
apnInfos = append(apnInfos, appDecodeYdDataDiagnosisApnInfos(result.ApnList)...)
}
if len(apnInfos) == 0 {
return appYdDataDiagnosisItem{
RateLimitStatus: "2",
RateLimitReason: "上游未返回达量限速明细",
}
}
triggered := false
reasons := make([]string, 0)
details := make([]string, 0, len(apnInfos))
apnDetails := make([]model.AppDiagnosisRateLimitApnItem, 0, len(apnInfos))
for _, apn := range apnInfos {
apnName := strings.TrimSpace(apn.ApnName)
displayApn := apnName
if displayApn == "" {
displayApn = "未命名APN"
}
if overUseText := appMapYdOverUseDetailsText(apn.OverUseDetails); overUseText != "" {
triggered = true
reasons = append(reasons, fmt.Sprintf("%s:%s", displayApn, overUseText))
}
if stateText, limited := appMapYdUsageStateText(apn.ApnServiceUsageState); limited {
triggered = true
reasons = append(reasons, fmt.Sprintf("%s:%s", displayApn, stateText))
}
pccInfos := appDecodeYdDataDiagnosisPccInfos(apn.PccList)
pccDetails := make([]model.AppDiagnosisRateLimitPccItem, 0, len(pccInfos))
pccText := make([]string, 0, len(pccInfos))
for _, pcc := range pccInfos {
pccName := appFirstNonEmpty(pcc.PccName, pcc.PccNameLower, pcc.PccServiceCode)
if pccName == "" {
pccName = "未命名PCC策略"
}
if stateText, limited := appMapYdUsageStateText(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, appMapYdPccPolicyStatusText(pcc.PccPolicyStatus), appMapYdUsageStateTextForDetail(pcc.PccServiceUsageState)))
pccDetails = append(pccDetails, model.AppDiagnosisRateLimitPccItem{
PccServiceCode: strings.TrimSpace(pcc.PccServiceCode),
PccName: appFirstNonEmpty(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, appMapYdApnStatusText(apn.ApnStatus), appMapYdUsageStateTextForDetail(apn.ApnServiceUsageState))}
if overUseText := appMapYdOverUseDetailsText(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.AppDiagnosisRateLimitApnItem{
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(appUniqueNonEmptyStrings(reasons), ";")
}
return appYdDataDiagnosisItem{
RateLimitStatus: status,
RateLimitReason: reason,
RateLimitDetail: strings.Join(appUniqueNonEmptyStrings(details), ";"),
ApnDetails: apnDetails,
}
}
func appDecodeYdDataDiagnosisApnInfos(raw json.RawMessage) []appYdDataDiagnosisApnInfo {
raw = appTrimJSONRaw(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(appTrimJSONRaw(wrapper.ApnInfo)) > 0 {
return appDecodeYdDataDiagnosisApnInfoList(wrapper.ApnInfo)
}
var item appYdDataDiagnosisApnInfo
if json.Unmarshal(raw, &item) == nil {
return []appYdDataDiagnosisApnInfo{item}
}
return nil
}
if raw[0] == '[' {
var wrappers []struct {
ApnInfo json.RawMessage `json:"apnInfo"`
}
if json.Unmarshal(raw, &wrappers) == nil {
items := make([]appYdDataDiagnosisApnInfo, 0)
for _, wrapper := range wrappers {
items = append(items, appDecodeYdDataDiagnosisApnInfoList(wrapper.ApnInfo)...)
}
if len(items) > 0 {
return items
}
}
return appDecodeYdDataDiagnosisApnInfoList(raw)
}
return nil
}
func appDecodeYdDataDiagnosisApnInfoList(raw json.RawMessage) []appYdDataDiagnosisApnInfo {
raw = appTrimJSONRaw(raw)
if len(raw) == 0 {
return nil
}
if raw[0] == '[' {
var items []appYdDataDiagnosisApnInfo
if json.Unmarshal(raw, &items) == nil && len(items) > 0 {
return items
}
}
if raw[0] == '{' {
var item appYdDataDiagnosisApnInfo
if json.Unmarshal(raw, &item) == nil {
return []appYdDataDiagnosisApnInfo{item}
}
}
return nil
}
func appDecodeYdDataDiagnosisPccInfos(raw json.RawMessage) []appYdDataDiagnosisPccInfo {
raw = appTrimJSONRaw(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(appTrimJSONRaw(wrapper.PccInfo)) > 0 {
return appDecodeYdDataDiagnosisPccInfoList(wrapper.PccInfo)
}
var item appYdDataDiagnosisPccInfo
if json.Unmarshal(raw, &item) == nil {
return []appYdDataDiagnosisPccInfo{item}
}
return nil
}
if raw[0] == '[' {
var wrappers []struct {
PccInfo json.RawMessage `json:"pccInfo"`
}
if json.Unmarshal(raw, &wrappers) == nil {
items := make([]appYdDataDiagnosisPccInfo, 0)
for _, wrapper := range wrappers {
items = append(items, appDecodeYdDataDiagnosisPccInfoList(wrapper.PccInfo)...)
}
if len(items) > 0 {
return items
}
}
return appDecodeYdDataDiagnosisPccInfoList(raw)
}
return nil
}
func appDecodeYdDataDiagnosisPccInfoList(raw json.RawMessage) []appYdDataDiagnosisPccInfo {
raw = appTrimJSONRaw(raw)
if len(raw) == 0 {
return nil
}
if raw[0] == '[' {
var items []appYdDataDiagnosisPccInfo
if json.Unmarshal(raw, &items) == nil && len(items) > 0 {
return items
}
}
if raw[0] == '{' {
var item appYdDataDiagnosisPccInfo
if json.Unmarshal(raw, &item) == nil {
return []appYdDataDiagnosisPccInfo{item}
}
}
return nil
}
func appTrimJSONRaw(raw json.RawMessage) json.RawMessage {
return json.RawMessage(strings.TrimSpace(string(raw)))
}
func appFirstNonEmpty(values ...string) string {
for _, value := range values {
if value = strings.TrimSpace(value); value != "" {
return value
}
}
return ""
}
func appUniqueNonEmptyStrings(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 appMapYdOverUseDetailsText(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(appUniqueNonEmptyStrings(texts), ";")
}
func appMapYdUsageStateText(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 appMapYdUsageStateTextForDetail(value string) string {
text, _ := appMapYdUsageStateText(value)
return text
}
func appMapYdApnStatusText(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 appMapYdPccPolicyStatusText(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)
}
}
type appYdDataDiagnosisItem struct {
RateLimitStatus string `json:"rateLimitStatus"`
RateLimitReason string `json:"rateLimitReason"`
RateLimitDetail string `json:"rateLimitDetail"`
ApnDetails []model.AppDiagnosisRateLimitApnItem `json:"apnDetails"`
}
func appDiagnosisNormalizeRateLimitBody(raw json.RawMessage) json.RawMessage {
if appDiagnosisRawString(raw, "rateLimitStatus", "rate_limit_status") != "" || len(appDiagnosisRateLimitApnDetails(raw)) > 0 {
return raw
}
var payload map[string]json.RawMessage
if json.Unmarshal(raw, &payload) != nil || len(payload["result"]) == 0 {
return raw
}
parsed, err := appParseYdDataDiagnosisBody(raw)
if err != nil || len(parsed.ApnDetails) == 0 {
return raw
}
normalized, err := json.Marshal(parsed)
if err != nil {
return raw
}
return normalized
}