You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

423 lines
11 KiB

package api_test
import (
"bytes"
"yunyan/comm"
"yunyan/lego/sys/mysql"
"yunyan/pb"
"yunyan/utils/license"
"yunyan/utils/mac"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"strconv"
"strings"
"testing"
"time"
)
// 生成自定义 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<<seed | 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_JIAMI(t *testing.T) {
pid, err := license.ProductID("0D82F1CCD83D72B78FA9")
fmt.Printf("pid:%X, err:%v\n", pid, err)
}
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)
}
func Test_DB(t *testing.T) {
//jdbc:mysql://139.224.212.237:3307/admin?user=root&password=li13451234&allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC
if sys, err := mysql.NewSys(
mysql.SetMySQLDsn("root:li13451234@tcp(139.224.212.237:3307)/admin?charset=utf8mb4&parseTime=True&loc=Local"),
); 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
}
}