You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
111 lines
3.6 KiB
111 lines
3.6 KiB
package user
|
|
|
|
import (
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"yunyan/comm"
|
|
"yunyan/lego/core"
|
|
"yunyan/lego/core/cbase"
|
|
"yunyan/lego/sys/postgres"
|
|
alitranslate "yunyan/sys/aliyun/translate"
|
|
)
|
|
|
|
// 实时机器翻译(同声传译 / 面对面翻译)的服务解析。
|
|
//
|
|
// 与实名认证同一套路子:翻译服务配在后台「第三方服务配置」(类别 comm.SvcCatMT=3),
|
|
// 存 console 共享库 postgres,凭据在服务端解密使用,**不下发客户端**。
|
|
//
|
|
// 为什么放服务端而不是像以前那样客户端直连:
|
|
// 以前客户端用火山翻译,AK/SK 是通过 AppConfig.env **明文下发给每一个客户端**的,
|
|
// 任何人抓个包或反编译就能拿到云账号凭据。改成服务端代调后凭据不再出网关。
|
|
|
|
// mtCacheTTL 翻译客户端的缓存时长。
|
|
//
|
|
// 同传是流式高频调用,每句都去查一次库、重建一次 SDK 客户端太浪费;
|
|
// 但也不能永久缓存——后台改了配置得能生效,所以给个短 TTL。
|
|
const mtCacheTTL = 60 * time.Second
|
|
|
|
type modelTranslateComp struct {
|
|
cbase.ModuleCompBase
|
|
module *User
|
|
|
|
mu sync.RWMutex
|
|
cached alitranslate.ISys
|
|
cachedId string
|
|
cachedAt time.Time
|
|
}
|
|
|
|
func (this *modelTranslateComp) Init(service core.IService, module core.IModule, comp core.IModuleComp, opt core.IModuleOptions) (err error) {
|
|
this.ModuleCompBase.Init(service, module, comp, opt)
|
|
this.module = module.(*User)
|
|
return
|
|
}
|
|
|
|
// resolveTranslator 取本应用可用的机器翻译服务。
|
|
//
|
|
// 作用域口径与 resolveThirdSvcs 一致:应用行(app_name=<app>)优先于全局行(app_name=''),
|
|
// 只取启用的、类别含 comm.SvcCatMT 的服务。
|
|
// 返回 svcId/provider 供落日志与响应回带,便于排查"到底用的哪家"。
|
|
func (this *modelTranslateComp) resolveTranslator() (svcId, provider string, t alitranslate.ISys, err error) {
|
|
this.mu.RLock()
|
|
if this.cached != nil && time.Since(this.cachedAt) < mtCacheTTL {
|
|
svcId, t = this.cachedId, this.cached
|
|
this.mu.RUnlock()
|
|
return svcId, "alibaba", t, nil
|
|
}
|
|
this.mu.RUnlock()
|
|
|
|
app := comm.AppName()
|
|
svcs := make([]*comm.ThirdSvcConfig, 0)
|
|
if err = postgres.Find(comm.TableSvcConfig, &svcs, "(app_name=? OR app_name='')", app); err != nil {
|
|
if err == postgres.ErrNoDocuments {
|
|
err = nil
|
|
} else {
|
|
return
|
|
}
|
|
}
|
|
|
|
var picked *comm.ThirdSvcConfig
|
|
for _, s := range svcs {
|
|
if !s.Enable || !comm.CategoriesHas(s.Categories, comm.SvcCatMT) {
|
|
continue
|
|
}
|
|
if picked == nil || (picked.AppName == "" && s.AppName == app && app != "") {
|
|
picked = s
|
|
}
|
|
}
|
|
if picked == nil {
|
|
return "", "", nil, nil // 未配置:调用方回 TranslateNotConfigured
|
|
}
|
|
|
|
fields, ferr := comm.ResolveSvcPlainFields(picked, nil, os.Getenv("FIELD_ENCRYPT_KEY"))
|
|
if ferr != nil {
|
|
return picked.Id, picked.Provider, nil, ferr
|
|
}
|
|
|
|
// 目前只实现了阿里云。换别家时在这里按 provider 分支即可
|
|
// (sys/ 下已有 bytedance/google/microsoft 的翻译实现可接)。
|
|
if strings.TrimSpace(strings.ToLower(picked.Provider)) != "alibaba" {
|
|
return picked.Id, picked.Provider, nil, nil
|
|
}
|
|
|
|
opts := []alitranslate.Option{
|
|
alitranslate.SetAccessKeyId(strings.TrimSpace(fields["access_key_id"])),
|
|
alitranslate.SetAccessKeySecret(strings.TrimSpace(fields["access_key_secret"])),
|
|
}
|
|
if r := strings.TrimSpace(fields["region"]); r != "" {
|
|
opts = append(opts, alitranslate.SetRegion(r))
|
|
}
|
|
sys, serr := alitranslate.NewSys(opts...)
|
|
if serr != nil {
|
|
return picked.Id, picked.Provider, nil, serr
|
|
}
|
|
|
|
this.mu.Lock()
|
|
this.cached, this.cachedId, this.cachedAt = sys, picked.Id, time.Now()
|
|
this.mu.Unlock()
|
|
return picked.Id, picked.Provider, sys, nil
|
|
}
|
|
|