From 93de43267e1663031fe5dc2f5ae40d128a182a76 Mon Sep 17 00:00:00 2001
From: doum <doum>
Date: 星期四, 18 六月 2026 17:24:51 +0800
Subject: [PATCH] 新增智能电表、空调管理

---
 server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerDeviceAutoBindServiceImpl.java |  281 +++++++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 237 insertions(+), 44 deletions(-)

diff --git a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerDeviceAutoBindServiceImpl.java b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerDeviceAutoBindServiceImpl.java
index 07a3d71..3fdaadc 100644
--- a/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerDeviceAutoBindServiceImpl.java
+++ b/server/visits/dmvisit_service/src/main/java/com/doumee/service/business/impl/YwCustomerDeviceAutoBindServiceImpl.java
@@ -2,10 +2,10 @@
 
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
-import com.doumee.core.constants.ResponseStatus;
 import com.doumee.core.exception.BusinessException;
 import com.doumee.core.model.LoginUserInfo;
 import com.doumee.core.utils.Constants;
+import com.doumee.core.utils.Utils;
 import com.doumee.dao.business.*;
 import com.doumee.dao.business.dto.YwCustomerGsConfigDTO;
 import com.doumee.dao.business.model.*;
@@ -13,6 +13,7 @@
 import com.doumee.service.business.YwCustomerRechargeBizService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
@@ -40,6 +41,7 @@
     @Autowired
     private YwConditionerMapper ywConditionerMapper;
     @Autowired
+    @Lazy
     private YwCustomerRechargeBizService ywCustomerRechargeBizService;
 
     @Override
@@ -49,38 +51,52 @@
             return;
         }
         YwContract contract = ywContractMapper.selectById(contractId);
