Files
wails-epic/internal/service/parser_service.go
hu xiaotong 1b90af57ba feat(database): 实现数据库功能并优化数据导出
- 新增数据库相关 API 和服务
- 实现数据导出功能,支持导出到 JSON 文件
- 优化数据导入流程,增加数据校验
- 新增数据库页面,展示解析数据和统计信息
- 更新捕获页面,支持导入数据到数据库
2025-07-04 12:48:40 +08:00

510 lines
13 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 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字段缺失或为空")
}
// 校验通过,直接解析数据
ps.logger.Info("远程原始数据校验通过,开始解析")
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
}