SpringBoot留言板项目

第一步 创建模块

第二步 创建项目结构

后端:

前端:

第三步 编写代码

后端:

1.MessageController.java

package com.itheima.controller;

import com.itheima.service.MessageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.Map;

@Controller
public class MessageController {
    private final MessageService messageService;

    @Autowired
    public MessageController(MessageService messageService) {
        this.messageService = messageService;
    }

    // 首页展示所有留言
    @GetMapping("/")
    public String index(Model model) {
        Map<String, Object> data = messageService.getAllMessagesWithOriginalInfo();
        model.addAttribute("messages", data.get("messages"));
        model.addAttribute("messageMap", data.get("messageMap"));
        return "index";
    }

    // 发表新留言
    @PostMapping("/postMessage")
    public String postMessage(
            @RequestParam("username") String username,
            @RequestParam("content") String content) {
        messageService.postMessage(username, content);
        return "redirect:/";
    }

    // 转发留言(修改为支持评论参数)
    @PostMapping("/forwardMessage")
    public String forwardMessage(
            @RequestParam("messageId") Integer messageId,
            @RequestParam("username") String username,
            @RequestParam(value = "comment", required = false, defaultValue = "") String comment) {
        messageService.forwardMessage(messageId, username, comment);
        return "redirect:/";
    }
}

2.Message.java

package com.itheima.entity;

import java.time.LocalDateTime;

public class Message {
    private Integer id;         
    private String username;   
    private String content;    
    private LocalDateTime createTime;  
    private boolean isForward; 
    private Integer originalId; 
    private String comment;    // 添加评论字段,用于存储转发时的评论

    // 构造函数
    public Message() {
        this.createTime = LocalDateTime.now();
    }

    public Message(String username, String content) {
        this.username = username;
        this.content = content;
        this.createTime = LocalDateTime.now();
        this.isForward = false;
    }

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

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

    public String getContent() {
        return content;
    }

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

    public LocalDateTime getCreateTime() {
        return createTime;
    }

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

    public boolean isForward() {
        return isForward;
    }

    public void setForward(boolean forward) {
        isForward = forward;
    }

    public Integer getOriginalId() {
        return originalId;
    }

    public void setOriginalId(Integer originalId) {
        this.originalId = originalId;
    }
    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }
}

3.MessageRepositoryImpl.java

package com.itheima.repository.impl;

import com.itheima.entity.Message;
import com.itheima.repository.MessageRepository;
import org.springframework.stereotype.Repository;

import java.util.*;

@Repository
public class MessageRepositoryImpl implements MessageRepository {

    private List<Message> messages = new ArrayList<>();

    @Override
    public void save(Message message) {
  
        if (message.getId() == null) {
            Integer maxId = getMaxId();
            message.setId(maxId != null ? maxId + 1 : 1);
        }
        messages.add(message);
    }

    @Override
    public List<Message> findAll() {
  
        return new ArrayList<>(messages);
    }

    @Override
    public Message findById(Integer id) {
        return messages.stream()
                .filter(message -> message.getId().equals(id))
                .findFirst()
                .orElse(null);
    }

    @Override
    public Integer getMaxId() {
        if (messages.isEmpty()) {
            return null;
        }
        return messages.stream()
                .map(Message::getId)
                .max(Comparator.naturalOrder())
                .orElse(null);
    }
}

4.MessageRepository.java

package com.itheima.repository;

import com.itheima.entity.Message;
import java.util.List;

public interface MessageRepository {

    void save(Message message);
    
    List<Message> findAll();
    
    Message findById(Integer id);

    Integer getMaxId();
}

5.MessageService.java

package com.itheima.service;

