从文件存储到数据库迁移:个人博客系统二次开发实战

标签:JavaWeb、JSP\Servlet、二次开发、数据库、性能优化
来源:取自空间信息与数字技术专业的周同学的期末大作业。
一、引言:为什么要进行二次开发?
在阅读此基于JSP+Servlet+JavaBean的个人博客系统后,我深入分析了该系统的运行情况与架构。虽然系统功能完整,符合大作业要求,但在实际使用过程中,我发现了若干性能瓶颈和架构缺陷,比如,该系统的数据存储使用的是文件存储,这种存储方式导致其数据的共享性和大大降低,提高了代码冗余度。
本文将记录我对这个系统进行的二次开发过程,重点解决原有缺陷,并将数据存储方式从文件系统迁移到MySQL数据库,同时保持原有功能不变。
二、运行环境与原始系统截图
2.1原始运行环境

  • 操作系统:Windows 11
  • JDK版本:Java 21.0.8 LTS
  • Web服务器:Apache Tomcat 10.1
  • 开发工具:IntelliJ IDEA 2025.3

实体类:

点击查看代码Article.java
package com.blog.bean;

import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 文章实体类(用于五个板块)
 */
public class Article implements Serializable {
    private String id;          // 文章ID
    private String category;    // 板块分类
    private String title;       // 标题
    private String content;     // 内容
    private String author;      // 作者(用户名)
    private String createTime;  // 创建时间
    private String updateTime;  // 更新时间

    // 分类常量
    public static final String CATEGORY_NEWS = "news";     // 生活新闻
    public static final String CATEGORY_STUDY = "study";   // 学习记录
    public static final String CATEGORY_SCHOOL = "school"; // 学校生活
    public static final String CATEGORY_PLAN = "plan";     // 规划人生
    public static final String CATEGORY_TRAVEL = "travel"; // 旅行打卡

    // 无参构造方法
    public Article() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        this.createTime = sdf.format(new Date());
        this.updateTime = this.createTime;
        this.id = generateId();
    }

    // 带参构造方法
    public Article(String category, String title, String content, String author) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        this.category = category;
        this.title = title;
        this.content = content;
        this.author = author;
        this.createTime = sdf.format(new Date());
        this.updateTime = this.createTime;
        this.id = generateId();
    }

    // 生成唯一ID
    private String generateId() {
        return "ART_" + System.currentTimeMillis() + "_" + (int) (Math.random() * 1000);
    }

    // Getter和Setter方法
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getCreateTime() {
        return createTime;
    }

    public void setCreateTime(String createTime) {
        this.createTime = createTime;
    }

    public String getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(String updateTime) {
        this.updateTime = updateTime;
    }

    // 获取分类中文名
    public String getCategoryName() {
        switch (category) {
            case CATEGORY_NEWS:
                return "生活新闻";
            case CATEGORY_STUDY:
                return "学习记录";
            case CATEGORY_SCHOOL:
                return "学校生活";
            case CATEGORY_PLAN:
                return "规划人生";
            case CATEGORY_TRAVEL:
                return "旅行打卡";
            default:
                return "未知分类";
        }
    }

    @Override
    public String toString() {
        return id + "::" + category + "::" + title + "::" + content + "::"
                + author + "::" + createTime + "::" + updateTime;
    }
}

点击查看代码Message.java
package com.blog.bean;

import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 留言实体类
 */
public class Message implements Serializable {
    private String id;          // 留言ID
    private String visitor;     // 访客昵称
    private String email;       // 邮箱(可选)
    private String content;     // 留言内容
    private String createTime;  // 留言时间
    private String ipAddress;   // IP地址

    // 无参构造方法
    public Message() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 格式化时间
        this.createTime = sdf.format(new Date());// 格式化当前时间
        this.id = generateId();// 生成唯一ID
    }

    // 带参构造方法
    public Message(String visitor, String email, String content, String ipAddress) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        this.visitor = visitor;
        this.email = email;
        this.content = content;
        this.createTime = sdf.format(new Date());
        this.ipAddress = ipAddress;
        this.id = generateId();
    }

    // 生成唯一ID(简单实现)
    private String generateId() {
        return "MSG_" + System.currentTimeMillis() + "_" + (int) (Math.random() * 1000);
    }

    // Getter和Setter方法
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getVisitor() {
        return visitor;
    }

    public void setVisitor(String visitor) {
        this.visitor = visitor;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getCreateTime() {
        return createTime;
    }

    public void setCreateTime(String createTime) {
        this.createTime = createTime;
    }

    public String getIpAddress() {
        return ipAddress;
    }

    public void setIpAddress(String ipAddress) {
        this.ipAddress = ipAddress;
    }

    @Override
    public String toString() {
        return id + "::" + visitor + "::" + (email != null ? email : "") + "::"
                + content + "::" + createTime + "::" + ipAddress;
    }
}

点击查看代码User.java
package com.blog.bean;

import java.io.Serializable;

/**
 * 用户实体类 - JavaBean
 * 实现Serializable接口以便序列化存储
 */
public class User implements Serializable {
    private String username;  // 用户名
    private String password;  // 密码
    private String nickname;  // 昵称
    private String gender;    // 性别
    private int age;          // 年龄
    private String zodiac;    // 星座

    // 无参构造方法(JavaBean规范要求)
    public User() {
    }

    // 带参构造方法
    public User(String username, String password, String nickname,
                String gender, int age, String zodiac) {
        this.username = username;
        this.password = password;
        this.nickname = nickname;
        this.gender = gender;
        this.age = age;
        this.zodiac = zodiac;
    }

    // Getter和Setter方法
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getZodiac() {
        return zodiac;
    }

    public void setZodiac(String zodiac) {
        this.zodiac = zodiac;
    }

    @Override
    public String toString() {
        return username + "," + password + "," + nickname + ","
                + gender + "," + age + "," + zodiac;
    }
}

数据访问类:

点击查看代码ArticleDAO.java
package com.blog.dao;

import com.blog.bean.Article;

import java.io.*;// 导入文件操作类
import java.text.SimpleDateFormat;// 导入日期格式化类
import java.util.ArrayList;// 导入数组列表类
import java.util.Date;// 导入日期类
import java.util.List;// 导入列表类

/**
 * 文章数据访问对象
 * 负责文章的存储和读取
 */
public class ArticleDAO {
    private static final String FILE_PATH = "D:\\JavaTools\\JavaCode\\MyWeb\\web\\data\\articles.txt";

