up, add OLAP columns page
This commit is contained in:
263
src/main/java/su/xserver/iikocon/iiko/IikoHandler.java
Normal file
263
src/main/java/su/xserver/iikocon/iiko/IikoHandler.java
Normal file
@@ -0,0 +1,263 @@
|
||||
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.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) {
|
||||
this.vertx = vertx;
|
||||
this.restaurantService = restaurantService;
|
||||
this.db = db;
|
||||
|
||||
createTablesIfNotExist().onFailure(err -> {
|
||||
log.error("Failed to initialize database", err);
|
||||
});
|
||||
|
||||
router.get("/api/reports/olap/columns").handler(this::getColumns);
|
||||
router.delete("/api/reports/olap/columns/:fieldKey").handler(this::deleteColumn);
|
||||
router.post("/api/reports/olap/initialize").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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user