Update from Git Manager GUI
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
|
||||
package de.ticketsystem.database;
|
||||
|
||||
import java.io.File;
|
||||
@@ -14,7 +13,6 @@ import de.ticketsystem.model.Ticket;
|
||||
import de.ticketsystem.model.TicketStatus;
|
||||
import java.sql.*;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
@@ -23,295 +21,9 @@ import java.io.FileWriter;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
public class DatabaseManager {
|
||||
// Test-Konstruktor für Unit-Tests (ohne Bukkit/Plugin)
|
||||
public DatabaseManager(File dataFile, YamlConfiguration dataConfig) {
|
||||
this.plugin = null;
|
||||
this.useMySQL = false;
|
||||
this.useJson = false;
|
||||
this.dataFileName = dataFile.getName();
|
||||
this.archiveFileName = "archive.json";
|
||||
this.dataFile = dataFile;
|
||||
this.dataConfig = dataConfig;
|
||||
validateLoadedTickets();
|
||||
}
|
||||
/**
|
||||
* Archiviert alle geschlossenen Tickets in eine separate Datei und entfernt sie aus dem aktiven Speicher.
|
||||
* @return Anzahl archivierter Tickets
|
||||
*/
|
||||
public int archiveClosedTickets() {
|
||||
List<Ticket> all = getAllTickets();
|
||||
List<Ticket> toArchive = new ArrayList<>();
|
||||
for (Ticket t : all) {
|
||||
if (t.getStatus() == TicketStatus.CLOSED) toArchive.add(t);
|
||||
}
|
||||
if (toArchive.isEmpty()) return 0;
|
||||
File archiveFile = new File(plugin.getDataFolder(), archiveFileName);
|
||||
JSONArray arr = new JSONArray();
|
||||
// Bestehendes Archiv laden
|
||||
if (archiveFile.exists()) {
|
||||
try (FileReader fr = new FileReader(archiveFile)) {
|
||||
JSONParser parser = new JSONParser();
|
||||
Object parsed = parser.parse(fr);
|
||||
if (parsed instanceof JSONArray oldArr) arr.addAll(oldArr);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
for (Ticket t : toArchive) arr.add(ticketToJson(t));
|
||||
try (FileWriter fw = new FileWriter(archiveFile)) {
|
||||
fw.write(arr.toJSONString());
|
||||
} catch (Exception e) {
|
||||
sendError("Fehler beim Archivieren: " + e.getMessage());
|
||||
return 0;
|
||||
}
|
||||
// Entferne archivierte Tickets aus aktivem Speicher
|
||||
int removed = 0;
|
||||
if (useMySQL) {
|
||||
try (Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement("DELETE FROM tickets WHERE id = ?")) {
|
||||
for (Ticket t : toArchive) {
|
||||
ps.setInt(1, t.getId());
|
||||
ps.executeUpdate();
|
||||
removed++;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sendError("Fehler beim Entfernen archivierter Tickets: " + e.getMessage());
|
||||
}
|
||||
} else {
|
||||
for (Ticket t : toArchive) {
|
||||
dataConfig.set("tickets." + t.getId(), null);
|
||||
removed++;
|
||||
}
|
||||
try { dataConfig.save(dataFile); } catch (Exception e) { sendError("Fehler beim Speichern nach Archivierung: " + e.getMessage()); }
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
/**
|
||||
* Liefert Statistiken über Tickets.
|
||||
*/
|
||||
public TicketStats getTicketStats() {
|
||||
List<Ticket> all = getAllTickets();
|
||||
int open = 0, closed = 0, forwarded = 0;
|
||||
java.util.Map<String, Integer> byPlayer = new java.util.HashMap<>();
|
||||
for (Ticket t : all) {
|
||||
switch (t.getStatus()) {
|
||||
case OPEN -> open++;
|
||||
case CLOSED -> closed++;
|
||||
case FORWARDED -> forwarded++;
|
||||
}
|
||||
byPlayer.merge(t.getCreatorName(), 1, Integer::sum);
|
||||
}
|
||||
return new TicketStats(all.size(), open, closed, forwarded, byPlayer);
|
||||
}
|
||||
|
||||
public static class TicketStats {
|
||||
public final int total, open, closed, forwarded;
|
||||
public final java.util.Map<String, Integer> byPlayer;
|
||||
public TicketStats(int total, int open, int closed, int forwarded, java.util.Map<String, Integer> byPlayer) {
|
||||
this.total = total; this.open = open; this.closed = closed; this.forwarded = forwarded; this.byPlayer = byPlayer;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Exportiert alle Tickets als JSON-Datei.
|
||||
* @param exportFile Ziel-Datei
|
||||
* @return Anzahl exportierter Tickets
|
||||
*/
|
||||
public int exportTickets(File exportFile) {
|
||||
List<Ticket> tickets = getAllTickets();
|
||||
JSONArray arr = new JSONArray();
|
||||
for (Ticket t : tickets) {
|
||||
arr.add(ticketToJson(t));
|
||||
}
|
||||
try (FileWriter fw = new FileWriter(exportFile)) {
|
||||
fw.write(arr.toJSONString());
|
||||
return tickets.size();
|
||||
} catch (IOException e) {
|
||||
sendError("Fehler beim Export: " + e.getMessage());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// ─────────────────────────── Felder ────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Importiert Tickets aus einer JSON-Datei.
|
||||
* @param importFile Quell-Datei
|
||||
* @return Anzahl importierter Tickets
|
||||
*/
|
||||
public int importTickets(File importFile) {
|
||||
int imported = 0;
|
||||
try (FileReader fr = new FileReader(importFile)) {
|
||||
JSONParser parser = new JSONParser();
|
||||
JSONArray arr = (JSONArray) parser.parse(fr);
|
||||
for (Object o : arr) {
|
||||
JSONObject obj = (JSONObject) o;
|
||||
Ticket t = ticketFromJson(obj);
|
||||
if (t != null) {
|
||||
int id = createTicket(t);
|
||||
if (id != -1) imported++;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sendError("Fehler beim Import: " + e.getMessage());
|
||||
}
|
||||
return imported;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt alle Tickets (egal welcher Status) zurück.
|
||||
*/
|
||||
public List<Ticket> getAllTickets() {
|
||||
List<Ticket> list = new ArrayList<>();
|
||||
if (useMySQL) {
|
||||
try (Connection conn = getConnection(); Statement stmt = conn.createStatement()) {
|
||||
ResultSet rs = stmt.executeQuery("SELECT * FROM tickets");
|
||||
while (rs.next()) list.add(mapRow(rs));
|
||||
} catch (SQLException e) {
|
||||
sendError("Fehler beim Abrufen aller Tickets: " + e.getMessage());
|
||||
}
|
||||
} else {
|
||||
if (dataConfig.contains("tickets")) {
|
||||
for (String key : dataConfig.getConfigurationSection("tickets").getKeys(false)) {
|
||||
Ticket t = (Ticket) dataConfig.get("tickets." + key);
|
||||
if (t != null) list.add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
// Hilfsmethoden für JSON-Konvertierung
|
||||
private JSONObject ticketToJson(Ticket t) {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("id", t.getId());
|
||||
obj.put("creatorUUID", t.getCreatorUUID().toString());
|
||||
obj.put("creatorName", t.getCreatorName());
|
||||
obj.put("message", t.getMessage());
|
||||
obj.put("world", t.getWorldName());
|
||||
obj.put("x", t.getX());
|
||||
obj.put("y", t.getY());
|
||||
obj.put("z", t.getZ());
|
||||
obj.put("yaw", t.getYaw());
|
||||
obj.put("pitch", t.getPitch());
|
||||
obj.put("status", t.getStatus().name());
|
||||
obj.put("createdAt", t.getCreatedAt() != null ? t.getCreatedAt().getTime() : null);
|
||||
obj.put("claimedAt", t.getClaimedAt() != null ? t.getClaimedAt().getTime() : null);
|
||||
obj.put("closedAt", t.getClosedAt() != null ? t.getClosedAt().getTime() : null);
|
||||
if (t.getClaimerUUID() != null) obj.put("claimerUUID", t.getClaimerUUID().toString());
|
||||
if (t.getClaimerName() != null) obj.put("claimerName", t.getClaimerName());
|
||||
if (t.getForwardedToUUID() != null) obj.put("forwardedToUUID", t.getForwardedToUUID().toString());
|
||||
if (t.getForwardedToName() != null) obj.put("forwardedToName", t.getForwardedToName());
|
||||
return obj;
|
||||
}
|
||||
|
||||
private Ticket ticketFromJson(JSONObject obj) {
|
||||
try {
|
||||
Ticket t = new Ticket();
|
||||
t.setId(((Long)obj.get("id")).intValue());
|
||||
t.setCreatorUUID(UUID.fromString((String)obj.get("creatorUUID")));
|
||||
t.setCreatorName((String)obj.get("creatorName"));
|
||||
t.setMessage((String)obj.get("message"));
|
||||
t.setWorldName((String)obj.get("world"));
|
||||
t.setX((Double)obj.get("x"));
|
||||
t.setY((Double)obj.get("y"));
|
||||
t.setZ((Double)obj.get("z"));
|
||||
t.setYaw(((Double)obj.get("yaw")).floatValue());
|
||||
t.setPitch(((Double)obj.get("pitch")).floatValue());
|
||||
t.setStatus(TicketStatus.valueOf((String)obj.get("status")));
|
||||
if (obj.get("createdAt") != null) t.setCreatedAt(new java.sql.Timestamp((Long)obj.get("createdAt")));
|
||||
if (obj.get("claimedAt") != null) t.setClaimedAt(new java.sql.Timestamp((Long)obj.get("claimedAt")));
|
||||
if (obj.get("closedAt") != null) t.setClosedAt(new java.sql.Timestamp((Long)obj.get("closedAt")));
|
||||
if (obj.get("claimerUUID") != null) t.setClaimerUUID(UUID.fromString((String)obj.get("claimerUUID")));
|
||||
if (obj.get("claimerName") != null) t.setClaimerName((String)obj.get("claimerName"));
|
||||
if (obj.get("forwardedToUUID") != null) t.setForwardedToUUID(UUID.fromString((String)obj.get("forwardedToUUID")));
|
||||
if (obj.get("forwardedToName") != null) t.setForwardedToName((String)obj.get("forwardedToName"));
|
||||
return t;
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().severe("Fehler beim Parsen eines Tickets: " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Migriert alle Tickets aus data.yml nach MySQL.
|
||||
*/
|
||||
public int migrateToMySQL() {
|
||||
if (useMySQL || dataConfig == null) return 0;
|
||||
int migrated = 0;
|
||||
try {
|
||||
for (String key : dataConfig.getConfigurationSection("tickets").getKeys(false)) {
|
||||
Ticket t = (Ticket) dataConfig.get("tickets." + key);
|
||||
if (t != null) {
|
||||
// Ticket in MySQL speichern
|
||||
useMySQL = true;
|
||||
int id = createTicket(t);
|
||||
useMySQL = false;
|
||||
if (id != -1) migrated++;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().severe("Fehler bei Migration zu MySQL: " + e.getMessage());
|
||||
}
|
||||
return migrated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migriert alle Tickets aus MySQL nach data.yml.
|
||||
*/
|
||||
public int migrateToFile() {
|
||||
if (!useMySQL) return 0;
|
||||
int migrated = 0;
|
||||
try (Connection conn = getConnection(); Statement stmt = conn.createStatement()) {
|
||||
ResultSet rs = stmt.executeQuery("SELECT * FROM tickets");
|
||||
while (rs.next()) {
|
||||
Ticket t = mapRow(rs);
|
||||
if (t != null) {
|
||||
useMySQL = false;
|
||||
int id = createTicket(t);
|
||||
useMySQL = true;
|
||||
if (id != -1) migrated++;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().severe("Fehler bei Migration zu Datei: " + e.getMessage());
|
||||
}
|
||||
return migrated;
|
||||
}
|
||||
private String dataFileName;
|
||||
private String archiveFileName;
|
||||
// Prüft geladene Tickets auf Korrektheit (Platzhalter)
|
||||
private void validateLoadedTickets() {
|
||||
if (dataConfig == null || !dataConfig.contains("tickets")) return;
|
||||
int invalid = 0;
|
||||
for (String key : dataConfig.getConfigurationSection("tickets").getKeys(false)) {
|
||||
Object obj = dataConfig.get("tickets." + key);
|
||||
if (!(obj instanceof Ticket t)) {
|
||||
sendError("Ungültiges Ticket-Objekt bei ID: " + key);
|
||||
invalid++;
|
||||
continue;
|
||||
}
|
||||
if (t.getCreatorUUID() == null || t.getCreatorName() == null || t.getMessage() == null || t.getStatus() == null) {
|
||||
sendError("Ticket mit fehlenden Pflichtfeldern: ID " + key);
|
||||
invalid++;
|
||||
}
|
||||
try { UUID.fromString(t.getCreatorUUID().toString()); } catch (Exception e) {
|
||||
sendError("Ungültige UUID bei Ticket ID: " + key);
|
||||
invalid++;
|
||||
}
|
||||
try { TicketStatus.valueOf(t.getStatus().name()); } catch (Exception e) {
|
||||
sendError("Ungültiger Status bei Ticket ID: " + key);
|
||||
invalid++;
|
||||
}
|
||||
}
|
||||
if (invalid > 0) {
|
||||
String msg = plugin != null ? plugin.formatMessage("messages.validation-warning").replace("{count}", String.valueOf(invalid)) : (invalid + " ungültige Tickets beim Laden gefunden.");
|
||||
sendError(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Backup der MySQL-Datenbank (Platzhalter)
|
||||
private void backupMySQL() {
|
||||
// TODO: Implementiere Backup-Logik für MySQL
|
||||
}
|
||||
|
||||
// Backup der Datei-basierten Daten (Platzhalter)
|
||||
private void backupDataFile() {
|
||||
// TODO: Implementiere Backup-Logik für data.yml/data.json
|
||||
}
|
||||
private final TicketPlugin plugin;
|
||||
private HikariDataSource dataSource;
|
||||
private boolean useMySQL;
|
||||
@@ -319,22 +31,26 @@ public class DatabaseManager {
|
||||
private File dataFile;
|
||||
private YamlConfiguration dataConfig;
|
||||
private JSONArray dataJson;
|
||||
private String dataFileName;
|
||||
private String archiveFileName;
|
||||
|
||||
// ─────────────────────────── Konstruktoren ─────────────────────────────
|
||||
|
||||
public DatabaseManager(TicketPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.useMySQL = plugin.getConfig().getBoolean("use-mysql", true);
|
||||
this.useJson = plugin.getConfig().getBoolean("use-json", false);
|
||||
this.useJson = plugin.getConfig().getBoolean("use-json", false);
|
||||
if (plugin.isDebug()) plugin.getLogger().info("[DEBUG] DatabaseManager initialisiert. useMySQL=" + useMySQL + ", useJson=" + useJson);
|
||||
// Speicherpfade aus config.yml (absolut oder relativ zum Plugin-Ordner)
|
||||
String dataPath = plugin.getConfig().getString("data-file", useJson ? "data.json" : "data.yml");
|
||||
|
||||
String dataPath = plugin.getConfig().getString("data-file", useJson ? "data.json" : "data.yml");
|
||||
String archivePath = plugin.getConfig().getString("archive-file", "archive.json");
|
||||
this.dataFileName = dataPath;
|
||||
this.dataFileName = dataPath;
|
||||
this.archiveFileName = archivePath;
|
||||
|
||||
if (!useMySQL) {
|
||||
if (plugin.isDebug()) plugin.getLogger().info("[DEBUG] Datei-Speicher wird verwendet: " + dataPath);
|
||||
if (useJson) {
|
||||
dataFile = resolvePath(dataPath);
|
||||
if (plugin.isDebug()) plugin.getLogger().info("[DEBUG] JSON-Datei: " + dataFile.getAbsolutePath());
|
||||
if (!dataFile.exists()) {
|
||||
try {
|
||||
dataFile.getParentFile().mkdirs();
|
||||
@@ -346,7 +62,7 @@ public class DatabaseManager {
|
||||
} else {
|
||||
try {
|
||||
JSONParser parser = new JSONParser();
|
||||
dataJson = (JSONArray) parser.parse(new java.io.FileReader(dataFile));
|
||||
dataJson = (JSONArray) parser.parse(new FileReader(dataFile));
|
||||
} catch (Exception e) {
|
||||
sendError("Konnte " + dataPath + " nicht laden: " + e.getMessage());
|
||||
dataJson = new JSONArray();
|
||||
@@ -354,7 +70,6 @@ public class DatabaseManager {
|
||||
}
|
||||
} else {
|
||||
dataFile = resolvePath(dataPath);
|
||||
if (plugin.isDebug()) plugin.getLogger().info("[DEBUG] YAML-Datei: " + dataFile.getAbsolutePath());
|
||||
if (!dataFile.exists()) {
|
||||
try {
|
||||
dataFile.getParentFile().mkdirs();
|
||||
@@ -369,18 +84,33 @@ public class DatabaseManager {
|
||||
}
|
||||
}
|
||||
|
||||
// Hilfsfunktion: Absoluten oder relativen Pfad auflösen
|
||||
// Konstruktor für Tests
|
||||
public DatabaseManager(File dataFile, YamlConfiguration dataConfig) {
|
||||
this.plugin = null;
|
||||
this.useMySQL = false;
|
||||
this.useJson = false;
|
||||
this.dataFileName = dataFile.getName();
|
||||
this.archiveFileName = "archive.json";
|
||||
this.dataFile = dataFile;
|
||||
this.dataConfig = dataConfig;
|
||||
validateLoadedTickets();
|
||||
}
|
||||
|
||||
// ─────────────────────────── Hilfsmethoden ─────────────────────────────
|
||||
|
||||
private File resolvePath(String path) {
|
||||
File f = new File(path);
|
||||
if (f.isAbsolute()) return f;
|
||||
return new File(plugin.getDataFolder(), path);
|
||||
return new File(plugin != null ? plugin.getDataFolder() : new File("."), path);
|
||||
}
|
||||
|
||||
// Fehlerausgabe im Chat und Log
|
||||
private void sendError(String msg) {
|
||||
if (plugin != null) plugin.getLogger().severe(msg);
|
||||
// Fehler an alle Admins im Chat senden
|
||||
Bukkit.getOnlinePlayers().stream().filter(p -> p.hasPermission("ticket.admin")).forEach(p -> p.sendMessage("§c[TicketSystem] " + msg));
|
||||
if (Bukkit.getServer() != null) {
|
||||
Bukkit.getOnlinePlayers().stream()
|
||||
.filter(p -> p.hasPermission("ticket.admin"))
|
||||
.forEach(p -> p.sendMessage("§c[TicketSystem] " + msg));
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────── Verbindung ────────────────────────────────
|
||||
@@ -389,7 +119,8 @@ public class DatabaseManager {
|
||||
if (useMySQL) {
|
||||
try {
|
||||
HikariConfig config = new HikariConfig();
|
||||
config.setJdbcUrl(String.format("jdbc:mysql://%s:%d/%s?useSSL=false&autoReconnect=true&characterEncoding=UTF-8",
|
||||
config.setJdbcUrl(String.format(
|
||||
"jdbc:mysql://%s:%d/%s?useSSL=false&autoReconnect=true&characterEncoding=UTF-8",
|
||||
plugin.getConfig().getString("mysql.host"),
|
||||
plugin.getConfig().getInt("mysql.port"),
|
||||
plugin.getConfig().getString("mysql.database")));
|
||||
@@ -403,14 +134,17 @@ public class DatabaseManager {
|
||||
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
|
||||
|
||||
dataSource = new HikariDataSource(config);
|
||||
|
||||
// Tabellen anlegen & fehlende Spalten ergänzen
|
||||
createTables();
|
||||
ensureColumns();
|
||||
|
||||
plugin.getLogger().info("MySQL-Verbindung erfolgreich hergestellt.");
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Fehler beim Verbinden mit MySQL: " + e.getMessage(), e);
|
||||
plugin.getLogger().warning("Weiche auf Datei-Speicherung (data.yml) aus!");
|
||||
useMySQL = false;
|
||||
// Datei-Storage initialisieren
|
||||
dataFile = new File(plugin.getDataFolder(), "data.yml");
|
||||
if (!dataFile.exists()) {
|
||||
try {
|
||||
@@ -434,7 +168,6 @@ public class DatabaseManager {
|
||||
dataSource.close();
|
||||
plugin.getLogger().info("MySQL-Verbindung getrennt.");
|
||||
}
|
||||
// Bei Datei-Storage nichts zu tun
|
||||
}
|
||||
|
||||
private Connection getConnection() throws SQLException {
|
||||
@@ -444,26 +177,28 @@ public class DatabaseManager {
|
||||
// ─────────────────────────── Tabellen erstellen ────────────────────────
|
||||
|
||||
private void createTables() {
|
||||
// close_comment ist jetzt von Anfang an in der CREATE-Anweisung enthalten
|
||||
String sql = """
|
||||
CREATE TABLE IF NOT EXISTS tickets (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
creator_uuid VARCHAR(36) NOT NULL,
|
||||
creator_name VARCHAR(16) NOT NULL,
|
||||
message VARCHAR(255) NOT NULL,
|
||||
world VARCHAR(64) NOT NULL,
|
||||
x DOUBLE NOT NULL,
|
||||
y DOUBLE NOT NULL,
|
||||
z DOUBLE NOT NULL,
|
||||
yaw FLOAT NOT NULL DEFAULT 0,
|
||||
pitch FLOAT NOT NULL DEFAULT 0,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'OPEN',
|
||||
claimer_uuid VARCHAR(36),
|
||||
claimer_name VARCHAR(16),
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
creator_uuid VARCHAR(36) NOT NULL,
|
||||
creator_name VARCHAR(16) NOT NULL,
|
||||
message VARCHAR(255) NOT NULL,
|
||||
world VARCHAR(64) NOT NULL,
|
||||
x DOUBLE NOT NULL,
|
||||
y DOUBLE NOT NULL,
|
||||
z DOUBLE NOT NULL,
|
||||
yaw FLOAT NOT NULL DEFAULT 0,
|
||||
pitch FLOAT NOT NULL DEFAULT 0,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'OPEN',
|
||||
claimer_uuid VARCHAR(36),
|
||||
claimer_name VARCHAR(16),
|
||||
forwarded_to_uuid VARCHAR(36),
|
||||
forwarded_to_name VARCHAR(16),
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
claimed_at TIMESTAMP,
|
||||
closed_at TIMESTAMP
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
claimed_at TIMESTAMP NULL,
|
||||
closed_at TIMESTAMP NULL,
|
||||
close_comment VARCHAR(500) NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
""";
|
||||
try (Connection conn = getConnection(); Statement stmt = conn.createStatement()) {
|
||||
@@ -473,6 +208,32 @@ public class DatabaseManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ergänzt fehlende Spalten in bestehenden Datenbanken automatisch.
|
||||
* Wichtig für Server, die das Plugin bereits installiert hatten bevor
|
||||
* close_comment existierte.
|
||||
*/
|
||||
private void ensureColumns() {
|
||||
// close_comment hinzufügen, falls nicht vorhanden
|
||||
String checkSql = """
|
||||
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'tickets'
|
||||
AND COLUMN_NAME = 'close_comment'
|
||||
""";
|
||||
try (Connection conn = getConnection();
|
||||
Statement stmt = conn.createStatement()) {
|
||||
ResultSet rs = stmt.executeQuery(checkSql);
|
||||
if (rs.next() && rs.getInt(1) == 0) {
|
||||
// Spalte existiert nicht → hinzufügen
|
||||
stmt.execute("ALTER TABLE tickets ADD COLUMN close_comment VARCHAR(500) NULL");
|
||||
plugin.getLogger().info("[TicketSystem] Spalte 'close_comment' wurde zur Datenbank hinzugefügt.");
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Fehler bei ensureColumns(): " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────── CRUD ──────────────────────────────────────
|
||||
|
||||
/**
|
||||
@@ -507,7 +268,6 @@ public class DatabaseManager {
|
||||
}
|
||||
return -1;
|
||||
} else {
|
||||
// Datei-Storage: Ticket-ID generieren
|
||||
int id = dataConfig.getInt("lastId", 0) + 1;
|
||||
ticket.setId(id);
|
||||
dataConfig.set("lastId", id);
|
||||
@@ -517,7 +277,6 @@ public class DatabaseManager {
|
||||
backupDataFile();
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().severe("Fehler beim Speichern von data.yml: " + e.getMessage());
|
||||
Bukkit.getOnlinePlayers().stream().filter(p -> p.hasPermission("ticket.admin")).forEach(p -> p.sendMessage("§c[TicketSystem] Fehler beim Speichern von data.yml: " + e.getMessage()));
|
||||
}
|
||||
return id;
|
||||
}
|
||||
@@ -547,7 +306,7 @@ public class DatabaseManager {
|
||||
t.setStatus(TicketStatus.CLAIMED);
|
||||
t.setClaimerUUID(claimerUUID);
|
||||
t.setClaimerName(claimerName);
|
||||
t.setClaimedAt(new java.sql.Timestamp(System.currentTimeMillis()));
|
||||
t.setClaimedAt(new Timestamp(System.currentTimeMillis()));
|
||||
dataConfig.set("tickets." + ticketId, t);
|
||||
try {
|
||||
dataConfig.save(dataFile);
|
||||
@@ -562,11 +321,15 @@ public class DatabaseManager {
|
||||
/**
|
||||
* Schließt ein Ticket (Status → CLOSED).
|
||||
*/
|
||||
public boolean closeTicket(int ticketId) {
|
||||
public boolean closeTicket(int ticketId, String closeComment) {
|
||||
if (useMySQL) {
|
||||
String sql = "UPDATE tickets SET status = 'CLOSED', closed_at = NOW() WHERE id = ? AND status != 'CLOSED'";
|
||||
String sql = """
|
||||
UPDATE tickets SET status = 'CLOSED', closed_at = NOW(), close_comment = ?
|
||||
WHERE id = ? AND status != 'CLOSED'
|
||||
""";
|
||||
try (Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||
ps.setInt(1, ticketId);
|
||||
ps.setString(1, closeComment != null ? closeComment : "");
|
||||
ps.setInt(2, ticketId);
|
||||
return ps.executeUpdate() > 0;
|
||||
} catch (SQLException e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Fehler beim Schließen des Tickets: " + e.getMessage(), e);
|
||||
@@ -576,7 +339,8 @@ public class DatabaseManager {
|
||||
Ticket t = getTicketById(ticketId);
|
||||
if (t == null || t.getStatus() == TicketStatus.CLOSED) return false;
|
||||
t.setStatus(TicketStatus.CLOSED);
|
||||
t.setClosedAt(new java.sql.Timestamp(System.currentTimeMillis()));
|
||||
t.setClosedAt(new Timestamp(System.currentTimeMillis()));
|
||||
t.setCloseComment(closeComment != null ? closeComment : "");
|
||||
dataConfig.set("tickets." + ticketId, t);
|
||||
try {
|
||||
dataConfig.save(dataFile);
|
||||
@@ -588,6 +352,38 @@ public class DatabaseManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Löscht ein Ticket anhand der ID.
|
||||
*/
|
||||
public boolean deleteTicket(int id) {
|
||||
if (useMySQL) {
|
||||
String sql = "DELETE FROM tickets WHERE id = ?";
|
||||
try (Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||
ps.setInt(1, id);
|
||||
int rows = ps.executeUpdate();
|
||||
if (rows > 0) {
|
||||
backupMySQL();
|
||||
return true;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
sendError("Fehler beim Löschen des Tickets: " + e.getMessage());
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
if (dataConfig.contains("tickets." + id)) {
|
||||
dataConfig.set("tickets." + id, null);
|
||||
try {
|
||||
dataConfig.save(dataFile);
|
||||
backupDataFile();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
sendError("Fehler beim Löschen des Tickets: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Leitet ein Ticket an einen anderen Supporter weiter (Status → FORWARDED).
|
||||
*/
|
||||
@@ -623,6 +419,8 @@ public class DatabaseManager {
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────── Abfragen ──────────────────────────────────
|
||||
|
||||
/**
|
||||
* Gibt alle Tickets mit einem bestimmten Status zurück.
|
||||
*/
|
||||
@@ -642,7 +440,6 @@ public class DatabaseManager {
|
||||
}
|
||||
return list;
|
||||
} else {
|
||||
// Datei-Storage: Alle Tickets filtern
|
||||
if (dataConfig.contains("tickets")) {
|
||||
for (String key : dataConfig.getConfigurationSection("tickets").getKeys(false)) {
|
||||
Ticket t = (Ticket) dataConfig.get("tickets." + key);
|
||||
@@ -655,6 +452,29 @@ public class DatabaseManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt alle Tickets zurück (alle Status).
|
||||
*/
|
||||
public List<Ticket> getAllTickets() {
|
||||
List<Ticket> list = new ArrayList<>();
|
||||
if (useMySQL) {
|
||||
try (Connection conn = getConnection(); Statement stmt = conn.createStatement()) {
|
||||
ResultSet rs = stmt.executeQuery("SELECT * FROM tickets");
|
||||
while (rs.next()) list.add(mapRow(rs));
|
||||
} catch (SQLException e) {
|
||||
sendError("Fehler beim Abrufen aller Tickets: " + e.getMessage());
|
||||
}
|
||||
} else {
|
||||
if (dataConfig.contains("tickets")) {
|
||||
for (String key : dataConfig.getConfigurationSection("tickets").getKeys(false)) {
|
||||
Ticket t = (Ticket) dataConfig.get("tickets." + key);
|
||||
if (t != null) list.add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt ein einzelnes Ticket anhand der ID zurück.
|
||||
*/
|
||||
@@ -678,11 +498,11 @@ public class DatabaseManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Anzahl offener Tickets (OPEN + FORWARDED) – für Join-Benachrichtigung.
|
||||
* Anzahl offener Tickets (OPEN) – für Join-Benachrichtigung.
|
||||
*/
|
||||
public int countOpenTickets() {
|
||||
if (useMySQL) {
|
||||
String sql = "SELECT COUNT(*) FROM tickets WHERE status IN ('OPEN', 'FORWARDED', 'CLAIMED')";
|
||||
String sql = "SELECT COUNT(*) FROM tickets WHERE status = 'OPEN'";
|
||||
try (Connection conn = getConnection(); Statement stmt = conn.createStatement()) {
|
||||
ResultSet rs = stmt.executeQuery(sql);
|
||||
if (rs.next()) return rs.getInt(1);
|
||||
@@ -695,7 +515,7 @@ public class DatabaseManager {
|
||||
if (dataConfig.contains("tickets")) {
|
||||
for (String key : dataConfig.getConfigurationSection("tickets").getKeys(false)) {
|
||||
Ticket t = (Ticket) dataConfig.get("tickets." + key);
|
||||
if (t != null && (t.getStatus() == TicketStatus.OPEN || t.getStatus() == TicketStatus.FORWARDED || t.getStatus() == TicketStatus.CLAIMED)) count++;
|
||||
if (t != null && t.getStatus() == TicketStatus.OPEN) count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
@@ -721,17 +541,176 @@ public class DatabaseManager {
|
||||
if (dataConfig.contains("tickets")) {
|
||||
for (String key : dataConfig.getConfigurationSection("tickets").getKeys(false)) {
|
||||
Ticket t = (Ticket) dataConfig.get("tickets." + key);
|
||||
if (t != null && uuid.equals(t.getCreatorUUID()) && (t.getStatus() == TicketStatus.OPEN || t.getStatus() == TicketStatus.CLAIMED || t.getStatus() == TicketStatus.FORWARDED)) count++;
|
||||
if (t != null && uuid.equals(t.getCreatorUUID())
|
||||
&& (t.getStatus() == TicketStatus.OPEN
|
||||
|| t.getStatus() == TicketStatus.CLAIMED
|
||||
|| t.getStatus() == TicketStatus.FORWARDED)) count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────── Archivierung ──────────────────────────────
|
||||
|
||||
/**
|
||||
* Archiviert alle geschlossenen Tickets in eine separate Datei.
|
||||
*/
|
||||
public int archiveClosedTickets() {
|
||||
List<Ticket> all = getAllTickets();
|
||||
List<Ticket> toArchive = new ArrayList<>();
|
||||
for (Ticket t : all) {
|
||||
if (t.getStatus() == TicketStatus.CLOSED) toArchive.add(t);
|
||||
}
|
||||
if (toArchive.isEmpty()) return 0;
|
||||
|
||||
File archiveFile = new File(plugin.getDataFolder(), archiveFileName);
|
||||
JSONArray arr = new JSONArray();
|
||||
if (archiveFile.exists()) {
|
||||
try (FileReader fr = new FileReader(archiveFile)) {
|
||||
JSONParser parser = new JSONParser();
|
||||
Object parsed = parser.parse(fr);
|
||||
if (parsed instanceof JSONArray oldArr) arr.addAll(oldArr);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
for (Ticket t : toArchive) arr.add(ticketToJson(t));
|
||||
try (FileWriter fw = new FileWriter(archiveFile)) {
|
||||
fw.write(arr.toJSONString());
|
||||
} catch (Exception e) {
|
||||
sendError("Fehler beim Archivieren: " + e.getMessage());
|
||||
return 0;
|
||||
}
|
||||
|
||||
int removed = 0;
|
||||
if (useMySQL) {
|
||||
try (Connection conn = getConnection();
|
||||
PreparedStatement ps = conn.prepareStatement("DELETE FROM tickets WHERE id = ?")) {
|
||||
for (Ticket t : toArchive) {
|
||||
ps.setInt(1, t.getId());
|
||||
ps.executeUpdate();
|
||||
removed++;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sendError("Fehler beim Entfernen archivierter Tickets: " + e.getMessage());
|
||||
}
|
||||
} else {
|
||||
for (Ticket t : toArchive) {
|
||||
dataConfig.set("tickets." + t.getId(), null);
|
||||
removed++;
|
||||
}
|
||||
try { dataConfig.save(dataFile); } catch (Exception e) {
|
||||
sendError("Fehler beim Speichern nach Archivierung: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
// ─────────────────────────── Statistiken ───────────────────────────────
|
||||
|
||||
public TicketStats getTicketStats() {
|
||||
List<Ticket> all = getAllTickets();
|
||||
int open = 0, claimed = 0, forwarded = 0, closed = 0;
|
||||
java.util.Map<String, Integer> byPlayer = new java.util.HashMap<>();
|
||||
for (Ticket t : all) {
|
||||
switch (t.getStatus()) {
|
||||
case OPEN -> open++;
|
||||
case CLAIMED -> claimed++;
|
||||
case FORWARDED -> forwarded++;
|
||||
case CLOSED -> closed++;
|
||||
}
|
||||
byPlayer.merge(t.getCreatorName(), 1, Integer::sum);
|
||||
}
|
||||
return new TicketStats(all.size(), open, closed, forwarded, byPlayer);
|
||||
}
|
||||
|
||||
public static class TicketStats {
|
||||
public final int total, open, closed, forwarded;
|
||||
public final java.util.Map<String, Integer> byPlayer;
|
||||
public TicketStats(int total, int open, int closed, int forwarded, java.util.Map<String, Integer> byPlayer) {
|
||||
this.total = total; this.open = open; this.closed = closed;
|
||||
this.forwarded = forwarded; this.byPlayer = byPlayer;
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────── Export / Import ────────────────────────────
|
||||
|
||||
public int exportTickets(File exportFile) {
|
||||
List<Ticket> tickets = getAllTickets();
|
||||
JSONArray arr = new JSONArray();
|
||||
for (Ticket t : tickets) arr.add(ticketToJson(t));
|
||||
try (FileWriter fw = new FileWriter(exportFile)) {
|
||||
fw.write(arr.toJSONString());
|
||||
return tickets.size();
|
||||
} catch (IOException e) {
|
||||
sendError("Fehler beim Export: " + e.getMessage());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int importTickets(File importFile) {
|
||||
int imported = 0;
|
||||
try (FileReader fr = new FileReader(importFile)) {
|
||||
JSONParser parser = new JSONParser();
|
||||
JSONArray arr = (JSONArray) parser.parse(fr);
|
||||
for (Object o : arr) {
|
||||
Ticket t = ticketFromJson((JSONObject) o);
|
||||
if (t != null && createTicket(t) != -1) imported++;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
sendError("Fehler beim Import: " + e.getMessage());
|
||||
}
|
||||
return imported;
|
||||
}
|
||||
|
||||
// ─────────────────────────── Migration ─────────────────────────────────
|
||||
|
||||
public int migrateToMySQL() {
|
||||
if (useMySQL || dataConfig == null) return 0;
|
||||
int migrated = 0;
|
||||
try {
|
||||
for (String key : dataConfig.getConfigurationSection("tickets").getKeys(false)) {
|
||||
Ticket t = (Ticket) dataConfig.get("tickets." + key);
|
||||
if (t != null) {
|
||||
useMySQL = true;
|
||||
int id = createTicket(t);
|
||||
useMySQL = false;
|
||||
if (id != -1) migrated++;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().severe("Fehler bei Migration zu MySQL: " + e.getMessage());
|
||||
}
|
||||
return migrated;
|
||||
}
|
||||
|
||||
public int migrateToFile() {
|
||||
if (!useMySQL) return 0;
|
||||
int migrated = 0;
|
||||
try (Connection conn = getConnection(); Statement stmt = conn.createStatement()) {
|
||||
ResultSet rs = stmt.executeQuery("SELECT * FROM tickets");
|
||||
while (rs.next()) {
|
||||
Ticket t = mapRow(rs);
|
||||
if (t != null) {
|
||||
useMySQL = false;
|
||||
int id = createTicket(t);
|
||||
useMySQL = true;
|
||||
if (id != -1) migrated++;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().severe("Fehler bei Migration zu Datei: " + e.getMessage());
|
||||
}
|
||||
return migrated;
|
||||
}
|
||||
|
||||
// ─────────────────────────── Mapping ───────────────────────────────────
|
||||
|
||||
/**
|
||||
* Liest eine Zeile aus dem ResultSet und erstellt ein Ticket-Objekt.
|
||||
* close_comment wird mit try-catch abgesichert, damit ältere Datenbanken
|
||||
* ohne diese Spalte nicht abstürzen.
|
||||
*/
|
||||
private Ticket mapRow(ResultSet rs) throws SQLException {
|
||||
File archiveFile = new File(plugin.getDataFolder(), archiveFileName);
|
||||
Ticket t = new Ticket();
|
||||
t.setId(rs.getInt("id"));
|
||||
t.setCreatorUUID(UUID.fromString(rs.getString("creator_uuid")));
|
||||
@@ -748,6 +727,16 @@ public class DatabaseManager {
|
||||
t.setClaimedAt(rs.getTimestamp("claimed_at"));
|
||||
t.setClosedAt(rs.getTimestamp("closed_at"));
|
||||
|
||||
// ── BUGFIX: close_comment mit try-catch absichern ──────────────────
|
||||
// Wenn die Spalte in einer alten DB noch nicht existiert, wird der
|
||||
// Fehler ignoriert statt die gesamte Ticket-Liste leer zu lassen.
|
||||
try {
|
||||
String closeComment = rs.getString("close_comment");
|
||||
if (closeComment != null) t.setCloseComment(closeComment);
|
||||
} catch (SQLException ignored) {
|
||||
// Spalte existiert noch nicht – ensureColumns() ergänzt sie beim nächsten Start
|
||||
}
|
||||
|
||||
String claimerUUID = rs.getString("claimer_uuid");
|
||||
if (claimerUUID != null) {
|
||||
t.setClaimerUUID(UUID.fromString(claimerUUID));
|
||||
@@ -760,4 +749,100 @@ public class DatabaseManager {
|
||||
}
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────── JSON-Hilfsmethoden ─────────────────────────
|
||||
|
||||
private JSONObject ticketToJson(Ticket t) {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("id", t.getId());
|
||||
obj.put("creatorUUID", t.getCreatorUUID().toString());
|
||||
obj.put("creatorName", t.getCreatorName());
|
||||
obj.put("message", t.getMessage());
|
||||
obj.put("world", t.getWorldName());
|
||||
obj.put("x", t.getX());
|
||||
obj.put("y", t.getY());
|
||||
obj.put("z", t.getZ());
|
||||
obj.put("yaw", t.getYaw());
|
||||
obj.put("pitch", t.getPitch());
|
||||
obj.put("status", t.getStatus().name());
|
||||
obj.put("createdAt", t.getCreatedAt() != null ? t.getCreatedAt().getTime() : null);
|
||||
obj.put("claimedAt", t.getClaimedAt() != null ? t.getClaimedAt().getTime() : null);
|
||||
obj.put("closedAt", t.getClosedAt() != null ? t.getClosedAt().getTime() : null);
|
||||
if (t.getClaimerUUID() != null) obj.put("claimerUUID", t.getClaimerUUID().toString());
|
||||
if (t.getClaimerName() != null) obj.put("claimerName", t.getClaimerName());
|
||||
if (t.getForwardedToUUID() != null) obj.put("forwardedToUUID", t.getForwardedToUUID().toString());
|
||||
if (t.getForwardedToName() != null) obj.put("forwardedToName", t.getForwardedToName());
|
||||
if (t.getCloseComment() != null) obj.put("closeComment", t.getCloseComment());
|
||||
return obj;
|
||||
}
|
||||
|
||||
private Ticket ticketFromJson(JSONObject obj) {
|
||||
try {
|
||||
Ticket t = new Ticket();
|
||||
t.setId(((Long) obj.get("id")).intValue());
|
||||
t.setCreatorUUID(UUID.fromString((String) obj.get("creatorUUID")));
|
||||
t.setCreatorName((String) obj.get("creatorName"));
|
||||
t.setMessage((String) obj.get("message"));
|
||||
t.setWorldName((String) obj.get("world"));
|
||||
t.setX((Double) obj.get("x"));
|
||||
t.setY((Double) obj.get("y"));
|
||||
t.setZ((Double) obj.get("z"));
|
||||
t.setYaw(((Double) obj.get("yaw")).floatValue());
|
||||
t.setPitch(((Double) obj.get("pitch")).floatValue());
|
||||
t.setStatus(TicketStatus.valueOf((String) obj.get("status")));
|
||||
if (obj.get("createdAt") != null) t.setCreatedAt(new Timestamp((Long) obj.get("createdAt")));
|
||||
if (obj.get("claimedAt") != null) t.setClaimedAt(new Timestamp((Long) obj.get("claimedAt")));
|
||||
if (obj.get("closedAt") != null) t.setClosedAt(new Timestamp((Long) obj.get("closedAt")));
|
||||
if (obj.get("claimerUUID") != null) t.setClaimerUUID(UUID.fromString((String) obj.get("claimerUUID")));
|
||||
if (obj.get("claimerName") != null) t.setClaimerName((String) obj.get("claimerName"));
|
||||
if (obj.get("forwardedToUUID") != null) t.setForwardedToUUID(UUID.fromString((String) obj.get("forwardedToUUID")));
|
||||
if (obj.get("forwardedToName") != null) t.setForwardedToName((String) obj.get("forwardedToName"));
|
||||
if (obj.get("closeComment") != null) t.setCloseComment((String) obj.get("closeComment"));
|
||||
return t;
|
||||
} catch (Exception e) {
|
||||
if (plugin != null) plugin.getLogger().severe("Fehler beim Parsen eines Tickets: " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────── Validierung ───────────────────────────────
|
||||
|
||||
private void validateLoadedTickets() {
|
||||
if (dataConfig == null || !dataConfig.contains("tickets")) return;
|
||||
int invalid = 0;
|
||||
for (String key : dataConfig.getConfigurationSection("tickets").getKeys(false)) {
|
||||
Object obj = dataConfig.get("tickets." + key);
|
||||
if (!(obj instanceof Ticket t)) {
|
||||
sendError("Ungültiges Ticket-Objekt bei ID: " + key);
|
||||
invalid++;
|
||||
continue;
|
||||
}
|
||||
if (t.getCreatorUUID() == null || t.getCreatorName() == null
|
||||
|| t.getMessage() == null || t.getStatus() == null) {
|
||||
sendError("Ticket mit fehlenden Pflichtfeldern: ID " + key);
|
||||
invalid++;
|
||||
}
|
||||
try { UUID.fromString(t.getCreatorUUID().toString()); }
|
||||
catch (Exception e) { sendError("Ungültige UUID bei Ticket ID: " + key); invalid++; }
|
||||
|
||||
try { TicketStatus.valueOf(t.getStatus().name()); }
|
||||
catch (Exception e) { sendError("Ungültiger Status bei Ticket ID: " + key); invalid++; }
|
||||
}
|
||||
if (invalid > 0) {
|
||||
String msg = plugin != null
|
||||
? plugin.formatMessage("messages.validation-warning").replace("{count}", String.valueOf(invalid))
|
||||
: invalid + " ungültige Tickets beim Laden gefunden.";
|
||||
sendError(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────── Backup (Platzhalter) ──────────────────────
|
||||
|
||||
private void backupMySQL() {
|
||||
// TODO: MySQL-Backup implementieren
|
||||
}
|
||||
|
||||
private void backupDataFile() {
|
||||
// TODO: Datei-Backup implementieren
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user