Compare commits
4 Commits
ffea3e85ae
...
f53e251d46
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f53e251d46 | ||
|
|
848b13506c | ||
|
|
effcc3838d | ||
|
|
bd6b240f52 |
137
README_API.md
137
README_API.md
@@ -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, // 父留言ID,null表示主留言
|
||||
"replyid": null, // 回复留言ID,null表示无回复
|
||||
"articleid": null // 关联的文章ID,null表示无关联
|
||||
},
|
||||
// 更多留言...
|
||||
@@ -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 => {
|
||||
|
||||
3815
logs/web_project.log
3815
logs/web_project.log
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
logs/web_project.log.2025-10-16.0.gz
Normal file
BIN
logs/web_project.log.2025-10-16.0.gz
Normal file
Binary file not shown.
BIN
logs/web_project.log.2025-10-20.0.gz
Normal file
BIN
logs/web_project.log.2025-10-20.0.gz
Normal file
Binary file not shown.
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ public class CategoryAttributeController {
|
||||
return categoryAttributeService.getCategoryAttributeById(id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据分类ID获取属性列表
|
||||
* @param categoryId 分类ID
|
||||
|
||||
@@ -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端点
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
// }
|
||||
// }
|
||||
@@ -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());
|
||||
// }
|
||||
// }
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -15,6 +15,9 @@ public class ArticleDto {
|
||||
@NotBlank(message = "内容不能为空")
|
||||
private String content;
|
||||
|
||||
@NotNull(message = "属性ID不能为空")
|
||||
private Integer attributeid;
|
||||
|
||||
private String img;
|
||||
|
||||
private Integer status;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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实体中选取数据
|
||||
|
||||
@@ -14,6 +14,7 @@ public interface CategoryRepository extends JpaRepository<Category, Integer> {
|
||||
* @param typename 分类名称
|
||||
* @return 返回符合条件的分类列表
|
||||
*/
|
||||
|
||||
List<Category> findByTypenameContaining(String typename);
|
||||
|
||||
/**
|
||||
|
||||
@@ -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> {
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -59,7 +59,8 @@ public interface IMessageService {
|
||||
* @return 匹配的消息列表
|
||||
*/
|
||||
ResponseMessage<List<Message>> searchMessagesByNickname(String nickname);
|
||||
|
||||
//删除所有评论
|
||||
ResponseMessage<Void> deleteAllMessages();
|
||||
/**
|
||||
* 获取指定文章的评论数量
|
||||
* @param articleId 文章ID
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user