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

This commit is contained in:
2025-06-29 00:36:30 +08:00
commit 8ae43bfba9
305 changed files with 36307 additions and 0 deletions

View 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
}