Merge remote-tracking branch 'origin/6170' into 6170

This commit is contained in:
2025-09-11 14:38:06 +08:00
29 changed files with 1634 additions and 37 deletions

View File

@ -0,0 +1,78 @@
package com.fuyuanshen.equipment.domain;
import com.baomidou.mybatisplus.annotation.*;
import com.fuyuanshen.common.mybatis.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serial;
/**
* 围栏进出记录对象 device_fence_access_record
*
* @author Lion Li
* @date 2025-09-11
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("device_fence_access_record")
public class DeviceFenceAccessRecord extends BaseEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 记录ID
*/
@TableId(value = "id")
private Long id;
/**
* 围栏ID
*/
private Long fenceId;
/**
* 设备标识
*/
private String deviceId;
/**
* 用户ID
*/
private Long userId;
/**
* 事件类型
*/
private Long eventType;
/**
* 纬度
*/
private Long latitude;
/**
* 经度
*/
private Long longitude;
/**
* 定位精度
*/
private Long accuracy;
/**
* 事件时间
*/
private Date eventTime;
/**
* 记录创建时间
*/
private Date createdTime;
}

View File

@ -0,0 +1,78 @@
package com.fuyuanshen.equipment.domain;
import com.baomidou.mybatisplus.annotation.*;
import com.fuyuanshen.common.mybatis.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serial;
/**
* 电子围栏对象 device_geo_fence
*
* @author Lion Li
* @date 2025-09-11
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("device_geo_fence")
public class DeviceGeoFence extends BaseEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 围栏唯一标识
*/
@TableId(value = "id")
private Long id;
/**
* 围栏名称
*/
private String name;
/**
* 围栏描述
*/
private String description;
/**
* 围栏区域类型, 0 POLYGON, 1 CIRCLE
*/
private Long areaType;
/**
* 围栏坐标数据
*/
private String coordinates;
/**
* 圆形围栏半径(米)
*/
private Long radius;
/**
* 是否激活
*/
private Long isActive;
/**
* 创建人
*/
private Long createdBy;
/**
* 创建时间
*/
private Date createdTime;
/**
* 更新时间
*/
private Date updatedTime;
}

View File

@ -6,6 +6,7 @@ import com.fuyuanshen.equipment.domain.DeviceChargeDischarge;
import com.fuyuanshen.common.mybatis.core.domain.BaseEntity;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import cn.hutool.core.date.DateUtil;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.util.Date;
@ -44,13 +45,25 @@ public class DeviceChargeDischargeBo extends BaseEntity {
* 开始时间
*/
@NotNull(message = "开始时间不能为空", groups = { AddGroup.class, EditGroup.class })
@JsonFormat(pattern = "yyyy-MM-dd")
private Date startTime;
// 添加字符串类型的setter方法来处理前端传递的字符串日期
public void setStartTime(String startTime) {
this.startTime = DateUtil.parseDate(startTime);
}
/**
* 结束时间
*/
@JsonFormat(pattern = "yyyy-MM-dd")
private Date endTime;
// 添加字符串类型的setter方法来处理前端传递的字符串日期
public void setEndTime(String endTime) {
this.endTime = DateUtil.parseDate(endTime);
}
/**
* 起始电量百分比(0-100)
*/

View File

@ -0,0 +1,83 @@
package com.fuyuanshen.equipment.domain.bo;
import com.fuyuanshen.common.core.validate.AddGroup;
import com.fuyuanshen.common.core.validate.EditGroup;
import com.fuyuanshen.common.mybatis.core.domain.BaseEntity;
import com.fuyuanshen.equipment.domain.DeviceFenceAccessRecord;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
/**
* 围栏进出记录业务对象 device_fence_access_record
*
* @author Lion Li
* @date 2025-09-11
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = DeviceFenceAccessRecord.class, reverseConvertGenerate = false)
public class DeviceFenceAccessRecordBo extends BaseEntity {
/**
* 记录ID
*/
@NotNull(message = "记录ID不能为空", groups = { EditGroup.class })
private Long id;
/**
* 围栏ID
*/
@NotNull(message = "围栏ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long fenceId;
/**
* 设备标识
*/
@NotBlank(message = "设备标识不能为空", groups = { AddGroup.class, EditGroup.class })
private String deviceId;
/**
* 用户ID
*/
private Long userId;
/**
* 事件类型
*/
@NotNull(message = "事件类型不能为空", groups = { AddGroup.class, EditGroup.class })
private Long eventType;
/**
* 纬度
*/
@NotNull(message = "纬度不能为空", groups = { AddGroup.class, EditGroup.class })
private Long latitude;
/**
* 经度
*/
@NotNull(message = "经度不能为空", groups = { AddGroup.class, EditGroup.class })
private Long longitude;
/**
* 定位精度
*/
private Long accuracy;
/**
* 事件时间
*/
@NotNull(message = "事件时间不能为空", groups = { AddGroup.class, EditGroup.class })
private Date eventTime;
/**
* 记录创建时间
*/
private Date createdTime;
}

View File

@ -0,0 +1,80 @@
package com.fuyuanshen.equipment.domain.bo;
import com.fuyuanshen.common.core.validate.AddGroup;
import com.fuyuanshen.common.core.validate.EditGroup;
import com.fuyuanshen.common.mybatis.core.domain.BaseEntity;
import com.fuyuanshen.equipment.domain.DeviceGeoFence;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
/**
* 电子围栏业务对象 device_geo_fence
*
* @author Lion Li
* @date 2025-09-11
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = DeviceGeoFence.class, reverseConvertGenerate = false)
public class DeviceGeoFenceBo extends BaseEntity {
/**
* 围栏唯一标识
*/
@NotNull(message = "围栏唯一标识不能为空", groups = { EditGroup.class })
private Long id;
/**
* 围栏名称
*/
@NotBlank(message = "围栏名称不能为空", groups = { AddGroup.class, EditGroup.class })
private String name;
/**
* 围栏描述
*/
private String description;
/**
* 围栏区域类型, 0 POLYGON, 1 CIRCLE
*/
@NotNull(message = "围栏区域类型, 0 POLYGON, 1 CIRCLE不能为空", groups = { AddGroup.class, EditGroup.class })
private Long areaType;
/**
* 围栏坐标数据
*/
@NotBlank(message = "围栏坐标数据不能为空", groups = { AddGroup.class, EditGroup.class })
private String coordinates;
/**
* 圆形围栏半径(米)
*/
private Long radius;
/**
* 是否激活
*/
private Long isActive;
/**
* 创建人
*/
private Long createdBy;
/**
* 创建时间
*/
private Date createdTime;
/**
* 更新时间
*/
private Date updatedTime;
}

