This commit is contained in:
dy
2025-09-02 15:36:22 +08:00
36 changed files with 815 additions and 207 deletions

View File

@@ -28,7 +28,7 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
* 前端访问路由地址为:/property/meterInfo
*
* @author lsm
* @date 2025-07-19
* @since 2025-07-19
*/
@Validated
@RequiredArgsConstructor
@@ -66,7 +66,7 @@ public class TbMeterInfoController extends BaseController {
@SaCheckPermission("property:meterInfo:query")
@GetMapping("/{id}")
public R<TbMeterInfoVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable("id") Long id) {
@PathVariable("id") Long id) {
return R.ok(tbMeterInfoService.queryById(id));
}
@@ -109,12 +109,24 @@ public class TbMeterInfoController extends BaseController {
* 生成 社区/建组/单元/楼栋/(水电气表)树结构
*
* @param meterType 水电气类型
*
* @param isMeter 是否返回仪表
* @return (水电气表)树结构
*/
@GetMapping("/tree/{meterType}")
public R<List<TreeNode<Long>>> queryMeterInfoTree(@PathVariable("meterType") Long meterType) {
return R.ok(tbMeterInfoService.queryMeterInfoTree(meterType));
@GetMapping("/tree")
public R<List<TreeNode<Long>>> queryMeterInfoTree(@RequestParam Long meterType, @RequestParam Boolean isMeter) {
return R.ok(tbMeterInfoService.queryMeterInfoTree(meterType, isMeter));
}
/**
* 获取水/电/气表当前读数/状态
*
* @param meterType 水电气类型
* @param floorId 楼栋id
*/
@GetMapping("/currentReading")
public R<Void> currentReading(@RequestParam Long meterType, @RequestParam Long floorId) {
tbMeterInfoService.getMeterStatus(meterType, floorId);
return R.ok();
}
}

View File

@@ -41,6 +41,10 @@ public class ServiceWorkOrdersType extends TenantEntity {
* 运作模式(0派单+抢单,1派单,2自动派单)
*/
private String operationMode;
/**
*部门id
*/
private Long deptId;
/**
* 排序值

View File

@@ -36,7 +36,11 @@ public class ServiceWorkOrdersTypeBo extends BaseEntity {
*/
@NotNull(message = "工单类型名称不能为空", groups = { AddGroup.class, EditGroup.class })
private String orderTypeName;
/**
*部门id
*/
@NotNull(message = "部门id", groups = { EditGroup.class })
private Long deptId;
/**
* 运作模式
*/

View File

@@ -44,6 +44,14 @@ public class ServiceWorkOrdersTypeVo implements Serializable {
*/
@ExcelProperty(value = "工单类型名称")
private String orderTypeName;
/**
*部门id
*/
private Long deptId;
/**
*部门名称
*/
private String deptName;
/**
* 运作模式

View File

@@ -6,7 +6,9 @@ import lombok.RequiredArgsConstructor;
import org.apache.dubbo.config.annotation.DubboService;
import org.dromara.property.api.RemoteAttendanceService;
import org.dromara.property.api.domain.vo.RemoteAttendanceUserGroupVo;
import org.dromara.property.domain.vo.attendanceVo.AttendanceAreaDeviceVo;
import org.dromara.property.domain.vo.attendanceVo.AttendanceUserGroupVo;
import org.dromara.property.service.attendanceService.IAttendanceAreaDeviceService;
import org.dromara.property.service.attendanceService.IAttendanceUserGroupService;
import java.util.Date;
@@ -23,6 +25,7 @@ public class RemoteAttendanceServiceImpl implements RemoteAttendanceService {
private final IAttendanceUserGroupService attendanceUserGroupService;
private final IAttendanceAreaDeviceService attendanceAreaDeviceService;
public List<RemoteAttendanceUserGroupVo> queryAttendPersonInfo(Date date) {
@@ -33,4 +36,12 @@ public class RemoteAttendanceServiceImpl implements RemoteAttendanceService {
return null;
}
@Override
public List<RemoteAttendanceUserGroupVo> queryAttendByCurrDateAndDeviceIp(Date date, String deviceIp) {
List<AttendanceUserGroupVo> ls = attendanceUserGroupService.queryAttendByCurrDateAndDeviceIp(date, deviceIp);
if (CollUtil.isNotEmpty(ls)) {
return BeanUtil.copyToList(ls, RemoteAttendanceUserGroupVo.class);
}
return null;
}
}

View File

@@ -1,8 +1,12 @@
package org.dromara.property.mapper.attendanceMapper;
import org.apache.ibatis.annotations.Param;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.property.domain.AttendanceUserGroup;
import org.dromara.property.domain.vo.attendanceVo.AttendanceUserGroupVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import java.util.Date;
import java.util.List;
/**
* 排班明细Mapper接口
@@ -12,4 +16,12 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
*/
public interface AttendanceUserGroupMapper extends BaseMapperPlus<AttendanceUserGroup, AttendanceUserGroupVo> {
/**
* 根据日期和设备ip 查询当前设备所在区域的排班人员信息
*
* @param date 查询日期
* @param deviceIp 设备ip
* @return 返回排班人员列表
*/
List<AttendanceUserGroupVo> queryAttendByCurrDateAndDeviceIp(@Param("currDate") Date currDate, @Param("deviceIp") String deviceIp);
}

View File

@@ -6,6 +6,7 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import java.util.Collection;
import java.util.Date;
import java.util.List;
/**
@@ -65,4 +66,5 @@ public interface IAttendanceAreaDeviceService {
* @return 是否删除成功
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}

View File

@@ -75,6 +75,15 @@ public interface IAttendanceUserGroupService {
*/
List<AttendanceUserGroupVo> queryAttendPersonInfo(Date date);
TableDataInfo<AttendanceUserGroupVo> queryScheduleView(AttendanceUserGroupBo bo, PageQuery pageQuery);
/**
* 根据日期和设备ip 查询当前设备所在区域的排班人员信息
*
* @param date 查询日期
* @param deviceIp 设备ip
* @return 返回排班人员列表
*/
List<AttendanceUserGroupVo> queryAttendByCurrDateAndDeviceIp(Date date, String deviceIp);
}

View File

@@ -227,7 +227,7 @@ public class ServiceWorkOrdersServiceImpl implements IServiceWorkOrdersService {
boolean flags = workOrdersRecordMapper.insert(serviceWorkOrdersRecord) > 0;
if (flags) {
if (serviceWorkOrdersType.getOperationMode().equals(OrderTypeOperationEnum.AUTOMATE_DISPATCH.getValue())) {
handleServiceWorkOrder(add);
handleServiceWorkOrder(add,serviceWorkOrdersType);
}
}
}
@@ -235,17 +235,19 @@ public class ServiceWorkOrdersServiceImpl implements IServiceWorkOrdersService {
}
//自动派单
private void handleServiceWorkOrder(ServiceWorkOrders serviceWorkOrders) {
private void handleServiceWorkOrder(ServiceWorkOrders serviceWorkOrders,ServiceWorkOrdersType serviceWorkOrdersType) {
LocalDate today = LocalDate.now();
// 1. 获取今日排班人员(优先查缓存,未命中则查询数据库并缓存)
List<AttendanceUserGroup> attendanceUserGroups = RedisUtils.getCacheList(DateUtil.today());
if (CollUtil.isEmpty(attendanceUserGroups)) {
attendanceUserGroups = attendanceUserGroupMapper.selectList(
new LambdaQueryWrapper<AttendanceUserGroup>()
.le(AttendanceUserGroup::getStartDate, today)
.ge(AttendanceUserGroup::getEndDate, today)
.orderByAsc(AttendanceUserGroup::getCreateTime)
);
List<AttendanceUserGroup> attendanceUserGroupList = attendanceUserGroupMapper.selectList(
new LambdaQueryWrapper<AttendanceUserGroup>()
.le(AttendanceUserGroup::getStartDate, today)
.ge(AttendanceUserGroup::getEndDate, today)
.eq(AttendanceUserGroup::getDeptId, serviceWorkOrdersType.getDeptId())
.orderByAsc(AttendanceUserGroup::getStartDate)
);
if (CollUtil.isEmpty(attendanceUserGroups)|| attendanceUserGroups.size() != attendanceUserGroupList.size()) {
attendanceUserGroups=attendanceUserGroupList;
Assert.isTrue(CollUtil.isNotEmpty(attendanceUserGroups), "暂无排班人员");
// 缓存当天排班数据(假设当天不会变)
RedisUtils.setCacheList(DateUtil.today(), attendanceUserGroups);
@@ -589,7 +591,7 @@ public class ServiceWorkOrdersServiceImpl implements IServiceWorkOrdersService {
boolean flags = workOrdersRecordMapper.insert(serviceWorkOrdersRecord) > 0;
if (flags) {
if (serviceWorkOrdersType.getOperationMode().equals(OrderTypeOperationEnum.AUTOMATE_DISPATCH.getValue())) {
handleServiceWorkOrder(add);
handleServiceWorkOrder(add,serviceWorkOrdersType);
}
}
}

View File

@@ -3,6 +3,7 @@ package org.dromara.property.service.impl;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.dubbo.config.annotation.DubboReference;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
@@ -18,6 +19,8 @@ import org.dromara.property.domain.bo.ServiceWorkOrdersTypeBo;
import org.dromara.property.domain.vo.ServiceWorkOrdersTypeVo;
import org.dromara.property.mapper.ServiceWorkOrdersTypeMapper;
import org.dromara.property.service.IServiceWorkOrdersTypeService;
import org.dromara.system.api.RemoteDeptService;
import org.dromara.system.api.domain.vo.RemoteDeptVo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -39,6 +42,8 @@ public class ServiceWorkOrdersTypeServiceImpl implements IServiceWorkOrdersTypeS
private final ServiceWorkOrdersTypeMapper baseMapper;
@DubboReference
private RemoteDeptService remoteDeptService;
/**
* 查询【工单类型】
*
@@ -47,7 +52,11 @@ public class ServiceWorkOrdersTypeServiceImpl implements IServiceWorkOrdersTypeS
*/
@Override
public ServiceWorkOrdersTypeVo queryById(Long id) {
return baseMapper.selectVoById(id);
ServiceWorkOrdersTypeVo serviceWorkOrdersTypeVo= baseMapper.selectVoById(id);
Long deptId = serviceWorkOrdersTypeVo.getDeptId();
RemoteDeptVo remoteDeptVo = remoteDeptService.selectDeptVoById(deptId);
serviceWorkOrdersTypeVo.setDeptName(remoteDeptVo.getDeptName());
return serviceWorkOrdersTypeVo;
}
/**
@@ -61,6 +70,11 @@ public class ServiceWorkOrdersTypeServiceImpl implements IServiceWorkOrdersTypeS
public TableDataInfo<ServiceWorkOrdersTypeVo> queryPageList(ServiceWorkOrdersTypeBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<ServiceWorkOrdersType> lqw = buildQueryWrapper(bo);
Page<ServiceWorkOrdersTypeVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
result.getRecords().stream().forEach(vo -> {
Long deptId = vo.getDeptId();
RemoteDeptVo remoteDeptVo = remoteDeptService.selectDeptVoById(deptId);
vo.setDeptName(remoteDeptVo.getDeptName());
});
return TableDataInfo.build(result);
}
@@ -73,7 +87,13 @@ public class ServiceWorkOrdersTypeServiceImpl implements IServiceWorkOrdersTypeS
@Override
public List<ServiceWorkOrdersTypeVo> queryList(ServiceWorkOrdersTypeBo bo) {
LambdaQueryWrapper<ServiceWorkOrdersType> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
List<ServiceWorkOrdersTypeVo> serviceWorkOrdersTypeVoList= baseMapper.selectVoList(lqw);
serviceWorkOrdersTypeVoList.stream().forEach(vo -> {
Long deptId = vo.getDeptId();
RemoteDeptVo remoteDeptVo = remoteDeptService.selectDeptVoById(deptId);
vo.setDeptName(remoteDeptVo.getDeptName());
});
return serviceWorkOrdersTypeVoList;
}
private LambdaQueryWrapper<ServiceWorkOrdersType> buildQueryWrapper(ServiceWorkOrdersTypeBo bo) {

View File

@@ -299,4 +299,9 @@ public class AttendanceUserGroupServiceImpl implements IAttendanceUserGroupServi
return TableDataInfo.build(attendanceArrangementVoPage);
}
@Override
public List<AttendanceUserGroupVo> queryAttendByCurrDateAndDeviceIp(Date date, String deviceIp) {
return this.baseMapper.queryAttendByCurrDateAndDeviceIp(date, deviceIp);
}
}

View File

@@ -118,10 +118,11 @@ public class CleanOrderServiceImpl implements ICleanOrderService {
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<CleanOrder> buildQueryWrapper(CleanOrderBo bo) {
private LambdaQueryWrapper<CleanOrder>buildQueryWrapper(CleanOrderBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<CleanOrder> lqw = Wrappers.lambdaQuery();
lqw.orderByAsc(CleanOrder::getId);
lqw.orderByDesc(CleanOrder::getCreateTime)
.orderByDesc(CleanOrder::getUpdateTime);
// lqw.eq(StringUtils.isNotBlank(bo.getLocation()), CleanOrder::getLocation, bo.getLocation());
// lqw.eq(StringUtils.isNotBlank(bo.getArea()), CleanOrder::getArea, bo.getArea());

View File

@@ -1,6 +1,8 @@
package org.dromara.property.service.impl.smartDevicesImpl;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import com.alibaba.fastjson.JSONObject;
import org.dromara.common.core.domain.TreeNode;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
@@ -13,13 +15,18 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.websocket.dto.WebSocketMessageDto;
import org.dromara.common.websocket.utils.WebSocketUtils;
import org.dromara.property.domain.bo.TbFloorBo;
import org.dromara.property.domain.vo.TbBuildingVo;
import org.dromara.property.domain.vo.TbCommunityVo;
import org.dromara.property.domain.vo.TbFloorVo;
import org.dromara.property.rocketmq.domain.MeterResult;
import org.dromara.property.service.ITbBuildingService;
import org.dromara.property.service.ITbCommunityService;
import org.dromara.property.service.ITbFloorService;
import org.dromara.property.tasks.HeartbeatTasks;
import org.dromara.property.utils.MeterRecordUtil;
import org.dromara.system.api.model.LoginUser;
import org.springframework.stereotype.Service;
import org.dromara.property.domain.bo.smartDevicesBo.TbMeterInfoBo;
@@ -29,10 +36,10 @@ import org.dromara.property.mapper.smartDevicesMapper.TbMeterInfoMapper;
import org.dromara.property.service.smartDevicesService.ITbMeterInfoService;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -51,6 +58,9 @@ public class TbMeterInfoServiceImpl implements ITbMeterInfoService {
private final ITbBuildingService buildingService;
private final ITbCommunityService communityService;
private final MeterRecordUtil meterRecordUtil;
private final HeartbeatTasks heartbeatTasks;
/**
* 查询水电气
*
@@ -106,16 +116,17 @@ public class TbMeterInfoServiceImpl implements ITbMeterInfoService {
LambdaQueryWrapper<TbMeterInfo> lqw = Wrappers.lambdaQuery();
lqw.orderByAsc(TbMeterInfo::getId);
lqw.like(StringUtils.isNotBlank(bo.getHostIp()), TbMeterInfo::getHostIp, bo.getHostIp());
lqw.like(StringUtils.isNotBlank(bo.getMeterName()), TbMeterInfo::getMeterName, bo.getMeterName());
lqw.eq(StringUtils.isNotBlank(bo.getMeterCode()), TbMeterInfo::getMeterCode, bo.getMeterCode());
lqw.eq(StringUtils.isNotBlank(bo.getFactoryNo()), TbMeterInfo::getFactoryNo, bo.getFactoryNo());
lqw.like(StringUtils.isNotBlank(bo.getMeterName()), TbMeterInfo::getMeterName, bo.getMeterName());
lqw.eq(StringUtils.isNotBlank(bo.getInstallLocation()), TbMeterInfo::getInstallLocation, bo.getInstallLocation());
lqw.eq(bo.getFloorId() != null, TbMeterInfo::getFloorId, bo.getFloorId());
lqw.eq(bo.getMaxRang() != null, TbMeterInfo::getMaxRang, bo.getMaxRang());
lqw.eq(bo.getMeterType() != null, TbMeterInfo::getMeterType, bo.getMeterType());
lqw.eq(bo.getMeterUnit() != null, TbMeterInfo::getMeterUnit, bo.getMeterUnit());
lqw.eq(StringUtils.isNotBlank(bo.getInstallLocation()), TbMeterInfo::getInstallLocation, bo.getInstallLocation());
lqw.eq(bo.getInitReading() != null, TbMeterInfo::getInitReading, bo.getInitReading());
lqw.eq(bo.getMaxRang() != null, TbMeterInfo::getMaxRang, bo.getMaxRang());
lqw.eq(bo.getCommunicationState() != null, TbMeterInfo::getCommunicationState, bo.getCommunicationState());
lqw.eq(bo.getRunningState() != null, TbMeterInfo::getRunningState, bo.getRunningState());
lqw.eq(bo.getCommunicationState() != null, TbMeterInfo::getCommunicationState, bo.getCommunicationState());
return lqw;
}
@@ -177,10 +188,11 @@ public class TbMeterInfoServiceImpl implements ITbMeterInfoService {
* 查询水电气树结构
*
* @param meterType 水电气类型
* @param isMeter 是否返回仪表
* @return 水电气树结构
*/
@Override
public List<TreeNode<Long>> queryMeterInfoTree(Long meterType) {
public List<TreeNode<Long>> queryMeterInfoTree(Long meterType, Boolean isMeter) {
// 默认加载社区树
List<TreeNode<Long>> treeList = new ArrayList<>();
List<TbCommunityVo> tbCommunityVos = communityService.queryAll();
@@ -223,20 +235,159 @@ public class TbMeterInfoServiceImpl implements ITbMeterInfoService {
}).toList();
treeList.addAll(l3);
TbMeterInfoBo bo = new TbMeterInfoBo();
bo.setMeterType(meterType);
List<TbMeterInfoVo> meterInfoVos = this.queryList(bo);
if (meterInfoVos != null && !meterInfoVos.isEmpty()) {
List<TreeNode<Long>> l4 = meterInfoVos.stream().map(item -> {
TreeNode<Long> node = new TreeNode<>();
node.setLevel(4);
node.setCode(item.getId());
node.setParentCode(item.getFloorId());
node.setLabel(item.getMeterName());
return node;
}).toList();
treeList.addAll(l4);
if (isMeter) {
TbMeterInfoBo bo = new TbMeterInfoBo();
bo.setMeterType(meterType);
List<TbMeterInfoVo> meterInfoVos = this.queryList(bo);
if (meterInfoVos != null && !meterInfoVos.isEmpty()) {
List<TreeNode<Long>> l4 = meterInfoVos.stream().map(item -> {
TreeNode<Long> node = new TreeNode<>();
node.setLevel(4);
node.setCode(item.getId());
node.setParentCode(item.getFloorId());
node.setLabel(item.getMeterName());
return node;
}).toList();
treeList.addAll(l4);
}
}
return TreeUtils.build(treeList, 0L);
}
/**
* 获取水/电/气表当前读数/状态
*
* @param meterType 水电气类型
* @param floorId 楼栋id
*/
@Override
public void getMeterStatus(Long meterType, Long floorId) {
// 参数校验
if (meterType == 0L || floorId == 0L) {
heartbeatTasks.stopTask("Meter_Status_Reading");
return;
}
// 获取当前登录用户
LoginUser user = LoginHelper.getLoginUser();
if (user == null) {
heartbeatTasks.stopTask("Meter_Status_Reading");
return;
}
// 初始化WebSocket消息
WebSocketMessageDto webSocketMessage = new WebSocketMessageDto();
JSONObject jsonObject = new JSONObject();
jsonObject.put("type", "meter");
// 查询仪表信息
TbMeterInfoBo meterInfoBo = new TbMeterInfoBo();
meterInfoBo.setFloorId(floorId);
meterInfoBo.setMeterType(meterType);
List<TbMeterInfoVo> meterInfoVoList = this.queryList(meterInfoBo);
// 如果没有仪表信息,直接返回
if (meterInfoVoList.isEmpty()) {
heartbeatTasks.stopTask("Meter_Status_Reading");
jsonObject.put("readingTime", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
jsonObject.put("data", new ArrayList<>());
webSocketMessage.setMessage(jsonObject.toString());
webSocketMessage.setSessionKeys(List.of(user.getUserId()));
WebSocketUtils.publishMessage(webSocketMessage);
return;
}
// 获取唯一的主机IP列表
String[] hostIpArr = meterInfoVoList.stream()
.map(TbMeterInfoVo::getHostIp)
.distinct()
.toArray(String[]::new);
// 统计每个IP对应的仪表数量
Map<String, Long> ipCountMap = new HashMap<>();
for (String ip : hostIpArr) {
ipCountMap.put(ip, baseMapper.selectCount(new LambdaQueryWrapper<TbMeterInfo>()
.eq(TbMeterInfo::getHostIp, ip)));
}
// 启动定时任务
heartbeatTasks.startHeartbeatTask("Meter_Status_Reading", () -> {
jsonObject.put("readingTime", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
List<MeterResult> meterResults;
try {
meterResults = meterRecordUtil.getMeterStatus(ipCountMap, hostIpArr);
} catch (Exception e) {
// 获取数据失败设置所有仪表通信状态为0并发送消息
handleMeterCommunicationFailure(user, jsonObject, meterInfoVoList, webSocketMessage);
return;
}
// 处理仪表读数和状态
processMeterResults(user, jsonObject, meterResults, meterInfoVoList, webSocketMessage);
}, 30000);
}
/**
* 处理仪表通信失败的情况
*
* @param meterInfoVoList 仪表列表
* @param jsonObject 消息对象
* @param webSocketMessage WebSocket消息
* @param user 当前用户
*/
private void handleMeterCommunicationFailure(LoginUser user, JSONObject jsonObject, List<TbMeterInfoVo> meterInfoVoList, WebSocketMessageDto webSocketMessage) {
meterInfoVoList.forEach(item -> item.setCommunicationState(0L));
jsonObject.put("data", meterInfoVoList);
webSocketMessage.setMessage(jsonObject.toString());
webSocketMessage.setSessionKeys(List.of(user.getUserId()));
WebSocketUtils.publishMessage(webSocketMessage);
}
/**
* 处理仪表读数结果
*
* @param meterInfoVoList 仪表列表
* @param meterResults 读数结果
* @param jsonObject 消息对象
* @param webSocketMessage WebSocket消息
* @param user 当前用户
*/
private void processMeterResults(LoginUser user, JSONObject jsonObject, List<MeterResult> meterResults, List<TbMeterInfoVo> meterInfoVoList, WebSocketMessageDto webSocketMessage) {
// 创建IP到结果的映射提高查找效率
Map<String, MeterResult> meterResultMap = meterResults.stream()
.collect(Collectors.toMap(MeterResult::getIp, Function.identity(), (v1, v2) -> v1));
for (TbMeterInfoVo item : meterInfoVoList) {
MeterResult meterResult = meterResultMap.get(item.getHostIp());
if (meterResult == null) {
// 如果没有找到对应的结果设置通信状态为0
item.setCommunicationState(0L);
continue;
}
// 解析读数
List<Float> collectionValue = meterResult.getCollectionValue();
String meterCode = item.getMeterCode();
try {
int codeIndex = Integer.parseInt(meterCode);
if (codeIndex >= 0 && codeIndex < collectionValue.size()) {
BigDecimal initReading = BigDecimal.valueOf(collectionValue.get(codeIndex))
.setScale(2, RoundingMode.HALF_UP);
item.setInitReading(initReading);
item.setCommunicationState(initReading.compareTo(BigDecimal.ZERO) == 0 ? 0L : 1L);
} else {
item.setCommunicationState(0L);
}
} catch (NumberFormatException e) {
item.setCommunicationState(0L);
}
}
jsonObject.put("data", meterInfoVoList);
webSocketMessage.setMessage(jsonObject.toString());
webSocketMessage.setSessionKeys(List.of(user.getUserId()));
WebSocketUtils.publishMessage(webSocketMessage);
}
}

View File

@@ -238,50 +238,74 @@ public class TbMeterRecordServiceImpl implements ITbMeterRecordService {
public Map<String, Object> getEnergyTrend(String floorId, String meterId, Long meterType, String day, String month, String year) {
Map<String, Object> resultMap = new HashMap<>();
// hour
List<Map<String, Object>> hourList = baseMapper.getHourTrend(StrUtil.isBlank(floorId) ? null : Long.parseLong(floorId), StrUtil.isBlank(meterId) ? null : Long.parseLong(meterId), meterType, day);
String yesterday = DateUtil.format(DateUtil.offsetDay(DateUtil.parse(day), -1), "yyyy-MM-dd");
// todayHour
Map<String, Object> todayMap = trendHourData(floorId, meterId, meterType, day);
// yesterdayHour
Map<String, Object> yesterdayMap = trendHourData(floorId, meterId, meterType, yesterday);
Map<String, Object> hourMap = new HashMap<>();
String[] hourCategories = hourList.stream()
.map(map -> map.get("hour").toString())
.toArray(String[]::new);
BigDecimal[] hourData = hourList.stream()
.map(map -> new BigDecimal(map.get("total_consumption").toString()))
.toArray(BigDecimal[]::new);
hourMap.put("categories", hourCategories);
hourMap.put("data", hourData);
hourMap.put("total" , hourList.stream().map(map -> new BigDecimal(map.get("total_consumption").toString())).reduce(BigDecimal::add).orElse(BigDecimal.ZERO).floatValue());
hourMap.put("today", todayMap);
hourMap.put("yesterday", yesterdayMap);
// day
String[] monthArr = month.split("-");
List<Map<String, Object>> dayList = baseMapper.getDayTrend(StrUtil.isBlank(floorId) ? null : Long.parseLong(floorId), StrUtil.isBlank(meterId) ? null : Long.parseLong(meterId), meterType, monthArr[0], monthArr[1]);
String lastMonth = Integer.parseInt(monthArr[1]) - 1 + "";
// nowMonth
Map<String, Object> nowMonthMap = trendDayData(floorId, meterId, meterType, monthArr[0], monthArr[1]);
// lastMonth
Map<String, Object> lastMonthMap = trendDayData(floorId, meterId, meterType, monthArr[0], lastMonth);
Map<String, Object> dayMap = new HashMap<>();
String[] dayCategories = dayList.stream()
.map(map -> map.get("day").toString())
.toArray(String[]::new);
BigDecimal[] dayData = dayList.stream()
.map(map -> new BigDecimal(map.get("total_consumption").toString()))
.toArray(BigDecimal[]::new);
dayMap.put("categories", dayCategories);
dayMap.put("data", dayData);
dayMap.put("total", dayList.stream().map(map -> new BigDecimal(map.get("total_consumption").toString())).reduce(BigDecimal::add).orElse(BigDecimal.ZERO).floatValue());
dayMap.put("nowMonth", nowMonthMap);
dayMap.put("lastMonth", lastMonthMap);
String lastYear = Integer.parseInt(year) - 1 + "";
// nowYear
Map<String, Object> nowYearMap = trendMonthData(floorId, meterId, meterType, year);
// lastYear
Map<String, Object> lastYearMap = trendMonthData(floorId, meterId, meterType, lastYear);
// month
List<Map<String, Object>> monthList = baseMapper.getMonthTrend(StrUtil.isBlank(floorId) ? null : Long.parseLong(floorId), StrUtil.isBlank(meterId) ? null : Long.parseLong(meterId), meterType, year);
Map<String, Object> monthMap = new HashMap<>();
String[] monthCategories = monthList.stream()
.map(map -> map.get("month").toString())
.toArray(String[]::new);
BigDecimal[] monthData = monthList.stream()
.map(map -> new BigDecimal(map.get("total_consumption").toString()))
.toArray(BigDecimal[]::new);
monthMap.put("categories", monthCategories);
monthMap.put("data", monthData);
monthMap.put("total", monthList.stream().map(map -> new BigDecimal(map.get("total_consumption").toString())).reduce(BigDecimal::add).orElse(BigDecimal.ZERO).floatValue());
monthMap.put("nowYear", nowYearMap);
monthMap.put("lastYear", lastYearMap);
resultMap.put("hour", hourMap);
resultMap.put("day", dayMap);
resultMap.put("month", monthMap);
return resultMap;
}
private Map<String, Object> trendHourData(String floorId, String meterId, Long meterType, String day) {
Map<String, Object> hourMap = new HashMap<>();
List<Map<String, Object>> hourList = baseMapper.getHourTrend(StrUtil.isBlank(floorId) ? null : Long.parseLong(floorId), StrUtil.isBlank(meterId) ? null : Long.parseLong(meterId), meterType, day);
List<String[]> hourData = new ArrayList<>();
hourList.forEach(item -> hourData.add(new String[]{item.get("hour").toString(), item.get("total_consumption").toString()}));
Float total = hourList.stream().map(map -> new BigDecimal(map.get("total_consumption").toString())).reduce(BigDecimal::add).orElse(BigDecimal.ZERO).floatValue();
hourMap.put("total", total);
hourMap.put("data", hourData);
return hourMap;
}
private Map<String, Object> trendDayData(String floorId, String meterId, Long meterType, String year, String month) {
Map<String, Object> dayMap = new HashMap<>();
List<Map<String, Object>> dayList = baseMapper.getDayTrend(StrUtil.isBlank(floorId) ? null : Long.parseLong(floorId), StrUtil.isBlank(meterId) ? null : Long.parseLong(meterId), meterType, year, month);
List<String[]> dayData = new ArrayList<>();
dayList.forEach(item -> dayData.add(new String[]{item.get("day").toString(), item.get("total_consumption").toString()}));
Float total = dayList.stream().map(map -> new BigDecimal(map.get("total_consumption").toString())).reduce(BigDecimal::add).orElse(BigDecimal.ZERO).floatValue();
dayMap.put("total", total);
dayMap.put("data", dayData);
return dayMap;
}
private Map<String, Object> trendMonthData(String floorId, String meterId, Long meterType, String year) {
Map<String, Object> resultMap = new HashMap<>();
List<Map<String, Object>> monthList = baseMapper.getMonthTrend(StrUtil.isBlank(floorId) ? null : Long.parseLong(floorId), StrUtil.isBlank(meterId) ? null : Long.parseLong(meterId), meterType, year);
List<String[]> monthData = new ArrayList<>();
monthList.forEach(item -> monthData.add(new String[]{item.get("month").toString(), item.get("total_consumption").toString()}));
Float total = monthList.stream().map(map -> new BigDecimal(map.get("total_consumption").toString())).reduce(BigDecimal::add).orElse(BigDecimal.ZERO).floatValue();
resultMap.put("total", total);
resultMap.put("data", monthData);
return resultMap;
}
}

View File

@@ -13,7 +13,7 @@ import java.util.List;
* 水电气Service接口
*
* @author lsm
* @date 2025-07-19
* @since 2025-07-19
*/
public interface ITbMeterInfoService {
@@ -71,8 +71,16 @@ public interface ITbMeterInfoService {
* 查询水电气树结构
*
* @param meterType 水电气类型
*
* @param isMeter 是否返回仪表
* @return 水电气树结构
*/
List<TreeNode<Long>> queryMeterInfoTree(Long meterType);
List<TreeNode<Long>> queryMeterInfoTree(Long meterType, Boolean isMeter);
/**
* 获取水/电/气表当前读数/状态
*
* @param meterType 水电气类型
* @param floorId 楼栋id
*/
void getMeterStatus(Long meterType, Long floorId);
}

View File

@@ -0,0 +1,42 @@
package org.dromara.property.tasks;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.*;
/**
* @author lsm
* @apiNote HeartbeatTasks
* @since 2025/9/1
*/
@Component
public class HeartbeatTasks {
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
private final Map<String, ScheduledFuture<?>> tasks = new ConcurrentHashMap<>();
public void startHeartbeatTask(String taskId, Runnable task, long intervalMs) {
// 先停止同名任务(如果存在)
stopTask(taskId);
// 创建新任务
ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(
task, 0, intervalMs, TimeUnit.MILLISECONDS);
tasks.put(taskId, future);
}
public void stopHeartbeatTask() {
// 停止所有心跳任务
tasks.values().forEach(future -> future.cancel(true));
tasks.clear();
}
public void stopTask(String taskId) {
ScheduledFuture<?> future = tasks.get(taskId);
if (future != null) {
future.cancel(true);
tasks.remove(taskId);
}
}
}

View File

@@ -0,0 +1,48 @@
package org.dromara.property.utils;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.dromara.property.rocketmq.domain.MeterResult;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
/**
* @author lsm
* @apiNote MeterRecordUtil
* @since 2025/8/30
*/
@Slf4j
@Component
public class MeterRecordUtil {
@Value("${eqp.config.meter.host}")
private String meterRecordUrl;
private static final String READING_URL = "reading";
@Data
public static class Result<T> {
private Integer code;
private T data;
}
/**
* 获取水/电/气表当前读数
*/
public List<MeterResult> getMeterStatus(Map<String, Long> ipMap, String[] ipArr) {
JSONObject jsonObject = new JSONObject();
jsonObject.putOnce("ipMap", ipMap);
jsonObject.putOnce("ipArr", ipArr);
String post = HttpUtil.post(meterRecordUrl + READING_URL, jsonObject.toString(), 3000);
Result<List<MeterResult>> result = JSONUtil.toBean(post, new TypeReference<Result<List<MeterResult>>>() {}, true);
return result.getData();
}
}

View File

@@ -4,4 +4,20 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.property.mapper.attendanceMapper.AttendanceUserGroupMapper">
<select id="queryAttendByCurrDateAndDeviceIp"
resultType="org.dromara.property.domain.vo.attendanceVo.AttendanceUserGroupVo">
SELECT
a.*
FROM
attendance_user_group a
LEFT JOIN attendance_arrangement_area b ON a.schedule_id = b.shcedule_id
LEFT JOIN attendance_area_device c ON b.area_id = c.area_id
LEFT JOIN sis_device_manage d ON c.device_manage_id = d.id
WHERE
d.device_ip = #{deviceIp}
AND a.start_date &lt;= #{currDate}
AND a.end_date &gt; #{currDate}
</select>
</mapper>