package config import ( "context" "encoding/json" "fmt" v1 "likedream-goframe/internal/api/v1" "likedream-goframe/internal/application/quesbank" "likedream-goframe/internal/common/db" errors2 "likedream-goframe/internal/common/errors" "likedream-goframe/internal/ent" "likedream-goframe/internal/ent/sysconfig" "likedream-goframe/internal/utils" "strings" ) // AI配置键常量 const ( AIConfigKey = "ai.config" ) // AIConfigServiceV1 AI配置服务 type AIConfigServiceV1 struct{} // NewAIConfigServiceV1 创建AI配置服务 func NewAIConfigServiceV1() *AIConfigServiceV1 { return &AIConfigServiceV1{} } // Get 获取AI配置 func (s *AIConfigServiceV1) Get(ctx context.Context, req *v1.AIConfigGetReq) *utils.Response[*v1.AIConfigGetResp] { config, err := db.Global.SysConfig.Query(). Where(sysconfig.KeyEQ(AIConfigKey)). Only(ctx) if err != nil { if ent.IsNotFound(err) { return utils.SuccessWithData(&v1.AIConfigGetResp{ Type: "chatglm", Model: "glm-4", APIKey: "", }) } utils.OkOrPanic(err) } var aiConfig quesbank.AIConfig if err := json.Unmarshal([]byte(config.Value), &aiConfig); err != nil { utils.OkOrPanic(errors2.NewBusinessError(500, "AI配置解析失败")) } maskedKey := maskAPIKey(aiConfig.APIKey) return utils.SuccessWithData(&v1.AIConfigGetResp{ Type: string(aiConfig.Type), URL: aiConfig.URL, Model: aiConfig.Model, APIKey: maskedKey, }) } // Update 更新AI配置 func (s *AIConfigServiceV1) Update(ctx context.Context, req *v1.AIConfigUpdateReq) *utils.Response[any] { if req.Type == "other" && req.URL == "" { utils.OkOrPanic(errors2.NewBusinessError(400, "自定义AI类型必须填写API地址")) } aiConfig := quesbank.AIConfig{ Type: quesbank.AIType(req.Type), URL: req.URL, Model: req.Model, APIKey: req.APIKey, } configValue, err := json.Marshal(aiConfig) if err != nil { utils.OkOrPanic(errors2.NewBusinessError(500, "配置序列化失败")) } config, err := db.Global.SysConfig.Query(). Where(sysconfig.KeyEQ(AIConfigKey)). Only(ctx) if err != nil { if ent.IsNotFound(err) { db.Global.SysConfig.Create(). SetKey(AIConfigKey). SetValue(string(configValue)). SetDescription("AI答题配置"). SetType("json"). SaveX(ctx) return utils.SuccessWithMsg("AI配置创建成功") } utils.OkOrPanic(err) } db.Global.SysConfig.UpdateOne(config). SetValue(string(configValue)). ExecX(ctx) return utils.SuccessWithMsg("AI配置更新成功") } // Test 测试AI配置 func (s *AIConfigServiceV1) Test(ctx context.Context, req *v1.AIConfigTestReq) *utils.Response[*v1.AIConfigTestResp] { if req.Type == "other" && req.URL == "" { return utils.SuccessWithData(&v1.AIConfigTestResp{ Success: false, Message: "自定义AI类型必须填写API地址", }) } aiConfig := quesbank.AIConfig{ Type: quesbank.AIType(req.Type), URL: req.URL, Model: req.Model, APIKey: req.APIKey, } aiService := quesbank.NewAIAnswerService(aiConfig) testMessages := quesbank.AIChatMessages{ Messages: []quesbank.AIChatMessage{ {Role: "user", Content: "你好,请回复\"测试成功\""}, }, } response, err := aiService.GetAnswer(ctx, testMessages) if err != nil { return utils.SuccessWithData(&v1.AIConfigTestResp{ Success: false, Message: fmt.Sprintf("AI连接测试失败: %v", err), }) } return utils.SuccessWithData(&v1.AIConfigTestResp{ Success: true, Message: fmt.Sprintf("AI连接测试成功,响应: %s", truncateString(response, 100)), }) } // GetTypeList 获取支持的AI类型列表 func (s *AIConfigServiceV1) GetTypeList(ctx context.Context, req *v1.AITypeListReq) *utils.Response[*v1.AITypeListResp] { types := []v1.AITypeItem{ {Type: "chatglm", Name: "智谱AI (ChatGLM)", Description: "智谱AI大模型,支持glm-4等模型", NeedURL: false}, {Type: "xinghuo", Name: "讯飞星火", Description: "讯飞星火认知大模型", NeedURL: false}, {Type: "tongyi", Name: "通义千问", Description: "阿里云通义千问大模型", NeedURL: false}, {Type: "doubao", Name: "豆包", Description: "字节跳动豆包大模型", NeedURL: false}, {Type: "openai", Name: "OpenAI", Description: "OpenAI GPT系列模型", NeedURL: false}, {Type: "other", Name: "其他 (兼容OpenAI格式)", Description: "支持任何兼容OpenAI API格式的服务", NeedURL: true}, } return utils.SuccessWithData(&v1.AITypeListResp{Types: types}) } // GetAIConfig 获取AI配置(内部使用) func (s *AIConfigServiceV1) GetAIConfig(ctx context.Context) (*quesbank.AIConfig, error) { config, err := db.Global.SysConfig.Query(). Where(sysconfig.KeyEQ(AIConfigKey)). Only(ctx) if err != nil { if ent.IsNotFound(err) { return nil, fmt.Errorf("AI配置未设置") } return nil, err } var aiConfig quesbank.AIConfig if err := json.Unmarshal([]byte(config.Value), &aiConfig); err != nil { return nil, fmt.Errorf("AI配置解析失败: %w", err) } return &aiConfig, nil } func maskAPIKey(key string) string { if len(key) <= 8 { return strings.Repeat("*", len(key)) } return key[:4] + strings.Repeat("*", len(key)-8) + key[len(key)-4:] } func truncateString(s string, maxLen int) string { if len(s) <= maxLen { return s } return s[:maxLen] + "..." }