master
YDY 6 months ago
parent c462d0b9e4
commit a7209da760

@ -0,0 +1,201 @@
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.*;
//APIApplication 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/accesspost请求更适合传递密码这类私密数据
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是用来传递查询条件比如文章标题、状态等筛选条件是一个条件封装的VOValue 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为12,3的文章
articleService.deleteArticles(articleIds);
return ResultVO.ok();
}
// 上传文章图片MultipartFile接收文件返回图片访问路径
@OptLog(optType = UPLOAD) //表示“上传”类型的操作,用来自动记录“上传文章图片”的操作日志
@ApiOperation("上传文章图片")
@ApiImplicitParam(name = "file", value = "文章图片", required = true, dataType = "MultipartFile")
// 给Swagger文档补充参数说明参数名file含义是“文章图片”为必填项数据类型为MultipartFileSpring中专门用来接收上传文件的类型
@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=1Spring会自动把查询参数匹配到ConditionVO的属性上
//ArticleSearchDTO是搜索结果的展示类只包含标题、简介、发布时间等搜索结果需要的信息
return ResultVO.ok(articleService.listArticlesBySearch(condition));
}
}

@ -0,0 +1,109 @@
package com.aurora.controller;
import com.aurora.annotation.OptLog;
import com.aurora.model.dto.AboutDTO;
import com.aurora.model.dto.AuroraAdminInfoDTO;
import com.aurora.model.dto.AuroraHomeInfoDTO;
import com.aurora.model.dto.WebsiteConfigDTO;
import com.aurora.enums.FilePathEnum;
import com.aurora.model.vo.ResultVO;
import com.aurora.service.AuroraInfoService;
import com.aurora.strategy.context.UploadStrategyContext;
import com.aurora.model.vo.AboutVO;
import com.aurora.model.vo.WebsiteConfigVO;
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 static com.aurora.constant.OptTypeConstant.UPDATE;
import static com.aurora.constant.OptTypeConstant.UPLOAD;
//AuroraInfoController控制器类用于处理访客上报和首页信息获取的请求
@Api(tags = "aurora信息")// 使用Swagger注解标记本类为API的"aurora信息"分组方便生成交互式API文档
@RestController// 标识本类为RESTful控制器组合了@Controller和@ResponseBody所有方法返回值直接写入HTTP响应体
public class AuroraInfoController {
@Autowired // 使用依赖注入自动装配AuroraInfoService用于处理访客上报和首页信息查询等业务逻辑
private AuroraInfoService auroraInfoService;
@Autowired // 注入文件上传策略上下文,用于处理图片等文件的上传操作
private UploadStrategyContext uploadStrategyContext;
@ApiOperation(value = "上报访客信息")
@PostMapping("/report") // 处理HTTP POST请求路径为/report
// 返回ResultVO<?>表示统一响应格式,?通配符表示返回值类型不确定或无返回体
public ResultVO<?> report() {
// 调用Service层方法处理访客上报的业务如增加访客计数
auroraInfoService.report();
// 返回操作成功的统一响应
return ResultVO.ok();
}
// 获取系统首页信息(前端展示用)
@ApiOperation(value = "获取系统信息")
@GetMapping("/") // 处理HTTP GET请求路径为/(通常是根路径或首页)
// 返回结果封装了AuroraHomeInfoDTO对象包含博客首页所需的各种信息比如文章标题...)
public ResultVO<AuroraHomeInfoDTO> getBlogHomeInfo() {
return ResultVO.ok(auroraInfoService.getAuroraHomeInfo());
}
// 获取系统后台管理信息(后台管理界面用)
@ApiOperation(value = "获取系统后台信息")
@GetMapping("/admin") // 处理HTTP GET请求路径为/admin
public ResultVO<AuroraAdminInfoDTO> getBlogBackInfo() {
// 调用Service获取后台管理信息可能包含统计数据、系统状态等
//AuroraAdminInfoDTO为后台信息的封装类
return ResultVO.ok(auroraInfoService.getAuroraAdminInfo());
}
//网站配置的后台接口
@OptLog(optType = UPDATE)// 自定义操作日志注解记录更新操作UPDATE为操作类型常量
@ApiOperation(value = "更新网站配置")
@PutMapping("/admin/website/config")
public ResultVO<?> updateWebsiteConfig(@Valid @RequestBody WebsiteConfigVO websiteConfigVO) {
// @Valid触发参数校验@RequestBody接收JSON格式的网站配置数据
auroraInfoService.updateWebsiteConfig(websiteConfigVO);
return ResultVO.ok();
}
@ApiOperation(value = "获取网站配置")//获取网站配置的后台接口
@GetMapping("/admin/website/config")
public ResultVO<WebsiteConfigDTO> getWebsiteConfig() {
return ResultVO.ok(auroraInfoService.getWebsiteConfig());
}
// 查看关于我信息(前台展示接口)
@ApiOperation(value = "查看关于我信息")
@GetMapping("/about")//处理GET请求的普通接口。普通用户也可访问
public ResultVO<AboutDTO> getAbout() {
//AboutDTO是“关于我”信息的封装类包含个人介绍头像联系方式等
return ResultVO.ok(auroraInfoService.getAbout());
}
//修改“关于我”信息的后台管理接口
@OptLog(optType = UPDATE) // 记录更新操作日志
@ApiOperation(value = "修改关于我信息")// 修改关于我信息(后台管理)
@PutMapping("/admin/about")
public ResultVO<?> updateAbout(@Valid @RequestBody AboutVO aboutVO) {
// @Valid校验AboutVO参数的合法性
auroraInfoService.updateAbout(aboutVO);
return ResultVO.ok();
}
@OptLog(optType = UPLOAD)// 记录上传操作日志
@ApiOperation(value = "上传博客配置图片")//后台接口
@ApiImplicitParam(name = "file", value = "图片", required = true, dataType = "MultipartFile")
// Swagger参数说明name指定参数名value是描述required=true表示必填dataType是数据类型
@PostMapping("/admin/config/images")
// 返回结果是图片访问路径的字符串
public ResultVO<String> savePhotoAlbumCover(MultipartFile file) {
//通过上传策略上下文执行文件上传FilePathEnum.CONFIG.getPath()指定配置文件存储路径
return ResultVO.ok(uploadStrategyContext.executeUploadStrategy(file, FilePathEnum.CONFIG.getPath()));
}
}

@ -0,0 +1,30 @@
package com.aurora.controller;
import com.aurora.exception.BizException;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.SneakyThrows;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
//业务异常处理控制器用于统一处理BizException类型的异常
@Api(tags = "异常处理模块")
@RestController//返回的结果会直接作为响应体返回
public class BizExceptionController {
@SneakyThrows //用于隐藏异常的显示抛出声明不用写throws Exception等声明
@ApiOperation("/处理BizException")
@RequestMapping("/bizException")//映射请求路径为:/bizException接收该路径的请求来处理异常
public void handleBizException(HttpServletRequest request) {
//HttpServletRequest request通过请求对象获取存储在其中的异常信息
if (request.getAttribute("bizException") instanceof BizException) {
//如果是BizException类型先打印该异常信息再将其强制转换为BizException并抛出
System.out.println(request.getAttribute("bizException"));
throw ((BizException) request.getAttribute("bizException"));
} else {
throw new Exception();//抛出新的Exception
}
}
}

@ -0,0 +1,74 @@
package com.aurora.controller;
import com.aurora.annotation.OptLog;
import com.aurora.model.dto.CategoryAdminDTO;
import com.aurora.model.dto.CategoryDTO;
import com.aurora.model.dto.CategoryOptionDTO;
import com.aurora.service.CategoryService;
import com.aurora.model.vo.CategoryVO;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.model.vo.ResultVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
import static com.aurora.constant.OptTypeConstant.*;
@Api(tags = "分类模块")
@RestController// 标识本类为RESTful控制器组合了@Controller和@ResponseBody所有方法返回值直接序列化为JSON响应
public class CategoryController {
@Autowired
private CategoryService categoryService; //用于处理分类相关的业务逻辑
@ApiOperation("获取所有分类")// Swagger注解描述API功能获取所有分类通常用于前台展示
@GetMapping("/categories/all")
// 返回值ResultVO<List<CategoryDTO>>是统一的响应包装类(用来规范接口返回的格式,比如包含响应状态、提示信息、数据等)
//List<CategoryDTO>是分类数据的列表CategoryDTO是分类相关的数据传输对象用来封装分类的信息
public ResultVO<List<CategoryDTO>> listCategories() {
// 调用Service层方法获取所有分类列表并用ResultVO包装成成功响应
return ResultVO.ok(categoryService.listCategories());
}
@ApiOperation(value = "查看后台分类列表")// 查看后台分类列表(通常包含分页和查询条件)
@GetMapping("/admin/categories")
// 返回分页结果包含CategoryAdminDTO列表可能比前台DTO包含更多管理字段
public ResultVO<PageResultDTO<CategoryAdminDTO>> listCategoriesAdmin(ConditionVO conditionVO) {
//ConditionVO是条件封装类用来接收前端传递的查询条件比如分类名称、分页信息等
return ResultVO.ok(categoryService.listCategoriesAdmin(conditionVO));
}
@ApiOperation(value = "搜索文章分类")//后台管理的搜索文章分类的接口
@GetMapping("/admin/categories/search")
// 返回CategoryOptionDTO列表通常用于下拉选择框等场景
//CategoryOptionDTO是适配“选项选择”场景的分类传输对象比如下拉选择框里的分类选项通常只封装分类ID和分类名称等
//List<CategoryOptionDTO>是符合搜索条件的分类选项列表
public ResultVO<List<CategoryOptionDTO>> listCategoriesAdminBySearch(ConditionVO conditionVO) {
return ResultVO.ok(categoryService.listCategoriesBySearch(conditionVO));
}
@OptLog(optType = DELETE)
@ApiOperation(value = "删除分类")
@DeleteMapping("/admin/categories")
// @RequestBody接收前端以JSON格式传递的请求体数据转换成要删除的分类ID列表ResultVO<?>表示无具体返回数据,仅返回操作成功的状态提示
public ResultVO<?> deleteCategories(@RequestBody List<Integer> categoryIds) {
categoryService.deleteCategories(categoryIds);
return ResultVO.ok();
}
@OptLog(optType = SAVE_OR_UPDATE)
@ApiOperation(value = "添加或修改分类")
@PostMapping("/admin/categories")
public ResultVO<?> saveOrUpdateCategory(@Valid @RequestBody CategoryVO categoryVO) {
// Service层会通过CategorVO中是否包含分类ID等信息判断是执行添加还是修改操作
categoryService.saveOrUpdateCategory(categoryVO);
return ResultVO.ok();
}
}

@ -0,0 +1,80 @@
package com.aurora.controller;
import com.aurora.annotation.AccessLimit;
import com.aurora.annotation.OptLog;
import com.aurora.model.dto.CommentAdminDTO;
import com.aurora.model.dto.CommentDTO;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.model.dto.ReplyDTO;
import com.aurora.service.CommentService;
import com.aurora.model.vo.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
import static com.aurora.constant.OptTypeConstant.*;
@Api(tags = "评论模块")
@RestController
public class CommentController {
@Autowired
private CommentService commentService;// 使用依赖注入自动装配CommentService用于处理评论相关的业务逻辑
@AccessLimit(seconds = 60, maxCount = 3)// 自定义注解访问限制60秒内同一用户最多允许3次请求防刷机制
@OptLog(optType = SAVE)//SAVE为操作类型常量
@ApiOperation("添加评论")
@PostMapping("/comments/save")
public ResultVO<?> saveComment(@Valid @RequestBody CommentVO commentVO) {
//CommentVO为评论的参数封装类
commentService.saveComment(commentVO);
return ResultVO.ok();
}
@ApiOperation("获取评论")// 获取评论列表(通常用于前台展示,支持分页和查询条件)
@GetMapping("/comments")
// 返回分页结果包含CommentDTO列表PageResultDTO为分页结果封装类包含总条数、当前页数等分页信息
public ResultVO<PageResultDTO<CommentDTO>> getComments(CommentVO commentVO) {
//CommentVO为评论的查询条件封装类用来接收前端传递的查询条件
return ResultVO.ok(commentService.listComments(commentVO));
}
@ApiOperation(value = "根据commentId获取回复")// 根据评论ID获取该评论的所有回复列表
@GetMapping("/comments/{commentId}/replies")
public ResultVO<List<ReplyDTO>> listRepliesByCommentId(@PathVariable("commentId") Integer commentId) {
return ResultVO.ok(commentService.listRepliesByCommentId(commentId));
}
@ApiOperation("获取前六个评论")
@GetMapping("/comments/topSix")
public ResultVO<List<CommentDTO>> listTopSixComments() {
return ResultVO.ok(commentService.listTopSixComments());
}
@ApiOperation(value = "查询后台评论")
@GetMapping("/admin/comments")
public ResultVO<PageResultDTO<CommentAdminDTO>> listCommentBackDTO(ConditionVO conditionVO) {
return ResultVO.ok(commentService.listCommentsAdmin(conditionVO));
}
@OptLog(optType = UPDATE)
@ApiOperation(value = "审核评论")
@PutMapping("/admin/comments/review")
public ResultVO<?> updateCommentsReview(@Valid @RequestBody ReviewVO reviewVO) {
commentService.updateCommentsReview(reviewVO);
return ResultVO.ok();
}
@OptLog(optType = DELETE)
@ApiOperation(value = "删除评论")
@DeleteMapping("/admin/comments")
public ResultVO<?> deleteComments(@RequestBody List<Integer> commentIdList) {
commentService.removeByIds(commentIdList);
return ResultVO.ok();
}
}

@ -0,0 +1,43 @@
package com.aurora.controller;
import com.aurora.annotation.OptLog;
import com.aurora.model.dto.ExceptionLogDTO;
import com.aurora.model.vo.ResultVO;
import com.aurora.service.ExceptionLogService;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.dto.PageResultDTO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static com.aurora.constant.OptTypeConstant.DELETE;
@Api(tags = "异常日志模块")
@RestController
public class ExceptionLogController {
@Autowired
private ExceptionLogService exceptionLogService;//Spring依赖注入注解自动注入ExceptionLogService的实例
@ApiOperation("获取异常日志")
@GetMapping("/admin/exception/logs")
public ResultVO<PageResultDTO<ExceptionLogDTO>> listExceptionLogs(ConditionVO conditionVO) {
//ConditionVO conditionVO封装后台查询异常日志的条件比如日志的时间范围、异常类型等
return ResultVO.ok(exceptionLogService.listExceptionLogs(conditionVO));
}
@OptLog(optType = DELETE)
@ApiOperation(value = "删除异常日志")
@DeleteMapping("/admin/exception/logs")
public ResultVO<?> deleteExceptionLogs(@RequestBody List<Integer> exceptionLogIds) {
exceptionLogService.removeByIds(exceptionLogIds);
return ResultVO.ok();
}
}

@ -0,0 +1,57 @@
package com.aurora.controller;
import com.aurora.annotation.OptLog;
import com.aurora.model.dto.FriendLinkAdminDTO;
import com.aurora.model.dto.FriendLinkDTO;
import com.aurora.model.vo.ResultVO;
import com.aurora.service.FriendLinkService;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.vo.FriendLinkVO;
import com.aurora.model.dto.PageResultDTO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
import static com.aurora.constant.OptTypeConstant.*;
@Api(tags = "友链模块")
@RestController
public class FriendLinkController {
@Autowired
private FriendLinkService friendLinkService;//调用Service层的友链业务方法
@ApiOperation(value = "查看友链列表")//用于前台展示
@GetMapping("/links")
public ResultVO<List<FriendLinkDTO>> listFriendLinks() {
return ResultVO.ok(friendLinkService.listFriendLinks());
}
@ApiOperation(value = "查看后台友链列表")
@GetMapping("/admin/links")
public ResultVO<PageResultDTO<FriendLinkAdminDTO>> listFriendLinkDTO(ConditionVO conditionVO) {
//ConditionVO conditionVO:封装后台查询友链的条件(比如友链名称关键词、分页信息等)
return ResultVO.ok(friendLinkService.listFriendLinksAdmin(conditionVO));
}
@OptLog(optType = SAVE_OR_UPDATE)
@ApiOperation(value = "保存或修改友链")
@PostMapping("/admin/links")
public ResultVO<?> saveOrUpdateFriendLink(@Valid @RequestBody FriendLinkVO friendLinkVO) {
//FriendLinkVO friendLinkVO:封装友链保存/修改的参数,且会被@Valid校验
friendLinkService.saveOrUpdateFriendLink(friendLinkVO);
return ResultVO.ok();
}
@OptLog(optType = DELETE)
@ApiOperation(value = "删除友链")
@DeleteMapping("/admin/links")
public ResultVO<?> deleteFriendLink(@RequestBody List<Integer> linkIdList) {
friendLinkService.removeByIds(linkIdList);
return ResultVO.ok();
}
}

@ -0,0 +1,87 @@
package com.aurora.controller;
import com.aurora.annotation.OptLog;
import com.aurora.model.dto.JobDTO;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.service.JobService;
import com.aurora.model.vo.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static com.aurora.constant.OptTypeConstant.*;
@Api(tags = "定时任务模块")//定时任务管理的控制器类
@RestController
public class JobController {
@Autowired
private JobService jobService;
@OptLog(optType = SAVE)
@ApiOperation("添加定时任务")
@PostMapping("/admin/jobs")
public ResultVO<?> saveJob(@RequestBody JobVO jobVO) {
//JobVO jobVO封装添加定时任务的所有参数包含定时任务的名称、执行时间、执行逻辑相关配置等添加所需信息
jobService.saveJob(jobVO);
return ResultVO.ok();
}
@OptLog(optType = UPDATE)
@ApiOperation("修改定时任务")
@PutMapping("/admin/jobs")
public ResultVO<?> updateJob(@RequestBody JobVO jobVO) {
jobService.updateJob(jobVO);
return ResultVO.ok();
}
@OptLog(optType = DELETE)
@ApiOperation("删除定时任务")
@DeleteMapping("/admin/jobs")
public ResultVO<?> deleteJobById(@RequestBody List<Integer> jobIds) {
jobService.deleteJobs(jobIds);
return ResultVO.ok();
}
@ApiOperation("根据id获取任务")
@GetMapping("/admin/jobs/{id}")//Spring的请求映射注解{id}是路径参数前端调用时需填充具体的任务ID
//如:/admin/jobs/5 表示查询ID为5的定时任务
public ResultVO<JobDTO> getJobById(@PathVariable("id") Integer jobId) {
//@PathVariable("id"):Spring的参数接收注解获取URL路径中{id}对应的实际值赋值给Integer jobId
//JobDTO是定时任务的数据传输对象封装定时任务的详细信息比如任务名称、执行规则、状态等返回该ID对应的定时任务详情
return ResultVO.ok(jobService.getJobById(jobId));
}
@ApiOperation("获取任务列表")
@GetMapping("/admin/jobs")
public ResultVO<PageResultDTO<JobDTO>> listJobs(JobSearchVO jobSearchVO) {
//JobSearchVO jobSearchVO封装定时任务的查询条件比如任务名称关键词、任务分组、任务状态等
return ResultVO.ok(jobService.listJobs(jobSearchVO));
}
@ApiOperation("更改任务的状态")//比如启用、禁用任务
@PutMapping("/admin/jobs/status")
public ResultVO<?> updateJobStatus(@RequestBody JobStatusVO jobStatusVO) {
//JobStatusVO jobStatusVO封装任务状态修改的参数包含任务ID、要修改的目标状态等信息
jobService.updateJobStatus(jobStatusVO);
return ResultVO.ok();
}
@ApiOperation("执行某个任务")//说明接口功能是手动触发某个定时任务立即执行(无需等待定时时间)
@PutMapping("/admin/jobs/run")
public ResultVO<?> runJob(@RequestBody JobRunVO jobRunVO) {
//JobRunVO jobRunVO封装手动执行任务的参数包含要执行的任务ID等信息
jobService.runJob(jobRunVO);
return ResultVO.ok();
}
@ApiOperation("获取所有job分组")//用于后台查询、添加任务时选择分组
@GetMapping("/admin/jobs/jobGroups")
public ResultVO<List<String>> listJobGroup() {
//返回值ResultVO<List<String>>:返回包含所有任务分组名称的字符串列表
return ResultVO.ok(jobService.listJobGroups());
}
}

@ -0,0 +1,58 @@
package com.aurora.controller;
import com.aurora.annotation.OptLog;
import com.aurora.model.dto.JobLogDTO;
import com.aurora.model.vo.ResultVO;
import com.aurora.service.JobLogService;
import com.aurora.model.vo.JobLogSearchVO;
import com.aurora.model.dto.PageResultDTO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static com.aurora.constant.OptTypeConstant.DELETE;
@Api(tags = "定时任务日志模块")
@RestController
public class JobLogController {
@Autowired
private JobLogService jobLogService;//调用Service层的日志业务方法
@ApiOperation("获取定时任务的日志列表")
@GetMapping("/admin/jobLogs")
public ResultVO<PageResultDTO<JobLogDTO>> listJobLogs(JobLogSearchVO jobLogSearchVO) {
//JobLogSearchVO jobLogSearchVO封装日志的查询条件比如任务ID、执行状态、时间范围等
return ResultVO.ok(jobLogService.listJobLogs(jobLogSearchVO));
}
@OptLog(optType = DELETE)
@ApiOperation("删除定时任务的日志")
@DeleteMapping("/admin/jobLogs")
public ResultVO<?> deleteJobLogs(@RequestBody List<Integer> ids) {
jobLogService.deleteJobLogs(ids);
return ResultVO.ok();
}
@OptLog(optType = DELETE)
@ApiOperation("清除定时任务的日志")
@DeleteMapping("/admin/jobLogs/clean")
//返回值ResultVO<?>:仅返回操作成功的状态,无具体业务数据
public ResultVO<?> cleanJobLogs() {
jobLogService.cleanJobLogs();
return ResultVO.ok();
}
@ApiOperation("获取定时任务日志的所有组名")//用于后台查询日志时筛选分组
@GetMapping("/admin/jobLogs/jobGroups")
//返回值ResultVO<?>:返回包含所有日志关联任务分组名称的结果(通常是字符串列表)
public ResultVO<?> listJobLogGroups() {
return ResultVO.ok(jobLogService.listJobLogGroups());
}
}

@ -0,0 +1,78 @@
package com.aurora.controller;
import com.aurora.annotation.OptLog;
import com.aurora.model.dto.LabelOptionDTO;
import com.aurora.model.dto.MenuDTO;
import com.aurora.model.dto.UserMenuDTO;
import com.aurora.model.vo.ResultVO;
import com.aurora.service.MenuService;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.vo.IsHiddenVO;
import com.aurora.model.vo.MenuVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
import static com.aurora.constant.OptTypeConstant.*;
@Api(tags = "菜单模块")
@RestController
public class MenuController {
@Autowired
private MenuService menuService;//调用Service层的菜单业务方法
@ApiOperation(value = "查看菜单列表")
@GetMapping("/admin/menus")
public ResultVO<List<MenuDTO>> listMenus(ConditionVO conditionVO) {
//ConditionVO conditionVO封装菜单的查询条件比如菜单名称关键词、菜单类型等
return ResultVO.ok(menuService.listMenus(conditionVO));
}
@OptLog(optType =SAVE_OR_UPDATE)
@ApiOperation(value = "新增或修改菜单")
@PostMapping("/admin/menus")
public ResultVO<?> saveOrUpdateMenu(@Valid @RequestBody MenuVO menuVO) {
//MenuVO menuVO封装菜单新增、修改的参数包含菜单名称、路径、父菜单ID等新增、修改所需信息且会被@Valid校验
//menuVO内涵菜单ID判断是新增还是修改
menuService.saveOrUpdateMenu(menuVO);
return ResultVO.ok();
}
@OptLog(optType = UPDATE)
@ApiOperation(value = "修改目录是否隐藏")
@PutMapping("/admin/menus/isHidden")
public ResultVO<?> updateMenuIsHidden(@RequestBody IsHiddenVO isHiddenVO) {
//IsHiddenVO isHiddenVO封装菜单隐藏状态修改的参数包含菜单ID\目标隐藏状态等信息
menuService.updateMenuIsHidden(isHiddenVO);
return ResultVO.ok();
}
@OptLog(optType = DELETE)
@ApiOperation(value = "删除菜单")
@DeleteMapping("/admin/menus/{menuId}")//{menuId}为路径参数前端调用时需填充具体的菜单ID
//例如/admin/menus/3表示删除ID为3的菜单
public ResultVO<?> deleteMenu(@PathVariable("menuId") Integer menuId) {
menuService.deleteMenu(menuId);
return ResultVO.ok();
}
@ApiOperation(value = "查看角色菜单选项")//获取用于角色分配菜单时的选项列表,比如给某个角色配置可访问的菜单时,展示可选的菜单
@GetMapping("/admin/role/menus")
//返回值ResultVO<List<LabelOptionDTO>>:返回所有可用的菜单选项列表
//LabelOptionDTO为选项类的数据传输对象包含菜单ID和菜单名称用于下拉选择等场景
public ResultVO<List<LabelOptionDTO>> listMenuOptions() {
return ResultVO.ok(menuService.listMenuOptions());
}
@ApiOperation(value = "查看当前用户菜单")//用于系统侧边栏展示等场景
@GetMapping("/admin/user/menus")
public ResultVO<List<UserMenuDTO>> listUserMenus() {
//无需参数,系统会自动获取当前登录用户的信息
return ResultVO.ok(menuService.listUserMenus());
}
}

@ -0,0 +1,43 @@
package com.aurora.controller;
import com.aurora.annotation.OptLog;
import com.aurora.model.dto.OperationLogDTO;
import com.aurora.model.vo.ResultVO;
import com.aurora.service.OperationLogService;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.dto.PageResultDTO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static com.aurora.constant.OptTypeConstant.DELETE;
@Api(tags = "操作日志模块")
@RestController
public class OperationLogController {
@Autowired
private OperationLogService operationLogService;//调用Service层的操作日志业务方法
@ApiOperation(value = "查看操作日志")//比如查看用户的新增、修改、删除等操作记录
@GetMapping("/admin/operation/logs")
public ResultVO<PageResultDTO<OperationLogDTO>> listOperationLogs(ConditionVO conditionVO) {
//ConditionVO conditionVO封装操作日志的查询条件比如操作人、操作类型、时间范围等
return ResultVO.ok(operationLogService.listOperationLogs(conditionVO));
}
@OptLog(optType = DELETE)
@ApiOperation(value = "删除操作日志")
@DeleteMapping("/admin/operation/logs")
public ResultVO<?> deleteOperationLogs(@RequestBody List<Integer> operationLogIds) {
operationLogService.removeByIds(operationLogIds);
return ResultVO.ok();
}
}

