UIPosts

UIPosts.java v2.0
支持PlaceholderAPI和多UI自定义,配置驱动贴子系统,Chest+书本,数据库存储,指令分页
作者: ScriptIrc Engine

命令列表

  • uiposts打开或操作多UI贴子界面,参数支持chest/book/reload等

权限列表

  • uiposts.adminUIPosts管理权限
package org.sircustom.uiposts;

import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BookMeta;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.metadata.FixedMetadataValue;
import javax.sql.DataSource;
import java.io.File;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;

/**
 * @pluginName UIPosts
 * @author ScriptIrc Engine
 * @version 2.0
 * @description 支持PlaceholderAPI和多UI自定义,配置驱动贴子系统,Chest+书本,数据库存储,指令分页
 * [command]uiposts|打开或操作多UI贴子界面,参数支持chest/book/reload等[/command]
 * [Permission]uiposts.admin|UIPosts管理权限[/Permission]
 */
public class UIPosts extends JavaPlugin implements Listener, TabCompleter {
    // 数据库文件后缀改为.db
    private static final String DB_FILE = "uiposts.db";
    private Connection dataSource;
    private Map<String, ConfigurationSection> guiConfig = new HashMap<>();
    private final Map<UUID, Boolean> awaitingPostInput = new ConcurrentHashMap<>();
    private final Map<UUID, Integer> awaitingReply = new ConcurrentHashMap<>();
    private final Map<Integer, List<Reply>> replyCache = new ConcurrentHashMap<>();
    private String cachedTime;
    private static final int MAX_POST_LENGTH = 100;
    private static final int MAX_REPLY_LENGTH = 50;
    private static final int CACHE_TTL = 300;
    private static final int MAX_PAGES = 100;

    @Override
    public void onEnable() {
        saveDefaultConfig();
        reloadConfig();
        Bukkit.getPluginManager().registerEvents(this, this);
        if (getCommand("uiposts") != null) {
            getCommand("uiposts").setTabCompleter(this);
        }
        initDatabase();
        loadConfig();
        startScheduledTasks();
        getLogger().info("UIPosts v2.0 已启动 - 配置已加载,数据库已连接");
    }

    @Override
    public void onDisable() {
        replyCache.clear();
        closeDatabase();
        getLogger().info("UIPosts 已安全关闭");
    }

    private void loadConfig() {
        FileConfiguration config = getConfig();
        guiConfig.put("chest", config.getConfigurationSection("gui.chest"));
        guiConfig.put("book", config.getConfigurationSection("gui.book"));
        getLogger().info("已加载GUI配置");
    }

    private void reloadPluginConfig() {
        reloadConfig();
        loadConfig();
        getLogger().info("配置已重载");
    }

    private void initDatabase() {
        try {
            // 保证db后缀
            File dbFile = new File(getDataFolder(), DB_FILE);
            if (!getDataFolder().exists()) {
                getDataFolder().mkdirs();
            }
            boolean isNewDB = !dbFile.exists();
            if (isNewDB) {
                dbFile.createNewFile();
            }
            Class.forName("org.sqlite.JDBC");
            dataSource = DriverManager.getConnection("jdbc:sqlite:" + dbFile.getPath());
            if (isNewDB) {
                try (Statement st = dataSource.createStatement()) {
                    st.executeUpdate("CREATE TABLE posts (id INTEGER PRIMARY KEY AUTOINCREMENT, player VARCHAR(16) NOT NULL, content TEXT NOT NULL, time VARCHAR(20) NOT NULL)");
                    st.executeUpdate("CREATE TABLE replies (id INTEGER PRIMARY KEY AUTOINCREMENT, post_id INTEGER NOT NULL, player VARCHAR(16) NOT NULL, content TEXT NOT NULL, time VARCHAR(20) NOT NULL, FOREIGN KEY(post_id) REFERENCES posts(id))");
                    st.executeUpdate("CREATE INDEX idx_post_id ON replies(post_id)");
                    getLogger().info("已创建新数据库");
                }
            }
        } catch (Exception e) {
            getLogger().log(Level.SEVERE, "数据库初始化失败", e);
        }
    }

    private void closeDatabase() {
        try {
            if (dataSource != null && !dataSource.isClosed()) {
                dataSource.close();
            }
        } catch (SQLException e) {
            getLogger().warning("关闭数据库失败: " + e.getMessage());
        }
    }

