Compare commits

...

4 Commits

Author SHA1 Message Date
qingfeng1121
f53e251d46 feat(消息): 添加回复ID字段支持消息回复功能
在Message和MessageDto中添加replyid字段,支持消息回复功能
添加删除所有评论的API端点
重构消息控制器方法顺序
```

```msg
feat(文章): 实现文章浏览量增加功能

添加incrementViewCount方法用于增加文章浏览量
在文章实体中添加likes字段记录点赞数
更新API文档说明新增字段
```

```msg
chore: 移除数据初始化类

注释掉CategoryDataInit和MessageDataInit类
这些初始化功能将由其他方式实现
2025-10-22 13:28:30 +08:00
qingfeng1121
848b13506c fix: 移除MessageDto中未使用的JPA注解并更新数据库配置
移除MessageDto中未使用的JPA注解以简化代码结构
在数据库连接URL中添加allowPublicKeyRetrieval参数以解决连接问题
清理过期的日志文件
2025-10-20 12:00:07 +08:00
qingfeng1121
effcc3838d refactor(pojo): 修正Article类中attributeid字段的列名拼写
feat(controller): 在ArticleController中添加根据属性ID获取文章的方法

style(repository): 在CategoryRepository方法上添加空行提高可读性

chore: 移除MyAfterProjecyApplication中多余的MapperScan注解
2025-10-19 11:11:56 +08:00
qingfeng1121
bd6b240f52 refactor: 删除未使用的Category_attribute接口
该接口未被项目使用且功能可由其他现有接口替代,移除以减少代码冗余
2025-10-18 10:29:23 +08:00
25 changed files with 3344 additions and 1156 deletions

View File

