| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760 |
- /**
- * network_client.cpp — 网络通信实现
- */
- #include "network_client.h"
- #include "feishu_client.h"
- #include "utils.h"
- #include "md5ex1.h"
- #include <iostream>
- size_t WriteMemoryCallback(void* contents, size_t size, size_t nmemb, std::string* str);
- // ==================== 飞书配置 ====================
- void InitCurlCommon(CURL* curl, std::string* response) {
- curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15L);
- curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, response);
- }
- int parse_create_bill_response(const std::string& response, const std::string& plate, bool is_in, std::string& tb_num_out) {
- tb_num_out.clear();
- cJSON* root = cJSON_Parse(response.c_str());
- if (!root) {
- std::cerr << "[解析失败] 响应不是合法JSON: " << response << std::endl;
- return static_cast<int>(ErrorCode::PARSE_ERROR);
- }
- if (DEBUG_LOG) {
- std::cout << response << std::endl;
- }
-
- cJSON* status = cJSON_GetObjectItem(root, "status");
- cJSON* data = cJSON_GetObjectItem(root, "data");
- cJSON* msg = cJSON_GetObjectItem(root, "msg");
- if (msg && cJSON_IsString(msg) && strstr(msg->valuestring, "已确认上线") != NULL) {
- std::cout << "检测到Test环境已确认上线,自动切换到正式环境" << std::endl;
- TestFlag = 0;
- cJSON_Delete(root);
- return static_cast<int>(ErrorCode::BUSINESS_ERROR);
- }
- // 业务错误在任何日志级别下都生效
- if (DEBUG_LOG == 0)//调试模式DEBUG_LOG = 1,正式运行DEBUG_LOG =0屏蔽掉
- {
- if (msg && cJSON_IsString(msg) && strstr(msg->valuestring, "非在网车辆") != NULL) {
- std::cerr << "车牌 " << plate << (is_in ? " 进站" : " 出站")
- << " 非在网车辆" << std::endl;
- cJSON_Delete(root);
- return static_cast<int>(ErrorCode::BUSINESS_ERROR);
- }
- }
- if (status && status->valueint == BUSINESS_ERROR_CODE) {
- std::cerr << "车牌 " << plate << (is_in ? " 进站" : " 出站")
- << " 业务异常: " << (msg ? msg->valuestring : "未知") << std::endl;
- cJSON_Delete(root);
- return static_cast<int>(ErrorCode::BUSINESS_ERROR);
- }
- if (DEBUG_LOG == 0)//调试模式DEBUG_LOG = 1,正式运行DEBUG_LOG =0屏蔽掉
- {
- if (!status || status->valueint != HTTP_STATUS_OK) {
- std::cerr << "[创建工单失败] 服务器返回非200状态: " << (status ? status->valueint : -1) << std::endl;
- if (msg && cJSON_IsString(msg)) {
- std::cout << "错误信息: " << msg->valuestring << std::endl;
- }
- cJSON_Delete(root);
- return static_cast<int>(ErrorCode::NETWORK_ERROR);
- }
- }
- if (!data) {
- std::cerr << "[解析失败] 响应缺少data字段" << std::endl;
- cJSON_Delete(root);
- return static_cast<int>(ErrorCode::PARSE_ERROR);
- }
- cJSON* tbNo = cJSON_GetObjectItem(data, "tbNo");
- if (!tbNo || !cJSON_IsString(tbNo) || strlen(tbNo->valuestring) == 0) {
- std::cerr << "[解析失败] 未找到有效的tbNo" << std::endl;
- cJSON_Delete(root);
- return static_cast<int>(ErrorCode::PARSE_ERROR);
- }
- tb_num_out = tbNo->valuestring;
- update_bill_cache_tb_num(plate, is_in, tb_num_out);
- std::cout << "[解析成功] " << (is_in ? "进站" : "出站") << "联单编号: " << tb_num_out << std::endl;
- cJSON_Delete(root);
- g_metrics.record_createbill(true);
- return static_cast<int>(ErrorCode::SUCCESS);
- }
- int parse_online_status_response(std::string resp)
- {
- std::cout << resp << std::endl;
- cJSON* root = cJSON_Parse(resp.c_str());
- if (NULL == root)
- {
- std::cout << "parse_upload_image_response error!!!" << std::endl;
- return -1;
- }
- cJSON* status = cJSON_GetObjectItem(root, "status");
- if (status && status->valueint == HTTP_STATUS_OK)
- {
- std::cout << "online status success!!!" << std::endl;
- }
- cJSON_Delete(root);
- return 0;
- }
- int parse_upload_image_response(std::string resp)
- {
- std::cout << resp << std::endl;
- cJSON* root = cJSON_Parse(resp.c_str());
- if (NULL == root)
- {
- std::cout << "parse_upload_image_response error!!!" << std::endl;
- return -1;
- }
- cJSON* status = cJSON_GetObjectItem(root, "status");
- if (status && status->valueint == HTTP_STATUS_OK)
- {
- std::cout << "upload image success!!!" << std::endl;
- cJSON_Delete(root);
- return 0;
- }
- std::cerr << "upload image failed" << std::endl;
- cJSON_Delete(root);
- return -1;
- }
- std::string json_create_bill_encode(std::shared_ptr<CarPlateInfo> plate_info)
- {
- CaptureInfo* plate_cap_info = plate_info->cap_info_copy.get();
- if (!plate_cap_info) {
- std::cerr << "json_create_bill_encode: cap_info为空" << std::endl;
- return std::string("");
- }
- cJSON* json = cJSON_CreateObject();
- bool is_special = false;
- {
- std::lock_guard<std::mutex> lock(g_special_plates_mtx);
- for (auto& p : g_special_plates) {
- if (p == plate_info->code) {
- is_special = true;
- break;
- }
- }
- }
- // fix24-v21: 使用照片抓拍时间(db_capture_time)替代当前时间,确保API上报时间与水印/DB一致
- std::string capture_time_str;
- if (plate_info->db_capture_time > 0) {
- struct tm tm_buf;
- struct tm* tm_ptr = localtime_r(&plate_info->db_capture_time, &tm_buf);
- if (tm_ptr) {
- char buf[64];
- strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm_ptr);
- capture_time_str = buf;
- } else {
- capture_time_str = get_format_time();
- }
- } else {
- capture_time_str = get_format_time();
- }
- if (g_wType == 1)
- {
- cJSON_AddStringToObject(json, "plateNumber", plate_info->code.c_str());
- cJSON_AddStringToObject(json, "outWorkSiteDate", capture_time_str.c_str());
- cJSON_AddStringToObject(json, "workSiteNo", point_number.c_str());
- cJSON_AddStringToObject(json, "doorNo", throughway.c_str());
- if (is_special) {
- cJSON_AddStringToObject(json, "platenumcolor", "黄色");
- cJSON_AddStringToObject(json, "vehicleType", "渣土车");
- }
- else {
- cJSON_AddStringToObject(json, "platenumcolor", plate_info->type.c_str());
- cJSON_AddStringToObject(json, "vehicleType", "");
- }
- cJSON_AddNumberToObject(json, "dType", plate_cap_info->dtype);
- }
- else if (g_wType == 3)
- {
- cJSON_AddStringToObject(json, "plateNumber", plate_info->code.c_str());
- cJSON_AddStringToObject(json, "inDisposalDate", capture_time_str.c_str());
- cJSON_AddStringToObject(json, "disposalNo", point_number.c_str());
- cJSON_AddStringToObject(json, "doorNo", throughway.c_str());
- if (is_special) {
- cJSON_AddStringToObject(json, "platenumcolor", "黄色");
- }
- else {
- cJSON_AddStringToObject(json, "platenumcolor", plate_info->type.c_str());
- }
- cJSON_AddNumberToObject(json, "dType", plate_cap_info->dtype);
- }
- char* string = cJSON_Print(json);
- cJSON_Delete(json);
- if (string == NULL)
- {
- return std::string("");
- }
- std::string res = std::string(string);
- free(string);
- return res;
- }
- std::string json_online_status_encode(CaptureInfo* cap_info)
- {
- if (!cap_info) {
- std::cerr << "json_online_status_encode: cap_info为空" << std::endl;
- return std::string("");
- }
- cJSON* json = cJSON_CreateObject();
- cJSON_AddStringToObject(json, "workSiteNo", point_number.c_str());
- cJSON_AddNumberToObject(json, "wType", g_wType);
- cJSON_AddNumberToObject(json, "dType", cap_info->dtype);
- cJSON_AddNumberToObject(json, "gType", cap_info->gtype);
- cJSON_AddNumberToObject(json, "status", cap_info->status);
- cJSON_AddStringToObject(json, "remark", "");
- cJSON_AddStringToObject(json, "timestamp", get_format_time().c_str());
- char* string = cJSON_Print(json);
- cJSON_Delete(json);
- if (string == NULL)
- {
- return std::string("");
- }
- std::string res = std::string(string);
- free(string);
- return res;
- }
- // ============================================
- // v41.2 新增: 批量状态上报 - 将4个摄像头合并为1个请求
- // ============================================
- std::string json_online_status_batch_encode()
- {
- cJSON* root = cJSON_CreateObject();
- cJSON_AddStringToObject(root, "workSiteNo", point_number.c_str());
- cJSON_AddNumberToObject(root, "wType", g_wType);
- cJSON_AddStringToObject(root, "timestamp", get_format_time().c_str());
-
- // 创建摄像头状态数组
- cJSON* cameraList = cJSON_CreateArray();
-
- // 4个摄像头的状态
- CaptureInfo* cameras[4] = {
- &plate_rec_app.front_info_in, // 进站低位
- &plate_rec_app.front_info_out, // 出站低位
- &plate_rec_app.side_info_in, // 进站高位
- &plate_rec_app.side_info_out // 出站高位
- };
-
- for (int i = 0; i < 4; i++) {
- cJSON* cam = cJSON_CreateObject();
- cJSON_AddNumberToObject(cam, "dType", cameras[i]->dtype);
- cJSON_AddNumberToObject(cam, "gType", cameras[i]->gtype);
- cJSON_AddNumberToObject(cam, "status", cameras[i]->status);
- cJSON_AddStringToObject(cam, "remark", "");
- cJSON_AddItemToArray(cameraList, cam);
- }
-
- cJSON_AddItemToObject(root, "cameraList", cameraList);
-
- char* string = cJSON_PrintUnformatted(root);
- cJSON_Delete(root);
- if (string == NULL) {
- return std::string("");
- }
- std::string res = std::string(string);
- free(string);
- return res;
- }
- // 前向声明
- std::string get_new_timestamp();
- // ==================== 签名计算 ====================
- std::string calculate_sign(const std::string& app_key, const std::string& app_secret, const std::string& timestamp) {
- std::string sign_str = app_secret + "#app_key" + app_key + "timestamp" + timestamp + "#" + app_secret;
- char md5_hex[33];
- md5_hexEx1(sign_str.c_str(), md5_hex);
- return std::string(md5_hex);
- }
- // ==================== libcurl初始化/清理 ====================
- int lib_curl_init()
- {
- curl_global_init(CURL_GLOBAL_DEFAULT);
- return 0;
- }
- void lib_curl_deinit()
- {
- curl_global_cleanup();
- }
- // ==================== 飞书Token缓存 ====================
- std::string get_cached_tenant_token() {
- time_t now = time(NULL);
- if (!g_tenant_token.empty() && now < g_token_expire_time.load() - TOKEN_REFRESH_BUFFER_SECONDS) {
- return g_tenant_token;
- }
- std::lock_guard<std::mutex> lock(g_token_mtx);
- now = time(NULL);
- if (!g_tenant_token.empty() && now < g_token_expire_time.load() - TOKEN_REFRESH_BUFFER_SECONDS) {
- return g_tenant_token;
- }
- std::string new_token = get_tenant_token();
- if (!new_token.empty()) {
- g_tenant_token = new_token;
- g_token_expire_time.store(time(NULL) + TOKEN_EXPIRE_SECONDS);
- std::cout << "[Token刷新] 飞书租户Token已更新,过期时间: " << g_token_expire_time.load() << std::endl;
- } else {
- std::cerr << "[Token刷新] 获取飞书Token失败,使用旧Token(如果存在)" << std::endl;
- }
- return g_tenant_token;
- }
- // 批量状态上报请求
- int lib_curl_online_status_batch_request()
- {
- std::string response;
- std::string timestamp = get_new_timestamp();
- std::string sign = calculate_sign(app_key, app_secret, timestamp);
- std::string req_url = "";
- if (TestFlag == 1) {
- req_url = app_api + "onlineStatusBatchTest?app_key=" +
- app_key + "&sign=" + sign + "×tamp=" + timestamp;
- } else {
- // 使用批量接口
- req_url = app_api + "onlineStatusBatch?app_key=" +
- app_key + "&sign=" + sign + "×tamp=" + timestamp;
- }
- // v41.2: MAX_RETRY=4 表示 初始请求 + 3次重试 = 4次请求
- const int MAX_RETRY = 4;
- for (int retry = 0; retry < MAX_RETRY; retry++) {
- if (retry > 0) {
- // 指数退让:2^retry = 2, 4, 8 秒
- int backoff_seconds = (1 << retry);
- std::cerr << "[批量状态] 重试第" << retry << "次(共" << MAX_RETRY-1 << "次),等待" << backoff_seconds << "秒..." << std::endl;
- std::this_thread::sleep_for(std::chrono::seconds(backoff_seconds));
- }
- CURL* curl = curl_easy_init();
- if (!curl) {
- std::cerr << "curl_easy_init failed" << std::endl;
- return -1;
- }
- std::string postdata = json_online_status_batch_encode();
- std::cout << "[批量状态] data: " << postdata << std::endl;
- struct curl_slist* headers = NULL;
- headers = curl_slist_append(headers, "Content-Type: application/json;charset=UTF-8");
- curl_easy_setopt(curl, CURLOPT_URL, req_url.c_str());
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
- curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
-
- curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15L);//3L
- curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L); // 增加超时时间
-
- curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postdata.c_str());
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
- CURLcode last_res = curl_easy_perform(curl);
- if (last_res == CURLE_OK) {
- std::cout << "[批量状态] success: " << response << std::endl;
- curl_slist_free_all(headers);
- curl_easy_cleanup(curl);
- return 0;
- }
- std::cerr << "[批量状态] Error: " << curl_easy_strerror(last_res) << std::endl;
- curl_slist_free_all(headers);
- curl_easy_cleanup(curl);
-
- if (last_res != CURLE_OPERATION_TIMEDOUT && last_res != CURLE_COULDNT_CONNECT) {
- break;
- }
- response.clear();
- }
- return -1;
- }
- // ============================================
- // ==================== 缺失的函数实现 ====================
- std::string get_new_timestamp() {
- auto now = std::chrono::system_clock::now();
- auto timestamp = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
- return std::to_string(timestamp);
- }
- std::string get_format_time_s() {
- time_t now = time(nullptr);
- struct tm tm_now;
- localtime_r(&now, &tm_now);
- char buf[64];
- strftime(buf, sizeof(buf), "%Y年%m月%d日%H:%M:%S", &tm_now);
- return std::string(buf);
- }
- std::string calculate_md5(const std::string& input) {
- char md5_hex[33];
- md5_hexEx1(input.c_str(), md5_hex);
- return std::string(md5_hex);
- }
- bool lib_curl_image_upload_request(std::shared_ptr<CarPlateInfo> plate_info, GTYPE gtype)
- {
- CaptureInfo* cap_info = plate_info->cap_info_copy.get();
- if (!cap_info) {
- std::cerr << "lib_curl_image_upload_request: cap_info为空" << std::endl;
- return false;
- }
- auto upload_start = std::chrono::steady_clock::now();
- std::string timestamp = get_new_timestamp();
- std::string sign = calculate_sign(app_key, app_secret, timestamp);
- std::string req_url = "";
- if (TestFlag == 1)
- {
- req_url = app_api + "uploadImgTest?app_key=" +
- app_key + "&sign=" + sign + "×tamp=" + timestamp;
- }
- else
- {
- req_url = app_api + "uploadImg?app_key=" +
- app_key + "&sign=" + sign + "×tamp=" + timestamp;
- }
- std::string tb_no = plate_info->tb_num;
- std::string f_type = (g_wType == 3) ? "2" : "1";
- std::string d_type = std::to_string(cap_info->dtype);
- std::string g_type = std::to_string(gtype);
- std::string filePath = "";
- if (gtype == POSITION_LO)
- {
- if (plate_info->pic_path_front.empty()) {
- std::cerr << "pic_path_front 为空,跳过上传" << std::endl;
- return false;
- }
- filePath = std::string(pathComm + "PlateJPG/") + plate_info->pic_path_front;
- }
- else if (gtype == POSITION_HI)
- {
- if (plate_info->pic_path_side.empty()) {
- std::cerr << "pic_path_side 为空,跳过上传" << std::endl;
- return false;
- }
- filePath = std::string(pathComm + "PlateJPG/") + plate_info->pic_path_side;
- }
- std::cout << "dtype:" << d_type << " gtype:" << g_type << " file:" << filePath << std::endl;
- if (filePath.empty()) {
- std::cerr << "文件路径为空,跳过上传" << std::endl;
- return false;
- }
- std::string response;
- // v41.2: MAX_RETRY=4 表示 初始请求 + 3次重试 = 4次请求
- const int MAX_RETRY = 4;
- for (int retry = 0; retry < MAX_RETRY; retry++) {
- if (retry > 0) {
- // 指数退让:2^retry = 2, 4, 8 秒
- int backoff_seconds = (1 << retry);
- std::cerr << "[图片上传] 重试第" << retry << "次(共" << MAX_RETRY-1 << "次),等待" << backoff_seconds << "秒..." << std::endl;
- std::this_thread::sleep_for(std::chrono::seconds(backoff_seconds));
- }
- CURL* curl = curl_easy_init();
- if (!curl) {
- std::cerr << "curl_easy_init failed" << std::endl;
- return false;
- }
- curl_mime* mime = curl_mime_init(curl);
- if (!mime) {
- std::cerr << "curl_mime_init failed" << std::endl;
- curl_easy_cleanup(curl);
- return false;
- }
- curl_mimepart* part = nullptr;
- part = curl_mime_addpart(mime);
- curl_mime_name(part, "tbNo");
- curl_mime_data(part, tb_no.c_str(), CURL_ZERO_TERMINATED);
- part = curl_mime_addpart(mime);
- curl_mime_name(part, "fType");
- curl_mime_data(part, f_type.c_str(), CURL_ZERO_TERMINATED);
- part = curl_mime_addpart(mime);
- curl_mime_name(part, "dType");
- curl_mime_data(part, d_type.c_str(), CURL_ZERO_TERMINATED);
- part = curl_mime_addpart(mime);
- curl_mime_name(part, "gType");
- curl_mime_data(part, g_type.c_str(), CURL_ZERO_TERMINATED);
- std::string file_display_name = filePath;
- size_t last_slash = filePath.find_last_of("/");
- if (last_slash != std::string::npos) {
- file_display_name = filePath.substr(last_slash + 1);
- }
- part = curl_mime_addpart(mime);
- curl_mime_name(part, "file");
- curl_mime_filedata(part, filePath.c_str());
- curl_mime_filename(part, file_display_name.c_str());
- curl_mime_type(part, "image/jpeg");
- curl_easy_setopt(curl, CURLOPT_URL, req_url.c_str());
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
- curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
- curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15L);
- curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
- curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
- CURLcode last_res = curl_easy_perform(curl);
- if (last_res == CURLE_OK) {
- int parse_res = parse_upload_image_response(response);
- auto upload_end = std::chrono::steady_clock::now();
- int duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(upload_end - upload_start).count();
- g_metrics.record_upload_time(duration_ms);
- curl_mime_free(mime);
- curl_easy_cleanup(curl);
- return (parse_res == 0);
- }
- std::cout << "[图片上传] Error curl_easy_perform" << std::endl;
- g_metrics.record_error(classify_upload_error(last_res, 0));
- if (last_res != CURLE_OPERATION_TIMEDOUT && last_res != CURLE_COULDNT_CONNECT) {
- curl_mime_free(mime);
- curl_easy_cleanup(curl);
- return false;
- }
- curl_mime_free(mime);
- curl_easy_cleanup(curl);
- response.clear();
- }
- auto upload_end = std::chrono::steady_clock::now();
- int duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(upload_end - upload_start).count();
- g_metrics.record_upload_time(duration_ms);
- return false;
- }
- int lib_curl_create_bill_request(std::shared_ptr<CarPlateInfo> plate_info, std::string& tb_num_out)
- {
- tb_num_out.clear();
- std::string response;
- std::string postdata = json_create_bill_encode(plate_info);
- std::string timestamp = get_new_timestamp();
- std::string sign = calculate_sign(app_key, app_secret, timestamp);
- std::string req_url = "";
- CaptureInfo* plate_cap_info = plate_info->cap_info_copy.get();
- if (!plate_cap_info) {
- std::cerr << "lib_curl_create_bill_request: cap_info为空" << std::endl;
- return static_cast<int>(ErrorCode::INVALID_PARAM);
- }
- bool is_in = (plate_cap_info->dtype == STATION_IN);
- bool is_business_error = false;
- if (TestFlag == 1)
- {
- req_url = app_api + "createBillTest?app_key=" +
- app_key + "&sign=" + sign + "×tamp=" + timestamp;
- }
- else
- {
- if (g_wType == 1)
- {
- req_url = app_api + "createBill?app_key=" +
- app_key + "&sign=" + sign + "×tamp=" + timestamp;
- }
- else if (g_wType == 3)
- {
- req_url = app_api + "consumeBill?app_key=" +
- app_key + "&sign=" + sign + "×tamp=" + timestamp;
- }
- else
- {
- req_url = app_api + "createBill?app_key=" +
- app_key + "&sign=" + sign + "×tamp=" + timestamp;
- }
- }
- std::cout << "url: " << req_url << std::endl;
- std::cout << "data: " << postdata << std::endl;
- const int MAX_RETRY = 3; // 初始请求 + 3次重试 = 4次请求
- for (int retry = 0; retry <= MAX_RETRY; retry++) {
- int backoff_seconds = (retry > 0) ? (1 << retry) : 0; // 指数退让: 2, 4, 8秒
- if (retry > 0) {
- std::cerr << "[创建工单] 重试第" << retry << "次(共" << MAX_RETRY << "次),等待" << backoff_seconds << "秒..." << std::endl;
- sleep(backoff_seconds);
- }
- CURL* curl = curl_easy_init();
- if (!curl) {
- std::cerr << "curl_easy_init failed" << std::endl;
- return static_cast<int>(ErrorCode::NETWORK_ERROR);
- }
- curl_easy_setopt(curl, CURLOPT_URL, req_url.c_str());
- struct curl_slist* headers = NULL;
- headers = curl_slist_append(headers, "Content-Type: application/json;charset=UTF-8");
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
- curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
- curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15L);
- curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
- curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postdata.c_str());
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
- CURLcode last_res = curl_easy_perform(curl);
- if (last_res == CURLE_OK) {
- int parse_res = parse_create_bill_response(response, plate_info->code, is_in, tb_num_out);
- if (parse_res == static_cast<int>(ErrorCode::SUCCESS)) {
- curl_slist_free_all(headers);
- curl_easy_cleanup(curl);
- increment_bill_count(plate_info->code, is_in);
- return static_cast<int>(ErrorCode::SUCCESS);
- }
- if (parse_res == static_cast<int>(ErrorCode::BUSINESS_ERROR)) {
- std::cerr << "[创建工单] 业务错误,不再重试" << std::endl;
- is_business_error = true;
- curl_slist_free_all(headers);
- curl_easy_cleanup(curl);
- break;
- }
- std::cerr << "[创建工单] 解析响应失败,准备重试" << std::endl;
- curl_slist_free_all(headers);
- curl_easy_cleanup(curl);
- response.clear();
- continue;
- }
- std::cout << "[创建工单] Error curl_easy_perform" << std::endl;
- g_metrics.record_createbill(false);
- g_metrics.record_error(classify_upload_error(last_res, 0));
- if (last_res != CURLE_OPERATION_TIMEDOUT && last_res != CURLE_COULDNT_CONNECT) {
- curl_slist_free_all(headers);
- curl_easy_cleanup(curl);
- return static_cast<int>(ErrorCode::NETWORK_ERROR);
- }
- curl_slist_free_all(headers);
- curl_easy_cleanup(curl);
- response.clear();
- }
- if (!is_business_error) {
- std::cerr << "[创建工单] 所有重试都失败" << std::endl;
- g_metrics.record_createbill(false);
- } else {
- g_metrics.record_createbill(false, true);
- }
- return is_business_error ? static_cast<int>(ErrorCode::BUSINESS_ERROR) : static_cast<int>(ErrorCode::NETWORK_ERROR);
- }
- int lib_curl_online_status_request(CaptureInfo* cap_info)
- {
- std::string response;
- std::string timestamp = get_new_timestamp();
- std::string sign = calculate_sign(app_key, app_secret, timestamp);
- std::string req_url = "";
- if (TestFlag == 1)
- {
- req_url = app_api + "onlineStatusTest?app_key=" +
- app_key + "&sign=" + sign + "×tamp=" + timestamp;
- }
- else
- {
- req_url = app_api + "onlineStatus?app_key=" +
- app_key + "&sign=" + sign + "×tamp=" + timestamp;
- }
- // v41.2优化:指数退让重试 (2秒、4秒、8秒),最多3次
- // v41.2: MAX_RETRY=4 表示 初始请求 + 3次重试 = 4次请求
- const int MAX_RETRY = 4;
- for (int retry = 0; retry < MAX_RETRY; retry++) {
- if (retry > 0) {
- // 指数退让:2^retry = 2, 4, 8 秒
- int backoff_seconds = (1 << retry);
- std::cerr << "[在线状态] 重试第" << retry << "次(共" << MAX_RETRY-1 << "次),等待" << backoff_seconds << "秒..." << std::endl;
- std::this_thread::sleep_for(std::chrono::seconds(backoff_seconds));
- }
- CURL* curl = curl_easy_init();
- if (!curl) {
- std::cerr << "curl_easy_init failed" << std::endl;
- return -1;
- }
- std::string postdata = json_online_status_encode(cap_info);
- std::cout << "data: " << postdata << std::endl;
- curl_easy_setopt(curl, CURLOPT_URL, req_url.c_str());
- struct curl_slist* headers = NULL;
- headers = curl_slist_append(headers, "Content-Type: application/json;charset=UTF-8");
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
- curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
- curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15L);
- curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
- curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postdata.c_str());
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
- CURLcode last_res = curl_easy_perform(curl);
- if (last_res == CURLE_OK) {
- parse_online_status_response(response);
- curl_slist_free_all(headers);
- curl_easy_cleanup(curl);
- return 0;
- }
- std::cout << "[在线状态] Error curl_easy_perform" << std::endl;
- if (last_res != CURLE_OPERATION_TIMEDOUT && last_res != CURLE_COULDNT_CONNECT) {
- curl_slist_free_all(headers);
- curl_easy_cleanup(curl);
- return -1;
- }
- curl_slist_free_all(headers);
- curl_easy_cleanup(curl);
- response.clear();
- }
- return -1;
- }
|