    @Override
    public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
        if (!(sender instanceof Player)) {
            sender.sendMessage(ChatColor.RED + "仅玩家可以使用此命令");
            return true;
        }
        Player player = (Player) sender;
        if (args.length > 0 && "reload".equalsIgnoreCase(args[0]) && player.hasPermission("uiposts.admin")) {
            reloadPluginConfig();
            player.sendMessage(ChatColor.GREEN + "配置已重载");
            return true;
        }
        String type = (args.length == 0) ? "chest" : args[0].toLowerCase();
        int page = 1;
        if (args.length >= 2) {
            try {
                page = Integer.parseInt(args[1]);
                page = Math.max(1, Math.min(page, MAX_PAGES));
            } catch (NumberFormatException e) {
                player.sendMessage(ChatColor.RED + "无效的页码");
                return true;
            }
        }
        switch (type) {
            case "chest":
                openChestGUI(player, page);
                break;
            case "book":
                openBookGUI(player, page);
                break;
            default:
                player.sendMessage(ChatColor.YELLOW + "用法: /uiposts [chest|book] [页码]");
        }
        return true;
    }

    @Override
    public List<String> onTabComplete(CommandSender sender, Command cmd, String alias, String[] args) {
        if (args.length == 1) {
            return Arrays.asList("chest", "book", "reload");
        }
        return Collections.emptyList();
    }

    // 打开箱子UI界面
    private void openChestGUI(Player player, int page) {
        ConfigurationSection cfg = guiConfig.get("chest");
        if (cfg == null) {
            player.sendMessage(ChatColor.RED + "未配置箱子界面");
            return;
        }
        int size = cfg.getInt("size", 27);
        String title = parsePlaceholders(cfg.getString("title", "§b§l帖子广场"), player, page);
        Inventory inv = Bukkit.createInventory(null, size, title);
        int postsPerPage = cfg.getIntegerList("post_slot").size();
        List<Post> posts = getPostsByPage(page, postsPerPage);
        List<Integer> postSlots = cfg.getIntegerList("post_slot");
        for (int i = 0; i < Math.min(posts.size(), postSlots.size()); i++) {
            Post post = posts.get(i);
            inv.setItem(postSlots.get(i), createPostItem(post));
        }
        inv.setItem(cfg.getInt("post_button_slot", 4), createButton(
                Material.ANVIL,
                cfg.getString("post_button", "§a§l我要发帖")
        ));
        inv.setItem(cfg.getInt("book_button_slot", 22), createButton(
                Material.WRITTEN_BOOK,
                cfg.getString("jump_book", "§6查看书本模式")
        ));
        if (hasNextPage(page, postsPerPage)) {
            inv.setItem(cfg.getInt("next_slot", 25), createButton(
                    Material.ARROW,
                    "§e下一页"
            ));
        }
        if (page > 1) {
            inv.setItem(cfg.getInt("prev_slot", 19), createButton(
                    Material.ARROW,
                    "§e上一页"
            ));
        }
        player.openInventory(inv);
        storePlayerState(player, "chest", page);
    }

    // 打开书本UI界面
    private void openBookGUI(Player player, int page) {
        ConfigurationSection cfg = guiConfig.get("book");
        if (cfg == null) {
            player.sendMessage(ChatColor.RED + "未配置书本界面");
            return;
        }
        int postsPerPage = cfg.getInt("posts_per_page", 5);
        ItemStack book = new ItemStack(Material.WRITTEN_BOOK);
        BookMeta meta = (BookMeta) book.getItemMeta();
        meta.setTitle(parsePlaceholders(cfg.getString("title", "§b§l帖子广场"), player, page));
        meta.setAuthor("UIPosts");
        List<String> pages = generateBookPages(page, postsPerPage);
        meta.setPages(pages);
        book.setItemMeta(meta);
        ItemStack oldItem = player.getInventory().getItemInMainHand().clone();
        player.getInventory().setItemInMainHand(book);
        new BukkitRunnable() {
            @Override
            public void run() {
                player.openBook(book);
                player.getInventory().setItemInMainHand(oldItem);
            }
        }.runTaskLater(this, 2L);
        storePlayerState(player, "book", page);
    }

    // 生成书本内帖子内容页面
    private List<String> generateBookPages(int currentPage, int postsPerPage) {
        List<String> pages = new ArrayList<>();
        List<Post> posts = getPostsByPage(currentPage, postsPerPage);
        if (posts.isEmpty()) {
            pages.add("§7暂无帖子\n§a请使用箱子界面发帖!");
            return pages;
        }
        StringBuilder currentContent = new StringBuilder();
        for (Post post : posts) {
            String postText = formatPostForBook(post);
            if (currentContent.length() + postText.length() > 1500) {
                pages.add(currentContent.toString());
                currentContent = new StringBuilder();
            }
            currentContent.append(postText);
        }
        if (currentContent.length() > 0) {
            pages.add(currentContent.toString());
        }
        pages.add(generateNavigationPage(currentPage));
        return pages;
    }

    private String generateNavigationPage(int currentPage) {
        int totalPages = getTotalPages(guiConfig.get("book").getInt("posts_per_page", 5));
        StringBuilder sb = new StringBuilder("§l§n导航菜单§r\n\n");
        sb.append("§7当前页: §e").append(currentPage).append("§7/§e").append(totalPages).append("\n\n");
        sb.append("§6[命令导航]\n");
        if (currentPage > 1) {
            sb.append("§a/up book ").append(currentPage - 1).append(" §7- 上一页\n");
        }
        if (currentPage < totalPages) {
            sb.append("§a/up book ").append(currentPage + 1).append(" §7- 下一页\n");
        }
        sb.append("§a/up chest §7- 返回箱子界面\n");
        return sb.toString();
    }

    // 监听箱子UI点击事件
    @EventHandler
    public void onInventoryClick(InventoryClickEvent event) {
        if (!(event.getWhoClicked() instanceof Player)) return;
        Player player = (Player) event.getWhoClicked();
        ItemStack clicked = event.getCurrentItem();
        if (clicked == null || !clicked.hasItemMeta()) return;
        if (!isUIPostsInventory(event.getView().getTitle())) return;
        event.setCancelled(true);
        PlayerState state = getPlayerState(player);
        if (state == null) return;
        String displayName = ChatColor.stripColor(clicked.getItemMeta().getDisplayName());
        if (clicked.getType() == Material.ANVIL && displayName.contains("发帖")) {
            player.closeInventory();
            askForPostInput(player);
        } else if (clicked.getType() == Material.WRITTEN_BOOK && displayName.contains("书本模式")) {
            player.closeInventory();
            openBookGUI(player, 1);
        } else if (clicked.getType() == Material.ARROW) {
            handlePaginationClick(player, state, displayName);
        } else if (clicked.getType() == Material.WRITABLE_BOOK) {
            handlePostClick(player, event.getSlot(), state.page);
        }
    }

    // 监听玩家聊天栏输入帖子或回复
    @EventHandler
    public void onChatInput(AsyncPlayerChatEvent event) {
        Player player = event.getPlayer();
        UUID uuid = player.getUniqueId();
        String message = event.getMessage();
        if (awaitingPostInput.containsKey(uuid)) {
            event.setCancelled(true);
            if ("取消".equalsIgnoreCase(message)) {
                player.sendMessage(ChatColor.RED + "已取消发帖");
                awaitingPostInput.remove(uuid);
                return;
            }
            Bukkit.getScheduler().runTask(this, () -> {
                savePost(player, message);
                awaitingPostInput.remove(uuid);
                openChestGUI(player, 1);
            });
            return;
        }
        if (awaitingReply.containsKey(uuid)) {
            event.setCancelled(true);
            if ("取消".equalsIgnoreCase(message)) {
                player.sendMessage(ChatColor.RED + "已取消留言");
                awaitingReply.remove(uuid);
                return;
            }
            int postId = awaitingReply.get(uuid);
            Bukkit.getScheduler().runTask(this, () -> {
                saveReply(player, postId, message);
                awaitingReply.remove(uuid);
                showBookForPost(player, getPost(postId));
            });
        }
    }

    // 分页获取帖子
    private List<Post> getPostsByPage(int page, int perPage) {
        int offset = (page - 1) * perPage;
        List<Post> posts = new ArrayList<>();
        if (perPage <= 0) return posts;
        String sql = "SELECT id, player, content, time FROM posts ORDER BY id DESC LIMIT ? OFFSET ?";
        try (PreparedStatement ps = dataSource.prepareStatement(sql)) {
            ps.setInt(1, perPage);
            ps.setInt(2, offset);
            try (ResultSet rs = ps.executeQuery()) {
                while (rs.next()) {
                    posts.add(new Post(
                        rs.getInt("id"),
                        rs.getString("player"),
                        rs.getString("content"),
                        rs.getString("time")
                    ));
                }
            }
        } catch (SQLException e) {
            getLogger().log(Level.SEVERE, "查询帖子失败", e);
        }
        return posts;
    }

    // 获取帖子留言
    private List<Reply> getReplies(int postId) {
        if (replyCache.containsKey(postId)) {
            return replyCache.get(postId);
        }
        List<Reply> replies = new ArrayList<>();
        String sql = "SELECT player, content, time FROM replies WHERE post_id = ? ORDER BY id ASC";
        try (PreparedStatement ps = dataSource.prepareStatement(sql)) {
            ps.setInt(1, postId);
            try (ResultSet rs = ps.executeQuery()) {
                while (rs.next()) {
                    replies.add(new Reply(
                        rs.getString("player"),
                        rs.getString("content"),
                        rs.getString("time")
                    ));
                }
            }
        } catch (SQLException e) {
            getLogger().log(Level.SEVERE, "查询留言失败", e);
        }
        replyCache.put(postId, replies);
        return replies;
    }

    // 保存帖子到数据库
    private void savePost(Player player, String content) {
        String time = getCurrentTime();
        String playerName = player.getName();
        if (content.length() > MAX_POST_LENGTH) {
            content = content.substring(0, MAX_POST_LENGTH);
        }
        String sql = "INSERT INTO posts(player, content, time) VALUES(?,?,?)";
        try (PreparedStatement ps = dataSource.prepareStatement(sql)) {
            ps.setString(1, playerName);
            ps.setString(2, content);
            ps.setString(3, time);
            ps.executeUpdate();
            replyCache.clear();
            player.sendMessage(ChatColor.GREEN + "帖子发布成功!");
        } catch (SQLException e) {
            getLogger().log(Level.SEVERE, "保存帖子失败", e);
            player.sendMessage(ChatColor.RED + "发帖失败,请稍后再试");
        }
    }

    // 保存留言到数据库
    private void saveReply(Player player, int postId, String content) {
        String time = getCurrentTime();
        String playerName = player.getName();
        if (content.length() > MAX_REPLY_LENGTH) {
            content = content.substring(0, MAX_REPLY_LENGTH);
        }
        String sql = "INSERT INTO replies(post_id, player, content, time) VALUES(?, ?, ?, ?)";
        try (PreparedStatement ps = dataSource.prepareStatement(sql)) {
            ps.setInt(1, postId);
            ps.setString(2, playerName);
            ps.setString(3, content);
            ps.setString(4, time);
            ps.executeUpdate();
            replyCache.remove(postId);
            player.sendMessage(ChatColor.GREEN + "留言成功!");
        } catch (SQLException e) {
            getLogger().log(Level.SEVERE, "保存留言失败", e);
            player.sendMessage(ChatColor.RED + "留言失败,请稍后再试");
        }
    }

    // 生成箱子内帖子样式
    private ItemStack createPostItem(Post post) {
        ItemStack item = new ItemStack(Material.WRITABLE_BOOK);
        ItemMeta meta = item.getItemMeta();
        meta.setDisplayName(ChatColor.GREEN + "【" + post.player + "】 - " + post.time);
        List<String> lore = new ArrayList<>();
        lore.add(ChatColor.WHITE + truncate(post.content, 25));
        lore.add(ChatColor.GRAY + "点击查看/留言");
        meta.setLore(lore);
        item.setItemMeta(meta);
        return item;
    }

    // 生成箱子按钮
    private ItemStack createButton(Material material, String name) {
        ItemStack item = new ItemStack(material);
        ItemMeta meta = item.getItemMeta();
        meta.setDisplayName(name);
        item.setItemMeta(meta);
        return item;
    }

    // 书本帖子内容格式化显示
    private String formatPostForBook(Post post) {
        StringBuilder sb = new StringBuilder();
        sb.append(ChatColor.BLUE).append("【").append(post.player).append("】 ")
                .append(ChatColor.DARK_GRAY).append(post.time).append("\n");
        sb.append(ChatColor.BLACK).append(post.content).append("\n\n");
        List<Reply> replies = getReplies(post.id);
        if (!replies.isEmpty()) {
            sb.append(ChatColor.DARK_GRAY).append("--- 留言 ---\n");
            int maxReplies = Math.min(replies.size(), 3);
            for (int i = 0; i < maxReplies; i++) {
                Reply reply = replies.get(i);
                sb.append(ChatColor.GRAY).append("• ").append(reply.player)
                        .append(": ").append(ChatColor.DARK_GREEN).append(reply.content)
                        .append("\n");
            }
            if (replies.size() > 3) {
                sb.append(ChatColor.GRAY).append("... 还有")
                        .append(replies.size() - 3).append("条留言\n");
            }
            sb.append("\n").append(ChatColor.DARK_AQUA)
                    .append("点击书本外区域输入留言内容");
        } else {
            sb.append(ChatColor.GRAY).append("暂无留言\n");
        }
        sb.append("\n");
        return sb.toString();
    }

    // 等待玩家发帖
    private void askForPostInput(Player player) {
        player.sendMessage(ChatColor.GOLD + "请在聊天栏输入您的帖子内容(最多100字)");
        player.sendMessage(ChatColor.GRAY + "输入" + ChatColor.RED + "取消" + ChatColor.GRAY + "来取消操作");
        awaitingPostInput.put(player.getUniqueId(), true);
    }

    // 等待玩家留言
    private void askForReplyInput(Player player, int postId) {
        player.sendMessage(ChatColor.GOLD + "请输入您的留言内容(最多50字)");
        player.sendMessage(ChatColor.GRAY + "输入" + ChatColor.RED + "取消" + ChatColor.GRAY + "来取消操作");
        awaitingReply.put(player.getUniqueId(), postId);
    }

    // 展示帖子详情书本
    private void showBookForPost(Player player, Post post) {
        if (post == null) {
            player.sendMessage(ChatColor.RED + "帖子不存在或已被删除");
            return;
        }
        ItemStack book = new ItemStack(Material.WRITTEN_BOOK);
        BookMeta meta = (BookMeta) book.getItemMeta();
        meta.setTitle("帖子详情");
        meta.setAuthor("UIPosts");
        meta.setPages(Collections.singletonList(formatPostForBook(post)));
        book.setItemMeta(meta);
        ItemStack oldItem = player.getInventory().getItemInMainHand().clone();
        player.getInventory().setItemInMainHand(book);
        new BukkitRunnable() {
            @Override
            public void run() {
                player.openBook(book);
                player.getInventory().setItemInMainHand(oldItem);
            }
        }.runTaskLater(this, 2L);
        askForReplyInput(player, post.id);
    }

    // 变量替换,支持PAPI
    private String parsePlaceholders(String text, Player player, int page) {
        String parsed = text
                .replace("{player}", player.getName())
                .replace("{page}", String.valueOf(page))
                .replace("{pages}", String.valueOf(getTotalPages(guiConfig.get("chest").getIntegerList("post_slot").size())));
        if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) {
            try {
                return me.clip.placeholderapi.PlaceholderAPI.setPlaceholders(player, parsed);
            } catch (Exception e) {
                getLogger().warning("PlaceholderAPI 处理失败: " + e.getMessage());
            }
        }
        return parsed;
    }

    private String getCurrentTime() {
        if (cachedTime != null) {
            return cachedTime;
        }
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date());
    }

    private void startScheduledTasks() {
        new BukkitRunnable() {
            @Override
            public void run() {
                updateTimeCache();
            }
        }.runTaskTimerAsynchronously(this, 0, 20 * 60 * 60);
        new BukkitRunnable() {
            @Override
            public void run() {
                replyCache.clear();
            }
        }.runTaskTimerAsynchronously(this, 0, 20 * CACHE_TTL);
    }

    private void updateTimeCache() {
        try {
            cachedTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date());
        } catch (Exception e) {
            getLogger().warning("时间更新失败: " + e.getMessage());
        }
    }

    // 帖子对象结构
    private static class Post {
        final int id;
        final String player;
        final String content;
        final String time;
        Post(int id, String player, String content, String time) {
            this.id = id;
            this.player = player;
            this.content = content;
            this.time = time;
        }
    }
    // 留言对象结构
    private static class Reply {
        final String player;
        final String content;
        final String time;
        Reply(String player, String content, String time) {
            this.player = player;
            this.content = content;
            this.time = time;
        }
    }
    // 玩家分页状态
    private static class PlayerState {
        final String guiType;
        final int page;
        PlayerState(String guiType, int page) {
            this.guiType = guiType;
            this.page = page;
        }
    }

    // 储存玩家GUI状态
    private void storePlayerState(Player player, String type, int page) {
        player.setMetadata("uiposts_state", new FixedMetadataValue(
                this, type + ":" + page
        ));
    }
    // 读取玩家GUI状态
    private PlayerState getPlayerState(Player player) {
        if (!player.hasMetadata("uiposts_state")) return null;
        String[] parts = player.getMetadata("uiposts_state").get(0).asString().split(":");
        if (parts.length != 2) return null;
        return new PlayerState(parts[0], Integer.parseInt(parts[1]));
    }
    // 是否UIPosts界面
    private boolean isUIPostsInventory(String title) {
        return title.contains("帖子广场");
    }
    // 统计总页数
    private int getTotalPages(int perPage) {
        if (perPage <= 0) return 1;
        String sql = "SELECT COUNT(*) AS total FROM posts";
        try (PreparedStatement ps = dataSource.prepareStatement(sql)) {
            try (ResultSet rs = ps.executeQuery()) {
                if (rs.next()) {
                    int totalPosts = rs.getInt("total");
                    return (int) Math.ceil((double) totalPosts / perPage);
                }
            }
        } catch (SQLException e) {
            getLogger().log(Level.SEVERE, "计算总页数失败", e);
        }
        return 1;
    }
    // 是否有下一页
    private boolean hasNextPage(int currentPage, int perPage) {
        int totalPages = getTotalPages(perPage);
        return currentPage < totalPages;
    }
    // 箱子界面 翻页
    private void handlePaginationClick(Player player, PlayerState state, String buttonName) {
        if (buttonName.contains("上一页") && state.page > 1) {
            if ("chest".equals(state.guiType)) {
                openChestGUI(player, state.page - 1);
            } else {
                openBookGUI(player, state.page - 1);
            }
        } else if (buttonName.contains("下一页")) {
            if ("chest".equals(state.guiType)) {
                openChestGUI(player, state.page + 1);
            } else {
                openBookGUI(player, state.page + 1);
            }
        }
    }

    // 点击帖子 进入详情
    private void handlePostClick(Player player, int slot, int page) {
        ConfigurationSection cfg = guiConfig.get("chest");
        if (cfg == null) return;
        List<Integer> postSlots = cfg.getIntegerList("post_slot");
        if (!postSlots.contains(slot)) return;
        int index = postSlots.indexOf(slot);
        int postsPerPage = postSlots.size();
        int offset = (page - 1) * postsPerPage + index;
        Post post = getPostByOffset(offset);
        if (post != null) {
            showBookForPost(player, post);
        }
    }
    // 通过偏移量拿帖子
    private Post getPostByOffset(int offset) {
        String sql = "SELECT id, player, content, time FROM posts ORDER BY id DESC LIMIT 1 OFFSET ?";
        try (PreparedStatement ps = dataSource.prepareStatement(sql)) {
            ps.setInt(1, offset);
            try (ResultSet rs = ps.executeQuery()) {
                if (rs.next()) {
                    return new Post(
                            rs.getInt("id"),
                            rs.getString("player"),
                            rs.getString("content"),
                            rs.getString("time")
                    );
                }
            }
        } catch (SQLException e) {
            getLogger().log(Level.SEVERE, "按偏移量查询帖子失败", e);
        }
        return null;
    }
    // 通过id拿帖子
    private Post getPost(int postId) {
        String sql = "SELECT player, content, time FROM posts WHERE id = ?";
        try (PreparedStatement ps = dataSource.prepareStatement(sql)) {
            ps.setInt(1, postId);
            try (ResultSet rs = ps.executeQuery()) {
                if (rs.next()) {
                    return new Post(
                            postId,
                            rs.getString("player"),
                            rs.getString("content"),
                            rs.getString("time")
                    );
                }
            }
        } catch (SQLException e) {
            getLogger().log(Level.SEVERE, "查询单个帖子失败", e);
        }
        return null;
    }
    // 字符串截断
    private String truncate(String text, int maxLength) {
        return text.length() > maxLength ? text.substring(0, maxLength) + "..." : text;
    }
}

上一篇: GUIFriends下一篇: UIPosts

举报内容

意见反馈