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