Files
aggregate_translate_server/app/admin/service/translator_service.go
hucan 8ae43bfba9
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
1
2025-06-29 00:36:30 +08:00

278 lines
8.2 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 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
}