View File

@ -0,0 +1,63 @@
package com.fuyuanshen.equipment.domain.dto;
import lombok.Data;
import java.util.List;
/**
* 围栏位置检查响应结果
*
* @author: 默苍璃
* @date: 2025-09-1110:11
*/
@Data
public class FenceCheckResponse {
/**
* 设备ID
*/
private String deviceId;
/**
* 检查时间
*/
private Long checkTime;
/**
* 进入围栏列表
*/
private List<FenceInfo> enteredFences;
/**
* 离开围栏列表
*/
private List<FenceInfo> exitedFences;
/**
* 当前所在围栏列表
*/
private List<FenceInfo> currentFences;
/**
* 围栏信息
*/
@Data
public static class FenceInfo {
/**
* 围栏ID
*/
private Long fenceId;
/**
* 围栏名称
*/
private String fenceName;
/**
* 围栏类型
*/
private Integer fenceType;
}
}

View File

@ -0,0 +1,39 @@
package com.fuyuanshen.equipment.domain.query;
/**
* @author: 默苍璃
* @date: 2025-09-1110:10
*/
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* 围栏位置检查请求参数
*/
@Data
public class FenceCheckRequest {
/**
* 设备ID
*/
@NotNull(message = "设备ID不能为空")
private String deviceId;
/**
* 纬度
*/
@NotNull(message = "纬度不能为空")
private Double latitude;
/**
* 经度
*/
@NotNull(message = "经度不能为空")
private Double longitude;
/**
* 定位精度(米)
*/
private Double accuracy;
}

View File

