1、
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				Build / build (push) Has been cancelled
				
			
		
			
				
	
				CodeQL / Analyze (go) (push) Has been cancelled
				
			
		
			
				
	
				build / Build (push) Has been cancelled
				
			
		
			
				
	
				GitHub Actions Mirror / mirror_to_gitee (push) Has been cancelled
				
			
		
			
				
	
				GitHub Actions Mirror / mirror_to_gitlab (push) Has been cancelled
				
			
		
			
				
	
				Issue Close Require / issue-close-require (push) Has been cancelled
				
			
		
			
				
	
				Issue Check Inactive / issue-check-inactive (push) Has been cancelled
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	Build / build (push) Has been cancelled
				
			CodeQL / Analyze (go) (push) Has been cancelled
				
			build / Build (push) Has been cancelled
				
			GitHub Actions Mirror / mirror_to_gitee (push) Has been cancelled
				
			GitHub Actions Mirror / mirror_to_gitlab (push) Has been cancelled
				
			Issue Close Require / issue-close-require (push) Has been cancelled
				
			Issue Check Inactive / issue-check-inactive (push) Has been cancelled
				
			This commit is contained in:
		| @ -159,3 +159,28 @@ func (e TmMember) GetMemberAdvent(c *gin.Context) { | ||||
|  | ||||
| 	e.OK(datas, "success") | ||||
| } | ||||
|  | ||||
| // 获取充值订单详情 | ||||
| func (e *TmRechargeLog) GetOrderInfo(c *gin.Context) { | ||||
| 	req := dto.TmRechargeLogGetOrderInfoReq{} | ||||
| 	s := service.TmRechargeLog{} | ||||
| 	err := e.MakeContext(c). | ||||
| 		MakeOrm(). | ||||
| 		Bind(&req). | ||||
| 		MakeService(&s.Service). | ||||
| 		Errors | ||||
|  | ||||
| 	if err != nil { | ||||
| 		e.Error(500, err, "") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	resp := dto.TmRechargeLogGetOrderInfoResp{} | ||||
| 	err = s.GetOrderInfo(&req, &resp) | ||||
| 	if err != nil { | ||||
| 		e.Error(500, nil, err.Error()) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	e.OK(resp, "success") | ||||
| } | ||||
|  | ||||
| @ -38,5 +38,6 @@ func registerTmMemberRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddl | ||||
| 		r2.GET("member-advent", api.GetMemberAdvent) //获取用户即将过期的充值信息 | ||||
|  | ||||
| 		r2.POST("recharge", rechargeApi.CreateOrder) //用户发起充值 | ||||
| 		r2.GET("order", rechargeApi.GetOrderInfo)    //获取充值订单信息 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -242,3 +242,13 @@ type TmRechargeCallbackReq struct { | ||||
| 	ToAddress     string          `json:"to_address"` | ||||
| 	TxHash        string          `json:"tx_hash"` | ||||
| } | ||||
|  | ||||
| type TmRechargeLogGetOrderInfoReq struct { | ||||
| 	OrderNo string `json:"orderNo" form:"orderNo" query:"orderNo" comment:"订单号"` | ||||
| } | ||||
|  | ||||
| type TmRechargeLogGetOrderInfoResp struct { | ||||
| 	Id      int    `json:"id"` | ||||
| 	OrderNo string `json:"orderNo"` | ||||
| 	Status  int    `json:"status"` | ||||
| } | ||||
|  | ||||
| @ -66,3 +66,11 @@ type TranslateUserInfoResp struct { | ||||
| 	UserApiKey  string `json:"userApiKey" comment:"用户API Key"` | ||||
| 	RemainChars int    `json:"remainChars" comment:"剩余可翻译字符数"` | ||||
| } | ||||
|  | ||||
| type DenosiTranslateRequest struct { | ||||
| 	Model       string    `json:"model"` | ||||
| 	Messages    []Message `json:"messages"` | ||||
| 	Temperature float64   `json:"temperature"` | ||||
| 	MaxTokens   int       `json:"max_tokens"` | ||||
| 	Stream      bool      `json:"stream"` | ||||
| } | ||||
|  | ||||
| @ -33,6 +33,12 @@ func (e TmPlatformAccount) QueryRemain(req *dto.TmPlatformAccountQueryRemainReq, | ||||
| 		return errors.New("查看对象不存在或无权查看") | ||||
| 	} | ||||
|  | ||||
| 	err = e.SyncPlatformAccount(entity) | ||||
|  | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func (e *TmPlatformAccount) SyncPlatformAccount(entity models.TmPlatformAccount) error { | ||||
| 	platformService := TmPlatform{Service: e.Service} | ||||
|  | ||||
| 	platform, err := platformService.GetById(entity.PlatformId) | ||||
| @ -49,7 +55,7 @@ func (e TmPlatformAccount) QueryRemain(req *dto.TmPlatformAccountQueryRemainReq, | ||||
| 		}, | ||||
| 	} | ||||
| 	config := TranslatorServiceConfig{ | ||||
| 		DefaultProvider: "deepseek", | ||||
| 		DefaultProvider: platform.Code, | ||||
| 		ProviderConfigs: provider, | ||||
| 	} | ||||
|  | ||||
| @ -72,49 +78,65 @@ func (e TmPlatformAccount) QueryRemain(req *dto.TmPlatformAccountQueryRemainReq, | ||||
|  | ||||
| 		redishelper.DefaultRedis.SetString(key, strconv.Itoa(remainCount)) | ||||
|  | ||||
| 		//如果为禁用则写入队列 | ||||
| 		if entity.Status == 2 { | ||||
| 			listKey := fmt.Sprintf(rediskey.TM_PLATFORM_ACCOUNT_LIST_KEY, entity.PlatformKey) | ||||
| 			vals, err := redishelper.DefaultRedis.GetAllList(listKey) | ||||
| 		// //如果为禁用则写入队列 | ||||
| 		// if entity.Status == 2 { | ||||
| 		// 	listKey := fmt.Sprintf(rediskey.TM_PLATFORM_ACCOUNT_LIST_KEY, entity.PlatformKey) | ||||
| 		// 	vals, err := redishelper.DefaultRedis.GetAllList(listKey) | ||||
|  | ||||
| 			if err != nil { | ||||
| 				e.Log.Errorf("获取redis列表失败 %v", err) | ||||
| 				return errors.New("获取redis列表失败") | ||||
| 			} | ||||
| 		// 	if err != nil { | ||||
| 		// 		e.Log.Errorf("获取redis列表失败 %v", err) | ||||
| 		// 		return errors.New("获取redis列表失败") | ||||
| 		// 	} | ||||
|  | ||||
| 			item := models.TmPlatformAccount{} | ||||
| 			hasData := false | ||||
| 		// 	item := models.TmPlatformAccount{} | ||||
| 		// 	hasData := false | ||||
|  | ||||
| 			for _, val := range vals { | ||||
| 				sonic.UnmarshalString(val, &item) | ||||
| 		// 	for _, val := range vals { | ||||
| 		// 		sonic.UnmarshalString(val, &item) | ||||
|  | ||||
| 				if item.ApiKey == entity.ApiKey { | ||||
| 					hasData = true | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| 		// 		if item.ApiKey == entity.ApiKey { | ||||
| 		// 			hasData = true | ||||
| 		// 			break | ||||
| 		// 		} | ||||
| 		// 	} | ||||
|  | ||||
| 			if !hasData { | ||||
| 				entity.Status = 1 | ||||
| 				val, err := sonic.MarshalString(entity) | ||||
| 		// 	if !hasData { | ||||
| 		// 		entity.Status = 1 | ||||
| 		// 		val, err := sonic.MarshalString(entity) | ||||
|  | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 		// 		if err != nil { | ||||
| 		// 			return err | ||||
| 		// 		} | ||||
|  | ||||
| 				err = e.Orm.Transaction(func(tx *gorm.DB) error { | ||||
| 					if err1 := redishelper.DefaultRedis.RPushList(listKey, val); err1 != nil { | ||||
| 						return err | ||||
| 					} | ||||
| 		// 		err = e.Orm.Transaction(func(tx *gorm.DB) error { | ||||
| 		// 			if err1 := redishelper.DefaultRedis.RPushList(listKey, val); err1 != nil { | ||||
| 		// 				return err | ||||
| 		// 			} | ||||
|  | ||||
| 					if err1 := tx.Model(entity).Update("status", 1).Error; err1 != nil { | ||||
| 						return err1 | ||||
| 		// 			if err1 := tx.Model(entity).Update("status", 1).Error; err1 != nil { | ||||
| 		// 				return err1 | ||||
| 		// 			} | ||||
| 		// 			return nil | ||||
| 		// 		}) | ||||
|  | ||||
| 		// 		return nil | ||||
| 		// 	} | ||||
| 		// } | ||||
| 	} | ||||
| 					return nil | ||||
| 				}) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // 同步所有第三方账号余额 | ||||
| func (e *TmPlatformAccount) SyncAll() error { | ||||
| 	var datas []models.TmPlatformAccount | ||||
| 	e.Orm.Model(&models.TmPlatformAccount{}).Where("status =1").Find(&datas) | ||||
| 	platformService := TmPlatform{} | ||||
| 	platformService.Orm = e.Orm | ||||
| 	platformService.Log = e.Log | ||||
|  | ||||
| 	for _, item := range datas { | ||||
| 		if err := e.SyncPlatformAccount(item); err != nil { | ||||
| 			e.Log.Errorf("同步平台账号余额 密钥:%s 失败 %v", item.ApiKey, err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|  | ||||
| @ -30,6 +30,22 @@ type TmRechargeLog struct { | ||||
| 	service.Service | ||||
| } | ||||
|  | ||||
| func (e TmRechargeLog) GetOrderInfo(req *dto.TmRechargeLogGetOrderInfoReq, resp *dto.TmRechargeLogGetOrderInfoResp) error { | ||||
| 	var data models.TmRechargeLog | ||||
| 	if err := e.Orm.Model(&models.TmRechargeLog{}). | ||||
| 		Where("order_no =?", req.OrderNo). | ||||
| 		First(&data).Error; err != nil { | ||||
| 		e.Log.Errorf("TmRechargeLogService GetOrderInfo error:%s \r\n", err) | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	resp.Id = data.Id | ||||
| 	resp.OrderNo = data.OrderNo | ||||
| 	resp.Status = data.Status | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // 清理过期订单 | ||||
| func (e TmRechargeLog) CleanExpiredOrder() error { | ||||
| 	expireTime := time.Now().Add(5 * time.Minute) | ||||
|  | ||||
| @ -31,3 +31,32 @@ func TestDeepSeekTranslator(t *testing.T) { | ||||
| 	} | ||||
| 	fmt.Println(result) | ||||
| } | ||||
|  | ||||
| // 测试DeepSeekTranslator方法 | ||||
| func TestDenosiTranslator(t *testing.T) { | ||||
| 	config := TranslatorServiceConfig{ | ||||
| 		DefaultProvider: "denosi", | ||||
| 		ProviderConfigs: map[string]interface{}{ | ||||
| 			"denosi": map[string]interface{}{ | ||||
| 				"apiKey":   "sk-PRXfCgLAefqo6jeYsJhKCU6hw5gmxkfNAOjISSQfOeHxqDw4", | ||||
| 				"endpoint": "https://api.denosi.com/v1/chat/completions", | ||||
| 				"model":    "gpt-4o-mini", | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	service, err := NewTranslatorService(&config) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		fmt.Sprintln("报错:", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	result, err := service.providers["denosi"].Translate("成吉思汗", "zh", "fr") | ||||
|  | ||||
| 	if err != nil { | ||||
| 		fmt.Sprintln("报错:", err) | ||||
| 		return | ||||
| 	} | ||||
| 	fmt.Println(result) | ||||
| } | ||||
|  | ||||
							
								
								
									
										112
									
								
								app/admin/service/translator_denosi.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								app/admin/service/translator_denosi.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,112 @@ | ||||
| package service | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"go-admin/app/admin/service/dto" | ||||
| 	"go-admin/utils/httphelper" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/bytedance/sonic" | ||||
| ) | ||||
|  | ||||
| type DenosiTranslatorConfig struct { | ||||
| 	ApiKey    string `json:"apiKey"` | ||||
| 	ApiSecret string `json:"apiSecret"` | ||||
| 	Endpoint  string `json:"endpoint"` | ||||
| 	Model     string `json:"model"` | ||||
| } | ||||
|  | ||||
| type DenosiTranslator struct { | ||||
| 	config *DenosiTranslatorConfig | ||||
| 	client *httphelper.HTTPClient | ||||
| } | ||||
|  | ||||
| // 初始化适配器实例 | ||||
| func NewDenosiTranslator(config *DenosiTranslatorConfig) *DenosiTranslator { | ||||
| 	defaultHeaders := map[string]string{ | ||||
| 		"Content-Type": "application/json", | ||||
| 	} | ||||
|  | ||||
| 	httpClient := httphelper.NewHTTPClient( | ||||
| 		15*time.Second, | ||||
| 		config.Endpoint, | ||||
| 		defaultHeaders, | ||||
| 	) | ||||
|  | ||||
| 	return &DenosiTranslator{ | ||||
| 		config: config, | ||||
| 		client: httpClient, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // 翻译 | ||||
| func (e *DenosiTranslator) Translate(text string, source string, target string) (*dto.TranslateResult, error) { | ||||
| 	// TODO: 实现Deepseek API调用 | ||||
| 	result := dto.TranslateResult{} | ||||
| 	reqBody := dto.DenosiTranslateRequest{ | ||||
| 		Model:  e.config.Model, // v3 fast模型 | ||||
| 		Stream: false, | ||||
| 		Messages: []dto.Message{ | ||||
| 			{Role: "system", Content: fmt.Sprintf("你是翻译专家,将内容从%s翻译为%s,仅返回翻译结果", source, target)}, | ||||
| 			{Role: "user", Content: fmt.Sprintf("翻译:%s", text)}, | ||||
| 		}, | ||||
| 		Temperature: 0, | ||||
| 	} | ||||
|  | ||||
| 	data, err := sonic.Marshal(reqBody) | ||||
| 	if err != nil { | ||||
| 		return &result, err | ||||
| 	} | ||||
|  | ||||
| 	req, err := http.NewRequest("POST", e.config.Endpoint, bytes.NewBuffer(data)) | ||||
| 	if err != nil { | ||||
| 		return &result, err | ||||
| 	} | ||||
|  | ||||
| 	req.Header.Set("Authorization", "Bearer "+e.config.ApiKey) | ||||
| 	req.Header.Set("Content-Type", "application/json") | ||||
|  | ||||
| 	client := &http.Client{Timeout: 10 * time.Second} | ||||
| 	resp, err := client.Do(req) | ||||
| 	if err != nil { | ||||
| 		return &result, err | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
|  | ||||
| 	body, _ := ioutil.ReadAll(resp.Body) | ||||
| 	fmt.Println(string(body)) | ||||
| 	if resp.StatusCode != 200 { | ||||
| 		return &result, fmt.Errorf("Denosi error: %s", string(body)) | ||||
| 	} | ||||
|  | ||||
| 	var dsResp dto.DeepSeekResponse | ||||
| 	err = json.Unmarshal(body, &dsResp) | ||||
| 	if err != nil { | ||||
| 		return &result, err | ||||
| 	} | ||||
|  | ||||
| 	if len(dsResp.Choices) == 0 { | ||||
| 		return &result, fmt.Errorf("no translation result from Denosi") | ||||
| 	} | ||||
|  | ||||
| 	result.TranslatedText = dsResp.Choices[0].Message.Content | ||||
| 	result.SourceLanguage = source | ||||
| 	result.TargetLanguage = target | ||||
|  | ||||
| 	return &result, nil | ||||
| } | ||||
|  | ||||
| func (e *DenosiTranslator) GetRemainCount() (int, error) { | ||||
| 	// TODO: 实现Deepseek API调用 | ||||
|  | ||||
| 	return 0, nil | ||||
| } | ||||
|  | ||||
| // 返回服务商 | ||||
| func (e *DenosiTranslator) GetPlatform() string { | ||||
| 	return "denosi" | ||||
| } | ||||
| @ -6,15 +6,12 @@ import ( | ||||
| 	"fmt" | ||||
| 	"go-admin/app/admin/models" | ||||
| 	"go-admin/app/admin/service/dto" | ||||
| 	"go-admin/common/mq" | ||||
| 	rediskey "go-admin/common/redis_key" | ||||
| 	"go-admin/common/statuscode" | ||||
| 	"go-admin/utils/redishelper" | ||||
| 	"time" | ||||
| 	"unicode/utf8" | ||||
|  | ||||
| 	commonDto "go-admin/common/dto" | ||||
|  | ||||
| 	"github.com/bytedance/sonic" | ||||
| 	"github.com/go-admin-team/go-admin-core/logger" | ||||
| 	"github.com/go-admin-team/go-admin-core/sdk/service" | ||||
| @ -86,8 +83,16 @@ func NewTranslatorService(cfg *TranslatorServiceConfig) (*TranslatorService, err | ||||
| 				logger.Error("failed to unmarshal config for Translator") | ||||
| 				return nil, TransUnMarshalConfigErr | ||||
| 			} | ||||
|  | ||||
| 			newAdapter = NewTransTranslator(&transCfg) | ||||
| 		case "denosi": | ||||
| 			transCfgBytes, _ := json.Marshal(providerCfg) | ||||
| 			var transCfg DenosiTranslatorConfig | ||||
| 			if err := json.Unmarshal(transCfgBytes, &transCfg); err != nil { | ||||
| 				logger.Error("failed to unmarshal config for Translator") | ||||
| 				return nil, TransUnMarshalConfigErr | ||||
| 			} | ||||
|  | ||||
| 			newAdapter = NewDenosiTranslator(&transCfg) | ||||
| 		default: | ||||
| 			return nil, UnSportPlatformErr | ||||
| 		} | ||||
| @ -237,20 +242,18 @@ func (s *TranslatorService) GetTranslator(platform string) (translator *Translat | ||||
| func (s *TranslatorService) GetNextAccount(platformCode string) (*models.TmPlatformAccount, error) { | ||||
| 	var val string | ||||
| 	var err error | ||||
| 	var remain int | ||||
| 	// var remain int | ||||
| 	key := fmt.Sprintf(rediskey.TM_PLATFORM_ACCOUNT_LIST_KEY, platformCode) | ||||
|  | ||||
| 	retries := []time.Duration{100 * time.Millisecond, 200 * time.Millisecond, 300 * time.Millisecond} | ||||
|  | ||||
| 	defer func() { | ||||
| 		if val != "" { | ||||
| 			if remain > 0 { | ||||
| 			// 放回队尾,继续轮询机制 | ||||
| 			if err := redishelper.DefaultRedis.RPushList(key, val); err != nil { | ||||
| 				s.Log.Errorf("failed to push account back to queue: %v", err) | ||||
| 			} | ||||
| 		} | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	// 尝试从 Redis 队列中弹出账号 ID,带重试机制 | ||||
| @ -269,35 +272,36 @@ func (s *TranslatorService) GetNextAccount(platformCode string) (*models.TmPlatf | ||||
| 		return nil, errors.New("failed to get account from queue after multiple retries: " + err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	accountService := TmPlatformAccount{Service: s.Service} | ||||
| 	// accountService := TmPlatformAccount{Service: s.Service} | ||||
| 	account := models.TmPlatformAccount{} | ||||
| 	sonic.UnmarshalString(val, &account) | ||||
|  | ||||
| 	switch platformCode { | ||||
| 	case "deepseek": | ||||
| 		remain = 999999 | ||||
| 	default: | ||||
| 		remain, _ = accountService.GetRemainCount(platformCode, account.ApiKey) | ||||
| 	} | ||||
| 	// switch platformCode { | ||||
| 	// case "deepseek": | ||||
| 	// 	remain = 999999 | ||||
| 	// default: | ||||
| 	// 	remain, _ = accountService.GetRemainCount(platformCode, account.ApiKey) | ||||
| 	// } | ||||
|  | ||||
| 	//取消限制 | ||||
| 	// 如果剩余字符数 <= 0,跳过该账号,不放回队列,并更新数据库状态 | ||||
| 	if remain <= 0 { | ||||
| 		event := commonDto.ExhaustedAccountMessage{ | ||||
| 			Id:       account.Id, | ||||
| 			Platform: platformCode, | ||||
| 		} | ||||
| 		payload, _ := sonic.Marshal(event) | ||||
| 		err = mq.MQ.Publish( // default exchange | ||||
| 			"account_exhausted_queue", | ||||
| 			payload, | ||||
| 		) | ||||
| 	// if remain <= 0 { | ||||
| 	// 	event := commonDto.ExhaustedAccountMessage{ | ||||
| 	// 		Id:       account.Id, | ||||
| 	// 		Platform: platformCode, | ||||
| 	// 	} | ||||
| 	// 	payload, _ := sonic.Marshal(event) | ||||
| 	// 	err = mq.MQ.Publish( // default exchange | ||||
| 	// 		"account_exhausted_queue", | ||||
| 	// 		payload, | ||||
| 	// 	) | ||||
|  | ||||
| 		if err != nil { | ||||
| 			s.Log.Errorf("发送账号耗尽通知失败:%v", err) | ||||
| 		} | ||||
| 	// 	if err != nil { | ||||
| 	// 		s.Log.Errorf("发送账号耗尽通知失败:%v", err) | ||||
| 	// 	} | ||||
|  | ||||
| 		return nil, errors.New("account exhausted and removed from queue") | ||||
| 	} | ||||
| 	// 	return nil, errors.New("account exhausted and removed from queue") | ||||
| 	// } | ||||
|  | ||||
| 	return &account, nil | ||||
| } | ||||
|  | ||||
| @ -15,6 +15,7 @@ func InitJob() { | ||||
| 		"RemainCharJob":        RemainCharJob{}, | ||||
| 		"CleanExpiredOrderJob": CleanExpiredOrderJob{}, | ||||
| 		"TrxPaymentJob":        TrxPaymentJob{}, | ||||
| 		"SyncRemainCharJob":    SyncRemainCharJob{}, | ||||
| 		// ... | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -2,11 +2,12 @@ package jobs | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	models2 "go-admin/app/jobs/models" | ||||
| 	"time" | ||||
|  | ||||
| 	log "github.com/go-admin-team/go-admin-core/logger" | ||||
| 	"github.com/go-admin-team/go-admin-core/sdk" | ||||
| 	models2 "go-admin/app/jobs/models" | ||||
| 	"gorm.io/gorm" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/robfig/cron/v3" | ||||
|  | ||||
| @ -59,7 +60,7 @@ func (e *ExecJob) Run() { | ||||
| 	//TODO: 待完善部分 | ||||
| 	//str := time.Now().Format(timeFormat) + " [INFO] JobCore " + string(e.EntryId) + "exec success , spend :" + latencyTime.String() | ||||
| 	//ws.SendAll(str) | ||||
| 	log.Info("[Job] JobCore %s exec success , spend :%v", e.Name, latencyTime) | ||||
| 	log.Infof("[Job] JobCore %s exec success , spend :%v", e.Name, latencyTime) | ||||
| 	return | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -14,6 +14,18 @@ type RemainCharJob struct{} | ||||
|  | ||||
| type CleanExpiredOrderJob struct{} | ||||
|  | ||||
| // 同步剩余字符 | ||||
| type SyncRemainCharJob struct{} | ||||
|  | ||||
| // 定时同步第三方用量 | ||||
| func (t SyncRemainCharJob) Exec(arg interface{}) error { | ||||
| 	platformAccountService := service.TmPlatformAccount{} | ||||
| 	platformAccountService.Orm = GetDb() | ||||
| 	platformAccountService.Log = logger.NewHelper(logger.DefaultLogger) | ||||
|  | ||||
| 	return platformAccountService.SyncAll() | ||||
| } | ||||
|  | ||||
| // 清理过期订单 | ||||
| func (t CleanExpiredOrderJob) Exec(arg interface{}) error { | ||||
| 	// expireTime := time.Now().Add(5 * time.Minute) | ||||
|  | ||||
| @ -50,3 +50,23 @@ func TestClean(t *testing.T) { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestSyncPlatformAccountChar(t *testing.T) { | ||||
|  | ||||
| 	initSetting() | ||||
|  | ||||
| 	job := SyncRemainCharJob{} | ||||
|  | ||||
| 	if err := job.Exec(nil); err != nil { | ||||
| 		t.Error(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func initSetting() { | ||||
| 	dsn := "root:123456@tcp(127.0.0.1:3306)/aggregate_translate?charset=utf8mb4&parseTime=True&loc=Local&timeout=1000ms" | ||||
| 	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{}) | ||||
| 	sdk.Runtime.SetDb("default", db) | ||||
|  | ||||
| 	redishelper.InitDefaultRedis("127.0.0.1:6379", "", 1) | ||||
| 	redishelper.InitLockRedisConn("127.0.0.1:6379", "", "1") | ||||
| } | ||||
|  | ||||
| @ -5,9 +5,11 @@ import ( | ||||
| 	"fmt" | ||||
| 	"go-admin/app/admin/service" | ||||
| 	"go-admin/app/admin/service/dto" | ||||
| 	rediskey "go-admin/common/redis_key" | ||||
| 	"go-admin/config" | ||||
| 	"go-admin/utils/redishelper" | ||||
| 	"go-admin/utils/utility" | ||||
| 	"io/ioutil" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| @ -36,8 +38,22 @@ func (j TrxPaymentJob) Exec(arg interface{}) error { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	key := fmt.Sprintf(rediskey.TM_RECHARGE_PRE_ORDER, "*") | ||||
| 	keys, err := redishelper.DefaultRedis.ScanKeys(key) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		logger.Error("查询redis key失败", err) | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if len(keys) == 0 { | ||||
| 		logger.Info("没有待处理订单") | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	rechargeService := service.TmRechargeLog{} | ||||
| 	rechargeService.Orm = GetDb() | ||||
| 	rechargeService.Log = logger.NewHelper(logger.DefaultLogger) | ||||
| 	platforms, err := rechargeService.GetPlatforms() | ||||
| 	toAddresss := []string{} | ||||
|  | ||||
| @ -67,6 +83,7 @@ func (j TrxPaymentJob) Exec(arg interface{}) error { | ||||
|  | ||||
| 		for _, transfer := range transfers { | ||||
| 			if transfer.TransactionID == "" || transfer.ToAddress != toAddress { | ||||
| 				logger.Infof("跳出插入 ", transfer) | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| @ -79,15 +96,20 @@ func (j TrxPaymentJob) Exec(arg interface{}) error { | ||||
|  | ||||
| 			if utility.ContainsString(toAddresss, item.ToAddress) { | ||||
| 				logs = append(logs, item) | ||||
| 			} else { | ||||
| 				logger.Infof("没有写入logs ", item) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if len(logs) > 0 { | ||||
|  | ||||
| 			err := rechargeService.PayCallBack(&logs) | ||||
|  | ||||
| 			if err != nil { | ||||
| 				logger.Error("执行完毕,err:", err.Error()) | ||||
| 			} | ||||
| 		} else { | ||||
| 			// logger.Infof("接收地址:%s 合约地址:%s 无数据", toAddress, UsdtContractAddress) | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| @ -96,19 +118,31 @@ func (j TrxPaymentJob) Exec(arg interface{}) error { | ||||
| // GetTRC20Transfers 获取指定 TRC20 代币的交易记录 | ||||
| func GetTRC20Transfers(contractAddress, accountAddress string, minTimestamp, maxTimestamp int64) ([]dto.TRC20Transfer, error) { | ||||
| 	url := fmt.Sprintf("%s/v1/accounts/%s/transactions/trc20?contract_address=%s", config.ExtConfig.TrxGridUrl, accountAddress, contractAddress) | ||||
|  | ||||
| 	if minTimestamp > 0 { | ||||
| 		url += fmt.Sprintf("&min_timestamp=%d", minTimestamp) | ||||
| 	} | ||||
| 	if maxTimestamp > 0 { | ||||
| 		url += fmt.Sprintf("&max_timestamp=%d", maxTimestamp) | ||||
| 	} | ||||
| 	resp, err := http.Get(url) | ||||
| 	// logger.Info("查询地址:", url) | ||||
| 	req, err := http.NewRequest("GET", url, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to create request: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	// 设置请求头(包含 TronGrid API Key) | ||||
| 	req.Header.Set("Accept", "*/*") | ||||
| 	req.Header.Set("TRON-PRO-API-KEY", config.ExtConfig.TronApiKey) // 从配置读取 API Key | ||||
|  | ||||
| 	client := &http.Client{} | ||||
| 	resp, err := client.Do(req) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to send request: %v", err) | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
|  | ||||
| 	body, err := ioutil.ReadAll(resp.Body) | ||||
| 	body, err := io.ReadAll(resp.Body) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to read response body: %v", err) | ||||
| 	} | ||||
| @ -116,6 +150,7 @@ func GetTRC20Transfers(contractAddress, accountAddress string, minTimestamp, max | ||||
| 	var result struct { | ||||
| 		Data []dto.TRC20Transfer `json:"data"` | ||||
| 	} | ||||
| 	// logger.Info("查询结果:", string(body)) | ||||
| 	if err := json.Unmarshal(body, &result); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to unmarshal response: %v", err) | ||||
| 	} | ||||
|  | ||||
| @ -13,6 +13,7 @@ type Extend struct { | ||||
| 	AMap       AMap // 这里配置对应配置文件的结构即可 | ||||
| 	Mq         MqConfig | ||||
| 	TrxGridUrl string `yaml:"trxGridUrl"` | ||||
| 	TronApiKey string `yaml:"tronApiKey"` | ||||
| } | ||||
|  | ||||
| type AMap struct { | ||||
|  | ||||
| @ -55,6 +55,7 @@ settings: | ||||
|       pass: "123456" | ||||
|     #trx api | ||||
|     trxGridUrl: "https://api.trongrid.io" | ||||
|     tronApiKey: "223c129e-73f5-470f-9464-f9969846c134" | ||||
|   cache: | ||||
|     redis: | ||||
|       addr: 127.0.0.1:6379 | ||||
|  | ||||
							
								
								
									
										44
									
								
								utils/utility/safego.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								utils/utility/safego.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | ||||
| package utility | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"runtime" | ||||
| 	"runtime/debug" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/go-admin-team/go-admin-core/logger" | ||||
| ) | ||||
|  | ||||
| // SafeGo 安全地启动一个 goroutine,捕获 panic | ||||
| func SafeGo(fn func()) { | ||||
| 	go func() { | ||||
| 		defer func() { | ||||
| 			if r := recover(); r != nil { | ||||
| 				// 记录 Goroutine ID、panic 信息和堆栈 | ||||
| 				logger.Error(fmt.Sprintf("Recovered from panic in Goroutine %s: %v\nStack Trace:\n%s", GetGoroutineID(), r, string(debug.Stack()))) | ||||
| 			} | ||||
| 		}() | ||||
| 		fn() | ||||
| 	}() | ||||
| } | ||||
|  | ||||
| // 获取 Goroutine ID | ||||
| func GetGoroutineID() string { | ||||
| 	buf := make([]byte, 64) | ||||
| 	n := runtime.Stack(buf, false) | ||||
| 	stack := string(buf[:n]) | ||||
| 	// 提取 Goroutine ID | ||||
| 	id := strings.Split(stack, " ")[1] | ||||
| 	return id | ||||
| } | ||||
|  | ||||
| func SafeGoParam[T any](fn func(T), param T) { | ||||
| 	go func() { | ||||
| 		defer func() { | ||||
| 			if r := recover(); r != nil { | ||||
| 				logger.Error(fmt.Sprintf(" SafeGoParam Recovered from panic in Goroutine %s: %v\nStack Trace:\n%s", GetGoroutineID(), r, string(debug.Stack()))) | ||||
| 			} | ||||
| 		}() | ||||
| 		fn(param) // 执行传入的函数 | ||||
| 	}() | ||||
| } | ||||
		Reference in New Issue
	
	Block a user