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.
 
 
 
 
 
 

284 lines
11 KiB

package main
import (
"context"
"yunyan/comm"
"yunyan/lego"
"yunyan/modules/agents"
"yunyan/modules/allhelp"
"yunyan/modules/analyze"
"yunyan/modules/echomeet"
"yunyan/modules/memory"
musicmodule "yunyan/modules/music"
"yunyan/modules/pay"
"yunyan/modules/svccfg"
"yunyan/modules/user"
"yunyan/services"
ali_auth "yunyan/sys/aliyun/auth"
apple_auth "yunyan/sys/auth/apple"
facebook_auth "yunyan/sys/auth/facebook"
firebase_auth "yunyan/sys/auth/firebase"
google_auth "yunyan/sys/auth/google"
wechat_auth "yunyan/sys/auth/wechat"
"yunyan/sys/doubao"
"yunyan/sys/email"
"yunyan/sys/google"
"yunyan/sys/ipinfo"
"yunyan/sys/musicobj"
"yunyan/sys/nats"
"yunyan/sys/pay/alipay"
"yunyan/sys/pay/appleiap"
"yunyan/sys/pay/googleiap"
"yunyan/sys/pay/paypal"
"yunyan/sys/pay/wechat"
"yunyan/sys/sms"
"yunyan/sys/tencentyun/cos"
"yunyan/sys/websearch/bochasearch"
"flag"
"fmt"
"os"
_ "time/tzdata" // 内嵌时区库:保证 analyze 统计时区(默认 Asia/Shanghai)在缺 tzdata 的容器也可解析
"yunyan/lego/base/rpcx"
"yunyan/lego/core"
"yunyan/lego/sys/cron"
"yunyan/lego/sys/log"
"yunyan/lego/sys/mysql"
"yunyan/lego/sys/pools"
"yunyan/lego/sys/postgres"
redissys "yunyan/lego/sys/redis"
"yunyan/lego/sys/wordfilter"
)
/*
服务类型:后台服务
*/
var (
conf = flag.String("conf", "./conf/home.yaml", "获取需要启动的服务配置文件") //启动服务的Id
)
// @title Home API
// @version 1.0
// @description 智能耳机服务
// @securityDefinitions.apikey BearerAuth
// @in header
// @name Authorization
func main() {
flag.Parse()
s := NewService(
rpcx.SetConfPath(*conf),
rpcx.SetVersion("1.0.0.0"),
)
s.OnInstallComp( //装备组件
services.NewHttpRouteComp(),
)
lego.Run(s, //运行模块
analyze.NewModule(),
user.NewModule(),
svccfg.NewModule(), //服务配置管理:接收 api 扇出的配置下发,重载本服务 sys 客户端
agents.NewModule(),
pay.NewModule(),
echomeet.NewModule(),
musicmodule.NewModule(),
allhelp.NewModule(),
memory.NewModule(), //记忆中心(拾忆):闹钟/待办/灵感/花销 + 周报月报
// bailianmodule.NewModule(),
)
}
func NewService(ops ...rpcx.Option) core.IService {
s := new(Service)
s.Configure(ops...)
return s
}
// worker 的服务对象定义
type Service struct {
services.ServiceBase
}
// 初始化worker需要的一些系统工具
func (this *Service) InitSys() {
this.ServiceBase.InitSys()
pools.Add(comm.Pool_UserSession, func() comm.IUserSession { return comm.NewUserSession() })
if err := cron.OnInit(this.GetSettings().Sys["cron"]); err != nil {
panic(fmt.Sprintf("init sys.cron err: %s", err.Error()))
} else {
log.Infof("init sys.cron success!")
}
//铭感词过滤
if err := wordfilter.OnInit(this.GetSettings().Sys["wordfilter"]); err != nil {
panic(fmt.Sprintf("init sys.wordfilter err: %s", err.Error()))
} else {
log.Infof("init sys.wordfilter success!")
}
//业务库(MySQL)
if err := mysql.OnInit(this.GetSettings().Sys["mysql"]); err != nil {
panic(fmt.Sprintf("init sys.mysql err: %s", err.Error()))
} else {
log.Infof("init sys.mysql success!")
}
// 业务功能配置托管:连上业务库后、各业务 sys(sms/wechat_auth/email/pay…) OnInit 之前,
// 从业务库 app_module_config 读值覆盖 Sys(库空则把当前 yaml seed 进库)。
// FIELD_ENCRYPT_KEY 须与 console 一致;改配置需重启生效。失败则降级用 yaml 默认、不阻断启动。
log.Infof("[ModuleConfig] 字段加密 key 指纹=%s(须与 console 启动日志里的指纹一致,否则解密必失败)",
comm.KeyFingerprint(os.Getenv("FIELD_ENCRYPT_KEY")))
if err := comm.LoadOrSeedModuleConfig(mysql.GetSys(), os.Getenv("FIELD_ENCRYPT_KEY"), this.GetSettings().Sys); err != nil {
log.Errorf("load module config err (回退 yaml 默认): %s", err.Error())
} else {
log.Infof("load module config success!")
}
//公共库(PostgreSQL/Supabase,含只读副本)
if err := postgres.OnInit(this.GetSettings().Sys["postgres"]); err != nil {
panic(fmt.Sprintf("init sys.postgres err: %s", err.Error()))
} else {
log.Infof("init sys.postgres success!")
}
//Redis
if err := redissys.OnInit(this.GetSettings().Sys["redis"]); err != nil {
panic(fmt.Sprintf("init sys.redis err: %s", err.Error()))
} else {
log.Infof("init sys.redis success!")
}
// 运维统计管道(埋点转发用)。容错:连不上不阻断业务,待 NATS 就绪重启即可。
if err := nats.OnInit(this.GetSettings().Sys["nats"]); err != nil {
log.Errorf("init sys.nats err (埋点转发暂不可用): %s", err.Error())
} else {
log.Infof("init sys.nats success!")
}
if err := cos.OnInit(this.GetSettings().Sys["cos"]); err != nil {
panic(fmt.Sprintf("init sys.cos err: %s", err.Error()))
} else {
log.Infof("init sys.cos success!")
}
// 短信服务:可选能力,缺配置降级(未初始化时 SendCaptcha 返回错误),console 配好后可热重载生效。
// 不能 panic —— home 在 5-in-1 容器里一死,entrypoint 会连坐杀掉其余服务(测试服常不配短信凭据)。
if err := sms.OnInit(this.GetSettings().Sys["sms"]); err != nil {
log.Warnf("init sys.sms err: %s (短信服务未配置,验证码短信不可用)", err.Error())
} else {
log.Infof("init sys.sms success!")
}
///第三方服务验证
// 以下第三方能力(语音/各路登录/存储/支付/AI/搜索)全部按「可选能力」处理:缺配置只 Warnf 降级,
// 绝不 panic —— 每个应用只配自己用到的那部分,测试环境可能一个凭据都不配。
if err := google.OnInit(this.GetSettings().Sys["google"]); err != nil {
log.Warnf("init sys.google err: %s (Google 语音服务未配置,相关功能不可用)", err.Error())
} else {
log.Infof("init sys.google success!")
}
if err := ali_auth.OnInit(this.GetSettings().Sys["aliyun"]); err != nil {
log.Warnf("init sys.ali_auth err: %s (阿里云认证未配置,一键登录不可用)", err.Error())
} else {
log.Infof("init sys.ali_auth success!")
}
// 会议相关单元服务(字节/阿里/Google/微软的转写·翻译·总结)由 echomeet 模块按其
// modules.echomeet 配置内部初始化,不在此处统一装配。
///第三方登验证接口
if err := google_auth.OnInit(this.GetSettings().Sys["google_auth"]); err != nil {
log.Warnf("init sys.google_auth err: %s (Google 登录未配置,该登录方式不可用)", err.Error())
} else {
log.Infof("init sys.google_auth success!")
}
if err := apple_auth.OnInit(this.GetSettings().Sys["apple_auth"]); err != nil {
log.Warnf("init sys.apple_auth err: %s (Apple 登录未配置,该登录方式不可用)", err.Error())
} else {
log.Infof("init sys.apple_auth success!")
}
if err := facebook_auth.OnInit(this.GetSettings().Sys["facebook_auth"]); err != nil {
log.Warnf("init sys.facebook_auth err: %s (Facebook 登录未配置,该登录方式不可用)", err.Error())
} else {
log.Infof("init sys.facebook_auth success!")
}
if err := firebase_auth.OnInit(this.GetSettings().Sys["firebase_auth"]); err != nil {
log.Warnf("init sys.firebase_auth err: %s (Firebase 登录未配置,该登录方式不可用)", err.Error())
} else {
log.Infof("init sys.firebase_auth success!")
}
if err := wechat_auth.OnInit(this.GetSettings().Sys["wechat_auth"]); err != nil {
log.Warnf("init sys.wechat_auth err: %s (微信登录未配置,该登录方式不可用)", err.Error())
} else {
log.Infof("init sys.wechat_auth success!")
}
if err := musicobj.OnInit(this.GetSettings().Sys["musicobj"]); err != nil {
log.Warnf("init sys.musicobj err: %s (音乐对象存储未配置,相关功能不可用)", err.Error())
} else {
log.Infof("init sys.musicobj success!")
}
// 邮件服务:同 sms,可选能力,缺配置降级(未初始化时 SendMail* 返回错误)。
if err := email.OnInit(this.GetSettings().Sys["email"]); err != nil {
log.Warnf("init sys.email err: %s (邮件服务未配置,验证码/通知邮件不可用)", err.Error())
} else {
log.Infof("init sys.email success!")
}
// 支付子系统属「可选能力」:配置缺失/密钥错误时降级为不可用(与上面的 sys.translate 同策略),
// 不能 panic —— home 在 5-in-1 容器里一死,entrypoint 会连坐杀掉 gateway/api/timer/mcp,
// 一个错误的支付密钥就此变成全站 502。未初始化时包级函数返回 ErrNotInited,不会 panic。
if err := wechat.OnInit(this.GetSettings().Sys["wechatpay"]); err != nil {
log.Warnf("init sys.wechatpay err: %s (微信支付未配置,支付功能不可用)", err.Error())
} else {
log.Infof("init sys.wechatpay success!")
}
if err := alipay.OnInit(this.GetSettings().Sys["alipay"]); err != nil {
log.Warnf("init sys.alipay err: %s (支付宝未配置,支付功能不可用)", err.Error())
} else {
log.Infof("init sys.alipay success!")
}
if err := paypal.OnInit(this.GetSettings().Sys["paypal"]); err != nil {
log.Warnf("init sys.paypal err: %s (PayPal 未配置,支付功能不可用,后台配好后热重载生效)", err.Error())
} else {
log.Infof("init sys.paypal success!")
}
if err := appleiap.OnInit(this.GetSettings().Sys["appleiap"]); err != nil {
log.Warnf("init sys.appleiap err: %s (苹果内购未配置,支付校验不可用,后台配好后热重载生效)", err.Error())
} else {
log.Infof("init sys.appleiap success!")
}
if err := googleiap.OnInit(this.GetSettings().Sys["googleiap"]); err != nil {
log.Warnf("init sys.googleiap err: %s (Google 内购未配置,支付校验不可用,后台配好后热重载生效)", err.Error())
} else {
log.Infof("init sys.googleiap success!")
}
if err := doubao.OnInit(this.GetSettings().Sys["doubao"]); err != nil {
panic(fmt.Sprintf("init sys.doubao err: %s", err.Error()))
} else {
log.Infof("init sys.doubao success!")
}
if err := ipinfo.OnInit(this.GetSettings().Sys["ipinfo"]); err != nil {
panic(fmt.Sprintf("init sys.ipinfo err: %s", err.Error()))
} else {
log.Infof("init sys.ipinfo success!")
}
//aitools
if err := bochasearch.OnInit(this.GetSettings().Sys["bochasearch"]); err != nil {
panic(fmt.Sprintf("init sys.bochasearch err: %s", err.Error()))
} else {
log.Infof("init sys.bochasearch success!")
}
// if err := bailian.OnInit(this.GetSettings().Sys["bailian"]); err != nil {
// panic(fmt.Sprintf("init sys.bailian err: %s", err.Error()))
// } else {
// log.Infof("init sys.bailian success!")
// }
// 业务功能配置热重载:登记可热更的 sys 与重载上下文,供 console 通过网关调
// user_reloadmoduleconfig 触发。必须放在各 sys OnInit 之后。见 configreload.go。
this.setupModuleReload()
}
func (this *Service) GetUserSession(ctx context.Context, meta map[string]string) (session comm.IUserSession) {
session = pools.Get[comm.IUserSession](comm.Pool_UserSession)
session.SetSession(ctx, meta)
return
}
func (this *Service) PutUserSession(session comm.IUserSession) {
session.Reset()
pools.Put(comm.Pool_UserSession, session)
}