import com.itheima.entity.Message;
import com.itheima.repository.MessageRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class MessageService {
    private final MessageRepository messageRepository;

    @Autowired
    public MessageService(MessageRepository messageRepository) {
        this.messageRepository = messageRepository;
    }

    // 发表新留言
    public void postMessage(String username, String content) {
        Message message = new Message(username, content);
        messageRepository.save(message);
    }

    // 获取所有留言(按创建时间降序排列,最新的在最上方)
    public List<Message> getAllMessages() {
        List<Message> messages = messageRepository.findAll();
        // 按创建时间降序排序
        messages.sort(Comparator.comparing(Message::getCreateTime).reversed());
        return messages;
    }
    
    // 获取所有留言及其相关信息(用于前端展示)
    public Map<String, Object> getAllMessagesWithOriginalInfo() {
        List<Message> messages = getAllMessages();
        Map<Integer, Message> messageMap = new HashMap<>();
        
        // 先构建所有消息的ID到消息对象的映射
        for (Message message : messages) {
            messageMap.put(message.getId(), message);
        }
        
        Map<String, Object> result = new HashMap<>();
        result.put("messages", messages);
        result.put("messageMap", messageMap);
        
        return result;
    }

    // 转发留言(修改为支持评论)
    public void forwardMessage(Integer messageId, String username, String comment) {
        Message originalMessage = messageRepository.findById(messageId);
        if (originalMessage != null) {
            // 创建转发的新消息
            Message forwardedMessage = new Message();
            forwardedMessage.setUsername(username);
            forwardedMessage.setContent(originalMessage.getContent());
            forwardedMessage.setForward(true);
            forwardedMessage.setOriginalId(messageId);
            forwardedMessage.setComment(comment); // 设置评论内容
            
            messageRepository.save(forwardedMessage);
        }
    }
}

前端:

1.index.html