@ -0,0 +1,93 @@
package com.aurora.controller;
import com.aurora.annotation.OptLog;
import com.aurora.model.dto.PhotoAlbumAdminDTO;
import com.aurora.model.dto.PhotoAlbumDTO;
import com.aurora.enums.FilePathEnum;
import com.aurora.model.vo.ResultVO;
import com.aurora.service.PhotoAlbumService;
import com.aurora.strategy.context.UploadStrategyContext;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.model.vo.PhotoAlbumVO;
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(tags = "相册模块")
@RestController
public class PhotoAlbumController {
@Autowired
private UploadStrategyContext uploadStrategyContext;//用于文件上传
@Autowired
private PhotoAlbumService photoAlbumService;//相册业务逻辑
@OptLog(optType = UPLOAD)
@ApiOperation(value = "上传相册封面")
@ApiImplicitParam(name = "file", value = "相册封面", required = true, dataType = "MultipartFile")
//Swagger注解说明接口的参数信息参数名file含义是相册封面必填类型为MultipartFileSpring的文件上传类型
@PostMapping("/admin/photos/albums/upload")
//返回值ResultVO<String>:返回上传后封面图片的存储路径(字符串格式)
public ResultVO<String> savePhotoAlbumCover(MultipartFile file) {
//MultipartFile file接收前端上传的相册封面文件
return ResultVO.ok(uploadStrategyContext.executeUploadStrategy(file, FilePathEnum.PHOTO.getPath()));
}
@OptLog(optType = SAVE_OR_UPDATE)
@ApiOperation(value = "保存或更新相册")
@PostMapping("/admin/photos/albums")
public ResultVO<?> saveOrUpdatePhotoAlbum(@Valid @RequestBody PhotoAlbumVO photoAlbumVO) {
//PhotoAlbumVO photoAlbumVO封装相册保存、更新的参数包含相册名称、封面路径、描述等信息且被@Valid校验
photoAlbumService.saveOrUpdatePhotoAlbum(photoAlbumVO);
return ResultVO.ok();
}
@ApiOperation(value = "查看后台相册列表")//分页
@GetMapping("/admin/photos/albums")
public ResultVO<PageResultDTO<PhotoAlbumAdminDTO>> listPhotoAlbumBacks(ConditionVO conditionVO) {
//ConditionVO conditionVO封装相册的查询条件比如相册名称关键词、创建时间范围等
return ResultVO.ok(photoAlbumService.listPhotoAlbumsAdmin(conditionVO));
}
@ApiOperation(value = "获取后台相册列表信息")//获取后台所有相册的基础信息列表(相比于分页列表,更偏向简洁的信息集合)
@GetMapping("/admin/photos/albums/info")
public ResultVO<List<PhotoAlbumDTO>> listPhotoAlbumBackInfos() {
return ResultVO.ok(photoAlbumService.listPhotoAlbumInfosAdmin());
}
@ApiOperation(value = "根据id获取后台相册信息")
@ApiImplicitParam(name = "albumId", value = "相册id", required = true, dataType = "Integer")
@GetMapping("/admin/photos/albums/{albumId}/info")
//返回值ResultVO<PhotoAlbumAdminDTO>返回指定ID的相册详细信息
//PhotoAlbumAdminDTO是后台相册详细数据传输对象
public ResultVO<PhotoAlbumAdminDTO> getPhotoAlbumBackById(@PathVariable("albumId") Integer albumId) {
return ResultVO.ok(photoAlbumService.getPhotoAlbumByIdAdmin(albumId));
}
@OptLog(optType = DELETE)
@ApiOperation(value = "根据id删除相册")
@ApiImplicitParam(name = "albumId", value = "相册id", required = true, dataType = "Integer")
@DeleteMapping("/admin/photos/albums/{albumId}")
public ResultVO<?> deletePhotoAlbumById(@PathVariable("albumId") Integer albumId) {
photoAlbumService.deletePhotoAlbumById(albumId);
return ResultVO.ok();
}
@ApiOperation(value = "获取相册列表")//获取前端展示用的相册列表,普通用户也可调用
@GetMapping("/photos/albums")
public ResultVO<List<PhotoAlbumDTO>> listPhotoAlbums() {
return ResultVO.ok(photoAlbumService.listPhotoAlbums());
}
}

@ -0,0 +1,96 @@
package com.aurora.controller;
import com.aurora.annotation.OptLog;
import com.aurora.enums.FilePathEnum;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.model.dto.PhotoAdminDTO;
import com.aurora.model.dto.PhotoDTO;
import com.aurora.service.PhotoService;
import com.aurora.model.vo.*;
import com.aurora.strategy.context.UploadStrategyContext;
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(tags = "照片模块")
@RestController
public class PhotoController {
@Autowired
private PhotoService photoService;//照片业务逻辑
@Autowired
private UploadStrategyContext uploadStrategyContext;//文件上传服务
@OptLog(optType = UPLOAD)
@ApiOperation(value = "上传照片")
@ApiImplicitParam(name = "file", value = "照片", required = true, dataType = "MultipartFile")
@PostMapping("/admin/photos/upload")
public ResultVO<String> savePhotoAlbumCover(MultipartFile file) {
return ResultVO.ok(uploadStrategyContext.executeUploadStrategy(file, FilePathEnum.PHOTO.getPath()));
}
@ApiOperation(value = "根据相册id获取照片列表")
@GetMapping("/admin/photos")
public ResultVO<PageResultDTO<PhotoAdminDTO>> listPhotos(ConditionVO conditionVO) {
return ResultVO.ok(photoService.listPhotos(conditionVO));
}
@OptLog(optType = UPDATE)//修改类型
@ApiOperation(value = "更新照片信息")
@PutMapping("/admin/photos")
public ResultVO<?> updatePhoto(@Valid @RequestBody PhotoInfoVO photoInfoVO) {
photoService.updatePhoto(photoInfoVO);
return ResultVO.ok();
}
@OptLog(optType = SAVE)
@ApiOperation(value = "保存照片")
@PostMapping("/admin/photos")
public ResultVO<?> savePhotos(@Valid @RequestBody PhotoVO photoVO) {
photoService.savePhotos(photoVO);
return ResultVO.ok();
}
@OptLog(optType = UPDATE)
@ApiOperation(value = "移动照片相册")//将照片从一个相册移到另一个相册
@PutMapping("/admin/photos/album")
public ResultVO<?> updatePhotosAlbum(@Valid @RequestBody PhotoVO photoVO) {
//PhotoVO photoVO封装照片移动的参数包含照片ID列表、目标相册ID等信息且会被@Valid检验检测目标相册ID是否存在
photoService.updatePhotosAlbum(photoVO);
return ResultVO.ok();
}
@OptLog(optType = UPDATE)
@ApiOperation(value = "更新照片删除状态")//通常为软删除
@PutMapping("/admin/photos/delete")
public ResultVO<?> updatePhotoDelete(@Valid @RequestBody DeleteVO deleteVO) {
//DeleteVO deleteVO封装照片删除状态更新的参数包含照片ID列表、目标删除状态等信息且会被@Valid检验
photoService.updatePhotoDelete(deleteVO);
return ResultVO.ok();
}
@OptLog(optType = DELETE)
@ApiOperation(value = "删除照片")//物理删除或彻底删除
@DeleteMapping("/admin/photos")
public ResultVO<?> deletePhotos(@RequestBody List<Integer> photoIds) {
photoService.deletePhotos(photoIds);
return ResultVO.ok();
}
@ApiOperation(value = "根据相册id查看照片列表")//前端展示
@GetMapping("/albums/{albumId}/photos")
public ResultVO<PhotoDTO> listPhotosByAlbumId(@PathVariable("albumId") Integer albumId) {
//获取对应相册的照片列表图片展示信息比如照片URL、名称等
return ResultVO.ok(photoService.listPhotosByAlbumId(albumId));
}
}

@ -0,0 +1,57 @@
package com.aurora.controller;
import com.aurora.annotation.OptLog;
import com.aurora.model.dto.LabelOptionDTO;
import com.aurora.model.dto.ResourceDTO;
import com.aurora.model.vo.ResultVO;
import com.aurora.service.ResourceService;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.vo.ResourceVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
import static com.aurora.constant.OptTypeConstant.*;
@Api(tags = "资源模块")
@RestController
public class ResourceController {
@Autowired
private ResourceService resourceService;//调用Service层的资源业务方法
@ApiOperation(value = "查看资源列表")//获取后台管理的资源列表(比如系统的菜单资源、接口资源等)
@GetMapping("/admin/resources")
public ResultVO<List<ResourceDTO>> listResources(ConditionVO conditionVO) {
return ResultVO.ok(resourceService.listResources(conditionVO));
}
@OptLog(optType = DELETE)
@ApiOperation(value = "删除资源")
@DeleteMapping("/admin/resources/{resourceId}")//{resourceId}为路径参数,前端需传入具体的资源ID
public ResultVO<?> deleteResource(@PathVariable("resourceId") Integer resourceId) {
resourceService.deleteResource(resourceId);
return ResultVO.ok();
}
@OptLog(optType = SAVE_OR_UPDATE)
@ApiOperation(value = "新增或修改资源")
@PostMapping("/admin/resources")
public ResultVO<?> saveOrUpdateResource(@RequestBody @Valid ResourceVO resourceVO) {
//ResourceVO resourceVO封装资源新增、修改的参数包含资源名称、路径、类型、父资源ID等信息
resourceService.saveOrUpdateResource(resourceVO);
return ResultVO.ok();
}
//获取用于角色分配资源时的选项列表(比如给某个角色配置可访问的资源时,展示所有可选的资源)
@ApiOperation(value = "查看角色资源选项")
@GetMapping("/admin/role/resources")
//返回所有可用的资源选项列表
public ResultVO<List<LabelOptionDTO>> listResourceOption() {
return ResultVO.ok(resourceService.listResourceOption());
}
}

@ -0,0 +1,57 @@
package com.aurora.controller;
import com.aurora.annotation.OptLog;
import com.aurora.model.dto.RoleDTO;
import com.aurora.model.dto.UserRoleDTO;
import com.aurora.model.vo.ResultVO;
import com.aurora.service.RoleService;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.model.vo.RoleVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
import static com.aurora.constant.OptTypeConstant.*;
@Api(tags = "角色模块")
@RestController
public class RoleController {
@Autowired
private RoleService roleService;
@ApiOperation(value = "查询用户角色选项")
@GetMapping("/admin/users/role")
public ResultVO<List<UserRoleDTO>> listUserRoles() {
//返回所有可用的用户角色选项列表
return ResultVO.ok(roleService.listUserRoles());
}
@ApiOperation(value = "查询角色列表")//用于后台管理角色,分页的角色列表
@GetMapping("/admin/roles")
public ResultVO<PageResultDTO<RoleDTO>> listRoles(ConditionVO conditionVO) {
return ResultVO.ok(roleService.listRoles(conditionVO));
}
@OptLog(optType = SAVE_OR_UPDATE)
@ApiOperation(value = "保存或更新角色")
@PostMapping("/admin/role")
public ResultVO<?> saveOrUpdateRole(@RequestBody @Valid RoleVO roleVO) {
roleService.saveOrUpdateRole(roleVO);
return ResultVO.ok();
}
@OptLog(optType = DELETE)
@ApiOperation(value = "删除角色")
@DeleteMapping("/admin/roles")
public ResultVO<?> deleteRoles(@RequestBody List<Integer> roleIdList) {
roleService.deleteRoles(roleIdList);
return ResultVO.ok();
}
}

@ -0,0 +1,67 @@
package com.aurora.controller;
import com.aurora.annotation.OptLog;
import com.aurora.model.dto.TagAdminDTO;
import com.aurora.model.dto.TagDTO;
import com.aurora.model.vo.ResultVO;
import com.aurora.service.TagService;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.model.vo.TagVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
import static com.aurora.constant.OptTypeConstant.*;
@Api(tags = "标签模块")
@RestController
public class TagController {
@Autowired
private TagService tagService;
@ApiOperation("获取所有标签")//获取系统中所有的标签列表
@GetMapping("/tags/all")
public ResultVO<List<TagDTO>> getAllTags() {
return ResultVO.ok(tagService.listTags());
}
@ApiOperation("获取前十个标签")//获取前10个标签
@GetMapping("/tags/topTen")
public ResultVO<List<TagDTO>> getTopTenTags() {
return ResultVO.ok(tagService.listTopTenTags());
}
@ApiOperation(value = "查询后台标签列表")//获取后台管理用的标签分页列表
@GetMapping("/admin/tags")
public ResultVO<PageResultDTO<TagAdminDTO>> listTagsAdmin(ConditionVO conditionVO) {
return ResultVO.ok(tagService.listTagsAdmin(conditionVO));
}
@ApiOperation(value = "搜索文章标签")
@GetMapping("/admin/tags/search")
public ResultVO<List<TagAdminDTO>> listTagsAdminBySearch(ConditionVO condition) {
return ResultVO.ok(tagService.listTagsAdminBySearch(condition));
}
@OptLog(optType = SAVE_OR_UPDATE)
@ApiOperation(value = "添加或修改标签")
@PostMapping("/admin/tags")
public ResultVO<?> saveOrUpdateTag(@Valid @RequestBody TagVO tagVO) {
tagService.saveOrUpdateTag(tagVO);
return ResultVO.ok();
}
@OptLog(optType = DELETE)
@ApiOperation(value = "删除标签")
@DeleteMapping("/admin/tags")
public ResultVO<?> deleteTag(@RequestBody List<Integer> tagIdList) {
tagService.deleteTag(tagIdList);
return ResultVO.ok();
}
}

@ -0,0 +1,87 @@
package com.aurora.controller;
import com.aurora.annotation.OptLog;
import com.aurora.model.dto.TalkAdminDTO;
import com.aurora.model.dto.TalkDTO;
import com.aurora.enums.FilePathEnum;
import com.aurora.model.vo.ResultVO;
import com.aurora.service.TalkService;
import com.aurora.strategy.context.UploadStrategyContext;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.model.vo.TalkVO;
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(tags = "说说模块")
@RestController
public class TalkController {
@Autowired
private TalkService talkService;//说说业务逻辑
@Autowired
private UploadStrategyContext uploadStrategyContext;//文件上传服务
@ApiOperation(value = "查看说说列表")//用于前端展示,普通用户可查看
@GetMapping("/talks")
public ResultVO<PageResultDTO<TalkDTO>> listTalks() {
return ResultVO.ok(talkService.listTalks());
}
@ApiOperation(value = "根据id查看说说")
@ApiImplicitParam(name = "talkId", value = "说说id", required = true, dataType = "Integer")
@GetMapping("/talks/{talkId}")
//返回指定ID的说说详情
public ResultVO<TalkDTO> getTalkById(@PathVariable("talkId") Integer talkId) {
return ResultVO.ok(talkService.getTalkById(talkId));
}
@OptLog(optType = UPLOAD)
@ApiOperation(value = "上传说说图片")
@ApiImplicitParam(name = "file", value = "说说图片", required = true, dataType = "MultipartFile")
@PostMapping("/admin/talks/images")
public ResultVO<String> saveTalkImages(MultipartFile file) {
//调用方法,传入图片文件和说说图片的存储路径枚举,完成上传后包装路径返回
return ResultVO.ok(uploadStrategyContext.executeUploadStrategy(file, FilePathEnum.TALK.getPath()));
}
@OptLog(optType = SAVE_OR_UPDATE)
@ApiOperation(value = "保存或修改说说")
@PostMapping("/admin/talks")
public ResultVO<?> saveOrUpdateTalk(@Valid @RequestBody TalkVO talkVO) {
talkService.saveOrUpdateTalk(talkVO);
return ResultVO.ok();
}
@OptLog(optType = DELETE)//记录删除说说的相关日志
@ApiOperation(value = "删除说说")
@DeleteMapping("/admin/talks")
public ResultVO<?> deleteTalks(@RequestBody List<Integer> talkIds) {
talkService.deleteTalks(talkIds);
return ResultVO.ok();
}
@ApiOperation(value = "查看后台说说")//获取后台管理用的说说分页列表
@GetMapping("/admin/talks")
public ResultVO<PageResultDTO<TalkAdminDTO>> listBackTalks(ConditionVO conditionVO) {
return ResultVO.ok(talkService.listBackTalks(conditionVO));
}
@ApiOperation(value = "根据id查看后台说说")//查看指定ID的后台说说详情包含管理相关的信息
@ApiImplicitParam(name = "talkId", value = "说说id", required = true, dataType = "Integer")
@GetMapping("/admin/talks/{talkId}")
public ResultVO<TalkAdminDTO> getBackTalkById(@PathVariable("talkId") Integer talkId) {
return ResultVO.ok(talkService.getBackTalkById(talkId));
}
}

@ -0,0 +1,88 @@
package com.aurora.controller;
import com.aurora.annotation.AccessLimit;
import com.aurora.annotation.OptLog;
import com.aurora.model.dto.*;
import com.aurora.service.UserAuthService;
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 javax.validation.Valid;
import java.util.List;
import static com.aurora.constant.OptTypeConstant.UPDATE;
@Api(tags = "用户账号模块")
@RestController
public class UserAuthController {
@Autowired
private UserAuthService userAuthService;//用于调用用户账号相关的业务方法
@AccessLimit(seconds = 60,maxCount = 1)//自定义限流注解限制60s内只能调用1次防止验证码被频繁发送
@ApiOperation(value = "发送邮箱验证码")//给指定用户名(邮箱)发送验证码
@ApiImplicitParam(name = "username", value = "用户名", required = true, dataType = "String")
@GetMapping("/users/code")
public ResultVO<?> sendCode(String username) {
//String username:接收用户的邮箱账号
userAuthService.sendCode(username);
return ResultVO.ok();
}
@ApiOperation(value = "获取用户区域分布")//获取用户在不同区域的分布情况(用于后台数据统计分布)
@GetMapping("/admin/users/area")
//返回用户区域分布列表
public ResultVO<List<UserAreaDTO>> listUserAreas(ConditionVO conditionVO) {
return ResultVO.ok(userAuthService.listUserAreas(conditionVO));
}
@ApiOperation(value = "查询后台用户列表")//分页
@GetMapping("/admin/users")
public ResultVO<PageResultDTO<UserAdminDTO>> listUsers(ConditionVO conditionVO) {
return ResultVO.ok(userAuthService.listUsers(conditionVO));
}
@ApiOperation(value = "用户注册")
@PostMapping("/users/register")
public ResultVO<?> register(@Valid @RequestBody UserVO userVO) {
userAuthService.register(userVO);
return ResultVO.ok();
}
@OptLog(optType = UPDATE)
@ApiOperation(value = "修改密码")//用户自主修改个人密码
@PutMapping("/users/password")
public ResultVO<?> updatePassword(@Valid @RequestBody UserVO user) {
//UserVO user封装密码修改的参数包含原密码、新密码、验证码等信息且会被@Valid校验
userAuthService.updatePassword(user);
return ResultVO.ok();
}
@OptLog(optType = UPDATE)
@ApiOperation(value = "修改管理员密码")//后台
@PutMapping("/admin/users/password")
public ResultVO<?> updateAdminPassword(@Valid @RequestBody PasswordVO passwordVO) {
userAuthService.updateAdminPassword(passwordVO);
return ResultVO.ok();
}
@ApiOperation("用户登出")//处理用户的登出请求(清除登录状态)
@PostMapping("/users/logout")
public ResultVO<UserLogoutStatusDTO> logout() {//返回登出结果
return ResultVO.ok(userAuthService.logout());
}
@ApiOperation(value = "qq登录")//通过QQ账号进行第三方登录
@PostMapping("/users/oauth/qq")
//返回登录结果和用户信息
public ResultVO<UserInfoDTO> qqLogin(@Valid @RequestBody QQLoginVO qqLoginVO) {
//QQLoginVO qqLoginVO封装QQ登录的参数包含QQ授权码、openid等第三方登录所需信息且会被@Valid校验
return ResultVO.ok(userAuthService.qqLogin(qqLoginVO));
}
}

@ -0,0 +1,98 @@
package com.aurora.controller;
import com.aurora.annotation.OptLog;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.model.dto.UserInfoDTO;
import com.aurora.model.dto.UserOnlineDTO;
import com.aurora.service.UserInfoService;
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 static com.aurora.constant.OptTypeConstant.DELETE;
import static com.aurora.constant.OptTypeConstant.UPDATE;
@Api(tags = "用户信息模块")
@RestController
public class UserInfoController {
@Autowired
private UserInfoService userInfoService;//调用用户信息相关的业务方法
@OptLog(optType = UPDATE)
@ApiOperation("更新用户信息")//用户自主更新个人基本信息
@PutMapping("/users/info")
public ResultVO<?> updateUserInfo(@Valid @RequestBody UserInfoVO userInfoVO) {
userInfoService.updateUserInfo(userInfoVO);
return ResultVO.ok();
}
@OptLog(optType = UPDATE)
@ApiOperation("更新用户头像")
@ApiImplicitParam(name = "file", value = "用户头像", required = true, dataType = "MultipartFile")
@PostMapping("/users/avatar")
public ResultVO<String> updateUserAvatar(MultipartFile file) {
//MultipartFile file:接受用户上传的头像文件,返回更新后头像的存储路径(字符串格式)
return ResultVO.ok(userInfoService.updateUserAvatar(file));
}
@OptLog(optType = UPDATE)
@ApiOperation("绑定用户邮箱")//用户绑定个人邮箱(通常需要验证码验证)
@PutMapping("/users/email")
public ResultVO<?> saveUserEmail(@Valid @RequestBody EmailVO emailVO) {
//EmailVO emailVO:封装邮箱绑定的参数,包含邮箱地址、验证码等绑定信息,且会被@Valid校验
userInfoService.saveUserEmail(emailVO);
return ResultVO.ok();
}
@OptLog(optType = UPDATE)
@ApiOperation("修改用户的订阅状态")//用户自主修改订阅相关状态(比如订阅网站通知、内容推送等)
@PutMapping("/users/subscribe")
public ResultVO<?> updateUserSubscribe(@RequestBody SubscribeVO subscribeVO) {
userInfoService.updateUserSubscribe(subscribeVO);
return ResultVO.ok();
}
@OptLog(optType = UPDATE)
@ApiOperation(value = "修改用户角色")//后台管理员修改用户的角色(比如普通用户改为编辑)
@PutMapping("/admin/users/role")
public ResultVO<?> updateUserRole(@Valid @RequestBody UserRoleVO userRoleVO) {
userInfoService.updateUserRole(userRoleVO);
return ResultVO.ok();
}
@OptLog(optType = UPDATE)
@ApiOperation(value = "修改用户禁用状态")//后台管理员禁用/解禁用户账号
@PutMapping("/admin/users/disable")
public ResultVO<?> updateUserDisable(@Valid @RequestBody UserDisableVO userDisableVO) {
userInfoService.updateUserDisable(userDisableVO);
return ResultVO.ok();
}
@ApiOperation(value = "查看在线用户")//后台管理员查看当前在线的用户列表
@GetMapping("/admin/users/online")
public ResultVO<PageResultDTO<UserOnlineDTO>> listOnlineUsers(ConditionVO conditionVO) {
return ResultVO.ok(userInfoService.listOnlineUsers(conditionVO));
}
@OptLog(optType = DELETE)//记录用户下线的日志
@ApiOperation(value = "下线用户")//后台管理员强制让指定在线用户下线(清除在线状态)
@DeleteMapping("/admin/users/{userInfoId}/online")
public ResultVO<?> removeOnlineUser(@PathVariable("userInfoId") Integer userInfoId) {
userInfoService.removeOnlineUser(userInfoId);
return ResultVO.ok();
}
@ApiOperation("根据id获取用户信息")//获取指定ID用户的个人信息用于前端查看他人信息或后台查看用户详情
@GetMapping("/users/info/{userInfoId}")
public ResultVO<UserInfoDTO> getUserInfoById(@PathVariable("userInfoId") Integer userInfoId) {
return ResultVO.ok(userInfoService.getUserInfoById(userInfoId));
}
}

@ -0,0 +1,10 @@
package com.aurora.mapper;// 在Maven项目中mapper包通常位于数据访问层专门负责与数据库的交互操作
import com.aurora.entity.About;//导入和数据库对应的实体类About,接口操作的就是这个实体类对应的数据库表
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository //标注在接口上用于标记这是一个数据访问层的组件避免IDE报“找不到Bean"的警告让Spring扫描并管理该Bean
public interface AboutMapper extends BaseMapper<About> {
//AboutMapper可以直接使用BaseMapper里的基础CRUD方法来操作About对应的数据库表about无需手动编写基础的SQL语句
}

@ -0,0 +1,63 @@
package com.aurora.mapper;
import com.aurora.model.dto.ArticleAdminDTO;
// 导入各种数据传输对象(DTO),用于前台数据展示
import com.aurora.model.dto.ArticleCardDTO;
import com.aurora.model.dto.ArticleDTO;
import com.aurora.model.dto.ArticleStatisticsDTO;
import com.aurora.entity.Article;//导入和数据库对应的实体类Article,接口操作的就是这个实体类对应的数据库表
// 导入查询条件值对象
import com.aurora.model.vo.ConditionVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
// 定义ArticleMapper接口继承自MyBatis-Plus的BaseMapper接口泛型为Article实体类
// 继承BaseMapper后自动获得了基本的CRUD增删改查方法如insert、deleteById、updateById、selectById等
public interface ArticleMapper extends BaseMapper<Article> {
// 获取置顶和推荐文章列表(通常用于前段首页卡片展示)
// 返回ArticleCardDTO列表包含文章卡片所需的基本信息
List<ArticleCardDTO> listTopAndFeaturedArticles();
// 分页获取所有文章列表
// @Param注解给参数起别名在XML中可以通过#{current}和#{size}引用
// current: 当前页码size: 每页大小(每页条数)
List<ArticleCardDTO> listArticles(@Param("current") Long current, @Param("size") Long size);
// 根据分类ID分页获取文章列表
// categoryId: 分类ID用于筛选指定分类的文章
List<ArticleCardDTO> getArticlesByCategoryId(@Param("current") Long current, @Param("size") Long size, @Param("categoryId") Integer categoryId);
// 根据文章ID获取单篇文章详情包含文章完整内容等信息
// 返回ArticleDTO通常比ArticleCardDTO包含更多详细信息如文章正文
ArticleDTO getArticleById(@Param("articleId") Integer articleId);
// 根据当前文章ID获取上一篇文章的卡片信息按时间或ID排序
// 用于文章详情页的"上一篇"导航
ArticleCardDTO getPreArticleById(@Param("articleId") Integer articleId);
// 根据当前文章ID获取下一篇文章的卡片信息按时间或ID排序
// 用于文章详情页的"下一篇"导航
ArticleCardDTO getNextArticleById(@Param("articleId") Integer articleId);
// 获取第一篇文章的卡片信息按时间或ID排序通常是最早的文章
ArticleCardDTO getFirstArticle();
// 获取最后一篇文章的卡片信息按时间或ID排序通常是最新的文章
ArticleCardDTO getLastArticle();
// 根据标签ID分页获取文章归档列表
// tagId: 标签ID用于筛选带有指定标签的文章
List<ArticleCardDTO> listArticlesByTagId(@Param("current") Long current, @Param("size") Long size, @Param("tagId") Integer tagId);
// 分页获取文章归档列表(通常按时间分组,如按年月)
// 归档页面通常显示文章标题和创建时间,不显示完整内容
List<ArticleCardDTO> listArchives(@Param("current") Long current, @Param("size") Long size);
// 统计后台文章数量(根据查询条件)
// conditionVO: 包含各种查询条件,如关键词、状态、时间范围等
// 返回符合条件的文章总数,用于分页计算
Integer countArticleAdmins(@Param("conditionVO") ConditionVO conditionVO);
// 按条件分页查询获取后台文章列表(管理员功能)
// 返回ArticleAdminDTO列表可能包含更多管理字段如状态、浏览量等
List<ArticleAdminDTO> listArticlesAdmin(@Param("current") Long current, @Param("size") Long size, @Param("conditionVO") ConditionVO conditionVO);
// 获取文章统计信息(如按分类、标签、状态统计文章数量)
// 用于后台仪表盘的数据展示
List<ArticleStatisticsDTO> listArticleStatistics();
//@Param("参数名")MyBatis的注解用于给方法参数命名方便在对应的XML映射文件中通过#{参数名}获取参数值
}

@ -0,0 +1,10 @@
package com.aurora.mapper;
import com.aurora.entity.ArticleTag;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface ArticleTagMapper extends BaseMapper<ArticleTag> {//文章-标签关联模块的数据访问层接口
//通常用于存储文章ID和标签ID的对应关系实现文章和标签的多对多关联
}

@ -0,0 +1,25 @@
package com.aurora.mapper;
// 导入数据传输对象(DTO),用于前台数据展示
import com.aurora.model.dto.CategoryAdminDTO;
import com.aurora.model.dto.CategoryDTO;
import com.aurora.entity.Category;// 导入与数据库表对应的实体类CategoryMapper接口操作的就是这个实体类对应的数据库表
import com.aurora.model.vo.ConditionVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;// 导入MyBatis的参数注解用于给Mapper方法参数起别名
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface CategoryMapper extends BaseMapper<Category> {
// 获取所有分类列表(通常用于前台展示)
// 返回CategoryDTO列表包含分类的基本信息如分类名称、文章数量等
List<CategoryDTO> listCategories();
// 分页获取后台分类列表(管理员功能)
// @Param注解给参数起别名在XML中可以通过#{current}、#{size}、#{conditionVO.xxx}引用
// current: 当前页码size: 每页大小conditionVO: 封装查询条件(如关键词、状态等)
// 返回分页结果包含CategoryAdminDTO列表可能比前台DTO包含更多管理字段
List<CategoryAdminDTO> listCategoriesAdmin(@Param("current") Long current, @Param("size") Long size, @Param("conditionVO") ConditionVO conditionVO);
}

