mirror of
https://gitee.com/hhyykk/ipms-sjy.git
synced 2025-07-24 16:05:08 +08:00
[feat] 新增文件夹
This commit is contained in:
@ -17,4 +17,6 @@ public interface DictTypeConstants {
|
||||
|
||||
String OPERATE_TYPE = "infra_operate_type"; // 操作类型
|
||||
|
||||
String CATEGORY_TYPE = "infra_category_type"; // 目录分类类型
|
||||
|
||||
}
|
||||
|
@ -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, "该目录下存在文件");
|
||||
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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")
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
}
|
@ -52,4 +52,9 @@ public class FileDO extends BaseDO {
|
||||
*/
|
||||
private Integer size;
|
||||
|
||||
/**
|
||||
* 目录id
|
||||
*/
|
||||
private Long categoryId;
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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>
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user