feat: 实现消息、文章和分类的树形结构展示功能

refactor: 重构消息、文章和疯言疯语的分页查询接口
refactor(controller): 调整疯言疯语控制器的更新接口参数
refactor(service): 优化消息服务的分页查询逻辑

fix: 修复JWT认证过滤器中的令牌验证问题
fix(properties): 修正生产环境数据库配置

style: 清理无用代码并删除HelpController
This commit is contained in:
qingfeng1121
2025-12-23 13:57:54 +08:00
parent 15eca0d0b5
commit 33498d75c5
29 changed files with 813 additions and 3789 deletions

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -44,11 +44,12 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
try { try {
// 获取token // 获取token
String token = getTokenFromRequest(request); String token = getTokenFromRequest(request);
System.out.println(token); // System.out.println(token);
if (token != null && validateToken(token)) { if (token != null) {
if (validateToken(token)) {
// 从token中获取用户名 // 从token中获取用户名
String username = jwtUtils.getUsernameFromToken(token); String username = jwtUtils.getUsernameFromToken(token);
System.out.println("username: " + username); // System.out.println("username: " + username);
// 加载用户信息 // 加载用户信息
UserDetails userDetails = userDetailsService.loadUserByUsername(username); UserDetails userDetails = userDetailsService.loadUserByUsername(username);
@@ -59,10 +60,36 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
// 设置认证信息到上下文 // 设置认证信息到上下文
SecurityContextHolder.getContext().setAuthentication(authentication); SecurityContextHolder.getContext().setAuthentication(authentication);
} else {
// 如果token无效但不为空返回401
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json");
response.getWriter().write("{\"message\":\"无效的认证令牌\"}");
return;
} }
}
} catch (io.jsonwebtoken.ExpiredJwtException e) {
// 专门处理令牌过期返回401和明确的错误信息
logger.error("令牌已过期: {}", e.getMessage());
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json");
response.getWriter().write("{\"message\":\"认证令牌已过期\"}");
return;
} catch (io.jsonwebtoken.JwtException e) {
// 其他JWT异常
logger.error("无效的token格式: {}", e.getMessage());
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json");
response.getWriter().write("{\"message\":\"无效的令牌格式\"}");
return;
} catch (Exception e) { } catch (Exception e) {
logger.error("无法设置用户认证: {}", e); logger.error("无法设置用户认证: {}", e);
SecurityContextHolder.clearContext(); SecurityContextHolder.clearContext();
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json");
response.getWriter().write("{\"message\":\"认证失败\"}");
return;
} }
filterChain.doFilter(request, response); filterChain.doFilter(request, response);
@@ -88,8 +115,17 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
String username = jwtUtils.getUsernameFromToken(token); String username = jwtUtils.getUsernameFromToken(token);
UserDetails userDetails = userDetailsService.loadUserByUsername(username); UserDetails userDetails = userDetailsService.loadUserByUsername(username);
return jwtUtils.validateToken(token, userDetails); return jwtUtils.validateToken(token, userDetails);
} catch (io.jsonwebtoken.ExpiredJwtException e) {
// 专门处理令牌过期,记录日志但不抛出异常
logger.error("令牌已过期: {}", e.getMessage());
return false;
} catch (io.jsonwebtoken.JwtException e) {
// 其他JWT异常
logger.error("无效的token格式: {}", e.getMessage());
return false;
} catch (Exception e) { } catch (Exception e) {
logger.error("无效的token: {}", e); // 其他异常
logger.error("验证token失败: {}", e.getMessage());
return false; return false;
} }
} }

View File

