This commit is contained in:
zt
2025-10-15 20:36:12 +08:00
parent 467a972a6d
commit 6398a28974
14 changed files with 404 additions and 28 deletions

View File

@ -628,6 +628,7 @@ public class DesDesignChangeServiceImpl extends ServiceImpl<DesDesignChangeMappe
} else if ("2".equals(bean.getDesignDisposal())) { } else if ("2".equals(bean.getDesignDisposal())) {
LambdaUpdateWrapper<DesVolumeFile> wrapper = new LambdaUpdateWrapper<>(); LambdaUpdateWrapper<DesVolumeFile> wrapper = new LambdaUpdateWrapper<>();
wrapper.set(DesVolumeFile::getType, DesVolumeFile.WASTE) wrapper.set(DesVolumeFile::getType, DesVolumeFile.WASTE)
.eq(DesVolumeFile::getType, DesVolumeFile.BLUEPRINT)
.eq(DesVolumeFile::getVolumeCatalogId, volumeCatalog.getDesign()); .eq(DesVolumeFile::getVolumeCatalogId, volumeCatalog.getDesign());
String saveFile = designChange.getSaveFile(); String saveFile = designChange.getSaveFile();
if (StringUtils.isNotBlank(saveFile)) { if (StringUtils.isNotBlank(saveFile)) {
@ -658,7 +659,7 @@ public class DesDesignChangeServiceImpl extends ServiceImpl<DesDesignChangeMappe
String lastVersion = list.getFirst().getVersion(); String lastVersion = list.getFirst().getVersion();
// 提取版本号数字部分去除LT-前缀) // 提取版本号数字部分去除LT-前缀)
String versionNumber = lastVersion; String versionNumber = lastVersion;
if (lastVersion.startsWith("LT-")) { if (lastVersion.startsWith("LT-") || lastVersion.startsWith("GC-")) {
versionNumber = lastVersion.substring(3); // 去除"LT-"前缀 versionNumber = lastVersion.substring(3); // 去除"LT-"前缀
} }
int majorVersion = (int) Math.floor(Double.parseDouble(versionNumber)) + 1; int majorVersion = (int) Math.floor(Double.parseDouble(versionNumber)) + 1;

View File

@ -421,17 +421,17 @@ public class DesVolumeFileServiceImpl extends ServiceImpl<DesVolumeFileMapper, D
if (DesVolumeFile.BLUEPRINT.equals(type)) { if (DesVolumeFile.BLUEPRINT.equals(type)) {
// 蓝图文件LT-1.0, LT-1.1, LT-1.2... // 蓝图文件LT-1.0, LT-1.1, LT-1.2...
double maxVersion = Double.parseDouble(maxBatchVersion); if (CollectionUtil.isEmpty(existingFilesInDB)) {
int majorVersion = (int) Math.floor(maxVersion); // 没有历史版本从1.0开始,根据文件索引递增
int minorVersion = (int) ((maxVersion - majorVersion) * 10) + fileIndex + 1; int minorVersion = fileIndex;
versionStr = "LT-1." + minorVersion;
// 如果是第一个文件且没有历史版本则从1.0开始
if (fileIndex == 0 && CollectionUtil.isEmpty(existingFilesInDB)) {
versionStr = "1.0";
} else { } else {
versionStr = majorVersion + "." + minorVersion; // 有历史版本,基于最高版本递增
double maxVersion = Double.parseDouble(maxBatchVersion);
int majorVersion = (int) Math.floor(maxVersion);
int minorVersion = (int) Math.round((maxVersion - majorVersion) * 10) + fileIndex + 1;
versionStr = "LT-" + majorVersion + "." + minorVersion;
} }
versionStr = "LT-" + versionStr;
} else if (DesVolumeFile.PROCESS.equals(type)) { } else if (DesVolumeFile.PROCESS.equals(type)) {
// 过程图纸:基于蓝图版本号 // 过程图纸:基于蓝图版本号
List<DesVolumeFile> blueprintFiles = baseMapper.selectList(new LambdaQueryWrapper<DesVolumeFile>() List<DesVolumeFile> blueprintFiles = baseMapper.selectList(new LambdaQueryWrapper<DesVolumeFile>()

View File

@ -330,6 +330,8 @@ public class MatMaterialsServiceImpl extends ServiceImpl<MatMaterialsMapper, Mat
lqw.eq(ObjectUtils.isNotEmpty(status), MatMaterials::getStatus, status); lqw.eq(ObjectUtils.isNotEmpty(status), MatMaterials::getStatus, status);
lqw.eq(ObjectUtils.isNotEmpty(projectId), MatMaterials::getProjectId, projectId); lqw.eq(ObjectUtils.isNotEmpty(projectId), MatMaterials::getProjectId, projectId);
lqw.eq(ObjectUtils.isNotEmpty(companyId), MatMaterials::getCompanyId, companyId); lqw.eq(ObjectUtils.isNotEmpty(companyId), MatMaterials::getCompanyId, companyId);
lqw.orderByDesc(MatMaterials::getCreateTime);
return lqw; return lqw;
} }

View File

@ -0,0 +1,51 @@
package org.dromara.project.controller.app;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
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.excel.utils.ExcelUtil;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.project.domain.dto.workwage.BusWorkWageCreateReq;
import org.dromara.project.domain.dto.workwage.BusWorkWageQueryReq;
import org.dromara.project.domain.dto.workwage.BusWorkWageUpdateReq;
import org.dromara.project.domain.vo.workwage.BusWorkWageVo;
import org.dromara.project.service.IBusWorkWageService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 工种薪水
*
* @author lilemy
* @date 2025-03-26
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/app/project/workWage")
public class BusWorkWageAppController extends BaseController {
private final IBusWorkWageService busWorkWageService;
/**
* 查询工种薪水列表
*/
@GetMapping("/list")
public R<List<BusWorkWageVo>> list(BusWorkWageQueryReq req) {
return R.ok(busWorkWageService.queryList(req));
}
}

View File

@ -1,6 +1,8 @@
package org.dromara.project.domain.dto.workwage; package org.dromara.project.domain.dto.workwage;
import lombok.Data; import lombok.Data;
import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
@ -31,6 +33,7 @@ public class BusWorkWageQueryReq implements Serializable {
*/ */
private String workType; private String workType;
/** /**
* 是否是特种兵1是 2否 * 是否是特种兵1是 2否
*/ */

View File

@ -6,6 +6,8 @@ import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data; import lombok.Data;
import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert; import org.dromara.common.excel.convert.ExcelDictConvert;
import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.project.domain.BusWorkWage; import org.dromara.project.domain.BusWorkWage;
import java.io.Serial; import java.io.Serial;
@ -47,6 +49,12 @@ public class BusWorkWageVo implements Serializable {
@ExcelDictFormat(dictType = "type_of_work") @ExcelDictFormat(dictType = "type_of_work")
private String workType; private String workType;
/**
* 工种
*/
@Translation(type = TransConstant.DICT_TYPE_TO_LABEL, mapper = "workType",other = "type_of_work")
private String workTypeName;
/** /**
* 是否是特种兵1是 2否 * 是否是特种兵1是 2否
*/ */

View File

@ -505,6 +505,7 @@ public class BusLeaveServiceImpl extends ServiceImpl<BusLeaveMapper, BusLeave>
@Override @Override
public TableDataInfo<BusLeaveVo> listByLoginUser(BusLeaveQueryReq req, PageQuery pageQuery) { public TableDataInfo<BusLeaveVo> listByLoginUser(BusLeaveQueryReq req, PageQuery pageQuery) {
LambdaQueryWrapper<BusLeave> lqw = new LambdaQueryWrapper<>(); LambdaQueryWrapper<BusLeave> lqw = new LambdaQueryWrapper<>();
lqw.orderByDesc(BusLeave::getCreateTime);
lqw.eq(ObjectUtils.isNotEmpty(req.getUserId()), BusLeave::getUserId, req.getUserId()); lqw.eq(ObjectUtils.isNotEmpty(req.getUserId()), BusLeave::getUserId, req.getUserId());

View File

@ -214,9 +214,9 @@ public class BusReissueCardServiceImpl extends ServiceImpl<BusReissueCardMapper,
byId.setClockStatus(BusAttendanceClockStatusEnum.REISSUE.getValue()); byId.setClockStatus(BusAttendanceClockStatusEnum.REISSUE.getValue());
attendanceService.updateById(byId); attendanceService.updateById(byId);
//缺卡补充当天的工资表 //缺卡补充当天的工资表
if(BusAttendanceClockStatusEnum.UNCLOCK.getValue().equals(clockStatus)){ // if(BusAttendanceClockStatusEnum.UNCLOCK.getValue().equals(clockStatus)){
userSalaryDetailService.insertByAttendance(byId.getUserId(),byId.getClockDate()); // userSalaryDetailService.insertByAttendance(byId.getUserId(),byId.getClockDate());
} // }
CompletableFuture.runAsync(() -> { CompletableFuture.runAsync(() -> {
try { try {
chatServerHandler.sendSystemMessageToUser(bean.getUserId(),"补卡申请已通过","1"); chatServerHandler.sendSystemMessageToUser(bean.getUserId(),"补卡申请已通过","1");
@ -511,9 +511,9 @@ public class BusReissueCardServiceImpl extends ServiceImpl<BusReissueCardMapper,
byId.setClockStatus(BusAttendanceClockStatusEnum.REISSUE.getValue()); byId.setClockStatus(BusAttendanceClockStatusEnum.REISSUE.getValue());
attendanceService.updateById(byId); attendanceService.updateById(byId);
//缺卡补充当天的工资表 //缺卡补充当天的工资表
if(BusAttendanceClockStatusEnum.UNCLOCK.getValue().equals(clockStatus)){ // if(BusAttendanceClockStatusEnum.UNCLOCK.getValue().equals(clockStatus)){
userSalaryDetailService.insertByAttendance(byId.getUserId(),byId.getClockDate()); // userSalaryDetailService.insertByAttendance(byId.getUserId(),byId.getClockDate());
} // }
CompletableFuture.runAsync(() -> { CompletableFuture.runAsync(() -> {
try { try {
chatServerHandler.sendSystemMessageToUser(bean.getUserId(),"补卡申请已通过","1"); chatServerHandler.sendSystemMessageToUser(bean.getUserId(),"补卡申请已通过","1");

View File

@ -13,6 +13,7 @@ import org.dromara.safety.domain.dto.fileFolder.FileFolderCreateDTO;
import org.dromara.safety.domain.dto.fileFolder.FileFolderMoveDTO; import org.dromara.safety.domain.dto.fileFolder.FileFolderMoveDTO;
import org.dromara.safety.domain.dto.fileFolder.ListQueryDto; import org.dromara.safety.domain.dto.fileFolder.ListQueryDto;
import org.dromara.safety.domain.vo.fileFolder.FileFolderTreeVO; import org.dromara.safety.domain.vo.fileFolder.FileFolderTreeVO;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -29,6 +30,7 @@ import org.dromara.safety.domain.vo.HseFileFolderVo;
import org.dromara.safety.domain.bo.HseFileFolderBo; import org.dromara.safety.domain.bo.HseFileFolderBo;
import org.dromara.safety.service.IHseFileFolderService; import org.dromara.safety.service.IHseFileFolderService;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.web.multipart.MultipartFile;
/** /**
* 会议纪要 * 会议纪要
@ -61,6 +63,7 @@ public class HseFileFolderController extends BaseController {
// 1. 查询所有子项利用path前缀匹配一次性加载所有层级 // 1. 查询所有子项利用path前缀匹配一次性加载所有层级
List<HseFileFolder> allItems = hseFileFolderService.list(new LambdaQueryWrapper<HseFileFolder>() List<HseFileFolder> allItems = hseFileFolderService.list(new LambdaQueryWrapper<HseFileFolder>()
.eq(HseFileFolder::getProjectId, dto.getProjectId())
.like(HseFileFolder::getPath, "," + dto.getParentId() + ",") // 包含父ID的所有子项 .like(HseFileFolder::getPath, "," + dto.getParentId() + ",") // 包含父ID的所有子项
.eq(dto.getType()!=null,HseFileFolder::getType, dto.getType()) .eq(dto.getType()!=null,HseFileFolder::getType, dto.getType())
.orderByDesc(HseFileFolder::getId) .orderByDesc(HseFileFolder::getId)
@ -122,8 +125,25 @@ public class HseFileFolderController extends BaseController {
return R.ok(hseFileFolderService.moveFileOrFolder(dto.getId(), dto.getTargetParentId())); return R.ok(hseFileFolderService.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 = hseFileFolderService.uploadAndUnzip(file, parentId, projectId);
return R.ok(result);
} catch (IllegalArgumentException e) {
return R.fail(e.getMessage());
} catch (Exception e) {
return R.fail("文件处理失败:" + e.getMessage());
}
}

View File

@ -16,6 +16,8 @@ public class FileFolderCreateDTO {
@NotBlank(message = "名称不能为空") @NotBlank(message = "名称不能为空")
private String name; private String name;
private Long projectId;
/** /**
* 父级ID0表示根目录 * 父级ID0表示根目录
*/ */
@ -32,6 +34,12 @@ public class FileFolderCreateDTO {
*/ */
private Integer sort=0; private Integer sort=0;
/**
* 文件id
*/
private Long fileId;
/** /**
* 文件后缀 * 文件后缀
*/ */

View File

@ -19,4 +19,8 @@ public class ListQueryDto {
private Integer type; private Integer type;
@NotNull(message = "项目不能为空")
private Long projectId;
} }

View File

@ -9,6 +9,8 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.multipart.MultipartFile;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -87,4 +89,15 @@ public interface IHseFileFolderService extends IService<HseFileFolder>{
* 移动文件或文件夹到指定目录 * 移动文件或文件夹到指定目录
*/ */
boolean moveFileOrFolder(Long id, Long targetParentId); boolean moveFileOrFolder(Long id, Long targetParentId);
/**
* 上传并解压ZIP文件
*/
String uploadAndUnzip(MultipartFile file, Long parentId, Long projectId) throws Exception;
/**
* 获取指定目录的树形结构(一次性加载所有层级)
* @return 树形结构列表
*/
String unzip(Long id, Long parentId);
} }

View File

@ -19,11 +19,14 @@ import org.dromara.safety.domain.HseFileFolder;
import org.dromara.safety.mapper.HseFileFolderMapper; import org.dromara.safety.mapper.HseFileFolderMapper;
import org.dromara.safety.service.IHseFileFolderService; import org.dromara.safety.service.IHseFileFolderService;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDateTime; import java.io.File;
import java.util.List; import java.io.FileInputStream;
import java.util.Map; import java.nio.charset.Charset;
import java.util.Collection; import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/** /**
* 会议纪要Service业务层处理 * 会议纪要Service业务层处理
@ -44,7 +47,7 @@ public class HseFileFolderServiceImpl extends ServiceImpl<HseFileFolderMapper, H
* @return 会议纪要 * @return 会议纪要
*/ */
@Override @Override
public HseFileFolderVo queryById(Long id){ public HseFileFolderVo queryById(Long id) {
return baseMapper.selectVoById(id); return baseMapper.selectVoById(id);
} }
@ -125,7 +128,7 @@ public class HseFileFolderServiceImpl extends ServiceImpl<HseFileFolderMapper, H
/** /**
* 保存前的数据校验 * 保存前的数据校验
*/ */
private void validEntityBeforeSave(HseFileFolder entity){ private void validEntityBeforeSave(HseFileFolder entity) {
//TODO 做一些数据校验,如唯一约束 //TODO 做一些数据校验,如唯一约束
} }
@ -138,7 +141,7 @@ public class HseFileFolderServiceImpl extends ServiceImpl<HseFileFolderMapper, H
*/ */
@Override @Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) { public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if(isValid){ if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验 //TODO 做一些业务上的校验,判断是否需要校验
} }
return baseMapper.deleteByIds(ids) > 0; return baseMapper.deleteByIds(ids) > 0;
@ -151,8 +154,9 @@ public class HseFileFolderServiceImpl extends ServiceImpl<HseFileFolderMapper, H
lqw.orderByDesc(HseFileFolder::getId); lqw.orderByDesc(HseFileFolder::getId);
lqw.orderByAsc(HseFileFolder::getSort); lqw.orderByAsc(HseFileFolder::getSort);
lqw.eq(HseFileFolder::getProjectId, dto.getProjectId());
lqw.eq(HseFileFolder::getParentId, dto.getParentId()); lqw.eq(HseFileFolder::getParentId, dto.getParentId());
lqw.eq(dto.getType()!=null,HseFileFolder::getType, dto.getType()); lqw.eq(dto.getType() != null, HseFileFolder::getType, dto.getType());
return baseMapper.selectVoList(lqw); return baseMapper.selectVoList(lqw);
} }
@ -168,11 +172,13 @@ public class HseFileFolderServiceImpl extends ServiceImpl<HseFileFolderMapper, H
// 2. 构建新文件/文件夹对象 // 2. 构建新文件/文件夹对象
HseFileFolder entity = new HseFileFolder(); HseFileFolder entity = new HseFileFolder();
entity.setProjectId(dto.getProjectId());
entity.setName(dto.getName()); entity.setName(dto.getName());
entity.setParentId(dto.getParentId()); entity.setParentId(dto.getParentId());
entity.setType(dto.getType()); entity.setType(dto.getType());
entity.setSort(dto.getSort() != null ? dto.getSort() : 0); entity.setSort(dto.getSort() != null ? dto.getSort() : 0);
entity.setRemark(dto.getRemark()); entity.setRemark(dto.getRemark());
entity.setFileId(dto.getFileId());
// 3. 设置层级和路径 // 3. 设置层级和路径
@ -236,7 +242,7 @@ public class HseFileFolderServiceImpl extends ServiceImpl<HseFileFolderMapper, H
} }
// 2. 防止循环移动(不能移动到自身或子目录下) // 2. 防止循环移动(不能移动到自身或子目录下)
if (source.getPath().contains("," + targetParentId + ",")) { if (targetParent.getPath().contains(source.getPath())) {
throw new ServiceException("不能将文件夹移动到其子目录下"); throw new ServiceException("不能将文件夹移动到其子目录下");
} }
@ -244,6 +250,7 @@ public class HseFileFolderServiceImpl extends ServiceImpl<HseFileFolderMapper, H
String oldPath = source.getPath(); String oldPath = source.getPath();
String oldParentPath = source.getParentId() == 0 ? ",0," : getById(source.getParentId()).getPath(); String oldParentPath = source.getParentId() == 0 ? ",0," : getById(source.getParentId()).getPath();
String newParentPath = targetParent.getPath(); String newParentPath = targetParent.getPath();
int oldLevel = source.getLevel();
// 4. 计算新路径和新层级 // 4. 计算新路径和新层级
String newPath = newParentPath + id + ","; String newPath = newParentPath + id + ",";
@ -260,7 +267,7 @@ public class HseFileFolderServiceImpl extends ServiceImpl<HseFileFolderMapper, H
// 计算路径替换的前后缀 // 计算路径替换的前后缀
String pathReplaceFrom = oldPath; String pathReplaceFrom = oldPath;
String pathReplaceTo = newPath; String pathReplaceTo = newPath;
int levelDiff = newLevel - source.getLevel(); // 层级变化量 int levelDiff = newLevel - oldLevel; // 层级变化量
// 批量更新子项 // 批量更新子项
baseMapper.batchUpdateChildPaths(pathReplaceFrom, pathReplaceTo, levelDiff, id); baseMapper.batchUpdateChildPaths(pathReplaceFrom, pathReplaceTo, levelDiff, id);
@ -268,4 +275,262 @@ public class HseFileFolderServiceImpl extends ServiceImpl<HseFileFolderMapper, H
return true; 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. 验证父目录是否存在且为文件夹
HseFileFolder 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, HseFileFolder 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<HseFileFolder> folderQuery = Wrappers.lambdaQuery();
folderQuery.eq(HseFileFolder::getProjectId, projectId)
.eq(HseFileFolder::getParentId, currentParentId)
.eq(HseFileFolder::getName, folderName)
.eq(HseFileFolder::getType, 1); // 文件夹类型
HseFileFolder existingFolder = getOne(folderQuery);
if (existingFolder == null) {
// 创建新的文件夹
HseFileFolder newFolder = new HseFileFolder();
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();
}
}
// 创建文件记录
HseFileFolder fileRecord = new HseFileFolder();
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));
}
// TODO: 这里应该与实际文件存储服务集成,设置真实的文件路径
fileRecord.setFilePath(""); // 实际应用中应设置真实文件存储路径
save(fileRecord);
// 更新文件的路径因为ID是自增的需要先保存再更新路径
fileRecord.setPath(currentPath + fileRecord.getId() + ",");
updateById(fileRecord);
fileCount++;
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文件
HseFileFolder 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. 验证目标目录是否存在且为文件夹
HseFileFolder parentFolder = null;
if (parentId != null && parentId != 0) {
parentFolder = getById(parentId);
if (parentFolder == null || parentFolder.getType() != 1) {
throw new ServiceException("指定的目标目录不存在或不是文件夹");
}
}
// 6. 下载ZIP文件到临时位置
try {
// 从URL下载文件
String fileUrl = zipFile.getFilePath();
File tempZipFile = downloadFileToTemp(fileUrl);
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 fileUrl 文件URL
* @return 临时文件
* @throws Exception 下载过程中的异常
*/
private File downloadFileToTemp(String fileUrl) throws Exception {
// 创建临时文件
File tempFile = File.createTempFile("download_", ".zip");
// 使用Apache HttpClient或其他HTTP客户端下载文件
// 这里简化处理,实际项目中应根据具体文件存储服务实现
// 示例实现(需要根据实际文件存储服务调整):
/*
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);
}
}
}
}
}
*/
// 如果使用MinIO或其他存储服务需要相应调整实现
// 占位实现,实际需要根据文件存储服务来实现
throw new ServiceException("未实现文件下载功能,请根据实际文件存储服务完善此方法");
}
} }

View File

@ -13,6 +13,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
update_time = NOW() update_time = NOW()
WHERE WHERE
path LIKE CONCAT(#{oldPath}, '%') path LIKE CONCAT(#{oldPath}, '%')
AND parent_id != #{parentId} AND id != #{parentId}
</update> </update>
</mapper> </mapper>