up test
This commit is contained in:
112
src/main/java/su/xserver/iikocon/test/IikoOlapClient.java
Normal file
112
src/main/java/su/xserver/iikocon/test/IikoOlapClient.java
Normal file
@@ -0,0 +1,112 @@
|
||||
package su.xserver.iikocon.test;
|
||||
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.Promise;
|
||||
import io.vertx.core.Vertx;
|
||||
import io.vertx.core.buffer.Buffer;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.client.HttpResponse;
|
||||
import io.vertx.ext.web.client.WebClient;
|
||||
|
||||
public class IikoOlapClient {
|
||||
|
||||
private final WebClient webClient;
|
||||
private final String baseUrl;
|
||||
private final String login;
|
||||
private final String password;
|
||||
|
||||
// Конструктор клиента
|
||||
public IikoOlapClient(Vertx vertx, String baseUrl, String login, String password) {
|
||||
this.webClient = WebClient.create(vertx);
|
||||
this.baseUrl = baseUrl;
|
||||
this.login = login;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
// Основной метод для получения OLAP-отчета
|
||||
public Future<JsonObject> getOlapReport(JsonObject reportRequest) {
|
||||
Promise<JsonObject> promise = Promise.promise();
|
||||
|
||||
// 1. Аутентификация
|
||||
authenticate()
|
||||
.compose(this::getOrganizations) // 2. Получение организаций
|
||||
.compose(orgId -> executeReport(reportRequest, orgId)) // 3. Запрос отчета
|
||||
.onSuccess(promise::complete)
|
||||
.onFailure(promise::fail);
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
// Аутентификация и получение токена
|
||||
private Future<String> authenticate() {
|
||||
Promise<String> promise = Promise.promise();
|
||||
|
||||
JsonObject authRequest = new JsonObject()
|
||||
.put("login", login)
|
||||
.put("password", password);
|
||||
|
||||
webClient.post(443, baseUrl, "/resto/api/auth")
|
||||
.ssl(true)
|
||||
.putHeader("Content-Type", "application/json")
|
||||
.sendJson(authRequest)
|
||||
.onSuccess(response -> {
|
||||
if (response.statusCode() == 200) {
|
||||
String token = response.bodyAsJsonObject().getString("token");
|
||||
promise.complete(token);
|
||||
} else {
|
||||
promise.fail("Authentication failed: " + response.statusMessage());
|
||||
}
|
||||
})
|
||||
.onFailure(promise::fail);
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
// Получение ID организации (для отчета)
|
||||
private Future<String> getOrganizations(String token) {
|
||||
Promise<String> promise = Promise.promise();
|
||||
|
||||
webClient.get(443, baseUrl, "/resto/api/organizations")
|
||||
.ssl(true)
|
||||
.putHeader("Authorization", "Bearer " + token)
|
||||
.send()
|
||||
.onSuccess(response -> {
|
||||
if (response.statusCode() == 200) {
|
||||
// Берем ID первой организации из списка
|
||||
String orgId = response.bodyAsJsonArray()
|
||||
.getJsonObject(0)
|
||||
.getString("id");
|
||||
promise.complete(orgId);
|
||||
} else {
|
||||
promise.fail("Failed to get organizations: " + response.statusMessage());
|
||||
}
|
||||
})
|
||||
.onFailure(promise::fail);
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
// Выполнение запроса OLAP-отчета
|
||||
private Future<JsonObject> executeReport(JsonObject reportRequest, String organizationId) {
|
||||
Promise<JsonObject> promise = Promise.promise();
|
||||
|
||||
// Добавляем ID организации в тело запроса
|
||||
JsonObject fullRequest = reportRequest.copy()
|
||||
.put("organizationId", organizationId);
|
||||
|
||||
webClient.post(443, baseUrl, "/resto/api/v2/reports/olap")
|
||||
.ssl(true)
|
||||
.putHeader("Content-Type", "application/json")
|
||||
.sendJson(fullRequest)
|
||||
.onSuccess(response -> {
|
||||
if (response.statusCode() == 200) {
|
||||
promise.complete(response.bodyAsJsonObject());
|
||||
} else {
|
||||
promise.fail("OLAP report request failed: " + response.statusMessage());
|
||||
}
|
||||
})
|
||||
.onFailure(promise::fail);
|
||||
|
||||
return promise.future();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,375 @@
|
||||
package su.xserver.iikocon.test;
|
||||
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.core.Promise;
|
||||
import io.vertx.core.Vertx;
|
||||
import io.vertx.core.json.JsonArray;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.client.WebClient;
|
||||
import io.vertx.ext.web.client.WebClientOptions;
|
||||
import io.vertx.mysqlclient.MySQLConnectOptions;
|
||||
import io.vertx.sqlclient.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HexFormat;
|
||||
import java.util.List;
|
||||
|
||||
public class IikoOlapColumnsImporter {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(IikoOlapColumnsImporter.class);
|
||||
private final WebClient httpClient;
|
||||
private final Pool dbPool;
|
||||
private final String iikoServer;
|
||||
private final String iikoLogin;
|
||||
private final String iikoPassword;
|
||||
private static long time;
|
||||
|
||||
private static final List<String> REPORT_TYPES = List.of("SALES", "TRANSACTIONS", "DELIVERIES");
|
||||
|
||||
public IikoOlapColumnsImporter(Vertx vertx,
|
||||
String iikoServer,
|
||||
String iikoLogin,
|
||||
String iikoPassword,
|
||||
String dbHost, int dbPort,
|
||||
String dbName, String dbUser, String dbPassword) {
|
||||
WebClientOptions options = new WebClientOptions()
|
||||
.setSsl(true)
|
||||
.setTrustAll(true)
|
||||
.setVerifyHost(false);
|
||||
this.httpClient = WebClient.create(vertx, options);
|
||||
this.iikoServer = iikoServer;
|
||||
this.iikoLogin = iikoLogin;
|
||||
this.iikoPassword = iikoPassword;
|
||||
|
||||
MySQLConnectOptions connectOptions = new MySQLConnectOptions()
|
||||
.setHost(dbHost)
|
||||
.setPort(dbPort)
|
||||
.setDatabase(dbName)
|
||||
.setUser(dbUser)
|
||||
.setPassword(dbPassword)
|
||||
.setCharset("utf8mb4");
|
||||
PoolOptions poolOptions = new PoolOptions().setMaxSize(5);
|
||||
this.dbPool = Pool.pool(vertx, connectOptions, poolOptions);
|
||||
}
|
||||
|
||||
// Главный метод: последовательно для каждого reportType делаем auth -> fetch -> store -> logout
|
||||
public Future<Void> fetchAndStoreAll() {
|
||||
return createTablesIfNotExist()
|
||||
.compose(v -> processAllReportTypesSequentially())
|
||||
.onSuccess(v -> log.info("All reports imported successfully"))
|
||||
.onFailure(err -> log.error("Import failed: {}", err.getMessage()));
|
||||
}
|
||||
|
||||
private Future<Void> processAllReportTypesSequentially() {
|
||||
time = System.currentTimeMillis();
|
||||
Future<Void> result = Future.succeededFuture();
|
||||
for (String reportType : REPORT_TYPES) {
|
||||
result = result.compose(v -> processOneReportType(reportType));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Future<Void> processOneReportType(String reportType) {
|
||||
log.info("Processing report type: {}", reportType);
|
||||
return authenticate()
|
||||
.compose(token -> {
|
||||
return fetchColumnsFromIiko(reportType, token)
|
||||
.compose(columnsJson -> storeColumnsToDb(reportType, columnsJson))
|
||||
.onComplete(ignored -> logout(token)); // logout всегда, даже при ошибке
|
||||
});
|
||||
}
|
||||
|
||||
// Аутентификация: GET /resto/api/auth?login=...&pass=SHA1
|
||||
private Future<String> authenticate() {
|
||||
Promise<String> promise = Promise.promise();
|
||||
String passHash = sha1(iikoPassword);
|
||||
String url = "https://" + iikoServer + ":443/resto/api/auth?login=" + iikoLogin + "&pass=" + passHash;
|
||||
|
||||
httpClient.getAbs(url)
|
||||
.send()
|
||||
.onSuccess(resp -> {
|
||||
if (resp.statusCode() == 200) {
|
||||
String token = resp.bodyAsString();
|
||||
log.info("Authenticated, token: {}", token);
|
||||
promise.complete(token);
|
||||
} else {
|
||||
promise.fail("Auth failed for " + iikoLogin + ": " + resp.statusCode());
|
||||
}
|
||||
})
|
||||
.onFailure(promise::fail);
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
// Logout: GET /resto/api/logout?key=токен
|
||||
private Future<Void> logout(String token) {
|
||||
if (token == null || token.isEmpty()) {
|
||||
return Future.succeededFuture();
|
||||
}
|
||||
Promise<Void> promise = Promise.promise();
|
||||
String url = "https://" + iikoServer + "/resto/api/logout?key=" + token;
|
||||
httpClient.getAbs(url)
|
||||
.send()
|
||||
.onSuccess(resp -> {
|
||||
log.info("Logout completed for token, status {}", resp.statusCode());
|
||||
promise.complete();
|
||||
})
|
||||
.onFailure(err -> {
|
||||
log.error("Logout request failed: {}", err.getMessage());
|
||||
promise.complete(); // не ломаем цепочку
|
||||
});
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
// Запрос полей для конкретного reportType
|
||||
private Future<JsonObject> fetchColumnsFromIiko(String reportType, String token) {
|
||||
|
||||
Promise<JsonObject> promise = Promise.promise();
|
||||
String url = "https://" + iikoServer + "/resto/api/v2/reports/olap/columns?key=" + token + "&reportType=" + reportType;
|
||||
|
||||
log.info("Connect to : {}", url);
|
||||
|
||||
httpClient.getAbs(url)
|
||||
.send()
|
||||
.onSuccess(resp -> {
|
||||
if (resp.statusCode() == 200) {
|
||||
JsonObject body = resp.bodyAsJsonObject();
|
||||
// Если есть обёртка data, распаковываем
|
||||
JsonObject data = body.containsKey("data") && body.getValue("data") instanceof JsonObject
|
||||
? body.getJsonObject("data")
|
||||
: body;
|
||||
promise.complete(data);
|
||||
|
||||
log.info("time: {}", (System.currentTimeMillis() - time) + "ms");
|
||||
} else {
|
||||
promise.fail("Failed to fetch columns for " + reportType + ": HTTP " + resp.statusCode());
|
||||
}
|
||||
})
|
||||
.onFailure(promise::fail);
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
// SHA-1
|
||||
private String sha1(String input) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-1");
|
||||
byte[] digest = md.digest(input.getBytes());
|
||||
return HexFormat.of().formatHex(digest).toLowerCase();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- Методы работы с БД (те же, с поддержкой UTF8) ----------
|
||||
private Future<Void> storeColumnsToDb(String reportType, JsonObject columns) {
|
||||
return getOrCreateReportType(reportType)
|
||||
.compose(reportTypeId -> {
|
||||
List<Future<Void>> fieldFutures = new ArrayList<>();
|
||||
for (String fieldKey : columns.fieldNames()) {
|
||||
JsonObject fieldDef = columns.getJsonObject(fieldKey);
|
||||
fieldFutures.add(storeSingleField(reportTypeId, fieldKey, fieldDef));
|
||||
}
|
||||
return Future.all(fieldFutures).mapEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
private Future<Integer> getOrCreateReportType(String reportType) {
|
||||
Promise<Integer> promise = Promise.promise();
|
||||
String selectSql = "SELECT report_type_id FROM report_types WHERE name = ?";
|
||||
dbPool.preparedQuery(selectSql)
|
||||
.execute(Tuple.of(reportType))
|
||||
.onComplete(ar -> {
|
||||
if (ar.succeeded() && ar.result().size() > 0) {
|
||||
promise.complete(ar.result().iterator().next().getInteger("report_type_id"));
|
||||
} else if (ar.succeeded()) {
|
||||
String insertSql = "INSERT INTO report_types (name, description) VALUES (?, ?)";
|
||||
dbPool.preparedQuery(insertSql)
|
||||
.execute(Tuple.of(reportType, "OLAP report type: " + reportType))
|
||||
.onComplete(insAr -> {
|
||||
if (insAr.succeeded()) {
|
||||
dbPool.preparedQuery(selectSql)
|
||||
.execute(Tuple.of(reportType))
|
||||
.onComplete(selAr -> {
|
||||
if (selAr.succeeded() && selAr.result().size() > 0) {
|
||||
promise.complete(selAr.result().iterator().next().getInteger("report_type_id"));
|
||||
} else {
|
||||
promise.fail("Cannot retrieve inserted report_type_id for " + reportType);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
promise.fail(insAr.cause());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
promise.fail(ar.cause());
|
||||
}
|
||||
});
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
private Future<Void> storeSingleField(int reportTypeId, String fieldKey, JsonObject fieldDef) {
|
||||
// Нормализованный ключ (без точек)
|
||||
String fieldKeyNormal = fieldKey.replace('.', '_');
|
||||
|
||||
String name = fieldDef.getString("name");
|
||||
String originalType = fieldDef.getString("type");
|
||||
String typeNormal = normalizeType(originalType);
|
||||
|
||||
boolean aggregationAllowed = fieldDef.getBoolean("aggregationAllowed", false);
|
||||
boolean groupingAllowed = fieldDef.getBoolean("groupingAllowed", false);
|
||||
boolean filteringAllowed = fieldDef.getBoolean("filteringAllowed", false);
|
||||
JsonArray tagsArray = fieldDef.getJsonArray("tags", new JsonArray());
|
||||
|
||||
String insertFieldSql = """
|
||||
INSERT INTO fields (
|
||||
report_type_id, field_key, field_key_normal, name, type, type_normal,
|
||||
aggregation_allowed, grouping_allowed, filtering_allowed
|
||||
)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
field_key_normal = VALUES(field_key_normal),
|
||||
name = VALUES(name),
|
||||
type_normal = VALUES(type_normal),
|
||||
aggregation_allowed = VALUES(aggregation_allowed),
|
||||
grouping_allowed = VALUES(grouping_allowed),
|
||||
filtering_allowed = VALUES(filtering_allowed)
|
||||
""";
|
||||
|
||||
return dbPool.preparedQuery(insertFieldSql)
|
||||
.execute(Tuple.of(
|
||||
reportTypeId, fieldKey, fieldKeyNormal, name, originalType, typeNormal,
|
||||
aggregationAllowed, groupingAllowed, filteringAllowed
|
||||
))
|
||||
.compose(ignored -> {
|
||||
String selectFieldIdSql = "SELECT field_id FROM fields WHERE report_type_id = ? AND field_key = ?";
|
||||
return dbPool.preparedQuery(selectFieldIdSql)
|
||||
.execute(Tuple.of(reportTypeId, fieldKey))
|
||||
.compose(rows -> {
|
||||
if (rows.size() == 0) {
|
||||
return Future.failedFuture("Field not found after upsert: " + fieldKey);
|
||||
}
|
||||
int fieldId = rows.iterator().next().getInteger("field_id");
|
||||
return processTags(fieldId, tagsArray);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private Future<Void> processTags(int fieldId, JsonArray tags) {
|
||||
List<Future<Void>> tagFutures = new ArrayList<>();
|
||||
for (Object tagObj : tags) {
|
||||
String tagName = tagObj.toString();
|
||||
tagFutures.add(getOrCreateTag(tagName)
|
||||
.compose(tagId -> linkFieldTag(fieldId, tagId)));
|
||||
}
|
||||
return Future.all(tagFutures).mapEmpty();
|
||||
}
|
||||
|
||||
private Future<Integer> getOrCreateTag(String tagName) {
|
||||
Promise<Integer> promise = Promise.promise();
|
||||
String selectSql = "SELECT tag_id FROM tags WHERE tag_name = ?";
|
||||
dbPool.preparedQuery(selectSql)
|
||||
.execute(Tuple.of(tagName))
|
||||
.onComplete(ar -> {
|
||||
if (ar.succeeded() && ar.result().size() > 0) {
|
||||
promise.complete(ar.result().iterator().next().getInteger("tag_id"));
|
||||
} else {
|
||||
String insertSql = "INSERT IGNORE INTO tags (tag_name) VALUES (?)";
|
||||
dbPool.preparedQuery(insertSql)
|
||||
.execute(Tuple.of(tagName))
|
||||
.onComplete(insAr -> {
|
||||
// После IGNORE всё равно выбираем ID (он мог уже существовать)
|
||||
dbPool.preparedQuery(selectSql)
|
||||
.execute(Tuple.of(tagName))
|
||||
.onComplete(selAr -> {
|
||||
if (selAr.succeeded() && selAr.result().size() > 0) {
|
||||
promise.complete(selAr.result().iterator().next().getInteger("tag_id"));
|
||||
} else {
|
||||
promise.fail("Cannot retrieve tag_id for " + tagName);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
private Future<Void> linkFieldTag(int fieldId, int tagId) {
|
||||
String sql = "INSERT IGNORE INTO field_tags (field_id, tag_id) VALUES (?, ?)";
|
||||
return dbPool.preparedQuery(sql)
|
||||
.execute(Tuple.of(fieldId, tagId))
|
||||
.mapEmpty();
|
||||
}
|
||||
|
||||
private Future<Void> createTablesIfNotExist() {
|
||||
String createReportTypesTable = """
|
||||
CREATE TABLE IF NOT EXISTS report_types (
|
||||
report_type_id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(50) UNIQUE NOT NULL,
|
||||
description TEXT NOT NULL
|
||||
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
|
||||
""";
|
||||
String createFieldsTable = """
|
||||
CREATE TABLE IF NOT EXISTS fields (
|
||||
field_id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
report_type_id INT NOT NULL,
|
||||
field_key VARCHAR(255) NOT NULL,
|
||||
field_key_normal VARCHAR(255) NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
type VARCHAR(50) NOT NULL,
|
||||
type_normal VARCHAR(50) NOT NULL,
|
||||
aggregation_allowed BOOLEAN NOT NULL DEFAULT 0,
|
||||
grouping_allowed BOOLEAN NOT NULL DEFAULT 0,
|
||||
filtering_allowed BOOLEAN NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uk_fields_report_type_field_key (report_type_id, field_key),
|
||||
FOREIGN KEY (report_type_id) REFERENCES report_types(report_type_id) ON DELETE RESTRICT
|
||||
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
|
||||
""";
|
||||
String createTagsTable = """
|
||||
CREATE TABLE IF NOT EXISTS tags (
|
||||
tag_id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
tag_name VARCHAR(100) UNIQUE NOT NULL
|
||||
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
|
||||
""";
|
||||
String createFieldTagsTable = """
|
||||
CREATE TABLE IF NOT EXISTS field_tags (
|
||||
field_id INT NOT NULL,
|
||||
tag_id INT NOT NULL,
|
||||
PRIMARY KEY (field_id, tag_id),
|
||||
FOREIGN KEY (field_id) REFERENCES fields(field_id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (tag_id) REFERENCES tags(tag_id) ON DELETE CASCADE
|
||||
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
|
||||
""";
|
||||
String createIdxFieldsReportType = "CREATE INDEX IF NOT EXISTS idx_fields_report_type ON fields(report_type_id)";
|
||||
String createIdxFieldsName = "CREATE INDEX IF NOT EXISTS idx_fields_name ON fields(name)";
|
||||
String createIdxFieldTagsTagId = "CREATE INDEX IF NOT EXISTS idx_field_tags_tag_id ON field_tags(tag_id)";
|
||||
|
||||
return dbPool.query(createReportTypesTable).execute()
|
||||
.compose(ignored -> dbPool.query(createFieldsTable).execute())
|
||||
.compose(ignored -> dbPool.query(createTagsTable).execute())
|
||||
.compose(ignored -> dbPool.query(createFieldTagsTable).execute())
|
||||
.compose(ignored -> dbPool.query(createIdxFieldsReportType).execute())
|
||||
.compose(ignored -> dbPool.query(createIdxFieldsName).execute())
|
||||
.compose(ignored -> dbPool.query(createIdxFieldTagsTagId).execute())
|
||||
.mapEmpty();
|
||||
}
|
||||
|
||||
private String normalizeType(String iikoType) {
|
||||
if (iikoType == null) return "string";
|
||||
return switch (iikoType) {
|
||||
case "ENUM", "STRING", "ID", "ID_STRING" -> "string";
|
||||
case "DATETIME" -> "datetime";
|
||||
case "INTEGER", "DURATION_IN_SECONDS" -> "integer";
|
||||
case "PERCENT", "AMOUNT", "MONEY" -> "decimal";
|
||||
default -> "string";
|
||||
};
|
||||
}
|
||||
|
||||
public void close() {
|
||||
dbPool.close();
|
||||
httpClient.close();
|
||||
}
|
||||
}
|
||||
43
src/main/java/su/xserver/iikocon/test/Main.java
Normal file
43
src/main/java/su/xserver/iikocon/test/Main.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package su.xserver.iikocon.test;
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Main {
|
||||
private static final Logger log = LoggerFactory.getLogger(Main.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
long time = System.currentTimeMillis();
|
||||
|
||||
Vertx vertx = Vertx.vertx();
|
||||
|
||||
IikoOlapColumnsImporter importer = new IikoOlapColumnsImporter(
|
||||
vertx,
|
||||
"folk-amber-co.iiko.it", // без https://
|
||||
"4444",
|
||||
"4444",
|
||||
"phpmyadmin.xserver.su", // хост MariaDB
|
||||
3306,
|
||||
"test", // имя БД
|
||||
"test",
|
||||
"test"
|
||||
);
|
||||
|
||||
|
||||
|
||||
importer.fetchAndStoreAll()
|
||||
.onComplete(ar -> {
|
||||
if (ar.succeeded()) {
|
||||
System.out.println("Import completed successfully.");
|
||||
log.info("time to sc: {}", (System.currentTimeMillis() - time) + "ms");
|
||||
|
||||
} else {
|
||||
System.err.println("Import failed: " + ar.cause().getMessage());
|
||||
}
|
||||
// importer.close();
|
||||
// vertx.close();
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
109
src/main/java/su/xserver/iikocon/test/ProxyVerticlev2.java
Normal file
109
src/main/java/su/xserver/iikocon/test/ProxyVerticlev2.java
Normal file
@@ -0,0 +1,109 @@
|
||||
package su.xserver.iikocon.test;
|
||||
|
||||
import io.vertx.core.AbstractVerticle;
|
||||
import io.vertx.core.Promise;
|
||||
import io.vertx.core.http.HttpMethod;
|
||||
import io.vertx.core.json.Json;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.web.Router;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import io.vertx.ext.web.client.WebClient;
|
||||
import io.vertx.ext.web.client.WebClientOptions;
|
||||
import io.vertx.ext.web.codec.BodyCodec;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HexFormat;
|
||||
|
||||
public class ProxyVerticlev2 extends AbstractVerticle {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ProxyVerticlev2.class);
|
||||
private WebClient webClient;
|
||||
|
||||
@Override
|
||||
public void start(Promise<Void> startPromise) {
|
||||
webClient = WebClient.create(vertx, new WebClientOptions()
|
||||
.setSsl(true)
|
||||
.setTrustAll(true)
|
||||
.setVerifyHost(false));
|
||||
|
||||
Router router = Router.router(vertx);
|
||||
router.get("/").handler(this::handleGet);
|
||||
|
||||
int port = 80;
|
||||
vertx.createHttpServer()
|
||||
.requestHandler(router)
|
||||
.listen(port).onComplete(http -> {
|
||||
if (http.succeeded()) {
|
||||
log.info("Proxy server started on port {}", port);
|
||||
startPromise.complete();
|
||||
} else {
|
||||
startPromise.fail(http.cause());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleGet(RoutingContext ctx) {
|
||||
String server = "folk-amber-co.iiko.it";
|
||||
String password = "4444";
|
||||
String login = "4444";
|
||||
String reportType = "DELIVERIES";
|
||||
|
||||
String signature = sha1(password);
|
||||
String authUrl = "https://" + server + ":443/resto/api/auth?login=" + login + "&pass=" + signature;
|
||||
webClient.getAbs(authUrl)
|
||||
.as(BodyCodec.string())
|
||||
.send()
|
||||
.onSuccess(authResp -> {
|
||||
if (authResp.statusCode() != 200) {
|
||||
fail(ctx, authResp.statusCode(), "Authentication failed: " + authResp.statusMessage());
|
||||
return;
|
||||
}
|
||||
String token = authResp.body();
|
||||
String dataUrl = "https://" + server + "/resto/api/v2/reports/olap/columns" +
|
||||
"?key=" + token + "&reportType=" + reportType;
|
||||
log.info("URL: {}", dataUrl);
|
||||
webClient.getAbs(dataUrl)
|
||||
.as(BodyCodec.jsonObject())
|
||||
.send()
|
||||
.onSuccess(dataResp -> {
|
||||
// logout (fire and forget)
|
||||
webClient.getAbs("https://" + server + ":443/resto/api/logout?key=" + token)
|
||||
.send()
|
||||
.onFailure(err -> log.error("Logout failed: {}", err.getMessage()));
|
||||
if (dataResp.statusCode() == 200) {
|
||||
JsonObject responseBody = dataResp.body();
|
||||
log.info(dataResp.headers().toString());
|
||||
Object data = responseBody.getValue("data");
|
||||
if (data == null) {
|
||||
ctx.response().setStatusCode(200).end(responseBody.encode());
|
||||
} else {
|
||||
// data может быть массивом, объектом или другим типом
|
||||
ctx.response().setStatusCode(200).end(Json.encode(data));
|
||||
}
|
||||
} else {
|
||||
fail(ctx, dataResp.statusCode(), "External API error: " + dataResp.statusMessage());
|
||||
}
|
||||
})
|
||||
.onFailure(err -> fail(ctx, 500, "Data request failed: " + err.getMessage()));
|
||||
})
|
||||
.onFailure(err -> fail(ctx, 500, "Auth request failed: " + err.getMessage()));
|
||||
}
|
||||
|
||||
private String sha1(String input) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-1");
|
||||
byte[] digest = md.digest(input.getBytes());
|
||||
return HexFormat.of().formatHex(digest).toLowerCase();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void fail(RoutingContext ctx, int status, String message) {
|
||||
log.info("Error: {}", message);
|
||||
ctx.response().setStatusCode(status).end(new JsonObject().put("error", message).encode());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user