Files
qiweimanager-master/http_client.go

246 lines
7.5 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}