<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>留言板</title>
  <style>
    /* 基础样式 */
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    
    body {
      font-family: 'Microsoft YaHei', 'PingFang SC', Arial, sans-serif;
      background-color: #f5f7fa;
      color: #333;
      line-height: 1.6;
    }
    
    .container {
      max-width: 800px;
      margin: 0 auto;
      padding: 20px;
    }
    
    /* 标题样式 */
    header {
      text-align: center;
      margin-bottom: 30px;
      padding-bottom: 15px;
      border-bottom: 2px solid #e1e8ed;
    }
    
    h1 {
      color: #2c3e50;
      font-size: 2.2rem;
      font-weight: 600;
      margin-bottom: 10px;
    }
    
    /* 表单区域样式 */
    .message-form {
      background-color: #fff;
      padding: 25px;
      border-radius: 12px;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
      margin-bottom: 30px;
      transition: transform 0.3s ease;
    }
    
    .message-form:hover {
      transform: translateY(-2px);
      box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
    }
    
    .message-form h2 {
      color: #34495e;
      font-size: 1.5rem;
      margin-bottom: 20px;
      font-weight: 500;
    }
    
    .form-group {
      margin-bottom: 20px;
    }
    
    .form-group label {
      display: block;
      font-weight: 500;
      margin-bottom: 8px;
      color: #555;
      font-size: 0.95rem;
    }
    
    .form-group input,
    .form-group textarea {
      width: 100%;
      padding: 12px 15px;
      border: 2px solid #e1e8ed;
      border-radius: 8px;
      font-size: 1rem;
      font-family: inherit;
      transition: border-color 0.3s ease, box-shadow 0.3s ease;
    }
    
    .form-group input:focus,
    .form-group textarea:focus {
      outline: none;
      border-color: #3498db;
      box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
    }
    
    .form-group textarea {
      resize: vertical;
      min-height: 100px;
      line-height: 1.5;
    }
    
    /* 按钮样式 */
    .btn {
      display: inline-block;
      padding: 12px 24px;
      border: none;
      border-radius: 8px;
      font-size: 1rem;
      font-weight: 500;
      cursor: pointer;
      transition: all 0.3s ease;
      text-decoration: none;
      text-align: center;
    }
    
    .btn-primary {
      background-color: #3498db;
      color: white;
    }
    
    .btn-primary:hover {
      background-color: #2980b9;
      transform: translateY(-1px);
      box-shadow: 0 4px 8px rgba(52, 152, 219, 0.3);
    }
    
    .btn-secondary {
      background-color: #95a5a6;
      color: white;
      padding: 8px 16px;
      font-size: 0.85rem;
    }
    
    .btn-secondary:hover {
      background-color: #7f8c8d;
      transform: translateY(-1px);
    }
    
    /* 留言列表样式 */
    .messages-section {
      background-color: #fff;
      padding: 25px;
      border-radius: 12px;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
    }
    
    .messages-section h2 {
      color: #34495e;
      font-size: 1.5rem;
      margin-bottom: 20px;
      font-weight: 500;
    }
    
    .no-messages {
      text-align: center;
      color: #95a5a6;
      padding: 40px 20px;
      font-style: italic;
    }
    
    .messages-list {
      list-style: none;
    }
    
    .message-item {
      padding: 15px 0;
      border-bottom: 1px solid #f0f0f0;
      transition: background-color 0.2s ease;
      display: flex;
      justify-content: space-between;
      align-items: flex-start;
    }
    
    .message-item:hover {
      background-color: #f8f9fa;
    }
    
    .message-item:last-child {
      border-bottom: none;
    }
    
    .message-content {
      flex-grow: 1;
      margin-right: 15px;
    }
    
    .message-username {
      font-weight: 600;
      color: #2c3e50;
      margin-right: 8px;
    }
    
    .message-text {
      color: #555;
      word-break: break-word;
    }
    
    /* 转发相关样式 */
    .forward-form {
      margin-left: auto;
    }
    
    .forward-area {
      margin-top: 10px;
      padding: 10px;
      background-color: #f8f9fa;
      border-radius: 8px;
    }
    
    .forward-comment {
      color: #e74c3c;
      font-style: italic;
      margin-top: 5px;
    }
    
    .original-message {
      background-color: #e8f4fd;
      padding: 8px 12px;
      border-radius: 6px;
      margin-top: 8px;
      font-size: 0.9em;
      border-left: 3px solid #3498db;
    }
    
    /* 响应式设计 */
    @media (max-width: 600px) {
      .container {
        padding: 15px;
      }
      
      h1 {
        font-size: 1.8rem;
      }
      
      .message-form,
      .messages-section {
        padding: 20px;
      }
      
      .message-item {
        flex-direction: column;
      }
      
      .forward-form {
        margin-top: 10px;
        align-self: flex-end;
      }
    }
    
    /* 添加一些动画效果 */
    @keyframes fadeIn {
      from {
        opacity: 0;
        transform: translateY(10px);
      }
      to {
        opacity: 1;
        transform: translateY(0);
      }
    }
    
    .fade-in {
      animation: fadeIn 0.5s ease;
    }
   </style>
