Initial qiwei secondary development handoff

This commit is contained in:
2026-06-23 21:11:20 +08:00
commit 858cb68f4f
207 changed files with 52782 additions and 0 deletions

245
http_client.go Normal file
View File

@@ -0,0 +1,245 @@
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
}