    /**
     * 保存文章
     *
     * @param article 文章对象
     * @return 保存是否成功
     */
    public boolean saveArticle(Article article) {
        try {
            File file = new File(FILE_PATH);

            // 如果目录不存在,创建目录
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }

            // 使用追加模式写入文件
            FileWriter writer = new FileWriter(file, true);
            writer.write(article.toString() + "\n");
            writer.close();

            System.out.println("文章保存成功: " + article.getId());
            return true;

        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 更新文章
     *
     * @param updatedArticle 更新后的文章对象
     * @return 更新是否成功
     */
    public boolean updateArticle(Article updatedArticle) {
        List<Article> articles = getAllArticles();
        boolean found = false;

        // 更新更新时间
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        updatedArticle.setUpdateTime(sdf.format(new Date()));

        // 查找并更新文章
        for (int i = 0; i < articles.size(); i++) {
            Article article = articles.get(i);
            if (article.getId().equals(updatedArticle.getId())) {
                articles.set(i, updatedArticle);
                found = true;
                break;
            }
        }

        if (!found) {
            return false;
        }

        // 将更新后的文章列表写回文件
        return writeAllArticles(articles);
    }

    /**
     * 删除文章
     *
     * @param articleId 文章ID
     * @return 删除是否成功
     */
    public boolean deleteArticle(String articleId) {
        List<Article> articles = getAllArticles();
        boolean removed = articles.removeIf(article -> article.getId().equals(articleId));

        if (!removed) {
            return false;
        }

        // 重新写入文件
        return writeAllArticles(articles);
    }

    /**
     * 获取所有文章
     *
     * @return 文章列表(按创建时间倒序)
     */
    public List<Article> getAllArticles() {
        List<Article> articles = new ArrayList<>();

        try {
            File file = new File(FILE_PATH);

            // 如果文件不存在,创建空文件
            if (!file.exists()) {
                file.getParentFile().mkdirs();
                file.createNewFile();
                return articles;
            }

            // 读取文件内容
            BufferedReader reader = new BufferedReader(new FileReader(file));
            String line;

            while ((line = reader.readLine()) != null) {
                String[] parts = line.split("::");
                if (parts.length >= 7) {
                    Article article = new Article();
                    article.setId(parts[0]);
                    article.setCategory(parts[1]);
                    article.setTitle(parts[2]);
                    article.setContent(parts[3]);
                    article.setAuthor(parts[4]);
                    article.setCreateTime(parts[5]);
                    article.setUpdateTime(parts[6]);
                    articles.add(article);
                }
            }
            reader.close();

        } catch (IOException e) {
            e.printStackTrace();
        }

        // 按创建时间倒序排序(最新的在前)
        articles.sort((a1, a2) -> a2.getCreateTime().compareTo(a1.getCreateTime()));

        return articles;
    }

    /**
     * 根据分类获取文章
     *
     * @param category 分类
     * @return 该分类的文章列表
     */
    public List<Article> getArticlesByCategory(String category) {
        List<Article> allArticles = getAllArticles();
        List<Article> filteredArticles = new ArrayList<>();

        for (Article article : allArticles) {
            if (article.getCategory().equals(category)) {
                filteredArticles.add(article);
            }
        }

        return filteredArticles;
    }

    /**
     * 根据ID获取文章
     *
     * @param articleId 文章ID
     * @return 文章对象,不存在返回null
     */
    public Article getArticleById(String articleId) {
        List<Article> articles = getAllArticles();

        for (Article article : articles) {
            if (article.getId().equals(articleId)) {
                return article;
            }
        }

        return null;
    }

    /**
     * 获取最新的N篇文章
     *
     * @param limit 数量限制
     * @return 最新的文章列表
     */
    public List<Article> getLatestArticles(int limit) {
        List<Article> allArticles = getAllArticles();
        int count = Math.min(allArticles.size(), limit);

        return allArticles.subList(0, count);
    }

    /**
     * 获取分类名称
     *
     * @param category 分类代码
     * @return 分类中文名
     */
    public static String getCategoryName(String category) {
        switch (category) {
            case Article.CATEGORY_NEWS:
                return "生活新闻";
            case Article.CATEGORY_STUDY:
                return "学习记录";
            case Article.CATEGORY_SCHOOL:
                return "学校生活";
            case Article.CATEGORY_PLAN:
                return "规划人生";
            case Article.CATEGORY_TRAVEL:
                return "旅行打卡";
            default:
                return "未知分类";
        }
    }

    /**
     * 获取所有分类
     *
     * @return 分类数组
     */
    public static String[][] getAllCategories() {
        return new String[][]{
                {Article.CATEGORY_NEWS, "生活新闻"},
                {Article.CATEGORY_STUDY, "学习记录"},
                {Article.CATEGORY_SCHOOL, "学校生活"},
                {Article.CATEGORY_PLAN, "规划人生"},
                {Article.CATEGORY_TRAVEL, "旅行打卡"}
        };
    }

    /**
     * 将文章列表写入文件
     *
     * @param articles 文章列表
     * @return 写入是否成功
     */
    private boolean writeAllArticles(List<Article> articles) {
        try {
            File file = new File(FILE_PATH);
            FileWriter writer = new FileWriter(file);

            for (Article article : articles) {
                writer.write(article.toString() + "\n");
            }
            writer.close();
            return true;

        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }
}
点击查看代码MessageDAO.java
package com.blog.dao;

import com.blog.bean.Message;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 留言数据访问对象
 * 负责留言数据的存储和读取
 */
public class MessageDAO {
    private static final String FILE_PATH = "D:\\JavaTools\\JavaCode\\MyWeb\\web\\data\\message.txt";

    /**
     * 保存留言
     * @param message 留言对象
     * @return 保存是否成功
     */
    public boolean saveMessage(Message message) {
        try {
            File file = new File(FILE_PATH);

            // 如果目录不存在,创建目录
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }

            // 使用追加模式写入文件
            FileWriter writer = new FileWriter(file, true);
            writer.write(message.toString() + "\n");
            writer.close();

            System.out.println("留言保存成功: " + message.getId());
            System.out.println("保存到文件: " + file.getAbsolutePath());
            return true;

        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 获取所有留言
     * @return 留言列表(按时间倒序)
     */
    public List<Message> getAllMessages() {
        List<Message> messages = new ArrayList<>();

        try {
            File file = new File(FILE_PATH);

            // 如果文件不存在,创建空文件
            if (!file.exists()) {
                file.getParentFile().mkdirs();
                file.createNewFile();
                return messages;
            }

            // 读取文件内容
            BufferedReader reader = new BufferedReader(new FileReader(file));
            String line;

            while ((line = reader.readLine()) != null) {
                String[] parts = line.split("::");
                if (parts.length >= 6) {
                    Message message = new Message();
                    message.setId(parts[0]);
                    message.setVisitor(parts[1]);
                    message.setEmail(parts[2].isEmpty() ? null : parts[2]);
                    message.setContent(parts[3]);
                    message.setCreateTime(parts[4]);
                    message.setIpAddress(parts[5]);
                    messages.add(message);
                }
            }
            reader.close();

        } catch (IOException e) {
            e.printStackTrace();
        }

        // 按时间倒序排序(最新的在前)
        messages.sort((m1, m2) -> m2.getCreateTime().compareTo(m1.getCreateTime()));

        return messages;
    }

    /**
     * 根据ID删除留言
     * @param messageId 留言ID
     * @return 删除是否成功
     */
    public boolean deleteMessage(String messageId) {
        List<Message> messages = getAllMessages();
        boolean removed = messages.removeIf(message -> message.getId().equals(messageId));

        if (!removed) {
            return false;
        }

        // 重新写入文件
        try {
            File file = new File(FILE_PATH);
            FileWriter writer = new FileWriter(file);

            for (Message message : messages) {
                writer.write(message.toString() + "\n");
            }
            writer.close();
            return true;

        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 获取留言数量
     * @return 留言总数
     */
    public int getMessageCount() {
        return getAllMessages().size();
    }

    /**
     * 获取文件路径(用于查看)
     * @return 文件绝对路径
     */
    public String getFilePath() {
        return FILE_PATH;
    }
}
点击查看代码UserDAO.java
package com.blog.dao;

import com.blog.bean.User;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 用户数据访问对象
 * 负责user.txt文件的读写操作
 */
public class UserDAO {
    private static String FILE_PATH = "D:\\JavaTools\\JavaCode\\MyWeb\\web\\data\\user.txt";

    /**
     * 设置文件路径
     * @param filePath 文件路径
     */
    public void setFilePath(String filePath) {
        FILE_PATH = filePath;
    }

    /**
     * 用户注册 - 保存用户信息到文件
     * @param user 用户对象
     * @return 注册是否成功
     */
    public boolean register(User user) {
        try {
            // 检查用户名是否已存在
            if (findByUsername(user.getUsername()) != null) {
                return false;
            }

            File file = new File(FILE_PATH);
            // 如果目录不存在,创建目录
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }

            // 使用追加模式写入文件
            FileWriter writer = new FileWriter(file, true);
            writer.write(user.toString() + "\n");
            writer.close();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 用户登录验证
     * @param username 用户名
     * @param password 密码
     * @return 验证通过的用户对象,失败返回null
     */
    public User login(String username, String password) {
        List<User> users = getAllUsers();

        for (User user : users) {
            if (user.getUsername().equals(username) &&
                    user.getPassword().equals(password)) {
                return user;
            }
        }
        return null;
    }

    /**
     * 根据用户名查找用户
     * @param username 用户名
     * @return 用户对象,不存在返回null
     */
    public User findByUsername(String username) {
        List<User> users = getAllUsers();

        for (User user : users) {
            if (user.getUsername().equals(username)) {
                return user;
            }
        }
        return null;
    }

    /**
     * 更新用户信息
     * @param updatedUser 更新后的用户对象
     * @return 更新是否成功
     */
    public boolean updateUser(User updatedUser) {
        List<User> users = getAllUsers();
        boolean found = false;

        // 查找并更新用户
        for (int i = 0; i < users.size(); i++) {
            User user = users.get(i);
            if (user.getUsername().equals(updatedUser.getUsername())) {
                // 保留原来的密码
                updatedUser.setPassword(user.getPassword());
                users.set(i, updatedUser);
                found = true;
                break;
            }
        }

        if (!found) {
            return false;
        }

        // 将更新后的用户列表写回文件
        try {
            File file = new File(FILE_PATH);
            FileWriter writer = new FileWriter(file);

            for (User user : users) {
                writer.write(user.toString() + "\n");
            }
            writer.close();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 更新用户密码
     * @param username 用户名
     * @param newPassword 新密码
     * @return 更新是否成功
     */
    public boolean updatePassword(String username, String newPassword) {
        List<User> users = getAllUsers();
        boolean found = false;

        // 查找并更新用户密码
        for (int i = 0; i < users.size(); i++) {
            User user = users.get(i);
            if (user.getUsername().equals(username)) {
                user.setPassword(newPassword);
                users.set(i, user);
                found = true;
                break;
            }
        }

        if (!found) {
            return false;
        }

        // 将更新后的用户列表写回文件
        try {
            File file = new File(FILE_PATH);
            FileWriter writer = new FileWriter(file);

            for (User user : users) {
                writer.write(user.toString() + "\n");
            }
            writer.close();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 获取所有用户列表
     * @return 用户列表
     */
    public List<User> getAllUsers() {
        List<User> users = new ArrayList<>();

        try {
            File file = new File(FILE_PATH);

            // 如果文件不存在,创建空文件
            if (!file.exists()) {
                file.getParentFile().mkdirs(); // 创建目录
                file.createNewFile();          // 创建文件
                return users;
            }

            // 读取文件内容
            BufferedReader reader = new BufferedReader(new FileReader(file));
            String line;

            while ((line = reader.readLine()) != null) {
                String[] parts = line.split(",");
                if (parts.length >= 6) {
                    User user = new User(
                            parts[0], parts[1], parts[2],
                            parts[3], Integer.parseInt(parts[4]), parts[5]
                    );
                    users.add(user);
                }
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return users;
    }
}

控制器类:

点击查看代码LoginServlet.java
package com.blog.servlet;

import com.blog.bean.User;
import com.blog.dao.UserDAO;
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;
import java.io.IOException;

/**
 * 登录Servlet
 * 处理用户登录请求
 */
@WebServlet("/login")
public class LoginServlet extends HttpServlet {

    private UserDAO userDAO = new UserDAO();

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 获取表单参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        // 验证登录
        User user = userDAO.login(username, password);

        if (user != null) {
            // 登录成功,将用户信息存入Session
            HttpSession session = request.getSession();
            session.setAttribute("user", user);
            session.setAttribute("loginStatus", "success");

            // 重定向到主页
            response.sendRedirect("home.jsp");
        } else {
            // 登录失败,设置错误信息
            request.setAttribute("errorMsg", "用户名或密码错误!");
            request.getRequestDispatcher("login.jsp").forward(request, response);
        }
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // GET请求直接转发到登录页面
        request.getRequestDispatcher("login.jsp").forward(request, response);
    }
}
点击查看代码RegisterServlet.java
package com.blog.servlet;

import com.blog.bean.User;
import com.blog.dao.UserDAO;
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;
import java.io.IOException;

/**
 * 注册Servlet
 * 处理用户注册请求
 */
@WebServlet("/register")
public class RegisterServlet extends HttpServlet {

    private UserDAO userDAO = new UserDAO();

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 获取表单参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String nickname = request.getParameter("nickname");
        String gender = request.getParameter("gender");
        String ageStr = request.getParameter("age");
        String zodiac = request.getParameter("zodiac");

        // 验证参数
        if (username == null || username.trim().isEmpty() ||
                password == null || password.trim().isEmpty()) {
            request.setAttribute("errorMsg", "用户名和密码不能为空!");
            request.getRequestDispatcher("register.jsp").forward(request, response);
            return;
        }

        int age = 20; // 默认年龄
        try {
            age = Integer.parseInt(ageStr);
        } catch (NumberFormatException e) {
            // 保持默认值
        }

        // 创建用户对象
        User user = new User(username, password, nickname, gender, age, zodiac);

        // 注册用户
        boolean success = userDAO.register(user);

        if (success) {
            // 注册成功,跳转到登录页面
            request.setAttribute("successMsg", "注册成功,请登录!");
            request.getRequestDispatcher("login.jsp").forward(request, response);
        } else {
            // 注册失败(用户名已存在)
            request.setAttribute("errorMsg", "用户名已存在!");
            request.getRequestDispatcher("register.jsp").forward(request, response);
        }
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // GET请求直接转发到注册页面
        request.getRequestDispatcher("register.jsp").forward(request, response);
    }
}
点击查看代码LogoutServlet.java
package com.blog.servlet;

import jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;
import java.io.IOException;

/**
 * 注销Servlet
 * 处理用户注销请求
 */
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 使当前Session失效
        HttpSession session = request.getSession();
        session.invalidate();

        // 重定向到首页
        response.sendRedirect("index.jsp");
    }
}
点击查看代码ArticleServlet.java
package com.blog.servlet;

import com.blog.bean.Article;
import com.blog.bean.User;
import com.blog.dao.ArticleDAO;
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;
import java.io.IOException;
import java.util.List;

/**
 * 文章管理Servlet
 * 处理文章的增删查改
 */
@WebServlet("/article")
public class ArticleServlet extends HttpServlet {

    private ArticleDAO articleDAO = new ArticleDAO();

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 检查用户是否已登录
        HttpSession session = request.getSession(false);
        if (session == null || session.getAttribute("user") == null) {
            response.sendRedirect("login.jsp");
            return;
        }

        User user = (User) session.getAttribute("user");
        String action = request.getParameter("action");

        if ("create".equals(action)) {
            doCreate(request, response, user);
        } else if ("update".equals(action)) {
            doUpdate(request, response, user);
        }
    }

    /**
     * 处理GET请求
     * 处理文章列表、查看、编辑、删除请求
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String action = request.getParameter("action");
        String category = request.getParameter("category");

        if ("delete".equals(action)) {
            doDelete(request, response);
        } else if ("edit".equals(action)) {
            doEdit(request, response);
        } else if ("list".equals(action)) {
            doList(request, response, category);
        } else if ("view".equals(action)) {
            doView(request, response);
        } else {
            // 默认显示所有分类的文章列表
            request.setAttribute("categories", ArticleDAO.getAllCategories());
            request.getRequestDispatcher("articleList.jsp").forward(request, response);
        }
    }

    /**
     * 创建文章
     */
    private void doCreate(HttpServletRequest request, HttpServletResponse response, User user)
            throws ServletException, IOException {

        String category = request.getParameter("category");
        String title = request.getParameter("title");
        String content = request.getParameter("content");

        // 验证参数
        if (category == null || category.trim().isEmpty() ||
                title == null || title.trim().isEmpty() ||
                content == null || content.trim().isEmpty()) {

            request.setAttribute("errorMsg", "请填写完整信息!");
            request.setAttribute("categories", ArticleDAO.getAllCategories());
            request.getRequestDispatcher("articleEdit.jsp").forward(request, response);
            return;
        }

        // 创建文章对象
        Article article = new Article(category, title.trim(), content.trim(), user.getUsername());

        // 保存文章
        boolean success = articleDAO.saveArticle(article);

        if (success) {
            request.setAttribute("successMsg", "文章发布成功!");
            response.sendRedirect("article?action=list&category=" + category);
        } else {
            request.setAttribute("errorMsg", "发布失败,请重试!");
            request.setAttribute("categories", ArticleDAO.getAllCategories());
            request.getRequestDispatcher("articleEdit.jsp").forward(request, response);
        }
    }

    /**
     * 更新文章
     */
    private void doUpdate(HttpServletRequest request, HttpServletResponse response, User user)
            throws ServletException, IOException {

        String articleId = request.getParameter("id");
        String category = request.getParameter("category");
        String title = request.getParameter("title");
        String content = request.getParameter("content");

        // 验证参数
        if (articleId == null || articleId.trim().isEmpty() ||
                category == null || category.trim().isEmpty() ||
                title == null || title.trim().isEmpty() ||
                content == null || content.trim().isEmpty()) {

            request.setAttribute("errorMsg", "请填写完整信息!");
            request.setAttribute("categories", ArticleDAO.getAllCategories());
            request.setAttribute("article", articleDAO.getArticleById(articleId));
            request.getRequestDispatcher("articleEdit.jsp").forward(request, response);
            return;
        }

        // 获取原文章并检查权限
        Article article = articleDAO.getArticleById(articleId);
        if (article == null) {
            request.setAttribute("errorMsg", "文章不存在!");
            request.getRequestDispatcher("articleList.jsp").forward(request, response);
            return;
        }

        // 检查是否是作者本人或管理员(这里简单实现,只检查作者)
        if (!article.getAuthor().equals(user.getUsername())) {
            request.setAttribute("errorMsg", "无权修改此文章!");
            request.getRequestDispatcher("articleList.jsp").forward(request, response);
            return;
        }

        // 更新文章信息
        article.setCategory(category);
        article.setTitle(title.trim());
        article.setContent(content.trim());

        // 更新文章
        boolean success = articleDAO.updateArticle(article);

        if (success) {
            request.setAttribute("successMsg", "文章更新成功!");
            response.sendRedirect("article?action=list&category=" + category);
        } else {
            request.setAttribute("errorMsg", "更新失败,请重试!");
            request.setAttribute("categories", ArticleDAO.getAllCategories());
            request.setAttribute("article", article);
            request.getRequestDispatcher("articleEdit.jsp").forward(request, response);
        }
    }

    /**
     * 删除文章
     */
    public void doDelete(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 检查用户是否已登录
        HttpSession session = request.getSession(false);
        if (session == null || session.getAttribute("user") == null) {
            response.sendRedirect("login.jsp");
            return;
        }

        User user = (User) session.getAttribute("user");
        String articleId = request.getParameter("id");
        String category = request.getParameter("category");

        if (articleId != null && !articleId.isEmpty()) {
            // 获取文章并检查权限
            Article article = articleDAO.getArticleById(articleId);
            if (article != null && article.getAuthor().equals(user.getUsername())) {
                boolean success = articleDAO.deleteArticle(articleId);
                if (success) {
                    request.setAttribute("successMsg", "文章删除成功!");
                } else {
                    request.setAttribute("errorMsg", "删除失败,请重试!");
                }
            } else {
                request.setAttribute("errorMsg", "无权删除此文章!");
            }
        }

        // 重定向到文章列表
        String redirectUrl = "article?action=list";
        if (category != null && !category.isEmpty()) {
            redirectUrl += "&category=" + category;
        }
        response.sendRedirect(redirectUrl);
    }

    /**
     * 编辑文章页面
     */
    private void doEdit(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 检查用户是否已登录
        HttpSession session = request.getSession(false);
        if (session == null || session.getAttribute("user") == null) {
            response.sendRedirect("login.jsp");
            return;
        }

        String articleId = request.getParameter("id");

        if (articleId != null && !articleId.isEmpty()) {
            // 编辑现有文章
            Article article = articleDAO.getArticleById(articleId);
            if (article != null) {
                request.setAttribute("article", article);
            }
        }

        request.setAttribute("categories", ArticleDAO.getAllCategories());
        request.getRequestDispatcher("articleEdit.jsp").forward(request, response);
    }

    /**
     * 查看文章详情
     */
    private void doView(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String articleId = request.getParameter("id");

        if (articleId != null && !articleId.isEmpty()) {
            Article article = articleDAO.getArticleById(articleId);
            if (article != null) {
                request.setAttribute("article", article);
                request.getRequestDispatcher("articleView.jsp").forward(request, response);
                return;
            }
        }

        request.setAttribute("errorMsg", "文章不存在!");
        request.getRequestDispatcher("articleList.jsp").forward(request, response);
    }

    /**
     * 显示文章列表
     */
    private void doList(HttpServletRequest request, HttpServletResponse response, String category)
            throws ServletException, IOException {

        if (category != null && !category.isEmpty()) {
            // 显示指定分类的文章
            List<Article> articles = articleDAO.getArticlesByCategory(category);
            request.setAttribute("articles", articles);
            request.setAttribute("currentCategory", category);
            request.setAttribute("categoryName", ArticleDAO.getCategoryName(category));
        } else {
            // 显示所有文章
            List<Article> articles = articleDAO.getAllArticles();
            request.setAttribute("articles", articles);
        }

        request.setAttribute("categories", ArticleDAO.getAllCategories());
        request.getRequestDispatcher("articleList.jsp").forward(request, response);
    }
}
点击查看代码MessageServlet.java
package com.blog.servlet;

import com.blog.bean.Message;
import com.blog.dao.MessageDAO;
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;
import java.io.IOException;

/**
 * 留言处理Servlet
 * 处理留言的提交、查看和删除
 */
@WebServlet("/message")
public class MessageServlet extends HttpServlet {

    private MessageDAO messageDAO = new MessageDAO();

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 获取表单参数
        String visitor = request.getParameter("visitor");
        String email = request.getParameter("email");
        String content = request.getParameter("content");

        // 验证参数
        if (visitor == null || visitor.trim().isEmpty() ||
                content == null || content.trim().isEmpty()) {
            request.setAttribute("errorMsg", "昵称和留言内容不能为空!");
            forwardToMessagePage(request, response);
            return;
        }

        // 获取客户端IP地址
        String ipAddress = getClientIpAddress(request);

        // 创建留言对象
        Message message = new Message(visitor.trim(),
                email != null ? email.trim() : null,
                content.trim(),
                ipAddress);

        // 保存留言
        boolean success = messageDAO.saveMessage(message);

        if (success) {
            request.setAttribute("successMsg", "留言成功!感谢您的反馈。");
        } else {
            request.setAttribute("errorMsg", "留言失败,请稍后重试!");
        }

        // 重新加载留言列表
        request.setAttribute("messages", messageDAO.getAllMessages());
        forwardToMessagePage(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String action = request.getParameter("action");

        if ("delete".equals(action)) {
            // 删除留言
            doDelete(request, response);
        } else {
            // 查看留言列表
            request.setAttribute("messages", messageDAO.getAllMessages());
            forwardToMessagePage(request, response);
        }
    }

    /**
     * 处理删除请求
     */
    public void doDelete(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String messageId = request.getParameter("id");

        if (messageId != null && !messageId.isEmpty()) {
            boolean success = messageDAO.deleteMessage(messageId);
            if (success) {
                request.setAttribute("successMsg", "留言删除成功!");
            } else {
                request.setAttribute("errorMsg", "删除失败,留言不存在!");
            }
        }

        // 重新加载留言列表
        request.setAttribute("messages", messageDAO.getAllMessages());
        forwardToMessagePage(request, response);
    }

    /**
     * 转发到留言页面
     */
    private void forwardToMessagePage(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 检查是否是AJAX请求
        if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
            // AJAX请求,返回JSON响应
            response.setContentType("application/json");
            response.setCharacterEncoding("UTF-8");

            boolean success = request.getAttribute("errorMsg") == null;
            String message = success ?
                    (String) request.getAttribute("successMsg") :
                    (String) request.getAttribute("errorMsg");

            response.getWriter().write("{\"success\":" + success + ",\"message\":\"" + message + "\"}");
        } else {
            // 普通请求,转发到JSP页面
            request.getRequestDispatcher("message.jsp").forward(request, response);
        }
    }

    /**
     * 获取客户端IP地址
     */
    private String getClientIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        return ip;
    }
}

点击查看代码UpdateProfileServlet.java
package com.blog.servlet;

import com.blog.bean.User;
import com.blog.dao.UserDAO;
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;
import java.io.IOException;

/**
 * 更新个人信息Servlet
 * 处理用户信息修改请求
 */
@WebServlet("/updateProfile")
public class UpdateProfileServlet extends HttpServlet {

    private UserDAO userDAO = new UserDAO();

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 检查用户是否已登录
        HttpSession session = request.getSession(false);
        if (session == null || session.getAttribute("user") == null) {
            response.sendRedirect("login.jsp");
            return;
        }

        User currentUser = (User) session.getAttribute("user");

        // 获取表单参数
        String nickname = request.getParameter("nickname");
        String gender = request.getParameter("gender");
        String ageStr = request.getParameter("age");
        String zodiac = request.getParameter("zodiac");
        String password = request.getParameter("password");
        String confirmPassword = request.getParameter("confirmPassword");

        // 验证密码(如果需要修改密码)
        if (password != null && !password.trim().isEmpty()) {
            if (!password.equals(confirmPassword)) {
                request.setAttribute("errorMsg", "两次输入的密码不一致!");
                request.getRequestDispatcher("editProfile.jsp").forward(request, response);
                return;
            }

            // 更新密码
            boolean passwordUpdated = userDAO.updatePassword(currentUser.getUsername(), password);
            if (passwordUpdated) {
                // 更新session中的用户对象密码
                currentUser.setPassword(password);
            }
        }

        int age = currentUser.getAge(); // 默认保持原年龄
        try {
            age = Integer.parseInt(ageStr);
        } catch (NumberFormatException e) {
            // 保持原值
        }

        // 创建更新后的用户对象
        User updatedUser = new User(
                currentUser.getUsername(),
                currentUser.getPassword(), // 密码可能已更新
                nickname,
                gender,
                age,
                zodiac
        );

        // 更新用户信息
        boolean success = userDAO.updateUser(updatedUser);

        if (success) {
            // 更新session中的用户对象
            session.setAttribute("user", updatedUser);

            // 设置成功消息
            request.setAttribute("successMsg", "个人信息更新成功!");
            request.getRequestDispatcher("home.jsp").forward(request, response);
        } else {
            request.setAttribute("errorMsg", "更新失败,请重试!");
            request.getRequestDispatcher("editProfile.jsp").forward(request, response);
        }
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // GET请求直接转发到编辑页面
        request.getRequestDispatcher("editProfile.jsp").forward(request, response);
    }
}

视图层:

点击查看代码index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
  // 检查用户是否已登录
  HttpSession userSession = request.getSession(false);
  boolean isLoggedIn = (userSession != null && userSession.getAttribute("user") != null);
%>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>个人博客 - 首页</title>
  <link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class="container">
  <!-- 导航栏 -->
  <header class="header">
    <nav class="nav">
      <div class="logo">
        <a href="index.jsp">个人博客</a>
      </div>
      <ul class="nav-list">
        <li><a href="home.jsp">个人首页</a></li>
        <li><a href="article?action=list&category=news">生活新闻</a></li>
        <li><a href="article?action=list&category=study">学习记录</a></li>
        <li><a href="article?action=list&category=school">学校生活</a></li>
        <li><a href="article?action=list&category=plan">规划人生</a></li>
        <li><a href="article?action=list&category=travel">旅行打卡</a></li>
        <li><a href="message.jsp">访客留言</a></li>
        <% if (isLoggedIn) { %>
        <li class="user-info-nav">
          <a href="#" class="user-name">
            <%= ((com.blog.bean.User)userSession.getAttribute("user")).getNickname() %>
          </a>
          <div class="user-menu">
            <a href="home.jsp">个人主页</a>
            <a href="article?action=list">文章管理</a>
            <a href="editProfile.jsp">编辑资料</a>
            <a href="logout">退出</a>
          </div>
        </li>
        <% } else { %>
        <li><a href="login.jsp">登录</a></li>
        <li><a href="register.jsp">注册</a></li>
        <% } %>
      </ul>
    </nav>
  </header>

  <!-- 主要内容 -->
  <main class="main-content">
    <div class="welcome-section">
      <h1>欢迎来到我的个人博客</h1>
      <p>记录生活,分享思考,规划未来</p>

      <% if (!isLoggedIn) { %>
      <div class="auth-buttons">
        <a href="login.jsp" class="btn btn-login">登录</a>
        <a href="register.jsp" class="btn btn-register">注册</a>
      </div>
      <p class="auth-tip">请先登录以访问完整内容</p>
      <% } else { %>
      <div class="welcome-user">
        <p>欢迎回来,<strong><%= ((com.blog.bean.User)userSession.getAttribute("user")).getNickname() %></strong>!</p>
        <a href="home.jsp" class="btn btn-home">进入个人主页</a>
      </div>
      <% } %>
    </div>

    <div class="preview">
      <h2>博客内容预览</h2>
      <div class="preview-content">
        <div class="preview-item">
          <h3>生活新闻</h3>
          <p>只要追求幸福和自我的完美,选择适合我们的生活。你会发现即使在每句可以表示爱,对生活的经历也会更加清晰。只要能有希望就会有完美的行为。</p>
        </div>
        <div class="preview-item">
          <h3>学习记录</h3>
          <p>有时候,我们只是需要换一扇窗户看风景,生活有微笑有害羞,不想说不等于无言,只有心里明了,也许永远远离沉默,选择享受...</p>
        </div>
        <div class="preview-item">
          <h3>旅行打卡</h3>
          <p>越过江河,越晚就不难,越现在还年轻。还可以走很长很长的路,还能听说很深很深的思念。去寻找那些曾出现在梦境中的路径,山口与田野。</p>
        </div>
      </div>
    </div>
  </main>

  <footer class="footer">
    <p>&copy; 2025 个人博客系统 | 基于JSP+Servlet+JavaBean开发</p>
  </footer>
</div>
</body>
</html>
点击查看代码home.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
  // 检查用户是否已登录
  HttpSession userSession = request.getSession(false);
  com.blog.bean.User user = null;

  if (userSession != null) {
    user = (com.blog.bean.User) userSession.getAttribute("user");
  }

  // 如果未登录,重定向到登录页面
  if (user == null) {
    response.sendRedirect("login.jsp");
    return;
  }
%>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title><%= user.getNickname() %>的个人博客</title>
  <link rel="stylesheet" href="css/style.css">
  <style>
    /* 用户个人信息卡片 */
    .user-profile {
      display: flex;
      align-items: center;
      margin-bottom: 20px;
      padding: 20px;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      color: white;
      border-radius: 10px;
    }
    /* 用户头像 */
    .user-avatar {
      width: 100px;
      height: 100px;
      border-radius: 50%;
      background-color: white;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 40px;
      margin-right: 20px;
      color: #764ba2;
    }
    /* 用户个人信息 */
    .user-info h2 {
      margin: 0 0 10px 0;
      font-size: 1.2em;
    }
    /* 用户详细信息 */
    .user-details {
      display: flex;
      gap: 15px;
      flex-wrap: wrap;
    }
    /* 用户详细信息项 */
    .user-details span {
      background: rgba(255, 255, 255, 0.2);
      padding: 5px 10px;
      border-radius: 15px;
    }
    /* 博客内容区域 */
    .blog-content {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
      gap: 20px;
      margin-top: 20px;
    }
    /* 博客分类区域 */
    .blog-section {
      background: white;
      padding: 20px;
      border-radius: 10px;
      box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
    }
    /* 博客分类标题 */
    .blog-section h3 {
      color: #333;
      border-bottom: 2px solid #667eea;
      padding-bottom: 10px;
      margin-bottom: 15px;
    }
    .blog-section p {
      line-height: 1.6;
      color: #666;
    }
    .article-meta {
      color: #999;
      font-size: 0.85em;
      margin: 10px 0;
    }
    .article-actions {
      display: flex;
      gap: 10px;
      margin-top: 15px;
    }
    .btn-small {
      padding: 6px 12px;
      font-size: 0.9em;
    }
    .no-content {
      color: #999;
      font-style: italic;
    }
    .view-all-link {
      display: block;
      text-align: right;
      margin-top: 10px;
      color: #667eea;
      text-decoration: none;
    }
    .view-all-link:hover {
      text-decoration: underline;
    }
  </style>
</head>
<body>
<div class="container">
  <!-- 导航栏 -->
  <header class="header">
    <nav class="nav">
      <div class="logo">
        <a href="index.jsp">个人博客</a>
      </div>
      <ul class="nav-list">
        <li><a href="#home" class="active">个人首页</a></li>
        <li><a href="article?action=list&category=news">生活新闻</a></li>
        <li><a href="article?action=list&category=study">学习记录</a></li>
        <li><a href="article?action=list&category=school">学校生活</a></li>
        <li><a href="article?action=list&category=plan">规划人生</a></li>
        <li><a href="article?action=list&category=travel">旅行打卡</a></li>
        <li><a href="message.jsp">访客留言</a></li>
        <li class="user-info-nav">
          <a href="#" class="user-name">
            <%= user.getNickname() %>
          </a>
          <div class="user-menu">
            <a href="home.jsp">个人主页</a>
            <a href="article?action=list">文章管理</a>
            <a href="editProfile.jsp">编辑资料</a>
            <a href="logout">退出登录</a>
          </div>
        </li>
      </ul>
    </nav>
  </header>

  <!-- 主要内容 -->
  <main class="main-content">
    <!-- 用户信息区域 -->
    <section id="home" class="user-profile">
      <div class="user-avatar">
        <%= user.getNickname().charAt(0) %>
      </div>
      <div class="user-info">
        <h2><%= user.getNickname() %></h2>
        <p>Nothing is impossible for a determined person.</p>
        <div class="user-details">
          <span><%= user.getGender() %></span>
          <span><%= user.getAge() %>岁</span>
          <span><%= user.getZodiac() %></span>
        </div>
      </div>
    </section>

    <!-- 博客内容区域 -->
    <div class="blog-content">
      <!-- 生活新闻 -->
      <section id="news" class="blog-section">
        <h3>生活新闻</h3>
        <%
          com.blog.dao.ArticleDAO articleDAO = new com.blog.dao.ArticleDAO();
          java.util.List<com.blog.bean.Article> newsArticles = articleDAO.getArticlesByCategory("news");

          if (!newsArticles.isEmpty()) {
            com.blog.bean.Article latestNews = newsArticles.get(0);
        %>
        <p><strong><%= latestNews.getTitle() %></strong></p>
        <p><%= latestNews.getContent().length() > 150 ?
                latestNews.getContent().substring(0, 150) + "..." : latestNews.getContent() %></p>
        <div class="article-meta">
          发布于:<%= latestNews.getCreateTime() %>
          <% if (!latestNews.getCreateTime().equals(latestNews.getUpdateTime())) { %>
          <br>更新于:<%= latestNews.getUpdateTime() %>
          <% } %>
        </div>
        <div class="article-actions">
          <a href="article?action=view&id=<%= latestNews.getId() %>" class="btn btn-small">阅读更多</a>
          <a href="article?action=list&category=news" class="btn btn-small">查看全部</a>
        </div>
        <%
        } else {
        %>
        <p class="no-content">只要追求幸福和自我的完美,选择适合我们的生活。你会发现即使在每句可以表示爱,对生活的经历也会更加清晰。只要能有希望就会有完美的行为。</p>
        <div class="article-actions">
          <a href="article?action=list&category=news" class="btn btn-small">查看生活新闻</a>
          <% if (user != null) { %>
          <a href="article?action=edit" class="btn btn-small">发布新闻</a>
          <% } %>
        </div>
        <%
          }
        %>
      </section>

      <!-- 学习记录 -->
      <section id="study" class="blog-section">
        <h3>学习记录</h3>
        <%
          java.util.List<com.blog.bean.Article> studyArticles = articleDAO.getArticlesByCategory("study");

          if (!studyArticles.isEmpty()) {
            com.blog.bean.Article latestStudy = studyArticles.get(0);
        %>
        <p><strong><%= latestStudy.getTitle() %></strong></p>
        <p><%= latestStudy.getContent().length() > 150 ?
                latestStudy.getContent().substring(0, 150) + "..." : latestStudy.getContent() %></p>
        <div class="article-meta">
          发布于:<%= latestStudy.getCreateTime() %>
          <% if (!latestStudy.getCreateTime().equals(latestStudy.getUpdateTime())) { %>
          <br>更新于:<%= latestStudy.getUpdateTime() %>
          <% } %>
        </div>
        <div class="article-actions">
          <a href="article?action=view&id=<%= latestStudy.getId() %>" class="btn btn-small">阅读更多</a>
          <a href="article?action=list&category=study" class="btn btn-small">查看全部</a>
        </div>
        <%
        } else {
        %>
        <p class="no-content">有时候,我们只是需要换一扇窗户看风景,生活有微笑有害羞,不想说不等于无言,只有心里明了,也许永远远离沉默,选择享受。沉默多了,承受也就越多,不知道已经打开另一扇窗户,放宽心情。也许就是另一个轻松美丽的世界,从一扇窗户到另一扇窗户,从一个世界到另一个世界,需要的是一个勇气、一抹心境。只有长生活本着理想的生活,我们才能成为真正充满的人。人生就是一场漫长时光,就看你能够迅速多久,坚持多久。</p>
        <div class="article-actions">
          <a href="article?action=list&category=study" class="btn btn-small">查看学习记录</a>
          <% if (user != null) { %>
          <a href="article?action=edit" class="btn btn-small">发布记录</a>
          <% } %>
        </div>
        <%
          }
        %>
      </section>

      <!-- 学校生活 -->
      <section id="school" class="blog-section">
        <h3>学校生活</h3>
        <%
          java.util.List<com.blog.bean.Article> schoolArticles = articleDAO.getArticlesByCategory("school");

          if (!schoolArticles.isEmpty()) {
            com.blog.bean.Article latestSchool = schoolArticles.get(0);
        %>
        <p><strong><%= latestSchool.getTitle() %></strong></p>
        <p><%= latestSchool.getContent().length() > 150 ?
                latestSchool.getContent().substring(0, 150) + "..." : latestSchool.getContent() %></p>
        <div class="article-meta">
          发布于:<%= latestSchool.getCreateTime() %>
          <% if (!latestSchool.getCreateTime().equals(latestSchool.getUpdateTime())) { %>
          <br>更新于:<%= latestSchool.getUpdateTime() %>
          <% } %>
        </div>
        <div class="article-actions">
          <a href="article?action=view&id=<%= latestSchool.getId() %>" class="btn btn-small">阅读更多</a>
          <a href="article?action=list&category=school" class="btn btn-small">查看全部</a>
        </div>
        <%
        } else {
        %>
        <p class="no-content">有时候,我们只是需要换一扇窗户看风景,生活有微笑有害羞,不想说不等于无言,只有心里明了,也许永远远离沉默,选择享受。</p>
        <div class="article-actions">
          <a href="article?action=list&category=school" class="btn btn-small">查看学校生活</a>
          <% if (user != null) { %>
          <a href="article?action=edit" class="btn btn-small">发布内容</a>
          <% } %>
        </div>
        <%
          }
        %>
      </section>

      <!-- 规划人生 -->
      <section id="plan" class="blog-section">
        <h3>规划人生</h3>
        <%
          java.util.List<com.blog.bean.Article> planArticles = articleDAO.getArticlesByCategory("plan");

          if (!planArticles.isEmpty()) {
            com.blog.bean.Article latestPlan = planArticles.get(0);
        %>
        <p><strong><%= latestPlan.getTitle() %></strong></p>
        <p><%= latestPlan.getContent().length() > 150 ?
                latestPlan.getContent().substring(0, 150) + "..." : latestPlan.getContent() %></p>
        <div class="article-meta">
          发布于:<%= latestPlan.getCreateTime() %>
          <% if (!latestPlan.getCreateTime().equals(latestPlan.getUpdateTime())) { %>
          <br>更新于:<%= latestPlan.getUpdateTime() %>
          <% } %>
        </div>
        <div class="article-actions">
          <a href="article?action=view&id=<%= latestPlan.getId() %>" class="btn btn-small">阅读更多</a>
          <a href="article?action=list&category=plan" class="btn btn-small">查看全部</a>
        </div>
        <%
        } else {
        %>
        <p class="no-content">人生规划是自我成长的重要部分。我们需要设定目标,制定计划,并坚持执行。无论是短期的小目标,还是长期的人生理想,每一步都值得认真对待。</p>
        <div class="article-actions">
          <a href="article?action=list&category=plan" class="btn btn-small">查看人生规划</a>
          <% if (user != null) { %>
          <a href="article?action=edit" class="btn btn-small">发布规划</a>
          <% } %>
        </div>
        <%
          }
        %>
      </section>

      <!-- 旅行打卡 -->
      <section id="travel" class="blog-section">
        <h3>旅行打卡</h3>
        <%
          java.util.List<com.blog.bean.Article> travelArticles = articleDAO.getArticlesByCategory("travel");

          if (!travelArticles.isEmpty()) {
            com.blog.bean.Article latestTravel = travelArticles.get(0);
        %>
        <p><strong><%= latestTravel.getTitle() %></strong></p>
        <p><%= latestTravel.getContent().length() > 150 ?
                latestTravel.getContent().substring(0, 150) + "..." : latestTravel.getContent() %></p>
        <div class="article-meta">
          发布于:<%= latestTravel.getCreateTime() %>
          <% if (!latestTravel.getCreateTime().equals(latestTravel.getUpdateTime())) { %>
          <br>更新于:<%= latestTravel.getUpdateTime() %>
          <% } %>
        </div>
        <div class="article-actions">
          <a href="article?action=view&id=<%= latestTravel.getId() %>" class="btn btn-small">阅读更多</a>
          <a href="article?action=list&category=travel" class="btn btn-small">查看全部</a>
        </div>
        <%
        } else {
        %>
        <p class="no-content">越过江河,越现在还年轻。还可以走很长很长的路,还能听说很深很深的思念。去寻找那些曾出现在梦境中的路径,山口与田野。</p>
        <div class="article-actions">
          <a href="article?action=list&category=travel" class="btn btn-small">查看旅行打卡</a>
          <% if (user != null) { %>
          <a href="article?action=edit" class="btn btn-small">发布游记</a>
          <% } %>
        </div>
        <%
          }
        %>
      </section>

      <!-- 访客留言 -->
      <section id="message" class="blog-section">
        <h3>访客留言</h3>
        <p>欢迎留下你的足迹和想法,我会认真阅读每一条留言。</p>

        <div class="message-preview">
          <p><strong>最新留言:</strong></p>
          <%
            // 获取最新的3条留言
            com.blog.dao.MessageDAO messageDAO = new com.blog.dao.MessageDAO();
            java.util.List<com.blog.bean.Message> recentMessages = messageDAO.getAllMessages();
            int count = Math.min(recentMessages.size(), 3);

            if (count > 0) {
              for (int i = 0; i < count; i++) {
                com.blog.bean.Message msg = recentMessages.get(i);
          %>
          <div class="recent-message">
            <strong><%= msg.getVisitor() %>:</strong>
            <%= msg.getContent().length() > 50 ?
                    msg.getContent().substring(0, 50) + "..." : msg.getContent() %>
            <span class="message-time"><%= msg.getCreateTime() %></span>
          </div>
          <%
            }
          } else {
          %>
          <p>还没有留言,快来第一个留言吧!</p>
          <%
            }
          %>
        </div>

        <div class="article-actions">
          <a href="message.jsp" class="btn btn-small">查看所有留言</a>
          <a href="message.jsp" class="btn btn-small">我要留言</a>
        </div>
      </section>
    </div>
  </main>

  <footer class="footer">
    <p>&copy; 2025 <%= user.getNickname() %>的个人博客 | 基于JSP+Servlet+JavaBean开发</p>
  </footer>
</div>

<script>
  // 导航栏滚动效果
  document.querySelectorAll('.nav-list a').forEach(anchor => {
    anchor.addEventListener('click', function(e) {
      if (this.getAttribute('href').startsWith('#')) {
        e.preventDefault();
        const targetId = this.getAttribute('href').substring(1);
        const targetElement = document.getElementById(targetId);

        if (targetElement) {
          window.scrollTo({
            top: targetElement.offsetTop - 80,
            behavior: 'smooth'
          });
        }
      }
    });
  });
</script>
</body>
</html>
点击查看代码login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>登录 - 个人博客</title>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class="container">
    <header class="header">
        <nav class="nav">
            <div class="logo">
                <a href="index.jsp">个人博客</a>
            </div>
            <ul class="nav-list">
                <li><a href="index.jsp">首页</a></li>
                <li><a href="article?action=list&category=news">生活新闻</a></li>
                <li><a href="article?action=list&category=study">学习记录</a></li>
                <li><a href="article?action=list&category=school">学校生活</a></li>
                <li><a href="article?action=list&category=plan">规划人生</a></li>
                <li><a href="article?action=list&category=travel">旅行打卡</a></li>
                <li><a href="message.jsp">访客留言</a></li>
                <li><a href="register.jsp">注册</a></li>
            </ul>
        </nav>
    </header>

    <main class="main-content">
        <div class="auth-container">
            <div class="auth-form">
                <h2>用户登录</h2>

                <%-- 显示错误信息 --%>
                <% if (request.getAttribute("errorMsg") != null) { %>
                <div class="error-message">
                    <%= request.getAttribute("errorMsg") %>
                </div>
                <% } %>

                <%-- 显示成功信息(注册成功后跳转到登录页面) --%>
                <% if (request.getAttribute("successMsg") != null) { %>
                <div class="success-message">
                    <%= request.getAttribute("successMsg") %>
                </div>
                <% } %>

                <form action="login" method="post">
                    <div class="form-group">
                        <label for="username">用户名:</label>
                        <input type="text" id="username" name="username"
                               required placeholder="请输入用户名">
                    </div>

                    <div class="form-group">
                        <label for="password">密码:</label>
                        <input type="password" id="password" name="password"
                               required placeholder="请输入密码">
                    </div>

                    <div class="form-group">
                        <button type="submit" class="btn btn-submit">登录</button>
                    </div>

                    <div class="form-links">
                        <p>还没有账号?<a href="register.jsp">立即注册</a></p>
                    </div>
                </form>
            </div>
        </div>
    </main>

    <footer class="footer">
        <p>&copy; 2025 个人博客系统 | 基于JSP+Servlet+JavaBean开发</p>
    </footer>
</div>
</body>
</html>
点击查看代码register.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>注册 - 个人博客</title>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class="container">
    <header class="header">
        <nav class="nav">
            <div class="logo">
                <a href="index.jsp">个人博客</a>
            </div>
            <ul class="nav-list">
                <li><a href="index.jsp">首页</a></li>
                <li><a href="article?action=list&category=news">生活新闻</a></li>
                <li><a href="article?action=list&category=study">学习记录</a></li>
                <li><a href="article?action=list&category=school">学校生活</a></li>
                <li><a href="article?action=list&category=plan">规划人生</a></li>
                <li><a href="article?action=list&category=travel">旅行打卡</a></li>
                <li><a href="message.jsp">访客留言</a></li>
                <li><a href="login.jsp">登录</a></li>
            </ul>
        </nav>
    </header>

    <main class="main-content">
        <div class="auth-container">
            <div class="auth-form">
                <h2>用户注册</h2>

                <%-- 显示错误信息 --%>
                <% if (request.getAttribute("errorMsg") != null) { %>
                <div class="error-message">
                    <%= request.getAttribute("errorMsg") %>
                </div>
                <% } %>

                <form action="register" method="post">
                    <div class="form-group">
                        <label for="username">用户名:</label>
                        <input type="text" id="username" name="username"
                               required placeholder="请输入用户名(登录使用)">
                    </div>

                    <div class="form-group">
                        <label for="password">密码:</label>
                        <input type="password" id="password" name="password"
                               required placeholder="请输入密码">
                    </div>

                    <div class="form-group">
                        <label for="nickname">昵称:</label>
                        <input type="text" id="nickname" name="nickname"
                               placeholder="例如:小猪猪">
                    </div>

                    <div class="form-group">
                        <label>性别:</label>
                        <div class="radio-group">
                            <input type="radio" id="male" name="gender" value="男" checked>
                            <label for="male">男</label>

                            <input type="radio" id="female" name="gender" value="女">
                            <label for="female">女</label>
                        </div>
                    </div>

                    <div class="form-group">
                        <label for="age">年龄:</label>
                        <input type="number" id="age" name="age"
                               value="20" min="1" max="100">
                    </div>

                    <div class="form-group">
                        <label for="zodiac">星座:</label>
                        <select id="zodiac" name="zodiac">
                            <option value="狮子座" selected>狮子座</option>
                            <option value="白羊座">白羊座</option>
                            <option value="金牛座">金牛座</option>
                            <option value="双子座">双子座</option>
                            <option value="巨蟹座">巨蟹座</option>
                            <option value="处女座">处女座</option>
                            <option value="天秤座">天秤座</option>
                            <option value="天蝎座">天蝎座</option>
                            <option value="射手座">射手座</option>
                            <option value="摩羯座">摩羯座</option>
                            <option value="水瓶座">水瓶座</option>
                            <option value="双鱼座">双鱼座</option>
                        </select>
                    </div>

                    <div class="form-group">
                        <button type="submit" class="btn btn-submit">注册</button>
                    </div>

                    <div class="form-links">
                        <p>已有账号?<a href="login.jsp">立即登录</a></p>
                    </div>
                </form>
            </div>
        </div>
    </main>

    <footer class="footer">
        <p>&copy; 2025 个人博客系统 | 基于JSP+Servlet+JavaBean开发</p>
    </footer>
</div>
</body>
</html>
点击查看代码editProfile.jsp
<%--
  Created by IntelliJ IDEA.
  User: Felix
  Date: 2026/1/7
  Time: 10:35
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
  // 检查用户是否已登录
  HttpSession userSession = request.getSession(false);
  com.blog.bean.User user = null;

  if (userSession != null) {
    user = (com.blog.bean.User) userSession.getAttribute("user");
  }

  // 如果未登录,重定向到登录页面
  if (user == null) {
    response.sendRedirect("login.jsp");
    return;
  }
%>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>编辑个人信息 - <%= user.getNickname() %>的个人博客</title>
  <link rel="stylesheet" href="css/style.css">
  <style>
    .profile-container {
      max-width: 600px;
      margin: 0 auto;
      padding: 40px 20px;
    }

    .profile-form {
      background: white;
      padding: 40px;
      border-radius: 10px;
      box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1);
    }

    .profile-header {
      text-align: center;
      margin-bottom: 30px;
    }

    .profile-header h2 {
      color: #333;
      margin-bottom: 10px;
    }

    .profile-header p {
      color: #666;
    }

    .form-section {
      margin-bottom: 30px;
      padding-bottom: 20px;
      border-bottom: 1px solid #eee;
    }

    .form-section h3 {
      color: #667eea;
      margin-bottom: 20px;
      font-size: 1.2em;
    }

    .password-note {
      color: #999;
      font-size: 0.9em;
      margin-top: 5px;
    }

    .form-actions {
      display: flex;
      justify-content: space-between;
      margin-top: 30px;
    }

    .btn-cancel {
      background-color: #e2e8f0;
      color: #4a5568;
    }

    .btn-cancel:hover {
      background-color: #cbd5e0;
    }

    .current-info {
      background-color: #f7fafc;
      padding: 15px;
      border-radius: 5px;
      margin-bottom: 20px;
      border-left: 4px solid #667eea;
    }

    .current-info p {
      margin: 5px 0;
      color: #4a5568;
    }
  </style>
</head>
<body>
<div class="container">
  <!-- 导航栏 -->
  <header class="header">
    <nav class="nav">
      <div class="logo">
        <a href="index.jsp">个人博客</a>
      </div>
      <ul class="nav-list">
        <li><a href="home.jsp">个人首页</a></li>
        <li><a href="article?action=list&category=news">生活新闻</a></li>
        <li><a href="article?action=list&category=study">学习记录</a></li>
        <li><a href="article?action=list&category=school">学校生活</a></li>
        <li><a href="article?action=list&category=plan">规划人生</a></li>
        <li><a href="article?action=list&category=travel">旅行打卡</a></li>
        <li><a href="message.jsp">访客留言</a></li>
        <% if (user != null) { %>
        <li class="user-info-nav">
          <a href="#" class="user-name"><%= user.getNickname() %></a>
          <div class="user-menu">
            <a href="home.jsp">个人主页</a>
            <a href="article?action=list">文章管理</a>
            <a href="logout">退出登录</a>
          </div>
        </li>
        <% } %>
      </ul>
    </nav>
  </header>

  <!-- 主要内容 -->
  <main class="main-content">
    <div class="profile-container">
      <div class="profile-form">
        <div class="profile-header">
          <h2>编辑个人信息</h2>
          <p>修改你的个人资料和密码</p>
        </div>

        <!-- 显示当前信息 -->
        <div class="current-info">
          <p><strong>当前信息:</strong></p>
          <p>用户名:<strong><%= user.getUsername() %></strong>(不可修改)</p>
          <p>昵称:<%= user.getNickname() %></p>
          <p>性别:<%= user.getGender() %></p>
          <p>年龄:<%= user.getAge() %>岁</p>
          <p>星座:<%= user.getZodiac() %></p>
        </div>

        <%-- 显示错误信息 --%>
        <% if (request.getAttribute("errorMsg") != null) { %>
        <div class="error-message">
          <%= request.getAttribute("errorMsg") %>
        </div>
        <% } %>

        <%-- 显示成功信息 --%>
        <% if (request.getAttribute("successMsg") != null) { %>
        <div class="success-message">
          <%= request.getAttribute("successMsg") %>
        </div>
        <% } %>

        <form action="updateProfile" method="post">
          <!-- 基本信息部分 -->
          <div class="form-section">
            <h3>基本信息</h3>

            <div class="form-group">
              <label for="nickname">昵称:</label>
              <input type="text" id="nickname" name="nickname"
                     value="<%= user.getNickname() %>"
                     placeholder="请输入昵称">
            </div>

            <div class="form-group">
              <label>性别:</label>
              <div class="radio-group">
                <input type="radio" id="male" name="gender" value="男"
                  <%= "男".equals(user.getGender()) ? "checked" : "" %>>
                <label for="male">男</label>

                <input type="radio" id="female" name="gender" value="女"
                  <%= "女".equals(user.getGender()) ? "checked" : "" %>>
                <label for="female">女</label>
              </div>
            </div>

            <div class="form-group">
              <label for="age">年龄:</label>
              <input type="number" id="age" name="age"
                     value="<%= user.getAge() %>" min="1" max="100">
            </div>

            <div class="form-group">
              <label for="zodiac">星座:</label>
              <select id="zodiac" name="zodiac">
                <option value="狮子座" <%= "狮子座".equals(user.getZodiac()) ? "selected" : "" %>>狮子座</option>
                <option value="白羊座" <%= "白羊座".equals(user.getZodiac()) ? "selected" : "" %>>白羊座</option>
                <option value="金牛座" <%= "金牛座".equals(user.getZodiac()) ? "selected" : "" %>>金牛座</option>
                <option value="双子座" <%= "双子座".equals(user.getZodiac()) ? "selected" : "" %>>双子座</option>
                <option value="巨蟹座" <%= "巨蟹座".equals(user.getZodiac()) ? "selected" : "" %>>巨蟹座</option>
                <option value="处女座" <%= "处女座".equals(user.getZodiac()) ? "selected" : "" %>>处女座</option>
                <option value="天秤座" <%= "天秤座".equals(user.getZodiac()) ? "selected" : "" %>>天秤座</option>
                <option value="天蝎座" <%= "天蝎座".equals(user.getZodiac()) ? "selected" : "" %>>天蝎座</option>
                <option value="射手座" <%= "射手座".equals(user.getZodiac()) ? "selected" : "" %>>射手座</option>
                <option value="摩羯座" <%= "摩羯座".equals(user.getZodiac()) ? "selected" : "" %>>摩羯座</option>
                <option value="水瓶座" <%= "水瓶座".equals(user.getZodiac()) ? "selected" : "" %>>水瓶座</option>
                <option value="双鱼座" <%= "双鱼座".equals(user.getZodiac()) ? "selected" : "" %>>双鱼座</option>
              </select>
            </div>
          </div>

          <!-- 密码修改部分 -->
          <div class="form-section">
            <h3>修改密码(可选)</h3>
            <p class="password-note">如果不需要修改密码,请留空以下字段</p>

            <div class="form-group">
              <label for="password">新密码:</label>
              <input type="password" id="password" name="password"
                     placeholder="如需修改密码,请输入新密码">
            </div>

            <div class="form-group">
              <label for="confirmPassword">确认新密码:</label>
              <input type="password" id="confirmPassword" name="confirmPassword"
                     placeholder="请再次输入新密码">
            </div>
          </div>

          <div class="form-actions">
            <a href="home.jsp" class="btn btn-cancel">取消</a>
            <button type="submit" class="btn btn-submit">保存更改</button>
          </div>
        </form>
      </div>
    </div>
  </main>

  <footer class="footer">
    <p>&copy; 2025 <%= user.getNickname() %>的个人博客 | 基于JSP+Servlet+JavaBean开发</p>
  </footer>
</div>
</body>
</html>
点击查看代码message.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="com.blog.bean.Message" %>
<%@ page import="java.util.List" %>
<%@ page import="com.blog.bean.User" %>
<%
    // 检查是否传递了消息列表
    List<Message> messages = (List<Message>) request.getAttribute("messages");
    if (messages == null) {
        response.sendRedirect("message?action=view");
        return;
    }

    // 检查用户是否已登录(用于显示管理功能)
    HttpSession userSession = request.getSession(false);
    User user = null;
    boolean isLoggedIn = false;

    if (userSession != null) {
        user = (User) userSession.getAttribute("user");
        isLoggedIn = (user != null);
    }
%>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>访客留言 - 个人博客</title>
    <link rel="stylesheet" href="css/style.css">
    <style>
        .message-container {
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
        }

        .message-form-container {
            background: white;
            padding: 30px;
            border-radius: 10px;
            box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1);
            margin-bottom: 30px;
        }

        .message-list-container {
            background: white;
            padding: 30px;
            border-radius: 10px;
            box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1);
        }

