package user import ( "yunyan/comm" "yunyan/lego/sys/log" "yunyan/lego/sys/mysql" "yunyan/pb" "yunyan/sys/ipinfo" ) // @Summary 获取应用配置(v3) // @Description 一次性下发客户端启动所需的全部配置:环境变量 + 智能体 + 产品 + 第三方服务(含 MCP) + 会议记录能力 + 通话翻译配置。 // 与 v2 的差别:不再单独下发 mcps(MCP 已并入第三方服务配置,在 thirdsvcs 里按 categories 含 "10" 识别); // 新增 meet(会议记录支持的语言与可选总结大模型,来自后台「会议记录服务」编排); // 新增 call_translate(通话翻译的选路规则 + 规则引用的翻译 agent,已按用户所在区域筛过); // agents 换成与 user_getagents_v3 相同的结构(含 type=chat|sts-chat 与 stt/llm/tts/sts 服务编排、 // MCP 绑定、变量、已补全的音色表),不再下发旧 agent 表。v1/v2 保持原样不受影响。 // @Tags User // @Accept json // @Produce json // @Security BearerAuth // @Param user body pb.UserGetAppConfigV3Req true "获取应用配置" // @Success 200 {object} comm.HttpResult{data=pb.UserGetAppConfigV3Resp} "成功返回(整体响应体经 AES-CBC 加密,响应头 X-Encrypted:1)" // @Router /api/home/user_getappconfig_v3 [post] // // 注册在 apiV3Comp(Version=v3)上,路由 = user_getappconfig_v3;已在 EncryptMsgs 中声明, // 响应含第三方服务明文凭据,整体加密后才出网关。 func (this *apiV3Comp) GetAppConfig(session comm.IUserSession, req *pb.UserGetAppConfigV3Req) (resp *pb.UserGetAppConfigV3Resp, errdata *pb.ErrorData) { region := pb.Region_RegionUSA ipdata, err := ipinfo.GetIPInfo(session.GetIP()) if ipdata != nil { region = ToRegion(ipdata) } this.module.Debug("GetAppConfigV3", log.Field{Key: "uid", Value: session.GetUserId()}, log.Field{Key: "ip", Value: session.GetIP()}, log.Field{Key: "region", Value: region}, log.Field{Key: "err", Value: err}, ) // 环境变量:应用自有配置(app_config) 打底,本区域的 global_config 覆盖(口径同 v2)。 // 注意 getdb 的第三个返回值是**旧 agent 表**,v3 已不下发它(见下方 agentItemsV3)。 config, globalconfig, _, _ := this.module.configmodel.getdb() env := mergeAppEnv(config, globalconfig, region) // 智能体:与 user_getagents_v3 同一份数据与结构(含 type、服务编排、MCP、变量、已补全音色), // 客户端只调 appconfig 也能直接把 agent 跑起来。 agents, err := this.agentItemsV3() if err != nil { errdata = &pb.ErrorData{Code: pb.ErrorCode_DBError, Message: err.Error()} return } products, err := this.module.cache.GetProducts() if err != nil && err != mysql.ErrNoDocuments { errdata = &pb.ErrorData{Code: pb.ErrorCode_DBError, Message: err.Error()} return } // 第三方服务(含 MCP 类):与 user_getthirdsvcs_v2 共用同一份解析口径。 thirdsvcs, err := resolveThirdSvcs(comm.AppName(), region, nil) if err != nil { errdata = &pb.ErrorData{Code: pb.ErrorCode_DBError, Message: err.Error()} return } resp = &pb.UserGetAppConfigV3Resp{ Env: env, Agents: agents, Products: products, Thirdsvcs: thirdsvcs, Meet: this.meetCapabilities(), // 通话翻译:按上面解析出的区域筛好的选路规则 + 规则引用的翻译 agent(见 api_v3_calltranslate.go)。 CallTranslate: this.callTranslateV3(region), } return } // mergeAppEnv 合并下发给客户端的环境变量:应用自有配置(app_config) 打底, // 本区域的 global_config 覆盖同名 key。口径与 v2 一致——非本区域的 global_config 一律不下发, // 否则客户端会拿到别的区域的服务地址/密钥。 func mergeAppEnv(config []*pb.DBAppConfigItem, globalconfig []*pb.DBGlobalConfigItem, region pb.Region) map[string]string { env := make(map[string]string, len(config)+len(globalconfig)) for _, v := range config { env[v.Key] = v.Value } for _, v := range globalconfig { if v.Region == region { env[v.Key] = v.Value } } return env } // meetCapabilities 取会议记录能力;echomeet 未随本服务装载时返回空壳(字段都在,列表为空), // 免得客户端还要判 null。 func (this *apiV3Comp) meetCapabilities() *pb.MeetCapabilitiesItem { item := &pb.MeetCapabilitiesItem{ TranscribeLanguages: []string{}, TranslateLanguages: []string{}, SummaryServices: []*pb.MeetSummaryServiceItem{}, } if this.module.echomeet == nil { return item } c := this.module.echomeet.MeetCapabilities() if len(c.TranscribeLanguages) > 0 { item.TranscribeLanguages = c.TranscribeLanguages } if len(c.TranslateLanguages) > 0 { item.TranslateLanguages = c.TranslateLanguages } item.TranscribeAnyLanguage = c.TranscribeAnyLanguage item.TranslateAnyLanguage = c.TranslateAnyLanguage item.SummaryClientChoice = c.SummaryClientChoice for _, s := range c.SummaryServices { item.SummaryServices = append(item.SummaryServices, &pb.MeetSummaryServiceItem{ SvcId: s.SvcId, Name: s.Name, IsDefault: s.IsDefault, }) } return item }