diff --git a/internal/logic/cron/cron.go b/internal/logic/cron/cron.go index ced363b..c543a9c 100644 --- a/internal/logic/cron/cron.go +++ b/internal/logic/cron/cron.go @@ -180,6 +180,13 @@ func (l *Logic) registerDefaultJobs(ctx context.Context) error { // return err //} + // 每5天执行一次角色配装信息刷新任务 + if err := l.AddJob(ctx, "hero_set_refresh_5days", "0 0 0 */5 * *", func() { + l.refreshHeroSetContent(ctx) + }); err != nil { + return err + } + return nil } @@ -196,7 +203,7 @@ func (l *Logic) syncDataFromThirdParty(ctx context.Context) { g.Log().Info(ctx, "Data sync completed") } -// syncHeroData 同步英雄数据 +//同步英雄数据 func (l *Logic) syncHeroData(ctx context.Context) { g.Log().Info(ctx, "Starting hero data sync...") @@ -284,3 +291,13 @@ func (l *Logic) refreshOssPresignUrlCacheJob(ctx context.Context) { g.Log().Info(ctx, "OSS presigned URL cache refresh completed") } } + +// 新增:定时刷新角色配装信息 +func (l *Logic) refreshHeroSetContent(ctx context.Context) { + g.Log().Info(ctx, "Starting hero set content refresh...") + if err := l.sync.RefreshAllHeroSetContent(ctx); err != nil { + g.Log().Error(ctx, "Hero set content refresh failed:", err) + return + } + g.Log().Info(ctx, "Hero set content refresh completed") +} diff --git a/internal/logic/cron/third_party_sync.go b/internal/logic/cron/third_party_sync.go index f97c5db..1c027e7 100644 --- a/internal/logic/cron/third_party_sync.go +++ b/internal/logic/cron/third_party_sync.go @@ -15,6 +15,7 @@ import ( "github.com/gogf/gf/v2/net/gclient" "github.com/gogf/gf/v2/os/gtime" "strconv" + "strings" "time" ) @@ -132,10 +133,9 @@ func (t *ThirdPartyDataSync) FetchHeroBuildsFromAPI(ctx context.Context, heroNam // 从API获取神器数据 func (t *ThirdPartyDataSync) fetchArtifactDataFromAPI(ctx context.Context) (string, error) { // 示例API地址 - apiURL := "https://static.smilegatemegaport.com/gameRecord/epic7/epic7_artifact.json?_=1729322698936" + apiURL := consts.ArtifactDataURL headers := map[string]string{ - //"User-Agent": "EpicGameBot/1.0", "Accept": "application/json", } @@ -282,27 +282,76 @@ func (t *ThirdPartyDataSync) processAndSaveHeroData(ctx context.Context, data [] // 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数组") + // 1. 解析json为map + var artifactMap map[string]struct { + Name string `json:"name"` + Rarity int `json:"rarity"` + Role string `json:"role"` + Stats struct { + Attack int `json:"attack"` + Health int `json:"health"` + Defense int `json:"defense"` + } `json:"stats"` + Code string `json:"code"` + } + if err := json.Unmarshal([]byte(data), &artifactMap); err != nil { + return fmt.Errorf("解析神器数据失败: %v", err) + } + g.Log().Info(ctx, "解析到", len(artifactMap), "个神器数据") + + // 2. 查询数据库所有神器,构建map + var dbArtifacts []*entity.EpicArtifactInfo + err := dao.EpicArtifactInfo.Ctx(ctx).Scan(&dbArtifacts) + if err != nil { + return fmt.Errorf("查询数据库神器失败: %v", err) + } + artifactDbMap := make(map[string]*entity.EpicArtifactInfo, len(dbArtifacts)) + for _, a := range dbArtifacts { + artifactDbMap[a.ArtifactCode] = a } - 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 + for _, art := range artifactMap { + var dbArt *entity.EpicArtifactInfo + if v, ok := artifactDbMap[art.Code]; ok { + dbArt = v + } + customImgUrl := getArtifactImageUrl(ctx, art.Code, dbArt) + artifact := &entity.EpicArtifactInfo{ + ArtifactName: i18n.GetZh(ctx, art.Name), + ArtifactNameEn: art.Name, + ArtifactCode: art.Code, + Rarity: strconv.Itoa(art.Rarity), + Role: art.Role, + StatsAttack: art.Stats.Attack, + StatsHealth: art.Stats.Health, + StatsDefense: art.Stats.Defense, + ImageUrl: customImgUrl, + Updater: "sync", + UpdateTime: gtime.Now(), + Deleted: false, + } + if dbArt != nil { + artifact.Id = dbArt.Id + _, err := dao.EpicArtifactInfo.Ctx(ctx). + Where(dao.EpicArtifactInfo.Columns().ArtifactCode, art.Code). + Data(artifact). + Update() + if err != nil { + g.Log().Error(ctx, "更新神器失败:", art.Name, err) + continue + } + g.Log().Info(ctx, "更新神器:", art.Name) + } else { + artifact.Creator = "sync" + artifact.CreateTime = gtime.Now() + _, err := dao.EpicArtifactInfo.Ctx(ctx).Data(artifact).Insert() + if err != nil { + g.Log().Error(ctx, "插入神器失败:", art.Name, err) + continue + } + g.Log().Info(ctx, "插入神器:", art.Name) } } - return nil } @@ -443,7 +492,7 @@ func (t *ThirdPartyDataSync) RefreshHeroSetContentByHeroInfo(ctx context.Context } } -// RefreshAllHeroSetContent 刷新所有角色配装字段 +//刷新所有角色配装字段 func (t *ThirdPartyDataSync) RefreshAllHeroSetContent(ctx context.Context) error { g.Log().Info(ctx, "开始批量刷新所有角色配装字段...") @@ -477,3 +526,20 @@ func (t *ThirdPartyDataSync) RefreshAllHeroSetContent(ctx context.Context) error g.Log().Info(ctx, "所有角色配装字段刷新完成") return nil } + +// 获取神器图片URL,已上传则复用,否则上传 +func getArtifactImageUrl(ctx context.Context, artCode string, dbArt *entity.EpicArtifactInfo) string { + if dbArt != nil && dbArt.ImageUrl != "" && strings.HasPrefix(dbArt.ImageUrl, consts.S3CustomDomain) { + return dbArt.ImageUrl + } + ossObjectKey := fmt.Sprintf("epic/artifact/images/%s.png", artCode) + ossUrl, err := util.DownloadAndUploadToOSS(ctx, "", ossObjectKey) + if err != nil || ossUrl == "" { + return "" + } + prefix := consts.S3Endpoint + "/" + consts.S3Bucket + if strings.HasPrefix(ossUrl, prefix) { + return consts.S3CustomDomain + ossUrl[len(prefix):] + } + return ossUrl +} diff --git a/internal/logic/cron/third_party_sync_test.go b/internal/logic/cron/third_party_sync_test.go index a6ab61e..e687da4 100644 --- a/internal/logic/cron/third_party_sync_test.go +++ b/internal/logic/cron/third_party_sync_test.go @@ -49,8 +49,8 @@ func TestSyncHeroData(t *testing.T) { * 测试同步神器数据 */ func TestSyncArtifactData_Success(t *testing.T) { + _ = i18n.RefreshI18n(context.Background()) sync := NewThirdPartyDataSync() - err := sync.SyncArtifactData(context.Background()) if err != nil { t.Errorf("expected success, but got error: %v", err)