@@ -2,7 +2,7 @@
## 项目概述
MyAfterProject是一个基于Spring Boot的后端博客系统提供文章管理、留言板等功能的API接口。本文档旨在帮助前端开发者理解和使用这些API接口
MyAfterProject是一个基于Spring Boot的后端博客系统提供文章管理、留言板等功能的RESTful API接口。系统集成了Spring Security和JWT认证机制确保API的安全性和权限控制。本文档旨在帮助前端开发者理解和使用这些API接口包括接口规范、请求/响应格式、认证方式及最佳实践
## 技术栈
@@ -10,8 +10,10 @@ MyAfterProject是一个基于Spring Boot的后端博客系统提供文章管
- **ORM框架**: Spring Data JPA
- **数据库**: MySQL
- **缓存**: Redis
- **认证**: Spring Security + JWT
- **API风格**: RESTful
- **认证授权**: Spring Security + JWT Token
- **API风格**: RESTful API
- **错误处理**: 全局异常处理机制
- **权限控制**: 基于角色的访问控制(RBAC)
## 项目结构
@@ -19,29 +21,45 @@ MyAfterProject是一个基于Spring Boot的后端博客系统提供文章管
src/main/java/com/qf/myafterprojecy/
├── controller/ # 控制器层处理HTTP请求
│ ├── ArticleController.java # 文章相关API
── MessageController.java # 留言相关API
── MessageController.java # 留言相关API
│ └── AuthController.java # 认证相关API
├── pojo/ # 实体类和数据传输对象
│ ├── Article.java # 文章实体
│ ├── Message.java # 留言实体
│ ├── ResponseMessage.java # 统一响应消息格式
── dto/ # 数据传输对象
├── ArticleDto.java # 文章DTO
└── MessageDto.java # 留言DTO
│ ├── Article.java # 文章实体
│ ├── Message.java # 留言实体
│ ├── User.java # 用户实体
── Role.java # 角色实体
├── ResponseMessage.java # 统一响应消息格式
└── dto/ # 数据传输对象
│ ├── ArticleDto.java # 文章DTO
│ └── MessageDto.java # 留言DTO
├── repository/ # 数据访问层
│ ├── ArticleRepository.java # 文章数据访问
── MessageRepository.java # 留言数据访问
│ ├── ArticleRepository.java # 文章数据访问
── MessageRepository.java # 留言数据访问
│ ├── UserRepository.java # 用户数据访问
│ └── RoleRepository.java # 角色数据访问
├── service/ # 业务逻辑层
│ ├── ArticleService.java # 文章服务实现
│ ├── IArticleService.java # 文章服务接口
│ ├── MessageService.java # 留言服务实现
│ └── IMessageService.java # 留言服务接口
│ ├── ArticleService.java # 文章服务实现
│ ├── IArticleService.java # 文章服务接口
│ ├── MessageService.java # 留言服务实现
│ └── IMessageService.java # 留言服务接口
├── config/ # 配置类
│ ├── SecurityConfig.java # Spring Security配置
│ └── security/ # 安全相关组件
│ ├── JwtTokenProvider.java # JWT令牌生成器
│ ├── JwtAuthenticationFilter.java # JWT认证过滤器
│ └── UserDetailsServiceImpl.java # 用户认证服务
├── GlobalExceptionHandler.java # 全局异常处理器
└── MyAfterProjecyApplication.java # 应用入口
```
## 配置信息
后端服务默认运行在 `http://localhost:8080` 端口前端项目需要将API请求指向该地址。
- **服务地址**: `http://localhost:8080`
- **API基础路径**: `/api`
- **认证头**: `Authorization: Bearer {token}`
- **默认测试账号**:
- 作者账号: `test_author` / `password123` (拥有AUTHOR角色)
- 管理员账号: `test_admin` / `admin123` (拥有ADMIN角色)
## API接口详细说明
@@ -178,7 +196,10 @@ POST /api/articles
**功能**: 创建新文章
**权限**: 需要AUTHOR角色
**权限**: 需要AUTHOR角色 (通过`@PreAuthorize("hasRole('AUTHOR')")`控制)
**请求头**:
- `Authorization: Bearer {token}` (必需)
**请求体**:
```json
@@ -213,7 +234,10 @@ PUT /api/articles/{id}
**功能**: 更新现有文章
**权限**: 需要AUTHOR或ADMIN角色
**权限**: 需要AUTHOR角色 (通过`@PreAuthorize("hasRole('AUTHOR')")`控制)
**请求头**:
- `Authorization: Bearer {token}` (必需)
**请求参数**:
- `id`: 文章ID路径参数
@@ -248,7 +272,10 @@ DELETE /api/articles/{id}
**功能**: 删除指定文章
**权限**: 需要AUTHOR或ADMIN角色
**权限**: 需要AUTHOR或ADMIN角色 (通过`@PreAuthorize("hasRole('AUTHOR') or hasRole('ADMIN')")`控制)
**请求头**:
- `Authorization: Bearer {token}` (必需)
**请求参数**:
- `id`: 文章ID路径参数
@@ -291,6 +318,7 @@ GET /api/messages
"content": "留言内容",
"createdAt": "2023-01-01T10:00:00",
"parentid": null, // 父留言IDnull表示主留言
"replyid": null, // 回复留言IDnull表示无回复
"articleid": null // 关联的文章IDnull表示无关联
},
// 更多留言...
@@ -362,6 +390,9 @@ DELETE /api/messages/{id}
**权限**: 需要ADMIN角色
**请求头**:
- `Authorization: Bearer {token}` (必需)
**请求参数**:
- `id`: 留言ID路径参数
@@ -411,6 +442,64 @@ DTO类用于前后端数据传输是对实体类的简化只包含需要
- **ArticleDto**: 包含文章的基本信息,用于创建和更新文章
- **MessageDto**: 包含留言的基本信息,用于创建和更新留言
- **LoginRequest**: 登录请求参数
- **SignupRequest**: 注册请求参数
- **JwtResponse**: JWT认证响应
## 认证与授权
系统使用JWT (JSON Web Token) 进行身份认证。在访问需要授权的API接口时前端需要在请求头中包含有效的JWT令牌。
### 获取JWT令牌
```
POST /api/auth/signin
```
**功能**: 用户登录并获取JWT令牌
**请求体**:
```json
{
"username": "test_author",
"password": "password123"
}
```
**返回数据**:
```json
{
"code": 200,
"message": "登录成功",
"success": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"type": "Bearer",
"id": 1,
"username": "test_author",
"email": "author@example.com",
"roles": ["ROLE_AUTHOR"]
}
}
```
### 用户注册
```
POST /api/auth/signup
```
**功能**: 用户注册新账号
**请求体**:
```json
{
"username": "新用户名",
"email": "user@example.com",
"password": "密码123",
"role": "author" // 可选值: user, author, admin
}
```
## 前端调用示例
@@ -438,6 +527,14 @@ const api = axios.create({
}
});
// 认证相关API
export const authAPI = {
// 用户登录
login: (credentials) => api.post('/auth/signin', credentials),
// 用户注册
register: (userData) => api.post('/auth/signup', userData)
};
// 请求拦截器 - 添加认证token
api.interceptors.request.use(
config => {

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,6 +1,5 @@
package com.qf.myafterprojecy;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

View File

@@ -52,6 +52,43 @@ public class ArticleController {
return articleService.getArticlesByTitle(title);
}
/**
* 根据属性ID获取该属性下的所有文章
* @param attributeId 属性ID
* @return 返回包含文章列表的ResponseMessage对象
*/
@GetMapping("/attribute/{attributeId}")
public ResponseMessage<List<Article>> getArticlesByAttribute(@PathVariable Integer attributeId) {
return articleService.getArticlesByAttribute(attributeId);
}
/**
* 根据分类ID获取该分类下的所有文章兼容旧接口
* @param categoryId 分类ID
* @return 返回包含文章列表的ResponseMessage对象
*/
@GetMapping("/category/{categoryId}")
public ResponseMessage<List<Article>> getArticlesByCategory(@PathVariable Integer categoryId) {
return articleService.getArticlesByCategory(categoryId);
}
/**
* 根据属性ID获取最新文章按创建时间降序
* @param attributeId 属性ID
* @return 返回包含最新文章列表的ResponseMessage对象
*/
@GetMapping("/attribute/{attributeId}/latest")
public ResponseMessage<List<Article>> getLatestArticlesByAttribute(@PathVariable Integer attributeId) {
return articleService.getLatestArticlesByAttribute(attributeId);
}
/**
* 获取浏览量最高的文章列表
* @return 返回包含热门文章列表的ResponseMessage对象
*/
@GetMapping("/popular")
public ResponseMessage<List<Article>> getMostViewedArticles() {
return articleService.getMostViewedArticles();
}
/**
* 创建新文章
* 仅限AUTHOR角色用户访问
@@ -63,7 +100,15 @@ public class ArticleController {
public ResponseMessage<Article> createArticle(@Valid @RequestBody ArticleDto articleDto) {
return articleService.saveArticle(articleDto);
}
/**文章浏览量
* 增加文章浏览量
* @param id 文章ID
* @return 返回包含更新后文章信息的ResponseMessage对象
*/
@PostMapping("/view/{id}")
public ResponseMessage<Article> incrementViewCount(@PathVariable Integer id) {
return articleService.incrementViewCount(id);
}
/**
* 更新现有文章
* 仅限AUTHOR角色用户访问
@@ -91,43 +136,4 @@ public class ArticleController {
return articleService.deleteArticle(id);
}
/**
* 根据分类ID获取该分类下的所有文章兼容旧接口
* @param categoryId 分类ID
* @return 返回包含文章列表的ResponseMessage对象
*/
@GetMapping("/category/{categoryId}")
public ResponseMessage<List<Article>> getArticlesByCategory(@PathVariable Integer categoryId) {
return articleService.getArticlesByCategory(categoryId);
}
/**
* 根据属性ID获取该属性下的所有文章
* @param attributeId 属性ID
* @return 返回包含文章列表的ResponseMessage对象
*/
@GetMapping("/attribute/{attributeId}")
public ResponseMessage<List<Article>> getArticlesByAttribute(@PathVariable Integer attributeId) {
return articleService.getArticlesByAttribute(attributeId);
}
/**
* 根据属性ID获取最新文章按创建时间降序
* @param attributeId 属性ID
* @return 返回包含最新文章列表的ResponseMessage对象
*/
@GetMapping("/attribute/{attributeId}/latest")
public ResponseMessage<List<Article>> getLatestArticlesByAttribute(@PathVariable Integer attributeId) {
return articleService.getLatestArticlesByAttribute(attributeId);
}
/**
* 获取浏览量最高的文章列表
* @return 返回包含热门文章列表的ResponseMessage对象
*/
@GetMapping("/popular")
public ResponseMessage<List<Article>> getMostViewedArticles() {
return articleService.getMostViewedArticles();
}
}

View File

@@ -34,6 +34,7 @@ public class CategoryAttributeController {
return categoryAttributeService.getCategoryAttributeById(id);
}
/**
* 根据分类ID获取属性列表
* @param categoryId 分类ID

View File

@@ -38,26 +38,6 @@ public class MessageController {
return messageService.getMessageById(id);
}
/**
* 创建新消息
*/
@PostMapping
public ResponseMessage<Message> createMessage(@RequestBody MessageDto message) {
logger.info("接收创建消息的请求: {}", message != null ? message.getNickname() : "null");
return messageService.saveMessage(message);
}
/**
* 根据ID删除消息
*/
@DeleteMapping("/{id}")
public ResponseMessage<Message> deleteMessage(@PathVariable Integer id) {
logger.info("接收删除消息的请求: {}", id);
return messageService.deleteMessage(id);
}
// 新增API端点
/**
* 根据文章ID获取消息列表
*/
@@ -102,4 +82,29 @@ public class MessageController {
logger.info("接收获取文章评论数量的请求: {}", articleId);
return messageService.getMessageCountByArticleId(articleId);
}
/**
* 创建新消息
*/
@PostMapping
public ResponseMessage<Message> createMessage(@RequestBody MessageDto message) {
logger.info("接收创建消息的请求: {}", message != null ? message.getNickname() : "null");
return messageService.saveMessage(message);
}
/**
* 根据ID删除消息
*/
@DeleteMapping("/{id}")
public ResponseMessage<Message> deleteMessage(@PathVariable Integer id) {
logger.info("接收删除消息的请求: {}", id);
return messageService.deleteMessage(id);
}
//删除所有评论
@DeleteMapping("/all")
public ResponseMessage<Void> deleteAllMessages() {
logger.info("接收删除所有消息的请求");
return messageService.deleteAllMessages();
}
// 新增API端点
}

View File

@@ -1,85 +1,85 @@
package com.qf.myafterprojecy.init;
// package com.qf.myafterprojecy.init;
import com.qf.myafterprojecy.pojo.Category;
import com.qf.myafterprojecy.repository.CategoryRepository;
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 com.qf.myafterprojecy.pojo.Category;
// import com.qf.myafterprojecy.repository.CategoryRepository;
// 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.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
// import java.time.LocalDateTime;
// import java.util.ArrayList;
// import java.util.List;
/**
* 分类数据初始化类,用于在应用启动时初始化分类数据
*/
@Component
public class CategoryDataInit implements ApplicationRunner {
// /**
// * 分类数据初始化类,用于在应用启动时初始化分类数据
// */
// @Component
// public class CategoryDataInit implements ApplicationRunner {
private static final Logger logger = LoggerFactory.getLogger(CategoryDataInit.class);
// private static final Logger logger = LoggerFactory.getLogger(CategoryDataInit.class);
@Autowired
private CategoryRepository categoryRepository;
// @Autowired
// private CategoryRepository categoryRepository;
@Override
public void run(ApplicationArguments args) throws Exception {
logger.info("===== 分类数据初始化开始 =====");
// @Override
// public void run(ApplicationArguments args) throws Exception {
// logger.info("===== 分类数据初始化开始 =====");
// 检查数据库中是否已有分类数据
long count = categoryRepository.count();
logger.info("当前数据库中分类数量: {}", count);
// // 检查数据库中是否已有分类数据
// long count = categoryRepository.count();
// logger.info("当前数据库中分类数量: {}", count);
// 如果没有分类数据,添加一些测试数据
if (count == 0) {
logger.info("数据库中没有分类数据,开始添加初始化数据...");
addInitialCategories();
} else {
logger.info("数据库中已存在分类数据,无需初始化");
}
// // 如果没有分类数据,添加一些测试数据
// if (count == 0) {
// logger.info("数据库中没有分类数据,开始添加初始化数据...");
// addInitialCategories();
// } else {
// logger.info("数据库中已存在分类数据,无需初始化");
// }
logger.info("===== 分类数据初始化结束 =====");
}
// logger.info("===== 分类数据初始化结束 =====");
// }
/**
* 添加初始分类数据
*/
private void addInitialCategories() {
List<Category> categories = new ArrayList<>();
// /**
// * 添加初始分类数据
// */
// private void addInitialCategories() {
// List<Category> categories = new ArrayList<>();
// 创建几个常见的文章分类
Category category1 = new Category();
category1.setTypename("技术分享");
category1.setDescription("技术文章、教程、经验分享等");
category1.setCreatedAt(LocalDateTime.now());
category1.setUpdatedAt(LocalDateTime.now());
categories.add(category1);
// // 创建几个常见的文章分类
// Category category1 = new Category();
// category1.setTypename("技术分享");
// category1.setDescription("技术文章、教程、经验分享等");
// category1.setCreatedAt(LocalDateTime.now());
// category1.setUpdatedAt(LocalDateTime.now());
// categories.add(category1);
Category category2 = new Category();
category2.setTypename("生活随笔");
category2.setDescription("日常生活、心情记录、随笔等");
category2.setCreatedAt(LocalDateTime.now());
category2.setUpdatedAt(LocalDateTime.now());
categories.add(category2);
// Category category2 = new Category();
// category2.setTypename("生活随笔");
// category2.setDescription("日常生活、心情记录、随笔等");
// category2.setCreatedAt(LocalDateTime.now());
// category2.setUpdatedAt(LocalDateTime.now());
// categories.add(category2);
Category category3 = new Category();
category3.setTypename("学习笔记");
category3.setDescription("学习过程中的笔记、总结等");
category3.setCreatedAt(LocalDateTime.now());
category3.setUpdatedAt(LocalDateTime.now());
categories.add(category3);
// Category category3 = new Category();
// category3.setTypename("学习笔记");
// category3.setDescription("学习过程中的笔记、总结等");
// category3.setCreatedAt(LocalDateTime.now());
// category3.setUpdatedAt(LocalDateTime.now());
// categories.add(category3);
Category category4 = new Category();
category4.setTypename("行业动态");
category4.setDescription("行业新闻、趋势分析等");
category4.setCreatedAt(LocalDateTime.now());
category4.setUpdatedAt(LocalDateTime.now());
categories.add(category4);
// Category category4 = new Category();
// category4.setTypename("行业动态");
// category4.setDescription("行业新闻、趋势分析等");
// category4.setCreatedAt(LocalDateTime.now());
// category4.setUpdatedAt(LocalDateTime.now());
// categories.add(category4);
// 保存分类数据到数据库
categoryRepository.saveAll(categories);
logger.info("成功添加 {} 条分类数据", categories.size());
}
}
// // 保存分类数据到数据库
// categoryRepository.saveAll(categories);
// logger.info("成功添加 {} 条分类数据", categories.size());
// }
// }

View File

@@ -1,96 +1,96 @@
package com.qf.myafterprojecy.init;
// 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 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;
// import java.util.Date;
/**
* 消息数据初始化类用于在应用启动时为Message表添加默认的测试数据
*/
@Component
public class MessageDataInit implements ApplicationRunner {
// /**
// * 消息数据初始化类用于在应用启动时为Message表添加默认的测试数据
// */
// @Component
// public class MessageDataInit implements ApplicationRunner {
private static final Logger logger = LoggerFactory.getLogger(MessageDataInit.class);
// private static final Logger logger = LoggerFactory.getLogger(MessageDataInit.class);
@Autowired
private MessageRepository messageRepository;
// @Autowired
// private MessageRepository messageRepository;
@Override
public void run(ApplicationArguments args) throws Exception {
logger.info("===== 消息数据初始化开始 =====");
// @Override
// public void run(ApplicationArguments args) throws Exception {
// logger.info("===== 消息数据初始化开始 =====");
// 检查数据库中是否已有消息数据
long count = messageRepository.count();
logger.info("当前数据库中消息数量: {}", count);
// // 检查数据库中是否已有消息数据
// long count = messageRepository.count();
// logger.info("当前数据库中消息数量: {}", count);
// 如果没有消息数据,添加一些测试数据
if (count == 0) {
logger.info("数据库中没有消息数据,开始添加初始化数据...");
addInitialMessages();
} else {
logger.info("数据库中已存在消息数据,无需初始化");
}
// // 如果没有消息数据,添加一些测试数据
// if (count == 0) {
// logger.info("数据库中没有消息数据,开始添加初始化数据...");
// addInitialMessages();
// } else {
// logger.info("数据库中已存在消息数据,无需初始化");
// }
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);
// 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 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 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 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);
// 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());
}
}
// logger.info("成功添加了{}条初始化消息数据", messageRepository.count());
// }
// }

View File

@@ -22,7 +22,7 @@ public class Article {
private String content;
@NotNull(message = "类别id不能为空")
@Column(name = "attributeid")
@Column(name = "attribute_id")
private Integer attributeid;
@Column(name = "img")
@@ -33,14 +33,26 @@ public class Article {
@Column(name = "updated_at")
private LocalDateTime updatedAt;
@Column(name = "view_count")
private Integer viewCount;
@Column(name = "likes")
private Integer likes; // 点赞数
@Column(name = "status")
private Integer status; // 0-草稿1-已发布2-已删除
// Getters and Setters
public Integer getLikes() {
return likes;
}
public void setLikes(Integer likes) {
this.likes = likes;
}
public Integer getAttributeid() {
return attributeid;

View File

@@ -8,22 +8,50 @@ import java.util.Date;
public class Message {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "messageid")
private Integer messageid;
@Column(name = "nickname")
private String nickname;
@Column(name = "email")
private String email;
@Column(columnDefinition = "text")
@Column(name = "content", columnDefinition = "text")
private String content;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "created_at")
private Date createdAt;
@Column(name = "parentid")
private Integer parentid;
@Column(name = "replyid")
private Integer replyid;
@Column(name = "articleid")
private Integer articleid;
@Column(name = "likes")
private Integer likes; // 点赞数
public Integer getLikes() {
return likes;
}
public void setLikes(Integer likes) {
this.likes = likes;
}
public Integer getReplyid() {
return replyid;
}
public void setReplyid(Integer replyid) {
this.replyid = replyid;
}
public Integer getMessageid() {
return messageid;

View File

@@ -15,6 +15,9 @@ public class ArticleDto {
@NotBlank(message = "内容不能为空")
private String content;
@NotNull(message = "属性ID不能为空")
private Integer attributeid;
private String img;
private Integer status;

View File

@@ -1,6 +1,5 @@
package com.qf.myafterprojecy.pojo.dto;
import javax.persistence.*;
import java.util.Date;
public class MessageDto {
@@ -16,9 +15,17 @@ public class MessageDto {
private Integer parentid;
private Integer replyid;
private Integer articleid;
public Integer getReplyid() {
return replyid;
}
public void setReplyid(Integer replyid) {
this.replyid = replyid;
}
public Integer getMessageid() {
return messageid;
}

View File

@@ -67,6 +67,7 @@ public interface ArticleRepository extends JpaRepository<Article, Integer> {
@Query("UPDATE Article a SET a.viewCount = a.viewCount + 1 WHERE a.articleid = :articleid")
void incrementViewCount(@Param("articleid") Integer articleid);
/**
* 根据浏览量降序查询状态为1的所有文章
* 该查询使用JPQL语句从Article实体中选取数据

View File

@@ -14,6 +14,7 @@ public interface CategoryRepository extends JpaRepository<Category, Integer> {
* @param typename 分类名称
* @return 返回符合条件的分类列表
*/
List<Category> findByTypenameContaining(String typename);
/**

View File

@@ -1,6 +0,0 @@
package com.qf.myafterprojecy.repository;
import org.hibernate.metamodel.model.convert.spi.JpaAttributeConverter;
public interface Category_attribute extends JpaAttributeConverter<Category_attribute, Integer> {
}

View File

@@ -163,6 +163,27 @@ public class ArticleService implements IArticleService {
return ResponseMessage.failure("获取属性文章失败");
}
}
/**
* 增加文章浏览量
* @param id 文章ID
* @return 返回包含更新后文章信息的ResponseMessage对象
*/
@Override
@Transactional(rollbackFor = Exception.class)
public ResponseMessage<Article> incrementViewCount(Integer id) {
try {
Article article = articleRepository.findById(id)
.orElseThrow(() -> new RuntimeException("文章不存在"));
article.setViewCount(article.getViewCount() + 1);
Article updatedArticle = articleRepository.save(article);
return ResponseMessage.success(updatedArticle);
} catch (Exception e) {
log.error("增加文章浏览量失败: {}", e.getMessage());
return ResponseMessage.failure("增加文章浏览量失败");
}
}
@Override
@Transactional(readOnly = true)

View File

@@ -39,4 +39,10 @@ public interface IArticleService {
*/
ResponseMessage<List<Article>> getLatestArticlesByAttribute(Integer attributeid);
ResponseMessage<List<Article>> getMostViewedArticles();
/**
* 增加文章浏览量
* @param id 文章ID
* @return 返回包含更新后文章信息的ResponseMessage对象
*/
ResponseMessage<Article> incrementViewCount(Integer id);
}

View File

@@ -59,7 +59,8 @@ public interface IMessageService {
* @return 匹配的消息列表
*/
ResponseMessage<List<Message>> searchMessagesByNickname(String nickname);
//删除所有评论
ResponseMessage<Void> deleteAllMessages();
/**
* 获取指定文章的评论数量
* @param articleId 文章ID

View File

@@ -191,7 +191,18 @@ public class MessageService implements IMessageService {
return ResponseMessage.failure("查询消息失败:" + e.getMessage());
}
}
//删除所有评论
@Override
public ResponseMessage<Void> deleteAllMessages() {
try {
logger.info("删除所有消息");
messageRepository.deleteAll();
return ResponseMessage.success(null, "删除成功", true);
} catch (DataAccessException e) {
logger.error("删除所有消息失败", e);
return ResponseMessage.failure("删除消息失败:" + e.getMessage());
}
}
@Override
public ResponseMessage<Long> getMessageCountByArticleId(Integer articleId) {
if (articleId == null || articleId <= 0) {

View File

@@ -3,7 +3,7 @@ server.port=8080
spring.application.name=web_project
# 数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/webproject?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&createDatabaseIfNotExist=true
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