        .message-item {
            border-bottom: 1px solid #eee;
            padding: 20px 0;
            position: relative;
        }

        .message-item:last-child {
            border-bottom: none;
        }

        .message-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px;
        }

        .message-visitor {
            font-weight: bold;
            color: #333;
            font-size: 1.1em;
        }

        .message-time {
            color: #999;
            font-size: 0.9em;
        }

        .message-email {
            color: #667eea;
            font-size: 0.9em;
            margin-left: 10px;
        }

        .message-content {
            color: #555;
            line-height: 1.6;
            margin: 15px 0;
            padding: 15px;
            background-color: #f9f9f9;
            border-radius: 5px;
            border-left: 4px solid #667eea;
        }

        .message-footer {
            display: flex;
            justify-content: space-between;
            align-items: center;
            color: #999;
            font-size: 0.85em;
        }

        .message-ip {
            font-family: monospace;
        }

        .delete-btn {
            background-color: #fc8181;
            color: white;
            border: none;
            padding: 5px 10px;
            border-radius: 3px;
            cursor: pointer;
            font-size: 0.8em;
        }

        .delete-btn:hover {
            background-color: #f56565;
        }

        .no-messages {
            text-align: center;
            padding: 40px;
            color: #999;
        }

        .message-count {
            text-align: center;
            margin-bottom: 20px;
            color: #667eea;
            font-size: 1.1em;
        }

        .form-row {
            display: flex;
            gap: 20px;
            margin-bottom: 20px;
        }

        .form-row .form-group {
            flex: 1;
        }

        .message-preview {
            background-color: #f0f9ff;
            border: 1px dashed #667eea;
            padding: 15px;
            border-radius: 5px;
            margin-top: 20px;
            display: none;
        }

        .preview-header {
            display: flex;
            justify-content: space-between;
            margin-bottom: 10px;
        }

        .char-count {
            text-align: right;
            color: #999;
            font-size: 0.9em;
            margin-top: 5px;
        }

        .btn-secondary {
            background-color: #e2e8f0;
            color: #4a5568;
        }

        .btn-secondary:hover {
            background-color: #cbd5e0;
        }
    </style>
