- 新增 StopAndParseCapture 函数,整合停止抓包和解析数据流程 -重构 ReadRawJsonFile 函数,返回解析后的 ParsedResult 对象 - 优化数据解析流程,提高代码可读性和性能 - 调整前端 CapturePage组件,使用新的解析接口
514 lines
13 KiB
Go
514 lines
13 KiB
Go
package service
|
||
|
||
import (
|
||
"bytes"
|
||
"encoding/json"
|
||
"equipment-analyzer/internal/config"
|
||
"equipment-analyzer/internal/model"
|
||
"equipment-analyzer/internal/parser"
|
||
"equipment-analyzer/internal/utils"
|
||
"fmt"
|
||
"io/ioutil"
|
||
"net/http"
|
||
"time"
|
||
)
|
||
|
||
type ParserService struct {
|
||
config *config.Config
|
||
logger *utils.Logger
|
||
hexParser *parser.HexParser
|
||
}
|
||
|
||
func NewParserService(cfg *config.Config, logger *utils.Logger) *ParserService {
|
||
return &ParserService{
|
||
config: cfg,
|
||
logger: logger,
|
||
hexParser: parser.NewHexParser(),
|
||
}
|
||
}
|
||
|
||
// ParseHexData 解析十六进制数据
|
||
func (ps *ParserService) ParseHexData(hexDataList []string) (*model.ParsedResult, string, error) {
|
||
if len(hexDataList) == 0 {
|
||
ps.logger.Warn("没有数据需要解析")
|
||
return &model.ParsedResult{
|
||
Items: make([]interface{}, 0),
|
||
Heroes: make([]interface{}, 0),
|
||
}, "", nil
|
||
}
|
||
|
||
ps.logger.Info("开始远程解析数据", "count", len(hexDataList))
|
||
|
||
url := "https://krivpfvxi0.execute-api.us-west-2.amazonaws.com/dev/getItems"
|
||
reqBody := map[string]interface{}{
|
||
"data": hexDataList,
|
||
}
|
||
jsonBytes, _ := json.Marshal(reqBody)
|
||
client := &http.Client{Timeout: 15 * time.Second}
|
||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonBytes))
|
||
if err == nil {
|
||
req.Header.Set("Content-Type", "application/json")
|
||
resp, err := client.Do(req)
|
||
if err == nil && resp.StatusCode == 200 {
|
||
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 parsedResult, "", nil
|
||
} else if err != nil {
|
||
ps.logger.Error("远程解析请求失败", "error", err)
|
||
return nil, "", fmt.Errorf("远程解析请求失败: %v", err)
|
||
} else {
|
||
ps.logger.Error("远程解析响应码异常", "status", resp.StatusCode)
|
||
return nil, "", fmt.Errorf("远程解析响应码异常: %d", resp.StatusCode)
|
||
}
|
||
} else {
|
||
ps.logger.Error("远程解析请求构建失败", "error", err)
|
||
return nil, "", fmt.Errorf("远程解析请求构建失败: %v", err)
|
||
}
|
||
}
|
||
|
||
// ReadRawJsonFile 读取rawJson内容并进行数据转换,返回ParsedResult对象
|
||
func (ps *ParserService) ReadRawJsonFile(rawJson string) (*model.ParsedResult, error) {
|
||
var rawData map[string]interface{}
|
||
if err := json.Unmarshal([]byte(rawJson), &rawData); err != nil {
|
||
ps.logger.Error("解析JSON失败", "error", err)
|
||
return nil, err
|
||
}
|
||
|
||
// 提取装备和英雄数据
|
||
equips, _ := rawData["data"].([]interface{})
|
||
// 修正:units 取最大长度的那组
|
||
var rawUnits []interface{}
|
||
if unitsRaw, ok := rawData["units"].([]interface{}); ok && len(unitsRaw) > 0 {
|
||
maxLen := 0
|
||
for _, u := range unitsRaw {
|
||
if arr, ok := u.([]interface{}); ok && len(arr) > maxLen {
|
||
maxLen = len(arr)
|
||
rawUnits = arr
|
||
}
|
||
}
|
||
}
|
||
|
||
// 过滤有效装备 (x => !!x.f)
|
||
var validEquips []interface{}
|
||
for _, equip := range equips {
|
||
if equipMap, ok := equip.(map[string]interface{}); ok {
|
||
if f, exists := equipMap["f"]; exists && f != nil && f != "" {
|
||
validEquips = append(validEquips, equip)
|
||
}
|
||
}
|
||
}
|
||
|
||
// 转换装备数据
|
||
convertedItems := ps.convertItemsAllWithLog(validEquips)
|
||
// 转换英雄数据(只对最大组)
|
||
convertedHeroes := ps.convertUnits(rawUnits)
|
||
|
||
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
|
||
}
|
||
|
||
return result, nil
|
||
}
|
||
|
||
// convertItems 转换装备数据
|
||
func (ps *ParserService) convertItems(rawItems []interface{}) []map[string]interface{} {
|
||
var convertedItems []map[string]interface{}
|
||
|
||
for _, rawItem := range rawItems {
|
||
if itemMap, ok := rawItem.(map[string]interface{}); ok {
|
||
convertedItem := ps.convertSingleItem(itemMap)
|
||
if convertedItem != nil {
|
||
convertedItems = append(convertedItems, convertedItem)
|
||
}
|
||
}
|
||
}
|
||
|
||
var filteredItems []map[string]interface{}
|
||
for _, item := range convertedItems {
|
||
filteredItems = append(filteredItems, item)
|
||
}
|
||
|
||
return filteredItems
|
||
}
|
||
|
||
// convertSingleItem 转换单个装备
|
||
func (ps *ParserService) convertSingleItem(item map[string]interface{}) map[string]interface{} {
|
||
converted := make(map[string]interface{})
|
||
|
||
// 复制基本字段
|
||
for key, value := range item {
|
||
converted[key] = value
|
||
}
|
||
|
||
// 转换装备类型
|
||
ps.convertGear(converted)
|
||
|
||
// 转换等级
|
||
ps.convertRank(converted)
|
||
|
||
// 转换套装
|
||
ps.convertSet(converted)
|
||
|
||
// 转换名称
|
||
ps.convertName(converted)
|
||
|
||
// 转换等级
|
||
ps.convertLevel(converted)
|
||
|
||
// 转换增强
|
||
ps.convertEnhance(converted)
|
||
|
||
// 转换主属性
|
||
ps.convertMainStat(converted)
|
||
|
||
// 转换副属性
|
||
ps.convertSubStats(converted)
|
||
|
||
// 转换ID
|
||
ps.convertId(converted)
|
||
|
||
// 转换装备ID
|
||
ps.convertEquippedId(converted)
|
||
|
||
return converted
|
||
}
|
||
|
||
// convertUnits 转换英雄数据
|
||
func (ps *ParserService) convertUnits(rawUnits []interface{}) []map[string]interface{} {
|
||
var convertedUnits []map[string]interface{}
|
||
|
||
for _, rawUnit := range rawUnits {
|
||
if unitMap, ok := rawUnit.(map[string]interface{}); ok {
|
||
if name, exists := unitMap["name"]; exists && name != nil && name != "" {
|
||
if id, exists := unitMap["id"]; exists && id != nil {
|
||
convertedUnit := make(map[string]interface{})
|
||
for key, value := range unitMap {
|
||
convertedUnit[key] = value
|
||
}
|
||
|
||
// 转换星星和觉醒
|
||
if g, exists := unitMap["g"]; exists {
|
||
convertedUnit["stars"] = g
|
||
}
|
||
if z, exists := unitMap["z"]; exists {
|
||
convertedUnit["awaken"] = z
|
||
}
|
||
|
||
convertedUnits = append(convertedUnits, convertedUnit)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return convertedUnits
|
||
}
|
||
|
||
// 转换函数实现
|
||
func (ps *ParserService) convertGear(item map[string]interface{}) {
|
||
if _, exists := item["type"]; !exists {
|
||
if code, exists := item["code"].(string); exists {
|
||
baseCode := code
|
||
if idx := len(baseCode) - 1; idx >= 0 {
|
||
gearLetter := string(baseCode[idx])
|
||
item["gear"] = gearByGearLetter[gearLetter]
|
||
}
|
||
}
|
||
} else {
|
||
if itemType, exists := item["type"].(string); exists {
|
||
item["gear"] = gearByIngameType[itemType]
|
||
}
|
||
}
|
||
}
|
||
|
||
func (ps *ParserService) convertRank(item map[string]interface{}) {
|
||
if g, exists := item["g"].(float64); exists {
|
||
rankIndex := int(g)
|
||
if rankIndex >= 0 && rankIndex < len(rankByIngameGrade) {
|
||
item["rank"] = rankByIngameGrade[rankIndex]
|
||
}
|
||
}
|
||
}
|
||
|
||
func (ps *ParserService) convertSet(item map[string]interface{}) {
|
||
if f, exists := item["f"].(string); exists {
|
||
item["set"] = setsByIngameSet[f]
|
||
}
|
||
}
|
||
|
||
func (ps *ParserService) convertName(item map[string]interface{}) {
|
||
if _, exists := item["name"]; !exists {
|
||
item["name"] = "Unknown"
|
||
}
|
||
}
|
||
|
||
func (ps *ParserService) convertLevel(item map[string]interface{}) {
|
||
if _, exists := item["level"]; !exists {
|
||
item["level"] = 0
|
||
}
|
||
}
|
||
|
||
func (ps *ParserService) convertEnhance(item map[string]interface{}) {
|
||
rank, rankExists := item["rank"].(string)
|
||
op, opExists := item["op"].([]interface{})
|
||
|
||
if rankExists && opExists {
|
||
countByRank := map[string]int{
|
||
"Normal": 5,
|
||
"Good": 6,
|
||
"Rare": 7,
|
||
"Heroic": 8,
|
||
"Epic": 9,
|
||
}
|
||
offsetByRank := map[string]int{
|
||
"Normal": 0,
|
||
"Good": 1,
|
||
"Rare": 2,
|
||
"Heroic": 3,
|
||
"Epic": 4,
|
||
}
|
||
|
||
count := countByRank[rank]
|
||
offset := offsetByRank[rank]
|
||
subsCount := len(op) - 1
|
||
if subsCount > count {
|
||
subsCount = count
|
||
}
|
||
|
||
enhance := (subsCount - offset) * 3
|
||
if enhance < 0 {
|
||
enhance = 0
|
||
}
|
||
item["enhance"] = enhance
|
||
}
|
||
}
|
||
|
||
func (ps *ParserService) convertMainStat(item map[string]interface{}) {
|
||
op, opExists := item["op"].([]interface{})
|
||
mainStatValue, mainStatExists := item["mainStatValue"].(float64)
|
||
|
||
if opExists && len(op) > 0 && mainStatExists {
|
||
if mainOp, ok := op[0].([]interface{}); ok && len(mainOp) > 0 {
|
||
if mainOpType, ok := mainOp[0].(string); ok {
|
||
mainType := statByIngameStat[mainOpType]
|
||
var mainValue float64
|
||
|
||
if ps.isFlat(mainOpType) {
|
||
mainValue = mainStatValue
|
||
} else {
|
||
mainValue = mainStatValue * 100
|
||
}
|
||
|
||
if mainValue == 0 || mainValue != mainValue { // NaN check
|
||
mainValue = 0
|
||
}
|
||
|
||
item["main"] = map[string]interface{}{
|
||
"type": mainType,
|
||
"value": mainValue,
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
func (ps *ParserService) convertSubStats(item map[string]interface{}) {
|
||
op, opExists := item["op"].([]interface{})
|
||
|
||
if !opExists || len(op) <= 1 {
|
||
item["substats"] = []interface{}{}
|
||
return
|
||
}
|
||
|
||
statAcc := make(map[string]map[string]interface{})
|
||
|
||
// 处理副属性 (从索引1开始)
|
||
for i := 1; i < len(op); i++ {
|
||
if opItem, ok := op[i].([]interface{}); ok && len(opItem) >= 2 {
|
||
opType, _ := opItem[0].(string)
|
||
opValue, _ := opItem[1].(float64)
|
||
|
||
annotation := ""
|
||
if len(opItem) > 2 {
|
||
annotation, _ = opItem[2].(string)
|
||
}
|
||
|
||
statType := statByIngameStat[opType]
|
||
var value float64
|
||
|
||
if ps.isFlat(opType) {
|
||
value = opValue
|
||
} else {
|
||
value = opValue * 100
|
||
}
|
||
|
||
if existingStat, exists := statAcc[statType]; exists {
|
||
existingStat["value"] = existingStat["value"].(float64) + value
|
||
|
||
if annotation == "c" {
|
||
existingStat["modified"] = true
|
||
} else if annotation != "u" {
|
||
rolls := existingStat["rolls"].(int) + 1
|
||
existingStat["rolls"] = rolls
|
||
existingStat["ingameRolls"] = rolls
|
||
}
|
||
} else {
|
||
rolls := 1
|
||
if annotation == "u" {
|
||
rolls = 0
|
||
}
|
||
|
||
statAcc[statType] = map[string]interface{}{
|
||
"value": value,
|
||
"rolls": rolls,
|
||
"ingameRolls": rolls,
|
||
"modified": annotation == "c",
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 转换为最终格式
|
||
var substats []interface{}
|
||
for statType, statData := range statAcc {
|
||
substat := map[string]interface{}{
|
||
"type": statType,
|
||
"value": statData["value"],
|
||
"rolls": statData["rolls"],
|
||
"ingameRolls": statData["ingameRolls"],
|
||
"modified": statData["modified"],
|
||
}
|
||
substats = append(substats, substat)
|
||
}
|
||
|
||
item["substats"] = substats
|
||
}
|
||
|
||
func (ps *ParserService) convertId(item map[string]interface{}) {
|
||
if id, exists := item["id"]; exists {
|
||
item["ingameId"] = id
|
||
}
|
||
}
|
||
|
||
func (ps *ParserService) convertEquippedId(item map[string]interface{}) {
|
||
if p, exists := item["p"]; exists {
|
||
item["ingameEquippedId"] = fmt.Sprintf("%v", p)
|
||
}
|
||
}
|
||
|
||
func (ps *ParserService) isFlat(text string) bool {
|
||
return text == "max_hp" || text == "speed" || text == "att" || text == "def"
|
||
}
|
||
|
||
// 映射常量
|
||
var (
|
||
rankByIngameGrade = []string{
|
||
"Unknown",
|
||
"Normal",
|
||
"Good",
|
||
"Rare",
|
||
"Heroic",
|
||
"Epic",
|
||
}
|
||
|
||
gearByIngameType = map[string]string{
|
||
"weapon": "Weapon",
|
||
"helm": "Helmet",
|
||
"armor": "Armor",
|
||
"neck": "Necklace",
|
||
"ring": "Ring",
|
||
"boot": "Boots",
|
||
}
|
||
|
||
gearByGearLetter = map[string]string{
|
||
"w": "Weapon",
|
||
"h": "Helmet",
|
||
"a": "Armor",
|
||
"n": "Necklace",
|
||
"r": "Ring",
|
||
"b": "Boots",
|
||
}
|
||
|
||
setsByIngameSet = map[string]string{
|
||
"set_acc": "HitSet",
|
||
"set_att": "AttackSet",
|
||
"set_coop": "UnitySet",
|
||
"set_counter": "CounterSet",
|
||
"set_cri_dmg": "DestructionSet",
|
||
"set_cri": "CriticalSet",
|
||
"set_def": "DefenseSet",
|
||
"set_immune": "ImmunitySet",
|
||
"set_max_hp": "HealthSet",
|
||
"set_penetrate": "PenetrationSet",
|
||
"set_rage": "RageSet",
|
||
"set_res": "ResistSet",
|
||
"set_revenge": "RevengeSet",
|
||
"set_scar": "InjurySet",
|
||
"set_speed": "SpeedSet",
|
||
"set_vampire": "LifestealSet",
|
||
"set_shield": "ProtectionSet",
|
||
"set_torrent": "TorrentSet",
|
||
}
|
||
|
||
statByIngameStat = map[string]string{
|
||
"att_rate": "AttackPercent",
|
||
"max_hp_rate": "HealthPercent",
|
||
"def_rate": "DefensePercent",
|
||
"att": "Attack",
|
||
"max_hp": "Health",
|
||
"def": "Defense",
|
||
"speed": "Speed",
|
||
"res": "EffectResistancePercent",
|
||
"cri": "CriticalHitChancePercent",
|
||
"cri_dmg": "CriticalHitDamagePercent",
|
||
"acc": "EffectivenessPercent",
|
||
"coop": "DualAttackChancePercent",
|
||
}
|
||
)
|
||
|
||
// 新增:不做enhance过滤的convertItems
|
||
func (ps *ParserService) convertItemsAllWithLog(rawItems []interface{}) []map[string]interface{} {
|
||
var convertedItems []map[string]interface{}
|
||
for _, rawItem := range rawItems {
|
||
if itemMap, ok := rawItem.(map[string]interface{}); ok {
|
||
convertedItem := ps.convertSingleItem(itemMap)
|
||
if convertedItem != nil {
|
||
convertedItems = append(convertedItems, convertedItem)
|
||
}
|
||
}
|
||
}
|
||
return convertedItems
|
||
}
|