修改代码

This commit is contained in:
lzm
2026-04-17 18:15:34 +08:00
parent 4bf348b847
commit 719997218c
52 changed files with 3787 additions and 3 deletions

64
lyzsys-module-tjt/pom.xml Normal file
View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>cn.iocoder.boot</groupId>
<artifactId>lyzsys</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>lyzsys-module-tjt</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>
tjt 模块,承载特建投设计产值统计业务。
</description>
<dependencies>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>lyzsys-module-infra</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>lyzsys-spring-boot-starter-biz-tenant</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>lyzsys-spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>lyzsys-spring-boot-starter-mybatis</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>lyzsys-spring-boot-starter-redis</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>lyzsys-spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>lyzsys-spring-boot-starter-excel</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,43 @@
package cn.iocoder.lyzsys.module.tjt.controller.admin.outputsplit;
import cn.iocoder.lyzsys.framework.common.pojo.CommonResult;
import cn.iocoder.lyzsys.module.tjt.controller.admin.outputsplit.vo.ProjectOutputSplitRespVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.outputsplit.vo.ProjectOutputSplitSaveReqVO;
import cn.iocoder.lyzsys.module.tjt.service.outputsplit.ProjectOutputSplitService;
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.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import static cn.iocoder.lyzsys.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 特建投页面4拆分比例")
@RestController
@RequestMapping("/tjt/output-split")
@Validated
public class ProjectOutputSplitController {
@Resource
private ProjectOutputSplitService projectOutputSplitService;
@GetMapping("/get-by-planning")
@Operation(summary = "根据合约规划获得页面4拆分比例")
@Parameter(name = "planningId", description = "合约规划 ID", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('tjt:output-split:query')")
public CommonResult<ProjectOutputSplitRespVO> getProjectOutputSplitByPlanningId(@RequestParam("planningId") Long planningId) {
return success(projectOutputSplitService.getProjectOutputSplit(planningId));
}
@PutMapping("/save")
@Operation(summary = "保存页面4拆分比例")
@PreAuthorize("@ss.hasPermission('tjt:output-split:update')")
public CommonResult<Long> saveProjectOutputSplit(@Valid @RequestBody ProjectOutputSplitSaveReqVO reqVO) {
return success(projectOutputSplitService.saveProjectOutputSplit(reqVO));
}
}

View File

@@ -0,0 +1,99 @@
package cn.iocoder.lyzsys.module.tjt.controller.admin.outputsplit.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 页面4拆分比例 Response VO")
@Data
public class ProjectOutputSplitRespVO {
@Schema(description = "页面4拆分 ID", example = "1")
private Long id;
@Schema(description = "项目 ID", example = "1")
private Long projectId;
@Schema(description = "合约规划 ID", example = "1")
private Long planningId;
@Schema(description = "项目名称")
private String projectName;
@Schema(description = "规划内容")
private String planningContent;
@Schema(description = "年度")
private Integer year;
@Schema(description = "考核产值")
private BigDecimal assessmentOutputValue;
@Schema(description = "项目经理")
private String projectManagerName;
@Schema(description = "项目负责人")
private String engineeringLeaderName;
@Schema(description = "项目经理比例")
private BigDecimal projectManagerRatio;
@Schema(description = "项目经理金额")
private BigDecimal projectManagerAmount;
@Schema(description = "项目负责人比例")
private BigDecimal engineeringLeaderRatio;
@Schema(description = "项目负责人金额")
private BigDecimal engineeringLeaderAmount;
@Schema(description = "专业所比例")
private BigDecimal officeRatio;
@Schema(description = "专业所金额")
private BigDecimal officeAmount;
@Schema(description = "建筑专业比例")
private BigDecimal archRatio;
@Schema(description = "建筑专业金额")
private BigDecimal archAmount;
@Schema(description = "装修专业比例")
private BigDecimal decorRatio;
@Schema(description = "装修专业金额")
private BigDecimal decorAmount;
@Schema(description = "结构专业比例")
private BigDecimal structRatio;
@Schema(description = "结构专业金额")
private BigDecimal structAmount;
@Schema(description = "水专业比例")
private BigDecimal waterRatio;
@Schema(description = "水专业金额")
private BigDecimal waterAmount;
@Schema(description = "电气专业比例")
private BigDecimal elecRatio;
@Schema(description = "电气专业金额")
private BigDecimal elecAmount;
@Schema(description = "暖通专业比例")
private BigDecimal hvacRatio;
@Schema(description = "暖通专业金额")
private BigDecimal hvacAmount;
@Schema(description = "数字化设计专业比例")
private BigDecimal digitalRatio;
@Schema(description = "数字化设计专业金额")
private BigDecimal digitalAmount;
}

View File

@@ -0,0 +1,57 @@
package cn.iocoder.lyzsys.module.tjt.controller.admin.outputsplit.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 页面4拆分比例新增/修改 Request VO")
@Data
public class ProjectOutputSplitSaveReqVO {
@Schema(description = "合同规划 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "合同规划 ID 不能为空")
private Long planningId;
@Schema(description = "项目经理比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.0200")
@NotNull(message = "项目经理比例不能为空")
private BigDecimal projectManagerRatio;
@Schema(description = "项目负责人比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.0300")
@NotNull(message = "项目负责人比例不能为空")
private BigDecimal engineeringLeaderRatio;
@Schema(description = "专业所比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.9500")
@NotNull(message = "专业所比例不能为空")
private BigDecimal officeRatio;
@Schema(description = "建筑专业比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.5000")
@NotNull(message = "建筑专业比例不能为空")
private BigDecimal archRatio;
@Schema(description = "装修专业比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.0000")
@NotNull(message = "装修专业比例不能为空")
private BigDecimal decorRatio;
@Schema(description = "结构专业比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.2000")
@NotNull(message = "结构专业比例不能为空")
private BigDecimal structRatio;
@Schema(description = "水专业比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.1000")
@NotNull(message = "水专业比例不能为空")
private BigDecimal waterRatio;
@Schema(description = "电气专业比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.1000")
@NotNull(message = "电气专业比例不能为空")
private BigDecimal elecRatio;
@Schema(description = "暖通专业比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.0500")
@NotNull(message = "暖通专业比例不能为空")
private BigDecimal hvacRatio;
@Schema(description = "数字化设计专业比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.0500")
@NotNull(message = "数字化设计专业比例不能为空")
private BigDecimal digitalRatio;
}

View File

@@ -0,0 +1,123 @@
package cn.iocoder.lyzsys.module.tjt.controller.admin.planning;
import cn.iocoder.lyzsys.framework.common.pojo.CommonResult;
import cn.iocoder.lyzsys.framework.common.pojo.PageResult;
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.planning.vo.ProjectPlanningPageReqVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.planning.vo.ProjectPlanningRespVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.planning.vo.ProjectPlanningSaveReqVO;
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planning.ProjectPlanningDO;
import cn.iocoder.lyzsys.module.tjt.service.planning.ProjectPlanningService;
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.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static cn.iocoder.lyzsys.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 特建投合约规划")
@RestController
@RequestMapping("/tjt/planning")
@Validated
public class ProjectPlanningController {
private static final int RATIO_SCALE = 4;
private static final BigDecimal ZERO_RATIO = BigDecimal.ZERO.setScale(RATIO_SCALE, RoundingMode.HALF_UP);
@Resource
private ProjectPlanningService projectPlanningService;
@PostMapping("/create")
@Operation(summary = "创建合约规划")
@PreAuthorize("@ss.hasPermission('tjt:planning:create')")
public CommonResult<Long> createProjectPlanning(@Valid @RequestBody ProjectPlanningSaveReqVO createReqVO) {
return success(projectPlanningService.createProjectPlanning(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "修改合约规划")
@PreAuthorize("@ss.hasPermission('tjt:planning:update')")
public CommonResult<Boolean> updateProjectPlanning(@Valid @RequestBody ProjectPlanningSaveReqVO updateReqVO) {
projectPlanningService.updateProjectPlanning(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除合约规划")
@Parameter(name = "id", description = "编号", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('tjt:planning:delete')")
public CommonResult<Boolean> deleteProjectPlanning(@RequestParam("id") Long id) {
projectPlanningService.deleteProjectPlanning(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Operation(summary = "批量删除合约规划")
@Parameter(name = "ids", description = "编号列表", required = true)
@PreAuthorize("@ss.hasPermission('tjt:planning:delete')")
public CommonResult<Boolean> deleteProjectPlanningList(@RequestParam("ids") List<Long> ids) {
projectPlanningService.deleteProjectPlanningList(ids);
return success(true);
}
@GetMapping("/page")
@Operation(summary = "获得合约规划分页")
@PreAuthorize("@ss.hasPermission('tjt:planning:query')")
public CommonResult<PageResult<ProjectPlanningRespVO>> getProjectPlanningPage(@Valid ProjectPlanningPageReqVO pageReqVO) {
PageResult<ProjectPlanningDO> pageResult = projectPlanningService.getProjectPlanningPage(pageReqVO);
Map<Long, BigDecimal> allocatedAmountMap = projectPlanningService.getAllocatedAmountMap(
CollectionUtils.convertSet(pageResult.getList(), ProjectPlanningDO::getId));
return success(BeanUtils.toBean(pageResult, ProjectPlanningRespVO.class,
respVO -> fillDistributionSummary(respVO, allocatedAmountMap)));
}
@GetMapping("/get")
@Operation(summary = "获得合约规划详情")
@Parameter(name = "id", description = "编号", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('tjt:planning:query')")
public CommonResult<ProjectPlanningRespVO> getProjectPlanning(@RequestParam("id") Long id) {
ProjectPlanningDO planning = projectPlanningService.getProjectPlanning(id);
if (planning == null) {
return success(null);
}
Map<Long, BigDecimal> allocatedAmountMap = projectPlanningService.getAllocatedAmountMap(
Collections.singleton(planning.getId()));
return success(BeanUtils.toBean(planning, ProjectPlanningRespVO.class,
respVO -> fillDistributionSummary(respVO, allocatedAmountMap)));
}
@GetMapping("/list-by-project")
@Operation(summary = "根据项目获得合约规划列表")
@Parameter(name = "projectId", description = "项目 ID", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('tjt:planning:query')")
public CommonResult<List<ProjectPlanningRespVO>> getProjectPlanningListByProjectId(@RequestParam("projectId") Long projectId) {
List<ProjectPlanningDO> list = projectPlanningService.getProjectPlanningListByProjectId(projectId);
Map<Long, BigDecimal> allocatedAmountMap = projectPlanningService.getAllocatedAmountMap(
CollectionUtils.convertSet(list, ProjectPlanningDO::getId));
return success(BeanUtils.toBean(list, ProjectPlanningRespVO.class,
respVO -> fillDistributionSummary(respVO, allocatedAmountMap)));
}
private void fillDistributionSummary(ProjectPlanningRespVO respVO, Map<Long, BigDecimal> allocatedAmountMap) {
BigDecimal allocatedRatio = allocatedAmountMap.getOrDefault(respVO.getId(), ZERO_RATIO);
BigDecimal totalDistributionAmount = respVO.getTotalDistributionAmount() == null
? ZERO_RATIO : respVO.getTotalDistributionAmount().setScale(RATIO_SCALE, RoundingMode.HALF_UP);
BigDecimal allocatedAmount = totalDistributionAmount.multiply(allocatedRatio)
.setScale(RATIO_SCALE, RoundingMode.HALF_UP);
respVO.setAllocatedAmount(allocatedAmount);
respVO.setPendingAmount(totalDistributionAmount.subtract(allocatedAmount)
.setScale(RATIO_SCALE, RoundingMode.HALF_UP));
}
}

View File

@@ -0,0 +1,37 @@
package cn.iocoder.lyzsys.module.tjt.controller.admin.planning.vo;
import cn.iocoder.lyzsys.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.lyzsys.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 合约规划分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class ProjectPlanningPageReqVO extends PageParam {
@Schema(description = "项目 ID", example = "1")
private Long projectId;
@Schema(description = "归属类型", example = "专业所")
private String ownershipType;
@Schema(description = "产值计算方式", example = "指导价法")
private String calculationMethod;
@Schema(description = "规划内容,模糊匹配", example = "建筑")
private String planningContent;
@Schema(description = "开始年度", example = "2026")
private Integer planningStartYear;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "创建时间")
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,127 @@
package cn.iocoder.lyzsys.module.tjt.controller.admin.planning.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 ProjectPlanningRespVO {
@Schema(description = "合约规划 ID", example = "1")
private Long id;
@Schema(description = "项目 ID", example = "1")
private Long projectId;
@Schema(description = "归属类型")
private String ownershipType;
@Schema(description = "产值计算方式")
private String calculationMethod;
@Schema(description = "规划内容")
private String planningContent;
@Schema(description = "规划金额")
private BigDecimal planningAmount;
@Schema(description = "管理费费率")
private BigDecimal managementFeeRate;
@Schema(description = "管理费")
private BigDecimal managementFee;
@Schema(description = "实施团队")
private String implementationTeam;
@Schema(description = "开始年度")
private Integer planningStartYear;
@Schema(description = "面积")
private BigDecimal planningArea;
@Schema(description = "设计阶段")
private String designStage;
@Schema(description = "本次设计阶段比例")
private BigDecimal currentDesignStageRatio;
@Schema(description = "审核审定是否外包")
private Boolean reviewOutsourceFlag;
@Schema(description = "审核审定占比")
private BigDecimal reviewOutsourceRatio;
@Schema(description = "总分配比例")
private BigDecimal totalDistributionAmount;
@Schema(description = "已分配比例")
private BigDecimal allocatedAmount;
@Schema(description = "待分配比例")
private BigDecimal pendingAmount;
@Schema(description = "Planning progress remark")
private String progressRemark;
@Schema(description = "楼栋数或户型数")
private Integer buildingOrUnitCount;
@Schema(description = "套图系数,保留两位小数")
private BigDecimal drawingSetFactor;
@Schema(description = "规模系数,保留两位小数")
private BigDecimal scaleFactor;
@Schema(description = "修改系数,保留两位小数")
private BigDecimal modificationFactor;
@Schema(description = "复杂系数/复杂等级按比例值返回100%=1.0000")
private BigDecimal complexityFactor;
@Schema(description = "内部指导单价(元/㎡)")
private BigDecimal internalGuidanceUnitPrice;
@Schema(description = "虚拟产值计算方式")
private String virtualCalculationMethod;
@Schema(description = "工日")
private BigDecimal workingDayCount;
@Schema(description = "工日单价")
private BigDecimal workingDayUnitPrice;
@Schema(description = "指导单价")
private BigDecimal guidanceUnitPrice;
@Schema(description = "指导总价")
private BigDecimal guidanceTotalPrice;
@Schema(description = "虚拟总价")
private BigDecimal virtualTotalPrice;
@Schema(description = "产值计算比例")
private BigDecimal calculationRatio;
@Schema(description = "合同单价(元/㎡)")
private BigDecimal contractUnitPrice;
@Schema(description = "合计调整系数")
private BigDecimal totalAdjustmentFactor;
@Schema(description = "考核面积")
private BigDecimal assessmentArea;
@Schema(description = "虚拟产值")
private BigDecimal virtualOutputValue;
@Schema(description = "考核产值")
private BigDecimal assessmentOutputValue;
@Schema(description = "创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,115 @@
package cn.iocoder.lyzsys.module.tjt.controller.admin.planning.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
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 ProjectPlanningSaveReqVO {
@Schema(description = "合约规划 ID", example = "1")
private Long id;
@Schema(description = "项目 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "项目 ID 不能为空")
private Long projectId;
@Schema(description = "归属类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "专业所")
@NotBlank(message = "归属类型不能为空")
@Size(max = 20, message = "归属类型长度不能超过 20 个字符")
private String ownershipType;
@Schema(description = "产值计算方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "指导价法")
@NotBlank(message = "产值计算方式不能为空")
@Size(max = 30, message = "产值计算方式长度不能超过 30 个字符")
private String calculationMethod;
@Schema(description = "规划内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "建筑设计")
@NotBlank(message = "规划内容不能为空")
@Size(max = 255, message = "规划内容长度不能超过 255 个字符")
private String planningContent;
@Schema(description = "规划金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "500000")
@NotNull(message = "规划金额不能为空")
private BigDecimal planningAmount;
@Schema(description = "管理费费率", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.0500")
@NotNull(message = "管理费费率不能为空")
private BigDecimal managementFeeRate;
@Schema(description = "实施团队", example = "建筑一所")
@Size(max = 100, message = "实施团队长度不能超过 100 个字符")
private String implementationTeam;
@Schema(description = "开始年度", example = "2026")
private Integer planningStartYear;
@Schema(description = "面积", example = "30000")
private BigDecimal planningArea;
@Schema(description = "设计阶段", example = "方案设计")
@Size(max = 50, message = "设计阶段长度不能超过 50 个字符")
private String designStage;
@Schema(description = "本次设计阶段比例", example = "0.3000")
private BigDecimal currentDesignStageRatio;
@Schema(description = "审核审定是否外包", example = "false")
private Boolean reviewOutsourceFlag;
@Schema(description = "审核审定占比", example = "0.0600")
private BigDecimal reviewOutsourceRatio;
@Schema(description = "总分配比例", example = "1.0000")
private BigDecimal totalDistributionAmount;
@Schema(description = "Planning progress remark", example = "Q1 extraction arrangement")
@Size(max = 500, message = "Progress remark length must be within 500 characters")
private String progressRemark;
@Schema(description = "楼栋数或户型数", example = "10")
private Integer buildingOrUnitCount;
@Schema(description = "套图系数,保留两位小数", example = "1.00")
private BigDecimal drawingSetFactor;
@Schema(description = "规模系数,保留两位小数", example = "1.00")
private BigDecimal scaleFactor;
@Schema(description = "修改系数,保留两位小数", example = "1.00")
private BigDecimal modificationFactor;
@Schema(description = "复杂系数/复杂等级按比例值存储100%=1.0000", example = "1.0000")
private BigDecimal complexityFactor;
@Schema(description = "内部指导单价(元/㎡)", example = "80.00")
private BigDecimal internalGuidanceUnitPrice;
@Schema(description = "虚拟产值计算方式", example = "工日法")
@Size(max = 30, message = "虚拟产值计算方式长度不能超过 30 个字符")
private String virtualCalculationMethod;
@Schema(description = "工日", example = "100")
private BigDecimal workingDayCount;
@Schema(description = "工日单价", example = "1000.00")
private BigDecimal workingDayUnitPrice;
@Schema(description = "指导单价", example = "88.00")
private BigDecimal guidanceUnitPrice;
@Schema(description = "指导总价", example = "880000.00")
private BigDecimal guidanceTotalPrice;
@Schema(description = "虚拟总价", example = "600000.00")
private BigDecimal virtualTotalPrice;
@Schema(description = "产值计算比例", example = "0.0800")
private BigDecimal calculationRatio;
}

View File

@@ -0,0 +1,83 @@
package cn.iocoder.lyzsys.module.tjt.controller.admin.planningquarter;
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.planningquarter.vo.ProjectPlanningQuarterRespVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.planningquarter.vo.ProjectPlanningQuarterSaveReqVO;
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planningquarter.ProjectPlanningQuarterDO;
import cn.iocoder.lyzsys.module.tjt.service.planningquarter.ProjectPlanningQuarterService;
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.*;
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-quarter")
@Validated
public class ProjectPlanningQuarterController {
@Resource
private ProjectPlanningQuarterService projectPlanningQuarterService;
@PostMapping("/create")
@Operation(summary = "创建季度分配")
@PreAuthorize("@ss.hasPermission('tjt:planning-quarter:create')")
public CommonResult<Long> createProjectPlanningQuarter(@Valid @RequestBody ProjectPlanningQuarterSaveReqVO createReqVO) {
return success(projectPlanningQuarterService.createProjectPlanningQuarter(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "修改季度分配")
@PreAuthorize("@ss.hasPermission('tjt:planning-quarter:update')")
public CommonResult<Boolean> updateProjectPlanningQuarter(@Valid @RequestBody ProjectPlanningQuarterSaveReqVO updateReqVO) {
projectPlanningQuarterService.updateProjectPlanningQuarter(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除季度分配")
@Parameter(name = "id", description = "编号", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('tjt:planning-quarter:delete')")
public CommonResult<Boolean> deleteProjectPlanningQuarter(@RequestParam("id") Long id) {
projectPlanningQuarterService.deleteProjectPlanningQuarter(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Operation(summary = "批量删除季度分配")
@Parameter(name = "ids", description = "编号列表", required = true)
@PreAuthorize("@ss.hasPermission('tjt:planning-quarter:delete')")
public CommonResult<Boolean> deleteProjectPlanningQuarterList(@RequestParam("ids") List<Long> ids) {
projectPlanningQuarterService.deleteProjectPlanningQuarterList(ids);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得季度分配详情")
@Parameter(name = "id", description = "编号", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('tjt:planning-quarter:query')")
public CommonResult<ProjectPlanningQuarterRespVO> getProjectPlanningQuarter(@RequestParam("id") Long id) {
ProjectPlanningQuarterDO quarter = projectPlanningQuarterService.getProjectPlanningQuarter(id);
return success(BeanUtils.toBean(quarter, ProjectPlanningQuarterRespVO.class));
}
@GetMapping("/list-by-planning")
@Operation(summary = "根据合约规划获得季度分配列表")
@Parameter(name = "planningId", description = "合约规划 ID", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('tjt:planning-quarter:query')")
public CommonResult<List<ProjectPlanningQuarterRespVO>> getProjectPlanningQuarterListByPlanningId(
@RequestParam("planningId") Long planningId) {
List<ProjectPlanningQuarterDO> list = projectPlanningQuarterService.getProjectPlanningQuarterListByPlanningId(planningId);
return success(BeanUtils.toBean(list, ProjectPlanningQuarterRespVO.class));
}
}

View File

@@ -0,0 +1,37 @@
package cn.iocoder.lyzsys.module.tjt.controller.admin.planningquarter.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 ProjectPlanningQuarterRespVO {
@Schema(description = "季度分配 ID", example = "1")
private Long id;
@Schema(description = "合约规划 ID", example = "1")
private Long planningId;
@Schema(description = "分配年度")
private Integer distributionYear;
@Schema(description = "季度")
private Integer quarterNo;
@Schema(description = "分配比例")
private BigDecimal distributionRatio;
@Schema(description = "分配金额")
private BigDecimal distributionAmount;
@Schema(description = "提取进度备注")
private String progressRemark;
@Schema(description = "创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,40 @@
package cn.iocoder.lyzsys.module.tjt.controller.admin.planningquarter.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 季度分配新增/修改 Request VO")
@Data
public class ProjectPlanningQuarterSaveReqVO {
@Schema(description = "季度分配 ID", example = "1")
private Long id;
@Schema(description = "合约规划 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "合约规划 ID 不能为空")
private Long planningId;
@Schema(description = "分配年度", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026")
@NotNull(message = "分配年度不能为空")
private Integer distributionYear;
@Schema(description = "季度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "季度不能为空")
@Min(value = 1, message = "季度必须在 1 到 4 之间")
@Max(value = 4, message = "季度必须在 1 到 4 之间")
private Integer quarterNo;
@Schema(description = "分配比例", example = "0.2500")
private BigDecimal distributionRatio;
@Schema(description = "提取进度备注", example = "Q1 提取")
@Size(max = 500, message = "提取进度备注长度不能超过 500 个字符")
private String progressRemark;
}

View File

@@ -0,0 +1,47 @@
package cn.iocoder.lyzsys.module.tjt.controller.admin.profit;
import cn.iocoder.lyzsys.framework.common.pojo.CommonResult;
import cn.iocoder.lyzsys.framework.common.pojo.PageResult;
import cn.iocoder.lyzsys.module.tjt.controller.admin.profit.vo.ProjectProfitPageReqVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.profit.vo.ProjectProfitRespVO;
import cn.iocoder.lyzsys.module.tjt.service.profit.ProjectProfitService;
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.GetMapping;
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 static cn.iocoder.lyzsys.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 特建投项目盈亏")
@RestController
@RequestMapping("/tjt/profit")
@Validated
public class ProjectProfitController {
@Resource
private ProjectProfitService projectProfitService;
@GetMapping("/get")
@Operation(summary = "获得项目盈亏详情")
@Parameter(name = "projectId", description = "项目 ID", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('tjt:profit:query')")
public CommonResult<ProjectProfitRespVO> getProjectProfit(@RequestParam("projectId") Long projectId) {
return success(projectProfitService.getProjectProfit(projectId));
}
@GetMapping("/page")
@Operation(summary = "获得项目盈亏分页")
@PreAuthorize("@ss.hasPermission('tjt:profit:query')")
public CommonResult<PageResult<ProjectProfitRespVO>> getProjectProfitPage(@Valid ProjectProfitPageReqVO pageReqVO) {
return success(projectProfitService.getProjectProfitPage(pageReqVO));
}
}

View File

@@ -0,0 +1,31 @@
package cn.iocoder.lyzsys.module.tjt.controller.admin.profit.vo;
import cn.iocoder.lyzsys.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.lyzsys.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 项目盈亏分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class ProjectProfitPageReqVO extends PageParam {
@Schema(description = "工程名称,模糊匹配", example = "设计")
private String projectName;
@Schema(description = "是否签订合同", example = "true")
private Boolean contractSignedFlag;
@Schema(description = "项目开始年度", example = "2026")
private Integer projectStartYear;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "创建时间")
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,55 @@
package cn.iocoder.lyzsys.module.tjt.controller.admin.profit.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 ProjectProfitRespVO {
@Schema(description = "项目 ID", example = "1")
private Long projectId;
@Schema(description = "工程名称")
private String projectName;
@Schema(description = "是否签订合同")
private Boolean contractSignedFlag;
@Schema(description = "合同金额")
private BigDecimal contractAmount;
@Schema(description = "最终结算金额")
private BigDecimal finalSettlementAmount;
@Schema(description = "综合所协作金额")
private BigDecimal comprehensivePlanningAmount;
@Schema(description = "专业分包金额")
private BigDecimal subcontractPlanningAmount;
@Schema(description = "专业所产值")
private BigDecimal majorOutputValue;
@Schema(description = "预计 K 值")
private BigDecimal expectedKValue;
@Schema(description = "专业所预计绩效")
private BigDecimal majorExpectedPerformance;
@Schema(description = "盈亏值")
private BigDecimal profitLossValue;
@Schema(description = "盈亏百分比")
private BigDecimal profitLossRate;
@Schema(description = "项目开始年度")
private Integer projectStartYear;
@Schema(description = "创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,82 @@
package cn.iocoder.lyzsys.module.tjt.controller.admin.project;
import cn.iocoder.lyzsys.framework.common.pojo.CommonResult;
import cn.iocoder.lyzsys.framework.common.pojo.PageResult;
import cn.iocoder.lyzsys.framework.common.util.object.BeanUtils;
import cn.iocoder.lyzsys.module.tjt.controller.admin.project.vo.ProjectPageReqVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.project.vo.ProjectRespVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.project.vo.ProjectSaveReqVO;
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.project.ProjectDO;
import cn.iocoder.lyzsys.module.tjt.service.project.ProjectService;
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.*;
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/project")
@Validated
public class ProjectController {
@Resource
private ProjectService projectService;
@PostMapping("/create")
@Operation(summary = "创建项目")
@PreAuthorize("@ss.hasPermission('tjt:project:create')")
public CommonResult<Long> createProject(@Valid @RequestBody ProjectSaveReqVO createReqVO) {
return success(projectService.createProject(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "修改项目")
@PreAuthorize("@ss.hasPermission('tjt:project:update')")
public CommonResult<Boolean> updateProject(@Valid @RequestBody ProjectSaveReqVO updateReqVO) {
projectService.updateProject(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除项目")
@Parameter(name = "id", description = "编号", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('tjt:project:delete')")
public CommonResult<Boolean> deleteProject(@RequestParam("id") Long id) {
projectService.deleteProject(id);
return success(true);
}
@DeleteMapping("/delete-list")
@Operation(summary = "批量删除项目")
@Parameter(name = "ids", description = "编号列表", required = true)
@PreAuthorize("@ss.hasPermission('tjt:project:delete')")
public CommonResult<Boolean> deleteProjectList(@RequestParam("ids") List<Long> ids) {
projectService.deleteProjectList(ids);
return success(true);
}
@GetMapping("/page")
@Operation(summary = "获得项目分页")
@PreAuthorize("@ss.hasPermission('tjt:project:query')")
public CommonResult<PageResult<ProjectRespVO>> getProjectPage(@Valid ProjectPageReqVO pageReqVO) {
PageResult<ProjectDO> pageResult = projectService.getProjectPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, ProjectRespVO.class));
}
@GetMapping("/get")
@Operation(summary = "获得项目详情")
@Parameter(name = "id", description = "编号", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('tjt:project:query')")
public CommonResult<ProjectRespVO> getProject(@RequestParam("id") Long id) {
return success(BeanUtils.toBean(projectService.getProject(id), ProjectRespVO.class));
}
}

View File

@@ -0,0 +1,31 @@
package cn.iocoder.lyzsys.module.tjt.controller.admin.project.vo;
import cn.iocoder.lyzsys.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.lyzsys.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 项目分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class ProjectPageReqVO extends PageParam {
@Schema(description = "工程名称,模糊匹配", example = "设计")
private String projectName;
@Schema(description = "是否签订合同", example = "true")
private Boolean contractSignedFlag;
@Schema(description = "项目开始年度", example = "2026")
private Integer projectStartYear;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "创建时间")
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,85 @@
package cn.iocoder.lyzsys.module.tjt.controller.admin.project.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 项目 Response VO")
@Data
@ExcelIgnoreUnannotated
public class ProjectRespVO {
private static final String FORMAT_YEAR_MONTH_DAY = "yyyy-MM-dd";
@Schema(description = "项目 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@ExcelProperty("项目ID")
private Long id;
@Schema(description = "工程名称", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("工程名称")
private String projectName;
@Schema(description = "是否签订合同", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("是否签订合同")
private Boolean contractSignedFlag;
@Schema(description = "合同金额", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("合同金额")
private BigDecimal contractAmount;
@Schema(description = "工程总面积", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("工程总面积")
private BigDecimal totalConstructionArea;
@Schema(description = "建设单位")
@ExcelProperty("建设单位")
private String constructionUnitName;
@Schema(description = "联系人")
@ExcelProperty("联系人")
private String contactName;
@Schema(description = "联系方式")
@ExcelProperty("联系方式")
private String contactPhone;
@Schema(description = "合同签订日期")
@ExcelProperty("合同签订日期")
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY)
private LocalDate contractSigningDate;
@Schema(description = "项目经理")
@ExcelProperty("项目经理")
private String projectManagerName;
@Schema(description = "工程负责人")
@ExcelProperty("工程负责人")
private String engineeringPrincipalName;
@Schema(description = "工程类型")
@ExcelProperty("工程类型")
private String projectType;
@Schema(description = "项目开始年度")
@ExcelProperty("项目开始年度")
private Integer projectStartYear;
@Schema(description = "最终结算金额")
@ExcelProperty("最终结算金额")
private BigDecimal finalSettlementAmount;
@Schema(description = "预计 K 值")
@ExcelProperty("预计 K 值")
private BigDecimal expectedKValue;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,79 @@
package cn.iocoder.lyzsys.module.tjt.controller.admin.project.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import static cn.iocoder.lyzsys.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
@Schema(description = "管理后台 - 项目新增/修改 Request VO")
@Data
public class ProjectSaveReqVO {
@Schema(description = "项目 ID", example = "1")
private Long id;
@Schema(description = "工程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "XX 设计项目")
@NotBlank(message = "工程名称不能为空")
@Size(max = 200, message = "工程名称长度不能超过 200 个字符")
private String projectName;
@Schema(description = "是否签订合同", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否签订合同不能为空")
private Boolean contractSignedFlag;
@Schema(description = "合同金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000000")
@NotNull(message = "合同金额不能为空")
private BigDecimal contractAmount;
@Schema(description = "工程总面积", requiredMode = Schema.RequiredMode.REQUIRED, example = "30000")
@NotNull(message = "工程总面积不能为空")
private BigDecimal totalConstructionArea;
@Schema(description = "建设单位", example = "XX 建设单位")
@Size(max = 200, message = "建设单位长度不能超过 200 个字符")
private String constructionUnitName;
@Schema(description = "联系人", example = "张三")
@Size(max = 64, message = "联系人长度不能超过 64 个字符")
private String contactName;
@Schema(description = "联系方式", example = "13800000000")
@Size(max = 32, message = "联系方式长度不能超过 32 个字符")
private String contactPhone;
@Schema(description = "合同签订日期", example = "2026-04-14")
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY)
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
private LocalDate contractSigningDate;
@Schema(description = "项目经理", example = "李四")
@Size(max = 64, message = "项目经理长度不能超过 64 个字符")
private String projectManagerName;
@Schema(description = "工程负责人", example = "王五")
@Size(max = 64, message = "工程负责人长度不能超过 64 个字符")
private String engineeringPrincipalName;
@Schema(description = "工程类型", example = "住宅")
@Size(max = 50, message = "工程类型长度不能超过 50 个字符")
private String projectType;
@Schema(description = "项目开始年度", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026")
@NotNull(message = "项目开始年度不能为空")
private Integer projectStartYear;
@Schema(description = "最终结算金额", example = "1200000")
private BigDecimal finalSettlementAmount;
@Schema(description = "预计 K 值", example = "0.8500")
private BigDecimal expectedKValue;
}

View File

@@ -0,0 +1,45 @@
package cn.iocoder.lyzsys.module.tjt.controller.admin.specialtyrolesplit;
import cn.iocoder.lyzsys.framework.common.pojo.CommonResult;
import cn.iocoder.lyzsys.module.tjt.controller.admin.specialtyrolesplit.vo.SpecialtyRoleSplitBatchSaveReqVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.specialtyrolesplit.vo.SpecialtyRoleSplitRespVO;
import cn.iocoder.lyzsys.module.tjt.service.specialtyrolesplit.SpecialtyRoleSplitService;
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.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import static cn.iocoder.lyzsys.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 特建投页面5角色比例")
@RestController
@RequestMapping("/tjt/specialty-role-split")
@Validated
public class SpecialtyRoleSplitController {
@Resource
private SpecialtyRoleSplitService specialtyRoleSplitService;
@GetMapping("/list-by-planning")
@Operation(summary = "根据合约规划获得页面5角色比例列表")
@Parameter(name = "planningId", description = "合约规划 ID", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('tjt:specialty-role-split:query')")
public CommonResult<List<SpecialtyRoleSplitRespVO>> getSpecialtyRoleSplitListByPlanningId(@RequestParam("planningId") Long planningId) {
return success(specialtyRoleSplitService.getSpecialtyRoleSplitListByPlanningId(planningId));
}
@PutMapping("/save-batch")
@Operation(summary = "批量保存页面5角色比例")
@PreAuthorize("@ss.hasPermission('tjt:specialty-role-split:update')")
public CommonResult<Boolean> saveSpecialtyRoleSplitBatch(@Valid @RequestBody SpecialtyRoleSplitBatchSaveReqVO reqVO) {
specialtyRoleSplitService.saveSpecialtyRoleSplitBatch(reqVO);
return success(true);
}
}

View File

@@ -0,0 +1,21 @@
package cn.iocoder.lyzsys.module.tjt.controller.admin.specialtyrolesplit.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 页面5角色人员 Response VO")
@Data
public class SpecialtyRolePersonRespVO {
@Schema(description = "人员名称")
private String personName;
@Schema(description = "人员比例")
private BigDecimal personRatio;
@Schema(description = "人员金额")
private BigDecimal personAmount;
}

View File

@@ -0,0 +1,18 @@
package cn.iocoder.lyzsys.module.tjt.controller.admin.specialtyrolesplit.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 页面5角色人员保存 Item Request VO")
@Data
public class SpecialtyRolePersonSaveReqVO {
@Schema(description = "人员名称", example = "张三")
private String personName;
@Schema(description = "人员比例", example = "0.1000")
private BigDecimal personRatio;
}

View File

@@ -0,0 +1,24 @@
package cn.iocoder.lyzsys.module.tjt.controller.admin.specialtyrolesplit.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
@Schema(description = "管理后台 - 页面5角色比例批量保存 Request VO")
@Data
public class SpecialtyRoleSplitBatchSaveReqVO {
@Schema(description = "合约规划 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "合约规划 ID 不能为空")
private Long planningId;
@Schema(description = "角色配置列表", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "角色配置列表不能为空")
@Valid
private List<SpecialtyRoleSplitSaveItemReqVO> items;
}

View File

@@ -0,0 +1,55 @@
package cn.iocoder.lyzsys.module.tjt.controller.admin.specialtyrolesplit.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
@Schema(description = "管理后台 - 页面5角色比例 Response VO")
@Data
public class SpecialtyRoleSplitRespVO {
@Schema(description = "角色比例 ID", example = "1")
private Long id;
@Schema(description = "页面4拆分 ID", example = "1")
private Long outputSplitId;
@Schema(description = "合同规划 ID", example = "1")
private Long planningId;
@Schema(description = "项目名称")
private String projectName;
@Schema(description = "规划内容")
private String planningContent;
@Schema(description = "专业编码")
private String specialtyCode;
@Schema(description = "专业名称")
private String specialtyName;
@Schema(description = "专业金额")
private BigDecimal specialtyAmount;
@Schema(description = "角色编码")
private String roleCode;
@Schema(description = "角色名称")
private String roleName;
@Schema(description = "角色比例")
private BigDecimal roleRatio;
@Schema(description = "角色金额")
private BigDecimal roleAmount;
@Schema(description = "人员配置列表")
private List<SpecialtyRolePersonRespVO> persons;
@Schema(description = "排序号")
private Integer sortNo;
}

View File

@@ -0,0 +1,32 @@
package cn.iocoder.lyzsys.module.tjt.controller.admin.specialtyrolesplit.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.List;
@Schema(description = "管理后台 - 页面5角色比例保存 Item Request VO")
@Data
public class SpecialtyRoleSplitSaveItemReqVO {
@Schema(description = "专业编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "arch")
@NotBlank(message = "专业编码不能为空")
private String specialtyCode;
@Schema(description = "角色编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "director")
@NotBlank(message = "角色编码不能为空")
private String roleCode;
@Schema(description = "角色比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.1000")
@NotNull(message = "角色比例不能为空")
private BigDecimal roleRatio;
@Schema(description = "人员配置列表")
@Valid
private List<SpecialtyRolePersonSaveReqVO> persons;
}

View File

@@ -0,0 +1,52 @@
package cn.iocoder.lyzsys.module.tjt.dal.dataobject.outputsplit;
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;
/**
* 页面 4 拆分比例 DO
*
* @author Codex
*/
@TableName("tjt_project_output_split")
@KeySequence("tjt_project_output_split_seq")
@Data
@EqualsAndHashCode(callSuper = true)
public class ProjectOutputSplitDO extends TenantBaseDO {
@TableId
private Long id;
private Long projectId;
private Long planningId;
private Integer year;
private BigDecimal projectManagerRatio;
private BigDecimal engineeringLeaderRatio;
private BigDecimal officeRatio;
private BigDecimal archRatio;
private BigDecimal decorRatio;
private BigDecimal structRatio;
private BigDecimal waterRatio;
private BigDecimal elecRatio;
private BigDecimal hvacRatio;
private BigDecimal digitalRatio;
}

View File

@@ -0,0 +1,94 @@
package cn.iocoder.lyzsys.module.tjt.dal.dataobject.planning;
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;
/**
* 合约规划 DO
*
* @author Codex
*/
@TableName("tjt_project_planning")
@KeySequence("tjt_project_planning_seq")
@Data
@EqualsAndHashCode(callSuper = true)
public class ProjectPlanningDO extends TenantBaseDO {
@TableId
private Long id;
private Long projectId;
private String ownershipType;
private String calculationMethod;
private String planningContent;
private BigDecimal planningAmount;
private BigDecimal managementFeeRate;
private BigDecimal managementFee;
private String implementationTeam;
private Integer planningStartYear;
private BigDecimal planningArea;
private String designStage;
private BigDecimal currentDesignStageRatio;
private Boolean reviewOutsourceFlag;
private BigDecimal reviewOutsourceRatio;
private BigDecimal totalDistributionAmount;
private String progressRemark;
private Integer buildingOrUnitCount;
private BigDecimal drawingSetFactor;
private BigDecimal scaleFactor;
private BigDecimal modificationFactor;
private BigDecimal complexityFactor;
private BigDecimal internalGuidanceUnitPrice;
private String virtualCalculationMethod;
private BigDecimal workingDayCount;
private BigDecimal workingDayUnitPrice;
private BigDecimal guidanceUnitPrice;
private BigDecimal guidanceTotalPrice;
private BigDecimal virtualTotalPrice;
private BigDecimal calculationRatio;
private BigDecimal contractUnitPrice;
private BigDecimal totalAdjustmentFactor;
private BigDecimal assessmentArea;
private BigDecimal virtualOutputValue;
private BigDecimal assessmentOutputValue;
}

View File

@@ -0,0 +1,36 @@
package cn.iocoder.lyzsys.module.tjt.dal.dataobject.planningquarter;
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;
/**
* 合约规划季度分配 DO
*
* @author Codex
*/
@TableName("tjt_project_planning_quarter")
@KeySequence("tjt_project_planning_quarter_seq")
@Data
@EqualsAndHashCode(callSuper = true)
public class ProjectPlanningQuarterDO extends TenantBaseDO {
@TableId
private Long id;
private Long planningId;
private Integer distributionYear;
private Integer quarterNo;
private BigDecimal distributionRatio;
private BigDecimal distributionAmount;
}

View File

@@ -0,0 +1,86 @@
package cn.iocoder.lyzsys.module.tjt.dal.dataobject.project;
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;
import java.time.LocalDate;
/**
* 项目 DO
*
* @author Codex
*/
@TableName("tjt_project")
@KeySequence("tjt_project_seq")
@Data
@EqualsAndHashCode(callSuper = true)
public class ProjectDO extends TenantBaseDO {
/**
* 主键 ID
*/
@TableId
private Long id;
/**
* 工程名称
*/
private String projectName;
/**
* 是否签订合同
*/
private Boolean contractSignedFlag;
/**
* 合同金额
*/
private BigDecimal contractAmount;
/**
* 工程总面积
*/
private BigDecimal totalConstructionArea;
/**
* 建设单位
*/
private String constructionUnitName;
/**
* 联系人
*/
private String contactName;
/**
* 联系方式
*/
private String contactPhone;
/**
* 合同签订日期
*/
private LocalDate contractSigningDate;
/**
* 项目经理
*/
private String projectManagerName;
/**
* 工程负责人
*/
private String engineeringPrincipalName;
/**
* 工程类型
*/
private String projectType;
/**
* 项目开始年度
*/
private Integer projectStartYear;
/**
* 最终结算金额
*/
private BigDecimal finalSettlementAmount;
/**
* 预计 K 值
*/
private BigDecimal expectedKValue;
}

View File

@@ -0,0 +1,42 @@
package cn.iocoder.lyzsys.module.tjt.dal.dataobject.specialtyrolesplit;
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;
/**
* 页面 5 角色比例 DO
*
* @author Codex
*/
@TableName("tjt_specialty_role_split")
@KeySequence("tjt_specialty_role_split_seq")
@Data
@EqualsAndHashCode(callSuper = true)
public class SpecialtyRoleSplitDO extends TenantBaseDO {
@TableId
private Long id;
private Long outputSplitId;
private String specialtyCode;
private String specialtyName;
private String roleCode;
private String roleName;
private BigDecimal roleRatio;
private String personNames;
private Integer sortNo;
}

View File

@@ -0,0 +1,29 @@
package cn.iocoder.lyzsys.module.tjt.dal.mysql.outputsplit;
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.outputsplit.ProjectOutputSplitDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
/**
* 页面 4 拆分比例 Mapper
*
* @author Codex
*/
@Mapper
public interface ProjectOutputSplitMapper extends BaseMapperX<ProjectOutputSplitDO> {
default ProjectOutputSplitDO selectByPlanningId(Long planningId) {
return selectOne(new LambdaQueryWrapperX<ProjectOutputSplitDO>()
.eq(ProjectOutputSplitDO::getPlanningId, planningId));
}
default List<ProjectOutputSplitDO> selectListByPlanningIds(Collection<Long> planningIds) {
return selectList(new LambdaQueryWrapperX<ProjectOutputSplitDO>()
.inIfPresent(ProjectOutputSplitDO::getPlanningId, planningIds));
}
}

View File

@@ -0,0 +1,37 @@
package cn.iocoder.lyzsys.module.tjt.dal.mysql.planning;
import cn.iocoder.lyzsys.framework.common.pojo.PageResult;
import cn.iocoder.lyzsys.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.lyzsys.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.lyzsys.module.tjt.controller.admin.planning.vo.ProjectPlanningPageReqVO;
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planning.ProjectPlanningDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 合约规划 Mapper
*
* @author Codex
*/
@Mapper
public interface ProjectPlanningMapper extends BaseMapperX<ProjectPlanningDO> {
default PageResult<ProjectPlanningDO> selectPage(ProjectPlanningPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<ProjectPlanningDO>()
.eqIfPresent(ProjectPlanningDO::getProjectId, reqVO.getProjectId())
.eqIfPresent(ProjectPlanningDO::getOwnershipType, reqVO.getOwnershipType())
.eqIfPresent(ProjectPlanningDO::getCalculationMethod, reqVO.getCalculationMethod())
.likeIfPresent(ProjectPlanningDO::getPlanningContent, reqVO.getPlanningContent())
.eqIfPresent(ProjectPlanningDO::getPlanningStartYear, reqVO.getPlanningStartYear())
.betweenIfPresent(ProjectPlanningDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(ProjectPlanningDO::getId));
}
default List<ProjectPlanningDO> selectListByProjectId(Long projectId) {
return selectList(new LambdaQueryWrapperX<ProjectPlanningDO>()
.eq(ProjectPlanningDO::getProjectId, projectId)
.orderByDesc(ProjectPlanningDO::getId));
}
}

View File

@@ -0,0 +1,47 @@
package cn.iocoder.lyzsys.module.tjt.dal.mysql.planningquarter;
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.planningquarter.ProjectPlanningQuarterDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* 合约规划季度分配 Mapper
*
* @author Codex
*/
@Mapper
public interface ProjectPlanningQuarterMapper extends BaseMapperX<ProjectPlanningQuarterDO> {
default ProjectPlanningQuarterDO selectByPlanningIdAndDistributionYearAndQuarter(Long planningId,
Integer distributionYear,
Integer quarterNo) {
return selectOne(ProjectPlanningQuarterDO::getPlanningId, planningId,
ProjectPlanningQuarterDO::getDistributionYear, distributionYear,
ProjectPlanningQuarterDO::getQuarterNo, quarterNo);
}
default List<ProjectPlanningQuarterDO> selectListByPlanningId(Long planningId) {
return selectList(new LambdaQueryWrapperX<ProjectPlanningQuarterDO>()
.eq(ProjectPlanningQuarterDO::getPlanningId, planningId)
.orderByAsc(ProjectPlanningQuarterDO::getDistributionYear)
.orderByAsc(ProjectPlanningQuarterDO::getQuarterNo)
.orderByAsc(ProjectPlanningQuarterDO::getId));
}
default List<ProjectPlanningQuarterDO> selectListByPlanningIds(Collection<Long> planningIds) {
if (planningIds == null || planningIds.isEmpty()) {
return Collections.emptyList();
}
return selectList(new LambdaQueryWrapperX<ProjectPlanningQuarterDO>()
.in(ProjectPlanningQuarterDO::getPlanningId, planningIds)
.orderByAsc(ProjectPlanningQuarterDO::getDistributionYear)
.orderByAsc(ProjectPlanningQuarterDO::getQuarterNo)
.orderByAsc(ProjectPlanningQuarterDO::getId));
}
}

View File

@@ -0,0 +1,27 @@
package cn.iocoder.lyzsys.module.tjt.dal.mysql.project;
import cn.iocoder.lyzsys.framework.common.pojo.PageResult;
import cn.iocoder.lyzsys.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.lyzsys.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.lyzsys.module.tjt.controller.admin.project.vo.ProjectPageReqVO;
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.project.ProjectDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 项目 Mapper
*
* @author Codex
*/
@Mapper
public interface ProjectMapper extends BaseMapperX<ProjectDO> {
default PageResult<ProjectDO> selectPage(ProjectPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<ProjectDO>()
.likeIfPresent(ProjectDO::getProjectName, reqVO.getProjectName())
.eqIfPresent(ProjectDO::getContractSignedFlag, reqVO.getContractSignedFlag())
.eqIfPresent(ProjectDO::getProjectStartYear, reqVO.getProjectStartYear())
.betweenIfPresent(ProjectDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(ProjectDO::getId));
}
}

View File

@@ -0,0 +1,25 @@
package cn.iocoder.lyzsys.module.tjt.dal.mysql.specialtyrolesplit;
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.specialtyrolesplit.SpecialtyRoleSplitDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 页面 5 角色比例 Mapper
*
* @author Codex
*/
@Mapper
public interface SpecialtyRoleSplitMapper extends BaseMapperX<SpecialtyRoleSplitDO> {
default List<SpecialtyRoleSplitDO> selectListByOutputSplitId(Long outputSplitId) {
return selectList(new LambdaQueryWrapperX<SpecialtyRoleSplitDO>()
.eq(SpecialtyRoleSplitDO::getOutputSplitId, outputSplitId)
.orderByAsc(SpecialtyRoleSplitDO::getSortNo)
.orderByAsc(SpecialtyRoleSplitDO::getId));
}
}

View File

@@ -0,0 +1,43 @@
package cn.iocoder.lyzsys.module.tjt.enums;
import cn.iocoder.lyzsys.framework.common.exception.ErrorCode;
/**
* 特建投模块错误码
*
* @author Codex
*/
public interface ErrorCodeConstants {
// ========== 项目管理 1-020-001-000 ==========
ErrorCode PROJECT_NOT_EXISTS = new ErrorCode(1_020_001_000, "项目不存在");
// ========== 合约规划管理 1-020-002-000 ==========
ErrorCode PROJECT_PLANNING_NOT_EXISTS = new ErrorCode(1_020_002_000, "合约规划不存在");
ErrorCode PROJECT_PLANNING_PROJECT_NOT_EXISTS = new ErrorCode(1_020_002_001, "关联项目不存在");
ErrorCode PROJECT_PLANNING_OWNERSHIP_TYPE_INVALID = new ErrorCode(1_020_002_002, "归属类型不正确");
ErrorCode PROJECT_PLANNING_CALCULATION_METHOD_INVALID = new ErrorCode(1_020_002_003, "产值计算方式不正确");
ErrorCode PROJECT_PLANNING_OWNERSHIP_TYPE_IMMUTABLE = new ErrorCode(1_020_002_004, "归属类型保存后不允许修改");
ErrorCode PROJECT_PLANNING_CALCULATION_METHOD_IMMUTABLE = new ErrorCode(1_020_002_005, "产值计算方式保存后不允许修改");
ErrorCode PROJECT_PLANNING_VIRTUAL_CALCULATION_METHOD_INVALID = new ErrorCode(1_020_002_006, "虚拟产值计算方式不正确");
// ========== 季度分配管理 1-020-003-000 ==========
ErrorCode PROJECT_PLANNING_QUARTER_NOT_EXISTS = new ErrorCode(1_020_003_000, "季度分配明细不存在");
ErrorCode PROJECT_PLANNING_QUARTER_DUPLICATE = new ErrorCode(1_020_003_001, "同一合约规划下该年度季度分配已存在");
ErrorCode PROJECT_PLANNING_QUARTER_PLANNING_NOT_EXISTS = new ErrorCode(1_020_003_002, "关联合约规划不存在");
// ========== 页面 4 拆分管理 1-020-004-000 ==========
ErrorCode PROJECT_OUTPUT_SPLIT_PLANNING_NOT_EXISTS = new ErrorCode(1_020_004_000, "关联合约规划不存在");
ErrorCode PROJECT_OUTPUT_SPLIT_NOT_MAJOR = new ErrorCode(1_020_004_001, "仅专业所记录允许进行页面4拆分");
ErrorCode PROJECT_OUTPUT_SPLIT_RATIO_INVALID = new ErrorCode(1_020_004_002, "页面4比例合计必须等于 100%");
// ========== 页面 5 角色拆分管理 1-020-005-000 ==========
ErrorCode SPECIALTY_ROLE_SPLIT_OUTPUT_SPLIT_NOT_EXISTS = new ErrorCode(1_020_005_000, "页面4拆分记录不存在");
ErrorCode SPECIALTY_ROLE_SPLIT_ROLE_RATIO_INVALID = new ErrorCode(1_020_005_001, "页面5五类角色比例合计必须等于 100%");
ErrorCode SPECIALTY_ROLE_SPLIT_SPECIALTY_INVALID = new ErrorCode(1_020_005_002, "专业编码不正确");
ErrorCode SPECIALTY_ROLE_SPLIT_ROLE_INVALID = new ErrorCode(1_020_005_003, "角色编码不正确");
ErrorCode SPECIALTY_ROLE_SPLIT_PERSON_INVALID = new ErrorCode(1_020_005_004, "页面5人员名称和比例必须同时填写");
ErrorCode SPECIALTY_ROLE_SPLIT_DESIGN_PERSON_REQUIRED = new ErrorCode(1_020_005_005, "页面5设计角色金额大于 0 时必须配置人员");
ErrorCode SPECIALTY_ROLE_SPLIT_PERSON_RATIO_INVALID = new ErrorCode(1_020_005_007, "页面5同一角色下人员比例之和不能大于 100%");
}

View File

@@ -0,0 +1,125 @@
package cn.iocoder.lyzsys.module.tjt.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* 页面 4 / 页面 5 业务常量
*
* @author Codex
*/
public final class OutputSplitBizConstants {
public static final String OWNERSHIP_TYPE_MAJOR = "专业所";
public static final String SPECIALTY_ARCH = "arch";
public static final String SPECIALTY_DECOR = "decor";
public static final String SPECIALTY_STRUCT = "struct";
public static final String SPECIALTY_WATER = "water";
public static final String SPECIALTY_ELEC = "elec";
public static final String SPECIALTY_HVAC = "hvac";
public static final String SPECIALTY_DIGITAL = "digital";
public static final String ROLE_DIRECTOR = "director";
public static final String ROLE_CHECK = "check";
public static final String ROLE_REVIEW = "review";
public static final String ROLE_APPROVE = "approve";
public static final String ROLE_DESIGN = "design";
public static final List<SpecialtyItem> SPECIALTY_ITEMS = Arrays.asList(
new SpecialtyItem(SPECIALTY_ARCH, "建筑", 1),
new SpecialtyItem(SPECIALTY_DECOR, "装修", 2),
new SpecialtyItem(SPECIALTY_STRUCT, "结构", 3),
new SpecialtyItem(SPECIALTY_WATER, "", 4),
new SpecialtyItem(SPECIALTY_ELEC, "电气", 5),
new SpecialtyItem(SPECIALTY_HVAC, "暖通", 6),
new SpecialtyItem(SPECIALTY_DIGITAL, "数字化设计", 7)
);
public static final List<RoleItem> ROLE_ITEMS = Arrays.asList(
new RoleItem(ROLE_DIRECTOR, "专业负责人", 1),
new RoleItem(ROLE_CHECK, "校对", 2),
new RoleItem(ROLE_REVIEW, "审核", 3),
new RoleItem(ROLE_APPROVE, "审定", 4),
new RoleItem(ROLE_DESIGN, "设计", 5)
);
private OutputSplitBizConstants() {
}
public static boolean isMajorOwnershipType(String value) {
return OWNERSHIP_TYPE_MAJOR.equals(value);
}
public static boolean isValidSpecialtyCode(String code) {
return SPECIALTY_ITEMS.stream().anyMatch(item -> item.getCode().equals(code));
}
public static boolean isValidRoleCode(String code) {
return ROLE_ITEMS.stream().anyMatch(item -> item.getCode().equals(code));
}
public static String getSpecialtyName(String code) {
return SPECIALTY_ITEMS.stream()
.filter(item -> item.getCode().equals(code))
.findFirst()
.map(SpecialtyItem::getName)
.orElse(code);
}
public static String getRoleName(String code) {
return ROLE_ITEMS.stream()
.filter(item -> item.getCode().equals(code))
.findFirst()
.map(RoleItem::getName)
.orElse(code);
}
public static int getRoleSortNo(String code) {
return ROLE_ITEMS.stream()
.filter(item -> item.getCode().equals(code))
.findFirst()
.map(RoleItem::getSortNo)
.orElse(0);
}
public static int getSpecialtySortNo(String code) {
return SPECIALTY_ITEMS.stream()
.filter(item -> item.getCode().equals(code))
.findFirst()
.map(SpecialtyItem::getSortNo)
.orElse(0);
}
public static Map<String, String> buildSpecialtyMap() {
Map<String, String> map = new LinkedHashMap<>();
for (SpecialtyItem item : SPECIALTY_ITEMS) {
map.put(item.getCode(), item.getName());
}
return map;
}
@Getter
@AllArgsConstructor
public static class SpecialtyItem {
private final String code;
private final String name;
private final Integer sortNo;
}
@Getter
@AllArgsConstructor
public static class RoleItem {
private final String code;
private final String name;
private final Integer sortNo;
}
}

View File

@@ -0,0 +1,83 @@
package cn.iocoder.lyzsys.module.tjt.enums;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* 合约规划业务类型常量
*
* @author Codex
*/
public final class ProjectPlanningBizTypeConstants {
public static final String OWNERSHIP_TYPE_MAJOR = "专业所";
public static final String OWNERSHIP_TYPE_COMPREHENSIVE = "综合所";
public static final String OWNERSHIP_TYPE_SUBCONTRACT = "专业分包";
public static final String CALCULATION_METHOD_GUIDANCE_PRICE = "指导价法";
public static final String CALCULATION_METHOD_CONTRACT_PRICE = "合同价法";
public static final String CALCULATION_METHOD_VIRTUAL_OUTPUT = "虚拟产值法";
public static final String VIRTUAL_CALCULATION_METHOD_GUIDANCE_PRICE = "指导单价法";
public static final String VIRTUAL_CALCULATION_METHOD_VIRTUAL_TOTAL_PRICE = "虚拟总价法";
public static final String VIRTUAL_CALCULATION_METHOD_WORKING_DAY = "工日法";
private static final Set<String> OWNERSHIP_TYPES = new HashSet<>(Arrays.asList(
OWNERSHIP_TYPE_MAJOR,
OWNERSHIP_TYPE_COMPREHENSIVE,
OWNERSHIP_TYPE_SUBCONTRACT
));
private static final Set<String> CALCULATION_METHODS = new HashSet<>(Arrays.asList(
CALCULATION_METHOD_GUIDANCE_PRICE,
CALCULATION_METHOD_CONTRACT_PRICE,
CALCULATION_METHOD_VIRTUAL_OUTPUT
));
private static final Set<String> VIRTUAL_CALCULATION_METHODS = new HashSet<>(Arrays.asList(
VIRTUAL_CALCULATION_METHOD_GUIDANCE_PRICE,
VIRTUAL_CALCULATION_METHOD_VIRTUAL_TOTAL_PRICE,
VIRTUAL_CALCULATION_METHOD_WORKING_DAY
));
private ProjectPlanningBizTypeConstants() {
}
public static boolean isValidOwnershipType(String value) {
return OWNERSHIP_TYPES.contains(value);
}
public static boolean isValidCalculationMethod(String value) {
return CALCULATION_METHODS.contains(value);
}
public static boolean isValidVirtualCalculationMethod(String value) {
return VIRTUAL_CALCULATION_METHODS.contains(value);
}
public static boolean isMajor(String value) {
return OWNERSHIP_TYPE_MAJOR.equals(value);
}
public static boolean isComprehensive(String value) {
return OWNERSHIP_TYPE_COMPREHENSIVE.equals(value);
}
public static boolean isSubcontract(String value) {
return OWNERSHIP_TYPE_SUBCONTRACT.equals(value);
}
public static boolean isGuidancePrice(String value) {
return CALCULATION_METHOD_GUIDANCE_PRICE.equals(value);
}
public static boolean isContractPrice(String value) {
return CALCULATION_METHOD_CONTRACT_PRICE.equals(value);
}
public static boolean isVirtualOutput(String value) {
return CALCULATION_METHOD_VIRTUAL_OUTPUT.equals(value);
}
}

View File

@@ -0,0 +1,32 @@
package cn.iocoder.lyzsys.module.tjt.service.outputsplit;
import cn.iocoder.lyzsys.module.tjt.controller.admin.outputsplit.vo.ProjectOutputSplitRespVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.outputsplit.vo.ProjectOutputSplitSaveReqVO;
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.outputsplit.ProjectOutputSplitDO;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Map;
/**
* 页面4拆分比例 Service 接口
*
* @author Codex
*/
public interface ProjectOutputSplitService {
ProjectOutputSplitRespVO getProjectOutputSplit(Long planningId);
Long saveProjectOutputSplit(ProjectOutputSplitSaveReqVO reqVO);
ProjectOutputSplitDO getOrCreateProjectOutputSplit(Long planningId);
Map<Long, ProjectOutputSplitDO> getProjectOutputSplitMap(Collection<Long> planningIds);
BigDecimal getSpecialtyAmount(ProjectOutputSplitDO outputSplit, BigDecimal assessmentOutputValue, String specialtyCode);
void deleteByPlanningId(Long planningId);
void deleteByPlanningIds(Collection<Long> planningIds);
}

View File

@@ -0,0 +1,269 @@
package cn.iocoder.lyzsys.module.tjt.service.outputsplit;
import cn.iocoder.lyzsys.framework.common.util.object.BeanUtils;
import cn.iocoder.lyzsys.module.tjt.controller.admin.outputsplit.vo.ProjectOutputSplitRespVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.outputsplit.vo.ProjectOutputSplitSaveReqVO;
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.project.ProjectDO;
import cn.iocoder.lyzsys.module.tjt.dal.mysql.outputsplit.ProjectOutputSplitMapper;
import cn.iocoder.lyzsys.module.tjt.dal.mysql.planning.ProjectPlanningMapper;
import cn.iocoder.lyzsys.module.tjt.dal.mysql.project.ProjectMapper;
import cn.iocoder.lyzsys.module.tjt.enums.OutputSplitBizConstants;
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.Map;
import java.util.function.Consumer;
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_NOT_EXISTS;
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_OUTPUT_SPLIT_NOT_MAJOR;
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_OUTPUT_SPLIT_PLANNING_NOT_EXISTS;
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_OUTPUT_SPLIT_RATIO_INVALID;
/**
* 页面4拆分比例 Service 实现类
*
* @author Codex
*/
@Service
@Validated
public class ProjectOutputSplitServiceImpl implements ProjectOutputSplitService {
private static final int RATIO_SCALE = 4;
private static final int AMOUNT_SCALE = 2;
private static final BigDecimal ZERO_RATIO = BigDecimal.ZERO.setScale(RATIO_SCALE, RoundingMode.HALF_UP);
private static final BigDecimal ONE_RATIO = BigDecimal.ONE.setScale(RATIO_SCALE, RoundingMode.HALF_UP);
private static final BigDecimal ZERO_AMOUNT = BigDecimal.ZERO.setScale(AMOUNT_SCALE, RoundingMode.HALF_UP);
@Resource
private ProjectOutputSplitMapper projectOutputSplitMapper;
@Resource
private ProjectPlanningMapper projectPlanningMapper;
@Resource
private ProjectMapper projectMapper;
@Override
public ProjectOutputSplitRespVO getProjectOutputSplit(Long planningId) {
ProjectPlanningDO planning = validateMajorPlanning(planningId);
ProjectDO project = validateProjectExists(planning.getProjectId());
ProjectOutputSplitDO outputSplit = projectOutputSplitMapper.selectByPlanningId(planningId);
if (outputSplit == null) {
outputSplit = buildDefaultOutputSplit(planning);
}
return buildRespVO(outputSplit, planning, project);
}
@Override
public Long saveProjectOutputSplit(ProjectOutputSplitSaveReqVO reqVO) {
ProjectPlanningDO planning = validateMajorPlanning(reqVO.getPlanningId());
validateOutputSplitRatios(reqVO);
ProjectOutputSplitDO outputSplit = projectOutputSplitMapper.selectByPlanningId(reqVO.getPlanningId());
if (outputSplit == null) {
outputSplit = BeanUtils.toBean(reqVO, ProjectOutputSplitDO.class);
outputSplit.setProjectId(planning.getProjectId());
outputSplit.setYear(getPlanningYear(planning));
normalizeRatios(outputSplit);
projectOutputSplitMapper.insert(outputSplit);
} else {
ProjectOutputSplitDO updateObj = BeanUtils.toBean(reqVO, ProjectOutputSplitDO.class);
updateObj.setId(outputSplit.getId());
updateObj.setProjectId(planning.getProjectId());
updateObj.setYear(getPlanningYear(planning));
normalizeRatios(updateObj);
projectOutputSplitMapper.updateById(updateObj);
outputSplit = updateObj;
}
return outputSplit.getId();
}
@Override
public ProjectOutputSplitDO getOrCreateProjectOutputSplit(Long planningId) {
ProjectPlanningDO planning = validateMajorPlanning(planningId);
ProjectOutputSplitDO outputSplit = projectOutputSplitMapper.selectByPlanningId(planningId);
if (outputSplit != null) {
return outputSplit;
}
ProjectOutputSplitDO createObj = buildDefaultOutputSplit(planning);
projectOutputSplitMapper.insert(createObj);
return createObj;
}
@Override
public Map<Long, ProjectOutputSplitDO> getProjectOutputSplitMap(Collection<Long> planningIds) {
if (planningIds == null || planningIds.isEmpty()) {
return Collections.emptyMap();
}
return projectOutputSplitMapper.selectListByPlanningIds(planningIds).stream()
.collect(Collectors.toMap(ProjectOutputSplitDO::getPlanningId, item -> item, (a, b) -> b));
}
@Override
public BigDecimal getSpecialtyAmount(ProjectOutputSplitDO outputSplit, BigDecimal assessmentOutputValue, String specialtyCode) {
if (outputSplit == null) {
return ZERO_AMOUNT;
}
BigDecimal officeAmount = multiplyAmount(amount(assessmentOutputValue), ratio(outputSplit.getOfficeRatio()));
return multiplyAmount(officeAmount, getSpecialtyRatio(outputSplit, specialtyCode));
}
@Override
public void deleteByPlanningId(Long planningId) {
ProjectOutputSplitDO outputSplit = projectOutputSplitMapper.selectByPlanningId(planningId);
if (outputSplit != null) {
projectOutputSplitMapper.deleteById(outputSplit.getId());
}
}
@Override
public void deleteByPlanningIds(Collection<Long> planningIds) {
if (planningIds == null || planningIds.isEmpty()) {
return;
}
projectOutputSplitMapper.delete(new cn.iocoder.lyzsys.framework.mybatis.core.query.LambdaQueryWrapperX<ProjectOutputSplitDO>()
.in(ProjectOutputSplitDO::getPlanningId, planningIds));
}
private ProjectOutputSplitRespVO buildRespVO(ProjectOutputSplitDO outputSplit, ProjectPlanningDO planning, ProjectDO project) {
ProjectOutputSplitRespVO respVO = BeanUtils.toBean(outputSplit, ProjectOutputSplitRespVO.class);
BigDecimal assessmentOutputValue = amount(planning.getAssessmentOutputValue());
BigDecimal projectManagerAmount = multiplyAmount(assessmentOutputValue, outputSplit.getProjectManagerRatio());
BigDecimal engineeringLeaderAmount = multiplyAmount(assessmentOutputValue, outputSplit.getEngineeringLeaderRatio());
BigDecimal officeAmount = multiplyAmount(assessmentOutputValue, outputSplit.getOfficeRatio());
respVO.setProjectName(project.getProjectName());
respVO.setPlanningContent(planning.getPlanningContent());
respVO.setYear(getPlanningYear(planning));
respVO.setAssessmentOutputValue(assessmentOutputValue);
respVO.setProjectManagerName(project.getProjectManagerName());
respVO.setEngineeringLeaderName(project.getEngineeringPrincipalName());
respVO.setProjectManagerAmount(projectManagerAmount);
respVO.setEngineeringLeaderAmount(engineeringLeaderAmount);
respVO.setOfficeAmount(officeAmount);
respVO.setArchAmount(multiplyAmount(officeAmount, outputSplit.getArchRatio()));
respVO.setDecorAmount(multiplyAmount(officeAmount, outputSplit.getDecorRatio()));
respVO.setStructAmount(multiplyAmount(officeAmount, outputSplit.getStructRatio()));
respVO.setWaterAmount(multiplyAmount(officeAmount, outputSplit.getWaterRatio()));
respVO.setElecAmount(multiplyAmount(officeAmount, outputSplit.getElecRatio()));
respVO.setHvacAmount(multiplyAmount(officeAmount, outputSplit.getHvacRatio()));
respVO.setDigitalAmount(multiplyAmount(officeAmount, outputSplit.getDigitalRatio()));
return respVO;
}
private ProjectPlanningDO validateMajorPlanning(Long planningId) {
ProjectPlanningDO planning = projectPlanningMapper.selectById(planningId);
if (planning == null) {
throw exception(PROJECT_OUTPUT_SPLIT_PLANNING_NOT_EXISTS);
}
if (!OutputSplitBizConstants.isMajorOwnershipType(planning.getOwnershipType())) {
throw exception(PROJECT_OUTPUT_SPLIT_NOT_MAJOR);
}
return planning;
}
private ProjectDO validateProjectExists(Long projectId) {
ProjectDO project = projectMapper.selectById(projectId);
if (project == null) {
throw exception(PROJECT_NOT_EXISTS);
}
return project;
}
private ProjectOutputSplitDO buildDefaultOutputSplit(ProjectPlanningDO planning) {
ProjectOutputSplitDO outputSplit = new ProjectOutputSplitDO();
outputSplit.setProjectId(planning.getProjectId());
outputSplit.setPlanningId(planning.getId());
outputSplit.setYear(getPlanningYear(planning));
outputSplit.setProjectManagerRatio(ZERO_RATIO);
outputSplit.setEngineeringLeaderRatio(ZERO_RATIO);
outputSplit.setOfficeRatio(ONE_RATIO);
outputSplit.setArchRatio(ONE_RATIO);
outputSplit.setDecorRatio(ZERO_RATIO);
outputSplit.setStructRatio(ZERO_RATIO);
outputSplit.setWaterRatio(ZERO_RATIO);
outputSplit.setElecRatio(ZERO_RATIO);
outputSplit.setHvacRatio(ZERO_RATIO);
outputSplit.setDigitalRatio(ZERO_RATIO);
return outputSplit;
}
private Integer getPlanningYear(ProjectPlanningDO planning) {
return planning.getPlanningStartYear() == null ? 0 : planning.getPlanningStartYear();
}
private void validateOutputSplitRatios(ProjectOutputSplitSaveReqVO reqVO) {
BigDecimal projectTotal = ratio(reqVO.getProjectManagerRatio())
.add(ratio(reqVO.getEngineeringLeaderRatio()))
.add(ratio(reqVO.getOfficeRatio()))
.setScale(RATIO_SCALE, RoundingMode.HALF_UP);
BigDecimal specialtyTotal = ratio(reqVO.getArchRatio())
.add(ratio(reqVO.getDecorRatio()))
.add(ratio(reqVO.getStructRatio()))
.add(ratio(reqVO.getWaterRatio()))
.add(ratio(reqVO.getElecRatio()))
.add(ratio(reqVO.getHvacRatio()))
.add(ratio(reqVO.getDigitalRatio()))
.setScale(RATIO_SCALE, RoundingMode.HALF_UP);
if (projectTotal.compareTo(ONE_RATIO) > 0 || specialtyTotal.compareTo(ONE_RATIO) != 0) {
throw exception(PROJECT_OUTPUT_SPLIT_RATIO_INVALID);
}
}
private void normalizeRatios(ProjectOutputSplitDO outputSplit) {
normalize(outputSplit::setProjectManagerRatio, outputSplit.getProjectManagerRatio());
normalize(outputSplit::setEngineeringLeaderRatio, outputSplit.getEngineeringLeaderRatio());
normalize(outputSplit::setOfficeRatio, outputSplit.getOfficeRatio());
normalize(outputSplit::setArchRatio, outputSplit.getArchRatio());
normalize(outputSplit::setDecorRatio, outputSplit.getDecorRatio());
normalize(outputSplit::setStructRatio, outputSplit.getStructRatio());
normalize(outputSplit::setWaterRatio, outputSplit.getWaterRatio());
normalize(outputSplit::setElecRatio, outputSplit.getElecRatio());
normalize(outputSplit::setHvacRatio, outputSplit.getHvacRatio());
normalize(outputSplit::setDigitalRatio, outputSplit.getDigitalRatio());
}
private void normalize(Consumer<BigDecimal> consumer, BigDecimal value) {
consumer.accept(ratio(value));
}
private BigDecimal getSpecialtyRatio(ProjectOutputSplitDO outputSplit, String specialtyCode) {
switch (specialtyCode) {
case OutputSplitBizConstants.SPECIALTY_ARCH:
return ratio(outputSplit.getArchRatio());
case OutputSplitBizConstants.SPECIALTY_DECOR:
return ratio(outputSplit.getDecorRatio());
case OutputSplitBizConstants.SPECIALTY_STRUCT:
return ratio(outputSplit.getStructRatio());
case OutputSplitBizConstants.SPECIALTY_WATER:
return ratio(outputSplit.getWaterRatio());
case OutputSplitBizConstants.SPECIALTY_ELEC:
return ratio(outputSplit.getElecRatio());
case OutputSplitBizConstants.SPECIALTY_HVAC:
return ratio(outputSplit.getHvacRatio());
case OutputSplitBizConstants.SPECIALTY_DIGITAL:
return ratio(outputSplit.getDigitalRatio());
default:
return ZERO_RATIO;
}
}
private BigDecimal multiplyAmount(BigDecimal left, BigDecimal right) {
return amount(left).multiply(ratio(right)).setScale(AMOUNT_SCALE, RoundingMode.HALF_UP);
}
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);
}
}

View File

@@ -0,0 +1,36 @@
package cn.iocoder.lyzsys.module.tjt.service.planning;
import cn.iocoder.lyzsys.framework.common.pojo.PageResult;
import cn.iocoder.lyzsys.module.tjt.controller.admin.planning.vo.ProjectPlanningPageReqVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.planning.vo.ProjectPlanningSaveReqVO;
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planning.ProjectPlanningDO;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* 合约规划 Service 接口
*
* @author Codex
*/
public interface ProjectPlanningService {
Long createProjectPlanning(ProjectPlanningSaveReqVO createReqVO);
void updateProjectPlanning(ProjectPlanningSaveReqVO updateReqVO);
void deleteProjectPlanning(Long id);
void deleteProjectPlanningList(List<Long> ids);
PageResult<ProjectPlanningDO> getProjectPlanningPage(ProjectPlanningPageReqVO pageReqVO);
List<ProjectPlanningDO> getProjectPlanningListByProjectId(Long projectId);
ProjectPlanningDO getProjectPlanning(Long id);
Map<Long, BigDecimal> getAllocatedAmountMap(Collection<Long> planningIds);
}

View File

@@ -0,0 +1,438 @@
package cn.iocoder.lyzsys.module.tjt.service.planning;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.lyzsys.framework.common.pojo.PageResult;
import cn.iocoder.lyzsys.framework.common.util.object.BeanUtils;
import cn.iocoder.lyzsys.module.tjt.controller.admin.planning.vo.ProjectPlanningPageReqVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.planning.vo.ProjectPlanningSaveReqVO;
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.project.ProjectDO;
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.specialtyrolesplit.SpecialtyRoleSplitService;
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.HashMap;
import java.util.List;
import java.util.Map;
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_IMMUTABLE;
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_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;
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_PLANNING_PROJECT_NOT_EXISTS;
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_PLANNING_VIRTUAL_CALCULATION_METHOD_INVALID;
/**
* 合约规划 Service 实现类
*
* @author Codex
*/
@Service
@Validated
public class ProjectPlanningServiceImpl implements ProjectPlanningService {
private static final int AMOUNT_SCALE = 2;
private static final int RATIO_SCALE = 4;
private static final int FACTOR_SCALE = 2;
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);
private static final BigDecimal ONE_RATIO = BigDecimal.ONE.setScale(RATIO_SCALE, RoundingMode.HALF_UP);
private static final BigDecimal DEFAULT_COMPREHENSIVE_RATIO = new BigDecimal("0.0800");
private static final BigDecimal DEFAULT_SUBCONTRACT_RATIO = new BigDecimal("0.0400");
private static final BigDecimal DEFAULT_MAJOR_OUTSOURCE_RATIO = new BigDecimal("0.0600");
private static final BigDecimal DEFAULT_COMMON_OUTSOURCE_RATIO = new BigDecimal("0.2500");
private static final BigDecimal DEFAULT_WORKING_DAY_UNIT_PRICE = new BigDecimal("1000.00");
private static final BigDecimal MIN_GUIDANCE_PRICE_RATIO = new BigDecimal("0.6000");
@Resource
private ProjectPlanningMapper projectPlanningMapper;
@Resource
private ProjectMapper projectMapper;
@Resource
private ProjectPlanningQuarterMapper projectPlanningQuarterMapper;
@Resource
private ProjectOutputSplitService projectOutputSplitService;
@Resource
private SpecialtyRoleSplitService specialtyRoleSplitService;
@Override
public Long createProjectPlanning(ProjectPlanningSaveReqVO createReqVO) {
ProjectDO project = validateProjectExists(createReqVO.getProjectId());
validateProjectPlanningForSave(createReqVO, null);
ProjectPlanningDO planning = BeanUtils.toBean(createReqVO, ProjectPlanningDO.class);
prepareProjectPlanning(planning, project);
calculateProjectPlanning(planning);
projectPlanningMapper.insert(planning);
return planning.getId();
}
@Override
public void updateProjectPlanning(ProjectPlanningSaveReqVO updateReqVO) {
ProjectPlanningDO dbPlanning = validateProjectPlanningExists(updateReqVO.getId());
ProjectDO project = validateProjectExists(updateReqVO.getProjectId());
validateProjectPlanningForSave(updateReqVO, dbPlanning);
ProjectPlanningDO updateObj = BeanUtils.toBean(updateReqVO, ProjectPlanningDO.class);
prepareProjectPlanning(updateObj, project);
calculateProjectPlanning(updateObj);
projectPlanningMapper.updateById(updateObj);
refreshQuarterDistributionAmounts(updateObj);
}
@Override
public void deleteProjectPlanning(Long id) {
validateProjectPlanningExists(id);
ProjectOutputSplitDO outputSplit = projectOutputSplitService.getProjectOutputSplitMap(Collections.singleton(id)).get(id);
if (outputSplit != null) {
specialtyRoleSplitService.deleteByOutputSplitId(outputSplit.getId());
projectOutputSplitService.deleteByPlanningId(id);
}
projectPlanningQuarterMapper.delete(ProjectPlanningQuarterDO::getPlanningId, id);
projectPlanningMapper.deleteById(id);
}
@Override
public void deleteProjectPlanningList(List<Long> ids) {
ids.forEach(this::validateProjectPlanningExists);
Map<Long, ProjectOutputSplitDO> outputSplitMap = projectOutputSplitService.getProjectOutputSplitMap(ids);
if (!outputSplitMap.isEmpty()) {
specialtyRoleSplitService.deleteByOutputSplitIds(outputSplitMap.values().stream()
.map(ProjectOutputSplitDO::getId)
.collect(java.util.stream.Collectors.toList()));
projectOutputSplitService.deleteByPlanningIds(ids);
}
projectPlanningQuarterMapper.deleteBatch(ProjectPlanningQuarterDO::getPlanningId, ids);
projectPlanningMapper.deleteBatchIds(ids);
}
@Override
public PageResult<ProjectPlanningDO> getProjectPlanningPage(ProjectPlanningPageReqVO pageReqVO) {
if (pageReqVO.getProjectId() != null) {
validateProjectExists(pageReqVO.getProjectId());
}
return projectPlanningMapper.selectPage(pageReqVO);
}
@Override
public List<ProjectPlanningDO> getProjectPlanningListByProjectId(Long projectId) {
validateProjectExists(projectId);
return projectPlanningMapper.selectListByProjectId(projectId);
}
@Override
public ProjectPlanningDO getProjectPlanning(Long id) {
return projectPlanningMapper.selectById(id);
}
@Override
public Map<Long, BigDecimal> getAllocatedAmountMap(Collection<Long> planningIds) {
if (planningIds == null || planningIds.isEmpty()) {
return Collections.emptyMap();
}
List<ProjectPlanningQuarterDO> quarterList = projectPlanningQuarterMapper.selectListByPlanningIds(planningIds);
Map<Long, BigDecimal> allocatedAmountMap = new HashMap<>();
for (Long planningId : planningIds) {
allocatedAmountMap.put(planningId, ZERO_RATIO);
}
for (ProjectPlanningQuarterDO quarter : quarterList) {
allocatedAmountMap.merge(quarter.getPlanningId(), ratio(quarter.getDistributionRatio()), BigDecimal::add);
}
allocatedAmountMap.replaceAll((key, value) -> ratio(value));
return allocatedAmountMap;
}
private void validateProjectPlanningForSave(ProjectPlanningSaveReqVO reqVO, ProjectPlanningDO dbPlanning) {
if (!ProjectPlanningBizTypeConstants.isValidOwnershipType(reqVO.getOwnershipType())) {
throw exception(PROJECT_PLANNING_OWNERSHIP_TYPE_INVALID);
}
if (!ProjectPlanningBizTypeConstants.isValidCalculationMethod(reqVO.getCalculationMethod())) {
throw exception(PROJECT_PLANNING_CALCULATION_METHOD_INVALID);
}
if (ProjectPlanningBizTypeConstants.isVirtualOutput(reqVO.getCalculationMethod())
&& StrUtil.isNotBlank(reqVO.getVirtualCalculationMethod())
&& !ProjectPlanningBizTypeConstants.isValidVirtualCalculationMethod(reqVO.getVirtualCalculationMethod())) {
throw exception(PROJECT_PLANNING_VIRTUAL_CALCULATION_METHOD_INVALID);
}
if (dbPlanning == null) {
return;
}
if (!Objects.equals(dbPlanning.getOwnershipType(), reqVO.getOwnershipType())) {
throw exception(PROJECT_PLANNING_OWNERSHIP_TYPE_IMMUTABLE);
}
if (!Objects.equals(dbPlanning.getCalculationMethod(), reqVO.getCalculationMethod())) {
throw exception(PROJECT_PLANNING_CALCULATION_METHOD_IMMUTABLE);
}
}
private void prepareProjectPlanning(ProjectPlanningDO planning, ProjectDO project) {
if (planning.getPlanningStartYear() == null) {
planning.setPlanningStartYear(project.getProjectStartYear());
}
if (planning.getReviewOutsourceFlag() == null) {
planning.setReviewOutsourceFlag(Boolean.FALSE);
}
planning.setPlanningAmount(amount(planning.getPlanningAmount()));
planning.setManagementFeeRate(ratio(planning.getManagementFeeRate()));
if (planning.getPlanningArea() == null) {
planning.setPlanningArea(project.getTotalConstructionArea());
}
planning.setPlanningArea(amount(planning.getPlanningArea()));
planning.setCurrentDesignStageRatio(ratio(planning.getCurrentDesignStageRatio()));
planning.setTotalDistributionAmount(planning.getTotalDistributionAmount() == null
? ONE_RATIO : ratio(planning.getTotalDistributionAmount()));
if (planning.getReviewOutsourceRatio() == null) {
planning.setReviewOutsourceRatio(defaultReviewOutsourceRatio(planning.getOwnershipType(),
Boolean.TRUE.equals(planning.getReviewOutsourceFlag())));
} else {
planning.setReviewOutsourceRatio(ratio(planning.getReviewOutsourceRatio()));
}
if ((ProjectPlanningBizTypeConstants.isComprehensive(planning.getOwnershipType())
|| ProjectPlanningBizTypeConstants.isSubcontract(planning.getOwnershipType()))
&& planning.getCalculationRatio() == null) {
planning.setCalculationRatio(defaultCalculationRatio(planning.getOwnershipType()));
} else if (planning.getCalculationRatio() != null) {
planning.setCalculationRatio(ratio(planning.getCalculationRatio()));
}
planning.setDrawingSetFactor(factorNullable(planning.getDrawingSetFactor()));
planning.setScaleFactor(factorNullable(planning.getScaleFactor()));
planning.setModificationFactor(factorNullable(planning.getModificationFactor()));
planning.setComplexityFactor(ratioNullable(planning.getComplexityFactor()));
planning.setInternalGuidanceUnitPrice(amountNullable(planning.getInternalGuidanceUnitPrice()));
planning.setWorkingDayCount(amountNullable(planning.getWorkingDayCount()));
planning.setWorkingDayUnitPrice(amountNullable(planning.getWorkingDayUnitPrice()));
planning.setGuidanceUnitPrice(amountNullable(planning.getGuidanceUnitPrice()));
planning.setGuidanceTotalPrice(amountNullable(planning.getGuidanceTotalPrice()));
planning.setVirtualTotalPrice(amountNullable(planning.getVirtualTotalPrice()));
if (ProjectPlanningBizTypeConstants.VIRTUAL_CALCULATION_METHOD_WORKING_DAY.equals(planning.getVirtualCalculationMethod())
&& planning.getWorkingDayUnitPrice() == null) {
planning.setWorkingDayUnitPrice(DEFAULT_WORKING_DAY_UNIT_PRICE);
}
}
private void calculateProjectPlanning(ProjectPlanningDO planning) {
planning.setManagementFee(multiplyAmount(planning.getPlanningAmount(), planning.getManagementFeeRate()));
planning.setContractUnitPrice(divideAmount(planning.getPlanningAmount(), planning.getPlanningArea()));
planning.setTotalAdjustmentFactor(ZERO_RATIO);
planning.setAssessmentArea(ZERO_AMOUNT);
planning.setVirtualOutputValue(ZERO_AMOUNT);
planning.setAssessmentOutputValue(ZERO_AMOUNT);
if (ProjectPlanningBizTypeConstants.isMajor(planning.getOwnershipType())) {
calculateMajorPlanning(planning);
return;
}
if (ProjectPlanningBizTypeConstants.isComprehensive(planning.getOwnershipType())
|| ProjectPlanningBizTypeConstants.isSubcontract(planning.getOwnershipType())) {
planning.setAssessmentOutputValue(multiplyAmount(
planning.getPlanningAmount(),
ratio(planning.getCalculationRatio()),
planning.getCurrentDesignStageRatio(),
oneMinus(planning.getReviewOutsourceRatio())));
}
}
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())));
return;
}
if (ProjectPlanningBizTypeConstants.isContractPrice(planning.getCalculationMethod())) {
BigDecimal totalAdjustmentFactor = calculateTotalAdjustmentFactor(planning);
planning.setTotalAdjustmentFactor(totalAdjustmentFactor);
planning.setAssessmentArea(multiplyAmount(planning.getPlanningArea(), totalAdjustmentFactor));
planning.setAssessmentOutputValue(multiplyAmount(
planning.getPlanningAmount(),
totalAdjustmentFactor,
planning.getCurrentDesignStageRatio(),
oneMinus(planning.getReviewOutsourceRatio())));
return;
}
if (ProjectPlanningBizTypeConstants.isVirtualOutput(planning.getCalculationMethod())) {
planning.setTotalAdjustmentFactor(ONE_RATIO);
planning.setVirtualOutputValue(calculateVirtualOutputValue(planning));
planning.setAssessmentOutputValue(multiplyAmount(
planning.getVirtualOutputValue(),
planning.getCurrentDesignStageRatio(),
oneMinus(planning.getReviewOutsourceRatio())));
}
}
private BigDecimal calculateTotalAdjustmentFactor(ProjectPlanningDO planning) {
return productRatio(
defaultFactor(planning.getDrawingSetFactor()),
defaultFactor(planning.getScaleFactor()),
defaultFactor(planning.getModificationFactor()),
defaultFactor(planning.getComplexityFactor()));
}
private BigDecimal calculateApplicableUnitPrice(BigDecimal contractUnitPrice, BigDecimal internalGuidanceUnitPrice) {
BigDecimal guidanceUnitPrice = amount(internalGuidanceUnitPrice);
if (guidanceUnitPrice.compareTo(BigDecimal.ZERO) <= 0) {
return ZERO_AMOUNT;
}
BigDecimal unitPrice = amount(contractUnitPrice);
BigDecimal minimumGuidancePrice = multiplyAmount(guidanceUnitPrice, MIN_GUIDANCE_PRICE_RATIO);
if (unitPrice.compareTo(guidanceUnitPrice) > 0) {
return guidanceUnitPrice;
}
if (unitPrice.compareTo(minimumGuidancePrice) >= 0) {
return unitPrice;
}
return minimumGuidancePrice;
}
private BigDecimal calculateVirtualOutputValue(ProjectPlanningDO planning) {
if (ProjectPlanningBizTypeConstants.VIRTUAL_CALCULATION_METHOD_WORKING_DAY.equals(planning.getVirtualCalculationMethod())) {
return multiplyAmount(planning.getWorkingDayCount(), planning.getWorkingDayUnitPrice());
}
if (ProjectPlanningBizTypeConstants.VIRTUAL_CALCULATION_METHOD_GUIDANCE_PRICE.equals(planning.getVirtualCalculationMethod())) {
if (isPositive(planning.getGuidanceTotalPrice())) {
return amount(planning.getGuidanceTotalPrice());
}
return multiplyAmount(planning.getGuidanceUnitPrice(), planning.getPlanningArea());
}
if (ProjectPlanningBizTypeConstants.VIRTUAL_CALCULATION_METHOD_VIRTUAL_TOTAL_PRICE.equals(planning.getVirtualCalculationMethod())) {
return amount(planning.getVirtualTotalPrice());
}
return ZERO_AMOUNT;
}
private BigDecimal defaultReviewOutsourceRatio(String ownershipType, boolean outsourceFlag) {
if (!outsourceFlag) {
return ZERO_RATIO;
}
if (ProjectPlanningBizTypeConstants.isMajor(ownershipType)) {
return DEFAULT_MAJOR_OUTSOURCE_RATIO;
}
return DEFAULT_COMMON_OUTSOURCE_RATIO;
}
private BigDecimal defaultCalculationRatio(String ownershipType) {
if (ProjectPlanningBizTypeConstants.isComprehensive(ownershipType)) {
return DEFAULT_COMPREHENSIVE_RATIO;
}
if (ProjectPlanningBizTypeConstants.isSubcontract(ownershipType)) {
return DEFAULT_SUBCONTRACT_RATIO;
}
return null;
}
private ProjectDO validateProjectExists(Long projectId) {
ProjectDO project = projectMapper.selectById(projectId);
if (project == null) {
throw exception(PROJECT_PLANNING_PROJECT_NOT_EXISTS);
}
return project;
}
private ProjectPlanningDO validateProjectPlanningExists(Long id) {
ProjectPlanningDO planning = projectPlanningMapper.selectById(id);
if (planning == null) {
throw exception(PROJECT_PLANNING_NOT_EXISTS);
}
return planning;
}
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 productRatio(BigDecimal... values) {
BigDecimal result = BigDecimal.ONE;
for (BigDecimal value : values) {
result = result.multiply(value == null ? BigDecimal.ONE : 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);
}
private BigDecimal oneMinus(BigDecimal value) {
return BigDecimal.ONE.subtract(ratio(value)).setScale(RATIO_SCALE, RoundingMode.HALF_UP);
}
private BigDecimal defaultFactor(BigDecimal value) {
return value == null ? ONE_RATIO : ratio(value);
}
private BigDecimal amount(BigDecimal value) {
return value == null ? ZERO_AMOUNT : value.setScale(AMOUNT_SCALE, RoundingMode.HALF_UP);
}
private BigDecimal amountNullable(BigDecimal value) {
return value == null ? null : amount(value);
}
private BigDecimal factor(BigDecimal value) {
return value == null ? BigDecimal.ZERO.setScale(FACTOR_SCALE, RoundingMode.HALF_UP)
: value.setScale(FACTOR_SCALE, RoundingMode.HALF_UP);
}
private BigDecimal factorNullable(BigDecimal value) {
return value == null ? null : factor(value);
}
private BigDecimal ratio(BigDecimal value) {
return value == null ? ZERO_RATIO : value.setScale(RATIO_SCALE, RoundingMode.HALF_UP);
}
private BigDecimal ratioNullable(BigDecimal value) {
return value == null ? null : ratio(value);
}
private boolean isPositive(BigDecimal value) {
return value != null && value.compareTo(BigDecimal.ZERO) > 0;
}
private void refreshQuarterDistributionAmounts(ProjectPlanningDO planning) {
List<ProjectPlanningQuarterDO> quarterList = projectPlanningQuarterMapper.selectListByPlanningId(planning.getId());
for (ProjectPlanningQuarterDO quarter : quarterList) {
quarter.setDistributionAmount(calculateQuarterDistributionAmount(planning, quarter.getDistributionRatio()));
projectPlanningQuarterMapper.updateById(quarter);
}
}
private BigDecimal calculateQuarterDistributionAmount(ProjectPlanningDO planning, BigDecimal distributionRatio) {
return multiplyAmount(
planning.getAssessmentOutputValue(),
planning.getTotalDistributionAmount(),
ratio(distributionRatio));
}
}

View File

@@ -0,0 +1,27 @@
package cn.iocoder.lyzsys.module.tjt.service.planningquarter;
import cn.iocoder.lyzsys.module.tjt.controller.admin.planningquarter.vo.ProjectPlanningQuarterSaveReqVO;
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planningquarter.ProjectPlanningQuarterDO;
import java.util.List;
/**
* 季度分配 Service 接口
*
* @author Codex
*/
public interface ProjectPlanningQuarterService {
Long createProjectPlanningQuarter(ProjectPlanningQuarterSaveReqVO createReqVO);
void updateProjectPlanningQuarter(ProjectPlanningQuarterSaveReqVO updateReqVO);
void deleteProjectPlanningQuarter(Long id);
void deleteProjectPlanningQuarterList(List<Long> ids);
ProjectPlanningQuarterDO getProjectPlanningQuarter(Long id);
List<ProjectPlanningQuarterDO> getProjectPlanningQuarterListByPlanningId(Long planningId);
}

View File

@@ -0,0 +1,146 @@
package cn.iocoder.lyzsys.module.tjt.service.planningquarter;
import cn.iocoder.lyzsys.framework.common.util.object.BeanUtils;
import cn.iocoder.lyzsys.module.tjt.controller.admin.planningquarter.vo.ProjectPlanningQuarterSaveReqVO;
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.mysql.planning.ProjectPlanningMapper;
import cn.iocoder.lyzsys.module.tjt.dal.mysql.planningquarter.ProjectPlanningQuarterMapper;
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.List;
import static cn.iocoder.lyzsys.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_PLANNING_QUARTER_DUPLICATE;
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_PLANNING_QUARTER_NOT_EXISTS;
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_PLANNING_QUARTER_PLANNING_NOT_EXISTS;
/**
* 季度分配 Service 实现类
*
* @author Codex
*/
@Service
@Validated
public class ProjectPlanningQuarterServiceImpl implements ProjectPlanningQuarterService {
private static final BigDecimal ZERO_AMOUNT = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
private static final BigDecimal ZERO_RATIO = BigDecimal.ZERO.setScale(4, RoundingMode.HALF_UP);
@Resource
private ProjectPlanningQuarterMapper projectPlanningQuarterMapper;
@Resource
private ProjectPlanningMapper projectPlanningMapper;
@Override
public Long createProjectPlanningQuarter(ProjectPlanningQuarterSaveReqVO createReqVO) {
ProjectPlanningDO planning = validatePlanningExists(createReqVO.getPlanningId());
if (isZeroQuarter(createReqVO)) {
return null;
}
validateQuarterUnique(null, createReqVO.getPlanningId(),
createReqVO.getDistributionYear(), createReqVO.getQuarterNo());
ProjectPlanningQuarterDO quarter = BeanUtils.toBean(createReqVO, ProjectPlanningQuarterDO.class);
prepareQuarter(quarter, planning);
projectPlanningQuarterMapper.insert(quarter);
return quarter.getId();
}
@Override
public void updateProjectPlanningQuarter(ProjectPlanningQuarterSaveReqVO updateReqVO) {
ProjectPlanningQuarterDO dbQuarter = validateQuarterExists(updateReqVO.getId());
ProjectPlanningDO planning = validatePlanningExists(updateReqVO.getPlanningId());
if (isZeroQuarter(updateReqVO)) {
projectPlanningQuarterMapper.deleteById(dbQuarter.getId());
return;
}
validateQuarterUnique(updateReqVO.getId(), updateReqVO.getPlanningId(),
updateReqVO.getDistributionYear(), updateReqVO.getQuarterNo());
ProjectPlanningQuarterDO updateObj = BeanUtils.toBean(updateReqVO, ProjectPlanningQuarterDO.class);
prepareQuarter(updateObj, planning);
projectPlanningQuarterMapper.updateById(updateObj);
}
@Override
public void deleteProjectPlanningQuarter(Long id) {
validateQuarterExists(id);
projectPlanningQuarterMapper.deleteById(id);
}
@Override
public void deleteProjectPlanningQuarterList(List<Long> ids) {
ids.forEach(this::validateQuarterExists);
projectPlanningQuarterMapper.deleteBatchIds(ids);
}
@Override
public ProjectPlanningQuarterDO getProjectPlanningQuarter(Long id) {
return projectPlanningQuarterMapper.selectById(id);
}
@Override
public List<ProjectPlanningQuarterDO> getProjectPlanningQuarterListByPlanningId(Long planningId) {
validatePlanningExists(planningId);
return projectPlanningQuarterMapper.selectListByPlanningId(planningId);
}
private void validateQuarterUnique(Long id, Long planningId, Integer distributionYear, Integer quarterNo) {
ProjectPlanningQuarterDO quarter = projectPlanningQuarterMapper.selectByPlanningIdAndDistributionYearAndQuarter(
planningId, distributionYear, quarterNo);
if (quarter == null) {
return;
}
if (id == null || !quarter.getId().equals(id)) {
throw exception(PROJECT_PLANNING_QUARTER_DUPLICATE);
}
}
private ProjectPlanningDO validatePlanningExists(Long planningId) {
ProjectPlanningDO planning = projectPlanningMapper.selectById(planningId);
if (planning == null) {
throw exception(PROJECT_PLANNING_QUARTER_PLANNING_NOT_EXISTS);
}
return planning;
}
private ProjectPlanningQuarterDO validateQuarterExists(Long id) {
ProjectPlanningQuarterDO quarter = projectPlanningQuarterMapper.selectById(id);
if (quarter == null) {
throw exception(PROJECT_PLANNING_QUARTER_NOT_EXISTS);
}
return quarter;
}
private boolean isZeroQuarter(ProjectPlanningQuarterSaveReqVO reqVO) {
return ratio(reqVO.getDistributionRatio()).compareTo(BigDecimal.ZERO) == 0;
}
private void prepareQuarter(ProjectPlanningQuarterDO quarter, ProjectPlanningDO planning) {
quarter.setDistributionRatio(ratio(quarter.getDistributionRatio()));
quarter.setDistributionAmount(multiplyAmount(
planning.getAssessmentOutputValue(),
planning.getTotalDistributionAmount(),
quarter.getDistributionRatio()));
}
private BigDecimal amount(BigDecimal value) {
return value == null ? ZERO_AMOUNT : value.setScale(2, RoundingMode.HALF_UP);
}
private BigDecimal ratio(BigDecimal value) {
return value == null ? ZERO_RATIO : value.setScale(4, 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(2, RoundingMode.HALF_UP);
}
}

View File

@@ -0,0 +1,18 @@
package cn.iocoder.lyzsys.module.tjt.service.profit;
import cn.iocoder.lyzsys.framework.common.pojo.PageResult;
import cn.iocoder.lyzsys.module.tjt.controller.admin.profit.vo.ProjectProfitPageReqVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.profit.vo.ProjectProfitRespVO;
/**
* 项目盈亏 Service 接口
*
* @author Codex
*/
public interface ProjectProfitService {
ProjectProfitRespVO getProjectProfit(Long projectId);
PageResult<ProjectProfitRespVO> getProjectProfitPage(ProjectProfitPageReqVO pageReqVO);
}

View File

@@ -0,0 +1,135 @@
package cn.iocoder.lyzsys.module.tjt.service.profit;
import cn.iocoder.lyzsys.framework.common.pojo.PageResult;
import cn.iocoder.lyzsys.framework.common.util.collection.CollectionUtils;
import cn.iocoder.lyzsys.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.lyzsys.module.tjt.controller.admin.profit.vo.ProjectProfitPageReqVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.profit.vo.ProjectProfitRespVO;
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planning.ProjectPlanningDO;
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.project.ProjectDO;
import cn.iocoder.lyzsys.module.tjt.dal.mysql.planning.ProjectPlanningMapper;
import cn.iocoder.lyzsys.module.tjt.dal.mysql.project.ProjectMapper;
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.Collections;
import java.util.List;
import java.util.Map;
import static cn.iocoder.lyzsys.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_NOT_EXISTS;
/**
* 项目盈亏 Service 实现类
*
* @author Codex
*/
@Service
@Validated
public class ProjectProfitServiceImpl implements ProjectProfitService {
private static final BigDecimal ZERO_AMOUNT = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
private static final BigDecimal ZERO_RATIO = BigDecimal.ZERO.setScale(4, RoundingMode.HALF_UP);
@Resource
private ProjectMapper projectMapper;
@Resource
private ProjectPlanningMapper projectPlanningMapper;
@Override
public ProjectProfitRespVO getProjectProfit(Long projectId) {
ProjectDO project = validateProjectExists(projectId);
List<ProjectPlanningDO> planningList = projectPlanningMapper.selectList(ProjectPlanningDO::getProjectId,
Collections.singleton(projectId));
return buildProjectProfit(project, planningList);
}
@Override
public PageResult<ProjectProfitRespVO> getProjectProfitPage(ProjectProfitPageReqVO pageReqVO) {
PageResult<ProjectDO> pageResult = projectMapper.selectPage(pageReqVO, new LambdaQueryWrapperX<ProjectDO>()
.likeIfPresent(ProjectDO::getProjectName, pageReqVO.getProjectName())
.eqIfPresent(ProjectDO::getContractSignedFlag, pageReqVO.getContractSignedFlag())
.eqIfPresent(ProjectDO::getProjectStartYear, pageReqVO.getProjectStartYear())
.betweenIfPresent(ProjectDO::getCreateTime, pageReqVO.getCreateTime())
.orderByDesc(ProjectDO::getId));
if (pageResult.getList().isEmpty()) {
return new PageResult<>(Collections.emptyList(), pageResult.getTotal());
}
Map<Long, List<ProjectPlanningDO>> planningMap = CollectionUtils.convertMultiMap(
projectPlanningMapper.selectList(ProjectPlanningDO::getProjectId,
CollectionUtils.convertSet(pageResult.getList(), ProjectDO::getId)),
ProjectPlanningDO::getProjectId);
List<ProjectProfitRespVO> list = CollectionUtils.convertList(pageResult.getList(),
project -> buildProjectProfit(project, planningMap.get(project.getId())));
return new PageResult<>(list, pageResult.getTotal());
}
private ProjectProfitRespVO buildProjectProfit(ProjectDO project, List<ProjectPlanningDO> planningList) {
List<ProjectPlanningDO> safePlanningList = planningList == null ? Collections.emptyList() : planningList;
BigDecimal comprehensivePlanningAmount = ZERO_AMOUNT;
BigDecimal subcontractPlanningAmount = ZERO_AMOUNT;
BigDecimal majorOutputValue = ZERO_AMOUNT;
for (ProjectPlanningDO planning : safePlanningList) {
if (ProjectPlanningBizTypeConstants.isComprehensive(planning.getOwnershipType())) {
comprehensivePlanningAmount = comprehensivePlanningAmount.add(amount(planning.getPlanningAmount()));
continue;
}
if (ProjectPlanningBizTypeConstants.isSubcontract(planning.getOwnershipType())) {
subcontractPlanningAmount = subcontractPlanningAmount.add(amount(planning.getPlanningAmount()));
continue;
}
if (ProjectPlanningBizTypeConstants.isMajor(planning.getOwnershipType())) {
majorOutputValue = majorOutputValue.add(amount(planning.getAssessmentOutputValue()));
}
}
BigDecimal expectedKValue = ratio(project.getExpectedKValue());
BigDecimal majorExpectedPerformance = majorOutputValue.multiply(expectedKValue).setScale(2, RoundingMode.HALF_UP);
BigDecimal finalSettlementAmount = amount(project.getFinalSettlementAmount());
BigDecimal profitLossValue = finalSettlementAmount
.subtract(comprehensivePlanningAmount)
.subtract(subcontractPlanningAmount)
.subtract(majorExpectedPerformance)
.setScale(2, RoundingMode.HALF_UP);
BigDecimal profitLossRate = finalSettlementAmount.compareTo(BigDecimal.ZERO) == 0
? ZERO_RATIO
: profitLossValue.divide(finalSettlementAmount, 4, RoundingMode.HALF_UP);
ProjectProfitRespVO respVO = new ProjectProfitRespVO();
respVO.setProjectId(project.getId());
respVO.setProjectName(project.getProjectName());
respVO.setContractSignedFlag(project.getContractSignedFlag());
respVO.setContractAmount(amount(project.getContractAmount()));
respVO.setFinalSettlementAmount(finalSettlementAmount);
respVO.setComprehensivePlanningAmount(comprehensivePlanningAmount.setScale(2, RoundingMode.HALF_UP));
respVO.setSubcontractPlanningAmount(subcontractPlanningAmount.setScale(2, RoundingMode.HALF_UP));
respVO.setMajorOutputValue(majorOutputValue.setScale(2, RoundingMode.HALF_UP));
respVO.setExpectedKValue(expectedKValue);
respVO.setMajorExpectedPerformance(majorExpectedPerformance);
respVO.setProfitLossValue(profitLossValue);
respVO.setProfitLossRate(profitLossRate);
respVO.setProjectStartYear(project.getProjectStartYear());
respVO.setCreateTime(project.getCreateTime());
return respVO;
}
private ProjectDO validateProjectExists(Long projectId) {
ProjectDO project = projectMapper.selectById(projectId);
if (project == null) {
throw exception(PROJECT_NOT_EXISTS);
}
return project;
}
private BigDecimal amount(BigDecimal value) {
return value == null ? ZERO_AMOUNT : value.setScale(2, RoundingMode.HALF_UP);
}
private BigDecimal ratio(BigDecimal value) {
return value == null ? ZERO_RATIO : value.setScale(4, RoundingMode.HALF_UP);
}
}

View File

@@ -0,0 +1,29 @@
package cn.iocoder.lyzsys.module.tjt.service.project;
import cn.iocoder.lyzsys.framework.common.pojo.PageResult;
import cn.iocoder.lyzsys.module.tjt.controller.admin.project.vo.ProjectPageReqVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.project.vo.ProjectSaveReqVO;
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.project.ProjectDO;
import java.util.List;
/**
* 项目 Service 接口
*
* @author Codex
*/
public interface ProjectService {
Long createProject(ProjectSaveReqVO createReqVO);
void updateProject(ProjectSaveReqVO updateReqVO);
void deleteProject(Long id);
void deleteProjectList(List<Long> ids);
PageResult<ProjectDO> getProjectPage(ProjectPageReqVO pageReqVO);
ProjectDO getProject(Long id);
}

View File

@@ -0,0 +1,72 @@
package cn.iocoder.lyzsys.module.tjt.service.project;
import cn.iocoder.lyzsys.framework.common.pojo.PageResult;
import cn.iocoder.lyzsys.framework.common.util.object.BeanUtils;
import cn.iocoder.lyzsys.module.tjt.controller.admin.project.vo.ProjectPageReqVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.project.vo.ProjectSaveReqVO;
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.project.ProjectDO;
import cn.iocoder.lyzsys.module.tjt.dal.mysql.project.ProjectMapper;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.List;
import static cn.iocoder.lyzsys.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_NOT_EXISTS;
/**
* 项目 Service 实现类
*
* @author Codex
*/
@Service
@Validated
public class ProjectServiceImpl implements ProjectService {
@Resource
private ProjectMapper projectMapper;
@Override
public Long createProject(ProjectSaveReqVO createReqVO) {
ProjectDO project = BeanUtils.toBean(createReqVO, ProjectDO.class);
projectMapper.insert(project);
return project.getId();
}
@Override
public void updateProject(ProjectSaveReqVO updateReqVO) {
validateProjectExists(updateReqVO.getId());
ProjectDO updateObj = BeanUtils.toBean(updateReqVO, ProjectDO.class);
projectMapper.updateById(updateObj);
}
@Override
public void deleteProject(Long id) {
validateProjectExists(id);
projectMapper.deleteById(id);
}
@Override
public void deleteProjectList(List<Long> ids) {
ids.forEach(this::validateProjectExists);
projectMapper.deleteBatchIds(ids);
}
@Override
public PageResult<ProjectDO> getProjectPage(ProjectPageReqVO pageReqVO) {
return projectMapper.selectPage(pageReqVO);
}
@Override
public ProjectDO getProject(Long id) {
return projectMapper.selectById(id);
}
private void validateProjectExists(Long id) {
if (projectMapper.selectById(id) == null) {
throw exception(PROJECT_NOT_EXISTS);
}
}
}

View File

@@ -0,0 +1,23 @@
package cn.iocoder.lyzsys.module.tjt.service.specialtyrolesplit;
import cn.iocoder.lyzsys.module.tjt.controller.admin.specialtyrolesplit.vo.SpecialtyRoleSplitBatchSaveReqVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.specialtyrolesplit.vo.SpecialtyRoleSplitRespVO;
import java.util.List;
/**
* 页面5角色比例 Service 接口
*
* @author Codex
*/
public interface SpecialtyRoleSplitService {
List<SpecialtyRoleSplitRespVO> getSpecialtyRoleSplitListByPlanningId(Long planningId);
void saveSpecialtyRoleSplitBatch(SpecialtyRoleSplitBatchSaveReqVO reqVO);
void deleteByOutputSplitId(Long outputSplitId);
void deleteByOutputSplitIds(List<Long> outputSplitIds);
}

View File

@@ -0,0 +1,402 @@
package cn.iocoder.lyzsys.module.tjt.service.specialtyrolesplit;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.lyzsys.framework.common.util.json.JsonUtils;
import cn.iocoder.lyzsys.framework.common.util.object.BeanUtils;
import cn.iocoder.lyzsys.module.tjt.controller.admin.specialtyrolesplit.vo.SpecialtyRolePersonRespVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.specialtyrolesplit.vo.SpecialtyRolePersonSaveReqVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.specialtyrolesplit.vo.SpecialtyRoleSplitBatchSaveReqVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.specialtyrolesplit.vo.SpecialtyRoleSplitRespVO;
import cn.iocoder.lyzsys.module.tjt.controller.admin.specialtyrolesplit.vo.SpecialtyRoleSplitSaveItemReqVO;
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.project.ProjectDO;
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.specialtyrolesplit.SpecialtyRoleSplitDO;
import cn.iocoder.lyzsys.module.tjt.dal.mysql.planning.ProjectPlanningMapper;
import cn.iocoder.lyzsys.module.tjt.dal.mysql.project.ProjectMapper;
import cn.iocoder.lyzsys.module.tjt.dal.mysql.specialtyrolesplit.SpecialtyRoleSplitMapper;
import cn.iocoder.lyzsys.module.tjt.enums.OutputSplitBizConstants;
import cn.iocoder.lyzsys.module.tjt.service.outputsplit.ProjectOutputSplitService;
import com.fasterxml.jackson.core.type.TypeReference;
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.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
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_NOT_EXISTS;
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_OUTPUT_SPLIT_PLANNING_NOT_EXISTS;
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.SPECIALTY_ROLE_SPLIT_DESIGN_PERSON_REQUIRED;
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.SPECIALTY_ROLE_SPLIT_PERSON_INVALID;
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.SPECIALTY_ROLE_SPLIT_PERSON_RATIO_INVALID;
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.SPECIALTY_ROLE_SPLIT_ROLE_INVALID;
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.SPECIALTY_ROLE_SPLIT_ROLE_RATIO_INVALID;
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.SPECIALTY_ROLE_SPLIT_SPECIALTY_INVALID;
/**
* 页面5角色比例 Service 实现类
*
* @author Codex
*/
@Service
@Validated
public class SpecialtyRoleSplitServiceImpl implements SpecialtyRoleSplitService {
private static final int RATIO_SCALE = 4;
private static final int AMOUNT_SCALE = 2;
private static final TypeReference<List<SpecialtyRolePersonSaveReqVO>> PERSON_LIST_TYPE =
new TypeReference<List<SpecialtyRolePersonSaveReqVO>>() {};
private static final BigDecimal ZERO_RATIO = BigDecimal.ZERO.setScale(RATIO_SCALE, RoundingMode.HALF_UP);
private static final BigDecimal ONE_RATIO = BigDecimal.ONE.setScale(RATIO_SCALE, RoundingMode.HALF_UP);
private static final BigDecimal ZERO_AMOUNT = BigDecimal.ZERO.setScale(AMOUNT_SCALE, RoundingMode.HALF_UP);
@Resource
private SpecialtyRoleSplitMapper specialtyRoleSplitMapper;
@Resource
private ProjectOutputSplitService projectOutputSplitService;
@Resource
private ProjectPlanningMapper projectPlanningMapper;
@Resource
private ProjectMapper projectMapper;
@Override
public List<SpecialtyRoleSplitRespVO> getSpecialtyRoleSplitListByPlanningId(Long planningId) {
ProjectPlanningDO planning = validatePlanningExists(planningId);
ProjectDO project = validateProjectExists(planning.getProjectId());
ProjectOutputSplitDO outputSplit = projectOutputSplitService.getOrCreateProjectOutputSplit(planningId);
List<SpecialtyRoleSplitDO> dbList = specialtyRoleSplitMapper.selectListByOutputSplitId(outputSplit.getId());
Map<String, SpecialtyRoleSplitDO> dbMap = dbList.stream().collect(Collectors.toMap(
item -> item.getSpecialtyCode() + ":" + item.getRoleCode(), item -> item, (a, b) -> b));
List<SpecialtyRoleSplitRespVO> result = new ArrayList<>();
BigDecimal assessmentOutputValue = planning.getAssessmentOutputValue();
for (OutputSplitBizConstants.SpecialtyItem specialtyItem : OutputSplitBizConstants.SPECIALTY_ITEMS) {
BigDecimal specialtyAmount = projectOutputSplitService.getSpecialtyAmount(
outputSplit, assessmentOutputValue, specialtyItem.getCode());
for (OutputSplitBizConstants.RoleItem roleItem : OutputSplitBizConstants.ROLE_ITEMS) {
SpecialtyRoleSplitDO dbItem = dbMap.get(specialtyItem.getCode() + ":" + roleItem.getCode());
BigDecimal roleRatio = getStoredRoleRatio(dbItem, roleItem.getCode());
BigDecimal roleAmount = multiplyAmount(specialtyAmount, roleRatio);
List<SpecialtyRolePersonRespVO> persons = buildRespPersons(dbItem, roleAmount, roleRatio);
SpecialtyRoleSplitRespVO respVO = dbItem == null
? new SpecialtyRoleSplitRespVO()
: BeanUtils.toBean(dbItem, SpecialtyRoleSplitRespVO.class);
if (respVO.getId() == null) {
respVO.setOutputSplitId(outputSplit.getId());
respVO.setSortNo(roleItem.getSortNo());
}
respVO.setPlanningId(planning.getId());
respVO.setProjectName(project.getProjectName());
respVO.setPlanningContent(planning.getPlanningContent());
respVO.setSpecialtyCode(specialtyItem.getCode());
respVO.setSpecialtyName(specialtyItem.getName());
respVO.setSpecialtyAmount(specialtyAmount);
respVO.setRoleCode(roleItem.getCode());
respVO.setRoleName(roleItem.getName());
respVO.setRoleRatio(roleRatio);
respVO.setRoleAmount(roleAmount);
respVO.setPersons(persons);
result.add(respVO);
}
}
return result;
}
@Override
public void saveSpecialtyRoleSplitBatch(SpecialtyRoleSplitBatchSaveReqVO reqVO) {
ProjectPlanningDO planning = validatePlanningExists(reqVO.getPlanningId());
ProjectOutputSplitDO outputSplit = projectOutputSplitService.getOrCreateProjectOutputSplit(reqVO.getPlanningId());
List<SpecialtyRoleSplitDO> dbList = specialtyRoleSplitMapper.selectListByOutputSplitId(outputSplit.getId());
Map<String, SpecialtyRoleSplitDO> dbMap = dbList.stream().collect(Collectors.toMap(
item -> item.getSpecialtyCode() + ":" + item.getRoleCode(), item -> item, (a, b) -> b));
Map<String, Map<String, SpecialtyRoleSplitSaveItemReqVO>> groupedMap = buildGroupedInput(reqVO.getItems());
BigDecimal assessmentOutputValue = planning.getAssessmentOutputValue();
for (OutputSplitBizConstants.SpecialtyItem specialtyItem : OutputSplitBizConstants.SPECIALTY_ITEMS) {
Map<String, SpecialtyRoleSplitSaveItemReqVO> roleMap = groupedMap.getOrDefault(
specialtyItem.getCode(), new LinkedHashMap<>());
Map<String, BigDecimal> ratioMap = new LinkedHashMap<>();
Map<String, List<SpecialtyRolePersonSaveReqVO>> personMap = new LinkedHashMap<>();
BigDecimal roleTotal = ZERO_RATIO;
BigDecimal specialtyAmount = projectOutputSplitService.getSpecialtyAmount(
outputSplit, assessmentOutputValue, specialtyItem.getCode());
for (OutputSplitBizConstants.RoleItem roleItem : OutputSplitBizConstants.ROLE_ITEMS) {
BigDecimal roleRatio = getSaveRoleRatio(roleMap, roleItem.getCode());
BigDecimal roleAmount = multiplyAmount(specialtyAmount, roleRatio);
List<SpecialtyRolePersonSaveReqVO> persons = normalizePersons(
getSavePersons(roleMap, roleItem.getCode()));
validateRolePersons(roleItem.getCode(), roleAmount, persons);
ratioMap.put(roleItem.getCode(), roleRatio);
personMap.put(roleItem.getCode(), persons);
roleTotal = roleTotal.add(roleRatio).setScale(RATIO_SCALE, RoundingMode.HALF_UP);
}
validateRoleTotal(roleTotal);
for (OutputSplitBizConstants.RoleItem roleItem : OutputSplitBizConstants.ROLE_ITEMS) {
upsertRole(dbMap, outputSplit.getId(), specialtyItem.getCode(), specialtyItem.getName(),
roleItem.getCode(), ratioMap.get(roleItem.getCode()),
toPersonJson(personMap.get(roleItem.getCode())));
}
}
}
@Override
public void deleteByOutputSplitId(Long outputSplitId) {
specialtyRoleSplitMapper.delete(SpecialtyRoleSplitDO::getOutputSplitId, outputSplitId);
}
@Override
public void deleteByOutputSplitIds(List<Long> outputSplitIds) {
if (outputSplitIds == null || outputSplitIds.isEmpty()) {
return;
}
specialtyRoleSplitMapper.delete(new cn.iocoder.lyzsys.framework.mybatis.core.query.LambdaQueryWrapperX<SpecialtyRoleSplitDO>()
.in(SpecialtyRoleSplitDO::getOutputSplitId, outputSplitIds));
}
private Map<String, Map<String, SpecialtyRoleSplitSaveItemReqVO>> buildGroupedInput(List<SpecialtyRoleSplitSaveItemReqVO> items) {
Map<String, Map<String, SpecialtyRoleSplitSaveItemReqVO>> result = new LinkedHashMap<>();
for (SpecialtyRoleSplitSaveItemReqVO item : items) {
if (!OutputSplitBizConstants.isValidSpecialtyCode(item.getSpecialtyCode())) {
throw exception(SPECIALTY_ROLE_SPLIT_SPECIALTY_INVALID);
}
if (!OutputSplitBizConstants.isValidRoleCode(item.getRoleCode())) {
throw exception(SPECIALTY_ROLE_SPLIT_ROLE_INVALID);
}
result.computeIfAbsent(item.getSpecialtyCode(), key -> new LinkedHashMap<>())
.put(item.getRoleCode(), item);
}
return result;
}
private void upsertRole(Map<String, SpecialtyRoleSplitDO> dbMap, Long outputSplitId, String specialtyCode,
String specialtyName, String roleCode, BigDecimal roleRatio, String personNames) {
String key = specialtyCode + ":" + roleCode;
SpecialtyRoleSplitDO dbItem = dbMap.get(key);
Integer sortNo = dbItem != null && dbItem.getSortNo() != null
? dbItem.getSortNo()
: OutputSplitBizConstants.getSpecialtySortNo(specialtyCode) * 10
+ OutputSplitBizConstants.getRoleSortNo(roleCode);
if (dbItem == null) {
SpecialtyRoleSplitDO createObj = new SpecialtyRoleSplitDO();
createObj.setOutputSplitId(outputSplitId);
createObj.setSpecialtyCode(specialtyCode);
createObj.setSpecialtyName(specialtyName);
createObj.setRoleCode(roleCode);
createObj.setRoleName(OutputSplitBizConstants.getRoleName(roleCode));
createObj.setRoleRatio(ratio(roleRatio));
createObj.setPersonNames(personNames);
createObj.setSortNo(sortNo);
specialtyRoleSplitMapper.insert(createObj);
return;
}
SpecialtyRoleSplitDO updateObj = new SpecialtyRoleSplitDO();
updateObj.setId(dbItem.getId());
updateObj.setOutputSplitId(outputSplitId);
updateObj.setSpecialtyCode(specialtyCode);
updateObj.setSpecialtyName(specialtyName);
updateObj.setRoleCode(roleCode);
updateObj.setRoleName(OutputSplitBizConstants.getRoleName(roleCode));
updateObj.setRoleRatio(ratio(roleRatio));
updateObj.setPersonNames(personNames);
updateObj.setSortNo(sortNo);
specialtyRoleSplitMapper.updateById(updateObj);
}
private List<SpecialtyRolePersonSaveReqVO> getSavePersons(Map<String, SpecialtyRoleSplitSaveItemReqVO> roleMap,
String roleCode) {
SpecialtyRoleSplitSaveItemReqVO item = roleMap.get(roleCode);
return item == null ? new ArrayList<>() : item.getPersons();
}
private BigDecimal getSaveRoleRatio(Map<String, SpecialtyRoleSplitSaveItemReqVO> roleMap, String roleCode) {
SpecialtyRoleSplitSaveItemReqVO item = roleMap.get(roleCode);
BigDecimal roleRatio = item == null ? ZERO_RATIO : ratio(item.getRoleRatio());
if (roleRatio.compareTo(ZERO_RATIO) < 0 || roleRatio.compareTo(ONE_RATIO) > 0) {
throw exception(SPECIALTY_ROLE_SPLIT_ROLE_RATIO_INVALID);
}
return roleRatio;
}
private List<SpecialtyRolePersonSaveReqVO> normalizePersons(List<SpecialtyRolePersonSaveReqVO> persons) {
List<SpecialtyRolePersonSaveReqVO> result = new ArrayList<>();
if (persons == null || persons.isEmpty()) {
return result;
}
for (SpecialtyRolePersonSaveReqVO person : persons) {
if (person == null) {
continue;
}
String personName = StrUtil.trim(person.getPersonName());
BigDecimal personRatio = person.getPersonRatio();
boolean hasName = StrUtil.isNotBlank(personName);
boolean hasRatio = personRatio != null;
if (!hasName && !hasRatio) {
continue;
}
if (!hasName || !hasRatio) {
throw exception(SPECIALTY_ROLE_SPLIT_PERSON_INVALID);
}
SpecialtyRolePersonSaveReqVO normalized = new SpecialtyRolePersonSaveReqVO();
normalized.setPersonName(personName);
BigDecimal normalizedRatio = ratio(personRatio);
if (normalizedRatio.compareTo(ZERO_RATIO) < 0 || normalizedRatio.compareTo(ONE_RATIO) > 0) {
throw exception(SPECIALTY_ROLE_SPLIT_PERSON_RATIO_INVALID);
}
normalized.setPersonRatio(normalizedRatio);
result.add(normalized);
}
return result;
}
private void validateRolePersons(String roleCode, BigDecimal roleAmount, List<SpecialtyRolePersonSaveReqVO> persons) {
BigDecimal personTotal = sumPersonRatios(persons);
if (personTotal.compareTo(ONE_RATIO) > 0) {
throw exception(SPECIALTY_ROLE_SPLIT_PERSON_RATIO_INVALID);
}
if (OutputSplitBizConstants.ROLE_DESIGN.equals(roleCode)
&& roleAmount.compareTo(ZERO_AMOUNT) > 0
&& persons.isEmpty()) {
throw exception(SPECIALTY_ROLE_SPLIT_DESIGN_PERSON_REQUIRED);
}
}
private void validateRoleTotal(BigDecimal roleTotal) {
if (ratio(roleTotal).compareTo(ONE_RATIO) != 0) {
throw exception(SPECIALTY_ROLE_SPLIT_ROLE_RATIO_INVALID);
}
}
private BigDecimal getStoredRoleRatio(SpecialtyRoleSplitDO dbItem, String roleCode) {
if (dbItem == null) {
return OutputSplitBizConstants.ROLE_DESIGN.equals(roleCode) ? ONE_RATIO : ZERO_RATIO;
}
return ratio(dbItem.getRoleRatio());
}
private List<SpecialtyRolePersonRespVO> buildRespPersons(SpecialtyRoleSplitDO dbItem, BigDecimal roleAmount,
BigDecimal roleRatio) {
List<SpecialtyRolePersonRespVO> result = new ArrayList<>();
for (SpecialtyRolePersonSaveReqVO person : parseStoredPersons(dbItem, roleRatio)) {
SpecialtyRolePersonRespVO respVO = new SpecialtyRolePersonRespVO();
respVO.setPersonName(person.getPersonName());
respVO.setPersonRatio(ratio(person.getPersonRatio()));
respVO.setPersonAmount(multiplyAmount(roleAmount, respVO.getPersonRatio()));
result.add(respVO);
}
return result;
}
private List<SpecialtyRolePersonSaveReqVO> parseStoredPersons(SpecialtyRoleSplitDO dbItem, BigDecimal roleRatio) {
List<SpecialtyRolePersonSaveReqVO> result = new ArrayList<>();
if (dbItem == null) {
return result;
}
String rawPersons = StrUtil.trimToEmpty(dbItem.getPersonNames());
BigDecimal storedRoleRatio = ratio(roleRatio);
if (StrUtil.isNotBlank(rawPersons) && JsonUtils.isJson(rawPersons)) {
List<SpecialtyRolePersonSaveReqVO> jsonPersons = JsonUtils.parseObjectQuietly(rawPersons, PERSON_LIST_TYPE);
if (jsonPersons != null) {
for (SpecialtyRolePersonSaveReqVO jsonPerson : jsonPersons) {
if (jsonPerson == null) {
continue;
}
String personName = StrUtil.trimToEmpty(jsonPerson.getPersonName());
BigDecimal personRatio = jsonPerson.getPersonRatio();
if (StrUtil.isBlank(personName) && personRatio == null) {
continue;
}
SpecialtyRolePersonSaveReqVO person = new SpecialtyRolePersonSaveReqVO();
person.setPersonName(personName);
person.setPersonRatio(ratio(personRatio));
result.add(person);
}
if (!result.isEmpty()) {
if (storedRoleRatio.compareTo(ZERO_RATIO) > 0
&& sumPersonRatios(result).compareTo(storedRoleRatio) == 0) {
return convertLegacyPersons(result, storedRoleRatio);
}
return result;
}
}
}
if (StrUtil.isNotBlank(rawPersons)) {
result.add(buildStoredPerson(rawPersons, storedRoleRatio.compareTo(ZERO_RATIO) > 0 ? ONE_RATIO : ZERO_RATIO));
return result;
}
return result;
}
private List<SpecialtyRolePersonSaveReqVO> convertLegacyPersons(List<SpecialtyRolePersonSaveReqVO> persons,
BigDecimal roleRatio) {
List<SpecialtyRolePersonSaveReqVO> result = new ArrayList<>();
if (roleRatio.compareTo(ZERO_RATIO) <= 0) {
return persons;
}
for (SpecialtyRolePersonSaveReqVO person : persons) {
SpecialtyRolePersonSaveReqVO converted = new SpecialtyRolePersonSaveReqVO();
converted.setPersonName(person.getPersonName());
converted.setPersonRatio(ratio(person.getPersonRatio().divide(roleRatio, RATIO_SCALE, RoundingMode.HALF_UP)));
result.add(converted);
}
return result;
}
private SpecialtyRolePersonSaveReqVO buildStoredPerson(String personName, BigDecimal personRatio) {
SpecialtyRolePersonSaveReqVO person = new SpecialtyRolePersonSaveReqVO();
person.setPersonName(personName);
person.setPersonRatio(ratio(personRatio));
return person;
}
private String toPersonJson(List<SpecialtyRolePersonSaveReqVO> persons) {
return persons == null || persons.isEmpty() ? null : JsonUtils.toJsonString(persons);
}
private BigDecimal sumPersonRatios(List<SpecialtyRolePersonSaveReqVO> persons) {
BigDecimal sum = ZERO_RATIO;
if (persons == null || persons.isEmpty()) {
return sum;
}
for (SpecialtyRolePersonSaveReqVO person : persons) {
if (person == null) {
continue;
}
sum = sum.add(ratio(person.getPersonRatio())).setScale(RATIO_SCALE, RoundingMode.HALF_UP);
}
return sum;
}
private ProjectPlanningDO validatePlanningExists(Long planningId) {
ProjectPlanningDO planning = projectPlanningMapper.selectById(planningId);
if (planning == null) {
throw exception(PROJECT_OUTPUT_SPLIT_PLANNING_NOT_EXISTS);
}
return planning;
}
private ProjectDO validateProjectExists(Long projectId) {
ProjectDO project = projectMapper.selectById(projectId);
if (project == null) {
throw exception(PROJECT_NOT_EXISTS);
}
return project;
}
private BigDecimal multiplyAmount(BigDecimal left, BigDecimal right) {
return amount(left).multiply(ratio(right)).setScale(AMOUNT_SCALE, RoundingMode.HALF_UP);
}
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);
}
}