</head>
<body>
<div class="container">
    <!-- 导航栏 -->
    <header class="header">
        <nav class="nav">
            <div class="logo">
                <a href="index.jsp">个人博客</a>
            </div>
            <ul class="nav-list">
                <li><a href="home.jsp">个人首页</a></li>
                <li><a href="article?action=list&category=news">生活新闻</a></li>
                <li><a href="article?action=list&category=study">学习记录</a></li>
                <li><a href="article?action=list&category=school">学校生活</a></li>
                <li><a href="article?action=list&category=plan">规划人生</a></li>
                <li><a href="article?action=list&category=travel">旅行打卡</a></li>
                <li><a href="message.jsp" class="active">访客留言</a></li>
                <% if (isLoggedIn) { %>
                <li class="user-info-nav">
                    <a href="#" class="user-name"><%= user.getNickname() %></a>
                    <div class="user-menu">
                        <a href="home.jsp">个人主页</a>
                        <a href="article?action=list">文章管理</a>
                        <a href="editProfile.jsp">编辑资料</a>
                        <a href="logout">退出登录</a>
                    </div>
                </li>
                <% } else { %>
                <li><a href="login.jsp">登录</a></li>
                <li><a href="register.jsp">注册</a></li>
                <% } %>
            </ul>
        </nav>
    </header>

    <!-- 主要内容 -->
    <main class="main-content">
        <div class="message-container">
            <!-- 留言表单 -->
            <div class="message-form-container">
                <h2>留下你的足迹</h2>

                <%-- 显示错误信息 --%>
                <% if (request.getAttribute("errorMsg") != null) { %>
                <div class="error-message">
                    <%= request.getAttribute("errorMsg") %>
                </div>
                <% } %>

                <%-- 显示成功信息 --%>
                <% if (request.getAttribute("successMsg") != null) { %>
                <div class="success-message">
                    <%= request.getAttribute("successMsg") %>
                </div>
                <% } %>

                <form id="messageForm" action="message" method="post">
                    <div class="form-row">
                        <div class="form-group">
                            <label for="visitor">你的昵称:</label>
                            <input type="text" id="visitor" name="visitor"
                                   required placeholder="请输入昵称" maxlength="20">
                        </div>

                        <div class="form-group">
                            <label for="email">邮箱地址:</label>
                            <input type="email" id="email" name="email"
                                   placeholder="请输入邮箱(可选)">
                        </div>
                    </div>

                    <div class="form-group">
                        <label for="content">留言内容:</label>
                        <textarea id="content" name="content" rows="5"
                                  required placeholder="写下你想说的话..." maxlength="500"></textarea>
                        <div class="char-count">
                            <span id="charCount">0</span>/500
                        </div>
                    </div>

                    <div class="form-actions">
                        <button type="button" id="previewBtn" class="btn btn-secondary">预览</button>
                        <button type="submit" class="btn btn-submit">提交留言</button>
                    </div>
                </form>

                <!-- 预览区域 -->
                <div id="messagePreview" class="message-preview">
                    <div class="preview-header">
                        <strong>留言预览</strong>
                        <button type="button" id="closePreview" style="background:none;border:none;color:#666;cursor:pointer;">×</button>
                    </div>
                    <div id="previewContent"></div>
                </div>
            </div>

            <!-- 留言列表 -->
            <div class="message-list-container">
                <h2>留言列表</h2>
                <div class="message-count">
                    共 <%= messages.size() %> 条留言
                </div>

                <% if (messages.isEmpty()) { %>
                <div class="no-messages">
                    <p>还没有留言,快来第一个留言吧!</p>
                </div>
                <% } else { %>
                <% for (Message message : messages) { %>
                <div class="message-item" id="message-<%= message.getId() %>">
                    <div class="message-header">
                        <div>
                            <span class="message-visitor"><%= message.getVisitor() %></span>
                            <% if (message.getEmail() != null && !message.getEmail().isEmpty()) { %>
                            <span class="message-email">(<%= message.getEmail() %>)</span>
                            <% } %>
                        </div>
                        <span class="message-time"><%= message.getCreateTime() %></span>
                    </div>

                    <div class="message-content">
                        <%= message.getContent().replace("\n", "<br>") %>
                    </div>

                    <div class="message-footer">
                        <span class="message-ip">IP: <%= message.getIpAddress() %></span>
                        <% if (isLoggedIn) { %>
                        <form action="message" method="get" style="display:inline;">
                            <input type="hidden" name="action" value="delete">
                            <input type="hidden" name="id" value="<%= message.getId() %>">
                            <button type="submit" class="delete-btn"
                                    onclick="return confirm('确定要删除这条留言吗?')">删除</button>
                        </form>
                        <% } %>
                    </div>
                </div>
                <% } %>
                <% } %>
            </div>
        </div>
    </main>

    <footer class="footer">
        <p>&copy; 2025 个人博客系统 | 留言数据保存在: data/messages.txt</p>
    </footer>
