物资、里程碑进度execl导出

This commit is contained in:
lcj
2025-09-06 19:07:10 +08:00
parent d7616960c6
commit 96d0406931
16 changed files with 805 additions and 223 deletions

View File

@ -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<DesVolumeCatalogMap
* @return 卷册目录
*/
@Override
public DesVolumeCatalogVo queryById(Long id,String type) {
public DesVolumeCatalogVo queryById(Long id, String type) {
DesVolumeCatalog volumeCatalog = this.getById(id);
if (volumeCatalog == null) {
throw new ServiceException("卷册目录信息不存在", HttpStatus.NOT_FOUND);
}
return this.getVo(volumeCatalog,type);
return this.getVo(volumeCatalog, type);
}
/**
@ -120,7 +118,7 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl<DesVolumeCatalogMap
*/
@Override
public List<DesVolumeCatalogVo> 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<DesVolumeCatalogMap
* @return 卷册目录文件列表
*/
@Override
public List<DesVolumeFile> queryFileListById(Long id,String type) {
public List<DesVolumeFile> 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<DesVolumeCatalogMap
volumeCatalog.setSerialNumber(serialNumber);
boolean save = this.save(volumeCatalog);
if (!save) {
RedisUtils.setCacheObject("DesVolumeCatalog:serialNumber",serialNumber);
RedisUtils.setCacheObject("DesVolumeCatalog:serialNumber", serialNumber);
throw new ServiceException("卷册目录新增失败", HttpStatus.ERROR);
}
return true;
@ -209,7 +207,7 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl<DesVolumeCatalogMap
if (count > 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<DesVolumeCatalogMap
desVolumeCatalogs.add(volumeCatalog);
}
baseMapper.insertBatch(desVolumeCatalogs);
RedisUtils.setCacheObject("DesVolumeCatalog:serialNumber",serialNumber);
RedisUtils.setCacheObject("DesVolumeCatalog:serialNumber", serialNumber);
}
/**
@ -290,14 +288,14 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl<DesVolumeCatalogMap
* @return 卷册目录封装对象
*/
@Override
public DesVolumeCatalogVo getVo(DesVolumeCatalog volumeCatalog,String type) {
public DesVolumeCatalogVo getVo(DesVolumeCatalog volumeCatalog, String type) {
DesVolumeCatalogVo vo = new DesVolumeCatalogVo();
if (volumeCatalog == null) {
return vo;
}
BeanUtils.copyProperties(volumeCatalog, vo);
// 关联文件信息
List<DesVolumeFile> volumeFiles = this.queryFileListById(volumeCatalog.getDesign(),type);
List<DesVolumeFile> volumeFiles = this.queryFileListById(volumeCatalog.getDesign(), type);
vo.setFileVoList(volumeFileService.getVoList(volumeFiles));
// 关联查阅人信息
List<DesVolumeFileViewer> allViewerList = volumeFileViewerService.lambdaQuery()
@ -341,7 +339,7 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl<DesVolumeCatalogMap
lqw.like(StringUtils.isNotBlank(documentName), DesVolumeCatalog::getDocumentName, documentName);
lqw.eq(StringUtils.isNotBlank(volumeNumber), DesVolumeCatalog::getVolumeNumber, volumeNumber);
lqw.eq(ObjectUtils.isNotEmpty(projectId), DesVolumeCatalog::getProjectId, projectId);
lqw.eq(StringUtils.isNotBlank(auditStatus),DesVolumeCatalog::getAuditStatus, auditStatus);
lqw.eq(StringUtils.isNotBlank(auditStatus), DesVolumeCatalog::getAuditStatus, auditStatus);
return lqw;
}
@ -361,7 +359,7 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl<DesVolumeCatalogMap
if (CollUtil.isEmpty(volumeCatalogList)) {
return volumeCatalogVoPage;
}
List<DesVolumeCatalogVo> volumeCatalogVoList = volumeCatalogList.stream().map(vo-> getVo(vo,null)).toList();
List<DesVolumeCatalogVo> volumeCatalogVoList = volumeCatalogList.stream().map(vo -> getVo(vo, null)).toList();
// 查询文件数量
LambdaQueryWrapper<DesVolumeFile> fileQueryWrapper = new LambdaQueryWrapper<>();
@ -369,9 +367,9 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl<DesVolumeCatalogMap
fileQueryWrapper.clear();
fileQueryWrapper.eq(DesVolumeFile::getVolumeCatalogId, desVolumeCatalogVo.getDesign());
List<DesVolumeFile> 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,7 +414,7 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl<DesVolumeCatalogMap
.orderByDesc(DesVolumeFile::getCreateTime)
);
if(list.isEmpty()){
if (list.isEmpty()) {
return Collections.emptyList();
}
List<Long> list1 = list.stream().map(DesVolumeFile::getVolumeCatalogId).distinct().toList();
@ -435,21 +433,15 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl<DesVolumeCatalogMap
Map<String, String> uniqueMajors = desUserService.getUserMajor(desUserBo);
Map<String, String> 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<DesVolumeCatalogMap
// 主 Sheet 设置表头
Row sheetRow = mainSheet.createRow(0);
String[] headers = {"子项名称", "专业", "专业编码",
"人员", "人员编码", "卷册号","资料名称","计划出图时间"};
"人员", "人员编码", "卷册号", "资料名称", "计划出图时间"};
for (int i = 0; i < headers.length; i++) {
Cell cell = sheetRow.createCell(i);
cell.setCellValue(headers[i]);
}
// 6. 设置专业下拉列表(第二列)
setMajorDropdown(mainSheet,uniqueMajors.size());
setMajorDropdown(mainSheet, uniqueMajors.size());
// 8. 设置人员下拉列表(第四列)
setPersonDropdown(mainSheet, userList.size());
String formulaTemplate = "IFERROR(INDEX(DropdownData!$B$1:$B$" + uniqueMajors.size() + ", MATCH(B{rowNum}, DropdownData!$A$1:$A$" + uniqueMajors.size()+ ", 0)),\"\")";
String formulaTemplate = "IFERROR(INDEX(DropdownData!$B$1:$B$" + uniqueMajors.size() + ", MATCH(B{rowNum}, DropdownData!$A$1:$A$" + uniqueMajors.size() + ", 0)),\"\")";
String formulaTemplate1 = "IFERROR(INDEX(DropdownData!$D$1:$D$" + userList.size() + ", MATCH(D{rowNum}, DropdownData!$C$1:$C$" + userList.size()+ ", 0)),\"\")";
String formulaTemplate1 = "IFERROR(INDEX(DropdownData!$D$1:$D$" + userList.size() + ", MATCH(D{rowNum}, DropdownData!$C$1:$C$" + userList.size() + ", 0)),\"\")";
for (int i = 1; i <= 100; i++) { // 从第2行到101行
@ -544,7 +535,6 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl<DesVolumeCatalogMap
}
// 保护工作表,仅允许编辑未锁定的单元格
mainSheet.protectSheet("123456"); // 空密码
@ -573,7 +563,6 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl<DesVolumeCatalogMap
}
/**
* 创建可编辑单元格样式
*/
@ -599,7 +588,7 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl<DesVolumeCatalogMap
/**
* 设置专业下拉列表第二列索引1
*/
private void setMajorDropdown(Sheet mainSheet,int majorCount) {
private void setMajorDropdown(Sheet mainSheet, int majorCount) {
DataValidationHelper helper = mainSheet.getDataValidationHelper();
// 专业数据范围数据Sheet的A列从第1行到专业数量行
@ -616,7 +605,6 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl<DesVolumeCatalogMap
}
/**
* 设置人员下拉列表第四列索引3
*/
@ -655,11 +643,11 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl<DesVolumeCatalogMap
}
desVolumeCatalog.setAuditStatus(processEvent.getStatus());
//如果完成,变更状态为已完成
if (processEvent.getStatus().equals("finish")){
if (processEvent.getStatus().equals("finish")) {
desVolumeCatalog.setDesignState("1");
}
if(BusinessStatusEnum.FINISH.getStatus().equals(processEvent.getStatus())){
if (BusinessStatusEnum.FINISH.getStatus().equals(processEvent.getStatus())) {
ArrayList<DesDrawing> desDrawings = new ArrayList<>();
List<DesVolumeFile> list = volumeFileService.lambdaQuery().eq(DesVolumeFile::getVolumeCatalogId, id).list();
@ -672,7 +660,7 @@ public class DesVolumeCatalogServiceImpl extends ServiceImpl<DesVolumeCatalogMap
desDrawings.add(desDrawing);
//异步处理二维码
self.addQRCodeToPDF(desVolumeFile.getId(),false)
self.addQRCodeToPDF(desVolumeFile.getId(), false)
.thenAccept(result -> log.info("图纸[{}-{} ]添加二维码成功", desVolumeFile.getFileName(), desVolumeFile.getId()))
.exceptionally(ex -> {
log.error("图纸[{}-{}]添加二维码失败", desVolumeFile.getFileName(), desVolumeFile.getId(), ex);

View File

@ -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;
/**
* 入库时间
*/

View File

@ -170,12 +170,14 @@ public interface IMatMaterialsService extends IService<MatMaterials> {
* 获取材料使用详情列表
*
* @param materials 材料
* @param inventoryList 材料库存列表
* @param putList 材料入库列表
* @param outList 材料出库列表
* @param useList 材料使用列表
* @return 材料使用详情列表
*/
List<MatMaterialsUseDetailVo> getUseDetailList(List<MatMaterials> materials,
List<MatMaterialsInventory> inventoryList,
List<MatMaterialsInventory> putList,
List<MatMaterialsInventory> outList,
List<MatMaterialsUseRecord> useList);
/**

View File

@ -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,84 +615,190 @@ public class MatMaterialsServiceImpl extends ServiceImpl<MatMaterialsMapper, Mat
result.getSize(),
result.getTotal()
);
/* if (CollUtil.isEmpty(materialsList)) {
return materialsVoPage;
}*/
return null;
if (CollUtil.isEmpty(materialsList)) {
return TableDataInfo.build(materialsVoPage);
}
// 查询材料出入库列表
List<Long> ids = materialsList.stream().map(MatMaterials::getId).toList();
List<MatMaterialsInventory> materialsInventoryList = materialsInventoryService.lambdaQuery()
.in(MatMaterialsInventory::getMaterialsId, ids)
.list();
List<MatMaterialsInventory> putList = materialsInventoryList.stream()
.filter(inventory -> inventory.getOutPut().equals(MatMaterialsInventoryOutPutEnum.PUT.getValue()))
.toList();
// 查询使用列表
List<MatMaterialsInventory> outList = materialsInventoryList.stream()
.filter(inventory -> inventory.getOutPut().equals(MatMaterialsInventoryOutPutEnum.OUT.getValue()))
.toList();
List<MatMaterialsUseRecord> useList = new ArrayList<>();
if (CollUtil.isNotEmpty(outList)) {
List<Long> outIds = outList.stream().map(MatMaterialsInventory::getId).toList();
useList = materialsUseRecordService.lambdaQuery()
.in(MatMaterialsUseRecord::getInventoryId, outIds)
.list();
}
List<MatMaterialsUseDetailVo> useDetailList = this.getUseDetailList(materialsList, putList, outList, useList);
materialsVoPage.setRecords(useDetailList);
return TableDataInfo.build(materialsVoPage);
}
/**
* 获取材料使用详情列表
*
* @param materials 材料
* @param inventoryList 材料库存列表
* @param putList 材料入库列表
* @param outList 材料出库列表
* @param useList 材料使用列表
* @return 材料使用详情列表
*/
@Override
public List<MatMaterialsUseDetailVo> getUseDetailList(List<MatMaterials> materials, List<MatMaterialsInventory> inventoryList, List<MatMaterialsUseRecord> useList) {
return List.of();
public List<MatMaterialsUseDetailVo> getUseDetailList(List<MatMaterials> materials,
List<MatMaterialsInventory> putList,
List<MatMaterialsInventory> outList,
List<MatMaterialsUseRecord> useList) {
Map<Long, MatMaterialsInventory> 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<MatMaterialsInventory> outs = outList.stream()
.filter(inventory -> inventory.getMaterialsId().equals(id))
.toList();
if (CollUtil.isNotEmpty(outs)) {
List<MatMaterialsInventoryOutUseVo> outUseVos = outs.stream().map(inventory -> {
MatMaterialsInventoryOutUseVo outUseVo = new MatMaterialsInventoryOutUseVo();
BeanUtils.copyProperties(inventory, outUseVo);
if (CollUtil.isNotEmpty(useList)) {
List<MatMaterialsUseRecord> uses = useList.stream()
.filter(record -> record.getInventoryId().equals(inventory.getId()))
.toList();
List<MatMaterialsUseRecordByOutVo> 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<MatMaterialsExcelVo> queryExcelList(MatMaterialsQueryReq req) {
// 查询数据库
LambdaQueryWrapper<MatMaterials> lqw = this.buildQueryWrapper(req);
List<MatMaterials> materials = this.list(lqw);
if (CollUtil.isEmpty(materials)) {
return List.of();
}
// 获取材料主键
List<Long> ids = materials.stream().map(MatMaterials::getId).toList();
// 获取出入库记录
List<MatMaterialsInventory> inventoryList = materialsInventoryService.lambdaQuery()
.in(MatMaterialsInventory::getMaterialsId, ids)
.list();
// 获取入库记录
List<MatMaterialsInventory> putList = inventoryList.stream()
.filter(inventory -> inventory.getOutPut().equals(MatMaterialsInventoryOutPutEnum.PUT.getValue()))
.toList();
Map<Long, MatMaterialsInventory> putMap = putList.stream()
.collect(Collectors.toMap(MatMaterialsInventory::getMaterialsId, inventory -> inventory));
// 获取使用记录
Map<Long, MatMaterialsInventory> putMap = inventoryList.stream()
.filter(inv -> inv.getOutPut().equals(MatMaterialsInventoryOutPutEnum.PUT.getValue()))
.collect(Collectors.toMap(MatMaterialsInventory::getMaterialsId, Function.identity()));
List<MatMaterialsInventory> outList = inventoryList.stream()
.filter(inventory -> inventory.getOutPut().equals(MatMaterialsInventoryOutPutEnum.OUT.getValue()))
.filter(inv -> inv.getOutPut().equals(MatMaterialsInventoryOutPutEnum.OUT.getValue()))
.toList();
List<MatMaterialsUseRecord> useList = new ArrayList<>();
if (CollUtil.isEmpty(outList)) {
List<Long> outIds = outList.stream().map(MatMaterialsInventory::getId).distinct().toList();
// 获取使用记录
useList = materialsUseRecordService.lambdaQuery()
Map<Long, List<MatMaterialsUseRecord>> useMap = new HashMap<>();
if (CollUtil.isNotEmpty(outList)) {
List<Long> outIds = outList.stream().map(MatMaterialsInventory::getId).toList();
List<MatMaterialsUseRecord> useList = materialsUseRecordService.lambdaQuery()
.in(MatMaterialsUseRecord::getInventoryId, outIds)
.list();
useMap = useList.stream().collect(Collectors.groupingBy(MatMaterialsUseRecord::getInventoryId));
}
List<MatMaterialsExcelVo> excelVoList = new ArrayList<>();
for (MatMaterials material : materials) {
MatMaterialsInventory put = putMap.get(material.getId());
List<MatMaterialsInventory> 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<MatMaterialsUseRecord> 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;
}
}
}
}
return excelVoList;
}
/**
* 构建一行导出数据
*/
private MatMaterialsExcelVo buildVo(MatMaterials material,
MatMaterialsInventory put,
MatMaterialsInventory out,
MatMaterialsUseRecord use,
boolean showMaterial,
boolean showOut) {
MatMaterialsExcelVo vo = new MatMaterialsExcelVo();
Long id = material.getId();
// 材料信息(只显示一次)
if (showMaterial) {
vo.setMaterialsName(material.getMaterialsName());
vo.setQuantityCount(material.getQuantityCount());
// 拼接入库记录
MatMaterialsInventory put = putMap.get(id);
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()));
}
List<MatMaterialsInventory> materialOutList = outList.stream()
.filter(inventory -> inventory.getMaterialsId().equals(id))
.toList();
List<MatMaterialsUseRecord> materialUseList = new ArrayList<>();
if (CollUtil.isNotEmpty(materialOutList)) {
// 拼接第一条出库数据
MatMaterialsInventory out = materialOutList.getFirst();
}
}
// 出库信息(同一条出库只显示一次)
if (showOut && out != null) {
vo.setRecipient(out.getRecipient());
vo.setOutNumber(out.getNumber());
vo.setOperator(out.getOperator());
@ -700,89 +808,21 @@ public class MatMaterialsServiceImpl extends ServiceImpl<MatMaterialsMapper, Mat
}
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<MatMaterialsExcelVo> childList = new ArrayList<>();
if (CollUtil.isNotEmpty(materialUseList)) {
for (int i = 1; i < materialOutList.size(); i++) {
MatMaterialsInventory out = materialOutList.get(i);
List<MatMaterialsUseRecord> 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();
// 使用信息(每条都显示)
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()));
}
excelVoList.add(vo);
excelVoList.addAll(childList);
vo.setSurplus(use.getResidueNumber());
vo.setRemark(use.getRemark());
}
return excelVoList;
return vo;
}
}

View File

@ -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<OutConstruction
List<BusProject> subProjectIds = projectService.lambdaQuery()
.eq(BusProject::getPId, projectId)
.list();
lqw.in(OutConstructionValue::getProjectId, projectId, subProjectIds);
if (CollUtil.isNotEmpty(subProjectIds)) {
List<Long> 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<OutConstruction
vo.setProjectName(busProjectVo.getProjectName());
//查询方阵以及子项目
if (vo.getMatrixId() != null) {
if (vo.getMatrixId() != null && vo.getMatrixId() != 0) {
FacMatrix facMatrix = facMatrixService.getById(vo.getMatrixId());
vo.setMatrixName(facMatrix.getMatrixName());
}

View File

@ -1,6 +1,7 @@
package org.dromara.progress.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.collection.CollUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
@ -13,13 +14,17 @@ 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.web.core.BaseController;
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.service.IPgsConstructionSchedulePlanService;
import org.springframework.beans.BeanUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@ -58,6 +63,36 @@ public class PgsConstructionSchedulePlanController extends BaseController {
ExcelUtil.exportExcel(list, "施工进度计划", PgsConstructionSchedulePlanVo.class, response);
}
/**
* 根据项目id导出施工进度计划模版
*/
@SaCheckPermission("progress:constructionSchedulePlan:exportTemplate")
@Log(title = "施工进度计划", businessType = BusinessType.EXPORT)
@PostMapping("/exportTemplate/{projectId}")
public void exportExcelByProjectId(@NotNull(message = "项目id不能为空")
@PathVariable Long projectId, HttpServletResponse response) {
pgsConstructionSchedulePlanService.exportExcelByProjectId(projectId, response);
}
/**
* 读取施工进度计划模版
*/
@SaCheckPermission("progress:constructionSchedulePlan:readTemplate")
@Log(title = "施工进度计划", businessType = BusinessType.IMPORT)
@PostMapping("/readTemplate")
public R<Void> readExcel(@RequestParam("file") MultipartFile file, Long projectId) {
List<PgsConstructionSchedulePlanExcelDto> list = pgsConstructionSchedulePlanService.readExcel(file, projectId);
if (CollUtil.isNotEmpty(list)) {
List<PgsConstructionSchedulePlan> planList = list.stream().map(dto -> {
PgsConstructionSchedulePlan plan = new PgsConstructionSchedulePlan();
BeanUtils.copyProperties(dto, plan);
return plan;
}).toList();
return toAjax(pgsConstructionSchedulePlanService.saveBatch(planList));
}
return toAjax(true);
}
/**
* 获取施工进度计划详细信息
*

View File

@ -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;
/**
* 状态

View File

@ -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;
/**
* 状态

View File

@ -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;
}

View File

@ -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;
/**

View File

@ -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;
/**
* 状态

View File

@ -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<PgsConstru
* @return 施工进度计划分页对象视图
*/
List<PgsConstructionSchedulePlanVo> getVoList(List<PgsConstructionSchedulePlan> constructionSchedulePlanList);
/**
* 导出Excel
*
* @param projectId 项目id
*/
void exportExcelByProjectId(Long projectId, HttpServletResponse response);
/**
* 读取Excel文件
*
* @param file Excel文件
* @param projectId 项目ID
* @return 读取的数据列表
*/
List<PgsConstructionSchedulePlanExcelDto> readExcel(MultipartFile file, Long projectId);
}

View File

@ -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<PgsConstructionSchedulePlanMapper, PgsConstructionSchedulePlan> implements IPgsConstructionSchedulePlanService {
public class PgsConstructionSchedulePlanServiceImpl extends ServiceImpl<PgsConstructionSchedulePlanMapper, PgsConstructionSchedulePlan>
implements IPgsConstructionSchedulePlanService {
@Resource
private IBusProjectService projectService;
@Resource
private ISysDictDataService dictDataService;
/**
* 查询施工进度计划
@ -146,4 +172,350 @@ public class PgsConstructionSchedulePlanServiceImpl extends ServiceImpl<PgsConst
public List<PgsConstructionSchedulePlanVo> getVoList(List<PgsConstructionSchedulePlan> 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<Long, String> projectStructureMap = projectService.getStructureAsList(projectId);
List<SysDictDataVo> dictDataVos = dictDataService.selectByDictType("project_construction_status");
Map<String, String> 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<Long, String> entry : projectStructureMap.entrySet()) {
Row row = dataSheet.createRow(rowIndex++);
row.createCell(0).setCellValue(entry.getValue());
row.createCell(1).setCellValue(entry.getKey().toString());
}
// 重置行索引填充人员和人员IDC列和D列
rowIndex = 0;
for (Map.Entry<String, String> 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<PgsConstructionSchedulePlanExcelDto> readExcel(MultipartFile file, Long projectId) {
List<PgsConstructionSchedulePlanExcelDto> 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;
}
}

View File

@ -809,9 +809,15 @@ public class PgsProgressPlanDetailServiceImpl extends ServiceImpl<PgsProgressPla
value.setProjectId(planDetail.getProjectId());
value.setMatrixId(category.getMatrixId());
value.setProgressCategoryId(progressCategoryId);
int artificialNum = planDetail.getFinishedNumber().intValue();
BigDecimal finishedNumber = planDetail.getFinishedNumber();
BigDecimal aiFill = planDetail.getAiFill();
// 如果完成数量为0, 则不保存
if (finishedNumber.compareTo(BigDecimal.ZERO) == 0 && aiFill.compareTo(BigDecimal.ZERO) == 0) {
continue;
}
int artificialNum = finishedNumber.intValue();
value.setArtificialNum(artificialNum);
int uavNum = planDetail.getAiFill().intValue();
int uavNum = aiFill.intValue();
value.setUavNum(uavNum);
value.setPlanNum(planDetail.getPlanNumber().intValue());
value.setReportDate(LocalDate.now());

View File

@ -11,6 +11,7 @@ import org.dromara.project.domain.vo.project.*;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
/**
@ -197,6 +198,14 @@ public interface IBusProjectService extends IService<BusProject> {
*/
BusProjectStructureVo getStructure(Long projectId);
/**
* 获取项目结构
*
* @param projectId 项目id
* @return 结构(key 主键idvalue 名称)
*/
Map<Long, String> getStructureAsList(Long projectId);
/**
* 改变项目所属用户
*

View File

@ -1066,6 +1066,57 @@ public class BusProjectServiceImpl extends ServiceImpl<BusProjectMapper, BusProj
return vo;
}
/**
* 获取项目结构
*
* @param projectId 项目id
* @return 结构(key 主键idvalue 名称)
*/
@Override
public Map<Long, String> getStructureAsList(Long projectId) {
Map<Long, String> map = new HashMap<>();
// 获取项目
BusProject project = this.getById(projectId);
if (project == null) {
throw new ServiceException("项目信息不存在", HttpStatus.NOT_FOUND);
}
map.put(project.getId(), "项目:" + project.getProjectName());
// 获取项目子项
List<BusProject> subProjects = this.lambdaQuery()
.eq(BusProject::getPId, projectId)
.list();
if (CollUtil.isNotEmpty(subProjects)) {
for (BusProject subProject : subProjects) {
map.put(subProject.getId(), "子项目:" + subProject.getProjectName());
}
// 获取方阵信息
List<FacMatrix> 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<PgsProgressCategory> 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;
}
/**
* 改变项目所属用户
*