fix: we no longer use sql-client-templates

This commit is contained in:
2026-05-08 13:55:46 +03:00
parent fa0b2518af
commit 1531215b43
9 changed files with 175 additions and 208 deletions

View File

@@ -40,7 +40,6 @@ dependencies {
implementation("io.vertx:vertx-launcher-application") implementation("io.vertx:vertx-launcher-application")
implementation("io.vertx:vertx-web-client") implementation("io.vertx:vertx-web-client")
implementation("io.vertx:vertx-config") implementation("io.vertx:vertx-config")
implementation("io.vertx:vertx-sql-client-templates")
implementation("io.vertx:vertx-health-check") implementation("io.vertx:vertx-health-check")
implementation("io.vertx:vertx-web") implementation("io.vertx:vertx-web")
implementation("io.vertx:vertx-mysql-client") implementation("io.vertx:vertx-mysql-client")

View File

@@ -569,7 +569,7 @@ public class MainVerticle extends AbstractVerticle {
return; return;
} }
if (!olapQueryService.isValidTableName(tableName)) { if (olapQueryService.isValidTableName(tableName)) {
rc.response().setStatusCode(400).end("Invalid tableName: must start with a letter and contain only letters and digits"); rc.response().setStatusCode(400).end("Invalid tableName: must start with a letter and contain only letters and digits");
return; return;
} }
@@ -606,7 +606,7 @@ public class MainVerticle extends AbstractVerticle {
return; return;
} }
if (!olapQueryService.isValidTableName(tableName)) { if (olapQueryService.isValidTableName(tableName)) {
rc.response().setStatusCode(400).end("Invalid tableName: must start with a letter and contain only letters and digits"); rc.response().setStatusCode(400).end("Invalid tableName: must start with a letter and contain only letters and digits");
return; return;
} }

View File

