优化
This commit is contained in:
@ -1517,6 +1517,7 @@ public class SubConstructionUserServiceImpl extends ServiceImpl<SubConstructionU
|
||||
lqw.eq(SubConstructionUser::getUserRole, "0");
|
||||
if (req.getProjectId() == null) {
|
||||
lqw.isNull(SubConstructionUser::getProjectId);
|
||||
lqw.apply("exists (select 1 from sys_user where user_id = sys_user_id and dept_id is null)");
|
||||
} else {
|
||||
lqw.eq(SubConstructionUser::getProjectId, req.getProjectId());
|
||||
}
|
||||
|
||||
@ -448,9 +448,9 @@ public class DesDrawingServiceImpl extends ServiceImpl<DesDrawingMapper, DesDraw
|
||||
try {
|
||||
ByteArrayOutputStream baos = PdfBoxQrCodeGenerator.addQRCodeToPDFOnAllPages(ossVo.getUrl(), bytes,isChangeFile);
|
||||
|
||||
try (FileOutputStream fileOut = new FileOutputStream("C:\\Users\\YuanJie\\Desktop\\test1.pdf")) {
|
||||
baos.writeTo(fileOut);
|
||||
}
|
||||
// try (FileOutputStream fileOut = new FileOutputStream("C:\\Users\\YuanJie\\Desktop\\test1.pdf")) {
|
||||
// baos.writeTo(fileOut);
|
||||
// }
|
||||
|
||||
FileNameMap fileNameMap = URLConnection.getFileNameMap();
|
||||
String originalName = ossVo.getOriginalName();
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package org.dromara.materials.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
@ -486,7 +487,10 @@ public class MatMaterialsServiceImpl extends ServiceImpl<MatMaterialsMapper, Mat
|
||||
if (receive != null) {
|
||||
BeanUtils.copyProperties(receive, vo);
|
||||
}
|
||||
List<MatMaterialsNumberVo> numberVos = validMaterials.stream().map(m -> {
|
||||
|
||||
List<MatMaterialsNumberVo> numberVos = new ArrayList<>();
|
||||
|
||||
for (MatMaterials m : validMaterials) {
|
||||
MatMaterialsNumberVo numberVo = new MatMaterialsNumberVo();
|
||||
BeanUtils.copyProperties(m, numberVo);
|
||||
MatMaterialsInventory inv = inventoryMap.get(m.getId());
|
||||
@ -501,8 +505,10 @@ public class MatMaterialsServiceImpl extends ServiceImpl<MatMaterialsMapper, Mat
|
||||
return outVo;
|
||||
}).toList());
|
||||
}
|
||||
return numberVo;
|
||||
}).toList();
|
||||
if(CollectionUtil.isNotEmpty(numberVo.getOutList())){
|
||||
numberVos.add(numberVo);
|
||||
}
|
||||
}
|
||||
vo.setMaterials(numberVos);
|
||||
resultList.add(vo);
|
||||
}
|
||||
|
||||
@ -105,4 +105,10 @@ public class BusWorkWageController extends BaseController {
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(busWorkWageService.deleteWithValidByIds(List.of(ids), true));
|
||||
}
|
||||
|
||||
@GetMapping("/allList")
|
||||
public R<List<BusWorkWageVo>> list(BusWorkWageQueryReq req) {
|
||||
return R.ok(busWorkWageService.queryList(req));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1255,6 +1255,21 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
|
||||
vo.setAttendance(full);
|
||||
vo.setHalfAttendance(half);
|
||||
vo.setAbsenteeism(absent);
|
||||
|
||||
String todayStr = currentDate.format(DateTimeFormatter.ISO_LOCAL_DATE); // 结果:"2024-10-10"
|
||||
|
||||
int count = 0;
|
||||
if (CollectionUtil.isNotEmpty(allUserIds)) {
|
||||
count = (int) leaveService.count(Wrappers.<BusLeave>lambdaQuery()
|
||||
.eq(BusLeave::getProjectId, dto.getProjectId())
|
||||
.in(BusLeave::getUserId, allUserIds)
|
||||
.eq(dto.getTeamId() != null, BusLeave::getTeamId, dto.getTeamId())
|
||||
// 关键:给日期字符串加单引号,避免 SQL 语法错误
|
||||
.apply("DATE(start_time) <= {0}", todayStr)
|
||||
.apply("DATE(end_time) >= {0}", todayStr)
|
||||
);
|
||||
}
|
||||
vo.setLeave(count);
|
||||
result.add(vo);
|
||||
currentDate = currentDate.plusDays(1);
|
||||
}
|
||||
@ -1287,12 +1302,17 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
|
||||
String todayStr = now.format(DateTimeFormatter.ISO_LOCAL_DATE); // 结果:"2024-10-10"
|
||||
|
||||
// 2. 拼接 SQL 时添加单引号,确保语法正确
|
||||
int count = (int)leaveService.count(Wrappers.<BusLeave>lambdaQuery()
|
||||
.eq(BusLeave::getTimeType, "2")
|
||||
int count = 0;
|
||||
if (CollectionUtil.isNotEmpty(allUserIds)) {
|
||||
count = (int) leaveService.count(Wrappers.<BusLeave>lambdaQuery()
|
||||
.eq(BusLeave::getProjectId, dto.getProjectId())
|
||||
.in(BusLeave::getUserId, allUserIds)
|
||||
.eq(dto.getTeamId() != null, BusLeave::getTeamId, allUserIds)
|
||||
// 关键:给日期字符串加单引号,避免 SQL 语法错误
|
||||
.apply("DATE(start_time) <= {0}", todayStr)
|
||||
.apply("DATE(end_time) >= {0}", todayStr)
|
||||
);
|
||||
}
|
||||
|
||||
List<BusAttendance> list;
|
||||
if (CollectionUtil.isEmpty(allUserIds)) {
|
||||
@ -1333,6 +1353,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
|
||||
vo.setAbsenteeism(absent);
|
||||
//计算考勤率
|
||||
vo.setAllUserNum(allUserIds.size() - count);
|
||||
vo.setLeave(count);
|
||||
vo.setClockNum(full + half);
|
||||
vo.setAttendanceRate(BigDecimalUtil.toPercentage(new BigDecimal(full + half),
|
||||
new BigDecimal(vo.getAllUserNum())));
|
||||
|
||||
@ -0,0 +1,162 @@
|
||||
package org.dromara.quality.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.dromara.quality.domain.QltFileFolder;
|
||||
|
||||
import org.dromara.quality.domain.dto.fileFolder.QltFileFolderCreateDTO;
|
||||
import org.dromara.quality.domain.dto.fileFolder.QltFileFolderMoveDTO;
|
||||
import org.dromara.quality.domain.dto.fileFolder.QltListQueryDto;
|
||||
import org.dromara.quality.domain.vo.QltFileFolderVo;
|
||||
|
||||
import org.dromara.quality.domain.vo.fileFolder.QltFileFolderTreeVO;
|
||||
import org.dromara.quality.service.IQltFileFolderService;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.common.core.domain.R;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* 质量会议纪要
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-10-16
|
||||
*/
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/quality/fileFolder")
|
||||
public class QltFileFolderController extends BaseController {
|
||||
|
||||
private final IQltFileFolderService qltFileFolderService;
|
||||
|
||||
/**
|
||||
* 查询会议纪要列表
|
||||
*/
|
||||
@GetMapping("/list-all")
|
||||
public R<List<QltFileFolderVo>> listAll(QltListQueryDto dto) {
|
||||
return R.ok(qltFileFolderService.listAll(dto));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询指定目录的树形结构(一次性加载所有层级)
|
||||
* @return 树形结构列表
|
||||
*/
|
||||
@GetMapping("/tree-all")
|
||||
public R<List<QltFileFolderTreeVO>> treeAll(QltListQueryDto dto) {
|
||||
|
||||
// 1. 查询所有子项(利用path前缀匹配,一次性加载所有层级)
|
||||
List<QltFileFolder> allItems = qltFileFolderService.list(new LambdaQueryWrapper<QltFileFolder>()
|
||||
.eq(QltFileFolder::getProjectId, dto.getProjectId())
|
||||
.like(QltFileFolder::getPath, "," + dto.getParentId() + ",") // 包含父ID的所有子项
|
||||
.eq(dto.getType()!=null,QltFileFolder::getType, dto.getType())
|
||||
.orderByDesc(QltFileFolder::getId)
|
||||
.orderByAsc(QltFileFolder::getSort)
|
||||
);
|
||||
|
||||
// 2. 构建树形结构
|
||||
List<QltFileFolderTreeVO> treeVOS = buildTree(allItems, dto.getParentId());
|
||||
return R.ok(treeVOS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归构建树形结构
|
||||
*/
|
||||
private List<QltFileFolderTreeVO> buildTree(List<QltFileFolder> allItems, Long parentId) {
|
||||
return allItems.stream()
|
||||
.filter(item -> parentId.equals(item.getParentId()))
|
||||
.map(item -> {
|
||||
QltFileFolderTreeVO vo = new QltFileFolderTreeVO();
|
||||
// 复制基本属性(可使用BeanUtils.copyProperties)
|
||||
vo.setId(item.getId());
|
||||
vo.setName(item.getName());
|
||||
vo.setParentId(item.getParentId());
|
||||
vo.setType(item.getType());
|
||||
vo.setLevel(item.getLevel());
|
||||
vo.setSort(item.getSort());
|
||||
vo.setFileSuffix(item.getFileSuffix());
|
||||
|
||||
// 递归查询子节点
|
||||
vo.setChildren(buildTree(allItems, item.getId()));
|
||||
return vo;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建文件或文件夹
|
||||
*/
|
||||
@PostMapping("/create")
|
||||
public R<QltFileFolder> create(@RequestBody QltFileFolderCreateDTO dto) {
|
||||
return R.ok(qltFileFolderService.createFileOrFolder(dto));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件或文件夹(级联删除子项)
|
||||
*/
|
||||
@DeleteMapping("/{ids}")
|
||||
@Transactional
|
||||
public R<Boolean> delete(@PathVariable List<Long> ids) {
|
||||
for (Long id : ids) {
|
||||
qltFileFolderService.deleteFileOrFolder(id);
|
||||
}
|
||||
return R.ok(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动文件或文件夹到指定目录
|
||||
*/
|
||||
@PostMapping("/move")
|
||||
@Transactional
|
||||
public R<Boolean> move(@RequestBody QltFileFolderMoveDTO dto) {
|
||||
return R.ok(qltFileFolderService.moveFileOrFolder(dto.getId(), dto.getTargetParentId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传ZIP文件并自动解压到指定目录(支持多层级)
|
||||
*/
|
||||
@PostMapping("/uploadAndUnzip")
|
||||
public R<String> uploadAndUnzip(
|
||||
@RequestParam("file") MultipartFile file,
|
||||
@RequestParam("parentId") Long parentId,
|
||||
@RequestParam("projectId") Long projectId) {
|
||||
|
||||
try {
|
||||
// 实际应用中,userId和deptId通常从登录信息中获取
|
||||
String result = qltFileFolderService.uploadAndUnzip(file, parentId, projectId);
|
||||
return R.ok(result);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return R.fail(e.getMessage());
|
||||
} catch (Exception e) {
|
||||
return R.fail("文件处理失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/unzip")
|
||||
public R<String> unzip(@RequestParam("id") Long id,
|
||||
@RequestParam("parentId") Long parentId) {
|
||||
return R.ok(qltFileFolderService.unzip(id, parentId));
|
||||
}
|
||||
|
||||
@PutMapping("/reName")
|
||||
public R<Boolean> reName(@RequestParam("id") Long id,
|
||||
@RequestParam("name") String name) {
|
||||
return R.ok(qltFileFolderService.reName(id,name));
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/download/{id}")
|
||||
public void download(@PathVariable Long id, HttpServletResponse response) throws IOException {
|
||||
qltFileFolderService.download(id, response);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
package org.dromara.quality.domain;
|
||||
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 质量会议纪要对象 qlt_file_folder
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-10-16
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("qlt_file_folder")
|
||||
public class QltFileFolder extends BaseEntity {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 项目id
|
||||
*/
|
||||
private Long projectId;
|
||||
|
||||
/**
|
||||
* 名称(文件名或文件夹名)
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 父级ID(0表示根目录)
|
||||
*/
|
||||
private Long parentId;
|
||||
|
||||
/**
|
||||
* 类型(1-文件夹,2-文件)
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 层级(根目录为1,子级+1)
|
||||
*/
|
||||
private Integer level;
|
||||
|
||||
/**
|
||||
* 同层级排序号
|
||||
*/
|
||||
private Integer sort;
|
||||
|
||||
/**
|
||||
* 层级路径(如:1,2,3 表示ID为1→2→3的层级)
|
||||
*/
|
||||
private String path;
|
||||
|
||||
/**
|
||||
* 文件id
|
||||
*/
|
||||
private Long fileId;
|
||||
|
||||
/**
|
||||
* 文件后缀
|
||||
*/
|
||||
private String fileSuffix;
|
||||
|
||||
/**
|
||||
* 存储路径
|
||||
*/
|
||||
private String filePath;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 删除标志(0代表存在 1代表删除)
|
||||
*/
|
||||
@TableLogic
|
||||
private String delFlag;
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,90 @@
|
||||
package org.dromara.quality.domain.bo;
|
||||
|
||||
import org.dromara.quality.domain.QltFileFolder;
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import jakarta.validation.constraints.*;
|
||||
|
||||
/**
|
||||
* 质量会议纪要业务对象 qlt_file_folder
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-10-16
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@AutoMapper(target = QltFileFolder.class, reverseConvertGenerate = false)
|
||||
public class QltFileFolderBo extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@NotNull(message = "主键ID不能为空", groups = { EditGroup.class })
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 项目id
|
||||
*/
|
||||
@NotNull(message = "项目id不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Long projectId;
|
||||
|
||||
/**
|
||||
* 名称(文件名或文件夹名)
|
||||
*/
|
||||
@NotBlank(message = "名称(文件名或文件夹名)不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 父级ID(0表示根目录)
|
||||
*/
|
||||
private Long parentId;
|
||||
|
||||
/**
|
||||
* 类型(1-文件夹,2-文件)
|
||||
*/
|
||||
@NotNull(message = "类型(1-文件夹,2-文件)不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 层级(根目录为1,子级+1)
|
||||
*/
|
||||
@NotNull(message = "层级(根目录为1,子级+1)不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private Integer level;
|
||||
|
||||
/**
|
||||
* 同层级排序号
|
||||
*/
|
||||
private Integer sort;
|
||||
|
||||
/**
|
||||
* 层级路径(如:1,2,3 表示ID为1→2→3的层级)
|
||||
*/
|
||||
@NotBlank(message = "层级路径(如:1,2,3 表示ID为1→2→3的层级)不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
private String path;
|
||||
|
||||
/**
|
||||
* 文件id
|
||||
*/
|
||||
private Long fileId;
|
||||
|
||||
/**
|
||||
* 文件后缀
|
||||
*/
|
||||
private String fileSuffix;
|
||||
|
||||
/**
|
||||
* 存储路径
|
||||
*/
|
||||
private String filePath;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
package org.dromara.quality.domain.dto.fileFolder;
|
||||
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
@Data
|
||||
public class QltFileFolderCreateDTO {
|
||||
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
@NotBlank(message = "名称不能为空")
|
||||
private String name;
|
||||
|
||||
private Long projectId;
|
||||
|
||||
/**
|
||||
* 父级ID(0表示根目录)
|
||||
*/
|
||||
private Long parentId = 0L;
|
||||
|
||||
/**
|
||||
* 文件类型
|
||||
*/
|
||||
@NotNull(message = "类型不能为空")
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 排序号
|
||||
*/
|
||||
private Integer sort=0;
|
||||
|
||||
|
||||
/**
|
||||
* 文件id
|
||||
*/
|
||||
private Long fileId;
|
||||
|
||||
/**
|
||||
* 文件后缀
|
||||
*/
|
||||
private String fileSuffix;
|
||||
|
||||
/**
|
||||
* 文件存储路径(仅文件有效)
|
||||
*/
|
||||
private String filePath;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
package org.dromara.quality.domain.dto.fileFolder;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class QltFileFolderMoveDTO {
|
||||
|
||||
@NotNull(message = "文件/文件夹ID不能为空")
|
||||
private Long id;
|
||||
|
||||
@NotNull(message = "目标父目录ID不能为空")
|
||||
private Long targetParentId;
|
||||
}
|
||||
@ -1,11 +1,11 @@
|
||||
package org.dromara.safety.domain.dto.fileFolder;
|
||||
package org.dromara.quality.domain.dto.fileFolder;
|
||||
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ListQueryDto {
|
||||
public class QltListQueryDto {
|
||||
/**
|
||||
* 父目录ID,默认0(根目录)
|
||||
*/
|
||||
@ -0,0 +1,109 @@
|
||||
package org.dromara.quality.domain.vo;
|
||||
|
||||
import org.dromara.quality.domain.QltFileFolder;
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import org.dromara.common.excel.annotation.ExcelDictFormat;
|
||||
import org.dromara.common.excel.convert.ExcelDictConvert;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 质量会议纪要视图对象 qlt_file_folder
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-10-16
|
||||
*/
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
@AutoMapper(target = QltFileFolder.class)
|
||||
public class QltFileFolderVo implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@ExcelProperty(value = "主键ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 项目id
|
||||
*/
|
||||
@ExcelProperty(value = "项目id")
|
||||
private Long projectId;
|
||||
|
||||
/**
|
||||
* 名称(文件名或文件夹名)
|
||||
*/
|
||||
@ExcelProperty(value = "名称", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(readConverterExp = "文=件名或文件夹名")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 父级ID(0表示根目录)
|
||||
*/
|
||||
@ExcelProperty(value = "父级ID", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(readConverterExp = "0=表示根目录")
|
||||
private Long parentId;
|
||||
|
||||
/**
|
||||
* 类型(1-文件夹,2-文件)
|
||||
*/
|
||||
@ExcelProperty(value = "类型", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(readConverterExp = "1=-文件夹,2-文件")
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 层级(根目录为1,子级+1)
|
||||
*/
|
||||
@ExcelProperty(value = "层级", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(readConverterExp = "根=目录为1,子级+1")
|
||||
private Integer level;
|
||||
|
||||
/**
|
||||
* 同层级排序号
|
||||
*/
|
||||
@ExcelProperty(value = "同层级排序号")
|
||||
private Integer sort;
|
||||
|
||||
/**
|
||||
* 层级路径(如:1,2,3 表示ID为1→2→3的层级)
|
||||
*/
|
||||
@ExcelProperty(value = "层级路径", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(readConverterExp = "如=:1,2,3,表=示ID为1→2→3的层级")
|
||||
private String path;
|
||||
|
||||
/**
|
||||
* 文件id
|
||||
*/
|
||||
@ExcelProperty(value = "文件id")
|
||||
private Long fileId;
|
||||
|
||||
/**
|
||||
* 文件后缀
|
||||
*/
|
||||
@ExcelProperty(value = "文件后缀")
|
||||
private String fileSuffix;
|
||||
|
||||
/**
|
||||
* 存储路径
|
||||
*/
|
||||
@ExcelProperty(value = "存储路径")
|
||||
private String filePath;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@ExcelProperty(value = "")
|
||||
private String remark;
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
package org.dromara.quality.domain.vo.fileFolder;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class QltFileFolderTreeVO {
|
||||
|
||||
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 名称(文件名或文件夹名)
|
||||
*/
|
||||
private String name;
|
||||
|
||||
|
||||
/**
|
||||
* 父级ID(0表示根目录)
|
||||
*/
|
||||
private Long parentId;
|
||||
|
||||
/**
|
||||
* 1-文件夹,2-文件
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 层级(根目录为1,子级+1)
|
||||
*/
|
||||
private Integer level;
|
||||
|
||||
/**
|
||||
* 同层级排序号
|
||||
*/
|
||||
private Integer sort;
|
||||
|
||||
/**
|
||||
* 文件id
|
||||
*/
|
||||
private Long fileId;
|
||||
|
||||
/**
|
||||
* 文件后缀
|
||||
*/
|
||||
private String fileSuffix;
|
||||
|
||||
/**
|
||||
* 子级
|
||||
*/
|
||||
private List<QltFileFolderTreeVO> children;
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package org.dromara.quality.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.dromara.quality.domain.QltFileFolder;
|
||||
import org.dromara.quality.domain.vo.QltFileFolderVo;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
|
||||
/**
|
||||
* 质量会议纪要Mapper接口
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-10-16
|
||||
*/
|
||||
public interface QltFileFolderMapper extends BaseMapperPlus<QltFileFolder, QltFileFolderVo> {
|
||||
|
||||
|
||||
void batchUpdateChildPaths(
|
||||
@Param("oldPath") String oldPath,
|
||||
@Param("newPath") String newPath,
|
||||
@Param("levelDiff") int levelDiff,
|
||||
@Param("parentId") Long parentId);
|
||||
}
|
||||
@ -0,0 +1,115 @@
|
||||
package org.dromara.quality.service;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.dromara.quality.domain.dto.fileFolder.QltFileFolderCreateDTO;
|
||||
import org.dromara.quality.domain.dto.fileFolder.QltListQueryDto;
|
||||
import org.dromara.quality.domain.vo.QltFileFolderVo;
|
||||
import org.dromara.quality.domain.bo.QltFileFolderBo;
|
||||
import org.dromara.quality.domain.QltFileFolder;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 质量会议纪要Service接口
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-10-16
|
||||
*/
|
||||
public interface IQltFileFolderService extends IService<QltFileFolder>{
|
||||
|
||||
/**
|
||||
* 查询质量会议纪要
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 质量会议纪要
|
||||
*/
|
||||
QltFileFolderVo queryById(Long id);
|
||||
|
||||
/**
|
||||
* 分页查询质量会议纪要列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 质量会议纪要分页列表
|
||||
*/
|
||||
TableDataInfo<QltFileFolderVo> queryPageList(QltFileFolderBo bo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询符合条件的质量会议纪要列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 质量会议纪要列表
|
||||
*/
|
||||
List<QltFileFolderVo> queryList(QltFileFolderBo bo);
|
||||
|
||||
/**
|
||||
* 新增质量会议纪要
|
||||
*
|
||||
* @param bo 质量会议纪要
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
Boolean insertByBo(QltFileFolderBo bo);
|
||||
|
||||
/**
|
||||
* 修改质量会议纪要
|
||||
*
|
||||
* @param bo 质量会议纪要
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
Boolean updateByBo(QltFileFolderBo bo);
|
||||
|
||||
/**
|
||||
* 校验并批量删除质量会议纪要信息
|
||||
*
|
||||
* @param ids 待删除的主键集合
|
||||
* @param isValid 是否进行有效性校验
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
List<QltFileFolderVo> listAll(QltListQueryDto dto);
|
||||
|
||||
/**
|
||||
* 创建文件或文件夹
|
||||
*/
|
||||
QltFileFolder createFileOrFolder(QltFileFolderCreateDTO dto);
|
||||
|
||||
/**
|
||||
* 删除文件或文件夹(级联删除子项)
|
||||
*/
|
||||
boolean deleteFileOrFolder(Long id);
|
||||
|
||||
/**
|
||||
* 移动文件或文件夹到指定目录
|
||||
*/
|
||||
boolean moveFileOrFolder(Long id, Long targetParentId);
|
||||
|
||||
/**
|
||||
* 上传并解压ZIP文件
|
||||
*/
|
||||
String uploadAndUnzip(MultipartFile file, Long parentId, Long projectId) throws Exception;
|
||||
|
||||
/**
|
||||
* 获取指定目录的树形结构(一次性加载所有层级)
|
||||
* @return 树形结构列表
|
||||
*/
|
||||
String unzip(Long id, Long parentId);
|
||||
|
||||
/**
|
||||
* 重命名文件或文件夹
|
||||
*/
|
||||
Boolean reName(Long id,String name);
|
||||
|
||||
/**
|
||||
* 下载
|
||||
*/
|
||||
void download(@PathVariable Long id, HttpServletResponse response) throws IOException;
|
||||
}
|
||||
@ -0,0 +1,585 @@
|
||||
package org.dromara.quality.service.impl;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.core.utils.file.FileUtils;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.oss.core.OssClient;
|
||||
import org.dromara.common.oss.factory.OssFactory;
|
||||
import org.dromara.quality.domain.dto.fileFolder.QltFileFolderCreateDTO;
|
||||
import org.dromara.quality.domain.dto.fileFolder.QltListQueryDto;
|
||||
import org.dromara.safety.domain.HseFileFolder;
|
||||
import org.dromara.safety.domain.dto.fileFolder.FileFolderCreateDTO;
|
||||
import org.dromara.safety.domain.vo.HseFileFolderVo;
|
||||
import org.dromara.system.domain.vo.SysOssVo;
|
||||
import org.dromara.system.service.ISysOssService;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.dromara.quality.domain.bo.QltFileFolderBo;
|
||||
import org.dromara.quality.domain.vo.QltFileFolderVo;
|
||||
import org.dromara.quality.domain.QltFileFolder;
|
||||
import org.dromara.quality.mapper.QltFileFolderMapper;
|
||||
import org.dromara.quality.service.IQltFileFolderService;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
/**
|
||||
* 质量会议纪要Service业务层处理
|
||||
*
|
||||
* @author Lion Li
|
||||
* @date 2025-10-16
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class QltFileFolderServiceImpl extends ServiceImpl<QltFileFolderMapper, QltFileFolder> implements IQltFileFolderService {
|
||||
|
||||
private final QltFileFolderMapper baseMapper;
|
||||
|
||||
private final ISysOssService ossService;
|
||||
|
||||
/**
|
||||
* 查询质量会议纪要
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 质量会议纪要
|
||||
*/
|
||||
@Override
|
||||
public QltFileFolderVo queryById(Long id){
|
||||
return baseMapper.selectVoById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询质量会议纪要列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 质量会议纪要分页列表
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<QltFileFolderVo> queryPageList(QltFileFolderBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<QltFileFolder> lqw = buildQueryWrapper(bo);
|
||||
Page<QltFileFolderVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询符合条件的质量会议纪要列表
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 质量会议纪要列表
|
||||
*/
|
||||
@Override
|
||||
public List<QltFileFolderVo> queryList(QltFileFolderBo bo) {
|
||||
LambdaQueryWrapper<QltFileFolder> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<QltFileFolder> buildQueryWrapper(QltFileFolderBo bo) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
LambdaQueryWrapper<QltFileFolder> lqw = Wrappers.lambdaQuery();
|
||||
lqw.orderByDesc(QltFileFolder::getId);
|
||||
lqw.eq(bo.getProjectId() != null, QltFileFolder::getProjectId, bo.getProjectId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getName()), QltFileFolder::getName, bo.getName());
|
||||
lqw.eq(bo.getParentId() != null, QltFileFolder::getParentId, bo.getParentId());
|
||||
lqw.eq(bo.getType() != null, QltFileFolder::getType, bo.getType());
|
||||
lqw.eq(bo.getLevel() != null, QltFileFolder::getLevel, bo.getLevel());
|
||||
lqw.eq(bo.getSort() != null, QltFileFolder::getSort, bo.getSort());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getPath()), QltFileFolder::getPath, bo.getPath());
|
||||
lqw.eq(bo.getFileId() != null, QltFileFolder::getFileId, bo.getFileId());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getFileSuffix()), QltFileFolder::getFileSuffix, bo.getFileSuffix());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getFilePath()), QltFileFolder::getFilePath, bo.getFilePath());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增质量会议纪要
|
||||
*
|
||||
* @param bo 质量会议纪要
|
||||
* @return 是否新增成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean insertByBo(QltFileFolderBo bo) {
|
||||
QltFileFolder add = MapstructUtils.convert(bo, QltFileFolder.class);
|
||||
validEntityBeforeSave(add);
|
||||
boolean flag = baseMapper.insert(add) > 0;
|
||||
if (flag) {
|
||||
bo.setId(add.getId());
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改质量会议纪要
|
||||
*
|
||||
* @param bo 质量会议纪要
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean updateByBo(QltFileFolderBo bo) {
|
||||
QltFileFolder update = MapstructUtils.convert(bo, QltFileFolder.class);
|
||||
validEntityBeforeSave(update);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存前的数据校验
|
||||
*/
|
||||
private void validEntityBeforeSave(QltFileFolder entity){
|
||||
//TODO 做一些数据校验,如唯一约束
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验并批量删除质量会议纪要信息
|
||||
*
|
||||
* @param ids 待删除的主键集合
|
||||
* @param isValid 是否进行有效性校验
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if(isValid){
|
||||
//TODO 做一些业务上的校验,判断是否需要校验
|
||||
}
|
||||
return baseMapper.deleteByIds(ids) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<QltFileFolderVo> listAll(QltListQueryDto dto) {
|
||||
|
||||
LambdaQueryWrapper<QltFileFolder> lqw = Wrappers.lambdaQuery();
|
||||
lqw.orderByDesc(QltFileFolder::getId);
|
||||
lqw.orderByAsc(QltFileFolder::getSort);
|
||||
|
||||
lqw.eq(QltFileFolder::getProjectId, dto.getProjectId());
|
||||
lqw.eq(QltFileFolder::getParentId, dto.getParentId());
|
||||
lqw.eq(dto.getType() != null, QltFileFolder::getType, dto.getType());
|
||||
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QltFileFolder createFileOrFolder(QltFileFolderCreateDTO dto) {
|
||||
// 1. 验证父目录是否存在
|
||||
if (dto.getParentId() != 0) {
|
||||
QltFileFolder parent = getById(dto.getParentId());
|
||||
if (parent == null || parent.getType() != 1) { // 父级必须是文件夹
|
||||
throw new ServiceException("父目录不存在或不是文件夹");
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 构建新文件/文件夹对象
|
||||
QltFileFolder entity = new QltFileFolder();
|
||||
entity.setProjectId(dto.getProjectId());
|
||||
entity.setName(dto.getName());
|
||||
entity.setParentId(dto.getParentId());
|
||||
entity.setType(dto.getType());
|
||||
entity.setSort(dto.getSort() != null ? dto.getSort() : 0);
|
||||
entity.setRemark(dto.getRemark());
|
||||
entity.setFileId(dto.getFileId());
|
||||
|
||||
|
||||
// 3. 设置层级和路径
|
||||
if (dto.getParentId() == 0) { // 根目录
|
||||
entity.setLevel(1);
|
||||
entity.setPath("," + entity.getId() + ","); // ID会在插入后自动生成,这里先占位
|
||||
} else { // 子目录/文件
|
||||
QltFileFolder parent = getById(dto.getParentId());
|
||||
entity.setLevel(parent.getLevel() + 1);
|
||||
entity.setPath(parent.getPath() + entity.getId() + ","); // ID会在插入后更新
|
||||
}
|
||||
|
||||
// 4. 处理文件特有属性
|
||||
if (dto.getType() == 2) { // 如果是文件
|
||||
entity.setFileSuffix(dto.getFileSuffix());
|
||||
entity.setFilePath(dto.getFilePath());
|
||||
}
|
||||
|
||||
// 5. 保存并更新路径(因为ID是自增的,需要先保存再更新路径)
|
||||
save(entity);
|
||||
|
||||
// 6. 修正路径中的ID
|
||||
if (dto.getParentId() == 0) {
|
||||
entity.setPath("," + entity.getId() + ",");
|
||||
} else {
|
||||
QltFileFolder parent = getById(dto.getParentId());
|
||||
entity.setPath(parent.getPath() + entity.getId() + ",");
|
||||
}
|
||||
updateById(entity);
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean deleteFileOrFolder(Long id) {
|
||||
QltFileFolder entity = getById(id);
|
||||
if (entity == null) {
|
||||
throw new ServiceException("文件/文件夹不存在");
|
||||
}
|
||||
|
||||
// 1. 删除自身及所有子项(通过path匹配)
|
||||
LambdaQueryWrapper<QltFileFolder> queryWrapper = Wrappers.<QltFileFolder>lambdaQuery()
|
||||
.like(QltFileFolder::getPath, "," + id + ",");
|
||||
|
||||
return remove(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean moveFileOrFolder(Long id, Long targetParentId) {
|
||||
// 1. 验证源文件和目标目录是否存在
|
||||
QltFileFolder source = getById(id);
|
||||
if (source == null) {
|
||||
throw new ServiceException("源文件/文件夹不存在");
|
||||
}
|
||||
|
||||
QltFileFolder targetParent = getById(targetParentId);
|
||||
if (targetParent == null || targetParent.getType() != 1) {
|
||||
throw new ServiceException("目标目录不存在或不是文件夹");
|
||||
}
|
||||
|
||||
// 2. 防止循环移动(不能移动到自身或子目录下)
|
||||
if (targetParent.getPath().contains(source.getPath())) {
|
||||
throw new ServiceException("不能将文件夹移动到其子目录下");
|
||||
}
|
||||
|
||||
// 3. 获取原路径和新路径的前缀
|
||||
String oldPath = source.getPath();
|
||||
String oldParentPath = source.getParentId() == 0 ? ",0," : getById(source.getParentId()).getPath();
|
||||
String newParentPath = targetParent.getPath();
|
||||
int oldLevel = source.getLevel();
|
||||
|
||||
// 4. 计算新路径和新层级
|
||||
String newPath = newParentPath + id + ",";
|
||||
int newLevel = targetParent.getLevel() + 1;
|
||||
|
||||
// 5. 更新自身信息
|
||||
source.setParentId(targetParentId);
|
||||
source.setLevel(newLevel);
|
||||
source.setPath(newPath);
|
||||
updateById(source);
|
||||
|
||||
// 6. 更新所有子项的路径和层级
|
||||
if (source.getType() == 1) { // 如果是文件夹,需要更新其子项
|
||||
// 计算路径替换的前后缀
|
||||
String pathReplaceFrom = oldPath;
|
||||
String pathReplaceTo = newPath;
|
||||
int levelDiff = newLevel - oldLevel; // 层级变化量
|
||||
|
||||
// 批量更新子项
|
||||
baseMapper.batchUpdateChildPaths(pathReplaceFrom, pathReplaceTo, levelDiff, id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public String uploadAndUnzip(MultipartFile file, Long parentId, Long projectId) throws Exception {
|
||||
// 1. 参数验证
|
||||
if (file == null || file.isEmpty()) {
|
||||
throw new ServiceException("上传文件不能为空");
|
||||
}
|
||||
|
||||
// 2. 验证文件类型是否为ZIP
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
if (originalFilename == null || !originalFilename.toLowerCase().endsWith(".zip")) {
|
||||
throw new ServiceException("只支持上传ZIP格式的文件");
|
||||
}
|
||||
|
||||
// 3. 验证父目录是否存在且为文件夹
|
||||
QltFileFolder parentFolder = null;
|
||||
if (parentId != null && parentId != 0) {
|
||||
parentFolder = getById(parentId);
|
||||
if (parentFolder == null || parentFolder.getType() != 1) {
|
||||
throw new ServiceException("指定的父目录不存在或不是文件夹");
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 创建临时文件用于解压
|
||||
File tempZipFile = File.createTempFile("upload_", ".zip");
|
||||
try {
|
||||
// 5. 保存上传的文件到临时位置
|
||||
file.transferTo(tempZipFile);
|
||||
|
||||
// 6. 解压ZIP文件
|
||||
String unzipResult = doUnzipFile(tempZipFile, parentId, projectId, parentFolder);
|
||||
|
||||
return unzipResult;
|
||||
} finally {
|
||||
// 7. 清理临时文件
|
||||
if (tempZipFile.exists()) {
|
||||
tempZipFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 实际执行解压操作的方法
|
||||
*
|
||||
* @param zipFile ZIP文件
|
||||
* @param parentId 父目录ID
|
||||
* @param projectId 项目ID
|
||||
* @param parentFolder 父目录对象
|
||||
* @return 解压结果信息
|
||||
* @throws Exception 解压过程中可能出现的异常
|
||||
*/
|
||||
private String doUnzipFile(File zipFile, Long parentId, Long projectId, QltFileFolder parentFolder) throws Exception {
|
||||
// 用于记录解压的文件数量
|
||||
int fileCount = 0;
|
||||
int folderCount = 0;
|
||||
|
||||
// 获取父级路径信息
|
||||
String parentPath = parentId == null || parentId == 0 ? "," : parentFolder.getPath();
|
||||
int parentLevel = parentId == null || parentId == 0 ? 0 : parentFolder.getLevel();
|
||||
|
||||
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile), Charset.forName("GBK"))) {
|
||||
ZipEntry entry;
|
||||
while ((entry = zis.getNextEntry()) != null) {
|
||||
// 跳过目录项(目录会根据文件路径自动创建)
|
||||
if (entry.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String entryName = entry.getName();
|
||||
// 处理中文文件名乱码问题
|
||||
if (!Charset.forName("GBK").newEncoder().canEncode(entryName)) {
|
||||
// 尝试用UTF-8编码解析
|
||||
entryName = new String(entryName.getBytes("ISO-8859-1"), "UTF-8");
|
||||
}
|
||||
|
||||
// 分离路径和文件名
|
||||
String[] pathParts = entryName.split("/");
|
||||
String fileName = pathParts[pathParts.length - 1];
|
||||
|
||||
// 构造文件在系统中的虚拟路径
|
||||
Long currentParentId = parentId == null ? 0L : parentId;
|
||||
String currentPath = parentPath;
|
||||
|
||||
// 如果ZIP中有目录结构,则需要逐级创建文件夹
|
||||
for (int i = 0; i < pathParts.length - 1; i++) {
|
||||
String folderName = pathParts[i];
|
||||
if (folderName.isEmpty()) continue;
|
||||
|
||||
// 查找是否已存在同名文件夹
|
||||
LambdaQueryWrapper<QltFileFolder> folderQuery = Wrappers.lambdaQuery();
|
||||
folderQuery.eq(QltFileFolder::getProjectId, projectId)
|
||||
.eq(QltFileFolder::getParentId, currentParentId)
|
||||
.eq(QltFileFolder::getName, folderName)
|
||||
.eq(QltFileFolder::getType, 1); // 文件夹类型
|
||||
|
||||
QltFileFolder existingFolder = getOne(folderQuery);
|
||||
|
||||
if (existingFolder == null) {
|
||||
// 创建新的文件夹
|
||||
QltFileFolder newFolder = new QltFileFolder();
|
||||
newFolder.setProjectId(projectId);
|
||||
newFolder.setName(folderName);
|
||||
newFolder.setParentId(currentParentId);
|
||||
newFolder.setType(1); // 文件夹
|
||||
newFolder.setLevel(parentLevel + i + 1);
|
||||
newFolder.setSort(0);
|
||||
newFolder.setPath(currentPath);
|
||||
|
||||
// 先保存获取ID
|
||||
save(newFolder);
|
||||
|
||||
// 更新路径,格式为 ,parentId,childId,
|
||||
newFolder.setPath(currentPath + newFolder.getId() + ",");
|
||||
updateById(newFolder);
|
||||
|
||||
currentParentId = newFolder.getId();
|
||||
currentPath = newFolder.getPath();
|
||||
folderCount++;
|
||||
} else {
|
||||
currentParentId = existingFolder.getId();
|
||||
currentPath = existingFolder.getPath();
|
||||
}
|
||||
}
|
||||
|
||||
// 创建文件记录
|
||||
QltFileFolder fileRecord = new QltFileFolder();
|
||||
fileRecord.setProjectId(projectId);
|
||||
fileRecord.setName(fileName);
|
||||
fileRecord.setParentId(currentParentId);
|
||||
fileRecord.setType(2); // 文件类型
|
||||
fileRecord.setLevel(parentLevel + pathParts.length);
|
||||
// 文件路径格式为 ,parentId,fileId,
|
||||
fileRecord.setPath(currentPath + fileRecord.getId() + ",");
|
||||
|
||||
// 设置文件扩展名
|
||||
int dotIndex = fileName.lastIndexOf(".");
|
||||
if (dotIndex > 0 && dotIndex < fileName.length() - 1) {
|
||||
fileRecord.setFileSuffix(fileName.substring(dotIndex + 1));
|
||||
}
|
||||
// 上传文件内容到OSS并设置文件路径
|
||||
// 上传文件内容到OSS并设置文件路径
|
||||
try {
|
||||
// 从ZIP流中读取文件内容
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[32768];
|
||||
int bytesRead;
|
||||
while ((bytesRead = zis.read(buffer)) != -1) {
|
||||
baos.write(buffer, 0, bytesRead);
|
||||
}
|
||||
byte[] fileContent = baos.toByteArray();
|
||||
|
||||
// 检查文件长度,如果为0则跳过保存
|
||||
if (fileContent.length > 0) {
|
||||
// 直接使用字节流上传到OSS,避免创建临时文件
|
||||
try (ByteArrayInputStream bais = new ByteArrayInputStream(fileContent)) {
|
||||
String contentType = FileUtils.getMimeType(fileName);
|
||||
SysOssVo ossVo = ossService.upload(bais, fileName, contentType, (long) fileContent.length);
|
||||
|
||||
// 设置真实的文件路径
|
||||
fileRecord.setFilePath(ossVo.getUrl());
|
||||
fileRecord.setFileId(ossVo.getOssId());
|
||||
}
|
||||
|
||||
save(fileRecord);
|
||||
|
||||
// 更新文件的路径(因为ID是自增的,需要先保存再更新路径)
|
||||
fileRecord.setPath(currentPath + fileRecord.getId() + ",");
|
||||
updateById(fileRecord);
|
||||
|
||||
fileCount++;
|
||||
}
|
||||
// 如果文件长度为0,则跳过保存操作
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException("文件上传失败: " + e.getMessage());
|
||||
}
|
||||
|
||||
zis.closeEntry();
|
||||
}
|
||||
}
|
||||
|
||||
return String.format("解压完成,共创建 %d 个文件夹,%d 个文件", folderCount, fileCount);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public String unzip(Long id, Long parentId) {
|
||||
// 1. 参数验证
|
||||
if (id == null) {
|
||||
throw new ServiceException("文件ID不能为空");
|
||||
}
|
||||
|
||||
// 2. 获取要解压的ZIP文件
|
||||
QltFileFolder zipFile = getById(id);
|
||||
if (zipFile == null) {
|
||||
throw new ServiceException("指定的文件不存在");
|
||||
}
|
||||
|
||||
// 3. 验证是否为ZIP文件
|
||||
if (!"zip".equalsIgnoreCase(zipFile.getFileSuffix())) {
|
||||
throw new ServiceException("只能解压ZIP格式的文件");
|
||||
}
|
||||
|
||||
// 4. 验证文件路径是否存在
|
||||
if (StringUtils.isBlank(zipFile.getFilePath())) {
|
||||
throw new ServiceException("文件存储路径不存在");
|
||||
}
|
||||
|
||||
// 5. 验证目标目录是否存在且为文件夹
|
||||
QltFileFolder parentFolder = null;
|
||||
if (parentId != null && parentId != 0) {
|
||||
parentFolder = getById(parentId);
|
||||
if (parentFolder == null || parentFolder.getType() != 1) {
|
||||
throw new ServiceException("指定的目标目录不存在或不是文件夹");
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 下载ZIP文件到临时位置
|
||||
try {
|
||||
// 从URL下载文件
|
||||
Long fileId = zipFile.getFileId();
|
||||
File tempZipFile = downloadFileToTemp(fileId);
|
||||
|
||||
if (tempZipFile == null || !tempZipFile.exists()) {
|
||||
throw new ServiceException("下载文件失败");
|
||||
}
|
||||
|
||||
try {
|
||||
// 7. 解压ZIP文件
|
||||
String unzipResult = doUnzipFile(tempZipFile, parentId, zipFile.getProjectId(), parentFolder);
|
||||
return unzipResult;
|
||||
} finally {
|
||||
// 8. 清理临时文件
|
||||
if (tempZipFile.exists()) {
|
||||
tempZipFile.delete();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException("解压文件失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从URL下载文件到临时位置
|
||||
*
|
||||
* @param fileId 文件id
|
||||
* @return 临时文件
|
||||
* @throws Exception 下载过程中的异常
|
||||
*/
|
||||
private File downloadFileToTemp(Long fileId) throws Exception {
|
||||
SysOssVo sysOss = ossService.getById(fileId);
|
||||
if (ObjectUtil.isNull(sysOss)) {
|
||||
throw new ServiceException("文件数据不存在!");
|
||||
}
|
||||
|
||||
OssClient storage = OssFactory.instance(sysOss.getService());
|
||||
// 直接使用OssClient返回的临时文件路径
|
||||
Path tempPath = storage.fileDownload(sysOss.getUrl());
|
||||
|
||||
return tempPath.toFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean reName(Long id, String name) {
|
||||
QltFileFolder fileFolder = getById(id);
|
||||
if (fileFolder != null) {
|
||||
fileFolder.setName(name);
|
||||
return updateById(fileFolder);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void download(Long id, HttpServletResponse response) throws IOException {
|
||||
|
||||
QltFileFolder fileFolder = getById(id);
|
||||
|
||||
if(fileFolder == null){
|
||||
throw new ServiceException("文件数据不存在!");
|
||||
}
|
||||
Long ossId = fileFolder.getFileId();
|
||||
SysOssVo sysOss = ossService.getById(ossId);
|
||||
if (ObjectUtil.isNull(sysOss)) {
|
||||
throw new ServiceException("文件数据不存在!");
|
||||
}
|
||||
FileUtils.setAttachmentResponseHeader(response, fileFolder.getName()+"."+fileFolder.getFileSuffix());
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
|
||||
OssClient storage = OssFactory.instance(sysOss.getService());
|
||||
storage.download(sysOss.getFileName(), response.getOutputStream(), response::setContentLengthLong);
|
||||
}
|
||||
}
|
||||
@ -1,35 +1,24 @@
|
||||
package org.dromara.safety.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.dromara.quality.domain.dto.fileFolder.SafeListQueryDto;
|
||||
import org.dromara.safety.domain.HseFileFolder;
|
||||
import org.dromara.safety.domain.dto.fileFolder.FileFolderCreateDTO;
|
||||
import org.dromara.safety.domain.dto.fileFolder.FileFolderMoveDTO;
|
||||
import org.dromara.safety.domain.dto.fileFolder.ListQueryDto;
|
||||
import org.dromara.safety.domain.vo.fileFolder.FileFolderTreeVO;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.core.validate.AddGroup;
|
||||
import org.dromara.common.core.validate.EditGroup;
|
||||
import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.excel.utils.ExcelUtil;
|
||||
import org.dromara.safety.domain.vo.HseFileFolderVo;
|
||||
import org.dromara.safety.domain.bo.HseFileFolderBo;
|
||||
import org.dromara.safety.service.IHseFileFolderService;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
@ -50,7 +39,7 @@ public class HseFileFolderController extends BaseController {
|
||||
* 查询会议纪要列表
|
||||
*/
|
||||
@GetMapping("/list-all")
|
||||
public R<List<HseFileFolderVo>> listAll(ListQueryDto dto) {
|
||||
public R<List<HseFileFolderVo>> listAll(SafeListQueryDto dto) {
|
||||
return R.ok(hseFileFolderService.listAll(dto));
|
||||
}
|
||||
|
||||
@ -59,7 +48,7 @@ public class HseFileFolderController extends BaseController {
|
||||
* @return 树形结构列表
|
||||
*/
|
||||
@GetMapping("/tree-all")
|
||||
public R<List<FileFolderTreeVO>> treeAll(ListQueryDto dto) {
|
||||
public R<List<FileFolderTreeVO>> treeAll(SafeListQueryDto dto) {
|
||||
|
||||
// 1. 查询所有子项(利用path前缀匹配,一次性加载所有层级)
|
||||
List<HseFileFolder> allItems = hseFileFolderService.list(new LambdaQueryWrapper<HseFileFolder>()
|
||||
@ -110,10 +99,13 @@ public class HseFileFolderController extends BaseController {
|
||||
/**
|
||||
* 删除文件或文件夹(级联删除子项)
|
||||
*/
|
||||
@DeleteMapping("/{id}")
|
||||
@DeleteMapping("/{ids}")
|
||||
@Transactional
|
||||
public R<Boolean> delete(@PathVariable Long id) {
|
||||
return R.ok(hseFileFolderService.deleteFileOrFolder(id));
|
||||
public R<Boolean> delete(@PathVariable List<Long> ids) {
|
||||
for (Long id : ids) {
|
||||
hseFileFolderService.deleteFileOrFolder(id);
|
||||
}
|
||||
return R.ok(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -145,14 +137,23 @@ public class HseFileFolderController extends BaseController {
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/unzip")
|
||||
public R<String> unzip(@RequestParam("id") Long id,
|
||||
@RequestParam("parentId") Long parentId) {
|
||||
return R.ok(hseFileFolderService.unzip(id, parentId));
|
||||
}
|
||||
|
||||
@PutMapping("/reName")
|
||||
public R<Boolean> reName(@RequestParam("id") Long id,
|
||||
@RequestParam("name") String name) {
|
||||
return R.ok(hseFileFolderService.reName(id,name));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@GetMapping("/download/{id}")
|
||||
public void download(@PathVariable Long id, HttpServletResponse response) throws IOException {
|
||||
hseFileFolderService.download(id, response);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
package org.dromara.quality.domain.dto.fileFolder;
|
||||
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class SafeListQueryDto {
|
||||
/**
|
||||
* 父目录ID,默认0(根目录)
|
||||
*/
|
||||
@NotNull(message = "父目录ID不能为空")
|
||||
private Long parentId;
|
||||
|
||||
|
||||
/**
|
||||
* 类型:1-文件夹,2-文件,null-全部
|
||||
*/
|
||||
private Integer type;
|
||||
|
||||
|
||||
@NotNull(message = "项目不能为空")
|
||||
private Long projectId;
|
||||
|
||||
|
||||
}
|
||||
@ -1,7 +1,8 @@
|
||||
package org.dromara.safety.service;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.dromara.quality.domain.dto.fileFolder.SafeListQueryDto;
|
||||
import org.dromara.safety.domain.dto.fileFolder.FileFolderCreateDTO;
|
||||
import org.dromara.safety.domain.dto.fileFolder.ListQueryDto;
|
||||
import org.dromara.safety.domain.vo.HseFileFolderVo;
|
||||
import org.dromara.safety.domain.bo.HseFileFolderBo;
|
||||
import org.dromara.safety.domain.HseFileFolder;
|
||||
@ -9,8 +10,10 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@ -73,7 +76,7 @@ public interface IHseFileFolderService extends IService<HseFileFolder>{
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
|
||||
List<HseFileFolderVo> listAll(ListQueryDto dto);
|
||||
List<HseFileFolderVo> listAll(SafeListQueryDto dto);
|
||||
|
||||
/**
|
||||
* 创建文件或文件夹
|
||||
@ -100,4 +103,14 @@ public interface IHseFileFolderService extends IService<HseFileFolder>{
|
||||
* @return 树形结构列表
|
||||
*/
|
||||
String unzip(Long id, Long parentId);
|
||||
|
||||
/**
|
||||
* 重命名文件或文件夹
|
||||
*/
|
||||
Boolean reName(Long id,String name);
|
||||
|
||||
/**
|
||||
* 下载
|
||||
*/
|
||||
void download(@PathVariable Long id, HttpServletResponse response) throws IOException;
|
||||
}
|
||||
|
||||
@ -1,17 +1,25 @@
|
||||
package org.dromara.safety.service.impl;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.core.utils.file.FileUtils;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.oss.core.OssClient;
|
||||
import org.dromara.common.oss.factory.OssFactory;
|
||||
import org.dromara.quality.domain.dto.fileFolder.SafeListQueryDto;
|
||||
import org.dromara.safety.domain.dto.fileFolder.FileFolderCreateDTO;
|
||||
import org.dromara.safety.domain.dto.fileFolder.ListQueryDto;
|
||||
import org.dromara.system.domain.vo.SysOssVo;
|
||||
import org.dromara.system.service.ISysOssService;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.dromara.safety.domain.bo.HseFileFolderBo;
|
||||
import org.dromara.safety.domain.vo.HseFileFolderVo;
|
||||
@ -21,9 +29,9 @@ import org.dromara.safety.service.IHseFileFolderService;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
@ -39,6 +47,7 @@ import java.util.zip.ZipInputStream;
|
||||
public class HseFileFolderServiceImpl extends ServiceImpl<HseFileFolderMapper, HseFileFolder> implements IHseFileFolderService {
|
||||
|
||||
private final HseFileFolderMapper baseMapper;
|
||||
private final ISysOssService ossService;
|
||||
|
||||
/**
|
||||
* 查询会议纪要
|
||||
@ -148,7 +157,7 @@ public class HseFileFolderServiceImpl extends ServiceImpl<HseFileFolderMapper, H
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HseFileFolderVo> listAll(ListQueryDto dto) {
|
||||
public List<HseFileFolderVo> listAll(SafeListQueryDto dto) {
|
||||
|
||||
LambdaQueryWrapper<HseFileFolder> lqw = Wrappers.lambdaQuery();
|
||||
lqw.orderByDesc(HseFileFolder::getId);
|
||||
@ -416,9 +425,29 @@ public class HseFileFolderServiceImpl extends ServiceImpl<HseFileFolderMapper, H
|
||||
if (dotIndex > 0 && dotIndex < fileName.length() - 1) {
|
||||
fileRecord.setFileSuffix(fileName.substring(dotIndex + 1));
|
||||
}
|
||||
// 上传文件内容到OSS并设置文件路径
|
||||
// 上传文件内容到OSS并设置文件路径
|
||||
try {
|
||||
// 从ZIP流中读取文件内容
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[32768];
|
||||
int bytesRead;
|
||||
while ((bytesRead = zis.read(buffer)) != -1) {
|
||||
baos.write(buffer, 0, bytesRead);
|
||||
}
|
||||
byte[] fileContent = baos.toByteArray();
|
||||
|
||||
// TODO: 这里应该与实际文件存储服务集成,设置真实的文件路径
|
||||
fileRecord.setFilePath(""); // 实际应用中应设置真实文件存储路径
|
||||
// 检查文件长度,如果为0则跳过保存
|
||||
if (fileContent.length > 0) {
|
||||
// 直接使用字节流上传到OSS,避免创建临时文件
|
||||
try (ByteArrayInputStream bais = new ByteArrayInputStream(fileContent)) {
|
||||
String contentType = FileUtils.getMimeType(fileName);
|
||||
SysOssVo ossVo = ossService.upload(bais, fileName, contentType, (long) fileContent.length);
|
||||
|
||||
// 设置真实的文件路径
|
||||
fileRecord.setFilePath(ossVo.getUrl());
|
||||
fileRecord.setFileId(ossVo.getOssId());
|
||||
}
|
||||
|
||||
save(fileRecord);
|
||||
|
||||
@ -427,6 +456,11 @@ public class HseFileFolderServiceImpl extends ServiceImpl<HseFileFolderMapper, H
|
||||
updateById(fileRecord);
|
||||
|
||||
fileCount++;
|
||||
}
|
||||
// 如果文件长度为0,则跳过保存操作
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException("文件上传失败: " + e.getMessage());
|
||||
}
|
||||
|
||||
zis.closeEntry();
|
||||
}
|
||||
@ -472,8 +506,8 @@ public class HseFileFolderServiceImpl extends ServiceImpl<HseFileFolderMapper, H
|
||||
// 6. 下载ZIP文件到临时位置
|
||||
try {
|
||||
// 从URL下载文件
|
||||
String fileUrl = zipFile.getFilePath();
|
||||
File tempZipFile = downloadFileToTemp(fileUrl);
|
||||
Long fileId = zipFile.getFileId();
|
||||
File tempZipFile = downloadFileToTemp(fileId);
|
||||
|
||||
if (tempZipFile == null || !tempZipFile.exists()) {
|
||||
throw new ServiceException("下载文件失败");
|
||||
@ -497,40 +531,49 @@ public class HseFileFolderServiceImpl extends ServiceImpl<HseFileFolderMapper, H
|
||||
/**
|
||||
* 从URL下载文件到临时位置
|
||||
*
|
||||
* @param fileUrl 文件URL
|
||||
* @param fileId 文件id
|
||||
* @return 临时文件
|
||||
* @throws Exception 下载过程中的异常
|
||||
*/
|
||||
private File downloadFileToTemp(String fileUrl) throws Exception {
|
||||
// 创建临时文件
|
||||
File tempFile = File.createTempFile("download_", ".zip");
|
||||
private File downloadFileToTemp(Long fileId) throws Exception {
|
||||
SysOssVo sysOss = ossService.getById(fileId);
|
||||
if (ObjectUtil.isNull(sysOss)) {
|
||||
throw new ServiceException("文件数据不存在!");
|
||||
}
|
||||
|
||||
// 使用Apache HttpClient或其他HTTP客户端下载文件
|
||||
// 这里简化处理,实际项目中应根据具体文件存储服务实现
|
||||
OssClient storage = OssFactory.instance(sysOss.getService());
|
||||
// 直接使用OssClient返回的临时文件路径
|
||||
Path tempPath = storage.fileDownload(sysOss.getUrl());
|
||||
|
||||
// 示例实现(需要根据实际文件存储服务调整):
|
||||
/*
|
||||
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
||||
HttpGet httpGet = new HttpGet(fileUrl);
|
||||
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity != null) {
|
||||
try (InputStream inputStream = entity.getContent();
|
||||
FileOutputStream outputStream = new FileOutputStream(tempFile)) {
|
||||
byte[] buffer = new byte[8192];
|
||||
int bytesRead;
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
return tempPath.toFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// 如果使用MinIO或其他存储服务,需要相应调整实现
|
||||
@Override
|
||||
public Boolean reName(Long id, String name) {
|
||||
HseFileFolder fileFolder = getById(id);
|
||||
if (fileFolder != null) {
|
||||
fileFolder.setName(name);
|
||||
return updateById(fileFolder);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 占位实现,实际需要根据文件存储服务来实现
|
||||
throw new ServiceException("未实现文件下载功能,请根据实际文件存储服务完善此方法");
|
||||
@Override
|
||||
public void download(Long id, HttpServletResponse response) throws IOException {
|
||||
|
||||
HseFileFolder fileFolder = getById(id);
|
||||
|
||||
if(fileFolder == null){
|
||||
throw new ServiceException("文件数据不存在!");
|
||||
}
|
||||
Long ossId = fileFolder.getFileId();
|
||||
SysOssVo sysOss = ossService.getById(ossId);
|
||||
if (ObjectUtil.isNull(sysOss)) {
|
||||
throw new ServiceException("文件数据不存在!");
|
||||
}
|
||||
FileUtils.setAttachmentResponseHeader(response, fileFolder.getName()+"."+fileFolder.getFileSuffix());
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
|
||||
OssClient storage = OssFactory.instance(sysOss.getService());
|
||||
storage.download(sysOss.getFileName(), response.getOutputStream(), response::setContentLengthLong);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
<?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="org.dromara.quality.mapper.QltFileFolderMapper">
|
||||
|
||||
|
||||
<update id="batchUpdateChildPaths">
|
||||
UPDATE hse_file_folder
|
||||
SET
|
||||
path = REPLACE(path, #{oldPath}, #{newPath}),
|
||||
level = level + #{levelDiff},
|
||||
update_time = NOW()
|
||||
WHERE
|
||||
path LIKE CONCAT(#{oldPath}, '%')
|
||||
AND id != #{parentId}
|
||||
</update>
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user