Files
iiko-connector/src/main/java/su/xserver/iikocon/iiko/IikoHandler.java
2026-05-07 01:35:07 +03:00

267 lines
10 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package su.xserver.iikocon.iiko;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.Tuple;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import su.xserver.iikocon.handler.AdminHandler;
import su.xserver.iikocon.handler.AuthHandler;
import su.xserver.iikocon.service.DataBaseService;
import su.xserver.iikocon.service.RestaurantService;
public class IikoHandler {
private final Logger log = LoggerFactory.getLogger("[IikoHandler]");
private final DataBaseService db;
private final Vertx vertx;
private final RestaurantService restaurantService;
public IikoHandler(Vertx vertx, Router router, DataBaseService db, RestaurantService restaurantService, AuthHandler authHandler) {
this.vertx = vertx;
this.restaurantService = restaurantService;
this.db = db;
createTablesIfNotExist().onFailure(err -> {
log.error("Failed to initialize database", err);
});
router.route("/api/reports/olap/*").handler(authHandler::requireAuth);
router.get("/api/reports/olap/columns").handler(this::getColumns);
router.delete("/api/reports/olap/columns/:fieldKey").handler(AdminHandler::requireAdmin).handler(this::deleteColumn);
router.post("/api/reports/olap/initialize").handler(AdminHandler::requireAdmin).handler(this::postInitialize);
}
private void getColumns(RoutingContext ctx) {
getAllFieldsWithReportAndTags()
.onSuccess(ar -> ctx.response()
.putHeader("Content-Type", "application/json")
.end(ar.encodePrettily()))
.onFailure(err -> ctx.response()
.setStatusCode(500)
.end(err.getMessage()));
}
public void deleteColumn(RoutingContext ctx) {
String fieldKey = ctx.pathParam("fieldKey");
String sql = "DELETE FROM iiko_fields_common WHERE field_key = ?";
db.getPool().preparedQuery(sql)
.execute(Tuple.of(fieldKey))
.onSuccess(res -> {
ctx.end();
})
.onFailure(err -> ctx.response().setStatusCode(500).end(err.getMessage()));
}
private void postInitialize(RoutingContext ctx) {
JsonObject body = ctx.body().asJsonObject();
if (body == null) {
ctx.response()
.setStatusCode(400)
.end("Request body is missing or not a JSON object");
return;
}
if (!body.containsKey("restaurantId") || body.getValue("restaurantId") == null) {
ctx.response()
.setStatusCode(400)
.end("restaurantId is required");
return;
}
Integer restaurantId;
try {
restaurantId = body.getInteger("restaurantId");
if (restaurantId == null) {
throw new IllegalArgumentException("restaurantId must be a number");
}
} catch (ClassCastException e) {
ctx.response()
.setStatusCode(400)
.end("restaurantId must be a valid integer");
return;
}
restaurantService.findById(restaurantId)
.onSuccess(rest -> {
IikoOlapClient iiko = new IikoOlapClient(vertx, rest);
iiko.checkConnection()
.onSuccess(ping -> clearTables()
.onSuccess(data -> {
IikoOlapColumnsImporter importer = new IikoOlapColumnsImporter(iiko, db);
importer.fetchAndStoreAll()
.onSuccess(res -> ctx.end("OK"))
.onFailure(err -> ctx.response()
.setStatusCode(400)
.end(err.getMessage()));
})
.onFailure(err -> ctx.response()
.setStatusCode(400)
.end(err.getMessage())))
.onFailure(err -> ctx.response().setStatusCode(400).end(err.getMessage()));
})
.onFailure(err -> ctx.response()
.setStatusCode(400)
.end(err.getMessage()));
}
public Future<JsonObject> getAllFieldsWithReportAndTags() {
String sql = """
SELECT
fc.field_key,
fc.field_key_normal,
fc.name,
fc.type,
fc.type_normal,
fc.aggregation_allowed,
fc.grouping_allowed,
fc.filtering_allowed,
GROUP_CONCAT(DISTINCT rt.name ORDER BY rt.name SEPARATOR ',') AS report_names,
GROUP_CONCAT(DISTINCT t.tag_name ORDER BY t.tag_name SEPARATOR ',') AS tag_names
FROM iiko_fields_common fc
LEFT JOIN iiko_report_type_fields rtf ON fc.field_id = rtf.field_id
LEFT JOIN iiko_report_types rt ON rtf.report_type_id = rt.report_type_id
LEFT JOIN iiko_field_tags ft ON fc.field_id = ft.field_id
LEFT JOIN iiko_tags t ON ft.tag_id = t.tag_id
GROUP BY fc.field_id
ORDER BY fc.field_key
""";
return db.getPool().query(sql).execute()
.map(rows -> {
JsonArray columnsArray = new JsonArray();
for (Row row : rows) {
String reportNamesStr = row.getString("report_names");
JsonArray reportTypes = new JsonArray();
if (reportNamesStr != null && !reportNamesStr.isBlank()) {
for (String name : reportNamesStr.split(",")) {
reportTypes.add(name.trim());
}
}
String tagNamesStr = row.getString("tag_names");
JsonArray tags = new JsonArray();
if (tagNamesStr != null && !tagNamesStr.isBlank()) {
for (String tag : tagNamesStr.split(",")) {
tags.add(tag.trim());
}
}
JsonObject fieldObj = new JsonObject()
.put("fieldKey", row.getString("field_key"))
.put("fieldKeyNormal", row.getString("field_key_normal"))
.put("reportTypes", reportTypes)
.put("name", row.getString("name"))
.put("type", row.getString("type"))
.put("typeNormal", row.getString("type_normal"))
.put("aggregationAllowed", row.getBoolean("aggregation_allowed"))
.put("groupingAllowed", row.getBoolean("grouping_allowed"))
.put("filteringAllowed", row.getBoolean("filtering_allowed"))
.put("tags", tags);
columnsArray.add(fieldObj);
}
return new JsonObject().put("columns", columnsArray);
});
}
private Future<Void> createTablesIfNotExist() {
String createReportTypes = """
CREATE TABLE IF NOT EXISTS iiko_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 createFieldsCommon = """
CREATE TABLE IF NOT EXISTS iiko_fields_common (
field_id INT AUTO_INCREMENT PRIMARY KEY,
field_key VARCHAR(255) NOT NULL UNIQUE,
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
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
""";
String createReportTypeFields = """
CREATE TABLE IF NOT EXISTS iiko_report_type_fields (
report_type_id INT NOT NULL,
field_id INT NOT NULL,
PRIMARY KEY (report_type_id, field_id),
FOREIGN KEY (report_type_id) REFERENCES iiko_report_types(report_type_id) ON DELETE CASCADE,
FOREIGN KEY (field_id) REFERENCES iiko_fields_common(field_id) ON DELETE CASCADE
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
""";
String createTags = """
CREATE TABLE IF NOT EXISTS iiko_tags (
tag_id INT AUTO_INCREMENT PRIMARY KEY,
tag_name VARCHAR(100) UNIQUE NOT NULL
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
""";
String createFieldTags = """
CREATE TABLE IF NOT EXISTS iiko_field_tags (
field_id INT NOT NULL,
tag_id INT NOT NULL,
PRIMARY KEY (field_id, tag_id),
FOREIGN KEY (field_id) REFERENCES iiko_fields_common(field_id) ON DELETE CASCADE,
FOREIGN KEY (tag_id) REFERENCES iiko_tags(tag_id) ON DELETE CASCADE
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
""";
String idxKeyNormal = "CREATE INDEX IF NOT EXISTS idx_fields_common_key_normal ON iiko_fields_common(field_key_normal)";
String idxFieldName = "CREATE INDEX IF NOT EXISTS idx_fields_common_name ON iiko_fields_common(name)";
String idxFieldTagsTag = "CREATE INDEX IF NOT EXISTS idx_field_tags_tag_id ON iiko_field_tags(tag_id)";
return db.getPool().query(createReportTypes).execute()
.compose(v -> db.getPool().query(createFieldsCommon).execute())
.compose(v -> db.getPool().query(createReportTypeFields).execute())
.compose(v -> db.getPool().query(createTags).execute())
.compose(v -> db.getPool().query(createFieldTags).execute())
.compose(v -> db.getPool().query(idxKeyNormal).execute())
.compose(v -> db.getPool().query(idxFieldName).execute())
.compose(v -> db.getPool().query(idxFieldTagsTag).execute())
.mapEmpty();
}
private Future<Void> clearTables() {
String sql = """
-- Отключаем проверку внешних ключей
SET FOREIGN_KEY_CHECKS = 0;
-- Удаляем данные из всех таблиц (порядок не важен при отключённой проверке)
DELETE FROM iiko_field_tags;
DELETE FROM iiko_report_type_fields;
DELETE FROM iiko_fields_common;
DELETE FROM iiko_tags;
DELETE FROM iiko_report_types;
-- Сбрасываем счётчики AUTO_INCREMENT (чтобы новые ID начинались с 1)
ALTER TABLE iiko_fields_common AUTO_INCREMENT = 1;
ALTER TABLE iiko_tags AUTO_INCREMENT = 1;
ALTER TABLE iiko_report_types AUTO_INCREMENT = 1;
-- Включаем проверку обратно
SET FOREIGN_KEY_CHECKS = 1;
""";
return db.getPool().query(sql).execute().mapEmpty();
}
}