@ -0,0 +1,47 @@
package com.aurora.mapper;
// 导入评论数据传输对象(DTO),用于前台数据展示
import com.aurora.model.dto.CommentAdminDTO;
import com.aurora.model.dto.CommentCountDTO;
import com.aurora.model.dto.CommentDTO;
import com.aurora.model.dto.ReplyDTO;
import com.aurora.entity.Comment;// 导入与数据库表对应的实体类Comment
// 导入查询条件值对象
import com.aurora.model.vo.CommentVO;
import com.aurora.model.vo.ConditionVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface CommentMapper extends BaseMapper<Comment> {
// 分页获取评论列表(通常用于前台文章详情页的评论展示)
// @Param注解给参数起别名在XML中可以通过#{current}、#{size}、#{commentVO.xxx}引用
// current: 当前页码size: 每页大小commentVO: 封装评论查询条件如文章ID、排序方式等
// 返回CommentDTO列表包含评论的基本信息和作者信息
List<CommentDTO> listComments(@Param("current") Long current, @Param("size") Long size, @Param("commentVO") CommentVO commentVO);
// 根据评论ID列表获取这些评论的所有回复列表
// commentIdList: 评论ID集合用于批量查询多个评论的回复
// 返回ReplyDTO列表包含回复的详细信息如回复内容、回复对象等
List<ReplyDTO> listReplies(@Param("commentIds") List<Integer> commentIdList);
// 获取前六个热门评论(通常用于前端优先首页展示)
// 可能按点赞数、创建时间等排序规则选取前六个评论
List<CommentDTO> listTopSixComments();
// 统计评论数量(根据查询条件)
// conditionVO: 封装统计条件如时间范围、文章ID、评论状态等
// 返回符合条件的评论总数,用于后台分页计算和数据统计
Integer countComments(@Param("conditionVO") ConditionVO conditionVO);
// 分页获取后台评论列表(管理员功能)
// 返回CommentAdminDTO列表可能包含更多管理字段如IP地址、审核状态、操作日志等
List<CommentAdminDTO> listCommentsAdmin(@Param("current") Long current, @Param("size") Long size, @Param("conditionVO") ConditionVO conditionVO);
// 根据类型和主题ID列表批量统计评论数量
// type: 评论类型如文章评论、友链评论等topicIds: 主题ID列表如文章ID列表
// 返回CommentCountDTO列表包含每个主题的评论数量统计
List<CommentCountDTO> listCommentCountByTypeAndTopicIds(@Param("type") Integer type, @Param("topicIds") List<Integer> topicIds);
// 根据类型和单个主题ID统计评论数量
// topicId: 单个主题ID如某篇文章的ID
// 返回CommentCountDTO包含指定主题的评论数量统计
CommentCountDTO listCommentCountByTypeAndTopicId(@Param("type") Integer type, @Param("topicId") Integer topicId);
}

@ -0,0 +1,25 @@
package com.aurora.mapper;
// 导入项目内定义的ArticleSearchDTO类该类作为Elasticsearch文档的实体映射用于搜索结果的传输
import com.aurora.model.dto.ArticleSearchDTO;
// 导入Spring Data Elasticsearch提供的核心接口ElasticsearchRepository它封装了基本的Elasticsearch操作
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
/**
* @author
* elasticsearch
*/
// @Repository注解将本接口标识为数据访问层DAO组件让Spring能够扫描并管理其实例如依赖注入
// 该注解是@Component的特殊化表明其主要职责是与数据库此处为Elasticsearch交互
@Repository
// 定义ElasticsearchMapper接口继承自ElasticsearchRepository接口
// ElasticsearchRepository是Spring Data Elasticsearch提供的基础仓库核心接口提供了丰富的CRUD和搜索方法
// 内置了和Elasticsearch交互的基础方法比如全文搜索、数据新增/删除到ES索引等
// 泛型参数说明:
// - ArticleSearchDTO指定该Repository管理的实体类型即Elasticsearch中文档对应的Java对象用于ES搜索的文章数据传输对象包含文章标题、内容、标签等用于搜索的字段
// 该类应使用@Document等注解配置索引映射如索引名、类型等
// - IntegerDTO对应数据指定实体主键ID的数据类型。在Elasticsearch中每个文档都有一个唯一ID这里使用Integer类型。
public interface ElasticsearchMapper extends ElasticsearchRepository<ArticleSearchDTO,Integer> {//基于文章搜索模块的数据访问层接口
}

@ -0,0 +1,13 @@
package com.aurora.mapper;
import com.aurora.entity.ExceptionLog;// 导入异常日志实体类ExceptionLog该实体类与数据库中的异常日志表相对应
import com.baomidou.mybatisplus.core.mapper.BaseMapper;// 导入MyBatis-Plus的核心接口BaseMapper它提供了丰富的CRUD增删改查操作方法
import org.springframework.stereotype.Repository;
@Repository
// 定义ExceptionLogMapper接口继承自MyBatis-Plus的BaseMapper接口
// 泛型参数ExceptionLog指定了该Mapper操作的实体类型
// 继承BaseMapper后无需编写任何方法实现或XML映射文件即可获得完整的CRUD功能
public interface ExceptionLogMapper extends BaseMapper<ExceptionLog> {//异常日志模块的数据访问层接口
}

@ -0,0 +1,10 @@
package com.aurora.mapper;
// 实体类中的字段通常与数据库表字段一一映射包含友链的基本信息如名称、URL等
import com.aurora.entity.FriendLink;//导入友情链接实体类FriendLink该实体类与数据库中的友情链接表相对应
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface FriendLinkMapper extends BaseMapper<FriendLink> {//友链模块的数据访问层接口
}

@ -0,0 +1,17 @@
package com.aurora.mapper;
import com.aurora.entity.JobLog;//导入定时任务日志实体类JobLog该实体类与数据库中的存储定时任务执行日志表相对应通常记录任务名称、
//执行状态、执行时间、异常信息等内容)
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
import java.util.List;// 导入Java集合框架中的List接口用于返回多个日志分组名称的集合
@Repository
public interface JobLogMapper extends BaseMapper<JobLog> {//定时任务日志模块的数据访问层接口
// 自定义查询方法:获取任务日志的所有分组列表(通常基于特定字段进行分组统计)
// 返回字符串列表,每个字符串代表一个分组标识(如按任务状态分组返回["SUCCESS", "FAILED"],或按时间维度分组如["2025-10", "2025-09"]
// 此方法常用于后台管理系统中的日志分类筛选、统计图表展示或日志归档功能
List<String> listJobLogGroups();
}

@ -0,0 +1,31 @@
package com.aurora.mapper;
// 导入定时任务数据传输对象(JobDTO),用于前台数据展示,通常包含更丰富的业务字段
import com.aurora.model.dto.JobDTO;
import com.aurora.entity.Job;// 导入与数据库表对应的实体类JobMapper接口操作的就是这个实体类对应的数据库表
import com.aurora.model.vo.JobSearchVO;// 导入定时任务搜索条件值对象(JobSearchVO),封装前端传递的搜索参数
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;// 导入Java集合框架中的List接口用于返回多个职位的集合
@Repository
// 定义JobMapper接口继承自MyBatis-Plus的BaseMapper接口
// 泛型参数Job指定了该Mapper操作的实体类型
// 继承BaseMapper后自动获得了数十个基本的CRUD增删改查方法
public interface JobMapper extends BaseMapper<Job> {
// 统计满足条件的定时任务数量(用于分页查询中的总数计算)
// @Param注解给参数起别名在XML中可以通过#{jobSearchVO.xxx}引用参数属性
// jobSearchVO: 封装搜索条件(如关键词、状态、时间范围等)
// 返回符合条件的定时任务操作总数Integer类型避免null值问题
Integer countJobs(@Param("jobSearchVO") JobSearchVO jobSearchVO);
// 分页获取定时任务操作列表(支持条件搜索)
// current: 当前页码从1开始size: 每页记录数
// jobSearchVO: 封装搜索条件对象,包含各种筛选参数
// 返回JobDTO列表包含定时任务操作信息和相关业务数据如定时操作任务名称、类型、统计信息等
List<JobDTO> listJobs(@Param("current") Long current, @Param("size") Long size, @Param("jobSearchVO")JobSearchVO jobSearchVO);
// 获取所有定时任务操作分组列表(用于分类筛选或下拉选择框)
// 返回字符串列表,每个字符串代表一个分组标识
List<String> listJobGroups();
}

@ -0,0 +1,20 @@
package com.aurora.mapper;
import com.aurora.entity.Menu;// 导入菜单实体类Menu该实体类与数据库中的菜单表相对应
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
import java.util.List;// 导入Java集合框架中的List接口用于返回多个菜单的集合
@Repository
//定义MenuMapper接口继承自MyBatis-Plus的BaseMapper接口
// 泛型参数Menu指定了该Mapper操作的实体类型
// 继承BaseMapper后自动获得了基本的CRUD方法如insert、deleteById、updateById、selectById等
public interface MenuMapper extends BaseMapper<Menu> {
// 自定义查询方法根据用户信息ID查询该用户有权访问的菜单列表
// 用户信息ID通常对应系统用户的主键ID
// 返回菜单实体列表,包含菜单的基本信息(如菜单名称、路径、图标等)
// 此方法通常用于实现基于角色的权限控制RBAC不同用户角色看到不同的菜单
List<Menu> listMenusByUserInfoId(Integer userInfoId);
}

@ -0,0 +1,12 @@
package com.aurora.mapper;
// 导入操作日志实体类OperationLog该实体类与数据库中的操作日志表如operate_log相对应
// 实体类中的字段如id、operateTime、className等与数据库表字段一一映射
import com.aurora.entity.OperationLog;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface OperationLogMapper extends BaseMapper<OperationLog> {
//OperationLog实体类通常用于记录系统操作日志如用户行为、后台管理操作
}

@ -0,0 +1,23 @@
package com.aurora.mapper;
// 导入相册管理数据传输对象PhotoAlbumAdminDTO用于后台管理界面展示相册信息通常包含相册基本属性及统计字段如照片数量
import com.aurora.model.dto.PhotoAlbumAdminDTO;
// 导入相册实体类PhotoAlbum该实体类与数据库中的相册表如 photo_album相对应其字段如相册名、封面图路径与表结构映射
import com.aurora.entity.PhotoAlbum;
// 导入条件查询值对象ConditionVO用于封装前端传递的复杂查询参数如关键词、状态、时间范围等
import com.aurora.model.vo.ConditionVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;// 导入 Java 集合框架中的 List 接口用于返回多个相册管理DTO对象的集合
@Repository
public interface PhotoAlbumMapper extends BaseMapper<PhotoAlbum> {
// 自定义查询方法:分页获取后台相册管理列表(通常用于管理员控制台)
// @Param 注解为参数指定别名,在 XML 映射文件中可通过 `#{current}`、`#{size}`、`#{condition.xxx}` 引用参数值
// current: 当前页码从1开始size: 每页记录数,用于实现分页查询
// condition: 封装查询条件的对象,可能包含搜索关键词(如相册名称)、筛选状态(如是否公开)、时间范围等
// 返回 PhotoAlbumAdminDTO 列表,该 DTO 通常扩展了相册基础信息,包含管理所需的附加字段(如相册下照片数量、创建人信息等)
List<PhotoAlbumAdminDTO> listPhotoAlbumsAdmin(@Param("current") Long current, @Param("size") Long size, @Param("condition") ConditionVO conditionVO);
}

@ -0,0 +1,10 @@
package com.aurora.mapper;
import com.aurora.entity.Photo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface PhotoMapper extends BaseMapper<Photo> {
}

@ -0,0 +1,10 @@
package com.aurora.mapper;
import com.aurora.entity.Resource;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface ResourceMapper extends BaseMapper<Resource> {
}

@ -0,0 +1,32 @@
package com.aurora.mapper;
// 导入资源角色数据传输对象ResourceRoleDTO用于封装资源与角色的关联信息通常在权限管理中使用
import com.aurora.model.dto.ResourceRoleDTO;
// 导入角色数据传输对象RoleDTO用于前台或服务层角色信息的展示可能包含角色基本属性及扩展字段
import com.aurora.model.dto.RoleDTO;
// 导入角色实体类Role该实体类与数据库中的角色表如 sys_role相对应其字段如角色名、状态与表结构映射 [1,2]
import com.aurora.entity.Role;
// 导入条件查询值对象ConditionVO用于封装前端传递的复杂查询参数如关键词、状态、时间范围等
import com.aurora.model.vo.ConditionVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;// 导入 Java 集合框架中的 List 接口,用于返回多个角色或字符串的集合
@Repository
public interface RoleMapper extends BaseMapper<Role> {
// 自定义查询方法:获取资源与角色的映射列表
// 返回 ResourceRoleDTO 列表,每个对象可能包含资源路径(如 URL和可访问该资源的角色列表如 ROLE_ADMIN
List<ResourceRoleDTO> listResourceRoles();
// 自定义查询方法根据用户信息ID查询该用户所拥有的角色标识列表
// @Param 注解为参数指定别名 "userInfoId",在 XML 映射文件中可通过 #{userInfoId}引用参数值
// userInfoId: 用户信息表的主键 ID用于关联查询用户-角色关系表
// 返回字符串列表,每个字符串代表一个角色标识(如 "admin"、"user"),通常用于构建用户的权限集合
List<String> listRolesByUserInfoId(@Param("userInfoId") Integer userInfoId);
// 自定义查询方法:分页获取角色列表(支持条件查询,通常用于后台角色管理界面)
// current: 当前页码从1开始size: 每页记录数
// conditionVO: 封装查询条件的对象,可能包含角色名称关键词、状态标识、创建时间范围等筛选条件
// 返回 RoleDTO 列表,该 DTO 通常扩展了角色基础信息,包含管理或展示所需的附加字段(如用户数量、权限列表等)
List<RoleDTO> listRoles(@Param("current") Long current, @Param("size") Long size, @Param("conditionVO") ConditionVO conditionVO);
}

@ -0,0 +1,10 @@
package com.aurora.mapper;
import com.aurora.entity.RoleMenu;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface RoleMenuMapper extends BaseMapper<RoleMenu> {
}

@ -0,0 +1,12 @@
package com.aurora.mapper;
import com.aurora.entity.RoleResource;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface RoleResourceMapper extends BaseMapper<RoleResource> {
}

@ -0,0 +1,29 @@
package com.aurora.mapper;
import com.aurora.model.dto.TagAdminDTO;// 导入标签管理数据传输对象TagAdminDTO用于后台管理界面展示标签信息可能包含更多管理字段
import com.aurora.model.dto.TagDTO;// 导入标签数据传输对象TagDTO用于前台数据展示通常包含标签基本属性及扩展字段
import com.aurora.entity.Tag;// 导入标签实体类Tag该实体类与数据库中的标签表如 tag 或 ms_tag相对应其字段与表结构映射
import com.aurora.model.vo.ConditionVO;// 导入条件查询值对象ConditionVO用于封装前端传递的复杂查询参数如关键词、状态、时间范围等
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface TagMapper extends BaseMapper<Tag> {
// 自定义查询方法:获取所有标签列表(通常用于前台标签或标签选择框),返回 TagDTO 列表,包含标签的基本信息(如标签名、颜色、图标等),用于前端展示
List<TagDTO> listTags();
// 自定义查询方法:获取前十热门标签列表(通常用于侧边栏或首页展示),热门标签的判定标准通常是关联的文章数量,关联文章越多的标签越热门
// 返回 TagDTO 列表通常按文章数量降序排列仅返回前10条记录
List<TagDTO> listTopTenTags();
// 自定义查询方法根据文章ID查询该文章的所有标签名称列表返回字符串列表每个字符串代表一个标签名称用于文章详情页展示文章的标签集合
// articleId: 文章的主键ID用于关联查询文章-标签关系表(如 ms_article_tag
List<String> listTagNamesByArticleId(Integer articleId);
// 自定义查询方法:分页获取后台标签管理列表(支持条件查询,用于管理员控制台)
// current: 当前页码从1开始size: 每页记录数用于实现分页查询LIMIT 机制)
// conditionVO: 封装查询条件的对象,可能包含标签名称关键词、状态标识、创建时间范围等筛选条件
// 返回 TagAdminDTO 列表,该 DTO 通常扩展了标签基础信息,包含管理所需的附加字段(如关联文章数、创建人信息等)
List<TagAdminDTO> listTagsAdmin(@Param("current") Long current, @Param("size") Long size, @Param("conditionVO") ConditionVO conditionVO);
}

@ -0,0 +1,24 @@
package com.aurora.mapper;
import com.aurora.model.dto.TalkAdminDTO;
import com.aurora.model.dto.TalkDTO;
import com.aurora.entity.Talk;
import com.aurora.model.vo.ConditionVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface TalkMapper extends BaseMapper<Talk> {
List<TalkDTO> listTalks(@Param("current") Long current, @Param("size") Long size);
TalkDTO getTalkById(@Param("talkId") Integer talkId);
List<TalkAdminDTO> listTalksAdmin(@Param("current") Long current, @Param("size") Long size, @Param("conditionVO") ConditionVO conditionVO);
TalkAdminDTO getTalkByIdAdmin(@Param("talkId") Integer talkId);
}

@ -0,0 +1,19 @@
package com.aurora.mapper;
import com.aurora.model.dto.UniqueViewDTO;// 导入独立访客数据传输对象UniqueViewDTO用于前台数据展示通常包含统计结果
import com.aurora.entity.UniqueView;// 导入独立访客实体类UniqueView该实体类与数据库中的独立访客统计表如 unique_view相对应
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.Date;// 导入 Java 的 Date 类,用于处理时间范围参数
import java.util.List;// 导入 Java 集合框架中的 List 接口,用于返回多个 UniqueViewDTO 对象的集合
@Repository
public interface UniqueViewMapper extends BaseMapper<UniqueView> {
// 自定义查询方法:根据时间范围查询独立访客统计列表(通常用于图表展示或数据分析)
// startTime: 统计开始时间endTime: 统计结束时间用于限定查询的时间范围如查询某一天的UV数据
// 返回 UniqueViewDTO 列表,每个对象可能包含统计时间点(如日期)和对应的独立访客数量
List<UniqueViewDTO> listUniqueViews(@Param("startTime") Date startTime, @Param("endTime") Date endTime);
}

@ -0,0 +1,26 @@
package com.aurora.mapper;
// 导入用户管理数据传输对象UserAdminDTO用于后台管理界面展示用户信息通常包含用户基本属性及扩展字段如角色信息、状态标识等
import com.aurora.model.dto.UserAdminDTO;
// 导入用户认证实体类UserAuth该实体类与数据库中的用户认证表如 user_auth相对应其字段如用户名、密码、状态与表结构映射
import com.aurora.entity.UserAuth;
// 导入条件查询值对象ConditionVO用于封装前端传递的复杂查询参数如关键词、状态、时间范围等
import com.aurora.model.vo.ConditionVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface UserAuthMapper extends BaseMapper<UserAuth> {
//自定义查询方法:分页获取后台用户管理列表(通常用于管理员控制台)
// conditionVO: 封装查询条件的对象,可能包含用户名关键词、账号状态、注册时间范围等筛选条件
// 返回 UserAdminDTO 列表,该 DTO 通常扩展了用户基础信息,包含管理所需的附加字段(如角色列表、最后登录时间等)
List<UserAdminDTO> listUsers(@Param("current") Long current, @Param("size") Long size, @Param("conditionVO") ConditionVO conditionVO);
// 自定义统计方法:根据条件统计用户数量(通常用于分页查询中的总数计算)
// conditionVO: 封装统计条件的对象,与 listUsers 方法中的条件一致,确保分页统计的准确性
// 返回符合条件的用户总数Integer 类型避免 null 值问题,用于前端分页组件计算总页数
Integer countUser(@Param("conditionVO") ConditionVO conditionVO);
}

@ -0,0 +1,10 @@
package com.aurora.mapper;
import com.aurora.entity.UserInfo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface UserInfoMapper extends BaseMapper<UserInfo> {
}

@ -0,0 +1,10 @@
package com.aurora.mapper;
import com.aurora.entity.UserRole;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRoleMapper extends BaseMapper<UserRole> {
}

@ -0,0 +1,12 @@
package com.aurora.mapper;
// 导入WebsiteConfig实体类该类与数据库中的网站配置表相对应
// 实体类中的字段如站点名称、Logo路径、备案号等与数据库表字段一一映射
import com.aurora.entity.WebsiteConfig;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface WebsiteConfigMapper extends BaseMapper<WebsiteConfig> {
// 接口体为空,不定义任何自定义方法
// 因为继承BaseMapper<WebsiteConfig>已经提供了数十个基础的数据库操作方法
}

@ -0,0 +1,71 @@
// service 层通常负责业务逻辑处理,是控制器层(Controller)和数据访问层(Mapper)之间的桥梁
package com.aurora.service;
// 导入文章相关的数据传输对象(DTO),用于在不同层之间传输数据
import com.aurora.model.dto.*;
import com.aurora.entity.Article;// 导入文章实体类(Article),与数据库中的文章表相对应
import com.aurora.model.vo.*;// 导入值对象(VO),用于接收前端传递的参数
import com.baomidou.mybatisplus.extension.service.IService;// 导入 MyBatis-Plus 的服务接口,提供了通用的服务层方法
// 导入 Java 集合框架中的 List 接口用于返回多个文章的集合或ID列表
import java.util.List;
// 定义文章服务接口,继承自 MyBatis-Plus 的 IService 接口,并指定泛型为 Article 实体类
// 继承 IService 后,自动获得了通用的 CRUD 操作方法,如 save、remove、update、get 等
public interface ArticleService extends IService<Article> {
// 获取置顶和推荐文章列表(通常用于首页展示)
// 返回 TopAndFeaturedArticlesDTO 对象,包含置顶文章和推荐文章列表
TopAndFeaturedArticlesDTO listTopAndFeaturedArticles();
// 分页获取所有文章列表(用于文章列表页)
// 返回 PageResultDTO<ArticleCardDTO> 分页结果,包含文章卡片信息列表
PageResultDTO<ArticleCardDTO> listArticles();
// 根据分类ID分页获取文章列表
// categoryId: 分类ID用于筛选指定分类下的文章
// 返回分页的文章卡片列表
PageResultDTO<ArticleCardDTO> listArticlesByCategoryId(Integer categoryId);
// 根据文章ID获取单个文章详情用于文章详情页
// articleId: 文章主键ID
// 返回 ArticleDTO 对象,包含文章的完整内容及关联信息
ArticleDTO getArticleById(Integer articleId);
// 校验文章访问密码(用于访问受密码保护的文章)
// articlePasswordVO: 包含文章ID和访问密码的值对象
// 无返回值,通常通过抛出异常来处理密码错误的情况
void accessArticle(ArticlePasswordVO articlePasswordVO);
// 根据标签ID分页获取文章列表
// tagId: 标签ID用于筛选带有指定标签的文章
// 返回分页的文章卡片列表
PageResultDTO<ArticleCardDTO> listArticlesByTagId(Integer tagId);
// 获取文章归档列表(按时间分组,如按年月归档)
// 返回分页的 ArchiveDTO 列表,包含归档时间和对应的文章列表
PageResultDTO<ArchiveDTO> listArchives();
// 获取后台文章管理列表(管理员功能,支持条件查询和分页)
// conditionVO: 封装查询条件的值对象,如关键词、状态、时间范围等
// 返回分页的 ArticleAdminDTO 列表,包含文章的管理信息
PageResultDTO<ArticleAdminDTO> listArticlesAdmin(ConditionVO conditionVO);
// 保存或更新文章根据ID判断是新增还是修改
// articleVO: 包含文章信息的值对象,用于接收前端传递的数据
// 无返回值,操作结果通过异常或日志处理
void saveOrUpdateArticle(ArticleVO articleVO);
// 更新文章的置顶和推荐状态
// articleTopFeaturedVO: 包含文章ID和置顶、推荐状态的值对象
// 无返回值,直接更新数据库中的文章状态字段
void updateArticleTopAndFeatured(ArticleTopFeaturedVO articleTopFeaturedVO);
// 更新文章的删除状态(逻辑删除,非物理删除)
// deleteVO: 包含文章ID和删除状态的值对象
// 无返回值,通过更新文章的删除标记字段实现软删除
void updateArticleDelete(DeleteVO deleteVO);
// 物理删除文章(从数据库中彻底删除)
// articleIds: 文章ID列表支持批量删除
// 无返回值,直接删除数据库中的文章记录
void deleteArticles(List<Integer> articleIds);
// 根据文章ID获取后台管理所需的文章详情可能包含更多管理字段
// articleId: 文章主键ID
// 返回 ArticleAdminViewDTO 对象,包含文章详情及管理信息
ArticleAdminViewDTO getArticleByIdAdmin(Integer articleId);
// 导出文章通常导出为特定格式的文件如Markdown、PDF等
// articleIdList: 需要导出的文章ID列表
// 返回字符串列表,可能包含导出文件的路径或导出内容的标识
List<String> exportArticles(List<Integer> articleIdList);
// 根据搜索条件查询文章列表(用于搜索功能)
// condition: 包含搜索关键词等条件的值对象
// 返回 ArticleSearchDTO 列表,包含文章的搜索相关信息
List<ArticleSearchDTO> listArticlesBySearch(ConditionVO condition);
}

@ -0,0 +1,8 @@
package com.aurora.service;
import com.aurora.entity.ArticleTag;
import com.baomidou.mybatisplus.extension.service.IService;
public interface ArticleTagService extends IService<ArticleTag> {
}

@ -0,0 +1,31 @@
package com.aurora.service;
//导入关于我信息的数据传输对象AboutDTO用于前台数据展示通常包含关于我页面的文本内容、作者信息等
import com.aurora.model.dto.AboutDTO;
// 导入博客后台信息数据传输对象AuroraAdminInfoDTO用于管理员控制台展示系统统计信息如访客数、文章数等
import com.aurora.model.dto.AuroraAdminInfoDTO;
// 导入博客首页信息数据传输对象AuroraHomeInfoDTO用于前台首页展示核心数据如置顶文章、分类列表等
import com.aurora.model.dto.AuroraHomeInfoDTO;
// 导入网站配置数据传输对象WebsiteConfigDTO用于前台获取站点基本信息如站点名称、Logo、备案号等
import com.aurora.model.dto.WebsiteConfigDTO;
// 导入关于我信息值对象AboutVO用于接收前端传递的关于我信息更新参数
import com.aurora.model.vo.AboutVO;
// 导入网站配置值对象WebsiteConfigVO用于接收前端传递的网站配置更新参数
import com.aurora.model.vo.WebsiteConfigVO;
public interface AuroraInfoService {
//上报访客信息,通常用于记录用户访问日志或更新访客计数
void report();//无参数和返回值,执行结果通过异常或日志处理
//获取博客首页展示信息(用于前台首页渲染)
//返回结果通常包含文章列表、分类数据、标签云、站点统计等聚合信息
AuroraHomeInfoDTO getAuroraHomeInfo();//@return AuroraHomeInfoDTO 首页信息对象,封装了前台首页所需的所有数据
// 获取博客后台管理信息(用于管理员控制台仪表盘)
//返回结果通常包含系统核心统计指标,如总访客数、文章数量、评论数量、系统状态等
AuroraAdminInfoDTO getAuroraAdminInfo();//返回值:后台信息对象,专用于管理员视角的数据展示
//更新网站配置信息如站点名称、Logo、页脚信息等,该方法通常需要权限校验,确保只有管理员可操作
//@param websiteConfigVO 网站配置值对象,包含待更新的配置字段
void updateWebsiteConfig(WebsiteConfigVO websiteConfigVO);
WebsiteConfigDTO getWebsiteConfig();//获取当前网站配置信息(用于前台或后台展示),WebsiteConfigDTO 网站配置数据传输对象,包含所有可配置的站点参数
void updateAbout(AboutVO aboutVO);//更新关于我信息(用于修改关于页面的内容),@param aboutVO:关于我信息值对象,包含更新后的关于我内容
AboutDTO getAbout();//获取关于我信息(用于前台关于页面展示),@return AboutDTO:关于我信息数据传输对象,用于前端渲染
}

