fix(cms): 分类写入前校验上级分类,拦截自引用与成环
分类是自关联结构,一旦出现「自己指向自己」或「祖先指向后代」的环,前端构建分类树时
会直接抛错,导致整个页面无法渲染(报 data error: {categoryId: x, parentId: x})。
- ArticleCategoryService 新增 checkParentValid:顶级分类放行;修改时沿父级链向上回溯,
父级链中不允许出现自己;带回溯深度上限兜底,避免历史脏数据造成死循环。
- Controller 的保存与更新入口都调用该校验,不通过时抛 BusinessException(中文提示)。
注:本提交同时包含这三个文件的整体重新格式化(忽略空白后实际逻辑改动为 52 行插入)。
This commit is contained in:
@@ -71,6 +71,8 @@ public class ArticleCategoryController extends BaseController {
|
|||||||
@ApiOperation("添加文章分类表")
|
@ApiOperation("添加文章分类表")
|
||||||
@PostMapping()
|
@PostMapping()
|
||||||
public ApiResult<?> save(@RequestBody ArticleCategory articleCategory) {
|
public ApiResult<?> save(@RequestBody ArticleCategory articleCategory) {
|
||||||
|
// 校验上级分类,避免产生自引用/成环的脏数据
|
||||||
|
articleCategoryService.checkParentValid(articleCategory);
|
||||||
// 记录当前登录用户id、租户id
|
// 记录当前登录用户id、租户id
|
||||||
User loginUser = getLoginUser();
|
User loginUser = getLoginUser();
|
||||||
if (loginUser != null) {
|
if (loginUser != null) {
|
||||||
@@ -87,6 +89,8 @@ public class ArticleCategoryController extends BaseController {
|
|||||||
@ApiOperation("修改文章分类表")
|
@ApiOperation("修改文章分类表")
|
||||||
@PutMapping()
|
@PutMapping()
|
||||||
public ApiResult<?> update(@RequestBody ArticleCategory articleCategory) {
|
public ApiResult<?> update(@RequestBody ArticleCategory articleCategory) {
|
||||||
|
// 校验上级分类,避免把自己的下级设成上级而形成环
|
||||||
|
articleCategoryService.checkParentValid(articleCategory);
|
||||||
if (articleCategoryService.updateById(articleCategory)) {
|
if (articleCategoryService.updateById(articleCategory)) {
|
||||||
return success("修改成功");
|
return success("修改成功");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,4 +40,16 @@ public interface ArticleCategoryService extends IService<ArticleCategory> {
|
|||||||
ArticleCategory getByIdRel(Integer categoryId);
|
ArticleCategory getByIdRel(Integer categoryId);
|
||||||
|
|
||||||
List<ArticleCategory> childList(Integer categoryId);
|
List<ArticleCategory> childList(Integer categoryId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验上级分类是否合法
|
||||||
|
* <p>
|
||||||
|
* 分类是自关联结构,一旦出现「自己指向自己」或「祖先指向后代」的环,
|
||||||
|
* 前端构建分类树时会直接抛错,导致整个页面无法渲染,因此写入前必须拦截。
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param category 待保存的分类,修改时需带 categoryId
|
||||||
|
* @throws com.gxwebsoft.common.core.exception.BusinessException 校验不通过时抛出,message 为面向前端的中文提示
|
||||||
|
*/
|
||||||
|
void checkParentValid(ArticleCategory category);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.gxwebsoft.cms.mapper.ArticleCategoryMapper;
|
|||||||
import com.gxwebsoft.cms.service.ArticleCategoryService;
|
import com.gxwebsoft.cms.service.ArticleCategoryService;
|
||||||
import com.gxwebsoft.cms.entity.ArticleCategory;
|
import com.gxwebsoft.cms.entity.ArticleCategory;
|
||||||
import com.gxwebsoft.cms.param.ArticleCategoryParam;
|
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.PageParam;
|
||||||
import com.gxwebsoft.common.core.web.PageResult;
|
import com.gxwebsoft.common.core.web.PageResult;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -53,4 +54,39 @@ public class ArticleCategoryServiceImpl extends ServiceImpl<ArticleCategoryMappe
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user