diff --git a/src/main/java/com/gxwebsoft/cms/controller/ArticleCategoryController.java b/src/main/java/com/gxwebsoft/cms/controller/ArticleCategoryController.java index bde2a0c..a4c90c1 100644 --- a/src/main/java/com/gxwebsoft/cms/controller/ArticleCategoryController.java +++ b/src/main/java/com/gxwebsoft/cms/controller/ArticleCategoryController.java @@ -1,140 +1,144 @@ -package com.gxwebsoft.cms.controller; - -import com.gxwebsoft.common.core.web.BaseController; -import com.gxwebsoft.cms.service.ArticleCategoryService; -import com.gxwebsoft.cms.entity.ArticleCategory; -import com.gxwebsoft.cms.param.ArticleCategoryParam; -import com.gxwebsoft.common.core.web.ApiResult; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.BatchParam; -import com.gxwebsoft.common.core.annotation.OperationLog; -import com.gxwebsoft.common.system.entity.User; -import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import javax.annotation.Resource; -import java.util.List; - -/** - * 文章分类表控制器 - * - * @author 科技小王子 - * @since 2022-11-22 17:49:15 - */ -@Api(tags = "文章分类表管理") -@RestController -@RequestMapping("/api/cms/article-category") -public class ArticleCategoryController extends BaseController { - @Resource - private ArticleCategoryService articleCategoryService; - - @PreAuthorize("hasAuthority('cms:articleCategory:list')") - @OperationLog - @ApiOperation("分页查询文章分类表") - @GetMapping("/page") - public ApiResult> page(ArticleCategoryParam param) { - PageParam page = new PageParam<>(param); -// page.setDefaultOrder("create_time desc"); - page.setDefaultOrder("sort_number"); - return success(articleCategoryService.page(page, page.getWrapper())); - // 使用关联查询 - //return success(articleCategoryService.pageRel(param)); - } - - @OperationLog - @ApiOperation("查询全部文章分类表") - @GetMapping() - public ApiResult> list(ArticleCategoryParam param) { - PageParam page = new PageParam<>(param); -// page.setDefaultOrder("create_time desc"); - page.setDefaultOrder("sort_number"); - return success(articleCategoryService.list(page.getOrderWrapper())); - // 使用关联查询 - //return success(articleCategoryService.listRel(param)); - } - - @PreAuthorize("hasAuthority('cms:articleCategory:list')") - @OperationLog - @ApiOperation("根据id查询文章分类表") - @GetMapping("/{id}") - public ApiResult get(@PathVariable("id") Integer id) { - return success(articleCategoryService.getById(id)); - // 使用关联查询 - //return success(articleCategoryService.getByIdRel(id)); - } - - @PreAuthorize("hasAuthority('cms:articleCategory:save')") - @OperationLog - @ApiOperation("添加文章分类表") - @PostMapping() - public ApiResult save(@RequestBody ArticleCategory articleCategory) { - // 记录当前登录用户id、租户id - User loginUser = getLoginUser(); - if (loginUser != null) { - articleCategory.setUserId(loginUser.getUserId()); - } - if (articleCategoryService.save(articleCategory)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('cms:articleCategory:update')") - @OperationLog - @ApiOperation("修改文章分类表") - @PutMapping() - public ApiResult update(@RequestBody ArticleCategory articleCategory) { - if (articleCategoryService.updateById(articleCategory)) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('cms:articleCategory:remove')") - @OperationLog - @ApiOperation("删除文章分类表") - @DeleteMapping("/{id}") - public ApiResult remove(@PathVariable("id") Integer id) { - if (articleCategoryService.removeById(id)) { - return success("删除成功"); - } - return fail("删除失败"); - } - - @PreAuthorize("hasAuthority('cms:articleCategory:save')") - @OperationLog - @ApiOperation("批量添加文章分类表") - @PostMapping("/batch") - public ApiResult saveBatch(@RequestBody List list) { - if (articleCategoryService.saveBatch(list)) { - return success("添加成功"); - } - return fail("添加失败"); - } - - @PreAuthorize("hasAuthority('cms:articleCategory:update')") - @OperationLog - @ApiOperation("批量修改文章分类表") - @PutMapping("/batch") - public ApiResult removeBatch(@RequestBody BatchParam batchParam) { - if (batchParam.update(articleCategoryService, "category_id")) { - return success("修改成功"); - } - return fail("修改失败"); - } - - @PreAuthorize("hasAuthority('cms:articleCategory:remove')") - @OperationLog - @ApiOperation("批量删除文章分类表") - @DeleteMapping("/batch") - public ApiResult removeBatch(@RequestBody List ids) { - if (articleCategoryService.removeByIds(ids)) { - return success("删除成功"); - } - return fail("删除失败"); - } - -} +package com.gxwebsoft.cms.controller; + +import com.gxwebsoft.common.core.web.BaseController; +import com.gxwebsoft.cms.service.ArticleCategoryService; +import com.gxwebsoft.cms.entity.ArticleCategory; +import com.gxwebsoft.cms.param.ArticleCategoryParam; +import com.gxwebsoft.common.core.web.ApiResult; +import com.gxwebsoft.common.core.web.PageResult; +import com.gxwebsoft.common.core.web.PageParam; +import com.gxwebsoft.common.core.web.BatchParam; +import com.gxwebsoft.common.core.annotation.OperationLog; +import com.gxwebsoft.common.system.entity.User; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.List; + +/** + * 文章分类表控制器 + * + * @author 科技小王子 + * @since 2022-11-22 17:49:15 + */ +@Api(tags = "文章分类表管理") +@RestController +@RequestMapping("/api/cms/article-category") +public class ArticleCategoryController extends BaseController { + @Resource + private ArticleCategoryService articleCategoryService; + + @PreAuthorize("hasAuthority('cms:articleCategory:list')") + @OperationLog + @ApiOperation("分页查询文章分类表") + @GetMapping("/page") + public ApiResult> page(ArticleCategoryParam param) { + PageParam page = new PageParam<>(param); +// page.setDefaultOrder("create_time desc"); + page.setDefaultOrder("sort_number"); + return success(articleCategoryService.page(page, page.getWrapper())); + // 使用关联查询 + //return success(articleCategoryService.pageRel(param)); + } + + @OperationLog + @ApiOperation("查询全部文章分类表") + @GetMapping() + public ApiResult> list(ArticleCategoryParam param) { + PageParam page = new PageParam<>(param); +// page.setDefaultOrder("create_time desc"); + page.setDefaultOrder("sort_number"); + return success(articleCategoryService.list(page.getOrderWrapper())); + // 使用关联查询 + //return success(articleCategoryService.listRel(param)); + } + + @PreAuthorize("hasAuthority('cms:articleCategory:list')") + @OperationLog + @ApiOperation("根据id查询文章分类表") + @GetMapping("/{id}") + public ApiResult get(@PathVariable("id") Integer id) { + return success(articleCategoryService.getById(id)); + // 使用关联查询 + //return success(articleCategoryService.getByIdRel(id)); + } + + @PreAuthorize("hasAuthority('cms:articleCategory:save')") + @OperationLog + @ApiOperation("添加文章分类表") + @PostMapping() + public ApiResult save(@RequestBody ArticleCategory articleCategory) { + // 校验上级分类,避免产生自引用/成环的脏数据 + articleCategoryService.checkParentValid(articleCategory); + // 记录当前登录用户id、租户id + User loginUser = getLoginUser(); + if (loginUser != null) { + articleCategory.setUserId(loginUser.getUserId()); + } + if (articleCategoryService.save(articleCategory)) { + return success("添加成功"); + } + return fail("添加失败"); + } + + @PreAuthorize("hasAuthority('cms:articleCategory:update')") + @OperationLog + @ApiOperation("修改文章分类表") + @PutMapping() + public ApiResult update(@RequestBody ArticleCategory articleCategory) { + // 校验上级分类,避免把自己的下级设成上级而形成环 + articleCategoryService.checkParentValid(articleCategory); + if (articleCategoryService.updateById(articleCategory)) { + return success("修改成功"); + } + return fail("修改失败"); + } + + @PreAuthorize("hasAuthority('cms:articleCategory:remove')") + @OperationLog + @ApiOperation("删除文章分类表") + @DeleteMapping("/{id}") + public ApiResult remove(@PathVariable("id") Integer id) { + if (articleCategoryService.removeById(id)) { + return success("删除成功"); + } + return fail("删除失败"); + } + + @PreAuthorize("hasAuthority('cms:articleCategory:save')") + @OperationLog + @ApiOperation("批量添加文章分类表") + @PostMapping("/batch") + public ApiResult saveBatch(@RequestBody List list) { + if (articleCategoryService.saveBatch(list)) { + return success("添加成功"); + } + return fail("添加失败"); + } + + @PreAuthorize("hasAuthority('cms:articleCategory:update')") + @OperationLog + @ApiOperation("批量修改文章分类表") + @PutMapping("/batch") + public ApiResult removeBatch(@RequestBody BatchParam batchParam) { + if (batchParam.update(articleCategoryService, "category_id")) { + return success("修改成功"); + } + return fail("修改失败"); + } + + @PreAuthorize("hasAuthority('cms:articleCategory:remove')") + @OperationLog + @ApiOperation("批量删除文章分类表") + @DeleteMapping("/batch") + public ApiResult removeBatch(@RequestBody List ids) { + if (articleCategoryService.removeByIds(ids)) { + return success("删除成功"); + } + return fail("删除失败"); + } + +} diff --git a/src/main/java/com/gxwebsoft/cms/service/ArticleCategoryService.java b/src/main/java/com/gxwebsoft/cms/service/ArticleCategoryService.java index dbdd6ab..5e9493f 100644 --- a/src/main/java/com/gxwebsoft/cms/service/ArticleCategoryService.java +++ b/src/main/java/com/gxwebsoft/cms/service/ArticleCategoryService.java @@ -1,43 +1,55 @@ -package com.gxwebsoft.cms.service; - -import com.baomidou.mybatisplus.extension.service.IService; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.cms.entity.ArticleCategory; -import com.gxwebsoft.cms.param.ArticleCategoryParam; - -import java.util.List; - -/** - * 文章分类表Service - * - * @author 科技小王子 - * @since 2022-11-22 17:49:15 - */ -public interface ArticleCategoryService extends IService { - - /** - * 分页关联查询 - * - * @param param 查询参数 - * @return PageResult - */ - PageResult pageRel(ArticleCategoryParam param); - - /** - * 关联查询全部 - * - * @param param 查询参数 - * @return List - */ - List listRel(ArticleCategoryParam param); - - /** - * 根据id查询 - * - * @param categoryId 文章分类ID - * @return ArticleCategory - */ - ArticleCategory getByIdRel(Integer categoryId); - - List childList(Integer categoryId); -} +package com.gxwebsoft.cms.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.gxwebsoft.common.core.web.PageResult; +import com.gxwebsoft.cms.entity.ArticleCategory; +import com.gxwebsoft.cms.param.ArticleCategoryParam; + +import java.util.List; + +/** + * 文章分类表Service + * + * @author 科技小王子 + * @since 2022-11-22 17:49:15 + */ +public interface ArticleCategoryService extends IService { + + /** + * 分页关联查询 + * + * @param param 查询参数 + * @return PageResult + */ + PageResult pageRel(ArticleCategoryParam param); + + /** + * 关联查询全部 + * + * @param param 查询参数 + * @return List + */ + List listRel(ArticleCategoryParam param); + + /** + * 根据id查询 + * + * @param categoryId 文章分类ID + * @return ArticleCategory + */ + ArticleCategory getByIdRel(Integer categoryId); + + List childList(Integer categoryId); + + /** + * 校验上级分类是否合法 + *

+ * 分类是自关联结构,一旦出现「自己指向自己」或「祖先指向后代」的环, + * 前端构建分类树时会直接抛错,导致整个页面无法渲染,因此写入前必须拦截。 + *

+ * + * @param category 待保存的分类,修改时需带 categoryId + * @throws com.gxwebsoft.common.core.exception.BusinessException 校验不通过时抛出,message 为面向前端的中文提示 + */ + void checkParentValid(ArticleCategory category); +} diff --git a/src/main/java/com/gxwebsoft/cms/service/impl/ArticleCategoryServiceImpl.java b/src/main/java/com/gxwebsoft/cms/service/impl/ArticleCategoryServiceImpl.java index 7c78bf7..7bb33e5 100644 --- a/src/main/java/com/gxwebsoft/cms/service/impl/ArticleCategoryServiceImpl.java +++ b/src/main/java/com/gxwebsoft/cms/service/impl/ArticleCategoryServiceImpl.java @@ -1,56 +1,92 @@ -package com.gxwebsoft.cms.service.impl; - -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.gxwebsoft.cms.mapper.ArticleCategoryMapper; -import com.gxwebsoft.cms.service.ArticleCategoryService; -import com.gxwebsoft.cms.entity.ArticleCategory; -import com.gxwebsoft.cms.param.ArticleCategoryParam; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.PageResult; -import org.springframework.stereotype.Service; - -import java.util.List; - -/** - * 文章分类表Service实现 - * - * @author 科技小王子 - * @since 2022-11-22 17:49:15 - */ -@Service -public class ArticleCategoryServiceImpl extends ServiceImpl implements ArticleCategoryService { - - @Override - public PageResult pageRel(ArticleCategoryParam param) { - PageParam page = new PageParam<>(param); - //page.setDefaultOrder("create_time desc"); - List list = baseMapper.selectPageRel(page, param); - return new PageResult<>(list, page.getTotal()); - } - - @Override - public List listRel(ArticleCategoryParam param) { - List list = baseMapper.selectListRel(param); - // 排序 - PageParam page = new PageParam<>(); - //page.setDefaultOrder("create_time desc"); - return page.sortRecords(list); - } - - @Override - public ArticleCategory getByIdRel(Integer categoryId) { - ArticleCategoryParam param = new ArticleCategoryParam(); - param.setCategoryId(categoryId); - return param.getOne(baseMapper.selectListRel(param)); - } - - @Override - public List childList(Integer categoryId) { - return list( - new LambdaQueryWrapper() - .eq(ArticleCategory::getParentId, categoryId) - ); - } - -} +package com.gxwebsoft.cms.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.gxwebsoft.cms.mapper.ArticleCategoryMapper; +import com.gxwebsoft.cms.service.ArticleCategoryService; +import com.gxwebsoft.cms.entity.ArticleCategory; +import com.gxwebsoft.cms.param.ArticleCategoryParam; +import com.gxwebsoft.common.core.exception.BusinessException; +import com.gxwebsoft.common.core.web.PageParam; +import com.gxwebsoft.common.core.web.PageResult; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 文章分类表Service实现 + * + * @author 科技小王子 + * @since 2022-11-22 17:49:15 + */ +@Service +public class ArticleCategoryServiceImpl extends ServiceImpl implements ArticleCategoryService { + + @Override + public PageResult pageRel(ArticleCategoryParam param) { + PageParam page = new PageParam<>(param); + //page.setDefaultOrder("create_time desc"); + List list = baseMapper.selectPageRel(page, param); + return new PageResult<>(list, page.getTotal()); + } + + @Override + public List listRel(ArticleCategoryParam param) { + List list = baseMapper.selectListRel(param); + // 排序 + PageParam page = new PageParam<>(); + //page.setDefaultOrder("create_time desc"); + return page.sortRecords(list); + } + + @Override + public ArticleCategory getByIdRel(Integer categoryId) { + ArticleCategoryParam param = new ArticleCategoryParam(); + param.setCategoryId(categoryId); + return param.getOne(baseMapper.selectListRel(param)); + } + + @Override + public List childList(Integer categoryId) { + return list( + new LambdaQueryWrapper() + .eq(ArticleCategory::getParentId, categoryId) + ); + } + + @Override + public void checkParentValid(ArticleCategory category) { + if (category == null) { + return; + } + Integer categoryId = category.getCategoryId(); + Integer parentId = category.getParentId(); + // 顶级分类 + if (parentId == null || parentId == 0) { + return; + } + if (categoryId != null && categoryId.equals(parentId)) { + throw new BusinessException("上级分类不能选择自己"); + } + if (categoryId == null) { + // 新增时父级必然已存在,无需继续校验 + return; + } + // 修改场景:沿父级链向上回溯,父级链中不允许出现自己 + Integer cursor = parentId; + // 兜底防止历史脏数据造成死循环 + int depth = 0; + int maxDepth = 64; + while (cursor != null && cursor != 0 && depth++ < maxDepth) { + if (cursor.equals(categoryId)) { + throw new BusinessException("上级分类不能选择自己的下级分类"); + } + ArticleCategory parent = getById(cursor); + if (parent == null) { + break; + } + cursor = parent.getParentId(); + } + } + +}