diff --git a/xinnengyuan/ruoyi-admin/src/main/resources/application-prod.yml b/xinnengyuan/ruoyi-admin/src/main/resources/application-prod.yml index 41f67e9e..fba67693 100644 --- a/xinnengyuan/ruoyi-admin/src/main/resources/application-prod.yml +++ b/xinnengyuan/ruoyi-admin/src/main/resources/application-prod.yml @@ -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 diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/mobileAttendanceMachine/DeviceMessageSender.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/mobileAttendanceMachine/DeviceMessageSender.java index a96ecf02..acff4231 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/mobileAttendanceMachine/DeviceMessageSender.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/mobileAttendanceMachine/DeviceMessageSender.java @@ -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 - * + *

* 考勤设备消息发送工具类(对应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 发送或接收过程中的异常 diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/mobileAttendanceMachine/DeviceWebSocketServer.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/mobileAttendanceMachine/DeviceWebSocketServer.java index fb2c00ea..33532311 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/mobileAttendanceMachine/DeviceWebSocketServer.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/mobileAttendanceMachine/DeviceWebSocketServer.java @@ -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; diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/mobileAttendanceMachine/KqjEntity.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/mobileAttendanceMachine/KqjEntity.java index eaa3bf6d..e505dd7a 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/mobileAttendanceMachine/KqjEntity.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/mobileAttendanceMachine/KqjEntity.java @@ -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 diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/controller/PgsProgressCategoryController.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/controller/PgsProgressCategoryController.java index 2e811382..1f78cb59 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/controller/PgsProgressCategoryController.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/controller/PgsProgressCategoryController.java @@ -90,6 +90,16 @@ public class PgsProgressCategoryController extends BaseController { return R.ok(list); } + /** + * 查询进度类别的甘特图结构列表 + */ + @SaCheckPermission("progress:progressCategory:query") + @GetMapping("/list/gantt/{progressCategoryId}") + public R> 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> listGantt(@NotNull(message = "主键不能为空") - @PathVariable Long projectId) { + @PathVariable Long projectId) { return R.ok(pgsProgressCategoryService.listGanttByProject(projectId)); } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/dto/progresscategory/PgsProgressCategoryProgressDto.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/dto/progresscategory/PgsProgressCategoryProgressDto.java new file mode 100644 index 00000000..cbddb447 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/dto/progresscategory/PgsProgressCategoryProgressDto.java @@ -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; +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/vo/progresscategory/PgsProgressCategoryGanttMatrixVo.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/vo/progresscategory/PgsProgressCategoryGanttMatrixVo.java new file mode 100644 index 00000000..1509f64b --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/vo/progresscategory/PgsProgressCategoryGanttMatrixVo.java @@ -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; +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/vo/progresscategory/PgsProgressCategoryGanttSubProjectVo.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/vo/progresscategory/PgsProgressCategoryGanttSubProjectVo.java new file mode 100644 index 00000000..e10c26b8 --- /dev/null +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/vo/progresscategory/PgsProgressCategoryGanttSubProjectVo.java @@ -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 children; +} diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/vo/progresscategory/PgsProgressCategoryGanttVo.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/vo/progresscategory/PgsProgressCategoryGanttVo.java index c4a0d7b1..bab051d6 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/vo/progresscategory/PgsProgressCategoryGanttVo.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/domain/vo/progresscategory/PgsProgressCategoryGanttVo.java @@ -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; } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/IPgsProgressCategoryService.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/IPgsProgressCategoryService.java index 0ca22b2a..6d446563 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/IPgsProgressCategoryService.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/IPgsProgressCategoryService.java @@ -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 categoryList); + /** + * 获取项目进度百分比 + * + * @param categoryList 项目进度列表 + * @return 项目进度百分比 + */ + PgsProgressCategoryProgressDto getProgressPercentage(List categoryList, List planList); + /** * 获取项目进度类别未完成数量 * @@ -229,4 +238,12 @@ public interface IPgsProgressCategoryService extends IService listGanttByProject(Long projectId); + + /** + * 根据进度类别,获取进度类别甘特图结构 + * + * @param progressCategoryId 进度类别id + * @return 进度类别甘特图结构 + */ + List getGanttStructureList(Long progressCategoryId); } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/impl/PgsProgressCategoryServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/impl/PgsProgressCategoryServiceImpl.java index 2945bf20..8c911902 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/impl/PgsProgressCategoryServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/impl/PgsProgressCategoryServiceImpl.java @@ -1417,7 +1417,7 @@ public class PgsProgressCategoryServiceImpl extends ServiceImpl 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 categoryList, + List 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 progressCategoryList = this.lambdaQuery() .in(PgsProgressCategory::getProjectId, projectIds) - .eq(PgsProgressCategory::getMatrixId, 0) .list(); if (CollUtil.isEmpty(progressCategoryList)) { return ganttList; } - // 封装进度类别数据 - List list = progressCategoryList.stream().map(p -> { - // 获取所有子节点 - List 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 planList = progressPlanService.lambdaQuery() - .eq(PgsProgressPlan::getProjectId, projectId) + .in(PgsProgressPlan::getProjectId, projectIds) .list(); - List 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 projectCategoryList = progressCategoryList.stream() + .filter(p -> p.getRelevancyStructure().equals(PgsRelevancyStructureEnum.PROJECT.getValue())) + .toList(); + + if (CollUtil.isNotEmpty(projectCategoryList)) { + // 封装进度类别数据 + List list = projectCategoryList.stream().map(p -> { + // 获取所有子节点 + List 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 finalChildren = children; + List 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 subProjectCategoryList = progressCategoryList.stream() + .filter(p -> p.getRelevancyStructure().equals(PgsRelevancyStructureEnum.SUB_PROJECT.getValue())) + .toList(); + if (CollUtil.isNotEmpty(subProjectCategoryList)) { + // 获取顶层的类别 + List topCategoryList = subProjectCategoryList.stream() + .filter(p -> p.getParentId().equals(0L)) + .toList(); + Map> topMap = topCategoryList.stream() + .collect(Collectors.groupingBy(PgsProgressCategory::getName)); + // 封装进度类别数据 + List list = new ArrayList<>(); + for (Map.Entry> entry : topMap.entrySet()) { + List value = entry.getValue(); + // 递归封装本身及子节点数据 + this.getCategoryGanttAndChildrenVo(0L, value, list, progressCategoryList, planList); + } + ganttList.addAll(list); + } + // 获取关联方阵项目的类别和计划 + List matrixCategoryList = progressCategoryList.stream() + .filter(p -> p.getRelevancyStructure().equals(PgsRelevancyStructureEnum.MATRIX.getValue())) + .toList(); + if (CollUtil.isNotEmpty(matrixCategoryList)) { + // 获取顶层的类别 + List topCategoryList = matrixCategoryList.stream() + .filter(p -> p.getParentId().equals(0L)) + .toList(); + Map> topMap = topCategoryList.stream() + .collect(Collectors.groupingBy(PgsProgressCategory::getName)); + // 封装进度类别数据 + List list = new ArrayList<>(); + for (Map.Entry> entry : topMap.entrySet()) { + List 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 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 projects = projectService.lambdaQuery() + .eq(BusProject::getPId, project.getPId()) + .list(); + List projectIds = projects.stream().map(BusProject::getId).toList(); + String name = category.getName(); + String relevancyStructure = category.getRelevancyStructure(); + // 获取对应的进度类别列表 + List 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> map = progressCategoryList.stream() + .collect(Collectors.groupingBy(PgsProgressCategory::getProjectId)); + return map.entrySet().stream().map(entry -> { + Long key = entry.getKey(); + List value = entry.getValue(); + PgsProgressCategoryGanttSubProjectVo vo = new PgsProgressCategoryGanttSubProjectVo(); + vo.setRelevancyStructure(PgsRelevancyStructureEnum.MATRIX.getValue()); + vo.setSubProjectId(key); + vo.setProgressCategoryName(name); + // 封装关联方阵结构 + List 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(); + } + } + + /** + * 计算进度类别(含子类别)的开始和结束时间。 + *

