|
|
@@ -9,17 +9,26 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
|
|
import cn.iocoder.yudao.module.pms.controller.admin.iotoperationmeetingattrs.vo.IotOperationMeetingAttrsPageReqVO;
|
|
|
import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttaskplan.vo.IotProjectTaskPlanPageReqVO;
|
|
|
import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttaskplan.vo.IotProjectTaskPlanSaveReqVO;
|
|
|
+import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttaskplan.vo.IotProjectTaskPlanStatisticsItemRespVO;
|
|
|
+import cn.iocoder.yudao.module.pms.controller.admin.iotprojecttaskplan.vo.IotProjectTaskPlanStatisticsRespVO;
|
|
|
import cn.iocoder.yudao.module.pms.dal.dataobject.iotoperationmeetingattrs.IotOperationMeetingAttrsDO;
|
|
|
import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttaskattrs.IotTaskAttrModelProperty;
|
|
|
import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttaskplan.IotProjectTaskPlanDO;
|
|
|
import cn.iocoder.yudao.module.pms.dal.mysql.iotoperationmeetingattrs.IotOperationMeetingAttrsMapper;
|
|
|
import cn.iocoder.yudao.module.pms.dal.mysql.iotprojecttaskplan.IotProjectTaskPlanMapper;
|
|
|
+import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
|
|
|
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
|
|
|
+import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO;
|
|
|
import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper;
|
|
|
+import cn.iocoder.yudao.module.system.service.dept.DeptService;
|
|
|
+import cn.iocoder.yudao.module.system.service.dict.DictDataService;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.validation.annotation.Validated;
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.YearMonth;
|
|
|
import java.util.*;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
@@ -45,6 +54,12 @@ public class IotProjectTaskPlanServiceImpl implements IotProjectTaskPlanService
|
|
|
@Resource
|
|
|
private DeptMapper deptMapper;
|
|
|
|
|
|
+ @Resource
|
|
|
+ private DeptService deptService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private DictDataService dictDataService;
|
|
|
+
|
|
|
@Override
|
|
|
public Long createIotProjectTaskPlan(IotProjectTaskPlanSaveReqVO createReqVO) {
|
|
|
// 插入
|
|
|
@@ -53,7 +68,7 @@ public class IotProjectTaskPlanServiceImpl implements IotProjectTaskPlanService
|
|
|
throw exception(IOT_PROJECT_TASK_PLAN_NOT_EXISTS);
|
|
|
}
|
|
|
// 校验当前队伍部门 指定时间所属月份 是否已经存在 工作量计划记录
|
|
|
- IotProjectTaskPlanDO existPlan = iotProjectTaskPlanMapper.selectOneByDeptIdAndMonth(createReqVO.getDeptId(), createReqVO.getStartTime(), null);
|
|
|
+ IotProjectTaskPlanDO existPlan = iotProjectTaskPlanMapper.selectOneByDeptIdAndMonth(createReqVO.getDeptId(), createReqVO.getStartTime(), createReqVO.getBusiness(), null);
|
|
|
if (ObjUtil.isNotEmpty(existPlan)) {
|
|
|
throw exception(IOT_PROJECT_TASK_PLAN_EXISTS);
|
|
|
}
|
|
|
@@ -67,7 +82,7 @@ public class IotProjectTaskPlanServiceImpl implements IotProjectTaskPlanService
|
|
|
// 校验存在
|
|
|
validateIotProjectTaskPlanExists(updateReqVO.getId());
|
|
|
// 校验当前队伍部门 指定时间所属月份 是否已经存在 工作量计划记录
|
|
|
- IotProjectTaskPlanDO existPlan = iotProjectTaskPlanMapper.selectOneByDeptIdAndMonth(updateReqVO.getDeptId(), updateReqVO.getStartTime(), updateReqVO.getId());
|
|
|
+ IotProjectTaskPlanDO existPlan = iotProjectTaskPlanMapper.selectOneByDeptIdAndMonth(updateReqVO.getDeptId(), updateReqVO.getStartTime(), updateReqVO.getBusiness(), updateReqVO.getId());
|
|
|
if (ObjUtil.isNotEmpty(existPlan)) {
|
|
|
throw exception(IOT_PROJECT_TASK_PLAN_EXISTS);
|
|
|
}
|
|
|
@@ -146,4 +161,519 @@ public class IotProjectTaskPlanServiceImpl implements IotProjectTaskPlanService
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public List<IotProjectTaskPlanStatisticsRespVO> summaryStatistics(IotProjectTaskPlanPageReqVO pageReqVO) {
|
|
|
+ List<IotProjectTaskPlanStatisticsRespVO> result = new ArrayList<>();
|
|
|
+ // 不分页统计所有数据
|
|
|
+ Set<Long> ids = new HashSet<>();
|
|
|
+ if (ObjUtil.isEmpty(pageReqVO.getDeptId())) {
|
|
|
+ // 如果没有传递 deptId 参数 默认根据当前登录人所属公司
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ ids = deptService.getChildDeptIdListFromCache(pageReqVO.getDeptId());
|
|
|
+ // 找到所有根部门子部门对象集合
|
|
|
+ ids.add(pageReqVO.getDeptId());
|
|
|
+ // 查询所有工作量计划列表
|
|
|
+ pageReqVO.setDeptIds(ids);
|
|
|
+ pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
|
|
+ PageResult<IotProjectTaskPlanDO> pagePlans = iotProjectTaskPlanMapper.selectPage(pageReqVO);
|
|
|
+ List<IotProjectTaskPlanDO> plans = pagePlans.getList();
|
|
|
+ /* if (CollUtil.isEmpty(plans)) {
|
|
|
+ return result;
|
|
|
+ } */
|
|
|
+ // 默认显示所有公司的汇总数据 点击公司 显示各项目部数据
|
|
|
+ // 点击项目部 显示 下属队伍的数据
|
|
|
+ // 首先判断点击的部门是属于 公司 还是 队伍 如果没有点击任何部门 默认选中最顶级部门
|
|
|
+ // 判断点击的组织树中的部门类型 类型(公司级1 项目部2 队伍3)
|
|
|
+ DeptDO selectedDept = deptService.getDept(pageReqVO.getDeptId());
|
|
|
+ if ("0".equals(selectedDept.getType())) {
|
|
|
+ // 选中集团 查询所有公司的汇总数据
|
|
|
+ // result = statisticsByCompany(plans, pageReqVO);
|
|
|
+ } else if ("1".equals(selectedDept.getType())) {
|
|
|
+ // 选中公司 查询公司下所有项目部的汇总数据
|
|
|
+ result = statisticsByProjectDept(plans, pageReqVO);
|
|
|
+ } else if ("2".equals(selectedDept.getType())) {
|
|
|
+ // 选中项目部 查询项目部下所有队伍的汇总数据
|
|
|
+ result = statisticsByProjectTeam(plans, pageReqVO);
|
|
|
+ } else if ("3".equals(selectedDept.getType())) {
|
|
|
+ // 选中队伍 查询单个队伍的汇总数据
|
|
|
+ result = statisticsByProjectTeam(plans, pageReqVO);
|
|
|
+ } else {
|
|
|
+ // 点击的部门没有类型 如 瑞恒 新疆分公司 查询下属项目部的汇总数据
|
|
|
+ result = statisticsByProjectDept(plans, pageReqVO);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据result集合内对象的 sort 属性正序排列 sort 类型为 integer 类型
|
|
|
+ if (CollUtil.isNotEmpty(result)) {
|
|
|
+ result.sort(Comparator.comparing(
|
|
|
+ IotProjectTaskPlanStatisticsRespVO::getSort,
|
|
|
+ Comparator.nullsLast(Comparator.naturalOrder())
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按项目部维度 汇总 统计数据
|
|
|
+ * @param plans 月度计划工作量列表
|
|
|
+ * @param pageReqVO 包含根部门ID(如 163L为 瑞都 根部门,或其他公司级部门ID)
|
|
|
+ * @return 项目部维度统计结果列表
|
|
|
+ */
|
|
|
+ private List<IotProjectTaskPlanStatisticsRespVO> statisticsByProjectDept(List<IotProjectTaskPlanDO> plans, IotProjectTaskPlanPageReqVO pageReqVO) {
|
|
|
+ List<IotProjectTaskPlanStatisticsRespVO> result = new ArrayList<>();
|
|
|
+
|
|
|
+ Set<Long> projectDeptIds = new HashSet<>();
|
|
|
+ // key项目部id value项目部名称
|
|
|
+ Map<Long, DeptDO> projectDeptPair = new HashMap<>();
|
|
|
+ // key部门id value部门parentId
|
|
|
+ Map<Long, Long> teamProjectIdPair = new HashMap<>();
|
|
|
+ // key队伍id value队伍部门
|
|
|
+ Map<Long, DeptDO> teamDeptPair = new HashMap<>();
|
|
|
+
|
|
|
+ // key队伍id/项目部id value累计 注气量(万方)
|
|
|
+ Map<Long, BigDecimal> cumulativeGasInjectionPair = new HashMap<>();
|
|
|
+ // key队伍id/项目部id value累计 进尺(米)
|
|
|
+ Map<Long, BigDecimal> cumulativeFootagePair = new HashMap<>();
|
|
|
+ // key队伍id/项目部id value累计 井次(井) 修井 井下 连油 井次
|
|
|
+ Map<Long, BigDecimal> cumulativeWellCountPair = new HashMap<>();
|
|
|
+ // key队伍id/项目部id value累计 压裂(层) 瑞都压裂层 5#国压裂层
|
|
|
+ Map<Long, BigDecimal> cumulativeFracLayerPair = new HashMap<>();
|
|
|
+ // key队伍id/项目部id value业务类型
|
|
|
+ Map<Long, String> projectBusinessPair = new HashMap<>();
|
|
|
+
|
|
|
+ // 月度统计(项目部 -> 年月 -> 统计值)
|
|
|
+ Map<Long, Map<String, MonthlyStats>> projectMonthStatsMap = new HashMap<>();
|
|
|
+
|
|
|
+ // 计划工作量 项目业务类型 key字典键值 value字典标签
|
|
|
+ Map<String, String> workloadPlanDictPair = new HashMap<>();
|
|
|
+
|
|
|
+ // key项目业务类型 value项目部id集合
|
|
|
+ Map<String, Set<Long>> projectBizPair = new HashMap<>();
|
|
|
+
|
|
|
+ // 队伍id 集合
|
|
|
+ Set<Long> teamIds = new HashSet<>();
|
|
|
+
|
|
|
+ // 以项目部为维度统计数据
|
|
|
+ // 找到所有项目部与队伍的对应关系
|
|
|
+ // 查询指定根部门下的所有子部门
|
|
|
+ Set<Long> allRhChildDeptIds = deptService.getChildDeptIdListFromCache(pageReqVO.getDeptId());
|
|
|
+ DeptListReqVO reqVO = new DeptListReqVO();
|
|
|
+ reqVO.setDeptIds(allRhChildDeptIds);
|
|
|
+ List<DeptDO> departments = deptService.getDeptList(reqVO);
|
|
|
+
|
|
|
+ // 查询 计划工作量 项目业务类型 项目业务(注气/钻井/修井/压裂/连油/井下)
|
|
|
+ List<DictDataDO> workloadPlanBizData = dictDataService.getDictDataListByDictType("rq_iot_workload_plan_biz");
|
|
|
+ if (CollUtil.isNotEmpty(workloadPlanBizData)) {
|
|
|
+ workloadPlanBizData.forEach(tech -> {
|
|
|
+ String label = tech.getLabel();
|
|
|
+ workloadPlanDictPair.put(tech.getValue(), label);
|
|
|
+ // 设置每个项目部的业务类型
|
|
|
+ String projectDeptIdsStr = tech.getRemark();
|
|
|
+ if (StrUtil.isNotBlank(projectDeptIdsStr)) {
|
|
|
+ // 数据格式 234,124,832
|
|
|
+ // 逗号分割、转Long、过滤空字符串、收集到Set
|
|
|
+ Set<Long> deptIdSet = StrUtil.splitTrim(projectDeptIdsStr, ",")
|
|
|
+ .stream()
|
|
|
+ .filter(StrUtil::isNotBlank)
|
|
|
+ .map(Long::valueOf)
|
|
|
+ .collect(Collectors.toSet());
|
|
|
+
|
|
|
+ // 存入map:存在则合并集合,不存在则新建
|
|
|
+ projectBizPair.computeIfAbsent(label, k -> new HashSet<>()).addAll(deptIdSet);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建项目部映射和父子部门关系
|
|
|
+ departments.forEach(dept -> {
|
|
|
+ if ("2".equals(dept.getType())) {
|
|
|
+ // 项目部
|
|
|
+ projectDeptIds.add(dept.getId());
|
|
|
+ projectDeptPair.put(dept.getId(), dept);
|
|
|
+ }
|
|
|
+ if ("3".equals(dept.getType())) {
|
|
|
+ // 队伍
|
|
|
+ teamIds.add(dept.getId());
|
|
|
+ teamDeptPair.put(dept.getId(), dept);
|
|
|
+ }
|
|
|
+ // 所有部门与上级部门的 id 对应关系
|
|
|
+ teamProjectIdPair.put(dept.getId(), dept.getParentId());
|
|
|
+
|
|
|
+ });
|
|
|
+ // 遍历 计划工作量列表 以项目部为维度组装 工作量数据
|
|
|
+ plans.forEach(plan -> {
|
|
|
+ BigDecimal gasInjection = ObjUtil.isNotEmpty(plan.getPlanGasInjection()) ? plan.getPlanGasInjection() : BigDecimal.ZERO;
|
|
|
+ BigDecimal footage = ObjUtil.isNotEmpty(plan.getPlanFootage()) ? plan.getPlanFootage() : BigDecimal.ZERO;
|
|
|
+ BigDecimal wellTrips = ObjUtil.isNotEmpty(plan.getPlanWellTrips()) ? plan.getPlanWellTrips() : BigDecimal.ZERO;
|
|
|
+ BigDecimal layers = ObjUtil.isNotEmpty(plan.getPlanLayers()) ? plan.getPlanLayers() : BigDecimal.ZERO;
|
|
|
+ String business = plan.getBusiness();
|
|
|
+ if (ObjUtil.isNotEmpty(plan.getDeptId()) && teamProjectIdPair.containsKey(plan.getDeptId())) {
|
|
|
+ // 项目部id
|
|
|
+ Long projectDeptId = 0l;
|
|
|
+ if (teamDeptPair.containsKey(plan.getDeptId())) {
|
|
|
+ // 日报 deptId 如果是队伍 获取队伍的上级项目部
|
|
|
+ projectDeptId = teamProjectIdPair.get(plan.getDeptId());
|
|
|
+ }
|
|
|
+ if (projectDeptPair.containsKey(plan.getDeptId())) {
|
|
|
+ // 日报 deptId 如果是项目部 使用当前项目部
|
|
|
+ projectDeptId = plan.getDeptId();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ObjUtil.isNotEmpty(projectDeptId) && (projectDeptId > 0)) {
|
|
|
+ // 累计注气量
|
|
|
+ cumulativeGasInjectionPair.compute(projectDeptId, (k, v) -> ObjUtil.isEmpty(v) ? gasInjection : v.add(gasInjection));
|
|
|
+ // 累计进尺
|
|
|
+ cumulativeFootagePair.compute(projectDeptId, (k, v) -> ObjUtil.isEmpty(v) ? footage : v.add(footage));
|
|
|
+ // 累计井次
|
|
|
+ cumulativeWellCountPair.compute(projectDeptId, (k, v) -> ObjUtil.isEmpty(v) ? wellTrips : v.add(wellTrips));
|
|
|
+ // 累计压裂层
|
|
|
+ cumulativeFracLayerPair.compute(projectDeptId, (k, v) -> ObjUtil.isEmpty(v) ? layers : v.add(layers));
|
|
|
+ // 工作量业务类型
|
|
|
+ if (StrUtil.isNotBlank(business) && workloadPlanDictPair.containsKey(business)) {
|
|
|
+ projectBusinessPair.put(projectDeptId, workloadPlanDictPair.get(business));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 按照 年 月 汇总统计 计划工作量 如 2025-03
|
|
|
+ LocalDateTime startTime = plan.getStartTime();
|
|
|
+ if (ObjUtil.isNotEmpty(startTime)) {
|
|
|
+ // 提取出时间中的 年 月
|
|
|
+ Integer year = startTime.getYear();
|
|
|
+ Integer month = startTime.getMonthValue();
|
|
|
+ String uniqueKey = StrUtil.join("-", year, month);
|
|
|
+ // 统计各项目下包含的按照 yyyy-MM 统计的各项工作量
|
|
|
+ // 获取该项目部的月度Map
|
|
|
+ Map<String, MonthlyStats> monthMap = projectMonthStatsMap.computeIfAbsent(projectDeptId, k -> new HashMap<>());
|
|
|
+ // 获取该年月的统计对象
|
|
|
+ MonthlyStats stats = monthMap.computeIfAbsent(uniqueKey, k -> new MonthlyStats());
|
|
|
+ // 累加
|
|
|
+ stats.gasInjection = stats.gasInjection.add(gasInjection);
|
|
|
+ stats.footage = stats.footage.add(footage);
|
|
|
+ stats.wellCount = stats.wellCount.add(wellTrips);
|
|
|
+ stats.fracLayer = stats.fracLayer.add(layers);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 组装查询时间区间内包含的 年 月
|
|
|
+ LocalDateTime[] startTimeArr = pageReqVO.getStartTime();
|
|
|
+ if (ObjUtil.isEmpty(startTimeArr)) {
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ if (startTimeArr.length < 1) {
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ List<String> allMonths = generateMonthRange(startTimeArr[0], startTimeArr[1]);
|
|
|
+
|
|
|
+ // 生成返回的数据列表集合
|
|
|
+ projectDeptPair.forEach((deptId, dept) -> {
|
|
|
+ IotProjectTaskPlanStatisticsRespVO statistics = new IotProjectTaskPlanStatisticsRespVO();
|
|
|
+ statistics.setProjectDeptId(deptId);
|
|
|
+ statistics.setProjectDeptName(dept.getName());
|
|
|
+ statistics.setSort(dept.getSort());
|
|
|
+ statistics.setType("2");
|
|
|
+ statistics.setCumulativeGasInjection(cumulativeGasInjectionPair.getOrDefault(deptId, BigDecimal.ZERO));
|
|
|
+ statistics.setCumulativeFootage(cumulativeFootagePair.getOrDefault(deptId, BigDecimal.ZERO));
|
|
|
+ statistics.setCumulativeWellCount(cumulativeWellCountPair.getOrDefault(deptId, BigDecimal.ZERO));
|
|
|
+ statistics.setCumulativeFracLayer(cumulativeFracLayerPair.getOrDefault(deptId, BigDecimal.ZERO));
|
|
|
+
|
|
|
+ // 遍历全部业务映射,收集该项目部绑定的所有业务(支持多业务)
|
|
|
+ List<String> bizNameList = new ArrayList<>();
|
|
|
+ for (Map.Entry<String, Set<Long>> bizEntry : projectBizPair.entrySet()) {
|
|
|
+ Set<Long> bindDeptIds = bizEntry.getValue();
|
|
|
+ if (CollUtil.isNotEmpty(bindDeptIds) && bindDeptIds.contains(deptId)) {
|
|
|
+ bizNameList.add(bizEntry.getKey());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 多业务逗号拼接,无匹配则为null
|
|
|
+ String businessName = CollUtil.isNotEmpty(bizNameList) ? CollUtil.join(bizNameList, ",") : StrUtil.EMPTY;
|
|
|
+ statistics.setBusiness(businessName);
|
|
|
+
|
|
|
+ // 处理月度明细
|
|
|
+ Map<String, MonthlyStats> existingMonthMap = projectMonthStatsMap.get(deptId);
|
|
|
+ List<IotProjectTaskPlanStatisticsItemRespVO> items = new ArrayList<>();
|
|
|
+ if (allMonths.isEmpty()) {
|
|
|
+ // 无时间范围时,仅输出已有的月度(兼容原逻辑)
|
|
|
+ if (CollUtil.isNotEmpty(existingMonthMap)) {
|
|
|
+ items = existingMonthMap.entrySet().stream()
|
|
|
+ .sorted(Map.Entry.comparingByKey())
|
|
|
+ .map(entry -> {
|
|
|
+ IotProjectTaskPlanStatisticsItemRespVO item = new IotProjectTaskPlanStatisticsItemRespVO();
|
|
|
+ item.setYearMonth(entry.getKey());
|
|
|
+ MonthlyStats ms = entry.getValue();
|
|
|
+ item.setCumulativeGasInjection(ms.gasInjection);
|
|
|
+ item.setCumulativeFootage(ms.footage);
|
|
|
+ item.setCumulativeWellCount(ms.wellCount);
|
|
|
+ item.setCumulativeFracLayer(ms.fracLayer);
|
|
|
+ return item;
|
|
|
+ })
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // ★ 补全所有月份
|
|
|
+ for (String month : allMonths) {
|
|
|
+ MonthlyStats stats = CollUtil.isNotEmpty(existingMonthMap) ? existingMonthMap.get(month) : null;
|
|
|
+ if (stats == null) {
|
|
|
+ stats = new MonthlyStats(); // 默认所有字段为 0
|
|
|
+ }
|
|
|
+ IotProjectTaskPlanStatisticsItemRespVO item = new IotProjectTaskPlanStatisticsItemRespVO();
|
|
|
+ item.setYearMonth(month);
|
|
|
+ item.setCumulativeGasInjection(stats.gasInjection);
|
|
|
+ item.setCumulativeFootage(stats.footage);
|
|
|
+ item.setCumulativeWellCount(stats.wellCount);
|
|
|
+ item.setCumulativeFracLayer(stats.fracLayer);
|
|
|
+ items.add(item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!items.isEmpty()) {
|
|
|
+ statistics.setItems(items);
|
|
|
+ }
|
|
|
+
|
|
|
+ result.add(statistics);
|
|
|
+ });
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 按队伍维度 汇总 统计数据
|
|
|
+ * @param plans 月度计划工作量列表
|
|
|
+ * @param pageReqVO 包含根部门ID(如 163L为 瑞都 根部门,或其他公司级部门ID)
|
|
|
+ * @return 队伍维度统计结果列表
|
|
|
+ */
|
|
|
+ private List<IotProjectTaskPlanStatisticsRespVO> statisticsByProjectTeam(List<IotProjectTaskPlanDO> plans, IotProjectTaskPlanPageReqVO pageReqVO) {
|
|
|
+ List<IotProjectTaskPlanStatisticsRespVO> result = new ArrayList<>();
|
|
|
+
|
|
|
+ Set<Long> teamDeptIds = new HashSet<>();
|
|
|
+ // key项目部id value项目部名称
|
|
|
+ Map<Long, DeptDO> projectDeptPair = new HashMap<>();
|
|
|
+ // key队伍id value队伍名称
|
|
|
+ Map<Long, DeptDO> teamDeptPair = new HashMap<>();
|
|
|
+ // key部门id value部门parentId
|
|
|
+ Map<Long, Long> teamProjectIdPair = new HashMap<>();
|
|
|
+
|
|
|
+ // key队伍id/项目部id value累计 注气量(万方)
|
|
|
+ Map<Long, BigDecimal> cumulativeGasInjectionPair = new HashMap<>();
|
|
|
+ // key队伍id/项目部id value累计 进尺(米)
|
|
|
+ Map<Long, BigDecimal> cumulativeFootagePair = new HashMap<>();
|
|
|
+ // key队伍id/项目部id value累计 井次(井) 修井 井下 连油 井次
|
|
|
+ Map<Long, BigDecimal> cumulativeWellCountPair = new HashMap<>();
|
|
|
+ // key队伍id/项目部id value累计 压裂(层) 瑞都压裂层 5#国压裂层
|
|
|
+ Map<Long, BigDecimal> cumulativeFracLayerPair = new HashMap<>();
|
|
|
+ // key队伍id/项目部id value业务类型
|
|
|
+ Map<Long, String> projectBusinessPair = new HashMap<>();
|
|
|
+
|
|
|
+ // 月度统计(队伍 -> 年月 -> 统计值)
|
|
|
+ Map<Long, Map<String, MonthlyStats>> teamMonthStatsMap = new HashMap<>();
|
|
|
+
|
|
|
+ // 计划工作量 项目业务类型 key字典键值 value字典标签
|
|
|
+ Map<String, String> workloadPlanDictPair = new HashMap<>();
|
|
|
+
|
|
|
+ // key项目业务类型 value项目部id集合
|
|
|
+ Map<String, Set<Long>> projectBizPair = new HashMap<>();
|
|
|
+
|
|
|
+ // 以 队伍 为维度统计数据
|
|
|
+ // 查询指定根部门下的所有子部门
|
|
|
+ Set<Long> allRhChildDeptIds = deptService.getChildDeptIdListFromCache(pageReqVO.getDeptId());
|
|
|
+ DeptListReqVO reqVO = new DeptListReqVO();
|
|
|
+ // 查询某支队伍 或 所属项目部的信息
|
|
|
+ allRhChildDeptIds.add(pageReqVO.getDeptId());
|
|
|
+ reqVO.setDeptIds(allRhChildDeptIds);
|
|
|
+ List<DeptDO> departments = deptService.getDeptList(reqVO);
|
|
|
+
|
|
|
+ // 查询 计划工作量 项目业务类型 项目业务(注气/钻井/修井/压裂/连油/井下)
|
|
|
+ List<DictDataDO> workloadPlanBizData = dictDataService.getDictDataListByDictType("rq_iot_workload_plan_biz");
|
|
|
+ if (CollUtil.isNotEmpty(workloadPlanBizData)) {
|
|
|
+ workloadPlanBizData.forEach(tech -> {
|
|
|
+ String label = tech.getLabel();
|
|
|
+ workloadPlanDictPair.put(tech.getValue(), label);
|
|
|
+ // 设置每个项目部的业务类型
|
|
|
+ String projectDeptIdsStr = tech.getRemark();
|
|
|
+ if (StrUtil.isNotBlank(projectDeptIdsStr)) {
|
|
|
+ // 数据格式 234,124,832
|
|
|
+ // 逗号分割、转Long、过滤空字符串、收集到Set
|
|
|
+ Set<Long> deptIdSet = StrUtil.splitTrim(projectDeptIdsStr, ",")
|
|
|
+ .stream()
|
|
|
+ .filter(StrUtil::isNotBlank)
|
|
|
+ .map(Long::valueOf)
|
|
|
+ .collect(Collectors.toSet());
|
|
|
+
|
|
|
+ // 存入map:存在则合并集合,不存在则新建
|
|
|
+ projectBizPair.computeIfAbsent(label, k -> new HashSet<>()).addAll(deptIdSet);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ // 组装队伍集合
|
|
|
+ departments.forEach(dept -> {
|
|
|
+ if ("3".equals(dept.getType())) {
|
|
|
+ // 队伍
|
|
|
+ teamDeptIds.add(dept.getId());
|
|
|
+ teamDeptPair.put(dept.getId(), dept);
|
|
|
+ }
|
|
|
+ if ("2".equals(dept.getType())) {
|
|
|
+ // 项目部
|
|
|
+ projectDeptPair.put(dept.getId(), dept);
|
|
|
+ }
|
|
|
+ teamProjectIdPair.put(dept.getId(), dept.getParentId());
|
|
|
+ });
|
|
|
+
|
|
|
+ // 遍历 计划工作量列表 以队伍为维度组装 工作量数据
|
|
|
+ plans.forEach(plan -> {
|
|
|
+ Long deptId = plan.getDeptId();
|
|
|
+ BigDecimal gasInjection = ObjUtil.isNotEmpty(plan.getPlanGasInjection()) ? plan.getPlanGasInjection() : BigDecimal.ZERO;
|
|
|
+ BigDecimal footage = ObjUtil.isNotEmpty(plan.getPlanFootage()) ? plan.getPlanFootage() : BigDecimal.ZERO;
|
|
|
+ BigDecimal wellTrips = ObjUtil.isNotEmpty(plan.getPlanWellTrips()) ? plan.getPlanWellTrips() : BigDecimal.ZERO;
|
|
|
+ BigDecimal layers = ObjUtil.isNotEmpty(plan.getPlanLayers()) ? plan.getPlanLayers() : BigDecimal.ZERO;
|
|
|
+ String business = plan.getBusiness();
|
|
|
+ if (ObjUtil.isNotEmpty(deptId) && teamProjectIdPair.containsKey(deptId)) {
|
|
|
+ // 累计注气量
|
|
|
+ cumulativeGasInjectionPair.compute(deptId, (k, v) -> ObjUtil.isEmpty(v) ? gasInjection : v.add(gasInjection));
|
|
|
+ // 累计进尺
|
|
|
+ cumulativeFootagePair.compute(deptId, (k, v) -> ObjUtil.isEmpty(v) ? footage : v.add(footage));
|
|
|
+ // 累计井次
|
|
|
+ cumulativeWellCountPair.compute(deptId, (k, v) -> ObjUtil.isEmpty(v) ? wellTrips : v.add(wellTrips));
|
|
|
+ // 累计压裂层
|
|
|
+ cumulativeFracLayerPair.compute(deptId, (k, v) -> ObjUtil.isEmpty(v) ? layers : v.add(layers));
|
|
|
+ // 工作量业务类型
|
|
|
+ if (StrUtil.isNotBlank(business) && workloadPlanDictPair.containsKey(business)) {
|
|
|
+ projectBusinessPair.put(deptId, workloadPlanDictPair.get(business));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 按照 年 月 汇总统计 计划工作量 如 2025-03
|
|
|
+ LocalDateTime startTime = plan.getStartTime();
|
|
|
+ if (ObjUtil.isNotEmpty(startTime)) {
|
|
|
+ // 提取出时间中的 年 月
|
|
|
+ Integer year = startTime.getYear();
|
|
|
+ Integer month = startTime.getMonthValue();
|
|
|
+ String uniqueKey = StrUtil.join("-", year, month);
|
|
|
+ // 统计各项目下包含的按照 yyyy-MM 统计的各项工作量
|
|
|
+ // 获取该项目部的月度Map
|
|
|
+ Map<String, MonthlyStats> monthMap = teamMonthStatsMap.computeIfAbsent(deptId, k -> new HashMap<>());
|
|
|
+ // 获取该年月的统计对象
|
|
|
+ MonthlyStats stats = monthMap.computeIfAbsent(uniqueKey, k -> new MonthlyStats());
|
|
|
+ // 累加
|
|
|
+ stats.gasInjection = stats.gasInjection.add(gasInjection);
|
|
|
+ stats.footage = stats.footage.add(footage);
|
|
|
+ stats.wellCount = stats.wellCount.add(wellTrips);
|
|
|
+ stats.fracLayer = stats.fracLayer.add(layers);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 组装查询时间区间内包含的 年 月
|
|
|
+ LocalDateTime[] startTimeArr = pageReqVO.getStartTime();
|
|
|
+ if (ObjUtil.isEmpty(startTimeArr)) {
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ if (startTimeArr.length < 1) {
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ List<String> allMonths = generateMonthRange(startTimeArr[0], startTimeArr[1]);
|
|
|
+
|
|
|
+ // 队伍数据 生成返回的数据列表集合
|
|
|
+ teamDeptPair.forEach((teamDeptId, dept) -> {
|
|
|
+ IotProjectTaskPlanStatisticsRespVO statistics = new IotProjectTaskPlanStatisticsRespVO();
|
|
|
+ statistics.setTeamId(teamDeptId);
|
|
|
+ statistics.setTeamName(dept.getName());
|
|
|
+ statistics.setSort(dept.getSort());
|
|
|
+ if (teamDeptPair.containsKey(teamDeptId)) {
|
|
|
+ statistics.setType("3"); // 最好是 项目部小队同时展示 因为有的瑞都日报挂在了项目部下
|
|
|
+ }
|
|
|
+ statistics.setCumulativeGasInjection(cumulativeGasInjectionPair.getOrDefault(teamDeptId, BigDecimal.ZERO));
|
|
|
+ statistics.setCumulativeFootage(cumulativeFootagePair.getOrDefault(teamDeptId, BigDecimal.ZERO));
|
|
|
+ statistics.setCumulativeWellCount(cumulativeWellCountPair.getOrDefault(teamDeptId, BigDecimal.ZERO));
|
|
|
+ statistics.setCumulativeFracLayer(cumulativeFracLayerPair.getOrDefault(teamDeptId, BigDecimal.ZERO));
|
|
|
+
|
|
|
+ // 遍历全部业务映射,收集该项目部绑定的所有业务(支持多业务)
|
|
|
+ List<String> bizNameList = new ArrayList<>();
|
|
|
+ if (teamProjectIdPair.containsKey(teamDeptId)) {
|
|
|
+ // 当前队伍的上级项目部id
|
|
|
+ Long projectDeptId = teamProjectIdPair.get(teamDeptId);
|
|
|
+ for (Map.Entry<String, Set<Long>> bizEntry : projectBizPair.entrySet()) {
|
|
|
+ Set<Long> bindDeptIds = bizEntry.getValue();
|
|
|
+ if (CollUtil.isNotEmpty(bindDeptIds) && bindDeptIds.contains(projectDeptId)) {
|
|
|
+ bizNameList.add(bizEntry.getKey());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 多业务逗号拼接,无匹配则为null
|
|
|
+ String businessName = CollUtil.isNotEmpty(bizNameList) ? CollUtil.join(bizNameList, ",") : StrUtil.EMPTY;
|
|
|
+ statistics.setBusiness(businessName);
|
|
|
+
|
|
|
+ // 处理月度明细
|
|
|
+ Map<String, MonthlyStats> existingMonthMap = teamMonthStatsMap.get(teamDeptId);
|
|
|
+ List<IotProjectTaskPlanStatisticsItemRespVO> items = new ArrayList<>();
|
|
|
+ if (allMonths.isEmpty()) {
|
|
|
+ // 无时间范围时,仅输出已有的月度(兼容原逻辑)
|
|
|
+ if (CollUtil.isNotEmpty(existingMonthMap)) {
|
|
|
+ items = existingMonthMap.entrySet().stream()
|
|
|
+ .sorted(Map.Entry.comparingByKey())
|
|
|
+ .map(entry -> {
|
|
|
+ IotProjectTaskPlanStatisticsItemRespVO item = new IotProjectTaskPlanStatisticsItemRespVO();
|
|
|
+ item.setYearMonth(entry.getKey());
|
|
|
+ MonthlyStats ms = entry.getValue();
|
|
|
+ item.setCumulativeGasInjection(ms.gasInjection);
|
|
|
+ item.setCumulativeFootage(ms.footage);
|
|
|
+ item.setCumulativeWellCount(ms.wellCount);
|
|
|
+ item.setCumulativeFracLayer(ms.fracLayer);
|
|
|
+ return item;
|
|
|
+ })
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // ★ 补全所有月份
|
|
|
+ for (String month : allMonths) {
|
|
|
+ MonthlyStats stats = existingMonthMap != null ? existingMonthMap.get(month) : null;
|
|
|
+ if (stats == null) {
|
|
|
+ stats = new MonthlyStats(); // 默认所有字段为 0
|
|
|
+ }
|
|
|
+ IotProjectTaskPlanStatisticsItemRespVO item = new IotProjectTaskPlanStatisticsItemRespVO();
|
|
|
+ item.setYearMonth(month);
|
|
|
+ item.setCumulativeGasInjection(stats.gasInjection);
|
|
|
+ item.setCumulativeFootage(stats.footage);
|
|
|
+ item.setCumulativeWellCount(stats.wellCount);
|
|
|
+ item.setCumulativeFracLayer(stats.fracLayer);
|
|
|
+ items.add(item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!items.isEmpty()) {
|
|
|
+ statistics.setItems(items);
|
|
|
+ }
|
|
|
+
|
|
|
+ result.add(statistics);
|
|
|
+ });
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成从 start 到 end 之间的所有月份字符串(格式:yyyy-M,无前导零)
|
|
|
+ */
|
|
|
+ private List<String> generateMonthRange(LocalDateTime start, LocalDateTime end) {
|
|
|
+ if (ObjUtil.isEmpty(start) || ObjUtil.isEmpty(end)) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+ List<String> months = new ArrayList<>();
|
|
|
+ YearMonth startYm = YearMonth.from(start);
|
|
|
+ YearMonth endYm = YearMonth.from(end);
|
|
|
+ YearMonth current = startYm;
|
|
|
+ while (!current.isAfter(endYm)) {
|
|
|
+ months.add(current.getYear() + "-" + current.getMonthValue());
|
|
|
+ current = current.plusMonths(1);
|
|
|
+ }
|
|
|
+ return months;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 内部类:月度统计辅助类
|
|
|
+ private static class MonthlyStats {
|
|
|
+ BigDecimal gasInjection = BigDecimal.ZERO;
|
|
|
+ BigDecimal footage = BigDecimal.ZERO;
|
|
|
+ BigDecimal wellCount = BigDecimal.ZERO;
|
|
|
+ BigDecimal fracLayer = BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+
|
|
|
}
|