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:
277
app/admin/service/translator_service.go
Normal file
277
app/admin/service/translator_service.go
Normal file
@ -0,0 +1,277 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"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"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type TranslatorServiceConfig struct {
|
||||
DefaultProvider string // 默认翻译服务商
|
||||
ProviderConfigs map[string]interface{} // 各个服务商的具体配置
|
||||
}
|
||||
|
||||
type TranslatorService struct {
|
||||
service.Service
|
||||
config *TranslatorServiceConfig
|
||||
providers map[string]Translator // 注册的翻译服务商适配器
|
||||
// cache cache.Cache // 缓存服务
|
||||
}
|
||||
|
||||
type TranslatorToolService struct {
|
||||
service.Service
|
||||
}
|
||||
|
||||
// NewTranslatorService 创建翻译服务实例
|
||||
func NewTranslatorService(cfg *TranslatorServiceConfig) (*TranslatorService, error) {
|
||||
svc := &TranslatorService{
|
||||
config: cfg,
|
||||
// cache: c,
|
||||
providers: make(map[string]Translator),
|
||||
}
|
||||
|
||||
// 根据配置初始化并注册翻译服务商适配器
|
||||
for providerName, providerCfg := range cfg.ProviderConfigs {
|
||||
var newAdapter Translator
|
||||
switch providerName {
|
||||
case "google":
|
||||
googleCfgBytes, _ := json.Marshal(providerCfg) // 假设配置是map[string]interface{},需要转换
|
||||
var googleCfg GoogleTranslatorConfig
|
||||
if err := json.Unmarshal(googleCfgBytes, &googleCfg); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to unmarshal config for GoogleTranslate")
|
||||
}
|
||||
newAdapter = NewGoogleTranslator(&googleCfg)
|
||||
// case "BaiduTranslate":
|
||||
// // newAdapter = adapter.NewBaiduTranslator(...)
|
||||
case "deepseek":
|
||||
deepseekCfgBytes, _ := json.Marshal(providerCfg) // 假设配置是map[string]interface{},需要转换
|
||||
var deepseekCfg DeepseekTranslatorConfig
|
||||
if err := json.Unmarshal(deepseekCfgBytes, &deepseekCfg); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to unmarshal config for DeepseekTranslator")
|
||||
}
|
||||
newAdapter = NewDeepseekTranslator(&deepseekCfg)
|
||||
case "deepl", "deepl_free":
|
||||
deeplCfgBytes, _ := json.Marshal(providerCfg) // 假设配置是map[string]interface{},需要转换
|
||||
var deeplCfg DeeplTranslatorConfig
|
||||
if err := json.Unmarshal(deeplCfgBytes, &deeplCfg); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to unmarshal config for DeepLTranslator")
|
||||
}
|
||||
newAdapter = NewDeeplTranslator(&deeplCfg)
|
||||
default:
|
||||
return nil, errors.Errorf("unsupported translation provider: %s", providerName)
|
||||
}
|
||||
svc.RegisterProvider(providerName, newAdapter)
|
||||
}
|
||||
|
||||
if len(svc.providers) == 0 {
|
||||
return nil, errors.New("no translation providers registered")
|
||||
}
|
||||
|
||||
return svc, nil
|
||||
}
|
||||
|
||||
// RegisterProvider 注册翻译服务商适配器
|
||||
func (s *TranslatorService) RegisterProvider(name string, provider Translator) {
|
||||
// s.providerMutex.Lock()
|
||||
// defer s.providerMutex.Unlock()
|
||||
s.providers[name] = provider
|
||||
logger.Infof("Registered translation provider: %s", name)
|
||||
}
|
||||
|
||||
// 翻译校验
|
||||
// return statusCode
|
||||
func (s *TranslatorService) TranslateJudge(req *dto.TranslateReq, apiKey string) (result *dto.TranslateResult, respCode int) {
|
||||
tmMemberService := TmMember{Service: s.Service}
|
||||
tmPlatformAccount := TmPlatformAccount{Service: s.Service}
|
||||
memberInfo, err1 := tmMemberService.GetByKey(apiKey)
|
||||
|
||||
if err1 != nil {
|
||||
s.Log.Errorf("获取用户信息失败:%v", err1)
|
||||
respCode = statuscode.ServerError
|
||||
return
|
||||
}
|
||||
|
||||
if memberInfo.Status == 2 {
|
||||
respCode = statuscode.ApiUnauthorized
|
||||
return
|
||||
}
|
||||
|
||||
remainCount, _ := tmMemberService.GetRemainCount(req.Platform, apiKey)
|
||||
count := utf8.RuneCountInString(req.Text)
|
||||
respCode = statuscode.Success
|
||||
|
||||
if remainCount < count {
|
||||
respCode = statuscode.InSufficRemainChar
|
||||
return
|
||||
}
|
||||
|
||||
Translator, code := s.GetTranslator(req.Platform)
|
||||
|
||||
if code != statuscode.Success {
|
||||
respCode = code
|
||||
return
|
||||
}
|
||||
|
||||
result, err := Translator.providers[req.Platform].Translate(req.Text, req.SourceLang, req.TargetLang)
|
||||
|
||||
if err == nil {
|
||||
err2 := tmMemberService.DecrBy(req.Platform, apiKey, count)
|
||||
|
||||
if err2 != nil {
|
||||
s.Log.Errorf("翻译计数失败:%v", err2)
|
||||
respCode = statuscode.ServerError
|
||||
return
|
||||
}
|
||||
|
||||
platformConfigInterface := Translator.config.ProviderConfigs[req.Platform]
|
||||
|
||||
// 尝试将 interface{} 断言为 map[string]interface{}
|
||||
if mapData, ok := platformConfigInterface.(map[string]interface{}); !ok {
|
||||
tmPlatformAccount.DecrRemainBy(req.Platform, mapData["apiKey"].(string), count)
|
||||
}
|
||||
|
||||
date := time.Now().Format("20060102")
|
||||
redishelper.DefaultRedis.IncrBy(fmt.Sprintf(rediskey.TM_MEMBER_DAILY_COUNT, date, apiKey, req.Platform), int64(count))
|
||||
//每日统计保留三天
|
||||
redishelper.DefaultRedis.Expire(fmt.Sprintf(rediskey.TM_MEMBER_DAILY_COUNT, date, apiKey, req.Platform), 3*24*time.Hour)
|
||||
} else {
|
||||
code = statuscode.ServerError
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *TranslatorService) GetTranslator(platform string) (translator *TranslatorService, code int) {
|
||||
platformKey := fmt.Sprintf(rediskey.TM_PLATFORM_KEY, platform)
|
||||
platformVal, _ := redishelper.DefaultRedis.GetString(platformKey)
|
||||
|
||||
if platformVal == "" {
|
||||
code = statuscode.PlatformNotSupport
|
||||
return
|
||||
}
|
||||
|
||||
var platformEntity models.TmPlatform
|
||||
account, err := s.GetNextAccount(platform)
|
||||
|
||||
if err != nil {
|
||||
s.Log.Errorf("获取翻译api失败:%v", err)
|
||||
}
|
||||
|
||||
sonic.UnmarshalString(platformVal, &platformEntity)
|
||||
|
||||
if account.Id == 0 || account.Status == 2 {
|
||||
code = statuscode.TransactionNotAvailable
|
||||
return
|
||||
}
|
||||
|
||||
configs := map[string]interface{}{}
|
||||
configs[platform] = map[string]interface{}{
|
||||
"apiKey": account.ApiKey,
|
||||
"apiSecret": account.ApiSecret,
|
||||
"endpoint": platformEntity.ApiBaseUrl,
|
||||
}
|
||||
cfg := TranslatorServiceConfig{
|
||||
DefaultProvider: platform,
|
||||
ProviderConfigs: configs,
|
||||
}
|
||||
|
||||
switch platform {
|
||||
case "deepl", "deepl_free", "deepseek":
|
||||
translator, err = NewTranslatorService(&cfg)
|
||||
|
||||
if err != nil {
|
||||
s.Log.Errorf("failed to create translator service: %s", err)
|
||||
code = statuscode.ServerError
|
||||
return
|
||||
}
|
||||
|
||||
code = statuscode.Success
|
||||
return
|
||||
default:
|
||||
code = statuscode.PlatformNotSupport
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 从 Redis List 顺序取出一个账号,使用后放回队尾,模拟轮询
|
||||
func (s *TranslatorService) GetNextAccount(platformCode string) (*models.TmPlatformAccount, error) {
|
||||
var val string
|
||||
var err error
|
||||
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,带重试机制
|
||||
for _, delay := range retries {
|
||||
val, err = redishelper.DefaultRedis.LPopList(key)
|
||||
if err == nil {
|
||||
// 成功获取到值,跳出重试循环
|
||||
break
|
||||
}
|
||||
|
||||
time.Sleep(delay)
|
||||
}
|
||||
|
||||
// 如果所有重试都失败了,并且不是因为队列为空
|
||||
if err != nil {
|
||||
return nil, errors.New("failed to get account from queue after multiple retries: " + err.Error())
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// 如果剩余字符数 <= 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 err != nil {
|
||||
s.Log.Errorf("发送账号耗尽通知失败:%v", err)
|
||||
}
|
||||
|
||||
return nil, errors.New("account exhausted and removed from queue")
|
||||
}
|
||||
|
||||
return &account, nil
|
||||
}
|
||||
Reference in New Issue
Block a user