@ -0,0 +1,28 @@
package com.aurora.service;
import com.aurora.model.dto.CategoryAdminDTO;// 导入分类管理的数据传输对象CategoryAdminDTO用于后台管理界面展示分类信息通常包含管理所需的扩展字段
import com.aurora.model.dto.CategoryDTO;
import com.aurora.model.dto.CategoryOptionDTO;// 导入分类选项数据传输对象CategoryOptionDTO通常用于下拉选择框等场景仅包含关键标识字段
import com.aurora.entity.Category;
import com.aurora.model.vo.CategoryVO;// 导入分类值对象CategoryVO用于接收前端传递的分类新增或修改参数
import com.aurora.model.vo.ConditionVO;// 导入条件查询值对象ConditionVO用于封装前端传递的复杂查询参数如关键词、状态、分页信息等
import com.aurora.model.dto.PageResultDTO;// 导入分页结果数据传输对象PageResultDTO用于封装分页查询结果包含数据列表和总数等信息
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
//主要用于处理分类的增删改查、列表查询和搜索等核心业务逻辑
public interface CategoryService extends IService<Category> {
List<CategoryDTO> listCategories();//获取所有分类列表(通常用于前台分类展示或导航菜单),包含分类的核心展示信息
//分页获取后台分类管理列表(支持条件查询,用于管理员控制台)
//用于后台管理界面,支持根据条件进行筛选和分页展示
//@return PageResultDTO<CategoryAdminDTO> 分页结果对象,包含分类管理数据列表和分页信息
PageResultDTO<CategoryAdminDTO> listCategoriesAdmin(ConditionVO conditionVO);
//根据搜索条件查询分类选项列表(通常用于搜索或筛选场景),该方法返回精简的分类选项,常用于下拉选择框或搜索联想
List<CategoryOptionDTO> listCategoriesBySearch(ConditionVO conditionVO);
void deleteCategories(List<Integer> categoryIds);//批量删除分类根据分类ID列表物理删除或逻辑删除
void saveOrUpdateCategory(CategoryVO categoryVO);//保存或更新分类信息根据ID自动判断是新增还是修改操作
}

@ -0,0 +1,33 @@
package com.aurora.service;
import com.aurora.model.dto.CommentAdminDTO;
import com.aurora.model.dto.CommentDTO;
import com.aurora.model.dto.ReplyDTO;// 导入回复数据传输对象ReplyDTO专门用于封装评论的回复信息通常包含被回复对象标识和回复内容
import com.aurora.entity.Comment;
import com.aurora.model.vo.CommentVO;// 导入评论值对象CommentVO用于接收前端传递的评论新增或修改参数通常进行参数校验
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.model.vo.ReviewVO;// 导入审核值对象ReviewVO用于接收评论审核操作的相关参数如评论ID集合和审核状态
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
//主要用于处理评论的增删改查、回复管理、热门评论筛选和审核等核心业务逻辑
public interface CommentService extends IService<Comment> {
void saveComment(CommentVO commentVO);//保存评论信息(包含评论和回复的保存逻辑)
PageResultDTO<CommentDTO> listComments(CommentVO commentVO);//分页获取评论列表(支持条件查询,用于文章详情页的评论展示)
List<ReplyDTO> listRepliesByCommentId(Integer commentId);//根据评论ID获取该评论的所有回复列表实现楼中楼功能
List<CommentDTO> listTopSixComments();//获取前六条热门评论(通常用于首页或侧边栏展示)
/**
* ,
* @param conditionVO
* @return PageResultDTO<CommentAdminDTO>
*/
PageResultDTO<CommentAdminDTO> listCommentsAdmin(ConditionVO conditionVO);
void updateCommentsReview(ReviewVO reviewVO);//更新评论的审核状态(批量审核通过或拒绝),需要权限校验,确保只有管理员可执行审核操作
}

@ -0,0 +1,13 @@
package com.aurora.service;
import com.aurora.model.dto.ExceptionLogDTO;
import com.aurora.entity.ExceptionLog;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.dto.PageResultDTO;
import com.baomidou.mybatisplus.extension.service.IService;
//主要用于处理异常日志的查询、分页展示等核心业务逻辑,通常与全局异常处理器或 AOP 切面配合使用
public interface ExceptionLogService extends IService<ExceptionLog> {
//分页获取异常日志列表(支持条件查询,通常用于后台管理系统的日志查看界面)
PageResultDTO<ExceptionLogDTO> listExceptionLogs(ConditionVO conditionVO);
}

@ -0,0 +1,21 @@
package com.aurora.service;
import com.aurora.model.dto.FriendLinkAdminDTO;
import com.aurora.model.dto.FriendLinkDTO;
import com.aurora.entity.FriendLink;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.vo.FriendLinkVO;
import com.aurora.model.dto.PageResultDTO;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface FriendLinkService extends IService<FriendLink> {
List<FriendLinkDTO> listFriendLinks();//获取所有友情链接列表(通常用于前台展示,如友情链接页面或侧边栏)
PageResultDTO<FriendLinkAdminDTO> listFriendLinksAdmin(ConditionVO conditionVO);//分页获取后台友情链接管理列表(支持条件查询,用于管理员控制台)
void saveOrUpdateFriendLink(FriendLinkVO friendLinkVO);
}

@ -0,0 +1,23 @@
package com.aurora.service;
import com.aurora.model.dto.JobLogDTO;
import com.aurora.entity.JobLog;
import com.aurora.model.vo.JobLogSearchVO;// 导入作业日志搜索值对象JobLogSearchVO用于封装前端传递的复杂查询参数如关键词、时间范围、执行状态等筛选条件
import com.aurora.model.dto.PageResultDTO;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface JobLogService extends IService<JobLog> {
//分页获取作业日志列表(支持条件查询,通常用于后台管理系统的日志查看界面)
PageResultDTO<JobLogDTO> listJobLogs(JobLogSearchVO jobLogSearchVO);
//批量删除作业日志记录根据ID列表物理删除或逻辑删除
void deleteJobLogs(List<Integer> ids);
//清空作业日志记录(通常用于清理过期或冗余的日志数据)
void cleanJobLogs();
//获取作业日志的所有分组列表(通常用于统计或筛选场景)
List<String> listJobLogGroups();
}

@ -0,0 +1,29 @@
package com.aurora.service;
import com.aurora.model.dto.JobDTO;
import com.aurora.entity.Job;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.model.vo.*;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface JobService extends IService<Job> {
void saveJob(JobVO jobVO);
void updateJob(JobVO jobVO);//导入作业值对象JobVO用于接收前端传递的作业新增或修改参数通常需要校验作业是否存在且未被执行中
void deleteJobs(List<Integer> tagIds);//批量删除作业根据ID列表物理删除或逻辑删除
JobDTO getJobById(Integer jobId);
PageResultDTO<JobDTO> listJobs(JobSearchVO jobSearchVO);// 导入作业搜索值对象JobSearchVO用于封装前端传递的复杂查询参数如关键词、状态、时间范围等筛选条件
void updateJobStatus(JobStatusVO jobStatusVO);// 导入作业状态值对象JobStatusVO用于接收作业状态更新参数如启用、禁用等操作
//该方法会绕过正常的调度计划,立即触发作业执行一次
void runJob(JobRunVO jobRunVO);// 导入作业运行值对象JobRunVO用于接收立即执行作业的参数如作业ID、执行参数等
List<String> listJobGroups();
}

@ -0,0 +1,28 @@
package com.aurora.service;
import com.aurora.model.dto.LabelOptionDTO;
import com.aurora.model.dto.MenuDTO;
import com.aurora.model.dto.UserMenuDTO;
import com.aurora.entity.Menu;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.vo.IsHiddenVO;
import com.aurora.model.vo.MenuVO;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
//主要用于处理菜单的增删改查、层级结构构建、权限过滤和状态管理等核心业务逻辑
public interface MenuService extends IService<Menu> {
List<MenuDTO> listMenus(ConditionVO conditionVO);//根据条件查询菜单列表(支持动态筛选,用于后台管理系统),常返回菜单的层级结构数据
void saveOrUpdateMenu(MenuVO menuVO);
void updateMenuIsHidden(IsHiddenVO isHiddenVO);//该方法通常需要权限校验,确保只有管理员可操作菜单的显示状态
void deleteMenu(Integer menuId);
List<LabelOptionDTO> listMenuOptions();
List<UserMenuDTO> listUserMenus();
}

@ -0,0 +1,13 @@
package com.aurora.service;
import com.aurora.model.dto.OperationLogDTO;
import com.aurora.entity.OperationLog;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.dto.PageResultDTO;
import com.baomidou.mybatisplus.extension.service.IService;
public interface OperationLogService extends IService<OperationLog> {
PageResultDTO<OperationLogDTO> listOperationLogs(ConditionVO conditionVO);
}

@ -0,0 +1,27 @@
package com.aurora.service;
import com.aurora.model.dto.PhotoAlbumAdminDTO;
import com.aurora.model.dto.PhotoAlbumDTO;
import com.aurora.entity.PhotoAlbum;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.model.vo.PhotoAlbumVO;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface PhotoAlbumService extends IService<PhotoAlbum> {
void saveOrUpdatePhotoAlbum(PhotoAlbumVO photoAlbumVO);
PageResultDTO<PhotoAlbumAdminDTO> listPhotoAlbumsAdmin(ConditionVO condition);
List<PhotoAlbumDTO> listPhotoAlbumInfosAdmin();
PhotoAlbumAdminDTO getPhotoAlbumByIdAdmin(Integer albumId);
void deletePhotoAlbumById(Integer albumId);
List<PhotoAlbumDTO> listPhotoAlbums();//返回结果通常按相册的创建时间或排序权重进行排序,包含前台展示所需的核心信息
}

@ -0,0 +1,29 @@
package com.aurora.service;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.model.dto.PhotoAdminDTO;
import com.aurora.model.dto.PhotoAlbumAdminDTO;
import com.aurora.model.dto.PhotoDTO;
import com.aurora.entity.Photo;
import com.aurora.model.vo.*;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface PhotoService extends IService<Photo> {
PageResultDTO<PhotoAdminDTO> listPhotos(ConditionVO conditionVO);
void updatePhoto(PhotoInfoVO photoInfoVO);// 导入照片信息值对象PhotoInfoVO用于接收前端传递的照片信息更新参数通常进行参数校验
void savePhotos(PhotoVO photoVO);//导入照片值对象PhotoVO用于接收前端传递的照片新增或批量操作参数
void updatePhotosAlbum(PhotoVO photoVO);
void updatePhotoDelete(DeleteVO deleteVO);//导入删除值对象DeleteVO用于接收删除操作的参数如逻辑删除状态标识
void deletePhotos(List<Integer> photoIds);
PhotoDTO listPhotosByAlbumId(Integer albumId);
}

@ -0,0 +1,160 @@
package com.aurora.service;
import org.springframework.data.domain.Sort;// 导入 Spring Data 的排序类Sort用于指定查询结果的排序方向如升序、降序
import org.springframework.data.geo.Distance;// 导入距离类Distance用于表示地理空间计算中的距离结果通常包含数值和单位
import org.springframework.data.geo.GeoResults;// 导入地理空间查询结果类GeoResults用于封装地理空间查询的返回结果如附近地点列表
import org.springframework.data.geo.Point;// 导入点类Point用于表示地理空间中的坐标点经度、纬度
import org.springframework.data.redis.connection.RedisGeoCommands;// 导入 Redis 地理空间命令相关类RedisGeoCommands提供地理空间操作的支持如添加地点、计算距离
import java.util.List;// 导入 Java 集合框架中的 List 接口,用于存储有序的元素序列,常用于方法参数或返回列表结果
import java.util.Map;// 导入 Map 接口,用于存储键值对映射关系,常用于哈希结构操作
import java.util.Set;// 导入 Set 接口,用于存储不重复的元素集合,常用于集合结构操作
// 抑制编译器所有类型的警告信息,避免编译时产生警告提示,使代码更简洁
@SuppressWarnings("all")
// 定义 Redis 服务接口RedisService声明一系列 Redis 数据操作的方法
// 该接口封装了 Redis 的常用功能,包括字符串、哈希、列表、集合、有序集合、位操作、地理空间等数据类型的操作
public interface RedisService {
/**
* , Redis
* @param key
* @param value
* @param time Redis
*/
void set(String key, Object value, long time);
void set(String key, Object value);//保存键值对(除非手动删除,否则永不过期)
Object get(String key);//根据键名获取值(Object为值对象该方法从Redis中查询指定键对应的值如果键不存在则返回 null
Boolean del(String key);//删除指定键的数据,该方法从Redis中移除指定键及其关联值删除成功返回 true否则返回 false
Long del(List<String> keys);//批量删除多个键的数据,该方法一次性删除多个键值对,返回实际删除的键数量
Boolean expire(String key, long time);//设置键的过期时间,该方法为已存在的键设置生存时间,超过指定时间后键自动失效
Long getExpire(String key);//获取键的剩余生存时间(单位:秒),该方法查询键的剩余存活时间,返回秒数;如果键不存在或永不过期,返回特定值
Boolean hasKey(String key);//判断键是否存在,该方法检查Redis是否存在指定的键常用于验证数据是否已缓存
//返回自增后的新值
Long incr(String key, long delta);//对键的值进行自增操作(整数),该方法将键对应的整数值增加指定步长,常用于计数器、限流等场景
//返回自增后的新值
Long incrExpire(String key, long time);//自增并设置过期时间,该方法在自增操作的同时重置键的过期时间,确保计数器在指定时间内有效
//返回自减后的新值
Long decr(String key, long delta);//对键的值进行自减操作(整数),该方法将键对应的整数值减少指定步长,常用于计数器、限流等场景
/**
* Redis
* @param key @param hashKey @return null
*/
Object hGet(String key, String hashKey);
//向哈希结构中设置字段值并设置过期时间,该方法在哈希中设置字段值,并为整个哈希键设置统一的过期时间
Boolean hSet(String key, String hashKey, Object value, long time);
//向哈希结构中设置字段值(永不过期),该方法仅设哈希字段值,不修改键的过期时间
void hSet(String key, String hashKey, Object value);
/**
* ,-
* @param key @return -Map
*/
Map<String, Object> hGetAll(String key);
//批量设置哈希结构的字段值并设置过期时间,该方法使用 Map 批量设置哈希字段,同时设置整个键的过期时间
Boolean hSetAll(String key, Map<String, Object> map, long time);
//批量设置哈希结构的字段值(永不过期),该方法批量设置哈希字段,不修改键的过期时间
void hSetAll(String key, Map<String, ?> map);
/**
* ,
* @param key @param hashKey
*/
void hDel(String key, Object... hashKey);
/**
* ,
* @param key @param hashKey @return truefalse
*/
Boolean hHasKey(String key, String hashKey);
//返回自增后的新值
Long hIncr(String key, String hashKey, Long delta);//对哈希结构中指定字段的值进行自增操作(整数),该方法增加哈希中某个字段的整数值,适用于对象属性的计数场景
//返回自减后的新值
Long hDecr(String key, String hashKey, Long delta);//对哈希结构中指定字段的值进行自减操作(整数),该方法增加哈希中某个字段的整数值,适用于对象属性的计数场景
//返回增加后的新分数
Double zIncr(String key, Object value, Double score);//增加有序集合中成员的分数,该方法为有序集合中指定成员增加分数,适用于排行榜、优先级队列等场景
//返回减少后的新分数
Double zDecr(String key, Object value, Double score);//减少有序集合中成员的分数,该方法为有序集合中指定成员减少分数,适用于排行榜、优先级队列等场景
//获取有序集合中指定排名范围的成员及分数(按分数从高到低),该方法返回有序集合中从高到低排序的成员及其分数,适用于 TopN 查询
Map<Object, Double> zReverseRangeWithScore(String key, long start, long end);
//返回分数值,如果成员不存在返回 null
Double zScore(String key, Object value);//获取有序集合中指定成员的分数,该方法查询特定成员在有序集合中的分数值
Map<Object, Double> zAllScore(String key);//获取有序集合中所有成员的分数,该方法返回有序集合中全部成员的分数映射,适用于数据导出或分析
Set<Object> sMembers(String key);//获取集合中的所有成员,该方法返回集合键对应的全部成员集合,适用于获取不重复的元素列表
//返回实际被添加的新成员数量(重复成员不计数)
Long sAdd(String key, Object... values);//向集合中添加一个或多个成员,该方法向集合中插入新成员,重复成员会被自动忽略
//返回实际被添加的新成员数量
Long sAddExpire(String key, long time, Object... values);//向集合中添加成员并设置过期时间,该方法添加成员的同时为整个集合键设置过期时间
Boolean sIsMember(String key, Object value);//判断指定成员是否在集合中,该方法检查成员是否属于集合,用于验证元素存在性
//返回成员数量 @param key 集合键名
Long sSize(String key);//获取集合的成员数量,该方法返回集合中元素的总数,即集合的基数
//返回实际被移除的成员数量
Long sRemove(String key, Object... values);//从集合中移除一个或多个成员,该方法从集合中删除指定成员,不存在则忽略
//返回元素列表
List<Object> lRange(String key, long start, long end);//获取列表中指定范围的元素,该方法返回列表键中从start到end索引的元素子集包含两端
Long lSize(String key);//获取列表的长度,该方法返回列表中元素的总数 @param key 列表键名
//@return 索引处的元素值
Object lIndex(String key, long index);//根据索引获取列表中的元素,该方法返回列表中指定索引位置的元素,支持负数索引(-1 表示最后一个)
//@return 插入后列表的长度
Long lPush(String key, Object value);//向列表尾部插入一个元素,该方法在列表的右端(尾部)添加新元素,适用于队列或日志追加场景
//@return 插入后列表的长度
Long lPush(String key, Object value, long time);//向列表尾部插入一个元素并设置过期时间,该方法插入元素的同时重置列表键的过期时间
//@return 插入后列表的长度
Long lPushAll(String key, Object... values);//向列表尾部批量插入多个元素,该方法一次性在列表尾部插入多个元素,元素顺序与参数顺序一致
//@return 插入后列表的长度
Long lPushAll(String key, Long time, Object... values);//向列表尾部批量插入多个元素并设置过期时间,该方法批量插入元素的同时设置列表键的过期时间
//返回实际移除数量 @param count 移除数量正数从头部开始搜索负数从尾部开始搜索0全部移除
Long lRemove(String key, long count, Object value);//从列表中移除指定数量的匹配元素,该方法删除列表中前count个与value相等的元素
/**
* ,Bitmap 1 0
* @param key @param offset @param b true1false0
* @return
*/
Boolean bitAdd(String key, int offset, boolean b);
Boolean bitGet(String key, int offset);//获取位图中指定偏移量的比特值,该方法查询位图中特定偏移量的比特状态
Long bitCount(String key);//统计位图中被设置为 1 的比特数量,该方法计算位图中所有值为 1 的比特总数,用于人口统计等
@param key
//@param limit:要提取的比特数量 @param offset:起始偏移量 @param key:位图键名 @return 比特字段整数值列表
List<Long> bitField(String key, int limit, int offset);//获取位图中指定范围的比特字段值,该方法提取位图中从指定偏移量开始的多个连续比特,返回其整数值列表
//@return 位图的字节数组表示
byte[] bitGetAll(String key);//获取整个位图的原始字节数据,该方法返回位图的完整字节数组,适用于底层数据处理或自定义解析
Long hyperAdd(String key, Object... value);//向HyperLogLog中添加元素,该方法用于基数统计向HyperLogLog结构中添加元素用于估算不重复元素数量
Long hyperGet(String... key);//合并多个HyperLogLog并返回估算的基数,该方法将多个HyperLogLog结构合并并返回估算的唯一元素总数
void hyperDel(String key);//删除HyperLogLog结构,该方法移除指定的HyperLogLog键
/**
* ,
* @param key: @param x: @param y: @param name:
* @return 10
*/
Long geoAdd(String key, Double x, Double y, String name);
// @return 坐标点列表,每个元素是对应地点的 Point 对象
List<Point> geoGetPointList(String key, Object... place);//获取指定地点的经纬度坐标列表,该方法查询一个或多个地点的坐标信息返回Point对象列表
/**
* , Distance
* @param key: @param placeOne: @param placeTow:
* @return Distance
*/
Distance geoCalculationDistance(String key, String placeOne, String placeTow);
/**
* ,
* @param key: @param place: @param distance: @param limit:
* @param sort:ASCDESC
* @return GeoResults
*/
GeoResults<RedisGeoCommands.GeoLocation<Object>> geoNearByPlace(String key, String place, Distance distance, long limit, Sort.Direction sort);
List<String> geoGetHash(String key, String... place);//获取地点的Geohash字符串表示,该方法返回一个或多个地点的 Geohash 值Geohash是一种用于表示地理位置的编码字符串
}

@ -0,0 +1,27 @@
package com.aurora.service;
import com.aurora.model.dto.LabelOptionDTO;
import com.aurora.model.dto.ResourceDTO;
import com.aurora.entity.Resource;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.vo.ResourceVO;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
//主要用于处理资源的增删改查、Swagger 文档导入、条件查询和选项列表生成等核心业务逻辑
public interface ResourceService extends IService<Resource> {
/**
* Swagger API ), Swagger JSON YAML
*
*/
void importSwagger();
void saveOrUpdateResource(ResourceVO resourceVO);
void deleteResource(Integer resourceId);
List<ResourceDTO> listResources(ConditionVO conditionVO);
List<LabelOptionDTO> listResourceOption();
}

@ -0,0 +1,8 @@
package com.aurora.service;
import com.aurora.entity.RoleMenu;
import com.baomidou.mybatisplus.extension.service.IService;
public interface RoleMenuService extends IService<RoleMenu> {
}

@ -0,0 +1,8 @@
package com.aurora.service;
import com.aurora.entity.RoleResource;
import com.baomidou.mybatisplus.extension.service.IService;
public interface RoleResourceService extends IService<RoleResource> {
}

@ -0,0 +1,23 @@
package com.aurora.service;
import com.aurora.model.dto.RoleDTO;// 导入角色数据传输对象RoleDTO用于前台或管理界面展示角色信息通常包含角色基本属性及扩展字段如用户数量、权限列表等
import com.aurora.model.dto.UserRoleDTO;// 导入用户角色数据传输对象UserRoleDTO用于关联用户和角色信息通常在用户管理或权限分配场景中使用
import com.aurora.entity.Role;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.model.vo.RoleVO;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface RoleService extends IService<Role> {
List<UserRoleDTO> listUserRoles();//获取用户角色关联列表,返回结果包含用户与角色的关联信息,适用于前端下拉选择或权限分配界面
PageResultDTO<RoleDTO> listRoles(ConditionVO conditionVO);
void saveOrUpdateRole(RoleVO roleVO);
void deleteRoles(List<Integer> ids);//一次性删除多个角色记录,通常需要检查角色是否被用户关联,避免误删正在使用的角色
}

@ -0,0 +1,27 @@
package com.aurora.service;
import com.aurora.model.dto.TagAdminDTO;
import com.aurora.model.dto.TagDTO;
import com.aurora.entity.Tag;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.model.vo.TagVO;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface TagService extends IService<Tag> {
List<TagDTO> listTags();
List<TagDTO> listTopTenTags();
PageResultDTO<TagAdminDTO> listTagsAdmin(ConditionVO conditionVO);
List<TagAdminDTO> listTagsAdminBySearch(ConditionVO conditionVO);
void saveOrUpdateTag(TagVO tagVO);
void deleteTag(List<Integer> tagIds);
}

@ -0,0 +1,28 @@
package com.aurora.service;
import com.aurora.model.dto.TalkAdminDTO;
import com.aurora.model.dto.TalkDTO;
import com.aurora.entity.Talk;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.model.vo.TalkVO;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface TalkService extends IService<Talk> {
PageResultDTO<TalkDTO> listTalks();
TalkDTO getTalkById(Integer talkId);
void saveOrUpdateTalk(TalkVO talkVO);
void deleteTalks(List<Integer> talkIdList);
PageResultDTO<TalkAdminDTO> listBackTalks(ConditionVO conditionVO);
//根据说说ID获取后台管理所需的谈话详情返回的TalkAdminDTO通常比TalkDTO包含更多管理字段如审核状态、操作日志等
TalkAdminDTO getBackTalkById(Integer talkId);
}

@ -0,0 +1,46 @@
package com.aurora.service;
import com.aurora.model.dto.UserDetailsDTO;// 导入用户详情数据传输对象UserDetailsDTO用于封装认证用户的详细信息如用户名、权限列表、登录时间等
import io.jsonwebtoken.Claims;// 导入 JWT 声明接口Claims代表 JWT Token 的负载部分,包含 token 中存储的键值对信息(如用户标识、签发时间等)
import javax.servlet.http.HttpServletRequest;// 导入 HTTP Servlet 请求对象HttpServletRequest用于获取客户端请求中的头信息、参数等以提取 token
//Token 服务通常用于身份验证和授权
//主要功能包括:为登录用户创建令牌、验证令牌有效性、解析令牌获取用户信息、管理令牌生命周期等
public interface TokenService {
/**
* JWT ,Payload
* @param userDetailsDTO ID
* @return String JWT HeaderPayloadSignature
*/
String createToken(UserDetailsDTO userDetailsDTO);
/**
* Subject JWT ,
* @param subject IDID
* @return String JWT
*/
String createToken(String subject);
//刷新用户令牌(通常用于延长令牌有效期),该方法检查现有令牌的有效期,若即将过期则生成新令牌并更新其过期时间,用户无需重新登录
void refreshToken(UserDetailsDTO userDetailsDTO);
//续签令牌(可能涉及重新生成令牌或更新令牌关键信息),与刷新不同,续签可能因安全策略(如密钥轮换)或用户信息变更(如权限更新)而重新生成令牌
void renewToken(UserDetailsDTO userDetailsDTO);
/**
* JWT Claims,
* @param token JWT
* @return Claims get() claims.get("username", String.class)
*/
Claims parseToken(String token);
/**
* HTTP
* Authorization: Bearer <token>
* @param request HTTP Servlet Authorization access_token
* @return UserDetailsDTO
*/
UserDetailsDTO getUserDetailDTO(HttpServletRequest request);
/**
*
* 线Redis使
* @param userId ID
*/
void delLoginUser(Integer userId);
}

@ -0,0 +1,13 @@
package com.aurora.service;
import com.aurora.model.dto.UniqueViewDTO;
import com.aurora.entity.UniqueView;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface UniqueViewService extends IService<UniqueView> {
List<UniqueViewDTO> listUniqueViews();
}

@ -0,0 +1,26 @@
package com.aurora.service;
import com.aurora.model.dto.*;
import com.aurora.model.vo.*;
import java.util.List;
//该接口处理用户注册、登录、密码管理、第三方认证和用户信息查询等功能
public interface UserAuthService {
void sendCode(String username);//向指定用户发送验证码(用于注册、登录或安全操作验证),该方法发送一次性验证码,并设置有效期和防刷机制
List<UserAreaDTO> listUserAreas(ConditionVO conditionVO);
void register(UserVO userVO);// 导入用户值对象UserVO用于接收前端传递的用户注册或信息修改参数通常进行参数校验
void updatePassword(UserVO userVO);//返回结果通常按地域分组统计用户数量,支持按时间范围、用户状态等条件筛选
void updateAdminPassword(PasswordVO passwordVO);// 导入密码值对象PasswordVO专门用于接收密码修改参数可能包含旧密码、新密码等敏感信息
PageResultDTO<UserAdminDTO> listUsers(ConditionVO condition);
UserLogoutStatusDTO logout();//用户退出登录(清除认证状态和令牌信息),该方法使当前用户的登录状态失效通常包括清除JWT令牌、会话信息和缓存数据
//@return UserInfoDTO 用户信息数据传输对象包含QQ登录成功后获取的用户基本信息
UserInfoDTO qqLogin(QQLoginVO qqLoginVO);// 导入QQ登录值对象QQLoginVO用于接收QQ第三方登录所需的参数如授权码、OpenID等
}