@@ -3,11 +3,9 @@ package com.qf.myafterprojecy.controller;
import com.qf.myafterprojecy.exceptopn.ResponseMessage; import com.qf.myafterprojecy.exceptopn.ResponseMessage;
import com.qf.myafterprojecy.pojo.Article; import com.qf.myafterprojecy.pojo.Article;
import com.qf.myafterprojecy.pojo.dto.ArticleDto; import com.qf.myafterprojecy.pojo.dto.ArticleDto;
import com.qf.myafterprojecy.pojo.dto.ArticleTreeDto;
import com.qf.myafterprojecy.pojo.dto.ArriclePageDto; import com.qf.myafterprojecy.pojo.dto.ArriclePageDto;
import com.qf.myafterprojecy.service.IArticleService; import com.qf.myafterprojecy.service.IArticleService;
import org.springframework.data.domain.Page;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@@ -52,7 +50,7 @@ public class ArticleController {
* @return 返回包含文章列表的ResponseMessage对象 * @return 返回包含文章列表的ResponseMessage对象
*/ */
@GetMapping("/status/{status}") @GetMapping("/status/{status}")
public ResponseMessage<List<Article>> getArticlesByStatus(@PathVariable Integer status) { public ResponseMessage<List<ArticleTreeDto>> getArticlesByStatus(@PathVariable Integer status) {
return articleService.getArticlesByStatus(status); return articleService.getArticlesByStatus(status);
} }
@@ -66,7 +64,7 @@ public class ArticleController {
// api/articles/status/page?status=1&page=1&size=2 // api/articles/status/page?status=1&page=1&size=2
// get 只能这样不能传递json // get 只能这样不能传递json
@GetMapping("/status/page") @GetMapping("/status/page")
public ResponseMessage<Page<Article>> getArticlesByStatusWithPagination(ArriclePageDto pageDto) { public ResponseMessage<List<ArticleTreeDto>> getArticlesByStatusWithPagination(ArriclePageDto pageDto) {
return articleService.getArticlesByStatusWithPagination(pageDto); return articleService.getArticlesByStatusWithPagination(pageDto);
} }
@@ -93,7 +91,7 @@ public class ArticleController {
* @return 返回包含文章列表的ResponseMessage对象 * @return 返回包含文章列表的ResponseMessage对象
*/ */
@GetMapping("/title/{title}") @GetMapping("/title/{title}")
public ResponseMessage<List<Article>> getArticlesByTitle(@PathVariable String title) { public ResponseMessage<List<ArticleTreeDto>> getArticlesByTitle(@PathVariable String title) {
return articleService.getArticlesByTitle(title); return articleService.getArticlesByTitle(title);
} }

View File

@@ -16,7 +16,7 @@ import java.util.List;
import javax.validation.Valid; import javax.validation.Valid;
@RestController @RestController
@RequestMapping("/api/category-attributes") @RequestMapping("/api/categoryattributes")
@Validated @Validated
public class CategoryAttributeController { public class CategoryAttributeController {

View File

@@ -3,6 +3,7 @@ package com.qf.myafterprojecy.controller;
import com.qf.myafterprojecy.exceptopn.ResponseMessage; import com.qf.myafterprojecy.exceptopn.ResponseMessage;
import com.qf.myafterprojecy.pojo.Category; import com.qf.myafterprojecy.pojo.Category;
import com.qf.myafterprojecy.pojo.dto.CategoryDto; import com.qf.myafterprojecy.pojo.dto.CategoryDto;
import com.qf.myafterprojecy.pojo.dto.CategoryTreeDto;
import com.qf.myafterprojecy.service.ICategoryService; import com.qf.myafterprojecy.service.ICategoryService;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -49,7 +50,16 @@ public class CategoryController {
log.info("接收获取所有分类列表的请求"); log.info("接收获取所有分类列表的请求");
return categoryService.getAllCategories(); return categoryService.getAllCategories();
} }
// 分类树
/**
* 获取分类树结构
* @return 返回分类树结构
*/
@GetMapping("/tree")
public ResponseMessage<List<CategoryTreeDto>> getCategoryTree() {
log.info("接收获取分类树结构的请求");
return categoryService.getCategoryTree();
}
/** /**
* 创建新分类 * 创建新分类
* @param categoryDto 分类数据传输对象 * @param categoryDto 分类数据传输对象

View File

@@ -1,74 +0,0 @@
package com.qf.myafterprojecy.controller;
import org.commonmark.node.Node;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 帮助控制器类处理前端调用api/help请求
* 提供README_API.md文件的读取和返回功能
*/
@RestController
@RequestMapping("/api/help")
public class HelpController {
/**
* 获取README_API.md文件内容
* @return 返回包含README_API.md文件内容的ResponseMessage对象
*/
@GetMapping
public ResponseMessage<String> getReadmeApi() {
try {
// 获取项目根目录
String rootPath = System.getProperty("user.dir") ;
// 构建README_API.md文件路径
File readmeFile = new File(rootPath, "README_API.md");
// 检查文件是否存在
if (!readmeFile.exists() || !readmeFile.isFile()) {
// 如果不存在,尝试使用类路径资源加载
try {
ClassPathResource resource = new ClassPathResource("README_API.md");
String markdownContent = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()), StandardCharsets.UTF_8);
// 将Markdown转换为HTML
String htmlContent = convertMarkdownToHtml(markdownContent);
return ResponseMessage.success(htmlContent, "获取API文档成功");
} catch (IOException e) {
return ResponseMessage.error("未找到README_API.md文件");
}
}
// 读取文件内容
String markdownContent = new String(FileCopyUtils.copyToByteArray(new FileInputStream(readmeFile)), StandardCharsets.UTF_8);
// 将Markdown转换为HTML
String htmlContent = convertMarkdownToHtml(markdownContent);
return ResponseMessage.success(htmlContent, "获取API文档成功");
} catch (IOException e) {
return ResponseMessage.error("读取README_API.md文件失败: " + e.getMessage());
}
}
/**
* 将Markdown文本转换为HTML
* @param markdown 原始Markdown文本
* @return 转换后的HTML字符串
*/
private String convertMarkdownToHtml(String markdown) {
Parser parser = Parser.builder().build();
Node document = parser.parse(markdown);
HtmlRenderer renderer = HtmlRenderer.builder().build();
return renderer.render(document);
}
}

View File

@@ -4,6 +4,7 @@ import com.qf.myafterprojecy.exceptopn.ResponseMessage;
import com.qf.myafterprojecy.pojo.Message; import com.qf.myafterprojecy.pojo.Message;
import com.qf.myafterprojecy.pojo.dto.MessageDto; import com.qf.myafterprojecy.pojo.dto.MessageDto;
import com.qf.myafterprojecy.pojo.dto.MessagePageDto; import com.qf.myafterprojecy.pojo.dto.MessagePageDto;
import com.qf.myafterprojecy.pojo.dto.MessageTreeDto;
import com.qf.myafterprojecy.service.IMessageService; import com.qf.myafterprojecy.service.IMessageService;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -35,7 +36,7 @@ public class MessageController {
* 分页查询消息 * 分页查询消息
*/ */
@GetMapping("/page") @GetMapping("/page")
public ResponseMessage<List<Message>> getMessagesByPage(MessagePageDto messagePageDto) { public ResponseMessage<List<MessageTreeDto>> getMessagesByPage(MessagePageDto messagePageDto) {
logger.info("接收分页查询消息的请求: {}", messagePageDto); logger.info("接收分页查询消息的请求: {}", messagePageDto);
return messageService.getMessagesByPage(messagePageDto); return messageService.getMessagesByPage(messagePageDto);
} }
@@ -45,8 +46,8 @@ public class MessageController {
* @return 消息数量 * @return 消息数量
* 文章ID为null时返回所有消息数量 * 文章ID为null时返回所有消息数量
*/ */
@GetMapping("/count") @GetMapping("/count/{articleId}")
public ResponseMessage<Integer> getMessageCount( Integer articleId) { public ResponseMessage<Integer> getMessageCount(@PathVariable Integer articleId) {
logger.info("接收获取消息数量的请求: {}", articleId); logger.info("接收获取消息数量的请求: {}", articleId);
return messageService.getMessageCountByArticleId(articleId); return messageService.getMessageCountByArticleId(articleId);
} }
@@ -62,11 +63,11 @@ public class MessageController {
/** /**
* 根据文章ID获取消息列表 * 根据文章ID获取消息列表
*/ */
@GetMapping("/article/{articleId}") // @GetMapping("/article/{articleId}")
public ResponseMessage<List<Message>> getMessagesByArticleId(@PathVariable Integer articleId) { // public ResponseMessage<List<Message>> getMessagesByArticleId(@PathVariable Integer articleId) {
logger.info("接收根据文章ID获取消息的请求: {}", articleId); // logger.info("接收根据文章ID获取消息的请求: {}", articleId);
return messageService.getMessagesByArticleId(articleId); // return messageService.getMessagesByArticleId(articleId);
} // }
/** /**
* 获取所有根消息(非回复的消息) * 获取所有根消息(非回复的消息)

View File

@@ -3,6 +3,8 @@ package com.qf.myafterprojecy.controller;
import com.qf.myafterprojecy.exceptopn.ResponseMessage; import com.qf.myafterprojecy.exceptopn.ResponseMessage;
import com.qf.myafterprojecy.pojo.Nonsense; import com.qf.myafterprojecy.pojo.Nonsense;
import com.qf.myafterprojecy.pojo.dto.NonsenseDto; import com.qf.myafterprojecy.pojo.dto.NonsenseDto;
import com.qf.myafterprojecy.pojo.dto.NonsensePageDto;
import com.qf.myafterprojecy.service.INonsenseService; import com.qf.myafterprojecy.service.INonsenseService;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -37,15 +39,14 @@ public class NonsenseController {
} }
/** /**
* 根据状态获取疯言疯语内容 * 根据分页信息获取疯言疯语内容
* @param status 状态0未发表 1已发表 2已删除 * @param page 分页信息
* @return 疯言疯语内容列表 * @return 疯言疯语内容列表
*/ */
@GetMapping("/status/{status}") @GetMapping("/page")
public ResponseMessage<List<Nonsense>> getNonsenseByStatus( public ResponseMessage<List<Nonsense>> getNonsenseByStatus(NonsensePageDto page) {
@PathVariable("status") Integer status) { logger.info("请求获取状态为{}的疯言疯语内容, 分页信息: {}", page.getStatus(), page);
logger.info("请求获取状态为{}的疯言疯语内容", status); return nonsenseService.getNonsenseByStatus(page);
return nonsenseService.getNonsenseByStatus(status);
} }
/** /**
@@ -78,11 +79,11 @@ public class NonsenseController {
* @param nonsenseDto 疯言疯语内容数据 * @param nonsenseDto 疯言疯语内容数据
* @return 更新结果 * @return 更新结果
*/ */
@PutMapping("/{id}") @PutMapping()
@PreAuthorize("hasRole('ADMIN')") @PreAuthorize("hasRole('ADMIN')")
public ResponseMessage<Nonsense> updateNonsense(@PathVariable("id") Integer id, @Valid @RequestBody NonsenseDto nonsenseDto) { public ResponseMessage<Nonsense> updateNonsense(@RequestBody NonsenseDto nonsenseDto) {
logger.info("请求更新ID为{}的疯言疯语内容", id); logger.info("请求更新ID为{}的疯言疯语内容", nonsenseDto.getId());
return nonsenseService.updateNonsense(id, nonsenseDto); return nonsenseService.updateNonsense(nonsenseDto);
} }
/** /**

View File

@@ -13,6 +13,6 @@ public class GlobalExceptionHandler {
@ExceptionHandler(value=Exception.class) @ExceptionHandler(value=Exception.class)
public ResponseMessage<String> handleException(Exception e, HttpServletRequest request) { public ResponseMessage<String> handleException(Exception e, HttpServletRequest request) {
logger.error("请求路径:{},异常消息:{}",request.getRequestURI(),e.getMessage()); logger.error("请求路径:{},异常消息:{}",request.getRequestURI(),e.getMessage());
return new ResponseMessage<>(500,"服务器异常",e.getMessage()); return new ResponseMessage<String>(500,"服务器异常",e.getMessage(),false);
} }
} }

View File

@@ -18,6 +18,8 @@ public class ResponseMessage<T> {
private boolean success; private boolean success;
// 响应数据,泛型类型,支持不同类型的数据 // 响应数据,泛型类型,支持不同类型的数据
private T data; private T data;
// 分页总页数,仅在分页查询时使用
private Integer totalPages;
/** /**
* 构造方法,用于创建响应消息对象 * 构造方法,用于创建响应消息对象
@@ -25,11 +27,13 @@ public class ResponseMessage<T> {
* @param code 状态码 * @param code 状态码
* @param message 响应消息 * @param message 响应消息
* @param data 响应数据 * @param data 响应数据
* @param totalPages 分页总页数
*/ */
public ResponseMessage(Integer code, String message, T data) { public ResponseMessage(Integer code, String message, T data, Integer totalPages) {
this.code = code; this.code = code;
this.message = message; this.message = message;
this.data = data; this.data = data;
this.totalPages = totalPages;
// 自动根据状态码判断是否成功 // 自动根据状态码判断是否成功
this.success = code >= 200 && code < 300; this.success = code >= 200 && code < 300;
} }
@@ -41,6 +45,22 @@ public class ResponseMessage<T> {
* @param data 响应数据 * @param data 响应数据
* @param success 是否成功 * @param success 是否成功
*/ */
public ResponseMessage(Integer code, String message, T data, boolean success, Integer totalPages) {
this.code = code;
this.message = message;
this.data = data;
this.success = success;
this.totalPages = totalPages;
}
/**
* 完整参数的构造方法
* @param code 状态码
* @param message 响应消息
* @param data 响应数据
* @param success 是否成功
* @param totalPages 分页总页数
*/
public ResponseMessage(Integer code, String message, T data, boolean success) { public ResponseMessage(Integer code, String message, T data, boolean success) {
this.code = code; this.code = code;
this.message = message; this.message = message;
@@ -48,6 +68,7 @@ public class ResponseMessage<T> {
this.success = success; this.success = success;
} }
// ----------------------------------- 成功响应方法 ----------------------------------- // ----------------------------------- 成功响应方法 -----------------------------------
/** /**
@@ -218,8 +239,8 @@ public class ResponseMessage<T> {
* @param <T> 数据类型 * @param <T> 数据类型
* @return 分页响应对象 * @return 分页响应对象
*/ */
public static <T> ResponseMessage<T> page(T data, String message) { public static <T> ResponseMessage<T> page(T data, String message, Integer totalPages) {
return new ResponseMessage<>(HttpStatus.OK.value(), message, data, true); return new ResponseMessage<>(HttpStatus.OK.value(), message, data, true, totalPages);
} }
public Integer getCode() { public Integer getCode() {

View File

@@ -0,0 +1,112 @@
package com.qf.myafterprojecy.pojo.dto;
import java.time.LocalDateTime;
public class ArticleTreeDto {
private Integer articleid;
private String title;
private String content;
private String img;
private Integer viewcount;
private Integer likes; // 点赞数
private Integer status;
private String markdownscontent;
private Integer attributeid;
private String attributename;
private Integer userid;
private String username;
private Integer commentcount; // 评论数
private LocalDateTime createtime;
private LocalDateTime updatetime;
public Integer getArticleid() {
return articleid;
}
public void setArticleid(Integer articleid) {
this.articleid = articleid;
}
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 getImg() {
return img;
}
public void setImg(String img) {
this.img = img;
}
public Integer getViewcount() {
return viewcount;
}
public void setViewcount(Integer viewcount) {
this.viewcount = viewcount;
}
public Integer getLikes() {
return likes;
}
public void setLikes(Integer likes) {
this.likes = likes;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getMarkdownscontent() {
return markdownscontent;
}
public void setMarkdownscontent(String markdownscontent) {
this.markdownscontent = markdownscontent;
}
public Integer getAttributeid() {
return attributeid;
}
public void setAttributeid(Integer attributeid) {
this.attributeid = attributeid;
}
public String getAttributename() {
return attributename;
}
public void setAttributename(String attributename) {
this.attributename = attributename;
}
public Integer getUserid() {
return userid;
}
public void setUserid(Integer userid) {
this.userid = userid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public LocalDateTime getCreatetime() {
return createtime;
}
public void setCreatetime(LocalDateTime createtime) {
this.createtime = createtime;
}
public LocalDateTime getUpdatetime() {
return updatetime;
}
public void setUpdatetime(LocalDateTime updatetime) {
this.updatetime = updatetime;
}
public Integer getCommentcount() {
return commentcount;
}
public void setCommentcount(Integer commentcount) {
this.commentcount = commentcount;
}
}

View File

@@ -0,0 +1,40 @@
package com.qf.myafterprojecy.pojo.dto;
import java.util.List;
import com.qf.myafterprojecy.pojo.Categoryattribute;
public class CategoryTreeDto {
private Integer id;
private String name;
private List<Categoryattribute> children;
// 构造方法
public CategoryTreeDto() {
}
// 全参构造方法
public CategoryTreeDto(Integer id, String name, List<Categoryattribute> children) {
this.id = id;
this.name = name;
this.children = children;
}
// getter和setter方法
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Categoryattribute> getChildren() {
return children;
}
public void setChildren(List<Categoryattribute> children) {
this.children = children;
}
}

View File

@@ -0,0 +1,43 @@
package com.qf.myafterprojecy.pojo.dto;
import java.util.ArrayList;
import java.util.List;
public class MessageTreeDto extends MessageDto {
private List<MessageTreeDto> children = new ArrayList<>();
private int replyCount = 0;
private String replyToNickname; // 被回复者昵称
private Integer replyToId; // 被回复消息ID
public List<MessageTreeDto> getChildren() {
return children;
}
public void setChildren(List<MessageTreeDto> children) {
this.children = children;
}
public int getReplyCount() {
return replyCount;
}
public void setReplyCount(int replyCount) {
this.replyCount = replyCount;
}
public String getReplyToNickname() {
return replyToNickname;
}
public void setReplyToNickname(String replyToNickname) {
this.replyToNickname = replyToNickname;
}
public Integer getReplyToId() {
return replyToId;
}
public void setReplyToId(Integer replyToId) {
this.replyToId = replyToId;
}
}

View File

@@ -0,0 +1,35 @@
package com.qf.myafterprojecy.pojo.dto;
public class NonsensePageDto {
private Integer status;
private Integer pageNum;
private Integer pageSize;
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Integer getPageNum() {
return pageNum;
}
public void setPageNum(Integer pageNum) {
this.pageNum = pageNum;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
@Override
public String toString() {
return "Page [pageNum=" + pageNum + ", pageSize=" + pageSize + "]";
}
public NonsensePageDto(Integer pageNum, Integer pageSize) {
super();
this.pageNum = pageNum;
this.pageSize = pageSize;
}
}

View File

@@ -43,4 +43,10 @@ public interface CategoryAttributeRepository extends JpaRepository<Categoryattri
* @return 属性对象 * @return 属性对象
*/ */
Optional<Categoryattribute> findByCategoryidAndAttributename(Integer categoryid, String attributename); Optional<Categoryattribute> findByCategoryidAndAttributename(Integer categoryid, String attributename);
/**
* 根据分类ID获取分类属性列表
* @param categoryids 分类ID列表
* @return 返回包含所有分类属性的列表
*/
List<Categoryattribute> findByCategoryidIn(List<Integer> categoryids);
} }

View File

@@ -16,17 +16,11 @@ import java.util.List;
@Repository @Repository
public interface MessageRepository extends JpaRepository<Message, Integer> { public interface MessageRepository extends JpaRepository<Message, Integer> {
// 根据文章ID查询消息
/**
* 根据文章ID查询消息
* @param articleid 文章ID
* @return 文章下的消息列表
*/
List<Message> findByArticleid(Integer articleid);
// 查询所有父消息(回复的根消息) // 查询所有父消息(回复的根消息)
/** /**
* 查询所有父消息(回复的根消息) * 查询所有父消息(回复的根消息)
*
* @return 根回复消息列表 * @return 根回复消息列表
*/ */
List<Message> findByParentidIsNull(); List<Message> findByParentidIsNull();
@@ -34,6 +28,7 @@ public interface MessageRepository extends JpaRepository<Message, Integer> {
// 根据父消息ID查询回复 // 根据父消息ID查询回复
/** /**
* 根据父消息ID查询回复 * 根据父消息ID查询回复
*
* @param parentid 父消息ID * @param parentid 父消息ID
* @return 回复消息列表 * @return 回复消息列表
*/ */
@@ -42,6 +37,7 @@ public interface MessageRepository extends JpaRepository<Message, Integer> {
// 根据昵称模糊查询消息 // 根据昵称模糊查询消息
/** /**
* 根据昵称模糊查询消息 * 根据昵称模糊查询消息
*
* @param nickname 昵称关键词 * @param nickname 昵称关键词
* @return 包含关键词的消息列表 * @return 包含关键词的消息列表
*/ */
@@ -49,35 +45,59 @@ public interface MessageRepository extends JpaRepository<Message, Integer> {
/** /**
* 查询指定文章下的所有父消息(根回复) * 查询指定文章下的所有父消息(根回复)
*
* @param articleId 文章ID * @param articleId 文章ID
* @return 根回复消息列表 * @return 根回复消息列表
*/ */
@Query("SELECT m FROM Message m WHERE m.articleid = :articleId AND m.parentid IS NULL ORDER BY m.createdAt DESC") @Query("SELECT m FROM Message m WHERE m.articleid = :articleId AND m.parentid IS NULL ORDER BY m.createdAt DESC")
List<Message> findRootMessagesByArticleId(@Param("articleId") Integer articleId); List<Message> findRootMessagesByArticleId(@Param("articleId") Integer articleId);
/** /**
* 点赞数增加 * 点赞数增加
*
* @param messageId 消息ID * @param messageId 消息ID
*/ */
@Modifying @Modifying
@Query("UPDATE Message m SET m.likes = COALESCE(m.likes, 0) + 1 WHERE m.messageid = :messageId") @Query("UPDATE Message m SET m.likes = COALESCE(m.likes, 0) + 1 WHERE m.messageid = :messageId")
void incrementLikes(@Param("messageId") Integer messageId); void incrementLikes(@Param("messageId") Integer messageId);
// 统计指定文章的评论数量
// 统计指定文章下的主留言数量
/** /**
* 根据文章ID分页查询消息 * 根据文章ID分页查询消息(不包括回复)
* @param articleid 文章ID * @param articleid 文章ID (可选)
* @param pageable 分页信息 * @param pageable 分页信息
* @return 分页消息列表 * @return 分页消息列表
*/ */
@Query("SELECT m FROM Message m WHERE m.articleid = :articleId ORDER BY m.createdAt DESC") @Query("SELECT m FROM Message m WHERE m.articleid = :articleId AND m.parentid IS NULL ORDER BY m.createdAt ASC")
Page<Message> findByArticleId(@Param("articleId") Integer articleId, PageRequest pageable); Page<Message> findByArticleId(@Param("articleId") Integer articleId, PageRequest pageable);
/** /**
* 根据页查询消息 * 根据父ID列表查询所有子回复包括多级嵌套
* 注意:这里不递归查数据库,而是查出所有 parentid 属于某集合的留言,
* 然后在 Java 中递归构建树(适用于层级不深的场景,如评论)
* @param parentIds 父ID列表
* @return 分页回复消息列表
*/
@Query("SELECT m FROM Message m WHERE m.parentid IN :parentIds")
List<Message> findRepliesByParentIds( @Param("parentIds") List<Integer> parentIds);
// 根据分页查询消息(不包括回复)
/**
* 根据分页查询消息(不包括回复)
* 不包含文章ID的消息
* @param pageable 分页信息 * @param pageable 分页信息
* @return 分页消息列表 * @return 分页消息列表
*/ */
@Query("SELECT m FROM Message m WHERE m.articleid IS NULL ORDER BY m.createdAt DESC") @Query("SELECT m FROM Message m WHERE m.articleid IS NULL AND m.parentid IS NULL ORDER BY m.createdAt ASC")
Page<Message> findAllMessages(PageRequest pageable); Page<Message> findAllMessages(PageRequest pageable);
/**
* 根据articleId查询所有回复消息
* @param articleId 文章ID
* @return 回复消息列表
*/
@Query("SELECT m FROM Message m WHERE m.articleid = :articleId AND m.parentid IS NOT NULL ORDER BY m.createdAt ASC")
List<Message> findAllRepliesByArticleId(@Param("articleId") Integer articleId);
/** /**
* 统计指定文章下的回复消息数量 * 统计指定文章下的回复消息数量
* @param articleId 文章ID * @param articleId 文章ID
@@ -87,8 +107,8 @@ public interface MessageRepository extends JpaRepository<Message, Integer> {
Integer countReplyByArticleId(@Param("articleId") Integer articleId); Integer countReplyByArticleId(@Param("articleId") Integer articleId);
/** /**
* 统计指定文章id parentid为空的回复消息数量 * 统计所有回复消息数量
* @param articleId 文章ID * 不包含文章ID的消息
* @return 回复消息数量 * @return 回复消息数量
*/ */
@Query("SELECT COUNT(m) FROM Message m WHERE m.articleid IS NULL AND m.parentid IS NULL") @Query("SELECT COUNT(m) FROM Message m WHERE m.articleid IS NULL AND m.parentid IS NULL")

View File

@@ -5,6 +5,8 @@ import com.qf.myafterprojecy.pojo.Nonsense;
import java.util.List; import java.util.List;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@@ -18,4 +20,13 @@ public interface NonsenseRepository extends JpaRepository<Nonsense, Integer> {
*/ */
@Query("SELECT n FROM Nonsense n WHERE n.status = :status") @Query("SELECT n FROM Nonsense n WHERE n.status = :status")
List<Nonsense> findByStatus(@Param("status") Integer status); List<Nonsense> findByStatus(@Param("status") Integer status);
/**
* 根据状态分页获取文章列表
* @param status 文章状态0未发表 1已发表 2已删除
* @param pageable 分页信息
* @return 返回包含文章列表的ResponseMessage对象
*/
@Query("SELECT n FROM Nonsense n WHERE n.status = :status")
Page<Nonsense> findPageByStatus(@Param("status") Integer status, PageRequest pageable);
} }

View File

@@ -4,6 +4,8 @@ import com.qf.myafterprojecy.exceptopn.ResponseMessage;
import com.qf.myafterprojecy.pojo.Article; import com.qf.myafterprojecy.pojo.Article;
import com.qf.myafterprojecy.pojo.dto.ArriclePageDto; import com.qf.myafterprojecy.pojo.dto.ArriclePageDto;
import com.qf.myafterprojecy.pojo.dto.ArticleDto; import com.qf.myafterprojecy.pojo.dto.ArticleDto;
import com.qf.myafterprojecy.pojo.dto.ArticleTreeDto;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import java.util.List; import java.util.List;
@@ -19,13 +21,13 @@ public interface IArticleService {
* @param title 文章标题的一部分,用于模糊查询 * @param title 文章标题的一部分,用于模糊查询
* @return 返回符合查询条件的文章列表 * @return 返回符合查询条件的文章列表
*/ */
ResponseMessage<List<Article>> getArticlesByTitle(String title); ResponseMessage<List<ArticleTreeDto>> getArticlesByTitle(String title);
/** /**
* 根据状态获取文章列表 * 根据状态获取文章列表
* @param status 文章状态0未发表 1已发表 2已删除 * @param status 文章状态0未发表 1已发表 2已删除
* @return 返回包含文章列表的ResponseMessage对象 * @return 返回包含文章列表的ResponseMessage对象
*/ */
ResponseMessage<List<Article>> getArticlesByStatus(Integer status); ResponseMessage<List<ArticleTreeDto>> getArticlesByStatus(Integer status);
/** /**
* 获取文章数量 * 获取文章数量
* @param status 文章状态0未发表 1已发表 2已删除 * @param status 文章状态0未发表 1已发表 2已删除
@@ -104,5 +106,5 @@ public interface IArticleService {
* @param size 每页大小 * @param size 每页大小
* @return 返回包含分页文章列表的ResponseMessage对象 * @return 返回包含分页文章列表的ResponseMessage对象
*/ */
ResponseMessage<Page<Article>> getArticlesByStatusWithPagination(ArriclePageDto arriclePageDto); ResponseMessage<List<ArticleTreeDto>> getArticlesByStatusWithPagination(ArriclePageDto arriclePageDto);
} }

View File

@@ -3,6 +3,7 @@ package com.qf.myafterprojecy.service;
import com.qf.myafterprojecy.exceptopn.ResponseMessage; import com.qf.myafterprojecy.exceptopn.ResponseMessage;
import com.qf.myafterprojecy.pojo.Category; import com.qf.myafterprojecy.pojo.Category;
import com.qf.myafterprojecy.pojo.dto.CategoryDto; import com.qf.myafterprojecy.pojo.dto.CategoryDto;
import com.qf.myafterprojecy.pojo.dto.CategoryTreeDto;
import java.util.List; import java.util.List;
@@ -19,7 +20,11 @@ public interface ICategoryService {
* @return 返回分类列表 * @return 返回分类列表
*/ */
ResponseMessage<List<Category>> getAllCategories(); ResponseMessage<List<Category>> getAllCategories();
/**
* 获取分类树
* @return 返回分类树
*/
ResponseMessage<List<CategoryTreeDto>> getCategoryTree();
/** /**
* 保存新分类 * 保存新分类
* @param categoryDto 分类数据传输对象 * @param categoryDto 分类数据传输对象

View File

@@ -4,8 +4,7 @@ import com.qf.myafterprojecy.exceptopn.ResponseMessage;
import com.qf.myafterprojecy.pojo.Message; import com.qf.myafterprojecy.pojo.Message;
import com.qf.myafterprojecy.pojo.dto.MessageDto; import com.qf.myafterprojecy.pojo.dto.MessageDto;
import com.qf.myafterprojecy.pojo.dto.MessagePageDto; import com.qf.myafterprojecy.pojo.dto.MessagePageDto;
import com.qf.myafterprojecy.pojo.dto.MessageTreeDto;
import java.util.List; import java.util.List;
@@ -22,7 +21,7 @@ public interface IMessageService {
* @param id * @param id
* @return * @return
*/ */
ResponseMessage<List<Message>> getMessagesByPage(MessagePageDto messagePageDto); ResponseMessage<List<MessageTreeDto>> getMessagesByPage(MessagePageDto messagePageDto);
/** /**
* 获取回复消息条数 如果id为空获取文章id为空的消息条数 * 获取回复消息条数 如果id为空获取文章id为空的消息条数
* @param articleId 文章id * @param articleId 文章id
@@ -68,7 +67,7 @@ public interface IMessageService {
* @param articleId 文章ID * @param articleId 文章ID
* @return 消息列表 * @return 消息列表
*/ */
ResponseMessage<List<Message>> getMessagesByArticleId(Integer articleId); // ResponseMessage<List<Message>> getMessagesByArticleId(Integer articleId);
/** /**
* 查询所有父消息(根回复) * 查询所有父消息(根回复)

View File

@@ -3,6 +3,7 @@ package com.qf.myafterprojecy.service;
import com.qf.myafterprojecy.exceptopn.ResponseMessage; import com.qf.myafterprojecy.exceptopn.ResponseMessage;
import com.qf.myafterprojecy.pojo.Nonsense; import com.qf.myafterprojecy.pojo.Nonsense;
import com.qf.myafterprojecy.pojo.dto.NonsenseDto; import com.qf.myafterprojecy.pojo.dto.NonsenseDto;
import com.qf.myafterprojecy.pojo.dto.NonsensePageDto;
import java.util.List; import java.util.List;
@@ -25,7 +26,7 @@ public interface INonsenseService {
* @param status 状态0未发表 1已发表 2已删除 * @param status 状态0未发表 1已发表 2已删除
* @return 疯言疯语内容列表 * @return 疯言疯语内容列表
*/ */
ResponseMessage<List<Nonsense>> getNonsenseByStatus(Integer status); ResponseMessage<List<Nonsense>> getNonsenseByStatus(NonsensePageDto page);
/** /**
* 更新疯言疯语内容状态 * 更新疯言疯语内容状态
@@ -48,7 +49,7 @@ public interface INonsenseService {
* @param nonsenseDto 疯言疯语内容数据传输对象 * @param nonsenseDto 疯言疯语内容数据传输对象
* @return 更新结果 * @return 更新结果
*/ */
ResponseMessage<Nonsense> updateNonsense(Integer id, NonsenseDto nonsenseDto); ResponseMessage<Nonsense> updateNonsense(NonsenseDto nonsenseDto);
/** /**
* 删除疯言疯语内容 * 删除疯言疯语内容

View File

@@ -5,8 +5,10 @@ import com.qf.myafterprojecy.pojo.Article;
import com.qf.myafterprojecy.pojo.Categoryattribute; import com.qf.myafterprojecy.pojo.Categoryattribute;
import com.qf.myafterprojecy.pojo.dto.ArriclePageDto; import com.qf.myafterprojecy.pojo.dto.ArriclePageDto;
import com.qf.myafterprojecy.pojo.dto.ArticleDto; import com.qf.myafterprojecy.pojo.dto.ArticleDto;
import com.qf.myafterprojecy.pojo.dto.ArticleTreeDto;
import com.qf.myafterprojecy.repository.ArticleRepository; import com.qf.myafterprojecy.repository.ArticleRepository;
import com.qf.myafterprojecy.repository.CategoryAttributeRepository; import com.qf.myafterprojecy.repository.CategoryAttributeRepository;
import com.qf.myafterprojecy.repository.MessageRepository;
import com.qf.myafterprojecy.service.IArticleService; import com.qf.myafterprojecy.service.IArticleService;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -34,6 +36,9 @@ public class ArticleService implements IArticleService {
@Autowired @Autowired
private CategoryAttributeRepository categoryAttributeRepository; private CategoryAttributeRepository categoryAttributeRepository;
@Autowired
private MessageRepository messageRepository;
/** /**
* 根据文章ID获取文章详情 * 根据文章ID获取文章详情
* @param id 文章ID * @param id 文章ID
@@ -95,7 +100,7 @@ public class ArticleService implements IArticleService {
*/ */
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseMessage<List<Article>> getArticlesByStatus(Integer status) { public ResponseMessage<List<ArticleTreeDto>> getArticlesByStatus(Integer status) {
try { try {
if (status == null) { if (status == null) {
return ResponseMessage.badRequest("文章状态不能为空"); return ResponseMessage.badRequest("文章状态不能为空");
@@ -104,7 +109,8 @@ public class ArticleService implements IArticleService {
return ResponseMessage.badRequest("文章状态值必须在0到2之间"); return ResponseMessage.badRequest("文章状态值必须在0到2之间");
} }
List<Article> articles = articleRepository.findByStatus(status); List<Article> articles = articleRepository.findByStatus(status);
return ResponseMessage.success(articles, "根据状态查询文章成功"); List<ArticleTreeDto> articleTreeDtos = getArticleTree(articles);
return ResponseMessage.success(articleTreeDtos, "根据状态查询文章成功");
} catch (Exception e) { } catch (Exception e) {
log.error("根据状态查询文章列表失败: {}", e.getMessage()); log.error("根据状态查询文章列表失败: {}", e.getMessage());
return ResponseMessage.error("根据状态查询文章列表失败"); return ResponseMessage.error("根据状态查询文章列表失败");
@@ -128,7 +134,7 @@ public class ArticleService implements IArticleService {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseMessage<Page<Article>> getArticlesByStatusWithPagination(ArriclePageDto arriclePageDto) { public ResponseMessage<List<ArticleTreeDto>> getArticlesByStatusWithPagination(ArriclePageDto arriclePageDto) {
if (arriclePageDto.getPagenum() == null || arriclePageDto.getPagenum() < 0) { if (arriclePageDto.getPagenum() == null || arriclePageDto.getPagenum() < 0) {
arriclePageDto.setPagenum(0); // 默认第一页 arriclePageDto.setPagenum(0); // 默认第一页
} }
@@ -136,12 +142,7 @@ public class ArticleService implements IArticleService {
arriclePageDto.setPagesize(10); // 默认每页10条最大100条 arriclePageDto.setPagesize(10); // 默认每页10条最大100条
} }
try { try {
// 如果文章状态值是否在0到2之间则根据文章状态查询文章列表
if (arriclePageDto.getStatus() < 0 || arriclePageDto.getStatus() > 2) {
return ResponseMessage.badRequest("文章状态值必须在0到2之间");
}
PageRequest pageRequest = PageRequest.of(arriclePageDto.getPagenum(), arriclePageDto.getPagesize()); PageRequest pageRequest = PageRequest.of(arriclePageDto.getPagenum(), arriclePageDto.getPagesize());
// 如果文章分类ID不为空则根据文章分类ID查询文章列表
if (arriclePageDto.getCategoryid() != null && arriclePageDto.getCategoryid() > 0) { if (arriclePageDto.getCategoryid() != null && arriclePageDto.getCategoryid() > 0) {
// 如果文章分类ID不为空则根据文章分类ID查询文章列表 // 如果文章分类ID不为空则根据文章分类ID查询文章列表
List<Categoryattribute> categoryAttribute = categoryAttributeRepository.findByCategoryId(arriclePageDto.getCategoryid()); List<Categoryattribute> categoryAttribute = categoryAttributeRepository.findByCategoryId(arriclePageDto.getCategoryid());
@@ -152,20 +153,25 @@ public class ArticleService implements IArticleService {
List<Integer> attributeids = categoryAttribute.stream().map(Categoryattribute::getAttributeid).collect(Collectors.toList()); List<Integer> attributeids = categoryAttribute.stream().map(Categoryattribute::getAttributeid).collect(Collectors.toList());
// 根据分类ID对应的属性ID数组分页查询文章列表 // 根据分类ID对应的属性ID数组分页查询文章列表
Page<Article> articlePage = articleRepository.findByStatusWithPagination(arriclePageDto.getStatus(), attributeids, pageRequest); Page<Article> articlePage = articleRepository.findByStatusWithPagination(arriclePageDto.getStatus(), attributeids, pageRequest);
return ResponseMessage.success(articlePage, "根据分类ID分页查询文章成功"); List<ArticleTreeDto> articleTreeDtos = getArticleTree(articlePage.getContent());
return ResponseMessage.page( articleTreeDtos, "根据分类ID分页查询文章成功", articlePage.getTotalPages());
} }
// 如果文章属性ID不为空则根据文章属性ID查询文章列表 // 如果文章属性ID不为空则根据文章属性ID查询文章列表
if (arriclePageDto.getAttributeid() != null && arriclePageDto.getAttributeid() > 0) { if (arriclePageDto.getAttributeid() != null && arriclePageDto.getAttributeid() > 0) {
Page<Article> articlePage = articleRepository.findByStatusWithPagination(arriclePageDto.getStatus(), arriclePageDto.getAttributeid(), pageRequest); Page<Article> articlePage = articleRepository.findByStatusWithPagination(arriclePageDto.getStatus(), arriclePageDto.getAttributeid(), pageRequest);
return ResponseMessage.success(articlePage, "根据属性ID分页查询文章成功"); List<ArticleTreeDto> articleTreeDtos = getArticleTree(articlePage.getContent());
return ResponseMessage.page( articleTreeDtos, "根据属性ID分页查询文章成功", articlePage.getTotalPages());
} }
// 如果文章标题不为空则根据文章标题查询文章列表 // 如果文章标题不为空则根据文章标题查询文章列表
if (arriclePageDto.getTitle() != null && !arriclePageDto.getTitle().isEmpty()) { if (arriclePageDto.getTitle() != null && !arriclePageDto.getTitle().isEmpty()) {
Page<Article> articlePage = articleRepository.findByStatusWithPagination(arriclePageDto.getStatus(), arriclePageDto.getTitle(), pageRequest); Page<Article> articlePage = articleRepository.findByStatusWithPagination(arriclePageDto.getStatus(), arriclePageDto.getTitle(), pageRequest);
return ResponseMessage.success(articlePage, "根据标题分页查询文章成功"); List<ArticleTreeDto> articleTreeDtos = getArticleTree(articlePage.getContent());
return ResponseMessage.page( articleTreeDtos, "根据标题分页查询文章成功", articlePage.getTotalPages());
} }
Page<Article> articlePage = articleRepository.findByStatusWithPagination(arriclePageDto.getStatus(), pageRequest); Page<Article> articlePage = articleRepository.findByStatusWithPagination(arriclePageDto.getStatus(), pageRequest);
return ResponseMessage.success(articlePage, "根据状态分页查询文章成功"); List<ArticleTreeDto> articleTreeDtos = getArticleTree(articlePage.getContent());
return ResponseMessage.page( articleTreeDtos, "根据状态分页查询文章成功", articlePage.getTotalPages());
} catch (Exception e) { } catch (Exception e) {
log.error("根据状态分页查询文章列表失败: {}", e.getMessage()); log.error("根据状态分页查询文章列表失败: {}", e.getMessage());
return ResponseMessage.error("根据状态分页查询文章列表失败"); return ResponseMessage.error("根据状态分页查询文章列表失败");
@@ -173,13 +179,14 @@ public class ArticleService implements IArticleService {
} }
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseMessage<List<Article>> getArticlesByTitle(String title) { public ResponseMessage<List<ArticleTreeDto>> getArticlesByTitle(String title) {
try { try {
if (title == null || title.isEmpty()) { if (title == null || title.isEmpty()) {
return ResponseMessage.badRequest("文章标题不能为空"); return ResponseMessage.badRequest("文章标题不能为空");
} }
List<Article> articles = articleRepository.findByTitle(title); List<Article> articles = articleRepository.findByTitle(title);
return ResponseMessage.success(articles, "根据标题查询文章成功"); List<ArticleTreeDto> articleTreeDtos = getArticleTree(articles);
return ResponseMessage.success(articleTreeDtos, "根据标题查询文章成功");
} catch (Exception e) { } catch (Exception e) {
log.error("根据标题查询文章列表失败: {}", e.getMessage()); log.error("根据标题查询文章列表失败: {}", e.getMessage());
return ResponseMessage.error("根据标题查询文章列表失败"); return ResponseMessage.error("根据标题查询文章列表失败");
@@ -208,7 +215,6 @@ public class ArticleService implements IArticleService {
article.setUpdatedAt(LocalDateTime.now()); article.setUpdatedAt(LocalDateTime.now());
article.setImg(articleDto.getImg() != null ? articleDto.getImg() : ""); article.setImg(articleDto.getImg() != null ? articleDto.getImg() : "");
article.setStatus(articleDto.getStatus() != null ? articleDto.getStatus() : 0); article.setStatus(articleDto.getStatus() != null ? articleDto.getStatus() : 0);
Article savedArticle = articleRepository.save(article); Article savedArticle = articleRepository.save(article);
return ResponseMessage.save(true, savedArticle); return ResponseMessage.save(true, savedArticle);
} catch (DataAccessException e) { } catch (DataAccessException e) {
@@ -225,6 +231,10 @@ public class ArticleService implements IArticleService {
.orElseThrow(() -> new RuntimeException("文章不存在")); .orElseThrow(() -> new RuntimeException("文章不存在"));
BeanUtils.copyProperties(articleDto, article); BeanUtils.copyProperties(articleDto, article);
article.setUpdatedAt(LocalDateTime.now()); article.setUpdatedAt(LocalDateTime.now());
Article updatedArticle = articleRepository.save(article); Article updatedArticle = articleRepository.save(article);
@@ -359,4 +369,41 @@ public class ArticleService implements IArticleService {
return ResponseMessage.error("获取热门文章失败"); return ResponseMessage.error("获取热门文章失败");
} }
} }
// =========补全文章树信息
public List<ArticleTreeDto> getArticleTree( List<Article> articlePage) {
try {
List<ArticleTreeDto> articleTreeDtos = articlePage.stream()
.map(article -> {
ArticleTreeDto dto = new ArticleTreeDto();
dto.setArticleid(article.getArticleid());
dto.setTitle(article.getTitle());
dto.setContent(article.getContent());
dto.setImg(article.getImg());
dto.setViewcount(article.getViewCount());
dto.setLikes(article.getLikes());
dto.setStatus(article.getStatus());
dto.setAttributeid(article.getAttributeid());
// 获取属性名称
Categoryattribute categoryAttribute = categoryAttributeRepository.findById(article.getAttributeid()).orElse(null);
if (categoryAttribute != null) {
dto.setAttributename(categoryAttribute.getAttributename());
}
dto.setMarkdownscontent(article.getMarkdownscontent());
// 设置用户信息Article实体中没有这些字段暂时设置为null
dto.setUserid(null);
dto.setUsername(null);
// 获取评论数
Integer commentcount = messageRepository.countReplyByArticleId(article.getArticleid());
dto.setCommentcount(commentcount);
dto.setCreatetime(article.getCreatedAt());
dto.setUpdatetime(article.getUpdatedAt());
return dto;
})
.collect(Collectors.toList());
return articleTreeDtos;
} catch (DataAccessException e) {
log.error("获取文章树失败: {}", e.getMessage());
return null;
}
}
} }

View File

@@ -3,6 +3,8 @@ package com.qf.myafterprojecy.service.impl;
import com.qf.myafterprojecy.exceptopn.ResponseMessage; import com.qf.myafterprojecy.exceptopn.ResponseMessage;
import com.qf.myafterprojecy.pojo.Category; import com.qf.myafterprojecy.pojo.Category;
import com.qf.myafterprojecy.pojo.dto.CategoryDto; import com.qf.myafterprojecy.pojo.dto.CategoryDto;
import com.qf.myafterprojecy.pojo.dto.CategoryTreeDto;
import com.qf.myafterprojecy.repository.CategoryAttributeRepository;
import com.qf.myafterprojecy.repository.CategoryRepository; import com.qf.myafterprojecy.repository.CategoryRepository;
import com.qf.myafterprojecy.service.ICategoryService; import com.qf.myafterprojecy.service.ICategoryService;
@@ -15,7 +17,9 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
@Service @Service
public class CategoryService implements ICategoryService { public class CategoryService implements ICategoryService {
@@ -24,6 +28,8 @@ public class CategoryService implements ICategoryService {
@Autowired @Autowired
private CategoryRepository categoryRepository; private CategoryRepository categoryRepository;
@Autowired
private CategoryAttributeRepository categoryAttributeRepository;
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
@@ -152,4 +158,35 @@ public class CategoryService implements ICategoryService {
return ResponseMessage.error("搜索分类失败"); return ResponseMessage.error("搜索分类失败");
} }
} }
// 获取分类树
@Override
@Transactional(readOnly = true)
public ResponseMessage<List<CategoryTreeDto>> getCategoryTree() {
try {
List<Category> categories = categoryRepository.findAll();
// System.out.println(categories);
List<CategoryTreeDto> tree = buildCategoryTree(categories);
return ResponseMessage.success(tree, "获取分类树成功");
} catch (DataAccessException e) {
log.error("获取分类树失败: {}", e.getMessage());
return ResponseMessage.error("获取分类树失败");
}
}
// 递归构建分类树
private List<CategoryTreeDto> buildCategoryTree(List<Category> categories) {
List<CategoryTreeDto> tree = new ArrayList<>();
for (Category category : categories) {
tree.add(buildCategoryTreeNode(category));
}
return tree;
}
// 构建分类树节点
private CategoryTreeDto buildCategoryTreeNode(Category category) {
CategoryTreeDto node = new CategoryTreeDto();
node.setId(category.getCategoryid());
node.setName(category.getTypename());
node.setChildren(categoryAttributeRepository.findByCategoryId(category.getCategoryid()));
return node;
}
} }

View File

@@ -4,6 +4,7 @@ import com.qf.myafterprojecy.exceptopn.ResponseMessage;
import com.qf.myafterprojecy.pojo.Message; import com.qf.myafterprojecy.pojo.Message;
import com.qf.myafterprojecy.pojo.dto.MessageDto; import com.qf.myafterprojecy.pojo.dto.MessageDto;
import com.qf.myafterprojecy.pojo.dto.MessagePageDto; import com.qf.myafterprojecy.pojo.dto.MessagePageDto;
import com.qf.myafterprojecy.pojo.dto.MessageTreeDto;
import com.qf.myafterprojecy.repository.MessageRepository; import com.qf.myafterprojecy.repository.MessageRepository;
import com.qf.myafterprojecy.service.IMessageService; import com.qf.myafterprojecy.service.IMessageService;
@@ -11,16 +12,20 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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.boot.autoconfigure.data.web.SpringDataWebProperties.Pageable;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -140,22 +145,23 @@ public class MessageService implements IMessageService {
} }
} }
@Override // @Override
public ResponseMessage<List<Message>> getMessagesByArticleId(Integer articleId) { // public ResponseMessage<List<Message>> getMessagesByArticleId(Integer
if (articleId == null || articleId <= 0) { // articleId) {
logger.warn("根据文章ID查询消息时ID无效: {}", articleId); // if (articleId == null || articleId <= 0) {
return ResponseMessage.badRequest("文章ID无效"); // logger.warn("根据文章ID查询消息时ID无效: {}", articleId);
} // return ResponseMessage.badRequest("文章ID无效");
// }
try { // try {
logger.info("根据文章ID查询消息: {}", articleId); // logger.info("根据文章ID查询消息: {}", articleId);
List<Message> messages = messageRepository.findByArticleid(articleId); // List<Message> messages = messageRepository.findByArticleid(articleId);
return ResponseMessage.success(messages, "查询成功"); // return ResponseMessage.success(messages, "查询成功");
} catch (DataAccessException e) { // } catch (DataAccessException e) {
logger.error("根据文章ID查询消息失败: {}", articleId, e); // logger.error("根据文章ID查询消息失败: {}", articleId, e);
return ResponseMessage.error("查询消息失败:" + e.getMessage()); // return ResponseMessage.error("查询消息失败:" + e.getMessage());
} // }
} // }
@Override @Override
public ResponseMessage<List<Message>> getRootMessages() { public ResponseMessage<List<Message>> getRootMessages() {
@@ -235,9 +241,8 @@ public class MessageService implements IMessageService {
} }
} }
@Override @Override
public ResponseMessage<List<Message>> getMessagesByPage(MessagePageDto messagePageDto) { public ResponseMessage<List<MessageTreeDto>> getMessagesByPage(MessagePageDto messagePageDto) {
if (messagePageDto == null) { if (messagePageDto == null) {
logger.warn("分页查询消息时参数为空"); logger.warn("分页查询消息时参数为空");
return ResponseMessage.badRequest("分页参数不能为空"); return ResponseMessage.badRequest("分页参数不能为空");
@@ -251,21 +256,16 @@ public class MessageService implements IMessageService {
return ResponseMessage.badRequest("每页数量无效"); return ResponseMessage.badRequest("每页数量无效");
} }
try { try {
// 如何文章id为空默认根据分页基础信息查询消息 // // 如何文章id为空默认根据分页基础信息查询消息
PageRequest pageable = PageRequest.of(messagePageDto.getPageNum(), messagePageDto.getPageSize()); PageRequest pageable = PageRequest.of(messagePageDto.getPageNum(), messagePageDto.getPageSize());
if (messagePageDto.getArticleid() != null && messagePageDto.getArticleid() > 0) { logger.info("根据分页基础信息查询所有消息: {}", messagePageDto);
// 如果文章ID存在根据文章ID查询消息 return buildMessageTreeDto(messagePageDto.getArticleid(), pageable);
Page<Message> messagePage = messageRepository.findByArticleId(messagePageDto.getArticleid(), pageable);
return ResponseMessage.success(messagePage.getContent(), "查询成功");
}
// 如果文章ID不存在根据分页基础信息查询所有消息
Page<Message> messagePage = messageRepository.findAllMessages(pageable);
return ResponseMessage.success(messagePage.getContent(), "查询成功");
} catch (DataAccessException e) { } catch (DataAccessException e) {
logger.error("分页查询消息失败: {}", messagePageDto, e); logger.error("分页查询消息失败: {}", messagePageDto, e);
return ResponseMessage.error("查询消息失败:" + e.getMessage()); return ResponseMessage.error("查询消息失败:" + e.getMessage());
} }
} }
// 获取回复消息条数 如果id为空获取文章id为空的消息条数 // 获取回复消息条数 如果id为空获取文章id为空的消息条数
@Override @Override
public ResponseMessage<Integer> getMessageCountByArticleId(Integer articleId) { public ResponseMessage<Integer> getMessageCountByArticleId(Integer articleId) {
@@ -283,4 +283,145 @@ public class MessageService implements IMessageService {
return ResponseMessage.error("查询回复数量失败:" + e.getMessage()); return ResponseMessage.error("查询回复数量失败:" + e.getMessage());
} }
} }
/**
* 构建消息树DTO
*
* @param articleId 文章ID (可选)
* @param pageable 分页信息
* @return 消息树DTO列表
*/
public ResponseMessage<List<MessageTreeDto>> buildMessageTreeDto(Integer articleId, PageRequest pageable) {
// 1. 获取主留言(分页)
Page<Message> messagePage = null;
if (articleId == null || articleId <= 0) {
// 如果文章ID不存在根据分页基础信息查询所有消息
logger.info("根据分页基础信息查询所有消息: {}", pageable);
messagePage = messageRepository.findAllMessages(pageable);
logger.info("根据分页基础信息查询所有消息: {}", messagePage);
} else {
messagePage = messageRepository.findByArticleId(articleId, pageable);
logger.info("根据文章ID分页查询消息: {}", messagePage);
}
List<Message> mainMessages = messagePage.getContent();
int totalPages = messagePage.getTotalPages();
// 如果没有消息,直接返回空列表
if (mainMessages.isEmpty()) {
logger.info("文章ID {} 下没有消息", articleId);
return ResponseMessage.success(null);
}
// 2. 获取所有子回复(一次性查询,减少数据库访问)
// 注意:这里不递归查数据库,而是查出所有 parentid 属于某集合的留言,
// 然后在 Java 中递归构建树(适用于层级不深的场景,如评论)、
List<Message> allReplies = new ArrayList<>();
if (articleId == null || articleId <= 0) {
// 如果文章ID不存在根据分页基础信息查询所有消息
// 从主留言中提取所有父ID
List<Integer> parentIds = mainMessages.stream().map(Message::getMessageid).collect(Collectors.toList());
logger.info("根据父ID列表查询所有子回复: {}", parentIds);
allReplies = messageRepository.findRepliesByParentIds(parentIds);
} else {
allReplies = messageRepository.findAllRepliesByArticleId(articleId);
logger.info("根据文章ID查询所有子回复: {}", articleId);
}
// 3. 合并所有消息(主留言 + 子回复)
List<Message> allMessages = new ArrayList<>(mainMessages);
allMessages.addAll(allReplies);
// 4. 构建消息ID到消息对象的映射
Map<Integer, Message> messageMap = new HashMap<>();
for (Message msg : allMessages) {
messageMap.put(msg.getMessageid(), msg);
}
// 5. 构建消息树
Map<Integer, MessageTreeDto> treeNodeMap = new HashMap<>();
List<MessageTreeDto> roots = new ArrayList<>();
// 5.1 先构建所有节点
for (Message msg : allMessages) {
MessageTreeDto node = convertToTreeDto(msg);
treeNodeMap.put(msg.getMessageid(), node);
}
// 5.2 构建父子关系
for (Message msg : allMessages) {
MessageTreeDto currentNode = treeNodeMap.get(msg.getMessageid());
Integer parentId = msg.getParentid();
// 处理回复信息
Integer replyId = msg.getReplyid();
// 如果是回复消息,获取被回复者的昵称
if (replyId != null && replyId > 0) {
Message replyMsg = messageMap.get(replyId);
if (replyMsg != null) {
// 建议在MessageTreeDto中添加replyToNickname字段
currentNode.setReplyToNickname(replyMsg.getNickname());
// 而不是修改发送者昵称currentNode.setNickname("@" + replyMsg.getNickname());
}
}
if (parentId == null || parentId <= 0) {
// 主留言,直接添加到根列表
roots.add(currentNode);
} else {
// 子回复添加到父节点的children列表
MessageTreeDto parentNode = treeNodeMap.get(parentId);
if (parentNode != null) {
parentNode.getChildren().add(currentNode);
}
}
}
// 6. 计算每个节点的回复数
for (MessageTreeDto node : treeNodeMap.values()) {
int replyCount = calculateReplyCount(node);
node.setReplyCount(replyCount);
}
// 7. 排序:根节点按时间倒序(最新的在前面)
roots.sort(Comparator.comparing(MessageTreeDto::getCreatedAt).reversed());
// 8. 对每个根节点的子节点按时间排序
for (MessageTreeDto root : roots) {
sortChildrenByTime(root);
}
return ResponseMessage.page(roots, "查询成功", totalPages);
}
/**
* 将Message转换为MessageTreeDto
*/
private MessageTreeDto convertToTreeDto(Message msg) {
MessageTreeDto dto = new MessageTreeDto();
// 使用Spring的BeanUtils进行属性复制
BeanUtils.copyProperties(msg, dto);
// 初始化children列表
dto.setChildren(new ArrayList<>());
dto.setReplyCount(0);
return dto;
}
/**
* 计算节点的回复总数(包括所有子回复)
*/
private int calculateReplyCount(MessageTreeDto node) {
int count = node.getChildren().size();
for (MessageTreeDto child : node.getChildren()) {
count += calculateReplyCount(child);
}
return count;
}
/**
* 递归按时间排序子节点
*/
private void sortChildrenByTime(MessageTreeDto node) {
node.getChildren().sort(Comparator.comparing(MessageTreeDto::getCreatedAt));
for (MessageTreeDto child : node.getChildren()) {
sortChildrenByTime(child);
}
}
} }

View File

@@ -3,6 +3,7 @@ package com.qf.myafterprojecy.service.impl;
import com.qf.myafterprojecy.exceptopn.ResponseMessage; import com.qf.myafterprojecy.exceptopn.ResponseMessage;
import com.qf.myafterprojecy.pojo.Nonsense; import com.qf.myafterprojecy.pojo.Nonsense;
import com.qf.myafterprojecy.pojo.dto.NonsenseDto; import com.qf.myafterprojecy.pojo.dto.NonsenseDto;
import com.qf.myafterprojecy.pojo.dto.NonsensePageDto;
import com.qf.myafterprojecy.repository.NonsenseRepository; import com.qf.myafterprojecy.repository.NonsenseRepository;
import com.qf.myafterprojecy.service.INonsenseService; import com.qf.myafterprojecy.service.INonsenseService;
@@ -10,7 +11,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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.boot.autoconfigure.data.web.SpringDataWebProperties.Pageable;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -48,38 +52,43 @@ public class NonsenseService implements INonsenseService {
if (nonsenseOptional.isPresent()) { if (nonsenseOptional.isPresent()) {
Nonsense nonsense = nonsenseOptional.get(); Nonsense nonsense = nonsenseOptional.get();
logger.info("获取ID为{}的疯言疯语内容成功,状态: {}", id, nonsense.getStatus()); logger.info("获取ID为{}的疯言疯语内容成功,状态: {}", id, nonsense.getStatus());
return new ResponseMessage<>(200, "获取成功", nonsense, true); return ResponseMessage.success(nonsense, "获取成功");
} else { } else {
logger.warn("未找到ID为{}的疯言疯语内容", id); logger.warn("未找到ID为{}的疯言疯语内容", id);
return new ResponseMessage<>(404, "未找到指定疯言疯语内容", null, false); return ResponseMessage.error(404, "未找到指定疯言疯语内容");
} }
} catch (DataAccessException e) { } catch (DataAccessException e) {
logger.error("根据ID查询疯言疯语内容失败ID: {}", id, e); logger.error("根据ID查询疯言疯语内容失败ID: {}", id, e);
return new ResponseMessage<>(500, "数据库查询异常", null, false); return ResponseMessage.error(500, "数据库查询异常");
} catch (Exception e) { } catch (Exception e) {
logger.error("根据ID查询疯言疯语内容失败ID: {}", id, e); logger.error("根据ID查询疯言疯语内容失败ID: {}", id, e);
return new ResponseMessage<>(500, "服务器内部错误", null, false); return ResponseMessage.error(500, "服务器内部错误");
} }
} }
@Override @Override
public ResponseMessage<List<Nonsense>> getNonsenseByStatus(Integer status) { public ResponseMessage<List<Nonsense>> getNonsenseByStatus(NonsensePageDto page) {
try { try {
// 验证状态值是否有效 // 验证状态值是否有效
if (status < 0 || status > 2) { if (page.getStatus() < 0 || page.getStatus() > 2) {
logger.warn("无效的状态值: {}", status); logger.warn("无效的状态值: {}", page.getStatus());
return new ResponseMessage<>(400, "无效的状态值必须是0(未发表)、1(已发表)或2(已删除)", null, false); return ResponseMessage.error(400, "无效的状态值必须是0(未发表)、1(已发表)或2(已删除)");
} }
// 分页查询
PageRequest pageable = PageRequest.of(page.getPageNum(), page.getPageSize());
List<Nonsense> nonsenseList = nonsenseRepository.findByStatus(status); Page<Nonsense> pageResult = nonsenseRepository.findPageByStatus(page.getStatus(), pageable);
// 根据状态过滤
return new ResponseMessage<>(200, "获取成功", nonsenseList, true); // 根据状态过滤已发表(1)的内容
List<Nonsense> filteredNonsense = pageResult.getContent();
return ResponseMessage.page(filteredNonsense, "获取成功", pageResult.getTotalPages());
} catch (DataAccessException e) { } catch (DataAccessException e) {
logger.error("根据状态获取疯言疯语内容失败,状态: {}", status, e); logger.error("根据状态获取疯言疯语内容失败,状态: {}", page.getStatus(), e);
return new ResponseMessage<>(500, "数据库查询异常", null, false); return ResponseMessage.error(500, "数据库查询异常");
} catch (Exception e) { } catch (Exception e) {
logger.error("根据状态获取疯言疯语内容失败,状态: {}", status, e); logger.error("根据状态获取疯言疯语内容失败,状态: {}", page.getStatus(), e);
return new ResponseMessage<>(500, "服务器内部错误", null, false); return ResponseMessage.error(500, "服务器内部错误");
} }
} }
@@ -90,7 +99,7 @@ public class NonsenseService implements INonsenseService {
// 验证状态值是否有效 // 验证状态值是否有效
if (status < 0 || status > 2) { if (status < 0 || status > 2) {
logger.warn("无效的状态值: {} 用于ID为{}的疯言疯语内容", status, id); logger.warn("无效的状态值: {} 用于ID为{}的疯言疯语内容", status, id);
return new ResponseMessage<>(400, "无效的状态值必须是0(未发表)、1(已发表)或2(已删除)", null, false); return ResponseMessage.error(400, "无效的状态值必须是0(未发表)、1(已发表)或2(已删除)");
} }
Optional<Nonsense> nonsenseOptional = nonsenseRepository.findById(id); Optional<Nonsense> nonsenseOptional = nonsenseRepository.findById(id);
@@ -101,17 +110,17 @@ public class NonsenseService implements INonsenseService {
Nonsense updatedNonsense = nonsenseRepository.save(nonsense); Nonsense updatedNonsense = nonsenseRepository.save(nonsense);
logger.info("更新疯言疯语内容状态成功ID: {}, 旧状态: {}, 新状态: {}", id, oldStatus, status); logger.info("更新疯言疯语内容状态成功ID: {}, 旧状态: {}, 新状态: {}", id, oldStatus, status);
return new ResponseMessage<>(200, "状态更新成功", updatedNonsense, true); return ResponseMessage.success(updatedNonsense, "状态更新成功");
} else { } else {
logger.warn("更新状态失败未找到ID为{}的疯言疯语内容", id); logger.warn("更新状态失败未找到ID为{}的疯言疯语内容", id);
return new ResponseMessage<>(404, "未找到指定疯言疯语内容", null, false); return ResponseMessage.error(404, "未找到指定疯言疯语内容");
} }
} catch (DataAccessException e) { } catch (DataAccessException e) {
logger.error("更新疯言疯语内容状态失败ID: {}, 状态: {}", id, status, e); logger.error("更新疯言疯语内容状态失败ID: {}, 状态: {}", id, status, e);
return new ResponseMessage<>(500, "数据库操作异常", null, false); return ResponseMessage.error(500, "数据库操作异常");
} catch (Exception e) { } catch (Exception e) {
logger.error("更新疯言疯语内容状态失败ID: {}, 状态: {}", id, status, e); logger.error("更新疯言疯语内容状态失败ID: {}, 状态: {}", id, status, e);
return new ResponseMessage<>(500, "服务器内部错误", null, false); return ResponseMessage.error(500, "服务器内部错误");
} }
} }
@@ -134,42 +143,42 @@ public class NonsenseService implements INonsenseService {
Nonsense savedNonsense = nonsenseRepository.save(nonsense); Nonsense savedNonsense = nonsenseRepository.save(nonsense);
logger.info("保存疯言疯语内容成功ID: {}, 状态: {}", savedNonsense.getId(), savedNonsense.getStatus()); logger.info("保存疯言疯语内容成功ID: {}, 状态: {}", savedNonsense.getId(), savedNonsense.getStatus());
return new ResponseMessage<>(200, "保存成功", savedNonsense, true); return ResponseMessage.success(savedNonsense, "保存成功");
} catch (DataAccessException e) { } catch (DataAccessException e) {
logger.error("保存疯言疯语内容失败", e); logger.error("保存疯言疯语内容失败", e);
return new ResponseMessage<>(500, "数据库操作异常", null, false); return ResponseMessage.error(500, "数据库操作异常");
} catch (Exception e) { } catch (Exception e) {
logger.error("保存疯言疯语内容失败", e); logger.error("保存疯言疯语内容失败", e);
return new ResponseMessage<>(500, "服务器内部错误", null, false); return ResponseMessage.error(500, "服务器内部错误");
} }
} }
@Override @Override
@Transactional @Transactional
public ResponseMessage<Nonsense> updateNonsense(Integer id, NonsenseDto nonsenseDto) { public ResponseMessage<Nonsense> updateNonsense(NonsenseDto nonsenseDto) {
try { try {
Optional<Nonsense> nonsenseOptional = nonsenseRepository.findById(id); Optional<Nonsense> nonsenseOptional = nonsenseRepository.findById(nonsenseDto.getId());
if (nonsenseOptional.isPresent()) { if (nonsenseOptional.isPresent()) {
Nonsense nonsense = nonsenseOptional.get(); Nonsense nonsense = nonsenseOptional.get();
// 只有当DTO中提供了status值时才更新 // 只有当DTO中提供了status值时才更新
if (nonsenseDto.getStatus() != null) { if (nonsenseDto.getStatus() != null) {
logger.info("更新疯言疯语内容状态ID: {}, 新状态: {}", id, nonsenseDto.getStatus()); logger.info("更新疯言疯语内容状态ID: {}, 新状态: {}", nonsenseDto.getId(), nonsenseDto.getStatus());
} }
BeanUtils.copyProperties(nonsenseDto, nonsense, "id"); BeanUtils.copyProperties(nonsenseDto, nonsense, "id");
Nonsense updatedNonsense = nonsenseRepository.save(nonsense); Nonsense updatedNonsense = nonsenseRepository.save(nonsense);
logger.info("更新疯言疯语内容成功ID: {}, 当前状态: {}", id, updatedNonsense.getStatus()); logger.info("更新疯言疯语内容成功ID: {}, 当前状态: {}", nonsenseDto.getId(), updatedNonsense.getStatus());
return new ResponseMessage<>(200, "更新成功", updatedNonsense, true); return ResponseMessage.success(updatedNonsense, "更新成功");
} else { } else {
logger.warn("更新失败未找到ID为{}的疯言疯语内容", id); logger.warn("更新失败未找到ID为{}的疯言疯语内容", nonsenseDto.getId());
return new ResponseMessage<>(404, "未找到指定疯言疯语内容", null, false); return ResponseMessage.error(404, "未找到指定疯言疯语内容");
} }
} catch (DataAccessException e) { } catch (DataAccessException e) {
logger.error("更新疯言疯语内容失败ID: {}", id, e); logger.error("更新疯言疯语内容失败ID: {}", nonsenseDto.getId(), e);
return new ResponseMessage<>(500, "数据库操作异常", null, false); return ResponseMessage.error(500, "数据库操作异常");
} catch (Exception e) { } catch (Exception e) {
logger.error("更新疯言疯语内容失败ID: {}", id, e); logger.error("更新疯言疯语内容失败ID: {}", nonsenseDto.getId(), e);
return new ResponseMessage<>(500, "服务器内部错误", null, false); return ResponseMessage.error(500, "服务器内部错误");
} }
} }
@@ -186,17 +195,17 @@ public class NonsenseService implements INonsenseService {
// 物理删除 // 物理删除
// nonsenseRepository.deleteById(id); // nonsenseRepository.deleteById(id);
logger.info("删除疯言疯语内容成功ID: {}", id); logger.info("删除疯言疯语内容成功ID: {}", id);
return new ResponseMessage<>(200, "删除成功", true, true); return ResponseMessage.success(true, "删除成功");
} else { } else {
logger.warn("删除失败未找到ID为{}的疯言疯语内容", id); logger.warn("删除失败未找到ID为{}的疯言疯语内容", id);
return new ResponseMessage<>(404, "未找到指定疯言疯语内容", false, false); return ResponseMessage.error(404, "未找到指定疯言疯语内容");
} }
} catch (DataAccessException e) { } catch (DataAccessException e) {
logger.error("删除疯言疯语内容失败ID: {}", id, e); logger.error("删除疯言疯语内容失败ID: {}", id, e);
return new ResponseMessage<>(500, "数据库操作异常", false, false); return ResponseMessage.error(500, "数据库操作异常");
} catch (Exception e) { } catch (Exception e) {
logger.error("删除疯言疯语内容失败ID: {}", id, e); logger.error("删除疯言疯语内容失败ID: {}", id, e);
return new ResponseMessage<>(500, "服务器内部错误", false, false); return ResponseMessage.error(500, "服务器内部错误");
} }
} }
} }

