|
|
@@ -1,13 +1,31 @@
|
|
|
package cn.iocoder.yudao.module.pms.service.iotoperationplan;
|
|
|
|
|
|
+import cn.iocoder.yudao.module.pms.constant.PmsConstants;
|
|
|
+import cn.iocoder.yudao.module.pms.controller.admin.vo.IotDeviceRespVO;
|
|
|
+import cn.iocoder.yudao.module.pms.dal.dataobject.IotDeviceDO;
|
|
|
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotopeationfill.IotOpeationFillDO;
|
|
|
+import cn.iocoder.yudao.module.pms.dal.dataobject.iotopeationfill.IotOpeationFillOrderDO;
|
|
|
import cn.iocoder.yudao.module.pms.dal.dataobject.iotoperationplandev.IotOperationPlanDevDO;
|
|
|
+import cn.iocoder.yudao.module.pms.dal.mysql.IotDeviceMapper;
|
|
|
+import cn.iocoder.yudao.module.pms.dal.mysql.iotopeationfill.IotOpeationFillMapper;
|
|
|
+import cn.iocoder.yudao.module.pms.dal.mysql.iotopeationfill.IotOpeationFillOrderMapper;
|
|
|
import cn.iocoder.yudao.module.pms.dal.mysql.iotoperationplandev.IotOperationPlanDevMapper;
|
|
|
-import com.google.common.collect.ImmutableBiMap;
|
|
|
+import cn.iocoder.yudao.module.pms.message.PmsMessage;
|
|
|
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
|
|
|
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
|
|
+import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
|
|
|
+import cn.iocoder.yudao.module.system.service.dept.DeptService;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import javax.annotation.Resource;
|
|
|
import org.springframework.validation.annotation.Validated;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
+import org.springframework.transaction.support.TransactionSynchronization;
|
|
|
+import org.springframework.transaction.support.TransactionSynchronizationManager;
|
|
|
|
|
|
+import java.time.LocalDate;
|
|
|
+import java.time.LocalDateTime;
|
|
|
import java.util.*;
|
|
|
import cn.iocoder.yudao.module.pms.controller.admin.iotoperationplan.vo.*;
|
|
|
import cn.iocoder.yudao.module.pms.dal.dataobject.iotoperationplan.IotOperationPlanDO;
|
|
|
@@ -18,7 +36,9 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
|
|
import cn.iocoder.yudao.module.pms.dal.mysql.iotoperationplan.IotOperationPlanMapper;
|
|
|
|
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
|
+import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_DEVICE_NOT_EXISTS;
|
|
|
import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_OPERATION_PLAN_NOT_EXISTS;
|
|
|
+import static cn.iocoder.yudao.module.pms.framework.config.MultiThreadConfiguration.PMS_THREAD_POOL_TASK_EXECUTOR;
|
|
|
|
|
|
|
|
|
/**
|
|
|
@@ -28,12 +48,27 @@ import static cn.iocoder.yudao.module.pms.enums.ErrorCodeConstant.IOT_OPERATION_
|
|
|
*/
|
|
|
@Service
|
|
|
@Validated
|
|
|
+@Slf4j
|
|
|
public class IotOperationPlanServiceImpl implements IotOperationPlanService {
|
|
|
|
|
|
@Resource
|
|
|
private IotOperationPlanMapper iotOperationPlanMapper;
|
|
|
@Resource
|
|
|
private IotOperationPlanDevMapper planDevMapper;
|
|
|
+ @Resource
|
|
|
+ private IotDeviceMapper iotDeviceMapper;
|
|
|
+ @Resource
|
|
|
+ private IotOpeationFillMapper iotOpeationFillMapper;
|
|
|
+ @Resource
|
|
|
+ private IotOpeationFillOrderMapper iotOpeationFillOrderMapper;
|
|
|
+ @Resource
|
|
|
+ private DeptService deptService;
|
|
|
+ @Resource
|
|
|
+ private AdminUserApi adminUserApi;
|
|
|
+ @Resource
|
|
|
+ private PmsMessage pmsMessage;
|
|
|
+ @Resource(name = PMS_THREAD_POOL_TASK_EXECUTOR)
|
|
|
+ private ThreadPoolTaskExecutor pmsThreadPoolTaskExecutor;
|
|
|
|
|
|
@Override
|
|
|
public Long createIotOperationPlan(IotOperationPlanSaveReqVO createReqVO) {
|
|
|
@@ -100,4 +135,190 @@ public class IotOperationPlanServiceImpl implements IotOperationPlanService {
|
|
|
iotOperationPlanMapper.updateById(planDO);
|
|
|
}
|
|
|
|
|
|
-}
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public String manualGenerateByDeviceId(Long deviceId) {
|
|
|
+ IotDeviceDO device = iotDeviceMapper.selectById(deviceId);
|
|
|
+ if (device == null || device.getDeptId() == null) {
|
|
|
+ throw exception(IOT_DEVICE_NOT_EXISTS);
|
|
|
+ }
|
|
|
+
|
|
|
+ Long deptId = device.getDeptId();
|
|
|
+ DeptDO dept = deptService.getDeptNoPermission(deptId);
|
|
|
+ if (dept == null) {
|
|
|
+ throw exception(IOT_DEVICE_NOT_EXISTS);
|
|
|
+ }
|
|
|
+
|
|
|
+ IotOperationPlanDO plan = findNearestEnabledPlan(dept);
|
|
|
+ if (plan == null) {
|
|
|
+ throw exception(IOT_OPERATION_PLAN_NOT_EXISTS);
|
|
|
+ }
|
|
|
+
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
+ LocalDateTime beginTime = LocalDate.now().atStartOfDay();
|
|
|
+ LocalDateTime endTime = beginTime.plusDays(1);
|
|
|
+ boolean deviceGeneratedToday = !iotOpeationFillMapper
|
|
|
+ .selectOrderIdsByDeviceAndCreateTime(deviceId, beginTime, endTime).isEmpty();
|
|
|
+
|
|
|
+ // 已经生成过的设备允许重建当天记录;尚未生成时必须先到达计划生成时间。
|
|
|
+ if (!deviceGeneratedToday && now.isBefore(calculateNextCreateTime(plan))) {
|
|
|
+ return "未到运行记录生成时间,无需生成";
|
|
|
+ }
|
|
|
+ if (!deviceGeneratedToday && !"sg".equals(device.getDeviceStatus())) {
|
|
|
+ return "当前设备不在施工状态,无需生成";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设备只是定位入口。重建时删除并重建它所在小部门当天的全部运行记录。
|
|
|
+ List<Long> oldOrderIds = iotOpeationFillMapper
|
|
|
+ .selectOrderIdsByDeptAndCreateTime(deptId, beginTime, endTime);
|
|
|
+ Set<Long> runLogDeviceIds = new HashSet<>();
|
|
|
+ runLogDeviceIds.add(deviceId);
|
|
|
+ if (!oldOrderIds.isEmpty()) {
|
|
|
+ runLogDeviceIds.addAll(iotOpeationFillMapper.selectDeviceIdsByOrderIds(oldOrderIds));
|
|
|
+ iotOpeationFillMapper.deleteManualRunLogs(runLogDeviceIds, beginTime, endTime);
|
|
|
+ iotOpeationFillMapper.logicalDeleteByOrderIds(oldOrderIds);
|
|
|
+ iotOpeationFillMapper.logicalDeleteOrdersByIds(oldOrderIds);
|
|
|
+ } else {
|
|
|
+ iotOpeationFillMapper.deleteManualRunLogs(runLogDeviceIds, beginTime, endTime);
|
|
|
+ }
|
|
|
+
|
|
|
+ List<IotDeviceRespVO> constructionDevices = iotOpeationFillMapper
|
|
|
+ .getFillDevices2(Collections.singleton(deptId));
|
|
|
+ if (constructionDevices.isEmpty()) {
|
|
|
+ return oldOrderIds.isEmpty()
|
|
|
+ ? "当前小部门没有可生成运行记录的施工设备"
|
|
|
+ : "已删除原运行记录,当前小部门没有施工设备";
|
|
|
+ }
|
|
|
+
|
|
|
+ List<Long> constructionDeviceIds = new ArrayList<>(constructionDevices.size());
|
|
|
+ for (IotDeviceRespVO constructionDevice : constructionDevices) {
|
|
|
+ constructionDeviceIds.add(constructionDevice.getId());
|
|
|
+ }
|
|
|
+ List<IotOpeationFillDO> fillDevices = iotOpeationFillMapper.getFillDevices(constructionDeviceIds);
|
|
|
+ List<IotOpeationFillOrderDO> newOrders = createOrders(dept, fillDevices, now);
|
|
|
+ if (newOrders.isEmpty()) {
|
|
|
+ return "当前小部门的施工设备未配置负责人,无法生成运行记录";
|
|
|
+ }
|
|
|
+
|
|
|
+ iotOpeationFillOrderMapper.insertBatch(newOrders);
|
|
|
+ Map<Integer, Long> orderIdByUserId = new HashMap<>();
|
|
|
+ for (IotOpeationFillOrderDO order : newOrders) {
|
|
|
+ orderIdByUserId.put(order.getUserId(), order.getId());
|
|
|
+ }
|
|
|
+
|
|
|
+ List<IotOpeationFillDO> newFills = new ArrayList<>();
|
|
|
+ for (IotOpeationFillDO fillDevice : fillDevices) {
|
|
|
+ Long orderId = orderIdByUserId.get(fillDevice.getUserId());
|
|
|
+ if (orderId == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ fillDevice.setDeviceId(fillDevice.getId());
|
|
|
+ fillDevice.setDeptId(deptId);
|
|
|
+ fillDevice.setOrderId(orderId);
|
|
|
+ fillDevice.setCreateTime(now);
|
|
|
+ fillDevice.setIsReport(0);
|
|
|
+ newFills.add(fillDevice);
|
|
|
+ }
|
|
|
+ if (!newFills.isEmpty()) {
|
|
|
+ iotOpeationFillMapper.insertFill(newFills);
|
|
|
+ }
|
|
|
+ sendMessagesAfterCommit(newOrders);
|
|
|
+
|
|
|
+ return oldOrderIds.isEmpty() ? "运行记录生成成功" : "运行记录重新生成成功";
|
|
|
+ }
|
|
|
+
|
|
|
+ private IotOperationPlanDO findNearestEnabledPlan(DeptDO dept) {
|
|
|
+ List<Long> ancestorDeptIds = new ArrayList<>();
|
|
|
+ Set<Long> visitedDeptIds = new HashSet<>();
|
|
|
+ DeptDO current = dept;
|
|
|
+ while (current != null && visitedDeptIds.add(current.getId())) {
|
|
|
+ ancestorDeptIds.add(current.getId());
|
|
|
+ if (current.getParentId() == null || DeptDO.PARENT_ID_ROOT.equals(current.getParentId())) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ current = deptService.getDeptNoPermission(current.getParentId());
|
|
|
+ }
|
|
|
+
|
|
|
+ List<IotOperationPlanDO> plans = iotOperationPlanMapper.selectEnabledListByDeptIds(ancestorDeptIds);
|
|
|
+ for (Long ancestorDeptId : ancestorDeptIds) {
|
|
|
+ for (IotOperationPlanDO plan : plans) {
|
|
|
+ if (ancestorDeptId.equals(plan.getDeptId())) {
|
|
|
+ return plan;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ private LocalDateTime calculateNextCreateTime(IotOperationPlanDO plan) {
|
|
|
+ LocalDateTime baseTime = plan.getLastCreateTime() != null
|
|
|
+ ? plan.getLastCreateTime() : plan.getBeginCreateTime();
|
|
|
+ if (baseTime == null || plan.getPlanCycle() == null || plan.getPlanCycle().intValue() <= 0) {
|
|
|
+ throw exception(IOT_OPERATION_PLAN_NOT_EXISTS);
|
|
|
+ }
|
|
|
+
|
|
|
+ int cycle = plan.getPlanCycle().intValue();
|
|
|
+ if ("hour".equals(plan.getPlanUnit())) {
|
|
|
+ return baseTime.plusHours(cycle);
|
|
|
+ }
|
|
|
+ if ("day".equals(plan.getPlanUnit())) {
|
|
|
+ return baseTime.plusDays(cycle);
|
|
|
+ }
|
|
|
+ if ("month".equals(plan.getPlanUnit())) {
|
|
|
+ return baseTime.plusMonths(cycle);
|
|
|
+ }
|
|
|
+ throw exception(IOT_OPERATION_PLAN_NOT_EXISTS);
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<IotOpeationFillOrderDO> createOrders(DeptDO dept, List<IotOpeationFillDO> fillDevices,
|
|
|
+ LocalDateTime createTime) {
|
|
|
+ Set<Integer> userIds = new LinkedHashSet<>();
|
|
|
+ for (IotOpeationFillDO fillDevice : fillDevices) {
|
|
|
+ if (fillDevice.getUserId() != null) {
|
|
|
+ userIds.add(fillDevice.getUserId());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ List<IotOpeationFillOrderDO> orders = new ArrayList<>(userIds.size());
|
|
|
+ for (Integer userId : userIds) {
|
|
|
+ AdminUserRespDTO user = adminUserApi.getUser(userId.longValue());
|
|
|
+ if (user == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ IotOpeationFillOrderDO order = new IotOpeationFillOrderDO();
|
|
|
+ order.setOrderName(dept.getName() + "/" + createTime.toLocalDate() + "运行记录填报");
|
|
|
+ order.setDeptId(dept.getId());
|
|
|
+ order.setOrderStatus(0);
|
|
|
+ order.setCreateTime(createTime);
|
|
|
+ order.setUserName(user.getNickname());
|
|
|
+ order.setUserId(userId);
|
|
|
+ order.setMobile(user.getMobile());
|
|
|
+ orders.add(order);
|
|
|
+ }
|
|
|
+ return orders;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void sendMessagesAfterCommit(List<IotOpeationFillOrderDO> orders) {
|
|
|
+ Runnable task = () -> pmsThreadPoolTaskExecutor.execute(() -> {
|
|
|
+ for (IotOpeationFillOrderDO order : orders) {
|
|
|
+ try {
|
|
|
+ pmsMessage.sendMessage(order.getId(), order.getOrderName(), PmsConstants.GENERATE_OPERATION,
|
|
|
+ order.getUserId().longValue(), order.getMobile());
|
|
|
+ } catch (Exception ex) {
|
|
|
+ log.error("手动生成运行记录后发送通知失败,orderId={}", order.getId(), ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ if (TransactionSynchronizationManager.isSynchronizationActive()) {
|
|
|
+ TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
|
|
+ @Override
|
|
|
+ public void afterCommit() {
|
|
|
+ task.run();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ task.run();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|