Files
verify/sdk/cpp/simple_test.cpp
admin ed4a561704 feat: add application API documentation and C++ SDK
- Add complete API documentation for application integration (docs/API_DOCUMENT.md)
- Fix check-update API to use version ID comparison instead of string comparison
- Add validation: client version must exist in server version list
- Add support for patch/incremental updates with base_version check
- Add C++ SDK with HTTP client, JSON parser, and crypto support
- Add simple_test.cpp for standalone testing on Windows

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 11:58:44 +08:00

448 lines
15 KiB
C++

// 独立测试程序 - 不依赖外部库
// 使用Windows原生WinHTTP进行HTTP请求
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winhttp.h>
#pragma comment(lib, "winhttp.lib")
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#endif
#include "json.hpp"
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <map>
using json = nlohmann::json;
// ============================================================================
// WinHTTP客户端
// ============================================================================
class HttpClient {
public:
HttpClient() {
#ifdef _WIN32
hSession_ = WinHttpOpen(L"VerifySDK/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
#endif
}
~HttpClient() {
#ifdef _WIN32
if (hConnect_) WinHttpCloseHandle(hConnect_);
if (hSession_) WinHttpCloseHandle(hSession_);
#endif
}
std::string get(const std::string& url) {
return request(url, "GET", "");
}
std::string post(const std::string& url, const std::string& body) {
return request(url, "POST", body);
}
private:
#ifdef _WIN32
HINTERNET hSession_ = nullptr;
HINTERNET hConnect_ = nullptr;
#endif
std::string request(const std::string& url, const std::string& method, const std::string& body) {
std::string result;
#ifdef _WIN32
// 解析URL
std::wstring wurl(url.begin(), url.end());
URL_COMPONENTS uc = { sizeof(uc) };
wchar_t host[256] = {0};
wchar_t path[1024] = {0};
uc.lpszHostName = host;
uc.dwHostNameLength = 256;
uc.lpszUrlPath = path;
uc.dwUrlPathLength = 1024;
if (!WinHttpCrackUrl(wurl.c_str(), 0, 0, &uc)) {
throw std::runtime_error("Failed to parse URL");
}
// 连接服务器
std::wstring whost(host);
hConnect_ = WinHttpConnect(hSession_, whost.c_str(), uc.nPort, 0);
if (!hConnect_) {
throw std::runtime_error("Failed to connect to server");
}
// 创建请求
std::wstring wpath(path);
std::wstring wmethod(method.begin(), method.end());
DWORD flags = (uc.nScheme == INTERNET_SCHEME_HTTPS) ? WINHTTP_FLAG_SECURE : 0;
HINTERNET hRequest = WinHttpOpenRequest(hConnect_, wmethod.c_str(), wpath.c_str(),
nullptr, WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES, flags);
if (!hRequest) {
throw std::runtime_error("Failed to create request");
}
// 发送请求
LPCWSTR headers = L"Content-Type: application/json\r\n";
BOOL bResult = WinHttpSendRequest(hRequest, headers, -1,
(LPVOID)body.c_str(), body.length(),
body.length(), 0);
if (!bResult) {
WinHttpCloseHandle(hRequest);
throw std::runtime_error("Failed to send request");
}
// 接收响应
bResult = WinHttpReceiveResponse(hRequest, nullptr);
if (!bResult) {
WinHttpCloseHandle(hRequest);
throw std::runtime_error("Failed to receive response");
}
// 读取数据
DWORD dwSize = 0;
DWORD dwDownloaded = 0;
do {
dwSize = 0;
if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) break;
if (dwSize == 0) break;
std::vector<char> buffer(dwSize + 1);
if (WinHttpReadData(hRequest, &buffer[0], dwSize, &dwDownloaded)) {
result.append(buffer.data(), dwDownloaded);
}
} while (dwSize > 0);
WinHttpCloseHandle(hRequest);
#else
// Linux实现(简化版)
result = curl_request(url, method, body);
#endif
return result;
}
#ifndef _WIN32
std::string curl_request(const std::string& url, const std::string& method, const std::string& body) {
// 简化的socket实现(仅用于演示)
return "{\"code\": 200, \"message\": \"success\", \"data\": {}}";
}
#endif
};
// ============================================================================
// 验证客户端
// ============================================================================
class VerifyClient {
public:
VerifyClient(const std::string& baseUrl, const std::string& appKey)
: baseUrl_(baseUrl), appKey_(appKey) {
// 移除末尾斜杠
if (!baseUrl_.empty() && baseUrl_.back() == '/') {
baseUrl_.pop_back();
}
}
void setToken(const std::string& token) {
token_ = token;
}
// 获取应用信息
json getAppInfo() {
std::string url = baseUrl_ + "/api/v1/app/" + appKey_ + "/info";
std::string response = http_.get(url);
return json::parse(response);
}
// 检查更新
json checkUpdate(const std::string& version = "") {
std::string url = baseUrl_ + "/api/v1/app/" + appKey_ + "/check-update";
if (!version.empty()) {
url += "?version=" + version;
}
std::string response = http_.get(url);
return json::parse(response);
}
// 用户登录
json login(const std::string& username, const std::string& password,
const std::string& deviceId) {
std::string url = baseUrl_ + "/api/v1/app/" + appKey_ + "/login";
json body;
body["username"] = username;
body["password"] = password;
body["device_id"] = deviceId;
std::string response = http_.post(url, body.dump());
json result = json::parse(response);
// 自动设置token
if (result["code"].get_int() == 200 && result["data"].contains("token")) {
token_ = result["data"]["token"].get_string();
}
return result;
}
// 用户注册
json registerUser(const std::string& username, const std::string& password,
const std::string& deviceId) {
std::string url = baseUrl_ + "/api/v1/app/" + appKey_ + "/register";
json body;
body["username"] = username;
body["password"] = password;
body["device_id"] = deviceId;
std::string response = http_.post(url, body.dump());
return json::parse(response);
}
// 心跳
json heartbeat(uint64_t userId, const std::string& deviceId,
const std::string& instanceId = "") {
std::string url = baseUrl_ + "/api/v1/app/" + appKey_ + "/heartbeat";
json body;
body["user_id"] = userId;
body["device_id"] = deviceId;
if (!instanceId.empty()) {
body["instance_id"] = instanceId;
}
std::string response = http_.post(url, body.dump());
return json::parse(response);
}
// 卡密充值
json recharge(const std::string& username, const std::string& cardKey,
const std::string& deviceId) {
std::string url = baseUrl_ + "/api/v1/app/" + appKey_ + "/recharge";
json body;
body["username"] = username;
body["card_key"] = cardKey;
body["device_id"] = deviceId;
std::string response = http_.post(url, body.dump());
return json::parse(response);
}
// 获取设备列表
json getDevices() {
std::string url = baseUrl_ + "/api/v1/app/" + appKey_ + "/devices";
std::string response = http_.get(url);
return json::parse(response);
}
// 获取云端变量
json getVariables() {
std::string url = baseUrl_ + "/api/v1/app/" + appKey_ + "/variables";
std::string response = http_.get(url);
return json::parse(response);
}
private:
std::string baseUrl_;
std::string appKey_;
std::string token_;
HttpClient http_;
};
// ============================================================================
// 测试函数
// ============================================================================
void printSeparator(const std::string& title) {
std::cout << "\n";
std::cout << "========================================\n";
std::cout << title << "\n";
std::cout << "========================================\n";
}
void testJson() {
printSeparator("JSON功能测试");
// 创建JSON对象
json obj = json::object();
obj["name"] = "测试应用";
obj["version"] = "1.0.0";
obj["active"] = true;
obj["count"] = 100;
std::cout << "[创建对象] ";
std::cout << obj.dump() << "\n";
// 创建数组
json arr = json::array();
arr.push_back("item1");
arr.push_back("item2");
arr.push_back(123);
std::cout << "[创建数组] ";
std::cout << arr.dump() << "\n";
// 解析JSON
std::string jsonStr = R"({"code":200,"message":"success","data":{"user_id":1,"token":"abc123"}})";
json parsed = json::parse(jsonStr);
std::cout << "[解析结果]\n";
std::cout << " code: " << parsed["code"].get_int() << "\n";
std::cout << " message: " << parsed["message"].get_string() << "\n";
std::cout << " user_id: " << parsed["data"]["user_id"].get_uint64() << "\n";
std::cout << "\nJSON测试通过!\n";
}
void testApi(const std::string& baseUrl, const std::string& appKey) {
printSeparator("API测试");
std::cout << "服务器: " << baseUrl << "\n";
std::cout << "AppKey: " << appKey << "\n\n";
VerifyClient client(baseUrl, appKey);
// 测试获取应用信息
std::cout << "[测试] 获取应用信息...\n";
try {
json info = client.getAppInfo();
if (info["code"].get_int() == 200) {
std::cout << " [成功] 应用名称: " << info["data"]["name"].get_string() << "\n";
std::cout << " [成功] 状态: " << info["data"]["status"].get_string() << "\n";
} else {
std::cout << " [错误] " << info["message"].get_string() << "\n";
}
} catch (const std::exception& e) {
std::cout << " [异常] " << e.what() << "\n";
}
// 测试检查更新
std::cout << "\n[测试] 检查更新...\n";
try {
json update = client.checkUpdate("1.0.0");
if (update["code"].get_int() == 200) {
std::cout << " [成功] 最新版本: " << update["data"]["latest_version"].get_string() << "\n";
std::cout << " [成功] 有更新: " << (update["data"]["has_update"].get_bool() ? "是" : "否") << "\n";
} else {
std::cout << " [错误] " << update["message"].get_string() << "\n";
}
} catch (const std::exception& e) {
std::cout << " [异常] " << e.what() << "\n";
}
// 测试不存在的版本
std::cout << "\n[测试] 检查不存在的版本...\n";
try {
json update = client.checkUpdate("99.99.99");
if (update["code"].get_int() == 200) {
std::cout << " [意外成功]\n";
} else {
std::cout << " [预期错误] " << update["message"].get_string() << "\n";
}
} catch (const std::exception& e) {
std::cout << " [预期异常] " << e.what() << "\n";
}
// 测试登录
std::cout << "\n[测试] 用户登录...\n";
try {
// 使用测试用户
json loginResult = client.login("test_cpp_user", "test123456", "cpp_test_device");
if (loginResult["code"].get_int() == 200) {
std::cout << " [成功] 用户ID: " << loginResult["data"]["user_id"].get_uint64() << "\n";
std::cout << " [成功] Token已获取\n";
uint64_t userId = loginResult["data"]["user_id"].get_uint64();
// 测试心跳
std::cout << "\n[测试] 心跳上报...\n";
json heartbeat = client.heartbeat(userId, "cpp_test_device", "instance_001");
if (heartbeat["code"].get_int() == 200) {
std::cout << " [成功] 心跳成功\n";
if (heartbeat["data"].contains("balance")) {
std::cout << " [信息] 余额: " << heartbeat["data"]["balance"].get_double() << "\n";
}
}
} else {
std::cout << " [错误] " << loginResult["message"].get_string() << "\n";
}
} catch (const std::exception& e) {
std::cout << " [异常] " << e.what() << "\n";
}
// 测试卡密充值(预期失败)
std::cout << "\n[测试] 卡密充值(无效卡密)...\n";
try {
json recharge = client.recharge("admin", "invalid_card_key", "cpp_test_device");
if (recharge["code"].get_int() == 200) {
std::cout << " [意外成功] 充值成功\n";
} else {
std::cout << " [预期错误] " << recharge["message"].get_string() << "\n";
}
} catch (const std::exception& e) {
std::cout << " [预期异常] " << e.what() << "\n";
}
}
// ============================================================================
// 主函数
// ============================================================================
int main(int argc, char* argv[]) {
std::cout << R"(
╔════════════════════════════════════════════════════════════╗
验证平台 SDK C++ 独立测试
Verify Platform SDK C++ Test
╚════════════════════════════════════════════════════════════╝
)" << "\n";
// 配置
std::string baseUrl = "https://gendan.xyz";
std::string appKey = "your_app_key"; // 替换为实际的appKey
// 解析命令行参数
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i];
if (arg == "--help" || arg == "-h") {
std::cout << "用法: " << argv[0] << " [选项]\n";
std::cout << "选项:\n";
std::cout << " --help, -h 显示帮助信息\n";
std::cout << " --url <URL> 设置服务器URL\n";
std::cout << " --key <APPKEY> 设置应用密钥\n";
std::cout << " --json-only 仅测试JSON功能\n";
return 0;
} else if (arg == "--url" && i + 1 < argc) {
baseUrl = argv[++i];
} else if (arg == "--key" && i + 1 < argc) {
appKey = argv[++i];
} else if (arg == "--json-only") {
testJson();
return 0;
}
}
try {
// JSON测试
testJson();
// API测试
testApi(baseUrl, appKey);
std::cout << "\n========================================\n";
std::cout << "所有测试完成!\n";
std::cout << "========================================\n";
} catch (const std::exception& e) {
std::cerr << "\n错误: " << e.what() << "\n";
return 1;
}
return 0;
}