package Sdf1_login; import java.io.File; import java.sql.*; import java.util.*; import java.util.logging.Logger; public class DatabaseManager { private Connection db; private final File dataFolder; private final Logger logger; public DatabaseManager(File dataFolder) { this.dataFolder = dataFolder; this.logger = Logger.getLogger("Sdf1_login"); } // ==================== 初始化 ==================== public void init() { try { Class.forName("org.sqlite.JDBC"); File dbFile = new File(dataFolder, "login.db"); db = DriverManager.getConnection( "jdbc:sqlite:" + dbFile.getAbsolutePath()); Statement st = db.createStatement(); st.execute("PRAGMA journal_mode=WAL"); // 用户表 st.execute("CREATE TABLE IF NOT EXISTS " + "users (" + "player_name TEXT PRIMARY KEY," + "password_hash TEXT NOT NULL," + "password_salt TEXT NOT NULL," + "email TEXT DEFAULT ''," + "register_time INTEGER DEFAULT 0," + "last_login_time INTEGER DEFAULT 0," + "total_online_time INTEGER DEFAULT 0," + "last_online_check INTEGER DEFAULT 0," + "is_logged_in INTEGER DEFAULT 0," + "blocks_broken INTEGER DEFAULT 0," + "blocks_placed INTEGER DEFAULT 0," + "gift_stage INTEGER DEFAULT 0," + "gift_claimed TEXT DEFAULT ''," + "invite_code TEXT DEFAULT ''," + "invited_by TEXT DEFAULT ''," + "monthly_invite_count INTEGER DEFAULT 0," + "invite_month TEXT DEFAULT ''," + "points INTEGER DEFAULT 0," + "checkin_streak INTEGER DEFAULT 0," + "last_checkin_date TEXT DEFAULT ''," + "total_checkin_days INTEGER DEFAULT 0," + "temp_password TEXT DEFAULT ''," + "temp_pw_expire INTEGER DEFAULT 0," + "temp_pw_used INTEGER DEFAULT 0," + "ip_address TEXT DEFAULT ''," + "tasks_completed TEXT DEFAULT ''," + "premium_uuid TEXT DEFAULT ''," + "register_ip TEXT DEFAULT ''," + "register_type TEXT DEFAULT 'manual'" + ")"); // 安全添加缺失列 safeAdd(st, "tasks_completed", "TEXT DEFAULT ''"); safeAdd(st, "temp_password", "TEXT DEFAULT ''"); safeAdd(st, "temp_pw_expire", "INTEGER DEFAULT 0"); safeAdd(st, "temp_pw_used", "INTEGER DEFAULT 0"); safeAdd(st, "ip_address", "TEXT DEFAULT ''"); safeAdd(st, "premium_uuid", "TEXT DEFAULT ''"); safeAdd(st, "register_ip", "TEXT DEFAULT ''"); safeAdd(st, "register_type", "TEXT DEFAULT 'manual'"); // 在 init 方法的 try 中添加 // 如果表存在但没有save_time列,添加它 try { st.execute("ALTER TABLE " + "inventory_backups " + "ADD COLUMN save_time " + "INTEGER DEFAULT 0"); } catch (SQLException ignored) { // 列已存在 } // 背包备份表 st.execute("CREATE TABLE IF NOT EXISTS " + "inventory_backups (" + "player_name TEXT PRIMARY KEY," + "inventory_data TEXT DEFAULT ''," + "armor_data TEXT DEFAULT ''," + "extra_data TEXT DEFAULT ''," + "player_level INTEGER DEFAULT 0," + "player_exp REAL DEFAULT 0.0)"); // 确保 inventory_backups 表结构正确 st.execute("CREATE TABLE IF NOT EXISTS " + "inventory_backups (" + "id INTEGER PRIMARY KEY " + "AUTOINCREMENT," + "player_name TEXT NOT NULL," + "contents_b64 TEXT," + "armor_b64 TEXT," + "extra_b64 TEXT," + "level INTEGER DEFAULT 0," + "experience REAL DEFAULT 0," + "save_time INTEGER DEFAULT 0" + ")"); // 安全报警表 st.execute("CREATE TABLE IF NOT EXISTS " + "security_alerts (" + "id INTEGER PRIMARY KEY AUTOINCREMENT," + "player_name TEXT NOT NULL," + "alert_ip TEXT NOT NULL," + "alert_time INTEGER NOT NULL," + "alert_date TEXT NOT NULL," + "notified INTEGER DEFAULT 0," + "notified_time INTEGER DEFAULT 0)"); // 工单表 st.execute("CREATE TABLE IF NOT EXISTS " + "tickets (" + "id INTEGER PRIMARY KEY AUTOINCREMENT," + "type TEXT NOT NULL," + "status TEXT DEFAULT 'pending'," + "priority TEXT DEFAULT 'normal'," + "requester TEXT NOT NULL," + "assigned_to TEXT DEFAULT ''," + "title TEXT DEFAULT ''," + "description TEXT DEFAULT ''," + "reward_amount INTEGER DEFAULT 0," + "score INTEGER DEFAULT 0," + "target_player TEXT DEFAULT ''," + "create_time INTEGER NOT NULL," + "update_time INTEGER DEFAULT 0," + "complete_time INTEGER DEFAULT 0," + "admin_confirmed INTEGER DEFAULT 0," + "reject_reason TEXT DEFAULT ''" + ")"); // 工单回复表 st.execute("CREATE TABLE IF NOT EXISTS " + "ticket_replies (" + "id INTEGER PRIMARY KEY " + "AUTOINCREMENT," + "ticket_id INTEGER NOT NULL," + "sender TEXT NOT NULL," + "message TEXT NOT NULL," + "create_time INTEGER NOT NULL" + ")"); // 服务商表 st.execute("CREATE TABLE IF NOT EXISTS " + "service_providers (" + "player_name TEXT PRIMARY KEY," + "role TEXT DEFAULT 'waiter'," + "active INTEGER DEFAULT 1," + "join_time INTEGER DEFAULT 0" + ")"); // 安全迁移 inventory_backups 表 // 不删除旧数据,只补缺失列 try { st.execute("ALTER TABLE " + "inventory_backups " + "ADD COLUMN " + "save_time INTEGER " + "DEFAULT 0"); } catch (SQLException ignored) { } try { st.execute("ALTER TABLE " + "inventory_backups " + "ADD COLUMN " + "level INTEGER " + "DEFAULT 0"); } catch (SQLException ignored) { } try { st.execute("ALTER TABLE " + "inventory_backups " + "ADD COLUMN " + "experience REAL " + "DEFAULT 0"); } catch (SQLException ignored) { } try { st.execute("ALTER TABLE " + "inventory_backups " + "ADD COLUMN " + "armor_b64 TEXT " + "DEFAULT ''"); } catch (SQLException ignored) { } try { st.execute("ALTER TABLE " + "inventory_backups " + "ADD COLUMN " + "extra_b64 TEXT " + "DEFAULT ''"); } catch (SQLException ignored) { } try { st.execute("ALTER TABLE " + "inventory_backups " + "ADD COLUMN " + "contents_b64 TEXT " + "DEFAULT ''"); } catch (SQLException ignored) { } st.close(); logger.info("[Sdf1_login] 数据库初始化完成"); } catch (Exception e) { throw new RuntimeException( "[Sdf1_login] DB初始化失败: " + e.getMessage(), e); } } private void safeAdd(Statement st, String col, String def) { try { st.executeUpdate("ALTER TABLE users " + "ADD COLUMN " + col + " " + def); } catch (SQLException ignored) { } } public void close() { try { if (db != null && !db.isClosed()) db.close(); } catch (Exception ignored) { } } // ==================== 用户操作 ==================== public boolean userExists(String name) { try { PreparedStatement ps = db.prepareStatement( "SELECT 1 FROM users " + "WHERE player_name=?"); ps.setString(1, name); boolean r = ps.executeQuery().next(); ps.close(); return r; } catch (SQLException e) { return false; } } public void createUser(String name, String hash, String salt) { try { PreparedStatement ps = db.prepareStatement( "INSERT INTO users " + "(player_name, " + "password_hash, " + "password_salt, " + "register_time) " + "VALUES (?,?,?,?)"); ps.setString(1, name); ps.setString(2, hash); ps.setString(3, salt); ps.setLong(4, System.currentTimeMillis()); ps.executeUpdate(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } } // ========== 临时密码管理 ========== public void clearTempPassword(String name) { setField(name, "temp_password", null); setField(name, "temp_pw_expire", 0); setField(name, "temp_pw_used", 1); } public void clearAllTempPasswords() { try { Statement st = db.createStatement(); st.executeUpdate( "UPDATE users SET " + "temp_password=NULL," + "temp_pw_expire=0," + "temp_pw_used=1 " + "WHERE temp_password " + "IS NOT NULL"); st.close(); } catch (SQLException e) { e.printStackTrace(); } } public String getPlayerIPByName( String name) { Object ip = getField(name, "ip"); return ip != null ? String.valueOf(ip) : null; } public boolean checkPasswordOrTemp( String name, String hash) { // 先检查主密码 try { PreparedStatement ps = db.prepareStatement( "SELECT password_hash " + "FROM users " + "WHERE player_name=?"); ps.setString(1, name); ResultSet rs = ps.executeQuery(); if (rs.next()) { String mainHash = rs.getString("password_hash"); rs.close(); ps.close(); if (hash.equals(mainHash)) return true; } else { rs.close(); ps.close(); } } catch (SQLException e) { e.printStackTrace(); } // 再检查临时密码 try { PreparedStatement ps2 = db.prepareStatement( "SELECT temp_password, " + "temp_pw_expire, " + "temp_pw_used " + "FROM users " + "WHERE player_name=?"); ps2.setString(1, name); ResultSet rs2 = ps2.executeQuery(); if (rs2.next()) { String tempHash = rs2.getString( "temp_password"); long expire = rs2.getLong( "temp_pw_expire"); int used = rs2.getInt( "temp_pw_used"); rs2.close(); ps2.close(); if (tempHash != null && !tempHash.isEmpty() && hash.equals(tempHash) && System.currentTimeMillis() < expire && used != 1) { return true; } } else { rs2.close(); ps2.close(); } } catch (SQLException e) { e.printStackTrace(); } return false; } public Map getUser(String name) { Map r = new LinkedHashMap<>(); try { PreparedStatement ps = db.prepareStatement( "SELECT * FROM users " + "WHERE player_name=?"); ps.setString(1, name); ResultSet rs = ps.executeQuery(); if (rs.next()) { for (int i = 1; i <= rs.getMetaData() .getColumnCount(); i++) { r.put(rs.getMetaData() .getColumnName(i), rs.getObject(i)); } } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } return r; } public void setField(String name, String field, Object val) { try { PreparedStatement ps = db.prepareStatement( "UPDATE users SET " + field + "=? " + "WHERE player_name=?"); if (val instanceof Integer) ps.setInt(1, (Integer) val); else if (val instanceof Long) ps.setLong(1, (Long) val); else ps.setString(1, String.valueOf(val)); ps.setString(2, name); ps.executeUpdate(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } } public Object getField(String name, String field) { try { PreparedStatement ps = db.prepareStatement( "SELECT " + field + " FROM users " + "WHERE player_name=?"); ps.setString(1, name); ResultSet rs = ps.executeQuery(); Object val = rs.next() ? rs.getObject(1) : null; rs.close(); ps.close(); return val; } catch (SQLException e) { return null; } } public boolean checkPassword(String name, String hash) { try { PreparedStatement ps = db.prepareStatement( "SELECT password_hash " + "FROM users " + "WHERE player_name=?"); ps.setString(1, name); ResultSet rs = ps.executeQuery(); if (rs.next()) { String stored = rs.getString("password_hash"); rs.close(); ps.close(); return hash.equals(stored); } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } return false; } public boolean isUsingTempPassword(String name) { String h = (String) getField(name, "temp_password"); return h != null && !h.isEmpty(); } public void setLoggedIn(String name, boolean val) { setField(name, "is_logged_in", val ? 1 : 0); } public void addPoints(String name, int amount) { Object cur = getField(name, "points"); int curVal = cur instanceof Number ? ((Number) cur).intValue() : 0; setField(name, "points", curVal + amount); } public boolean deductPoints(String name, int amount) { Object cur = getField(name, "points"); int curVal = cur instanceof Number ? ((Number) cur).intValue() : 0; if (curVal < amount) return false; setField(name, "points", curVal - amount); return true; } public List> getAllUsers() { List> list = new ArrayList<>(); try { Statement st = db.createStatement(); ResultSet rs = st.executeQuery( "SELECT * FROM users"); while (rs.next()) { Map row = new LinkedHashMap<>(); for (int i = 1; i <= rs.getMetaData() .getColumnCount(); i++) { row.put(rs.getMetaData() .getColumnName(i), rs.getObject(i)); } list.add(row); } rs.close(); st.close(); } catch (SQLException e) { e.printStackTrace(); } return list; } public List> getTicketsByProviderAll( String provider) { List> list = new ArrayList<>(); try { PreparedStatement ps = db.prepareStatement( "SELECT * FROM tickets " + "WHERE assigned_to=? " + "ORDER BY create_time DESC"); ps.setString(1, provider); ResultSet rs = ps.executeQuery(); while (rs.next()) { Map row = new LinkedHashMap<>(); for (int i = 1; i <= rs .getMetaData() .getColumnCount(); i++) { row.put(rs.getMetaData() .getColumnName(i), rs.getObject(i)); } list.add(row); } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } return list; } public void deleteUser(String name) { try { PreparedStatement ps = db.prepareStatement( "DELETE FROM users " + "WHERE player_name=?"); ps.setString(1, name); ps.executeUpdate(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } } public void recordIP(String name, String ip) { if (ip != null && !ip.isEmpty()) { setField(name, "ip_address", ip); } } // ==================== IP查询 ==================== /** * 统计指定IP注册的账号数量 */ public int countIPAccounts(String ip) { try { PreparedStatement ps = db.prepareStatement( "SELECT COUNT(*) FROM users " + "WHERE ip_address=?"); ps.setString(1, ip); ResultSet rs = ps.executeQuery(); int count = 0; if (rs.next()) count = rs.getInt(1); rs.close(); ps.close(); return count; } catch (SQLException e) { return 0; } } /** * 获取指定IP下所有账号详情 */ public List> getAccountDetailsByIP(String ip) { List> list = new ArrayList<>(); try { PreparedStatement ps = db.prepareStatement( "SELECT player_name, " + "register_time " + "FROM users " + "WHERE ip_address=? " + "ORDER BY register_time"); ps.setString(1, ip); ResultSet rs = ps.executeQuery(); while (rs.next()) { Map row = new LinkedHashMap<>(); row.put("player_name", rs.getString("player_name")); row.put("register_time", rs.getLong("register_time")); list.add(row); } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } return list; } // ==================== 背包备份 ==================== public void saveInventoryBackup(String name, String contents, String armor, String extra, int level, double experience) { try { PreparedStatement del = db.prepareStatement( "DELETE FROM " + "inventory_backups " + "WHERE player_name=?"); del.setString(1, name); del.executeUpdate(); del.close(); PreparedStatement ps = db.prepareStatement( "INSERT INTO " + "inventory_backups" + " (player_name, " + "contents_b64, " + "armor_b64, " + "extra_b64, " + "level, " + "experience, " + "save_time) " + "VALUES " + "(?,?,?,?,?,?,?)"); ps.setString(1, name); ps.setString(2, contents); ps.setString(3, armor); ps.setString(4, extra); ps.setInt(5, level); ps.setDouble(6, experience); ps.setLong(7, System.currentTimeMillis()); ps.executeUpdate(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } } public String[] loadInventoryBackup( String name) { try { PreparedStatement ps = db.prepareStatement( "SELECT contents_b64, " + "armor_b64, " + "extra_b64, " + "level, " + "experience " + "FROM " + "inventory_backups " + "WHERE player_name=? " + "ORDER BY save_time " + "DESC LIMIT 1"); ps.setString(1, name); ResultSet rs = ps.executeQuery(); if (rs.next()) { String[] result = new String[5]; result[0] = rs.getString( "contents_b64"); result[1] = rs.getString( "armor_b64"); result[2] = rs.getString( "extra_b64"); result[3] = String.valueOf( rs.getInt("level")); result[4] = String.valueOf( rs.getDouble("experience")); rs.close(); ps.close(); return result; } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } return null; } public void deleteInventoryBackup( String name) { try { PreparedStatement ps = db.prepareStatement( "DELETE FROM inventory_backups " + "WHERE player_name=?"); ps.setString(1, name); ps.executeUpdate(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } } public boolean hasInventoryBackup( String name) { try { PreparedStatement ps = db.prepareStatement( "SELECT id FROM " + "inventory_backups " + "WHERE player_name=? " + "LIMIT 1"); ps.setString(1, name); ResultSet rs = ps.executeQuery(); boolean has = rs.next(); rs.close(); ps.close(); return has; } catch (SQLException e) { e.printStackTrace(); return false; } } // ==================== 安全报警 ==================== public void recordSecurityAlert( String playerName, String ip) { try { PreparedStatement ps = db.prepareStatement( "INSERT INTO security_alerts " + "(player_name, alert_ip, " + "alert_time, alert_date) " + "VALUES (?,?,?,?)"); ps.setString(1, playerName); ps.setString(2, ip); long now = System.currentTimeMillis(); ps.setLong(3, now); ps.setString(4, new java.text.SimpleDateFormat( "yyyy-MM-dd HH:mm:ss") .format(new java.util.Date( now))); ps.executeUpdate(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } } public List> getUnnotifiedAlerts( String playerName) { List> list = new ArrayList<>(); try { PreparedStatement ps = db.prepareStatement( "SELECT * FROM security_alerts " + "WHERE player_name=? " + "AND notified=0 " + "ORDER BY alert_time ASC"); ps.setString(1, playerName); ResultSet rs = ps.executeQuery(); while (rs.next()) { Map row = new LinkedHashMap<>(); for (int i = 1; i <= rs.getMetaData() .getColumnCount(); i++) { row.put(rs.getMetaData() .getColumnName(i), rs.getObject(i)); } list.add(row); } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } return list; } public void markAlertNotified(int alertId) { try { PreparedStatement ps = db.prepareStatement( "UPDATE security_alerts " + "SET notified=1, " + "notified_time=? " + "WHERE id=?"); ps.setLong(1, System.currentTimeMillis()); ps.setInt(2, alertId); ps.executeUpdate(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } } /** * 获取玩家最近N条背包备份 */ public List> getInventoryBackups(String name, int limit) { List> list = new ArrayList<>(); try { PreparedStatement ps = db.prepareStatement( "SELECT id, save_time " + "FROM inventory_backups " + "WHERE player_name=? " + "ORDER BY save_time DESC " + "LIMIT ?"); ps.setString(1, name); ps.setInt(2, limit); ResultSet rs = ps.executeQuery(); while (rs.next()) { Map row = new LinkedHashMap<>(); row.put("id", rs.getInt("id")); row.put("save_time", rs.getLong("save_time")); list.add(row); } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } return list; } /** * 获取指定ID的备份数据 */ public String[] getInventoryBackupById( int backupId) { try { PreparedStatement ps = db.prepareStatement( "SELECT contents_b64, " + "armor_b64, " + "extra_b64, " + "level, " + "experience " + "FROM inventory_backups " + "WHERE id=?"); ps.setInt(1, backupId); ResultSet rs = ps.executeQuery(); if (rs.next()) { String[] result = new String[5]; result[0] = rs.getString( "contents_b64"); result[1] = rs.getString( "armor_b64"); result[2] = rs.getString( "extra_b64"); result[3] = String.valueOf( rs.getInt("level")); result[4] = String.valueOf( rs.getDouble("experience")); rs.close(); ps.close(); return result; } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } return null; } // ==================== 工单操作 ==================== public int createTicket(String type, String status, String priority, String requester, String title, String description, int rewardAmount, String targetPlayer) { try { PreparedStatement ps = db.prepareStatement( "INSERT INTO tickets " + "(type, status, priority, " + "requester, title, " + "description, " + "reward_amount, " + "target_player, " + "create_time) " + "VALUES (?,?,?,?,?,?,?,?,?)"); ps.setString(1, type); ps.setString(2, status); ps.setString(3, priority); ps.setString(4, requester); ps.setString(5, title); ps.setString(6, description); ps.setInt(7, rewardAmount); ps.setString(8, targetPlayer); ps.setLong(9, System.currentTimeMillis()); ps.executeUpdate(); ResultSet rs = ps.getGeneratedKeys(); int id = -1; if (rs.next()) id = rs.getInt(1); rs.close(); ps.close(); return id; } catch (SQLException e) { e.printStackTrace(); return -1; } } public void updateTicketStatus(int ticketId, String status) { try { PreparedStatement ps = db.prepareStatement( "UPDATE tickets SET status=?, " + "update_time=? " + "WHERE id=?"); ps.setString(1, status); ps.setLong(2, System.currentTimeMillis()); ps.setInt(3, ticketId); ps.executeUpdate(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } } public void assignTicket(int ticketId, String provider) { try { PreparedStatement ps = db.prepareStatement( "UPDATE tickets SET " + "assigned_to=?, " + "status='processing', " + "update_time=? " + "WHERE id=?"); ps.setString(1, provider); ps.setLong(2, System.currentTimeMillis()); ps.setInt(3, ticketId); ps.executeUpdate(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } } public void completeTicket(int ticketId) { try { PreparedStatement ps = db.prepareStatement( "UPDATE tickets SET " + "status='completed', " + "complete_time=?, " + "update_time=? " + "WHERE id=?"); long now = System.currentTimeMillis(); ps.setLong(1, now); ps.setLong(2, now); ps.setInt(3, ticketId); ps.executeUpdate(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } } public void confirmTicket(int ticketId, int score) { try { PreparedStatement ps = db.prepareStatement( "UPDATE tickets SET " + "admin_confirmed=1, " + "score=?, " + "status='resolved', " + "update_time=? " + "WHERE id=?"); ps.setInt(1, score); ps.setLong(2, System.currentTimeMillis()); ps.setInt(3, ticketId); ps.executeUpdate(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } } public void rejectTicketComplete(int ticketId, String reason) { try { PreparedStatement ps = db.prepareStatement( "UPDATE tickets SET " + "status='rejected', " + "reject_reason=?, " + "update_time=? " + "WHERE id=?"); ps.setString(1, reason); ps.setLong(2, System.currentTimeMillis()); ps.setInt(3, ticketId); ps.executeUpdate(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } } /** * 更新工单任意字段 */ public void updateTicketField(int ticketId, String field, Object value) { try { PreparedStatement ps = db.prepareStatement( "UPDATE tickets SET " + field + "=? WHERE id=?"); if (value instanceof Integer) ps.setInt(1, (Integer) value); else if (value instanceof Long) ps.setLong(1, (Long) value); else ps.setString(1, String.valueOf(value)); ps.setInt(2, ticketId); ps.executeUpdate(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } } public Map getTicket( int ticketId) { try { PreparedStatement ps = db.prepareStatement( "SELECT * FROM tickets " + "WHERE id=?"); ps.setInt(1, ticketId); ResultSet rs = ps.executeQuery(); if (rs.next()) { Map row = new LinkedHashMap<>(); for (int i = 1; i <= rs.getMetaData() .getColumnCount(); i++) { row.put(rs.getMetaData() .getColumnName(i), rs.getObject(i)); } rs.close(); ps.close(); return row; } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } return null; } public List> getTicketsByStatus(String status) { List> list = new ArrayList<>(); try { PreparedStatement ps = db.prepareStatement( "SELECT * FROM tickets " + "WHERE status=? " + "ORDER BY create_time DESC"); ps.setString(1, status); ResultSet rs = ps.executeQuery(); while (rs.next()) { Map row = new LinkedHashMap<>(); for (int i = 1; i <= rs.getMetaData() .getColumnCount(); i++) { row.put(rs.getMetaData() .getColumnName(i), rs.getObject(i)); } list.add(row); } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } return list; } public void addTicketReply(int ticketId, String sender, String message) { try { PreparedStatement ps = db.prepareStatement( "INSERT INTO ticket_replies " + "(ticket_id, sender, " + "message, create_time) " + "VALUES (?,?,?,?)"); ps.setInt(1, ticketId); ps.setString(2, sender); ps.setString(3, message); ps.setLong(4, System.currentTimeMillis()); ps.executeUpdate(); ps.close(); // 更新工单状态为已回复 updateTicketStatus(ticketId, "replied"); } catch (SQLException e) { e.printStackTrace(); } } public List> getTicketReplies(int ticketId) { List> list = new ArrayList<>(); try { PreparedStatement ps = db.prepareStatement( "SELECT * FROM ticket_replies " + "WHERE ticket_id=? " + "ORDER BY create_time ASC"); ps.setInt(1, ticketId); ResultSet rs = ps.executeQuery(); while (rs.next()) { Map row = new LinkedHashMap<>(); for (int i = 1; i <= rs .getMetaData() .getColumnCount(); i++) { row.put(rs.getMetaData() .getColumnName(i), rs.getObject(i)); } list.add(row); } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } return list; } /** * 获取已回复但超时未互动的工单 */ public List> getRepliedStaleTickets( long timeoutMs) { List> list = new ArrayList<>(); try { long cutoff = System.currentTimeMillis() - timeoutMs; PreparedStatement ps = db.prepareStatement( "SELECT * FROM tickets " + "WHERE status='replied' " + "AND update_time < ?"); ps.setLong(1, cutoff); ResultSet rs = ps.executeQuery(); while (rs.next()) { Map row = new LinkedHashMap<>(); for (int i = 1; i <= rs .getMetaData() .getColumnCount(); i++) { row.put(rs.getMetaData() .getColumnName(i), rs.getObject(i)); } list.add(row); } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } return list; } public List> getTicketsByPlayer(String playerName) { List> list = new ArrayList<>(); try { PreparedStatement ps = db.prepareStatement( "SELECT * FROM tickets " + "WHERE requester=? " + "ORDER BY create_time DESC"); ps.setString(1, playerName); ResultSet rs = ps.executeQuery(); while (rs.next()) { Map row = new LinkedHashMap<>(); for (int i = 1; i <= rs.getMetaData() .getColumnCount(); i++) { row.put(rs.getMetaData() .getColumnName(i), rs.getObject(i)); } list.add(row); } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } return list; } public List> getTicketsByProvider(String provider) { List> list = new ArrayList<>(); try { PreparedStatement ps = db.prepareStatement( "SELECT * FROM tickets " + "WHERE assigned_to=? " + "AND (status='processing' " + "OR status='replied') " + "ORDER BY create_time DESC"); ps.setString(1, provider); ResultSet rs = ps.executeQuery(); while (rs.next()) { Map row = new LinkedHashMap<>(); for (int i = 1; i <= rs .getMetaData() .getColumnCount(); i++) { row.put(rs.getMetaData() .getColumnName(i), rs.getObject(i)); } list.add(row); } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } return list; } public List> getUnconfirmedCompleted( long timeoutMs) { List> list = new ArrayList<>(); try { long cutoff = System.currentTimeMillis() - timeoutMs; PreparedStatement ps = db.prepareStatement( "SELECT * FROM tickets " + "WHERE status='completed' " + "AND admin_confirmed=0 " + "AND complete_time < ?"); ps.setLong(1, cutoff); ResultSet rs = ps.executeQuery(); while (rs.next()) { Map row = new LinkedHashMap<>(); for (int i = 1; i <= rs.getMetaData() .getColumnCount(); i++) { row.put(rs.getMetaData() .getColumnName(i), rs.getObject(i)); } list.add(row); } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } return list; } // ==================== 服务商操作 ==================== public void addServiceProvider(String name, String role) { try { PreparedStatement ps = db.prepareStatement( "INSERT OR REPLACE INTO " + "service_providers " + "(player_name, role, " + "active, join_time) " + "VALUES (?,?,1,?)"); ps.setString(1, name); ps.setString(2, role); ps.setLong(3, System.currentTimeMillis()); ps.executeUpdate(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } } public void removeServiceProvider( String name) { try { PreparedStatement ps = db.prepareStatement( "DELETE FROM service_providers " + "WHERE player_name=?"); ps.setString(1, name); ps.executeUpdate(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } } public boolean isServiceProvider( String name) { try { PreparedStatement ps = db.prepareStatement( "SELECT 1 FROM service_providers " + "WHERE player_name=? " + "AND active=1"); ps.setString(1, name); boolean r = ps.executeQuery().next(); ps.close(); return r; } catch (SQLException e) { return false; } } public List> getAllServiceProviders() { List> list = new ArrayList<>(); try { Statement st = db.createStatement(); ResultSet rs = st.executeQuery( "SELECT * FROM service_providers " + "WHERE active=1"); while (rs.next()) { Map row = new LinkedHashMap<>(); for (int i = 1; i <= rs.getMetaData() .getColumnCount(); i++) { row.put(rs.getMetaData() .getColumnName(i), rs.getObject(i)); } list.add(row); } rs.close(); st.close(); } catch (SQLException e) { e.printStackTrace(); } return list; } public Connection getDb() { return db; } }