Compare commits

16 Commits

24 changed files with 376 additions and 124 deletions

View File

@ -71,6 +71,8 @@ public class RedisKeyExpirationListener implements MessageListener {
deviceUpdateWrapper.eq("device_imei", element); deviceUpdateWrapper.eq("device_imei", element);
deviceUpdateWrapper.set("online_status", 0); deviceUpdateWrapper.set("online_status", 0);
deviceMapper.update(deviceUpdateWrapper); deviceMapper.update(deviceUpdateWrapper);
}else{
RedisUtils.deleteObject(deviceOnlineStatusRedisKey);
} }
} finally { } finally {
//释放锁 //释放锁

View File

@ -54,7 +54,7 @@ public class ReceiverMessageHandler implements MessageHandler {
RedisUtils.offerDeduplicated(queueKey,dedupKey,deviceImei, Duration.ofHours(24)); RedisUtils.offerDeduplicated(queueKey,dedupKey,deviceImei, Duration.ofHours(24));
//在线状态 //在线状态
String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ deviceImei + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX ; String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ deviceImei + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX ;
RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "1", Duration.ofSeconds(62)); RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "1", Duration.ofSeconds(120));
} }
String state = payloadDict.getStr("state"); String state = payloadDict.getStr("state");

View File

@ -98,21 +98,24 @@ public class MqttMessageConsumer {
String threadName = Thread.currentThread().getName(); String threadName = Thread.currentThread().getName();
try { try {
log.info("业务处理线程 {} 开始处理消息: {}", threadName, message); log.info("业务处理线程 {} 开始处理消息: {}", threadName, message);
String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ message + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX ; // String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY+ DEVICE_KEY_PREFIX+ message + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX ;
String deviceOnlineStatusRedis = RedisUtils.getCacheObject(deviceOnlineStatusRedisKey); // String deviceOnlineStatusRedis = RedisUtils.getCacheObject(deviceOnlineStatusRedisKey);
if(StringUtils.isBlank(deviceOnlineStatusRedis)){ // if(StringUtils.isBlank(deviceOnlineStatusRedis)){
// UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
// updateWrapper.eq("device_imei", message)
// .set("online_status", 1);
// deviceMapper.update(updateWrapper);
// }
QueryWrapper<Device> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("device_imei", message);
queryWrapper.eq("online_status", 1);
Long count = deviceMapper.selectCount(queryWrapper);
if(count == 0){
UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>(); UpdateWrapper<Device> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("device_imei", message) updateWrapper.eq("device_imei", message)
.set("online_status", 1); .set("online_status", 1);
deviceMapper.update(updateWrapper); deviceMapper.update(updateWrapper);
} }
// QueryWrapper<Device> queryWrapper = new QueryWrapper<>();
// queryWrapper.eq("device_imei", message);
// queryWrapper.eq("online_status", 1);
// Long count = deviceMapper.selectCount(queryWrapper);
// if(count == 0){
//
// }
// 模拟业务处理耗时 // 模拟业务处理耗时
// Thread.sleep(200); // Thread.sleep(200);

View File

@ -114,4 +114,9 @@ public class AppDeviceShareVo implements Serializable {
* 创建时间 * 创建时间
*/ */
private String createTime; private String createTime;
/**
* 设备详情页面
*/
private String detailPageUrl;
} }

View File

@ -9,6 +9,7 @@ import org.slf4j.LoggerFactory;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.InputStream; import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.net.URLConnection; import java.net.URLConnection;
@ -33,23 +34,62 @@ public class IgnoreFailedImageConverter implements Converter<URL> {
@Override @Override
public WriteCellData<?> convertToExcelData(URL value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { public WriteCellData<?> convertToExcelData(URL value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
if (value == null) { if (value == null) {
return null; logger.debug("图片URL为空");
return new WriteCellData<>(new byte[0]);
} }
try { try {
logger.debug("开始加载图片: {}", value);
URLConnection conn = value.openConnection(); URLConnection conn = value.openConnection();
conn.setConnectTimeout(2000); // 2秒超时 // 增加连接和读取超时时间
conn.setReadTimeout(3000); // 3秒超时 conn.setConnectTimeout(10000); // 10秒连接超时
conn.setReadTimeout(30000); // 30秒读取超时
// 添加User-Agent避免被服务器拦截
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ExcelExporter/1.0");
// 如果是HTTP连接设置一些额外的属性
if (conn instanceof HttpURLConnection) {
HttpURLConnection httpConn = (HttpURLConnection) conn;
httpConn.setRequestMethod("GET");
// 不使用缓存
httpConn.setUseCaches(false);
// 跟随重定向
httpConn.setInstanceFollowRedirects(true);
}
long contentLength = conn.getContentLengthLong();
logger.debug("连接建立成功,图片大小: {} 字节", contentLength);
// 检查内容长度是否有效
if (contentLength == 0) {
logger.warn("图片文件为空: {}", value);
return new WriteCellData<>(new byte[0]);
}
// 限制图片大小(防止过大文件导致内存问题)
if (contentLength > 10 * 1024 * 1024) { // 10MB限制
logger.warn("图片文件过大 ({} bytes),跳过加载: {}", contentLength, value);
return new WriteCellData<>(new byte[0]);
}
try (InputStream inputStream = conn.getInputStream()) { try (InputStream inputStream = conn.getInputStream()) {
// byte[] bytes = FileUtils.readInputStream(inputStream, value.toString()); // byte[] bytes = FileUtils.readInputStream(inputStream, value.toString());
// 替代 FileUtils.readInputStream 的自定义方法 // 替代 FileUtils.readInputStream 的自定义方法
byte[] bytes = readInputStream(inputStream); byte[] bytes = readInputStream(inputStream);
// 检查读取到的数据是否为空
if (bytes == null || bytes.length == 0) {
logger.warn("读取到空的图片数据: {}", value);
return new WriteCellData<>(new byte[0]);
}
logger.debug("成功读取图片数据,大小: {} 字节", bytes.length);
return new WriteCellData<>(bytes); return new WriteCellData<>(bytes);
} }
} catch (Exception e) { } catch (Exception e) {
// 静默忽略错误,只记录日志 // 静默忽略错误,只记录日志
logger.debug("忽略图片加载失败: {}, 原因: {}", value, e.getMessage()); logger.warn("图片加载失败: {}, 原因: {}", value, e.getMessage(), e);
// return null; // 返回null表示不写入图片 // return null; // 返回null表示不写入图片
return new WriteCellData<>(new byte[0]); // 返回空数组而不是 null return new WriteCellData<>(new byte[0]); // 返回空数组而不是 null
} }
@ -66,9 +106,17 @@ public class IgnoreFailedImageConverter implements Converter<URL> {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[8192]; byte[] buffer = new byte[8192];
int bytesRead; int bytesRead;
int totalBytes = 0;
while ((bytesRead = inputStream.read(buffer)) != -1) { while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead); outputStream.write(buffer, 0, bytesRead);
totalBytes += bytesRead;
// 如果读取的数据过大,提前终止
if (totalBytes > 10 * 1024 * 1024) { // 10MB限制
logger.warn("读取的图片数据超过10MB限制提前终止");
break;
}
} }
return outputStream.toByteArray(); return outputStream.toByteArray();

View File

@ -69,4 +69,9 @@ public class DeviceFenceAccessRecord extends BaseEntity {
*/ */
private Date eventTime; private Date eventTime;
/**
* 事件地址
*/
private String eventAddress;
} }

View File

@ -1,5 +1,6 @@
package com.fuyuanshen.equipment.domain.bo; package com.fuyuanshen.equipment.domain.bo;
import cn.idev.excel.annotation.ExcelProperty;
import com.fuyuanshen.common.core.validate.AddGroup; import com.fuyuanshen.common.core.validate.AddGroup;
import com.fuyuanshen.common.core.validate.EditGroup; import com.fuyuanshen.common.core.validate.EditGroup;
import com.fuyuanshen.common.mybatis.core.domain.BaseEntity; import com.fuyuanshen.common.mybatis.core.domain.BaseEntity;
@ -8,7 +9,9 @@ import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*; import jakarta.validation.constraints.*;
import java.util.Date; import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
/** /**
@ -25,19 +28,24 @@ public class DeviceFenceAccessRecordBo extends BaseEntity {
/** /**
* 记录ID * 记录ID
*/ */
@NotNull(message = "记录ID不能为空", groups = { EditGroup.class }) @NotNull(message = "记录ID不能为空", groups = {EditGroup.class})
private Long id; private Long id;
/** /**
* 围栏ID * 围栏ID
*/ */
@NotNull(message = "围栏ID不能为空", groups = { AddGroup.class, EditGroup.class }) @NotNull(message = "围栏ID不能为空", groups = {AddGroup.class, EditGroup.class})
private Long fenceId; private Long fenceId;
/**
* 围栏名称
*/
private String fenceName;
/** /**
* 设备标识 * 设备标识
*/ */
@NotBlank(message = "设备标识不能为空", groups = { AddGroup.class, EditGroup.class }) @NotBlank(message = "设备标识不能为空", groups = {AddGroup.class, EditGroup.class})
private String deviceId; private String deviceId;
/** /**
@ -48,19 +56,19 @@ public class DeviceFenceAccessRecordBo extends BaseEntity {
/** /**
* 事件类型 * 事件类型
*/ */
@NotNull(message = "事件类型不能为空", groups = { AddGroup.class, EditGroup.class }) @NotNull(message = "事件类型不能为空", groups = {AddGroup.class, EditGroup.class})
private Long eventType; private Long eventType;
/** /**
* 纬度 * 纬度
*/ */
@NotNull(message = "纬度不能为空", groups = { AddGroup.class, EditGroup.class }) @NotNull(message = "纬度不能为空", groups = {AddGroup.class, EditGroup.class})
private Double latitude; private Double latitude;
/** /**
* 经度 * 经度
*/ */
@NotNull(message = "经度不能为空", groups = { AddGroup.class, EditGroup.class }) @NotNull(message = "经度不能为空", groups = {AddGroup.class, EditGroup.class})
private Double longitude; private Double longitude;
/** /**
@ -71,9 +79,14 @@ public class DeviceFenceAccessRecordBo extends BaseEntity {
/** /**
* 事件时间 * 事件时间
*/ */
@NotNull(message = "事件时间不能为空", groups = { AddGroup.class, EditGroup.class }) @NotNull(message = "事件时间不能为空", groups = {AddGroup.class, EditGroup.class})
private Date eventTime; private Date eventTime;
/**
* 事件地址
*/
private String eventAddress;
/** /**
* 开始时间 * 开始时间
*/ */

View File

@ -19,24 +19,25 @@ import java.net.URL;
@ContentRowHeight(100) // 内容行高 @ContentRowHeight(100) // 内容行高
public class DeviceExcelExportDTO { public class DeviceExcelExportDTO {
@ExcelProperty("ID") // @ExcelProperty("ID")
private Long id; // private Long id;
@ExcelProperty("设备类型")
private Long deviceType; // @ExcelProperty("设备类型")
// private Long deviceType;
// @ExcelProperty("客户号") // @ExcelProperty("客户号")
// private Long customerId; // private Long customerId;
@ExcelProperty("所属客户") // @ExcelProperty("所属客户")
private String customerName; // private String customerName;
@ExcelProperty("设备名称") @ExcelProperty("设备名称")
@ColumnWidth(20) @ColumnWidth(20)
private String deviceName; private String deviceName;
@ExcelProperty(value = "设备图片", converter = IgnoreFailedImageConverter.class) @ExcelProperty(value = "设备图片", converter = IgnoreFailedImageConverter.class)
@ColumnWidth(15) // 设置图片列宽度 @ColumnWidth(30) // 设置图片列宽度
private URL devicePic; // 使用URL类型 private URL devicePic; // 使用URL类型
@ExcelProperty("设备MAC") @ExcelProperty("设备MAC")
@ -51,28 +52,37 @@ public class DeviceExcelExportDTO {
@ColumnWidth(20) @ColumnWidth(20)
private String deviceImei; private String deviceImei;
@ExcelProperty("经度") @ExcelProperty("设备类型")
private String longitude; @ColumnWidth(20)
private String typeName;
@ExcelProperty("纬度") // @ExcelProperty("经度")
private String latitude; // private String longitude;
// @ExcelProperty("纬度")
// private String latitude;
/**
* 绑定状态
* 0 未绑定
* 1 已绑定
*/
@ExcelProperty("绑定状态")
@ColumnWidth(20)
private String bindingStatus;
@ExcelProperty("备注") @ExcelProperty("备注")
@ColumnWidth(30) @ColumnWidth(30)
private String remark; private String remark;
@ExcelProperty("设备类型名称")
@ColumnWidth(20)
private String typeName;
/** /**
* 设备状态 * 设备状态
* 0 失效 * 0 失效
* 1 正常 * 1 正常
*/ */
@ExcelProperty("设备状态") // @ExcelProperty("设备状态")
@ColumnWidth(20) // @ColumnWidth(20)
private String deviceStatus; // private String deviceStatus;
@ExcelProperty("创建时间") @ExcelProperty("创建时间")
@ColumnWidth(20) @ColumnWidth(20)

View File

@ -16,24 +16,24 @@ import lombok.Data;
@ContentRowHeight(100) // 内容行高 @ContentRowHeight(100) // 内容行高
public class DeviceExcelImportDTO { public class DeviceExcelImportDTO {
@ExcelProperty("设备类型") // @ExcelProperty("设备类型")
private Long deviceType; // private Long deviceType;
@ExcelProperty("客户号")
private Long customerId;
@ExcelProperty("设备名称") @ExcelProperty("设备名称")
@ColumnWidth(20) @ColumnWidth(20)
private String deviceName; private String deviceName;
@ExcelProperty(value = "设备图片", converter = ByteArrayImageConverter.class) @ExcelProperty("设备类型名称")
@ColumnWidth(15) private String typeName;
private byte[] devicePic;
// 添加图片写入方法 // @ExcelProperty(value = "设备图片", converter = ByteArrayImageConverter.class)
public void setDevicePicFromBytes(byte[] bytes) { // @ColumnWidth(15)
this.devicePic = bytes; // private byte[] devicePic;
} //
// // 添加图片写入方法
// public void setDevicePicFromBytes(byte[] bytes) {
// this.devicePic = bytes;
// }
@ExcelProperty("设备MAC") @ExcelProperty("设备MAC")
@ColumnWidth(20) @ColumnWidth(20)
@ -43,24 +43,21 @@ public class DeviceExcelImportDTO {
@ColumnWidth(20) @ColumnWidth(20)
private String deviceImei; private String deviceImei;
@ExcelProperty("设备SN") @ExcelProperty("蓝牙名称")
@ColumnWidth(20) private String bluetoothName;
private String deviceSn;
@ExcelProperty("经度") // @ExcelProperty("设备SN")
private String longitude; // @ColumnWidth(20)
// private String deviceSn;
@ExcelProperty("纬度") //
private String latitude; // @ExcelProperty("经度")
// private String longitude;
//
// @ExcelProperty("纬度")
// private String latitude;
@ExcelProperty("备注") @ExcelProperty("备注")
@ColumnWidth(30) @ColumnWidth(30)
private String remark; private String remark;
@ExcelProperty("设备类型名称")
private String typeName;
@ExcelProperty("蓝牙名称")
private String bluetoothName;
} }

View File

@ -43,7 +43,6 @@ public class DeviceForm {
@Schema(title = "蓝牙名称") @Schema(title = "蓝牙名称")
private String bluetoothName; private String bluetoothName;
@Schema(title = "设备IMEI") @Schema(title = "设备IMEI")
private String deviceImei; private String deviceImei;

View File

@ -90,13 +90,13 @@ public class DeviceAlarmVo implements Serializable {
* 经度 * 经度
*/ */
@ExcelProperty(value = "经度") @ExcelProperty(value = "经度")
private Long longitude; private Double longitude;
/** /**
* 纬度 * 纬度
*/ */
@ExcelProperty(value = "纬度") @ExcelProperty(value = "纬度")
private Long latitude; private Double latitude;
/** /**
* 报警位置 * 报警位置

View File

@ -77,13 +77,13 @@ public class DeviceFenceAccessRecordVo implements Serializable {
* 纬度 * 纬度
*/ */
@ExcelProperty(value = "纬度") @ExcelProperty(value = "纬度")
private Long latitude; private Double latitude;
/** /**
* 经度 * 经度
*/ */
@ExcelProperty(value = "经度") @ExcelProperty(value = "经度")
private Long longitude; private Double longitude;
/** /**
* 定位精度 * 定位精度
@ -97,6 +97,12 @@ public class DeviceFenceAccessRecordVo implements Serializable {
@ExcelProperty(value = "事件时间") @ExcelProperty(value = "事件时间")
private Date eventTime; private Date eventTime;
/**
* 事件地址
*/
@ExcelProperty(value = "事件地址")
private String eventAddress;
/** /**
* 记录创建时间 * 记录创建时间
*/ */

View File

@ -72,7 +72,7 @@ public class UploadDeviceDataListener implements ReadListener<DeviceExcelImportD
// 设置图片数据 // 设置图片数据
byte[] imageData = rowImageMap.get(rowIndex); byte[] imageData = rowImageMap.get(rowIndex);
if (imageData != null) { if (imageData != null) {
recordWithImage.setDevicePicFromBytes(imageData); // recordWithImage.setDevicePicFromBytes(imageData);
} }
failedRecordsWithImages.add(recordWithImage); failedRecordsWithImages.add(recordWithImage);
@ -125,7 +125,6 @@ public class UploadDeviceDataListener implements ReadListener<DeviceExcelImportD
rowDeviceMap.put(rowIndex, device); rowDeviceMap.put(rowIndex, device);
rowDtoMap.put(rowIndex, data); rowDtoMap.put(rowIndex, data);
rowIndexList.add(rowIndex); rowIndexList.add(rowIndex);
} }
@ -191,7 +190,8 @@ public class UploadDeviceDataListener implements ReadListener<DeviceExcelImportD
if (device != null) { if (device != null) {
try { try {
byte[] imageData = picture.getPictureData().getData(); byte[] imageData = picture.getPictureData().getData();
String extraValue = getCellValue(sheet, rowIndex, 4); // 表示Excel表格中的第3列因为索引从0开始计算
String extraValue = getCellValue(sheet, rowIndex, 2);
String imageUrl = uploadAndGenerateUrl(imageData, extraValue); String imageUrl = uploadAndGenerateUrl(imageData, extraValue);
device.setDevicePic(imageUrl); device.setDevicePic(imageUrl);

View File

@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus; import com.fuyuanshen.common.mybatis.core.mapper.BaseMapperPlus;
import com.fuyuanshen.equipment.domain.DeviceFenceAccessRecord; import com.fuyuanshen.equipment.domain.DeviceFenceAccessRecord;
import com.fuyuanshen.equipment.domain.bo.DeviceFenceAccessRecordBo;
import com.fuyuanshen.equipment.domain.vo.DeviceFenceAccessRecordVo; import com.fuyuanshen.equipment.domain.vo.DeviceFenceAccessRecordVo;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
@ -29,5 +30,14 @@ public interface DeviceFenceAccessRecordMapper extends BaseMapperPlus<DeviceFenc
List<DeviceFenceAccessRecordVo> selectVoPageWithFenceAndDeviceName(@Param(Constants.WRAPPER) Wrapper<DeviceFenceAccessRecord> wrapper); List<DeviceFenceAccessRecordVo> selectVoPageWithFenceAndDeviceName(@Param(Constants.WRAPPER) Wrapper<DeviceFenceAccessRecord> wrapper);
/**
* 分页查询围栏进出记录列表纯XML形式
*
* @param page 分页参数
* @param bo 查询条件
* @return 围栏进出记录分页列表
*/
Page<DeviceFenceAccessRecordVo> selectVoPageByXml(Page<DeviceFenceAccessRecord> page, @Param("bo") DeviceFenceAccessRecordBo bo);
} }

View File

@ -5,6 +5,7 @@ import com.alibaba.excel.util.DateUtils;
import com.fuyuanshen.equipment.domain.Device; import com.fuyuanshen.equipment.domain.Device;
import com.fuyuanshen.equipment.domain.dto.DeviceExcelExportDTO; import com.fuyuanshen.equipment.domain.dto.DeviceExcelExportDTO;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.io.IOException; import java.io.IOException;
@ -20,6 +21,7 @@ import java.util.stream.Collectors;
* @date: 2025-06-0618:22 * @date: 2025-06-0618:22
*/ */
@Service @Service
@Slf4j
public class DeviceExportService { public class DeviceExportService {
public void export(List<Device> devices, HttpServletResponse response) { public void export(List<Device> devices, HttpServletResponse response) {
@ -36,22 +38,24 @@ public class DeviceExportService {
// 转换为DTO列表 // 转换为DTO列表
List<DeviceExcelExportDTO> dtoList = devices.stream().map(device -> { List<DeviceExcelExportDTO> dtoList = devices.stream().map(device -> {
DeviceExcelExportDTO dto = new DeviceExcelExportDTO(); DeviceExcelExportDTO dto = new DeviceExcelExportDTO();
dto.setId(device.getId()); // dto.setId(device.getId());
dto.setDeviceType(device.getDeviceType()); // dto.setDeviceType(device.getDeviceType());
dto.setCustomerName(device.getCustomerName()); // dto.setCustomerName(device.getCustomerName());
dto.setDeviceName(device.getDeviceName()); dto.setDeviceName(device.getDeviceName());
dto.setDeviceMac(device.getDeviceMac()); dto.setDeviceMac(device.getDeviceMac());
// 设备IMEI // 设备IMEI
dto.setDeviceImei(device.getDeviceImei()); dto.setDeviceImei(device.getDeviceImei());
// 蓝牙名称 // 蓝牙名称
dto.setBluetoothName(device.getBluetoothName()); dto.setBluetoothName(device.getBluetoothName());
dto.setLongitude(device.getLongitude()); // dto.setLongitude(device.getLongitude());
dto.setLatitude(device.getLatitude()); // dto.setLatitude(device.getLatitude());
dto.setRemark(device.getRemark()); dto.setRemark(device.getRemark());
dto.setTypeName(device.getTypeName()); dto.setTypeName(device.getTypeName());
dto.setCreateBy(device.getCreateByName()); dto.setCreateBy(device.getCreateByName());
Integer deviceStatus = device.getDeviceStatus(); Integer deviceStatus = device.getDeviceStatus();
dto.setDeviceStatus(deviceStatus == 1 ? "正常" : "失效"); Integer bindingStatus = device.getBindingStatus();
// dto.setDeviceStatus(deviceStatus == 1 ? "正常" : "失效");
dto.setBindingStatus(bindingStatus == 1 ? "已绑定" : "未绑定");
// 时间戳转换 // 时间戳转换
dto.setCreateTime(DateUtils.format(device.getCreateTime(), "yyyy-MM-dd HH:mm:ss")); dto.setCreateTime(DateUtils.format(device.getCreateTime(), "yyyy-MM-dd HH:mm:ss"));
@ -72,17 +76,31 @@ public class DeviceExportService {
private void handleDevicePic(Device device, DeviceExcelExportDTO dto) { private void handleDevicePic(Device device, DeviceExcelExportDTO dto) {
String picUrl = device.getDevicePic(); String picUrl = device.getDevicePic();
log.info("处理设备图片设备ID: {}, 图片URL: {}", device.getId(), picUrl);
if (picUrl != null && !picUrl.trim().isEmpty()) { if (picUrl != null && !picUrl.trim().isEmpty()) {
try { try {
// 自动将HTTP转换为HTTPS以避免重定向问题
if (picUrl.startsWith("http://")) {
picUrl = "https://" + picUrl.substring(7);
log.info("自动将HTTP转换为HTTPS: {}", picUrl);
}
// 尝试创建URL对象会自动验证格式 // 尝试创建URL对象会自动验证格式
dto.setDevicePic(new URL(picUrl)); URL url = new URL(picUrl);
} catch (MalformedURLException e) {
dto.setDevicePic(url);
log.info("成功设置设备图片URL到DTO");
} catch (Exception e) {
// 不是有效URL时设置为null // 不是有效URL时设置为null
log.info("设置设备图片失败设备ID: {}, URL: {}, 错误: {}", device.getId(), picUrl, e.getMessage());
dto.setDevicePic(null); dto.setDevicePic(null);
} }
} else { } else {
log.info("设备没有设置图片设备ID: {}", device.getId());
dto.setDevicePic(null); dto.setDevicePic(null);
} }
} }
} }

View File

@ -54,7 +54,8 @@ public class DeviceFenceAccessRecordServiceImpl implements IDeviceFenceAccessRec
@Override @Override
public TableDataInfo<DeviceFenceAccessRecordVo> queryPageList(DeviceFenceAccessRecordBo bo, PageQuery pageQuery) { public TableDataInfo<DeviceFenceAccessRecordVo> queryPageList(DeviceFenceAccessRecordBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<DeviceFenceAccessRecord> lqw = buildQueryWrapper(bo); LambdaQueryWrapper<DeviceFenceAccessRecord> lqw = buildQueryWrapper(bo);
Page<DeviceFenceAccessRecordVo> result = baseMapper.selectVoPageWithFenceAndDeviceName(pageQuery.build(), lqw); // Page<DeviceFenceAccessRecordVo> result = baseMapper.selectVoPageWithFenceAndDeviceName(pageQuery.build(), lqw);
Page<DeviceFenceAccessRecordVo> result = baseMapper.selectVoPageByXml(pageQuery.build(), bo);
return TableDataInfo.build(result); return TableDataInfo.build(result);
} }
@ -85,6 +86,9 @@ public class DeviceFenceAccessRecordServiceImpl implements IDeviceFenceAccessRec
lqw.eq(bo.getAccuracy() != null, DeviceFenceAccessRecord::getAccuracy, bo.getAccuracy()); lqw.eq(bo.getAccuracy() != null, DeviceFenceAccessRecord::getAccuracy, bo.getAccuracy());
lqw.eq(bo.getEventTime() != null, DeviceFenceAccessRecord::getEventTime, bo.getEventTime()); lqw.eq(bo.getEventTime() != null, DeviceFenceAccessRecord::getEventTime, bo.getEventTime());
lqw.eq(bo.getCreateTime() != null, DeviceFenceAccessRecord::getCreateTime, bo.getCreateTime()); lqw.eq(bo.getCreateTime() != null, DeviceFenceAccessRecord::getCreateTime, bo.getCreateTime());
if (StringUtils.isNotBlank(bo.getFenceName())) {
params.put("fenceName", bo.getFenceName());
}
return lqw; return lqw;
} }

View File

@ -21,6 +21,7 @@ import com.fuyuanshen.equipment.service.IDeviceFenceAccessRecordService;
import com.fuyuanshen.equipment.service.IDeviceFenceStatusService; import com.fuyuanshen.equipment.service.IDeviceFenceStatusService;
import com.fuyuanshen.equipment.service.IDeviceGeoFenceService; import com.fuyuanshen.equipment.service.IDeviceGeoFenceService;
import com.fuyuanshen.equipment.utils.map.GeoFenceChecker; import com.fuyuanshen.equipment.utils.map.GeoFenceChecker;
import com.fuyuanshen.equipment.utils.map.GetAddressFromLatUtil;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -240,6 +241,9 @@ public class DeviceGeoFenceServiceImpl implements IDeviceGeoFenceService {
recordBo.setFenceId(fence.getId()); recordBo.setFenceId(fence.getId());
recordBo.setLatitude(request.getLatitude()); recordBo.setLatitude(request.getLatitude());
recordBo.setLongitude(request.getLongitude()); recordBo.setLongitude(request.getLongitude());
String address = GetAddressFromLatUtil.getAdd(request.getLongitude().toString(), request.getLatitude().toString());
recordBo.setEventAddress(address);
recordBo.setEventTime(new Date()); recordBo.setEventTime(new Date());
// 1表示进入围栏2表示离开围栏 // 1表示进入围栏2表示离开围栏
recordBo.setEventType(currentStatus); recordBo.setEventType(currentStatus);

View File

@ -139,8 +139,27 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
@Override @Override
public List<Device> queryAll(DeviceQueryCriteria criteria) { public List<Device> queryAll(DeviceQueryCriteria criteria) {
criteria.setCurrentOwnerId(LoginHelper.getUserId());
// 角色管理员
Long userId = LoginHelper.getUserId();
List<SysRoleVo> roles = roleService.selectRolesAuthByUserId(userId);
boolean isAdmin = false;
if (CollectionUtil.isNotEmpty(roles)) {
for (SysRoleVo role : roles) {
if (role.getRoleKey().equals("admin")) {
isAdmin = true;
criteria.setIsAdmin(true);
break;
}
}
}
// 只有非admin用户才设置当前用户ID条件
if (!isAdmin) {
criteria.setCurrentOwnerId(LoginHelper.getUserId());
}
return deviceMapper.findAll(criteria); return deviceMapper.findAll(criteria);
} }

View File

@ -98,18 +98,35 @@ public class DeviceTypeServiceImpl extends ServiceImpl<DeviceTypeMapper, DeviceT
public List<DeviceType> queryDeviceTypes() { public List<DeviceType> queryDeviceTypes() {
DeviceTypeQueryCriteria criteria = new DeviceTypeQueryCriteria(); DeviceTypeQueryCriteria criteria = new DeviceTypeQueryCriteria();
// 管理员 // // 管理员
String username = LoginHelper.getUsername(); // String username = LoginHelper.getUsername();
if (!username.equals("admin")) { // if (!username.equals("admin")) {
criteria.setCustomerId(LoginHelper.getUserId()); // criteria.setCustomerId(LoginHelper.getUserId());
//
// Long userId = LoginHelper.getUserId();
// criteria.setCustomerId(userId);
// }
Long userId = LoginHelper.getUserId(); // 角色管理员
criteria.setCustomerId(userId); Long userId = LoginHelper.getUserId();
List<SysRoleVo> roles = roleService.selectRolesAuthByUserId(userId);
boolean isAdmin = false;
if (CollectionUtil.isNotEmpty(roles)) {
for (SysRoleVo role : roles) {
if (role.getRoleKey().contains("admin")) {
isAdmin = true;
break;
}
}
}
if (!isAdmin) {
criteria.setCustomerId(LoginHelper.getUserId());
} }
return deviceTypeMapper.findAll(criteria); return deviceTypeMapper.findAll(criteria);
} }
/** /**
* 根据设备类型名称查询设备类型 * 根据设备类型名称查询设备类型
* *
@ -119,7 +136,23 @@ public class DeviceTypeServiceImpl extends ServiceImpl<DeviceTypeMapper, DeviceT
@Override @Override
public DeviceType queryByName(String typeName) { public DeviceType queryByName(String typeName) {
DeviceTypeQueryCriteria criteria = new DeviceTypeQueryCriteria(); DeviceTypeQueryCriteria criteria = new DeviceTypeQueryCriteria();
criteria.setCustomerId(LoginHelper.getUserId());
// 角色管理员
Long userId = LoginHelper.getUserId();
List<SysRoleVo> roles = roleService.selectRolesAuthByUserId(userId);
boolean isAdmin = false;
if (CollectionUtil.isNotEmpty(roles)) {
for (SysRoleVo role : roles) {
if (role.getRoleKey().contains("admin")) {
isAdmin = true;
break;
}
}
}
if (!isAdmin) {
criteria.setCustomerId(LoginHelper.getUserId());
}
criteria.setTypeName(typeName); criteria.setTypeName(typeName);
DeviceType deviceType = deviceTypeMapper.queryByName(criteria); DeviceType deviceType = deviceTypeMapper.queryByName(criteria);
return deviceType; return deviceType;

View File

@ -8,6 +8,8 @@ import org.slf4j.LoggerFactory;
import java.net.URL; import java.net.URL;
/** /**
* 根据经纬度获取地址信息工具类
*
* @author: 默苍璃 * @author: 默苍璃
* @date: 2025-07-2615:59 * @date: 2025-07-2615:59
*/ */
@ -74,5 +76,6 @@ public class GetAddressFromLatUtil {
System.out.println("通过API获取到具体位置:" + res); System.out.println("通过API获取到具体位置:" + res);
return res; return res;
} }
} }

View File

@ -29,9 +29,10 @@
and da.treatment_state = #{bo.treatmentState} and da.treatment_state = #{bo.treatmentState}
</if> </if>
<if test="bo.queryTime1 != null and bo.queryTime2 != null "> <if test="bo.queryTime1 != null and bo.queryTime2 != null ">
and da.start_time between #{bo.queryTime1} and #{bo.queryTime2} and da.start_time BETWEEN #{bo.queryTime1} AND #{bo.queryTime2}
</if> </if>
</where> </where>
order by da.create_time DESC
</select> </select>

View File

@ -8,22 +8,89 @@
<select id="selectVoPageWithFenceAndDeviceName" <select id="selectVoPageWithFenceAndDeviceName"
resultType="com.fuyuanshen.equipment.domain.vo.DeviceFenceAccessRecordVo"> resultType="com.fuyuanshen.equipment.domain.vo.DeviceFenceAccessRecordVo">
SELECT r.id, SELECT r.id,
r.fence_id, r.fence_id,
f.name AS fence_name, f.name AS fence_name,
r.device_id, r.device_id,
d.device_name, d.device_name,
r.user_id, r.user_id,
r.event_type, r.event_type,
r.latitude, r.latitude,
r.longitude, r.longitude,
r.accuracy, r.accuracy,
r.event_time, r.event_time,
r.create_time r.create_time
FROM device_fence_access_record r FROM device_fence_access_record r
LEFT JOIN device_geo_fence f ON r.fence_id = f.id LEFT JOIN device_geo_fence f ON r.fence_id = f.id
LEFT JOIN device d ON r.device_id = d.id LEFT JOIN device d ON r.device_id = d.id
${ew.customSqlSegment} ${ew.customSqlSegment}
<where>
<if test="ew.params != null">
<if test="ew.params.fenceName != null and ew.params.fenceName != ''">
AND f.name LIKE CONCAT('%', #{ew.params.fenceName}, '%')
</if>
</if>
</where>
ORDER BY r.id ASC ORDER BY r.id ASC
</select> </select>
<!-- 分页查询围栏进出记录列表纯XML形式 -->
<select id="selectVoPageByXml" resultType="com.fuyuanshen.equipment.domain.vo.DeviceFenceAccessRecordVo">
SELECT r.id,
r.fence_id,
f.name AS fence_name,
r.device_id,
d.device_name,
r.user_id,
r.event_type,
r.latitude,
r.longitude,
r.accuracy,
r.event_time, r.event_address,
r.create_time
FROM device_fence_access_record r
LEFT JOIN device_geo_fence f ON r.fence_id = f.id
LEFT JOIN device d ON r.device_id = d.id
<where>
<if test="bo.fenceId != null">
AND r.fence_id = #{bo.fenceId}
</if>
<if test="bo.deviceId != null and bo.deviceId != ''">
AND r.device_id = #{bo.deviceId}
</if>
<if test="bo.userId != null">
AND r.user_id = #{bo.userId}
</if>
<if test="bo.eventType != null">
AND r.event_type = #{bo.eventType}
</if>
<if test="bo.latitude != null">
AND r.latitude = #{bo.latitude}
</if>
<if test="bo.longitude != null">
AND r.longitude = #{bo.longitude}
</if>
<if test="bo.accuracy != null">
AND r.accuracy = #{bo.accuracy}
</if>
<if test="bo.eventTime != null">
AND r.event_time = #{bo.eventTime}
</if>
<if test="bo.createTime != null">
AND r.create_time = #{bo.createTime}
</if>
<if test="bo.fenceName != null and bo.fenceName != ''">
AND f.name LIKE CONCAT('%', #{bo.fenceName}, '%')
</if>
<!-- 添加时间范围筛选条件 -->
<if test="bo.beginTime != null and bo.beginTime != ''">
AND r.event_time >= #{bo.beginTime}
</if>
<if test="bo.endTime != null and bo.endTime != ''">
AND r.event_time <![CDATA[ <= ]]> #{bo.endTime}
</if>
</where>
ORDER BY r.event_time DESC
</select>
</mapper> </mapper>

View File

@ -239,8 +239,7 @@
where d.device_mac = #{deviceMac} where d.device_mac = #{deviceMac}
</select> </select>
<select id="queryWebDeviceList" resultType="com.fuyuanshen.equipment.domain.vo.WebDeviceVo"> <select id="queryWebDeviceList" resultType="com.fuyuanshen.equipment.domain.vo.WebDeviceVo">
select d.id, d.device_name, d.device_name, select * from (select d.id, d.device_name,
d.device_name,
d.device_mac, d.device_mac,
d.device_sn, d.device_sn,
d.device_imei, d.device_imei,
@ -252,40 +251,41 @@
ap.name personnelBy, ap.name personnelBy,
d.device_status, d.device_status,
d.online_status, d.online_status,
c.binding_time c.binding_time,
ROW_NUMBER() OVER (PARTITION BY d.id ORDER BY c.binding_time) AS row_num
from device d from device d
inner join device_type dt on d.device_type = dt.id inner join device_type dt on d.device_type = dt.id
inner join app_device_bind_record c on d.id = c.device_id and c.communication_mode = 0 inner join app_device_bind_record c on d.id = c.device_id
left join app_personnel_info ap on ap.device_id = d.id left join app_personnel_info ap on ap.device_id = d.id
where dt.communication_mode in (0, 2) where dt.communication_mode in (0, 2) ) a where a.row_num = 1
<if test="criteria.deviceType != null"> <if test="criteria.deviceType != null">
and d.device_type = #{criteria.deviceType} and a.device_type = #{criteria.deviceType}
</if> </if>
<if test="criteria.deviceName != null and criteria.deviceName != ''"> <if test="criteria.deviceName != null and criteria.deviceName != ''">
and d.device_name like concat('%', #{criteria.deviceName}, '%') and a.device_name like concat('%', #{criteria.deviceName}, '%')
</if> </if>
<if test="criteria.deviceImei != null and criteria.deviceImei != ''"> <if test="criteria.deviceImei != null and criteria.deviceImei != ''">
and (d.device_imei = #{criteria.deviceImei} and a.device_imei = #{criteria.deviceImei}
</if> </if>
<if test="criteria.content != null and criteria.content != ''"> <if test="criteria.content != null and criteria.content != ''">
AND d.device_imei = #{criteria.content} or d.device_mac = #{criteria.content} AND (a.device_imei = #{criteria.content} or a.device_mac = #{criteria.content})
</if> </if>
<if test="criteria.deviceStatus != null"> <if test="criteria.deviceStatus != null">
and d.device_status = #{criteria.deviceStatus} and a.device_status = #{criteria.deviceStatus}
</if> </if>
<if test="criteria.personnelBy != null and criteria.personnelBy != ''"> <if test="criteria.personnelBy != null and criteria.personnelBy != ''">
and ap.name like concat('%', #{criteria.personnelBy}, '%') and a.personnelBy like concat('%', #{criteria.personnelBy}, '%')
</if> </if>
<if test="criteria.communicationMode != null"> <if test="criteria.communicationMode != null">
and dt.communication_mode = #{criteria.communicationMode} and a.communication_mode = #{criteria.communicationMode}
</if> </if>
<if test="criteria.groupId != null"> <if test="criteria.groupId != null">
and d.group_id = #{criteria.groupId} and a.group_id = #{criteria.groupId}
</if> </if>
<if test="criteria.onlineStatus != null"> <if test="criteria.onlineStatus != null">
and d.online_status = #{criteria.onlineStatus} and a.online_status = #{criteria.onlineStatus}
</if> </if>
ORDER BY d.create_time DESC ORDER BY a.binding_time DESC
</select> </select>
<select id="getLocationHistory" resultType="com.fuyuanshen.equipment.domain.vo.LocationHistoryVo"> <select id="getLocationHistory" resultType="com.fuyuanshen.equipment.domain.vo.LocationHistoryVo">
select a.id,a.device_name,a.device_type,b.type_name deviceTypeName,a.device_imei,a.device_mac from device a select a.id,a.device_name,a.device_type,b.type_name deviceTypeName,a.device_imei,a.device_mac from device a

View File

@ -49,8 +49,13 @@
parameterType="com.fuyuanshen.equipment.domain.query.DeviceTypeQueryCriteria"> parameterType="com.fuyuanshen.equipment.domain.query.DeviceTypeQueryCriteria">
SELECT dt.*, dg.id AS grant_id SELECT dt.*, dg.id AS grant_id
FROM device_type dt FROM device_type dt
JOIN device_type_grants dg ON dt.id = dg.device_type_id JOIN device_type_grants dg ON dt.id = dg.device_type_id
WHERE dt.type_name = #{criteria.typeName} <where>
AND dg.customer_id = #{criteria.customerId} dt.type_name = #{criteria.typeName}
<if test="criteria.customerId != null">
and dg.customer_id = #{criteria.customerId}
</if>
</where>
</select> </select>
</mapper> </mapper>