@ -0,0 +1,35 @@
package com.aurora.service;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.model.dto.UserInfoDTO;
import com.aurora.model.dto.UserOnlineDTO;// 导入在线用户数据传输对象UserOnlineDTO用于管理在线用户信息通常包含会话信息等
import com.aurora.entity.UserInfo;
import com.aurora.model.vo.*;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.multipart.MultipartFile;// 导入 Spring 的文件上传类,用于处理用户头像等文件上传
//主要用于处理用户信息的更新、头像管理、邮箱订阅、角色权限和在线用户管理等核心业务
public interface UserInfoService extends IService<UserInfo> {
void updateUserInfo(UserInfoVO userInfoVO);
/**
* ,
* @param file MultipartFile
* @return String 访URL,访便
*/
String updateUserAvatar(MultipartFile file);
void saveUserEmail(EmailVO emailVO);
void updateUserSubscribe(SubscribeVO subscribeVO);// 导入订阅值对象SubscribeVO用于处理用户订阅相关的参数
void updateUserRole(UserRoleVO userRoleVO);//该方法通常需要权限校验,确保只有管理员可以操作用户角色
void updateUserDisable(UserDisableVO userDisableVO);//导入用户禁用值对象UserDisableVO用于处理用户账户禁用状态参数
PageResultDTO<UserOnlineDTO> listOnlineUsers(ConditionVO conditionVO);
void removeOnlineUser(Integer userInfoId);//强制下线指定用户根据用户ID移除在线会话,该方法使指定用户的登录会话立即失效,用户需要重新登录
UserInfoDTO getUserInfoById(Integer id);
}

@ -0,0 +1,8 @@
package com.aurora.service;
import com.aurora.entity.UserRole;
import com.baomidou.mybatisplus.extension.service.IService;
public interface UserRoleService extends IService<UserRole> {
}