View File

@@ -18,13 +18,13 @@ import java.util.function.Function;
@Component @Component
public class JwtUtils { public class JwtUtils {
@Value("${jwt.secret:default_secret_key_for_development}") @Value("${jwt.secret}")
private String secret; private String secret;
@Value("${jwt.expiration:86400000}") @Value("${jwt.expiration}")
private long expiration; private long expiration;
@Value("${jwt.token-prefix:Bearer}") @Value("${jwt.token-prefix}")
private String tokenPrefix; private String tokenPrefix;
/** /**

View File

@@ -9,11 +9,14 @@ server.port=7070
# ==================================================================== # ====================================================================
# 数据库与JPA配置 - 生产用 # 数据库与JPA配置 - 生产用
# ==================================================================== # ====================================================================
spring.datasource.url=${DB_URL:jdbc:mysql://mysql:3306/webproject?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai} # spring.datasource.url=${DB_URL:jdbc:mysql://mysql:3306/webproject?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai}
spring.datasource.username=${DB_USERNAME:root} # spring.datasource.username=${DB_USERNAME:root}
spring.datasource.password=${DB_PASSWORD:root} # spring.datasource.password=${DB_PASSWORD:root}
# spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/webproject?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&createDatabaseIfNotExist=true&allowPublicKeyRetrieval=true
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据库连接池配置(生产环境优化版) # 数据库连接池配置(生产环境优化版)
spring.datasource.hikari.maximum-pool-size=20 spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5 spring.datasource.hikari.minimum-idle=5
@@ -24,13 +27,13 @@ spring.datasource.hikari.connection-test-query=SELECT 1
spring.datasource.hikari.pool-name=WebProjectHikariCP spring.datasource.hikari.pool-name=WebProjectHikariCP
# JPA配置生产环境禁用自动DDL避免意外修改表结构 # JPA配置生产环境禁用自动DDL避免意外修改表结构
spring.jpa.hibernate.ddl-auto=create spring.jpa.hibernate.ddl-auto=none
spring.jpa.show-sql=false spring.jpa.show-sql=false
spring.jpa.properties.hibernate.format_sql=false spring.jpa.properties.hibernate.format_sql=false
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
spring.jpa.open-in-view=false spring.jpa.open-in-view=false
# JPA性能优化配置 # JPA性能优化配置(生产环境开启批量处理)
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
@@ -38,8 +41,8 @@ spring.jpa.properties.hibernate.order_updates=true
# ==================================================================== # ====================================================================
# JWT 配置 - 生产用(敏感信息从环境变量读取) # JWT 配置 - 生产用(敏感信息从环境变量读取)
# ==================================================================== # ====================================================================
jwt.secret=${JWT_SECRET:} jwt.secret=${JWT_SECRET:6a1f4832-29bf-4ac5-9408-a8813b6f2dfe}
jwt.expiration=${JWT_EXPIRATION:86400000} jwt.expiration=${JWT_EXPIRATION:3600000}
jwt.header=Authorization jwt.header=Authorization
jwt.token-prefix=Bearer jwt.token-prefix=Bearer
@@ -47,7 +50,8 @@ jwt.token-prefix=Bearer
# 安全与CORS配置 - 生产用 # 安全与CORS配置 - 生产用
# ==================================================================== # ====================================================================
# CORS配置生产环境限制为具体域名 # CORS配置生产环境限制为具体域名
cors.allowed-origins=http://qf1121.top,https://qf1121.top,http://www.qf1121.top,https://www.qf1121.top # cors.allowed-origins=http://qf1121.top,https://qf1121.top,http://www.qf1121.top,https://www.qf1121.top
cors.allowed-origins=http://localhost:3000,http://localhost:8080,http://localhost:5173
cors.allowed-methods=GET,POST,PUT,DELETE,OPTIONS cors.allowed-methods=GET,POST,PUT,DELETE,OPTIONS
cors.allowed-headers=* cors.allowed-headers=*
cors.allow-credentials=true cors.allow-credentials=true

View File

@@ -3,8 +3,7 @@
# ==================================================================== # ====================================================================
# 环境激活配置 # 环境激活配置
# 说明:默认激活开发环境,生产环境部署时应通过命令行参数或环境变量覆盖 # 说明:默认激活开发环境,生产环境部署时应通过命令行参数或环境变量覆盖
spring.profiles.active=dev spring.profiles.active=prod
server.port=7070
# 应用名称(通用配置) # 应用名称(通用配置)
spring.application.name=web_project spring.application.name=web_project