</div>

<script>
    // 字符计数
    const contentTextarea = document.getElementById('content');
    const charCount = document.getElementById('charCount');

    contentTextarea.addEventListener('input', function() {
        charCount.textContent = this.value.length;
    });

    // 留言预览功能
    const previewBtn = document.getElementById('previewBtn');
    const closePreview = document.getElementById('closePreview');
    const messagePreview = document.getElementById('messagePreview');
    const previewContent = document.getElementById('previewContent');

    previewBtn.addEventListener('click', function() {
        const visitor = document.getElementById('visitor').value;
        const email = document.getElementById('email').value;
        const content = document.getElementById('content').value;

        if (!visitor || !content) {
            alert('请填写昵称和留言内容!');
            return;
        }

        let previewHtml = '';
        previewHtml += '<div class="message-header">';
        previewHtml += '<div>';
        previewHtml += '<span class="message-visitor">' + visitor + '</span>';
        if (email) {
            previewHtml += '<span class="message-email">(' + email + ')</span>';
        }
        previewHtml += '</div>';
        previewHtml += '<span class="message-time">刚刚</span>';
        previewHtml += '</div>';
        previewHtml += '<div class="message-content">';
        previewHtml += content.replace(/\n/g, '<br>');
        previewHtml += '</div>';

        previewContent.innerHTML = previewHtml;
        messagePreview.style.display = 'block';
    });

    closePreview.addEventListener('click', function() {
        messagePreview.style.display = 'none';
    });

    // 表单提交前的验证
    document.getElementById('messageForm').addEventListener('submit', function(e) {
        const visitor = document.getElementById('visitor').value.trim();
        const content = document.getElementById('content').value.trim();

        if (!visitor) {
            e.preventDefault();
            alert('请填写昵称!');
            document.getElementById('visitor').focus();
            return;
        }

        if (!content) {
            e.preventDefault();
            alert('请填写留言内容!');
            document.getElementById('content').focus();
            return;
        }

        if (content.length > 500) {
            e.preventDefault();
            alert('留言内容不能超过500字!');
            return;
        }
    });
