feat(cron): 添加角色配装信息刷新任务并优化神器数据同步功能

- 新增每5天执行一次的角色配装信息刷新任务
- 重构神器数据同步功能,优化数据处理和保存逻辑- 添加神器图片URL获取和上传逻辑
- 更新相关测试用例
This commit is contained in:
hxt
2025-07-26 16:22:12 +08:00
parent fc41c5ca73
commit 707b3cb347
6 changed files with 145 additions and 69 deletions

View File

@@ -32,43 +32,43 @@ func NewThirdPartyDataSync() *ThirdPartyDataSync {
}
func (t *ThirdPartyDataSync) SyncHeroData(ctx context.Context) error {
g.Log().Info(ctx, "开始同步英雄数据...")
util.Info(ctx, "开始同步英雄数据...")
// 示例从第三方API获取英雄数据
heroData, err := t.fetchHeroDataFromAPI(ctx)
if err != nil || heroData == nil {
g.Log().Error(ctx, "获取英雄数据失败:", err)
util.Error(ctx, "获取英雄数据失败:", err)
return err
}
// 处理并保存数据
if err := t.processAndSaveHeroData(ctx, heroData); err != nil {
g.Log().Error(ctx, "处理英雄数据失败:", err)
util.Error(ctx, "处理英雄数据失败:", err)
return err
}
g.Log().Info(ctx, "英雄数据同步完成")
util.Info(ctx, "英雄数据同步完成")
return nil
}
// 同步神器数据
func (t *ThirdPartyDataSync) SyncArtifactData(ctx context.Context) error {
g.Log().Info(ctx, "开始同步神器数据...")
util.Info(ctx, "开始同步神器数据...")
//从第三方API获取神器数据
artifactData, err := t.fetchArtifactDataFromAPI(ctx)
if err != nil {
g.Log().Error(ctx, "获取神器数据失败:", err)
util.Error(ctx, "获取神器数据失败:", err)
return err
}
// 处理并保存数据
if err := t.processAndSaveArtifactData(ctx, artifactData); err != nil {
g.Log().Error(ctx, "处理神器数据失败:", err)
util.Error(ctx, "处理神器数据失败:", err)
return err
}
g.Log().Info(ctx, "神器数据同步完成")
util.Info(ctx, "神器数据同步完成")
return nil
}
@@ -169,11 +169,16 @@ func (t *ThirdPartyDataSync) processAndSaveHeroData(ctx context.Context, data []
return fmt.Errorf("解析英雄数据到DTO失败: %v", err)
}
g.Log().Info(ctx, "解析到", len(heroes), "个英雄数据")
util.Info(ctx, "解析到", len(heroes), "个英雄数据")
// 一次性查出所有数据库英雄构建map
var dbHeroes []*entity.EpicHeroInfo
err := dao.EpicHeroInfo.Ctx(ctx).Scan(&dbHeroes)
err := dao.EpicHeroInfo.Ctx(ctx).
Fields(
dao.EpicHeroInfo.Columns().HeroCode,
dao.EpicHeroInfo.Columns().RawJson,
).
Scan(&dbHeroes)
if err != nil {
return fmt.Errorf("查询数据库英雄失败: %v", err)
}
@@ -194,7 +199,7 @@ func (t *ThirdPartyDataSync) processAndSaveHeroData(ctx context.Context, data []
// 只更新 rawJson 字段
rawJsonBytes, err := json.Marshal(hero)
if err != nil {
g.Log().Error(ctx, "序列化英雄数据失败:", err)
util.Error(ctx, "序列化英雄数据失败:", err)
continue
}
rawJson := string(rawJsonBytes)
@@ -206,7 +211,7 @@ func (t *ThirdPartyDataSync) processAndSaveHeroData(ctx context.Context, data []
}).
Update()
if err != nil {
g.Log().Error(ctx, "更新英雄rawJson失败:", err)
util.Error(ctx, "更新英雄rawJson失败:", err)
continue
}
g.Log().Debug(ctx, "更新英雄rawJson:", hero.Code)
@@ -225,7 +230,7 @@ func (t *ThirdPartyDataSync) processAndSaveHeroData(ctx context.Context, data []
ossObjectKey := fmt.Sprintf("epic/hero/images/%s.png", hero.Code)
ossUrl, err := util.DownloadAndUploadToOSS(ctx, hero.Assets.Icon, ossObjectKey)
if err != nil {
g.Log().Error(ctx, "上传英雄图片到OSS失败:", err)
util.Error(ctx, "上传英雄图片到OSS失败:", err)
return err // 直接返回,后续数据库不会插入
}
fmt.Println(ossUrl)
@@ -262,7 +267,7 @@ func (t *ThirdPartyDataSync) processAndSaveHeroData(ctx context.Context, data []
_, err = dao.EpicHeroInfo.Ctx(ctx).Data(newHero).Insert()
if err != nil {
g.Log().Error(ctx, "插入新英雄失败:", err)
util.Error(ctx, "插入新英雄失败:", err)
continue
}
g.Log().Debug(ctx, "插入新英雄:", hero.Code)
@@ -272,7 +277,7 @@ func (t *ThirdPartyDataSync) processAndSaveHeroData(ctx context.Context, data []
key := ossObjectKey
err := util.RefreshOssPresignedUrlCache(ctx, []string{key}, 24*time.Hour)
if err != nil {
g.Log().Error(ctx, "刷新新英雄图片预签名URL失败:", key, err)
util.Error(ctx, "刷新新英雄图片预签名URL失败:", key, err)
}
}
}
@@ -297,7 +302,7 @@ func (t *ThirdPartyDataSync) processAndSaveArtifactData(ctx context.Context, dat
if err := json.Unmarshal([]byte(data), &artifactMap); err != nil {
return fmt.Errorf("解析神器数据失败: %v", err)
}
g.Log().Info(ctx, "解析到", len(artifactMap), "个神器数据")
util.Info(ctx, "解析到", len(artifactMap), "个神器数据")
// 2. 查询数据库所有神器构建map
var dbArtifacts []*entity.EpicArtifactInfo
@@ -355,10 +360,10 @@ func (t *ThirdPartyDataSync) processAndSaveArtifactData(ctx context.Context, dat
dao.EpicArtifactInfo.Columns().Deleted: artifact.Deleted,
}).Update()
if err != nil {
g.Log().Error(ctx, "更新神器失败:", art.Name, err)
util.Error(ctx, "更新神器失败:", art.Name, err)
continue
}
g.Log().Info(ctx, "更新神器:", art.Name)
util.Info(ctx, "更新神器:", art.Name)
} else {
artifact.Creator = "sync"
_, err := dao.EpicArtifactInfo.Ctx(ctx).Data(g.Map{
@@ -378,10 +383,10 @@ func (t *ThirdPartyDataSync) processAndSaveArtifactData(ctx context.Context, dat
dao.EpicArtifactInfo.Columns().Deleted: artifact.Deleted,
}).Insert()
if err != nil {
g.Log().Error(ctx, "插入神器失败:", art.Name, err)
util.Error(ctx, "插入神器失败:", art.Name, err)
continue
}
g.Log().Info(ctx, "插入神器:", art.Name)
util.Info(ctx, "插入神器:", art.Name)
}
}
return nil
@@ -392,6 +397,10 @@ func (t *ThirdPartyDataSync) saveHeroData(ctx context.Context, hero *dto.ThirdPa
// 查询是否存在
var dbHero *entity.EpicHeroInfo
err := dao.EpicHeroInfo.Ctx(ctx).
Fields(
dao.EpicHeroInfo.Columns().HeroCode,
dao.EpicHeroInfo.Columns().RawJson,
).
Where(dao.EpicHeroInfo.Columns().HeroCode, hero.Code).
Scan(&dbHero)
if err != nil {
@@ -481,23 +490,23 @@ func (t *ThirdPartyDataSync) saveArtifactData(ctx context.Context, artifact *dto
// SyncAllData 同步所有数据
func (t *ThirdPartyDataSync) SyncAllData(ctx context.Context) error {
g.Log().Info(ctx, "开始同步所有第三方数据...")
util.Info(ctx, "开始同步所有第三方数据...")
// 同步英雄数据
if err := t.SyncHeroData(ctx); err != nil {
g.Log().Error(ctx, "英雄数据同步失败:", err)
util.Error(ctx, "英雄数据同步失败:", err)
// 继续同步其他数据
}
// 同步神器数据
if err := t.SyncArtifactData(ctx); err != nil {
g.Log().Error(ctx, "神器数据同步失败:", err)
util.Error(ctx, "神器数据同步失败:", err)
// 继续同步其他数据
}
// 可以继续添加其他数据类型的同步
g.Log().Info(ctx, "所有第三方数据同步完成")
util.Info(ctx, "所有第三方数据同步完成")
return nil
}
@@ -519,14 +528,14 @@ func (t *ThirdPartyDataSync) RefreshHeroSetContentByHeroInfo(ctx context.Context
g.Log().Infof(ctx, "已更新: %s", heroName)
return nil
} else {
g.Log().Error(ctx, "配装数据无效(长度<=200): %s", heroName)
util.Error(ctx, "配装数据无效(长度<=200): %s", heroName)
return fmt.Errorf("配装数据无效(长度<=200)")
}
}
// 刷新所有角色配装字段
func (t *ThirdPartyDataSync) RefreshAllHeroSetContent(ctx context.Context) error {
g.Log().Info(ctx, "开始批量刷新所有角色配装字段...")
util.Info(ctx, "开始批量刷新所有角色配装字段...")
// 1. 查询所有角色按set_update_time正序排列为空的在最前面
var heroList []*entity.EpicHeroInfo
@@ -534,7 +543,7 @@ func (t *ThirdPartyDataSync) RefreshAllHeroSetContent(ctx context.Context) error
OrderAsc("set_update_time").
Scan(&heroList)
if err != nil {
g.Log().Error(ctx, "查询epic_hero_info失败:", err)
util.Error(ctx, "查询epic_hero_info失败:", err)
return err
}
@@ -578,12 +587,12 @@ func getArtifactImageUrl(ctx context.Context, artCode string, dbArt *entity.Epic
// 从Redis获取神器爬虫数据
redisVal, err := util.RedisCache.Get(ctx, "epic7:artifacts")
if err != nil || redisVal == nil {
g.Log().Error(ctx, "获取Redis神器数据失败:", err)
util.Error(ctx, "获取Redis神器数据失败:", err)
return ""
}
var artifactArr []map[string]interface{}
if err := json.Unmarshal([]byte(redisVal.String()), &artifactArr); err != nil {
g.Log().Error(ctx, "解析Redis神器数据失败:", err)
util.Error(ctx, "解析Redis神器数据失败:", err)
return ""
}
g.Log().Debug(ctx, "Redis中共有", len(artifactArr), "个神器数据")
@@ -690,7 +699,7 @@ func getArtifactImageUrl(ctx context.Context, artCode string, dbArt *entity.Epic
if bestMatch != nil {
archSrc, ok := bestMatch["arch_src"].(string)
if !ok || archSrc == "" {
g.Log().Error(ctx, "匹配成功但arch_src为空:", bestMatchName)
util.Error(ctx, "匹配成功但arch_src为空:", bestMatchName)
return ""
}
g.Log().Debug(ctx, "开始下载并上传图片:", bestMatchName, "图片路径:", archSrc)
@@ -701,7 +710,7 @@ func getArtifactImageUrl(ctx context.Context, artCode string, dbArt *entity.Epic
ossObjectKey := fmt.Sprintf("epic/artifact/images/%s.png", artCode)
ossUrl, err := util.DownloadAndUploadToOSS(ctx, imgUrl, ossObjectKey)
if err != nil || ossUrl == "" {
g.Log().Error(ctx, "下载上传图片失败:", bestMatchName, "错误:", err)
util.Error(ctx, "下载上传图片失败:", bestMatchName, "错误:", err)
return ""
}
prefix := consts.S3Endpoint + "/" + consts.S3Bucket
@@ -753,7 +762,12 @@ func FixHeroAndArtifactNameToChinese(ctx context.Context) error {
// 2. 修复英雄表
var heroes []*entity.EpicHeroInfo
err = dao.EpicHeroInfo.Ctx(ctx).Scan(&heroes)
err = dao.EpicHeroInfo.Ctx(ctx).
Fields(
dao.EpicHeroInfo.Columns().HeroCode,
dao.EpicHeroInfo.Columns().HeroName,
).
Scan(&heroes)
if err != nil {
return fmt.Errorf("查询epic_hero_info失败: %v", err)
}