feat(cron): 添加角色配装信息刷新任务并优化神器数据同步功能
- 新增每5天执行一次的角色配装信息刷新任务 - 重构神器数据同步功能,优化数据处理和保存逻辑- 添加神器图片URL获取和上传逻辑 - 更新相关测试用例
This commit is contained in:
@@ -180,6 +180,13 @@ func (l *Logic) registerDefaultJobs(ctx context.Context) error {
|
|||||||
// return err
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +203,7 @@ func (l *Logic) syncDataFromThirdParty(ctx context.Context) {
|
|||||||
g.Log().Info(ctx, "Data sync completed")
|
g.Log().Info(ctx, "Data sync completed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// syncHeroData 同步英雄数据
|
//同步英雄数据
|
||||||
func (l *Logic) syncHeroData(ctx context.Context) {
|
func (l *Logic) syncHeroData(ctx context.Context) {
|
||||||
g.Log().Info(ctx, "Starting hero data sync...")
|
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")
|
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")
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/gogf/gf/v2/net/gclient"
|
"github.com/gogf/gf/v2/net/gclient"
|
||||||
"github.com/gogf/gf/v2/os/gtime"
|
"github.com/gogf/gf/v2/os/gtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -132,10 +133,9 @@ func (t *ThirdPartyDataSync) FetchHeroBuildsFromAPI(ctx context.Context, heroNam
|
|||||||
// 从API获取神器数据
|
// 从API获取神器数据
|
||||||
func (t *ThirdPartyDataSync) fetchArtifactDataFromAPI(ctx context.Context) (string, error) {
|
func (t *ThirdPartyDataSync) fetchArtifactDataFromAPI(ctx context.Context) (string, error) {
|
||||||
// 示例API地址
|
// 示例API地址
|
||||||
apiURL := "https://static.smilegatemegaport.com/gameRecord/epic7/epic7_artifact.json?_=1729322698936"
|
apiURL := consts.ArtifactDataURL
|
||||||
|
|
||||||
headers := map[string]string{
|
headers := map[string]string{
|
||||||
//"User-Agent": "EpicGameBot/1.0",
|
|
||||||
"Accept": "application/json",
|
"Accept": "application/json",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,27 +282,76 @@ func (t *ThirdPartyDataSync) processAndSaveHeroData(ctx context.Context, data []
|
|||||||
|
|
||||||
// processAndSaveArtifactData 处理并保存神器数据
|
// processAndSaveArtifactData 处理并保存神器数据
|
||||||
func (t *ThirdPartyDataSync) processAndSaveArtifactData(ctx context.Context, data string) error {
|
func (t *ThirdPartyDataSync) processAndSaveArtifactData(ctx context.Context, data string) error {
|
||||||
// 使用 gjson 解析
|
// 1. 解析json为map
|
||||||
j := gjson.New(data)
|
var artifactMap map[string]struct {
|
||||||
zhcn := j.Get("zh-CN")
|
Name string `json:"name"`
|
||||||
// 检查json对象本身和其内部值,并使用 .Var().IsSlice() 这种更可靠的方式判断是否为数组
|
Rarity int `json:"rarity"`
|
||||||
if !zhcn.IsSlice() {
|
Role string `json:"role"`
|
||||||
return fmt.Errorf("神器数据格式错误,期望是一个JSON数组")
|
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
|
for _, art := range artifactMap {
|
||||||
if err := zhcn.Scan(&artifacts); err != nil {
|
var dbArt *entity.EpicArtifactInfo
|
||||||
return fmt.Errorf("解析神器数据到DTO失败: %v", err)
|
if v, ok := artifactDbMap[art.Code]; ok {
|
||||||
}
|
dbArt = v
|
||||||
|
}
|
||||||
// 批量处理数据
|
customImgUrl := getArtifactImageUrl(ctx, art.Code, dbArt)
|
||||||
for _, artifact := range artifacts {
|
artifact := &entity.EpicArtifactInfo{
|
||||||
if err := t.saveArtifactData(ctx, artifact); err != nil {
|
ArtifactName: i18n.GetZh(ctx, art.Name),
|
||||||
g.Log().Error(ctx, "保存神器数据失败:", err)
|
ArtifactNameEn: art.Name,
|
||||||
continue
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -443,7 +492,7 @@ func (t *ThirdPartyDataSync) RefreshHeroSetContentByHeroInfo(ctx context.Context
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RefreshAllHeroSetContent 刷新所有角色配装字段
|
//刷新所有角色配装字段
|
||||||
func (t *ThirdPartyDataSync) RefreshAllHeroSetContent(ctx context.Context) error {
|
func (t *ThirdPartyDataSync) RefreshAllHeroSetContent(ctx context.Context) error {
|
||||||
g.Log().Info(ctx, "开始批量刷新所有角色配装字段...")
|
g.Log().Info(ctx, "开始批量刷新所有角色配装字段...")
|
||||||
|
|
||||||
@@ -477,3 +526,20 @@ func (t *ThirdPartyDataSync) RefreshAllHeroSetContent(ctx context.Context) error
|
|||||||
g.Log().Info(ctx, "所有角色配装字段刷新完成")
|
g.Log().Info(ctx, "所有角色配装字段刷新完成")
|
||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ func TestSyncHeroData(t *testing.T) {
|
|||||||
* 测试同步神器数据
|
* 测试同步神器数据
|
||||||
*/
|
*/
|
||||||
func TestSyncArtifactData_Success(t *testing.T) {
|
func TestSyncArtifactData_Success(t *testing.T) {
|
||||||
|
_ = i18n.RefreshI18n(context.Background())
|
||||||
sync := NewThirdPartyDataSync()
|
sync := NewThirdPartyDataSync()
|
||||||
|
|
||||||
err := sync.SyncArtifactData(context.Background())
|
err := sync.SyncArtifactData(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("expected success, but got error: %v", err)
|
t.Errorf("expected success, but got error: %v", err)
|
||||||
|
|||||||
Reference in New Issue
Block a user