diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/cleaner/CleanerStatusService.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/cleaner/CleanerStatusService.java new file mode 100644 index 0000000..061574e --- /dev/null +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/cleaner/CleanerStatusService.java @@ -0,0 +1,118 @@ +package com.viewsh.module.ops.environment.service.cleaner; + +import com.viewsh.module.ops.dal.dataobject.cleaner.OpsCleanerStatusDO; +import com.viewsh.module.ops.enums.CleanerStatusEnum; + +import java.util.List; + +/** + * 保洁员状态服务 + * 属于保洁业务特定逻辑,放在 environment-biz 模块中 + * + * @author lzh + */ +public interface CleanerStatusService { + + /** + * 根据设备ID查询保洁员状态 + * + * @param deviceId 设备ID + * @return 保洁员状态 + */ + OpsCleanerStatusDO getByDeviceId(Long deviceId); + + /** + * 处理设备离线 + * 当IoT设备离线时调用,由IoT模块的消息总线触发 + * + * @param deviceId 设备ID + * @param reason 离线原因 + */ + void handleDeviceOffline(Long deviceId, String reason); + + /** + * 处理设备上线 + * 当IoT设备上线时调用,由IoT模块的消息总线触发 + * + * @param deviceId 设备ID + */ + void handleDeviceOnline(Long deviceId); + + /** + * 更新保洁员业务状态 + * 用于业务逻辑更新(如接单、完成工单等) + * + * @param userId 保洁员ID + * @param newStatus 新状态 + * @param remark 备注 + */ + void updateStatus(Long userId, CleanerStatusEnum newStatus, String remark); + + /** + * 更新保洁员当前所在区域 + * + * @param userId 保洁员ID + * @param areaId 区域ID + * @param areaName 区域名称 + */ + void updateCurrentArea(Long userId, Long areaId, String areaName); + + /** + * 查询指定区域的保洁员列表 + * + * @param areaId 区域ID + * @return 保洁员列表 + */ + List listCleanersByArea(Long areaId); + + /** + * 查询可接单的保洁员(IDLE状态) + * + * @param areaId 区域ID + * @return 可接单的保洁员列表 + */ + List listAvailableCleaners(Long areaId); + + /** + * 查询附近的保洁员(基于区域) + * + * @param areaId 区域ID + * @param radius 半径(米) + * @return 附近保洁员列表 + */ + List findNearbyCleaners(Long areaId, Integer radius); + + /** + * 检查保洁员是否在线 + * + * @param userId 保洁员ID + * @return 是否在线 + */ + boolean isOnline(Long userId); + + /** + * 获取保洁员当前状态 + * + * @param userId 保洁员ID + * @return 保洁员状态 + */ + OpsCleanerStatusDO getStatus(Long userId); + + /** + * 设置当前工单 + * 当保洁员接单时调用 + * + * @param userId 保洁员ID + * @param opsOrderId 工单ID + * @param opsOrderCode 工单编号 + */ + void setCurrentWorkOrder(Long userId, Long opsOrderId, String opsOrderCode); + + /** + * 清除当前工单 + * 当工单完成或取消时调用 + * + * @param userId 保洁员ID + */ + void clearCurrentWorkOrder(Long userId); +} diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/cleaner/CleanerStatusServiceImpl.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/cleaner/CleanerStatusServiceImpl.java new file mode 100644 index 0000000..d83466e --- /dev/null +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/cleaner/CleanerStatusServiceImpl.java @@ -0,0 +1,194 @@ +package com.viewsh.module.ops.environment.service.cleaner; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.viewsh.module.ops.dal.dataobject.cleaner.OpsCleanerStatusDO; +import com.viewsh.module.ops.dal.mysql.cleaner.OpsCleanerStatusMapper; + +import com.viewsh.module.ops.enums.CleanerStatusEnum; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 保洁员状态服务实现 + * 属于保洁业务特定逻辑,放在 environment-biz 模块中 + * + * @author lzh + */ +@Service +@Slf4j +public class CleanerStatusServiceImpl implements CleanerStatusService { + + @Resource + private OpsCleanerStatusMapper cleanerStatusMapper; + + @Override + public OpsCleanerStatusDO getByDeviceId(Long deviceId) { + return cleanerStatusMapper.selectByDeviceId(deviceId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void handleDeviceOffline(Long deviceId, String reason) { + // 1. 根据设备ID查询保洁员 + OpsCleanerStatusDO cleaner = getByDeviceId(deviceId); + if (cleaner == null) { + log.warn("设备未关联保洁员,忽略离线事件: deviceId={}", deviceId); + return; + } + + // 2. 如果保洁员已经是OFFLINE状态,则跳过 + if (CleanerStatusEnum.OFFLINE.getCode().equals(cleaner.getStatus())) { + log.debug("保洁员已是OFFLINE状态,无需更新: userId={}, deviceId={}", + cleaner.getUserId(), deviceId); + return; + } + + // 3. 更新保洁员状态为OFFLINE + cleaner.setStatusEnum(CleanerStatusEnum.OFFLINE); + cleaner.setOfflineReason(reason); + cleaner.setStatusChangeTime(LocalDateTime.now()); + cleaner.setLastDeviceSyncTime(LocalDateTime.now()); + cleanerStatusMapper.updateById(cleaner); + + // 4. 如果保洁员正在执行工单,需要记录日志 + if (cleaner.getCurrentOpsOrderId() != null) { + log.warn("保洁员设备离线,正在执行的工单可能需要暂停: userId={}, orderId={}", + cleaner.getUserId(), cleaner.getCurrentOpsOrderId()); + // TODO: 触发工单暂停逻辑(后续在派单引擎中实现) + } + + log.info("保洁员已离线: userId={}, deviceId={}, reason={}", + cleaner.getUserId(), deviceId, reason); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void handleDeviceOnline(Long deviceId) { + // 1. 根据设备ID查询保洁员 + OpsCleanerStatusDO cleaner = getByDeviceId(deviceId); + if (cleaner == null) { + log.warn("设备未关联保洁员,忽略上线事件: deviceId={}", deviceId); + return; + } + + // 2. 如果保洁员之前是OFFLINE状态,则恢复为IDLE + if (CleanerStatusEnum.OFFLINE.getCode().equals(cleaner.getStatus())) { + cleaner.setStatusEnum(CleanerStatusEnum.IDLE); + cleaner.setOfflineReason(null); + cleaner.setStatusChangeTime(LocalDateTime.now()); + cleaner.setLastDeviceSyncTime(LocalDateTime.now()); + cleanerStatusMapper.updateById(cleaner); + + log.info("保洁员已上线: userId={}, deviceId={}", cleaner.getUserId(), deviceId); + } else { + log.debug("保洁员当前状态非OFFLINE,无需更新: userId={}, status={}", + cleaner.getUserId(), cleaner.getStatus()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateStatus(Long userId, CleanerStatusEnum newStatus, String remark) { + OpsCleanerStatusDO cleaner = cleanerStatusMapper.selectByUserId(userId); + if (cleaner == null) { + throw new RuntimeException("保洁员不存在: " + userId); + } + + CleanerStatusEnum oldStatus = cleaner.getStatusEnum(); + cleaner.setStatusEnum(newStatus); + cleaner.setRemark(remark); + cleaner.setStatusChangeTime(LocalDateTime.now()); + cleanerStatusMapper.updateById(cleaner); + + log.info("保洁员状态已更新: userId={}, oldStatus={}, newStatus={}, remark={}", + userId, oldStatus, newStatus, remark); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateCurrentArea(Long userId, Long areaId, String areaName) { + OpsCleanerStatusDO cleaner = cleanerStatusMapper.selectByUserId(userId); + if (cleaner == null) { + throw new RuntimeException("保洁员不存在: " + userId); + } + + cleaner.setCurrentAreaId(areaId); + cleaner.setCurrentAreaName(areaName); + cleanerStatusMapper.updateById(cleaner); + + log.info("保洁员位置已更新: userId={}, areaId={}, areaName={}", + userId, areaId, areaName); + } + + @Override + public List listCleanersByArea(Long areaId) { + return cleanerStatusMapper.selectListByAreaId(areaId); + } + + @Override + public List listAvailableCleaners(Long areaId) { + return cleanerStatusMapper.selectList( + new LambdaQueryWrapper() + .eq(OpsCleanerStatusDO::getCurrentAreaId, areaId) + .eq(OpsCleanerStatusDO::getStatus, CleanerStatusEnum.IDLE.getCode()) + ); + } + + @Override + public List findNearbyCleaners(Long areaId, Integer radius) { + // TODO: 后续可以基于地理位置实现真实的附近查询 + // 目前简化为按区域查询 + return listCleanersByArea(areaId); + } + + @Override + public boolean isOnline(Long userId) { + OpsCleanerStatusDO cleaner = cleanerStatusMapper.selectByUserId(userId); + if (cleaner == null) { + return false; + } + return !CleanerStatusEnum.OFFLINE.getCode().equals(cleaner.getStatus()); + } + + @Override + public OpsCleanerStatusDO getStatus(Long userId) { + return cleanerStatusMapper.selectByUserId(userId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void setCurrentWorkOrder(Long userId, Long opsOrderId, String opsOrderCode) { + OpsCleanerStatusDO cleaner = cleanerStatusMapper.selectByUserId(userId); + if (cleaner == null) { + throw new RuntimeException("保洁员不存在: " + userId); + } + + cleaner.setCurrentOpsOrderId(opsOrderId); + cleaner.setCurrentOpsOrderCode(opsOrderCode); + cleanerStatusMapper.updateById(cleaner); + + log.info("保洁员已接单: userId={}, orderId={}, orderCode={}", + userId, opsOrderId, opsOrderCode); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void clearCurrentWorkOrder(Long userId) { + OpsCleanerStatusDO cleaner = cleanerStatusMapper.selectByUserId(userId); + if (cleaner == null) { + throw new RuntimeException("保洁员不存在: " + userId); + } + + log.info("保洁员已完成工单: userId={}, orderId={}, orderCode={}", + userId, cleaner.getCurrentOpsOrderId(), cleaner.getCurrentOpsOrderCode()); + + cleaner.setCurrentOpsOrderId(null); + cleaner.setCurrentOpsOrderCode(null); + cleanerStatusMapper.updateById(cleaner); + } +}