@@ -6,15 +6,14 @@ import io.vertx.core.json.JsonObject;
import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Pool;
import io.vertx.sqlclient.Row; import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.Tuple; import io.vertx.sqlclient.Tuple;
import io.vertx.sqlclient.templates.SqlTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import su.xserver.iikocon.service.ExternalDataBaseService; import su.xserver.iikocon.service.ExternalDataBaseService;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.*; import java.util.*;
public class OlapQueryService { public class OlapQueryService {
private static final Logger log = LoggerFactory.getLogger(OlapQueryService.class);
private final Pool pool; private final Pool pool;
private final ExternalDataBaseService externalDataBaseService; private final ExternalDataBaseService externalDataBaseService;
private final SqlGenerator sqlGenerator; private final SqlGenerator sqlGenerator;
@@ -62,17 +61,16 @@ public class OlapQueryService {
public Future<Integer> createQuery(String name, int dbConnectionId, JsonObject config, public Future<Integer> createQuery(String name, int dbConnectionId, JsonObject config,
List<Integer> restaurantIds, String generatedSql, boolean active) { List<Integer> restaurantIds, String generatedSql, boolean active) {
JsonObject fullConfig = generateFullIikoJson(config); JsonObject fullConfig = generateFullIikoJson(config);
Map<String, Object> params = Map.of( String sql = "INSERT INTO olap_queries (name, db_connection_id, config_json, full_config_json, sql_text, active) VALUES (?, ?, ?, ?, ?, ?)";
"name", name, Tuple params = Tuple.of(
"db_connection_id", dbConnectionId, name,
"config_json", config.encode(), dbConnectionId,
"full_config_json", fullConfig.encode(), config.encode(),
"sql_text", generatedSql != null ? generatedSql : "", fullConfig.encode(),
"active", active generatedSql != null ? generatedSql : "",
active
); );
String sql = "INSERT INTO olap_queries (name, db_connection_id, config_json, full_config_json, sql_text, active) VALUES (#{name}, #{db_connection_id}, #{config_json}, #{full_config_json}, #{sql_text}, #{active})"; return pool.preparedQuery(sql).execute(params)
return SqlTemplate.forUpdate(pool, sql)
.execute(params)
.compose(rows -> getLastInsertId()) .compose(rows -> getLastInsertId())
.compose(queryId -> linkRestaurants(queryId, restaurantIds).map(queryId)); .compose(queryId -> linkRestaurants(queryId, restaurantIds).map(queryId));
} }
@@ -80,22 +78,23 @@ public class OlapQueryService {
public Future<Void> updateQuery(int id, String name, int dbConnectionId, JsonObject config, public Future<Void> updateQuery(int id, String name, int dbConnectionId, JsonObject config,
List<Integer> restaurantIds, String generatedSql, boolean active) { List<Integer> restaurantIds, String generatedSql, boolean active) {
JsonObject fullConfig = generateFullIikoJson(config); JsonObject fullConfig = generateFullIikoJson(config);
Map<String, Object> params = Map.of( String sql = "UPDATE olap_queries SET name = ?, db_connection_id = ?, config_json = ?, full_config_json = ?, sql_text = ?, active = ? WHERE id = ?";
"id", id, Tuple params = Tuple.of(
"name", name, name,
"db_connection_id", dbConnectionId, dbConnectionId,
"config_json", config.encode(), config.encode(),
"full_config_json", fullConfig.encode(), fullConfig.encode(),
"sql_text", generatedSql != null ? generatedSql : "", generatedSql != null ? generatedSql : "",
"active", active active,
id
); );
String sql = "UPDATE olap_queries SET name = #{name}, db_connection_id = #{db_connection_id}, config_json = #{config_json}, full_config_json = #{full_config_json}, sql_text = #{sql_text}, active = #{active} WHERE id = #{id}"; return pool.preparedQuery(sql).execute(params)
return SqlTemplate.forUpdate(pool, sql)
.execute(params)
.compose(v -> pool.query("DELETE FROM olap_query_restaurants WHERE query_id = " + id).execute() .compose(v -> pool.query("DELETE FROM olap_query_restaurants WHERE query_id = " + id).execute()
.compose(del -> linkRestaurants(id, restaurantIds))).mapEmpty(); .compose(del -> linkRestaurants(id, restaurantIds)))
.mapEmpty();
} }
public JsonObject generateFullIikoJson(JsonObject clientConfig) { public JsonObject generateFullIikoJson(JsonObject clientConfig) {
String reportType = clientConfig.getString("reportType", "SALES"); String reportType = clientConfig.getString("reportType", "SALES");
boolean buildSummary = clientConfig.getBoolean("buildSummary", false); boolean buildSummary = clientConfig.getBoolean("buildSummary", false);
@@ -153,17 +152,15 @@ public class OlapQueryService {
} }
private JsonObject buildDateFilter(String reportType, String dateToStr, int daysBack) { private JsonObject buildDateFilter(String reportType, String dateToStr, int daysBack) {
// Определяем корректную дату "до" (конец дня) ZonedDateTime toDate;
java.time.ZonedDateTime toDate;
if (dateToStr != null && !dateToStr.isEmpty()) { if (dateToStr != null && !dateToStr.isEmpty()) {
toDate = java.time.LocalDate.parse(dateToStr).atStartOfDay(java.time.ZoneOffset.UTC); toDate = LocalDate.parse(dateToStr).atStartOfDay(ZoneOffset.UTC);
} else { } else {
toDate = java.time.ZonedDateTime.now(java.time.ZoneOffset.UTC); toDate = ZonedDateTime.now(ZoneOffset.UTC);
} }
toDate = toDate.withHour(23).withMinute(59).withSecond(59).withNano(999_999_999); toDate = toDate.withHour(23).withMinute(59).withSecond(59).withNano(999_999_999);
java.time.ZonedDateTime fromDate = toDate.minusDays(Math.max(1, daysBack)) ZonedDateTime fromDate = toDate.minusDays(Math.max(1, daysBack))
.withHour(0).withMinute(0).withSecond(0).withNano(0); .withHour(0).withMinute(0).withSecond(0).withNano(0);
String filterKey = "TRANSACTIONS".equals(reportType) ? "DateTime.DateTyped" : "OpenDate.Typed"; String filterKey = "TRANSACTIONS".equals(reportType) ? "DateTime.DateTyped" : "OpenDate.Typed";
return new JsonObject().put(filterKey, new JsonObject() return new JsonObject().put(filterKey, new JsonObject()
.put("filterType", "DateRange") .put("filterType", "DateRange")
@@ -179,23 +176,19 @@ public class OlapQueryService {
private Future<Void> linkRestaurants(int queryId, List<Integer> restaurantIds) { private Future<Void> linkRestaurants(int queryId, List<Integer> restaurantIds) {
if (restaurantIds == null || restaurantIds.isEmpty()) return Future.succeededFuture(); if (restaurantIds == null || restaurantIds.isEmpty()) return Future.succeededFuture();
String sql = "INSERT INTO olap_query_restaurants (query_id, restaurant_id) VALUES (?, ?)";
List<Future<Void>> futures = new ArrayList<>(); List<Future<Void>> futures = new ArrayList<>();
for (Integer restId : restaurantIds) { for (Integer restId : restaurantIds) {
Map<String, Object> params = Map.of("query_id", queryId, "restaurant_id", restId); futures.add(pool.preparedQuery(sql).execute(Tuple.of(queryId, restId)).mapEmpty());
futures.add(SqlTemplate.forUpdate(pool,
"INSERT INTO olap_query_restaurants (query_id, restaurant_id) VALUES (#{query_id}, #{restaurant_id})")
.execute(params).mapEmpty());
} }
return Future.all(futures).mapEmpty(); return Future.all(futures).mapEmpty();
} }
// Удаление
public Future<Void> deleteQuery(int id) { public Future<Void> deleteQuery(int id) {
return SqlTemplate.forUpdate(pool, "DELETE FROM olap_queries WHERE id = #{id}") String sql = "DELETE FROM olap_queries WHERE id = ?";
.execute(Map.of("id", id)).mapEmpty(); return pool.preparedQuery(sql).execute(Tuple.of(id)).mapEmpty();
} }
// Получить все запросы (без config_json, для списка)
public Future<JsonArray> getAllQueries() { public Future<JsonArray> getAllQueries() {
String sql = """ String sql = """
SELECT q.id, q.name, q.db_connection_id, q.active, q.last_run, q.last_run_success, q.created, q.updated, SELECT q.id, q.name, q.db_connection_id, q.active, q.last_run, q.last_run_success, q.created, q.updated,
@@ -226,13 +219,10 @@ public class OlapQueryService {
}); });
} }
// Получить один запрос с полной конфигурацией
public Future<JsonObject> getQueryById(int id) { public Future<JsonObject> getQueryById(int id) {
String querySql = "SELECT id, name, db_connection_id, config_json, sql_text, active, created, updated FROM olap_queries WHERE id = ?"; String querySql = "SELECT id, name, db_connection_id, config_json, sql_text, active, created, updated FROM olap_queries WHERE id = ?";
String restaurantsSql = "SELECT restaurant_id FROM olap_query_restaurants WHERE query_id = ?"; String restaurantsSql = "SELECT restaurant_id FROM olap_query_restaurants WHERE query_id = ?";
return pool.preparedQuery(querySql).execute(Tuple.of(id))
return pool.preparedQuery(querySql).execute(io.vertx.sqlclient.Tuple.of(id))
.compose(rows -> { .compose(rows -> {
if (rows.size() == 0) return Future.succeededFuture(null); if (rows.size() == 0) return Future.succeededFuture(null);
Row row = rows.iterator().next(); Row row = rows.iterator().next();
@@ -245,8 +235,7 @@ public class OlapQueryService {
.put("active", row.getBoolean("active")) .put("active", row.getBoolean("active"))
.put("created", row.getLocalDateTime("created") != null ? row.getLocalDateTime("created").toString() : null) .put("created", row.getLocalDateTime("created") != null ? row.getLocalDateTime("created").toString() : null)
.put("updated", row.getLocalDateTime("updated") != null ? row.getLocalDateTime("updated").toString() : null); .put("updated", row.getLocalDateTime("updated") != null ? row.getLocalDateTime("updated").toString() : null);
return pool.preparedQuery(restaurantsSql).execute(Tuple.of(id))
return pool.preparedQuery(restaurantsSql).execute(io.vertx.sqlclient.Tuple.of(id))
.map(restRows -> { .map(restRows -> {
JsonArray restIds = new JsonArray(); JsonArray restIds = new JsonArray();
restRows.forEach(restRow -> restIds.add(restRow.getInteger("restaurant_id"))); restRows.forEach(restRow -> restIds.add(restRow.getInteger("restaurant_id")));
@@ -262,7 +251,6 @@ public class OlapQueryService {
return pool.preparedQuery(sql).execute(Tuple.of(success, queryId)).mapEmpty(); return pool.preparedQuery(sql).execute(Tuple.of(success, queryId)).mapEmpty();
} }
// Генерация SQL на основе конфигурации и ID подключения
public Future<String> generateSql(JsonObject config, int dbConnectionId) { public Future<String> generateSql(JsonObject config, int dbConnectionId) {
return externalDataBaseService.findById(dbConnectionId) return externalDataBaseService.findById(dbConnectionId)
.compose(conn -> { .compose(conn -> {
@@ -278,9 +266,8 @@ public class OlapQueryService {
} }
public boolean isValidTableName(String tableName) { public boolean isValidTableName(String tableName) {
if (tableName == null) return false; if (tableName == null) return true;
String trimmed = tableName.trim(); String trimmed = tableName.trim();
// Первый символ — английская буква, далее буквы или цифры return !trimmed.matches("^[A-Za-z][A-Za-z0-9]*$");
return trimmed.matches("^[A-Za-z][A-Za-z0-9]*$");
} }
} }

View File

@@ -11,13 +11,9 @@ import io.vertx.jdbcclient.JDBCPool;
import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Pool;
import io.vertx.sqlclient.PoolOptions; import io.vertx.sqlclient.PoolOptions;
import io.vertx.sqlclient.Row; import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.templates.SqlTemplate; import io.vertx.sqlclient.Tuple;
import su.xserver.iikocon.handler.AdminHandler; import su.xserver.iikocon.handler.AdminHandler;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class ExternalDataBaseService { public class ExternalDataBaseService {
private final Pool pool; private final Pool pool;
private final Vertx vertx; private final Vertx vertx;
@@ -106,7 +102,7 @@ public class ExternalDataBaseService {
CREATE TABLE IF NOT EXISTS external_database ( CREATE TABLE IF NOT EXISTS external_database (
id INT AUTO_INCREMENT PRIMARY KEY, id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) UNIQUE NOT NULL, name VARCHAR(255) UNIQUE NOT NULL,
type VARCHAR(40) UNIQUE NOT NULL, type VARCHAR(40) NOT NULL,
host VARCHAR(255) NOT NULL, host VARCHAR(255) NOT NULL,
port INT NOT NULL, port INT NOT NULL,
database VARCHAR(255) NOT NULL, database VARCHAR(255) NOT NULL,
@@ -120,18 +116,9 @@ public class ExternalDataBaseService {
} }
public Future<Void> createDataBase(String name, String type, String host, int port, String database, String user, String password) { public Future<Void> createDataBase(String name, String type, String host, int port, String database, String user, String password) {
Map<String, Object> params = Map.of( String sql = "INSERT INTO external_database (name, type, host, port, database, user, password) VALUES (?, ?, ?, ?, ?, ?, ?)";
"name", name, return pool.preparedQuery(sql)
"type", type, .execute(Tuple.of(name, type, host, port, database, user, password))
"host", host,
"port", port,
"database", database,
"user", user,
"password", password
);
return SqlTemplate.forUpdate(pool,
"INSERT INTO external_database (name, type, host, port, database, user, password) VALUES (#{name}, #{type}, #{host}, #{port}, #{database}, #{user}, #{password})")
.execute(params)
.mapEmpty(); .mapEmpty();
} }
@@ -159,45 +146,47 @@ public class ExternalDataBaseService {
} }
public Future<JsonObject> findById(int id) { public Future<JsonObject> findById(int id) {
return SqlTemplate.forQuery(pool, String sql = "SELECT id, name, type, host, port, database, user, password, created, updated FROM external_database WHERE id = ?";
"SELECT id, name, type, host, port, database, user, password, created, updated FROM external_database WHERE id = #{id}") return pool.preparedQuery(sql)
.mapTo(row -> new JsonObject() .execute(Tuple.of(id))
.put("id", row.getInteger("id")) .map(rows -> {
.put("name", row.getString("name")) if (rows.iterator().hasNext()) {
.put("type", row.getString("type")) Row row = rows.iterator().next();
.put("host", row.getString("host")) return new JsonObject()
.put("port", row.getInteger("port")) .put("id", row.getInteger("id"))
.put("database", row.getString("database")) .put("name", row.getString("name"))
.put("user", row.getString("user")) .put("type", row.getString("type"))
.put("password", row.getString("password")) .put("host", row.getString("host"))
.put("created", row.getLocalDateTime("created") != null ? row.getLocalDateTime("created").toString() : null) .put("port", row.getInteger("port"))
.put("updated", row.getLocalDateTime("updated") != null ? row.getLocalDateTime("updated").toString() : null)) .put("database", row.getString("database"))
.execute(Collections.singletonMap("id", id)) .put("user", row.getString("user"))
.map(rows -> rows.iterator().hasNext() ? rows.iterator().next() : null); .put("password", row.getString("password"))
.put("created", row.getLocalDateTime("created") != null ? row.getLocalDateTime("created").toString() : null)
.put("updated", row.getLocalDateTime("updated") != null ? row.getLocalDateTime("updated").toString() : null);
} else {
return null;
}
});
} }
public Future<Void> updateDataBase(int id, String name, String type, String host, int port, String database, String user, String password) { public Future<Void> updateDataBase(int id, String name, String type, String host, int port, String database, String user, String password) {
Map<String, Object> params = new HashMap<>();
params.put("id", id);
params.put("name", name);
params.put("type", type);
params.put("host", host);
params.put("port", port);
params.put("database", database);
params.put("user", user);
String sql; String sql;
Tuple params;
if (password != null && !password.isEmpty()) { if (password != null && !password.isEmpty()) {
params.put("password", password); sql = "UPDATE external_database SET name = ?, type = ?, host = ?, port = ?, database = ?, user = ?, password = ? WHERE id = ?";
sql = "UPDATE external_database SET name = #{name}, type = #{type}, host = #{host}, port = #{port}, database = #{database}, user = #{user}, password = #{password} WHERE id = #{id}"; params = Tuple.of(name, type, host, port, database, user, password, id);
} else { } else {
sql = "UPDATE external_database SET name = #{name}, type = #{type}, host = #{host}, port = #{port}, database = #{database}, user = #{user} WHERE id = #{id}"; sql = "UPDATE external_database SET name = ?, type = ?, host = ?, port = ?, database = ?, user = ? WHERE id = ?";
params = Tuple.of(name, type, host, port, database, user, id);
} }
return SqlTemplate.forUpdate(pool, sql).execute(params).mapEmpty(); return pool.preparedQuery(sql)
.execute(params)
.mapEmpty();
} }
public Future<Void> deleteDataBase(int id) { public Future<Void> deleteDataBase(int id) {
return SqlTemplate.forUpdate(pool, "DELETE FROM external_database WHERE id = #{id}") return pool.preparedQuery("DELETE FROM external_database WHERE id = ?")
.execute(Collections.singletonMap("id", id)) .execute(Tuple.of(id))
.mapEmpty(); .mapEmpty();
} }

View File

@@ -5,14 +5,11 @@ import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject; import io.vertx.core.json.JsonObject;
import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Pool;
import io.vertx.sqlclient.Row; import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.templates.SqlTemplate; import io.vertx.sqlclient.Tuple;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class RestaurantService { public class RestaurantService {
private final Pool pool; private final Pool pool;
@@ -43,7 +40,7 @@ public class RestaurantService {
CREATE TABLE IF NOT EXISTS restaurants ( CREATE TABLE IF NOT EXISTS restaurants (
id INT AUTO_INCREMENT PRIMARY KEY, id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) UNIQUE NOT NULL, name VARCHAR(255) UNIQUE NOT NULL,
login VARCHAR(255) UNIQUE NOT NULL, login VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL, password VARCHAR(255) NOT NULL,
host VARCHAR(255) NOT NULL, host VARCHAR(255) NOT NULL,
https BOOLEAN DEFAULT FALSE, https BOOLEAN DEFAULT FALSE,
@@ -56,15 +53,9 @@ public class RestaurantService {
public Future<Void> createRestaurant(String name, String login, String password, String host, boolean https) { public Future<Void> createRestaurant(String name, String login, String password, String host, boolean https) {
String hashedPassword = hashPassword(password); String hashedPassword = hashPassword(password);
Map<String, Object> params = Map.of( String sql = "INSERT INTO restaurants (name, login, password, host, https) VALUES (?, ?, ?, ?, ?)";
"name", name, Tuple params = Tuple.of(name, login, hashedPassword, host, https);
"login", login, return pool.preparedQuery(sql)
"password", hashedPassword,
"host", host,
"https", https
);
return SqlTemplate.forUpdate(pool,
"INSERT INTO restaurants (name, login, password, host, https) VALUES (#{name}, #{login}, #{password}, #{host}, #{https})")
.execute(params) .execute(params)
.mapEmpty(); .mapEmpty();
} }
@@ -91,42 +82,44 @@ public class RestaurantService {
} }
public Future<JsonObject> findById(int id) { public Future<JsonObject> findById(int id) {
return SqlTemplate.forQuery(pool, String sql = "SELECT id, name, login, password, https, host, created, updated FROM restaurants WHERE id = ?";
"SELECT id, name, login, password, https, host, created, updated FROM restaurants WHERE id = #{id}") return pool.preparedQuery(sql)
.mapTo(row -> new JsonObject() .execute(Tuple.of(id))
.put("id", row.getInteger("id")) .map(rows -> {
.put("name", row.getString("name")) Row row = rows.iterator().hasNext() ? rows.iterator().next() : null;
.put("login", row.getString("login")) if (row == null) return null;
.put("password", row.getString("password")) return new JsonObject()
.put("https", row.getBoolean("https")) .put("id", row.getInteger("id"))
.put("host", row.getString("host")) .put("name", row.getString("name"))
.put("created", row.getLocalDateTime("created") != null ? row.getLocalDateTime("created").toString() : null) .put("login", row.getString("login"))
.put("updated", row.getLocalDateTime("updated") != null ? row.getLocalDateTime("updated").toString() : null)) .put("password", row.getString("password"))
.execute(Collections.singletonMap("id", id)) .put("https", row.getBoolean("https"))
.map(rows -> rows.iterator().hasNext() ? rows.iterator().next() : null); .put("host", row.getString("host"))
.put("created", row.getLocalDateTime("created") != null ? row.getLocalDateTime("created").toString() : null)
.put("updated", row.getLocalDateTime("updated") != null ? row.getLocalDateTime("updated").toString() : null);
});
} }
public Future<Void> updateRestaurant(int id, String name, String login, String password, String host, boolean https) { public Future<Void> updateRestaurant(int id, String name, String login, String password, String host, boolean https) {
Map<String, Object> params = new HashMap<>();
params.put("id", id);
params.put("name", name);
params.put("login", login);
params.put("host", host);
params.put("https", https);
String sql; String sql;
Tuple params;
if (password != null && !password.isEmpty()) { if (password != null && !password.isEmpty()) {
String hashedPassword = hashPassword(password); String hashedPassword = hashPassword(password);
params.put("password", hashedPassword); sql = "UPDATE restaurants SET name = ?, login = ?, password = ?, host = ?, https = ? WHERE id = ?";
sql = "UPDATE restaurants SET name = #{name}, login = #{login}, password = #{password}, host = #{host}, https = #{https} WHERE id = #{id}"; params = Tuple.of(name, login, hashedPassword, host, https, id);
} else { } else {
sql = "UPDATE restaurants SET name = #{name}, login = #{login}, host = #{host}, https = #{https} WHERE id = #{id}"; sql = "UPDATE restaurants SET name = ?, login = ?, host = ?, https = ? WHERE id = ?";
params = Tuple.of(name, login, host, https, id);
} }
return SqlTemplate.forUpdate(pool, sql).execute(params).mapEmpty(); return pool.preparedQuery(sql)
.execute(params)
.mapEmpty();
} }
public Future<Void> deleteRestaurant(int id) { public Future<Void> deleteRestaurant(int id) {
return SqlTemplate.forUpdate(pool, "DELETE FROM restaurants WHERE id = #{id}") String sql = "DELETE FROM restaurants WHERE id = ?";
.execute(Collections.singletonMap("id", id)) return pool.preparedQuery(sql)
.execute(Tuple.of(id))
.mapEmpty(); .mapEmpty();
} }
} }

View File

@@ -4,10 +4,10 @@ import io.vertx.core.Future;
import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject; import io.vertx.core.json.JsonObject;
import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Pool;
import io.vertx.sqlclient.templates.SqlTemplate; import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.Tuple;
import java.util.List; import java.util.List;
import java.util.Map;
public class SettingsService { public class SettingsService {
private final Pool pool; private final Pool pool;
@@ -136,16 +136,20 @@ public class SettingsService {
} }
public Future<String> get(String key) { public Future<String> get(String key) {
return SqlTemplate.forQuery(pool, "SELECT setting_value FROM app_settings WHERE setting_key = #{key}") String sql = "SELECT setting_value FROM app_settings WHERE setting_key = ?";
.execute(Map.of("key", key)) return pool.preparedQuery(sql)
.map(rows -> rows.iterator().hasNext() ? rows.iterator().next().getString("setting_value") : null); .execute(Tuple.of(key))
.map(rows -> {
Row row = rows.iterator().hasNext() ? rows.iterator().next() : null;
return row != null ? row.getString("setting_value") : null;
});
} }
public Future<Void> set(String key, String value) { public Future<Void> set(String key, String value) {
return SqlTemplate.forUpdate(pool, String sql = "INSERT INTO app_settings (setting_key, setting_value) VALUES (?, ?) " +
"INSERT INTO app_settings (setting_key, setting_value) VALUES (#{key}, #{value}) " + "ON DUPLICATE KEY UPDATE setting_value = ?";
"ON DUPLICATE KEY UPDATE setting_value = #{value}") return pool.preparedQuery(sql)
.execute(Map.of("key", key, "value", value)) .execute(Tuple.of(key, value, value))
.mapEmpty(); .mapEmpty();
} }

View File

@@ -6,7 +6,6 @@ import io.vertx.core.json.JsonObject;
import io.vertx.sqlclient.Pool; import io.vertx.sqlclient.Pool;
import io.vertx.sqlclient.Row; import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.Tuple; import io.vertx.sqlclient.Tuple;
import io.vertx.sqlclient.templates.SqlTemplate;
import org.mindrot.jbcrypt.BCrypt; import org.mindrot.jbcrypt.BCrypt;
import java.util.*; import java.util.*;
@@ -44,18 +43,9 @@ public class UserService {
public Future<Void> createUser(String login, String email, String password, String ip, boolean active, String role) { public Future<Void> createUser(String login, String email, String password, String ip, boolean active, String role) {
String hash = BCrypt.hashpw(password, BCrypt.gensalt()); String hash = BCrypt.hashpw(password, BCrypt.gensalt());
Map<String, Object> params = Map.of( String sql = "INSERT INTO users (login, email, password, ip, active, role) VALUES (?, ?, ?, ?, ?, ?)";
"login", login, Tuple params = Tuple.of(login, email, hash, ip, active, role);
"email", email, return pool.preparedQuery(sql).execute(params).mapEmpty();
"password", hash,
"ip", ip,
"active", active,
"role", role
);
return SqlTemplate.forUpdate(pool,
"INSERT INTO users (login, email, password, ip, active, role) VALUES (#{login}, #{email}, #{password}, #{ip}, #{active}, #{role})")
.execute(params)
.mapEmpty();
} }
public Future<Void> createUser(String login, String email, String password, String ip, boolean active) { public Future<Void> createUser(String login, String email, String password, String ip, boolean active) {
@@ -67,8 +57,9 @@ public class UserService {
} }
public Future<Void> setActive(int id, boolean active) { public Future<Void> setActive(int id, boolean active) {
return SqlTemplate.forUpdate(pool, "UPDATE users SET active = #{active} WHERE id = #{id}") return pool.preparedQuery("UPDATE users SET active = ? WHERE id = ?")
.execute(Map.of("id", id, "active", active)).mapEmpty(); .execute(Tuple.of(active, id))
.mapEmpty();
} }
public Future<JsonObject> findByLoginOrEmail(String loginOrEmail) { public Future<JsonObject> findByLoginOrEmail(String loginOrEmail) {
@@ -106,24 +97,30 @@ public class UserService {
} }
public Future<Void> updateUser(int id, String login, String email, String password, String ip, String role) { public Future<Void> updateUser(int id, String login, String email, String password, String ip, String role) {
Map<String, Object> params = new HashMap<>(); List<Object> values = new ArrayList<>();
params.put("id", id); values.add(login);
params.put("login", login); values.add(email);
params.put("email", email); values.add(ip);
params.put("ip", ip); if (role != null) {
if (role != null) params.put("role", role); values.add(role);
}
String sql; String sql;
if (password != null && !password.isEmpty()) { if (password != null && !password.isEmpty()) {
String hash = BCrypt.hashpw(password, BCrypt.gensalt()); String hash = BCrypt.hashpw(password, BCrypt.gensalt());
params.put("password", hash); sql = "UPDATE users SET login = ?, email = ?, password = ?, ip = ?" +
sql = "UPDATE users SET login = #{login}, email = #{email}, password = #{password}, ip = #{ip}" (role != null ? ", role = ?" : "") + " WHERE id = ?";
+ (role != null ? ", role = #{role}" : "") + " WHERE id = #{id}"; values.add(2, hash);
} else { } else {
sql = "UPDATE users SET login = #{login}, email = #{email}, ip = #{ip}" sql = "UPDATE users SET login = ?, email = ?, ip = ?" +
+ (role != null ? ", role = #{role}" : "") + " WHERE id = #{id}"; (role != null ? ", role = ?" : "") + " WHERE id = ?";
} }
return SqlTemplate.forUpdate(pool, sql).execute(params).mapEmpty();
values.add(id); // для WHERE
return pool.preparedQuery(sql)
.execute(Tuple.tuple(values))
.mapEmpty();
} }
public Future<Void> updateUserIp(int userId, String ip) { public Future<Void> updateUserIp(int userId, String ip) {
@@ -133,54 +130,60 @@ public class UserService {
} }
public Future<Void> deleteUser(int id) { public Future<Void> deleteUser(int id) {
return SqlTemplate.forUpdate(pool, "DELETE FROM users WHERE id = #{id}") return pool.preparedQuery("DELETE FROM users WHERE id = ?")
.execute(Collections.singletonMap("id", id)) .execute(Tuple.of(id))
.mapEmpty(); .mapEmpty();
} }
public Future<JsonObject> getProfile(int userId) { public Future<JsonObject> getProfile(int userId) {
return SqlTemplate.forQuery(pool, String sql = "SELECT id, login, email, role, language, ip, created, updated FROM users WHERE id = ?";
"SELECT id, login, email, role, language, ip, created, updated FROM users WHERE id = #{id}") return pool.preparedQuery(sql)
.mapTo(row -> new JsonObject() .execute(Tuple.of(userId))
.put("id", row.getInteger("id")) .map(rows -> {
.put("login", row.getString("login")) if (rows.size() == 0) return null;
.put("email", row.getString("email")) Row row = rows.iterator().next();
.put("role", row.getString("role")) return new JsonObject()
.put("language", row.getString("language")) .put("id", row.getInteger("id"))
.put("ip", row.getString("ip")) .put("login", row.getString("login"))
.put("created", row.getLocalDateTime("created") != null ? row.getLocalDateTime("created").toString() : null) .put("email", row.getString("email"))
.put("updated", row.getLocalDateTime("updated") != null ? row.getLocalDateTime("updated").toString() : null)) .put("role", row.getString("role"))
.execute(Map.of("id", userId)) .put("language", row.getString("language"))
.map(rows -> rows.iterator().hasNext() ? rows.iterator().next() : null); .put("ip", row.getString("ip"))
.put("created", row.getLocalDateTime("created") != null ? row.getLocalDateTime("created").toString() : null)
.put("updated", row.getLocalDateTime("updated") != null ? row.getLocalDateTime("updated").toString() : null);
});
} }
public Future<Void> updateProfile(int userId, String email, String password, String language) { public Future<Void> updateProfile(int userId, String email, String password, String language) {
Map<String, Object> params = new HashMap<>();
params.put("id", userId);
List<String> setClauses = new ArrayList<>(); List<String> setClauses = new ArrayList<>();
List<Object> values = new ArrayList<>();
if (email != null) { if (email != null) {
setClauses.add("email = #{email}"); setClauses.add("email = ?");
params.put("email", email); values.add(email);
} }
if (password != null && !password.isEmpty()) { if (password != null && !password.isEmpty()) {
String hash = BCrypt.hashpw(password, BCrypt.gensalt()); String hash = BCrypt.hashpw(password, BCrypt.gensalt());
setClauses.add("password = #{password}"); setClauses.add("password = ?");
params.put("password", hash); values.add(hash);
} }
if (language != null) { if (language != null) {
setClauses.add("language = #{language}"); setClauses.add("language = ?");
params.put("language", language); values.add(language);
} }
if (setClauses.isEmpty()) { if (setClauses.isEmpty()) {
return Future.succeededFuture(); return Future.succeededFuture();
} }
String sql = "UPDATE users SET " + String.join(", ", setClauses) + " WHERE id = #{id}"; values.add(userId);
return SqlTemplate.forUpdate(pool, sql).execute(params).mapEmpty(); String sql = "UPDATE users SET " + String.join(", ", setClauses) + " WHERE id = ?";
return pool.preparedQuery(sql)
.execute(Tuple.tuple(values))
.mapEmpty();
} }
public boolean checkPassword(String plain, String hash) { public boolean checkPassword(String plain, String hash) {

View File

@@ -18,13 +18,5 @@ public class DateRangeSetup {
System.out.println("dateFrom=" + formattedDateFrom); System.out.println("dateFrom=" + formattedDateFrom);
System.out.println("dateTo=" + formattedDateTo); System.out.println("dateTo=" + formattedDateTo);
System.out.println("CodeGenerator=" + CodeGenerator.generateCorporateCode());
System.out.println("CodeGenerator=" + CodeGenerator.generateCorporateCode());
System.out.println("CodeGenerator=" + CodeGenerator.generateCorporateCode());
System.out.println("CodeGenerator=" + CodeGenerator.generateCorporateCode());
System.out.println("CodeGenerator=" + CodeGenerator.generateCorporateCode());
System.out.println("CodeGenerator=" + CodeGenerator.generateCorporateCode());
System.out.println("CodeGenerator=" + CodeGenerator.generateCorporateCode());
} }
} }