[feat] 新增文件夹

This commit is contained in:
2024-07-11 21:28:49 +08:00
parent a25b771856
commit ed1c548c00
17 changed files with 768 additions and 3 deletions

View File

@ -17,4 +17,6 @@ public interface DictTypeConstants {
String OPERATE_TYPE = "infra_operate_type"; // 操作类型
String CATEGORY_TYPE = "infra_category_type"; // 目录分类类型
}

View File

@ -68,4 +68,13 @@ public interface ErrorCodeConstants {
ErrorCode DEMO03_GRADE_NOT_EXISTS = new ErrorCode(1_001_201_008, "学生班级不存在");
ErrorCode DEMO03_GRADE_EXISTS = new ErrorCode(1_001_201_009, "学生班级已存在");
// ========= 文件夹 1_001_008_000
ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(1_001_008_001, "文件目录不存在");
ErrorCode CATEGORY_EXITS_CHILDREN = new ErrorCode(1_001_008_002, "存在存在子文件目录,无法删除");
ErrorCode CATEGORY_PARENT_NOT_EXITS = new ErrorCode(1_001_008_003,"父级文件目录不存在");
ErrorCode CATEGORY_PARENT_ERROR = new ErrorCode(1_001_008_004, "不能设置自己为父文件目录");
ErrorCode CATEGORY_NAME_DUPLICATE = new ErrorCode(1_001_008_005, "已经存在该文件夹名称的文件目录");
ErrorCode CATEGORY_PARENT_IS_CHILD = new ErrorCode(1_001_008_006, "不能设置自己的子Category为父Category");
ErrorCode CATEGORY_EXISTS_FILES = new ErrorCode(1_001_008_007, "该目录下存在文件");
}

View File

