добавил таблицу message_state и тригеры который считает в ней актуальное количество всех лайков и ответов на сообщения! (и всё рабоатет - тесты проходят)
This commit is contained in:
AidarKC 2026-01-06 01:11:29 +03:00
parent eb922d918b
commit 93c007b2b9
2 changed files with 124 additions and 30 deletions

View File

@ -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;
""");
}
}
}

View File

@ -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");