package service import ( "bytes" "context" "encoding/json" "fmt" "github.com/PuerkitoBio/goquery" v1 "github.com/go-nunu/nunu-layout-advanced/api/v1" "strings" ) type ParserService interface { GetMessage(ctx context.Context, req []byte) (string, error) ParseAlert(html string) (message string, err error) GetRuleId(ctx context.Context, htmlBytes []byte) (string, error) ParseSDKOnlineHTMLTable(htmlContent string) ([]v1.SDKInfo, error) } func NewParserService( service *Service, ) ParserService { return &parserService{ Service: service, } } type parserService struct { *Service } // 解析 alert 消息 func (s *parserService) ParseAlert(html string) (message string, err error) { doc, err := goquery.NewDocumentFromReader(strings.NewReader(html)) if err != nil { return "", err } sel := doc.Find(".alert") if sel.Length() == 0 { // 没有 .alert 元素 return "", nil } // 找到 .alert,继续提取 t := strings.TrimSpace(sel.Find("h4").Text()) full := strings.TrimSpace(sel.Text()) full = strings.TrimPrefix(full, "×") full = strings.TrimSpace(full) m := strings.TrimSpace(strings.TrimPrefix(full, t)) return m, nil } func (s *parserService) GetMessage(ctx context.Context, req []byte) (string, error) { type msg struct { Message string `json:"msg"` // 如果字段叫 msg,用 `json:"msg"` } var m msg if err := json.Unmarshal(req, &m); err != nil { return "", fmt.Errorf("解析 message 失败: %v", err) } if m.Message == "no affect row" { return "", fmt.Errorf("没有该条数据") } return m.Message, nil } func (s *parserService) GetRuleId(ctx context.Context, htmlBytes []byte) (string, error) { // 1. 把 []byte 包成 io.Reader reader := bytes.NewReader(htmlBytes) // 2. 用 goquery 解析 doc, err := goquery.NewDocumentFromReader(reader) if err != nil { return "", err } // 方法一:按位置拿(第 2 个 tr、第 2 个 td) id := doc. Find("table.table tbody tr").Eq(1). // 跳过表头行,拿第一条数据 Find("td").Eq(1).Text() // 第 2 个 td return strings.TrimSpace(id), nil } // 解析 Sdk在线情况 表格 func (s *parserService) ParseSDKOnlineHTMLTable(htmlContent string) ([]v1.SDKInfo, error) { // 创建goquery文档 doc, err := goquery.NewDocumentFromReader(strings.NewReader(htmlContent)) if err != nil { return nil, fmt.Errorf("解析HTML失败: %v", err) } var sdkInfos []v1.SDKInfo // 查找表格并解析数据行 doc.Find("table.table.table-hover tbody tr").Each(func(i int, s *goquery.Selection) { // 跳过表头行(如果有的话) if s.Find("th").Length() > 0 { return } var info v1.SDKInfo // 解析每一列的数据 s.Find("td").Each(func(j int, td *goquery.Selection) { text := strings.TrimSpace(td.Text()) // 根据列的位置分配到对应字段(跳过第一列的复选框) switch j { case 1: // 规则ID info.RuleID = text case 2: // 客户端IP info.ClientIP = text case 3: // 网关IP info.GatewayIP = text case 4: // SDK-UUID info.SDKUUID = text case 5: // 会话ID info.SessionID = text case 6: // SDK类型 info.SDKType = text case 7: // SDK版本 info.SDKVersion = text case 8: // 系统 info.System = text case 9: // 附加信息 info.ExtraInfo = text } }) // 只有当规则ID不为空时才添加记录 if info.RuleID != "" { sdkInfos = append(sdkInfos, info) } }) return sdkInfos, nil }