</script>
</body>
</html>
点击查看代码articleList.jsp
<%--
  Created by IntelliJ IDEA.
  User: Felix
  Date: 2026/1/7
  Time: 16:57
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="com.blog.bean.Article" %>
<%@ page import="java.util.List" %>
<%@ page import="com.blog.bean.User" %>
<%
    // 检查用户是否已登录
    HttpSession userSession = request.getSession(false);
    User user = null;
    boolean isLoggedIn = false;

    if (userSession != null) {
        user = (User) userSession.getAttribute("user");
        isLoggedIn = (user != null);
    }

    List<Article> articles = (List<Article>) request.getAttribute("articles");
    String[][] categories = (String[][]) request.getAttribute("categories");
    String currentCategory = (String) request.getAttribute("currentCategory");
    String categoryName = (String) request.getAttribute("categoryName");
%>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>文章管理 - 个人博客</title>
    <link rel="stylesheet" href="css/style.css">
    <style>
        .article-container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
        }

        .article-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 30px;
            padding-bottom: 20px;
            border-bottom: 2px solid #667eea;
        }

        .category-tabs {
            display: flex;
            gap: 10px;
            margin-bottom: 30px;
            flex-wrap: wrap;
        }

        .category-tab {
            padding: 10px 20px;
            background-color: #f1f5f9;
            border-radius: 20px;
            text-decoration: none;
            color: #475569;
            transition: all 0.3s;
        }

        .category-tab:hover {
            background-color: #e2e8f0;
        }

        .category-tab.active {
            background-color: #667eea;
            color: white;
        }

        .article-list {
            display: grid;
            gap: 20px;
        }

        .article-item {
            background: white;
            padding: 25px;
            border-radius: 10px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            transition: transform 0.3s;
        }

        .article-item:hover {
            transform: translateY(-5px);
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
        }

        .article-title {
            font-size: 1.3em;
            color: #333;
            margin-bottom: 10px;
            text-decoration: none;
            display: block;
        }

        .article-title:hover {
            color: #667eea;
        }

        .article-meta {
            display: flex;
            justify-content: space-between;
            color: #666;
            font-size: 0.9em;
            margin-bottom: 15px;
            padding-bottom: 10px;
            border-bottom: 1px solid #eee;
        }

        .article-content {
            color: #555;
            line-height: 1.6;
            margin-bottom: 20px;
        }

        .article-content-preview {
            display: -webkit-box;
            -webkit-line-clamp: 3;
            -webkit-box-orient: vertical;
            overflow: hidden;
        }

        .article-actions {
            display: flex;
            gap: 10px;
            justify-content: flex-end;
        }

        .btn-small {
            padding: 6px 12px;
            font-size: 0.9em;
        }

        .btn-view {
            background-color: #48bb78;
            color: white;
        }

        .btn-edit {
            background-color: #4299e1;
            color: white;
        }

        .btn-delete {
            background-color: #fc8181;
            color: white;
        }

        .empty-message {
            text-align: center;
            padding: 60px;
            color: #999;
            background: white;
            border-radius: 10px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
        }

        .category-badge {
            display: inline-block;
            padding: 4px 12px;
            background-color: #667eea;
            color: white;
            border-radius: 12px;
            font-size: 0.85em;
            margin-right: 10px;
        }

        .time-info {
            color: #94a3b8;
        }
    </style>
</head>
<body>
<div class="container">
    <!-- 导航栏 -->
    <header class="header">
        <nav class="nav">
            <div class="logo">
                <a href="index.jsp">个人博客</a>
            </div>
            <ul class="nav-list">
                <li><a href="home.jsp">个人首页</a></li>
                <li><a href="article?action=list&category=news">生活新闻</a></li>
                <li><a href="article?action=list&category=study">学习记录</a></li>
                <li><a href="article?action=list&category=school">学校生活</a></li>
                <li><a href="article?action=list&category=plan">规划人生</a></li>
                <li><a href="article?action=list&category=travel">旅行打卡</a></li>
                <li><a href="message.jsp">访客留言</a></li>
                <% if (isLoggedIn) { %>
                <li class="user-info-nav">
                    <a href="#" class="user-name"><%= user.getNickname() %></a>
                    <div class="user-menu">
                        <a href="home.jsp">个人主页</a>
                        <a href="article?action=list">文章管理</a>
                        <a href="editProfile.jsp">编辑资料</a>
                        <a href="logout">退出登录</a>
                    </div>
                </li>
                <% } else { %>
                <li><a href="login.jsp">登录</a></li>
                <li><a href="register.jsp">注册</a></li>
                <% } %>
            </ul>
        </nav>
    </header>

    <!-- 主要内容 -->
    <main class="main-content">
        <div class="article-container">
            <div class="article-header">
                <h1><%= categoryName != null ? categoryName : "所有文章" %></h1>
                <% if (isLoggedIn) { %>
                <a href="article?action=edit" class="btn btn-submit">+ 发布新文章</a>
                <% } %>
            </div>

            <%-- 显示错误信息 --%>
            <% if (request.getAttribute("errorMsg") != null) { %>
            <div class="error-message">
                <%= request.getAttribute("errorMsg") %>
            </div>
            <% } %>

            <%-- 显示成功信息 --%>
            <% if (request.getAttribute("successMsg") != null) { %>
            <div class="success-message">
                <%= request.getAttribute("successMsg") %>
            </div>
            <% } %>

            <!-- 分类标签 -->
            <div class="category-tabs">
                <a href="article?action=list"
                   class="category-tab <%= currentCategory == null ? "active" : "" %>">
                    全部
                </a>
                <% for (String[] category : categories) { %>
                <a href="article?action=list&category=<%= category[0] %>"
                   class="category-tab <%= category[0].equals(currentCategory) ? "active" : "" %>">
                    <%= category[1] %>
                </a>
                <% } %>
            </div>

            <!-- 文章列表 -->
            <div class="article-list">
                <% if (articles == null || articles.isEmpty()) { %>
                <div class="empty-message">
                    <h3>暂无文章</h3>
                    <p>还没有发布任何文章,快来第一个发布吧!</p>
                    <% if (isLoggedIn) { %>
                    <a href="article?action=edit" class="btn btn-submit" style="margin-top: 20px;">发布文章</a>
                    <% } else { %>
                    <p>请先<a href="login.jsp">登录</a>后发布文章</p>
                    <% } %>
                </div>
                <% } else { %>
                <% for (Article article : articles) { %>
                <div class="article-item">
                    <a href="article?action=view&id=<%= article.getId() %>" class="article-title">
                        <span class="category-badge"><%= article.getCategoryName() %></span>
                        <%= article.getTitle() %>
                    </a>

                    <div class="article-meta">
                        <span>作者:<%= article.getAuthor() %></span>
                        <span class="time-info">
                                        发布于:<%= article.getCreateTime() %>
                                        <% if (!article.getCreateTime().equals(article.getUpdateTime())) { %>
                                            (更新于:<%= article.getUpdateTime() %>)
                                        <% } %>
                                    </span>
                    </div>

                    <div class="article-content article-content-preview">
                        <%= article.getContent().length() > 200 ?
                                article.getContent().substring(0, 200) + "..." : article.getContent() %>
                    </div>

                    <div class="article-actions">
                        <a href="article?action=view&id=<%= article.getId() %>"
                           class="btn btn-small btn-view">查看详情</a>

                        <% if (isLoggedIn && article.getAuthor().equals(user.getUsername())) { %>
                        <a href="article?action=edit&id=<%= article.getId() %>"
                           class="btn btn-small btn-edit">编辑</a>
                        <a href="article?action=delete&id=<%= article.getId() %>&category=<%= article.getCategory() %>"
                           class="btn btn-small btn-delete"
                           onclick="return confirm('确定要删除这篇文章吗?')">删除</a>
                        <% } %>
                    </div>
                </div>
                <% } %>
                <% } %>
            </div>
        </div>
    </main>

    <footer class="footer">
        <p>&copy; 2025 个人博客系统 | 文章数据保存在: data/articles.txt</p>
    </footer>
</div>
</body>
</html>

点击查看代码articleEdit.jsp
<%--
  Created by IntelliJ IDEA.
  User: Felix
  Date: 2026/1/7
  Time: 16:57
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="com.blog.bean.Article" %>
<%@ page import="com.blog.bean.User" %>
<%
    // 检查用户是否已登录
    HttpSession userSession = request.getSession(false);
    User user = null;

    if (userSession != null) {
        user = (User) userSession.getAttribute("user");
    }

    // 如果未登录,重定向到登录页面
    if (user == null) {
        response.sendRedirect("login.jsp");
        return;
    }

    Article article = (Article) request.getAttribute("article");
    String[][] categories = (String[][]) request.getAttribute("categories");

    boolean isEditMode = (article != null);
    String pageTitle = isEditMode ? "编辑文章" : "发布新文章";
    String formAction = isEditMode ? "article?action=update" : "article?action=create";
%>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><%= pageTitle %> - 个人博客</title>
    <link rel="stylesheet" href="css/style.css">
    <style>
        .edit-container {
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
        }

        .edit-form {
            background: white;
            padding: 40px;
            border-radius: 10px;
            box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1);
        }

        .edit-header {
            text-align: center;
            margin-bottom: 30px;
            padding-bottom: 20px;
            border-bottom: 2px solid #667eea;
        }

        .edit-header h2 {
            color: #333;
            margin-bottom: 10px;
        }

        .form-actions {
            display: flex;
            justify-content: space-between;
            margin-top: 30px;
        }

        .char-count {
            text-align: right;
            color: #999;
            font-size: 0.9em;
            margin-top: 5px;
        }

        .preview-area {
            background-color: #f9f9f9;
            border: 1px solid #ddd;
            border-radius: 5px;
            padding: 15px;
            margin-top: 20px;
            display: none;
        }

        .preview-title {
            font-size: 1.4em;
            color: #333;
            margin-bottom: 10px;
            padding-bottom: 10px;
            border-bottom: 1px solid #eee;
        }

        .preview-content {
            line-height: 1.6;
            color: #555;
        }
    </style>
