更新配置文件,添加批处理回调服务器地址和令牌的默认值;优化批处理操作处理逻辑

This commit is contained in:
sladro 2026-03-01 18:43:23 +08:00
parent a3b2765419
commit f6d867dae4
2 changed files with 310 additions and 21 deletions

View File

@ -9,6 +9,8 @@ public:
// API配置 // API配置
static constexpr const char* API_PREFIX = "/api"; static constexpr const char* API_PREFIX = "/api";
static constexpr const char* BATCH_CALLBACK_SERVER_ADDRESS = "";
static constexpr const char* BATCH_CALLBACK_TOKEN = "";
// 超时配置 // 超时配置
static const int HTTP_TIMEOUT_MS = 30000; // 30秒 static const int HTTP_TIMEOUT_MS = 30000; // 30秒

View File

@ -13,13 +13,22 @@
#include "HierarchyStatisticsAnalyzer.h" #include "HierarchyStatisticsAnalyzer.h"
#include "ComponentChildrenManager.h" #include "ComponentChildrenManager.h"
#include "BatchOperationManager.h" #include "BatchOperationManager.h"
#include "Config.h"
#include <wfcSession.h> #include <wfcSession.h>
#include <wfcGlobal.h> #include <wfcGlobal.h>
#include <Windows.h>
#include <winhttp.h>
#include <string> #include <string>
#include <sstream> #include <sstream>
#include <regex> #include <regex>
#include <atomic> #include <atomic>
#include <memory> #include <memory>
#include <chrono>
#include <ctime>
#include <thread>
#include <vector>
#pragma comment(lib, "winhttp.lib")
#ifdef _DEBUG #ifdef _DEBUG
#define new DEBUG_NEW #define new DEBUG_NEW
@ -385,6 +394,256 @@ std::string EscapeJsonString(const std::string& str) {
return escaped; return escaped;
} }
std::wstring Utf8ToWide(const std::string& input) {
if (input.empty()) {
return L"";
}
int required = MultiByteToWideChar(CP_UTF8, 0, input.c_str(), -1, nullptr, 0);
if (required <= 0) {
return L"";
}
std::wstring output(required, L'\0');
MultiByteToWideChar(CP_UTF8, 0, input.c_str(), -1, &output[0], required);
if (!output.empty() && output.back() == L'\0') {
output.pop_back();
}
return output;
}
std::string GetCurrentIsoUtcTimeString() {
std::time_t now = std::time(nullptr);
std::tm utc_tm{};
gmtime_s(&utc_tm, &now);
char buffer[32] = {0};
std::strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%SZ", &utc_tm);
return std::string(buffer);
}
std::string BuildBatchCallbackUrl(const std::string& callback_server_address) {
if (callback_server_address.empty()) {
return "";
}
std::string base = callback_server_address;
while (!base.empty() && base.back() == '/') {
base.pop_back();
}
return base + "/api/v1/plugin-callbacks/task-result";
}
std::string BuildBatchCallbackResultJson(const BatchOperationsResponse& batch_result) {
std::ostringstream json;
json << "{"
<< "\"total_operations\": " << batch_result.total_operations << ","
<< "\"successful_operations\": " << batch_result.successful_operations << ","
<< "\"failed_operations\": " << batch_result.failed_operations << ","
<< "\"execution_time\": \"" << EscapeJsonString(batch_result.execution_time) << "\","
<< "\"results\": [";
for (size_t i = 0; i < batch_result.results.size(); ++i) {
if (i > 0) json << ",";
const BatchOperationResult& op_result = batch_result.results[i];
json << "{"
<< "\"operation_index\": " << op_result.operation_index << ","
<< "\"operation_type\": \"" << EscapeJsonString(op_result.operation_type) << "\","
<< "\"success\": " << (op_result.success ? "true" : "false") << ","
<< "\"execution_time\": \"" << EscapeJsonString(op_result.execution_time) << "\"";
if (op_result.success) {
json << ",\"result\": " << op_result.result_json << ",\"error\": null";
} else {
json << ",\"result\": null,"
<< "\"error\": \"" << EscapeJsonString(op_result.error_message) << "\"";
}
json << "}";
}
json << "]"
<< "}";
return json.str();
}
std::string BuildTaskResultCallbackPayload(const std::string& execution_id,
const std::string& software_id,
bool is_success,
const std::string& error_message,
const std::string& result_json,
const std::string& token) {
std::ostringstream json;
json << "{"
<< "\"execution_id\": \"" << EscapeJsonString(execution_id) << "\","
<< "\"software_id\": \"" << EscapeJsonString(software_id) << "\","
<< "\"status\": \"" << (is_success ? "success" : "failed") << "\",";
if (is_success) {
json << "\"error_message\": null,";
} else {
json << "\"error_message\": \"" << EscapeJsonString(error_message) << "\",";
}
if (is_success) {
json << "\"result\": " << result_json << ",";
} else {
json << "\"result\": {},";
}
json << "\"finished_at\": \"" << GetCurrentIsoUtcTimeString() << "\","
<< "\"token\": \"" << EscapeJsonString(token) << "\""
<< "}";
return json.str();
}
bool PostJsonToUrl(const std::string& url,
const std::string& json_body,
DWORD& http_status_code,
std::string& error_message) {
http_status_code = 0;
error_message.clear();
std::wstring wurl = Utf8ToWide(url);
if (wurl.empty()) {
error_message = "Invalid callback URL";
return false;
}
URL_COMPONENTS components;
ZeroMemory(&components, sizeof(components));
components.dwStructSize = sizeof(components);
components.dwSchemeLength = (DWORD)-1;
components.dwHostNameLength = (DWORD)-1;
components.dwUrlPathLength = (DWORD)-1;
components.dwExtraInfoLength = (DWORD)-1;
if (!WinHttpCrackUrl(wurl.c_str(), 0, 0, &components)) {
error_message = "WinHttpCrackUrl failed";
return false;
}
std::wstring host(components.lpszHostName, components.dwHostNameLength);
std::wstring path(components.lpszUrlPath ? std::wstring(components.lpszUrlPath, components.dwUrlPathLength) : L"/");
if (components.dwExtraInfoLength > 0 && components.lpszExtraInfo) {
path += std::wstring(components.lpszExtraInfo, components.dwExtraInfoLength);
}
bool secure = (components.nScheme == INTERNET_SCHEME_HTTPS);
INTERNET_PORT port = components.nPort;
DWORD flags = secure ? WINHTTP_FLAG_SECURE : 0;
HINTERNET session = WinHttpOpen(L"MFCCreoDll/1.0",
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS,
0);
if (!session) {
error_message = "WinHttpOpen failed";
return false;
}
DWORD timeout = 10000;
WinHttpSetTimeouts(session, timeout, timeout, timeout, timeout);
HINTERNET connection = WinHttpConnect(session, host.c_str(), port, 0);
if (!connection) {
WinHttpCloseHandle(session);
error_message = "WinHttpConnect failed";
return false;
}
HINTERNET request = WinHttpOpenRequest(connection,
L"POST",
path.c_str(),
nullptr,
WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,
flags);
if (!request) {
WinHttpCloseHandle(connection);
WinHttpCloseHandle(session);
error_message = "WinHttpOpenRequest failed";
return false;
}
std::wstring headers = L"Content-Type: application/json\r\n";
bool send_ok = WinHttpSendRequest(request,
headers.c_str(),
(DWORD)-1,
(LPVOID)json_body.data(),
static_cast<DWORD>(json_body.size()),
static_cast<DWORD>(json_body.size()),
0) == TRUE;
if (!send_ok || WinHttpReceiveResponse(request, nullptr) != TRUE) {
WinHttpCloseHandle(request);
WinHttpCloseHandle(connection);
WinHttpCloseHandle(session);
error_message = "WinHTTP send/receive failed";
return false;
}
DWORD size = sizeof(http_status_code);
WinHttpQueryHeaders(request,
WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
WINHTTP_HEADER_NAME_BY_INDEX,
&http_status_code,
&size,
WINHTTP_NO_HEADER_INDEX);
WinHttpCloseHandle(request);
WinHttpCloseHandle(connection);
WinHttpCloseHandle(session);
return (http_status_code >= 200 && http_status_code < 300);
}
void SendTaskResultCallbackWithRetry(const std::string& callback_server_address,
const std::string& execution_id,
const std::string& software_id,
const BatchOperationsResponse& batch_result,
const std::string& callback_token) {
if (callback_server_address.empty() || execution_id.empty() || callback_token.empty()) {
return;
}
bool is_success = batch_result.success;
std::string callback_error = batch_result.error_message;
if (!is_success && callback_error.empty()) {
callback_error = "Batch operations failed";
}
std::string result_json = BuildBatchCallbackResultJson(batch_result);
std::string payload = BuildTaskResultCallbackPayload(
execution_id,
software_id.empty() ? "creo" : software_id,
is_success,
callback_error,
result_json,
callback_token
);
std::string callback_url = BuildBatchCallbackUrl(callback_server_address);
if (callback_url.empty()) {
return;
}
const std::vector<int> retry_delays_seconds = {1, 3, 5};
const int max_attempts = 1 + static_cast<int>(retry_delays_seconds.size());
for (int attempt = 0; attempt < max_attempts; ++attempt) {
DWORD status_code = 0;
std::string post_error;
bool ok = PostJsonToUrl(callback_url, payload, status_code, post_error);
if (ok) {
return;
}
if (attempt < static_cast<int>(retry_delays_seconds.size())) {
std::this_thread::sleep_for(std::chrono::seconds(retry_delays_seconds[attempt]));
}
}
}
// 模型导出路由处理器 // 模型导出路由处理器
HttpResponse ExportModelHandler(const HttpRequest& request) { HttpResponse ExportModelHandler(const HttpRequest& request) {
if (request.method != "POST") { if (request.method != "POST") {
@ -2068,6 +2327,27 @@ HttpResponse BatchOperationsHandler(const HttpRequest& request) {
try { try {
// Parse batch operations request // Parse batch operations request
BatchOperationsRequest batch_request; BatchOperationsRequest batch_request;
std::string execution_id = ExtractJsonValue(request.body, "execution_id");
std::string callback_server_address = Config::BATCH_CALLBACK_SERVER_ADDRESS;
if (callback_server_address.empty()) {
callback_server_address = ExtractJsonValue(request.body, "callback_server_address");
}
if (callback_server_address.empty()) {
callback_server_address = ExtractJsonValue(request.body, "callback_server");
}
if (callback_server_address.empty()) {
callback_server_address = ExtractJsonValue(request.body, "server_address");
}
if (callback_server_address.empty()) {
callback_server_address = ExtractJsonValue(request.body, "callback_base_url");
}
std::string callback_token = Config::BATCH_CALLBACK_TOKEN;
if (callback_token.empty()) {
callback_token = ExtractJsonValue(request.body, "callback_token");
}
if (callback_token.empty()) {
callback_token = ExtractJsonValue(request.body, "token");
}
batch_request.software_type = ExtractJsonValue(request.body, "software_type"); batch_request.software_type = ExtractJsonValue(request.body, "software_type");
@ -2093,6 +2373,13 @@ HttpResponse BatchOperationsHandler(const HttpRequest& request) {
// Execute batch operations // Execute batch operations
BatchOperationsResponse batch_result = BatchOperationManager::Instance().ExecuteBatchOperations(batch_request); BatchOperationsResponse batch_result = BatchOperationManager::Instance().ExecuteBatchOperations(batch_request);
SendTaskResultCallbackWithRetry(
callback_server_address,
execution_id,
batch_request.software_type,
batch_result,
callback_token
);
// Build response JSON // Build response JSON
std::ostringstream json; std::ostringstream json;