Files
epic-go/internal/logic/cron/third_party_sync.go
hu xiaotong cecb19e497 feat(cron): 实现定时任务管理功能
- 新增 cron模块,支持定时任务管理- 实现了任务列表获取、任务添加、任务移除和任务状态获取等接口
- 添加了默认任务,包括数据同步、数据清理、健康检查和缓存刷新等
- 实现了优雅关闭功能,确保在服务停止时正确停止所有任务
- 添加了定时任务相关文档和使用指南
2025-06-23 15:19:38 +08:00

239 lines
6.6 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"
"epic/internal/model/dto"
"epic/utility"
"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"
"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 {
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, "开始同步神器数据...")
utility.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 := "https://api.example.com/heroes"
// 添加请求头
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)
// 检查json对象本身和其内部值并使用 .Var().IsSlice() 这种更可靠的方式判断是否为数组
if j == nil || j.IsNil() || !j.Var().IsSlice() {
return fmt.Errorf("英雄数据格式错误期望是一个JSON数组")
}
var heroes []*dto.ThirdPartyHeroDTO
if err := j.Scan(&heroes); err != nil {
return fmt.Errorf("解析英雄数据到DTO失败: %v", err)
}
g.Log().Info(ctx, "解析到", len(heroes), "个英雄数据")
// 批量处理数据
for _, hero := range heroes {
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)
}
g.Log().Info(ctx, "解析到", len(artifacts), "个神器数据")
// 批量处理数据
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 {
// TODO: 实现具体的数据库保存逻辑
// 现在 hero 是一个强类型对象,可以直接使用 hero.Code, hero.Name 等
// 示例:记录同步日志
syncLog := map[string]interface{}{
"type": "hero_sync",
"hero_code": hero.Code,
"sync_time": gtime.Now(),
"status": "success",
}
g.Log().Debug(ctx, "保存英雄数据:", syncLog)
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
}