package main import ( "bytes" "context" "encoding/json" "fmt" "io/ioutil" "net/http" "strings" "time" ) // HTTPClient HTTP客户端结构体,用于替代原来的IPC客户端 type HTTPClient struct { httpClient *http.Client serverURL string ctx context.Context } // NewHTTPClient 创建一个新的HTTP客户端 func NewHTTPClient(ctx context.Context, port int) *HTTPClient { return &HTTPClient{ httpClient: &http.Client{ Timeout: 30 * time.Second, // 设置请求超时时间 }, serverURL: fmt.Sprintf("http://localhost:%d", port), ctx: ctx, } } // SendWxWorkData 向辅助程序的HTTP服务发送企业微信数据 func (c *HTTPClient) SendWxWorkData(clientId string, jsonData string) (bool, error) { // 解析JSON数据以获取消息类型 var message map[string]interface{} timestamp1 := time.Now().Format("2006-01-02 15:04:05.000") globalLogger.Info("[进入HTTP SendWxWorkData请求] 时间: %s", timestamp1) messageTypeValue := -1 if err := json.Unmarshal([]byte(jsonData), &message); err != nil { globalLogger.Warn("解析JSON数据失败: %v, 原始数据: %s", err, jsonData) } else { // 获取消息类型 messageType, typeExists := message["type"] if typeExists { typeValue, ok := messageType.(float64) // JSON解析数字默认为float64 if ok { messageTypeValue = int(typeValue) } } } // 记录所有请求的日志 timestamp := time.Now().Format("2006-01-02 15:04:05.000") globalLogger.Info("[HTTPSendWxWorkData请求] 时间: %s, 客户端ID: %s, 消息类型: %d, 数据: %s", timestamp, clientId, messageTypeValue, jsonData) // 创建请求体 requestBody := map[string]interface{}{ "clientId": clientId, "data": jsonData, } // 序列化请求体 jsonBytes, err := json.Marshal(requestBody) if err != nil { globalLogger.Error("序列化请求体失败: %v", err) return false, err } // 创建HTTP请求 url := fmt.Sprintf("%s/api/send-wxwork-data", c.serverURL) req, err := http.NewRequestWithContext(c.ctx, "POST", url, bytes.NewBuffer(jsonBytes)) if err != nil { globalLogger.Error("创建HTTP请求失败: %v", err) return false, err } // 设置请求头 req.Header.Set("Content-Type", "application/json") // 发送请求 - 添加重试机制 timestamp2 := time.Now().Format("2006-01-02 15:04:05.000") globalLogger.Info("[发送HTTP请求] 时间: %s, URL: %s", timestamp2, url) // 设置重试参数 maxRetries := 3 retryInterval := 1 * time.Second var lastErr error for i := 0; i < maxRetries; i++ { resp, err := c.httpClient.Do(req) if err == nil { defer resp.Body.Close() return handleHTTPResponse(resp, messageTypeValue, clientId) } lastErr = err globalLogger.Error("HTTP请求失败 (尝试 %d/%d): %v, URL: %s", i+1, maxRetries, err, url) // 如果是连接被拒绝的错误,可能是辅助程序刚启动还未准备好,尝试重启辅助程序 errMsg := err.Error() if strings.Contains(errMsg, "connectex: No connection could be made") || strings.Contains(errMsg, "connection refused") { globalLogger.Info("尝试重新启动辅助程序...") // 调用外部的startHelperProgram函数 // 注意:这里需要在main.go中将startHelperProgram声明为可导出的函数 // 或者通过其他方式实现辅助程序的重启 } // 如果不是最后一次尝试,等待一段时间后重试 if i < maxRetries-1 { globalLogger.Info("%d秒后重试...", retryInterval/time.Second) time.Sleep(retryInterval) } } // 所有重试都失败 globalLogger.Error("HTTP请求失败,已尝试所有重试: %v", lastErr) return false, lastErr } // handleHTTPResponse 处理HTTP响应 func handleHTTPResponse(resp *http.Response, messageTypeValue int, clientId string) (bool, error) { timestamp := time.Now().Format("2006-01-02 15:04:05.000") globalLogger.Info("[HTTP响应接收] 时间: %s, 客户端ID: %s, 状态码: %d", timestamp, clientId, resp.StatusCode) // 读取响应体 body, err := ioutil.ReadAll(resp.Body) if err != nil { globalLogger.Error("读取HTTP响应体失败: %v", err) return false, err } globalLogger.Info("[HTTP响应内容] 长度: %d 字节, 内容: %s", len(body), string(body)) // 解析响应体 var result map[string]interface{} if err := json.Unmarshal(body, &result); err != nil { globalLogger.Error("解析HTTP响应体失败: %v, 响应内容: %s", err, string(body)) return false, err } globalLogger.Info("[HTTP响应解析] 成功, 解析结果: %v", result) // 获取success字段 successValue, successExists := result["success"] if !successExists { globalLogger.Error("HTTP响应中缺少success字段") return false, fmt.Errorf("返回结果格式错误") } success, ok := successValue.(bool) if !ok { globalLogger.Error("HTTP响应的success字段类型错误: %T", successValue) return false, fmt.Errorf("返回结果字段类型错误") } // 检查是否包含data字段 if data, exists := result["data"]; exists { globalLogger.Info("[HTTP响应数据] 客户端ID: %s, 数据: %v", clientId, data) } // 记录返回日志(如果是特定类型的消息) timestampReturn := time.Now().Format("2006-01-02 15:04:05.000") globalLogger.Info("[HTTPSendWxWorkData返回] 时间: %s, 客户端ID: %s, 消息类型: %d, 成功: %v", timestampReturn, clientId, messageTypeValue, success) return success, nil } // CheckHealth 检查辅助程序的HTTP服务健康状态 func (c *HTTPClient) CheckHealth() (bool, error) { url := fmt.Sprintf("%s/api/health", c.serverURL) resp, err := c.httpClient.Get(url) if err != nil { globalLogger.Error("健康检查请求失败: %v", err) return false, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { globalLogger.Error("健康检查失败,状态码: %d", resp.StatusCode) return false, fmt.Errorf("健康检查失败,状态码: %d", resp.StatusCode) } return true, nil } // GetJSON calls a helper HTTP endpoint and decodes its JSON body. func (c *HTTPClient) GetJSON(path string) (map[string]interface{}, error) { url := fmt.Sprintf("%s%s", c.serverURL, path) req, err := http.NewRequestWithContext(c.ctx, "GET", url, nil) if err != nil { return nil, err } resp, err := c.httpClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } var result map[string]interface{} if err := json.Unmarshal(body, &result); err != nil { return nil, fmt.Errorf("解析响应失败: %v, body=%s", err, string(body)) } if resp.StatusCode < 200 || resp.StatusCode >= 300 { return result, fmt.Errorf("HTTP状态码错误: %d", resp.StatusCode) } return result, nil } // PostJSON calls a helper HTTP endpoint with a JSON payload. func (c *HTTPClient) PostJSON(path string, payload interface{}) (map[string]interface{}, error) { jsonBytes, err := json.Marshal(payload) if err != nil { return nil, err } url := fmt.Sprintf("%s%s", c.serverURL, path) req, err := http.NewRequestWithContext(c.ctx, "POST", url, bytes.NewBuffer(jsonBytes)) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/json") resp, err := c.httpClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } var result map[string]interface{} if err := json.Unmarshal(body, &result); err != nil { return nil, fmt.Errorf("解析响应失败: %v, body=%s", err, string(body)) } if resp.StatusCode < 200 || resp.StatusCode >= 300 { return result, fmt.Errorf("HTTP状态码错误: %d", resp.StatusCode) } return result, nil }