192 lines
4.3 KiB
Go
192 lines
4.3 KiB
Go
|
package devicesdk
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"crypto/aes"
|
||
|
"crypto/cipher"
|
||
|
"encoding/base64"
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"net/http"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
type DeviceClient struct {
|
||
|
baseURL string
|
||
|
encryptKey string
|
||
|
client *http.Client
|
||
|
}
|
||
|
|
||
|
type DeviceInfo struct {
|
||
|
UID string `json:"uid"`
|
||
|
DeviceModel string `json:"device_model"`
|
||
|
LicenseCode string `json:"license_code,omitempty"`
|
||
|
}
|
||
|
|
||
|
type ValidateResponse struct {
|
||
|
Status string `json:"status"`
|
||
|
LicenseType string `json:"license_type"`
|
||
|
ExpireTime time.Time `json:"expire_time"`
|
||
|
StartCount int `json:"start_count"`
|
||
|
MaxUses int `json:"max_uses"`
|
||
|
Timestamp int64 `json:"timestamp"`
|
||
|
}
|
||
|
|
||
|
type ApiResponse struct {
|
||
|
Code int `json:"code"`
|
||
|
Message string `json:"message"`
|
||
|
Error string `json:"error"`
|
||
|
Data interface{} `json:"data"`
|
||
|
}
|
||
|
|
||
|
func NewDeviceClient(baseURL, encryptKey string) *DeviceClient {
|
||
|
return &DeviceClient{
|
||
|
baseURL: baseURL,
|
||
|
encryptKey: encryptKey,
|
||
|
client: &http.Client{Timeout: time.Second * 30},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (c *DeviceClient) RegisterDevice(info *DeviceInfo) error {
|
||
|
data, err := json.Marshal(info)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("序列化请求数据失败: %v", err)
|
||
|
}
|
||
|
|
||
|
resp, err := c.post("/api/devices/register", data)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if resp.Code != 0 {
|
||
|
return fmt.Errorf(resp.Error)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (c *DeviceClient) ValidateDevice(uid string) (*ValidateResponse, error) {
|
||
|
resp, err := c.get(fmt.Sprintf("/api/devices/%s/validate", uid))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
if resp.Code != 0 {
|
||
|
return nil, fmt.Errorf(resp.Error)
|
||
|
}
|
||
|
|
||
|
decrypted, err := c.decryptResponse(resp.Data.(string))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
var validateResp ValidateResponse
|
||
|
if err := json.Unmarshal([]byte(decrypted), &validateResp); err != nil {
|
||
|
return nil, fmt.Errorf("解析响应数据失败: %v", err)
|
||
|
}
|
||
|
|
||
|
return &validateResp, nil
|
||
|
}
|
||
|
|
||
|
func (c *DeviceClient) UpdateStartCount(uid string) (int, error) {
|
||
|
resp, err := c.post(fmt.Sprintf("/api/devices/%s/start", uid), nil)
|
||
|
if err != nil {
|
||
|
return 0, err
|
||
|
}
|
||
|
|
||
|
if resp.Code != 0 {
|
||
|
return 0, fmt.Errorf(resp.Error)
|
||
|
}
|
||
|
|
||
|
data := resp.Data.(map[string]interface{})
|
||
|
return int(data["start_count"].(float64)), nil
|
||
|
}
|
||
|
|
||
|
func (c *DeviceClient) BindLicense(uid, licenseCode string) error {
|
||
|
data, err := json.Marshal(map[string]string{
|
||
|
"license_code": licenseCode,
|
||
|
})
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("序列化请求数据失败: %v", err)
|
||
|
}
|
||
|
|
||
|
resp, err := c.post(fmt.Sprintf("/api/devices/%s/license", uid), data)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if resp.Code != 0 {
|
||
|
return fmt.Errorf(resp.Error)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// HTTP 请求辅助方法
|
||
|
func (c *DeviceClient) get(path string) (*ApiResponse, error) {
|
||
|
resp, err := c.client.Get(c.baseURL + path)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("HTTP请求失败: %v", err)
|
||
|
}
|
||
|
defer resp.Body.Close()
|
||
|
|
||
|
return c.parseResponse(resp)
|
||
|
}
|
||
|
|
||
|
func (c *DeviceClient) post(path string, data []byte) (*ApiResponse, error) {
|
||
|
var resp *http.Response
|
||
|
var err error
|
||
|
|
||
|
if data == nil {
|
||
|
resp, err = c.client.Post(c.baseURL+path, "application/json", nil)
|
||
|
} else {
|
||
|
resp, err = c.client.Post(c.baseURL+path, "application/json", bytes.NewBuffer(data))
|
||
|
}
|
||
|
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("HTTP请求失败: %v", err)
|
||
|
}
|
||
|
defer resp.Body.Close()
|
||
|
|
||
|
return c.parseResponse(resp)
|
||
|
}
|
||
|
|
||
|
func (c *DeviceClient) parseResponse(resp *http.Response) (*ApiResponse, error) {
|
||
|
body, err := io.ReadAll(resp.Body)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("读取响应数据失败: %v", err)
|
||
|
}
|
||
|
|
||
|
var apiResp ApiResponse
|
||
|
if err := json.Unmarshal(body, &apiResp); err != nil {
|
||
|
return nil, fmt.Errorf("解析响应数据失败: %v", err)
|
||
|
}
|
||
|
|
||
|
return &apiResp, nil
|
||
|
}
|
||
|
|
||
|
func (c *DeviceClient) decryptResponse(encrypted string) (string, error) {
|
||
|
ciphertext, err := base64.StdEncoding.DecodeString(encrypted)
|
||
|
if err != nil {
|
||
|
return "", fmt.Errorf("base64解码失败: %v", err)
|
||
|
}
|
||
|
|
||
|
block, err := aes.NewCipher([]byte(c.encryptKey))
|
||
|
if err != nil {
|
||
|
return "", fmt.Errorf("创建AES cipher失败: %v", err)
|
||
|
}
|
||
|
|
||
|
if len(ciphertext) < aes.BlockSize {
|
||
|
return "", fmt.Errorf("密文长度不足")
|
||
|
}
|
||
|
|
||
|
iv := ciphertext[:aes.BlockSize]
|
||
|
ciphertext = ciphertext[aes.BlockSize:]
|
||
|
|
||
|
stream := cipher.NewCFBDecrypter(block, iv)
|
||
|
stream.XORKeyStream(ciphertext, ciphertext)
|
||
|
|
||
|
return string(ciphertext), nil
|
||
|
}
|