refactor(消息模块): 重构消息服务及控制器功能

重构消息模块,包括以下主要变更:
1. 将MessageRepository从CrudRepository扩展改为JpaRepository
2. 新增消息查询方法,支持按文章ID、父消息ID和昵称查询
3. 完善消息服务层逻辑,增加日志记录和错误处理
4. 扩展消息控制器API,新增获取根消息、回复消息等端点
5. 添加消息数据初始化组件和检查器
6. 优化全局异常处理,增加请求路径日志

同时调整文章模块:
1. 移除按作者查询文章功能
2. 统一分类查询参数命名
3. 优化文章服务层代码结构

配置变更:
1. 添加缓存相关依赖
2. 调整数据库连接配置
3. 暂时禁用Hibernate二级缓存
This commit is contained in:
qingfeng1121
2025-10-11 11:17:12 +08:00
parent 60f4752124
commit 470cf71713
15 changed files with 5025 additions and 6756 deletions

File diff suppressed because it is too large Load Diff

Binary file not shown.

25
pom.xml
View File

@@ -80,6 +80,31 @@
<artifactId>commonmark</artifactId> <artifactId>commonmark</artifactId>
<version>0.18.2</version> <version>0.18.2</version>
</dependency> </dependency>
<!-- JCache API -->
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
</dependency>
<!-- EHCache 3作为JCache实现 -->
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>8.0.1</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies> </dependencies>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>

View File

@@ -1,13 +1,21 @@
package com.qf.myafterprojecy; package com.qf.myafterprojecy;
import com.qf.myafterprojecy.pojo.ResponseMessage; import com.qf.myafterprojecy.pojo.ResponseMessage;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice @RestControllerAdvice
public class GlobalExceptionHandler { public class GlobalExceptionHandler {
Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(value=Exception.class) @ExceptionHandler(value=Exception.class)
public ResponseMessage<String> handleException(Exception e){ public ResponseMessage<String> handleException(Exception e, HttpServletRequest request) {
logger.error("请求路径:{},异常消息:{}",request.getRequestURI(),e.getMessage());
return new ResponseMessage<>(500,"服务器异常",e.getMessage()); return new ResponseMessage<>(500,"服务器异常",e.getMessage());
} }
} }

View File