</head>
<body>
  <div class="container">
    <header>
      <h1>留言板</h1>
    </header>
    
    <!-- 发表留言表单 -->
    <div class="message-form fade-in">
      <h2>发表留言</h2>
      <form action="/postMessage" 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="content">留言内容:</label>
          <textarea id="content" name="content" required placeholder="请输入您的留言内容..."></textarea>
        </div>
        <button type="submit" class="btn btn-primary">发表留言</button>
      </form>
    </div>
    
    <!-- 所有留言列表 -->
    <div class="messages-section fade-in">
      <h2>所有留言</h2>
      <div th:if="${messages.empty}" class="no-messages">
        暂无留言,快来发表第一条留言吧!
      </div>
      <ul th:unless="${messages.empty}" class="messages-list">
        <li th:each="message : ${messages}" class="message-item fade-in">
          <div class="message-content">
            <!-- 显示用户名 -->
            <span th:text="${message.username}" class="message-username"></span>
            
            <!-- 对于普通消息,显示内容 -->
            <span th:if="!${message.forward}" th:text="${message.content}" class="message-text"></span>
            
            <!-- 对于转发消息,显示'被转发用户名:留言'的形式 -->
            <span th:if="${message.forward}" class="message-text" th:text="|被转发${messageMap[message.originalId].username}:${messageMap[message.originalId].content}|"></span>
            
            <!-- 显示转发的评论 -->
            <div th:if="${message.comment != null and not #strings.isEmpty(message.comment)}" class="forward-comment">
              评论: <span th:text="${message.comment}"></span>
            </div>
          </div>
          
          <!-- 转发表单,添加评论输入框 -->
          <div class="forward-form">
            <button type="button" class="btn btn-secondary forward-btn" th:attr="data-id=${message.id}">转发</button>
            
            <!-- 转发评论表单 (默认隐藏) -->
            <form th:id="forwardForm_+${message.id}" action="/forwardMessage" method="post" style="display: none; margin-top: 10px;">
              <input type="hidden" name="messageId" th:value="${message.id}">
              <div class="form-group" style="margin-bottom: 10px;">
                <input type="text" name="username" placeholder="您的用户名" required style="padding: 6px 10px; width: 100%;">
              </div>
              <div class="form-group" style="margin-bottom: 10px;">
                <textarea name="comment" placeholder="添加您的评论..." style="padding: 6px 10px; width: 100%; min-height: 60px;"></textarea>
              </div>
              <div style="display: flex; gap: 5px;">
                <button type="submit" class="btn btn-primary" style="padding: 6px 12px; font-size: 0.8rem;">确认转发</button>
                <button type="button" class="btn btn-secondary cancel-forward" th:attr="data-id=${message.id}" style="padding: 6px 12px; font-size: 0.8rem;">取消</button>
              </div>
            </form>
          </div>
        </li>
      </ul>
    </div>
  </div>
  
  <script>
    // JavaScript代码保持不变
    document.addEventListener('DOMContentLoaded', function() {
      // 为表单添加提交确认
      const form = document.querySelector('form[action="/postMessage"]');
      if (form) {
        form.addEventListener('submit', function(e) {
          const username = document.getElementById('username').value;
          const content = document.getElementById('content').value;
          
          if (!username.trim() || !content.trim()) {
            alert('请填写完整的用户名和留言内容!');
            e.preventDefault();
          }
        });
      }
      
      // 转发按钮点击事件
      const forwardButtons = document.querySelectorAll('.forward-btn');
      forwardButtons.forEach(button => {
        button.addEventListener('click', function() {
          const messageId = this.getAttribute('data-id');
          const forwardForm = document.getElementById('forwardForm_' + messageId);
          forwardForm.style.display = 'block';
          this.style.display = 'none';
        });
      });
      
      // 取消转发按钮点击事件
      const cancelButtons = document.querySelectorAll('.cancel-forward');
      cancelButtons.forEach(button => {
        button.addEventListener('click', function() {
          const messageId = this.getAttribute('data-id');
          const forwardForm = document.getElementById('forwardForm_' + messageId);
          const forwardButton = document.querySelector('.forward-btn[data-id="' + messageId + '"]');
          forwardForm.style.display = 'none';
          forwardButton.style.display = 'inline-block';
        });
      });
      
      // 转发表单提交确认
      const forwardForms = document.querySelectorAll('form[action="/forwardMessage"]');
      forwardForms.forEach(form => {
        form.addEventListener('submit', function(e) {
          const username = this.querySelector('input[name="username"]').value;
          
          if (!username.trim()) {
            alert('请填写您的用户名!');
            e.preventDefault();
          }
        });
      });
    });
  </script>
</body>
</html>

配置:

application.properties.java

spring.thymeleaf.cache=false
spring.thymeleaf.mode=HTML
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.servlet.content-type=text/html

server.port=8080
spring.application.name=springboot-messageboard-demo

第四步 运行

启动项目,输入网站:localhost:8080

项目界面:

效果

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值