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") | 	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.GET("member-advent", api.GetMemberAdvent) //获取用户即将过期的充值信息 | ||||||
|  |  | ||||||
| 		r2.POST("recharge", rechargeApi.CreateOrder) //用户发起充值 | 		r2.POST("recharge", rechargeApi.CreateOrder) //用户发起充值 | ||||||
|  | 		r2.GET("order", rechargeApi.GetOrderInfo)    //获取充值订单信息 | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -242,3 +242,13 @@ type TmRechargeCallbackReq struct { | |||||||
| 	ToAddress     string          `json:"to_address"` | 	ToAddress     string          `json:"to_address"` | ||||||
| 	TxHash        string          `json:"tx_hash"` | 	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"` | 	UserApiKey  string `json:"userApiKey" comment:"用户API Key"` | ||||||
| 	RemainChars int    `json:"remainChars" comment:"剩余可翻译字符数"` | 	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("查看对象不存在或无权查看") | 		return errors.New("查看对象不存在或无权查看") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	err = e.SyncPlatformAccount(entity) | ||||||
|  |  | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *TmPlatformAccount) SyncPlatformAccount(entity models.TmPlatformAccount) error { | ||||||
| 	platformService := TmPlatform{Service: e.Service} | 	platformService := TmPlatform{Service: e.Service} | ||||||
|  |  | ||||||
| 	platform, err := platformService.GetById(entity.PlatformId) | 	platform, err := platformService.GetById(entity.PlatformId) | ||||||
| @ -49,7 +55,7 @@ func (e TmPlatformAccount) QueryRemain(req *dto.TmPlatformAccountQueryRemainReq, | |||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| 	config := TranslatorServiceConfig{ | 	config := TranslatorServiceConfig{ | ||||||
| 		DefaultProvider: "deepseek", | 		DefaultProvider: platform.Code, | ||||||
| 		ProviderConfigs: provider, | 		ProviderConfigs: provider, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @ -72,49 +78,65 @@ func (e TmPlatformAccount) QueryRemain(req *dto.TmPlatformAccountQueryRemainReq, | |||||||
|  |  | ||||||
| 		redishelper.DefaultRedis.SetString(key, strconv.Itoa(remainCount)) | 		redishelper.DefaultRedis.SetString(key, strconv.Itoa(remainCount)) | ||||||
|  |  | ||||||
| 		//如果为禁用则写入队列 | 		// //如果为禁用则写入队列 | ||||||
| 		if entity.Status == 2 { | 		// if entity.Status == 2 { | ||||||
| 			listKey := fmt.Sprintf(rediskey.TM_PLATFORM_ACCOUNT_LIST_KEY, entity.PlatformKey) | 		// 	listKey := fmt.Sprintf(rediskey.TM_PLATFORM_ACCOUNT_LIST_KEY, entity.PlatformKey) | ||||||
| 			vals, err := redishelper.DefaultRedis.GetAllList(listKey) | 		// 	vals, err := redishelper.DefaultRedis.GetAllList(listKey) | ||||||
|  |  | ||||||
| 			if err != nil { | 		// 	if err != nil { | ||||||
| 				e.Log.Errorf("获取redis列表失败 %v", err) | 		// 		e.Log.Errorf("获取redis列表失败 %v", err) | ||||||
| 				return errors.New("获取redis列表失败") | 		// 		return errors.New("获取redis列表失败") | ||||||
| 			} | 		// 	} | ||||||
|  |  | ||||||
| 			item := models.TmPlatformAccount{} | 		// 	item := models.TmPlatformAccount{} | ||||||
| 			hasData := false | 		// 	hasData := false | ||||||
|  |  | ||||||
| 			for _, val := range vals { | 		// 	for _, val := range vals { | ||||||
| 				sonic.UnmarshalString(val, &item) | 		// 		sonic.UnmarshalString(val, &item) | ||||||
|  |  | ||||||
| 				if item.ApiKey == entity.ApiKey { | 		// 		if item.ApiKey == entity.ApiKey { | ||||||
| 					hasData = true | 		// 			hasData = true | ||||||
| 					break | 		// 			break | ||||||
| 				} | 		// 		} | ||||||
| 			} | 		// 	} | ||||||
|  |  | ||||||
| 			if !hasData { | 		// 	if !hasData { | ||||||
| 				entity.Status = 1 | 		// 		entity.Status = 1 | ||||||
| 				val, err := sonic.MarshalString(entity) | 		// 		val, err := sonic.MarshalString(entity) | ||||||
|  |  | ||||||
| 				if err != nil { | 		// 		if err != nil { | ||||||
| 					return err | 		// 			return err | ||||||
| 				} | 		// 		} | ||||||
|  |  | ||||||
| 				err = e.Orm.Transaction(func(tx *gorm.DB) error { | 		// 		err = e.Orm.Transaction(func(tx *gorm.DB) error { | ||||||
| 					if err1 := redishelper.DefaultRedis.RPushList(listKey, val); err1 != nil { | 		// 			if err1 := redishelper.DefaultRedis.RPushList(listKey, val); err1 != nil { | ||||||
| 						return err | 		// 				return err | ||||||
| 					} | 		// 			} | ||||||
|  |  | ||||||
| 					if err1 := tx.Model(entity).Update("status", 1).Error; err1 != nil { | 		// 			if err1 := tx.Model(entity).Update("status", 1).Error; err1 != nil { | ||||||
| 						return err1 | 		// 				return err1 | ||||||
| 					} | 		// 			} | ||||||
| 					return nil | 		// 			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 | 	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 { | func (e TmRechargeLog) CleanExpiredOrder() error { | ||||||
| 	expireTime := time.Now().Add(5 * time.Minute) | 	expireTime := time.Now().Add(5 * time.Minute) | ||||||
|  | |||||||
| @ -31,3 +31,32 @@ func TestDeepSeekTranslator(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 	fmt.Println(result) | 	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" | 	"fmt" | ||||||
| 	"go-admin/app/admin/models" | 	"go-admin/app/admin/models" | ||||||
| 	"go-admin/app/admin/service/dto" | 	"go-admin/app/admin/service/dto" | ||||||
| 	"go-admin/common/mq" |  | ||||||
| 	rediskey "go-admin/common/redis_key" | 	rediskey "go-admin/common/redis_key" | ||||||
| 	"go-admin/common/statuscode" | 	"go-admin/common/statuscode" | ||||||
| 	"go-admin/utils/redishelper" | 	"go-admin/utils/redishelper" | ||||||
| 	"time" | 	"time" | ||||||
| 	"unicode/utf8" | 	"unicode/utf8" | ||||||
|  |  | ||||||
| 	commonDto "go-admin/common/dto" |  | ||||||
|  |  | ||||||
| 	"github.com/bytedance/sonic" | 	"github.com/bytedance/sonic" | ||||||
| 	"github.com/go-admin-team/go-admin-core/logger" | 	"github.com/go-admin-team/go-admin-core/logger" | ||||||
| 	"github.com/go-admin-team/go-admin-core/sdk/service" | 	"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") | 				logger.Error("failed to unmarshal config for Translator") | ||||||
| 				return nil, TransUnMarshalConfigErr | 				return nil, TransUnMarshalConfigErr | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			newAdapter = NewTransTranslator(&transCfg) | 			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: | 		default: | ||||||
| 			return nil, UnSportPlatformErr | 			return nil, UnSportPlatformErr | ||||||
| 		} | 		} | ||||||
| @ -237,18 +242,16 @@ func (s *TranslatorService) GetTranslator(platform string) (translator *Translat | |||||||
| func (s *TranslatorService) GetNextAccount(platformCode string) (*models.TmPlatformAccount, error) { | func (s *TranslatorService) GetNextAccount(platformCode string) (*models.TmPlatformAccount, error) { | ||||||
| 	var val string | 	var val string | ||||||
| 	var err error | 	var err error | ||||||
| 	var remain int | 	// var remain int | ||||||
| 	key := fmt.Sprintf(rediskey.TM_PLATFORM_ACCOUNT_LIST_KEY, platformCode) | 	key := fmt.Sprintf(rediskey.TM_PLATFORM_ACCOUNT_LIST_KEY, platformCode) | ||||||
|  |  | ||||||
| 	retries := []time.Duration{100 * time.Millisecond, 200 * time.Millisecond, 300 * time.Millisecond} | 	retries := []time.Duration{100 * time.Millisecond, 200 * time.Millisecond, 300 * time.Millisecond} | ||||||
|  |  | ||||||
| 	defer func() { | 	defer func() { | ||||||
| 		if val != "" { | 		if val != "" { | ||||||
| 			if remain > 0 { | 			// 放回队尾,继续轮询机制 | ||||||
| 				// 放回队尾,继续轮询机制 | 			if err := redishelper.DefaultRedis.RPushList(key, val); err != nil { | ||||||
| 				if err := redishelper.DefaultRedis.RPushList(key, val); err != nil { | 				s.Log.Errorf("failed to push account back to queue: %v", err) | ||||||
| 					s.Log.Errorf("failed to push account back to queue: %v", err) |  | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	}() | 	}() | ||||||
| @ -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()) | 		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{} | 	account := models.TmPlatformAccount{} | ||||||
| 	sonic.UnmarshalString(val, &account) | 	sonic.UnmarshalString(val, &account) | ||||||
|  |  | ||||||
| 	switch platformCode { | 	// switch platformCode { | ||||||
| 	case "deepseek": | 	// case "deepseek": | ||||||
| 		remain = 999999 | 	// 	remain = 999999 | ||||||
| 	default: | 	// default: | ||||||
| 		remain, _ = accountService.GetRemainCount(platformCode, account.ApiKey) | 	// 	remain, _ = accountService.GetRemainCount(platformCode, account.ApiKey) | ||||||
| 	} | 	// } | ||||||
|  |  | ||||||
|  | 	//取消限制 | ||||||
| 	// 如果剩余字符数 <= 0,跳过该账号,不放回队列,并更新数据库状态 | 	// 如果剩余字符数 <= 0,跳过该账号,不放回队列,并更新数据库状态 | ||||||
| 	if remain <= 0 { | 	// if remain <= 0 { | ||||||
| 		event := commonDto.ExhaustedAccountMessage{ | 	// 	event := commonDto.ExhaustedAccountMessage{ | ||||||
| 			Id:       account.Id, | 	// 		Id:       account.Id, | ||||||
| 			Platform: platformCode, | 	// 		Platform: platformCode, | ||||||
| 		} | 	// 	} | ||||||
| 		payload, _ := sonic.Marshal(event) | 	// 	payload, _ := sonic.Marshal(event) | ||||||
| 		err = mq.MQ.Publish( // default exchange | 	// 	err = mq.MQ.Publish( // default exchange | ||||||
| 			"account_exhausted_queue", | 	// 		"account_exhausted_queue", | ||||||
| 			payload, | 	// 		payload, | ||||||
| 		) | 	// 	) | ||||||
|  |  | ||||||
| 		if err != nil { | 	// 	if err != nil { | ||||||
| 			s.Log.Errorf("发送账号耗尽通知失败:%v", err) | 	// 		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 | 	return &account, nil | ||||||
| } | } | ||||||
|  | |||||||
| @ -15,6 +15,7 @@ func InitJob() { | |||||||
| 		"RemainCharJob":        RemainCharJob{}, | 		"RemainCharJob":        RemainCharJob{}, | ||||||
| 		"CleanExpiredOrderJob": CleanExpiredOrderJob{}, | 		"CleanExpiredOrderJob": CleanExpiredOrderJob{}, | ||||||
| 		"TrxPaymentJob":        TrxPaymentJob{}, | 		"TrxPaymentJob":        TrxPaymentJob{}, | ||||||
|  | 		"SyncRemainCharJob":    SyncRemainCharJob{}, | ||||||
| 		// ... | 		// ... | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -2,11 +2,12 @@ package jobs | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	models2 "go-admin/app/jobs/models" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
| 	log "github.com/go-admin-team/go-admin-core/logger" | 	log "github.com/go-admin-team/go-admin-core/logger" | ||||||
| 	"github.com/go-admin-team/go-admin-core/sdk" | 	"github.com/go-admin-team/go-admin-core/sdk" | ||||||
| 	models2 "go-admin/app/jobs/models" |  | ||||||
| 	"gorm.io/gorm" | 	"gorm.io/gorm" | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"github.com/robfig/cron/v3" | 	"github.com/robfig/cron/v3" | ||||||
|  |  | ||||||
| @ -59,7 +60,7 @@ func (e *ExecJob) Run() { | |||||||
| 	//TODO: 待完善部分 | 	//TODO: 待完善部分 | ||||||
| 	//str := time.Now().Format(timeFormat) + " [INFO] JobCore " + string(e.EntryId) + "exec success , spend :" + latencyTime.String() | 	//str := time.Now().Format(timeFormat) + " [INFO] JobCore " + string(e.EntryId) + "exec success , spend :" + latencyTime.String() | ||||||
| 	//ws.SendAll(str) | 	//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 | 	return | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
| @ -14,6 +14,18 @@ type RemainCharJob struct{} | |||||||
|  |  | ||||||
| type CleanExpiredOrderJob 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 { | func (t CleanExpiredOrderJob) Exec(arg interface{}) error { | ||||||
| 	// expireTime := time.Now().Add(5 * time.Minute) | 	// expireTime := time.Now().Add(5 * time.Minute) | ||||||
|  | |||||||
| @ -50,3 +50,23 @@ func TestClean(t *testing.T) { | |||||||
| 		t.Error(err) | 		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" | 	"fmt" | ||||||
| 	"go-admin/app/admin/service" | 	"go-admin/app/admin/service" | ||||||
| 	"go-admin/app/admin/service/dto" | 	"go-admin/app/admin/service/dto" | ||||||
|  | 	rediskey "go-admin/common/redis_key" | ||||||
| 	"go-admin/config" | 	"go-admin/config" | ||||||
|  | 	"go-admin/utils/redishelper" | ||||||
| 	"go-admin/utils/utility" | 	"go-admin/utils/utility" | ||||||
| 	"io/ioutil" | 	"io" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
| @ -36,8 +38,22 @@ func (j TrxPaymentJob) Exec(arg interface{}) error { | |||||||
| 		return nil | 		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 := service.TmRechargeLog{} | ||||||
| 	rechargeService.Orm = GetDb() | 	rechargeService.Orm = GetDb() | ||||||
|  | 	rechargeService.Log = logger.NewHelper(logger.DefaultLogger) | ||||||
| 	platforms, err := rechargeService.GetPlatforms() | 	platforms, err := rechargeService.GetPlatforms() | ||||||
| 	toAddresss := []string{} | 	toAddresss := []string{} | ||||||
|  |  | ||||||
| @ -67,6 +83,7 @@ func (j TrxPaymentJob) Exec(arg interface{}) error { | |||||||
|  |  | ||||||
| 		for _, transfer := range transfers { | 		for _, transfer := range transfers { | ||||||
| 			if transfer.TransactionID == "" || transfer.ToAddress != toAddress { | 			if transfer.TransactionID == "" || transfer.ToAddress != toAddress { | ||||||
|  | 				logger.Infof("跳出插入 ", transfer) | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| @ -79,15 +96,20 @@ func (j TrxPaymentJob) Exec(arg interface{}) error { | |||||||
|  |  | ||||||
| 			if utility.ContainsString(toAddresss, item.ToAddress) { | 			if utility.ContainsString(toAddresss, item.ToAddress) { | ||||||
| 				logs = append(logs, item) | 				logs = append(logs, item) | ||||||
|  | 			} else { | ||||||
|  | 				logger.Infof("没有写入logs ", item) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if len(logs) > 0 { | 		if len(logs) > 0 { | ||||||
|  |  | ||||||
| 			err := rechargeService.PayCallBack(&logs) | 			err := rechargeService.PayCallBack(&logs) | ||||||
|  |  | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				logger.Error("执行完毕,err:", err.Error()) | 				logger.Error("执行完毕,err:", err.Error()) | ||||||
| 			} | 			} | ||||||
|  | 		} else { | ||||||
|  | 			// logger.Infof("接收地址:%s 合约地址:%s 无数据", toAddress, UsdtContractAddress) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| @ -96,19 +118,31 @@ func (j TrxPaymentJob) Exec(arg interface{}) error { | |||||||
| // GetTRC20Transfers 获取指定 TRC20 代币的交易记录 | // GetTRC20Transfers 获取指定 TRC20 代币的交易记录 | ||||||
| func GetTRC20Transfers(contractAddress, accountAddress string, minTimestamp, maxTimestamp int64) ([]dto.TRC20Transfer, error) { | 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) | 	url := fmt.Sprintf("%s/v1/accounts/%s/transactions/trc20?contract_address=%s", config.ExtConfig.TrxGridUrl, accountAddress, contractAddress) | ||||||
|  |  | ||||||
| 	if minTimestamp > 0 { | 	if minTimestamp > 0 { | ||||||
| 		url += fmt.Sprintf("&min_timestamp=%d", minTimestamp) | 		url += fmt.Sprintf("&min_timestamp=%d", minTimestamp) | ||||||
| 	} | 	} | ||||||
| 	if maxTimestamp > 0 { | 	if maxTimestamp > 0 { | ||||||
| 		url += fmt.Sprintf("&max_timestamp=%d", maxTimestamp) | 		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 { | 	if err != nil { | ||||||
| 		return nil, fmt.Errorf("failed to send request: %v", err) | 		return nil, fmt.Errorf("failed to send request: %v", err) | ||||||
| 	} | 	} | ||||||
| 	defer resp.Body.Close() | 	defer resp.Body.Close() | ||||||
|  |  | ||||||
| 	body, err := ioutil.ReadAll(resp.Body) | 	body, err := io.ReadAll(resp.Body) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, fmt.Errorf("failed to read response body: %v", err) | 		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 { | 	var result struct { | ||||||
| 		Data []dto.TRC20Transfer `json:"data"` | 		Data []dto.TRC20Transfer `json:"data"` | ||||||
| 	} | 	} | ||||||
|  | 	// logger.Info("查询结果:", string(body)) | ||||||
| 	if err := json.Unmarshal(body, &result); err != nil { | 	if err := json.Unmarshal(body, &result); err != nil { | ||||||
| 		return nil, fmt.Errorf("failed to unmarshal response: %v", err) | 		return nil, fmt.Errorf("failed to unmarshal response: %v", err) | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -13,6 +13,7 @@ type Extend struct { | |||||||
| 	AMap       AMap // 这里配置对应配置文件的结构即可 | 	AMap       AMap // 这里配置对应配置文件的结构即可 | ||||||
| 	Mq         MqConfig | 	Mq         MqConfig | ||||||
| 	TrxGridUrl string `yaml:"trxGridUrl"` | 	TrxGridUrl string `yaml:"trxGridUrl"` | ||||||
|  | 	TronApiKey string `yaml:"tronApiKey"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type AMap struct { | type AMap struct { | ||||||
|  | |||||||
| @ -55,6 +55,7 @@ settings: | |||||||
|       pass: "123456" |       pass: "123456" | ||||||
|     #trx api |     #trx api | ||||||
|     trxGridUrl: "https://api.trongrid.io" |     trxGridUrl: "https://api.trongrid.io" | ||||||
|  |     tronApiKey: "223c129e-73f5-470f-9464-f9969846c134" | ||||||
|   cache: |   cache: | ||||||
|     redis: |     redis: | ||||||
|       addr: 127.0.0.1:6379 |       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