fix: 第一轮修复 - 记忆管理/IoT操控/历史消息持久化/动作消息/链路优化/安全配置

- 修复记忆管理数据库连接不可用 (ai-core重编译+Unicode修复)
- 修复IoT子会话工具调用链路日志缺失
- 新增最终审查子会话(review_provider) 支持消息格式解析拆分
- 实现历史消息持久化(后端存储+前端分页加载)
- 前端新增动作消息(ActionMessage)类型和渲染
- 优化对话链路速度(非阻塞子会话+快速问候通道)
- JWT密钥环境变量化(无默认值启动panic)
- Token自动刷新机制(401拦截器+refresh接口)
- WebSocket指数退避重连(jitter+最大10次)
- localStorage清理一致性(cyrene_前缀+版本检查)
- IoT环境变量统一为IOT_SERVICE_URL
This commit is contained in:
2026-05-21 23:10:07 +08:00
parent 8b7d4ec19a
commit a058b0ab8e
53 changed files with 5535 additions and 241 deletions
@@ -14,9 +14,15 @@ type Config struct {
// Load 从环境变量加载配置
func Load() *Config {
// 向后兼容:优先使用 IOT_SERVICE_URL,回退到 IOT_DEBUG_SERVICE_URL
iotURL := os.Getenv("IOT_SERVICE_URL")
if iotURL == "" {
iotURL = getEnv("IOT_DEBUG_SERVICE_URL", "http://localhost:8083")
}
return &Config{
Port: getEnv("PORT", "8092"),
IoTServiceURL: getEnv("IOT_SERVICE_URL", "http://localhost:8083"),
IoTServiceURL: iotURL,
DataDir: getEnv("DATA_DIR", "/tmp/cyrene_data"),
DBUrl: getEnv("DB_URL", ""),
}
@@ -102,21 +102,27 @@ func (c *IoTClient) GetDevice(id string) (*IoTDevice, error) {
// ToggleDevice 切换设备开关状态
func (c *IoTClient) ToggleDevice(id string) error {
log.Printf("[tool-engine:IoT-client] 🔄 切换设备: id=%s, url=%s", id, c.baseURL+"/api/v1/devices/"+id+"/toggle")
req, err := http.NewRequest(http.MethodPost, c.baseURL+"/api/v1/devices/"+id+"/toggle", nil)
if err != nil {
log.Printf("[tool-engine:IoT-client] ❌ 创建切换请求失败: device=%s, err=%v", id, err)
return fmt.Errorf("创建切换请求失败: %w", err)
}
resp, err := c.client.Do(req)
if err != nil {
log.Printf("[tool-engine:IoT-client] ❌ 切换设备 HTTP 失败: device=%s, err=%v", id, err)
return fmt.Errorf("切换设备 %s 失败: %w", id, err)
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusNotFound {
log.Printf("[tool-engine:IoT-client] ❌ 设备不存在: %s", id)
return fmt.Errorf("设备 %s 不存在", id)
}
if resp.StatusCode != http.StatusOK {
log.Printf("[tool-engine:IoT-client] ❌ 切换设备返回非200: device=%s, status=%d", id, resp.StatusCode)
return fmt.Errorf("切换设备 %s 返回状态码 %d", id, resp.StatusCode)
}
@@ -125,21 +131,26 @@ func (c *IoTClient) ToggleDevice(id string) error {
c.cache = nil
c.mu.Unlock()
log.Printf("[tool-engine:IoT-client] ✅ 切换设备成功: %s", id)
return nil
}
// SetDeviceProperty 设置设备属性(温度、亮度、位置、模式、颜色等)
func (c *IoTClient) SetDeviceProperty(id string, field string, value interface{}) error {
log.Printf("[tool-engine:IoT-client] 🔧 设置设备属性: device=%s, field=%s, value=%v, url=%s", id, field, value, c.baseURL+"/api/v1/devices/"+id+"/set")
body, err := json.Marshal(map[string]interface{}{
"field": field,
"value": value,
})
if err != nil {
log.Printf("[tool-engine:IoT-client] ❌ 序列化请求失败: device=%s, err=%v", id, err)
return fmt.Errorf("序列化请求失败: %w", err)
}
req, err := http.NewRequest(http.MethodPost, c.baseURL+"/api/v1/devices/"+id+"/set", nil)
if err != nil {
log.Printf("[tool-engine:IoT-client] ❌ 创建设置请求失败: device=%s, err=%v", id, err)
return fmt.Errorf("创建设置请求失败: %w", err)
}
req.Header.Set("Content-Type", "application/json")
@@ -147,11 +158,13 @@ func (c *IoTClient) SetDeviceProperty(id string, field string, value interface{}
resp, err := c.client.Do(req)
if err != nil {
log.Printf("[tool-engine:IoT-client] ❌ 设置设备属性 HTTP 失败: device=%s, field=%s, err=%v", id, field, err)
return fmt.Errorf("设置设备 %s 属性失败: %w", id, err)
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusNotFound {
log.Printf("[tool-engine:IoT-client] ❌ 设备不存在: %s", id)
return fmt.Errorf("设备 %s 不存在", id)
}
if resp.StatusCode != http.StatusOK {
@@ -160,8 +173,10 @@ func (c *IoTClient) SetDeviceProperty(id string, field string, value interface{}
}
json.NewDecoder(resp.Body).Decode(&errResp)
if errResp.Error != "" {
log.Printf("[tool-engine:IoT-client] ❌ 设置设备属性失败: device=%s, err=%s", id, errResp.Error)
return fmt.Errorf("设置设备 %s 属性失败: %s", id, errResp.Error)
}
log.Printf("[tool-engine:IoT-client] ❌ 设置设备属性返回非200: device=%s, status=%d", id, resp.StatusCode)
return fmt.Errorf("设置设备 %s 属性返回状态码 %d", id, resp.StatusCode)
}
@@ -170,6 +185,7 @@ func (c *IoTClient) SetDeviceProperty(id string, field string, value interface{}
c.cache = nil
c.mu.Unlock()
log.Printf("[tool-engine:IoT-client] ✅ 设置设备属性成功: device=%s, field=%s, value=%v", id, field, value)
return nil
}