@ -0,0 +1,376 @@
package com.aurora.service.impl;
import com.alibaba.fastjson.JSON;
import com.aurora.model.dto.*;
import com.aurora.entity.Article;
import com.aurora.entity.ArticleTag;
import com.aurora.entity.Category;
import com.aurora.entity.Tag;
import com.aurora.enums.FileExtEnum;
import com.aurora.enums.FilePathEnum;
import com.aurora.exception.BizException;
import com.aurora.mapper.ArticleMapper;
import com.aurora.mapper.ArticleTagMapper;
import com.aurora.mapper.CategoryMapper;
import com.aurora.mapper.TagMapper;
import com.aurora.service.ArticleService;
import com.aurora.service.ArticleTagService;
import com.aurora.service.RedisService;
import com.aurora.service.TagService;
import com.aurora.strategy.context.SearchStrategyContext;
import com.aurora.strategy.context.UploadStrategyContext;
import com.aurora.util.BeanCopyUtil;
import com.aurora.util.PageUtil;
import com.aurora.util.UserUtil;
import com.aurora.model.vo.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.SneakyThrows;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.ByteArrayInputStream;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import static com.aurora.constant.RabbitMQConstant.SUBSCRIBE_EXCHANGE;
import static com.aurora.constant.RedisConstant.*;
import static com.aurora.enums.ArticleStatusEnum.*;
import static com.aurora.enums.StatusCodeEnum.ARTICLE_ACCESS_FAIL;
@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {
@Autowired
private ArticleMapper articleMapper;
@Autowired
private ArticleTagMapper articleTagMapper;
@Autowired
private CategoryMapper categoryMapper;
@Autowired
private TagMapper tagMapper;
@Autowired
private TagService tagService;
@Autowired
private ArticleTagService articleTagService;
@Autowired
private RedisService redisService;
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private UploadStrategyContext uploadStrategyContext;
@Autowired
private SearchStrategyContext searchStrategyContext;
@SneakyThrows
@Override
public TopAndFeaturedArticlesDTO listTopAndFeaturedArticles() {
List<ArticleCardDTO> articleCardDTOs = articleMapper.listTopAndFeaturedArticles();
if (articleCardDTOs.size() == 0) {
return new TopAndFeaturedArticlesDTO();
} else if (articleCardDTOs.size() > 3) {
articleCardDTOs = articleCardDTOs.subList(0, 3);
}
TopAndFeaturedArticlesDTO topAndFeaturedArticlesDTO = new TopAndFeaturedArticlesDTO();
topAndFeaturedArticlesDTO.setTopArticle(articleCardDTOs.get(0));
articleCardDTOs.remove(0);
topAndFeaturedArticlesDTO.setFeaturedArticles(articleCardDTOs);
return topAndFeaturedArticlesDTO;
}
@SneakyThrows
@Override
public PageResultDTO<ArticleCardDTO> listArticles() {
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<Article>()
.eq(Article::getIsDelete, 0)
.in(Article::getStatus, 1, 2);
CompletableFuture<Integer> asyncCount = CompletableFuture.supplyAsync(() -> articleMapper.selectCount(queryWrapper));
List<ArticleCardDTO> articles = articleMapper.listArticles(PageUtil.getLimitCurrent(), PageUtil.getSize());
return new PageResultDTO<>(articles, asyncCount.get());
}
@SneakyThrows
@Override
public PageResultDTO<ArticleCardDTO> listArticlesByCategoryId(Integer categoryId) {
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<Article>().eq(Article::getCategoryId, categoryId);
CompletableFuture<Integer> asyncCount = CompletableFuture.supplyAsync(() -> articleMapper.selectCount(queryWrapper));
List<ArticleCardDTO> articles = articleMapper.getArticlesByCategoryId(PageUtil.getLimitCurrent(), PageUtil.getSize(), categoryId);
return new PageResultDTO<>(articles, asyncCount.get());
}
@SneakyThrows
@Override
public ArticleDTO getArticleById(Integer articleId) {
Article articleForCheck = articleMapper.selectOne(new LambdaQueryWrapper<Article>().eq(Article::getId, articleId));
if (Objects.isNull(articleForCheck)) {
return null;
}
if (articleForCheck.getStatus().equals(2)) {
Boolean isAccess;
try {
isAccess = redisService.sIsMember(ARTICLE_ACCESS + UserUtil.getUserDetailsDTO().getId(), articleId);
} catch (Exception exception) {
throw new BizException(ARTICLE_ACCESS_FAIL);
}
if (isAccess.equals(false)) {
throw new BizException(ARTICLE_ACCESS_FAIL);
}
}
updateArticleViewsCount(articleId);
CompletableFuture<ArticleDTO> asyncArticle = CompletableFuture.supplyAsync(() -> articleMapper.getArticleById(articleId));
CompletableFuture<ArticleCardDTO> asyncPreArticle = CompletableFuture.supplyAsync(() -> {
ArticleCardDTO preArticle = articleMapper.getPreArticleById(articleId);
if (Objects.isNull(preArticle)) {
preArticle = articleMapper.getLastArticle();
}
return preArticle;
});
CompletableFuture<ArticleCardDTO> asyncNextArticle = CompletableFuture.supplyAsync(() -> {
ArticleCardDTO nextArticle = articleMapper.getNextArticleById(articleId);
if (Objects.isNull(nextArticle)) {
nextArticle = articleMapper.getFirstArticle();
}
return nextArticle;
});
ArticleDTO article = asyncArticle.get();
if (Objects.isNull(article)) {
return null;
}
Double score = redisService.zScore(ARTICLE_VIEWS_COUNT, articleId);
if (Objects.nonNull(score)) {
article.setViewCount(score.intValue());
}
article.setPreArticleCard(asyncPreArticle.get());
article.setNextArticleCard(asyncNextArticle.get());
return article;
}
@Override
public void accessArticle(ArticlePasswordVO articlePasswordVO) {
Article article = articleMapper.selectOne(new LambdaQueryWrapper<Article>().eq(Article::getId, articlePasswordVO.getArticleId()));
if (Objects.isNull(article)) {
throw new BizException("文章不存在");
}
if (article.getPassword().equals(articlePasswordVO.getArticlePassword())) {
redisService.sAdd(ARTICLE_ACCESS + UserUtil.getUserDetailsDTO().getId(), articlePasswordVO.getArticleId());
} else {
throw new BizException("密码错误");
}
}
@SneakyThrows
@Override
public PageResultDTO<ArticleCardDTO> listArticlesByTagId(Integer tagId) {
LambdaQueryWrapper<ArticleTag> queryWrapper = new LambdaQueryWrapper<ArticleTag>().eq(ArticleTag::getTagId, tagId);
CompletableFuture<Integer> asyncCount = CompletableFuture.supplyAsync(() -> articleTagMapper.selectCount(queryWrapper));
List<ArticleCardDTO> articles = articleMapper.listArticlesByTagId(PageUtil.getLimitCurrent(), PageUtil.getSize(), tagId);
return new PageResultDTO<>(articles, asyncCount.get());
}
@SneakyThrows
@Override
public PageResultDTO<ArchiveDTO> listArchives() {
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<Article>().eq(Article::getIsDelete, 0).eq(Article::getStatus, 1);
CompletableFuture<Integer> asyncCount = CompletableFuture.supplyAsync(() -> articleMapper.selectCount(queryWrapper));
List<ArticleCardDTO> articles = articleMapper.listArchives(PageUtil.getLimitCurrent(), PageUtil.getSize());
HashMap<String, List<ArticleCardDTO>> map = new HashMap<>();
for (ArticleCardDTO article : articles) {
LocalDateTime createTime = article.getCreateTime();
int month = createTime.getMonth().getValue();
int year = createTime.getYear();
String key = year + "-" + month;
if (Objects.isNull(map.get(key))) {
List<ArticleCardDTO> articleCardDTOS = new ArrayList<>();
articleCardDTOS.add(article);
map.put(key, articleCardDTOS);
} else {
map.get(key).add(article);
}
}
List<ArchiveDTO> archiveDTOs = new ArrayList<>();
map.forEach((key, value) -> archiveDTOs.add(ArchiveDTO.builder().Time(key).articles(value).build()));
archiveDTOs.sort((o1, o2) -> {
String[] o1s = o1.getTime().split("-");
String[] o2s = o2.getTime().split("-");
int o1Year = Integer.parseInt(o1s[0]);
int o1Month = Integer.parseInt(o1s[1]);
int o2Year = Integer.parseInt(o2s[0]);
int o2Month = Integer.parseInt(o2s[1]);
if (o1Year > o2Year) {
return -1;
} else if (o1Year < o2Year) {
return 1;
} else return Integer.compare(o2Month, o1Month);
});
return new PageResultDTO<>(archiveDTOs, asyncCount.get());
}
@SneakyThrows
@Override
public PageResultDTO<ArticleAdminDTO> listArticlesAdmin(ConditionVO conditionVO) {
CompletableFuture<Integer> asyncCount = CompletableFuture.supplyAsync(() -> articleMapper.countArticleAdmins(conditionVO));
List<ArticleAdminDTO> articleAdminDTOs = articleMapper.listArticlesAdmin(PageUtil.getLimitCurrent(), PageUtil.getSize(), conditionVO);
Map<Object, Double> viewsCountMap = redisService.zAllScore(ARTICLE_VIEWS_COUNT);
articleAdminDTOs.forEach(item -> {
Double viewsCount = viewsCountMap.get(item.getId());
if (Objects.nonNull(viewsCount)) {
item.setViewsCount(viewsCount.intValue());
}
});
return new PageResultDTO<>(articleAdminDTOs, asyncCount.get());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void saveOrUpdateArticle(ArticleVO articleVO) {
Category category = saveArticleCategory(articleVO);
Article article = BeanCopyUtil.copyObject(articleVO, Article.class);
if (Objects.nonNull(category)) {
article.setCategoryId(category.getId());
}
article.setUserId(UserUtil.getUserDetailsDTO().getUserInfoId());
this.saveOrUpdate(article);
saveArticleTag(articleVO, article.getId());
if (article.getStatus().equals(1)) {
rabbitTemplate.convertAndSend(SUBSCRIBE_EXCHANGE, "*", new Message(JSON.toJSONBytes(article.getId()), new MessageProperties()));
}
}
@Override
public void updateArticleTopAndFeatured(ArticleTopFeaturedVO articleTopFeaturedVO) {
Article article = Article.builder()
.id(articleTopFeaturedVO.getId())
.isTop(articleTopFeaturedVO.getIsTop())
.isFeatured(articleTopFeaturedVO.getIsFeatured())
.build();
articleMapper.updateById(article);
}
@Override
public void updateArticleDelete(DeleteVO deleteVO) {
List<Article> articles = deleteVO.getIds().stream()
.map(id -> Article.builder()
.id(id)
.isDelete(deleteVO.getIsDelete())
.build())
.collect(Collectors.toList());
this.updateBatchById(articles);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteArticles(List<Integer> articleIds) {
articleTagMapper.delete(new LambdaQueryWrapper<ArticleTag>()
.in(ArticleTag::getArticleId, articleIds));
articleMapper.deleteBatchIds(articleIds);
}
@Override
@Transactional(rollbackFor = Exception.class)
public ArticleAdminViewDTO getArticleByIdAdmin(Integer articleId) {
Article article = articleMapper.selectById(articleId);
Category category = categoryMapper.selectById(article.getCategoryId());
String categoryName = null;
if (Objects.nonNull(category)) {
categoryName = category.getCategoryName();
}
List<String> tagNames = tagMapper.listTagNamesByArticleId(articleId);
ArticleAdminViewDTO articleAdminViewDTO = BeanCopyUtil.copyObject(article, ArticleAdminViewDTO.class);
articleAdminViewDTO.setCategoryName(categoryName);
articleAdminViewDTO.setTagNames(tagNames);
return articleAdminViewDTO;
}
@Override
public List<String> exportArticles(List<Integer> articleIds) {
List<Article> articles = articleMapper.selectList(new LambdaQueryWrapper<Article>()
.select(Article::getArticleTitle, Article::getArticleContent)
.in(Article::getId, articleIds));
List<String> urls = new ArrayList<>();
for (Article article : articles) {
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(article.getArticleContent().getBytes())) {
String url = uploadStrategyContext.executeUploadStrategy(article.getArticleTitle() + FileExtEnum.MD.getExtName(), inputStream, FilePathEnum.MD.getPath());
urls.add(url);
} catch (Exception e) {
e.printStackTrace();
throw new BizException("导出文章失败");
}
}
return urls;
}
@Override
public List<ArticleSearchDTO> listArticlesBySearch(ConditionVO condition) {
return searchStrategyContext.executeSearchStrategy(condition.getKeywords());
}
public void updateArticleViewsCount(Integer articleId) {
redisService.zIncr(ARTICLE_VIEWS_COUNT, articleId, 1D);
}
private Category saveArticleCategory(ArticleVO articleVO) {
Category category = categoryMapper.selectOne(new LambdaQueryWrapper<Category>()
.eq(Category::getCategoryName, articleVO.getCategoryName()));
if (Objects.isNull(category) && !articleVO.getStatus().equals(DRAFT.getStatus())) {
category = Category.builder()
.categoryName(articleVO.getCategoryName())
.build();
categoryMapper.insert(category);
}
return category;
}
@Transactional(rollbackFor = Exception.class)
public void saveArticleTag(ArticleVO articleVO, Integer articleId) {
if (Objects.nonNull(articleVO.getId())) {
articleTagMapper.delete(new LambdaQueryWrapper<ArticleTag>()
.eq(ArticleTag::getArticleId, articleVO.getId()));
}
List<String> tagNames = articleVO.getTagNames();
if (CollectionUtils.isNotEmpty(tagNames)) {
List<Tag> existTags = tagService.list(new LambdaQueryWrapper<Tag>()
.in(Tag::getTagName, tagNames));
List<String> existTagNames = existTags.stream()
.map(Tag::getTagName)
.collect(Collectors.toList());
List<Integer> existTagIds = existTags.stream()
.map(Tag::getId)
.collect(Collectors.toList());
tagNames.removeAll(existTagNames);
if (CollectionUtils.isNotEmpty(tagNames)) {
List<Tag> tags = tagNames.stream().map(item -> Tag.builder()
.tagName(item)
.build())
.collect(Collectors.toList());
tagService.saveBatch(tags);
List<Integer> tagIds = tags.stream()
.map(Tag::getId)
.collect(Collectors.toList());
existTagIds.addAll(tagIds);
}
List<ArticleTag> articleTags = existTagIds.stream().map(item -> ArticleTag.builder()
.articleId(articleId)
.tagId(item)
.build())
.collect(Collectors.toList());
articleTagService.saveBatch(articleTags);
}
}
}

@ -0,0 +1,12 @@
package com.aurora.service.impl;
import com.aurora.entity.ArticleTag;
import com.aurora.mapper.ArticleTagMapper;
import com.aurora.service.ArticleTagService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class ArticleTagServiceImpl extends ServiceImpl<ArticleTagMapper, ArticleTag> implements ArticleTagService {
}

@ -0,0 +1,207 @@
package com.aurora.service.impl;
import com.alibaba.fastjson.JSON;
import com.aurora.model.dto.*;
import com.aurora.entity.*;
import com.aurora.mapper.*;
import com.aurora.service.AuroraInfoService;
import com.aurora.service.RedisService;
import com.aurora.service.UniqueViewService;
import com.aurora.util.BeanCopyUtil;
import com.aurora.util.IpUtil;
import com.aurora.model.vo.AboutVO;
import com.aurora.model.vo.WebsiteConfigVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.OperatingSystem;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import static com.aurora.constant.CommonConstant.*;
import static com.aurora.constant.RedisConstant.*;
@Service
public class AuroraInfoServiceImpl implements AuroraInfoService {
@Autowired
private WebsiteConfigMapper websiteConfigMapper;
@Autowired
private ArticleMapper articleMapper;
@Autowired
private CategoryMapper categoryMapper;
@Autowired
private TagMapper tagMapper;
@Autowired
private CommentMapper commentMapper;
@Autowired
private TalkMapper talkMapper;
@Autowired
private UserInfoMapper userInfoMapper;
@Autowired
private AboutMapper aboutMapper;
@Autowired
private RedisService redisService;
@Autowired
private UniqueViewService uniqueViewService;
@Autowired
private HttpServletRequest request;
@Override
public void report() {
String ipAddress = IpUtil.getIpAddress(request);
UserAgent userAgent = IpUtil.getUserAgent(request);
Browser browser = userAgent.getBrowser();
OperatingSystem operatingSystem = userAgent.getOperatingSystem();
String uuid = ipAddress + browser.getName() + operatingSystem.getName();
String md5 = DigestUtils.md5DigestAsHex(uuid.getBytes());
if (!redisService.sIsMember(UNIQUE_VISITOR, md5)) {
String ipSource = IpUtil.getIpSource(ipAddress);
if (StringUtils.isNotBlank(ipSource)) {
String ipProvince = IpUtil.getIpProvince(ipSource);
redisService.hIncr(VISITOR_AREA, ipProvince, 1L);
} else {
redisService.hIncr(VISITOR_AREA, UNKNOWN, 1L);
}
redisService.incr(BLOG_VIEWS_COUNT, 1);
redisService.sAdd(UNIQUE_VISITOR, md5);
}
}
@SneakyThrows
@Override
public AuroraHomeInfoDTO getAuroraHomeInfo() {
CompletableFuture<Integer> asyncArticleCount = CompletableFuture.supplyAsync(() -> articleMapper.selectCount(new LambdaQueryWrapper<Article>().eq(Article::getIsDelete, FALSE)));
CompletableFuture<Integer> asyncCategoryCount = CompletableFuture.supplyAsync(() -> categoryMapper.selectCount(null));
CompletableFuture<Integer> asyncTagCount = CompletableFuture.supplyAsync(() -> tagMapper.selectCount(null));
CompletableFuture<Integer> asyncTalkCount = CompletableFuture.supplyAsync(() -> talkMapper.selectCount(null));
CompletableFuture<WebsiteConfigDTO> asyncWebsiteConfig = CompletableFuture.supplyAsync(this::getWebsiteConfig);
CompletableFuture<Integer> asyncViewCount = CompletableFuture.supplyAsync(() -> {
Object count = redisService.get(BLOG_VIEWS_COUNT);
return Integer.parseInt(Optional.ofNullable(count).orElse(0).toString());
});
return AuroraHomeInfoDTO.builder()
.articleCount(asyncArticleCount.get())
.categoryCount(asyncCategoryCount.get())
.tagCount(asyncTagCount.get())
.talkCount(asyncTalkCount.get())
.websiteConfigDTO(asyncWebsiteConfig.get())
.viewCount(asyncViewCount.get()).build();
}
@Override
public AuroraAdminInfoDTO getAuroraAdminInfo() {
Object count = redisService.get(BLOG_VIEWS_COUNT);
Integer viewsCount = Integer.parseInt(Optional.ofNullable(count).orElse(0).toString());
Integer messageCount = commentMapper.selectCount(new LambdaQueryWrapper<Comment>().eq(Comment::getType, 2));
Integer userCount = userInfoMapper.selectCount(null);
Integer articleCount = articleMapper.selectCount(new LambdaQueryWrapper<Article>()
.eq(Article::getIsDelete, FALSE));
List<UniqueViewDTO> uniqueViews = uniqueViewService.listUniqueViews();
List<ArticleStatisticsDTO> articleStatisticsDTOs = articleMapper.listArticleStatistics();
List<CategoryDTO> categoryDTOs = categoryMapper.listCategories();
List<TagDTO> tagDTOs = BeanCopyUtil.copyList(tagMapper.selectList(null), TagDTO.class);
Map<Object, Double> articleMap = redisService.zReverseRangeWithScore(ARTICLE_VIEWS_COUNT, 0, 4);
AuroraAdminInfoDTO auroraAdminInfoDTO = AuroraAdminInfoDTO.builder()
.articleStatisticsDTOs(articleStatisticsDTOs)
.tagDTOs(tagDTOs)
.viewsCount(viewsCount)
.messageCount(messageCount)
.userCount(userCount)
.articleCount(articleCount)
.categoryDTOs(categoryDTOs)
.uniqueViewDTOs(uniqueViews)
.build();
if (CollectionUtils.isNotEmpty(articleMap)) {
List<ArticleRankDTO> articleRankDTOList = listArticleRank(articleMap);
auroraAdminInfoDTO.setArticleRankDTOs(articleRankDTOList);
}
return auroraAdminInfoDTO;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateWebsiteConfig(WebsiteConfigVO websiteConfigVO) {
WebsiteConfig websiteConfig = WebsiteConfig.builder()
.id(DEFAULT_CONFIG_ID)
.config(JSON.toJSONString(websiteConfigVO))
.build();
websiteConfigMapper.updateById(websiteConfig);
redisService.del(WEBSITE_CONFIG);
}
@Override
public WebsiteConfigDTO getWebsiteConfig() {
WebsiteConfigDTO websiteConfigDTO;
Object websiteConfig = redisService.get(WEBSITE_CONFIG);
if (Objects.nonNull(websiteConfig)) {
websiteConfigDTO = JSON.parseObject(websiteConfig.toString(), WebsiteConfigDTO.class);
} else {
String config = websiteConfigMapper.selectById(DEFAULT_CONFIG_ID).getConfig();
websiteConfigDTO = JSON.parseObject(config, WebsiteConfigDTO.class);
redisService.set(WEBSITE_CONFIG, config);
}
return websiteConfigDTO;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateAbout(AboutVO aboutVO) {
About about = About.builder()
.id(DEFAULT_ABOUT_ID)
.content(JSON.toJSONString(aboutVO))
.build();
aboutMapper.updateById(about);
redisService.del(ABOUT);
}
@Override
public AboutDTO getAbout() {
AboutDTO aboutDTO;
Object about = redisService.get(ABOUT);
if (Objects.nonNull(about)) {
aboutDTO = JSON.parseObject(about.toString(), AboutDTO.class);
} else {
String content = aboutMapper.selectById(DEFAULT_ABOUT_ID).getContent();
aboutDTO = JSON.parseObject(content, AboutDTO.class);
redisService.set(ABOUT, content);
}
return aboutDTO;
}
private List<ArticleRankDTO> listArticleRank(Map<Object, Double> articleMap) {
List<Integer> articleIds = new ArrayList<>(articleMap.size());
articleMap.forEach((key, value) -> articleIds.add((Integer) key));
return articleMapper.selectList(new LambdaQueryWrapper<Article>()
.select(Article::getId, Article::getArticleTitle)
.in(Article::getId, articleIds))
.stream().map(article -> ArticleRankDTO.builder()
.articleTitle(article.getArticleTitle())
.viewsCount(articleMap.get(article.getId()).intValue())
.build())
.sorted(Comparator.comparingInt(ArticleRankDTO::getViewsCount).reversed())
.collect(Collectors.toList());
}
}

@ -0,0 +1,88 @@
package com.aurora.service.impl;
import com.aurora.model.dto.CategoryAdminDTO;
import com.aurora.model.dto.CategoryDTO;
import com.aurora.model.dto.CategoryOptionDTO;
import com.aurora.entity.Article;
import com.aurora.entity.Category;
import com.aurora.exception.BizException;
import com.aurora.mapper.ArticleMapper;
import com.aurora.mapper.CategoryMapper;
import com.aurora.service.CategoryService;
import com.aurora.util.BeanCopyUtil;
import com.aurora.util.PageUtil;
import com.aurora.model.vo.CategoryVO;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.dto.PageResultDTO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Objects;
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
@Autowired
private CategoryMapper categoryMapper;
@Autowired
private ArticleMapper articleMapper;
@Override
public List<CategoryDTO> listCategories() {
return categoryMapper.listCategories();
}
@SneakyThrows
@Override
public PageResultDTO<CategoryAdminDTO> listCategoriesAdmin(ConditionVO conditionVO) {
Integer count = categoryMapper.selectCount(new LambdaQueryWrapper<Category>()
.like(StringUtils.isNotBlank(conditionVO.getKeywords()), Category::getCategoryName, conditionVO.getKeywords()));
if (count == 0) {
return new PageResultDTO<>();
}
List<CategoryAdminDTO> categoryList = categoryMapper.listCategoriesAdmin(PageUtil.getLimitCurrent(), PageUtil.getSize(), conditionVO);
return new PageResultDTO<>(categoryList, count);
}
@SneakyThrows
@Override
public List<CategoryOptionDTO> listCategoriesBySearch(ConditionVO conditionVO) {
List<Category> categoryList = categoryMapper.selectList(new LambdaQueryWrapper<Category>()
.like(StringUtils.isNotBlank(conditionVO.getKeywords()), Category::getCategoryName, conditionVO.getKeywords())
.orderByDesc(Category::getId));
return BeanCopyUtil.copyList(categoryList, CategoryOptionDTO.class);
}
@Override
public void deleteCategories(List<Integer> categoryIds) {
Integer count = articleMapper.selectCount(new LambdaQueryWrapper<Article>()
.in(Article::getCategoryId, categoryIds));
if (count > 0) {
throw new BizException("删除失败,该分类下存在文章");
}
categoryMapper.deleteBatchIds(categoryIds);
}
@Override
public void saveOrUpdateCategory(CategoryVO categoryVO) {
Category existCategory = categoryMapper.selectOne(new LambdaQueryWrapper<Category>()
.select(Category::getId)
.eq(Category::getCategoryName, categoryVO.getCategoryName()));
if (Objects.nonNull(existCategory) && !existCategory.getId().equals(categoryVO.getId())) {
throw new BizException("分类名已存在");
}
Category category = Category.builder()
.id(categoryVO.getId())
.categoryName(categoryVO.getCategoryName())
.build();
this.saveOrUpdate(category);
}
}

@ -0,0 +1,328 @@
package com.aurora.service.impl;
import com.alibaba.fastjson.JSON;
import com.aurora.model.dto.*;
import com.aurora.entity.Article;
import com.aurora.entity.Comment;
import com.aurora.entity.Talk;
import com.aurora.entity.UserInfo;
import com.aurora.enums.CommentTypeEnum;
import com.aurora.exception.BizException;
import com.aurora.mapper.ArticleMapper;
import com.aurora.mapper.CommentMapper;
import com.aurora.mapper.TalkMapper;
import com.aurora.mapper.UserInfoMapper;
import com.aurora.service.AuroraInfoService;
import com.aurora.service.CommentService;
import com.aurora.util.HTMLUtil;
import com.aurora.util.PageUtil;
import com.aurora.util.UserUtil;
import com.aurora.model.vo.CommentVO;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.model.vo.ReviewVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.SneakyThrows;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import static com.aurora.constant.CommonConstant.*;
import static com.aurora.constant.RabbitMQConstant.EMAIL_EXCHANGE;
import static com.aurora.enums.CommentTypeEnum.*;
@Service
public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment> implements CommentService {
@Value("${website.url}")
private String websiteUrl;
@Autowired
private CommentMapper commentMapper;
@Autowired
private ArticleMapper articleMapper;
@Autowired
private TalkMapper talkMapper;
@Autowired
private UserInfoMapper userInfoMapper;
@Autowired
private AuroraInfoService auroraInfoService;
@Autowired
private RabbitTemplate rabbitTemplate;
private static final List<Integer> types = new ArrayList<>();
@PostConstruct
public void init() {
CommentTypeEnum[] values = CommentTypeEnum.values();
for (CommentTypeEnum value : values) {
types.add(value.getType());
}
}
@Override
public void saveComment(CommentVO commentVO) {
checkCommentVO(commentVO);
WebsiteConfigDTO websiteConfig = auroraInfoService.getWebsiteConfig();
Integer isCommentReview = websiteConfig.getIsCommentReview();
commentVO.setCommentContent(HTMLUtil.filter(commentVO.getCommentContent()));
Comment comment = Comment.builder()
.userId(UserUtil.getUserDetailsDTO().getUserInfoId())
.replyUserId(commentVO.getReplyUserId())
.topicId(commentVO.getTopicId())
.commentContent(commentVO.getCommentContent())
.parentId(commentVO.getParentId())
.type(commentVO.getType())
.isReview(isCommentReview == TRUE ? FALSE : TRUE)
.build();
commentMapper.insert(comment);
String fromNickname = UserUtil.getUserDetailsDTO().getNickname();
if (websiteConfig.getIsEmailNotice().equals(TRUE)) {
CompletableFuture.runAsync(() -> notice(comment, fromNickname));
}
}
@Override
public PageResultDTO<CommentDTO> listComments(CommentVO commentVO) {
Integer commentCount = commentMapper.selectCount(new LambdaQueryWrapper<Comment>()
.eq(Objects.nonNull(commentVO.getTopicId()), Comment::getTopicId, commentVO.getTopicId())
.eq(Comment::getType, commentVO.getType())
.isNull(Comment::getParentId)
.eq(Comment::getIsReview, TRUE));
if (commentCount == 0) {
return new PageResultDTO<>();
}
List<CommentDTO> commentDTOs = commentMapper.listComments(PageUtil.getLimitCurrent(), PageUtil.getSize(), commentVO);
if (CollectionUtils.isEmpty(commentDTOs)) {
return new PageResultDTO<>();
}
List<Integer> commentIds = commentDTOs.stream()
.map(CommentDTO::getId)
.collect(Collectors.toList());
List<ReplyDTO> replyDTOS = commentMapper.listReplies(commentIds);
Map<Integer, List<ReplyDTO>> replyMap = replyDTOS.stream()
.collect(Collectors.groupingBy(ReplyDTO::getParentId));
commentDTOs.forEach(item -> item.setReplyDTOs(replyMap.get(item.getId())));
return new PageResultDTO<>(commentDTOs, commentCount);
}
@Override
public List<ReplyDTO> listRepliesByCommentId(Integer commentId) {
return commentMapper.listReplies(Collections.singletonList(commentId));
}
@Override
public List<CommentDTO> listTopSixComments() {
return commentMapper.listTopSixComments();
}
@SneakyThrows
@Override
public PageResultDTO<CommentAdminDTO> listCommentsAdmin(ConditionVO conditionVO) {
CompletableFuture<Integer> asyncCount = CompletableFuture.supplyAsync(() -> commentMapper.countComments(conditionVO));
List<CommentAdminDTO> commentBackDTOList = commentMapper.listCommentsAdmin(PageUtil.getLimitCurrent(), PageUtil.getSize(), conditionVO);
return new PageResultDTO<>(commentBackDTOList, asyncCount.get());
}
@Override
public void updateCommentsReview(ReviewVO reviewVO) {
List<Comment> comments = reviewVO.getIds().stream().map(item -> Comment.builder()
.id(item)
.isReview(reviewVO.getIsReview())
.build())
.collect(Collectors.toList());
this.updateBatchById(comments);
}
public void checkCommentVO(CommentVO commentVO) {
if (!types.contains(commentVO.getType())) {
throw new BizException("参数校验异常");
}
if (Objects.requireNonNull(getCommentEnum(commentVO.getType())) == ARTICLE || Objects.requireNonNull(getCommentEnum(commentVO.getType())) == TALK) {
if (Objects.isNull(commentVO.getTopicId())) {
throw new BizException("参数校验异常");
} else {
if (Objects.requireNonNull(getCommentEnum(commentVO.getType())) == ARTICLE) {
Article article = articleMapper.selectOne(new LambdaQueryWrapper<Article>().select(Article::getId, Article::getUserId).eq(Article::getId, commentVO.getTopicId()));
if (Objects.isNull(article)) {
throw new BizException("参数校验异常");
}
}
if (Objects.requireNonNull(getCommentEnum(commentVO.getType())) == TALK) {
Talk talk = talkMapper.selectOne(new LambdaQueryWrapper<Talk>().select(Talk::getId, Talk::getUserId).eq(Talk::getId, commentVO.getTopicId()));
if (Objects.isNull(talk)) {
throw new BizException("参数校验异常");
}
}
}
}
if (Objects.requireNonNull(getCommentEnum(commentVO.getType())) == LINK
|| Objects.requireNonNull(getCommentEnum(commentVO.getType())) == ABOUT
|| Objects.requireNonNull(getCommentEnum(commentVO.getType())) == MESSAGE) {
if (Objects.nonNull(commentVO.getTopicId())) {
throw new BizException("参数校验异常");
}
}
if (Objects.isNull(commentVO.getParentId())) {
if (Objects.nonNull(commentVO.getReplyUserId())) {
throw new BizException("参数校验异常");
}
}
if (Objects.nonNull(commentVO.getParentId())) {
Comment parentComment = commentMapper.selectOne(new LambdaQueryWrapper<Comment>().select(Comment::getId, Comment::getParentId, Comment::getType).eq(Comment::getId, commentVO.getParentId()));
if (Objects.isNull(parentComment)) {
throw new BizException("参数校验异常");
}
if (Objects.nonNull(parentComment.getParentId())) {
throw new BizException("参数校验异常");
}
if (!commentVO.getType().equals(parentComment.getType())) {
throw new BizException("参数校验异常");
}
if (Objects.isNull(commentVO.getReplyUserId())) {
throw new BizException("参数校验异常");
} else {
UserInfo existUser = userInfoMapper.selectOne(new LambdaQueryWrapper<UserInfo>().select(UserInfo::getId).eq(UserInfo::getId, commentVO.getReplyUserId()));
if (Objects.isNull(existUser)) {
throw new BizException("参数校验异常");
}
}
}
}
private void notice(Comment comment, String fromNickname) {
if (comment.getUserId().equals(comment.getReplyUserId())) {
if (Objects.nonNull(comment.getParentId())) {
Comment parentComment = commentMapper.selectById(comment.getParentId());
if (parentComment.getUserId().equals(comment.getUserId())) {
return;
}
}
}
if (comment.getUserId().equals(BLOGGER_ID) && Objects.isNull(comment.getParentId())) {
return;
}
if (Objects.nonNull(comment.getParentId())) {
Comment parentComment = commentMapper.selectById(comment.getParentId());
if (!comment.getReplyUserId().equals(parentComment.getUserId())
&& !comment.getReplyUserId().equals(comment.getUserId())) {
UserInfo userInfo = userInfoMapper.selectById(comment.getUserId());
UserInfo replyUserinfo = userInfoMapper.selectById(comment.getReplyUserId());
Map<String, Object> map = new HashMap<>();
String topicId = Objects.nonNull(comment.getTopicId()) ? comment.getTopicId().toString() : "";
String url = websiteUrl + getCommentPath(comment.getType()) + topicId;
map.put("content", userInfo.getNickname() + "在" + Objects.requireNonNull(getCommentEnum(comment.getType())).getDesc()
+ "的评论区@了你,"
+ "<a style=\"text-decoration:none;color:#12addb\" href=\"" + url + "\">点击查看</a>");
EmailDTO emailDTO = EmailDTO.builder()
.email(replyUserinfo.getEmail())
.subject(MENTION_REMIND)
.template("common.html")
.commentMap(map)
.build();
rabbitTemplate.convertAndSend(EMAIL_EXCHANGE, "*", new Message(JSON.toJSONBytes(emailDTO), new MessageProperties()));
}
if (comment.getUserId().equals(parentComment.getUserId())) {
return;
}
}
String title;
Integer userId = BLOGGER_ID;
String topicId = Objects.nonNull(comment.getTopicId()) ? comment.getTopicId().toString() : "";
if (Objects.nonNull(comment.getReplyUserId())) {
userId = comment.getReplyUserId();
} else {
switch (Objects.requireNonNull(getCommentEnum(comment.getType()))) {
case ARTICLE:
userId = articleMapper.selectById(comment.getTopicId()).getUserId();
break;
case TALK:
userId = talkMapper.selectById(comment.getTopicId()).getUserId();
default:
break;
}
}
if (Objects.requireNonNull(getCommentEnum(comment.getType())).equals(ARTICLE)) {
title = articleMapper.selectById(comment.getTopicId()).getArticleTitle();
} else {
title = Objects.requireNonNull(getCommentEnum(comment.getType())).getDesc();
}
UserInfo userInfo = userInfoMapper.selectById(userId);
if (StringUtils.isNotBlank(userInfo.getEmail())) {
EmailDTO emailDTO = getEmailDTO(comment, userInfo, fromNickname, topicId, title);
rabbitTemplate.convertAndSend(EMAIL_EXCHANGE, "*", new Message(JSON.toJSONBytes(emailDTO), new MessageProperties()));
}
}
private EmailDTO getEmailDTO(Comment comment, UserInfo userInfo, String fromNickname, String topicId, String title) {
EmailDTO emailDTO = new EmailDTO();
Map<String, Object> map = new HashMap<>();
if (comment.getIsReview().equals(TRUE)) {
String url = websiteUrl + getCommentPath(comment.getType()) + topicId;
if (Objects.isNull(comment.getParentId())) {
emailDTO.setEmail(userInfo.getEmail());
emailDTO.setSubject(COMMENT_REMIND);
emailDTO.setTemplate("owner.html");
String createTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(comment.getCreateTime());
map.put("time", createTime);
map.put("url", url);
map.put("title", title);
map.put("nickname", fromNickname);
map.put("content", comment.getCommentContent());
} else {
Comment parentComment = commentMapper.selectOne(new LambdaQueryWrapper<Comment>().select(Comment::getUserId, Comment::getCommentContent, Comment::getCreateTime).eq(Comment::getId, comment.getParentId()));
if (!userInfo.getId().equals(parentComment.getUserId())) {
userInfo = userInfoMapper.selectById(parentComment.getUserId());
}
emailDTO.setEmail(userInfo.getEmail());
emailDTO.setSubject(COMMENT_REMIND);
emailDTO.setTemplate("user.html");
map.put("url", url);
map.put("title", title);
String createTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").format(parentComment.getCreateTime());
map.put("time", createTime);
map.put("toUser", userInfo.getNickname());
map.put("fromUser", fromNickname);
map.put("parentComment", parentComment.getCommentContent());
if (!comment.getReplyUserId().equals(parentComment.getUserId())) {
UserInfo mentionUserInfo = userInfoMapper.selectById(comment.getReplyUserId());
if (Objects.nonNull(mentionUserInfo.getWebsite())) {
map.put("replyComment", "<a style=\"text-decoration:none;color:#12addb\" href=\""
+ mentionUserInfo.getWebsite()
+ "\">@" + mentionUserInfo.getNickname() + " " + "</a>" + parentComment.getCommentContent());
} else {
map.put("replyComment", "@" + mentionUserInfo.getNickname() + " " + parentComment.getCommentContent());
}
} else {
map.put("replyComment", comment.getCommentContent());
}
}
} else {
String adminEmail = userInfoMapper.selectById(BLOGGER_ID).getEmail();
emailDTO.setEmail(adminEmail);
emailDTO.setSubject(CHECK_REMIND);
emailDTO.setTemplate("common.html");
map.put("content", "您收到了一条新的回复,请前往后台管理页面审核");
}
emailDTO.setCommentMap(map);
return emailDTO;
}
}

@ -0,0 +1,32 @@
package com.aurora.service.impl;
import com.aurora.model.dto.ExceptionLogDTO;
import com.aurora.entity.ExceptionLog;
import com.aurora.mapper.ExceptionLogMapper;
import com.aurora.service.ExceptionLogService;
import com.aurora.util.BeanCopyUtil;
import com.aurora.util.PageUtil;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.dto.PageResultDTO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ExceptionLogServiceImpl extends ServiceImpl<ExceptionLogMapper, ExceptionLog> implements ExceptionLogService {
@Override
public PageResultDTO<ExceptionLogDTO> listExceptionLogs(ConditionVO conditionVO) {
Page<ExceptionLog> page = new Page<>(PageUtil.getCurrent(), PageUtil.getSize());
Page<ExceptionLog> exceptionLogPage = this.page(page, new LambdaQueryWrapper<ExceptionLog>()
.like(StringUtils.isNotBlank(conditionVO.getKeywords()), ExceptionLog::getOptDesc, conditionVO.getKeywords())
.orderByDesc(ExceptionLog::getId));
List<ExceptionLogDTO> exceptionLogDTOs = BeanCopyUtil.copyList(exceptionLogPage.getRecords(), ExceptionLogDTO.class);
return new PageResultDTO<>(exceptionLogDTOs, (int) exceptionLogPage.getTotal());
}
}

@ -0,0 +1,49 @@
package com.aurora.service.impl;
import com.aurora.model.dto.FriendLinkAdminDTO;
import com.aurora.model.dto.FriendLinkDTO;
import com.aurora.entity.FriendLink;
import com.aurora.mapper.FriendLinkMapper;
import com.aurora.service.FriendLinkService;
import com.aurora.util.BeanCopyUtil;
import com.aurora.util.PageUtil;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.vo.FriendLinkVO;
import com.aurora.model.dto.PageResultDTO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class FriendLinkServiceImpl extends ServiceImpl<FriendLinkMapper, FriendLink> implements FriendLinkService {
@Autowired
private FriendLinkMapper friendLinkMapper;
@Override
public List<FriendLinkDTO> listFriendLinks() {
List<FriendLink> friendLinks = friendLinkMapper.selectList(null);
return BeanCopyUtil.copyList(friendLinks, FriendLinkDTO.class);
}
@Override
public PageResultDTO<FriendLinkAdminDTO> listFriendLinksAdmin(ConditionVO conditionVO) {
Page<FriendLink> page = new Page<>(PageUtil.getCurrent(), PageUtil.getSize());
Page<FriendLink> friendLinkPage = friendLinkMapper.selectPage(page, new LambdaQueryWrapper<FriendLink>()
.like(StringUtils.isNotBlank(conditionVO.getKeywords()), FriendLink::getLinkName, conditionVO.getKeywords()));
List<FriendLinkAdminDTO> friendLinkBackDTOs = BeanCopyUtil.copyList(friendLinkPage.getRecords(), FriendLinkAdminDTO.class);
return new PageResultDTO<>(friendLinkBackDTOs, (int) friendLinkPage.getTotal());
}
@Override
public void saveOrUpdateFriendLink(FriendLinkVO friendLinkVO) {
FriendLink friendLink = BeanCopyUtil.copyObject(friendLinkVO, FriendLink.class);
this.saveOrUpdate(friendLink);
}
}

@ -0,0 +1,63 @@
package com.aurora.service.impl;
import com.aurora.model.dto.JobLogDTO;
import com.aurora.entity.JobLog;
import com.aurora.mapper.JobLogMapper;
import com.aurora.service.JobLogService;
import com.aurora.util.BeanCopyUtil;
import com.aurora.util.PageUtil;
import com.aurora.model.vo.JobLogSearchVO;
import com.aurora.model.dto.PageResultDTO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Objects;
@Service
public class JobLogServiceImpl extends ServiceImpl<JobLogMapper, JobLog> implements JobLogService {
@Autowired
private JobLogMapper jobLogMapper;
@SneakyThrows
@Override
public PageResultDTO<JobLogDTO> listJobLogs(JobLogSearchVO jobLogSearchVO) {
LambdaQueryWrapper<JobLog> queryWrapper = new LambdaQueryWrapper<JobLog>()
.orderByDesc(JobLog::getCreateTime)
.eq(Objects.nonNull(jobLogSearchVO.getJobId()), JobLog::getJobId, jobLogSearchVO.getJobId())
.like(StringUtils.isNotBlank(jobLogSearchVO.getJobName()), JobLog::getJobName, jobLogSearchVO.getJobName())
.like(StringUtils.isNotBlank(jobLogSearchVO.getJobGroup()), JobLog::getJobGroup, jobLogSearchVO.getJobGroup())
.eq(Objects.nonNull(jobLogSearchVO.getStatus()), JobLog::getStatus, jobLogSearchVO.getStatus())
.between(Objects.nonNull(jobLogSearchVO.getStartTime()) && Objects.nonNull(jobLogSearchVO.getEndTime()),
JobLog::getCreateTime,
jobLogSearchVO.getStartTime(),
jobLogSearchVO.getEndTime());
Page<JobLog> page = new Page<>(PageUtil.getCurrent(), PageUtil.getSize());
Page<JobLog> jobLogPage = jobLogMapper.selectPage(page, queryWrapper);
List<JobLogDTO> jobLogDTOs = BeanCopyUtil.copyList(jobLogPage.getRecords(), JobLogDTO.class);
return new PageResultDTO<>(jobLogDTOs, (int)jobLogPage.getTotal());
}
@Override
public void deleteJobLogs(List<Integer> ids) {
LambdaQueryWrapper<JobLog> queryWrapper = new LambdaQueryWrapper<JobLog>().in(JobLog::getId, ids);
jobLogMapper.delete(queryWrapper);
}
@Override
public void cleanJobLogs() {
jobLogMapper.delete(null);
}
@Override
public List<String> listJobLogGroups() {
return jobLogMapper.listJobLogGroups();
}
}

@ -0,0 +1,153 @@
package com.aurora.service.impl;
import com.aurora.enums.JobStatusEnum;
import com.aurora.model.dto.JobDTO;
import com.aurora.entity.Job;
import com.aurora.mapper.JobMapper;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.service.JobService;
import com.aurora.util.BeanCopyUtil;
import com.aurora.util.CronUtil;
import com.aurora.util.PageUtil;
import com.aurora.util.ScheduleUtil;
import com.aurora.model.vo.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.SneakyThrows;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import javax.annotation.PostConstruct;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Service
public class JobServiceImpl extends ServiceImpl<JobMapper, Job> implements JobService {
@Autowired
private Scheduler scheduler;
@Autowired
private JobMapper jobMapper;
@SneakyThrows
@PostConstruct
public void init() {
scheduler.clear();
List<Job> jobs = jobMapper.selectList(null);
for (Job job : jobs) {
ScheduleUtil.createScheduleJob(scheduler, job);
}
}
@SneakyThrows
@Transactional(rollbackFor = Exception.class)
public void saveJob(JobVO jobVO) {
checkCronIsValid(jobVO);
Job job = BeanCopyUtil.copyObject(jobVO, Job.class);
int row = jobMapper.insert(job);
if (row > 0) ScheduleUtil.createScheduleJob(scheduler, job);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateJob(JobVO jobVO) {
checkCronIsValid(jobVO);
Job temp = jobMapper.selectById(jobVO.getId());
Job job = BeanCopyUtil.copyObject(jobVO, Job.class);
int row = jobMapper.updateById(job);
if (row > 0) updateSchedulerJob(job, temp.getJobGroup());
}
@SneakyThrows
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteJobs(List<Integer> tagIds) {
List<Job> jobs = jobMapper.selectList(new LambdaQueryWrapper<Job>().in(Job::getId, tagIds));
int row = jobMapper.delete(new LambdaQueryWrapper<Job>().in(Job::getId, tagIds));
if (row > 0) {
jobs.forEach(item -> {
try {
scheduler.deleteJob(ScheduleUtil.getJobKey(item.getId(), item.getJobGroup()));
} catch (SchedulerException e) {
throw new RuntimeException(e);
}
});
}
}
@Override
public JobDTO getJobById(Integer jobId) {
Job job = jobMapper.selectById(jobId);
JobDTO jobDTO = BeanCopyUtil.copyObject(job, JobDTO.class);
Date nextExecution = CronUtil.getNextExecution(jobDTO.getCronExpression());
jobDTO.setNextValidTime(nextExecution);
return jobDTO;
}
@SneakyThrows
@Override
public PageResultDTO<JobDTO> listJobs(JobSearchVO jobSearchVO) {
CompletableFuture<Integer> asyncCount = CompletableFuture.supplyAsync(() -> jobMapper.countJobs(jobSearchVO));
List<JobDTO> jobDTOs = jobMapper.listJobs(PageUtil.getLimitCurrent(), PageUtil.getSize(), jobSearchVO);
return new PageResultDTO<>(jobDTOs, asyncCount.get());
}
@SneakyThrows
@Override
public void updateJobStatus(JobStatusVO jobStatusVO) {
Job job = jobMapper.selectById(jobStatusVO.getId());
if (job.getStatus().equals(jobStatusVO.getStatus())) {
return;
}
Integer status = jobStatusVO.getStatus();
Integer jobId = job.getId();
String jobGroup = job.getJobGroup();
LambdaUpdateWrapper<Job> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(Job::getId, jobStatusVO.getId()).set(Job::getStatus, status);
int row = jobMapper.update(null, updateWrapper);
if (row > 0) {
if (JobStatusEnum.NORMAL.getValue().equals(status)) {
scheduler.resumeJob(ScheduleUtil.getJobKey(jobId, jobGroup));
} else if (JobStatusEnum.PAUSE.getValue().equals(status)) {
scheduler.pauseJob(ScheduleUtil.getJobKey(jobId, jobGroup));
}
}
}
@SneakyThrows
@Override
public void runJob(JobRunVO jobRunVO) {
Integer jobId = jobRunVO.getId();
String jobGroup = jobRunVO.getJobGroup();
scheduler.triggerJob(ScheduleUtil.getJobKey(jobId, jobGroup));
}
@Override
public List<String> listJobGroups() {
return jobMapper.listJobGroups();
}
private void checkCronIsValid(JobVO jobVO) {
boolean valid = CronUtil.isValid(jobVO.getCronExpression());
Assert.isTrue(valid, "Cron表达式无效!");
}
@SneakyThrows
public void updateSchedulerJob(Job job, String jobGroup) {
Integer jobId = job.getId();
JobKey jobKey = ScheduleUtil.getJobKey(jobId, jobGroup);
if (scheduler.checkExists(jobKey)) {
scheduler.deleteJob(jobKey);
}
ScheduleUtil.createScheduleJob(scheduler, job);
}
}

@ -0,0 +1,175 @@
package com.aurora.service.impl;
import com.aurora.model.dto.LabelOptionDTO;
import com.aurora.model.dto.MenuDTO;
import com.aurora.model.dto.UserMenuDTO;
import com.aurora.entity.Menu;
import com.aurora.entity.RoleMenu;
import com.aurora.exception.BizException;
import com.aurora.mapper.MenuMapper;
import com.aurora.mapper.RoleMenuMapper;
import com.aurora.service.MenuService;
import com.aurora.util.BeanCopyUtil;
import com.aurora.util.UserUtil;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.vo.IsHiddenVO;
import com.aurora.model.vo.MenuVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.stream.Collectors;
import static com.aurora.constant.CommonConstant.COMPONENT;
import static com.aurora.constant.CommonConstant.TRUE;
@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {
@Autowired
private MenuMapper menuMapper;
@Autowired
private RoleMenuMapper roleMenuMapper;
@Override
public List<MenuDTO> listMenus(ConditionVO conditionVO) {
List<Menu> menus = menuMapper.selectList(new LambdaQueryWrapper<Menu>()
.like(StringUtils.isNotBlank(conditionVO.getKeywords()), Menu::getName, conditionVO.getKeywords()));
List<Menu> catalogs = listCatalogs(menus);
Map<Integer, List<Menu>> childrenMap = getMenuMap(menus);
List<MenuDTO> menuDTOs = catalogs.stream().map(item -> {
MenuDTO menuDTO = BeanCopyUtil.copyObject(item, MenuDTO.class);
List<MenuDTO> list = BeanCopyUtil.copyList(childrenMap.get(item.getId()), MenuDTO.class).stream()
.sorted(Comparator.comparing(MenuDTO::getOrderNum))
.collect(Collectors.toList());
menuDTO.setChildren(list);
childrenMap.remove(item.getId());
return menuDTO;
}).sorted(Comparator.comparing(MenuDTO::getOrderNum)).collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(childrenMap)) {
List<Menu> childrenList = new ArrayList<>();
childrenMap.values().forEach(childrenList::addAll);
List<MenuDTO> childrenDTOList = childrenList.stream()
.map(item -> BeanCopyUtil.copyObject(item, MenuDTO.class))
.sorted(Comparator.comparing(MenuDTO::getOrderNum))
.collect(Collectors.toList());
menuDTOs.addAll(childrenDTOList);
}
return menuDTOs;
}
@Transactional(rollbackFor = Exception.class)
@Override
public void saveOrUpdateMenu(MenuVO menuVO) {
Menu menu = BeanCopyUtil.copyObject(menuVO, Menu.class);
this.saveOrUpdate(menu);
}
@Override
public void updateMenuIsHidden(IsHiddenVO isHiddenVO) {
Menu menu = BeanCopyUtil.copyObject(isHiddenVO, Menu.class);
menuMapper.updateById(menu);
}
@Override
public void deleteMenu(Integer menuId) {
Integer count = roleMenuMapper.selectCount(new LambdaQueryWrapper<RoleMenu>()
.eq(RoleMenu::getMenuId, menuId));
if (count > 0) {
throw new BizException("菜单下有角色关联");
}
List<Integer> menuIds = menuMapper.selectList(new LambdaQueryWrapper<Menu>()
.select(Menu::getId)
.eq(Menu::getParentId, menuId))
.stream()
.map(Menu::getId)
.collect(Collectors.toList());
menuIds.add(menuId);
menuMapper.deleteBatchIds(menuIds);
}
@Override
public List<LabelOptionDTO> listMenuOptions() {
List<Menu> menus = menuMapper.selectList(new LambdaQueryWrapper<Menu>()
.select(Menu::getId, Menu::getName, Menu::getParentId, Menu::getOrderNum));
List<Menu> catalogs = listCatalogs(menus);
Map<Integer, List<Menu>> childrenMap = getMenuMap(menus);
return catalogs.stream().map(item -> {
List<LabelOptionDTO> list = new ArrayList<>();
List<Menu> children = childrenMap.get(item.getId());
if (CollectionUtils.isNotEmpty(children)) {
list = children.stream()
.sorted(Comparator.comparing(Menu::getOrderNum))
.map(menu -> LabelOptionDTO.builder()
.id(menu.getId())
.label(menu.getName())
.build())
.collect(Collectors.toList());
}
return LabelOptionDTO.builder()
.id(item.getId())
.label(item.getName())
.children(list)
.build();
}).collect(Collectors.toList());
}
@Override
public List<UserMenuDTO> listUserMenus() {
List<Menu> menus = menuMapper.listMenusByUserInfoId(UserUtil.getUserDetailsDTO().getUserInfoId());
List<Menu> catalogs = listCatalogs(menus);
Map<Integer, List<Menu>> childrenMap = getMenuMap(menus);
return convertUserMenuList(catalogs, childrenMap);
}
private List<Menu> listCatalogs(List<Menu> menus) {
return menus.stream()
.filter(item -> Objects.isNull(item.getParentId()))
.sorted(Comparator.comparing(Menu::getOrderNum))
.collect(Collectors.toList());
}
private Map<Integer, List<Menu>> getMenuMap(List<Menu> menus) {
return menus.stream()
.filter(item -> Objects.nonNull(item.getParentId()))
.collect(Collectors.groupingBy(Menu::getParentId));
}
private List<UserMenuDTO> convertUserMenuList(List<Menu> catalogList, Map<Integer, List<Menu>> childrenMap) {
return catalogList.stream().map(item -> {
UserMenuDTO userMenuDTO = new UserMenuDTO();
List<UserMenuDTO> list = new ArrayList<>();
List<Menu> children = childrenMap.get(item.getId());
if (CollectionUtils.isNotEmpty(children)) {
userMenuDTO = BeanCopyUtil.copyObject(item, UserMenuDTO.class);
list = children.stream()
.sorted(Comparator.comparing(Menu::getOrderNum))
.map(menu -> {
UserMenuDTO dto = BeanCopyUtil.copyObject(menu, UserMenuDTO.class);
dto.setHidden(menu.getIsHidden().equals(TRUE));
return dto;
})
.collect(Collectors.toList());
} else {
userMenuDTO.setPath(item.getPath());
userMenuDTO.setComponent(COMPONENT);
list.add(UserMenuDTO.builder()
.path("")
.name(item.getName())
.icon(item.getIcon())
.component(item.getComponent())
.build());
}
userMenuDTO.setHidden(item.getIsHidden().equals(TRUE));
userMenuDTO.setChildren(list);
return userMenuDTO;
}).collect(Collectors.toList());
}
}

@ -0,0 +1,34 @@
package com.aurora.service.impl;
import com.aurora.model.dto.OperationLogDTO;
import com.aurora.entity.OperationLog;
import com.aurora.mapper.OperationLogMapper;
import com.aurora.service.OperationLogService;
import com.aurora.util.BeanCopyUtil;
import com.aurora.util.PageUtil;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.dto.PageResultDTO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class OperationLogServiceImpl extends ServiceImpl<OperationLogMapper, OperationLog> implements OperationLogService {
@Override
public PageResultDTO<OperationLogDTO> listOperationLogs(ConditionVO conditionVO) {
Page<OperationLog> page = new Page<>(PageUtil.getCurrent(), PageUtil.getSize());
Page<OperationLog> operationLogPage = this.page(page, new LambdaQueryWrapper<OperationLog>()
.like(StringUtils.isNotBlank(conditionVO.getKeywords()), OperationLog::getOptModule, conditionVO.getKeywords())
.or()
.like(StringUtils.isNotBlank(conditionVO.getKeywords()), OperationLog::getOptDesc, conditionVO.getKeywords())
.orderByDesc(OperationLog::getId));
List<OperationLogDTO> operationLogDTOs = BeanCopyUtil.copyList(operationLogPage.getRecords(), OperationLogDTO.class);
return new PageResultDTO<>(operationLogDTOs, (int) operationLogPage.getTotal());
}
}

@ -0,0 +1,110 @@
package com.aurora.service.impl;
import com.aurora.model.dto.PhotoAlbumAdminDTO;
import com.aurora.model.dto.PhotoAlbumDTO;
import com.aurora.entity.Photo;
import com.aurora.entity.PhotoAlbum;
import com.aurora.exception.BizException;
import com.aurora.mapper.PhotoAlbumMapper;
import com.aurora.mapper.PhotoMapper;
import com.aurora.service.PhotoAlbumService;
import com.aurora.util.BeanCopyUtil;
import com.aurora.util.PageUtil;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.model.vo.PhotoAlbumVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Objects;
import static com.aurora.constant.CommonConstant.FALSE;
import static com.aurora.constant.CommonConstant.TRUE;
import static com.aurora.enums.PhotoAlbumStatusEnum.PUBLIC;
@Service
public class PhotoAlbumServiceImpl extends ServiceImpl<PhotoAlbumMapper, PhotoAlbum> implements PhotoAlbumService {
@Autowired
private PhotoAlbumMapper photoAlbumMapper;
@Autowired
private PhotoMapper photoMapper;
@Transactional(rollbackFor = Exception.class)
@Override
public void saveOrUpdatePhotoAlbum(PhotoAlbumVO photoAlbumVO) {
PhotoAlbum album = photoAlbumMapper.selectOne(new LambdaQueryWrapper<PhotoAlbum>()
.select(PhotoAlbum::getId)
.eq(PhotoAlbum::getAlbumName, photoAlbumVO.getAlbumName()));
if (Objects.nonNull(album) && !album.getId().equals(photoAlbumVO.getId())) {
throw new BizException("相册名已存在");
}
PhotoAlbum photoAlbum = BeanCopyUtil.copyObject(photoAlbumVO, PhotoAlbum.class);
this.saveOrUpdate(photoAlbum);
}
@Override
public PageResultDTO<PhotoAlbumAdminDTO> listPhotoAlbumsAdmin(ConditionVO conditionVO) {
Integer count = photoAlbumMapper.selectCount(new LambdaQueryWrapper<PhotoAlbum>()
.like(StringUtils.isNotBlank(conditionVO.getKeywords()), PhotoAlbum::getAlbumName, conditionVO.getKeywords())
.eq(PhotoAlbum::getIsDelete, FALSE));
if (count == 0) {
return new PageResultDTO<>();
}
List<PhotoAlbumAdminDTO> photoAlbumBacks = photoAlbumMapper.listPhotoAlbumsAdmin(PageUtil.getLimitCurrent(), PageUtil.getSize(), conditionVO);
return new PageResultDTO<>(photoAlbumBacks, count);
}
@Override
public List<PhotoAlbumDTO> listPhotoAlbumInfosAdmin() {
List<PhotoAlbum> photoAlbums = photoAlbumMapper.selectList(new LambdaQueryWrapper<PhotoAlbum>()
.eq(PhotoAlbum::getIsDelete, FALSE));
return BeanCopyUtil.copyList(photoAlbums, PhotoAlbumDTO.class);
}
@Override
public PhotoAlbumAdminDTO getPhotoAlbumByIdAdmin(Integer albumId) {
PhotoAlbum photoAlbum = photoAlbumMapper.selectById(albumId);
Integer photoCount = photoMapper.selectCount(new LambdaQueryWrapper<Photo>()
.eq(Photo::getAlbumId, albumId)
.eq(Photo::getIsDelete, FALSE));
PhotoAlbumAdminDTO album = BeanCopyUtil.copyObject(photoAlbum, PhotoAlbumAdminDTO.class);
album.setPhotoCount(photoCount);
return album;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deletePhotoAlbumById(Integer albumId) {
Integer count = photoMapper.selectCount(new LambdaQueryWrapper<Photo>()
.eq(Photo::getAlbumId, albumId));
if (count > 0) {
photoAlbumMapper.updateById(PhotoAlbum.builder()
.id(albumId)
.isDelete(TRUE)
.build());
photoMapper.update(new Photo(), new LambdaUpdateWrapper<Photo>()
.set(Photo::getIsDelete, TRUE)
.eq(Photo::getAlbumId, albumId));
} else {
photoAlbumMapper.deleteById(albumId);
}
}
@Override
public List<PhotoAlbumDTO> listPhotoAlbums() {
List<PhotoAlbum> photoAlbumList = photoAlbumMapper.selectList(new LambdaQueryWrapper<PhotoAlbum>()
.eq(PhotoAlbum::getStatus, PUBLIC.getStatus())
.eq(PhotoAlbum::getIsDelete, FALSE)
.orderByDesc(PhotoAlbum::getId));
return BeanCopyUtil.copyList(photoAlbumList, PhotoAlbumDTO.class);
}
}

@ -0,0 +1,141 @@
package com.aurora.service.impl;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.model.dto.PhotoAdminDTO;
import com.aurora.model.dto.PhotoAlbumAdminDTO;
import com.aurora.model.dto.PhotoDTO;
import com.aurora.entity.Photo;
import com.aurora.entity.PhotoAlbum;
import com.aurora.exception.BizException;
import com.aurora.mapper.PhotoAlbumMapper;
import com.aurora.mapper.PhotoMapper;
import com.aurora.service.PhotoAlbumService;
import com.aurora.service.PhotoService;
import com.aurora.util.BeanCopyUtil;
import com.aurora.util.PageUtil;
import com.aurora.model.vo.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import static com.aurora.constant.CommonConstant.FALSE;
import static com.aurora.enums.PhotoAlbumStatusEnum.PUBLIC;
@Service
public class PhotoServiceImpl extends ServiceImpl<PhotoMapper, Photo> implements PhotoService {
@Autowired
private PhotoMapper photoMapper;
@Autowired
private PhotoAlbumService photoAlbumService;
@Override
public PageResultDTO<PhotoAdminDTO> listPhotos(ConditionVO conditionVO) {
Page<Photo> page = new Page<>(PageUtil.getCurrent(), PageUtil.getSize());
Page<Photo> photoPage = photoMapper.selectPage(page, new LambdaQueryWrapper<Photo>()
.eq(Objects.nonNull(conditionVO.getAlbumId()), Photo::getAlbumId, conditionVO.getAlbumId())
.eq(Photo::getIsDelete, conditionVO.getIsDelete())
.orderByDesc(Photo::getId)
.orderByDesc(Photo::getUpdateTime));
List<PhotoAdminDTO> photos = BeanCopyUtil.copyList(photoPage.getRecords(), PhotoAdminDTO.class);
return new PageResultDTO<>(photos, (int) photoPage.getTotal());
}
@Transactional(rollbackFor = Exception.class)
@Override
public void updatePhoto(PhotoInfoVO photoInfoVO) {
Photo photo = BeanCopyUtil.copyObject(photoInfoVO, Photo.class);
photoMapper.updateById(photo);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void savePhotos(PhotoVO photoVO) {
List<Photo> photoList = photoVO.getPhotoUrls().stream().map(item -> Photo.builder()
.albumId(photoVO.getAlbumId())
.photoName(IdWorker.getIdStr())
.photoSrc(item)
.build())
.collect(Collectors.toList());
this.saveBatch(photoList);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void updatePhotosAlbum(PhotoVO photoVO) {
List<Photo> photoList = photoVO.getPhotoIds().stream().map(item -> Photo.builder()
.id(item)
.albumId(photoVO.getAlbumId())
.build())
.collect(Collectors.toList());
this.updateBatchById(photoList);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void updatePhotoDelete(DeleteVO deleteVO) {
List<Photo> photoList = deleteVO.getIds().stream().map(item -> Photo.builder()
.id(item)
.isDelete(deleteVO.getIsDelete())
.build())
.collect(Collectors.toList());
this.updateBatchById(photoList);
if (deleteVO.getIsDelete().equals(FALSE)) {
List<PhotoAlbum> photoAlbumList = photoMapper.selectList(new LambdaQueryWrapper<Photo>()
.select(Photo::getAlbumId)
.in(Photo::getId, deleteVO.getIds())
.groupBy(Photo::getAlbumId))
.stream()
.map(item -> PhotoAlbum.builder()
.id(item.getAlbumId())
.isDelete(FALSE)
.build())
.collect(Collectors.toList());
photoAlbumService.updateBatchById(photoAlbumList);
}
}
@Transactional(rollbackFor = Exception.class)
@Override
public void deletePhotos(List<Integer> photoIds) {
photoMapper.deleteBatchIds(photoIds);
}
@Override
public PhotoDTO listPhotosByAlbumId(Integer albumId) {
PhotoAlbum photoAlbum = photoAlbumService.getOne(new LambdaQueryWrapper<PhotoAlbum>()
.eq(PhotoAlbum::getId, albumId)
.eq(PhotoAlbum::getIsDelete, FALSE)
.eq(PhotoAlbum::getStatus, PUBLIC.getStatus()));
if (Objects.isNull(photoAlbum)) {
throw new BizException("相册不存在");
}
Page<Photo> page = new Page<>(PageUtil.getCurrent(), PageUtil.getSize());
List<String> photos = photoMapper.selectPage(page, new LambdaQueryWrapper<Photo>()
.select(Photo::getPhotoSrc)
.eq(Photo::getAlbumId, albumId)
.eq(Photo::getIsDelete, FALSE)
.orderByDesc(Photo::getId))
.getRecords()
.stream()
.map(Photo::getPhotoSrc)
.collect(Collectors.toList());
return PhotoDTO.builder()
.photoAlbumCover(photoAlbum.getAlbumCover())
.photoAlbumName(photoAlbum.getAlbumName())
.photos(photos)
.build();
}
}

@ -0,0 +1,326 @@
package com.aurora.service.impl;
import com.aurora.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Service
@SuppressWarnings("all")
public class RedisServiceImpl implements RedisService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public void set(String key, Object value, long time) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
}
@Override
public void set(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
@Override
public Object get(String key) {
return redisTemplate.opsForValue().get(key);
}
@Override
public Boolean del(String key) {
return redisTemplate.delete(key);
}
@Override
public Long del(List<String> keys) {
return redisTemplate.delete(keys);
}
@Override
public Boolean expire(String key, long time) {
return redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
@Override
public Long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
@Override
public Boolean hasKey(String key) {
return redisTemplate.hasKey(key);
}
@Override
public Long incr(String key, long delta) {
return redisTemplate.opsForValue().increment(key, delta);
}
@Override
public Long incrExpire(String key, long time) {
Long count = redisTemplate.opsForValue().increment(key, 1);
if (count != null && count == 1) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return count;
}
@Override
public Long decr(String key, long delta) {
return redisTemplate.opsForValue().increment(key, -delta);
}
@Override
public Object hGet(String key, String hashKey) {
return redisTemplate.opsForHash().get(key, hashKey);
}
@Override
public Boolean hSet(String key, String hashKey, Object value, long time) {
redisTemplate.opsForHash().put(key, hashKey, value);
return expire(key, time);
}
@Override
public void hSet(String key, String hashKey, Object value) {
redisTemplate.opsForHash().put(key, hashKey, value);
}
@Override
public Map hGetAll(String key) {
return redisTemplate.opsForHash().entries(key);
}
@Override
public Boolean hSetAll(String key, Map<String, Object> map, long time) {
redisTemplate.opsForHash().putAll(key, map);
return expire(key, time);
}
@Override
public void hSetAll(String key, Map<String, ?> map) {
redisTemplate.opsForHash().putAll(key, map);
}
@Override
public void hDel(String key, Object... hashKey) {
redisTemplate.opsForHash().delete(key, hashKey);
}
@Override
public Boolean hHasKey(String key, String hashKey) {
return redisTemplate.opsForHash().hasKey(key, hashKey);
}
@Override
public Long hIncr(String key, String hashKey, Long delta) {
return redisTemplate.opsForHash().increment(key, hashKey, delta);
}
@Override
public Long hDecr(String key, String hashKey, Long delta) {
return redisTemplate.opsForHash().increment(key, hashKey, -delta);
}
@Override
public Double zIncr(String key, Object value, Double score) {
return redisTemplate.opsForZSet().incrementScore(key, value, score);
}
@Override
public Double zDecr(String key, Object value, Double score) {
return redisTemplate.opsForZSet().incrementScore(key, value, -score);
}
@Override
public Map<Object, Double> zReverseRangeWithScore(String key, long start, long end) {
return redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end)
.stream()
.collect(Collectors.toMap(ZSetOperations.TypedTuple::getValue, ZSetOperations.TypedTuple::getScore));
}
@Override
public Double zScore(String key, Object value) {
return redisTemplate.opsForZSet().score(key, value);
}
@Override
public Map<Object, Double> zAllScore(String key) {
return Objects.requireNonNull(redisTemplate.opsForZSet().rangeWithScores(key, 0, -1))
.stream()
.collect(Collectors.toMap(ZSetOperations.TypedTuple::getValue, ZSetOperations.TypedTuple::getScore));
}
@Override
public Set<Object> sMembers(String key) {
return redisTemplate.opsForSet().members(key);
}
@Override
public Long sAdd(String key, Object... values) {
return redisTemplate.opsForSet().add(key, values);
}
@Override
public Long sAddExpire(String key, long time, Object... values) {
Long count = redisTemplate.opsForSet().add(key, values);
expire(key, time);
return count;
}
@Override
public Boolean sIsMember(String key, Object value) {
return redisTemplate.opsForSet().isMember(key, value);
}
@Override
public Long sSize(String key) {
return redisTemplate.opsForSet().size(key);
}
@Override
public Long sRemove(String key, Object... values) {
return redisTemplate.opsForSet().remove(key, values);
}
@Override
public List<Object> lRange(String key, long start, long end) {
return redisTemplate.opsForList().range(key, start, end);
}
@Override
public Long lSize(String key) {
return redisTemplate.opsForList().size(key);
}
@Override
public Object lIndex(String key, long index) {
return redisTemplate.opsForList().index(key, index);
}
@Override
public Long lPush(String key, Object value) {
return redisTemplate.opsForList().rightPush(key, value);
}
@Override
public Long lPush(String key, Object value, long time) {
Long index = redisTemplate.opsForList().rightPush(key, value);
expire(key, time);
return index;
}
@Override
public Long lPushAll(String key, Object... values) {
return redisTemplate.opsForList().rightPushAll(key, values);
}
@Override
public Long lPushAll(String key, Long time, Object... values) {
Long count = redisTemplate.opsForList().rightPushAll(key, values);
expire(key, time);
return count;
}
@Override
public Long lRemove(String key, long count, Object value) {
return redisTemplate.opsForList().remove(key, count, value);
}
@Override
public Boolean bitAdd(String key, int offset, boolean b) {
return redisTemplate.opsForValue().setBit(key, offset, b);
}
@Override
public Boolean bitGet(String key, int offset) {
return redisTemplate.opsForValue().getBit(key, offset);
}
@Override
public Long bitCount(String key) {
return redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(key.getBytes()));
}
@Override
public List<Long> bitField(String key, int limit, int offset) {
return redisTemplate.execute((RedisCallback<List<Long>>) con ->
con.bitField(key.getBytes(),
BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(limit)).valueAt(offset)));
}
@Override
public byte[] bitGetAll(String key) {
return redisTemplate.execute((RedisCallback<byte[]>) con -> con.get(key.getBytes()));
}
@Override
public Long hyperAdd(String key, Object... value) {
return redisTemplate.opsForHyperLogLog().add(key, value);
}
@Override
public Long hyperGet(String... key) {
return redisTemplate.opsForHyperLogLog().size(key);
}
@Override
public void hyperDel(String key) {
redisTemplate.opsForHyperLogLog().delete(key);
}
@Override
public Long geoAdd(String key, Double x, Double y, String name) {
return redisTemplate.opsForGeo().add(key, new Point(x, y), name);
}
@Override
public List<Point> geoGetPointList(String key, Object... place) {
return redisTemplate.opsForGeo().position(key, place);
}
@Override
public Distance geoCalculationDistance(String key, String placeOne, String placeTow) {
return redisTemplate.opsForGeo()
.distance(key, placeOne, placeTow, RedisGeoCommands.DistanceUnit.KILOMETERS);
}
@Override
public GeoResults<RedisGeoCommands.GeoLocation<Object>> geoNearByPlace(String key, String place, Distance distance, long limit, Sort.Direction sort) {
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates();
// 判断排序方式
if (Sort.Direction.ASC == sort) {
args.sortAscending();
} else {
args.sortDescending();
}
args.limit(limit);
return redisTemplate.opsForGeo()
.radius(key, place, distance, args);
}
@Override
public List<String> geoGetHash(String key, String... place) {
return redisTemplate.opsForGeo()
.hash(key, place);
}
}

@ -0,0 +1,175 @@
package com.aurora.service.impl;
import com.aurora.model.dto.LabelOptionDTO;
import com.aurora.model.dto.ResourceDTO;
import com.aurora.entity.Resource;
import com.aurora.entity.RoleResource;
import com.aurora.exception.BizException;
import com.aurora.handler.FilterInvocationSecurityMetadataSourceImpl;
import com.aurora.mapper.ResourceMapper;
import com.aurora.mapper.RoleResourceMapper;
import com.aurora.service.ResourceService;
import com.aurora.util.BeanCopyUtil;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.vo.ResourceVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import static com.aurora.constant.CommonConstant.FALSE;
@Service
public class ResourceServiceImpl extends ServiceImpl<ResourceMapper, Resource> implements ResourceService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private ResourceMapper resourceMapper;
@Autowired
private RoleResourceMapper roleResourceMapper;
@Autowired
private FilterInvocationSecurityMetadataSourceImpl filterInvocationSecurityMetadataSource;
@SuppressWarnings("all")
@Transactional(rollbackFor = Exception.class)
@Override
public void importSwagger() {
this.remove(null);
roleResourceMapper.delete(null);
List<Resource> resources = new ArrayList<>();
Map<String, Object> data = restTemplate.getForObject("http://localhost:8080/v2/api-docs", Map.class);
List<Map<String, String>> tagList = (List<Map<String, String>>) data.get("tags");
tagList.forEach(item -> {
Resource resource = Resource.builder()
.resourceName(item.get("name"))
.isAnonymous(FALSE)
.createTime(LocalDateTime.now())
.build();
resources.add(resource);
});
this.saveBatch(resources);
Map<String, Integer> permissionMap = resources.stream()
.collect(Collectors.toMap(Resource::getResourceName, Resource::getId));
resources.clear();
Map<String, Map<String, Map<String, Object>>> path = (Map<String, Map<String, Map<String, Object>>>) data.get("paths");
path.forEach((url, value) -> value.forEach((requestMethod, info) -> {
String permissionName = info.get("summary").toString();
List<String> tag = (List<String>) info.get("tags");
Integer parentId = permissionMap.get(tag.get(0));
Resource resource = Resource.builder()
.resourceName(permissionName)
.url(url.replaceAll("\\{[^}]*\\}", "*"))
.parentId(parentId)
.requestMethod(requestMethod.toUpperCase())
.isAnonymous(FALSE)
.createTime(LocalDateTime.now())
.build();
resources.add(resource);
}));
this.saveBatch(resources);
}
@Override
public void saveOrUpdateResource(ResourceVO resourceVO) {
Resource resource = BeanCopyUtil.copyObject(resourceVO, Resource.class);
this.saveOrUpdate(resource);
filterInvocationSecurityMetadataSource.clearDataSource();
}
@Override
public void deleteResource(Integer resourceId) {
Integer count = roleResourceMapper.selectCount(new LambdaQueryWrapper<RoleResource>()
.eq(RoleResource::getResourceId, resourceId));
if (count > 0) {
throw new BizException("该资源下存在角色");
}
List<Integer> resourceIds = resourceMapper.selectList(new LambdaQueryWrapper<Resource>()
.select(Resource::getId).
eq(Resource::getParentId, resourceId))
.stream()
.map(Resource::getId)
.collect(Collectors.toList());
resourceIds.add(resourceId);
resourceMapper.deleteBatchIds(resourceIds);
}
@Override
public List<ResourceDTO> listResources(ConditionVO conditionVO) {
List<Resource> resources = resourceMapper.selectList(new LambdaQueryWrapper<Resource>()
.like(StringUtils.isNotBlank(conditionVO.getKeywords()), Resource::getResourceName, conditionVO.getKeywords()));
List<Resource> parents = listResourceModule(resources);
Map<Integer, List<Resource>> childrenMap = listResourceChildren(resources);
List<ResourceDTO> resourceDTOs = parents.stream().map(item -> {
ResourceDTO resourceDTO = BeanCopyUtil.copyObject(item, ResourceDTO.class);
List<ResourceDTO> child = BeanCopyUtil.copyList(childrenMap.get(item.getId()), ResourceDTO.class);
resourceDTO.setChildren(child);
childrenMap.remove(item.getId());
return resourceDTO;
}).collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(childrenMap)) {
List<Resource> childrenList = new ArrayList<>();
childrenMap.values().forEach(childrenList::addAll);
List<ResourceDTO> childrenDTOs = childrenList.stream()
.map(item -> BeanCopyUtil.copyObject(item, ResourceDTO.class))
.collect(Collectors.toList());
resourceDTOs.addAll(childrenDTOs);
}
return resourceDTOs;
}
@Override
public List<LabelOptionDTO> listResourceOption() {
List<Resource> resources = resourceMapper.selectList(new LambdaQueryWrapper<Resource>()
.select(Resource::getId, Resource::getResourceName, Resource::getParentId)
.eq(Resource::getIsAnonymous, FALSE));
List<Resource> parents = listResourceModule(resources);
Map<Integer, List<Resource>> childrenMap = listResourceChildren(resources);
return parents.stream().map(item -> {
List<LabelOptionDTO> list = new ArrayList<>();
List<Resource> children = childrenMap.get(item.getId());
if (CollectionUtils.isNotEmpty(children)) {
list = children.stream()
.map(resource -> LabelOptionDTO.builder()
.id(resource.getId())
.label(resource.getResourceName())
.build())
.collect(Collectors.toList());
}
return LabelOptionDTO.builder()
.id(item.getId())
.label(item.getResourceName())
.children(list)
.build();
}).collect(Collectors.toList());
}
private List<Resource> listResourceModule(List<Resource> resourceList) {
return resourceList.stream()
.filter(item -> Objects.isNull(item.getParentId()))
.collect(Collectors.toList());
}
private Map<Integer, List<Resource>> listResourceChildren(List<Resource> resourceList) {
return resourceList.stream()
.filter(item -> Objects.nonNull(item.getParentId()))
.collect(Collectors.groupingBy(Resource::getParentId));
}
}

@ -0,0 +1,12 @@
package com.aurora.service.impl;
import com.aurora.entity.RoleMenu;
import com.aurora.mapper.RoleMenuMapper;
import com.aurora.service.RoleMenuService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class RoleMenuServiceImpl extends ServiceImpl<RoleMenuMapper, RoleMenu> implements RoleMenuService {
}

@ -0,0 +1,12 @@
package com.aurora.service.impl;
import com.aurora.entity.RoleResource;
import com.aurora.mapper.RoleResourceMapper;
import com.aurora.service.RoleResourceService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class RoleResourceServiceImpl extends ServiceImpl<RoleResourceMapper, RoleResource> implements RoleResourceService {
}

@ -0,0 +1,123 @@
package com.aurora.service.impl;
import com.aurora.constant.CommonConstant;
import com.aurora.model.dto.RoleDTO;
import com.aurora.model.dto.UserRoleDTO;
import com.aurora.entity.Role;
import com.aurora.entity.RoleMenu;
import com.aurora.entity.RoleResource;
import com.aurora.entity.UserRole;
import com.aurora.exception.BizException;
import com.aurora.handler.FilterInvocationSecurityMetadataSourceImpl;
import com.aurora.mapper.RoleMapper;
import com.aurora.mapper.UserRoleMapper;
import com.aurora.service.RoleMenuService;
import com.aurora.service.RoleResourceService;
import com.aurora.service.RoleService;
import com.aurora.util.BeanCopyUtil;
import com.aurora.util.PageUtil;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.model.vo.RoleVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@Service
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {
@Autowired
private RoleMapper roleMapper;
@Autowired
private UserRoleMapper userRoleMapper;
@Autowired
private RoleResourceService roleResourceService;
@Autowired
private RoleMenuService roleMenuService;
@Autowired
private FilterInvocationSecurityMetadataSourceImpl filterInvocationSecurityMetadataSource;
@Override
public List<UserRoleDTO> listUserRoles() {
List<Role> roleList = roleMapper.selectList(new LambdaQueryWrapper<Role>()
.select(Role::getId, Role::getRoleName));
return BeanCopyUtil.copyList(roleList, UserRoleDTO.class);
}
@SneakyThrows
@Override
public PageResultDTO<RoleDTO> listRoles(ConditionVO conditionVO) {
LambdaQueryWrapper<Role> queryWrapper = new LambdaQueryWrapper<Role>()
.like(StringUtils.isNotBlank(conditionVO.getKeywords()), Role::getRoleName, conditionVO.getKeywords());
CompletableFuture<Integer> asyncCount = CompletableFuture.supplyAsync(() -> roleMapper.selectCount(queryWrapper));
List<RoleDTO> roleDTOs = roleMapper.listRoles(PageUtil.getLimitCurrent(), PageUtil.getSize(), conditionVO);
return new PageResultDTO<>(roleDTOs, asyncCount.get());
}
@Transactional(rollbackFor = Exception.class)
@Override
public void saveOrUpdateRole(RoleVO roleVO) {
Role roleCheck = roleMapper.selectOne(new LambdaQueryWrapper<Role>()
.select(Role::getId)
.eq(Role::getRoleName, roleVO.getRoleName()));
if (Objects.nonNull(roleCheck) && !(roleCheck.getId().equals(roleVO.getId()))) {
throw new BizException("该角色存在");
}
Role role = Role.builder()
.id(roleVO.getId())
.roleName(roleVO.getRoleName())
.isDisable(CommonConstant.FALSE)
.build();
this.saveOrUpdate(role);
if (Objects.nonNull(roleVO.getResourceIds())) {
if (Objects.nonNull(roleVO.getId())) {
roleResourceService.remove(new LambdaQueryWrapper<RoleResource>()
.eq(RoleResource::getRoleId, roleVO.getId()));
}
List<RoleResource> roleResourceList = roleVO.getResourceIds().stream()
.map(resourceId -> RoleResource.builder()
.roleId(role.getId())
.resourceId(resourceId)
.build())
.collect(Collectors.toList());
roleResourceService.saveBatch(roleResourceList);
filterInvocationSecurityMetadataSource.clearDataSource();
}
if (Objects.nonNull(roleVO.getMenuIds())) {
if (Objects.nonNull(roleVO.getId())) {
roleMenuService.remove(new LambdaQueryWrapper<RoleMenu>().eq(RoleMenu::getRoleId, roleVO.getId()));
}
List<RoleMenu> roleMenuList = roleVO.getMenuIds().stream()
.map(menuId -> RoleMenu.builder()
.roleId(role.getId())
.menuId(menuId)
.build())
.collect(Collectors.toList());
roleMenuService.saveBatch(roleMenuList);
}
}
@Override
public void deleteRoles(List<Integer> roleIdList) {
Integer count = userRoleMapper.selectCount(new LambdaQueryWrapper<UserRole>()
.in(UserRole::getRoleId, roleIdList));
if (count > 0) {
throw new BizException("该角色下存在用户");
}
roleMapper.deleteBatchIds(roleIdList);
}
}

@ -0,0 +1,90 @@
package com.aurora.service.impl;
import com.aurora.model.dto.TagAdminDTO;
import com.aurora.model.dto.TagDTO;
import com.aurora.entity.ArticleTag;
import com.aurora.entity.Tag;
import com.aurora.exception.BizException;
import com.aurora.mapper.ArticleTagMapper;
import com.aurora.mapper.TagMapper;
import com.aurora.service.TagService;
import com.aurora.util.BeanCopyUtil;
import com.aurora.util.PageUtil;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.model.vo.TagVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Objects;
@Service
public class TagServiceImpl extends ServiceImpl<TagMapper, Tag> implements TagService {
@Autowired
private TagMapper tagMapper;
@Autowired
private ArticleTagMapper articleTagMapper;
@Override
public List<TagDTO> listTags() {
return tagMapper.listTags();
}
@Override
public List<TagDTO> listTopTenTags() {
return tagMapper.listTopTenTags();
}
@SneakyThrows
@Override
public PageResultDTO<TagAdminDTO> listTagsAdmin(ConditionVO conditionVO) {
Integer count = tagMapper.selectCount(new LambdaQueryWrapper<Tag>()
.like(StringUtils.isNotBlank(conditionVO.getKeywords()), Tag::getTagName, conditionVO.getKeywords()));
if (count == 0) {
return new PageResultDTO<>();
}
List<TagAdminDTO> tags = tagMapper.listTagsAdmin(PageUtil.getLimitCurrent(), PageUtil.getSize(), conditionVO);
return new PageResultDTO<>(tags, count);
}
@SneakyThrows
@Override
public List<TagAdminDTO> listTagsAdminBySearch(ConditionVO conditionVO) {
List<Tag> tags = tagMapper.selectList(new LambdaQueryWrapper<Tag>()
.like(StringUtils.isNotBlank(conditionVO.getKeywords()), Tag::getTagName, conditionVO.getKeywords())
.orderByDesc(Tag::getId));
return BeanCopyUtil.copyList(tags, TagAdminDTO.class);
}
@Override
public void saveOrUpdateTag(TagVO tagVO) {
Tag existTag = tagMapper.selectOne(new LambdaQueryWrapper<Tag>()
.select(Tag::getId)
.eq(Tag::getTagName, tagVO.getTagName()));
if (Objects.nonNull(existTag) && !existTag.getId().equals(tagVO.getId())) {
throw new BizException("标签名已存在");
}
Tag tag = BeanCopyUtil.copyObject(tagVO, Tag.class);
this.saveOrUpdate(tag);
}
@Override
public void deleteTag(List<Integer> tagIds) {
Integer count = articleTagMapper.selectCount(new LambdaQueryWrapper<ArticleTag>()
.in(ArticleTag::getTagId, tagIds));
if (count > 0) {
throw new BizException("删除失败,该标签下存在文章");
}
tagMapper.deleteBatchIds(tagIds);
}
}

@ -0,0 +1,120 @@
package com.aurora.service.impl;
import com.alibaba.fastjson.JSON;
import com.aurora.enums.CommentTypeEnum;
import com.aurora.model.dto.CommentCountDTO;
import com.aurora.model.dto.TalkAdminDTO;
import com.aurora.model.dto.TalkDTO;
import com.aurora.entity.Talk;
import com.aurora.exception.BizException;
import com.aurora.mapper.CommentMapper;
import com.aurora.mapper.TalkMapper;
import com.aurora.service.TalkService;
import com.aurora.util.BeanCopyUtil;
import com.aurora.util.CommonUtil;
import com.aurora.util.PageUtil;
import com.aurora.util.UserUtil;
import com.aurora.model.vo.ConditionVO;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.model.vo.TalkVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import static com.aurora.enums.TalkStatusEnum.PUBLIC;
@Service
public class TalkServiceImpl extends ServiceImpl<TalkMapper, Talk> implements TalkService {
@Autowired
private TalkMapper talkMapper;
@Autowired
private CommentMapper commentMapper;
@Override
public PageResultDTO<TalkDTO> listTalks() {
Integer count = talkMapper.selectCount((new LambdaQueryWrapper<Talk>()
.eq(Talk::getStatus, PUBLIC.getStatus())));
if (count == 0) {
return new PageResultDTO<>();
}
List<TalkDTO> talkDTOs = talkMapper.listTalks(PageUtil.getLimitCurrent(), PageUtil.getSize());
List<Integer> talkIds = talkDTOs.stream()
.map(TalkDTO::getId)
.collect(Collectors.toList());
Map<Integer, Integer> commentCountMap = commentMapper.listCommentCountByTypeAndTopicIds(CommentTypeEnum.TALK.getType(), talkIds)
.stream()
.collect(Collectors.toMap(CommentCountDTO::getId, CommentCountDTO::getCommentCount));
talkDTOs.forEach(item -> {
item.setCommentCount(commentCountMap.get(item.getId()));
if (Objects.nonNull(item.getImages())) {
item.setImgs(CommonUtil.castList(JSON.parseObject(item.getImages(), List.class), String.class));
}
});
return new PageResultDTO<>(talkDTOs, count);
}
@Override
public TalkDTO getTalkById(Integer talkId) {
TalkDTO talkDTO = talkMapper.getTalkById(talkId);
if (Objects.isNull(talkDTO)) {
throw new BizException("说说不存在");
}
if (Objects.nonNull(talkDTO.getImages())) {
talkDTO.setImgs(CommonUtil.castList(JSON.parseObject(talkDTO.getImages(), List.class), String.class));
}
CommentCountDTO commentCountDTO = commentMapper.listCommentCountByTypeAndTopicId(CommentTypeEnum.TALK.getType(), talkId);
if (Objects.nonNull(commentCountDTO)) {
talkDTO.setCommentCount(commentCountDTO.getCommentCount());
}
return talkDTO;
}
@Override
public void saveOrUpdateTalk(TalkVO talkVO) {
Talk talk = BeanCopyUtil.copyObject(talkVO, Talk.class);
talk.setUserId(UserUtil.getUserDetailsDTO().getUserInfoId());
this.saveOrUpdate(talk);
}
@Override
public void deleteTalks(List<Integer> talkIds) {
talkMapper.deleteBatchIds(talkIds);
}
@Override
public PageResultDTO<TalkAdminDTO> listBackTalks(ConditionVO conditionVO) {
Integer count = talkMapper.selectCount(new LambdaQueryWrapper<Talk>()
.eq(Objects.nonNull(conditionVO.getStatus()), Talk::getStatus, conditionVO.getStatus()));
if (count == 0) {
return new PageResultDTO<>();
}
List<TalkAdminDTO> talkDTOs = talkMapper.listTalksAdmin(PageUtil.getLimitCurrent(), PageUtil.getSize(), conditionVO);
talkDTOs.forEach(item -> {
if (Objects.nonNull(item.getImages())) {
item.setImgs(CommonUtil.castList(JSON.parseObject(item.getImages(), List.class), String.class));
}
});
return new PageResultDTO<>(talkDTOs, count);
}
@Override
public TalkAdminDTO getBackTalkById(Integer talkId) {
TalkAdminDTO talkBackDTO = talkMapper.getTalkByIdAdmin(talkId);
if (Objects.nonNull(talkBackDTO.getImages())) {
talkBackDTO.setImgs(CommonUtil.castList(JSON.parseObject(talkBackDTO.getImages(), List.class), String.class));
}
return talkBackDTO;
}
}

@ -0,0 +1,100 @@
package com.aurora.service.impl;
import com.aurora.model.dto.UserDetailsDTO;
import com.aurora.service.RedisService;
import com.aurora.service.TokenService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Base64;
import java.util.Optional;
import java.util.UUID;
import static com.aurora.constant.AuthConstant.*;
import static com.aurora.constant.RedisConstant.LOGIN_USER;
@Service
public class TokenServiceImpl implements TokenService {
@Value("${jwt.secret}")
private String secret;
@Autowired
private RedisService redisService;
@Override
public String createToken(UserDetailsDTO userDetailsDTO) {
refreshToken(userDetailsDTO);
String userId = userDetailsDTO.getId().toString();
return createToken(userId);
}
@Override
public String createToken(String subject) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
SecretKey secretKey = generalKey();
return Jwts.builder().setId(getUuid()).setSubject(subject)
.setIssuer("huaweimian")
.signWith(signatureAlgorithm, secretKey).compact();
}
@Override
public void refreshToken(UserDetailsDTO userDetailsDTO) {
LocalDateTime currentTime = LocalDateTime.now();
userDetailsDTO.setExpireTime(currentTime.plusSeconds(EXPIRE_TIME));
String userId = userDetailsDTO.getId().toString();
redisService.hSet(LOGIN_USER, userId, userDetailsDTO, EXPIRE_TIME);
}
@Override
public void renewToken(UserDetailsDTO userDetailsDTO) {
LocalDateTime expireTime = userDetailsDTO.getExpireTime();
LocalDateTime currentTime = LocalDateTime.now();
if (Duration.between(currentTime, expireTime).toMinutes() <= TWENTY_MINUTES) {
refreshToken(userDetailsDTO);
}
}
@Override
public Claims parseToken(String token) {
SecretKey secretKey = generalKey();
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();
}
@Override
public UserDetailsDTO getUserDetailDTO(HttpServletRequest request) {
String token = Optional.ofNullable(request.getHeader(TOKEN_HEADER)).orElse("").replaceFirst(TOKEN_PREFIX, "");
if (StringUtils.hasText(token) && !token.equals("null")) {
Claims claims = parseToken(token);
String userId = claims.getSubject();
return (UserDetailsDTO) redisService.hGet(LOGIN_USER, userId);
}
return null;
}
@Override
public void delLoginUser(Integer userId) {
redisService.hDel(LOGIN_USER, String.valueOf(userId));
}
public String getUuid() {
return UUID.randomUUID().toString().replaceAll("-", "");
}
public SecretKey generalKey() {
byte[] encodedKey = Base64.getDecoder().decode(secret);
return new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
}
}

