From 29005c5ee8eda81cd82d84920658165d6f0e6dd0 Mon Sep 17 00:00:00 2001 From: lzm <2316711944@qq.com> Date: Wed, 29 Apr 2026 15:46:24 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=8C=87=E5=AF=BC=E4=BB=B7?= =?UTF-8?q?=E6=B3=95=E6=98=8E=E7=BB=86=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../planning/vo/ProjectPlanningRespVO.java | 20 +- .../planning/vo/ProjectPlanningSaveReqVO.java | 16 +- .../ProjectPlanningGuideDetailController.java | 66 ++++ ...jectPlanningGuideDetailBatchSaveReqVO.java | 22 ++ .../vo/ProjectPlanningGuideDetailRespVO.java | 70 +++++ .../ProjectPlanningGuideDetailSaveReqVO.java | 76 +++++ .../planning/ProjectPlanningDO.java | 4 - .../ProjectPlanningGuideDetailDO.java | 55 ++++ .../ProjectPlanningGuideDetailMapper.java | 33 ++ .../module/tjt/enums/ErrorCodeConstants.java | 2 + .../ProjectPlanningBizTypeConstants.java | 4 + .../planning/ProjectPlanningServiceImpl.java | 29 +- .../ProjectPlanningGuideDetailService.java | 27 ++ ...ProjectPlanningGuideDetailServiceImpl.java | 289 ++++++++++++++++++ .../service/project/ProjectServiceImpl.java | 4 + .../ProjectOutputReportServiceImpl.java | 179 +++++++---- .../builder/ProjectBudgetExcelBuilder.java | 97 +++++- .../src/main/resources/application-local.yaml | 2 +- 18 files changed, 886 insertions(+), 109 deletions(-) create mode 100644 lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planningguidedetail/ProjectPlanningGuideDetailController.java create mode 100644 lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planningguidedetail/vo/ProjectPlanningGuideDetailBatchSaveReqVO.java create mode 100644 lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planningguidedetail/vo/ProjectPlanningGuideDetailRespVO.java create mode 100644 lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planningguidedetail/vo/ProjectPlanningGuideDetailSaveReqVO.java create mode 100644 lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/dal/dataobject/planningguidedetail/ProjectPlanningGuideDetailDO.java create mode 100644 lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/dal/mysql/planningguidedetail/ProjectPlanningGuideDetailMapper.java create mode 100644 lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/planningguidedetail/ProjectPlanningGuideDetailService.java create mode 100644 lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/planningguidedetail/ProjectPlanningGuideDetailServiceImpl.java diff --git a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planning/vo/ProjectPlanningRespVO.java b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planning/vo/ProjectPlanningRespVO.java index e8280c6..330746d 100644 --- a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planning/vo/ProjectPlanningRespVO.java +++ b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planning/vo/ProjectPlanningRespVO.java @@ -19,12 +19,6 @@ public class ProjectPlanningRespVO { @Schema(description = "归属类型") private String ownershipType; - @Schema(description = "设计部位") - private String designPart; - - @Schema(description = "建筑类型") - private String buildingType; - @Schema(description = "产值计算方式") private String calculationMethod; @@ -34,7 +28,7 @@ public class ProjectPlanningRespVO { @Schema(description = "规划金额") private BigDecimal planningAmount; - @Schema(description = "管理费费率") + @Schema(description = "管理费率") private BigDecimal managementFeeRate; @Schema(description = "管理费") @@ -76,19 +70,19 @@ public class ProjectPlanningRespVO { @Schema(description = "楼栋数或户型数") private Integer buildingOrUnitCount; - @Schema(description = "套图系数,保留两位小数") + @Schema(description = "套图系数") private BigDecimal drawingSetFactor; - @Schema(description = "规模系数,保留两位小数") + @Schema(description = "规模系数") private BigDecimal scaleFactor; - @Schema(description = "修改系数,保留两位小数") + @Schema(description = "修改系数") private BigDecimal modificationFactor; - @Schema(description = "复杂系数/复杂等级,按比例值返回(100%=1.0000)") + @Schema(description = "复杂系数") private BigDecimal complexityFactor; - @Schema(description = "内部指导单价(元/㎡)") + @Schema(description = "内部指导单价(元/m²)") private BigDecimal internalGuidanceUnitPrice; @Schema(description = "虚拟产值计算方式") @@ -109,7 +103,7 @@ public class ProjectPlanningRespVO { @Schema(description = "产值计算比例") private BigDecimal calculationRatio; - @Schema(description = "合同单价(元/㎡)") + @Schema(description = "合同单价(元/m²)") private BigDecimal contractUnitPrice; @Schema(description = "合计调整系数") diff --git a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planning/vo/ProjectPlanningSaveReqVO.java b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planning/vo/ProjectPlanningSaveReqVO.java index a919af3..b7e144c 100644 --- a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planning/vo/ProjectPlanningSaveReqVO.java +++ b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planning/vo/ProjectPlanningSaveReqVO.java @@ -26,14 +26,6 @@ public class ProjectPlanningSaveReqVO { @Size(max = 20, message = "归属类型长度不能超过 20 个字符") private String ownershipType; - @Schema(description = "设计部位", example = "地上部分") - @Size(max = 20, message = "设计部位长度不能超过 20 个字符") - private String designPart; - - @Schema(description = "建筑类型", example = "住宅") - @Size(max = 100, message = "建筑类型长度不能超过 100 个字符") - private String buildingType; - @Schema(description = "产值计算方式,页面 2 维护", example = "指导价法") @Size(max = 30, message = "产值计算方式长度不能超过 30 个字符") private String calculationMethod; @@ -47,8 +39,8 @@ public class ProjectPlanningSaveReqVO { @NotNull(message = "规划金额不能为空") private BigDecimal planningAmount; - @Schema(description = "管理费费率", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.0500") - @NotNull(message = "管理费费率不能为空") + @Schema(description = "管理费率", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.0500") + @NotNull(message = "管理费率不能为空") @DecimalMin(value = "0.0000", message = "managementFeeRate must be >= 0") @DecimalMax(value = "1.0000", message = "managementFeeRate must be <= 1") private BigDecimal managementFeeRate; @@ -106,10 +98,10 @@ public class ProjectPlanningSaveReqVO { @DecimalMax(value = "1.0000", message = "complexityFactor must be <= 1") private BigDecimal complexityFactor; - @Schema(description = "内部指导单价(元/㎡)", example = "80.00") + @Schema(description = "内部指导单价(元/m²)", example = "80.00") private BigDecimal internalGuidanceUnitPrice; - @Schema(description = "虚拟产值计算方式:指导单价法 / 指导总价法 / 工日法", example = "工日法") + @Schema(description = "虚拟产值计算方式:指导单价法/指导总价法/工日法", example = "工日法") @Size(max = 30, message = "虚拟产值计算方式长度不能超过 30 个字符") private String virtualCalculationMethod; diff --git a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planningguidedetail/ProjectPlanningGuideDetailController.java b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planningguidedetail/ProjectPlanningGuideDetailController.java new file mode 100644 index 0000000..8756f21 --- /dev/null +++ b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planningguidedetail/ProjectPlanningGuideDetailController.java @@ -0,0 +1,66 @@ +package cn.iocoder.lyzsys.module.tjt.controller.admin.planningguidedetail; + +import cn.iocoder.lyzsys.framework.common.pojo.CommonResult; +import cn.iocoder.lyzsys.framework.common.util.object.BeanUtils; +import cn.iocoder.lyzsys.module.tjt.controller.admin.planningguidedetail.vo.ProjectPlanningGuideDetailBatchSaveReqVO; +import cn.iocoder.lyzsys.module.tjt.controller.admin.planningguidedetail.vo.ProjectPlanningGuideDetailRespVO; +import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planningguidedetail.ProjectPlanningGuideDetailDO; +import cn.iocoder.lyzsys.module.tjt.service.planningguidedetail.ProjectPlanningGuideDetailService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; + +import static cn.iocoder.lyzsys.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 合约规划指导价法明细") +@RestController +@RequestMapping("/tjt/planning-guide-detail") +@Validated +public class ProjectPlanningGuideDetailController { + + @Resource + private ProjectPlanningGuideDetailService projectPlanningGuideDetailService; + + @GetMapping("/list-by-planning") + @Operation(summary = "根据合约规划获得指导价法明细列表") + @Parameter(name = "planningId", description = "合约规划 ID", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('tjt:planning:query')") + public CommonResult> getProjectPlanningGuideDetailListByPlanningId( + @RequestParam("planningId") Long planningId) { + List list = + projectPlanningGuideDetailService.getProjectPlanningGuideDetailListByPlanningId(planningId); + return success(BeanUtils.toBean(list, ProjectPlanningGuideDetailRespVO.class)); + } + + @PostMapping("/batch-save") + @Operation(summary = "批量保存指导价法明细") + @PreAuthorize("@ss.hasPermission('tjt:planning:update')") + public CommonResult batchSaveProjectPlanningGuideDetail( + @Valid @RequestBody ProjectPlanningGuideDetailBatchSaveReqVO reqVO) { + projectPlanningGuideDetailService.batchSaveProjectPlanningGuideDetail(reqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除指导价法明细") + @Parameter(name = "id", description = "明细 ID", required = true, example = "1") + @PreAuthorize("@ss.hasPermission('tjt:planning:update')") + public CommonResult deleteProjectPlanningGuideDetail(@RequestParam("id") Long id) { + projectPlanningGuideDetailService.deleteProjectPlanningGuideDetail(id); + return success(true); + } + +} diff --git a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planningguidedetail/vo/ProjectPlanningGuideDetailBatchSaveReqVO.java b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planningguidedetail/vo/ProjectPlanningGuideDetailBatchSaveReqVO.java new file mode 100644 index 0000000..ad1b1b8 --- /dev/null +++ b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planningguidedetail/vo/ProjectPlanningGuideDetailBatchSaveReqVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.lyzsys.module.tjt.controller.admin.planningguidedetail.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import java.util.List; + +@Schema(description = "管理后台 - 合约规划指导价法明细批量保存 Request VO") +@Data +public class ProjectPlanningGuideDetailBatchSaveReqVO { + + @Schema(description = "合约规划 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "合约规划 ID 不能为空") + private Long planningId; + + @Schema(description = "指导价法明细列表") + @Valid + private List details; + +} diff --git a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planningguidedetail/vo/ProjectPlanningGuideDetailRespVO.java b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planningguidedetail/vo/ProjectPlanningGuideDetailRespVO.java new file mode 100644 index 0000000..8bbb5ad --- /dev/null +++ b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planningguidedetail/vo/ProjectPlanningGuideDetailRespVO.java @@ -0,0 +1,70 @@ +package cn.iocoder.lyzsys.module.tjt.controller.admin.planningguidedetail.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 合约规划指导价法明细 Response VO") +@Data +public class ProjectPlanningGuideDetailRespVO { + + @Schema(description = "明细 ID", example = "1") + private Long id; + + @Schema(description = "合约规划 ID", example = "1") + private Long planningId; + + @Schema(description = "项目 ID", example = "1") + private Long projectId; + + @Schema(description = "设计部位") + private String designPart; + + @Schema(description = "建筑类型") + private String buildingType; + + @Schema(description = "设计面积") + private BigDecimal designArea; + + @Schema(description = "内部指导单价(元/m²)") + private BigDecimal internalGuidanceUnitPrice; + + @Schema(description = "楼栋数/户型数") + private Integer buildingOrUnitCount; + + @Schema(description = "套图系数") + private BigDecimal drawingSetFactor; + + @Schema(description = "规模系数") + private BigDecimal scaleFactor; + + @Schema(description = "修改系数") + private BigDecimal modificationFactor; + + @Schema(description = "复杂系数") + private BigDecimal complexityFactor; + + @Schema(description = "合计调整系数") + private BigDecimal totalAdjustmentFactor; + + @Schema(description = "设计占比") + private BigDecimal designRatio; + + @Schema(description = "考核面积") + private BigDecimal assessmentArea; + + @Schema(description = "考核产值") + private BigDecimal assessmentOutputValue; + + @Schema(description = "排序号") + private Integer sortNo; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + +} diff --git a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planningguidedetail/vo/ProjectPlanningGuideDetailSaveReqVO.java b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planningguidedetail/vo/ProjectPlanningGuideDetailSaveReqVO.java new file mode 100644 index 0000000..7e3d8a9 --- /dev/null +++ b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/controller/admin/planningguidedetail/vo/ProjectPlanningGuideDetailSaveReqVO.java @@ -0,0 +1,76 @@ +package cn.iocoder.lyzsys.module.tjt.controller.admin.planningguidedetail.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.DecimalMax; +import javax.validation.constraints.DecimalMin; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.math.BigDecimal; + +@Schema(description = "管理后台 - 合约规划指导价法明细新增/修改 Request VO") +@Data +public class ProjectPlanningGuideDetailSaveReqVO { + + @Schema(description = "明细 ID", example = "1") + private Long id; + + @Schema(description = "设计部位", requiredMode = Schema.RequiredMode.REQUIRED, example = "地上部分") + @NotBlank(message = "设计部位不能为空") + @Size(max = 20, message = "设计部位长度不能超过 20 个字符") + private String designPart; + + @Schema(description = "建筑类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "住宅") + @NotBlank(message = "建筑类型不能为空") + @Size(max = 100, message = "建筑类型长度不能超过 100 个字符") + private String buildingType; + + @Schema(description = "设计面积", requiredMode = Schema.RequiredMode.REQUIRED, example = "12000") + @NotNull(message = "设计面积不能为空") + @DecimalMin(value = "0", message = "designArea must be >= 0") + private BigDecimal designArea; + + @Schema(description = "内部指导单价(元/m²)", requiredMode = Schema.RequiredMode.REQUIRED, example = "80") + @NotNull(message = "内部指导单价不能为空") + @DecimalMin(value = "0", message = "internalGuidanceUnitPrice must be >= 0") + private BigDecimal internalGuidanceUnitPrice; + + @Schema(description = "楼栋数/户型数", example = "10") + private Integer buildingOrUnitCount; + + @Schema(description = "套图系数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0000") + @NotNull(message = "套图系数不能为空") + @DecimalMin(value = "0", message = "drawingSetFactor must be >= 0") + private BigDecimal drawingSetFactor; + + @Schema(description = "规模系数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0000") + @NotNull(message = "规模系数不能为空") + @DecimalMin(value = "0", message = "scaleFactor must be >= 0") + private BigDecimal scaleFactor; + + @Schema(description = "修改系数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0000") + @NotNull(message = "修改系数不能为空") + @DecimalMin(value = "0", message = "modificationFactor must be >= 0") + private BigDecimal modificationFactor; + + @Schema(description = "复杂系数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0000") + @NotNull(message = "复杂系数不能为空") + @DecimalMin(value = "0", message = "complexityFactor must be >= 0") + private BigDecimal complexityFactor; + + @Schema(description = "设计占比", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.5000") + @NotNull(message = "设计占比不能为空") + @DecimalMin(value = "0", message = "designRatio must be >= 0") + @DecimalMax(value = "1", message = "designRatio must be <= 1") + private BigDecimal designRatio; + + @Schema(description = "排序号", example = "1") + private Integer sortNo; + + @Schema(description = "备注", example = "样板房") + @Size(max = 500, message = "备注长度不能超过 500 个字符") + private String remark; + +} diff --git a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/dal/dataobject/planning/ProjectPlanningDO.java b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/dal/dataobject/planning/ProjectPlanningDO.java index e71e488..481655d 100644 --- a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/dal/dataobject/planning/ProjectPlanningDO.java +++ b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/dal/dataobject/planning/ProjectPlanningDO.java @@ -27,10 +27,6 @@ public class ProjectPlanningDO extends TenantBaseDO { private String ownershipType; - private String designPart; - - private String buildingType; - private String calculationMethod; private String planningContent; diff --git a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/dal/dataobject/planningguidedetail/ProjectPlanningGuideDetailDO.java b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/dal/dataobject/planningguidedetail/ProjectPlanningGuideDetailDO.java new file mode 100644 index 0000000..eacf653 --- /dev/null +++ b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/dal/dataobject/planningguidedetail/ProjectPlanningGuideDetailDO.java @@ -0,0 +1,55 @@ +package cn.iocoder.lyzsys.module.tjt.dal.dataobject.planningguidedetail; + +import cn.iocoder.lyzsys.framework.tenant.core.db.TenantBaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; + +@TableName("tjt_project_planning_guide_detail") +@KeySequence("tjt_project_planning_guide_detail_seq") +@Data +@EqualsAndHashCode(callSuper = true) +public class ProjectPlanningGuideDetailDO extends TenantBaseDO { + + @TableId + private Long id; + + private Long planningId; + + private Long projectId; + + private String designPart; + + private String buildingType; + + private BigDecimal designArea; + + private BigDecimal internalGuidanceUnitPrice; + + private Integer buildingOrUnitCount; + + private BigDecimal drawingSetFactor; + + private BigDecimal scaleFactor; + + private BigDecimal modificationFactor; + + private BigDecimal complexityFactor; + + private BigDecimal totalAdjustmentFactor; + + private BigDecimal designRatio; + + private BigDecimal assessmentArea; + + private BigDecimal assessmentOutputValue; + + private Integer sortNo; + + private String remark; + +} diff --git a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/dal/mysql/planningguidedetail/ProjectPlanningGuideDetailMapper.java b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/dal/mysql/planningguidedetail/ProjectPlanningGuideDetailMapper.java new file mode 100644 index 0000000..0f12b68 --- /dev/null +++ b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/dal/mysql/planningguidedetail/ProjectPlanningGuideDetailMapper.java @@ -0,0 +1,33 @@ +package cn.iocoder.lyzsys.module.tjt.dal.mysql.planningguidedetail; + +import cn.iocoder.lyzsys.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.lyzsys.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planningguidedetail.ProjectPlanningGuideDetailDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +@Mapper +public interface ProjectPlanningGuideDetailMapper extends BaseMapperX { + + default List selectListByPlanningId(Long planningId) { + return selectList(new LambdaQueryWrapperX() + .eq(ProjectPlanningGuideDetailDO::getPlanningId, planningId) + .orderByAsc(ProjectPlanningGuideDetailDO::getSortNo) + .orderByAsc(ProjectPlanningGuideDetailDO::getId)); + } + + default List selectListByPlanningIds(Collection planningIds) { + if (planningIds == null || planningIds.isEmpty()) { + return Collections.emptyList(); + } + return selectList(new LambdaQueryWrapperX() + .in(ProjectPlanningGuideDetailDO::getPlanningId, planningIds) + .orderByAsc(ProjectPlanningGuideDetailDO::getPlanningId) + .orderByAsc(ProjectPlanningGuideDetailDO::getSortNo) + .orderByAsc(ProjectPlanningGuideDetailDO::getId)); + } + +} diff --git a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/enums/ErrorCodeConstants.java b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/enums/ErrorCodeConstants.java index 5e07ab5..af95f6e 100644 --- a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/enums/ErrorCodeConstants.java +++ b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/enums/ErrorCodeConstants.java @@ -23,6 +23,8 @@ public interface ErrorCodeConstants { ErrorCode PROJECT_PLANNING_WORKING_DAY_COUNT_REQUIRED = new ErrorCode(1_020_002_007, "工日法下工日不能为空"); ErrorCode PROJECT_PLANNING_WORKING_DAY_UNIT_PRICE_REQUIRED = new ErrorCode(1_020_002_008, "工日法下工日单价不能为空"); ErrorCode PROJECT_PLANNING_DESIGN_PART_INVALID = new ErrorCode(1_020_002_009, "设计部位不正确"); + ErrorCode PROJECT_PLANNING_GUIDE_DETAIL_NOT_EXISTS = new ErrorCode(1_020_002_010, "指导价法明细不存在"); + ErrorCode PROJECT_PLANNING_GUIDE_DETAIL_SCENE_INVALID = new ErrorCode(1_020_002_011, "当前合约规划不是专业所指导价法,不能维护指导价法明细"); // ========== 季度分配管理 1-020-003-000 ========== ErrorCode PROJECT_PLANNING_QUARTER_NOT_EXISTS = new ErrorCode(1_020_003_000, "季度分配明细不存在"); diff --git a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/enums/ProjectPlanningBizTypeConstants.java b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/enums/ProjectPlanningBizTypeConstants.java index 4192e73..e30cf56 100644 --- a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/enums/ProjectPlanningBizTypeConstants.java +++ b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/enums/ProjectPlanningBizTypeConstants.java @@ -83,6 +83,10 @@ public final class ProjectPlanningBizTypeConstants { return CALCULATION_METHOD_GUIDANCE_PRICE.equals(value); } + public static boolean isMajorGuidanceScene(String ownershipType, String calculationMethod) { + return isMajor(ownershipType) && isGuidancePrice(calculationMethod); + } + public static boolean isContractPrice(String value) { return CALCULATION_METHOD_CONTRACT_PRICE.equals(value); } diff --git a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/planning/ProjectPlanningServiceImpl.java b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/planning/ProjectPlanningServiceImpl.java index a8eb37f..222e481 100644 --- a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/planning/ProjectPlanningServiceImpl.java +++ b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/planning/ProjectPlanningServiceImpl.java @@ -13,6 +13,7 @@ import cn.iocoder.lyzsys.module.tjt.dal.mysql.planning.ProjectPlanningMapper; import cn.iocoder.lyzsys.module.tjt.dal.mysql.planningquarter.ProjectPlanningQuarterMapper; import cn.iocoder.lyzsys.module.tjt.dal.mysql.project.ProjectMapper; import cn.iocoder.lyzsys.module.tjt.service.outputsplit.ProjectOutputSplitService; +import cn.iocoder.lyzsys.module.tjt.service.planningguidedetail.ProjectPlanningGuideDetailService; import cn.iocoder.lyzsys.module.tjt.service.specialtyrolesplit.SpecialtyRoleSplitService; import cn.iocoder.lyzsys.module.tjt.enums.ProjectPlanningBizTypeConstants; import org.springframework.stereotype.Service; @@ -30,7 +31,6 @@ import java.util.Objects; import static cn.iocoder.lyzsys.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_PLANNING_CALCULATION_METHOD_INVALID; -import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_PLANNING_DESIGN_PART_INVALID; import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_PLANNING_NOT_EXISTS; import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_PLANNING_OWNERSHIP_TYPE_IMMUTABLE; import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_PLANNING_OWNERSHIP_TYPE_INVALID; @@ -71,6 +71,8 @@ public class ProjectPlanningServiceImpl implements ProjectPlanningService { private ProjectOutputSplitService projectOutputSplitService; @Resource private SpecialtyRoleSplitService specialtyRoleSplitService; + @Resource + private ProjectPlanningGuideDetailService projectPlanningGuideDetailService; @Override public Long createProjectPlanning(ProjectPlanningSaveReqVO createReqVO) { @@ -80,6 +82,7 @@ public class ProjectPlanningServiceImpl implements ProjectPlanningService { prepareProjectPlanning(planning, project); calculateProjectPlanning(planning); projectPlanningMapper.insert(planning); + projectPlanningGuideDetailService.handlePlanningSaved(planning, null); return planning.getId(); } @@ -92,6 +95,11 @@ public class ProjectPlanningServiceImpl implements ProjectPlanningService { prepareProjectPlanning(updateObj, project); calculateProjectPlanning(updateObj); projectPlanningMapper.updateById(updateObj); + projectPlanningGuideDetailService.handlePlanningSaved(updateObj, dbPlanning); + if (ProjectPlanningBizTypeConstants.isMajorGuidanceScene( + updateObj.getOwnershipType(), updateObj.getCalculationMethod())) { + return; + } refreshQuarterDistributionAmounts(updateObj); } @@ -104,6 +112,7 @@ public class ProjectPlanningServiceImpl implements ProjectPlanningService { projectOutputSplitService.deleteByPlanningId(id); } projectPlanningQuarterMapper.delete(ProjectPlanningQuarterDO::getPlanningId, id); + projectPlanningGuideDetailService.deleteByPlanningId(id); projectPlanningMapper.deleteById(id); } @@ -118,6 +127,7 @@ public class ProjectPlanningServiceImpl implements ProjectPlanningService { projectOutputSplitService.deleteByPlanningIds(ids); } projectPlanningQuarterMapper.deleteBatch(ProjectPlanningQuarterDO::getPlanningId, ids); + projectPlanningGuideDetailService.deleteByPlanningIds(ids); projectPlanningMapper.deleteBatchIds(ids); } @@ -161,10 +171,6 @@ public class ProjectPlanningServiceImpl implements ProjectPlanningService { if (!ProjectPlanningBizTypeConstants.isValidOwnershipType(reqVO.getOwnershipType())) { throw exception(PROJECT_PLANNING_OWNERSHIP_TYPE_INVALID); } - if (StrUtil.isNotBlank(reqVO.getDesignPart()) - && !ProjectPlanningBizTypeConstants.isValidDesignPart(reqVO.getDesignPart())) { - throw exception(PROJECT_PLANNING_DESIGN_PART_INVALID); - } if (StrUtil.isNotBlank(reqVO.getCalculationMethod()) && !ProjectPlanningBizTypeConstants.isValidCalculationMethod(reqVO.getCalculationMethod())) { throw exception(PROJECT_PLANNING_CALCULATION_METHOD_INVALID); @@ -256,16 +262,9 @@ public class ProjectPlanningServiceImpl implements ProjectPlanningService { private void calculateMajorPlanning(ProjectPlanningDO planning) { if (ProjectPlanningBizTypeConstants.isGuidancePrice(planning.getCalculationMethod())) { - BigDecimal totalAdjustmentFactor = calculateTotalAdjustmentFactor(planning); - planning.setTotalAdjustmentFactor(totalAdjustmentFactor); - planning.setAssessmentArea(multiplyAmount(planning.getPlanningArea(), totalAdjustmentFactor)); - BigDecimal applicableUnitPrice = calculateApplicableUnitPrice( - planning.getContractUnitPrice(), planning.getInternalGuidanceUnitPrice()); - planning.setAssessmentOutputValue(multiplyAmount( - applicableUnitPrice, - planning.getAssessmentArea(), - planning.getCurrentDesignStageRatio(), - oneMinus(planning.getReviewOutsourceRatio()))); + planning.setTotalAdjustmentFactor(ZERO_RATIO); + planning.setAssessmentArea(ZERO_AMOUNT); + planning.setAssessmentOutputValue(ZERO_AMOUNT); return; } if (ProjectPlanningBizTypeConstants.isContractPrice(planning.getCalculationMethod())) { diff --git a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/planningguidedetail/ProjectPlanningGuideDetailService.java b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/planningguidedetail/ProjectPlanningGuideDetailService.java new file mode 100644 index 0000000..a0e04ca --- /dev/null +++ b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/planningguidedetail/ProjectPlanningGuideDetailService.java @@ -0,0 +1,27 @@ +package cn.iocoder.lyzsys.module.tjt.service.planningguidedetail; + +import cn.iocoder.lyzsys.module.tjt.controller.admin.planningguidedetail.vo.ProjectPlanningGuideDetailBatchSaveReqVO; +import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planning.ProjectPlanningDO; +import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planningguidedetail.ProjectPlanningGuideDetailDO; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public interface ProjectPlanningGuideDetailService { + + void batchSaveProjectPlanningGuideDetail(ProjectPlanningGuideDetailBatchSaveReqVO reqVO); + + void deleteProjectPlanningGuideDetail(Long id); + + List getProjectPlanningGuideDetailListByPlanningId(Long planningId); + + Map> getProjectPlanningGuideDetailMapByPlanningIds(Collection planningIds); + + void deleteByPlanningId(Long planningId); + + void deleteByPlanningIds(Collection planningIds); + + void handlePlanningSaved(ProjectPlanningDO planning, ProjectPlanningDO dbPlanning); + +} diff --git a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/planningguidedetail/ProjectPlanningGuideDetailServiceImpl.java b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/planningguidedetail/ProjectPlanningGuideDetailServiceImpl.java new file mode 100644 index 0000000..213318a --- /dev/null +++ b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/planningguidedetail/ProjectPlanningGuideDetailServiceImpl.java @@ -0,0 +1,289 @@ +package cn.iocoder.lyzsys.module.tjt.service.planningguidedetail; + +import cn.iocoder.lyzsys.framework.common.util.collection.CollectionUtils; +import cn.iocoder.lyzsys.framework.common.util.object.BeanUtils; +import cn.iocoder.lyzsys.module.tjt.controller.admin.planningguidedetail.vo.ProjectPlanningGuideDetailBatchSaveReqVO; +import cn.iocoder.lyzsys.module.tjt.controller.admin.planningguidedetail.vo.ProjectPlanningGuideDetailSaveReqVO; +import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planning.ProjectPlanningDO; +import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planningquarter.ProjectPlanningQuarterDO; +import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planningguidedetail.ProjectPlanningGuideDetailDO; +import cn.iocoder.lyzsys.module.tjt.dal.mysql.planning.ProjectPlanningMapper; +import cn.iocoder.lyzsys.module.tjt.dal.mysql.planningquarter.ProjectPlanningQuarterMapper; +import cn.iocoder.lyzsys.module.tjt.dal.mysql.planningguidedetail.ProjectPlanningGuideDetailMapper; +import cn.iocoder.lyzsys.module.tjt.enums.ProjectPlanningBizTypeConstants; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import static cn.iocoder.lyzsys.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_PLANNING_DESIGN_PART_INVALID; +import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_PLANNING_GUIDE_DETAIL_NOT_EXISTS; +import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_PLANNING_GUIDE_DETAIL_SCENE_INVALID; +import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_PLANNING_NOT_EXISTS; + +@Service +@Validated +public class ProjectPlanningGuideDetailServiceImpl implements ProjectPlanningGuideDetailService { + + private static final int AMOUNT_SCALE = 2; + private static final int RATIO_SCALE = 4; + + private static final BigDecimal ZERO_AMOUNT = BigDecimal.ZERO.setScale(AMOUNT_SCALE, RoundingMode.HALF_UP); + private static final BigDecimal ZERO_RATIO = BigDecimal.ZERO.setScale(RATIO_SCALE, RoundingMode.HALF_UP); + + @Resource + private ProjectPlanningGuideDetailMapper projectPlanningGuideDetailMapper; + @Resource + private ProjectPlanningMapper projectPlanningMapper; + @Resource + private ProjectPlanningQuarterMapper projectPlanningQuarterMapper; + + @Override + public void batchSaveProjectPlanningGuideDetail(ProjectPlanningGuideDetailBatchSaveReqVO reqVO) { + ProjectPlanningDO planning = validateGuideDetailPlanning(reqVO.getPlanningId()); + List reqDetails = reqVO.getDetails() == null + ? Collections.emptyList() : reqVO.getDetails(); + List existingList = projectPlanningGuideDetailMapper.selectListByPlanningId(reqVO.getPlanningId()); + Map existingMap = + CollectionUtils.convertMap(existingList, ProjectPlanningGuideDetailDO::getId); + Set retainedIds = reqDetails.stream() + .map(ProjectPlanningGuideDetailSaveReqVO::getId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + for (int i = 0; i < reqDetails.size(); i++) { + ProjectPlanningGuideDetailSaveReqVO detailReqVO = reqDetails.get(i); + validateGuideDetailForSave(detailReqVO); + ProjectPlanningGuideDetailDO detail = BeanUtils.toBean(detailReqVO, ProjectPlanningGuideDetailDO.class); + detail.setPlanningId(planning.getId()); + detail.setProjectId(planning.getProjectId()); + detail.setSortNo(detailReqVO.getSortNo() == null ? i + 1 : detailReqVO.getSortNo()); + calculateGuideDetail(detail, planning.getReviewOutsourceRatio()); + if (detail.getId() == null) { + projectPlanningGuideDetailMapper.insert(detail); + continue; + } + if (!existingMap.containsKey(detail.getId())) { + throw exception(PROJECT_PLANNING_GUIDE_DETAIL_NOT_EXISTS); + } + projectPlanningGuideDetailMapper.updateById(detail); + } + + List deletedIds = existingList.stream() + .map(ProjectPlanningGuideDetailDO::getId) + .filter(id -> !retainedIds.contains(id)) + .collect(Collectors.toList()); + if (!deletedIds.isEmpty()) { + projectPlanningGuideDetailMapper.deleteBatchIds(deletedIds); + } + + recalculatePlanningSummary(planning.getId()); + } + + @Override + public void deleteProjectPlanningGuideDetail(Long id) { + ProjectPlanningGuideDetailDO detail = projectPlanningGuideDetailMapper.selectById(id); + if (detail == null) { + throw exception(PROJECT_PLANNING_GUIDE_DETAIL_NOT_EXISTS); + } + projectPlanningGuideDetailMapper.deleteById(id); + recalculatePlanningSummary(detail.getPlanningId()); + } + + @Override + public List getProjectPlanningGuideDetailListByPlanningId(Long planningId) { + validatePlanningExists(planningId); + return projectPlanningGuideDetailMapper.selectListByPlanningId(planningId); + } + + @Override + public Map> getProjectPlanningGuideDetailMapByPlanningIds(Collection planningIds) { + if (planningIds == null || planningIds.isEmpty()) { + return Collections.emptyMap(); + } + return CollectionUtils.convertMultiMap( + projectPlanningGuideDetailMapper.selectListByPlanningIds(planningIds), + ProjectPlanningGuideDetailDO::getPlanningId + ); + } + + @Override + public void deleteByPlanningId(Long planningId) { + if (planningId == null) { + return; + } + projectPlanningGuideDetailMapper.delete(ProjectPlanningGuideDetailDO::getPlanningId, planningId); + } + + @Override + public void deleteByPlanningIds(Collection planningIds) { + if (planningIds == null || planningIds.isEmpty()) { + return; + } + projectPlanningGuideDetailMapper.deleteBatch(ProjectPlanningGuideDetailDO::getPlanningId, planningIds); + } + + @Override + public void handlePlanningSaved(ProjectPlanningDO planning, ProjectPlanningDO dbPlanning) { + boolean currentGuideScene = isGuideDetailScene(planning); + boolean previousGuideScene = isGuideDetailScene(dbPlanning); + if (!currentGuideScene) { + if (previousGuideScene) { + deleteByPlanningId(planning.getId()); + } + return; + } + recalculateGuideDetails(planning.getId(), ratio(planning.getReviewOutsourceRatio())); + recalculatePlanningSummary(planning.getId()); + } + + private void recalculateGuideDetails(Long planningId, BigDecimal reviewOutsourceRatio) { + List detailList = projectPlanningGuideDetailMapper.selectListByPlanningId(planningId); + if (detailList.isEmpty()) { + return; + } + for (ProjectPlanningGuideDetailDO detail : detailList) { + calculateGuideDetail(detail, reviewOutsourceRatio); + projectPlanningGuideDetailMapper.updateById(detail); + } + } + + private void recalculatePlanningSummary(Long planningId) { + ProjectPlanningDO planning = validatePlanningExists(planningId); + List detailList = projectPlanningGuideDetailMapper.selectListByPlanningId(planningId); + + BigDecimal totalDesignArea = ZERO_AMOUNT; + BigDecimal totalAssessmentArea = ZERO_AMOUNT; + BigDecimal totalAssessmentOutputValue = ZERO_AMOUNT; + for (ProjectPlanningGuideDetailDO detail : detailList) { + totalDesignArea = totalDesignArea.add(amount(detail.getDesignArea())).setScale(AMOUNT_SCALE, RoundingMode.HALF_UP); + totalAssessmentArea = totalAssessmentArea.add(amount(detail.getAssessmentArea())).setScale(AMOUNT_SCALE, RoundingMode.HALF_UP); + totalAssessmentOutputValue = totalAssessmentOutputValue.add(amount(detail.getAssessmentOutputValue())) + .setScale(AMOUNT_SCALE, RoundingMode.HALF_UP); + } + + ProjectPlanningDO updateObj = new ProjectPlanningDO(); + updateObj.setId(planningId); + updateObj.setPlanningArea(totalDesignArea); + updateObj.setContractUnitPrice(divideAmount(planning.getPlanningAmount(), totalDesignArea)); + updateObj.setTotalAdjustmentFactor(ZERO_RATIO); + updateObj.setAssessmentArea(totalAssessmentArea); + updateObj.setVirtualOutputValue(ZERO_AMOUNT); + updateObj.setAssessmentOutputValue(totalAssessmentOutputValue); + projectPlanningMapper.updateById(updateObj); + + ProjectPlanningDO refreshedPlanning = projectPlanningMapper.selectById(planningId); + refreshQuarterDistributionAmounts(refreshedPlanning); + } + + private void validateGuideDetailForSave(ProjectPlanningGuideDetailSaveReqVO reqVO) { + if (!ProjectPlanningBizTypeConstants.isValidDesignPart(reqVO.getDesignPart())) { + throw exception(PROJECT_PLANNING_DESIGN_PART_INVALID); + } + } + + private ProjectPlanningDO validateGuideDetailPlanning(Long planningId) { + ProjectPlanningDO planning = validatePlanningExists(planningId); + if (!isGuideDetailScene(planning)) { + throw exception(PROJECT_PLANNING_GUIDE_DETAIL_SCENE_INVALID); + } + return planning; + } + + private ProjectPlanningDO validatePlanningExists(Long planningId) { + ProjectPlanningDO planning = projectPlanningMapper.selectById(planningId); + if (planning == null) { + throw exception(PROJECT_PLANNING_NOT_EXISTS); + } + return planning; + } + + private boolean isGuideDetailScene(ProjectPlanningDO planning) { + return planning != null && ProjectPlanningBizTypeConstants.isMajorGuidanceScene( + planning.getOwnershipType(), planning.getCalculationMethod()); + } + + private void calculateGuideDetail(ProjectPlanningGuideDetailDO detail, BigDecimal reviewOutsourceRatio) { + detail.setDesignArea(amount(detail.getDesignArea())); + detail.setInternalGuidanceUnitPrice(amount(detail.getInternalGuidanceUnitPrice())); + detail.setDrawingSetFactor(ratio(detail.getDrawingSetFactor())); + detail.setScaleFactor(ratio(detail.getScaleFactor())); + detail.setModificationFactor(ratio(detail.getModificationFactor())); + detail.setComplexityFactor(ratio(detail.getComplexityFactor())); + detail.setDesignRatio(ratio(detail.getDesignRatio())); + detail.setTotalAdjustmentFactor(multiplyRatio( + detail.getDrawingSetFactor(), + detail.getScaleFactor(), + detail.getModificationFactor(), + detail.getComplexityFactor() + )); + detail.setAssessmentArea(multiplyAmount(detail.getDesignArea(), detail.getTotalAdjustmentFactor())); + detail.setAssessmentOutputValue(multiplyAmount( + detail.getInternalGuidanceUnitPrice(), + detail.getAssessmentArea(), + detail.getDesignRatio(), + oneMinus(reviewOutsourceRatio) + )); + } + + private void refreshQuarterDistributionAmounts(ProjectPlanningDO planning) { + if (planning == null) { + return; + } + List quarterList = projectPlanningQuarterMapper.selectListByPlanningId(planning.getId()); + for (ProjectPlanningQuarterDO quarter : quarterList) { + quarter.setDistributionAmount(multiplyAmount( + planning.getAssessmentOutputValue(), + planning.getTotalDistributionAmount(), + ratio(quarter.getDistributionRatio()) + )); + projectPlanningQuarterMapper.updateById(quarter); + } + } + + private BigDecimal amount(BigDecimal value) { + return value == null ? ZERO_AMOUNT : value.setScale(AMOUNT_SCALE, RoundingMode.HALF_UP); + } + + private BigDecimal ratio(BigDecimal value) { + return value == null ? ZERO_RATIO : value.setScale(RATIO_SCALE, RoundingMode.HALF_UP); + } + + private BigDecimal oneMinus(BigDecimal value) { + return BigDecimal.ONE.subtract(ratio(value)).setScale(RATIO_SCALE, RoundingMode.HALF_UP); + } + + private BigDecimal multiplyAmount(BigDecimal... values) { + BigDecimal result = BigDecimal.ONE; + for (BigDecimal value : values) { + result = result.multiply(value == null ? BigDecimal.ZERO : value); + } + return result.setScale(AMOUNT_SCALE, RoundingMode.HALF_UP); + } + + private BigDecimal multiplyRatio(BigDecimal... values) { + BigDecimal result = BigDecimal.ONE; + for (BigDecimal value : values) { + result = result.multiply(value == null ? BigDecimal.ZERO : value); + } + return result.setScale(RATIO_SCALE, RoundingMode.HALF_UP); + } + + private BigDecimal divideAmount(BigDecimal dividend, BigDecimal divisor) { + if (dividend == null || divisor == null || divisor.compareTo(BigDecimal.ZERO) == 0) { + return ZERO_AMOUNT; + } + return dividend.divide(divisor, AMOUNT_SCALE, RoundingMode.HALF_UP); + } + +} diff --git a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/project/ProjectServiceImpl.java b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/project/ProjectServiceImpl.java index 35c54e4..8863aed 100644 --- a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/project/ProjectServiceImpl.java +++ b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/project/ProjectServiceImpl.java @@ -20,6 +20,7 @@ import cn.iocoder.lyzsys.module.tjt.dal.mysql.project.ProjectMapper; import cn.iocoder.lyzsys.module.tjt.dal.mysql.projectroleperson.ProjectRolePersonMapper; import cn.iocoder.lyzsys.module.tjt.service.employee.EmployeeService; import cn.iocoder.lyzsys.module.tjt.service.outputsplit.ProjectOutputSplitService; +import cn.iocoder.lyzsys.module.tjt.service.planningguidedetail.ProjectPlanningGuideDetailService; import cn.iocoder.lyzsys.module.tjt.service.specialtyrolesplit.SpecialtyRoleSplitService; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -69,6 +70,8 @@ public class ProjectServiceImpl implements ProjectService { private ProjectOutputSplitService projectOutputSplitService; @Resource private SpecialtyRoleSplitService specialtyRoleSplitService; + @Resource + private ProjectPlanningGuideDetailService projectPlanningGuideDetailService; @Override public Long createProject(ProjectSaveReqVO createReqVO) { @@ -300,6 +303,7 @@ public class ProjectServiceImpl implements ProjectService { projectOutputSplitService.deleteByPlanningIds(planningIds); } projectPlanningQuarterMapper.deleteBatch(ProjectPlanningQuarterDO::getPlanningId, planningIds); + projectPlanningGuideDetailService.deleteByPlanningIds(planningIds); projectPlanningMapper.deleteBatchIds(planningIds); } diff --git a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/report/ProjectOutputReportServiceImpl.java b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/report/ProjectOutputReportServiceImpl.java index f4b29b0..a0aea26 100644 --- a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/report/ProjectOutputReportServiceImpl.java +++ b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/report/ProjectOutputReportServiceImpl.java @@ -18,6 +18,7 @@ import cn.iocoder.lyzsys.module.tjt.dal.dataobject.office.OfficeDO; import cn.iocoder.lyzsys.module.tjt.dal.dataobject.outputsplit.ProjectOutputSplitDO; import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planning.ProjectPlanningDO; import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planningquarter.ProjectPlanningQuarterDO; +import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planningguidedetail.ProjectPlanningGuideDetailDO; import cn.iocoder.lyzsys.module.tjt.dal.dataobject.project.ProjectDO; import cn.iocoder.lyzsys.module.tjt.dal.dataobject.projectroleperson.ProjectRolePersonDO; import cn.iocoder.lyzsys.module.tjt.dal.dataobject.yearkvalue.YearKValueDO; @@ -32,6 +33,7 @@ import cn.iocoder.lyzsys.module.tjt.dal.mysql.yearkvalue.YearKValueMapper; import cn.iocoder.lyzsys.module.tjt.enums.OutputSplitBizConstants; import cn.iocoder.lyzsys.module.tjt.enums.ProjectPlanningBizTypeConstants; import cn.iocoder.lyzsys.module.tjt.service.outputsplit.ProjectOutputSplitService; +import cn.iocoder.lyzsys.module.tjt.service.planningguidedetail.ProjectPlanningGuideDetailService; import cn.iocoder.lyzsys.module.tjt.service.report.builder.EmployeeOutputSummaryExcelBuilder; import cn.iocoder.lyzsys.module.tjt.service.report.builder.ProjectBudgetExcelBuilder; import cn.iocoder.lyzsys.module.tjt.service.report.builder.ProjectLeadQuarterOutputExcelBuilder; @@ -105,6 +107,8 @@ public class ProjectOutputReportServiceImpl implements ProjectOutputReportServic @Resource private SpecialtyRoleSplitService specialtyRoleSplitService; @Resource + private ProjectPlanningGuideDetailService projectPlanningGuideDetailService; + @Resource private ProjectBudgetExcelBuilder projectBudgetExcelBuilder; @Resource private ProjectQuarterOutputExcelBuilder projectQuarterOutputExcelBuilder; @@ -124,6 +128,10 @@ public class ProjectOutputReportServiceImpl implements ProjectOutputReportServic List planningList = sortPlanningList(projectPlanningMapper.selectListByProjectId(project.getId())); List rolePersons = projectRolePersonMapper.selectListByProjectId(project.getId()); Map> quarterMap = getQuarterMap(planningList); + Map> guideDetailMap = projectPlanningGuideDetailService + .getProjectPlanningGuideDetailMapByPlanningIds(planningList.stream() + .map(ProjectPlanningDO::getId) + .collect(Collectors.toList())); ProjectBudgetExcelBuilder.ExportData data = new ProjectBudgetExcelBuilder.ExportData(); data.setProjectCode(""); @@ -146,7 +154,7 @@ public class ProjectOutputReportServiceImpl implements ProjectOutputReportServic totalAssessmentOutputValue = totalAssessmentOutputValue.add(amount(planning.getAssessmentOutputValue())) .setScale(2, RoundingMode.HALF_UP); } - data.setBudgetRows(buildProjectBudgetRows(planningList)); + data.setBudgetRows(buildProjectBudgetRows(planningList, guideDetailMap)); data.setTotalAssessmentArea(totalAssessmentArea); data.setTotalAssessmentOutputValue(totalAssessmentOutputValue); data.setTotalAssessmentOutputValueWan(amountToWan(totalAssessmentOutputValue)); @@ -191,7 +199,6 @@ public class ProjectOutputReportServiceImpl implements ProjectOutputReportServic List planningList = projectPlanningMapper.selectListByProjectId(project.getId()).stream() .sorted(Comparator .comparingInt((ProjectPlanningDO item) -> projectLeadOutputTypeOrder(item.getOwnershipType())) - .thenComparingInt((ProjectPlanningDO item) -> designPartOrder(item.getDesignPart())) .thenComparing(ProjectPlanningDO::getId)) .collect(Collectors.toList()); List projectRolePersons = projectRolePersonMapper.selectListByProjectId(project.getId()); @@ -737,102 +744,128 @@ public class ProjectOutputReportServiceImpl implements ProjectOutputReportServic return rows; } - private List buildProjectBudgetRows(List planningList) { + private List buildProjectBudgetRows( + List planningList, + Map> guideDetailMap) { if (planningList == null || planningList.isEmpty()) { return Collections.emptyList(); } - Map> planningContentMap = new LinkedHashMap<>(); + List rows = new ArrayList<>(); planningList.stream() .sorted(Comparator.comparing(ProjectPlanningDO::getId)) - .forEach(planning -> planningContentMap - .computeIfAbsent(resolveBudgetPlanningGroupKey(planning), key -> new ArrayList<>()) - .add(planning)); - - List rows = new ArrayList<>(); - for (List planningGroup : planningContentMap.values()) { - appendBudgetPlanningRows(rows, planningGroup); - } + .forEach(planning -> { + if (!ProjectPlanningBizTypeConstants.isMajorGuidanceScene( + planning.getOwnershipType(), planning.getCalculationMethod())) { + return; + } + List detailList = guideDetailMap == null + ? Collections.emptyList() + : guideDetailMap.getOrDefault(planning.getId(), Collections.emptyList()); + if (detailList.isEmpty()) { + return; + } + appendBudgetPlanningRows(rows, planning, sortBudgetGuideDetailList(detailList)); + }); return rows; } private void appendBudgetPlanningRows(List rows, - List planningGroup) { + ProjectPlanningDO planning, + List planningGroup) { if (planningGroup == null || planningGroup.isEmpty()) { return; } - List groundPlanningList = filterBudgetPartPlanningList( + String planningContent = resolveBudgetPlanningContent(planning); + List groundPlanningList = filterBudgetPartPlanningList( planningGroup, ProjectPlanningBizTypeConstants.DESIGN_PART_REAL_ESTATE); - List undergroundPlanningList = filterBudgetPartPlanningList( + List undergroundPlanningList = filterBudgetPartPlanningList( planningGroup, ProjectPlanningBizTypeConstants.DESIGN_PART_UNDERGROUND); - appendBudgetPartRows(rows, ProjectPlanningBizTypeConstants.DESIGN_PART_REAL_ESTATE, groundPlanningList); - appendBudgetPartRows(rows, ProjectPlanningBizTypeConstants.DESIGN_PART_UNDERGROUND, undergroundPlanningList); - rows.add(buildBudgetSummaryRow(ProjectBudgetExcelBuilder.BudgetRowType.PLANNING_TOTAL, "总计", planningGroup)); + appendBudgetPartRows(rows, planning, planningContent, + ProjectPlanningBizTypeConstants.DESIGN_PART_REAL_ESTATE, groundPlanningList); + appendBudgetPartRows(rows, planning, planningContent, + ProjectPlanningBizTypeConstants.DESIGN_PART_UNDERGROUND, undergroundPlanningList); + rows.add(buildBudgetSummaryRow(ProjectBudgetExcelBuilder.BudgetRowType.PLANNING_TOTAL, + planning, planningContent, "总计", planningGroup)); } - private void appendBudgetPartRows(List rows, String designPart, - List partPlanningList) { + private void appendBudgetPartRows(List rows, + ProjectPlanningDO planning, + String planningContent, + String designPart, + List partPlanningList) { if (partPlanningList == null || partPlanningList.isEmpty()) { - rows.add(buildBudgetEmptyRow(designPart)); return; } - for (ProjectPlanningDO planning : partPlanningList) { - rows.add(buildBudgetDetailRow(designPart, planning)); + for (ProjectPlanningGuideDetailDO detail : partPlanningList) { + rows.add(buildBudgetDetailRow(planning, planningContent, designPart, detail)); } if (partPlanningList.size() > 2) { rows.add(buildBudgetSummaryRow(ProjectBudgetExcelBuilder.BudgetRowType.PART_SUBTOTAL, - designPart + "总计", partPlanningList)); + planning, planningContent, designPart + "总计", partPlanningList)); } } - private List filterBudgetPartPlanningList(List planningGroup, String designPart) { + private List filterBudgetPartPlanningList( + List planningGroup, String designPart) { return planningGroup.stream() .filter(item -> Objects.equals(item.getDesignPart(), designPart)) - .sorted(Comparator.comparing(ProjectPlanningDO::getId)) + .sorted(Comparator + .comparingInt((ProjectPlanningGuideDetailDO item) -> + item.getSortNo() == null ? Integer.MAX_VALUE : item.getSortNo()) + .thenComparing(ProjectPlanningGuideDetailDO::getId)) .collect(Collectors.toList()); } - private String resolveBudgetPlanningGroupKey(ProjectPlanningDO planning) { - String planningContent = defaultString(planning == null ? null : planning.getPlanningContent()); - return planningContent.isEmpty() ? "__PLANNING__" + (planning == null ? "" : planning.getId()) : planningContent; + private String resolveBudgetPlanningContent(ProjectPlanningDO planning) { + return defaultString(planning == null ? null : planning.getPlanningContent()); } - private ProjectBudgetExcelBuilder.BudgetRow buildBudgetDetailRow(String designPart, ProjectPlanningDO planning) { - ProjectBudgetExcelBuilder.BudgetRow row = initBudgetRow(ProjectBudgetExcelBuilder.BudgetRowType.DETAIL, designPart); - row.setPlanningContent(defaultString(planning.getPlanningContent())); - row.setDisplayBuildingType(defaultString(planning.getBuildingType())); - row.setInternalGuidanceUnitPrice(planning.getInternalGuidanceUnitPrice()); - row.setDesignArea(planning.getPlanningArea()); - row.setBuildingOrUnitCount(planning.getBuildingOrUnitCount()); - row.setDrawingSetFactor(planning.getDrawingSetFactor()); - row.setScaleFactor(planning.getScaleFactor()); - row.setModificationFactor(planning.getModificationFactor()); - row.setComplexityFactor(planning.getComplexityFactor()); - row.setSubtotalArea(planning.getAssessmentArea()); - row.setCurrentDesignStageRatio(planning.getCurrentDesignStageRatio()); - row.setAssessmentOutputValueWan(planning.getAssessmentOutputValue() == null - ? null : amountToWan(planning.getAssessmentOutputValue())); + private ProjectBudgetExcelBuilder.BudgetRow buildBudgetDetailRow(ProjectPlanningDO planning, + String planningContent, + String designPart, + ProjectPlanningGuideDetailDO detail) { + ProjectBudgetExcelBuilder.BudgetRow row = initBudgetRow( + ProjectBudgetExcelBuilder.BudgetRowType.DETAIL, planning, planningContent, designPart); + row.setDisplayBuildingType(defaultString(detail.getBuildingType())); + row.setInternalGuidanceUnitPrice(detail.getInternalGuidanceUnitPrice()); + row.setDesignArea(detail.getDesignArea()); + row.setBuildingOrUnitCount(detail.getBuildingOrUnitCount()); + row.setDrawingSetFactor(detail.getDrawingSetFactor()); + row.setScaleFactor(detail.getScaleFactor()); + row.setModificationFactor(detail.getModificationFactor()); + row.setComplexityFactor(detail.getComplexityFactor()); + row.setSubtotalArea(detail.getAssessmentArea()); + row.setCurrentDesignStageRatio(detail.getDesignRatio()); + row.setAssessmentOutputValueWan(detail.getAssessmentOutputValue() == null + ? null : amountToWan(detail.getAssessmentOutputValue())); return row; } private ProjectBudgetExcelBuilder.BudgetRow buildBudgetEmptyRow(String designPart) { - return initBudgetRow(ProjectBudgetExcelBuilder.BudgetRowType.PART_EMPTY, designPart); + return initBudgetRow(ProjectBudgetExcelBuilder.BudgetRowType.PART_EMPTY, null, "", designPart); } private ProjectBudgetExcelBuilder.BudgetRow buildBudgetSummaryRow(ProjectBudgetExcelBuilder.BudgetRowType rowType, + ProjectPlanningDO planning, + String planningContent, String displayDesignPart, - List planningList) { - ProjectBudgetExcelBuilder.BudgetRow row = initBudgetRow(rowType, displayDesignPart); - row.setDesignArea(sumPlanningArea(planningList)); - row.setSubtotalArea(sumAssessmentArea(planningList)); - row.setAssessmentOutputValueWan(amountToWan(sumAssessmentOutputValue(planningList))); + List detailList) { + ProjectBudgetExcelBuilder.BudgetRow row = initBudgetRow(rowType, planning, planningContent, displayDesignPart); + row.setDesignArea(sumGuideDetailDesignArea(detailList)); + row.setSubtotalArea(sumGuideDetailAssessmentArea(detailList)); + row.setAssessmentOutputValueWan(amountToWan(sumGuideDetailAssessmentOutputValue(detailList))); return row; } private ProjectBudgetExcelBuilder.BudgetRow initBudgetRow(ProjectBudgetExcelBuilder.BudgetRowType rowType, + ProjectPlanningDO planning, + String planningContent, String displayDesignPart) { ProjectBudgetExcelBuilder.BudgetRow row = new ProjectBudgetExcelBuilder.BudgetRow(); row.setRowType(rowType); + row.setPlanningId(planning == null ? null : planning.getId()); + row.setPlanningContent(planningContent); row.setDisplayDesignPart(displayDesignPart); return row; } @@ -870,6 +903,48 @@ public class ProjectOutputReportServiceImpl implements ProjectOutputReportServic return total; } + private List sortBudgetGuideDetailList(List detailList) { + return detailList.stream() + .sorted(Comparator + .comparingInt((ProjectPlanningGuideDetailDO item) -> designPartOrder(item.getDesignPart())) + .thenComparingInt(item -> item.getSortNo() == null ? Integer.MAX_VALUE : item.getSortNo()) + .thenComparing(ProjectPlanningGuideDetailDO::getId)) + .collect(Collectors.toList()); + } + + private BigDecimal sumGuideDetailDesignArea(List detailList) { + BigDecimal total = ZERO_AMOUNT; + if (detailList == null || detailList.isEmpty()) { + return total; + } + for (ProjectPlanningGuideDetailDO detail : detailList) { + total = total.add(amount(detail.getDesignArea())).setScale(2, RoundingMode.HALF_UP); + } + return total; + } + + private BigDecimal sumGuideDetailAssessmentArea(List detailList) { + BigDecimal total = ZERO_AMOUNT; + if (detailList == null || detailList.isEmpty()) { + return total; + } + for (ProjectPlanningGuideDetailDO detail : detailList) { + total = total.add(amount(detail.getAssessmentArea())).setScale(2, RoundingMode.HALF_UP); + } + return total; + } + + private BigDecimal sumGuideDetailAssessmentOutputValue(List detailList) { + BigDecimal total = ZERO_AMOUNT; + if (detailList == null || detailList.isEmpty()) { + return total; + } + for (ProjectPlanningGuideDetailDO detail : detailList) { + total = total.add(amount(detail.getAssessmentOutputValue())).setScale(2, RoundingMode.HALF_UP); + } + return total; + } + private List buildQuarterBudgetRows( List planningList, Map> quarterMap, Integer reportYear) { @@ -958,9 +1033,7 @@ public class ProjectOutputReportServiceImpl implements ProjectOutputReportServic private List sortPlanningList(List planningList) { return planningList.stream() - .sorted(Comparator - .comparingInt((ProjectPlanningDO item) -> designPartOrder(item.getDesignPart())) - .thenComparing(ProjectPlanningDO::getId)) + .sorted(Comparator.comparing(ProjectPlanningDO::getId)) .collect(Collectors.toList()); } diff --git a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/report/builder/ProjectBudgetExcelBuilder.java b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/report/builder/ProjectBudgetExcelBuilder.java index 989bd71..2f90844 100644 --- a/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/report/builder/ProjectBudgetExcelBuilder.java +++ b/lyzsys-module-tjt/src/main/java/cn/iocoder/lyzsys/module/tjt/service/report/builder/ProjectBudgetExcelBuilder.java @@ -12,6 +12,7 @@ import org.springframework.stereotype.Component; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.List; +import java.util.Objects; @Component public class ProjectBudgetExcelBuilder extends AbstractProjectOutputExcelBuilder { @@ -22,8 +23,7 @@ public class ProjectBudgetExcelBuilder extends AbstractProjectOutputExcelBuilder private static final String BUDGET_SHEET_TITLE = "附件4 建筑(装饰)工程项目考核产值预算表"; private static final String QUARTER_BUDGET_SHEET_NAME = "项目考核产值年度季度预算计取表"; private static final String QUARTER_BUDGET_SHEET_FALLBACK_NAME = "年度季度预算计取表"; - private static final String QUARTER_BUDGET_REMARK = "注:\n" - + "1、设计阶段考核产值为总考核产值×阶段占比;本年度/季度考核产值为阶段考核产值×本年度/季度发放比例。"; + private static final String QUARTER_BUDGET_REMARK = "注:1、设计阶段考核产值为总考核产值×阶段占比;本年度/季度考核产值为阶段考核产值×本年度/季度发放比例。"; private static final String DATE_TEXT = "日期: 年 月 日"; public Workbook build(ExportData data) { @@ -52,6 +52,7 @@ public class ProjectBudgetExcelBuilder extends AbstractProjectOutputExcelBuilder int rowIndex = 0; rowIndex = writeMergedTitleRow(sheet, rowIndex, BUDGET_SHEET_TITLE, titleStyle, 12); rowIndex = buildBudgetHeader(sheet, rowIndex, headerStyle); + int dataStartRow = rowIndex; if (data.getBudgetRows() != null) { for (BudgetRow rowData : data.getBudgetRows()) { @@ -75,6 +76,7 @@ public class ProjectBudgetExcelBuilder extends AbstractProjectOutputExcelBuilder } } } + mergeBudgetPlanningContentColumn(sheet, data.getBudgetRows(), dataStartRow); Row remarkRow = sheet.createRow(rowIndex++); setMergedRegionText(sheet, remarkRow.getRowNum(), remarkRow.getRowNum(), 0, 12, @@ -116,7 +118,7 @@ public class ProjectBudgetExcelBuilder extends AbstractProjectOutputExcelBuilder private void writeBudgetDetailRow(Sheet sheet, int rowIndex, BudgetRow rowData, CellStyle style) { Row row = sheet.createRow(rowIndex); - setText(row, 0, "", style); + setText(row, 0, safeText(rowData.getPlanningContent()), style); setText(row, 1, safeText(rowData.getDisplayDesignPart()), style); setText(row, 2, safeText(rowData.getDisplayBuildingType()), style); setText(row, 3, textOrBlank(rowData.getInternalGuidanceUnitPrice()), style); @@ -133,7 +135,7 @@ public class ProjectBudgetExcelBuilder extends AbstractProjectOutputExcelBuilder private void writeBudgetEmptyRow(Sheet sheet, int rowIndex, BudgetRow rowData, CellStyle style) { Row row = sheet.createRow(rowIndex); - setText(row, 0, "", style); + setText(row, 0, safeText(rowData.getPlanningContent()), style); setText(row, 1, safeText(rowData.getDisplayDesignPart()), style); for (int i = 2; i <= 12; i++) { setText(row, i, "", style); @@ -142,7 +144,7 @@ public class ProjectBudgetExcelBuilder extends AbstractProjectOutputExcelBuilder private void writeBudgetSummaryRow(Sheet sheet, int rowIndex, BudgetRow rowData, CellStyle style) { Row row = sheet.createRow(rowIndex); - setText(row, 0, "", style); + setText(row, 0, safeText(rowData.getPlanningContent()), style); setText(row, 1, safeText(rowData.getDisplayDesignPart()), style); setMergedRegionText(sheet, rowIndex, rowIndex, 2, 3, SUMMARY_PLACEHOLDER, style); setText(row, 4, text(rowData.getDesignArea()), style); @@ -152,6 +154,31 @@ public class ProjectBudgetExcelBuilder extends AbstractProjectOutputExcelBuilder setText(row, 12, text(rowData.getAssessmentOutputValueWan()), style); } + private void mergeBudgetPlanningContentColumn(Sheet sheet, List budgetRows, int dataStartRow) { + if (budgetRows == null || budgetRows.isEmpty()) { + return; + } + int mergeStartRow = dataStartRow; + Long currentPlanningId = budgetRows.get(0).getPlanningId(); + for (int i = 1; i < budgetRows.size(); i++) { + Long planningId = budgetRows.get(i).getPlanningId(); + if (Objects.equals(currentPlanningId, planningId)) { + continue; + } + mergeBudgetPlanningContentRange(sheet, mergeStartRow, dataStartRow + i - 1); + mergeStartRow = dataStartRow + i; + currentPlanningId = planningId; + } + mergeBudgetPlanningContentRange(sheet, mergeStartRow, dataStartRow + budgetRows.size() - 1); + } + + private void mergeBudgetPlanningContentRange(Sheet sheet, int startRow, int endRow) { + if (endRow <= startRow) { + return; + } + merge(sheet, startRow, endRow, 0, 0); + } + private void buildQuarterBudgetSheet(Workbook workbook, ExportData data) { Sheet sheet = createSheet(workbook, QUARTER_BUDGET_SHEET_NAME, QUARTER_BUDGET_SHEET_FALLBACK_NAME); setColumnWidths(sheet, 10, 20, 18, 24, 16, 16, 16, 12, 14, 14, 12, 12, 12, 12, 14, 14, 12, 12, 12, 12, 14, 24); @@ -167,9 +194,12 @@ public class ProjectBudgetExcelBuilder extends AbstractProjectOutputExcelBuilder IndexedColors.GOLD, HorizontalAlignment.CENTER, true, (short) 10); CellStyle centeredCellStyle = createDerivedStyle(workbook, cellStyle, null, HorizontalAlignment.CENTER, false, (short) 10); + CellStyle rightAlignedInfoStyle = createDerivedStyle(workbook, infoValueStyle, + null, HorizontalAlignment.RIGHT, false, (short) 10); int rowIndex = 0; rowIndex = buildQuarterBudgetHeader(sheet, rowIndex, data, headerStyle, centeredCellStyle); + int dataStartRow = rowIndex; for (QuarterBudgetRow rowData : data.getQuarterBudgetRows()) { Row row = sheet.createRow(rowIndex++); @@ -201,6 +231,7 @@ public class ProjectBudgetExcelBuilder extends AbstractProjectOutputExcelBuilder setText(row, 20, textOrBlank(rowData.getYearTotalAmountWan()), cellStyle); setText(row, 21, safeText(rowData.getRatioRemark()), cellStyle); } + mergeQuarterBudgetProjectAndOutputType(sheet, data.getQuarterBudgetRows(), dataStartRow); Row remarkRow = sheet.createRow(rowIndex++); setMergedRegionText(sheet, remarkRow.getRowNum(), remarkRow.getRowNum(), 0, 21, @@ -215,12 +246,55 @@ public class ProjectBudgetExcelBuilder extends AbstractProjectOutputExcelBuilder "市场运营中心负责人:", infoValueStyle); Row dateRow = sheet.createRow(rowIndex); - setMergedRegionText(sheet, dateRow.getRowNum(), dateRow.getRowNum(), 0, 6, - DATE_TEXT, infoValueStyle); - setMergedRegionText(sheet, dateRow.getRowNum(), dateRow.getRowNum(), 7, 13, - DATE_TEXT, infoValueStyle); - setMergedRegionText(sheet, dateRow.getRowNum(), dateRow.getRowNum(), 14, 21, - DATE_TEXT, infoValueStyle); + setMergedRegionText(sheet, dateRow.getRowNum(), dateRow.getRowNum(), 0, 21, + DATE_TEXT, rightAlignedInfoStyle); + } + + private void mergeQuarterBudgetProjectAndOutputType(Sheet sheet, + List rows, + int dataStartRow) { + if (rows == null || rows.isEmpty()) { + return; + } + int detailRowCount = 0; + for (QuarterBudgetRow row : rows) { + if (row != null && !row.isTotalRow()) { + detailRowCount++; + } + } + if (detailRowCount <= 0) { + return; + } + if (detailRowCount > 1) { + merge(sheet, dataStartRow, dataStartRow + detailRowCount - 1, 1, 1); + } + + int currentStartRow = -1; + String currentOutputType = null; + int currentRowIndex = dataStartRow; + for (QuarterBudgetRow row : rows) { + if (row == null || row.isTotalRow()) { + continue; + } + String outputType = safeText(row.getOutputType()); + if (currentStartRow < 0) { + currentStartRow = currentRowIndex; + currentOutputType = outputType; + } else if (!Objects.equals(currentOutputType, outputType)) { + mergeQuarterBudgetOutputTypeRange(sheet, currentStartRow, currentRowIndex - 1); + currentStartRow = currentRowIndex; + currentOutputType = outputType; + } + currentRowIndex++; + } + mergeQuarterBudgetOutputTypeRange(sheet, currentStartRow, dataStartRow + detailRowCount - 1); + } + + private void mergeQuarterBudgetOutputTypeRange(Sheet sheet, int startRow, int endRow) { + if (startRow < 0 || endRow <= startRow) { + return; + } + merge(sheet, startRow, endRow, 2, 2); } private int buildQuarterBudgetHeader(Sheet sheet, int rowIndex, ExportData data, @@ -344,6 +418,7 @@ public class ProjectBudgetExcelBuilder extends AbstractProjectOutputExcelBuilder @Data public static class BudgetRow { private BudgetRowType rowType; + private Long planningId; private String planningContent; private String displayDesignPart; private String displayBuildingType; diff --git a/lyzsys-server/src/main/resources/application-local.yaml b/lyzsys-server/src/main/resources/application-local.yaml index 9b1eba0..099659b 100644 --- a/lyzsys-server/src/main/resources/application-local.yaml +++ b/lyzsys-server/src/main/resources/application-local.yaml @@ -52,7 +52,7 @@ spring: # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例 # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 - url: jdbc:sqlserver://192.168.1.34:1433;DatabaseName=tjt_czjs;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true;useUnicode=true;characterEncoding=utf-8 # SQLServer 连接的示例 + url: jdbc:sqlserver://47.106.184.244:14333;DatabaseName=tjt_czjs;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true;useUnicode=true;characterEncoding=utf-8 # SQLServer 连接的示例 # # url: jdbc:dm://127.0.0.1:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 # url: jdbc:kingbase8://127.0.0.1:54321/test # 人大金仓 KingbaseES 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/postgres # OpenGauss 连接的示例