</head>
<body>
<div class="container">
    <!-- 导航栏 -->
    <header class="header">
        <nav class="nav">
            <div class="logo">
                <a href="index.jsp">个人博客</a>
            </div>
            <ul class="nav-list">
                <li><a href="home.jsp">个人首页</a></li>
                <li><a href="article?action=list&category=news">生活新闻</a></li>
                <li><a href="article?action=list&category=study">学习记录</a></li>
                <li><a href="article?action=list&category=school">学校生活</a></li>
                <li><a href="article?action=list&category=plan">规划人生</a></li>
                <li><a href="article?action=list&category=travel">旅行打卡</a></li>
                <li><a href="message.jsp">访客留言</a></li>
                <% if (user != null) { %>
                <li class="user-info-nav">
                    <a href="#" class="user-name"><%= user.getNickname() %></a>
                    <div class="user-menu">
                        <a href="home.jsp">个人主页</a>
                        <a href="article?action=list">文章管理</a>
                        <a href="editProfile.jsp">编辑资料</a>
                        <a href="logout">退出登录</a>
                    </div>
                </li>
                <% } %>
            </ul>
        </nav>
    </header>

    <!-- 主要内容 -->
    <main class="main-content">
        <div class="edit-container">
            <div class="edit-form">
                <div class="edit-header">
                    <h2><%= pageTitle %></h2>
                    <p>在这里撰写和编辑你的文章内容</p>
                </div>

                <%-- 显示错误信息 --%>
                <% if (request.getAttribute("errorMsg") != null) { %>
                <div class="error-message">
                    <%= request.getAttribute("errorMsg") %>
                </div>
                <% } %>

                <form action="<%= formAction %>" method="post">
                    <% if (isEditMode) { %>
                    <input type="hidden" name="id" value="<%= article.getId() %>">
                    <% } %>

                    <div class="form-group">
                        <label for="category">选择分类:</label>
                        <select id="category" name="category" required>
                            <option value="">-- 请选择分类 --</option>
                            <% for (String[] category : categories) { %>
                            <option value="<%= category[0] %>"
                                    <% if (isEditMode && article.getCategory().equals(category[0])) { %>
                                    selected
                                    <% } %>
                            >
                                <%= category[1] %>
                            </option>
                            <% } %>
                        </select>
                    </div>

                    <div class="form-group">
                        <label for="title">文章标题:</label>
                        <input type="text" id="title" name="title"
                               value="<%= isEditMode ? article.getTitle() : "" %>"
                               required placeholder="请输入文章标题" maxlength="100">
                    </div>

                    <div class="form-group">
                        <label for="content">文章内容:</label>
                        <textarea id="content" name="content" rows="15"
                                  required placeholder="请输入文章内容..."><%= isEditMode ? article.getContent() : "" %></textarea>
                        <div class="char-count">
                            <span id="charCount">0</span>/5000
                        </div>
                    </div>

                    <div class="form-actions">
                        <button type="button" id="previewBtn" class="btn btn-secondary">预览</button>
                        <div>
                            <a href="article?action=list" class="btn btn-cancel">取消</a>
                            <button type="submit" class="btn btn-submit">
                                <%= isEditMode ? "更新文章" : "发布文章" %>
                            </button>
                        </div>
                    </div>
                </form>

                <!-- 预览区域 -->
                <div id="previewArea" class="preview-area">
                    <div class="preview-header">
                        <strong>文章预览</strong>
                        <button type="button" id="closePreview" style="background:none;border:none;color:#666;cursor:pointer;">×</button>
                    </div>
                    <div id="previewTitle" class="preview-title"></div>
                    <div id="previewContent" class="preview-content"></div>
                </div>
            </div>
        </div>
    </main>

    <footer class="footer">
        <p>&copy; 2025 个人博客系统</p>
    </footer>
</div>

<script>
    // 字符计数
    const contentTextarea = document.getElementById('content');
    const charCount = document.getElementById('charCount');

    // 初始化字符计数
    charCount.textContent = contentTextarea.value.length;

    contentTextarea.addEventListener('input', function() {
        charCount.textContent = this.value.length;
    });

    // 文章预览功能
    const previewBtn = document.getElementById('previewBtn');
    const closePreview = document.getElementById('closePreview');
    const previewArea = document.getElementById('previewArea');
    const previewTitle = document.getElementById('previewTitle');
    const previewContent = document.getElementById('previewContent');

    previewBtn.addEventListener('click', function() {
        const categorySelect = document.getElementById('category');
        const categoryText = categorySelect.options[categorySelect.selectedIndex].text;
        const title = document.getElementById('title').value;
        const content = document.getElementById('content').value;

        if (!categorySelect.value || !title || !content) {
            alert('请先填写完整的文章信息!');
            return;
        }

        previewTitle.innerHTML = `<span style="background:#667eea;color:white;padding:2px 8px;border-radius:4px;font-size:0.8em;margin-right:10px;">${categoryText}</span>${title}`;
        previewContent.innerHTML = content.replace(/\n/g, '<br>');
        previewArea.style.display = 'block';

        // 滚动到预览区域
        previewArea.scrollIntoView({ behavior: 'smooth' });
    });

    closePreview.addEventListener('click', function() {
        previewArea.style.display = 'none';
    });

    // 表单提交验证
    document.querySelector('form').addEventListener('submit', function(e) {
        const category = document.getElementById('category').value;
        const title = document.getElementById('title').value.trim();
        const content = document.getElementById('content').value.trim();

        if (!category) {
            e.preventDefault();
            alert('请选择文章分类!');
            return;
        }

        if (!title) {
            e.preventDefault();
            alert('请输入文章标题!');
            document.getElementById('title').focus();
            return;
        }

        if (!content) {
            e.preventDefault();
            alert('请输入文章内容!');
            document.getElementById('content').focus();
            return;
        }

        if (content.length > 5000) {
            e.preventDefault();
            alert('文章内容不能超过5000字!');
            return;
        }
    });
</script>
</body>
</html>

点击查看代码articleView.jsp
<%--
  Created by IntelliJ IDEA.
  User: Felix
  Date: 2026/1/7
  Time: 16:58
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="com.blog.bean.Article" %>
<%@ page import="com.blog.bean.User" %>
<%
    Article article = (Article) request.getAttribute("article");

    if (article == null) {
        response.sendRedirect("article?action=list");
        return;
    }

    // 检查用户是否已登录
    HttpSession userSession = request.getSession(false);
    User user = null;
    boolean isLoggedIn = false;
    boolean isAuthor = false;

    if (userSession != null) {
        user = (User) userSession.getAttribute("user");
        isLoggedIn = (user != null);
        if (isLoggedIn) {
            isAuthor = article.getAuthor().equals(user.getUsername());
        }
    }
%>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><%= article.getTitle() %> - 个人博客</title>
    <link rel="stylesheet" href="css/style.css">
    <style>
        .view-container {
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
        }

        .article-view {
            background: white;
            padding: 40px;
            border-radius: 10px;
            box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1);
        }

        .article-header {
            text-align: center;
            margin-bottom: 40px;
            padding-bottom: 30px;
            border-bottom: 2px solid #667eea;
        }

        .article-category {
            display: inline-block;
            padding: 6px 15px;
            background-color: #667eea;
            color: white;
            border-radius: 15px;
            font-size: 0.9em;
            margin-bottom: 20px;
        }

        .article-title {
            font-size: 2em;
            color: #333;
            margin-bottom: 20px;
            line-height: 1.3;
        }

        .article-meta {
            color: #666;
            font-size: 0.95em;
        }

        .article-meta span {
            margin-right: 20px;
        }

        .article-content {
            line-height: 1.8;
            color: #444;
            font-size: 1.1em;
            margin-bottom: 40px;
        }

        .article-footer {
            padding-top: 30px;
            border-top: 1px solid #eee;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .article-actions {
            display: flex;
            gap: 10px;
        }

        .time-info {
            color: #94a3b8;
            font-size: 0.9em;
        }

        .content-section {
            white-space: pre-wrap;
            word-wrap: break-word;
        }
    </style>
</head>
<body>
<div class="container">
    <!-- 导航栏 -->
    <header class="header">
        <nav class="nav">
            <div class="logo">
                <a href="index.jsp">个人博客</a>
            </div>
            <ul class="nav-list">
                <li><a href="home.jsp">个人首页</a></li>
                <li><a href="article?action=list&category=news">生活新闻</a></li>
                <li><a href="article?action=list&category=study">学习记录</a></li>
                <li><a href="article?action=list&category=school">学校生活</a></li>
                <li><a href="article?action=list&category=plan">规划人生</a></li>
                <li><a href="article?action=list&category=travel">旅行打卡</a></li>
                <li><a href="message.jsp">访客留言</a></li>
                <% if (isLoggedIn) { %>
                <li class="user-info-nav">
                    <a href="#" class="user-name"><%= user.getNickname() %></a>
                    <div class="user-menu">
                        <a href="home.jsp">个人主页</a>
                        <a href="article?action=list">文章管理</a>
                        <a href="editProfile.jsp">编辑资料</a>
                        <a href="logout">退出登录</a>
                    </div>
                </li>
                <% } else { %>
                <li><a href="login.jsp">登录</a></li>
                <li><a href="register.jsp">注册</a></li>
                <% } %>
            </ul>
        </nav>
    </header>

    <!-- 主要内容 -->
    <main class="main-content">
        <div class="view-container">
            <div class="article-view">
                <div class="article-header">
                    <div class="article-category"><%= article.getCategoryName() %></div>
                    <h1 class="article-title"><%= article.getTitle() %></h1>
                    <div class="article-meta">
                        <span>作者:<%= article.getAuthor() %></span>
                        <span>发布时间:<%= article.getCreateTime() %></span>
                    </div>
                </div>

                <div class="article-content content-section"><%= article.getContent() %></div>

                <div class="article-footer">
                    <div class="time-info">
                        最后更新:<%= article.getUpdateTime() %>
                    </div>

                    <div class="article-actions">
                        <a href="article?action=list&category=<%= article.getCategory() %>"
                           class="btn btn-small">返回列表</a>

                        <% if (isAuthor) { %>
                        <a href="article?action=edit&id=<%= article.getId() %>"
                           class="btn btn-small btn-edit">编辑文章</a>
                        <a href="article?action=delete&id=<%= article.getId() %>&category=<%= article.getCategory() %>"
                           class="btn btn-small btn-delete"
                           onclick="return confirm('确定要删除这篇文章吗?')">删除文章</a>
                        <% } %>
                    </div>
                </div>
            </div>
        </div>
    </main>

    <footer class="footer">
        <p> 2025 个人博客系统</p>
    </footer>
</div>
</body>
</html>

页面排版

点击查看代码style.css
/* 全局样式 */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Arial', 'Microsoft YaHei', sans-serif;
    line-height: 1.6;
    color: #333;
    background-color: #f5f5f5;
}

.container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 20px;
}

/* 导航栏样式 */
.header {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    position: sticky;
    top: 0;
    z-index: 1000;
}

.nav {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 15px 0;
}

.logo a {
    color: white;
    font-size: 24px;
    font-weight: bold;
    text-decoration: none;
}

.nav-list {
    display: flex;
    list-style: none;
    gap: 20px;
}

.nav-list li a {
    color: white;
    text-decoration: none;
    padding: 8px 16px;
    border-radius: 4px;
    transition: background-color 0.3s;
}

.nav-list li a:hover {
    background-color: rgba(255, 255, 255, 0.1);
}

.nav-list li a.active {
    background-color: rgba(255, 255, 255, 0.2);
}

.user-info-nav {
    position: relative;
}

.user-name {
    background-color: rgba(255, 255, 255, 0.2);
    padding: 8px 16px;
    border-radius: 20px;
}

.user-menu {
    display: none;
    position: absolute;
    top: 100%;
    right: 0;
    background: white;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
    border-radius: 4px;
    min-width: 120px;
}

.user-info-nav:hover .user-menu {
    display: block;
}

.user-menu a {
    color: #333 !important;
    display: block;
    padding: 10px;
    text-decoration: none;
}

.user-menu a:hover {
    background-color: #f5f5f5;
}

/* 主要内容区域 */
.main-content {
    padding: 40px 0;
}

.welcome-section {
    text-align: center;
    padding: 60px 20px;
    background: white;
    border-radius: 10px;
    box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1);
    margin-bottom: 40px;
}

.welcome-section h1 {
    font-size: 2.5em;
    color: #333;
    margin-bottom: 20px;
}

.welcome-section p {
    font-size: 1.2em;
    color: #666;
    margin-bottom: 30px;
}

.auth-buttons {
    display: flex;
    justify-content: center;
    gap: 20px;
    margin-bottom: 20px;
}

.btn {
    display: inline-block;
    padding: 12px 30px;
    border-radius: 25px;
    text-decoration: none;
    font-weight: bold;
    transition: all 0.3s;
    border: none;
    cursor: pointer;
    font-size: 16px;
}

.btn-login {
    background-color: #667eea;
    color: white;
}

.btn-register {
    background-color: #764ba2;
    color: white;
}

.btn-home {
    background-color: #48bb78;
    color: white;
    margin-top: 20px;
}

.btn:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}

.auth-tip {
    color: #999;
    font-size: 0.9em;
}

/* 预览区域 */
.preview {
    background: white;
    padding: 40px;
    border-radius: 10px;
    box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1);
}

.preview h2 {
    text-align: center;
    margin-bottom: 30px;
    color: #333;
}

.preview-content {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 30px;
}

.preview-item {
    padding: 20px;
    background: #f8f9fa;
    border-radius: 8px;
    transition: transform 0.3s;
}

