From 96d0406931ddb7134c92034a7e79991e24a063ec Mon Sep 17 00:00:00 2001 From: lcj <2331845269@qq.com> Date: Sat, 6 Sep 2025 19:07:10 +0800 Subject: [PATCH] =?UTF-8?q?=E7=89=A9=E8=B5=84=E3=80=81=E9=87=8C=E7=A8=8B?= =?UTF-8?q?=E7=A2=91=E8=BF=9B=E5=BA=A6execl=E5=AF=BC=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/DesVolumeCatalogServiceImpl.java | 64 ++- .../vo/materials/MatMaterialsUseDetailVo.java | 7 +- .../service/IMatMaterialsService.java | 10 +- .../service/impl/MatMaterialsServiceImpl.java | 312 ++++++++------- .../impl/OutConstructionValueServiceImpl.java | 12 +- ...PgsConstructionSchedulePlanController.java | 35 ++ .../domain/PgsConstructionSchedulePlan.java | 10 +- .../PgsConstructionSchedulePlanCreateReq.java | 15 +- .../PgsConstructionSchedulePlanExcelDto.java | 71 ++++ .../PgsConstructionSchedulePlanUpdateReq.java | 10 +- .../PgsConstructionSchedulePlanVo.java | 15 +- .../IPgsConstructionSchedulePlanService.java | 19 + ...gsConstructionSchedulePlanServiceImpl.java | 378 +++++++++++++++++- .../PgsProgressPlanDetailServiceImpl.java | 10 +- .../project/service/IBusProjectService.java | 9 + .../service/impl/BusProjectServiceImpl.java | 51 +++ 16 files changed, 805 insertions(+), 223 deletions(-) create mode 100644 xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/dto/constructionscheduleplan/PgsConstructionSchedulePlanExcelDto.java diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/design/service/impl/DesVolumeCatalogServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/design/service/impl/DesVolumeCatalogServiceImpl.java index 4ca476a8..3e67ec93 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/design/service/impl/DesVolumeCatalogServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/design/service/impl/DesVolumeCatalogServiceImpl.java @@ -10,7 +10,6 @@ import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddressList; -import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.dromara.common.core.constant.HttpStatus; import org.dromara.common.core.domain.event.ProcessDeleteEvent; @@ -36,7 +35,6 @@ import org.dromara.project.service.IBusProjectService; import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.service.ISysUserService; import org.springframework.beans.BeanUtils; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; @@ -91,12 +89,12 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl queryList(DesVolumeCatalogQueryReq req) { - return this.list(this.buildQueryWrapper(req)).stream().map(vo-> this.getVo(vo,req.getType())).toList(); + return this.list(this.buildQueryWrapper(req)).stream().map(vo -> this.getVo(vo, req.getType())).toList(); } /** @@ -130,10 +128,10 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl queryFileListById(Long id,String type) { + public List queryFileListById(Long id, String type) { return volumeFileService.lambdaQuery() .eq(DesVolumeFile::getVolumeCatalogId, id) - .eq(StringUtils.isNotBlank(type),DesVolumeFile::getType, type) + .eq(StringUtils.isNotBlank(type), DesVolumeFile::getType, type) .orderByAsc(DesVolumeFile::getStatus) .orderByDesc(DesVolumeFile::getCreateDept) .list(); @@ -178,7 +176,7 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl 0) { throw new ServiceException("卷册目录已存在", HttpStatus.BAD_REQUEST); } - Long count1 = desUserService.getCount(req.getPrincipal(),req.getSpecialty(),req.getProjectId()); + Long count1 = desUserService.getCount(req.getPrincipal(), req.getSpecialty(), req.getProjectId()); if (count1 == 0) { throw new ServiceException("所选专业不包含该人员"); } @@ -219,7 +217,7 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl volumeFiles = this.queryFileListById(volumeCatalog.getDesign(),type); + List volumeFiles = this.queryFileListById(volumeCatalog.getDesign(), type); vo.setFileVoList(volumeFileService.getVoList(volumeFiles)); // 关联查阅人信息 List allViewerList = volumeFileViewerService.lambdaQuery() @@ -341,7 +339,7 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl volumeCatalogVoList = volumeCatalogList.stream().map(vo-> getVo(vo,null)).toList(); + List volumeCatalogVoList = volumeCatalogList.stream().map(vo -> getVo(vo, null)).toList(); // 查询文件数量 LambdaQueryWrapper fileQueryWrapper = new LambdaQueryWrapper<>(); @@ -369,9 +367,9 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl list = volumeFileService.list(fileQueryWrapper); - if (list != null && !list.isEmpty()){ + if (list != null && !list.isEmpty()) { desVolumeCatalogVo.setFileCount((long) list.size()); - }else{ + } else { desVolumeCatalogVo.setFileCount(0L); } @@ -416,12 +414,12 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl list1 = list.stream().map(DesVolumeFile::getVolumeCatalogId).distinct().toList(); - return baseMapper.selectVoList(Wrappers.lambdaQuery(DesVolumeCatalog.class) + return baseMapper.selectVoList(Wrappers.lambdaQuery(DesVolumeCatalog.class) .eq(DesVolumeCatalog::getProjectId, projectId) .in(DesVolumeCatalog::getDesign, list1) .orderByDesc(DesVolumeCatalog::getCreateTime) @@ -435,21 +433,15 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl uniqueMajors = desUserService.getUserMajor(desUserBo); Map userList = desUserService.getUserList(desUserBo); - - - //TODO 数据绑定下拉 -// String[] namesArray = names.toArray(String[]::new); - - // 2. 设置响应头 // 设置响应头,指定Excel格式和下载文件名 response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); - response.setHeader("Content-Disposition", "attachment; filename="+ URLEncoder.encode("收资清单模板.xlsx", StandardCharsets.UTF_8)); + response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("卷册目录模板.xlsx", StandardCharsets.UTF_8)); Workbook workbook = new XSSFWorkbook(); // 创建主 Sheet 和隐藏 Sheet - Sheet mainSheet = workbook.createSheet("收资清单模板"); + Sheet mainSheet = workbook.createSheet("卷册目录模板"); Sheet dataSheet = workbook.createSheet("DropdownData"); workbook.setSheetHidden(workbook.getSheetIndex(dataSheet), true); @@ -483,23 +475,22 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl desDrawings = new ArrayList<>(); List list = volumeFileService.lambdaQuery().eq(DesVolumeFile::getVolumeCatalogId, id).list(); @@ -672,7 +660,7 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl log.info("图纸[{}-{} ]添加二维码成功", desVolumeFile.getFileName(), desVolumeFile.getId())) .exceptionally(ex -> { log.error("图纸[{}-{}]添加二维码失败", desVolumeFile.getFileName(), desVolumeFile.getId(), ex); diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/materials/domain/vo/materials/MatMaterialsUseDetailVo.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/materials/domain/vo/materials/MatMaterialsUseDetailVo.java index fd7c83c4..eff02452 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/materials/domain/vo/materials/MatMaterialsUseDetailVo.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/materials/domain/vo/materials/MatMaterialsUseDetailVo.java @@ -36,7 +36,7 @@ public class MatMaterialsUseDetailVo implements Serializable { /** * 供货单位 */ - private String companyName; + private String supplier; /** * 入库id @@ -53,11 +53,6 @@ public class MatMaterialsUseDetailVo implements Serializable { */ private String operator; - /** - * 交接单位 - */ - private String recipient; - /** * 入库时间 */ diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/materials/service/IMatMaterialsService.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/materials/service/IMatMaterialsService.java index 15b09a3e..0d6989bb 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/materials/service/IMatMaterialsService.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/materials/service/IMatMaterialsService.java @@ -169,13 +169,15 @@ public interface IMatMaterialsService extends IService { /** * 获取材料使用详情列表 * - * @param materials 材料 - * @param inventoryList 材料库存列表 - * @param useList 材料使用列表 + * @param materials 材料 + * @param putList 材料入库列表 + * @param outList 材料出库列表 + * @param useList 材料使用列表 * @return 材料使用详情列表 */ List getUseDetailList(List materials, - List inventoryList, + List putList, + List outList, List useList); /** diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/materials/service/impl/MatMaterialsServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/materials/service/impl/MatMaterialsServiceImpl.java index 23266db2..6fa87d31 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/materials/service/impl/MatMaterialsServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/materials/service/impl/MatMaterialsServiceImpl.java @@ -31,8 +31,10 @@ import org.dromara.materials.domain.dto.materialsinventory.MatMaterialsInventory import org.dromara.materials.domain.enums.MatMaterialsInventoryOutPutEnum; import org.dromara.materials.domain.enums.MatMaterialsInventoryReceiveStatusEnum; import org.dromara.materials.domain.vo.materials.*; +import org.dromara.materials.domain.vo.materialsinventory.MatMaterialsInventoryOutUseVo; import org.dromara.materials.domain.vo.materialsinventory.MatMaterialsInventoryOutVo; import org.dromara.materials.domain.vo.materialsinventory.MatMaterialsInventoryVo; +import org.dromara.materials.domain.vo.materialsuserecord.MatMaterialsUseRecordByOutVo; import org.dromara.materials.mapper.MatMaterialsMapper; import org.dromara.materials.service.*; import org.dromara.project.domain.BusProject; @@ -613,176 +615,214 @@ public class MatMaterialsServiceImpl extends ServiceImpl ids = materialsList.stream().map(MatMaterials::getId).toList(); + List materialsInventoryList = materialsInventoryService.lambdaQuery() + .in(MatMaterialsInventory::getMaterialsId, ids) + .list(); + List putList = materialsInventoryList.stream() + .filter(inventory -> inventory.getOutPut().equals(MatMaterialsInventoryOutPutEnum.PUT.getValue())) + .toList(); + // 查询使用列表 + List outList = materialsInventoryList.stream() + .filter(inventory -> inventory.getOutPut().equals(MatMaterialsInventoryOutPutEnum.OUT.getValue())) + .toList(); + List useList = new ArrayList<>(); + if (CollUtil.isNotEmpty(outList)) { + List outIds = outList.stream().map(MatMaterialsInventory::getId).toList(); + useList = materialsUseRecordService.lambdaQuery() + .in(MatMaterialsUseRecord::getInventoryId, outIds) + .list(); + } + List useDetailList = this.getUseDetailList(materialsList, putList, outList, useList); + materialsVoPage.setRecords(useDetailList); + return TableDataInfo.build(materialsVoPage); } /** * 获取材料使用详情列表 * - * @param materials 材料 - * @param inventoryList 材料库存列表 - * @param useList 材料使用列表 + * @param materials 材料 + * @param putList 材料入库列表 + * @param outList 材料出库列表 + * @param useList 材料使用列表 * @return 材料使用详情列表 */ @Override - public List getUseDetailList(List materials, List inventoryList, List useList) { - return List.of(); + public List getUseDetailList(List materials, + List putList, + List outList, + List useList) { + Map putMap = putList.stream() + .collect(Collectors.toMap(MatMaterialsInventory::getMaterialsId, inventory -> inventory)); + return materials.stream().map(material -> { + MatMaterialsUseDetailVo vo = new MatMaterialsUseDetailVo(); + BeanUtils.copyProperties(material, vo); + Long id = material.getId(); + MatMaterialsInventory put = putMap.get(id); + vo.setSupplier(put.getRecipient()); + vo.setInventoryId(put.getId()); + vo.setNumber(put.getNumber()); + vo.setOperator(put.getOperator()); + vo.setEnterTime(put.getCreateTime()); + if (CollUtil.isNotEmpty(outList)) { + List outs = outList.stream() + .filter(inventory -> inventory.getMaterialsId().equals(id)) + .toList(); + if (CollUtil.isNotEmpty(outs)) { + List outUseVos = outs.stream().map(inventory -> { + MatMaterialsInventoryOutUseVo outUseVo = new MatMaterialsInventoryOutUseVo(); + BeanUtils.copyProperties(inventory, outUseVo); + if (CollUtil.isNotEmpty(useList)) { + List uses = useList.stream() + .filter(record -> record.getInventoryId().equals(inventory.getId())) + .toList(); + List useRecordList = uses.stream().map(record -> { + MatMaterialsUseRecordByOutVo useRecordVo = new MatMaterialsUseRecordByOutVo(); + BeanUtils.copyProperties(record, useRecordVo); + return useRecordVo; + }).toList(); + outUseVo.setUseList(useRecordList); + } + return outUseVo; + }).toList(); + vo.setOutList(outUseVos); + } + + } + return vo; + }).toList(); } /** - * 获取材料库存数据列表 + * 获取材料库存数据列表(导出 Excel 用) * * @param req 查询条件 * @return 材料库存数据列表 */ @Override public List queryExcelList(MatMaterialsQueryReq req) { - // 查询数据库 LambdaQueryWrapper lqw = this.buildQueryWrapper(req); List materials = this.list(lqw); if (CollUtil.isEmpty(materials)) { return List.of(); } - // 获取材料主键 + List ids = materials.stream().map(MatMaterials::getId).toList(); - // 获取出入库记录 + List inventoryList = materialsInventoryService.lambdaQuery() .in(MatMaterialsInventory::getMaterialsId, ids) .list(); - // 获取入库记录 - List putList = inventoryList.stream() - .filter(inventory -> inventory.getOutPut().equals(MatMaterialsInventoryOutPutEnum.PUT.getValue())) - .toList(); - Map putMap = putList.stream() - .collect(Collectors.toMap(MatMaterialsInventory::getMaterialsId, inventory -> inventory)); - // 获取使用记录 + + Map putMap = inventoryList.stream() + .filter(inv -> inv.getOutPut().equals(MatMaterialsInventoryOutPutEnum.PUT.getValue())) + .collect(Collectors.toMap(MatMaterialsInventory::getMaterialsId, Function.identity())); + List outList = inventoryList.stream() - .filter(inventory -> inventory.getOutPut().equals(MatMaterialsInventoryOutPutEnum.OUT.getValue())) + .filter(inv -> inv.getOutPut().equals(MatMaterialsInventoryOutPutEnum.OUT.getValue())) .toList(); - List useList = new ArrayList<>(); - if (CollUtil.isEmpty(outList)) { - List outIds = outList.stream().map(MatMaterialsInventory::getId).distinct().toList(); - // 获取使用记录 - useList = materialsUseRecordService.lambdaQuery() + + Map> useMap = new HashMap<>(); + if (CollUtil.isNotEmpty(outList)) { + List outIds = outList.stream().map(MatMaterialsInventory::getId).toList(); + List useList = materialsUseRecordService.lambdaQuery() .in(MatMaterialsUseRecord::getInventoryId, outIds) .list(); + useMap = useList.stream().collect(Collectors.groupingBy(MatMaterialsUseRecord::getInventoryId)); } - List excelVoList = new ArrayList<>(); - for (MatMaterials material : materials) { - MatMaterialsExcelVo vo = new MatMaterialsExcelVo(); - Long id = material.getId(); - vo.setMaterialsName(material.getMaterialsName()); - vo.setQuantityCount(material.getQuantityCount()); - // 拼接入库记录 - MatMaterialsInventory put = putMap.get(id); - vo.setSupplier(put.getRecipient()); - vo.setPutNumber(put.getNumber()); - vo.setSigner(put.getOperator()); - if (put.getCreateTime() != null) { - vo.setPutTime(DateUtils.parseDateToStr(FormatsType.YYYY_MM_DD_DOT, put.getCreateTime())); - } - List materialOutList = outList.stream() - .filter(inventory -> inventory.getMaterialsId().equals(id)) - .toList(); - List materialUseList = new ArrayList<>(); - if (CollUtil.isNotEmpty(materialOutList)) { - // 拼接第一条出库数据 - MatMaterialsInventory out = materialOutList.getFirst(); - vo.setRecipient(out.getRecipient()); - vo.setOutNumber(out.getNumber()); - vo.setOperator(out.getOperator()); - vo.setShipper(out.getShipper()); - if (out.getCreateTime() != null) { - vo.setReceivingDate(DateUtils.parseDateToStr(FormatsType.YYYY_MM_DD_DOT, out.getCreateTime())); - } - vo.setResidue(out.getResidue()); - vo.setDisposition(out.getDisposition()); - materialUseList = useList.stream() - .filter(record -> record.getInventoryId().equals(out.getId())) - .toList(); - if (CollUtil.isNotEmpty(materialUseList)) { - // 拼接第一条使用记录 - MatMaterialsUseRecord use = materialUseList.getFirst(); - vo.setUsePart(use.getUsePart()); - vo.setUseNumber(use.getUseNumber()); - if (use.getCreateTime() != null) { - vo.setUseTime(DateUtils.parseDateToStr(FormatsType.YYYY_MM_DD_DOT, use.getCreateTime())); - } - vo.setSurplus(use.getResidueNumber()); - vo.setRemark(use.getRemark()); - } - } - List childList = new ArrayList<>(); - if (CollUtil.isNotEmpty(materialUseList)) { - for (int i = 1; i < materialOutList.size(); i++) { - MatMaterialsInventory out = materialOutList.get(i); - List outUseList = materialUseList.stream() - .filter(record -> record.getInventoryId().equals(out.getId())) - .toList(); - if (CollUtil.isNotEmpty(outUseList)) { - for (int j = 1; j < outUseList.size(); j++) { - MatMaterialsExcelVo child = new MatMaterialsExcelVo(); - MatMaterialsUseRecord use = outUseList.get(j); - if (j == 1) { - // 拼接出库记录 - child.setRecipient(out.getRecipient()); - child.setOutNumber(out.getNumber()); - child.setOperator(out.getOperator()); - child.setShipper(out.getShipper()); - if (out.getCreateTime() != null) { - child.setReceivingDate(DateUtils.parseDateToStr(FormatsType.YYYY_MM_DD_DOT, out.getCreateTime())); - } - child.setResidue(out.getResidue()); - child.setDisposition(out.getDisposition()); - } - // 拼接使用记录 - vo.setUsePart(use.getUsePart()); - vo.setUseNumber(use.getUseNumber()); - if (use.getCreateTime() != null) { - vo.setUseTime(DateUtils.parseDateToStr(FormatsType.YYYY_MM_DD_DOT, use.getCreateTime())); - } - vo.setSurplus(use.getResidueNumber()); - vo.setRemark(use.getRemark()); - childList.add(child); - } - } else { - MatMaterialsExcelVo child = new MatMaterialsExcelVo(); - child.setRecipient(out.getRecipient()); - child.setOutNumber(out.getNumber()); - child.setOperator(out.getOperator()); - child.setShipper(out.getShipper()); - if (out.getCreateTime() != null) { - child.setReceivingDate(DateUtils.parseDateToStr(FormatsType.YYYY_MM_DD_DOT, out.getCreateTime())); - } - child.setResidue(out.getResidue()); - child.setDisposition(out.getDisposition()); - childList.add(child); - } - } - } else { - childList = materialOutList.stream() - .skip(1) - .map(out -> { - MatMaterialsExcelVo v = new MatMaterialsExcelVo(); - v.setRecipient(out.getRecipient()); - v.setOutNumber(out.getNumber()); - v.setOperator(out.getOperator()); - v.setShipper(out.getShipper()); - if (out.getCreateTime() != null) { - v.setReceivingDate(DateUtils.parseDateToStr(FormatsType.YYYY_MM_DD_DOT, out.getCreateTime())); - } - v.setResidue(out.getResidue()); - v.setDisposition(out.getDisposition()); - return v; - }).toList(); + List excelVoList = new ArrayList<>(); + + for (MatMaterials material : materials) { + MatMaterialsInventory put = putMap.get(material.getId()); + + List materialOutList = outList.stream() + .filter(inv -> inv.getMaterialsId().equals(material.getId())) + .toList(); + + boolean firstMaterialRow = true; // 记录材料是否是第一行 + + if (CollUtil.isEmpty(materialOutList)) { + excelVoList.add(buildVo(material, put, null, null, firstMaterialRow, false)); + continue; + } + + for (MatMaterialsInventory out : materialOutList) { + List outUseList = useMap.getOrDefault(out.getId(), List.of()); + boolean firstOutRow = true; // 记录出库是否是第一行 + + if (CollUtil.isEmpty(outUseList)) { + excelVoList.add(buildVo(material, put, out, null, firstMaterialRow, firstOutRow)); + firstMaterialRow = false; + } else { + for (MatMaterialsUseRecord use : outUseList) { + excelVoList.add(buildVo(material, put, out, use, firstMaterialRow, firstOutRow)); + firstMaterialRow = false; + firstOutRow = false; + } + } } - excelVoList.add(vo); - excelVoList.addAll(childList); } + return excelVoList; } + + + /** + * 构建一行导出数据 + */ + private MatMaterialsExcelVo buildVo(MatMaterials material, + MatMaterialsInventory put, + MatMaterialsInventory out, + MatMaterialsUseRecord use, + boolean showMaterial, + boolean showOut) { + MatMaterialsExcelVo vo = new MatMaterialsExcelVo(); + + // 材料信息(只显示一次) + if (showMaterial) { + vo.setMaterialsName(material.getMaterialsName()); + vo.setQuantityCount(material.getQuantityCount()); + + if (put != null) { + vo.setSupplier(put.getRecipient()); + vo.setPutNumber(put.getNumber()); + vo.setSigner(put.getOperator()); + if (put.getCreateTime() != null) { + vo.setPutTime(DateUtils.parseDateToStr(FormatsType.YYYY_MM_DD_DOT, put.getCreateTime())); + } + } + } + + // 出库信息(同一条出库只显示一次) + if (showOut && out != null) { + vo.setRecipient(out.getRecipient()); + vo.setOutNumber(out.getNumber()); + vo.setOperator(out.getOperator()); + vo.setShipper(out.getShipper()); + if (out.getCreateTime() != null) { + vo.setReceivingDate(DateUtils.parseDateToStr(FormatsType.YYYY_MM_DD_DOT, out.getCreateTime())); + } + vo.setResidue(out.getResidue()); + vo.setDisposition(out.getDisposition()); + } + + // 使用信息(每条都显示) + if (use != null) { + vo.setUsePart(use.getUsePart()); + vo.setUseNumber(use.getUseNumber()); + if (use.getCreateTime() != null) { + vo.setUseTime(DateUtils.parseDateToStr(FormatsType.YYYY_MM_DD_DOT, use.getCreateTime())); + } + vo.setSurplus(use.getResidueNumber()); + vo.setRemark(use.getRemark()); + } + + return vo; + } + + } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/out/service/impl/OutConstructionValueServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/out/service/impl/OutConstructionValueServiceImpl.java index a88c9254..fe4a3574 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/out/service/impl/OutConstructionValueServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/out/service/impl/OutConstructionValueServiceImpl.java @@ -1,5 +1,6 @@ package org.dromara.out.service.impl; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; @@ -32,6 +33,7 @@ import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; import java.math.BigDecimal; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; @@ -109,7 +111,13 @@ public class OutConstructionValueServiceImpl extends ServiceImpl subProjectIds = projectService.lambdaQuery() .eq(BusProject::getPId, projectId) .list(); - lqw.in(OutConstructionValue::getProjectId, projectId, subProjectIds); + if (CollUtil.isNotEmpty(subProjectIds)) { + List ids = new ArrayList<>(subProjectIds.stream().map(BusProject::getId).toList()); + ids.add(projectId); + lqw.in(OutConstructionValue::getProjectId, ids); + } else { + lqw.eq(OutConstructionValue::getProjectId, projectId); + } } lqw.eq(bo.getMatrixId() != null, OutConstructionValue::getMatrixId, bo.getMatrixId()); lqw.eq(bo.getProgressCategoryId() != null, OutConstructionValue::getProgressCategoryId, bo.getProgressCategoryId()); @@ -225,7 +233,7 @@ public class OutConstructionValueServiceImpl extends ServiceImpl readExcel(@RequestParam("file") MultipartFile file, Long projectId) { + List list = pgsConstructionSchedulePlanService.readExcel(file, projectId); + if (CollUtil.isNotEmpty(list)) { + List planList = list.stream().map(dto -> { + PgsConstructionSchedulePlan plan = new PgsConstructionSchedulePlan(); + BeanUtils.copyProperties(dto, plan); + return plan; + }).toList(); + return toAjax(pgsConstructionSchedulePlanService.saveBatch(planList)); + } + return toAjax(true); + } + /** * 获取施工进度计划详细信息 * diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/PgsConstructionSchedulePlan.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/PgsConstructionSchedulePlan.java index ba4cba9f..daa30ea1 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/PgsConstructionSchedulePlan.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/PgsConstructionSchedulePlan.java @@ -7,7 +7,7 @@ import lombok.EqualsAndHashCode; import org.dromara.common.mybatis.core.domain.BaseEntity; import java.io.Serial; -import java.util.Date; +import java.time.LocalDate; /** * 施工进度计划对象 pgs_construction_schedule_plan @@ -57,22 +57,22 @@ public class PgsConstructionSchedulePlan extends BaseEntity { /** * 预计开始时间 */ - private Date planStartDate; + private LocalDate planStartDate; /** * 预计结束时间 */ - private Date planEndDate; + private LocalDate planEndDate; /** * 实际开始时间 */ - private Date practicalStartDate; + private LocalDate practicalStartDate; /** * 实际结束时间 */ - private Date practicalEndDate; + private LocalDate practicalEndDate; /** * 状态 diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/dto/constructionscheduleplan/PgsConstructionSchedulePlanCreateReq.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/dto/constructionscheduleplan/PgsConstructionSchedulePlanCreateReq.java index e645fe2b..704a8eff 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/dto/constructionscheduleplan/PgsConstructionSchedulePlanCreateReq.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/dto/constructionscheduleplan/PgsConstructionSchedulePlanCreateReq.java @@ -1,13 +1,12 @@ package org.dromara.progress.domain.dto.constructionscheduleplan; -import com.fasterxml.jackson.annotation.JsonFormat; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Data; import java.io.Serial; import java.io.Serializable; -import java.util.Date; +import java.time.LocalDate; /** * @author lilemy @@ -49,26 +48,22 @@ public class PgsConstructionSchedulePlanCreateReq implements Serializable { /** * 预计开始时间 */ - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") - private Date planStartDate; + private LocalDate planStartDate; /** * 预计结束时间 */ - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") - private Date planEndDate; + private LocalDate planEndDate; /** * 实际开始时间 */ - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") - private Date practicalStartDate; + private LocalDate practicalStartDate; /** * 实际结束时间 */ - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") - private Date practicalEndDate; + private LocalDate practicalEndDate; /** * 状态 diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/dto/constructionscheduleplan/PgsConstructionSchedulePlanExcelDto.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/dto/constructionscheduleplan/PgsConstructionSchedulePlanExcelDto.java new file mode 100644 index 00000000..92fc6b8e --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/dto/constructionscheduleplan/PgsConstructionSchedulePlanExcelDto.java @@ -0,0 +1,71 @@ +package org.dromara.progress.domain.dto.constructionscheduleplan; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.time.LocalDate; + +/** + * @author lilemy + * @date 2025-09-06 17:12 + */ +@Data +@AllArgsConstructor +public class PgsConstructionSchedulePlanExcelDto { + + /** + * 项目ID + */ + private Long projectId; + + /** + * 父ID + */ + private Long parentId; + + /** + * 节点名称 + */ + private String nodeName; + + /** + * 对应项目结构 + */ + private Long projectStructure; + + /** + * 对应项目结构名称 + */ + private String projectStructureName; + + /** + * 预计开始时间 + */ + private LocalDate planStartDate; + + /** + * 预计结束时间 + */ + private LocalDate planEndDate; + + /** + * 实际开始时间 + */ + private LocalDate practicalStartDate; + + /** + * 实际结束时间 + */ + private LocalDate practicalEndDate; + + /** + * 状态 + */ + private String status; + + /** + * 备注 + */ + private String remark; + +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/dto/constructionscheduleplan/PgsConstructionSchedulePlanUpdateReq.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/dto/constructionscheduleplan/PgsConstructionSchedulePlanUpdateReq.java index bf595e88..c0975bc7 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/dto/constructionscheduleplan/PgsConstructionSchedulePlanUpdateReq.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/dto/constructionscheduleplan/PgsConstructionSchedulePlanUpdateReq.java @@ -1,11 +1,10 @@ package org.dromara.progress.domain.dto.constructionscheduleplan; -import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import java.io.Serial; import java.io.Serializable; -import java.util.Date; +import java.time.LocalDate; /** * @author lilemy @@ -40,19 +39,16 @@ public class PgsConstructionSchedulePlanUpdateReq implements Serializable { /** * 实际开始时间 */ - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") - private Date practicalStartDate; + private LocalDate practicalStartDate; /** * 实际结束时间 */ - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") - private Date practicalEndDate; + private LocalDate practicalEndDate; /** * 状态 */ - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") private String status; /** diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/vo/constructionscheduleplan/PgsConstructionSchedulePlanVo.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/vo/constructionscheduleplan/PgsConstructionSchedulePlanVo.java index 3a9469a1..2d970648 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/vo/constructionscheduleplan/PgsConstructionSchedulePlanVo.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/vo/constructionscheduleplan/PgsConstructionSchedulePlanVo.java @@ -2,7 +2,6 @@ package org.dromara.progress.domain.vo.constructionscheduleplan; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; -import com.fasterxml.jackson.annotation.JsonFormat; import io.github.linpeilie.annotations.AutoMapper; import lombok.Data; import org.dromara.common.excel.annotation.ExcelDictFormat; @@ -11,7 +10,7 @@ import org.dromara.progress.domain.PgsConstructionSchedulePlan; import java.io.Serial; import java.io.Serializable; -import java.util.Date; +import java.time.LocalDate; /** @@ -68,29 +67,25 @@ public class PgsConstructionSchedulePlanVo implements Serializable { * 预计开始时间 */ @ExcelProperty(value = "预计开始时间") - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "GMT+8") - private Date planStartDate; + private LocalDate planStartDate; /** * 预计结束时间 */ @ExcelProperty(value = "预计结束时间") - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "GMT+8") - private Date planEndDate; + private LocalDate planEndDate; /** * 实际开始时间 */ @ExcelProperty(value = "实际开始时间") - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "GMT+8") - private Date practicalStartDate; + private LocalDate practicalStartDate; /** * 实际结束时间 */ @ExcelProperty(value = "实际结束时间") - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "GMT+8") - private Date practicalEndDate; + private LocalDate practicalEndDate; /** * 状态 diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/IPgsConstructionSchedulePlanService.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/IPgsConstructionSchedulePlanService.java index e0e55ac4..0a8a2270 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/IPgsConstructionSchedulePlanService.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/IPgsConstructionSchedulePlanService.java @@ -2,11 +2,14 @@ package org.dromara.progress.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.IService; +import jakarta.servlet.http.HttpServletResponse; import org.dromara.progress.domain.PgsConstructionSchedulePlan; import org.dromara.progress.domain.dto.constructionscheduleplan.PgsConstructionSchedulePlanCreateReq; +import org.dromara.progress.domain.dto.constructionscheduleplan.PgsConstructionSchedulePlanExcelDto; import org.dromara.progress.domain.dto.constructionscheduleplan.PgsConstructionSchedulePlanQueryReq; import org.dromara.progress.domain.dto.constructionscheduleplan.PgsConstructionSchedulePlanUpdateReq; import org.dromara.progress.domain.vo.constructionscheduleplan.PgsConstructionSchedulePlanVo; +import org.springframework.web.multipart.MultipartFile; import java.util.Collection; import java.util.List; @@ -82,4 +85,20 @@ public interface IPgsConstructionSchedulePlanService extends IService getVoList(List constructionSchedulePlanList); + + /** + * 导出Excel + * + * @param projectId 项目id + */ + void exportExcelByProjectId(Long projectId, HttpServletResponse response); + + /** + * 读取Excel文件 + * + * @param file Excel文件 + * @param projectId 项目ID + * @return 读取的数据列表 + */ + List readExcel(MultipartFile file, Long projectId); } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/impl/PgsConstructionSchedulePlanServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/impl/PgsConstructionSchedulePlanServiceImpl.java index 599de90e..eb61ebc8 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/impl/PgsConstructionSchedulePlanServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/impl/PgsConstructionSchedulePlanServiceImpl.java @@ -2,23 +2,41 @@ package org.dromara.progress.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.dromara.common.core.constant.HttpStatus; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.ObjectUtils; import org.dromara.common.core.utils.StringUtils; +import org.dromara.design.domain.bo.DesUserBo; import org.dromara.progress.domain.PgsConstructionSchedulePlan; import org.dromara.progress.domain.dto.constructionscheduleplan.PgsConstructionSchedulePlanCreateReq; +import org.dromara.progress.domain.dto.constructionscheduleplan.PgsConstructionSchedulePlanExcelDto; import org.dromara.progress.domain.dto.constructionscheduleplan.PgsConstructionSchedulePlanQueryReq; import org.dromara.progress.domain.dto.constructionscheduleplan.PgsConstructionSchedulePlanUpdateReq; import org.dromara.progress.domain.vo.constructionscheduleplan.PgsConstructionSchedulePlanVo; import org.dromara.progress.mapper.PgsConstructionSchedulePlanMapper; import org.dromara.progress.service.IPgsConstructionSchedulePlanService; +import org.dromara.project.service.IBusProjectService; +import org.dromara.system.domain.vo.SysDictDataVo; +import org.dromara.system.service.ISysDictDataService; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; -import java.util.Collection; -import java.util.List; +import java.io.IOException; +import java.io.InputStream; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.*; /** * 施工进度计划Service业务层处理 @@ -26,8 +44,16 @@ import java.util.List; * @author lilemy * @date 2025-08-01 */ +@Slf4j @Service -public class PgsConstructionSchedulePlanServiceImpl extends ServiceImpl implements IPgsConstructionSchedulePlanService { +public class PgsConstructionSchedulePlanServiceImpl extends ServiceImpl + implements IPgsConstructionSchedulePlanService { + + @Resource + private IBusProjectService projectService; + + @Resource + private ISysDictDataService dictDataService; /** * 查询施工进度计划 @@ -146,4 +172,350 @@ public class PgsConstructionSchedulePlanServiceImpl extends ServiceImpl getVoList(List constructionSchedulePlanList) { return constructionSchedulePlanList.stream().map(this::getVo).toList(); } + + // region + + /** + * 导出Excel + * + * @param projectId 项目id + */ + @Override + public void exportExcelByProjectId(Long projectId, HttpServletResponse response) { + DesUserBo desUserBo = new DesUserBo(); + desUserBo.setProjectId(projectId); + Map projectStructureMap = projectService.getStructureAsList(projectId); + List dictDataVos = dictDataService.selectByDictType("project_construction_status"); + Map statusMap = new HashMap<>(); + for (SysDictDataVo vo : dictDataVos) { + statusMap.put(vo.getDictValue(), vo.getDictLabel()); + } + + + // 2. 设置响应头 + // 设置响应头,指定Excel格式和下载文件名 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("施工里程碑计划模版.xlsx", StandardCharsets.UTF_8)); + + Workbook workbook = new XSSFWorkbook(); + // 创建主 Sheet 和隐藏 Sheet + Sheet mainSheet = workbook.createSheet("施工里程碑计划模版"); + Sheet dataSheet = workbook.createSheet("DropdownData"); + workbook.setSheetHidden(workbook.getSheetIndex(dataSheet), true); + + // 3. 创建单元格样式 + CellStyle editableStyle = createEditableCellStyle(workbook); // 可编辑单元格样式 + CellStyle protectedStyle = createProtectedCellStyle(workbook); // 受保护单元格样式(ID列用 + //填充隐藏数据Sheet + int rowIndex = 0; + // 填充项目关联结构(A列和B列) + for (Map.Entry entry : projectStructureMap.entrySet()) { + Row row = dataSheet.createRow(rowIndex++); + row.createCell(0).setCellValue(entry.getValue()); + row.createCell(1).setCellValue(entry.getKey().toString()); + } + // 重置行索引,填充人员和人员ID(C列和D列) + rowIndex = 0; + for (Map.Entry entry : statusMap.entrySet()) { + Row row = dataSheet.getRow(rowIndex); + if (row == null) { + row = dataSheet.createRow(rowIndex); + } + row.createCell(4).setCellValue(entry.getValue()); + row.createCell(5).setCellValue(entry.getKey()); + rowIndex++; + } + // 主 Sheet 设置表头 + Row sheetRow = mainSheet.createRow(0); + String[] headers = {"节点名称", "对应项目结构", "对应项目结构编码", "预计开始时间(格式:2025-09-06)", + "预计结束时间", "实际开始时间", "实际结束时间", "状态", "状态编码", "备注"}; + for (int i = 0; i < headers.length; i++) { + Cell cell = sheetRow.createCell(i); + cell.setCellValue(headers[i]); + } + // 6. 设置专业下拉列表(第二列) + setMajorDropdown(mainSheet, projectStructureMap.size()); + setStatusDropdown(mainSheet, statusMap.size()); + String formulaTemplate = "IFERROR(INDEX(DropdownData!$B$1:$B$" + projectStructureMap.size() + ", MATCH(B{rowNum}, DropdownData!$A$1:$A$" + projectStructureMap.size() + ", 0)),\"\")"; + String formulaTemplate1 = "IFERROR(INDEX(DropdownData!$F$1:$F$" + statusMap.size() + ", MATCH(H{rowNum}, DropdownData!$E$1:$E$" + statusMap.size() + ", 0)),\"\")"; + for (int i = 1; i <= 1000; i++) { // 从第2行到101行 + Row row = mainSheet.createRow(i); + int currentRowNum = i + 1; // Excel行号从1开始 + String formula = formulaTemplate.replace("{rowNum}", String.valueOf(currentRowNum)); + + Cell cell = row.createCell(1); + cell.setCellStyle(editableStyle); //专业不锁定 + + Cell idCell = row.createCell(2); + idCell.setCellFormula(formula); + idCell.setCellStyle(protectedStyle); // 应用隐藏公式样式 + + String formula1 = formulaTemplate1.replace("{rowNum}", String.valueOf(currentRowNum)); + + Cell cell1 = row.createCell(7); + cell1.setCellStyle(editableStyle); //专业不锁定 + + Cell idCell1 = row.createCell(8); + idCell1.setCellFormula(formula1); + idCell1.setCellStyle(protectedStyle); // 应用隐藏公式样式 + + } + + + for (int i = 1; i <= 100; i++) { + Row row = mainSheet.getRow(i); + if (row == null) { + row = mainSheet.createRow(i); + } + Cell cell = row.createCell(0); + cell.setCellStyle(editableStyle); + + Cell cell3 = row.createCell(3); + cell3.setCellStyle(editableStyle); + + Cell cell4 = row.createCell(4); + cell4.setCellStyle(editableStyle); + + Cell cell5 = row.createCell(5); + cell5.setCellStyle(editableStyle); + + Cell cell6 = row.createCell(6); + cell6.setCellStyle(editableStyle); + + Cell cell9 = row.createCell(9); + cell9.setCellStyle(editableStyle); + + } + + + // 保护工作表,仅允许编辑未锁定的单元格 + mainSheet.protectSheet("123456"); // 空密码 + + // 核心:锁定表头(第1行)和前1列(包含ID列) + mainSheet.createFreezePane(0, 1, 0, 1); + + + // 调整列宽 + mainSheet.setColumnWidth(0, 20 * 256); + mainSheet.setColumnWidth(1, 20 * 280); + mainSheet.setColumnWidth(2, 20 * 280); + mainSheet.setColumnWidth(3, 20 * 400); + mainSheet.setColumnWidth(4, 20 * 200); + mainSheet.setColumnWidth(5, 20 * 200); + mainSheet.setColumnWidth(6, 20 * 200); + mainSheet.setColumnWidth(7, 20 * 200); + mainSheet.setColumnWidth(8, 20 * 100); + mainSheet.setColumnWidth(9, 20 * 200); + + // 直接写入响应输出流 + try { + workbook.write(response.getOutputStream()); + workbook.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + } + + + /** + * 创建可编辑单元格样式 + */ + private CellStyle createEditableCellStyle(Workbook workbook) { + CellStyle style = workbook.createCellStyle(); + style.setLocked(false); // 关键:允许编辑 + return style; + } + + /** + * 创建受保护单元格样式(用于ID列) + */ + private CellStyle createProtectedCellStyle(Workbook workbook) { + CellStyle style = workbook.createCellStyle(); + DataFormat dataFormat = workbook.createDataFormat(); + short formatIndex = dataFormat.getFormat("0"); // 匹配“自定义→0”格式 + style.setDataFormat(formatIndex); + style.setHidden(true); // 隐藏公式 + style.setLocked(true); + return style; + } + + /** + * 设置专业下拉列表(第二列,索引1) + */ + private void setMajorDropdown(Sheet mainSheet, int majorCount) { + DataValidationHelper helper = mainSheet.getDataValidationHelper(); + + // 专业数据范围:数据Sheet的A列(从第1行到专业数量行) + String majorRange = "DropdownData!$A$1:$A$" + majorCount; + + DataValidationConstraint constraint = helper.createFormulaListConstraint(majorRange); + // 作用范围:第2行到100行,第二列 + CellRangeAddressList addressList = new CellRangeAddressList(1, 1000, 1, 1); + + DataValidation validation = helper.createValidation(constraint, addressList); + validation.setShowErrorBox(true); + mainSheet.addValidationData(validation); + + } + + /** + * 设置专业下拉列表(第二列,索引1) + */ + private void setStatusDropdown(Sheet mainSheet, int majorCount) { + DataValidationHelper helper = mainSheet.getDataValidationHelper(); + + // 专业数据范围:数据Sheet的A列(从第1行到专业数量行) + String majorRange = "DropdownData!$E$1:$E$" + majorCount; + + DataValidationConstraint constraint = helper.createFormulaListConstraint(majorRange); + // 作用范围:第2行到100行,第二列 + CellRangeAddressList addressList = new CellRangeAddressList(1, 1000, 7, 7); + + DataValidation validation = helper.createValidation(constraint, addressList); + validation.setShowErrorBox(true); + mainSheet.addValidationData(validation); + + } + + // endregion + + + /** + * 读取Excel文件 + * + * @param file Excel文件 + * @param projectId 项目ID + * @return 读取的数据列表 + */ + @Override + public List readExcel(MultipartFile file, Long projectId) { + List dataList = new ArrayList<>(); + + try (InputStream inputStream = file.getInputStream(); + XSSFWorkbook workbook = new XSSFWorkbook(inputStream)) { + + XSSFSheet sheet = workbook.getSheetAt(0); + + // 从第二行(index=1)开始读取数据,跳过表头 + for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) { + Row row = sheet.getRow(rowIndex); + if (hasValidData(row)) { + + String nodeName = getCellValue(row.getCell(0)); + String projectStructureName = getCellValue(row.getCell(1)); + Long projectStructure = Long.valueOf(getCellValue(row.getCell(2))); + LocalDate planStartDate = getLocalDateValue(row.getCell(3)); + LocalDate planEndDate = getLocalDateValue(row.getCell(4)); + LocalDate practicalStartDate = getLocalDateValue(row.getCell(5)); + LocalDate practicalEndDate = getLocalDateValue(row.getCell(6)); + String status = getCellValue(row.getCell(8)); + String remark = getCellValue(row.getCell(9)); + + PgsConstructionSchedulePlanExcelDto excelData = new PgsConstructionSchedulePlanExcelDto( + projectId, 0L, nodeName, projectStructure, projectStructureName, + planStartDate, planEndDate, practicalStartDate, practicalEndDate, status, remark + ); + dataList.add(excelData); + } + } + } catch (Exception e) { + log.error("读取表格数据失败", e); + throw new ServiceException("读取表格数据失败"); + } + + return dataList; + } + + private static boolean hasValidData(Row row) { + // 遍历行中的所有单元格 + for (int cellIndex = 0; cellIndex < row.getLastCellNum(); cellIndex++) { + Cell cell = row.getCell(cellIndex, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); + String cellValue = getCellValue(cell).trim(); + + // 只要有一个单元格有非空值,就认为是有效行 + if (!cellValue.isEmpty()) { + return true; + } + } + return false; + } + + private static String getCellValue(Cell cell) { + if (cell == null) { + return ""; + } + // 使用CellType枚举判断单元格类型(POI 4.0+版本推荐方式) + CellType cellType = cell.getCellType(); + // 对于公式单元格,获取其计算结果的类型 + if (cellType == CellType.FORMULA) { + cellType = cell.getCachedFormulaResultType(); + } + + switch (cellType) { + case STRING: + return cell.getStringCellValue().trim(); + case NUMERIC: + if (DateUtil.isCellDateFormatted(cell)) { + Date date = cell.getDateCellValue(); + return date.toString(); + } else { + // 处理数字类型,避免科学计数法 + return String.valueOf((long) cell.getNumericCellValue()); + } + case BOOLEAN: + return String.valueOf(cell.getBooleanCellValue()); + default: + return ""; + } + } + + private static LocalDate getLocalDateValue(Cell cell) { + if (cell == null) { + return null; + } + + CellType cellType = cell.getCellType(); + if (cellType == CellType.FORMULA) { + cellType = cell.getCachedFormulaResultType(); + } + + try { + if (cellType == CellType.NUMERIC) { + if (DateUtil.isCellDateFormatted(cell)) { + Date date = cell.getDateCellValue(); + return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + } else { + // 如果是数字但不是日期,就尝试转为 LocalDate (例如 20250730) + double numericValue = cell.getNumericCellValue(); + String text = String.valueOf((long) numericValue); + return LocalDate.parse(text); + } + } else if (cellType == CellType.STRING) { + String text = cell.getStringCellValue().trim(); + if (text.isEmpty()) { + return null; + } + // 尝试解析不同格式 + try { + return LocalDate.parse(text); // 默认 ISO 格式 yyyy-MM-dd + } catch (Exception e) { + // 如果 Excel 是 yyyy/MM/dd 或 yyyy.MM.dd,可以额外处理 + try { + return LocalDate.parse(text, java.time.format.DateTimeFormatter.ofPattern("yyyy/MM/dd")); + } catch (Exception ignored) { + } + try { + return LocalDate.parse(text, java.time.format.DateTimeFormatter.ofPattern("yyyy.MM.dd")); + } catch (Exception ignored) { + } + return null; // 不识别的格式就返回 null + } + } + } catch (Exception e) { + return null; + } + return null; + } } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/impl/PgsProgressPlanDetailServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/impl/PgsProgressPlanDetailServiceImpl.java index da9fe134..1fa7c83a 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/impl/PgsProgressPlanDetailServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/impl/PgsProgressPlanDetailServiceImpl.java @@ -809,9 +809,15 @@ public class PgsProgressPlanDetailServiceImpl extends ServiceImpl { */ BusProjectStructureVo getStructure(Long projectId); + /** + * 获取项目结构 + * + * @param projectId 项目id + * @return 结构(key 主键id,value 名称) + */ + Map getStructureAsList(Long projectId); + /** * 改变项目所属用户 * diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusProjectServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusProjectServiceImpl.java index ef6faba1..232f5372 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusProjectServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/project/service/impl/BusProjectServiceImpl.java @@ -1066,6 +1066,57 @@ public class BusProjectServiceImpl extends ServiceImpl getStructureAsList(Long projectId) { + Map map = new HashMap<>(); + // 获取项目 + BusProject project = this.getById(projectId); + if (project == null) { + throw new ServiceException("项目信息不存在", HttpStatus.NOT_FOUND); + } + map.put(project.getId(), "项目:" + project.getProjectName()); + + // 获取项目子项 + List subProjects = this.lambdaQuery() + .eq(BusProject::getPId, projectId) + .list(); + if (CollUtil.isNotEmpty(subProjects)) { + for (BusProject subProject : subProjects) { + map.put(subProject.getId(), "子项目:" + subProject.getProjectName()); + } + // 获取方阵信息 + List matrixList = matrixService.lambdaQuery() + .in(FacMatrix::getProjectId, subProjects.stream().map(BusProject::getId).toList()) + .list(); + if (CollUtil.isNotEmpty(matrixList)) { + for (FacMatrix matrix : matrixList) { + map.put(matrix.getId(), "方阵:" + matrix.getMatrixName()); + } + } + // 获取分项工程 + List progressCategoryList = progressCategoryService.lambdaQuery() + .in(PgsProgressCategory::getMatrixId, matrixList.stream().map(FacMatrix::getId).toList()) + .list(); + if (CollUtil.isNotEmpty(progressCategoryList)) { + for (PgsProgressCategory progressCategory : progressCategoryList) { + String matrixName = progressCategory.getMatrixName(); + if (StringUtils.isNotBlank(matrixName)) { + map.put(progressCategory.getId(), "分项工程:" + matrixName + "-" + progressCategory.getName()); + } else { + map.put(progressCategory.getId(), "分项工程:" + progressCategory.getName()); + } + } + } + } + return map; + } + /** * 改变项目所属用户 *