@ -21,6 +21,18 @@ public class AlarmInformationVo {
*/
private Integer processingAlarm = 0;
/**
* 今日报警总数
*/
private Integer alarmsTotalToday = 0;
/**
* 今日总处理报警
*/
private Integer processingAlarmToday = 0;
/**
* 强制报警
*/
@ -37,7 +49,7 @@ public class AlarmInformationVo {
private Integer alarmManual = 0;
/**
* 电子围栏
*
*/
private Integer fenceElectronic = 0;

View File

@ -26,6 +26,11 @@ public class DataOverviewVo {
*/
private Integer bindingNew = 0;
/**
* 已绑定设备
*/
private Integer binding = 0;
/**
* 异常设备
*/

View File

@ -0,0 +1,94 @@
package com.fuyuanshen.equipment.domain.vo;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import com.fuyuanshen.common.excel.annotation.ExcelDictFormat;
import com.fuyuanshen.common.excel.convert.ExcelDictConvert;
import com.fuyuanshen.equipment.domain.DeviceFenceAccessRecord;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 围栏进出记录视图对象 device_fence_access_record
*
* @author Lion Li
* @date 2025-09-11
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = DeviceFenceAccessRecord.class)
public class DeviceFenceAccessRecordVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 记录ID
*/
@ExcelProperty(value = "记录ID")
private Long id;
/**
* 围栏ID
*/
@ExcelProperty(value = "围栏ID")
private Long fenceId;
/**
* 设备标识
*/
@ExcelProperty(value = "设备标识")
private String deviceId;
/**
* 用户ID
*/
@ExcelProperty(value = "用户ID")
private Long userId;
/**
* 事件类型
*/
@ExcelProperty(value = "事件类型")
private Long eventType;
/**
* 纬度
*/
@ExcelProperty(value = "纬度")
private Long latitude;
/**
* 经度
*/
@ExcelProperty(value = "经度")
private Long longitude;
/**
* 定位精度
*/
@ExcelProperty(value = "定位精度")
private Long accuracy;
/**
* 事件时间
*/
@ExcelProperty(value = "事件时间")
private Date eventTime;
/**
* 记录创建时间
*/
@ExcelProperty(value = "记录创建时间")
private Date createdTime;
}

View File

@ -0,0 +1,94 @@
package com.fuyuanshen.equipment.domain.vo;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import com.fuyuanshen.common.excel.annotation.ExcelDictFormat;
import com.fuyuanshen.common.excel.convert.ExcelDictConvert;
import com.fuyuanshen.equipment.domain.DeviceGeoFence;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 电子围栏视图对象 device_geo_fence
*
* @author Lion Li
* @date 2025-09-11
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = DeviceGeoFence.class)
public class DeviceGeoFenceVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 围栏唯一标识
*/
@ExcelProperty(value = "围栏唯一标识")
private Long id;
/**
* 围栏名称
*/
@ExcelProperty(value = "围栏名称")
private String name;
/**
* 围栏描述
*/
@ExcelProperty(value = "围栏描述")
private String description;
/**
* 围栏区域类型, 0 POLYGON, 1 CIRCLE
*/
@ExcelProperty(value = "围栏区域类型, 0 POLYGON, 1 CIRCLE")
private Integer areaType;
/**
* 围栏坐标数据
*/
@ExcelProperty(value = "围栏坐标数据")
private String coordinates;
/**
* 圆形围栏半径(米)
*/
@ExcelProperty(value = "圆形围栏半径(米)")
private Long radius;
/**
* 是否激活
*/
@ExcelProperty(value = "是否激活")
private Long isActive;
/**
* 创建人
*/
@ExcelProperty(value = "创建人")
private Long createdBy;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createdTime;
/**
* 更新时间
*/
@ExcelProperty(value = "更新时间")
private Date updatedTime;
}

View File

@ -26,4 +26,24 @@ public class EquipmentClassificationVo {
*/
private Integer devices4GAndBluetooth = 0;
/**
* 设备总数
*/
private Integer total;
/**
* 计算设备总数
*
* @return 设备总数
*/
public Integer getTotal() {
if (total == null) {
total = (equipment4G == null ? 0 : equipment4G) +
(deviceBluetooth == null ? 0 : deviceBluetooth) +
(devices4GAndBluetooth == null ? 0 : devices4GAndBluetooth);
}
return total;
}
}

View File

@ -0,0 +1,15 @@
package com.fuyuanshen.equipment.mapper;
import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus;
import com.fuyuanshen.equipment.domain.DeviceFenceAccessRecord;
import com.fuyuanshen.equipment.domain.vo.DeviceFenceAccessRecordVo;
/**
* 围栏进出记录Mapper接口
*
* @author Lion Li
* @date 2025-09-11
*/
public interface DeviceFenceAccessRecordMapper extends BaseMapperPlus<DeviceFenceAccessRecord, DeviceFenceAccessRecordVo> {
}

View File

@ -0,0 +1,15 @@
package com.fuyuanshen.equipment.mapper;
import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus;
import com.fuyuanshen.equipment.domain.DeviceGeoFence;
import com.fuyuanshen.equipment.domain.vo.DeviceGeoFenceVo;
/**
* 电子围栏Mapper接口
*
* @author Lion Li
* @date 2025-09-11
*/
public interface DeviceGeoFenceMapper extends BaseMapperPlus<DeviceGeoFence, DeviceGeoFenceVo> {
}

View File