.preview-item:hover {
    transform: translateY(-5px);
}

.preview-item h3 {
    color: #667eea;
    margin-bottom: 15px;
    border-bottom: 2px solid #667eea;
    padding-bottom: 10px;
}

/* 认证表单样式 */
.auth-container {
    max-width: 500px;
    margin: 0 auto;
    padding: 40px 20px;
}

.auth-form {
    background: white;
    padding: 40px;
    border-radius: 10px;
    box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1);
}

.auth-form h2 {
    text-align: center;
    margin-bottom: 30px;
    color: #333;
}

.form-group {
    margin-bottom: 20px;
}

.form-group label {
    display: block;
    margin-bottom: 8px;
    color: #555;
    font-weight: bold;
}

.form-group input,
.form-group select,
.form-group textarea {
    width: 100%;
    padding: 12px;
    border: 1px solid #ddd;
    border-radius: 4px;
    font-size: 16px;
    transition: border-color 0.3s;
}

.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
    border-color: #667eea;
    outline: none;
}

.radio-group {
    display: flex;
    gap: 20px;
    margin-top: 8px;
}

.radio-group input[type="radio"] {
    width: auto;
    margin-right: 5px;
}

.btn-submit {
    width: 100%;
    background-color: #667eea;
    color: white;
    padding: 14px;
    font-size: 16px;
}

.btn-submit:hover {
    background-color: #5a67d8;
}

.form-links {
    text-align: center;
    margin-top: 20px;
}

.form-links a {
    color: #667eea;
    text-decoration: none;
}

.form-links a:hover {
    text-decoration: underline;
}

.error-message {
    background-color: #fed7d7;
    color: #c53030;
    padding: 12px;
    border-radius: 4px;
    margin-bottom: 20px;
    text-align: center;
}

.success-message {
    background-color: #c6f6d5;
    color: #22543d;
    padding: 12px;
    border-radius: 4px;
    margin-bottom: 20px;
    text-align: center;
}

/* 页脚样式 */
.footer {
    text-align: center;
    padding: 30px 0;
    color: #666;
    border-top: 1px solid #eee;
    margin-top: 40px;
}

/* 响应式设计 */
/*弹性布局*/
@media (max-width: 768px) {
    .nav {
        flex-direction: column;
        gap: 15px;
    }

    .nav-list {
        flex-wrap: wrap;
        justify-content: center;
    }

    .preview-content {
        grid-template-columns: 1fr;
    }

    .welcome-section h1 {
        font-size: 2em;
    }

    .auth-buttons {
        flex-direction: column;
        align-items: center;
    }

    .btn {
        width: 200px;
        text-align: center;
    }
}
/* 用户菜单样式 */
.user-info-nav {
    position: relative;
}

.user-name {
    background-color: rgba(255, 255, 255, 0.2);
    padding: 8px 16px;
    border-radius: 20px;
    display: inline-block;
}

.user-menu {
    display: none;
    position: absolute;
    top: 100%;
    right: 0;
    background: white;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
    border-radius: 4px;
    min-width: 150px;
    z-index: 1000;
}

.user-info-nav:hover .user-menu {
    display: block;
}

.user-menu a {
    color: #333 !important;
    display: block;
    padding: 10px 15px;
    text-decoration: none;
    border-bottom: 1px solid #eee;
}

.user-menu a:last-child {
    border-bottom: none;
}

.user-menu a:hover {
    background-color: #f5f5f5;
    color: #667eea !important;
}
/* 留言相关样式 */
.message-preview {
    background-color: #f8f9fa;
    padding: 15px;
    border-radius: 5px;
    margin: 15px 0;
}

.recent-message {
    border-bottom: 1px solid #eee;
    padding: 10px 0;
    color: #555;
}

.recent-message:last-child {
    border-bottom: none;
}

.message-time {
    color: #999;
    font-size: 0.85em;
    float: right;
}

.message-actions {
    display: flex;
    gap: 10px;
    margin-top: 20px;
}

.btn-message {
    background-color: #48bb78;
    color: white;
}

.char-count {
    text-align: right;
    color: #999;
    font-size: 0.9em;
    margin-top: 5px;
}

.btn-secondary {
    background-color: #e2e8f0;
    color: #4a5568;
}

.btn-secondary:hover {
    background-color: #cbd5e0;
}
/* 文章相关样式 */
.article-meta {
    color: #999;
    font-size: 0.85em;
    margin: 10px 0;
}

.article-actions {
    display: flex;
    gap: 10px;
    margin-top: 15px;
}

.btn-small {
    padding: 6px 12px;
    font-size: 0.9em;
}

.no-content {
    color: #999;
    font-style: italic;
}

.view-all-link {
    display: block;
    text-align: right;
    margin-top: 10px;
    color: #667eea;
    text-decoration: none;
}

.view-all-link:hover {
    text-decoration: underline;
}

.message-preview {
    background-color: #f8f9fa;
    padding: 15px;
    border-radius: 5px;
    margin: 15px 0;
}

.recent-message {
    border-bottom: 1px solid #eee;
    padding: 10px 0;
    color: #555;
}

.recent-message:last-child {
    border-bottom: none;
}

.message-time {
    color: #999;
    font-size: 0.85em;
    float: right;
}

2.2原始系统运行截图

  • 图一:原始系统首页,展示博客预览和登录入口
    image
  • 图二:文章列表页面,显示所有文章并编辑文章等功能
    image
  • 图三:txt文件存储方式
    image
    三、原始系统缺陷分析
    3.1文件并发访问问题
    当多个用户同时访问或修改数据时,文件操作可能产生冲突,导致数据丢失或损坏。
    3.2性能瓶颈
    随着数据量增长,每次操作都需要读取整个文件,性能急剧下降。
    3.3数据一致性问题
    在更新或删除操作中,如果程序中途崩溃,可能导致数据文件损坏。
    3.4数据冗余
    相同的用户信息在多个文件中重复存储(文章文件中有作者名,但没有用户详细信息)。
    四、重构方案设计
    4.1重构目标:将文件存储迁移到MySQL数据库,保证数据一致性和完整性
    4.2数据库设计:
    表结构设计
点击查看代码sql
-- 创建数据库
CREATE DATABASE personal_blog CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

USE personal_blog;

-- 用户表
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) UNIQUE NOT NULL,
    password VARCHAR(100) NOT NULL,
    nickname VARCHAR(50) NOT NULL,
    gender ENUM('男', '女') DEFAULT '男',
    age INT DEFAULT 20,
    zodiac VARCHAR(20),
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_username (username)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 文章表
CREATE TABLE articles (
    id INT PRIMARY KEY AUTO_INCREMENT,
    article_id VARCHAR(50) UNIQUE NOT NULL COMMENT '兼容原有ID格式',
    category ENUM('news', 'study', 'school', 'plan', 'travel') NOT NULL,
    title VARCHAR(200) NOT NULL,
    content TEXT NOT NULL,
    author_id INT NOT NULL,
    author_name VARCHAR(50) NOT NULL,
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    view_count INT DEFAULT 0,
    FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE CASCADE,
    FULLTEXT INDEX ft_content (title, content) WITH PARSER ngram,
    INDEX idx_category (category),
    INDEX idx_create_time (create_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 留言表
CREATE TABLE messages (
    id INT PRIMARY KEY AUTO_INCREMENT,
    message_id VARCHAR(50) UNIQUE NOT NULL COMMENT '兼容原有ID格式',
    visitor_name VARCHAR(50) NOT NULL,
    visitor_email VARCHAR(100),
    content TEXT NOT NULL,
    ip_address VARCHAR(50),
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_create_time (create_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 系统日志表
CREATE TABLE system_logs (
    id INT PRIMARY KEY AUTO_INCREMENT,
    log_level VARCHAR(20),
    message TEXT,
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

五、核心代码重构
5.1数据库连接工具类

点击查看代码DBUtil.java
package com.blog.util;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * 数据库连接工具类 - 使用HikariCP连接池
 */
public class DBUtil {
    private static HikariDataSource dataSource;
    
    static {
        try {
            HikariConfig config = new HikariConfig();
            config.setJdbcUrl("jdbc:mysql://localhost:3306/personal_blog?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8mb4");
            config.setUsername("root");
            config.setPassword("123456");
            config.setDriverClassName("com.mysql.cj.jdbc.Driver");
            
            // 连接池配置
            config.setMaximumPoolSize(20);          // 最大连接数
            config.setMinimumIdle(5);                // 最小空闲连接
            config.setConnectionTimeout(30000);      // 连接超时时间(ms)
            config.setIdleTimeout(600000);           // 空闲超时时间(ms)
            config.setMaxLifetime(1800000);          // 连接最大生命周期(ms)
            
            // 连接测试
            config.setConnectionTestQuery("SELECT 1");
            config.setValidationTimeout(5000);
            
            dataSource = new HikariDataSource(config);
            
            // 测试连接
            try (Connection conn = dataSource.getConnection()) {
                System.out.println("数据库连接成功!");
            }
            
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("数据库连接池初始化失败!", e);
        }
    }
    
    /**
     * 获取数据库连接
     */
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
    
    /**
     * 关闭连接池
     */
    public static void close() {
        if (dataSource != null && !dataSource.isClosed()) {
            dataSource.close();
        }
    }
}
5.2重构后的UserDAO 改进点:使用PreparedStatement防止SQL注入、支持事务操作、自动生成ID和时间
点击查看代码
package com.blog.dao;

import com.blog.bean.User;
import com.blog.util.DBUtil;
import org.apache.commons.dbutils.*;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import java.sql.*;
import java.util.List;

/**
 * 重构后的UserDAO - 使用数据库存储
 */
public class UserDAO {
    private QueryRunner runner = new QueryRunner();
    private RowProcessor processor = new BasicRowProcessor(new GenerousBeanProcessor());
    
    /**
     * 用户注册
     */
    public boolean register(User user) {
        String sql = "INSERT INTO users (username, password, nickname, gender, age, zodiac) " +
                     "VALUES (?, ?, ?, ?, ?, ?)";
        
        try (Connection conn = DBUtil.getConnection()) {
            int result = runner.update(conn, sql,
                user.getUsername(),
                user.getPassword(),  // 实际项目中应该加密存储
                user.getNickname(),
                user.getGender(),
                user.getAge(),
                user.getZodiac()
            );
            
            // 记录日志
            logOperation("用户注册", user.getUsername(), result > 0);
            
            return result > 0;
            
        } catch (SQLException e) {
            e.printStackTrace();
            logError("用户注册失败", e);
            return false;
        }
    }
    
    /**
     * 用户登录验证
     */
    public User login(String username, String password) {
        String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
        
        try (Connection conn = DBUtil.getConnection()) {
            User user = runner.query(conn, sql, 
                new BeanHandler<>(User.class, processor), 
                username, password);
            
            logOperation("用户登录", username, user != null);
            return user;
            
        } catch (SQLException e) {
            e.printStackTrace();
            logError("用户登录失败", e);
            return null;
        }
    }
    
    /**
     * 根据用户名查找用户
     */
    public User findByUsername(String username) {
        String sql = "SELECT * FROM users WHERE username = ?";
        
        try (Connection conn = DBUtil.getConnection()) {
            return runner.query(conn, sql, 
                new BeanHandler<>(User.class, processor), 
                username);
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }
    
    /**
     * 更新用户信息
     */
    public boolean updateUser(User updatedUser) {
        String sql = "UPDATE users SET nickname = ?, gender = ?, age = ?, zodiac = ? " +
                     "WHERE username = ?";
        
        try (Connection conn = DBUtil.getConnection()) {
            int result = runner.update(conn, sql,
                updatedUser.getNickname(),
                updatedUser.getGender(),
                updatedUser.getAge(),
                updatedUser.getZodiac(),
                updatedUser.getUsername()
            );
            
            logOperation("更新用户信息", updatedUser.getUsername(), result > 0);
            return result > 0;
            
        } catch (SQLException e) {
            e.printStackTrace();
            logError("更新用户信息失败", e);
            return false;
        }
    }
    
    /**
     * 更新密码
     */
    public boolean updatePassword(String username, String newPassword) {
        String sql = "UPDATE users SET password = ? WHERE username = ?";
        
        try (Connection conn = DBUtil.getConnection()) {
            int result = runner.update(conn, sql, newPassword, username);
            logOperation("更新密码", username, result > 0);
            return result > 0;
        } catch (SQLException e) {
            e.printStackTrace();
            logError("更新密码失败", e);
            return false;
        }
    }
    
    /**
     * 获取所有用户
     */
    public List<User> getAllUsers() {
        String sql = "SELECT * FROM users ORDER BY create_time DESC";
        
        try (Connection conn = DBUtil.getConnection()) {
            return runner.query(conn, sql, 
                new BeanListHandler<>(User.class, processor));
        } catch (SQLException e) {
            e.printStackTrace();
            logError("获取用户列表失败", e);
            return new ArrayList<>();
        }
    }
    
    /**
     * 记录操作日志
     */
    private void logOperation(String operation, String detail, boolean success) {
        String sql = "INSERT INTO system_logs (log_level, message) VALUES (?, ?)";
        String level = success ? "INFO" : "WARN";
        String message = operation + " - " + detail + " - " + (success ? "成功" : "失败");
        
        try (Connection conn = DBUtil.getConnection()) {
            runner.update(conn, sql, level, message);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 记录错误日志
     */
    private void logError(String message, Exception e) {
        String sql = "INSERT INTO system_logs (log_level, message) VALUES ('ERROR', ?)";
        String fullMessage = message + ": " + e.getMessage();
        
        try (Connection conn = DBUtil.getConnection()) {
            runner.update(conn, sql, fullMessage);
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
    }
}

五、难点总结与体会
数据库连接池的配置、数据库的设计以及数据的迁移。在最开始数据总是丢失,经过反复修改调试才成功,当然还有修改原始代码中读取文件数据的代码也耗费了大量的时间,这让我意识到原始系统采用文件存储,虽然简单易实现,但扩展性差。这次经历让我学到,好的架构设计不能只看当下,更要为未来程序的扩展考虑。

posted @ 2026-03-07 15:42  辞暮烟火  阅读(15)  评论(0)    收藏  举报