@ -0,0 +1,29 @@
package com.aurora.service.impl;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.aurora.model.dto.UniqueViewDTO;
import com.aurora.entity.UniqueView;
import com.aurora.mapper.UniqueViewMapper;
import com.aurora.service.UniqueViewService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
@Service
public class UniqueViewServiceImpl extends ServiceImpl<UniqueViewMapper, UniqueView> implements UniqueViewService {
@Autowired
private UniqueViewMapper uniqueViewMapper;
@Override
public List<UniqueViewDTO> listUniqueViews() {
DateTime startTime = DateUtil.beginOfDay(DateUtil.offsetDay(new Date(), -7));
DateTime endTime = DateUtil.endOfDay(new Date());
return uniqueViewMapper.listUniqueViews(startTime, endTime);
}
}

@ -0,0 +1,206 @@
package com.aurora.service.impl;
import com.alibaba.fastjson.JSON;
import com.aurora.constant.CommonConstant;
import com.aurora.model.dto.*;
import com.aurora.entity.UserAuth;
import com.aurora.entity.UserInfo;
import com.aurora.entity.UserRole;
import com.aurora.enums.LoginTypeEnum;
import com.aurora.enums.RoleEnum;
import com.aurora.exception.BizException;
import com.aurora.mapper.UserAuthMapper;
import com.aurora.mapper.UserInfoMapper;
import com.aurora.mapper.UserRoleMapper;
import com.aurora.service.AuroraInfoService;
import com.aurora.service.RedisService;
import com.aurora.service.TokenService;
import com.aurora.service.UserAuthService;
import com.aurora.strategy.context.SocialLoginStrategyContext;
import com.aurora.util.PageUtil;
import com.aurora.util.UserUtil;
import com.aurora.model.vo.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import lombok.SneakyThrows;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.stream.Collectors;
import static com.aurora.constant.RabbitMQConstant.EMAIL_EXCHANGE;
import static com.aurora.constant.RedisConstant.*;
import static com.aurora.enums.UserAreaTypeEnum.getUserAreaType;
import static com.aurora.util.CommonUtil.checkEmail;
import static com.aurora.util.CommonUtil.getRandomCode;
@Service
public class UserAuthServiceImpl implements UserAuthService {
@Autowired
private UserAuthMapper userAuthMapper;
@Autowired
private UserInfoMapper userInfoMapper;
@Autowired
private UserRoleMapper userRoleMapper;
@Autowired
private RedisService redisService;
@Autowired
private AuroraInfoService auroraInfoService;
@Autowired
private TokenService tokenService;
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private SocialLoginStrategyContext socialLoginStrategyContext;
@Override
public void sendCode(String username) {
if (!checkEmail(username)) {
throw new BizException("请输入正确邮箱");
}
String code = getRandomCode();
Map<String, Object> map = new HashMap<>();
map.put("content", "您的验证码为 " + code + " 有效期15分钟请不要告诉他人哦");
EmailDTO emailDTO = EmailDTO.builder()
.email(username)
.subject(CommonConstant.CAPTCHA)
.template("common.html")
.commentMap(map)
.build();
rabbitTemplate.convertAndSend(EMAIL_EXCHANGE, "*", new Message(JSON.toJSONBytes(emailDTO), new MessageProperties()));
redisService.set(USER_CODE_KEY + username, code, CODE_EXPIRE_TIME);
}
@Override
@SuppressWarnings("unchecked")
public List<UserAreaDTO> listUserAreas(ConditionVO conditionVO) {
List<UserAreaDTO> userAreaDTOs = new ArrayList<>();
switch (Objects.requireNonNull(getUserAreaType(conditionVO.getType()))) {
case USER:
Object userArea = redisService.get(USER_AREA);
if (Objects.nonNull(userArea)) {
userAreaDTOs = JSON.parseObject(userArea.toString(), List.class);
}
return userAreaDTOs;
case VISITOR:
Map<String, Object> visitorArea = redisService.hGetAll(VISITOR_AREA);
if (Objects.nonNull(visitorArea)) {
userAreaDTOs = visitorArea.entrySet().stream()
.map(item -> UserAreaDTO.builder()
.name(item.getKey())
.value(Long.valueOf(item.getValue().toString()))
.build())
.collect(Collectors.toList());
}
return userAreaDTOs;
default:
break;
}
return userAreaDTOs;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void register(UserVO userVO) {
if (!checkEmail(userVO.getUsername())) {
throw new BizException("邮箱格式不对!");
}
if (checkUser(userVO)) {
throw new BizException("邮箱已被注册!");
}
UserInfo userInfo = UserInfo.builder()
.email(userVO.getUsername())
.nickname(CommonConstant.DEFAULT_NICKNAME + IdWorker.getId())
.avatar(auroraInfoService.getWebsiteConfig().getUserAvatar())
.build();
userInfoMapper.insert(userInfo);
UserRole userRole = UserRole.builder()
.userId(userInfo.getId())
.roleId(RoleEnum.USER.getRoleId())
.build();
userRoleMapper.insert(userRole);
UserAuth userAuth = UserAuth.builder()
.userInfoId(userInfo.getId())
.username(userVO.getUsername())
.password(BCrypt.hashpw(userVO.getPassword(), BCrypt.gensalt()))
.loginType(LoginTypeEnum.EMAIL.getType())
.build();
userAuthMapper.insert(userAuth);
}
@Override
public void updatePassword(UserVO userVO) {
if (!checkUser(userVO)) {
throw new BizException("邮箱尚未注册!");
}
userAuthMapper.update(new UserAuth(), new LambdaUpdateWrapper<UserAuth>()
.set(UserAuth::getPassword, BCrypt.hashpw(userVO.getPassword(), BCrypt.gensalt()))
.eq(UserAuth::getUsername, userVO.getUsername()));
}
@Override
@SuppressWarnings("all")
public void updateAdminPassword(PasswordVO passwordVO) {
UserAuth user = userAuthMapper.selectOne(new LambdaQueryWrapper<UserAuth>()
.eq(UserAuth::getId, UserUtil.getUserDetailsDTO().getId()));
if (Objects.nonNull(user) && BCrypt.checkpw(passwordVO.getOldPassword(), user.getPassword())) {
UserAuth userAuth = UserAuth.builder()
.id(UserUtil.getUserDetailsDTO().getId())
.password(BCrypt.hashpw(passwordVO.getNewPassword(), BCrypt.gensalt()))
.build();
userAuthMapper.updateById(userAuth);
} else {
throw new BizException("旧密码不正确");
}
}
@Override
public PageResultDTO<UserAdminDTO> listUsers(ConditionVO conditionVO) {
Integer count = userAuthMapper.countUser(conditionVO);
if (count == 0) {
return new PageResultDTO<>();
}
List<UserAdminDTO> UserAdminDTOs = userAuthMapper.listUsers(PageUtil.getLimitCurrent(), PageUtil.getSize(), conditionVO);
return new PageResultDTO<>(UserAdminDTOs, count);
}
@SneakyThrows
@Override
public UserLogoutStatusDTO logout() {
tokenService.delLoginUser(UserUtil.getUserDetailsDTO().getId());
return new UserLogoutStatusDTO("注销成功");
}
@Transactional(rollbackFor = Exception.class)
@Override
public UserInfoDTO qqLogin(QQLoginVO qqLoginVO) {
return socialLoginStrategyContext.executeLoginStrategy(JSON.toJSONString(qqLoginVO), LoginTypeEnum.QQ);
}
private Boolean checkUser(UserVO user) {
if (!user.getCode().equals(redisService.get(USER_CODE_KEY + user.getUsername()))) {
throw new BizException("验证码错误!");
}
UserAuth userAuth = userAuthMapper.selectOne(new LambdaQueryWrapper<UserAuth>()
.select(UserAuth::getUsername)
.eq(UserAuth::getUsername, user.getUsername()));
return Objects.nonNull(userAuth);
}
}

@ -0,0 +1,86 @@
package com.aurora.service.impl;
import com.aurora.model.dto.UserDetailsDTO;
import com.aurora.entity.UserAuth;
import com.aurora.entity.UserInfo;
import com.aurora.exception.BizException;
import com.aurora.mapper.RoleMapper;
import com.aurora.mapper.UserAuthMapper;
import com.aurora.mapper.UserInfoMapper;
import com.aurora.util.IpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
@Service
public class UserDetailServiceImpl implements UserDetailsService {
@Autowired
private UserAuthMapper userAuthMapper;
@Autowired
private UserInfoMapper userInfoMapper;
@Autowired
private RoleMapper roleMapper;
@Resource
private HttpServletRequest request;
@SneakyThrows
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (StringUtils.isBlank(username)) {
throw new BizException("用户名不能为空!");
}
UserAuth userAuth = userAuthMapper.selectOne(new LambdaQueryWrapper<UserAuth>()
.select(UserAuth::getId, UserAuth::getUserInfoId, UserAuth::getUsername, UserAuth::getPassword, UserAuth::getLoginType)
.eq(UserAuth::getUsername, username));
if (Objects.isNull(userAuth)) {
throw new BizException("用户不存在!");
}
return convertUserDetail(userAuth, request);
}
public UserDetailsDTO convertUserDetail(UserAuth user, HttpServletRequest request) {
UserInfo userInfo = userInfoMapper.selectById(user.getUserInfoId());
List<String> roles = roleMapper.listRolesByUserInfoId(userInfo.getId());
String ipAddress = IpUtil.getIpAddress(request);
String ipSource = IpUtil.getIpSource(ipAddress);
UserAgent userAgent = IpUtil.getUserAgent(request);
return UserDetailsDTO.builder()
.id(user.getId())
.loginType(user.getLoginType())
.userInfoId(userInfo.getId())
.username(user.getUsername())
.password(user.getPassword())
.email(userInfo.getEmail())
.roles(roles)
.nickname(userInfo.getNickname())
.avatar(userInfo.getAvatar())
.intro(userInfo.getIntro())
.website(userInfo.getWebsite())
.isSubscribe(userInfo.getIsSubscribe())
.ipAddress(ipAddress)
.ipSource(ipSource)
.isDisable(userInfo.getIsDisable())
.browser(userAgent.getBrowser().getName())
.os(userAgent.getOperatingSystem().getName())
.lastLoginTime(LocalDateTime.now())
.build();
}
}

