feat: 实现自建情景功能
## 功能概述 - 用户可创建、编辑、删除自定义情景 - 支持自定义情景名称、图标、描述、Prompt、首句引导 - 完整的权限隔离,用户只能管理自己的情景 - 深度集成 Eino 框架,动态加载自建情景 Prompt ## 后端实现 ### 数据库 - 新增 user_scenarios 表 - 支持用户配额(最多 20 个) - 字段验证:description 可选,prompt 最小 10 字符 ### API - GET /api/scenarios - 获取用户情景列表 - POST /api/scenarios - 创建情景 - GET /api/scenarios/:id - 获取详情 - PATCH /api/scenarios/:id - 更新情景 - DELETE /api/scenarios/:id - 删除情景 ### Eino 集成 - PipelineState 添加 UserID 字段 - nodes_history 动态加载用户自建情景 - GetScenarioPrompt 支持自建情景优先级 ## 前端实现 ### 组件 - CreateScenarioModal - 创建情景对话框 - EditScenarioModal - 编辑情景对话框 - ConfigPanel 改造 - 分组显示系统预置和自建情景 ### Hook - useScenarios - 合并系统和自建情景,提供 CRUD 接口 ### 国际化 - 中文、英文、日文翻译支持 ## 问题修复 - 修复 CORS 问题:使用 Vite 代理 - 统一验证规则:description 可选,prompt 最小 10 字符 - 修复数据库约束:使用 NULLIF 处理空字符串 ## 文件变更 新增文件: 13 个 修改文件: 14 个 详见文档: docs/自建情景功能完整文档.md
This commit is contained in:
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/hhs/camtalk/internal/models"
|
||||
"github.com/hhs/camtalk/internal/orchestrator"
|
||||
"github.com/hhs/camtalk/internal/session"
|
||||
"github.com/hhs/camtalk/internal/store"
|
||||
)
|
||||
|
||||
// newUpgrader 根据配置创建 WebSocket upgrader。
|
||||
@@ -93,19 +94,19 @@ func (w *WSClient) SendError(err models.WsError) error {
|
||||
}
|
||||
|
||||
// ServeWS 处理 WebSocket 升级请求。
|
||||
func ServeWS(sessionMgr session.Manager, orch orchestrator.Orchestrator, cfg *config.Config, tokenMgr *auth.TokenManager) gin.HandlerFunc {
|
||||
func ServeWS(sessionMgr session.Manager, orch orchestrator.Orchestrator, cfg *config.Config, tokenMgr *auth.TokenManager, scenarioRepo store.UserScenarioRepository) gin.HandlerFunc {
|
||||
upgrader := newUpgrader(cfg)
|
||||
heartbeatInterval := time.Duration(cfg.Server.HeartbeatInterval) * time.Second
|
||||
heartbeatTimeout := time.Duration(cfg.Server.HeartbeatTimeout) * time.Second
|
||||
version := cfg.App.Version
|
||||
|
||||
return func(c *gin.Context) {
|
||||
serveWS(c, sessionMgr, orch, upgrader, heartbeatInterval, heartbeatTimeout, version, tokenMgr)
|
||||
serveWS(c, sessionMgr, orch, upgrader, heartbeatInterval, heartbeatTimeout, version, tokenMgr, scenarioRepo)
|
||||
}
|
||||
}
|
||||
|
||||
func serveWS(c *gin.Context, sessionMgr session.Manager, orch orchestrator.Orchestrator,
|
||||
upgrader websocket.Upgrader, heartbeatInterval, heartbeatTimeout time.Duration, version string, tokenMgr *auth.TokenManager) {
|
||||
upgrader websocket.Upgrader, heartbeatInterval, heartbeatTimeout time.Duration, version string, tokenMgr *auth.TokenManager, scenarioRepo store.UserScenarioRepository) {
|
||||
|
||||
// --- JWT 认证(upgrade 前完成,失败直接返回 HTTP 错误) ---
|
||||
token := c.Query("token")
|
||||
@@ -289,7 +290,21 @@ func serveWS(c *gin.Context, sessionMgr session.Manager, orch orchestrator.Orche
|
||||
if scenarioID != "" && scenarioID != "free_chat" {
|
||||
sess, err := client.sessionMgr.Get(context.Background(), sessionID)
|
||||
if err == nil && sess != nil {
|
||||
greeting := llm.GetScenarioGreeting(scenarioID, sess.Config.Language)
|
||||
// 加载用户自建情景
|
||||
var customGreetings map[string]string
|
||||
if sess.UserID != "" && scenarioRepo != nil {
|
||||
scenarios, err := scenarioRepo.FindByUserID(context.Background(), sess.UserID)
|
||||
if err == nil && len(scenarios) > 0 {
|
||||
customGreetings = make(map[string]string, len(scenarios))
|
||||
for _, s := range scenarios {
|
||||
if s.Greeting != "" {
|
||||
customGreetings[s.ID] = s.Greeting
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
greeting := llm.GetScenarioGreeting(scenarioID, sess.Config.Language, customGreetings)
|
||||
if greeting != "" {
|
||||
// 发送首句作为 AI 消息
|
||||
_ = client.SendJSON(models.WsLLMChunk{
|
||||
|
||||
Reference in New Issue
Block a user