From 93c007b2b9859425c6f135598637cf7fab3ffd3deb96bf867efc3446a1636df4 Mon Sep 17 00:00:00 2001 From: AidarKC Date: Tue, 6 Jan 2026 01:11:29 +0300 Subject: [PATCH] =?UTF-8?q?05=2001=2025=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D1=82=D0=B0=D0=B1=D0=BB=D0=B8=D1=86=D1=83=20mes?= =?UTF-8?q?sage=5Fstate=20=D0=B8=20=D1=82=D1=80=D0=B8=D0=B3=D0=B5=D1=80?= =?UTF-8?q?=D1=8B=20=D0=BA=D0=BE=D1=82=D0=BE=D1=80=D1=8B=D0=B9=20=D1=81?= =?UTF-8?q?=D1=87=D0=B8=D1=82=D0=B0=D0=B5=D1=82=20=D0=B2=20=D0=BD=D0=B5?= =?UTF-8?q?=D0=B9=20=D0=B0=D0=BA=D1=82=D1=83=D0=B0=D0=BB=D1=8C=D0=BD=D0=BE?= =?UTF-8?q?=D0=B5=20=D0=BA=D0=BE=D0=BB=D0=B8=D1=87=D0=B5=D1=81=D1=82=D0=B2?= =?UTF-8?q?=D0=BE=20=D0=B2=D1=81=D0=B5=D1=85=20=D0=BB=D0=B0=D0=B9=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=B8=20=D0=BE=D1=82=D0=B2=D0=B5=D1=82=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=BD=D0=B0=20=D1=81=D0=BE=D0=BE=D0=B1=D1=89=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F!=20(=D0=B8=20=D0=B2=D1=81=D1=91=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D0=B0=D1=82=D0=B5=D1=82=20-=20=D1=82=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D1=8B=20=D0=BF=D1=80=D0=BE=D1=85=D0=BE=D0=B4=D1=8F?= =?UTF-8?q?=D1=82)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/shine/db/DatabaseInitializer.java | 119 ++++++++++++++++-- .../java/test/it/IT_03_AddBlock_NoAuth.java | 35 +++--- 2 files changed, 124 insertions(+), 30 deletions(-) diff --git a/shine-server-db/src/main/java/shine/db/DatabaseInitializer.java b/shine-server-db/src/main/java/shine/db/DatabaseInitializer.java index 0a93db3..0983a60 100644 --- a/shine-server-db/src/main/java/shine/db/DatabaseInitializer.java +++ b/shine-server-db/src/main/java/shine/db/DatabaseInitializer.java @@ -22,7 +22,7 @@ import java.sql.Statement; * - blockchain_state * - blocks * - connections_state (текущее состояние связей) - * + * - message_stats (счётчики лайков/ответов на сообщения) */ public class DatabaseInitializer { @@ -247,7 +247,6 @@ public class DatabaseInitializer { FOREIGN KEY (login) REFERENCES solana_users(login), - -- состояние уникально по пользователю, типу связи и цели UNIQUE (login, relType, to_login) ); """); @@ -269,15 +268,6 @@ public class DatabaseInitializer { // ===================================================================== // 8) Trigger: при вставке connection-блоков в blocks — обновлять connections_state - // - // Правило: - // - msgType=3 (ConnectionBody) - // - subType 10/20/30 => добавить/обновить запись состояния - // - subType 11/21/31 => удалить запись состояния (без ошибок, даже если её нет) - // - // Примечание: - // - "повторное добавление" не должно падать => используем UPSERT (DO UPDATE) - // - "удаление того, чего нет" не падает => обычный DELETE // ===================================================================== st.executeUpdate(""" CREATE TRIGGER IF NOT EXISTS trg_blocks_connection_state_ai @@ -285,7 +275,6 @@ public class DatabaseInitializer { WHEN NEW.msgType = 3 BEGIN - -- ADD / UPDATE: 10/20/30 INSERT INTO connections_state ( login, relType, to_login, toBchName, toBlockGlobalNumber, toBlockHashe ) @@ -305,7 +294,6 @@ public class DatabaseInitializer { toBlockGlobalNumber = excluded.toBlockGlobalNumber, toBlockHashe = excluded.toBlockHashe; - -- DELETE: 11/21/31 => удалить соответствующую "положительную" связь (10/20/30) DELETE FROM connections_state WHERE login = NEW.login AND to_login = NEW.to_login @@ -319,6 +307,111 @@ public class DatabaseInitializer { END; """); + + // ===================================================================== + // 9) message_stats — счётчики лайков/ответов на конкретный блок-цель + // + // Правило системы: + // - to_login берём из toBchName: отрезаем последние 3 символа + // ===================================================================== + st.executeUpdate(""" + CREATE TABLE IF NOT EXISTS message_stats ( + to_login TEXT NOT NULL, + to_bch_name TEXT NOT NULL, + to_block_global_number INTEGER NOT NULL, + to_block_hash TEXT NOT NULL, + + likes_count INTEGER NOT NULL DEFAULT 0, + replies_count INTEGER NOT NULL DEFAULT 0, + + UNIQUE ( + to_login, + to_bch_name, + to_block_global_number, + to_block_hash + ) + ); + """); + + st.executeUpdate(""" + CREATE INDEX IF NOT EXISTS idx_message_stats_target + ON message_stats (to_bch_name, to_block_global_number, to_block_hash); + """); + + st.executeUpdate(""" + CREATE INDEX IF NOT EXISTS idx_message_stats_login + ON message_stats (to_login); + """); + + // ===================================================================== + // 10) Trigger: LIKE (Reaction) + // - msgType=2 (REACTION) + // - msgSubType=1 (LIKE) + // ===================================================================== + st.executeUpdate(""" + CREATE TRIGGER IF NOT EXISTS trg_blocks_message_stats_like_ai + AFTER INSERT ON blocks + WHEN NEW.msgType = 2 AND NEW.msgSubType = 1 + BEGIN + INSERT INTO message_stats ( + to_login, + to_bch_name, + to_block_global_number, + to_block_hash, + likes_count, + replies_count + ) + SELECT + substr(NEW.toBchName, 1, length(NEW.toBchName) - 3), + NEW.toBchName, + NEW.toBlockGlobalNumber, + NEW.toBlockHashe, + 1, + 0 + WHERE NEW.toBchName IS NOT NULL + AND length(NEW.toBchName) > 3 + AND NEW.toBlockGlobalNumber IS NOT NULL + AND NEW.toBlockHashe IS NOT NULL + ON CONFLICT(to_login, to_bch_name, to_block_global_number, to_block_hash) + DO UPDATE SET + likes_count = message_stats.likes_count + 1; + END; + """); + + // ===================================================================== + // 11) Trigger: REPLY (Text) + // - msgType=1 (TEXT) + // - msgSubType=2 (REPLY) + // ===================================================================== + st.executeUpdate(""" + CREATE TRIGGER IF NOT EXISTS trg_blocks_message_stats_reply_ai + AFTER INSERT ON blocks + WHEN NEW.msgType = 1 AND NEW.msgSubType = 2 + BEGIN + INSERT INTO message_stats ( + to_login, + to_bch_name, + to_block_global_number, + to_block_hash, + likes_count, + replies_count + ) + SELECT + substr(NEW.toBchName, 1, length(NEW.toBchName) - 3), + NEW.toBchName, + NEW.toBlockGlobalNumber, + NEW.toBlockHashe, + 0, + 1 + WHERE NEW.toBchName IS NOT NULL + AND length(NEW.toBchName) > 3 + AND NEW.toBlockGlobalNumber IS NOT NULL + AND NEW.toBlockHashe IS NOT NULL + ON CONFLICT(to_login, to_bch_name, to_block_global_number, to_block_hash) + DO UPDATE SET + replies_count = message_stats.replies_count + 1; + END; + """); } } } \ No newline at end of file diff --git a/src/test/java/test/it/IT_03_AddBlock_NoAuth.java b/src/test/java/test/it/IT_03_AddBlock_NoAuth.java index c5a0ac3..351dcd0 100644 --- a/src/test/java/test/it/IT_03_AddBlock_NoAuth.java +++ b/src/test/java/test/it/IT_03_AddBlock_NoAuth.java @@ -11,7 +11,6 @@ import test.it.addBlockUtils.ChainState; import test.it.utils.*; import utils.crypto.Ed25519Util; -import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.Base64; @@ -98,7 +97,7 @@ public class IT_03_AddBlock_NoAuth { ); // ========================================================= - // 3) USER1 блоки (как было раньше в IT_03) + // 3) USER1 блоки (под message_stats) // ========================================================= if (TestConfig.DEBUG()) { TestLog.titleBlock(""" @@ -122,21 +121,25 @@ public class IT_03_AddBlock_NoAuth { sender1.send(new HeaderBody(TestConfig.LOGIN()), t); assertTrue(st1.hasHeader()); - if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: TEXT#1 (NEW)"); + // 3 NEW + if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: TEXT#1 (NEW) <- будет LIKE + REPLY"); sender1.send(new TextBody(TextBody.SUB_NEW, "Hello #1 (NEW) from IT_03 test"), t); - if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: TEXT#2 (NEW)"); + if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: TEXT#2 (NEW) <- будет ONLY LIKE"); sender1.send(new TextBody(TextBody.SUB_NEW, "Hello #2 (NEW) from IT_03 test"), t); - if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: TEXT#3 (NEW)"); + if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: TEXT#3 (NEW) <- будет ONLY REPLY"); sender1.send(new TextBody(TextBody.SUB_NEW, "Hello #3 (NEW) from IT_03 test"), t); byte[] text1Hash = st1.getGlobalHash32(1); byte[] text2Hash = st1.getGlobalHash32(2); + byte[] text3Hash = st1.getGlobalHash32(3); assertNotNull(text1Hash); assertNotNull(text2Hash); + assertNotNull(text3Hash); - if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: TEXT#4 (REPLY -> TEXT#1)"); + // 2 REPLY + if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: TEXT#4 (REPLY -> TEXT#1) (делает TEXT#1: replies+1)"); sender1.send(new TextBody( TextBody.SUB_REPLY, "Reply to TEXT#1", @@ -145,19 +148,17 @@ public class IT_03_AddBlock_NoAuth { text1Hash ), t); - byte[] reply1Hash = st1.getGlobalHash32(4); - assertNotNull(reply1Hash); - - if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: TEXT#5 (REPLY -> TEXT#4)"); + if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: TEXT#5 (REPLY -> TEXT#3) (делает TEXT#3: replies+1)"); sender1.send(new TextBody( TextBody.SUB_REPLY, - "Reply to REPLY (TEXT#4)", + "Reply to TEXT#3", TestConfig.BCH_NAME(), - 4, - reply1Hash + 3, + text3Hash ), t); - if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: REACT#1 (LIKE -> TEXT#1)"); + // 2 LIKE + if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: REACT#1 (LIKE -> TEXT#1) (делает TEXT#1: likes+1)"); sender1.send(new ReactionBody( ReactionBody.SUB_LIKE, TestConfig.BCH_NAME(), @@ -165,12 +166,12 @@ public class IT_03_AddBlock_NoAuth { text1Hash ), t); - if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: REACT#2 (LIKE -> TEXT#4)"); + if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: REACT#2 (LIKE -> TEXT#2) (делает TEXT#2: likes+1)"); sender1.send(new ReactionBody( ReactionBody.SUB_LIKE, TestConfig.BCH_NAME(), - 4, - reply1Hash + 2, + text2Hash ), t); assertEquals(7, st1.globalLastNumber(), "USER1: должно быть 8 блоков: globalLastNumber=7");