@@ -32,6 +32,7 @@ public class ArticleController {
@GetMapping("/{id}") @GetMapping("/{id}")
public ResponseMessage<Article> getArticle(@PathVariable Integer id) { public ResponseMessage<Article> getArticle(@PathVariable Integer id) {
return articleService.getArticleById(id); return articleService.getArticleById(id);
} }
/** /**
@@ -82,24 +83,15 @@ public class ArticleController {
return articleService.deleteArticle(id); return articleService.deleteArticle(id);
} }
/**
* 根据作者ID获取其所有文章
* @param authorId 作者ID
* @return 返回包含文章列表的ResponseMessage对象
*/
@GetMapping("/author/{authorId}")
public ResponseMessage<List<Article>> getArticlesByAuthor(@PathVariable Integer authorId) {
return articleService.getArticlesByAuthor(authorId);
}
/** /**
* 根据分类ID获取该分类下的所有文章 * 根据分类ID获取该分类下的所有文章
* @param categoryId 分类ID * @param typeid 分类ID
* @return 返回包含文章列表的ResponseMessage对象 * @return 返回包含文章列表的ResponseMessage对象
*/ */
@GetMapping("/category/{categoryId}") @GetMapping("/category/{categoryId}")
public ResponseMessage<List<Article>> getArticlesByCategory(@PathVariable Integer categoryId) { public ResponseMessage<List<Article>> getArticlesByCategory(@PathVariable Integer typeid) {
return articleService.getArticlesByCategory(categoryId); return articleService.getArticlesByCategory(typeid);
} }
/** /**

View File

@@ -4,32 +4,102 @@ import com.qf.myafterprojecy.pojo.Message;
import com.qf.myafterprojecy.pojo.ResponseMessage; import com.qf.myafterprojecy.pojo.ResponseMessage;
import com.qf.myafterprojecy.pojo.dto.MessageDto; import com.qf.myafterprojecy.pojo.dto.MessageDto;
import com.qf.myafterprojecy.service.IMessageService; import com.qf.myafterprojecy.service.IMessageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController @RestController
@RequestMapping("/api/messages") @RequestMapping("/api/messages")
public class MessageController { public class MessageController {
private static final Logger logger = LoggerFactory.getLogger(MessageController.class);
@Autowired @Autowired
private IMessageService messageService; private IMessageService messageService;
/**
* 获取所有消息
*/
@GetMapping @GetMapping
public ResponseMessage<Iterable<Message>> getAllMessages() { public ResponseMessage<Iterable<Message>> getAllMessages() {
logger.info("接收获取所有消息的请求");
return messageService.getAllMessages(); return messageService.getAllMessages();
} }
/**
* 根据ID获取消息
*/
@GetMapping("/{id}") @GetMapping("/{id}")
public ResponseMessage<Message> getMessage(@PathVariable Integer id) { public ResponseMessage<Message> getMessage(@PathVariable Integer id) {
logger.info("接收根据ID获取消息的请求: {}", id);
return messageService.getMessageById(id); return messageService.getMessageById(id);
} }
/**
* 创建新消息
*/
@PostMapping @PostMapping
public ResponseMessage<Message> createMessage(@RequestBody MessageDto message) { public ResponseMessage<Message> createMessage(@RequestBody MessageDto message) {
logger.info("接收创建消息的请求: {}", message != null ? message.getNickname() : "null");
return messageService.saveMessage(message); return messageService.saveMessage(message);
} }
/**
* 根据ID删除消息
*/
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
public ResponseMessage<Message> deleteMessage(@PathVariable Integer id) { public ResponseMessage<Message> deleteMessage(@PathVariable Integer id) {
logger.info("接收删除消息的请求: {}", id);
return messageService.deleteMessage(id); return messageService.deleteMessage(id);
} }
// 新增API端点
/**
* 根据文章ID获取消息列表
*/
@GetMapping("/article/{articleId}")
public ResponseMessage<List<Message>> getMessagesByArticleId(@PathVariable Integer articleId) {
logger.info("接收根据文章ID获取消息的请求: {}", articleId);
return messageService.getMessagesByArticleId(articleId);
}
/**
* 获取所有根消息(非回复的消息)
*/
@GetMapping("/root")
public ResponseMessage<List<Message>> getRootMessages() {
logger.info("接收获取所有根消息的请求");
return messageService.getRootMessages();
}
/**
* 根据父消息ID获取回复列表
*/
@GetMapping("/{parentId}/replies")
public ResponseMessage<List<Message>> getRepliesByParentId(@PathVariable Integer parentId) {
logger.info("接收根据父消息ID获取回复的请求: {}", parentId);
return messageService.getRepliesByParentId(parentId);
}
/**
* 根据昵称搜索消息
*/
@GetMapping("/search")
public ResponseMessage<List<Message>> searchMessagesByNickname(@RequestParam String nickname) {
logger.info("接收根据昵称搜索消息的请求: {}", nickname);
return messageService.searchMessagesByNickname(nickname);
}
/**
* 获取指定文章的评论数量
*/
@GetMapping("/count/article/{articleId}")
public ResponseMessage<Long> getMessageCountByArticleId(@PathVariable Integer articleId) {
logger.info("接收获取文章评论数量的请求: {}", articleId);
return messageService.getMessageCountByArticleId(articleId);
}
} }

View File