+ * 逻辑说明: + * 1. 若该进度类别为叶子节点,则直接根据其关联的计划表(Plan)的开始/结束时间计算。 + * 2. 若该进度类别有子节点,则递归计算所有子节点的开始/结束时间, + * 以最早的开始时间和最晚的结束时间作为该类别的时间区间。 + *

+ *

+ * + * @param progressCategoryId 当前进度类别ID + * @param allChildren 所有进度类别(树形结构的扁平化列表) + * @param allPlanList 所有进度计划列表 + * @return 当前进度类别的开始和结束时间(可能为空) + */ + private PgsProgressCategoryGanttVo getCategoryStartDateAndEndDate(Long progressCategoryId, + List allChildren, + List allPlanList) { + PgsProgressCategoryGanttVo ganttVo = new PgsProgressCategoryGanttVo(); + + // 获取该类别下的所有叶子节点(无子节点的类别) + List children = this.getLeafNodesByTopId(progressCategoryId, allChildren); + + if (CollUtil.isEmpty(children)) { + // --- 叶子节点:直接根据计划计算 --- + List 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 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; + } + + /** + * 根据名称,递归获取进度类别(含子类别)的甘特图数据。 + *

+ * 逻辑说明: + * 1. 获取该进度类别下的所有叶子节点(无子节点的类别)。 + * 2. 递归获取所有叶子节点的甘特图数据。 + * 3. 获取所有叶子节点的甘特图数据,并添加到结果列表中。 + *

+ * + * @param parentId 父级ID + * @param value 当前进度类别列表 + * @param list 结果列表 + * @param progressCategoryList 所有进度类别列表 + * @param planList 所有进度计划列表 + */ + private void getCategoryGanttAndChildrenVo(Long parentId, + List value, + List list, + List progressCategoryList, + List 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 children = value.stream() + .map(category -> getLeafNodesByTopId(category.getId(), progressCategoryList)) + .filter(CollUtil::isNotEmpty) + .flatMap(Collection::stream) + .distinct() + .toList(); + if (CollUtil.isEmpty(children)) { + children = value; + } + // 获取子节点的进度计划 + List finalChildren = children; + List 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 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 nextLevelChildren = children.stream() + .filter(child -> child.getAncestors().split(",").length == level) + .distinct() + .toList(); + Map> nextMap = nextLevelChildren.stream() + .collect(Collectors.groupingBy(PgsProgressCategory::getName)); + for (Map.Entry> entry : nextMap.entrySet()) { + getCategoryGanttAndChildrenVo(first.getId(), entry.getValue(), list, progressCategoryList, planList); + } + } + } + } diff --git a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/impl/PgsProgressPlanServiceImpl.java b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/impl/PgsProgressPlanServiceImpl.java index 075885c8..e1cce7b7 100644 --- a/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/impl/PgsProgressPlanServiceImpl.java +++ b/xinnengyuan/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/progress/service/impl/PgsProgressPlanServiceImpl.java @@ -127,16 +127,6 @@ public class PgsProgressPlanServiceImpl extends ServiceImpl