设计计划
This commit is contained in:
@ -0,0 +1,146 @@
|
|||||||
|
package org.dromara.design.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;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import org.dromara.common.core.domain.R;
|
||||||
|
import org.dromara.common.excel.utils.ExcelUtil;
|
||||||
|
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||||
|
import org.dromara.common.log.annotation.Log;
|
||||||
|
import org.dromara.common.log.enums.BusinessType;
|
||||||
|
import org.dromara.common.web.core.BaseController;
|
||||||
|
import org.dromara.design.domain.DesConstructionSchedulePlan;
|
||||||
|
import org.dromara.design.domain.dto.constructionscheduleplan.*;
|
||||||
|
import org.dromara.design.domain.vo.DesConstructionSchedulePlanVo;
|
||||||
|
import org.dromara.design.service.IDesConstructionSchedulePlanService;
|
||||||
|
|
||||||
|
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设计计划
|
||||||
|
*
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-08-01
|
||||||
|
*/
|
||||||
|
@Validated
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/design/constructionSchedulePlan")
|
||||||
|
public class DesConstructionSchedulePlanController extends BaseController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IDesConstructionSchedulePlanService desConstructionSchedulePlanService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询设计计划列表
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("design:constructionSchedulePlan:list")
|
||||||
|
@GetMapping("/list")
|
||||||
|
public R<List<DesConstructionSchedulePlanVo>> list(DesConstructionSchedulePlanQueryReq req) {
|
||||||
|
List<DesConstructionSchedulePlanVo> list = desConstructionSchedulePlanService.queryList(req);
|
||||||
|
return R.ok(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出设计计划列表
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("design:constructionSchedulePlan:export")
|
||||||
|
@Log(title = "施工进度计划", businessType = BusinessType.EXPORT)
|
||||||
|
@PostMapping("/export")
|
||||||
|
public void export(DesConstructionSchedulePlanQueryReq req, HttpServletResponse response) {
|
||||||
|
List<DesConstructionSchedulePlanVo> list = desConstructionSchedulePlanService.queryList(req);
|
||||||
|
ExcelUtil.exportExcel(list, "施工进度计划", DesConstructionSchedulePlanVo.class, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据项目id导出设计计划模版
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("design:constructionSchedulePlan:exportTemplate")
|
||||||
|
@Log(title = "施工进度计划", businessType = BusinessType.EXPORT)
|
||||||
|
@PostMapping("/exportTemplate/{projectId}")
|
||||||
|
public void exportExcelByProjectId(@NotNull(message = "项目id不能为空")
|
||||||
|
@PathVariable Long projectId, HttpServletResponse response) {
|
||||||
|
desConstructionSchedulePlanService.exportExcelByProjectId(projectId, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取设计计划模版
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("design:constructionSchedulePlan:readTemplate")
|
||||||
|
@Log(title = "施工进度计划", businessType = BusinessType.IMPORT)
|
||||||
|
@PostMapping("/readTemplate")
|
||||||
|
public R<Void> readExcel(@RequestParam("file") MultipartFile file, Long projectId) {
|
||||||
|
List<DesConstructionSchedulePlanExcelDto> list = desConstructionSchedulePlanService.readExcel(file, projectId);
|
||||||
|
if (CollUtil.isNotEmpty(list)) {
|
||||||
|
List<DesConstructionSchedulePlan> planList = desConstructionSchedulePlanService.convertToEntities(list);
|
||||||
|
return toAjax(desConstructionSchedulePlanService.saveBatch(planList));
|
||||||
|
}
|
||||||
|
return toAjax(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取设计计划详细信息
|
||||||
|
*
|
||||||
|
* @param id 主键
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("design:constructionSchedulePlan:query")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public R<DesConstructionSchedulePlanVo> getInfo(@NotNull(message = "主键不能为空")
|
||||||
|
@PathVariable Long id) {
|
||||||
|
return R.ok(desConstructionSchedulePlanService.queryById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增设计计划
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("design:constructionSchedulePlan:add")
|
||||||
|
@Log(title = "施工进度计划", businessType = BusinessType.INSERT)
|
||||||
|
@RepeatSubmit()
|
||||||
|
@PostMapping()
|
||||||
|
public R<DesConstructionSchedulePlanVo> add(@Validated @RequestBody DesConstructionSchedulePlanCreateReq req) {
|
||||||
|
return R.ok(desConstructionSchedulePlanService.insertByBo(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改设计计划
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("design:constructionSchedulePlan:edit")
|
||||||
|
@Log(title = "施工进度计划", businessType = BusinessType.UPDATE)
|
||||||
|
@RepeatSubmit()
|
||||||
|
@PutMapping()
|
||||||
|
public R<Void> edit(@Validated @RequestBody DesConstructionSchedulePlanUpdateReq req) {
|
||||||
|
return toAjax(desConstructionSchedulePlanService.updateByBo(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改设计计划为完成状态
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("design:constructionSchedulePlan:editFinish")
|
||||||
|
@Log(title = "施工进度计划", businessType = BusinessType.UPDATE)
|
||||||
|
@RepeatSubmit()
|
||||||
|
@PutMapping("/finish")
|
||||||
|
public R<Void> editFinish(@Validated @RequestBody DesConstructionSchedulePlanFinishReq req) {
|
||||||
|
return toAjax(desConstructionSchedulePlanService.updateFinish(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除设计计划
|
||||||
|
*
|
||||||
|
* @param ids 主键串
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("design:constructionSchedulePlan:remove")
|
||||||
|
@Log(title = "施工进度计划", businessType = BusinessType.DELETE)
|
||||||
|
@DeleteMapping("/{ids}")
|
||||||
|
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||||
|
@PathVariable Long[] ids) {
|
||||||
|
return toAjax(desConstructionSchedulePlanService.deleteByIds(List.of(ids)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
package org.dromara.design.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 施工进度计划对象 pgs_construction_schedule_plan
|
||||||
|
*
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-08-01
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName("des_construction_schedule_plan")
|
||||||
|
public class DesConstructionSchedulePlan extends BaseEntity {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
@TableId(value = "id")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目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;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
package org.dromara.design.domain.dto.constructionscheduleplan;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-08-01 14:05
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class DesConstructionSchedulePlanCreateReq implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 4060838737379600701L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目ID
|
||||||
|
*/
|
||||||
|
@NotNull(message = "项目ID不能为空")
|
||||||
|
private Long projectId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 父ID
|
||||||
|
*/
|
||||||
|
private Long parentId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节点名称
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "节点名称不能为空")
|
||||||
|
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;
|
||||||
|
}
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
package org.dromara.design.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 DesConstructionSchedulePlanExcelDto {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编号(1, 1.1, 1.1.1)
|
||||||
|
*/
|
||||||
|
private String number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目ID
|
||||||
|
*/
|
||||||
|
private Long projectId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节点名称
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
package org.dromara.design.domain.dto.constructionscheduleplan;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-09-17 10:15
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class DesConstructionSchedulePlanFinishReq implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 8139653508791280689L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键
|
||||||
|
*/
|
||||||
|
@NotNull(message = "主键不能为空")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完成时间
|
||||||
|
*/
|
||||||
|
@NotNull(message = "完成时间不能为空")
|
||||||
|
private LocalDate finishDate;
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package org.dromara.design.domain.dto.constructionscheduleplan;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-08-01 14:05
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class DesConstructionSchedulePlanQueryReq implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 9021370369055688811L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目ID
|
||||||
|
*/
|
||||||
|
private Long projectId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节点名称
|
||||||
|
*/
|
||||||
|
private String nodeName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对应项目结构名称
|
||||||
|
*/
|
||||||
|
private String projectStructureName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态
|
||||||
|
*/
|
||||||
|
private String status;
|
||||||
|
}
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
package org.dromara.design.domain.dto.constructionscheduleplan;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-08-01 14:06
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class DesConstructionSchedulePlanUpdateReq implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 6955873817030428268L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节点名称
|
||||||
|
*/
|
||||||
|
private String nodeName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 父ID
|
||||||
|
*/
|
||||||
|
private Long parentId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对应项目结构
|
||||||
|
*/
|
||||||
|
private Long projectStructure;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对应项目结构名称
|
||||||
|
*/
|
||||||
|
private String projectStructureName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预计开始时间
|
||||||
|
*/
|
||||||
|
private LocalDate planStartDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预计结束时间
|
||||||
|
*/
|
||||||
|
private LocalDate planEndDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实际开始时间
|
||||||
|
*/
|
||||||
|
private LocalDate practicalStartDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实际结束时间
|
||||||
|
*/
|
||||||
|
private LocalDate practicalEndDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态
|
||||||
|
*/
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备注
|
||||||
|
*/
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,104 @@
|
|||||||
|
package org.dromara.design.domain.vo;
|
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import io.github.linpeilie.annotations.AutoMapper;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.dromara.common.excel.annotation.ExcelDictFormat;
|
||||||
|
import org.dromara.common.excel.convert.ExcelDictConvert;
|
||||||
|
import org.dromara.progress.domain.PgsConstructionSchedulePlan;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 施工进度计划视图对象 pgs_construction_schedule_plan
|
||||||
|
*
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-08-01
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ExcelIgnoreUnannotated
|
||||||
|
@AutoMapper(target = PgsConstructionSchedulePlan.class)
|
||||||
|
public class DesConstructionSchedulePlanVo implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键ID
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "主键ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目ID
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "项目ID")
|
||||||
|
private Long projectId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 父ID
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "父ID")
|
||||||
|
private Long parentId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节点名称
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "节点名称")
|
||||||
|
private String nodeName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对应项目结构
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "对应项目结构")
|
||||||
|
private Long projectStructure;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对应项目结构名称
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "对应项目结构名称")
|
||||||
|
private String projectStructureName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预计开始时间
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "预计开始时间")
|
||||||
|
private LocalDate planStartDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预计结束时间
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "预计结束时间")
|
||||||
|
private LocalDate planEndDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实际开始时间
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "实际开始时间")
|
||||||
|
private LocalDate practicalStartDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实际结束时间
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "实际结束时间")
|
||||||
|
private LocalDate practicalEndDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
|
||||||
|
@ExcelDictFormat(dictType = "project_construction_status")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备注
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "备注")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
package org.dromara.design.mapper;
|
||||||
|
|
||||||
|
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||||
|
import org.dromara.design.domain.DesConstructionSchedulePlan;
|
||||||
|
import org.dromara.design.domain.vo.DesConstructionSchedulePlanVo;
|
||||||
|
import org.dromara.progress.domain.PgsConstructionSchedulePlan;
|
||||||
|
import org.dromara.progress.domain.vo.constructionscheduleplan.PgsConstructionSchedulePlanVo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 施工进度计划Mapper接口
|
||||||
|
*
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-08-01
|
||||||
|
*/
|
||||||
|
public interface DesConstructionSchedulePlanMapper extends BaseMapperPlus<DesConstructionSchedulePlan, DesConstructionSchedulePlanVo> {
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,117 @@
|
|||||||
|
package org.dromara.design.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.dromara.design.domain.DesConstructionSchedulePlan;
|
||||||
|
import org.dromara.design.domain.dto.constructionscheduleplan.*;
|
||||||
|
import org.dromara.design.domain.vo.DesConstructionSchedulePlanVo;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 施工进度计划Service接口
|
||||||
|
*
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-08-01
|
||||||
|
*/
|
||||||
|
public interface IDesConstructionSchedulePlanService extends IService<DesConstructionSchedulePlan> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询施工进度计划
|
||||||
|
*
|
||||||
|
* @param id 主键
|
||||||
|
* @return 施工进度计划
|
||||||
|
*/
|
||||||
|
DesConstructionSchedulePlanVo queryById(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询符合条件的施工进度计划列表
|
||||||
|
*
|
||||||
|
* @param req 查询条件
|
||||||
|
* @return 施工进度计划列表
|
||||||
|
*/
|
||||||
|
List<DesConstructionSchedulePlanVo> queryList(DesConstructionSchedulePlanQueryReq req);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增施工进度计划
|
||||||
|
*
|
||||||
|
* @param req 施工进度计划
|
||||||
|
* @return 新增施工进度计划封装
|
||||||
|
*/
|
||||||
|
DesConstructionSchedulePlanVo insertByBo(DesConstructionSchedulePlanCreateReq req);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改施工进度计划
|
||||||
|
*
|
||||||
|
* @param req 施工进度计划
|
||||||
|
* @return 是否修改成功
|
||||||
|
*/
|
||||||
|
Boolean updateByBo(DesConstructionSchedulePlanUpdateReq req);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改施工进度计划为完成状态
|
||||||
|
*
|
||||||
|
* @param req 施工进度计划
|
||||||
|
* @return 是否修改成功
|
||||||
|
*/
|
||||||
|
Boolean updateFinish(DesConstructionSchedulePlanFinishReq req);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除施工进度计划信息
|
||||||
|
*
|
||||||
|
* @param ids 待删除的主键集合
|
||||||
|
* @return 是否删除成功
|
||||||
|
*/
|
||||||
|
Boolean deleteByIds(Collection<Long> ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取施工进度计划视图对象
|
||||||
|
*
|
||||||
|
* @param constructionSchedulePlan 施工进度计划对象
|
||||||
|
* @return 施工进度计划视图对象
|
||||||
|
*/
|
||||||
|
DesConstructionSchedulePlanVo getVo(DesConstructionSchedulePlan constructionSchedulePlan);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取施工进度计划查询条件封装
|
||||||
|
*
|
||||||
|
* @param req 查询条件
|
||||||
|
* @return 查询条件封装
|
||||||
|
*/
|
||||||
|
LambdaQueryWrapper<DesConstructionSchedulePlan> buildQueryWrapper(DesConstructionSchedulePlanQueryReq req);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取施工进度计划分页对象视图
|
||||||
|
*
|
||||||
|
* @param constructionSchedulePlanList 施工进度计划分页对象
|
||||||
|
* @return 施工进度计划分页对象视图
|
||||||
|
*/
|
||||||
|
List<DesConstructionSchedulePlanVo> getVoList(List<DesConstructionSchedulePlan> constructionSchedulePlanList);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出Excel
|
||||||
|
*
|
||||||
|
* @param projectId 项目id
|
||||||
|
*/
|
||||||
|
void exportExcelByProjectId(Long projectId, HttpServletResponse response);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取Excel文件
|
||||||
|
*
|
||||||
|
* @param file Excel文件
|
||||||
|
* @param projectId 项目ID
|
||||||
|
* @return 读取的数据列表
|
||||||
|
*/
|
||||||
|
List<DesConstructionSchedulePlanExcelDto> readExcel(MultipartFile file, Long projectId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Excel数据转换为实体列表
|
||||||
|
*
|
||||||
|
* @param excelList Excel数据列表
|
||||||
|
* @return 实体列表
|
||||||
|
*/
|
||||||
|
List<DesConstructionSchedulePlan> convertToEntities(List<DesConstructionSchedulePlanExcelDto> excelList);
|
||||||
|
}
|
||||||
@ -0,0 +1,627 @@
|
|||||||
|
package org.dromara.design.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||||
|
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.DesConstructionSchedulePlan;
|
||||||
|
import org.dromara.design.domain.bo.DesUserBo;
|
||||||
|
import org.dromara.design.domain.dto.constructionscheduleplan.*;
|
||||||
|
import org.dromara.design.domain.vo.DesConstructionSchedulePlanVo;
|
||||||
|
import org.dromara.design.mapper.DesConstructionSchedulePlanMapper;
|
||||||
|
import org.dromara.design.service.IDesConstructionSchedulePlanService;
|
||||||
|
|
||||||
|
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.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业务层处理
|
||||||
|
*
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-08-01
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class DesConstructionSchedulePlanServiceImpl extends ServiceImpl<DesConstructionSchedulePlanMapper, DesConstructionSchedulePlan>
|
||||||
|
implements IDesConstructionSchedulePlanService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IBusProjectService projectService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ISysDictDataService dictDataService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询施工进度计划
|
||||||
|
*
|
||||||
|
* @param id 主键
|
||||||
|
* @return 施工进度计划
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public DesConstructionSchedulePlanVo queryById(Long id) {
|
||||||
|
DesConstructionSchedulePlan constructionSchedulePlan = this.getById(id);
|
||||||
|
if (constructionSchedulePlan == null) {
|
||||||
|
throw new ServiceException("施工进度计划信息不存在", HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
return this.getVo(constructionSchedulePlan);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询符合条件的施工进度计划列表
|
||||||
|
*
|
||||||
|
* @param req 查询条件
|
||||||
|
* @return 施工进度计划列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<DesConstructionSchedulePlanVo> queryList(DesConstructionSchedulePlanQueryReq req) {
|
||||||
|
List<DesConstructionSchedulePlan> result = this.list(this.buildQueryWrapper(req));
|
||||||
|
return this.getVoList(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增施工进度计划
|
||||||
|
*
|
||||||
|
* @param req 施工进度计划
|
||||||
|
* @return 新增施工进度计划封装
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public DesConstructionSchedulePlanVo insertByBo(DesConstructionSchedulePlanCreateReq req) {
|
||||||
|
DesConstructionSchedulePlan constructionSchedulePlan = new DesConstructionSchedulePlan();
|
||||||
|
BeanUtils.copyProperties(req, constructionSchedulePlan);
|
||||||
|
boolean save = this.save(constructionSchedulePlan);
|
||||||
|
if (!save) {
|
||||||
|
throw new ServiceException("新增施工进度计划信息异常", HttpStatus.ERROR);
|
||||||
|
}
|
||||||
|
DesConstructionSchedulePlan newConstructionSchedulePlan = this.getById(constructionSchedulePlan.getId());
|
||||||
|
return this.getVo(newConstructionSchedulePlan);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改施工进度计划
|
||||||
|
*
|
||||||
|
* @param req 施工进度计划
|
||||||
|
* @return 是否修改成功
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Boolean updateByBo(DesConstructionSchedulePlanUpdateReq req) {
|
||||||
|
DesConstructionSchedulePlan constructionSchedulePlan = new DesConstructionSchedulePlan();
|
||||||
|
BeanUtils.copyProperties(req, constructionSchedulePlan);
|
||||||
|
return this.updateById(constructionSchedulePlan);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改施工进度计划为完成状态
|
||||||
|
*
|
||||||
|
* @param req 施工进度计划
|
||||||
|
* @return 是否修改成功
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Boolean updateFinish(DesConstructionSchedulePlanFinishReq req) {
|
||||||
|
DesConstructionSchedulePlan plan = this.getById(req.getId());
|
||||||
|
if (plan == null) {
|
||||||
|
throw new ServiceException("施工进度计划信息不存在", HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
if (plan.getStatus().equals("4")) {
|
||||||
|
throw new ServiceException("施工进度计划已完成", HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
LocalDate practicalStartDate = plan.getPracticalStartDate();
|
||||||
|
LocalDate finishDate = req.getFinishDate();
|
||||||
|
if (practicalStartDate == null) {
|
||||||
|
throw new ServiceException("请先填写实际开始时间", HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
if (finishDate.isBefore(practicalStartDate)) {
|
||||||
|
throw new ServiceException("实际结束时间不能早于实际开始时间", HttpStatus.NOT_FOUND);
|
||||||
|
}
|
||||||
|
plan.setStatus("4");
|
||||||
|
plan.setPracticalEndDate(finishDate);
|
||||||
|
return this.updateById(plan);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除施工进度计划信息
|
||||||
|
*
|
||||||
|
* @param ids 待删除的主键集合
|
||||||
|
* @return 是否删除成功
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public Boolean deleteByIds(Collection<Long> ids) {
|
||||||
|
return this.removeBatchByIds(ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取施工进度计划视图对象
|
||||||
|
*
|
||||||
|
* @param constructionSchedulePlan 施工进度计划对象
|
||||||
|
* @return 施工进度计划视图对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public DesConstructionSchedulePlanVo getVo(DesConstructionSchedulePlan constructionSchedulePlan) {
|
||||||
|
DesConstructionSchedulePlanVo vo = new DesConstructionSchedulePlanVo();
|
||||||
|
if (constructionSchedulePlan == null) {
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
BeanUtils.copyProperties(constructionSchedulePlan, vo);
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取施工进度计划查询条件封装
|
||||||
|
*
|
||||||
|
* @param req 查询条件
|
||||||
|
* @return 查询条件封装
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public LambdaQueryWrapper<DesConstructionSchedulePlan> buildQueryWrapper(DesConstructionSchedulePlanQueryReq req) {
|
||||||
|
LambdaQueryWrapper<DesConstructionSchedulePlan> lqw = new LambdaQueryWrapper<>();
|
||||||
|
if (req == null) {
|
||||||
|
return lqw;
|
||||||
|
}
|
||||||
|
Long projectId = req.getProjectId();
|
||||||
|
String nodeName = req.getNodeName();
|
||||||
|
String status = req.getStatus();
|
||||||
|
lqw.eq(ObjectUtils.isNotEmpty(projectId), DesConstructionSchedulePlan::getProjectId, projectId);
|
||||||
|
lqw.like(StringUtils.isNotBlank(nodeName), DesConstructionSchedulePlan::getNodeName, nodeName);
|
||||||
|
lqw.eq(StringUtils.isNotBlank(status), DesConstructionSchedulePlan::getStatus, status);
|
||||||
|
return lqw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取施工进度计划分页对象视图
|
||||||
|
*
|
||||||
|
* @param constructionSchedulePlanList 施工进度计划分页对象
|
||||||
|
* @return 施工进度计划分页对象视图
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<DesConstructionSchedulePlanVo> getVoList(List<DesConstructionSchedulePlan> constructionSchedulePlanList) {
|
||||||
|
return constructionSchedulePlanList.stream().map(this::getVo).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// region 导出 excel
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出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);
|
||||||
|
if (projectStructureMap.isEmpty()) {
|
||||||
|
throw new ServiceException("获取项目列表失败,项目为空!!!");
|
||||||
|
}
|
||||||
|
List<SysDictDataVo> dictDataVos = dictDataService.selectByDictType("project_construction_status");
|
||||||
|
if (dictDataVos == null || dictDataVos.isEmpty()) {
|
||||||
|
throw new ServiceException("项目施工状态为空!!");
|
||||||
|
}
|
||||||
|
Map<String, String> statusMap = new HashMap<>();
|
||||||
|
for (SysDictDataVo vo : dictDataVos) {
|
||||||
|
statusMap.put(vo.getDictValue(), vo.getDictLabel());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
// 重置行索引,填充人员和人员ID(C列和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 = {"编号(格式:1,1.1,1.1.1。用于进行父子结构关联)", "节点名称",
|
||||||
|
"预计开始时间(输入格式:2025-09-06)", "预计结束时间", "实际开始时间", "实际结束时间", "状态", "状态编码", "备注"};
|
||||||
|
for (int i = 0; i < headers.length; i++) {
|
||||||
|
Cell cell = sheetRow.createCell(i);
|
||||||
|
cell.setCellValue(headers[i]);
|
||||||
|
if (i == 0) {
|
||||||
|
CellStyle css = workbook.createCellStyle();
|
||||||
|
DataFormat format = workbook.createDataFormat();
|
||||||
|
css.setDataFormat(format.getFormat("@"));
|
||||||
|
cell.setCellStyle(css);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// 6. 设置专业下拉列表(第二列)
|
||||||
|
// setMajorDropdown(mainSheet, projectStructureMap.size());
|
||||||
|
setStatusDropdown(mainSheet, statusMap.size());
|
||||||
|
// String formulaTemplate = "IFERROR(INDEX(DropdownData!$B$1:$B$" + projectStructureMap.size() + ", MATCH(C{rowNum}, DropdownData!$A$1:$A$" + projectStructureMap.size() + ", 0)),\"\")";
|
||||||
|
String formulaTemplate1 = "IFERROR(INDEX(DropdownData!$F$1:$F$" + statusMap.size() + ", MATCH(G{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(2);
|
||||||
|
// cell.setCellStyle(editableStyle); //专业不锁定
|
||||||
|
//
|
||||||
|
// Cell idCell = row.createCell(3);
|
||||||
|
// idCell.setCellFormula(formula);
|
||||||
|
// idCell.setCellStyle(protectedStyle); // 应用隐藏公式样式
|
||||||
|
|
||||||
|
String formula1 = formulaTemplate1.replace("{rowNum}", String.valueOf(currentRowNum));
|
||||||
|
|
||||||
|
Cell cell1 = row.createCell(6);
|
||||||
|
cell1.setCellStyle(editableStyle); //专业不锁定
|
||||||
|
|
||||||
|
Cell idCell1 = row.createCell(7);
|
||||||
|
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);
|
||||||
|
CellStyle textStyle = workbook.createCellStyle();
|
||||||
|
textStyle.setLocked(false);
|
||||||
|
DataFormat format = workbook.createDataFormat();
|
||||||
|
textStyle.setDataFormat(format.getFormat("@")); // "@" 表示文本格式
|
||||||
|
cell.setCellStyle(textStyle);
|
||||||
|
|
||||||
|
Cell cell1 = row.createCell(1);
|
||||||
|
cell1.setCellStyle(editableStyle);
|
||||||
|
|
||||||
|
Cell cell4 = row.createCell(2);
|
||||||
|
cell4.setCellStyle(editableStyle);
|
||||||
|
|
||||||
|
Cell cell5 = row.createCell(3);
|
||||||
|
cell5.setCellStyle(editableStyle);
|
||||||
|
|
||||||
|
Cell cell6 = row.createCell(4);
|
||||||
|
cell6.setCellStyle(editableStyle);
|
||||||
|
|
||||||
|
Cell cell7 = row.createCell(5);
|
||||||
|
cell7.setCellStyle(editableStyle);
|
||||||
|
|
||||||
|
Cell cell10 = row.createCell(8);
|
||||||
|
cell10.setCellStyle(editableStyle);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 保护工作表,仅允许编辑未锁定的单元格
|
||||||
|
mainSheet.protectSheet("123456"); // 空密码
|
||||||
|
|
||||||
|
// 核心:锁定表头(第1行)和前1列(包含ID列)
|
||||||
|
mainSheet.createFreezePane(0, 1, 0, 1);
|
||||||
|
|
||||||
|
// 调整列宽(更新列索引)
|
||||||
|
mainSheet.setColumnWidth(0, 20 * 320); // 编号
|
||||||
|
mainSheet.setColumnWidth(1, 20 * 280); // 节点名称
|
||||||
|
mainSheet.setColumnWidth(2, 20 * 320); // 预计开始时间
|
||||||
|
mainSheet.setColumnWidth(3, 20 * 200); // 预计结束时间
|
||||||
|
mainSheet.setColumnWidth(4, 20 * 200); // 实际开始时间
|
||||||
|
mainSheet.setColumnWidth(5, 20 * 200); // 实际结束时间
|
||||||
|
mainSheet.setColumnWidth(6, 20 * 100); // 状态
|
||||||
|
mainSheet.setColumnWidth(7, 20 * 200); // 状态编码
|
||||||
|
mainSheet.setColumnWidth(8, 20 * 200); // 备注
|
||||||
|
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
|
||||||
|
// 直接写入响应输出流
|
||||||
|
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, 2, 2);
|
||||||
|
|
||||||
|
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, 6, 6);
|
||||||
|
|
||||||
|
DataValidation validation = helper.createValidation(constraint, addressList);
|
||||||
|
validation.setShowErrorBox(true);
|
||||||
|
mainSheet.addValidationData(validation);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取Excel文件
|
||||||
|
*
|
||||||
|
* @param file Excel文件
|
||||||
|
* @param projectId 项目ID
|
||||||
|
* @return 读取的数据列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<DesConstructionSchedulePlanExcelDto> readExcel(MultipartFile file, Long projectId) {
|
||||||
|
List<DesConstructionSchedulePlanExcelDto> 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 number = getCellValue(row.getCell(0));
|
||||||
|
String nodeName = getCellValue(row.getCell(1));
|
||||||
|
String projectStructureName = null;
|
||||||
|
Long projectStructure = null;
|
||||||
|
LocalDate planStartDate = getLocalDateValue(row.getCell(2));
|
||||||
|
LocalDate planEndDate = getLocalDateValue(row.getCell(3));
|
||||||
|
LocalDate practicalStartDate = getLocalDateValue(row.getCell(4));
|
||||||
|
LocalDate practicalEndDate = getLocalDateValue(row.getCell(5));
|
||||||
|
String status = getCellValue(row.getCell(7));
|
||||||
|
String remark = getCellValue(row.getCell(8));
|
||||||
|
|
||||||
|
DesConstructionSchedulePlanExcelDto excelData = new DesConstructionSchedulePlanExcelDto(
|
||||||
|
number, projectId, nodeName, projectStructure, projectStructureName,
|
||||||
|
planStartDate, planEndDate, practicalStartDate, practicalEndDate, status, remark
|
||||||
|
);
|
||||||
|
dataList.add(excelData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("读取表格数据失败", e);
|
||||||
|
throw new ServiceException("读取表格数据失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Excel数据转换为实体列表
|
||||||
|
*
|
||||||
|
* @param excelList Excel数据列表
|
||||||
|
* @return 实体列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<DesConstructionSchedulePlan> convertToEntities(List<DesConstructionSchedulePlanExcelDto> excelList) {
|
||||||
|
List<DesConstructionSchedulePlan> result = new ArrayList<>();
|
||||||
|
Map<String, Long> numberIdMap = new HashMap<>();
|
||||||
|
|
||||||
|
for (DesConstructionSchedulePlanExcelDto dto : excelList) {
|
||||||
|
DesConstructionSchedulePlan entity = new DesConstructionSchedulePlan();
|
||||||
|
|
||||||
|
LocalDate planStartDate = dto.getPlanStartDate();
|
||||||
|
LocalDate planEndDate = dto.getPlanEndDate();
|
||||||
|
if (planStartDate == null || planEndDate == null || planStartDate.isAfter(planEndDate)) {
|
||||||
|
throw new ServiceException("计划开始时间和计划结束时间不能为空,且计划开始时间不能大于计划结束时间", HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成主键
|
||||||
|
Long id = IdWorker.getId();
|
||||||
|
entity.setId(id);
|
||||||
|
|
||||||
|
entity.setProjectId(dto.getProjectId());
|
||||||
|
entity.setNodeName(dto.getNodeName());
|
||||||
|
entity.setProjectStructure(dto.getProjectStructure());
|
||||||
|
entity.setProjectStructureName(dto.getProjectStructureName());
|
||||||
|
entity.setPlanStartDate(planStartDate);
|
||||||
|
entity.setPlanEndDate(planEndDate);
|
||||||
|
entity.setPracticalStartDate(dto.getPracticalStartDate());
|
||||||
|
entity.setPracticalEndDate(dto.getPracticalEndDate());
|
||||||
|
entity.setStatus(dto.getStatus());
|
||||||
|
entity.setRemark(dto.getRemark());
|
||||||
|
// 确定父ID
|
||||||
|
String number = dto.getNumber();
|
||||||
|
if (number != null && number.contains(".")) {
|
||||||
|
String parentNumber = number.substring(0, number.lastIndexOf("."));
|
||||||
|
Long parentId = numberIdMap.get(parentNumber);
|
||||||
|
if (parentId == null) {
|
||||||
|
throw new ServiceException("未找到父编号:" + parentNumber, HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
entity.setParentId(parentId);
|
||||||
|
} else {
|
||||||
|
entity.setParentId(0L); // 顶级节点
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存当前编号对应的id
|
||||||
|
numberIdMap.put(number, id);
|
||||||
|
result.add(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
// 处理数字类型,避免科学计数法
|
||||||
|
// 处理数字,移除不必要的.0后缀
|
||||||
|
String numericValue = String.valueOf(cell.getNumericCellValue());
|
||||||
|
if (numericValue.endsWith(".0")) {
|
||||||
|
return numericValue.substring(0, numericValue.length() - 2);
|
||||||
|
}
|
||||||
|
return numericValue;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -302,7 +302,11 @@ public class PgsConstructionSchedulePlanServiceImpl extends ServiceImpl<PgsConst
|
|||||||
row = mainSheet.createRow(i);
|
row = mainSheet.createRow(i);
|
||||||
}
|
}
|
||||||
Cell cell = row.createCell(0);
|
Cell cell = row.createCell(0);
|
||||||
cell.setCellStyle(editableStyle);
|
CellStyle textStyle = workbook.createCellStyle();
|
||||||
|
textStyle.setLocked(false);
|
||||||
|
DataFormat format = workbook.createDataFormat();
|
||||||
|
textStyle.setDataFormat(format.getFormat("@")); // "@" 表示文本格式
|
||||||
|
cell.setCellStyle(textStyle);
|
||||||
|
|
||||||
Cell cell1 = row.createCell(1);
|
Cell cell1 = row.createCell(1);
|
||||||
cell1.setCellStyle(editableStyle);
|
cell1.setCellStyle(editableStyle);
|
||||||
|
|||||||
Reference in New Issue
Block a user