@ -99,11 +99,11 @@ public interface DeviceMapper extends BaseMapper<Device> {
/**
* 获取设备使用数据
*
* @param deviceId 设备ID
* @param deviceTypeId 设备ID
* @param range 时间范围 1:半年 2:一年
* @return 每月使用数据列表
*/
List<Map<String, Object>> getEquipmentUsageData(Long deviceId, Integer range);
List<Map<String, Object>> getEquipmentUsageData(@Param("deviceTypeId") Long deviceTypeId, @Param("range") Integer range);
// 在DeviceMapper.java中添加方法
int getUsageDataForMonth(@Param("deviceId") Long deviceId,

View File

@ -139,9 +139,9 @@ public interface DeviceService extends IService<Device> {
/**
* 获取设备使用数据
*
* @param deviceId
* @param deviceTypeId
* @param range
* @return
*/
List<Map<String, Object>> getEquipmentUsageData(Long deviceId, Integer range);
List<Map<String, Object>> getEquipmentUsageData(Long deviceTypeId, Integer range);
}

View File

@ -0,0 +1,68 @@
package com.fuyuanshen.equipment.service;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
import com.fuyuanshen.equipment.domain.bo.DeviceFenceAccessRecordBo;
import com.fuyuanshen.equipment.domain.vo.DeviceFenceAccessRecordVo;
import java.util.Collection;
import java.util.List;
/**
* 围栏进出记录Service接口
*
* @author Lion Li
* @date 2025-09-11
*/
public interface IDeviceFenceAccessRecordService {
/**
* 查询围栏进出记录
*
* @param id 主键
* @return 围栏进出记录
*/
DeviceFenceAccessRecordVo queryById(Long id);
/**
* 分页查询围栏进出记录列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 围栏进出记录分页列表
*/
TableDataInfo<DeviceFenceAccessRecordVo> queryPageList(DeviceFenceAccessRecordBo bo, PageQuery pageQuery);
/**
* 查询符合条件的围栏进出记录列表
*
* @param bo 查询条件
* @return 围栏进出记录列表
*/
List<DeviceFenceAccessRecordVo> queryList(DeviceFenceAccessRecordBo bo);
/**
* 新增围栏进出记录
*
* @param bo 围栏进出记录
* @return 是否新增成功
*/
Boolean insertByBo(DeviceFenceAccessRecordBo bo);
/**
* 修改围栏进出记录
*
* @param bo 围栏进出记录
* @return 是否修改成功
*/
Boolean updateByBo(DeviceFenceAccessRecordBo bo);
/**
* 校验并批量删除围栏进出记录信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}

View File

@ -0,0 +1,78 @@
package com.fuyuanshen.equipment.service;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
import com.fuyuanshen.equipment.domain.bo.DeviceGeoFenceBo;
import com.fuyuanshen.equipment.domain.dto.FenceCheckResponse;
import com.fuyuanshen.equipment.domain.query.FenceCheckRequest;
import com.fuyuanshen.equipment.domain.vo.DeviceGeoFenceVo;
import java.util.Collection;
import java.util.List;
/**
* 电子围栏Service接口
*
* @author Lion Li
* @date 2025-09-11
*/
public interface IDeviceGeoFenceService {
/**
* 查询电子围栏
*
* @param id 主键
* @return 电子围栏
*/
DeviceGeoFenceVo queryById(Long id);
/**
* 分页查询电子围栏列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 电子围栏分页列表
*/
TableDataInfo<DeviceGeoFenceVo> queryPageList(DeviceGeoFenceBo bo, PageQuery pageQuery);
/**
* 查询符合条件的电子围栏列表
*
* @param bo 查询条件
* @return 电子围栏列表
*/
List<DeviceGeoFenceVo> queryList(DeviceGeoFenceBo bo);
/**
* 新增电子围栏
*
* @param bo 电子围栏
* @return 是否新增成功
*/
Boolean insertByBo(DeviceGeoFenceBo bo);
/**
* 修改电子围栏
*
* @param bo 电子围栏
* @return 是否修改成功
*/
Boolean updateByBo(DeviceGeoFenceBo bo);
/**
* 校验并批量删除电子围栏信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 检查设备位置与围栏的关系
*
* @param request 位置检查请求
* @return 位置检查结果
*/
FenceCheckResponse checkPosition(FenceCheckRequest request);
}

View File

@ -0,0 +1,140 @@
package com.fuyuanshen.equipment.service.impl;
import com.fuyuanshen.common.core.utils.MapstructUtils;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fuyuanshen.equipment.domain.DeviceFenceAccessRecord;
import com.fuyuanshen.equipment.domain.bo.DeviceFenceAccessRecordBo;
import com.fuyuanshen.equipment.domain.vo.DeviceFenceAccessRecordVo;
import com.fuyuanshen.equipment.mapper.DeviceFenceAccessRecordMapper;
import com.fuyuanshen.equipment.service.IDeviceFenceAccessRecordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.Collection;
/**
* 围栏进出记录Service业务层处理
*
* @author Lion Li
* @date 2025-09-11
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class DeviceFenceAccessRecordServiceImpl implements IDeviceFenceAccessRecordService {
private final DeviceFenceAccessRecordMapper baseMapper;
/**
* 查询围栏进出记录
*
* @param id 主键
* @return 围栏进出记录
*/
@Override
public DeviceFenceAccessRecordVo queryById(Long id){
return baseMapper.selectVoById(id);
}
/**
* 分页查询围栏进出记录列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 围栏进出记录分页列表
*/
@Override
public TableDataInfo<DeviceFenceAccessRecordVo> queryPageList(DeviceFenceAccessRecordBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<DeviceFenceAccessRecord> lqw = buildQueryWrapper(bo);
Page<DeviceFenceAccessRecordVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询符合条件的围栏进出记录列表
*
* @param bo 查询条件
* @return 围栏进出记录列表
*/
@Override
public List<DeviceFenceAccessRecordVo> queryList(DeviceFenceAccessRecordBo bo) {
LambdaQueryWrapper<DeviceFenceAccessRecord> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<DeviceFenceAccessRecord> buildQueryWrapper(DeviceFenceAccessRecordBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<DeviceFenceAccessRecord> lqw = Wrappers.lambdaQuery();
lqw.orderByAsc(DeviceFenceAccessRecord::getId);
lqw.eq(bo.getFenceId() != null, DeviceFenceAccessRecord::getFenceId, bo.getFenceId());
lqw.eq(StringUtils.isNotBlank(bo.getDeviceId()), DeviceFenceAccessRecord::getDeviceId, bo.getDeviceId());
lqw.eq(bo.getUserId() != null, DeviceFenceAccessRecord::getUserId, bo.getUserId());
lqw.eq(bo.getEventType() != null, DeviceFenceAccessRecord::getEventType, bo.getEventType());
lqw.eq(bo.getLatitude() != null, DeviceFenceAccessRecord::getLatitude, bo.getLatitude());
lqw.eq(bo.getLongitude() != null, DeviceFenceAccessRecord::getLongitude, bo.getLongitude());
lqw.eq(bo.getAccuracy() != null, DeviceFenceAccessRecord::getAccuracy, bo.getAccuracy());
lqw.eq(bo.getEventTime() != null, DeviceFenceAccessRecord::getEventTime, bo.getEventTime());
lqw.eq(bo.getCreatedTime() != null, DeviceFenceAccessRecord::getCreatedTime, bo.getCreatedTime());
return lqw;
}
/**
* 新增围栏进出记录
*
* @param bo 围栏进出记录
* @return 是否新增成功
*/
@Override
public Boolean insertByBo(DeviceFenceAccessRecordBo bo) {
DeviceFenceAccessRecord add = MapstructUtils.convert(bo, DeviceFenceAccessRecord.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改围栏进出记录
*
* @param bo 围栏进出记录
* @return 是否修改成功
*/
@Override
public Boolean updateByBo(DeviceFenceAccessRecordBo bo) {
DeviceFenceAccessRecord update = MapstructUtils.convert(bo, DeviceFenceAccessRecord.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(DeviceFenceAccessRecord entity){
//TODO 做一些数据校验,如唯一约束
}
/**
* 校验并批量删除围栏进出记录信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if(isValid){
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
}

View File

@ -0,0 +1,199 @@
package com.fuyuanshen.equipment.service.impl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fuyuanshen.common.core.utils.MapstructUtils;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fuyuanshen.equipment.domain.DeviceGeoFence;
import com.fuyuanshen.equipment.domain.bo.DeviceGeoFenceBo;
import com.fuyuanshen.equipment.domain.dto.FenceCheckResponse;
import com.fuyuanshen.equipment.domain.query.FenceCheckRequest;
import com.fuyuanshen.equipment.domain.vo.DeviceGeoFenceVo;
import com.fuyuanshen.equipment.mapper.DeviceGeoFenceMapper;
import com.fuyuanshen.equipment.service.IDeviceGeoFenceService;
import com.fuyuanshen.equipment.utils.map.GeoFenceChecker;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.Collection;
/**
* 电子围栏Service业务层处理
*
* @author Lion Li
* @date 2025-09-11
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class DeviceGeoFenceServiceImpl implements IDeviceGeoFenceService {
private final DeviceGeoFenceMapper baseMapper;
/**
* 查询电子围栏
*
* @param id 主键
* @return 电子围栏
*/
@Override
public DeviceGeoFenceVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 分页查询电子围栏列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 电子围栏分页列表
*/
@Override
public TableDataInfo<DeviceGeoFenceVo> queryPageList(DeviceGeoFenceBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<DeviceGeoFence> lqw = buildQueryWrapper(bo);
Page<DeviceGeoFenceVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询符合条件的电子围栏列表
*
* @param bo 查询条件
* @return 电子围栏列表
*/
@Override
public List<DeviceGeoFenceVo> queryList(DeviceGeoFenceBo bo) {
LambdaQueryWrapper<DeviceGeoFence> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<DeviceGeoFence> buildQueryWrapper(DeviceGeoFenceBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<DeviceGeoFence> lqw = Wrappers.lambdaQuery();
lqw.orderByAsc(DeviceGeoFence::getId);
lqw.like(StringUtils.isNotBlank(bo.getName()), DeviceGeoFence::getName, bo.getName());
lqw.eq(StringUtils.isNotBlank(bo.getDescription()), DeviceGeoFence::getDescription, bo.getDescription());
lqw.eq(bo.getAreaType() != null, DeviceGeoFence::getAreaType, bo.getAreaType());
lqw.eq(StringUtils.isNotBlank(bo.getCoordinates()), DeviceGeoFence::getCoordinates, bo.getCoordinates());
lqw.eq(bo.getRadius() != null, DeviceGeoFence::getRadius, bo.getRadius());
lqw.eq(bo.getIsActive() != null, DeviceGeoFence::getIsActive, bo.getIsActive());
lqw.eq(bo.getCreatedBy() != null, DeviceGeoFence::getCreatedBy, bo.getCreatedBy());
lqw.eq(bo.getCreatedTime() != null, DeviceGeoFence::getCreatedTime, bo.getCreatedTime());
lqw.eq(bo.getUpdatedTime() != null, DeviceGeoFence::getUpdatedTime, bo.getUpdatedTime());
return lqw;
}
/**
* 新增电子围栏
*
* @param bo 电子围栏
* @return 是否新增成功
*/
@Override
public Boolean insertByBo(DeviceGeoFenceBo bo) {
DeviceGeoFence add = MapstructUtils.convert(bo, DeviceGeoFence.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改电子围栏
*
* @param bo 电子围栏
* @return 是否修改成功
*/
@Override
public Boolean updateByBo(DeviceGeoFenceBo bo) {
DeviceGeoFence update = MapstructUtils.convert(bo, DeviceGeoFence.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(DeviceGeoFence entity) {
// TODO 做一些数据校验,如唯一约束
}
/**
* 校验并批量删除电子围栏信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
/**
* 检查设备位置与围栏的关系
*
* @param request 位置检查请求
* @return 位置检查结果
*/
@Override
public FenceCheckResponse checkPosition(FenceCheckRequest request) {
// 1. 获取所有激活的围栏
DeviceGeoFenceBo bo = new DeviceGeoFenceBo();
bo.setIsActive(1L); // 假设1表示激活状态
List<DeviceGeoFenceVo> activeFences = queryList(bo);
// 2. 判断设备位置与各围栏的关系
FenceCheckResponse response = new FenceCheckResponse();
response.setDeviceId(request.getDeviceId());
response.setCheckTime(System.currentTimeMillis());
// 这里需要实现具体的围栏判断逻辑
// 根据您之前提供的算法实现点在围栏内的判断
for (DeviceGeoFenceVo fence : activeFences) {
String coordinates = fence.getCoordinates();
// 在需要转换的地方
ObjectMapper objectMapper = new ObjectMapper();
List<GeoFenceChecker.Coordinate> coordinateList = List.of();
try {
coordinateList = objectMapper.readValue(coordinates,
new TypeReference<List<GeoFenceChecker.Coordinate>>() {
});
} catch (Exception e) {
// 处理解析异常
log.error("坐标数据解析失败: {}", e.getMessage());
}
FenceCheckResponse.FenceInfo fenceInfo = new FenceCheckResponse.FenceInfo();
boolean pointInFence = GeoFenceChecker.isPointInFence(request.getLatitude(), request.getLongitude(), fence.getAreaType(), coordinateList, fence.getRadius());
if (pointInFence) {
fenceInfo.setFenceId(fence.getId());
fenceInfo.setFenceName(fence.getName());
fenceInfo.setFenceType(fence.getAreaType());
response.getEnteredFences().add(fenceInfo);
} else {
response.getExitedFences().add(fenceInfo);
}
}
return response;
}
}

View File

@ -630,6 +630,7 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
@Override
public EquipmentClassificationVo getEquipmentClassification() {
EquipmentClassificationVo equipmentClassification = deviceMapper.getEquipmentClassification();
equipmentClassification.getTotal();
return equipmentClassification;
}
@ -648,13 +649,13 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
/**
* 获取设备使用数据
*
* @param deviceId 设备ID
* @param deviceTypeId 设备ID
* @param range 时间范围 1:半年 2:一年
* @return 每月使用数据列表
*/
@Override
public List<Map<String, Object>> getEquipmentUsageData(Long deviceId, Integer range) {
List<Map<String, Object>> equipmentUsageData = deviceMapper.getEquipmentUsageData(deviceId, range);
public List<Map<String, Object>> getEquipmentUsageData(Long deviceTypeId, Integer range) {
List<Map<String, Object>> equipmentUsageData = deviceMapper.getEquipmentUsageData(deviceTypeId, range);
return equipmentUsageData;
}

View File

@ -0,0 +1,24 @@
package com.fuyuanshen.equipment.utils.map;
/**
* @author: 默苍璃
* @date: 2025-09-1110:27
*/
public class Coordinate {
private double lat;
private double lng;
// 构造函数
public Coordinate() {}
public Coordinate(double lat, double lng) {
this.lat = lat;
this.lng = lng;
}
// getter和setter方法
public double getLat() { return lat; }
public void setLat(double lat) { this.lat = lat; }
public double getLng() { return lng; }
public void setLng(double lng) { this.lng = lng; }
}

View File

@ -0,0 +1,135 @@
package com.fuyuanshen.equipment.utils.map;
import java.util.List;
/**
* @author: 默苍璃
* @date: 2025-09-1110:13
*/
public class GeoFenceChecker {
/**
* 点是否在围栏内
*
* @param pointLat 点的纬度
* @param pointLng 点的经度
* @param fenceType 围栏类型0 POLYGON 或 1 CIRCLE
* @param coordinates 围栏坐标点列表
* @param radius 圆形围栏半径仅对CIRCLE类型有效
* @return true表示在围栏内false表示在围栏外
*/
public static boolean isPointInFence(double pointLat, double pointLng,
Integer fenceType,
List<Coordinate> coordinates,
Long radius) {
if (fenceType == 0) {
return isPointInPolygon(pointLat, pointLng, coordinates);
} else if (fenceType == 1) {
if (coordinates == null || coordinates.isEmpty() || radius == null) {
return false;
}
// 圆形围栏只有一个坐标点(圆心)
Coordinate center = coordinates.get(0);
return isPointInCircle(pointLat, pointLng, center.lat, center.lng, radius);
}
return false;
}
/**
* 使用射线投射算法判断点是否在多边形内
*
* @param pointLat 点的纬度
* @param pointLng 点的经度
* @param polygon 多边形顶点坐标列表
* @return true表示在多边形内false表示在多边形外
*/
public static boolean isPointInPolygon(double pointLat, double pointLng,
List<Coordinate> polygon) {
if (polygon == null || polygon.size() < 3) {
return false;
}
int intersectCount = 0;
int vertexCount = polygon.size();
// 遍历多边形的每条边
for (int i = 0; i < vertexCount; i++) {
Coordinate p1 = polygon.get(i);
Coordinate p2 = polygon.get((i + 1) % vertexCount);
// 检查点是否在边的y范围内
if (p1.lat != p2.lat &&
(pointLat >= Math.min(p1.lat, p2.lat)) &&
(pointLat < Math.max(p1.lat, p2.lat))) {
// 计算边与从点发出的水平射线的交点经度
double intersectLng = (pointLat - p1.lat) * (p2.lng - p1.lng) / (p2.lat - p1.lat) + p1.lng;
// 如果交点在点的右侧则计数加1
if (pointLng < intersectLng) {
intersectCount++;
}
}
}
// 奇数个交点表示点在多边形内
return intersectCount % 2 == 1;
}
/**
* 判断点是否在圆形内
*
* @param pointLat 点的纬度
* @param pointLng 点的经度
* @param centerLat 圆心纬度
* @param centerLng 圆心经度
* @param radius 半径(米)
* @return true表示在圆内false表示在圆外
*/
public static boolean isPointInCircle(double pointLat, double pointLng,
double centerLat, double centerLng,
Long radius) {
double distance = calculateDistance(pointLat, pointLng, centerLat, centerLng);
return distance <= radius;
}
/**
* 计算两点间距离使用Haversine公式
*
* @param lat1 点1纬度
* @param lng1 点1经度
* @param lat2 点2纬度
* @param lng2 点2经度
* @return 距离(米)
*/
public static double calculateDistance(double lat1, double lng1, double lat2, double lng2) {
final double EARTH_RADIUS = 6371000; // 地球半径(米)
double dLat = Math.toRadians(lat2 - lat1);
double dLng = Math.toRadians(lng2 - lng1);
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
Math.sin(dLng / 2) * Math.sin(dLng / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return EARTH_RADIUS * c;
}
/**
* 坐标点类
*/
public static class Coordinate {
public double lat;
public double lng;
public Coordinate() {
}
public Coordinate(double lat, double lng) {
this.lat = lat;
this.lng = lng;
}
}
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fuyuanshen.equipment.mapper.DeviceFenceAccessRecordMapper">
</mapper>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fuyuanshen.equipment.mapper.DeviceGeoFenceMapper">
</mapper>

View File

@ -321,11 +321,14 @@
<!-- 获取数据总览 -->
<select id="getDataOverview" resultType="com.fuyuanshen.equipment.domain.vo.DataOverviewVo">
SELECT (SELECT COUNT(1) FROM device) AS devicesNumber,
(SELECT COUNT(1) FROM device WHERE device_status = 1) AS equipmentOnline,
(SELECT COUNT(1) FROM device WHERE DATE (create_time) = CURDATE()) AS bindingNew, (
(SELECT COUNT(1) FROM device WHERE online_status = 1) AS equipmentOnline,
(SELECT COUNT(1) FROM device WHERE DATE (create_time) = CURDATE() AND binding_status = 1 ) AS bindingNew, (
SELECT COUNT (1)
FROM device
WHERE device_status = 2) AS equipmentAbnormal
WHERE binding_status = 1 ) AS binding, (
SELECT COUNT (1)
FROM device
WHERE online_status = 2) AS equipmentAbnormal
</select>
<!-- 获取设备分类 -->
@ -347,55 +350,67 @@
<!-- 获取告警信息 -->
<select id="getAlarmInformation" resultType="com.fuyuanshen.equipment.domain.vo.AlarmInformationVo">
SELECT (SELECT COUNT(1) FROM device_alarm WHERE treatment_state = 0 AND DATE (create_time) = CURDATE()) AS alarmsTotal, (
SELECT (SELECT COUNT(1) FROM device_alarm WHERE treatment_state = 0) AS alarmsTotal
, (SELECT COUNT(1)
FROM device_alarm
WHERE treatment_state = 0) AS processingAlarm
, (SELECT COUNT(1)
FROM device_alarm
WHERE treatment_state = 0 AND
DATE (create_time) = CURDATE()) AS alarmsTotalToday
, (
SELECT COUNT (1)
FROM device_alarm
WHERE treatment_state = 0
AND DATE (create_time) = CURDATE()) AS processingAlarm
AND DATE (create_time) = CURDATE()) AS processingAlarmToday
, (
SELECT COUNT (1)
FROM device_alarm
WHERE device_action = 0
AND DATE (create_time) = CURDATE()) AS alarmForced
) AS alarmForced
, (
SELECT COUNT (1)
FROM device_alarm
WHERE device_action = 1
AND DATE (create_time) = CURDATE()) AS intrusionImpact
) AS intrusionImpact
, (
SELECT COUNT (1)
FROM device_alarm
WHERE device_action = 2
AND DATE (create_time) = CURDATE()) AS alarmManual
) AS alarmManual
, (
SELECT COUNT (1)
FROM device_alarm
WHERE device_action = 3 AND DATE (create_time) = CURDATE()) AS fenceElectronic
WHERE device_action = 3) AS fenceElectronic
</select>
<!-- 获取设备使用数据 -->
<select id="getEquipmentUsageData" resultType="map">
SELECT COUNT(CASE WHEN MONTH (dl.create_time) = 1 THEN 1 END) AS m1,
COUNT(CASE WHEN MONTH (dl.create_time) = 2 THEN 1 END) AS m2,
COUNT(CASE WHEN MONTH (dl.create_time) = 3 THEN 1 END) AS m3,
COUNT(CASE WHEN MONTH (dl.create_time) = 4 THEN 1 END) AS m4,
COUNT(CASE WHEN MONTH (dl.create_time) = 5 THEN 1 END) AS m5,
COUNT(CASE WHEN MONTH (dl.create_time) = 6 THEN 1 END) AS m6,
COUNT(CASE WHEN MONTH (dl.create_time) = 7 THEN 1 END) AS m7,
COUNT(CASE WHEN MONTH (dl.create_time) = 8 THEN 1 END) AS m8,
COUNT(CASE WHEN MONTH (dl.create_time) = 9 THEN 1 END) AS m9,
COUNT(CASE WHEN MONTH (dl.create_time) = 10 THEN 1 END) AS m10,
COUNT(CASE WHEN MONTH (dl.create_time) = 11 THEN 1 END) AS m11,
COUNT(CASE WHEN MONTH (dl.create_time) = 12 THEN 1 END) AS m12
SELECT COUNT(CASE WHEN MONTH (dl.create_time) = 1 THEN 1 END) AS m1,
COUNT(CASE WHEN MONTH (dl.create_time) = 2 THEN 1 END) AS m2,
COUNT(CASE WHEN MONTH (dl.create_time) = 3 THEN 1 END) AS m3,
COUNT(CASE WHEN MONTH (dl.create_time) = 4 THEN 1 END) AS m4,
COUNT(CASE WHEN MONTH (dl.create_time) = 5 THEN 1 END) AS m5,
COUNT(CASE WHEN MONTH (dl.create_time) = 6 THEN 1 END) AS m6,
COUNT(CASE WHEN MONTH (dl.create_time) = 7 THEN 1 END) AS m7,
COUNT(CASE WHEN MONTH (dl.create_time) = 8 THEN 1 END) AS m8,
COUNT(CASE WHEN MONTH (dl.create_time) = 9 THEN 1 END) AS m9,
COUNT(CASE WHEN MONTH (dl.create_time) = 10 THEN 1 END) AS m10,
COUNT(CASE WHEN MONTH (dl.create_time) = 11 THEN 1 END) AS m11,
COUNT(CASE WHEN MONTH (dl.create_time) = 12 THEN 1 END) AS m12
FROM device_log dl
LEFT JOIN device d ON dl.device_id = d.id
LEFT JOIN device_type dt ON d.device_type = dt.id
WHERE dt.id = #{deviceId}
AND (
LEFT JOIN device d ON dl.device_id = d.id
LEFT JOIN device_type dt ON d.device_type = dt.id
<where>
<if test="deviceTypeId != null">
dt.id = #{deviceTypeId}
</if>
AND (
(#{range} = 1 AND dl.create_time >= DATE_SUB(NOW(), INTERVAL 6 MONTH)) OR
(#{range} = 2 AND dl.create_time >= DATE_SUB(NOW(), INTERVAL 12 MONTH))
)
</where>
</select>
<select id="getUsageDataForMonth" resultType="java.lang.Integer">