本文目录导读:

我来为您提供一个完整的Java评论功能案例,包含前后端实现。
数据库设计
CREATE TABLE `comment` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `content` text NOT NULL COMMENT '评论内容', `user_id` bigint(20) NOT NULL COMMENT '用户ID', `user_name` varchar(50) NOT NULL COMMENT '用户名称', `article_id` bigint(20) NOT NULL COMMENT '文章ID', `parent_id` bigint(20) DEFAULT NULL COMMENT '父评论ID', `reply_user_id` bigint(20) DEFAULT NULL COMMENT '回复用户ID', `reply_user_name` varchar(50) DEFAULT NULL COMMENT '回复用户名称', `likes` int(11) DEFAULT '0' COMMENT '点赞数', `status` tinyint(4) DEFAULT '1' COMMENT '状态:1正常 0删除', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`), KEY `idx_article_id` (`article_id`), KEY `idx_user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='评论表';
实体类
// Comment.java
package com.example.entity;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Data
public class Comment {
private Long id;
private String content;
private Long userId;
private String userName;
private Long articleId;
private Long parentId;
private Long replyUserId;
private String replyUserName;
private Integer likes;
private Integer status;
private LocalDateTime createTime;
private LocalDateTime updateTime;
// 用于展示的子评论列表
private List<Comment> children;
}
DAO层
// CommentMapper.java
package com.example.mapper;
import com.example.entity.Comment;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface CommentMapper {
// 添加评论
@Insert("INSERT INTO comment(content, user_id, user_name, article_id, parent_id, " +
"reply_user_id, reply_user_name, likes, status, create_time, update_time) " +
"VALUES(#{content}, #{userId}, #{userName}, #{articleId}, #{parentId}, " +
"#{replyUserId}, #{replyUserName}, 0, 1, NOW(), NOW())")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(Comment comment);
// 根据文章ID查询父评论(顶级评论)
@Select("SELECT * FROM comment WHERE article_id = #{articleId} AND parent_id IS NULL AND status = 1 " +
"ORDER BY create_time DESC")
List<Comment> findParentComments(Long articleId);
// 根据父评论ID查询子评论
@Select("SELECT * FROM comment WHERE parent_id = #{parentId} AND status = 1 " +
"ORDER BY create_time ASC")
List<Comment> findChildComments(Long parentId);
// 删除评论(逻辑删除)
@Update("UPDATE comment SET status = 0 WHERE id = #{id}")
int delete(Long id);
// 点赞
@Update("UPDATE comment SET likes = likes + 1 WHERE id = #{id}")
int likeComment(Long id);
}
Service层
// CommentService.java
package com.example.service;
import com.example.entity.Comment;
import com.example.mapper.CommentMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
@Service
public class CommentService {
@Autowired
private CommentMapper commentMapper;
// 添加评论
@Transactional
public Comment addComment(Comment comment) {
commentMapper.insert(comment);
return comment;
}
// 获取文章的评论列表(树形结构)
public List<Comment> getCommentsByArticleId(Long articleId) {
// 获取所有父评论
List<Comment> parentComments = commentMapper.findParentComments(articleId);
// 为每个父评论添加子评论
for (Comment parent : parentComments) {
List<Comment> children = commentMapper.findChildComments(parent.getId());
parent.setChildren(children);
}
return parentComments;
}
// 删除评论
@Transactional
public boolean deleteComment(Long id) {
// 逻辑删除评论
commentMapper.delete(id);
// 如果有子评论,也一并删除
List<Comment> children = commentMapper.findChildComments(id);
for (Comment child : children) {
commentMapper.delete(child.getId());
}
return true;
}
// 点赞评论
public boolean likeComment(Long id) {
commentMapper.likeComment(id);
return true;
}
}
Controller层
// CommentController.java
package com.example.controller;
import com.example.common.Result;
import com.example.entity.Comment;
import com.example.service.CommentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/comment")
public class CommentController {
@Autowired
private CommentService commentService;
// 添加评论
@PostMapping("/add")
public Result addComment(@RequestBody Comment comment) {
// 模拟用户信息(实际应从登录用户获取)
comment.setUserId(1L);
comment.setUserName("测试用户");
Comment result = commentService.addComment(comment);
return Result.success(result);
}
// 获取文章的评论
@GetMapping("/list/{articleId}")
public Result getComments(@PathVariable Long articleId) {
List<Comment> comments = commentService.getCommentsByArticleId(articleId);
return Result.success(comments);
}
// 删除评论
@DeleteMapping("/delete/{id}")
public Result deleteComment(@PathVariable Long id) {
commentService.deleteComment(id);
return Result.success();
}
// 点赞评论
@PostMapping("/like/{id}")
public Result likeComment(@PathVariable Long id) {
commentService.likeComment(id);
return Result.success();
}
}
统一返回结果类
// Result.java
package com.example.common;
import lombok.Data;
@Data
public class Result<T> {
private Integer code;
private String message;
private T data;
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(200);
result.setMessage("success");
result.setData(data);
return result;
}
public static Result success() {
return success(null);
}
public static <T> Result<T> error(String message) {
Result<T> result = new Result<>();
result.setCode(500);
result.setMessage(message);
return result;
}
}
前端HTML示例
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">评论系统</title>
<style>
.comment {
border: 1px solid #ddd;
padding: 15px;
margin-bottom: 10px;
border-radius: 5px;
}
.comment-header {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
}
.comment-user {
font-weight: bold;
color: #333;
}
.comment-time {
color: #999;
font-size: 12px;
}
.comment-content {
margin-bottom: 10px;
}
.comment-actions {
display: flex;
gap: 15px;
}
.comment-actions button {
border: none;
background: none;
cursor: pointer;
color: #666;
}
.comment-actions button:hover {
color: #007bff;
}
.children {
margin-left: 30px;
margin-top: 10px;
}
.reply-form {
margin-top: 10px;
}
.reply-form textarea {
width: 100%;
height: 60px;
margin-bottom: 10px;
}
</style>
</head>
<body>
<div class="container">
<h2>文章评论</h2>
<!-- 评论表单 -->
<div class="comment-form">
<textarea id="commentContent" placeholder="写下你的评论..." style="width:100%;height:80px;"></textarea>
<button onclick="submitComment()">发表评论</button>
</div>
<!-- 评论列表 -->
<div id="commentList" class="comment-list"></div>
</div>
<script>
// 获取评论列表
async function loadComments() {
try {
const response = await fetch('/api/comment/list/1'); // 文章ID为1
const result = await response.json();
if (result.code === 200) {
renderComments(result.data);
}
} catch (error) {
console.error('加载评论失败:', error);
}
}
// 渲染评论
function renderComments(comments) {
const commentList = document.getElementById('commentList');
commentList.innerHTML = comments.map(comment => renderComment(comment)).join('');
}
// 渲染单个评论
function renderComment(comment) {
return `
<div class="comment" id="comment-${comment.id}">
<div class="comment-header">
<span class="comment-user">${comment.userName}</span>
<span class="comment-time">${formatTime(comment.createTime)}</span>
</div>
<div class="comment-content">${comment.content}</div>
<div class="comment-actions">
<button onclick="likeComment(${comment.id})">👍 ${comment.likes || 0}</button>
<button onclick="replyComment(${comment.id}, '${comment.userName}')">回复</button>
<button onclick="deleteComment(${comment.id})">删除</button>
</div>
<!-- 子评论 -->
${comment.children && comment.children.length > 0 ? `
<div class="children">
${comment.children.map(child => renderComment(child)).join('')}
</div>
` : ''}
</div>
`;
}
// 提交评论
async function submitComment(parentId = null, replyUserId = null, replyUserName = null) {
const content = document.getElementById('commentContent').value;
if (!content.trim()) {
alert('请输入评论内容');
return;
}
try {
const response = await fetch('/api/comment/add', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
content: content,
articleId: 1,
parentId: parentId,
replyUserId: replyUserId,
replyUserName: replyUserName
})
});
const result = await response.json();
if (result.code === 200) {
document.getElementById('commentContent').value = '';
loadComments();
}
} catch (error) {
console.error('提交评论失败:', error);
}
}
// 点赞评论
async function likeComment(id) {
try {
const response = await fetch(`/api/comment/like/${id}`, {method: 'POST'});
const result = await response.json();
if (result.code === 200) {
loadComments();
}
} catch (error) {
console.error('点赞失败:', error);
}
}
// 删除评论
async function deleteComment(id) {
if (!confirm('确定删除此评论?')) return;
try {
const response = await fetch(`/api/comment/delete/${id}`, {method: 'DELETE'});
const result = await response.json();
if (result.code === 200) {
loadComments();
}
} catch (error) {
console.error('删除失败:', error);
}
}
// 格式化时间
function formatTime(time) {
const date = new Date(time);
return date.toLocaleString('zh-CN');
}
// 初始化加载评论
loadComments();
</script>
</body>
</html>
配置文件
# application.yml
server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/comment_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: your_password
mybatis:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
功能特点
- 支持嵌套评论:实现评论和回复的树形结构
- 评论点赞:支持对评论进行点赞
- 评论删除:逻辑删除,保留数据完整性
- 时间排序:父评论按时间倒序,子评论按时间正序
- 前端交互:完整的HTML+JavaScript实现
这个案例包含了评论功能的核心实现,您可以根据实际需求进行扩展,比如添加用户认证、评论审核、分页加载等功能。