@@ -0,0 +1,96 @@
package com.qf.myafterprojecy.init;
import com.qf.myafterprojecy.pojo.Message;
import com.qf.myafterprojecy.repository.MessageRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* 消息数据初始化类用于在应用启动时为Message表添加默认的测试数据
*/
@Component
public class MessageDataInit implements ApplicationRunner {
private static final Logger logger = LoggerFactory.getLogger(MessageDataInit.class);
@Autowired
private MessageRepository messageRepository;
@Override
public void run(ApplicationArguments args) throws Exception {
logger.info("===== 消息数据初始化开始 =====");
// 检查数据库中是否已有消息数据
long count = messageRepository.count();
logger.info("当前数据库中消息数量: {}", count);
// 如果没有消息数据,添加一些测试数据
if (count == 0) {
logger.info("数据库中没有消息数据,开始添加初始化数据...");
addInitialMessages();
} else {
logger.info("数据库中已存在消息数据,无需初始化");
}
logger.info("===== 消息数据初始化结束 =====");
}
private void addInitialMessages() {
// 添加第一篇文章的评论
Message message1 = new Message();
message1.setNickname("系统用户");
message1.setEmail("system@example.com");
message1.setContent("这是系统自动添加的第一条评论,欢迎使用本系统!");
message1.setCreatedAt(new Date());
message1.setArticleid(1);
message1.setParentid(null); // 根评论
messageRepository.save(message1);
// 添加回复
Message reply1 = new Message();
reply1.setNickname("管理员");
reply1.setEmail("admin@example.com");
reply1.setContent("感谢您的支持,如有任何问题请随时联系我们!");
reply1.setCreatedAt(new Date());
reply1.setArticleid(1);
reply1.setParentid(message1.getMessageid()); // 回复第一条评论
messageRepository.save(reply1);
// 添加第二篇文章的评论
Message message2 = new Message();
message2.setNickname("访客");
message2.setEmail("visitor@example.com");
message2.setContent("这篇文章写得非常好,学到了很多知识。");
message2.setCreatedAt(new Date());
message2.setArticleid(2);
message2.setParentid(null);
messageRepository.save(message2);
// 再添加一些测试数据
Message message3 = new Message();
message3.setNickname("测试用户1");
message3.setEmail("test1@example.com");
message3.setContent("这是测试内容1");
message3.setCreatedAt(new Date());
message3.setArticleid(1);
message3.setParentid(null);
messageRepository.save(message3);
Message reply2 = new Message();
reply2.setNickname("测试用户2");
reply2.setEmail("test2@example.com");
reply2.setContent("回复测试内容1");
reply2.setCreatedAt(new Date());
reply2.setArticleid(1);
reply2.setParentid(message3.getMessageid());
messageRepository.save(reply2);
logger.info("成功添加了{}条初始化消息数据", messageRepository.count());
}
}

View File

