进度计划甘特图

This commit is contained in:
lcj
2025-10-14 17:21:37 +08:00
parent 87e58ca4af
commit 0fb3fd70a6
12 changed files with 594 additions and 56 deletions

View File

@ -326,7 +326,7 @@ ys7:
app-key: 3acf9f1a43dc4209841e0893003db0a2
app-secret: 4bbf3e9394f55d3af6e3af27b2d3db36
job:
capture-enabled: true # 控制是否启用萤石抓拍任务
capture-enabled: false # 控制是否启用萤石抓拍任务
# 斯巴达算法
sparta:
url: http://119.3.204.120:8040

View File

@ -1,15 +1,13 @@
package org.dromara.mobileAttendanceMachine;
import lombok.extern.log4j.Log4j2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
/**
* @Author 铁憨憨
* @Date 2025/10/14 15:48
* @Version 1.0
*
* <p>
* 考勤设备消息发送工具类对应Golang的ws包工具方法
*/
@ -18,10 +16,11 @@ import org.springframework.stereotype.Service;
public class DeviceMessageSender {
/**
* 组装下发人员信息对应Golang的TheSenderInformationOfTheAssemblyPersonnel
* @param sn 设备编号
*
* @param sn 设备编号
* @param userId 用户ID
* @param name 用户名
* @param face 人脸模板地址HTTP链接
* @param name 用户名
* @param face 人脸模板地址HTTP链接
* @return 异常信息无异常则返回null
*/
public static Exception sendPersonnelInformation(String sn, String userId, String name, String face) {
@ -55,6 +54,7 @@ public class DeviceMessageSender {
/**
* 获取打卡设备所有人员对应Golang的SelectUserAll
*
* @param sn 设备编号
* @return 响应结果(包含人员信息)
* @throws Exception 发送或接收过程中的异常
@ -81,7 +81,8 @@ public class DeviceMessageSender {
/**
* 删除指定人员对应Golang的DelByUserId
* @param sn 设备编号
*
* @param sn 设备编号
* @param userId 要删除的用户ID
* @return 响应结果
* @throws Exception 发送或接收过程中的异常
@ -109,7 +110,8 @@ public class DeviceMessageSender {
/**
* 批量删除指定人员对应Golang的BatchDelete
* @param sn 设备编号
*
* @param sn 设备编号
* @param userIds 要删除的用户ID列表
* @return 响应结果
* @throws Exception 发送或接收过程中的异常
@ -137,6 +139,7 @@ public class DeviceMessageSender {
/**
* 删除指定考勤机全部人员对应Golang的DelAll
*
* @param sn 设备编号
* @return 响应结果
* @throws Exception 发送或接收过程中的异常

View File

@ -2,8 +2,6 @@ package org.dromara.mobileAttendanceMachine;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.log4j.Log4j2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;

View File

@ -3,7 +3,6 @@ package org.dromara.mobileAttendanceMachine;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.net.http.WebSocket;
@ -15,7 +14,6 @@ import java.net.http.WebSocket;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class KqjEntity {
@Data

View File

@ -90,6 +90,16 @@ public class PgsProgressCategoryController extends BaseController {
return R.ok(list);
}
/**
* 查询进度类别的甘特图结构列表
*/
@SaCheckPermission("progress:progressCategory:query")
@GetMapping("/list/gantt/{progressCategoryId}")
public R<List<PgsProgressCategoryGanttSubProjectVo>> listGanttStructure(@NotNull(message = "类别主键不能为空")
@PathVariable Long progressCategoryId) {
return R.ok(pgsProgressCategoryService.getGanttStructureList(progressCategoryId));
}
/**
* 导出进度类别列表
*/
@ -353,7 +363,7 @@ public class PgsProgressCategoryController extends BaseController {
@SaCheckPermission("progress:progressCategory:query")
@GetMapping("/gantt/{projectId}")
public R<List<PgsProgressCategoryGanttVo>> listGantt(@NotNull(message = "主键不能为空")
@PathVariable Long projectId) {
@PathVariable Long projectId) {
return R.ok(pgsProgressCategoryService.listGanttByProject(projectId));
}

View File

@ -0,0 +1,37 @@
package org.dromara.progress.domain.dto.progresscategory;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* @author lilemy
* @date 2025-10-13 17:50
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PgsProgressCategoryProgressDto {
/**
* 已完成百分比
*/
private BigDecimal finishProgress;
/**
* 已计划百分比
*/
private BigDecimal planProgress;
/**
* 未计划百分比
*/
private BigDecimal unPlanProgress;
/**
* 延期百分比
*/
private BigDecimal delayProgress;
}

View File

@ -0,0 +1,37 @@
package org.dromara.progress.domain.vo.progresscategory;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lilemy
* @date 2025-10-14 16:24
*/
@Data
public class PgsProgressCategoryGanttMatrixVo implements Serializable {
@Serial
private static final long serialVersionUID = 2999870755203852970L;
/**
* 主键id
*/
private Long matrixId;
/**
* 名称
*/
private String matrixName;
/**
* 关联类别id
*/
private Long progressCategoryId;
/**
* 关联类别名称
*/
private String progressCategoryName;
}

View File

@ -0,0 +1,51 @@
package org.dromara.progress.domain.vo.progresscategory;
import lombok.Data;
import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* @author lilemy
* @date 2025-10-14 16:22
*/
@Data
public class PgsProgressCategoryGanttSubProjectVo implements Serializable {
@Serial
private static final long serialVersionUID = -8517387026258889743L;
/**
* 关联结构(1子项目 2方阵 3项目)
*/
private String relevancyStructure;
/**
* 主键id
*/
private Long subProjectId;
/**
* 名称
*/
@Translation(type = TransConstant.PROJECT_ID_TO_NAME,mapper = "subProjectId")
private String subProjectName;
/**
* 关联类别id
*/
private Long progressCategoryId;
/**
* 关联类别名称
*/
private String progressCategoryName;
/**
* 关联方阵结构列表
*/
private List<PgsProgressCategoryGanttMatrixVo> children;
}

View File

@ -43,7 +43,37 @@ public class PgsProgressCategoryGanttVo implements Serializable {
private LocalDate endDate;
/**
* 进度
* 已完成百分比
*/
private BigDecimal progress;
private BigDecimal finishProgress;
/**
* 已计划百分比
*/
private BigDecimal planProgress;
/**
* 未计划百分比
*/
private BigDecimal unPlanProgress;
/**
* 延期百分比
*/
private BigDecimal delayProgress;
/**
* 关联结构(1子项目 2方阵 3项目)
*/
private String relevancyStructure;
/**
* 完成状态0未开始 1进行中 2已完成
*/
private String status;
/**
* 排序
*/
private Long sort;
}

View File

@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.facility.domain.FacMatrix;
import org.dromara.progress.domain.PgsProgressCategory;
import org.dromara.progress.domain.PgsProgressPlan;
import org.dromara.progress.domain.dto.progresscategory.*;
import org.dromara.progress.domain.vo.progresscategory.*;
@ -198,6 +199,14 @@ public interface IPgsProgressCategoryService extends IService<PgsProgressCategor
*/
BigDecimal getCompletedPercentage(List<PgsProgressCategory> categoryList);
/**
* 获取项目进度百分比
*
* @param categoryList 项目进度列表
* @return 项目进度百分比
*/
PgsProgressCategoryProgressDto getProgressPercentage(List<PgsProgressCategory> categoryList, List<PgsProgressPlan> planList);
/**
* 获取项目进度类别未完成数量
*
@ -229,4 +238,12 @@ public interface IPgsProgressCategoryService extends IService<PgsProgressCategor
* @return 进度类别甘特图结构
*/
List<PgsProgressCategoryGanttVo> listGanttByProject(Long projectId);
/**
* 根据进度类别,获取进度类别甘特图结构
*
* @param progressCategoryId 进度类别id
* @return 进度类别甘特图结构
*/
List<PgsProgressCategoryGanttSubProjectVo> getGanttStructureList(Long progressCategoryId);
}

View File

@ -1417,7 +1417,7 @@ public class PgsProgressCategoryServiceImpl extends ServiceImpl<PgsProgressCateg
List<PgsProgressCategory> allChildren = allCategory.stream()
.filter(item -> {
String ancestors = item.getAncestors();
return ancestors != null && ancestors.contains("," + topId + ",");
return ancestors != null && (ancestors.contains("," + topId + ",") || ancestors.contains("," + topId));
})
.toList();
@ -1512,6 +1512,84 @@ public class PgsProgressCategoryServiceImpl extends ServiceImpl<PgsProgressCateg
return BigDecimalUtil.toPercentage(totalCompleted, totalWork);
}
/**
* 获取项目进度百分比
*
* @param categoryList 项目进度列表
* @return 项目进度百分比
*/
@Override
public PgsProgressCategoryProgressDto getProgressPercentage(List<PgsProgressCategory> categoryList,
List<PgsProgressPlan> planList) {
PgsProgressCategoryProgressDto dto = new PgsProgressCategoryProgressDto();
// 如果没有数据则返回0
if (CollUtil.isEmpty(categoryList)) {
return dto;
}
// 总完成数量
BigDecimal totalCompleted = BigDecimal.ZERO;
// 总计划数量
BigDecimal totalPlan = BigDecimal.ZERO;
// 总延期数量
BigDecimal totalDelay = BigDecimal.ZERO;
// 总数量
BigDecimal totalWork = BigDecimal.ZERO;
// 遍历所有项目进度,计算总完成数和总数
for (PgsProgressCategory category : categoryList) {
BigDecimal completed = category.getCompleted();
BigDecimal total = category.getTotal();
if (PgsProgressUnitTypeEnum.PERCENTAGE.getValue().equals(category.getUnitType())) {
completed = completed.divide(BigDecimal.valueOf(100L), 4, RoundingMode.HALF_UP).multiply(total);
}
totalCompleted = totalCompleted.add(completed);
totalWork = totalWork.add(total);
// 如果当前类型已完成,则直接返回
if (completed.compareTo(total) == 0) {
continue;
}
// 获取当前类别的计划数量
BigDecimal plan = planList.stream()
.filter(item -> item.getProgressCategoryId().equals(category.getId()))
// 1. 获取延期数量为0的计划
.filter(item -> item.getDelayNumber().compareTo(BigDecimal.ZERO) == 0)
// 2. 过滤掉已完成数量 >= 计划数量的计划
.filter(item -> item.getFinishedNumber().compareTo(item.getPlanNumber()) < 0)
// 3. 对每个计划取「计划数量 - 完成数量」
.map(item -> {
BigDecimal planNumber = item.getPlanNumber();
if (PgsProgressUnitTypeEnum.PERCENTAGE.getValue().equals(category.getUnitType())) {
planNumber = planNumber.divide(BigDecimal.valueOf(100L), 4, RoundingMode.HALF_UP).multiply(total);
}
return planNumber.subtract(item.getFinishedNumber());
})
// 4. 累加
.reduce(BigDecimal.ZERO, BigDecimal::add);
totalPlan = totalPlan.add(plan);
// 获取当前类别的延期数量
BigDecimal delay = planList.stream()
.filter(item -> item.getProgressCategoryId().equals(category.getId()))
.filter(item -> item.getDelayNumber().compareTo(BigDecimal.ZERO) > 0)
.map(item -> {
BigDecimal delayNum = item.getDelayNumber();
if (PgsProgressUnitTypeEnum.PERCENTAGE.getValue().equals(category.getUnitType())) {
delayNum = delayNum.divide(BigDecimal.valueOf(100L), 4, RoundingMode.HALF_UP).multiply(total);
}
return delayNum;
})
.reduce(BigDecimal.ZERO, BigDecimal::add);
totalDelay = totalDelay.add(delay);
}
dto.setFinishProgress(BigDecimalUtil.toPercentage(totalCompleted, totalWork));
dto.setPlanProgress(BigDecimalUtil.toPercentage(totalPlan, totalWork));
// 计算延期量 -> 历史延迟量 - 未完成计划数量
totalDelay = totalDelay.subtract(totalPlan).max(BigDecimal.ZERO);
dto.setDelayProgress(BigDecimalUtil.toPercentage(totalDelay, totalWork));
// 计算未计划量
BigDecimal totalUnPlan = totalWork.subtract(totalCompleted).subtract(totalPlan).subtract(totalDelay);
dto.setUnPlanProgress(BigDecimalUtil.toPercentage(totalUnPlan, totalWork));
return dto;
}
/**
* 获取项目进度类别未完成数量
*
@ -1886,43 +1964,318 @@ public class PgsProgressCategoryServiceImpl extends ServiceImpl<PgsProgressCateg
// 获取当前项目所有进度类别
List<PgsProgressCategory> progressCategoryList = this.lambdaQuery()
.in(PgsProgressCategory::getProjectId, projectIds)
.eq(PgsProgressCategory::getMatrixId, 0)
.list();
if (CollUtil.isEmpty(progressCategoryList)) {
return ganttList;
}
// 封装进度类别数据
List<PgsProgressCategoryGanttVo> list = progressCategoryList.stream().map(p -> {
// 获取所有子节点
List<PgsProgressCategory> children = this.getLeafNodesByTopId(p.getId(), progressCategoryList);
PgsProgressCategoryGanttVo vo = new PgsProgressCategoryGanttVo();
vo.setId(p.getId());
vo.setParentId(p.getParentId());
vo.setText(p.getName());
vo.setStartDate(null);
vo.setEndDate(null);
vo.setProgress(this.getCompletedPercentage(children)
.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));
return vo;
}).toList();
ganttList.addAll(list);
// 获取当前项目所有计划
List<PgsProgressPlan> planList = progressPlanService.lambdaQuery()
.eq(PgsProgressPlan::getProjectId, projectId)
.in(PgsProgressPlan::getProjectId, projectIds)
.list();
List<PgsProgressCategoryGanttVo> list1 = planList.stream().map(p -> {
PgsProgressCategoryGanttVo vo = new PgsProgressCategoryGanttVo();
vo.setId(p.getId());
vo.setParentId(p.getProgressCategoryId());
vo.setText(p.getProgressCategoryName() + "-" + "计划");
vo.setStartDate(p.getStartDate());
vo.setEndDate(p.getEndDate());
vo.setProgress(BigDecimalUtil.toPercentage(p.getFinishedNumber(), p.getPlanNumber())
.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));
return vo;
}).toList();
ganttList.addAll(list1);
// 获取关联项目的类别和计划
List<PgsProgressCategory> projectCategoryList = progressCategoryList.stream()
.filter(p -> p.getRelevancyStructure().equals(PgsRelevancyStructureEnum.PROJECT.getValue()))
.toList();
if (CollUtil.isNotEmpty(projectCategoryList)) {
// 封装进度类别数据
List<PgsProgressCategoryGanttVo> list = projectCategoryList.stream().map(p -> {
// 获取所有子节点
List<PgsProgressCategory> children = this.getLeafNodesByTopId(p.getId(), progressCategoryList);
PgsProgressCategoryGanttVo vo = new PgsProgressCategoryGanttVo();
vo.setId(p.getId());
vo.setParentId(p.getParentId());
vo.setText(p.getName());
PgsProgressCategoryGanttVo ganttVo = this.getCategoryStartDateAndEndDate(p.getId(), progressCategoryList, planList);
vo.setStartDate(ganttVo.getStartDate());
vo.setEndDate(ganttVo.getEndDate());
if (CollUtil.isEmpty(children)) {
children = List.of(p);
}
// 获取当前子节点的所有计划
List<PgsProgressCategory> finalChildren = children;
List<PgsProgressPlan> childrenPlanList = planList.stream()
.filter(plan -> finalChildren.stream().anyMatch(category -> category.getId().equals(plan.getProgressCategoryId())))
.toList();
PgsProgressCategoryProgressDto percentage = this.getProgressPercentage(finalChildren, childrenPlanList);
vo.setFinishProgress(percentage.getFinishProgress());
vo.setPlanProgress(percentage.getPlanProgress());
vo.setUnPlanProgress(percentage.getUnPlanProgress());
vo.setDelayProgress(percentage.getDelayProgress());
vo.setRelevancyStructure(PgsRelevancyStructureEnum.PROJECT.getValue());
vo.setStatus(CollUtil.isNotEmpty(children) ? this.getParentStatus(children) : p.getStatus());
vo.setSort(p.getSort());
return vo;
}).toList();
ganttList.addAll(list);
}
// 获取关联子项目的类别和计划
List<PgsProgressCategory> subProjectCategoryList = progressCategoryList.stream()
.filter(p -> p.getRelevancyStructure().equals(PgsRelevancyStructureEnum.SUB_PROJECT.getValue()))
.toList();
if (CollUtil.isNotEmpty(subProjectCategoryList)) {
// 获取顶层的类别
List<PgsProgressCategory> topCategoryList = subProjectCategoryList.stream()
.filter(p -> p.getParentId().equals(0L))
.toList();
Map<String, List<PgsProgressCategory>> topMap = topCategoryList.stream()
.collect(Collectors.groupingBy(PgsProgressCategory::getName));
// 封装进度类别数据
List<PgsProgressCategoryGanttVo> list = new ArrayList<>();
for (Map.Entry<String, List<PgsProgressCategory>> entry : topMap.entrySet()) {
List<PgsProgressCategory> value = entry.getValue();
// 递归封装本身及子节点数据
this.getCategoryGanttAndChildrenVo(0L, value, list, progressCategoryList, planList);
}
ganttList.addAll(list);
}
// 获取关联方阵项目的类别和计划
List<PgsProgressCategory> matrixCategoryList = progressCategoryList.stream()
.filter(p -> p.getRelevancyStructure().equals(PgsRelevancyStructureEnum.MATRIX.getValue()))
.toList();
if (CollUtil.isNotEmpty(matrixCategoryList)) {
// 获取顶层的类别
List<PgsProgressCategory> topCategoryList = matrixCategoryList.stream()
.filter(p -> p.getParentId().equals(0L))
.toList();
Map<String, List<PgsProgressCategory>> topMap = topCategoryList.stream()
.collect(Collectors.groupingBy(PgsProgressCategory::getName));
// 封装进度类别数据
List<PgsProgressCategoryGanttVo> list = new ArrayList<>();
for (Map.Entry<String, List<PgsProgressCategory>> entry : topMap.entrySet()) {
List<PgsProgressCategory> value = entry.getValue();
// 递归封装本身及子节点数据
this.getCategoryGanttAndChildrenVo(0L, value, list, progressCategoryList, planList);
}
ganttList.addAll(list);
}
// 按 sort 字段升序排序
ganttList.sort(Comparator.comparing(PgsProgressCategoryGanttVo::getSort));
return ganttList;
}
/**
* 根据进度类别,获取进度类别甘特图结构
*
* @param progressCategoryId 进度类别id
* @return 进度类别甘特图结构
*/
@Override
public List<PgsProgressCategoryGanttSubProjectVo> getGanttStructureList(Long progressCategoryId) {
// 获取类别信息
PgsProgressCategory category = this.getById(progressCategoryId);
if (category == null) {
throw new ServiceException("进度类别不存在", HttpStatus.NOT_FOUND);
}
// 获取项目信息
BusProject project = projectService.getById(category.getProjectId());
// 整合所有项目id
List<BusProject> projects = projectService.lambdaQuery()
.eq(BusProject::getPId, project.getPId())
.list();
List<Long> projectIds = projects.stream().map(BusProject::getId).toList();
String name = category.getName();
String relevancyStructure = category.getRelevancyStructure();
// 获取对应的进度类别列表
List<PgsProgressCategory> progressCategoryList = this.lambdaQuery()
.in(PgsProgressCategory::getProjectId, projectIds)
.eq(PgsProgressCategory::getName, name)
.eq(PgsProgressCategory::getRelevancyStructure, relevancyStructure)
.list();
if (PgsRelevancyStructureEnum.SUB_PROJECT.getValue().equals(relevancyStructure)) {
// 构建关联子项目的结构
return progressCategoryList.stream().map(p -> {
PgsProgressCategoryGanttSubProjectVo vo = new PgsProgressCategoryGanttSubProjectVo();
vo.setRelevancyStructure(PgsRelevancyStructureEnum.SUB_PROJECT.getValue());
vo.setSubProjectId(p.getProjectId());
vo.setProgressCategoryId(p.getId());
vo.setProgressCategoryName(p.getName());
return vo;
}).toList();
} else if (PgsRelevancyStructureEnum.MATRIX.getValue().equals(relevancyStructure)) {
// 构建关联方阵的结构
Map<Long, List<PgsProgressCategory>> map = progressCategoryList.stream()
.collect(Collectors.groupingBy(PgsProgressCategory::getProjectId));
return map.entrySet().stream().map(entry -> {
Long key = entry.getKey();
List<PgsProgressCategory> value = entry.getValue();
PgsProgressCategoryGanttSubProjectVo vo = new PgsProgressCategoryGanttSubProjectVo();
vo.setRelevancyStructure(PgsRelevancyStructureEnum.MATRIX.getValue());
vo.setSubProjectId(key);
vo.setProgressCategoryName(name);
// 封装关联方阵结构
List<PgsProgressCategoryGanttMatrixVo> children = value.stream().map(v -> {
PgsProgressCategoryGanttMatrixVo matrixVo = new PgsProgressCategoryGanttMatrixVo();
matrixVo.setMatrixId(v.getMatrixId());
matrixVo.setMatrixName(v.getMatrixName());
matrixVo.setProgressCategoryId(v.getId());
matrixVo.setProgressCategoryName(v.getName());
return matrixVo;
}).toList();
vo.setChildren(children);
return vo;
}).toList();
} else {
return List.of();
}
}
/**
* 计算进度类别(含子类别)的开始和结束时间。
* <p>
* 逻辑说明:
* 1. 若该进度类别为叶子节点则直接根据其关联的计划表Plan的开始/结束时间计算。
* 2. 若该进度类别有子节点,则递归计算所有子节点的开始/结束时间,
* 以最早的开始时间和最晚的结束时间作为该类别的时间区间。
* </p>
* <p>
*
* @param progressCategoryId 当前进度类别ID
* @param allChildren 所有进度类别(树形结构的扁平化列表)
* @param allPlanList 所有进度计划列表
* @return 当前进度类别的开始和结束时间(可能为空)
*/
private PgsProgressCategoryGanttVo getCategoryStartDateAndEndDate(Long progressCategoryId,
List<PgsProgressCategory> allChildren,
List<PgsProgressPlan> allPlanList) {
PgsProgressCategoryGanttVo ganttVo = new PgsProgressCategoryGanttVo();
// 获取该类别下的所有叶子节点(无子节点的类别)
List<PgsProgressCategory> children = this.getLeafNodesByTopId(progressCategoryId, allChildren);
if (CollUtil.isEmpty(children)) {
// --- 叶子节点:直接根据计划计算 ---
List<PgsProgressPlan> planList = allPlanList.stream()
.filter(p -> p.getProgressCategoryId().equals(progressCategoryId))
.toList();
if (CollUtil.isNotEmpty(planList)) {
LocalDate startDate = planList.stream()
.map(PgsProgressPlan::getStartDate)
.filter(Objects::nonNull)
.min(LocalDate::compareTo)
.orElse(null);
LocalDate endDate = planList.stream()
.map(PgsProgressPlan::getEndDate)
.filter(Objects::nonNull)
.max(LocalDate::compareTo)
.orElse(null);
ganttVo.setStartDate(startDate);
ganttVo.setEndDate(endDate);
}
} else {
// --- 非叶子节点:递归计算子节点 ---
List<PgsProgressCategoryGanttVo> childVoList = children.stream()
.map(child -> getCategoryStartDateAndEndDate(child.getId(), allChildren, allPlanList))
.toList();
LocalDate minStart = childVoList.stream()
.map(PgsProgressCategoryGanttVo::getStartDate)
.filter(Objects::nonNull)
.min(LocalDate::compareTo)
.orElse(null);
LocalDate maxEnd = childVoList.stream()
.map(PgsProgressCategoryGanttVo::getEndDate)
.filter(Objects::nonNull)
.max(LocalDate::compareTo)
.orElse(null);
ganttVo.setStartDate(minStart);
ganttVo.setEndDate(maxEnd);
}
return ganttVo;
}
/**
* 根据名称,递归获取进度类别(含子类别)的甘特图数据。
* <p>
* 逻辑说明:
* 1. 获取该进度类别下的所有叶子节点(无子节点的类别)。
* 2. 递归获取所有叶子节点的甘特图数据。
* 3. 获取所有叶子节点的甘特图数据,并添加到结果列表中。
* </p>
*
* @param parentId 父级ID
* @param value 当前进度类别列表
* @param list 结果列表
* @param progressCategoryList 所有进度类别列表
* @param planList 所有进度计划列表
*/
private void getCategoryGanttAndChildrenVo(Long parentId,
List<PgsProgressCategory> value,
List<PgsProgressCategoryGanttVo> list,
List<PgsProgressCategory> progressCategoryList,
List<PgsProgressPlan> planList) {
if (CollUtil.isEmpty(value)) {
return;
}
// 封装数据
PgsProgressCategoryGanttVo vo = new PgsProgressCategoryGanttVo();
PgsProgressCategory first = value.getFirst();
vo.setId(first.getId());
vo.setParentId(parentId);
vo.setText(first.getName());
// 获取子节点
List<PgsProgressCategory> children = value.stream()
.map(category -> getLeafNodesByTopId(category.getId(), progressCategoryList))
.filter(CollUtil::isNotEmpty)
.flatMap(Collection::stream)
.distinct()
.toList();
if (CollUtil.isEmpty(children)) {
children = value;
}
// 获取子节点的进度计划
List<PgsProgressCategory> finalChildren = children;
List<PgsProgressPlan> childrenPlanList = planList.stream()
.filter(plan -> finalChildren.stream().anyMatch(category -> category.getId().equals(plan.getProgressCategoryId())))
.toList();
// 获取进度情况
PgsProgressCategoryProgressDto percentage = this.getProgressPercentage(finalChildren, childrenPlanList);
vo.setFinishProgress(percentage.getFinishProgress());
vo.setPlanProgress(percentage.getPlanProgress());
vo.setUnPlanProgress(percentage.getUnPlanProgress());
vo.setDelayProgress(percentage.getDelayProgress());
// 计算所有类别的开始时间和结束时间
List<PgsProgressCategoryGanttVo> ganttVoList = value.stream()
.map(category -> getCategoryStartDateAndEndDate(category.getId(), progressCategoryList, planList))
.toList();
// 获取最小开始时间
LocalDate minStart = ganttVoList.stream()
.map(PgsProgressCategoryGanttVo::getStartDate)
.filter(Objects::nonNull)
.min(LocalDate::compareTo)
.orElse(null);
// 获取最大结束时间
LocalDate maxEnd = ganttVoList.stream()
.map(PgsProgressCategoryGanttVo::getEndDate)
.filter(Objects::nonNull)
.max(LocalDate::compareTo)
.orElse(null);
vo.setStartDate(minStart);
vo.setEndDate(maxEnd);
vo.setRelevancyStructure(first.getRelevancyStructure());
vo.setStatus(CollUtil.isNotEmpty(children) ? this.getParentStatus(children) : first.getStatus());
vo.setSort(first.getSort());
list.add(vo);
// 递归,获取子节点的数据
if (CollUtil.isNotEmpty(children)) {
int level = first.getAncestors().split(",").length + 1;
// 仅获取下一层级的子节点数据
List<PgsProgressCategory> nextLevelChildren = children.stream()
.filter(child -> child.getAncestors().split(",").length == level)
.distinct()
.toList();
Map<String, List<PgsProgressCategory>> nextMap = nextLevelChildren.stream()
.collect(Collectors.groupingBy(PgsProgressCategory::getName));
for (Map.Entry<String, List<PgsProgressCategory>> entry : nextMap.entrySet()) {
getCategoryGanttAndChildrenVo(first.getId(), entry.getValue(), list, progressCategoryList, planList);
}
}
}
}

View File

@ -127,16 +127,6 @@ public class PgsProgressPlanServiceImpl extends ServiceImpl<PgsProgressPlanMappe
if (overlap) {
throw new ServiceException("该进度类型下,时间区间已存在重叠的数据");
}
Long projectId = progressPlan.getProjectId();
if (projectService.getById(projectId) == null) {
throw new ServiceException("对应项目不存在", HttpStatus.NOT_FOUND);
}
Long matrixId = progressPlan.getMatrixId();
FacMatrix matrix = matrixService.getById(matrixId);
String matrixName = null;
if (matrix != null) {
matrixName = matrix.getMatrixName();
}
Long progressCategoryId = progressPlan.getProgressCategoryId();
// 校验日期是否合法
/* PgsProgressPlan lastProgressPlan = this.lambdaQuery()
@ -148,6 +138,18 @@ public class PgsProgressPlanServiceImpl extends ServiceImpl<PgsProgressPlanMappe
throw new ServiceException("开始日期不能早于上一条进度计划结束日期", HttpStatus.BAD_REQUEST);
}*/
PgsProgressCategory progressCategory = progressCategoryService.getById(progressCategoryId);
progressPlan.setProjectId(progressCategory.getProjectId());
progressPlan.setMatrixId(progressCategory.getMatrixId());
Long projectId = progressPlan.getProjectId();
if (projectService.getById(projectId) == null) {
throw new ServiceException("对应项目不存在", HttpStatus.NOT_FOUND);
}
Long matrixId = progressPlan.getMatrixId();
FacMatrix matrix = matrixService.getById(matrixId);
String matrixName = null;
if (matrix != null) {
matrixName = matrix.getMatrixName();
}
this.validPlanNumber(req.getPlanNumber(), progressCategory);
progressPlan.setProgressCategoryName(progressCategory.getName());
progressPlan.setMatrixName(matrixName);
@ -172,6 +174,8 @@ public class PgsProgressPlanServiceImpl extends ServiceImpl<PgsProgressPlanMappe
if (!result) {
throw new ServiceException("新增进度计划详情失败,数据库操作失败", HttpStatus.ERROR);
}
} else {
throw new ServiceException("新增进度计划详情失败,请进行均分", HttpStatus.BAD_REQUEST);
}
// 更新进度分类计划总数量
boolean update = progressCategoryService.lambdaUpdate()