# Design Document: lorep Report Plugin ## Overview Плагин lorep — система репортов для Paper 1.21, написанная на Java 21. Использует Gradle для сборки, SQLite для хранения данных и интегрируется с Discord через webhooks. **Технологический стек:** - Java 21 - Paper API 1.21 - Gradle (Kotlin DSL) - SQLite (встроенная БД) или PostgreSQL (внешняя БД) - HikariCP (connection pool для PostgreSQL) - OkHttp (HTTP клиент для webhooks) **Git репозиторий:** https://git.lokili.xyz/loki/lorep.git ## Architecture ```mermaid graph TB subgraph Plugin Main[LorepPlugin] CMD[CommandManager] GUI[GuiManager] DB[DatabaseManager] WH[WebhookService] CFG[ConfigManager] end subgraph External Discord[Discord Webhook] SQLite[(SQLite DB)] end Main --> CMD Main --> GUI Main --> DB Main --> WH Main --> CFG CMD --> DB CMD --> WH GUI --> DB WH --> Discord DB --> SQLite CFG --> Main ``` ### Структура проекта ``` lorep/ ├── .gitignore ├── build.gradle.kts ├── settings.gradle.kts ├── src/ │ └── main/ │ ├── java/ │ │ └── dev/ │ │ └── loki/ │ │ └── lorep/ │ │ ├── LorepPlugin.java │ │ ├── command/ │ │ │ ├── ReportCommand.java │ │ │ ├── ReportGuiCommand.java │ │ │ └── ReportStatsCommand.java │ │ ├── database/ │ │ │ ├── DatabaseManager.java │ │ │ ├── SQLiteDatabaseManager.java │ │ │ ├── PostgreSQLDatabaseManager.java │ │ │ └── Report.java │ │ ├── gui/ │ │ │ ├── ReportGui.java │ │ │ └── GuiClickListener.java │ │ ├── webhook/ │ │ │ └── DiscordWebhook.java │ │ ├── config/ │ │ │ └── ConfigManager.java │ │ └── util/ │ │ ├── TimeUtil.java │ │ └── MessageUtil.java │ └── resources/ │ ├── plugin.yml │ └── config.yml └── src/ └── test/ └── java/ └── dev/ └── loki/ └── lorep/ ├── database/ │ └── DatabaseManagerTest.java └── util/ └── TimeUtilTest.java ``` ## Components and Interfaces ### 1. LorepPlugin (Main Class) ```java public final class LorepPlugin extends JavaPlugin { private DatabaseManager databaseManager; private ConfigManager configManager; private DiscordWebhook discordWebhook; @Override public void onEnable(); @Override public void onDisable(); // Getters for managers } ``` ### 2. Report (Data Model) ```java public record Report( int id, UUID reporterUuid, String reporterName, UUID targetUuid, String targetName, String reason, Instant createdAt ) {} ``` ### 3. DatabaseManager ```java public interface DatabaseManager { void initialize(); void close(); void saveReport(Report report); List getReportsForTarget(UUID targetUuid); List getAllReports(); int getReportCount(UUID targetUuid); boolean hasReported(UUID reporterUuid, UUID targetUuid); List getReportsPaginated(int page, int pageSize); } // Реализации: public class SQLiteDatabaseManager implements DatabaseManager { } public class PostgreSQLDatabaseManager implements DatabaseManager { } ``` ### 4. DiscordWebhook ```java public class DiscordWebhook { public DiscordWebhook(String webhookUrl); public void sendReport(Report report); } ``` ### 5. ConfigManager ```java public class ConfigManager { public String getWebhookUrl(); public String getDatabasePath(); public String getMessage(String key); } ``` ### 6. ReportGui ```java public class ReportGui { public void open(Player player, int page); public ItemStack createReportItem(Report report, int totalReports); } ``` ## Data Models ### Report Entity | Field | Type | Description | |-------|------|-------------| | id | INTEGER | Primary key, auto-increment | | reporter_uuid | TEXT | UUID репортера | | reporter_name | TEXT | Имя репортера | | target_uuid | TEXT | UUID цели | | target_name | TEXT | Имя цели | | reason | TEXT | Причина репорта | | created_at | INTEGER | Unix timestamp создания | ### SQL Schema ```sql CREATE TABLE IF NOT EXISTS reports ( id INTEGER PRIMARY KEY AUTOINCREMENT, reporter_uuid TEXT NOT NULL, reporter_name TEXT NOT NULL, target_uuid TEXT NOT NULL, target_name TEXT NOT NULL, reason TEXT NOT NULL, created_at INTEGER NOT NULL ); CREATE INDEX idx_target_uuid ON reports(target_uuid); CREATE INDEX idx_reporter_target ON reports(reporter_uuid, target_uuid); ``` ### Configuration (config.yml) ```yaml # Discord Webhook URL webhook-url: "" # Database settings database: # Type: sqlite or postgresql type: "sqlite" # SQLite settings sqlite: file: "reports.db" # PostgreSQL settings postgresql: host: "localhost" port: 5432 database: "lorep" username: "lorep" password: "password" pool-size: 10 # Messages messages: report-sent: "&aРепорт успешно отправлен!" already-reported: "&cВы уже отправляли репорт на этого игрока!" self-report: "&cВы не можете отправить репорт на себя!" player-not-found: "&cИгрок не найден!" no-permission: "&cУ вас нет прав на эту команду!" usage: "&eИспользование: /report <ник> <причина>" ``` ## Correctness Properties *A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.* ### Property 1: Report Creation Integrity *For any* valid reporter UUID, target UUID, and reason string, creating a report and then querying reports for that target SHALL return a list containing the created report with matching fields. **Validates: Requirements 1.1, 2.1** ### Property 2: Duplicate Report Prevention *For any* reporter-target pair, if a report already exists from that reporter to that target, attempting to create another report SHALL be rejected. **Validates: Requirements 1.2** ### Property 3: Report Persistence Round-Trip *For any* set of reports saved to the database, reloading the database SHALL return the same reports with identical data. **Validates: Requirements 2.2** ### Property 4: Report Ordering *For any* collection of reports with different timestamps, querying all reports SHALL return them sorted by creation timestamp in descending order (newest first). **Validates: Requirements 2.3** ### Property 5: Webhook Payload Completeness *For any* report sent to the webhook, the payload SHALL contain reporter name, target name, reason, and formatted timestamp. **Validates: Requirements 3.1** ### Property 6: Pagination Correctness *For any* list of N reports and page size P, requesting page K SHALL return at most P reports starting from index K*P, and the total page count SHALL equal ceil(N/P). **Validates: Requirements 4.4** ### Property 7: Report Count Accuracy *For any* target UUID, the report count returned by getReportCount SHALL equal the actual number of reports in the database for that target. **Validates: Requirements 5.1** ### Property 8: Permission Enforcement *For any* command execution, if the player lacks the required permission, the command SHALL be denied and return false. **Validates: Requirements 6.1, 6.2, 6.3** ### Property 9: Configuration Parsing *For any* valid config.yml file, parsing SHALL correctly extract webhook URL and database path values. **Validates: Requirements 7.1, 7.3, 7.4** ## Error Handling | Scenario | Handling | |----------|----------| | Database connection failure | Log error, disable plugin gracefully | | Webhook request failure | Log warning, continue operation | | Invalid player name | Return error message to player | | Missing config values | Use default values | | SQL exceptions | Log error, return empty result | ## Testing Strategy ### Property-Based Testing Library - **JQwik** — библиотека для property-based тестирования в Java - Минимум 100 итераций на каждый property test ### Unit Tests - DatabaseManager: CRUD операции, pagination - TimeUtil: форматирование времени - ConfigManager: парсинг конфигурации ### Property-Based Tests Каждый property test должен быть аннотирован: ```java // **Feature: lorep-report-plugin, Property 1: Report Creation Integrity** @Property(tries = 100) void reportCreationIntegrity(@ForAll UUID reporter, @ForAll UUID target, @ForAll String reason) { // test implementation } ``` ### Integration Tests - Полный цикл создания репорта - GUI открытие и навигация - Webhook отправка (с mock сервером)