@@ -4,7 +4,6 @@ import com.qf.myafterprojecy.pojo.Article;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@@ -12,8 +11,6 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
@Repository // 表明这是一个数据访问层组件,用于持久层操作 @Repository // 表明这是一个数据访问层组件,用于持久层操作
//public interface ArticleRepository extends CrudRepository<Article,Integer> {
//}
public interface ArticleRepository extends JpaRepository<Article, Integer> { public interface ArticleRepository extends JpaRepository<Article, Integer> {
/** /**
* 根据文章ID查询文章信息的方法 * 根据文章ID查询文章信息的方法
@@ -28,7 +25,6 @@ public interface ArticleRepository extends JpaRepository<Article, Integer> {
* 根据文章ID查询已发布的文章 * 根据文章ID查询已发布的文章
* 使用JPQL查询语句只查询状态为1已发布且指定ID的文章 * 使用JPQL查询语句只查询状态为1已发布且指定ID的文章
* *
* @param id 作者ID作为查询参数传入
* @return 返回符合查询条件的文章列表 * @return 返回符合查询条件的文章列表
*/ */
@Query("SELECT a FROM Article a WHERE a.status = 1") @Query("SELECT a FROM Article a WHERE a.status = 1")
@@ -41,8 +37,8 @@ public interface ArticleRepository extends JpaRepository<Article, Integer> {
* @param typeid 分类ID通过@Param注解映射到查询语句中的:typeid参数 * @param typeid 分类ID通过@Param注解映射到查询语句中的:typeid参数
* @return 返回符合条件Article对象的列表 * @return 返回符合条件Article对象的列表
*/ */
@Query("SELECT a FROM Article a WHERE a.status = 1 AND a.typeid = :categoryId") @Query("SELECT a FROM Article a WHERE a.status = 1 AND a.typeid = :typeid")
List<Article> findPublishedByCategory(@Param("categoryId") Integer categoryId); List<Article> findPublishedByCategory(@Param("typeid") Integer typeid);
/** /**
* 使用@Modifying注解标记这是一个修改操作通常用于UPDATE或DELETE语句 * 使用@Modifying注解标记这是一个修改操作通常用于UPDATE或DELETE语句

View File

@@ -1,8 +1,32 @@
package com.qf.myafterprojecy.repository; package com.qf.myafterprojecy.repository;
import com.qf.myafterprojecy.pojo.Message; import com.qf.myafterprojecy.pojo.Message;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
public interface MessageRepository extends CrudRepository<Message, Integer> { import java.util.List;
@Repository
public interface MessageRepository extends JpaRepository<Message, Integer> {
// 根据文章ID查询消息
List<Message> findByArticleid(Integer articleid);
// 查询所有父消息(回复的根消息)
List<Message> findByParentidIsNull();
// 根据父消息ID查询回复
List<Message> findByParentid(Integer parentid);
// 根据昵称模糊查询消息
List<Message> findByNicknameContaining(String nickname);
// 查询指定文章下的所有父消息(根回复)
@Query("SELECT m FROM Message m WHERE m.articleid = ?1 AND m.parentid IS NULL ORDER BY m.createdAt DESC")
List<Message> findRootMessagesByArticleId(Integer articleId);
// 统计指定文章的评论数量
@Query("SELECT COUNT(m) FROM Message m WHERE m.articleid = ?1")
Long countByArticleId(Integer articleId);
} }

View File

@@ -0,0 +1,165 @@
package com.qf.myafterprojecy.runner;
import com.qf.myafterprojecy.pojo.Message;
import com.qf.myafterprojecy.pojo.ResponseMessage;
import com.qf.myafterprojecy.pojo.dto.MessageDto;
import com.qf.myafterprojecy.repository.MessageRepository;
import com.qf.myafterprojecy.service.IMessageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Optional;
/**
* 消息数据检查器,用于验证消息相关的业务代码是否正常工作
*/
@Component
public class MessageDataChecker implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(MessageDataChecker.class);
@Autowired
private MessageRepository messageRepository;
@Autowired
private IMessageService messageService;
@Override
public void run(String... args) throws Exception {
logger.info("===== 消息数据检查器开始运行 =====");
// 检查数据库中是否已有消息数据
long count = messageRepository.count();
logger.info("当前数据库中消息数量: {}", count);
// 如果没有消息数据,添加一些测试数据
if (count == 0) {
logger.info("数据库中没有消息数据,开始添加测试数据...");
addTestMessages();
}
// 测试查询方法
testQueryMethods();
// 测试服务层方法
testServiceMethods();
logger.info("===== 消息数据检查器运行结束 =====");
}
private void addTestMessages() {
// 添加第一篇文章的评论
Message message1 = new Message();
message1.setNickname("张三");
message1.setEmail("zhangsan@example.com");
message1.setContent("这是一篇很棒的文章!");
message1.setCreatedAt(new Date());
message1.setArticleid(1);
message1.setParentid(null); // 根评论
messageRepository.save(message1);
// 添加回复
Message reply1 = new Message();
reply1.setNickname("李四");
reply1.setEmail("lisi@example.com");
reply1.setContent("同意你的观点!");
reply1.setCreatedAt(new Date());
reply1.setArticleid(1);
reply1.setParentid(message1.getMessageid()); // 回复第一篇评论
messageRepository.save(reply1);
// 添加第二篇文章的评论
Message message2 = new Message();
message2.setNickname("王五");
message2.setEmail("wangwu@example.com");
message2.setContent("学到了很多东西,谢谢分享!");
message2.setCreatedAt(new Date());
message2.setArticleid(2);
message2.setParentid(null);
messageRepository.save(message2);
logger.info("成功添加了{}条测试消息数据", messageRepository.count());
}
private void testQueryMethods() {
logger.info("===== 测试Repository查询方法 =====");
// 测试根据文章ID查询
List<Message> article1Messages = messageRepository.findByArticleid(1);
logger.info("文章ID为1的消息数量: {}", article1Messages.size());
// 测试查询所有根消息
List<Message> rootMessages = messageRepository.findByParentidIsNull();
logger.info("根消息数量: {}", rootMessages.size());
// 测试根据昵称模糊查询
List<Message> zhangMessages = messageRepository.findByNicknameContaining("");
logger.info("昵称包含'张'的消息数量: {}", zhangMessages.size());
// 测试统计文章评论数量
Long article1Count = messageRepository.countByArticleId(1);
logger.info("文章ID为1的评论数量: {}", article1Count);
// 如果有根消息,测试查询回复
if (!rootMessages.isEmpty()) {
Integer firstRootId = rootMessages.get(0).getMessageid();
List<Message> replies = messageRepository.findByParentid(firstRootId);
logger.info("消息ID为{}的回复数量: {}", firstRootId, replies.size());
}
}
private void testServiceMethods() {
logger.info("===== 测试Service层方法 =====");
// 测试获取所有消息
ResponseMessage<Iterable<Message>> allMessagesResponse = messageService.getAllMessages();
logger.info("获取所有消息: 成功={}, 消息数量={}", allMessagesResponse.isSuccess(),
((List<Message>)allMessagesResponse.getData()).size());
// 测试根据ID获取消息
if (messageRepository.count() > 0) {
Message firstMessage = messageRepository.findAll().iterator().next();
Integer messageId = firstMessage.getMessageid();
ResponseMessage<Message> messageResponse = messageService.getMessageById(messageId);
logger.info("根据ID{}获取消息: 成功={}, 昵称={}", messageId,
messageResponse.isSuccess(),
messageResponse.getData() != null ? messageResponse.getData().getNickname() : "null");
// 测试获取指定文章的评论数量
ResponseMessage<Long> countResponse = messageService.getMessageCountByArticleId(1);
logger.info("获取文章ID为1的评论数量: 成功={}, 数量={}",
countResponse.isSuccess(), countResponse.getData());
// 测试获取根消息
ResponseMessage<List<Message>> rootResponse = messageService.getRootMessages();
logger.info("获取根消息: 成功={}, 数量={}",
rootResponse.isSuccess(),
rootResponse.getData() != null ? rootResponse.getData().size() : 0);
}
// 测试保存新消息
MessageDto newMessage = new MessageDto();
newMessage.setNickname("测试用户");
newMessage.setEmail("test@example.com");
newMessage.setContent("这是一条测试消息");
newMessage.setArticleid(1);
newMessage.setParentid(null);
ResponseMessage<Message> saveResponse = messageService.saveMessage(newMessage);
logger.info("保存新消息: 成功={}, 消息ID={}",
saveResponse.isSuccess(),
saveResponse.getData() != null ? saveResponse.getData().getMessageid() : "null");
// 如果保存成功,测试删除
if (saveResponse.isSuccess() && saveResponse.getData() != null) {
Integer savedId = saveResponse.getData().getMessageid();
ResponseMessage<Message> deleteResponse = messageService.deleteMessage(savedId);
logger.info("删除消息ID{}: 成功={}", savedId, deleteResponse.isSuccess());
}
}
}

View File

@@ -107,19 +107,6 @@ public class ArticleService implements IArticleService {
} }
} }
@Override
@Transactional(readOnly = true)
public ResponseMessage<List<Article>> getArticlesByAuthor(Integer authorId) {
try {
// 由于Article实体中没有authorId字段返回所有已发布的文章
List<Article> articles = articleRepository.findPublishedByAuthor();
return ResponseMessage.success(articles);
} catch (DataAccessException e) {
log.error("获取作者文章失败: {}", e.getMessage());
return ResponseMessage.failure("获取作者文章失败");
}
}
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseMessage<List<Article>> getArticlesByCategory(Integer categoryId) { public ResponseMessage<List<Article>> getArticlesByCategory(Integer categoryId) {

View File

@@ -12,7 +12,6 @@ public interface IArticleService {
ResponseMessage<Article> saveArticle(ArticleDto articleDto); ResponseMessage<Article> saveArticle(ArticleDto articleDto);
ResponseMessage<Article> updateArticle(Integer id, ArticleDto articleDto); ResponseMessage<Article> updateArticle(Integer id, ArticleDto articleDto);
ResponseMessage<Article> deleteArticle(Integer id); ResponseMessage<Article> deleteArticle(Integer id);
ResponseMessage<List<Article>> getArticlesByAuthor(Integer authorId); ResponseMessage<List<Article>> getArticlesByCategory(Integer typeid);
ResponseMessage<List<Article>> getArticlesByCategory(Integer categoryId);
ResponseMessage<List<Article>> getMostViewedArticles(); ResponseMessage<List<Article>> getMostViewedArticles();
} }

View File

@@ -4,6 +4,8 @@ import com.qf.myafterprojecy.pojo.Message;
import com.qf.myafterprojecy.pojo.ResponseMessage; import com.qf.myafterprojecy.pojo.ResponseMessage;
import com.qf.myafterprojecy.pojo.dto.MessageDto; import com.qf.myafterprojecy.pojo.dto.MessageDto;
import java.util.List;
public interface IMessageService { public interface IMessageService {
/** /**
* 获取所有消息的方法 * 获取所有消息的方法
@@ -29,4 +31,39 @@ public interface IMessageService {
* @return ResponseMessage<Message> 包含操作结果的响应消息其中泛型Message表示被删除的消息内容 * @return ResponseMessage<Message> 包含操作结果的响应消息其中泛型Message表示被删除的消息内容
*/ */
ResponseMessage<Message> deleteMessage(Integer id); ResponseMessage<Message> deleteMessage(Integer id);
// 新增方法
/**
* 根据文章ID查询消息
* @param articleId 文章ID
* @return 消息列表
*/
ResponseMessage<List<Message>> getMessagesByArticleId(Integer articleId);
/**
* 查询所有父消息(根回复)
* @return 父消息列表
*/
ResponseMessage<List<Message>> getRootMessages();
/**
* 根据父消息ID查询回复
* @param parentId 父消息ID
* @return 回复消息列表
*/
ResponseMessage<List<Message>> getRepliesByParentId(Integer parentId);
/**
* 根据昵称模糊查询消息
* @param nickname 昵称
* @return 匹配的消息列表
*/
ResponseMessage<List<Message>> searchMessagesByNickname(String nickname);
/**
* 获取指定文章的评论数量
* @param articleId 文章ID
* @return 评论数量
*/
ResponseMessage<Long> getMessageCountByArticleId(Integer articleId);
} }

View File

@@ -4,57 +4,209 @@ import com.qf.myafterprojecy.pojo.Message;
import com.qf.myafterprojecy.pojo.ResponseMessage; import com.qf.myafterprojecy.pojo.ResponseMessage;
import com.qf.myafterprojecy.pojo.dto.MessageDto; import com.qf.myafterprojecy.pojo.dto.MessageDto;
import com.qf.myafterprojecy.repository.MessageRepository; import com.qf.myafterprojecy.repository.MessageRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
//import static jdk.nashorn.internal.runtime.regexp.joni.Config.log; import java.util.Date;
import java.util.List;
import java.util.Optional;
@Service @Service
public class MessageService implements IMessageService { public class MessageService implements IMessageService {
private static final Logger logger = LoggerFactory.getLogger(MessageService.class);
@Autowired @Autowired
private MessageRepository messageRepository; private MessageRepository messageRepository;
@Override @Override
public ResponseMessage<Iterable<Message>> getAllMessages() { public ResponseMessage<Iterable<Message>> getAllMessages() {
return ResponseMessage.success(messageRepository.findAll(), "查询成功", true); try {
logger.info("查询所有消息");
Iterable<Message> messages = messageRepository.findAll();
return ResponseMessage.success(messages, "查询成功", true);
} catch (DataAccessException e) {
logger.error("查询所有消息失败", e);
return ResponseMessage.failure("查询消息失败:" + e.getMessage());
}
} }
@Override @Override
public ResponseMessage<Message> getMessageById(Integer id) { public ResponseMessage<Message> getMessageById(Integer id) {
return ResponseMessage.success(messageRepository.findById(id).orElse(null), "查询成功", true); if (id == null || id <= 0) {
logger.warn("获取消息时ID无效: {}", id);
return ResponseMessage.failure("消息ID无效");
}
try {
logger.info("根据ID查询消息: {}", id);
Optional<Message> messageOptional = messageRepository.findById(id);
if (messageOptional.isPresent()) {
return ResponseMessage.success(messageOptional.get(), "查询成功", true);
} else {
logger.warn("未找到ID为{}的消息", id);
return ResponseMessage.failure("未找到指定消息");
}
} catch (DataAccessException e) {
logger.error("查询消息失败: {}", id, e);
return ResponseMessage.failure("查询消息失败:" + e.getMessage());
}
} }
@Override @Override
public ResponseMessage<Message> saveMessage(MessageDto messageDto) { public ResponseMessage<Message> saveMessage(MessageDto messageDto) {
// 参数校验 // 参数校验
if (messageDto == null) { if (messageDto == null) {
logger.warn("保存消息时参数为空");
throw new IllegalArgumentException("MessageDto cannot be null"); throw new IllegalArgumentException("MessageDto cannot be null");
} }
// 业务逻辑校验 // 业务逻辑校验
if (StringUtils.isEmpty(messageDto.getContent())) { if (StringUtils.isEmpty(messageDto.getContent())) {
logger.warn("保存消息时内容为空");
throw new IllegalArgumentException("Message content cannot be empty"); throw new IllegalArgumentException("Message content cannot be empty");
} }
if (StringUtils.isEmpty(messageDto.getNickname())) {
logger.warn("保存消息时昵称为空");
throw new IllegalArgumentException("Message nickname cannot be empty");
}
// 调用Repository保存数据 // 调用Repository保存数据
try { try {
logger.info("保存消息: {}", messageDto.getNickname());
Message message = new Message(); Message message = new Message();
BeanUtils.copyProperties(messageDto,message); BeanUtils.copyProperties(messageDto, message);
message.setCreatedAt(new Date()); // 设置创建时间
// 如果是回复确保parentid有效
if (messageDto.getParentid() != null && messageDto.getParentid() > 0) {
if (!messageRepository.existsById(messageDto.getParentid())) {
logger.warn("回复的父消息不存在: {}", messageDto.getParentid());
return ResponseMessage.failure("回复的父消息不存在");
}
}
Message savedMessage = messageRepository.save(message); Message savedMessage = messageRepository.save(message);
logger.info("消息保存成功: {}", savedMessage.getMessageid());
return ResponseMessage.success(savedMessage, "保存成功", true); return ResponseMessage.success(savedMessage, "保存成功", true);
} catch (DataAccessException e) { } catch (DataAccessException e) {
return ResponseMessage.failure("Failed to save message"); logger.error("保存消息失败", e);
return ResponseMessage.failure("保存消息失败:" + e.getMessage());
} }
} }
@Override @Override
public ResponseMessage<Message> deleteMessage(Integer id) { public ResponseMessage<Message> deleteMessage(Integer id) {
if (id == null || id <= 0) {
logger.warn("删除消息时ID无效: {}", id);
return ResponseMessage.failure("消息ID无效");
}
try {
logger.info("删除消息: {}", id);
if (messageRepository.existsById(id)) { if (messageRepository.existsById(id)) {
messageRepository.deleteById(id); messageRepository.deleteById(id);
// 同时删除该消息的所有回复
List<Message> replies = messageRepository.findByParentid(id);
if (replies != null && !replies.isEmpty()) {
messageRepository.deleteAll(replies);
logger.info("同时删除了{}条回复消息", replies.size());
}
logger.info("消息删除成功: {}", id);
return ResponseMessage.success(null, "删除成功", true); return ResponseMessage.success(null, "删除成功", true);
} else { } else {
return ResponseMessage.failure("Message not found"); logger.warn("未找到要删除的消息: {}", id);
return ResponseMessage.failure("未找到要删除的消息");
}
} catch (DataAccessException e) {
logger.error("删除消息失败: {}", id, e);
return ResponseMessage.failure("删除消息失败:" + e.getMessage());
}
}
@Override
public ResponseMessage<List<Message>> getMessagesByArticleId(Integer articleId) {
if (articleId == null || articleId <= 0) {
logger.warn("根据文章ID查询消息时ID无效: {}", articleId);
return ResponseMessage.failure("文章ID无效");
}
try {
logger.info("根据文章ID查询消息: {}", articleId);
List<Message> messages = messageRepository.findByArticleid(articleId);
return ResponseMessage.success(messages, "查询成功", true);
} catch (DataAccessException e) {
logger.error("根据文章ID查询消息失败: {}", articleId, e);
return ResponseMessage.failure("查询消息失败:" + e.getMessage());
}
}
@Override
public ResponseMessage<List<Message>> getRootMessages() {
try {
logger.info("查询所有根消息");
List<Message> messages = messageRepository.findByParentidIsNull();
return ResponseMessage.success(messages, "查询成功", true);
} catch (DataAccessException e) {
logger.error("查询根消息失败", e);
return ResponseMessage.failure("查询消息失败:" + e.getMessage());
}
}
@Override
public ResponseMessage<List<Message>> getRepliesByParentId(Integer parentId) {
if (parentId == null || parentId <= 0) {
logger.warn("根据父消息ID查询回复时ID无效: {}", parentId);
return ResponseMessage.failure("父消息ID无效");
}
try {
logger.info("根据父消息ID查询回复: {}", parentId);
List<Message> replies = messageRepository.findByParentid(parentId);
return ResponseMessage.success(replies, "查询成功", true);
} catch (DataAccessException e) {
logger.error("查询回复消息失败: {}", parentId, e);
return ResponseMessage.failure("查询消息失败:" + e.getMessage());
}
}
@Override
public ResponseMessage<List<Message>> searchMessagesByNickname(String nickname) {
if (StringUtils.isEmpty(nickname)) {
logger.warn("根据昵称查询消息时昵称为空");
return ResponseMessage.failure("昵称不能为空");
}
try {
logger.info("根据昵称查询消息: {}", nickname);
List<Message> messages = messageRepository.findByNicknameContaining(nickname);
return ResponseMessage.success(messages, "查询成功", true);
} catch (DataAccessException e) {
logger.error("根据昵称查询消息失败: {}", nickname, e);
return ResponseMessage.failure("查询消息失败:" + e.getMessage());
}
}
@Override
public ResponseMessage<Long> getMessageCountByArticleId(Integer articleId) {
if (articleId == null || articleId <= 0) {
logger.warn("获取文章评论数量时ID无效: {}", articleId);
return ResponseMessage.failure("文章ID无效");
}
try {
logger.info("获取文章评论数量: {}", articleId);
Long count = messageRepository.countByArticleId(articleId);
return ResponseMessage.success(count, "查询成功", true);
} catch (DataAccessException e) {
logger.error("获取文章评论数量失败: {}", articleId, e);
return ResponseMessage.failure("查询评论数量失败:" + e.getMessage());
} }
} }
} }

View File

@@ -3,7 +3,7 @@ server.port=8080
spring.application.name=web_project spring.application.name=web_project
# 数据库配置 # 数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/webproject?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai spring.datasource.url=jdbc:mysql://localhost:3306/webproject?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&createDatabaseIfNotExist=true
spring.datasource.username=root spring.datasource.username=root
spring.datasource.password=123456 spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
@@ -27,28 +27,29 @@ spring.jpa.open-in-view=false
spring.jpa.properties.hibernate.jdbc.batch_size=30 spring.jpa.properties.hibernate.jdbc.batch_size=30
spring.jpa.properties.hibernate.order_inserts=true spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true spring.jpa.properties.hibernate.order_updates=true
spring.jpa.properties.hibernate.cache.use_second_level_cache=true # 暂时禁用Hibernate二级缓存和查询缓存
spring.jpa.properties.hibernate.cache.use_query_cache=true # spring.jpa.properties.hibernate.cache.use_second_level_cache=true
# spring.jpa.properties.hibernate.cache.use_query_cache=true
# 缓存配置 # 缓存配置
spring.cache.type=redis # spring.cache.type=redis
spring.cache.redis.time-to-live=1800000 # spring.cache.redis.time-to-live=1800000
spring.cache.redis.key-prefix=CACHE_ # spring.cache.redis.key-prefix=CACHE_
spring.cache.redis.use-key-prefix=true # spring.cache.redis.use-key-prefix=true
spring.cache.redis.cache-null-values=false # spring.cache.redis.cache-null-values=false
# Redis配置 # Redis配置
spring.redis.host=localhost # spring.redis.host=localhost
spring.redis.port=6379 # spring.redis.port=6379
spring.redis.password=123456 # spring.redis.password=123456
spring.redis.database=0 # spring.redis.database=0
spring.redis.timeout=10000ms # spring.redis.timeout=10000ms
# Redis连接池优化配置 # Redis连接池优化配置
spring.redis.lettuce.pool.max-active=8 #spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=10000ms #spring.redis.lettuce.pool.max-wait=10000ms
spring.redis.lettuce.pool.max-idle=8 #spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=2 #spring.redis.lettuce.pool.min-idle=2
spring.redis.lettuce.shutdown-timeout=100ms #spring.redis.lettuce.shutdown-timeout=100ms
# 日志配置 # 日志配置
logging.level.root=INFO logging.level.root=INFO
@@ -100,10 +101,10 @@ server.servlet.session.timeout=30m
server.session.tracking-modes=cookie server.session.tracking-modes=cookie
# 国际化配置 # 国际化配置
spring.mvc.locale-resolver=fixed #spring.mvc.locale-resolver=fixed
spring.mvc.locale=zh_CN #spring.mvc.locale=zh_CN
#
# 响应编码配置 ## 响应编码配置
spring.http.encoding.charset=UTF-8 #spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true #spring.http.encoding.enabled=true
spring.http.encoding.force=true #spring.http.encoding.force=true