fix: 前端消息拆分+动作消息样式+DevTools自主思考状态保持+记忆表名修复
- 侧边栏底部 "昔涟 AI" 改为 "昔涟" - 暂时禁用消息朗读按钮 - 修复前端 response 处理器:支持 gateway 发送的 content+role+msg_type 字段, 使动作消息(括号内容)正确拆分为独立的 ActionMessageBubble 显示 - 修复 DevTools 自主思考面板:5秒自动刷新后展开的思考日志不再自动折叠 - 修复 memory-service 表名不一致:memory_entries → memories, 解决 DevTools 记忆管理页面查不到 admin 用户记忆的问题 - 修复 sessionStore 解析历史消息时 msgType 未定义引用 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -454,6 +454,30 @@ func (h *ChatHandler) handleHistoryRequest(client *ws.Client, msg ws.ClientMessa
|
||||
}
|
||||
|
||||
messages := h.hub.GetConversation(client.UserID, sessionID)
|
||||
|
||||
// 如果内存缓存为空,尝试从数据库恢复(网关重启后缓存丢失的情况)
|
||||
if len(messages) == 0 && h.sessionStore != nil && h.sessionStore.IsAvailable() {
|
||||
dbMessages, err := h.sessionStore.GetMessages(sessionID, 50, 0)
|
||||
if err == nil && len(dbMessages) > 0 {
|
||||
log.Printf("[history] 从数据库恢复会话历史: session=%s, %d 条消息", sessionID, len(dbMessages))
|
||||
// 恢复到内存缓存
|
||||
for _, dbMsg := range dbMessages {
|
||||
messages = append(messages, ws.Message{
|
||||
ID: fmt.Sprintf("db_%d", dbMsg.ID),
|
||||
Role: dbMsg.Role,
|
||||
Content: dbMsg.Content,
|
||||
Timestamp: dbMsg.CreatedAt.UnixMilli(),
|
||||
})
|
||||
h.hub.CacheMessage(client.UserID, sessionID, ws.Message{
|
||||
ID: fmt.Sprintf("db_%d", dbMsg.ID),
|
||||
Role: dbMsg.Role,
|
||||
Content: dbMsg.Content,
|
||||
Timestamp: dbMsg.CreatedAt.UnixMilli(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if messages == nil {
|
||||
messages = []ws.Message{}
|
||||
}
|
||||
@@ -488,6 +512,73 @@ func (h *ChatHandler) SendSystemMessage(userID, sessionID, text string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// HandleProactiveMessage 处理来自 AI-Core 后台思考的主动消息
|
||||
// POST /api/v1/internal/proactive-message
|
||||
func (h *ChatHandler) HandleProactiveMessage(c *gin.Context) {
|
||||
var req struct {
|
||||
UserID string `json:"user_id" binding:"required"`
|
||||
Content string `json:"content" binding:"required"`
|
||||
SessionID string `json:"session_id"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数无效: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 检查用户是否在线
|
||||
onlineCount := h.hub.UserClientCount(req.UserID)
|
||||
if onlineCount == 0 {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": false,
|
||||
"reason": "user_offline",
|
||||
"message": "用户不在线,消息未发送",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 构建主动消息
|
||||
msgID := "proactive_" + generateID()
|
||||
msg := ws.ServerMessage{
|
||||
Type: "response",
|
||||
MessageID: msgID,
|
||||
Content: req.Content,
|
||||
Role: "assistant",
|
||||
MsgType: "proactive",
|
||||
SessionID: req.SessionID,
|
||||
Timestamp: time.Now().UnixMilli(),
|
||||
}
|
||||
|
||||
data, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
log.Printf("[proactive] 序列化消息失败: %v", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "内部错误"})
|
||||
return
|
||||
}
|
||||
|
||||
h.hub.SendToUser(req.UserID, data)
|
||||
|
||||
// 同时缓存到对话历史(使用 admin 的主 session)
|
||||
sessionID := req.SessionID
|
||||
if sessionID == "" {
|
||||
sessionID = "session_admin_main"
|
||||
}
|
||||
h.hub.CacheMessage(req.UserID, sessionID, ws.Message{
|
||||
ID: msgID,
|
||||
Role: "assistant",
|
||||
Content: req.Content,
|
||||
Timestamp: time.Now().UnixMilli(),
|
||||
})
|
||||
h.hub.RecordMessage(sessionID, "assistant", req.Content)
|
||||
|
||||
log.Printf("[proactive] 主动消息已推送: user=%s, online=%d, content_len=%d", req.UserID, onlineCount, len(req.Content))
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "消息已推送",
|
||||
"delivered": onlineCount,
|
||||
})
|
||||
}
|
||||
|
||||
func generateID() string {
|
||||
return time.Now().Format("20060102150405") + randomStr(6)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user