|
|
@@ -11,12 +11,16 @@ import cn.iocoder.yudao.module.pms.controller.admin.iotoperationmeeting.vo.IotOp
|
|
|
import cn.iocoder.yudao.module.pms.controller.admin.iotoperationmeeting.vo.IotOperationMeetingRespVO;
|
|
|
import cn.iocoder.yudao.module.pms.controller.admin.iotoperationmeeting.vo.IotOperationMeetingSaveBatchVO;
|
|
|
import cn.iocoder.yudao.module.pms.controller.admin.iotoperationmeeting.vo.IotOperationMeetingSaveReqVO;
|
|
|
+import cn.iocoder.yudao.module.pms.controller.admin.iotoperationmeetingattrs.vo.IotOperationMeetingAttrsPageReqVO;
|
|
|
import cn.iocoder.yudao.module.pms.controller.admin.iotoperationmeetingdetail.vo.IotOperationMeetingDetailPageReqVO;
|
|
|
import cn.iocoder.yudao.module.pms.controller.admin.iotoperationmeetingdetail.vo.IotOperationMeetingDetailRespVO;
|
|
|
import cn.iocoder.yudao.module.pms.controller.admin.iotoperationmeetingdetail.vo.IotOperationMeetingDetailSaveReqVO;
|
|
|
import cn.iocoder.yudao.module.pms.dal.dataobject.iotoperationmeeting.IotOperationMeetingDO;
|
|
|
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotoperationmeetingattrs.IotOperationMeetingAttrsDO;
|
|
|
import cn.iocoder.yudao.module.pms.dal.dataobject.iotoperationmeetingdetail.IotOperationMeetingDetailDO;
|
|
|
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotprojecttaskattrs.IotTaskAttrModelProperty;
|
|
|
import cn.iocoder.yudao.module.pms.dal.mysql.iotoperationmeeting.IotOperationMeetingMapper;
|
|
|
+import cn.iocoder.yudao.module.pms.dal.mysql.iotoperationmeetingattrs.IotOperationMeetingAttrsMapper;
|
|
|
import cn.iocoder.yudao.module.pms.dal.mysql.iotoperationmeetingdetail.IotOperationMeetingDetailMapper;
|
|
|
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
|
|
|
import cn.iocoder.yudao.module.system.service.dept.DeptService;
|
|
|
@@ -26,13 +30,14 @@ 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.time.LocalDateTime;
|
|
|
import java.util.*;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
|
-import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_OPERATION_MEETING_EXISTS;
|
|
|
-import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_OPERATION_MEETING_NOT_EXISTS;
|
|
|
+import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.*;
|
|
|
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.DEPT_NOT_FOUND;
|
|
|
|
|
|
/**
|
|
|
@@ -50,6 +55,9 @@ public class IotOperationMeetingServiceImpl implements IotOperationMeetingServic
|
|
|
private IotOperationMeetingDetailMapper iotOperationMeetingDetailMapper;
|
|
|
@Resource
|
|
|
private DeptService deptservice;
|
|
|
+ @Resource
|
|
|
+ private IotOperationMeetingAttrsMapper iotOperationMeetingAttrsMapper;
|
|
|
+
|
|
|
|
|
|
@Override
|
|
|
public Long createIotOperationMeeting(IotOperationMeetingSaveReqVO createReqVO) {
|
|
|
@@ -174,15 +182,176 @@ public class IotOperationMeetingServiceImpl implements IotOperationMeetingServic
|
|
|
resultId = id;
|
|
|
meetingIds.add(iotOperationMeeting.getId());
|
|
|
}
|
|
|
+
|
|
|
+ // 查询当年上一期次的会议内容 公司所属项目工作量细化数据 计算环比使用 设备利用率...
|
|
|
+ IotOperationMeetingDetailPageReqVO beforeReqVO = new IotOperationMeetingDetailPageReqVO();
|
|
|
+ // 当前日期所属年
|
|
|
+ LocalDateTime currentDate = LocalDateTime.now();
|
|
|
+ beforeReqVO.setYear(currentDate.getYear());
|
|
|
+ beforeReqVO.setSort(meeting.getSort() - 1);
|
|
|
+ List<IotOperationMeetingDetailDO> beforeSummarizedDetails = iotOperationMeetingDetailMapper.summarizedProjectDetails(beforeReqVO);
|
|
|
+ // key项目名称 value项目设备利用率
|
|
|
+ Map<String, BigDecimal> projectUtilizationPair = new HashMap<>();
|
|
|
+ if (CollUtil.isNotEmpty(beforeSummarizedDetails)) {
|
|
|
+ // 查询每个项目的设备利用率
|
|
|
+ beforeSummarizedDetails.forEach(detail -> {
|
|
|
+ // 每个项目的工作量扩展属性列表
|
|
|
+ List<IotTaskAttrModelProperty> properties = detail.getExtProperty();
|
|
|
+ if (CollUtil.isNotEmpty(properties)) {
|
|
|
+ // 瑞恒设备利用率
|
|
|
+ BigDecimal utilizationRate = getActualValue(properties, "utilizationRate");
|
|
|
+ projectUtilizationPair.put(detail.getProjectName(), utilizationRate);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
// 编辑时先删除明细 再新增明细
|
|
|
if (ObjUtil.isNotEmpty(saveBatchVO.getDetails())) {
|
|
|
List<IotOperationMeetingDetailSaveReqVO> details = saveBatchVO.getDetails();
|
|
|
List<IotOperationMeetingDetailDO> meetingDetails = new ArrayList<>();
|
|
|
details.forEach(detail -> {
|
|
|
+ // 保存项目明细时 计算工作量 汇总数据 完成比 设备利用率 ...
|
|
|
+ List<IotTaskAttrModelProperty> properties = detail.getExtProperty();
|
|
|
+ if (CollUtil.isNotEmpty(properties)) {
|
|
|
+ // 找到每个项目的 拆分后的工作量字段值
|
|
|
+ // 157 瑞恒 注气完成比 gasCompletionRatio = 实际注气量/计划注气量
|
|
|
+ if (Long.valueOf(157).equals(meeting.getDeptId())) {
|
|
|
+ BigDecimal planGasInjection = getActualValue(properties, "planGasInjection");
|
|
|
+ BigDecimal actualGasInjection = getActualValue(properties, "actualGasInjection");
|
|
|
+ if (planGasInjection.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
+ // 注气完成比 gasCompletionRatio = 实际注气量/计划注气量 23.85%
|
|
|
+ BigDecimal gasCompletionRatio = actualGasInjection.divide(planGasInjection, 4, RoundingMode.HALF_UP);
|
|
|
+ // 将 完成比 作为单独的1个元素添加到 extProperty
|
|
|
+ IotTaskAttrModelProperty completionRatioProperty = new IotTaskAttrModelProperty();
|
|
|
+ completionRatioProperty.setName("注气完成比");
|
|
|
+ completionRatioProperty.setAccessMode("r");
|
|
|
+ completionRatioProperty.setRequired(0);
|
|
|
+ completionRatioProperty.setSort(3);
|
|
|
+ completionRatioProperty.setUnit("%");
|
|
|
+ completionRatioProperty.setDataType("double");
|
|
|
+ completionRatioProperty.setIdentifier("gasCompletionRatio");
|
|
|
+ completionRatioProperty.setActualValue(gasCompletionRatio.toString());
|
|
|
+ completionRatioProperty.setMaxValue(StrUtil.EMPTY);
|
|
|
+ completionRatioProperty.setMinValue(StrUtil.EMPTY);
|
|
|
+ completionRatioProperty.setDefaultValue(StrUtil.EMPTY);
|
|
|
+ properties.add(completionRatioProperty);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 瑞鹰 进尺完成比 井次完成比
|
|
|
+ if (Long.valueOf(158).equals(meeting.getDeptId())) {
|
|
|
+ // 计划进尺
|
|
|
+ BigDecimal planFootage = getActualValue(properties, "planFootage");
|
|
|
+ // 实际进尺
|
|
|
+ BigDecimal actualFootage = getActualValue(properties, "actualFootage");
|
|
|
+ if (planFootage.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
+ // 进尺完成比 footageCompletionRatio = 实际进尺/计划进尺 23.85%
|
|
|
+ BigDecimal footageCompletionRatio = actualFootage.divide(planFootage, 4, RoundingMode.HALF_UP);
|
|
|
+ // 将 完成比 作为单独的1个元素添加到 extProperty
|
|
|
+ IotTaskAttrModelProperty footageCompletionRatioProperty = new IotTaskAttrModelProperty();
|
|
|
+ footageCompletionRatioProperty.setName("进尺完成比");
|
|
|
+ footageCompletionRatioProperty.setAccessMode("r");
|
|
|
+ footageCompletionRatioProperty.setRequired(0);
|
|
|
+ footageCompletionRatioProperty.setSort(3);
|
|
|
+ footageCompletionRatioProperty.setUnit("%");
|
|
|
+ footageCompletionRatioProperty.setDataType("double");
|
|
|
+ footageCompletionRatioProperty.setIdentifier("footageCompletionRatio");
|
|
|
+ footageCompletionRatioProperty.setActualValue(footageCompletionRatio.toString());
|
|
|
+ footageCompletionRatioProperty.setMaxValue(StrUtil.EMPTY);
|
|
|
+ footageCompletionRatioProperty.setMinValue(StrUtil.EMPTY);
|
|
|
+ footageCompletionRatioProperty.setDefaultValue(StrUtil.EMPTY);
|
|
|
+ properties.add(footageCompletionRatioProperty);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 瑞都(层数完成比 井次完成比) 5#公司 层数完成比
|
|
|
+ if (Long.valueOf(163).equals(meeting.getDeptId()) || Long.valueOf(388).equals(meeting.getDeptId())) {
|
|
|
+ // 计划层数
|
|
|
+ BigDecimal planLayers = getActualValue(properties, "planLayers");
|
|
|
+ // 实际层数
|
|
|
+ BigDecimal actualLayers = getActualValue(properties, "actualLayers");
|
|
|
+ if (planLayers.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
+ // 层数完成比 layersCompletionRatio = 实际层数/计划层数 23.85%
|
|
|
+ BigDecimal layersCompletionRatio = actualLayers.divide(planLayers, 4, RoundingMode.HALF_UP);
|
|
|
+ // 将 完成比 作为单独的1个元素添加到 extProperty
|
|
|
+ IotTaskAttrModelProperty layersCompletionRatioProperty = new IotTaskAttrModelProperty();
|
|
|
+ layersCompletionRatioProperty.setName("层数完成比");
|
|
|
+ layersCompletionRatioProperty.setAccessMode("r");
|
|
|
+ layersCompletionRatioProperty.setRequired(0);
|
|
|
+ layersCompletionRatioProperty.setSort(3);
|
|
|
+ layersCompletionRatioProperty.setUnit("%");
|
|
|
+ layersCompletionRatioProperty.setDataType("double");
|
|
|
+ layersCompletionRatioProperty.setIdentifier("layersCompletionRatio");
|
|
|
+ layersCompletionRatioProperty.setActualValue(layersCompletionRatio.toString());
|
|
|
+ layersCompletionRatioProperty.setMaxValue(StrUtil.EMPTY);
|
|
|
+ layersCompletionRatioProperty.setMinValue(StrUtil.EMPTY);
|
|
|
+ layersCompletionRatioProperty.setDefaultValue(StrUtil.EMPTY);
|
|
|
+ properties.add(layersCompletionRatioProperty);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 瑞鹰 瑞都 同时 具备 井次 工作量
|
|
|
+ if (Long.valueOf(163).equals(meeting.getDeptId()) || Long.valueOf(158).equals(meeting.getDeptId())) {
|
|
|
+ // 计划井次
|
|
|
+ BigDecimal planWellTrips = getActualValue(properties, "planWellTrips");
|
|
|
+ // 实际井次
|
|
|
+ BigDecimal actualWellTrips = getActualValue(properties, "actualWellTrips");
|
|
|
+ if (planWellTrips.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
+ // 井次完成比 wellTripsCompletionRatio = 实际井次/计划井次 23.85%
|
|
|
+ BigDecimal wellTripsCompletionRatio = actualWellTrips.divide(planWellTrips, 4, RoundingMode.HALF_UP);
|
|
|
+ // 将 完成比 作为单独的1个元素添加到 extProperty
|
|
|
+ IotTaskAttrModelProperty wellTripsCompletionRatioProperty = new IotTaskAttrModelProperty();
|
|
|
+ wellTripsCompletionRatioProperty.setName("井次完成比");
|
|
|
+ wellTripsCompletionRatioProperty.setAccessMode("r");
|
|
|
+ wellTripsCompletionRatioProperty.setRequired(0);
|
|
|
+ wellTripsCompletionRatioProperty.setSort(7);
|
|
|
+ wellTripsCompletionRatioProperty.setUnit("%");
|
|
|
+ wellTripsCompletionRatioProperty.setDataType("double");
|
|
|
+ wellTripsCompletionRatioProperty.setIdentifier("wellTripsCompletionRatio");
|
|
|
+ wellTripsCompletionRatioProperty.setActualValue(wellTripsCompletionRatio.toString());
|
|
|
+ wellTripsCompletionRatioProperty.setMaxValue(StrUtil.EMPTY);
|
|
|
+ wellTripsCompletionRatioProperty.setMinValue(StrUtil.EMPTY);
|
|
|
+ wellTripsCompletionRatioProperty.setDefaultValue(StrUtil.EMPTY);
|
|
|
+ properties.add(wellTripsCompletionRatioProperty);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 每个专业公司设备利用率计算逻辑相同 utilizationRate
|
|
|
+ // 设备利用率 环比 (当前期利用率 - 上一期利用率) / 上一期利用率
|
|
|
+ // 计算当前期次会议 当前项目的设备利用率 施工设备数量/投运设备数量 已经通过前端 计算赋值
|
|
|
+ BigDecimal utilizationRate = getActualValue(properties, "utilizationRate");
|
|
|
+
|
|
|
+ String projectName = detail.getProjectName();
|
|
|
+ BigDecimal beforeUtilizationRate = BigDecimal.ZERO;
|
|
|
+ if (StrUtil.isNotBlank(projectName) && projectUtilizationPair.containsKey(projectName)) {
|
|
|
+ // 上一期次的设备利用率
|
|
|
+ beforeUtilizationRate = projectUtilizationPair.get(projectName);
|
|
|
+ }
|
|
|
+ if (beforeUtilizationRate.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
+ BigDecimal periodOnPeriod = utilizationRate.subtract(beforeUtilizationRate)
|
|
|
+ .divide(beforeUtilizationRate, 4, RoundingMode.HALF_UP);
|
|
|
+ // 将 设备利用率 环比 作为单独的1个元素添加到 extProperty
|
|
|
+ IotTaskAttrModelProperty periodOnPeriodProperty = new IotTaskAttrModelProperty();
|
|
|
+ periodOnPeriodProperty.setName("环比");
|
|
|
+ periodOnPeriodProperty.setAccessMode("r");
|
|
|
+ periodOnPeriodProperty.setRequired(0);
|
|
|
+ periodOnPeriodProperty.setSort(8);
|
|
|
+ periodOnPeriodProperty.setUnit("%");
|
|
|
+ periodOnPeriodProperty.setDataType("double");
|
|
|
+ periodOnPeriodProperty.setIdentifier("periodOnPeriod");
|
|
|
+ periodOnPeriodProperty.setActualValue(periodOnPeriod.toString());
|
|
|
+ periodOnPeriodProperty.setMaxValue(StrUtil.EMPTY);
|
|
|
+ periodOnPeriodProperty.setMinValue(StrUtil.EMPTY);
|
|
|
+ periodOnPeriodProperty.setDefaultValue(StrUtil.EMPTY);
|
|
|
+ properties.add(periodOnPeriodProperty);
|
|
|
+ }
|
|
|
+
|
|
|
+ detail.setExtProperty(properties);
|
|
|
+ }
|
|
|
IotOperationMeetingDetailDO detailDO = BeanUtils.toBean(detail, IotOperationMeetingDetailDO.class);
|
|
|
detailDO.setId(null);
|
|
|
detailDO.setMeetingId(meetingIds.get(0));
|
|
|
- detailDO.setDeptId(meeting.getDeptId());
|
|
|
+ detailDO.setDeptId(meeting.getDeptId()); // 所属公司
|
|
|
detailDO.setSort(meeting.getSort()); // 期次索引
|
|
|
meetingDetails.add(detailDO);
|
|
|
});
|
|
|
@@ -191,6 +360,29 @@ public class IotOperationMeetingServiceImpl implements IotOperationMeetingServic
|
|
|
return resultId;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 从工作量扩展属性中获取 指定名称的值
|
|
|
+ */
|
|
|
+ private BigDecimal getActualValue(List<IotTaskAttrModelProperty> properties, String identifier) {
|
|
|
+ // 空值直接返回 0
|
|
|
+ if (CollUtil.isEmpty(properties)) {
|
|
|
+ return BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Java 8 流式优雅查找目标属性
|
|
|
+ return properties.stream()
|
|
|
+ // 过滤标识符 = planGasInjection
|
|
|
+ .filter(property -> identifier.equals(property.getIdentifier()))
|
|
|
+ // 提取值
|
|
|
+ .map(IotTaskAttrModelProperty::getActualValue)
|
|
|
+ .filter(StrUtil::isNotBlank)
|
|
|
+ // 只取第一个匹配的
|
|
|
+ .findFirst()
|
|
|
+ // 转成 BigDecimal,无值则返回 0
|
|
|
+ .map(BigDecimal::new)
|
|
|
+ .orElse(BigDecimal.ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
public Set<String> cachedProjects() {
|
|
|
Set<String> projectNames = new HashSet<>();
|
|
|
@@ -378,6 +570,24 @@ public class IotOperationMeetingServiceImpl implements IotOperationMeetingServic
|
|
|
if (CollUtil.isEmpty(meetings)) {
|
|
|
return resultMeetings;
|
|
|
}
|
|
|
+
|
|
|
+ // 查询 当年 上一期次的会议财务数据 汇总后计算 收入 挂账 回款 环比
|
|
|
+ Set<Long> beforeCompanyIds = new HashSet<>();
|
|
|
+ IotOperationMeetingDetailPageReqVO beforeReqVO = new IotOperationMeetingDetailPageReqVO();
|
|
|
+ beforeReqVO.setYear(year);
|
|
|
+ beforeReqVO.setSort(reqVO.getSort() - 1);
|
|
|
+ List<IotOperationMeetingDetailDO> beforeSummarizedDetails = iotOperationMeetingDetailMapper.summarizedProjectDetails(beforeReqVO);
|
|
|
+ if (CollUtil.isNotEmpty(beforeSummarizedDetails)) {
|
|
|
+ // 如果上期次存在数据 才比较 财务数据 环比
|
|
|
+ beforeSummarizedDetails.forEach(detail -> {
|
|
|
+ beforeCompanyIds.add(detail.getDeptId());
|
|
|
+ });
|
|
|
+ }
|
|
|
+ // 组装每个专业公司各项目的累加财务数据 本期 收入 挂账 回款 并计算 环比
|
|
|
+ Map<Long, BigDecimal> revenuePair = new HashMap<>();
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
// 组装每个会议的需要集团支持的事项
|
|
|
Map<Long, IotOperationMeetingDO> supportPair = new HashMap<>();
|
|
|
meetings.forEach(meeting -> {
|
|
|
@@ -439,4 +649,44 @@ public class IotOperationMeetingServiceImpl implements IotOperationMeetingServic
|
|
|
return Collections.emptySet();
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public List<IotTaskAttrModelProperty> currentCompanyExtProperties() {
|
|
|
+ List<IotTaskAttrModelProperty> workloadProperties = new ArrayList<>();
|
|
|
+ // 当前登录人所属公司的deptId 关联表 rq_iot_operation_meeting_attrs
|
|
|
+ Long loginUserDeptId = SecurityFrameworkUtils.getLoginUserDeptId();
|
|
|
+ DeptDO dept = deptservice.getDept(loginUserDeptId);
|
|
|
+ if (ObjUtil.isEmpty(dept)) {
|
|
|
+ throw exception(DEPT_NOT_FOUND);
|
|
|
+ }
|
|
|
+ IotOperationMeetingAttrsPageReqVO reqVO = new IotOperationMeetingAttrsPageReqVO();
|
|
|
+ reqVO.setDeptId(dept.getId());
|
|
|
+ reqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
|
|
+ PageResult<IotOperationMeetingAttrsDO> pageAttrs = iotOperationMeetingAttrsMapper.selectPage(reqVO);
|
|
|
+ if (ObjUtil.isEmpty(pageAttrs)) {
|
|
|
+ throw exception(IOT_OPERATION_MEETING_ATTRS_NOT_EXISTS);
|
|
|
+ }
|
|
|
+ List<IotOperationMeetingAttrsDO> attrs = pageAttrs.getList();
|
|
|
+ if (CollUtil.isEmpty(attrs)) {
|
|
|
+ throw exception(IOT_OPERATION_MEETING_ATTRS_NOT_EXISTS);
|
|
|
+ }
|
|
|
+ attrs.forEach(attr -> {
|
|
|
+ // 下期工作计划 标识 defaultValue = next
|
|
|
+ // 可见性 标识 required = 1
|
|
|
+ // 只读 标识 accessMode = r
|
|
|
+ // 可读可写 标识 accessMode = rw
|
|
|
+ IotTaskAttrModelProperty property = attr.getExtProperty();
|
|
|
+ if (ObjUtil.isNotEmpty(property) && ObjUtil.isNotEmpty(property.getRequired()) && 1 == property.getRequired()) {
|
|
|
+ workloadProperties.add(attr.getExtProperty());
|
|
|
+ }
|
|
|
+ });
|
|
|
+ // 按照 sort 值 升序 排序
|
|
|
+ if (CollUtil.isNotEmpty(workloadProperties)) {
|
|
|
+ workloadProperties.sort(Comparator.comparing(
|
|
|
+ IotTaskAttrModelProperty::getSort,
|
|
|
+ Comparator.nullsLast(Comparator.naturalOrder())
|
|
|
+ ));
|
|
|
+ }
|
|
|
+ return workloadProperties;
|
|
|
+ }
|
|
|
+
|
|
|
}
|