@ -0,0 +1,175 @@
package com.aurora.service.impl;
import com.aurora.model.dto.PageResultDTO;
import com.aurora.model.dto.UserDetailsDTO;
import com.aurora.model.dto.UserInfoDTO;
import com.aurora.model.dto.UserOnlineDTO;
import com.aurora.entity.UserAuth;
import com.aurora.entity.UserInfo;
import com.aurora.entity.UserRole;
import com.aurora.enums.FilePathEnum;
import com.aurora.exception.BizException;
import com.aurora.mapper.UserAuthMapper;
import com.aurora.mapper.UserInfoMapper;
import com.aurora.service.RedisService;
import com.aurora.service.TokenService;
import com.aurora.service.UserInfoService;
import com.aurora.service.UserRoleService;
import com.aurora.strategy.context.UploadStrategyContext;
import com.aurora.util.BeanCopyUtil;
import com.aurora.util.UserUtil;
import com.aurora.model.vo.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.util.*;
import java.util.stream.Collectors;
import static com.aurora.constant.RedisConstant.USER_CODE_KEY;
import static com.aurora.util.PageUtil.getLimitCurrent;
import static com.aurora.util.PageUtil.getSize;
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {
@Autowired
private UserInfoMapper userInfoMapper;
@Autowired
private UserAuthMapper userAuthMapper;
@Autowired
private TokenService tokenService;
@Autowired
private RedisService redisService;
@Autowired
private UserRoleService userRoleService;
@Autowired
private UploadStrategyContext uploadStrategyContext;
@Transactional(rollbackFor = Exception.class)
@Override
public void updateUserInfo(UserInfoVO userInfoVO) {
UserInfo userInfo = UserInfo.builder()
.id(UserUtil.getUserDetailsDTO().getUserInfoId())
.nickname(userInfoVO.getNickname())
.intro(userInfoVO.getIntro())
.website(userInfoVO.getWebsite())
.build();
userInfoMapper.updateById(userInfo);
}
@Override
public String updateUserAvatar(MultipartFile file) {
String avatar = uploadStrategyContext.executeUploadStrategy(file, FilePathEnum.AVATAR.getPath());
UserInfo userInfo = UserInfo.builder()
.id(UserUtil.getUserDetailsDTO().getUserInfoId())
.avatar(avatar)
.build();
userInfoMapper.updateById(userInfo);
return avatar;
}
@Transactional(rollbackFor = Exception.class)
@Override
public void saveUserEmail(EmailVO emailVO) {
if (Objects.isNull(redisService.get(USER_CODE_KEY + emailVO.getEmail()))) {
throw new BizException("验证码错误");
}
if (!emailVO.getCode().equals(redisService.get(USER_CODE_KEY + emailVO.getEmail()).toString())) {
throw new BizException("验证码错误!");
}
UserInfo userInfo = UserInfo.builder()
.id(UserUtil.getUserDetailsDTO().getUserInfoId())
.email(emailVO.getEmail())
.build();
userInfoMapper.updateById(userInfo);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void updateUserSubscribe(SubscribeVO subscribeVO) {
UserInfo temp = userInfoMapper.selectOne(new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getId, subscribeVO.getUserId()));
if (StringUtils.isEmpty(temp.getEmail())) {
throw new BizException("邮箱未绑定!");
}
UserInfo userInfo = UserInfo.builder()
.id(subscribeVO.getUserId())
.isSubscribe(subscribeVO.getIsSubscribe())
.build();
userInfoMapper.updateById(userInfo);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void updateUserRole(UserRoleVO userRoleVO) {
UserInfo userInfo = UserInfo.builder()
.id(userRoleVO.getUserInfoId())
.nickname(userRoleVO.getNickname())
.build();
userInfoMapper.updateById(userInfo);
userRoleService.remove(new LambdaQueryWrapper<UserRole>()
.eq(UserRole::getUserId, userRoleVO.getUserInfoId()));
List<UserRole> userRoleList = userRoleVO.getRoleIds().stream()
.map(roleId -> UserRole.builder()
.roleId(roleId)
.userId(userRoleVO.getUserInfoId())
.build())
.collect(Collectors.toList());
userRoleService.saveBatch(userRoleList);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void updateUserDisable(UserDisableVO userDisableVO) {
removeOnlineUser(userDisableVO.getId());
UserInfo userInfo = UserInfo.builder()
.id(userDisableVO.getId())
.isDisable(userDisableVO.getIsDisable())
.build();
userInfoMapper.updateById(userInfo);
}
@Override
public PageResultDTO<UserOnlineDTO> listOnlineUsers(ConditionVO conditionVO) {
Map<String, Object> userMaps = redisService.hGetAll("login_user");
Collection<Object> values = userMaps.values();
ArrayList<UserDetailsDTO> userDetailsDTOs = new ArrayList<>();
for (Object value : values) {
userDetailsDTOs.add((UserDetailsDTO) value);
}
List<UserOnlineDTO> userOnlineDTOs = BeanCopyUtil.copyList(userDetailsDTOs, UserOnlineDTO.class);
List<UserOnlineDTO> onlineUsers = userOnlineDTOs.stream()
.filter(item -> StringUtils.isBlank(conditionVO.getKeywords()) || item.getNickname().contains(conditionVO.getKeywords()))
.sorted(Comparator.comparing(UserOnlineDTO::getLastLoginTime).reversed())
.collect(Collectors.toList());
int fromIndex = getLimitCurrent().intValue();
int size = getSize().intValue();
int toIndex = onlineUsers.size() - fromIndex > size ? fromIndex + size : onlineUsers.size();
List<UserOnlineDTO> userOnlineList = onlineUsers.subList(fromIndex, toIndex);
return new PageResultDTO<>(userOnlineList, onlineUsers.size());
}
@Override
public void removeOnlineUser(Integer userInfoId) {
Integer userId = userAuthMapper.selectOne(new LambdaQueryWrapper<UserAuth>().eq(UserAuth::getUserInfoId, userInfoId)).getId();
tokenService.delLoginUser(userId);
}
@Override
public UserInfoDTO getUserInfoById(Integer id) {
UserInfo userInfo = userInfoMapper.selectById(id);
return BeanCopyUtil.copyObject(userInfo, UserInfoDTO.class);
}
}

@ -0,0 +1,12 @@
package com.aurora.service.impl;
import com.aurora.entity.UserRole;
import com.aurora.mapper.UserRoleMapper;
import com.aurora.service.UserRoleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> implements UserRoleService {
}
Loading…
Cancel
Save