Files
epic-go/internal/logic/cron/third_party_sync.go
hu xiaotong c86598e60c i18n翻译
2025-07-14 12:36:07 +08:00

302 lines
8.1 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 cron
import (
"context"
"encoding/json"
"epic/internal/consts"
"epic/internal/dao"
"epic/internal/logic/i18n"
"epic/internal/model/dto"
"epic/internal/model/entity"
"epic/internal/util"
"fmt"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/gclient"
"github.com/gogf/gf/v2/os/gtime"
"strconv"
"time"
)
// ThirdPartyDataSync 第三方数据同步器
type ThirdPartyDataSync struct {
client *gclient.Client
}
// NewThirdPartyDataSync 创建第三方数据同步器
func NewThirdPartyDataSync() *ThirdPartyDataSync {
return &ThirdPartyDataSync{
client: gclient.New().Timeout(30 * time.Second),
}
}
// SyncHeroData 同步英雄数据
func (t *ThirdPartyDataSync) SyncHeroData(ctx context.Context) error {
g.Log().Info(ctx, "开始同步英雄数据...")
// 示例从第三方API获取英雄数据
heroData, err := t.fetchHeroDataFromAPI(ctx)
if err != nil || heroData == nil {
g.Log().Error(ctx, "获取英雄数据失败:", err)
return err
}
// 处理并保存数据
if err := t.processAndSaveHeroData(ctx, heroData); err != nil {
g.Log().Error(ctx, "处理英雄数据失败:", err)
return err
}
g.Log().Info(ctx, "英雄数据同步完成")
return nil
}
// SyncArtifactData 同步神器数据
func (t *ThirdPartyDataSync) SyncArtifactData(ctx context.Context) error {
g.Log().Info(ctx, "开始同步神器数据...")
util.RedisCache.Set(ctx, "artifacts_all11", "asd", 0)
// 示例从第三方API获取神器数据
//artifactData, err := t.fetchArtifactDataFromAPI(ctx)
//if err != nil {
// g.Log().Error(ctx, "获取神器数据失败:", err)
// return err
//}
//
//// 处理并保存数据
//if err := t.processAndSaveArtifactData(ctx, artifactData); err != nil {
// g.Log().Error(ctx, "处理神器数据失败:", err)
// return err
//}
g.Log().Info(ctx, "神器数据同步完成")
return nil
}
// fetchHeroDataFromAPI 从API获取英雄数据
func (t *ThirdPartyDataSync) fetchHeroDataFromAPI(ctx context.Context) ([]byte, error) {
// 示例API地址实际使用时需要替换为真实的API
apiURL := consts.HeroListURL
// 添加请求头
headers := map[string]string{
"User-Agent": "EpicGameBot/1.0",
"Accept": "application/json",
}
// 发送GET请求
resp, err := t.client.Header(headers).Get(ctx, apiURL)
if err != nil {
return nil, fmt.Errorf("API请求失败: %v", err)
}
defer resp.Close()
// 检查响应状态
if resp.StatusCode != 200 {
return nil, fmt.Errorf("API响应错误状态码: %d", resp.StatusCode)
}
// 读取响应内容
content := resp.ReadAll()
g.Log().Debug(ctx, "API响应内容长度:", len(content))
return content, nil
}
// 从API获取神器数据
func (t *ThirdPartyDataSync) fetchArtifactDataFromAPI(ctx context.Context) (string, error) {
// 示例API地址
apiURL := "https://static.smilegatemegaport.com/gameRecord/epic7/epic7_artifact.json?_=1729322698936"
headers := map[string]string{
//"User-Agent": "EpicGameBot/1.0",
"Accept": "application/json",
}
resp, err := t.client.Header(headers).Get(ctx, apiURL)
if err != nil {
return "", fmt.Errorf("API请求失败: %v", err)
}
defer resp.Close()
if resp.StatusCode != 200 {
return "", fmt.Errorf("API响应错误状态码: %d", resp.StatusCode)
}
content := resp.ReadAll()
g.Log().Debug(ctx, "神器API响应内容长度:", len(content))
return string(content), nil
}
// processAndSaveHeroData 处理并保存英雄数据
func (t *ThirdPartyDataSync) processAndSaveHeroData(ctx context.Context, data []byte) error {
// 使用 gjson 解析
j := gjson.New(data)
if j == nil || j.IsNil() {
return fmt.Errorf("英雄数据格式错误期望是一个JSON对象")
}
// 先解析为 map[string]*ThirdPartyHeroDTO
var heroes map[string]*dto.ThirdPartyHeroDTO
if err := j.Scan(&heroes); err != nil {
return fmt.Errorf("解析英雄数据到DTO失败: %v", err)
}
g.Log().Info(ctx, "解析到", len(heroes), "个英雄数据")
// 遍历 map设置 Name 字段,并保存
for name, hero := range heroes {
hero.Name = name // 将 map 的 key 作为 Name 字段
if err := t.saveHeroData(ctx, hero); err != nil {
g.Log().Error(ctx, "保存英雄数据失败:", err)
continue
}
}
return nil
}
// processAndSaveArtifactData 处理并保存神器数据
func (t *ThirdPartyDataSync) processAndSaveArtifactData(ctx context.Context, data string) error {
// 使用 gjson 解析
j := gjson.New(data)
zhcn := j.Get("zh-CN")
// 检查json对象本身和其内部值并使用 .Var().IsSlice() 这种更可靠的方式判断是否为数组
if !zhcn.IsSlice() {
return fmt.Errorf("神器数据格式错误期望是一个JSON数组")
}
var artifacts []*dto.ThirdPartyArtifactDTO
if err := zhcn.Scan(&artifacts); err != nil {
return fmt.Errorf("解析神器数据到DTO失败: %v", err)
}
// 批量处理数据
for _, artifact := range artifacts {
if err := t.saveArtifactData(ctx, artifact); err != nil {
g.Log().Error(ctx, "保存神器数据失败:", err)
continue
}
}
return nil
}
// saveHeroData 保存单个英雄数据
func (t *ThirdPartyDataSync) saveHeroData(ctx context.Context, hero *dto.ThirdPartyHeroDTO) error {
// 查询是否存在
var dbHero *entity.EpicHeroInfo
err := dao.EpicHeroInfo.Ctx(ctx).
Where(dao.EpicHeroInfo.Columns().HeroCode, hero.Code).
Scan(&dbHero)
if err != nil {
return err
}
// 获取原始 JSON 字符串
rawJsonBytes, err := json.Marshal(hero)
if err != nil {
return err
}
rawJson := string(rawJsonBytes)
if dbHero != nil && dbHero.HeroCode != "" {
// 查到记录
if dbHero.RawJson == "" {
// 只更新 rawJson 字段
_, err = dao.EpicHeroInfo.Ctx(ctx).
Where(dao.EpicHeroInfo.Columns().HeroCode, hero.Code).
Data(g.Map{
dao.EpicHeroInfo.Columns().RawJson: rawJson,
// 可选:更新时间
dao.EpicHeroInfo.Columns().UpdateTime: gtime.Now(),
}).
Update()
if err != nil {
return err
}
g.Log().Debug(ctx, "更新英雄rawJson:", hero.Code)
}
// 已有 rawJson不做处理
} else {
status60 := hero.CalculatedStatus["Lv60SixStarFullyAwakened"]
status60json, _ := gjson.EncodeString(status60)
heroJson, _ := gjson.EncodeString(hero)
// 使用i18n服务转换字段
zhHeroName := i18n.GetZh(ctx, hero.Name)
zhRole := i18n.GetZh(ctx, hero.Role)
zhAttribute := i18n.GetZh(ctx, hero.Attribute)
newHero := &entity.EpicHeroInfo{
Id: 0,
HeroName: zhHeroName,
HeroCode: hero.Code,
HeroAttrLv60: status60json,
Creator: "",
CreateTime: nil,
Updater: "",
UpdateTime: nil,
Deleted: false,
NickName: "",
Rarity: strconv.Itoa(hero.Rarity),
Role: zhRole,
Zodiac: "",
HeadImgUrl: "",
Attribute: zhAttribute,
Remark: "",
RawJson: heroJson,
}
// 没查到,插入新记录,字段按 DO 结构体补全
_, err = dao.EpicHeroInfo.Ctx(ctx).Data(newHero).Insert()
if err != nil {
return err
}
g.Log().Debug(ctx, "插入新英雄:", hero.Code)
}
return nil
}
// 保存单个神器数据
func (t *ThirdPartyDataSync) saveArtifactData(ctx context.Context, artifact *dto.ThirdPartyArtifactDTO) error {
// TODO: 实现具体的数据库保存逻辑
// 现在 artifact 是一个强类型对象, 可以直接使用 artifact.Code, artifact.Name 等
// 示例:记录同步日志
syncLog := map[string]interface{}{
"type": "artifact_sync",
"artifact_code": artifact.Code,
"sync_time": gtime.Now(),
"status": "success",
}
g.Log().Debug(ctx, "保存神器数据:", syncLog)
return nil
}
// SyncAllData 同步所有数据
func (t *ThirdPartyDataSync) SyncAllData(ctx context.Context) error {
g.Log().Info(ctx, "开始同步所有第三方数据...")
// 同步英雄数据
if err := t.SyncHeroData(ctx); err != nil {
g.Log().Error(ctx, "英雄数据同步失败:", err)
// 继续同步其他数据
}
// 同步神器数据
if err := t.SyncArtifactData(ctx); err != nil {
g.Log().Error(ctx, "神器数据同步失败:", err)
// 继续同步其他数据
}
// 可以继续添加其他数据类型的同步
g.Log().Info(ctx, "所有第三方数据同步完成")
return nil
}