diff --git a/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java b/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java index 3690f0f..ed9e1d8 100644 --- a/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java +++ b/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java @@ -91,4 +91,13 @@ public class CacheConstants * 定位日志队列 */ public static final String LOCATION_LOG_QUEUE = "location_log_queue"; + /** + * 待插入的命令日志 + */ + public static final String INSERT_COMMAND_LOG = "insert_command_log"; + + /** + * 运营区禁行区列表 + */ + public static final String NO_RIDING_AREA_SUB_LIST = "no_riding_area_sub_list:"; } diff --git a/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java b/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java index f9923a9..ca79063 100644 --- a/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java +++ b/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java @@ -3,13 +3,13 @@ package com.ruoyi.common.utils; import java.lang.management.ManagementFactory; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.YearMonth; import java.time.ZoneId; -import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Calendar; @@ -384,11 +384,14 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils } /** - * 将时间戳转换为LocalDateTime + * 将时间戳转换为LocalDateTime,并且保持yyyy-MM-dd HH:mm:ss格式 * @param timestamp 时间戳 */ public static LocalDateTime toLocalDateTime(Long timestamp) { - return LocalDateTime.ofEpochSecond(timestamp, 0, ZoneOffset.of("+8")); + if (timestamp == null) { + return null; + } + return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault()); } public static LocalDateTime toLocalDateTime(String time) { diff --git a/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/utils/map/GeoUtils.java b/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/utils/map/GeoUtils.java index 1264504..e65f5a6 100644 --- a/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/utils/map/GeoUtils.java +++ b/common-ruoyi/ruoyi-common/src/main/java/com/ruoyi/common/utils/map/GeoUtils.java @@ -234,12 +234,13 @@ public class GeoUtils { /** * 判断一个点是否在一个多边形区域内或者在区域外(考虑误差) * */ - public static boolean isInPolygonWithTolerance(BigDecimal longitude, BigDecimal latitude, Geometry polygon, double tolerance) { + public static boolean isInPolygonWithTolerance(BigDecimal longitude, BigDecimal latitude, Geometry polygon, BigDecimal tolerance) { if (longitude == null || latitude == null || polygon == null) { return false; } double lon = longitude.doubleValue(); double lat = latitude.doubleValue(); + double toleranceValue = tolerance == null ? 0 : tolerance.doubleValue(); GeometryFactory geometryFactory = new GeometryFactory(); Coordinate coordinate = new Coordinate(lon, lat); @@ -252,9 +253,7 @@ public class GeoUtils { Coordinate[] coordinates = polygon.getCoordinates(); for (Coordinate coord : coordinates) { double distance = calculateDistance(lat, lon, coord.y, coord.x); -// log.info("距离----distance:{}",distance); - if (distance <= tolerance) { -// log.info("最小距离----distance:{}",distance); + if (distance <= toleranceValue) { return true; } } @@ -265,10 +264,13 @@ public class GeoUtils { /** * 判断一个点是否在一个缩短后的圆形区域内 * */ - public static boolean isInPolygonWithShorten(String longitude, String latitude, Geometry polygon, double shortenDistance) { - double lon = Double.parseDouble(longitude); - double lat = Double.parseDouble(latitude); - + public static boolean isInPolygonWithShorten(BigDecimal longitude, BigDecimal latitude, Geometry polygon, BigDecimal shortenDistance) { + if (longitude == null || latitude == null || polygon == null ) { + return false; + } + double lon = longitude.doubleValue(); + double lat = latitude.doubleValue(); + double shortenDistanceValue = shortenDistance == null ? 0 : shortenDistance.doubleValue(); GeometryFactory geometryFactory = new GeometryFactory(); Coordinate coordinate = new Coordinate(lon, lat); Point point = geometryFactory.createPoint(coordinate); @@ -278,9 +280,7 @@ public class GeoUtils { Coordinate[] coordinates = polygon.getCoordinates(); for (Coordinate coord : coordinates) { double distance = calculateDistance(lat, lon, coord.y, coord.x); -// log.info("距离----distance:{}",distance); - if (shortenDistance >= distance) { -// log.info("最小距离----distance:{}",distance); + if (shortenDistanceValue >= distance) { return false; } } diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/area/domain/Area.java b/ruoyi-service/src/main/java/com/ruoyi/bst/area/domain/Area.java index 5eb3c19..5d12d5d 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/bst/area/domain/Area.java +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/area/domain/Area.java @@ -161,4 +161,11 @@ public class Area extends BaseEntity @ApiModelProperty("创建人ID") private Long createId; + @Excel(name = "靠近边界播报距离") + @ApiModelProperty("靠近边界播报距离(米)") + private BigDecimal boundaryDistance; + + @Excel(name = "运营区外断电距离") + @ApiModelProperty("运营区外断电距离(米)") + private BigDecimal outageDistance; } diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/area/mapper/AreaMapper.xml b/ruoyi-service/src/main/java/com/ruoyi/bst/area/mapper/AreaMapper.xml index 8eb916c..ca8d15b 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/bst/area/mapper/AreaMapper.xml +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/area/mapper/AreaMapper.xml @@ -44,6 +44,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ba.create_id, ba.create_time, ba.deleted, + ba.boundary_distance, + ba.outage_distance, su.nick_name as user_name, su.agent_id as agent_id, suc.nick_name as create_name @@ -142,6 +144,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" guide_switch, create_id, create_time, + boundary_distance, + outage_distance, #{userId}, @@ -176,6 +180,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{guideSwitch}, #{createId}, #{createTime}, + #{boundaryDistance}, + #{outageDistance}, @@ -220,6 +226,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" guide_switch = #{data.guideSwitch}, create_id = #{data.createId}, create_time = #{data.createTime}, + boundary_distance = #{data.boundaryDistance}, + outage_distance = #{data.outageDistance}, diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/area/service/impl/AreaConverterImpl.java b/ruoyi-service/src/main/java/com/ruoyi/bst/area/service/impl/AreaConverterImpl.java index caf7913..5ca25b5 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/bst/area/service/impl/AreaConverterImpl.java +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/area/service/impl/AreaConverterImpl.java @@ -30,6 +30,8 @@ public class AreaConverterImpl implements AreaConverter { po.setMsgSwitch(data.getMsgSwitch()); po.setNoRidingOutage(data.getNoRidingOutage()); po.setAreaOutOutage(data.getAreaOutOutage()); + po.setBoundaryDistance(data.getBoundaryDistance()); + po.setOutageDistance(data.getOutageDistance()); // 还车设置 po.setError(data.getError()); @@ -68,6 +70,8 @@ public class AreaConverterImpl implements AreaConverter { po.setMsgSwitch(data.getMsgSwitch()); po.setNoRidingOutage(data.getNoRidingOutage()); po.setAreaOutOutage(data.getAreaOutOutage()); + po.setBoundaryDistance(data.getBoundaryDistance()); + po.setOutageDistance(data.getOutageDistance()); // 还车设置 po.setError(data.getError()); diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/area/utils/AreaUtil.java b/ruoyi-service/src/main/java/com/ruoyi/bst/area/utils/AreaUtil.java index 17d9fa3..c2f5d97 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/bst/area/utils/AreaUtil.java +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/area/utils/AreaUtil.java @@ -2,23 +2,46 @@ package com.ruoyi.bst.area.utils; import java.math.BigDecimal; -import com.ruoyi.bst.area.domain.Area; -import com.ruoyi.common.utils.map.GeoUtils; import org.locationtech.jts.geom.Geometry; +import com.ruoyi.bst.area.domain.Area; +import com.ruoyi.bst.area.domain.AreaVO; +import com.ruoyi.common.utils.map.GeoUtils; + public class AreaUtil { + // 是否在运营区内 public static boolean isInArea(Area area, BigDecimal longitude, BigDecimal latitude) { - double tolerance = 0; // 误差距离 - if (area.getError() != null) { - tolerance = area.getError().doubleValue(); + Geometry geometry = GeoUtils.fromWkt(area.getBoundary()); + if (geometry == null) { + return false; + } + return GeoUtils.isInPolygonWithTolerance(longitude, latitude, geometry, area.getOutageDistance()); + } + + // 是否在运营区最小内边界 + public static boolean isInAreaMin(AreaVO area, BigDecimal longitude, BigDecimal latitude) { + if (area == null) { + return false; } Geometry geometry = GeoUtils.fromWkt(area.getBoundary()); if (geometry == null) { return false; } - return GeoUtils.isInPolygonWithTolerance(longitude, latitude, geometry, tolerance); + return GeoUtils.isInPolygonWithShorten(longitude, latitude, geometry, area.getBoundaryDistance()); + } + + // 是否在运营区最大外边界 + public static boolean isInAreaMax(AreaVO area, BigDecimal longitude, BigDecimal latitude) { + if (area == null) { + return false; + } + Geometry geometry = GeoUtils.fromWkt(area.getBoundary()); + if (geometry == null) { + return false; + } + return GeoUtils.isInPolygonWithTolerance(longitude, latitude, geometry, area.getOutageDistance()); } } diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/domain/AreaSub.java b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/domain/AreaSub.java index 7ab2358..21c4197 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/domain/AreaSub.java +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/domain/AreaSub.java @@ -58,7 +58,7 @@ public class AreaSub extends BaseEntity @Excel(name = "还车误差") @ApiModelProperty("还车误差") @Min(value = 0, message = "还车误差不允许为负数") - private Integer error; + private BigDecimal error; @Excel(name = "经度") @ApiModelProperty("经度") diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/domain/vo/AreaSubSimpleVO.java b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/domain/vo/AreaSubSimpleVO.java new file mode 100644 index 0000000..0afcaff --- /dev/null +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/domain/vo/AreaSubSimpleVO.java @@ -0,0 +1,11 @@ +package com.ruoyi.bst.areaSub.domain.vo; + +import lombok.Data; + +/** + * @author wjh + * 2025/4/3 + */ +@Data +public class AreaSubSimpleVO { +} diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/service/AreaSubService.java b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/service/AreaSubService.java index 27f4946..8404608 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/service/AreaSubService.java +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/service/AreaSubService.java @@ -50,22 +50,6 @@ public interface AreaSubService */ public int updateAreaSub(AreaSub areaSub); - /** - * 批量删除子区域 - * - * @param ids 需要删除的子区域主键集合 - * @return 结果 - */ - public int deleteAreaSubByIds(Long[] ids); - - /** - * 删除子区域信息 - * - * @param id 子区域主键 - * @return 结果 - */ - public int deleteAreaSubById(Long id); - /** * 根据运营区ID查询子区域列表 * @@ -94,4 +78,11 @@ public interface AreaSubService * @return 子区域列表 */ public List selectParkingAreaByAreaId(Long areaId); + + /** + * 查询运营区的可还车的子区域列表 + * @param areaId 运营区ID + * @return 子区域列表 + */ + public List selectSimpleNoRidingListByAreaId(Long areaId); } diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/service/impl/AreaSubServiceImpl.java b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/service/impl/AreaSubServiceImpl.java index 3a08278..df98072 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/service/impl/AreaSubServiceImpl.java +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/service/impl/AreaSubServiceImpl.java @@ -15,6 +15,8 @@ import com.ruoyi.bst.areaSub.domain.enums.AreaSubStatus; import com.ruoyi.bst.areaSub.domain.enums.AreaSubType; import com.ruoyi.bst.areaSub.mapper.AreaSubMapper; import com.ruoyi.bst.areaSub.service.AreaSubService; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.collection.CollectionUtils; import com.ruoyi.common.utils.map.GeoUtils; @@ -31,6 +33,9 @@ public class AreaSubServiceImpl implements AreaSubService @Autowired private AreaSubMapper areaSubMapper; + @Autowired + private RedisCache redisCache; + /** * 查询子区域 * @@ -83,7 +88,13 @@ public class AreaSubServiceImpl implements AreaSubService Geometry geometry = GeoUtils.toGeometry(areaSub.getBoundaryStr()); areaSub.setBoundary(GeoUtils.wkt(geometry)); } - return areaSubMapper.insertAreaSub(areaSub); + int result = areaSubMapper.insertAreaSub(areaSub); + + if (result > 0) { + this.clearCache(areaSub.getAreaId()); + } + + return result; } /** @@ -100,32 +111,15 @@ public class AreaSubServiceImpl implements AreaSubService Geometry geometry = GeoUtils.toGeometry(areaSub.getBoundaryStr()); areaSub.setBoundary(GeoUtils.wkt(geometry)); } - return areaSubMapper.updateAreaSub(areaSub); + int result = areaSubMapper.updateAreaSub(areaSub); + + if (result > 0) { + this.clearCache(areaSub.getAreaId()); + } + + return result; } - /** - * 批量删除子区域 - * - * @param ids 需要删除的子区域主键 - * @return 结果 - */ - @Override - public int deleteAreaSubByIds(Long[] ids) - { - return areaSubMapper.deleteAreaSubByIds(ids); - } - - /** - * 删除子区域信息 - * - * @param id 子区域主键 - * @return 结果 - */ - @Override - public int deleteAreaSubById(Long id) - { - return areaSubMapper.deleteAreaSubById(id); - } /** * 根据运营区ID查询子区域列表 @@ -150,9 +144,37 @@ public class AreaSubServiceImpl implements AreaSubService */ @Override public int logicDel(List ids) { - return areaSubMapper.logicDel(ids); + if (CollectionUtils.isEmptyElement(ids)) { + return 0; + } + + List list = this.selectAreaSubByIds(ids); + if (CollectionUtils.isEmptyElement(list)) { + return 0; + } + + // 删除数据库 + int result = areaSubMapper.logicDel(ids); + + // 删除缓存 + if (result > 0) { + for (AreaSubVO areaSub : list) { + this.clearCache(areaSub.getAreaId()); + } + } + + return result; } + private List selectAreaSubByIds(List ids) { + if (CollectionUtils.isEmptyElement(ids)) { + return Collections.emptyList(); + } + AreaSubQuery query = new AreaSubQuery(); + query.setIds(ids); + return areaSubMapper.selectAreaSubList(query); + } + @Override public List selectParkingAreaByAreaId(Long areaId) { if (areaId == null) { @@ -164,4 +186,27 @@ public class AreaSubServiceImpl implements AreaSubService query.setType(AreaSubType.PARKING.getCode()); return areaSubMapper.selectAreaSubList(query); } + + @Override + public List selectSimpleNoRidingListByAreaId(Long areaId) { + if (areaId == null) { + return Collections.emptyList(); + } + String key = CacheConstants.NO_RIDING_AREA_SUB_LIST + areaId; + List list = redisCache.getCacheObject(key); + if (CollectionUtils.isEmptyElement(list)) { + AreaSubQuery query = new AreaSubQuery(); + query.setAreaId(areaId); + list = areaSubMapper.selectAreaSubList(query); + } + return list; + } + + private void clearCache(Long areaId) { + if (areaId == null) { + return; + } + String key = CacheConstants.NO_RIDING_AREA_SUB_LIST + areaId; + redisCache.deleteObject(key); + } } diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/utils/AreaSubUtil.java b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/utils/AreaSubUtil.java index b8e7ff9..fc4751b 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/utils/AreaSubUtil.java +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/areaSub/utils/AreaSubUtil.java @@ -26,9 +26,9 @@ public class AreaSubUtil { if (CollectionUtils.isEmptyElement(areaSubList)) { return null; } - double tolerance = 0; // 误差距离 + BigDecimal tolerance = BigDecimal.ZERO; // 误差距离 if (areaError != null) { - tolerance = areaError.doubleValue(); + tolerance = areaError; } for (AreaSubVO area : areaSubList) { Geometry geometry = GeoUtils.fromWkt(area.getBoundary()); @@ -36,9 +36,8 @@ public class AreaSubUtil { continue; } if(area.getError() != null ){ - tolerance = area.getError().doubleValue(); + tolerance = area.getError(); } -// log.debug(" ---- 区域{} ---- 定位 {},{} ----",area.getName(), longitude, latitude); boolean inCircle = GeoUtils.isInPolygonWithTolerance(longitude, latitude, geometry, tolerance); if (inCircle) { return area; @@ -47,4 +46,29 @@ public class AreaSubUtil { return null; } + /** + * 获取靠近的区域 + * @param areaSubList 区域列表 + * @param longitude 经度 + * @param latitude 纬度 + * @param error 误差 + * @return 靠近的区域 + */ + public static AreaSubVO getNearAreaSub(List areaSubList, BigDecimal longitude, BigDecimal latitude, BigDecimal error) { + if (CollectionUtils.isEmptyElement(areaSubList)) { + return null; + } + for (AreaSubVO area : areaSubList) { + Geometry geometry = GeoUtils.fromWkt(area.getBoundary()); + if (geometry == null) { + continue; + } + boolean inCircle = GeoUtils.isInPolygonWithTolerance(longitude, latitude, geometry, error); + if (inCircle) { + return area; + } + } + return null; + } + } diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/domain/CommandLog.java b/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/domain/CommandLog.java new file mode 100644 index 0000000..ce509e5 --- /dev/null +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/domain/CommandLog.java @@ -0,0 +1,64 @@ +package com.ruoyi.bst.commandLog.domain; + +import java.time.LocalDateTime; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; +import org.springframework.format.annotation.DateTimeFormat; + +/** + * 命令日志对象 bst_command_log + * + * @author ruoyi + * @date 2025-04-03 + */ +@Data +public class CommandLog extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + private Long id; + + @Excel(name = "类型", readConverterExp = "1=-在线API,2=-蓝牙") + @ApiModelProperty("类型") + private String type; + + @Excel(name = "MAC号") + @ApiModelProperty("MAC号") + private String mac; + + @Excel(name = "发送命令的原因") + @ApiModelProperty("发送命令的原因") + private String reason; + + @Excel(name = "命令") + @ApiModelProperty("命令") + private String command; + + @Excel(name = "命令发送的结果") + @ApiModelProperty("命令发送的结果") + private String result; + + @Excel(name = "操作人ID") + @ApiModelProperty("操作人ID") + private Long userId; + + @Excel(name = "操作人名称") + @ApiModelProperty("操作人名称") + private String userName; + + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty("操作时间") + private LocalDateTime operaTime; + + @Excel(name = "返回值code") + @ApiModelProperty("返回值code") + private Integer iotCode; + +} diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/domain/CommandLogQuery.java b/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/domain/CommandLogQuery.java new file mode 100644 index 0000000..b1100d8 --- /dev/null +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/domain/CommandLogQuery.java @@ -0,0 +1,13 @@ +package com.ruoyi.bst.commandLog.domain; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data + +public class CommandLogQuery extends CommandLogVO{ + + @ApiModelProperty("精准MAC") + private String eqMac; + +} diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/domain/CommandLogVO.java b/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/domain/CommandLogVO.java new file mode 100644 index 0000000..e637c54 --- /dev/null +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/domain/CommandLogVO.java @@ -0,0 +1,8 @@ +package com.ruoyi.bst.commandLog.domain; + +import lombok.Data; + +@Data +public class CommandLogVO extends CommandLog { + +} diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/domain/enums/CommandLogType.java b/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/domain/enums/CommandLogType.java new file mode 100644 index 0000000..9c22176 --- /dev/null +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/domain/enums/CommandLogType.java @@ -0,0 +1,16 @@ +package com.ruoyi.bst.commandLog.domain.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum CommandLogType { + + API("1", "在线API"), + BLUETOOTH("2", "蓝牙"); + + private final String type; + private final String msg; + +} diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/mapper/CommandLogMapper.java b/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/mapper/CommandLogMapper.java new file mode 100644 index 0000000..723267f --- /dev/null +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/mapper/CommandLogMapper.java @@ -0,0 +1,73 @@ +package com.ruoyi.bst.commandLog.mapper; + +import java.time.LocalDate; +import java.util.List; +import com.ruoyi.bst.commandLog.domain.CommandLog; +import com.ruoyi.bst.commandLog.domain.CommandLogVO; +import com.ruoyi.bst.commandLog.domain.CommandLogQuery; +import org.apache.ibatis.annotations.Param; + +/** + * 命令日志Mapper接口 + * + * @author ruoyi + * @date 2025-04-03 + */ +public interface CommandLogMapper +{ + /** + * 查询命令日志 + * + * @param id 命令日志主键 + * @return 命令日志 + */ + CommandLogVO selectCommandLogById(Long id); + + /** + * 查询命令日志列表 + * + * @param query 命令日志 + * @return 命令日志集合 + */ + List selectCommandLogList(@Param("query")CommandLogQuery query); + + /** + * 新增命令日志 + * + * @param commandLog 命令日志 + * @return 结果 + */ + int insertCommandLog(CommandLog commandLog); + + /** + * 批量新增命令日志 + */ + int batchInsert(@Param("list") List list); + + /** + * 修改命令日志 + * + * @param commandLog 命令日志 + * @return 结果 + */ + public int updateCommandLog(@Param("data") CommandLog commandLog); + + /** + * 删除命令日志 + * + * @param id 命令日志主键 + * @return 结果 + */ + int deleteCommandLogById(Long id); + + /** + * 批量删除命令日志 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteCommandLogByIds(Long[] ids); + + + int clearBeforeDateLater(@Param("date") LocalDate date); +} diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/mapper/CommandLogMapper.xml b/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/mapper/CommandLogMapper.xml new file mode 100644 index 0000000..49e0d51 --- /dev/null +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/mapper/CommandLogMapper.xml @@ -0,0 +1,148 @@ + + + + + + + + select + bcl.id, + bcl.type, + bcl.mac, + bcl.reason, + bcl.command, + bcl.result, + bcl.user_id, + bcl.user_name, + bcl.opera_time, + bcl.iot_code + from bst_command_log bcl + + + + and bcl.id = #{query.id} + and bcl.type = #{query.type} + and bcl.mac like concat('%', #{query.mac}, '%') + and bcl.mac = #{query.eqMac} + and bcl.reason like concat('%', #{query.reason}, '%') + and bcl.command like concat('%', #{query.command}, '%') + and bcl.result like concat('%', #{query.result}, '%') + and bcl.user_id = #{query.userId} + and bcl.user_name like concat('%', #{query.userName}, '%') + and bcl.iot_code = #{query.iotCode} + ${query.params.dataScope} + + + + + + + + insert into bst_command_log + + type, + mac, + reason, + command, + result, + user_id, + user_name, + opera_time, + iot_code, + + values + + + #{i.type}, + default, + #{i.mac}, + default, + #{i.reason}, + default, + #{i.command}, + default, + #{i.result}, + default, + #{i.userId}, + default, + #{i.userName}, + default, + #{i.operaTime}, + default, + #{i.iotCode}, + default, + + + + + + insert into bst_command_log + + type, + mac, + reason, + command, + result, + user_id, + user_name, + opera_time, + iot_code, + + + #{type}, + #{mac}, + #{reason}, + #{command}, + #{result}, + #{userId}, + #{userName}, + #{operaTime}, + #{iotCode}, + + + + + update bst_command_log + + + + where id = #{data.id} + + + + type = #{data.type}, + mac = #{data.mac}, + reason = #{data.reason}, + command = #{data.command}, + result = #{data.result}, + user_id = #{data.userId}, + user_name = #{data.userName}, + opera_time = #{data.operaTime}, + iot_code = #{data.iotCode}, + + + + delete from bst_command_log where id = #{id} + + + + delete from bst_command_log where id in + + #{id} + + + + + delete from bst_command_log where date(opera_time) < #{date} + + diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/service/CommandLogService.java b/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/service/CommandLogService.java new file mode 100644 index 0000000..b7f9936 --- /dev/null +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/service/CommandLogService.java @@ -0,0 +1,93 @@ +package com.ruoyi.bst.commandLog.service; + +import java.time.LocalDate; +import java.util.List; + +import com.ruoyi.bst.commandLog.domain.CommandLog; +import com.ruoyi.bst.commandLog.domain.CommandLogQuery; +import com.ruoyi.bst.commandLog.domain.CommandLogVO; +import com.ruoyi.common.core.domain.model.LoginUser; + +/** + * 命令日志Service接口 + * + * @author ruoyi + * @date 2025-04-03 + */ +public interface CommandLogService +{ + /** + * 查询命令日志 + * + * @param id 命令日志主键 + * @return 命令日志 + */ + public CommandLogVO selectCommandLogById(Long id); + + /** + * 查询命令日志列表 + * + * @param commandLog 命令日志 + * @return 命令日志集合 + */ + public List selectCommandLogList(CommandLogQuery commandLog); + + /** + * 新增命令日志 + * + * @param commandLog 命令日志 + * @return 结果 + */ + public int insertCommandLog(CommandLog commandLog); + + /** + * 修改命令日志 + * + * @param commandLog 命令日志 + * @return 结果 + */ + public int updateCommandLog(CommandLog commandLog); + + /** + * 批量删除命令日志 + * + * @param ids 需要删除的命令日志主键集合 + * @return 结果 + */ + public int deleteCommandLogByIds(Long[] ids); + + /** + * 删除命令日志信息 + * + * @param id 命令日志主键 + * @return 结果 + */ + public int deleteCommandLogById(Long id); + + /** + * 插入一条API日志 + * + * @param mac MAC + * @param command 命令 + * @param result 结果 + * @param reason 原因 + * @param loginUser + * @param iotCode + */ + int addApiLog(String mac, String command, String result, String reason, LoginUser loginUser, Integer iotCode); + + /** + * 插入一条蓝牙命令日志 + */ + int addBluetoothLog(CommandLog po, LoginUser loginUser); + + /** + * 批量插入 + */ + int batchInsert(List list); + + /** + * 清除指定日期之前的日志 + */ + int clearBeforeDateLater(LocalDate date); +} diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/service/impl/CommandLogServiceImpl.java b/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/service/impl/CommandLogServiceImpl.java new file mode 100644 index 0000000..93529fa --- /dev/null +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/commandLog/service/impl/CommandLogServiceImpl.java @@ -0,0 +1,161 @@ +package com.ruoyi.bst.commandLog.service.impl; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; + +import com.ruoyi.bst.commandLog.domain.enums.CommandLogType; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.collection.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.ruoyi.bst.commandLog.domain.CommandLog; +import com.ruoyi.bst.commandLog.domain.CommandLogQuery; +import com.ruoyi.bst.commandLog.domain.CommandLogVO; +import com.ruoyi.bst.commandLog.mapper.CommandLogMapper; +import com.ruoyi.bst.commandLog.service.CommandLogService; + +/** + * 命令日志Service业务层处理 + * + * @author ruoyi + * @date 2025-04-03 + */ +@Service +public class CommandLogServiceImpl implements CommandLogService +{ + @Autowired + private CommandLogMapper commandLogMapper; + + @Autowired + private RedisCache redisCache; + + /** + * 查询命令日志 + * + * @param id 命令日志主键 + * @return 命令日志 + */ + @Override + public CommandLogVO selectCommandLogById(Long id) + { + return commandLogMapper.selectCommandLogById(id); + } + + /** + * 查询命令日志列表 + * + * @param commandLog 命令日志 + * @return 命令日志 + */ + @Override + public List selectCommandLogList(CommandLogQuery commandLog) + { + return commandLogMapper.selectCommandLogList(commandLog); + } + + /** + * 新增命令日志 + * + * @param commandLog 命令日志 + * @return 结果 + */ + @Override + public int insertCommandLog(CommandLog commandLog) + { + return commandLogMapper.insertCommandLog(commandLog); + } + + /** + * 修改命令日志 + * + * @param commandLog 命令日志 + * @return 结果 + */ + @Override + public int updateCommandLog(CommandLog commandLog) + { + return commandLogMapper.updateCommandLog(commandLog); + } + + /** + * 批量删除命令日志 + * + * @param ids 需要删除的命令日志主键 + * @return 结果 + */ + @Override + public int deleteCommandLogByIds(Long[] ids) + { + return commandLogMapper.deleteCommandLogByIds(ids); + } + + /** + * 删除命令日志信息 + * + * @param id 命令日志主键 + * @return 结果 + */ + @Override + public int deleteCommandLogById(Long id) + { + return commandLogMapper.deleteCommandLogById(id); + } + + + @Override + public int addApiLog(String mac, String command, String result, String reason, LoginUser loginUser, Integer iotCode) { + CommandLog po = new CommandLog(); + po.setType(CommandLogType.API.getType()); + po.setMac(mac); + po.setCommand(command); + po.setResult(result); + po.setReason(reason); + po.setIotCode(iotCode); + return this.addLog(po, loginUser); + } + + @Override + public int addBluetoothLog(CommandLog po, LoginUser loginUser) { + po.setType(CommandLogType.BLUETOOTH.getType()); + return this.addLog(po, loginUser); + } + + @Override + public int batchInsert(List list) { + if (CollectionUtils.isEmptyElement(list)) { + return 0; + } + return commandLogMapper.batchInsert(list); + } + + @Override + public int clearBeforeDateLater(LocalDate date) { + if (date == null) { + return 0; + } + return commandLogMapper.clearBeforeDateLater(date); + } + + private int addLog(CommandLog po, LoginUser loginUser) { + if (po == null || StringUtils.isBlank(po.getMac())) { + return 0; + } + po.setOperaTime(LocalDateTime.now()); + + if (loginUser != null) { + po.setUserId(loginUser.getUserId()); + po.setUserName(loginUser.getNickName()); + po.setUserId(loginUser.getUserId()); + } + + // 添加到缓存中 + redisCache.rightPush(CacheConstants.INSERT_COMMAND_LOG, po); + + return 1; + } +} diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/device/mapper/DeviceMapper.xml b/ruoyi-service/src/main/java/com/ruoyi/bst/device/mapper/DeviceMapper.xml index 0847b1f..108dfa7 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/bst/device/mapper/DeviceMapper.xml +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/device/mapper/DeviceMapper.xml @@ -307,6 +307,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" bd.mac, bd.sn, bd.status, + bd.lock_status, + bd.area_id, bod.order_id from bst_device bd left join bst_order_device bod on bod.id = bd.order_device_id diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/device/service/DeviceIotService.java b/ruoyi-service/src/main/java/com/ruoyi/bst/device/service/DeviceIotService.java index 1bee5d8..a74cc13 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/bst/device/service/DeviceIotService.java +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/device/service/DeviceIotService.java @@ -26,6 +26,16 @@ public interface DeviceIotService { */ int lock(DeviceVO device, boolean isAdmin, String reason, boolean requiredIot); + /** + * 强制断电 + * @param device 设备 + * @param isAdmin 是否管理员操作 + * @param reason 原因 + * @param requiredIot 是否IOT操作必须成功 + * @return 结果 + */ + int qLock(DeviceVO device, boolean isAdmin, String reason, boolean requiredIot); + /** * 播放语音 * @param device 设备 @@ -99,4 +109,5 @@ public interface DeviceIotService { * @param ids 设备ID列表,若为空,则监控所有设备 */ void monitor(List ids); + } diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/device/service/impl/DeviceIotServiceImpl.java b/ruoyi-service/src/main/java/com/ruoyi/bst/device/service/impl/DeviceIotServiceImpl.java index 898f7b1..788b530 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/bst/device/service/impl/DeviceIotServiceImpl.java +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/device/service/impl/DeviceIotServiceImpl.java @@ -49,8 +49,8 @@ public class DeviceIotServiceImpl implements DeviceIotService { @Autowired private ScheduledExecutorService scheduledExecutorService; - private final static Integer SUB_FAST = 300; // 上报频率(快) - private final static Integer SUB_SLOW = 3600; // 上报频率(慢) + private final static Integer SUB_FAST = 5; // 上报频率(快) + private final static Integer SUB_SLOW = 300; // 上报频率(慢) @Override public int unlock(DeviceVO device, boolean isAdmin, String reason, boolean requiredIot) { @@ -137,6 +137,43 @@ public class DeviceIotServiceImpl implements DeviceIotService { return result == null ? 0 : result; } + @Override + public int qLock(DeviceVO device, boolean isAdmin, String reason, boolean requiredIot) { + if (device == null || device.getId() == null) { + return 0; + } + ServiceUtil.assertion(!DeviceStatus.canLock().contains(device.getStatus()), "设备%s当前状态不允许锁车", device.getSn()); + + // 是否有正在进行的订单 + boolean hasOrder = device.getOrderDeviceId() != null && OrderDeviceStatus.inUse().contains(device.getOrderDeviceStatus()); + + Integer result = transactionTemplate.execute(status -> { + // 更新设备状态 + Device data = new Device(); + data.setLockStatus(DeviceLockStatus.CLOSE.getCode()); + data.setStatus(hasOrder ? DeviceStatus.TEMP_LOCKED.getCode() : DeviceStatus.AVAILABLE.getCode()); + DeviceQuery query = new DeviceQuery(); + query.setId(device.getId()); + query.setStatusList(DeviceStatus.canLock()); + int rows = deviceMapper.updateByQuery(data, query); + + if (rows > 0) { + // 发送命令锁车 + CommandResponse res = iotService.qLock(device, SUB_FAST, reason); + ServiceUtil.assertion(requiredIot && !IotUtil.isSuccess(res), IotUtil.getMsg(res)); + } + + return rows; + }); + + // 异步执行3次设备监控 + if (!requiredIot) { + this.asyncMonitorCount(device.getId(), 3); + } + + return result == null ? 0 : result; + } + @Override public boolean play(DeviceVO device, String type, String reason) { if (device == null) { diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/locationLog/domain/LocationLogQuery.java b/ruoyi-service/src/main/java/com/ruoyi/bst/locationLog/domain/LocationLogQuery.java index 4e8fcfc..1059971 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/bst/locationLog/domain/LocationLogQuery.java +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/locationLog/domain/LocationLogQuery.java @@ -11,6 +11,9 @@ import lombok.Data; @Data public class LocationLogQuery extends LocationLogVO { + @ApiModelProperty("精准MAC") + private String eqMac; + @ApiModelProperty("id列表") private List ids; @@ -22,4 +25,7 @@ public class LocationLogQuery extends LocationLogVO { @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime endTime; + @ApiModelProperty("时间范围") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private List timeRange; } diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/locationLog/mapper/LocationLogMapper.xml b/ruoyi-service/src/main/java/com/ruoyi/bst/locationLog/mapper/LocationLogMapper.xml index d14a683..acae94e 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/bst/locationLog/mapper/LocationLogMapper.xml +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/locationLog/mapper/LocationLogMapper.xml @@ -35,6 +35,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" and bll.id = #{query.id} and bll.mac like concat('%', #{query.mac}, '%') + and bll.mac = #{query.eqMac} and bll.sn like concat('%', #{query.sn}, '%') and bll.status = #{query.status} and bll.lock_status = #{query.lockStatus} @@ -42,6 +43,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" and bll.iot_status = #{query.iotStatus} and bll.device_id = #{query.deviceId} and bll.order_id = #{query.orderId} + and bll.at >= #{query.startTime} + and bll.at <= #{query.endTime} + + and bll.at >= #{query.timeRange[0]} + and bll.at <= #{query.timeRange[1]} + and bll.id in diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/locationLog/service/LocationLogConverter.java b/ruoyi-service/src/main/java/com/ruoyi/bst/locationLog/service/LocationLogConverter.java index 71df27e..c65e9c7 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/bst/locationLog/service/LocationLogConverter.java +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/locationLog/service/LocationLogConverter.java @@ -1,5 +1,6 @@ package com.ruoyi.bst.locationLog.service; +import com.ruoyi.bst.device.domain.DeviceVO; import com.ruoyi.bst.locationLog.domain.LocationLog; import com.ruoyi.iot.domain.ReceiveMsg; @@ -8,6 +9,6 @@ public interface LocationLogConverter { /** * 将ReceiveMsg转换为LocationLog */ - LocationLog toPo(ReceiveMsg msg); - + LocationLog toPo(DeviceVO device); + } diff --git a/ruoyi-service/src/main/java/com/ruoyi/bst/locationLog/service/impl/LocationLogConverterImpl.java b/ruoyi-service/src/main/java/com/ruoyi/bst/locationLog/service/impl/LocationLogConverterImpl.java index f06507f..a014577 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/bst/locationLog/service/impl/LocationLogConverterImpl.java +++ b/ruoyi-service/src/main/java/com/ruoyi/bst/locationLog/service/impl/LocationLogConverterImpl.java @@ -29,33 +29,25 @@ public class LocationLogConverterImpl implements LocationLogConverter { private IotConverter iotConverter; @Override - public LocationLog toPo(ReceiveMsg msg) { - if (msg == null || StringUtils.isBlank(msg.getDevName())) { + public LocationLog toPo(DeviceVO device) { + if (device == null || StringUtils.isBlank(device.getMac())) { return null; } - // 查询设备信息 - DeviceVO device = deviceService.selectSimpleByMacForLocationLog(msg.getDevName()); - if (device == null) { - return null; - } - // 转为设备信息 - IotDeviceSysInfo sys = IotUtil.toSysInfo(msg.getValue()); - // 设置设备信息 - DeviceUtil.setIotSysInfo(device, sys); - // 转为定位日志 LocationLog po = new LocationLog(); po.setMac(device.getMac()); po.setSn(device.getSn()); po.setStatus(device.getStatus()); po.setLockStatus(device.getLockStatus()); + po.setIotStatus(device.getIotStatus()); + po.setQuality(device.getQuality()); po.setVoltage(device.getVoltage()); po.setSignal(device.getSignalStrength()); po.setSatellites(device.getSatellites()); po.setLongitude(device.getLongitude()); po.setLatitude(device.getLatitude()); - po.setAt(DateUtils.toLocalDateTime(msg.getAt())); + po.setAt(device.getLastLocationTime()); po.setDeviceId(device.getId()); po.setOrderId(device.getOrderId()); return po; diff --git a/ruoyi-service/src/main/java/com/ruoyi/iot/service/IotService.java b/ruoyi-service/src/main/java/com/ruoyi/iot/service/IotService.java index 5fe5027..419508a 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/iot/service/IotService.java +++ b/ruoyi-service/src/main/java/com/ruoyi/iot/service/IotService.java @@ -54,6 +54,15 @@ public interface IotService { * @return 结果 */ CommandResponse lock(IotDevice device, int sub, String reason); + + /** + * 强制断电 + * @param device 设备 + * @param sub 上报频率 + * @param reason 原因 + * @return 结果 + */ + CommandResponse qLock(IotDevice device, int sub, String reason); /** * 播放语音 diff --git a/ruoyi-service/src/main/java/com/ruoyi/iot/service/impl/IotReceiveServiceImpl.java b/ruoyi-service/src/main/java/com/ruoyi/iot/service/impl/IotReceiveServiceImpl.java index 325bce7..c8c377f 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/iot/service/impl/IotReceiveServiceImpl.java +++ b/ruoyi-service/src/main/java/com/ruoyi/iot/service/impl/IotReceiveServiceImpl.java @@ -1,16 +1,40 @@ package com.ruoyi.iot.service.impl; +import java.math.BigDecimal; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.List; + +import org.locationtech.jts.geom.Geometry; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import com.ruoyi.bst.area.domain.AreaVO; +import com.ruoyi.bst.area.service.AreaService; +import com.ruoyi.bst.area.utils.AreaUtil; +import com.ruoyi.bst.areaSub.domain.AreaSubVO; +import com.ruoyi.bst.areaSub.service.AreaSubService; +import com.ruoyi.bst.areaSub.utils.AreaSubUtil; +import com.ruoyi.bst.device.domain.DeviceVO; +import com.ruoyi.bst.device.domain.enums.DeviceLockStatus; +import com.ruoyi.bst.device.domain.enums.DeviceStatus; +import com.ruoyi.bst.device.service.DeviceIotService; +import com.ruoyi.bst.device.service.DeviceService; +import com.ruoyi.bst.device.utils.DeviceUtil; import com.ruoyi.bst.locationLog.domain.LocationLog; import com.ruoyi.bst.locationLog.service.LocationLogConverter; import com.ruoyi.common.constant.CacheConstants; import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.map.GeoUtils; +import com.ruoyi.common.utils.map.GpsCoordinateUtils; +import com.ruoyi.iot.constants.IotConstants; +import com.ruoyi.iot.domain.IotDeviceSysInfo; import com.ruoyi.iot.domain.ReceiveMsg; import com.ruoyi.iot.enums.ReceiveType; import com.ruoyi.iot.service.IotReceiveService; import com.ruoyi.iot.service.IotService; +import com.ruoyi.iot.util.IotUtil; import lombok.extern.slf4j.Slf4j; @@ -31,6 +55,18 @@ public class IotReceiveServiceImpl implements IotReceiveService { @Autowired private LocationLogConverter locationLogConverter; + @Autowired + private DeviceService deviceService; + + @Autowired + private AreaSubService areaSubService; + + @Autowired + private AreaService areaService; + + @Autowired + private DeviceIotService deviceIotService; + @Override public void handleReceive(ReceiveMsg msg) { if (msg == null) { @@ -38,20 +74,139 @@ public class IotReceiveServiceImpl implements IotReceiveService { } // 数据点推送 if (ReceiveType.DATA_POINT.getType().equals(msg.getType())) { - this.handleLocationLog(msg); + log.info("dsId = {}", msg.getDsId()); + if (IotConstants.DS_SYS.equals(msg.getDsId())) { + // 查询设备信息 + DeviceVO device = deviceService.selectSimpleByMacForLocationLog(msg.getDevName()); + if (device == null) { + return; + } + // 转为设备信息 + IotDeviceSysInfo sys = IotUtil.toSysInfo(msg.getValue()); + // 设置设备信息 + DeviceUtil.setIotSysInfo(device, sys); + LocalDateTime at = DateUtils.toLocalDateTime(msg.getAt()); + device.setLastTime(at); + device.setLastLocationTime(at); + + this.handleArea(device); + + this.handleLocationLog(device); + } } // 生命周期 else if (ReceiveType.DEVICE_STATUS.getType().equals(msg.getType())) { } } - private void handleLocationLog(ReceiveMsg msg) { - log.info("处理定位日志: {}", msg); + /** + * 处理车辆运营区 + */ + private void handleArea(DeviceVO device) { + if (device == null) { + return; + } + // 相差一分钟以上的消息不做处理 + Duration duration = Duration.between(device.getLastTime(), LocalDateTime.now()); + if (duration.getSeconds() > 60) { + return; + } + // 管理员开锁,不处理 + boolean isAdminUnlocking = DeviceStatus.DISPATCHING.getCode().equals(device.getStatus()); + if (isAdminUnlocking) { + return; + } + // 坐标转换 WGS84 转 GCJ02 + List position = GpsCoordinateUtils.coordinateConvert(device.getLongitude(), device.getLatitude()); + BigDecimal lon = position.get(1); + BigDecimal lat = position.get(0); + log.info("转换后的GCJ02经纬度:{}---{}", lon, lat); + + // 查询运营区 + AreaVO area = areaService.selectAreaById(device.getAreaId()); + if(area == null){ + return; + } + // 查询运营区内的禁行区 + List noRidingList = areaSubService.selectSimpleNoRidingListByAreaId(device.getAreaId()); + + // 处理设备在运营区内的行为 + handleDeviceArea(device, area, noRidingList); + + } + + // 设备运营区处理 + private void handleDeviceArea(DeviceVO device, AreaVO area, List noRidingList) { + if (device == null || area == null) { + return; + } + + // 车辆是否开锁 + boolean isOpen = DeviceLockStatus.OPEN.getCode().equals(device.getLockStatus()); + + BigDecimal boundaryDistance = area.getBoundaryDistance();// 靠近运营区边界时的播报距离 + BigDecimal outAreaDistance = area.getOutageDistance();// 超出运营区外断电距离 + + // 创建多边形对象 + Geometry polygon = GeoUtils.fromWkt(area.getBoundary()); + // 是否在运营区最小内边界 + boolean isInAreaMin = AreaUtil.isInAreaMin(area, device.getLongitude(), device.getLatitude()); + // 是否在运营区最大外边界 + boolean isInAreaMax = AreaUtil.isInAreaMax(area, device.getLongitude(), device.getLatitude()); + // 是否在运营区内 + boolean isInArea = AreaUtil.isInArea(area, device.getLongitude(), device.getLatitude()); + // 是否在禁行区内 + AreaSubVO inAreaSub = AreaSubUtil.getInAreaSub(noRidingList, device.getLongitude(), device.getLatitude(), area.getError()); + boolean isInNoRidingArea = inAreaSub != null; + // 是否靠近禁行区 + AreaSubVO nearAreaSub = AreaSubUtil.getNearAreaSub(noRidingList, device.getLongitude(), device.getLatitude(), boundaryDistance); + boolean isNearyNoRidingArea = nearAreaSub != null; + + // 在运营区内,并且不在禁行区内,并且车辆为关锁状态,为车辆上电 + if (isInAreaMax && !isInNoRidingArea && !isOpen) { + deviceIotService.unlock(device, false, "重新返回运营区上电", false); + return; + } + + // 靠近运营区内边界,且车辆为开锁状态,播报语音警告 + if(isOpen) { + if (isNearyNoRidingArea) { + deviceIotService.play(device, IotConstants.PLAY_BOUNDARY_NEAR, "靠近禁行区"); + return; + } + if (isInArea && !isInAreaMin) { + deviceIotService.play(device, IotConstants.PLAY_BOUNDARY_NEAR, "靠近运营区内边界"); + return; + } + + // 靠近运营区外边界,且车辆为开锁状态,播报二次语音警告 + if(!isInArea && isInAreaMax) { + deviceIotService.play(device, IotConstants.PLAY_BOUNDARY_OUT, "超出运营区外边界,还未到达断电距离"); + return; + } + + // 超出运营区域外的最大范围,且车辆为开锁状态,强制断电 + if (area.getAreaOutOutage() != null && area.getAreaOutOutage()) { + if (isInNoRidingArea) { + deviceIotService.qLock(device, false, "进入禁行区,强制断电", false); + return; + } + if (!isInAreaMax) { + deviceIotService.qLock(device, false, "超出运营区外边界最大值,强制断电", false); + return; + } + } + } + + } + + // 定位日志处理 + private void handleLocationLog(DeviceVO device) { // 转换定位日志 - LocationLog po = locationLogConverter.toPo(msg); + LocationLog po = locationLogConverter.toPo(device); if (po == null) { - log.error("转换定位日志失败: {}", msg); + log.error("转换定位日志失败: {}", device.getMac()); return; } diff --git a/ruoyi-service/src/main/java/com/ruoyi/iot/service/impl/IotServiceImpl.java b/ruoyi-service/src/main/java/com/ruoyi/iot/service/impl/IotServiceImpl.java index 955bd10..a4991d8 100644 --- a/ruoyi-service/src/main/java/com/ruoyi/iot/service/impl/IotServiceImpl.java +++ b/ruoyi-service/src/main/java/com/ruoyi/iot/service/impl/IotServiceImpl.java @@ -8,13 +8,14 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import com.ruoyi.bst.device.service.DeviceService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.bst.commandLog.service.CommandLogService; +import com.ruoyi.bst.device.service.DeviceService; import com.ruoyi.common.constant.CacheConstants; import com.ruoyi.common.constant.HttpStatus; import com.ruoyi.common.core.domain.model.LoginUser; @@ -86,6 +87,9 @@ public class IotServiceImpl implements IotService { @Autowired private ScheduledExecutorService scheduledExecutorService; + @Autowired + private CommandLogService commandLogService; + // 查询OneNet设备在线状态 @Override public String getOnlineStatus(IotDevice device, String type) { @@ -287,7 +291,7 @@ public class IotServiceImpl implements IotService { } } - // TODO 异步添加日志 + // 异步添加日志 private void addCommandLog(String deviceName, String command, String result, String reason, Integer iotCode) { LoginUser loginUser = null; try { @@ -295,7 +299,7 @@ public class IotServiceImpl implements IotService { } catch (Exception e) { log.error(e.getMessage()); } -// commandLogService.addApiLog(deviceName, command, result, reason, loginUser, iotCode); + commandLogService.addApiLog(deviceName, command, result, reason, loginUser, iotCode); } private CommandResponse uploadData(String deviceName, String productId, String reason) { @@ -310,7 +314,7 @@ public class IotServiceImpl implements IotService { if (device == null) { return null; } - String command = IotConstants.COMMAND_UNLOCK + IotConstants.COMMAND_SEPARATOR + IotConstants.COMMAND_SUB + sub; + String command = IotConstants.COMMAND_UNLOCK + IotConstants.COMMAND_SUB + sub + IotConstants.COMMAND_SEPARATOR; return sendCommand(device.mac(), command, productId, reason); } @@ -319,10 +323,19 @@ public class IotServiceImpl implements IotService { if (device == null) { return null; } - String command = IotConstants.COMMAND_LOCK + IotConstants.COMMAND_SEPARATOR + IotConstants.COMMAND_SUB + sub; + String command = IotConstants.COMMAND_LOCK + IotConstants.COMMAND_SUB + sub + IotConstants.COMMAND_SEPARATOR; return sendCommand(device.mac(), command, productId, reason); } + @Override + public CommandResponse qLock(IotDevice device, int sub, String reason) { + if (device == null) { + return null; + } + String command = IotConstants.COMMAND_QLOSE + IotConstants.COMMAND_SUB + sub + IotConstants.COMMAND_SEPARATOR; + return sendCommand(device.mac(), command, productId, reason); + } + @Override public CommandResponse play(IotDevice device, String type, String reason) { if (device == null || StringUtils.isBlank(type)) { diff --git a/ruoyi-web/src/main/java/com/ruoyi/task/commandLog/CommandLogTask.java b/ruoyi-web/src/main/java/com/ruoyi/task/commandLog/CommandLogTask.java new file mode 100644 index 0000000..c3aed03 --- /dev/null +++ b/ruoyi-web/src/main/java/com/ruoyi/task/commandLog/CommandLogTask.java @@ -0,0 +1,46 @@ +package com.ruoyi.task.commandLog; + +import com.ruoyi.bst.commandLog.domain.CommandLog; +import com.ruoyi.bst.commandLog.service.CommandLogService; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.collection.CollectionUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.util.List; + +/** + * @author wjh + * 2025/1/4 + */ +@Component +@Slf4j +public class CommandLogTask { + + @Autowired + private RedisCache redisCache; + + @Autowired + private CommandLogService commandLogService; + + // 从缓存中获取数据,并插入到数据库中 + public void recordRedis() { + List list = redisCache.getAndClearCacheList(CacheConstants.INSERT_COMMAND_LOG); + if (CollectionUtils.isEmptyElement(list)) { + log.info("暂无需要保存的数据"); + return; + } + + // 插入数据库 + int insert = commandLogService.batchInsert(list); + log.info("插入命令日志成功,共插入{}条数据", insert); + } + + + public void clearBeforeDays(Integer days) { + commandLogService.clearBeforeDateLater(LocalDate.now().plusDays(-days)); + } +} diff --git a/ruoyi-web/src/main/java/com/ruoyi/web/bst/CommandLogController.java b/ruoyi-web/src/main/java/com/ruoyi/web/bst/CommandLogController.java new file mode 100644 index 0000000..b89b288 --- /dev/null +++ b/ruoyi-web/src/main/java/com/ruoyi/web/bst/CommandLogController.java @@ -0,0 +1,110 @@ +package com.ruoyi.web.bst; + +import java.util.List; + +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ruoyi.bst.commandLog.domain.CommandLog; +import com.ruoyi.bst.commandLog.domain.CommandLogQuery; +import com.ruoyi.bst.commandLog.domain.CommandLogVO; +import com.ruoyi.bst.commandLog.service.CommandLogService; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; + +/** + * 命令日志Controller + * + * @author ruoyi + * @date 2025-04-03 + */ +@RestController +@RequestMapping("/bst/commandLog") +public class CommandLogController extends BaseController +{ + @Autowired + private CommandLogService commandLogService; + + /** + * 查询命令日志列表 + */ + @PreAuthorize("@ss.hasPermi('bst:commandLog:list')") + @GetMapping("/list") + public TableDataInfo list(CommandLogQuery query) + { + startPage(); + startOrderBy(); + List list = commandLogService.selectCommandLogList(query); + return getDataTable(list); + } + + /** + * 导出命令日志列表 + */ + @PreAuthorize("@ss.hasPermi('bst:commandLog:export')") + @Log(title = "命令日志", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, CommandLogQuery query) + { + List list = commandLogService.selectCommandLogList(query); + ExcelUtil util = new ExcelUtil(CommandLogVO.class); + util.exportExcel(response, list, "命令日志数据"); + } + + /** + * 获取命令日志详细信息 + */ + @PreAuthorize("@ss.hasPermi('bst:commandLog:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) + { + return success(commandLogService.selectCommandLogById(id)); + } + + /** + * 新增命令日志 + */ + @PreAuthorize("@ss.hasPermi('bst:commandLog:add')") + @Log(title = "命令日志", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody CommandLog commandLog) + { + return toAjax(commandLogService.insertCommandLog(commandLog)); + } + + /** + * 修改命令日志 + */ + @PreAuthorize("@ss.hasPermi('bst:commandLog:edit')") + @Log(title = "命令日志", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody CommandLog commandLog) + { + return toAjax(commandLogService.updateCommandLog(commandLog)); + } + + /** + * 删除命令日志 + */ + @PreAuthorize("@ss.hasPermi('bst:commandLog:remove')") + @Log(title = "命令日志", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) + { + return toAjax(commandLogService.deleteCommandLogByIds(ids)); + } +} diff --git a/ruoyi-web/src/main/java/com/ruoyi/web/bst/LocationLogController.java b/ruoyi-web/src/main/java/com/ruoyi/web/bst/LocationLogController.java index d755edc..402daef 100644 --- a/ruoyi-web/src/main/java/com/ruoyi/web/bst/LocationLogController.java +++ b/ruoyi-web/src/main/java/com/ruoyi/web/bst/LocationLogController.java @@ -55,6 +55,22 @@ public class LocationLogController extends BaseController return getDataTable(list); } + /** + * 查询全部,不分页 + */ + @PreAuthorize("@ss.hasPermi('bst:locationLog:list')") + @GetMapping("/listAll") + public AjaxResult listAll(LocationLogQuery query) + { + if (query.getDeviceId() == null && query.getEqMac() == null && query.getOrderId() == null) { + return error("设备ID、MAC、订单ID不能同时为空"); + } + startOrderBy(); + query.setScope(true); + List list = locationLogService.selectLocationLogList(query); + return success(list); + } + /** * 根据时间段查询全部定位日志 */