|
|
package com.aurora.controller;
|
|
|
|
|
|
import com.aurora.annotation.OptLog;
|
|
|
import com.aurora.model.dto.*;
|
|
|
import com.aurora.enums.FilePathEnum;
|
|
|
import com.aurora.service.ArticleService;
|
|
|
import com.aurora.strategy.context.ArticleImportStrategyContext;
|
|
|
import com.aurora.strategy.context.UploadStrategyContext;
|
|
|
import com.aurora.model.vo.*;
|
|
|
import io.swagger.annotations.Api;
|
|
|
import io.swagger.annotations.ApiImplicitParam;
|
|
|
import io.swagger.annotations.ApiOperation;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
import org.springframework.web.multipart.MultipartFile;
|
|
|
|
|
|
import javax.validation.Valid;
|
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
import static com.aurora.constant.OptTypeConstant.*;
|
|
|
//API(Application Programming Interface,应用程序编程接口)就像是一个软件服务员
|
|
|
// 它定义了一套明确的规则,让不同的软件应用能够安全、高效地相互“对话”和协作,而无需了解对方内部的复杂细节
|
|
|
//使用Swagger注解标记本类为API的"文章模块"分组,方便生成交互式文档
|
|
|
@Api(tags = "文章模块") //类定义,@Api注解来自Swagger,用于分组API文档,给API接口添加标签
|
|
|
@RestController //一个REST控制器,返回数据而非视图,返回数据通常为JSON格式,结合了@Controller和@ResponseBody,用于REST API
|
|
|
public class ArticleController { //每个API方法使用@GetMapping、@PostMapping等,对应HTTP方法
|
|
|
//使用依赖注入自动装配ArticleService,避免手动实例化(推荐构造函数注入,但字段注入常见于简单场景)
|
|
|
@Autowired //字段注入:@Autowired 用于依赖注入
|
|
|
private ArticleService articleService;
|
|
|
|
|
|
@Autowired
|
|
|
private UploadStrategyContext uploadStrategyContext; // 注入文件上传策略上下文,用于处理文件存储(如文章图片)
|
|
|
|
|
|
@Autowired
|
|
|
private ArticleImportStrategyContext articleImportStrategyContext; // 注入文章导入策略上下文,支持多种格式的文章导入(如Markdown、Word)
|
|
|
|
|
|
// 获取置顶和推荐文章:HTTP GET请求,路径为/articles/topAndFeatured
|
|
|
@ApiOperation("获取置顶和推荐文章")
|
|
|
@GetMapping("/articles/topAndFeatured")
|
|
|
public ResultVO<TopAndFeaturedArticlesDTO> listTopAndFeaturedArticles() {
|
|
|
return ResultVO.ok(articleService.listTopAndFeaturedArticles());// 调用Service层方法,返回统一封装的结果对象ResultVO
|
|
|
}
|
|
|
|
|
|
// 获取所有文章列表(分页卡片形式)
|
|
|
@ApiOperation("获取所有文章")
|
|
|
@GetMapping("/articles/all")
|
|
|
//返回值是ResultVO<PageResultDTO<ArticleCardDTO>>,其中ResultVO是统一的返回结果包装类
|
|
|
//PageResultDTO是分页结果的封装类,ArticleCardDTO是文章卡片形式的数据传输对象(只包含文章列表展示需要的信息,比如标题、简介)
|
|
|
public ResultVO<PageResultDTO<ArticleCardDTO>> listArticles() {
|
|
|
return ResultVO.ok(articleService.listArticles());
|
|
|
}
|
|
|
|
|
|
// 根据分类ID查询文章:使用@RequestParam接收URL查询参数(如?categoryId=1)
|
|
|
@ApiOperation("根据分类id获取文章")
|
|
|
@GetMapping("/articles/categoryId")
|
|
|
public ResultVO<PageResultDTO<ArticleCardDTO>> getArticlesByCategoryId(@RequestParam Integer categoryId) {
|
|
|
//@RequestParam用来接收URL查询参数(就是URL中?后面的参数),如果@RequestParam后没写参数名,默认会匹配和方法名参数一致的查询参数
|
|
|
return ResultVO.ok(articleService.listArticlesByCategoryId(categoryId));
|
|
|
//PageResultDTO<ArticleCardDTO>为分页文章卡片数据
|
|
|
}
|
|
|
|
|
|
// 根据文章ID获取详情:使用@PathVariable从URL路径中提取参数(如/articles/1)
|
|
|
@ApiOperation("根据id获取文章") //是Swagger相关的注解,用来给接口添加说明,方便生成接口文档让开发、测试人员查看
|
|
|
@GetMapping("/articles/{articleId}")
|
|
|
//@GetMapping表示这是一个处理GET请求的接口,接口路径为/articles/{articleId}
|
|
|
//其中{articleId}是一个路径参数占位符比如请求/articles/1时,1就会作为articleId的值
|
|
|
public ResultVO<ArticleDTO> getArticleById(@PathVariable("articleId") Integer articleId) {
|
|
|
//@PathVariable("articleId"):用于从URL路径中提取和占位符所对应的参数,把它赋值给方法参数Integer articleId
|
|
|
return ResultVO.ok(articleService.getArticleById(articleId));
|
|
|
//调用articleService(业务对象)的getArticleById方法,传入获取到的articleId来查询文章详情
|
|
|
//最后用ResultVO.ok()把查询到的ArticleDTO(文章数据传输对象)包装成统一的返回结果并返回
|
|
|
}
|
|
|
|
|
|
// 校验文章访问密码:@RequestBody接收JSON格式的密码对象
|
|
|
@ApiOperation("校验文章访问密码")
|
|
|
@PostMapping("/articles/access")//处理post请求的接口。请求路径为/articles/access(post请求更适合传递密码这类私密数据)
|
|
|
public ResultVO<?> accessArticle(@Valid @RequestBody ArticlePasswordVO articlePasswordVO) {
|
|
|
// @Valid触发参数校验,确保ArticlePasswordVO对象中的参数符合约束(如非空,密码格式,必填)
|
|
|
//@RequestBody表示把请求体(RequestBody)中的JSON数据转换为ArticlePasswordVO类型的对象articlePasswordVO
|
|
|
//调用业务对象articleService的accessArticle方法,传入密码参数对象进行校验,校验完成后通过ResultVO.ok()返回成功的统一结果
|
|
|
articleService.accessArticle(articlePasswordVO);
|
|
|
return ResultVO.ok();// 返回空结果表示成功
|
|
|
}
|
|
|
|
|
|
@ApiOperation("根据标签id获取文章")
|
|
|
@GetMapping("/articles/tagId")
|
|
|
public ResultVO<PageResultDTO<ArticleCardDTO>> listArticlesByTagId(@RequestParam Integer tagId) {
|
|
|
return ResultVO.ok(articleService.listArticlesByTagId(tagId));
|
|
|
}
|
|
|
|
|
|
@ApiOperation("获取所有文章归档") //获取文章归档列表(按时间分组)
|
|
|
@GetMapping("/archives/all") //无参数
|
|
|
public ResultVO<PageResultDTO<ArchiveDTO>> listArchives() {
|
|
|
//<ArchiveDTO>是文章归档的数据传输对象(包含年月,对应的文章数量等信息)
|
|
|
return ResultVO.ok(articleService.listArchives());
|
|
|
}
|
|
|
|
|
|
@ApiOperation("获取后台文章")
|
|
|
@GetMapping("/admin/articles") //路径带/admin说明是后台接口,通常需要权限
|
|
|
public ResultVO<PageResultDTO<ArticleAdminDTO>> listArticlesAdmin(ConditionVO conditionVO) {
|
|
|
//ConditionVO conditionVO是用来传递查询条件,比如文章标题、状态等筛选条件,是一个条件封装的VO(Value Object,值对象)类
|
|
|
//ArticleAdminDTO是后台文章的数据传输对象(会包含更多的管理相关的信息)
|
|
|
return ResultVO.ok(articleService.listArticlesAdmin(conditionVO));
|
|
|
}
|
|
|
|
|
|
// 保存或更新文章:@RequestBody接收文章数据,@Valid触发校验
|
|
|
@OptLog(optType = SAVE_OR_UPDATE) //项目里自定义注解,记录操作日志(如保存/更新),作用是自动记录该接口的操作日志
|
|
|
@ApiOperation("保存和修改文章")
|
|
|
@PostMapping("/admin/articles")
|
|
|
public ResultVO<?> saveOrUpdateArticle(@Valid @RequestBody ArticleVO articleVO) {
|
|
|
//@Valid触发对ArticleVO对象的参数校验(比如文章标题不为空,内容长度是否符合要求等),需要在ArticleVO的属性上配置@NotBlank、@Length等校验注解
|
|
|
//@RequestBody:将前端传递到请求体(JSON形式)的文章数据,转换成ArticleVO类型的对象articleVO
|
|
|
articleService.saveOrUpdateArticle(articleVO);
|
|
|
return ResultVO.ok();
|
|
|
}
|
|
|
|
|
|
@OptLog(optType = UPDATE)
|
|
|
@ApiOperation("修改文章是否置顶和推荐")
|
|
|
@PutMapping("/admin/articles/topAndFeatured") //处理PUT请求的后台接口(PUT常用于更新数据)
|
|
|
public ResultVO<?> updateArticleTopAndFeatured(@Valid @RequestBody ArticleTopFeaturedVO articleTopFeaturedVO) {
|
|
|
//@RequestBody将请求体中JSON格式的“文章ID+置顶状态+推荐状态”数据,转换成ArticleTopFeaturedVO类型的对象articleTopFeaturedVO
|
|
|
articleService.updateArticleTopAndFeatured(articleTopFeaturedVO);
|
|
|
return ResultVO.ok();
|
|
|
}
|
|
|
|
|
|
// 逻辑删除或恢复文章(通过更新状态字段而非物理删除),不是从数据库删除,而是通过修改文章的“状态字段”
|
|
|
@ApiOperation("删除或者恢复文章")
|
|
|
@PutMapping("/admin/articles")
|
|
|
public ResultVO<?> updateArticleDelete(@Valid @RequestBody DeleteVO deleteVO) {
|
|
|
articleService.updateArticleDelete(deleteVO);
|
|
|
return ResultVO.ok();
|
|
|
}
|
|
|
|
|
|
//直接从数据库删除文章,无法恢复
|
|
|
@OptLog(optType = DELETE)
|
|
|
@ApiOperation(value = "物理删除文章")
|
|
|
@DeleteMapping("/admin/articles/delete") //处理DELETE请求的后台接口(DELETE请求专门用于删除类操作)
|
|
|
public ResultVO<?> deleteArticles(@RequestBody List<Integer> articleIds) {
|
|
|
//@RequestBody:将请求体中JSON形式的文章ID列表(List<Integer> articleIds)转换成相应的集合对象,比如前端传递[1,2,3],就会对应删除ID为1,2,3的文章
|
|
|
articleService.deleteArticles(articleIds);
|
|
|
return ResultVO.ok();
|
|
|
}
|
|
|
|
|
|
// 上传文章图片:MultipartFile接收文件,返回图片访问路径
|
|
|
@OptLog(optType = UPLOAD) //表示“上传”类型的操作,用来自动记录“上传文章图片”的操作日志
|
|
|
@ApiOperation("上传文章图片")
|
|
|
@ApiImplicitParam(name = "file", value = "文章图片", required = true, dataType = "MultipartFile")
|
|
|
// 给Swagger文档补充参数说明,参数名file,含义是“文章图片”,为必填项,数据类型为MultipartFile(Spring中专门用来接收上传文件的类型)
|
|
|
@PostMapping("/admin/articles/images")
|
|
|
public ResultVO<String> saveArticleImages(MultipartFile file) {
|
|
|
// 通过策略上下文上传文件,FilePathEnum指定存储路径
|
|
|
//参数MultipartFile:接受前端上传的图片文件
|
|
|
//返回值ResultVO<String>:返回图片的访问路径(字符串形式,比如/upload/artic;e/202505/123.jpg)
|
|
|
return ResultVO.ok(uploadStrategyContext.executeUploadStrategy(file, FilePathEnum.ARTICLE.getPath()));
|
|
|
}
|
|
|
|
|
|
// 后台根据文章ID获取详情(可能包含敏感字段)
|
|
|
@ApiOperation("根据id查看后台文章")
|
|
|
@ApiImplicitParam(name = "articleId", value = "文章id", required = true, dataType = "Integer")
|
|
|
@GetMapping("/admin/articles/{articleId}")//{articleId}为路径参数,比如查询ID为。。的文章
|
|
|
public ResultVO<ArticleAdminViewDTO> getArticleBackById(@PathVariable("articleId") Integer articleId) {
|
|
|
//参数@PathVariable:将URL路径中{articleId}对应的数值转换成Integer类型的参数articleId
|
|
|
//ArticleAdminViewDTO是后台文章详情的数据传输对象,包含文章的完整信息(含敏感字段)
|
|
|
return ResultVO.ok(articleService.getArticleByIdAdmin(articleId));
|
|
|
}
|
|
|
|
|
|
// 导入文章:文件+类型参数,支持多种格式
|
|
|
@OptLog(optType = UPLOAD)
|
|
|
@ApiOperation(value = "导入文章")
|
|
|
@PostMapping("/admin/articles/import")
|
|
|
public ResultVO<?> importArticles(MultipartFile file, @RequestParam(required = false) String type) {
|
|
|
//参数MultipartFile:接受前端上传的文章导入文件(各种格式)
|
|
|
//@RequestParam(required = false) String type:接受文件类型参数,required=false表示类型参数可选(不传则默认形式)
|
|
|
articleImportStrategyContext.importArticles(file, type);
|
|
|
return ResultVO.ok();
|
|
|
}
|
|
|
|
|
|
// 导出文章:返回导出文件的路径或ID列表
|
|
|
@OptLog(optType = EXPORT) //表示这是“导出”类型的操作,用来记录“到处文章”的操作日志
|
|
|
@ApiOperation(value = "导出文章")
|
|
|
@ApiImplicitParam(name = "articleIdList", value = "文章id", required = true, dataType = "List<Integer>")
|
|
|
//value为必填项,数据类型List<Integer>(文章ID的集合)
|
|
|
@PostMapping("/admin/articles/export")//导出操作需传递文章ID列表,用POST请求更适合传递集合类参数
|
|
|
public ResultVO<List<String>> exportArticles(@RequestBody List<Integer> articleIds) {
|
|
|
//@RequestBody:将请求体中JSON形式的文章ID列表转换成List<Integer>类型的参数articleIds
|
|
|
return ResultVO.ok(articleService.exportArticles(articleIds));
|
|
|
//调用业务层articleService.exportArticles(articleIds)执行文章导出操作,导出完成后返回文件路径/ID列表的统一结果
|
|
|
}
|
|
|
|
|
|
// 搜索文章接口:通过ConditionVO封装搜索条件(如关键词、分类、发布时间范围等)
|
|
|
@ApiOperation(value = "搜索文章")
|
|
|
@GetMapping("/articles/search")
|
|
|
public ResultVO<List<ArticleSearchDTO>> listArticlesBySearch(ConditionVO condition) {
|
|
|
//ConditionVO condition:接收前端传递的搜索条件
|
|
|
//如果是GET请求,条件会以查询参数的形式传递,比如/articles/search?keyword=春天&pageNum=1,Spring会自动把查询参数匹配到ConditionVO的属性上)
|
|
|
//ArticleSearchDTO是搜索结果的展示类(只包含标题、简介、发布时间等搜索结果需要的信息)
|
|
|
return ResultVO.ok(articleService.listArticlesBySearch(condition));
|
|
|
}
|
|
|
|
|
|
}
|