-        if (contract == null || Objects.equals(contract.getIsdeleted(), Constants.ONE)) {
+        if (contract == null || Objects.equals(contract.getIsdeleted(), Constants.ONE) || contract.getRenterId() == null) {
             return;
         }
-        if (contract.getRenterId() == null) {
-            return;
-        }
-        if (!isActiveContract(contract)) {
-            return;
-        }
-        List<Integer> roomIds = listContractRoomIds(contractId);
-        if (roomIds.isEmpty()) {
-            return;
-        }
-        bindElectricals(contract, roomIds, user);
-        bindConditioners(contract, roomIds, user);
+        refreshCustomerDevices(contract.getRenterId(), user);
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void syncByCustomerId(Integer customerId, LoginUserInfo user) {
+        refreshCustomerDevices(customerId, user);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void refreshCustomerDevices(Integer customerId, LoginUserInfo user) {
         if (customerId == null) {
             return;
         }
+        LoginUserInfo opUser = user != null ? user : systemUser();
         List<YwContract> contracts = ywContractMapper.selectList(new QueryWrapper<YwContract>().lambda()
                 .eq(YwContract::getRenterId, customerId)
-                .eq(YwContract::getIsdeleted, Constants.ZERO)
-                .in(YwContract::getStatus, Arrays.asList(Constants.ZERO, Constants.ONE, Constants.THREE)));
-        for (YwContract c : contracts) {
-            if (isActiveContract(c)) {
-                syncByContractId(c.getId(), user);
+                .eq(YwContract::getIsdeleted, Constants.ZERO));
+        List<YwContract> activeContracts = contracts.stream().filter(this::isActiveContract).collect(Collectors.toList());
+        Set<Integer> activeContractIds = activeContracts.stream().map(YwContract::getId).collect(Collectors.toSet());
+
+        for (YwContract contract : contracts) {
+            if (!activeContractIds.contains(contract.getId())) {
+                unbindByContractId(contract.getId(), opUser);
             }
         }
+
+        Set<Integer> roomIds = new LinkedHashSet<>();
+        for (YwContract contract : activeContracts) {
+            roomIds.addAll(listContractRoomIds(contract.getId()));
+        }
+        if (roomIds.isEmpty()) {
+            clearContractElectricalBindings(customerId, opUser);
+            softDeleteAllConditionerRels(customerId, opUser);
+            return;
+        }
+
+        List<Integer> roomIdList = new ArrayList<>(roomIds);
+        for (YwContract contract : activeContracts) {
+            bindElectricals(contract, listContractRoomIds(contract.getId()), opUser);
+        }
+        bindConditionersMerged(customerId, roomIdList, opUser);
     }
 
     @Override
@@ -100,14 +116,153 @@
                 .eq(YwCustomerElectrical::getIsdeleted, Constants.ZERO));
     }
 
+    @Override
+    public List<Integer> listActiveContractRoomIds(Integer customerId) {
+        if (customerId == null) {
+            return Collections.emptyList();
+        }
+        List<YwContract> active = listActiveContracts(customerId);
+        Set<Integer> roomIds = new LinkedHashSet<>();
+        for (YwContract contract : active) {
+            roomIds.addAll(listContractRoomIds(contract.getId()));
+        }
+        return new ArrayList<>(roomIds);
+    }
+
+    @Override
+    public List<Integer> listElectricalIdsByActiveContracts(Integer customerId) {
+        List<Integer> roomIds = listActiveContractRoomIds(customerId);
+        if (roomIds.isEmpty()) {
+            return Collections.emptyList();
+        }
+        return new ArrayList<>(resolveElectricalIdsFromRooms(roomIds));
+    }
+
+    @Override
+    public List<Integer> listConditionerIdsByActiveContracts(Integer customerId) {
+        List<Integer> roomIds = listActiveContractRoomIds(customerId);
+        if (roomIds.isEmpty()) {
+            return Collections.emptyList();
+        }
+        return new ArrayList<>(resolveConditionerIdsFromRooms(roomIds));
+    }
+
+    @Override
+    public Map<Integer, List<Integer>> batchListElectricalIdsByActiveContracts(List<Integer> customerIds) {
+        return batchResolveDeviceIds(customerIds, Constants.ZERO);
+    }
+
+    @Override
+    public Map<Integer, List<Integer>> batchListConditionerIdsByActiveContracts(List<Integer> customerIds) {
+        return batchResolveDeviceIds(customerIds, Constants.ONE);
+    }
+
+    private Map<Integer, List<Integer>> batchResolveDeviceIds(List<Integer> customerIds, int deviceType) {
+        if (CollectionUtils.isEmpty(customerIds)) {
+            return Collections.emptyMap();
+        }
+        Map<Integer, Set<Integer>> roomsByCustomer = batchActiveContractRoomIds(customerIds);
+        Set<Integer> allRoomIds = roomsByCustomer.values().stream()
+                .flatMap(Set::stream).collect(Collectors.toCollection(LinkedHashSet::new));
+        Map<Integer, Set<Integer>> devicesByRoom = mapDevicesByRoom(allRoomIds, deviceType);
+
+        Map<Integer, List<Integer>> result = new LinkedHashMap<>();
+        for (Integer customerId : customerIds) {
+            Set<Integer> roomIds = roomsByCustomer.getOrDefault(customerId, Collections.emptySet());
+            Set<Integer> deviceIds = new LinkedHashSet<>();
+            for (Integer roomId : roomIds) {
+                deviceIds.addAll(devicesByRoom.getOrDefault(roomId, Collections.emptySet()));
+            }
+            result.put(customerId, new ArrayList<>(deviceIds));
+        }
+        return result;
+    }
+
+    private Map<Integer, Set<Integer>> batchActiveContractRoomIds(List<Integer> customerIds) {
+        List<YwContract> contracts = ywContractMapper.selectList(new QueryWrapper<YwContract>().lambda()
+                .in(YwContract::getRenterId, customerIds)
+                .eq(YwContract::getIsdeleted, Constants.ZERO));
+        Map<Integer, List<YwContract>> activeByCustomer = contracts.stream()
+                .filter(this::isActiveContract)
+                .collect(Collectors.groupingBy(YwContract::getRenterId));
+
+        List<Integer> contractIds = activeByCustomer.values().stream()
+                .flatMap(List::stream).map(YwContract::getId).distinct().collect(Collectors.toList());
+        Map<Integer, List<Integer>> roomsByContract = loadContractRoomMap(contractIds);
+
+        Map<Integer, Set<Integer>> roomsByCustomer = new LinkedHashMap<>();
+        for (Integer customerId : customerIds) {
+            Set<Integer> roomIds = new LinkedHashSet<>();
+            for (YwContract contract : activeByCustomer.getOrDefault(customerId, Collections.emptyList())) {
+                roomIds.addAll(roomsByContract.getOrDefault(contract.getId(), Collections.emptyList()));
+            }
+            roomsByCustomer.put(customerId, roomIds);
+        }
+        return roomsByCustomer;
+    }
+
+    private Map<Integer, List<Integer>> loadContractRoomMap(List<Integer> contractIds) {
+        if (CollectionUtils.isEmpty(contractIds)) {
+            return Collections.emptyMap();
+        }
+        return ywContractRoomMapper.selectList(new QueryWrapper<YwContractRoom>().lambda()
+                        .in(YwContractRoom::getContractId, contractIds)
+                        .eq(YwContractRoom::getType, Constants.ZERO)
+                        .eq(YwContractRoom::getIsdeleted, Constants.ZERO))
+                .stream()
+                .collect(Collectors.groupingBy(YwContractRoom::getContractId,
+                        Collectors.mapping(YwContractRoom::getRoomId,
+                                Collectors.collectingAndThen(Collectors.toList(),
+                                        list -> list.stream().filter(Objects::nonNull).distinct().collect(Collectors.toList())))));
+    }
+
+    private Map<Integer, Set<Integer>> mapDevicesByRoom(Set<Integer> roomIds, int deviceType) {
+        if (CollectionUtils.isEmpty(roomIds)) {
+            return Collections.emptyMap();
+        }
+        List<Integer> roomIdList = new ArrayList<>(roomIds);
+        Map<Integer, Set<Integer>> result = new HashMap<>();
+        List<YwElectricalRoom> relRooms = ywElectricalRoomMapper.selectList(new QueryWrapper<YwElectricalRoom>().lambda()
+                .in(YwElectricalRoom::getRoomId, roomIdList)
+                .eq(YwElectricalRoom::getType, deviceType)
+                .eq(YwElectricalRoom::getIsdeleted, Constants.ZERO));
+        for (YwElectricalRoom rel : relRooms) {
+            if (rel.getRoomId() == null || rel.getObjId() == null) {
+                continue;
+            }
+            result.computeIfAbsent(rel.getRoomId(), k -> new LinkedHashSet<>()).add(rel.getObjId());
+        }
+        if (deviceType == Constants.ONE) {
+            List<YwConditioner> byRoom = ywConditionerMapper.selectList(new QueryWrapper<YwConditioner>().lambda()
+                    .in(YwConditioner::getRoomId, roomIdList)
+                    .eq(YwConditioner::getIsdeleted, Constants.ZERO));
+            for (YwConditioner conditioner : byRoom) {
+                if (conditioner.getRoomId() == null || conditioner.getId() == null) {
+                    continue;
+                }
+                result.computeIfAbsent(conditioner.getRoomId(), k -> new LinkedHashSet<>()).add(conditioner.getId());
+            }
+        }
+        return result;
+    }
+
+    private List<YwContract> listActiveContracts(Integer customerId) {
+        return ywContractMapper.selectList(new QueryWrapper<YwContract>().lambda()
+                        .eq(YwContract::getRenterId, customerId)
+                        .eq(YwContract::getIsdeleted, Constants.ZERO))
+                .stream().filter(this::isActiveContract).collect(Collectors.toList());
+    }
+
     private boolean isActiveContract(YwContract contract) {
-        if (contract.getStartDate() == null || contract.getEndDate() == null) {
+        if (contract == null || contract.getStartDate() == null || contract.getEndDate() == null) {
+            return false;
+        }
+        if (Objects.equals(contract.getStatus(), Constants.FOUR)) {
             return false;
         }
         long now = System.currentTimeMillis();
         return contract.getStartDate().getTime() <= now
-                && contract.getEndDate().getTime() >= now
-                && !Objects.equals(contract.getStatus(), Constants.FOUR);
+                && Utils.Date.getEnd(contract.getEndDate()).getTime() >= now;
     }
 
     private List<Integer> listContractRoomIds(Integer contractId) {
@@ -119,13 +274,39 @@
                 .collect(Collectors.toList());
     }
 
-    private void bindElectricals(YwContract contract, List<Integer> roomIds, LoginUserInfo user) {
-        List<YwElectricalRoom> relRooms = ywElectricalRoomMapper.selectList(new QueryWrapper<YwElectricalRoom>().lambda()
+    private Set<Integer> resolveElectricalIdsFromRooms(List<Integer> roomIds) {
+        if (CollectionUtils.isEmpty(roomIds)) {
+            return Collections.emptySet();
+        }
+        return ywElectricalRoomMapper.selectList(new QueryWrapper<YwElectricalRoom>().lambda()
+                        .in(YwElectricalRoom::getRoomId, roomIds)
+                        .eq(YwElectricalRoom::getType, Constants.ZERO)
+                        .eq(YwElectricalRoom::getIsdeleted, Constants.ZERO))
+                .stream().map(YwElectricalRoom::getObjId).filter(Objects::nonNull)
+                .collect(Collectors.toCollection(LinkedHashSet::new));
+    }
+
+    private Set<Integer> resolveConditionerIdsFromRooms(List<Integer> roomIds) {
+        if (CollectionUtils.isEmpty(roomIds)) {
+            return Collections.emptySet();
+        }
+        Set<Integer> conditionerIds = new LinkedHashSet<>();
+        List<YwElectricalRoom> acRooms = ywElectricalRoomMapper.selectList(new QueryWrapper<YwElectricalRoom>().lambda()
                 .in(YwElectricalRoom::getRoomId, roomIds)
-                .eq(YwElectricalRoom::getType, Constants.ZERO)
+                .eq(YwElectricalRoom::getType, Constants.ONE)
                 .eq(YwElectricalRoom::getIsdeleted, Constants.ZERO));
-        Set<Integer> electricalIds = relRooms.stream().map(YwElectricalRoom::getObjId)
-                .filter(Objects::nonNull).collect(Collectors.toCollection(LinkedHashSet::new));
+        acRooms.stream().map(YwElectricalRoom::getObjId).filter(Objects::nonNull).forEach(conditionerIds::add);
+        if (conditionerIds.isEmpty()) {
+            ywConditionerMapper.selectList(new QueryWrapper<YwConditioner>().lambda()
+                            .in(YwConditioner::getRoomId, roomIds)
+                            .eq(YwConditioner::getIsdeleted, Constants.ZERO))
+                    .stream().map(YwConditioner::getId).filter(Objects::nonNull).forEach(conditionerIds::add);
+        }
+        return conditionerIds;
+    }
+
+    private void bindElectricals(YwContract contract, List<Integer> roomIds, LoginUserInfo user) {
+        Set<Integer> electricalIds = resolveElectricalIdsFromRooms(roomIds);
         if (electricalIds.isEmpty()) {
             return;
         }
@@ -142,7 +323,8 @@
                     .eq(YwCustomerElectrical::getIsdeleted, Constants.ZERO)
                     .last("limit 1"));
             if (exist != null) {
-                if (exist.getContractId() == null) {
+                if (!Objects.equals(exist.getContractId(), contract.getId())
+                        || !Objects.equals(exist.getBindSource(), BIND_SOURCE_CONTRACT)) {
                     exist.setContractId(contract.getId());
                     exist.setBindSource(BIND_SOURCE_CONTRACT);
                     exist.setEditDate(now);
@@ -165,24 +347,14 @@
         }
     }
 
-    private void bindConditioners(YwContract contract, List<Integer> roomIds, LoginUserInfo user) {
-        Set<Integer> conditionerIds = new LinkedHashSet<>();
-        List<YwElectricalRoom> acRooms = ywElectricalRoomMapper.selectList(new QueryWrapper<YwElectricalRoom>().lambda()
-                .in(YwElectricalRoom::getRoomId, roomIds)
-                .eq(YwElectricalRoom::getType, Constants.ONE)
-                .eq(YwElectricalRoom::getIsdeleted, Constants.ZERO));
-        acRooms.stream().map(YwElectricalRoom::getObjId).filter(Objects::nonNull).forEach(conditionerIds::add);
+    private void bindConditionersMerged(Integer customerId, List<Integer> roomIds, LoginUserInfo user) {
+        Set<Integer> conditionerIds = resolveConditionerIdsFromRooms(roomIds);
         if (conditionerIds.isEmpty()) {
-            List<YwConditioner> byRoom = ywConditionerMapper.selectList(new QueryWrapper<YwConditioner>().lambda()
-                    .in(YwConditioner::getRoomId, roomIds)
-                    .eq(YwConditioner::getIsdeleted, Constants.ZERO));
-            byRoom.stream().map(YwConditioner::getId).forEach(conditionerIds::add);
-        }
-        if (conditionerIds.isEmpty()) {
+            softDeleteAllConditionerRels(customerId, user);
             return;
         }
         YwCustomerGsConfigDTO dto = new YwCustomerGsConfigDTO();
-        dto.setCustomerId(contract.getRenterId());
+        dto.setCustomerId(customerId);
         dto.setIsPwr(Constants.ONE);
         dto.setIsRestStop(Constants.ZERO);
         dto.setGsBz("鍚堝悓鑷姩鍏宠仈");
@@ -196,12 +368,33 @@
         }
         dto.setConditioners(items);
         try {
-            ywCustomerRechargeBizService.saveCustomerGsConfig(dto, user != null ? user : systemUser());
+            ywCustomerRechargeBizService.saveCustomerGsConfig(dto, user);
         } catch (BusinessException e) {
-            log.warn("auto bind conditioner GS failed contractId={}: {}", contract.getId(), e.getMessage());
+            log.warn("auto bind conditioner GS failed customerId={}: {}", customerId, e.getMessage());
         }
     }
 
+    private void clearContractElectricalBindings(Integer customerId, LoginUserInfo user) {
+        Date now = new Date();
+        ywCustomerElectricalMapper.update(null, new UpdateWrapper<YwCustomerElectrical>().lambda()
+                .set(YwCustomerElectrical::getIsdeleted, Constants.ONE)
+                .set(YwCustomerElectrical::getEditDate, now)
+                .set(YwCustomerElectrical::getEditor, user != null ? user.getId() : null)
+                .eq(YwCustomerElectrical::getCustomerId, customerId)
+                .eq(YwCustomerElectrical::getBindSource, BIND_SOURCE_CONTRACT)
+                .eq(YwCustomerElectrical::getIsdeleted, Constants.ZERO));
+    }
+
+    private void softDeleteAllConditionerRels(Integer customerId, LoginUserInfo user) {
+        Date now = new Date();
+        ywCustomerConditionerMapper.update(null, new UpdateWrapper<YwCustomerConditioner>().lambda()
+                .set(YwCustomerConditioner::getIsdeleted, Constants.ONE)
+                .set(YwCustomerConditioner::getEditDate, now)
+                .set(YwCustomerConditioner::getEditor, user != null ? user.getId() : null)
+                .eq(YwCustomerConditioner::getCustomerId, customerId)
+                .eq(YwCustomerConditioner::getIsdeleted, Constants.ZERO));
+    }
+
     private Set<Integer> listBoundElectricalIdsExcept(Integer customerId) {
         List<YwCustomerElectrical> list = ywCustomerElectricalMapper.selectList(new QueryWrapper<YwCustomerElectrical>().lambda()
                 .eq(YwCustomerElectrical::getIsdeleted, Constants.ZERO)

--
Gitblit v1.9.3