package api_test import ( "bytes" "encoding/hex" "encoding/json" "fmt" "io" "net/http" "os" "strconv" "strings" "testing" "time" "yunyan/comm" "yunyan/lego/sys/mysql" "yunyan/pb" "yunyan/utils/mac" ) // 生成自定义 MAC 地址(64 位 / 8 字节) func generateCustomMAC(devicetype byte, factoryid byte, batchid uint32, serial uint32) (string, error) { if serial > 0xFFFFFF { return "", fmt.Errorf("serial number exceeds 3-byte limit (max 16777215)") } mac := make([]byte, 8) // Byte 0: 设备类型 mac[0] = devicetype // Byte 1: 厂商 ID mac[1] = factoryid // Byte 2~4: 批次 ID(日期压缩或编号) mac[2] = byte((batchid >> 16) & 0xFF) mac[3] = byte((batchid >> 8) & 0xFF) mac[4] = byte(batchid & 0xFF) // Byte 5~7: 序号(3字节) mac[5] = byte((serial >> 16) & 0xFF) mac[6] = byte((serial >> 8) & 0xFF) mac[7] = byte(serial & 0xFF) // 格式化成 MAC 字符串:每个字节2位,共 8 段 return fmt.Sprintf("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], mac[6], mac[7]), nil } // 解析自定义 MAC 地址字符串为结构体数据 func parseCustomMAC(macStr string) (devicetype, factoryid byte, batchid, serial uint32, err error) { // 去掉冒号并转换为字节 macStr = strings.ReplaceAll(macStr, ":", "") data, err := hex.DecodeString(macStr) if err != nil { return } if len(data) != 8 { err = fmt.Errorf("invalid MAC length: expected 8 bytes, got %d", len(data)) return } // Byte 0: 设备类型 devicetype = data[0] // Byte 1: 厂商 ID factoryid = data[1] // Byte 2~4: 批次 ID batchid = uint32(data[2])<<16 | uint32(data[3])<<8 | uint32(data[4]) // Byte 5~7: 序号 serial = uint32(data[5])<<16 | uint32(data[6])<<8 | uint32(data[7]) return } func Test_Sys_Chat(t *testing.T) { // str, _ := GetQWeather("上海") // fmt.Println(str) mac, err := generateCustomMAC(0x01, 0x02, 250624, 123456) if err != nil { fmt.Println("Error:", err) return } fmt.Println("MAC Address:", mac) devtype, factory, batchid, serial, err := parseCustomMAC(mac) if err != nil { fmt.Println("Error:", err) return } fmt.Printf("设备类型: 0x%02X\n厂商 ID: 0x%02X\n批次 ID: %d\n序列号: %d\n", devtype, factory, batchid, serial) } func GetQWeather(city string) (value string, err error) { var ( georesp *http.Response resp *http.Response // body []byte ) apiKey := "9249776ad0a34213ba4a698931fb16f6" // 1. 获取城市ID geoUrl := fmt.Sprintf("https://geoapi.qweather.com/v2/city/lookup?location=%s&key=%s", city, apiKey) georesp, err = http.Get(geoUrl) if err != nil { return "", err } defer georesp.Body.Close() var geoData map[string]interface{} json.NewDecoder(georesp.Body).Decode(&geoData) cityID := geoData["location"].([]interface{})[0].(map[string]interface{})["id"].(string) fmt.Printf("获取城市id:%s\n", cityID) // 2. 获取天气数据 // https://api.qweather.com/v7/weather/now?location=101010100&&key=9249776ad0a34213ba4a698931fb16f6 weatherUrl := fmt.Sprintf("https://api.qweather.com/v7/weather/now?location=%s&key=%s", cityID, apiKey) if resp, err = http.Get(weatherUrl); err != nil { return } defer resp.Body.Close() // body, err = io.ReadAll(resp.Body) var weatherData map[string]interface{} json.NewDecoder(resp.Body).Decode(&weatherData) temp := weatherData["now"].(map[string]interface{})["temp"].(string) // 温度 text := weatherData["now"].(map[string]interface{})["text"].(string) // 天气状况 // return string(body), nil value = fmt.Sprintf("%s: %s℃, %s", city, temp, text) return } func Test_TavilyApi(t *testing.T) { // API配置 apiURL := "https://api.tavily.com/search" apiKey := "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" // 替换为你的实际API密钥 // 创建请求体 requestBody := struct { Query string `json:"query"` }{ Query: "今天武汉的天气", // 你可以修改查询内容 } // 序列化请求体 jsonData, err := json.Marshal(requestBody) if err != nil { fmt.Printf("Error marshaling JSON: %v\n", err) return } // 创建HTTP请求 req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonData)) if err != nil { fmt.Printf("Error creating request: %v\n", err) return } // 设置请求头 req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+apiKey) // 发送请求 client := &http.Client{} resp, err := client.Do(req) if err != nil { fmt.Printf("Error making request: %v\n", err) return } defer resp.Body.Close() // 读取响应体 body, err := io.ReadAll(resp.Body) if err != nil { fmt.Printf("Error reading response body: %v\n", err) return } // 处理响应 fmt.Printf("Status Code: %d\n", resp.StatusCode) fmt.Printf("Response Body:\n%s\n", body) } //  测试api返回效率 func Test_API_AI(t *testing.T) { // 测试参数 apiURL := "http://ideapsound.com/ai/funcchat" // 替换为你的实际地址 testMessage := &AIChatReq{ Msg: "今天的天气如何", Contexts: []string{ "我在武汉", }, } // 运行测试 if err := testFuncChat(apiURL, testMessage); err != nil { fmt.Printf("测试失败: %v\n", err) } } type AIChatReq struct { Msg string `json:"msg"` Contexts []string `json:"contexts"` } type SSEEvent struct { Event string Data string } func testFuncChat(url string, req *AIChatReq) error { // 准备请求体 reqBody, err := json.Marshal(req) if err != nil { return fmt.Errorf("编码请求体失败: %w", err) } // 创建HTTP请求 httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(reqBody)) if err != nil { return fmt.Errorf("创建请求失败: %w", err) } httpReq.Header.Set("Accept", "text/event-stream") httpReq.Header.Set("Content-Type", "application/json") // 记录开始时间 startTime := time.Now() var firstResponseTime time.Duration // 发送请求 client := &http.Client{} resp, err := client.Do(httpReq) if err != nil { return fmt.Errorf("请求失败: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("非200状态码: %d", resp.StatusCode) } // 读取SSE流 buf := make([]byte, 4096) gotFirstResponse := false totalBytes := 0 eventCount := 0 for { n, err := resp.Body.Read(buf) if n > 0 { totalBytes += n if !gotFirstResponse { firstResponseTime = time.Since(startTime) gotFirstResponse = true fmt.Printf("收到第一个响应时间: %v\n", firstResponseTime) } // 简单解析SSE事件 events := parseSSE(buf[:n]) eventCount += len(events) for _, event := range events { fmt.Printf("事件[%s]: %s\n", event.Event, event.Data) } } if err != nil { if err == io.EOF { break } return fmt.Errorf("读取响应失败: %w", err) } } // 输出统计信息 totalTime := time.Since(startTime) fmt.Println("\n测试结果:") fmt.Printf("第一个响应时间: %v\n", firstResponseTime) fmt.Printf("总传输时间: %v\n", totalTime) fmt.Printf("接收事件数量: %d\n", eventCount) fmt.Printf("总数据量: %d bytes\n", totalBytes) fmt.Printf("平均传输速率: %.2f bytes/ms\n", float64(totalBytes)/totalTime.Seconds()/1000) return nil } // 简单SSE解析器 func parseSSE(data []byte) []SSEEvent { var events []SSEEvent var currentEvent SSEEvent lines := bytes.Split(data, []byte("\n")) for _, line := range lines { if len(line) == 0 { continue } parts := bytes.SplitN(line, []byte(":"), 2) if len(parts) < 2 { continue } field := string(parts[0]) value := string(parts[1]) if len(value) > 0 && value[0] == ' ' { value = value[1:] } switch field { case "event": currentEvent.Event = value case "data": currentEvent.Data = value events = append(events, currentEvent) currentEvent = SSEEvent{} } } return events } // 混淆函数(修复移位错误) func obfuscate(numStr string, seed int) string { // 限制 seed 范围为 1 到 31 seed = seed % 31 if seed == 0 { seed = 1 // 避免移位 0 位 } // 将字符串转为数字 num, _ := strconv.Atoi(numStr) // 使用 uint32 确保无符号移位 num32 := uint32(num) obfNum := int((num32<>(32-seed)) ^ uint32(seed)) // 转为字符串并确保长度一致 result := fmt.Sprintf("%0*d", len(numStr), obfNum%int(pow10(len(numStr)))) return result } // 计算10的n次方 func pow10(n int) int { result := 1 for i := 0; i < n; i++ { result *= 10 } return result } func Test_GenerateMac(t *testing.T) { // 测试生成设备 MAC 地址(productid + 产品级序号) productid := uint16(0x0302) serial := uint32(1000000) code := mac.Generate(productid, serial) fmt.Println("生成的MAC地址:", code) // 测试解析 MAC 地址 parsedProductid, parsedSerial, err := mac.Parse(code) if err != nil { t.Errorf("解析MAC地址失败: %v", err) } // 验证解析结果是否与原始输入一致 if parsedProductid != productid { t.Errorf("解析的productid不匹配: 期望 0x%04X, 实际 0x%04X", productid, parsedProductid) } if parsedSerial != serial { t.Errorf("解析的serial不匹配: 期望 %d, 实际 %d", serial, parsedSerial) } fmt.Printf("解析结果: productid=0x%04X, serial=%d\n", parsedProductid, parsedSerial) } // Test_DB 连真实库建表/查数据做一次性排查,默认跳过。 // // DSN 从环境变量取,未设置即跳过。 // ⚠️ 原先写死的是云雁上海机的地址**和明文库密码**——那台机 2026-09-13 已从本仓库移除 // (本机没有它的钥匙),写死的凭据留在 git 里只是风险,跑起来也只会连不上然后静默 return。 // // API_TEST_DSN='root:pwd@tcp(host:3306)/admin?charset=utf8mb4&parseTime=True&loc=Local' \ // go test -v -run Test_DB ./modules/api/ func Test_DB(t *testing.T) { dsn := os.Getenv("API_TEST_DSN") if dsn == "" { t.Skip("未设置 API_TEST_DSN,跳过") } if sys, err := mysql.NewSys( mysql.SetMySQLDsn(dsn), ); err != nil { fmt.Printf("err:%v", err) return } else { // if err = sys.CreateTable(comm.TableLicense, &pb.DBAuthCode{}); err != nil { // fmt.Printf("创建表失败: %v", err) // return // } if err = sys.CreateTable(comm.TableBrand, &pb.DBBrand{}); err != nil { fmt.Printf("创建表失败: %v", err) } else { //设置brand表的主键从0xA001开始 sys.Exec(fmt.Sprintf("ALTER TABLE %s AUTO_INCREMENT = %d", comm.TableBrand, 0xA001)) } if err = sys.CreateTable(comm.TableProductionBatch, &pb.DBProductionBatch{}); err != nil { fmt.Printf("创建表失败: %v", err) } if err = sys.CreateTable(comm.TableProduct, &pb.DBProduct{}); err != nil { fmt.Printf("创建表失败: %v", err) } else { //设置product表的主键从1000开始 sys.Exec(fmt.Sprintf("ALTER TABLE %s AUTO_INCREMENT = %d", comm.TableProduct, 0xB001)) } if err = sys.CreateTable(comm.TableProductVersion, &pb.DBProductVersion{}); err != nil { fmt.Printf("创建表失败: %v", err) } // if err = sys.CreateTable(comm.TableLicense, &pb.DBAuthCode{}); err != nil { // fmt.Printf("创建表失败: %v", err) // } if err = sys.CreateTable(comm.TableGoods, &pb.DBGoods{}); err != nil { fmt.Printf("创建表失败: %v", err) } if err = sys.CreateTable(comm.TableWakeupVoice, &pb.DBWakeupVoice{}); err != nil { fmt.Printf("创建表失败: %v", err) } else { //设置product表的主键从1000开始 sys.Exec(fmt.Sprintf("ALTER TABLE %s AUTO_INCREMENT = %d", comm.TableWakeupVoice, 0xD001)) } return } }