parser.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. package service
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/json"
  6. "fmt"
  7. "github.com/PuerkitoBio/goquery"
  8. v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
  9. "strings"
  10. )
  11. type ParserService interface {
  12. GetMessage(ctx context.Context, req []byte) (string, error)
  13. ParseAlert(html string) (message string, err error)
  14. GetRuleId(ctx context.Context, htmlBytes []byte) (string, error)
  15. ParseSDKOnlineHTMLTable(htmlContent string) ([]v1.SDKInfo, error)
  16. }
  17. func NewParserService(
  18. service *Service,
  19. ) ParserService {
  20. return &parserService{
  21. Service: service,
  22. }
  23. }
  24. type parserService struct {
  25. *Service
  26. }
  27. // 解析 alert 消息
  28. func (s *parserService) ParseAlert(html string) (message string, err error) {
  29. doc, err := goquery.NewDocumentFromReader(strings.NewReader(html))
  30. if err != nil {
  31. return "", err
  32. }
  33. sel := doc.Find(".alert")
  34. if sel.Length() == 0 {
  35. // 没有 .alert 元素
  36. return "", nil
  37. }
  38. // 找到 .alert,继续提取
  39. t := strings.TrimSpace(sel.Find("h4").Text())
  40. full := strings.TrimSpace(sel.Text())
  41. full = strings.TrimPrefix(full, "×")
  42. full = strings.TrimSpace(full)
  43. m := strings.TrimSpace(strings.TrimPrefix(full, t))
  44. return m, nil
  45. }
  46. func (s *parserService) GetMessage(ctx context.Context, req []byte) (string, error) {
  47. type msg struct {
  48. Message string `json:"msg"` // 如果字段叫 msg,用 `json:"msg"`
  49. }
  50. var m msg
  51. if err := json.Unmarshal(req, &m); err != nil {
  52. return "", fmt.Errorf("解析 message 失败: %v", err)
  53. }
  54. if m.Message == "no affect row" {
  55. return "", fmt.Errorf("没有该条数据")
  56. }
  57. return m.Message, nil
  58. }
  59. func (s *parserService) GetRuleId(ctx context.Context, htmlBytes []byte) (string, error) {
  60. // 1. 把 []byte 包成 io.Reader
  61. reader := bytes.NewReader(htmlBytes)
  62. // 2. 用 goquery 解析
  63. doc, err := goquery.NewDocumentFromReader(reader)
  64. if err != nil {
  65. return "", err
  66. }
  67. // 方法一:按位置拿(第 2 个 tr、第 2 个 td)
  68. id := doc.
  69. Find("table.table tbody tr").Eq(1). // 跳过表头行,拿第一条数据
  70. Find("td").Eq(1).Text() // 第 2 个 td
  71. return strings.TrimSpace(id), nil
  72. }
  73. // 解析 Sdk在线情况 表格
  74. func (s *parserService) ParseSDKOnlineHTMLTable(htmlContent string) ([]v1.SDKInfo, error) {
  75. // 创建goquery文档
  76. doc, err := goquery.NewDocumentFromReader(strings.NewReader(htmlContent))
  77. if err != nil {
  78. return nil, fmt.Errorf("解析HTML失败: %v", err)
  79. }
  80. var sdkInfos []v1.SDKInfo
  81. // 查找表格并解析数据行
  82. doc.Find("table.table.table-hover tbody tr").Each(func(i int, s *goquery.Selection) {
  83. // 跳过表头行(如果有的话)
  84. if s.Find("th").Length() > 0 {
  85. return
  86. }
  87. var info v1.SDKInfo
  88. // 解析每一列的数据
  89. s.Find("td").Each(func(j int, td *goquery.Selection) {
  90. text := strings.TrimSpace(td.Text())
  91. // 根据列的位置分配到对应字段(跳过第一列的复选框)
  92. switch j {
  93. case 1: // 规则ID
  94. info.RuleID = text
  95. case 2: // 客户端IP
  96. info.ClientIP = text
  97. //case 3: // 网关IP
  98. // info.GatewayIP = text
  99. case 4: // SDK-UUID
  100. info.SDKUUID = text
  101. case 5: // 会话ID
  102. info.SessionID = text
  103. case 6: // SDK类型
  104. info.SDKType = text
  105. //case 7: // SDK版本
  106. // info.SDKVersion = text
  107. case 8: // 系统
  108. info.System = text
  109. case 9: // 附加信息
  110. // 对于附加信息列,提取JSON内容
  111. info.ExtraInfo = extractJSONFromExtraInfo(text)
  112. }
  113. })
  114. // 只有当规则ID不为空时才添加记录
  115. if info.RuleID != "" {
  116. sdkInfos = append(sdkInfos, info)
  117. }
  118. })
  119. return sdkInfos, nil
  120. }
  121. // extractJSONFromExtraInfo 从附加信息字符串中提取JSON内容
  122. func extractJSONFromExtraInfo(text string) string {
  123. text = strings.TrimSpace(text)
  124. // 尝试直接解析
  125. if result := tryParseJSON(text); result != "" {
  126. return result
  127. }
  128. // 尝试解析JSON字符串(去掉外层引号)
  129. if strings.HasPrefix(text, `"`) && strings.HasSuffix(text, `"`) {
  130. var jsonContent string
  131. if json.Unmarshal([]byte(text), &jsonContent) == nil {
  132. if result := tryParseJSON(jsonContent); result != "" {
  133. return result
  134. }
  135. }
  136. }
  137. // 从复杂文本中提取JSON
  138. return extractFromComplexText(text)
  139. }
  140. // 统一的JSON解析和格式化函数
  141. func tryParseJSON(text string) string {
  142. var temp interface{}
  143. if json.Unmarshal([]byte(text), &temp) == nil {
  144. if formatted, err := json.Marshal(temp); err == nil {
  145. return string(formatted)
  146. }
  147. return text
  148. }
  149. return ""
  150. }
  151. // 简化的复杂文本JSON提取
  152. func extractFromComplexText(text string) string {
  153. // 找到最后一个完整的JSON对象
  154. for end := strings.LastIndex(text, "}"); end != -1; end = strings.LastIndex(text[:end], "}") {
  155. // 向前查找匹配的开始大括号
  156. braceCount := 1
  157. for start := end - 1; start >= 0; start-- {
  158. switch text[start] {
  159. case '}':
  160. braceCount++
  161. case '{':
  162. braceCount--
  163. if braceCount == 0 {
  164. candidate := text[start : end+1]
  165. if result := tryParseJSON(candidate); result != "" {
  166. return result
  167. }
  168. break // 跳出内层循环,继续寻找下一个'}'
  169. }
  170. }
  171. }
  172. }
  173. return "查看"
  174. }