refactor(frontend): 重构数据解析逻辑

- 新增 StopAndParseCapture 函数,整合停止抓包和解析数据流程
-重构 ReadRawJsonFile 函数,返回解析后的 ParsedResult 对象
- 优化数据解析流程,提高代码可读性和性能
- 调整前端 CapturePage组件,使用新的解析接口
This commit is contained in:
hxt
2025-07-02 22:42:43 +08:00
parent 3af8fd6e5e
commit 7865b0da7f
9 changed files with 135 additions and 133 deletions

View File

@@ -4,12 +4,10 @@ import {DownloadOutlined, PlayCircleOutlined, SettingOutlined, StopOutlined} fro
import '../App.css';
import {
ExportData,
GetCapturedData,
GetNetworkInterfaces,
ParseData,
ReadRawJsonFile,
StartCapture,
StopCapture
StopAndParseCapture
} from '../../wailsjs/go/service/App';
import {useCaptureStore} from '../store/useCaptureStore';
@@ -146,68 +144,24 @@ function CapturePage() {
setCapturedData([]);
try {
setLoading(true);
const stopResult = await safeApiCall(
() => StopCapture(),
'停止抓包失败'
// 新接口:直接停止抓包并解析
const parsedData = await safeApiCall(
() => StopAndParseCapture(),
'停止抓包并解析数据失败'
);
if (stopResult === undefined) {
setIsCapturing(false);
console.log("解析数据:"+JSON.stringify(parsedData))
if (!parsedData || !Array.isArray((parsedData as CaptureResult).items)) {
setParsedData({ items: [], heroes: [] } as CaptureResult);
showMessage('error', '解析数据失败');
return;
}
setIsCapturing(false);
const data = await safeApiCall(
() => GetCapturedData(),
'获取抓包数据失败'
);
if (!data || data.length === 0) {
setCapturedData([]);
showMessage('warning', '未捕获到数据');
return;
}
setCapturedData(data);
if (data && data.length > 0) {
data.forEach((item, idx) => {
let hexStr = '';
if (/^[0-9a-fA-F\s]+$/.test(item)) {
hexStr = item;
} else {
hexStr = Array.from(item).map(c => c.charCodeAt(0).toString(16).padStart(2, '0')).join(' ');
}
console.log(`抓包数据[${idx}]:`, hexStr);
});
}
if (data.length > 0) {
const parsed = await safeApiCall(
() => ParseData(data),
'解析数据失败'
);
if (!parsed) {
setParsedData({ items: [], heroes: [] });
showMessage('error', '解析数据失败');
return;
}
try {
const parsedData = JSON.parse(parsed);
if (!parsedData || !Array.isArray(parsedData.items)) {
setParsedData({ items: [], heroes: [] });
showMessage('error', '解析数据失败');
return;
}
setParsedData(parsedData);
showMessage('success', '数据处理完成');
} catch (parseError) {
console.error('解析JSON失败:', parseError);
setParsedData({ items: [], heroes: [] });
showMessage('error', '解析数据失败');
}
} else {
showMessage('warning', '未捕获到数据');
}
setParsedData(parsedData as CaptureResult);
showMessage('success', '数据处理完成');
} catch (error) {
console.error('停止抓包时发生未知错误:', error);
setIsCapturing(false);
setCapturedData([]);
setParsedData({ items: [], heroes: [] });
setParsedData({ items: [], heroes: [] } as CaptureResult);
setLoading(false);
showMessage('error', '抓包失败,已重置状态');
return;
@@ -269,8 +223,7 @@ function CapturePage() {
const fetchParsedDataFromBackend = async () => {
setLoading(true);
try {
const raw = await ReadRawJsonFile();
const json = JSON.parse(raw);
const json = await ReadRawJsonFile();
console.log('已加载本地解析数据:', json);
const safeData = {
items: Array.isArray(json.items) ? json.items : [],

View File

@@ -34,6 +34,20 @@ export namespace model {
this.is_loopback = source["is_loopback"];
}
}
export class ParsedResult {
items: any[];
heroes: any[];
static createFrom(source: any = {}) {
return new ParsedResult(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.items = source["items"];
this.heroes = source["heroes"];
}
}
}

View File

@@ -12,8 +12,10 @@ export function GetNetworkInterfaces():Promise<Array<model.NetworkInterface>>;
export function ParseData(arg1:Array<string>):Promise<string>;
export function ReadRawJsonFile():Promise<string>;
export function ReadRawJsonFile():Promise<model.ParsedResult>;
export function StartCapture(arg1:string):Promise<void>;
export function StopAndParseCapture():Promise<model.ParsedResult>;
export function StopCapture():Promise<void>;

View File

@@ -30,6 +30,10 @@ export function StartCapture(arg1) {
return window['go']['service']['App']['StartCapture'](arg1);
}
export function StopAndParseCapture() {
return window['go']['service']['App']['StopAndParseCapture']();
}
export function StopCapture() {
return window['go']['service']['App']['StopCapture']();
}

View File

@@ -29,3 +29,9 @@ type CaptureStatus struct {
Status string `json:"status"`
Error string `json:"error,omitempty"`
}
// ParsedResult 解析结果
type ParsedResult struct {
Items []interface{} `json:"items"`
Heroes []interface{} `json:"heroes"`
}

View File

@@ -101,3 +101,33 @@ func (cs *CaptureService) processData(ctx context.Context) {
}
}
}
// StopAndParseCapture 停止抓包并解析数据
func (cs *CaptureService) StopAndParseCapture(parser *ParserService) (*model.ParsedResult, error) {
cs.mutex.Lock()
defer cs.mutex.Unlock()
if !cs.isCapturing {
return nil, fmt.Errorf("capture not running")
}
cs.packetCapture.Stop()
cs.isCapturing = false
cs.logger.Info("Packet capture stopped (StopAndParseCapture)")
// 处理所有收集的数据
cs.packetCapture.ProcessAllData()
// 获取抓包数据
rawData := cs.packetCapture.GetCapturedData()
if len(rawData) == 0 {
return nil, fmt.Errorf("no captured data")
}
// 解析数据
result, _, err := parser.ParseHexData(rawData)
if err != nil {
return nil, fmt.Errorf("解析数据失败: %v", err)
}
return result, nil
}

View File

@@ -2,8 +2,8 @@ package service
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"time"
"equipment-analyzer/internal/capture"
@@ -104,46 +104,29 @@ func (a *App) GetCapturedData() ([]string, error) {
// ParseData 解析数据为JSON
func (a *App) ParseData(hexDataList []string) (string, error) {
result, err := a.parserService.ParseHexData(hexDataList)
_, rawJson, err := a.parserService.ParseHexData(hexDataList)
if err != nil {
a.logger.Error("解析数据失败", "error", err)
return "", err
}
jsonData, err := json.MarshalIndent(result, "", " ")
if err != nil {
a.logger.Error("JSON序列化失败", "error", err)
return "", err
}
a.logger.Info("数据解析完成", "count", len(result.Data))
return string(jsonData), nil
return rawJson, nil
}
// ExportData 导出数据到文件
func (a *App) ExportData(hexDataList []string, filename string) error {
result, err := a.parserService.ParseHexData(hexDataList)
result, rawJson, err := a.parserService.ParseHexData(hexDataList)
if err != nil {
a.logger.Error("解析数据失败", "error", err)
return err
}
jsonData, err := json.MarshalIndent(result, "", " ")
if err != nil {
a.logger.Error("JSON序列化失败", "error", err)
return err
}
// 这里可以添加文件写入逻辑
a.logger.Info("导出数据", "filename", filename, "count", len(result.Data))
a.logger.Info("导出数据", "filename", filename, "count", len(result.Items))
// 简单示例:写入到当前目录
err = utils.WriteFile(filename, jsonData)
err = utils.WriteFile(filename, []byte(rawJson))
if err != nil {
a.logger.Error("写入文件失败", "error", err)
return err
}
return nil
}
@@ -163,6 +146,20 @@ func (a *App) getStatusMessage() string {
}
// ReadRawJsonFile 供前端调用读取output_raw.json内容
func (a *App) ReadRawJsonFile() (string, error) {
return a.parserService.ReadRawJsonFile()
func (a *App) ReadRawJsonFile() (*model.ParsedResult, error) {
data, err := ioutil.ReadFile("output_raw.json")
if err != nil {
return nil, err
}
return a.parserService.ReadRawJsonFile(string(data))
}
// StopAndParseCapture 停止抓包并解析数据,供前端调用
func (a *App) StopAndParseCapture() (*model.ParsedResult, error) {
result, err := a.captureService.StopAndParseCapture(a.parserService)
if err != nil {
a.logger.Error("停止抓包并解析数据失败", "error", err)
return nil, err
}
return result, nil
}

View File

@@ -28,19 +28,17 @@ func NewParserService(cfg *config.Config, logger *utils.Logger) *ParserService {
}
// ParseHexData 解析十六进制数据
func (ps *ParserService) ParseHexData(hexDataList []string) (*model.CaptureResult, error) {
func (ps *ParserService) ParseHexData(hexDataList []string) (*model.ParsedResult, string, error) {
if len(hexDataList) == 0 {
ps.logger.Warn("没有数据需要解析")
return &model.CaptureResult{
Data: make([]model.Equipment, 0),
Units: make([]interface{}, 0),
}, nil
return &model.ParsedResult{
Items: make([]interface{}, 0),
Heroes: make([]interface{}, 0),
}, "", nil
}
ps.logger.Info("开始远程解析数据", "count", len(hexDataList))
// 远程接口解析
//fmt.Println("开始远程解析数据", len(hexDataList))
url := "https://krivpfvxi0.execute-api.us-west-2.amazonaws.com/dev/getItems"
reqBody := map[string]interface{}{
"data": hexDataList,
@@ -55,45 +53,51 @@ func (ps *ParserService) ParseHexData(hexDataList []string) (*model.CaptureResul
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
// 直接写入本地文件
// 新校验逻辑校验data和units字段
var raw map[string]interface{}
if err := json.Unmarshal(body, &raw); err != nil {
ps.logger.Error("远程json解析失败", "error", err)
return nil, "", fmt.Errorf("远程json解析失败: %v", err)
}
// 校验data字段
dataArr, dataOk := raw["data"].([]interface{})
if !dataOk || len(dataArr) == 0 {
ps.logger.Error("远程json校验失败data字段缺失或为空")
return nil, "", fmt.Errorf("远程json校验失败data字段缺失或为空")
}
// 校验通过再写入本地文件
fileErr := ioutil.WriteFile("output_raw.json", body, 0644)
if fileErr != nil {
ps.logger.Error("写入原始json文件失败", "error", fileErr)
}
ps.logger.Info("远程原始数据已写入output_raw.json")
parsedResult, err := ps.ReadRawJsonFile(string(body))
if err != nil {
return nil, "", err
}
// 返回空集合,保证前端不报错
return &model.CaptureResult{
Data: make([]model.Equipment, 0),
Units: make([]interface{}, 0),
}, nil
return parsedResult, "", nil
} else if err != nil {
ps.logger.Error("远程解析请求失败", "error", err)
return nil, fmt.Errorf("远程解析请求失败: %v", err)
return nil, "", fmt.Errorf("远程解析请求失败: %v", err)
} else {
ps.logger.Error("远程解析响应码异常", "status", resp.StatusCode)
return nil, fmt.Errorf("远程解析响应码异常: %d", resp.StatusCode)
return nil, "", fmt.Errorf("远程解析响应码异常: %d", resp.StatusCode)
}
} else {
ps.logger.Error("远程解析请求构建失败", "error", err)
return nil, fmt.Errorf("远程解析请求构建失败: %v", err)
return nil, "", fmt.Errorf("远程解析请求构建失败: %v", err)
}
}
// ReadRawJsonFile 读取output_raw.json文件内容并进行数据转换
func (ps *ParserService) ReadRawJsonFile() (string, error) {
data, err := ioutil.ReadFile("output_raw.json")
if err != nil {
ps.logger.Error("读取output_raw.json失败", "error", err)
return "", err
}
// 解析原始JSON数据
// ReadRawJsonFile 读取rawJson内容并进行数据转换返回ParsedResult对象
func (ps *ParserService) ReadRawJsonFile(rawJson string) (*model.ParsedResult, error) {
var rawData map[string]interface{}
if err := json.Unmarshal(data, &rawData); err != nil {
if err := json.Unmarshal([]byte(rawJson), &rawData); err != nil {
ps.logger.Error("解析JSON失败", "error", err)
return "", err
return nil, err
}
// 提取装备和英雄数据
@@ -110,9 +114,6 @@ func (ps *ParserService) ReadRawJsonFile() (string, error) {
}
}
// 1. 原始装备总数
//fmt.Println("原始装备总数:", len(equips))
// 过滤有效装备 (x => !!x.f)
var validEquips []interface{}
for _, equip := range equips {
@@ -122,29 +123,24 @@ func (ps *ParserService) ReadRawJsonFile() (string, error) {
}
}
}
//fmt.Println("过滤f字段后装备数:", len(validEquips))
// 转换装备数据
convertedItems := ps.convertItemsAllWithLog(validEquips)
//fmt.Println("转换后装备数:", len(convertedItems))
// 转换英雄数据(只对最大组)
convertedHeroes := ps.convertUnits(rawUnits)
// 构建最终结果
result := map[string]interface{}{
"items": convertedItems,
"heroes": convertedHeroes,
result := &model.ParsedResult{
Items: make([]interface{}, len(convertedItems)),
Heroes: make([]interface{}, len(convertedHeroes)),
}
for i, v := range convertedItems {
result.Items[i] = v
}
for i, v := range convertedHeroes {
result.Heroes[i] = v
}
// 序列化为JSON字符串
resultJSON, err := json.Marshal(result)
if err != nil {
ps.logger.Error("序列化结果失败", "error", err)
return "", err
}
return string(resultJSON), nil
return result, nil
}
// convertItems 转换装备数据

File diff suppressed because one or more lines are too long