feat: 多功能升级 — 流式逐字渲染、对话缓存、会话组织优化、记忆管理修复、性能仪表盘

- 前端消息流式逐字渲染 (AI-Core ChatStream → SSE → Gateway → WebSocket stream_chunk → fadeInUp + cursorBlink)
- 后端对话缓存 (conversationCache sync.Map, GET /sessions/:id/messages)
- 前端侧边栏历史多轮对话显示
- DevTools 性能监控图标移至首页仪表盘
- DevTools 用户记忆查询/删减功能修复 (补全 DELETE 数据链路)
- 后端和 DevTools 按用户分类组织实时活动会话 (map[userID]map[sessionID]*Client)
- 新增 docs/api-reference/ 路由参考文档
- 新增 docs/message-flow-architecture.md 消息链路架构文档
This commit is contained in:
2026-05-16 17:44:03 +08:00
parent 63513210b7
commit 186513f381
24 changed files with 1024 additions and 216 deletions
@@ -19,11 +19,13 @@ type SessionHandler struct {
// SessionInfo 会话信息
type SessionInfo struct {
ID string `json:"id"`
UserID string `json:"user_id"`
Title string `json:"title"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
ID string `json:"id"`
UserID string `json:"user_id"`
Title string `json:"title"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
MessageCount int `json:"message_count"`
IsActive bool `json:"is_active"`
}
// NewSessionHandler 创建会话处理器
@@ -108,6 +110,21 @@ func (h *SessionHandler) Get(c *gin.Context) {
c.JSON(http.StatusNotFound, gin.H{"error": "会话不存在"})
}
// GetMessages 获取会话的完整消息列表
func (h *SessionHandler) GetMessages(c *gin.Context) {
userID := middleware.GetUserID(c)
sessionID := c.Param("id")
messages := h.hub.GetConversation(userID, sessionID)
if messages == nil {
messages = []ws.Message{}
}
c.JSON(http.StatusOK, gin.H{
"messages": messages,
})
}
// ========== Admin 端点 ==========
// ListActiveSessions 获取当前所有活跃 WebSocket 会话列表 (管理员)
@@ -123,6 +140,18 @@ func (h *SessionHandler) ListActiveSessions(c *gin.Context) {
})
}
// GetActiveSessions 返回按用户分组的活跃会话列表
func (h *SessionHandler) GetActiveSessions(c *gin.Context) {
sessionsByUser := h.hub.GetActiveSessionsByUser()
if sessionsByUser == nil {
sessionsByUser = make(map[string][]*ws.SessionState)
}
c.JSON(http.StatusOK, gin.H{
"users": sessionsByUser,
})
}
// GetSession 获取指定会话的详细信息 (管理员)
func (h *SessionHandler) GetSession(c *gin.Context) {
sessionID := c.Param("id")