@ -0,0 +1,94 @@
package cn.iocoder.yudao.module.infra.controller.admin.category;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.constraints.*;
import jakarta.validation.*;
import jakarta.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.infra.controller.admin.category.vo.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.category.CategoryDO;
import cn.iocoder.yudao.module.infra.service.category.CategoryService;
@Tag(name = "管理后台 - 文件目录")
@RestController
@RequestMapping("/infra/category")
@Validated
public class CategoryController {
@Resource
private CategoryService categoryService;
@PostMapping("/create")
@Operation(summary = "创建文件目录")
@PreAuthorize("@ss.hasPermission('infra:category:create')")
public CommonResult<Long> createCategory(@Valid @RequestBody CategorySaveReqVO createReqVO) {
return success(categoryService.createCategory(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新文件目录")
@PreAuthorize("@ss.hasPermission('infra:category:update')")
public CommonResult<Boolean> updateCategory(@Valid @RequestBody CategorySaveReqVO updateReqVO) {
categoryService.updateCategory(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除文件目录")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('infra:category:delete')")
public CommonResult<Boolean> deleteCategory(@RequestParam("id") Long id) {
categoryService.deleteCategory(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得文件目录")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('infra:category:query')")
public CommonResult<CategoryRespVO> getCategory(@RequestParam("id") Long id) {
CategoryDO category = categoryService.getCategory(id);
return success(BeanUtils.toBean(category, CategoryRespVO.class));
}
@GetMapping("/list")
@Operation(summary = "获得文件目录列表")
@PreAuthorize("@ss.hasPermission('infra:category:query')")
public CommonResult<List<CategoryRespVO>> getCategoryList(@Valid CategoryListReqVO listReqVO) {
List<CategoryDO> list = categoryService.getCategoryList(listReqVO);
return success(BeanUtils.toBean(list, CategoryRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出文件目录 Excel")
@PreAuthorize("@ss.hasPermission('infra:category:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportCategoryExcel(@Valid CategoryListReqVO listReqVO,
HttpServletResponse response) throws IOException {
List<CategoryDO> list = categoryService.getCategoryList(listReqVO);
// 导出 Excel
ExcelUtils.write(response, "文件目录.xls", "数据", CategoryRespVO.class,
BeanUtils.toBean(list, CategoryRespVO.class));
}
}

View File

@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.infra.controller.admin.category.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
@Schema(description = "管理后台 - 文件目录列表 Request VO")
@Data
public class CategoryListReqVO {
@Schema(description = "编号")
private String code;
@Schema(description = "文件夹名称", example = "赵六")
private String name;
}

View File

@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.infra.controller.admin.category.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import com.alibaba.excel.annotation.*;
@Schema(description = "管理后台 - 文件目录 Response VO")
@Data
@ExcelIgnoreUnannotated
public class CategoryRespVO {
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("id")
private Long id;
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("编号")
private String code;
@Schema(description = "文件夹名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
@ExcelProperty("文件夹名称")
private String name;
@Schema(description = "父id", example = "3502")
@ExcelProperty("父id")
private Long parentId;
@Schema(description = "描述", example = "你说的对")
@ExcelProperty("描述")
private String description;
}

View File

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.infra.controller.admin.category.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import jakarta.validation.constraints.*;
@Schema(description = "管理后台 - 文件目录新增/修改 Request VO")
@Data
public class CategorySaveReqVO {
@Schema(description = "主键", example = "19810")
private Long id;
@Schema(description = "编号")
private String code;
@Schema(description = "文件夹名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
@NotEmpty(message = "文件夹名称不能为空")
private String name;
@Schema(description = "父id", example = "3502")
private Long parentId;
@Schema(description = "描述", example = "你说的对")
private String description;
}

View File

@ -50,7 +50,9 @@ public class FileController {
public CommonResult<FileRespVO> uploadFileEx(FileUploadReqVO uploadReqVO) throws Exception {
MultipartFile file = uploadReqVO.getFile();
String path = uploadReqVO.getPath();
return success(fileService.createFileEx(file.getOriginalFilename(), path, IoUtil.readBytes(file.getInputStream())));
Long categoryId = uploadReqVO.getCategoryId();
String categoryPath = uploadReqVO.getCategoryPath();
return success(fileService.createFileEx(file.getOriginalFilename(), path, categoryId, categoryPath, IoUtil.readBytes(file.getInputStream())));
}
@GetMapping("/presigned-url")

View File

@ -17,4 +17,10 @@ public class FileUploadReqVO {
@Schema(description = "文件附件", example = "yudaoyuanma.png")
private String path;
@Schema(description = "文件目录id")
private Long categoryId;
@Schema(description = "文件夹路径")
private String categoryPath;
}

View File

@ -0,0 +1,53 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.category;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 文件目录 DO
*
* @author 管理员
*/
@TableName("infra_category")
@KeySequence("infra_category_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CategoryDO extends BaseDO {
public static final Long PARENT_ID_ROOT = 0L;
/**
* 主键
*/
@TableId
private Long id;
/**
* 编号
*/
private String code;
/**
* 文件夹名称
*/
private String name;
/**
* 父id
*/
private Long parentId;
/**
* 描述
*/
private String description;
/**
* 排序
*/
private Integer sort;
}

View File

@ -52,4 +52,9 @@ public class FileDO extends BaseDO {
*/
private Integer size;
/**
* 目录id
*/
private Long categoryId;
}

View File

@ -0,0 +1,42 @@
package cn.iocoder.yudao.module.infra.dal.mysql.category;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.infra.dal.dataobject.category.CategoryDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.infra.controller.admin.category.vo.*;
/**
* 文件目录 Mapper
*
* @author 管理员
*/
@Mapper
public interface CategoryMapper extends BaseMapperX<CategoryDO> {
default List<CategoryDO> selectList(CategoryListReqVO reqVO) {
return selectList(new LambdaQueryWrapperX<CategoryDO>()
.eqIfPresent(CategoryDO::getCode, reqVO.getCode())
.likeIfPresent(CategoryDO::getName, reqVO.getName())
.orderByDesc(CategoryDO::getId));
}
default CategoryDO selectByParentIdAndName(Long parentId, String name) {
return selectOne(CategoryDO::getParentId, parentId, CategoryDO::getName, name);
}
default CategoryDO selectByParentIdAndCode(Long parentId, String code) {
return selectOne(CategoryDO::getParentId, parentId, CategoryDO::getCode, code);
}
default Long selectCountByParentId(Long parentId) {
return selectCount(CategoryDO::getParentId, parentId);
}
default CategoryDO getCategoryByCode(String code) {
return selectOne(CategoryDO::getCode, code);
}
}

View File

@ -0,0 +1,74 @@
package cn.iocoder.yudao.module.infra.service.category;
import java.util.*;
import jakarta.validation.*;
import cn.iocoder.yudao.module.infra.controller.admin.category.vo.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.category.CategoryDO;
/**
* 文件目录 Service 接口
*
* @author 管理员
*/
public interface CategoryService {
/**
* 创建文件目录
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createCategory(@Valid CategorySaveReqVO createReqVO);
/**
* 更新文件目录
*
* @param updateReqVO 更新信息
*/
void updateCategory(@Valid CategorySaveReqVO updateReqVO);
/**
* 删除文件目录
*
* @param id 编号
*/
void deleteCategory(Long id);
/**
* 获得文件目录
*
* @param id 编号
* @return 文件目录
*/
CategoryDO getCategory(Long id);
/**
* 获得文件目录列表
*
* @param listReqVO 查询条件
* @return 文件目录列表
*/
List<CategoryDO> getCategoryList(CategoryListReqVO listReqVO);
/**
* 初始化根目录
*
* @return 根目录
*/
CategoryDO initRootCategory();
/**
* 根据 code 获取文件目录
* @param code code
* @return 文件目录
*/
CategoryDO getCategoryByCode(String code);
/**
* 根据 path 创建文件目录
* @param path 路径 eg 设计/桩基/钢筋
* @param isCodeBased 是否基于编码创建, true-基于编码创建名称从字典获取false-基于名称创建 编码都为空
* @return 最后一级目录的id
*/
Long createCategoryWithPath(String path, boolean isCodeBased);
}

View File

@ -0,0 +1,224 @@
package cn.iocoder.yudao.module.infra.service.category;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.infra.controller.admin.category.vo.CategoryListReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.category.vo.CategorySaveReqVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.category.CategoryDO;
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;
import cn.iocoder.yudao.module.infra.dal.mysql.category.CategoryMapper;
import cn.iocoder.yudao.module.infra.service.file.FileService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
/**
* 文件目录 Service 实现类
*
* @author 管理员
*/
@Service
@Validated
public class CategoryServiceImpl implements CategoryService {
private static final String ROOT_CATEGORY_CODE = "root";
private static final String ROOT_CATEGORY_NAME = "根目录";
@Resource
private CategoryMapper categoryMapper;
@Resource
private FileService fileService;
@Override
public Long createCategory(CategorySaveReqVO createReqVO) {
// 校验父id的有效性
validateParentCategory(null, createReqVO.getParentId());
// 校验文件夹名称的唯一性
validateCategoryNameUnique(null, createReqVO.getParentId(), createReqVO.getName());
if (createReqVO.getParentId() == null) {
createReqVO.setParentId(0L);
}
// 插入
CategoryDO category = BeanUtils.toBean(createReqVO, CategoryDO.class);
categoryMapper.insert(category);
// 返回
return category.getId();
}
@Override
public void updateCategory(CategorySaveReqVO updateReqVO) {
// 校验存在
validateCategoryExists(updateReqVO.getId());
// 校验父id的有效性
validateParentCategory(updateReqVO.getId(), updateReqVO.getParentId());
// 校验文件夹名称的唯一性
validateCategoryNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName());
// 更新
CategoryDO updateObj = BeanUtils.toBean(updateReqVO, CategoryDO.class);
categoryMapper.updateById(updateObj);
}
@Override
public void deleteCategory(Long id) {
// 校验存在
validateCategoryExists(id);
// 校验是否有子文件目录
if (categoryMapper.selectCountByParentId(id) > 0) {
throw exception(CATEGORY_EXITS_CHILDREN);
}
// 验证文件夹下是否有问题
validateCategoryWithFiles(id);
// 删除
categoryMapper.deleteById(id);
}
private void validateCategoryExists(Long id) {
if (categoryMapper.selectById(id) == null) {
throw exception(CATEGORY_NOT_EXISTS);
}
}
private void validateCategoryWithFiles(Long id) {
List<FileDO> fileList = fileService.getFileListWithCategoryId(id);
if (!fileList.isEmpty()) {
throw exception(CATEGORY_EXISTS_FILES);
}
}
private void validateParentCategory(Long id, Long parentId) {
if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) {
return;
}
// 1. 不能设置自己为父文件目录
if (Objects.equals(id, parentId)) {
throw exception(CATEGORY_PARENT_ERROR);
}
// 2. 父文件目录不存在
CategoryDO parentCategory = categoryMapper.selectById(parentId);
if (parentCategory == null) {
throw exception(CATEGORY_PARENT_NOT_EXITS);
}
// 3. 递归校验父文件目录,如果父文件目录是自己的子文件目录,则报错,避免形成环路
if (id == null) { // id 为空,说明新增,不需要考虑环路
return;
}
for (int i = 0; i < Short.MAX_VALUE; i++) {
// 3.1 校验环路
parentId = parentCategory.getParentId();
if (Objects.equals(id, parentId)) {
throw exception(CATEGORY_PARENT_IS_CHILD);
}
// 3.2 继续递归下一级父文件目录
if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) {
break;
}
parentCategory = categoryMapper.selectById(parentId);
if (parentCategory == null) {
break;
}
}
}
private void validateCategoryNameUnique(Long id, Long parentId, String name) {
CategoryDO category = categoryMapper.selectByParentIdAndName(parentId, name);
if (category == null) {
return;
}
// 如果 id 为空,说明不用比较是否为相同 id 的文件目录
if (id == null) {
throw exception(CATEGORY_NAME_DUPLICATE);
}
if (!Objects.equals(category.getId(), id)) {
throw exception(CATEGORY_NAME_DUPLICATE);
}
}
@Override
public CategoryDO getCategory(Long id) {
return categoryMapper.selectById(id);
}
@Override
public List<CategoryDO> getCategoryList(CategoryListReqVO listReqVO) {
return categoryMapper.selectList(listReqVO);
}
@Override
public CategoryDO initRootCategory() {
CategoryDO categoryDO = new CategoryDO();
categoryDO.setCode(ROOT_CATEGORY_CODE);
categoryDO.setName(ROOT_CATEGORY_NAME);
categoryDO.setDescription(ROOT_CATEGORY_NAME);
categoryMapper.insert(categoryDO);
return getCategoryByCode(ROOT_CATEGORY_CODE);
}
@Override
public CategoryDO getCategoryByCode(String code) {
return categoryMapper.getCategoryByCode(code);
}
@Override
public Long createCategoryWithPath(String path, boolean isCodeBased) {
// 检查路径有效性
if (path == null || path.trim().isEmpty() || path.equals("/")) {
throw new IllegalArgumentException("无效的分类路径");
}
// hyk: 如果要使用一个作为根目录,释放以下代码
/* Long parentId = null;
// 校验根路径是否存在,不存在则创建根
CategoryDO rootCategory = getCategoryByCode(ROOT_CATEGORY_CODE);
if (rootCategory == null) {
rootCategory = initRootCategory();
parentId = rootCategory.getId();
}*/
// 默认没有根路径第一级parent_id 为0
Long parentId = 0L;
// 解析路径
String[] categoryElements = path.split("/");
// 根据路径创建文件夹,并返回最后一个文件夹的id
for (String categoryElement : categoryElements) {
// 获取父级目录
parentId = createCategoryIfNeeded(parentId, categoryElement, isCodeBased);
}
return parentId;
}
private Long createCategoryIfNeeded(Long parentId, String element, boolean isCodeBased) {
CategoryDO category;
if (isCodeBased) {
category = categoryMapper.selectByParentIdAndCode(parentId, element);
} else {
category = categoryMapper.selectByParentIdAndName(parentId, element);
}
if (category == null) {
CategoryDO newCategory = new CategoryDO();
if (isCodeBased) {
newCategory.setCode(element);
} else {
newCategory.setName(element);
}
newCategory.setParentId(parentId);
categoryMapper.insert(newCategory);
return newCategory.getId();
} else {
return category.getId();
}
}
}

View File

@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePresigned
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileRespVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;
import java.util.List;
/**
* 文件 Service 接口
*
@ -68,8 +70,12 @@ public interface FileService {
* 创建文件ex
* @param name 文件名
* @param path 路径
* @param categoryId 分类id
* @param categoryPath 分类路径
* @param content 文件内容
* @return 文件对象
*/
FileRespVO createFileEx(String name, String path, byte[] content);
FileRespVO createFileEx(String name, String path, Long categoryId ,String categoryPath ,byte[] content);
List<FileDO> getFileListWithCategoryId(Long categoryId);
}

View File

@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.io.FileUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileRespVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.category.CategoryDO;
import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient;
import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.FilePresignedUrlRespDTO;
import cn.iocoder.yudao.module.infra.framework.file.core.utils.FileTypeUtils;
@ -14,10 +15,13 @@ import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO
import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;
import cn.iocoder.yudao.module.infra.dal.mysql.file.FileMapper;
import cn.iocoder.yudao.module.infra.service.category.CategoryService;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import org.springframework.stereotype.Service;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_NOT_EXISTS;
@ -32,6 +36,9 @@ public class FileServiceImpl implements FileService {
@Resource
private FileConfigService fileConfigService;
@Resource
private CategoryService categoryService;
@Resource
private FileMapper fileMapper;
@ -116,7 +123,18 @@ public class FileServiceImpl implements FileService {
@Override
@SneakyThrows
public FileRespVO createFileEx(String name, String path, byte[] content) {
public FileRespVO createFileEx(String name, String path, Long categoryId, String categoryPah ,byte[] content) {
Long caId = null;
// 处理目录
if (categoryId != null) {
CategoryDO category = categoryService.getCategory(categoryId);
if (category != null) {
caId = category.getId();
}
} else if (categoryPah != null) {
caId = categoryService.createCategoryWithPath(categoryPah, false);
}
// 计算默认的 path 名
String type = FileTypeUtils.getMineType(content, name);
if (StrUtil.isEmpty(path)) {
@ -139,6 +157,7 @@ public class FileServiceImpl implements FileService {
file.setPath(path);
file.setUrl(url);
file.setType(type);
file.setCategoryId(caId);
file.setSize(content.length);
fileMapper.insert(file);
FileRespVO vo = new FileRespVO();
@ -147,4 +166,9 @@ public class FileServiceImpl implements FileService {
return vo;
}
@Override
public List<FileDO> getFileListWithCategoryId(Long categoryId) {
return fileMapper.selectList(FileDO::getCategoryId, categoryId);
}
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.infra.dal.mysql.category.CategoryMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

View File

@ -0,0 +1,133 @@
package cn.iocoder.yudao.module.infra.service.category;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import jakarta.annotation.Resource;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.infra.controller.admin.category.vo.*;
import cn.iocoder.yudao.module.infra.dal.dataobject.category.CategoryDO;
import cn.iocoder.yudao.module.infra.dal.mysql.category.CategoryMapper;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Import;
import java.util.*;
import java.time.LocalDateTime;
import static cn.hutool.core.util.RandomUtil.*;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
/**
* {@link CategoryServiceImpl} 的单元测试类
*
* @author 管理员
*/
@Import(CategoryServiceImpl.class)
public class CategoryServiceImplTest extends BaseDbUnitTest {
@Resource
private CategoryServiceImpl categoryService;
@Resource
private CategoryMapper categoryMapper;
@Test
public void testCreateCategory_success() {
// 准备参数
CategorySaveReqVO createReqVO = randomPojo(CategorySaveReqVO.class).setId(null);
// 调用
Long categoryId = categoryService.createCategory(createReqVO);
// 断言
assertNotNull(categoryId);
// 校验记录的属性是否正确
CategoryDO category = categoryMapper.selectById(categoryId);
assertPojoEquals(createReqVO, category, "id");
}
@Test
public void testUpdateCategory_success() {
// mock 数据
CategoryDO dbCategory = randomPojo(CategoryDO.class);
categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据
// 准备参数
CategorySaveReqVO updateReqVO = randomPojo(CategorySaveReqVO.class, o -> {
o.setId(dbCategory.getId()); // 设置更新的 ID
});
// 调用
categoryService.updateCategory(updateReqVO);
// 校验是否更新正确
CategoryDO category = categoryMapper.selectById(updateReqVO.getId()); // 获取最新的
assertPojoEquals(updateReqVO, category);
}
@Test
public void testUpdateCategory_notExists() {
// 准备参数
CategorySaveReqVO updateReqVO = randomPojo(CategorySaveReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> categoryService.updateCategory(updateReqVO), CATEGORY_NOT_EXISTS);
}
@Test
public void testDeleteCategory_success() {
// mock 数据
CategoryDO dbCategory = randomPojo(CategoryDO.class);
categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbCategory.getId();
// 调用
categoryService.deleteCategory(id);
// 校验数据不存在了
assertNull(categoryMapper.selectById(id));
}
@Test
public void testDeleteCategory_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> categoryService.deleteCategory(id), CATEGORY_NOT_EXISTS);
}
@Test
@Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
public void testGetCategoryList() {
// mock 数据
CategoryDO dbCategory = randomPojo(CategoryDO.class, o -> { // 等会查询到
o.setCode(null);
o.setName(null);
});
categoryMapper.insert(dbCategory);
// 测试 code 不匹配
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCode(null)));
// 测试 name 不匹配
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName(null)));
// 准备参数
CategoryListReqVO reqVO = new CategoryListReqVO();
reqVO.setCode(null);
reqVO.setName(null);
// 调用
List<CategoryDO> list = categoryService.getCategoryList(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbCategory, list.get(0));
}
}