12 Commits
dev ... td_h5

Author SHA1 Message Date
zc
d43acc31d9 配置 2026-06-19 15:11:15 +08:00
zc
58b76371c5 配置 2026-05-12 15:15:55 +08:00
zc
e9a698c728 Merge branch 'refs/heads/dev_zc' into td_h5
# Conflicts:
#	example/mqtt-car/src/main/java/org/dromara/mica/mqtt/server/entity/CarPassGather.java
#	example/mqtt-car/src/main/java/org/dromara/mica/mqtt/server/listener/CarMessageListener.java
#	example/mqtt-car/src/main/java/org/dromara/mica/mqtt/server/task/HeartbeatOnLineTask.java
#	example/mqtt-car/src/main/resources/application-dev.yml
#	example/mqtt-car/src/main/resources/application-prod.yml
2026-05-12 14:05:15 +08:00
zc
118729ccb2 优化redis,mysql超时异常处理,定时任务线程池扩容 2026-05-12 11:46:26 +08:00
zc
2248060905 h5提交 2026-05-12 10:49:59 +08:00
zc
17eb0c69ff 配置 2026-05-11 12:30:25 +08:00
zc
985ec252c6 开关闸控制 2026-05-09 10:33:36 +08:00
zc
4c41f6f77e 主键自增 2026-04-30 15:40:37 +08:00
zc
90003d79c5 主键自增 2026-04-30 14:54:25 +08:00
zc
7871c2ac70 满车位锁杆 2026-04-30 14:49:53 +08:00
zc
778ab69fbc 新疆 2026-04-29 14:59:03 +08:00
zc
c403b07aeb 代码优化 2026-03-16 16:54:57 +08:00
37 changed files with 516 additions and 704 deletions

View File

@@ -0,0 +1,35 @@
package org.dromara.mica.mqtt.server.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
/**
* 定时任务线程池配置
* 解决默认定时任务线程池(大小为1)被阻塞导致所有定时任务无法执行的问题
*/
@Slf4j
@Configuration
@EnableScheduling
public class SchedulerConfig {
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
// 增加线程池大小,避免单线程阻塞影响所有定时任务
scheduler.setPoolSize(5);
scheduler.setThreadNamePrefix("mqtt-scheduler-");
// 等待所有任务完成后再关闭
scheduler.setWaitForTasksToCompleteOnShutdown(true);
// 等待时间
scheduler.setAwaitTerminationSeconds(60);
// 设置异常处理器,确保异常被记录但不中断定时任务
scheduler.setErrorHandler(throwable -> {
log.error("定时任务执行异常", throwable);
});
return scheduler;
}
}

View File

@@ -49,13 +49,55 @@ public class ServerController {
return service.set_time(body);
}
@GetMapping("/openFloodgate/{sn}")
public boolean openFloodgate(@PathVariable String sn){
return service.openFloodgate(sn);
//手动触发抬杆
@PostMapping("/gpio_out")
public boolean gpio_out(@RequestBody JSONObject js) {
String sn = js.getString("sn");
Integer io = js.getInteger("io");
Integer value = js.getInteger("value");
return service.gpio_out(sn, io, value);
}
@GetMapping("/lock/{sn}")
public boolean lock(@PathVariable String sn){
return service.locked(sn);
@PostMapping("/set_io_lock_status")
public JSONObject lock(@RequestBody JSONObject js) {
String sn = js.getString("sn");
Integer status = js.getInteger("status");
Integer ioout = js.getInteger("ioout");
boolean publish = service.set_io_lock_status(sn, status,ioout);
JSONObject jsonObject = new JSONObject();
if (publish) {
jsonObject.put("code", 200);
} else {
jsonObject.put("code", 500);
}
return jsonObject;
}
//开闸
@PostMapping("/set_io_lock_open")
public JSONObject lockOpen(@RequestBody JSONObject js) {
String sn = js.getString("sn");
boolean publish = service.lockOpen(sn);
JSONObject jsonObject = new JSONObject();
if (publish) {
jsonObject.put("code", 200);
} else {
jsonObject.put("code", 500);
}
return jsonObject;
}
//关闸
@PostMapping("/set_io_lock_close")
public JSONObject lockClose(@RequestBody JSONObject js) {
String sn = js.getString("sn");
boolean publish = service.lockClose(sn);
JSONObject jsonObject = new JSONObject();
if (publish) {
jsonObject.put("code", 200);
} else {
jsonObject.put("code", 500);
}
return jsonObject;
}
}

View File

@@ -1,5 +1,7 @@
package org.dromara.mica.mqtt.server.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
@@ -16,8 +18,8 @@ public class CarInfo implements Serializable {
private static final long serialVersionUID = 1L;
/** customer_id */
private Long id;
@TableId(type = IdType.AUTO)
private Long customerId;
/** 白名单生效时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")

View File

@@ -1,70 +0,0 @@
package org.dromara.mica.mqtt.server.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
*
* @author lz
* @date 2026-02-24
*/
@Data
@TableName("car_park")
public class CarPark implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(type = IdType.AUTO) // 自增主键
private Long id;
/**
* 车场名称
*/
private String name;
/**
* 所属空间
*/
private Long spaceId;
/**
* 车位总数
*/
private Long carsNum;
/**
* 余位数
*/
private Long surplusNum;
/**
* 备注
*/
private String remark;
/**
* 创建者
*/
private Long createUser;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT) // 插入时自动填充
private Date createTime;
/**
* 更新者
*/
private Long updateUser;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE) // 插入/更新时自动填充
private Date updateTime;
}

View File

@@ -1,5 +1,7 @@
package org.dromara.mica.mqtt.server.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
@@ -22,12 +24,13 @@ public class CarParkItem implements Serializable {
/**
* id
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 设备id
*/
private Long equipmentId;
private String equipmentId;
/**
* 停车场id
@@ -44,4 +47,14 @@ public class CarParkItem implements Serializable {
*/
private String area;
/**
* 车位数量
*/
private Integer carsNum;
/**
* 满车位自动锁杆0不锁1
*/
private Integer autoLock;
}

View File

@@ -36,11 +36,6 @@ public class CarParkRecord implements Serializable {
*/
private Long customerId;
/*
* 车牌号
* */
private String plate;
/**
* 是否下发0未下发1已下发
*/
@@ -51,9 +46,4 @@ public class CarParkRecord implements Serializable {
*/
private String clientId;
/*
* 设备序列号
* */
private String sequence;
}

View File

@@ -1,5 +1,7 @@
package org.dromara.mica.mqtt.server.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
@@ -22,6 +24,7 @@ public class CarPassGather implements Serializable {
/**
* id
*/
@TableId(type = IdType.AUTO )
private Long id;
/**

View File

@@ -1,10 +1,10 @@
package org.dromara.mica.mqtt.server.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.io.Serial;
import java.io.Serializable;
@@ -24,6 +24,7 @@ public class CarPassRecord implements Serializable {
/**
* id
*/
@TableId(type = IdType.AUTO)
private Long id;
/**

View File

@@ -1,6 +1,8 @@
package org.dromara.mica.mqtt.server.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@@ -8,19 +10,17 @@ import java.io.Serial;
import java.io.Serializable;
@Data
@TableName("sys_equipment")
public class Equipment implements Serializable {
@TableName("sys_io_lock_equipment")
public class IoLockEquipment implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/** 设备Id */
@TableId(type = IdType.AUTO)
private Long id;
/** 所属产品Id */
private Long productId;
/** 设备名称 */
private String name;
@@ -33,20 +33,7 @@ public class Equipment implements Serializable {
/** 设备密码 */
private String password;
/** 设备区域 */
private Long spaceId;
/** 设备位置 */
private Long pointId;
/** 对接状态(0未对接 1对接成功) */
private Long state;
/** 设备状态(0在线 1离线) */
private String flag;
/* 车辆设备字段 杆是否被锁住 0:未上锁 1: 已上锁 */
private String locked;
}

View File

@@ -5,6 +5,7 @@ import cn.hutool.core.date.DateUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.dromara.mica.mqtt.core.annotation.MqttServerFunction;
@@ -20,6 +21,7 @@ import org.dromara.mica.mqtt.server.service.ICarParkItemService;
import org.dromara.mica.mqtt.server.service.ICarParkRecordService;
import org.dromara.mica.mqtt.server.service.ICarPassGatherService;
import org.dromara.mica.mqtt.server.service.ICarPassRecordService;
import org.dromara.mica.mqtt.server.service.impl.ServerService;
import org.dromara.mica.mqtt.server.utils.AESUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -29,6 +31,7 @@ import org.tio.utils.hutool.StrUtil;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -44,22 +47,21 @@ public class CarMessageListener {
@Autowired
RedisService redisService;
@Autowired
ICarParkRecordService carParkRecordService;
// @Autowired
// ICarParkRecordService carParkRecordService;
//
// @Autowired
// ICarPassRecordService carPassRecordService;
//
// @Autowired
// ICarParkItemService carParkItemService;
//
// @Autowired
// ICarPassGatherService carPassGatherService;
@Autowired
ICarPassRecordService carPassRecordService;
// private static String key = "1234567898765432";
@Autowired
ICarParkItemService carParkItemService;
@Autowired
ICarPassGatherService carPassGatherService;
private static String key = "1234567898765432";
private String jinjiangUrl = "http://127.0.0.1:6609/";
// private String jinjiangUrl = "http://192.168.155.42:6609/";
// private String jinjiangUrl = "http://127.0.0.1:6609/";
/**
* 心跳
@@ -72,7 +74,7 @@ public class CarMessageListener {
String sn = topicVars.get("sn");
log.info("接收到来自客户端 [{}] 的心跳消息 -> Topic: {}", sn, topic);
// 更新客户端的最后心跳
// 更新客户端的最后心跳
redisService.setCacheObject(CacheConstants.EQUIPMENT_HEARTBEAT + sn, FlagEnums.ONLINE.getCode(), CacheConstants.OFFLINE_THRESHOLD, TimeUnit.MILLISECONDS);
}
@@ -82,7 +84,7 @@ public class CarMessageListener {
* @param topic
* @param message
*/
@MqttServerFunction("device/${sn}/message/down/white_list_operator/reply")
/*@MqttServerFunction("device/${sn}/message/down/white_list_operator/reply")
@Transactional(rollbackFor = Exception.class)
public void white_list_operator_reply(String topic, byte[] message) {
log.info("接收到车牌下发消息 -> Topic: {}, message: {}", topic, new String(message));
@@ -97,12 +99,12 @@ public class CarMessageListener {
carParkRecord.setClientId(whiteListOperatorPO.getId());
//新增修改和删除车牌得回执信息一致通过id区分
if (whiteListOperatorPO.getId().contains("del_")) {
carParkRecord.setSync("3");
carParkRecord.setSync("2");
} else {
carParkRecord.setSync("1");
}
carParkRecordService.updateByClientId(carParkRecord);
}
}*/
/**
* 车牌入场出场识别监听
@@ -114,66 +116,98 @@ public class CarMessageListener {
public void ivs_result(String topic, Map<String, String> topicVars, byte[] message) throws Exception {
String sn = topicVars.get("sn");
log.info("接收到车辆识别消息 -> Topic: {}", topic);
String data = new String(message, StandardCharsets.UTF_8);
JSONObject jsonObject = JSONObject.parseObject(data);
JSONObject payload = jsonObject.getJSONObject("payload");
String id = jsonObject.getString("id");
JSONObject alarmInfoPlate = payload.getJSONObject("AlarmInfoPlate");
JSONObject result = alarmInfoPlate.getJSONObject("result");
JSONObject plateResult = result.getJSONObject("PlateResult");
JSONObject carBrand = plateResult.getJSONObject("car_brand");
String license = plateResult.getString("license");
String colorType = plateResult.getString("colorType");
String str = "";
if ("5".equals(colorType)) {
str = AESUtil.decrptyAES_ECB(license, key).substring(0, 20);
} else {
str = AESUtil.decrptyAES_ECB(license, key).substring(0, 18);
}
license = AESUtil.UTF8decode(str);
log.info("解密前车牌:{},解谜后的车牌:{}", plateResult.getString("license"), license);
//保存通行记录
CarPassRecord record = new CarPassRecord();
record.setCarColor(plateResult.getString("carColor"));
record.setColorType(colorType);
record.setDirection(plateResult.getString("direction"));
record.setLicense(license);
record.setDataType("0");
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("url", plateResult.getString("full_image_content"));
String repose = HttpUtil.createPost(jinjiangUrl + "file/uploadMinioCarBase64")
.body(JSON.toJSONString(paramMap))
.execute()
.body();
log.info("imgRsp:{}", repose);
JSONObject imgRsp = JSONObject.parseObject(repose);
if (null != imgRsp && imgRsp.getInteger("code") == 200) {
record.setUrl(imgRsp.getJSONObject("data").getString("url"));
}
// record.setPassTime(DateUtil.date(plateResult.getLong("start_time")));
record.setPassTime(new Date());
record.setSn(sn);
record.setUniqueNo(id);
record.setTriggerType(plateResult.getString("triggerType"));
record.setType(carBrand.getString("type"));
carPassRecordService.save(record);
//保存/删除在场数据
CarParkItem carParkItem = carParkItemService.selectBySn(sn);
//入场新增数据
if (StrUtil.equals(carParkItem.getWay(), "0")) {
CarPassGather carPassGather = new CarPassGather();
carPassGather.setLicense(license);
carPassGather.setParkId(carParkItem.getParkId());
carPassGather.setArea(carParkItem.getArea());
carPassGather.setJoinTime(record.getPassTime());
carPassGather.setSn(sn);
carPassGatherService.save(carPassGather);
} else {//出场删除数据
carPassGatherService.deleteByLicense(license);
}
//
// //如果开启了锁杆,且车位满了
// CarParkItem carParkItem = carParkItemService.selectBySn(sn);
// long count = carPassGatherService.count(new QueryWrapper<CarPassGather>().eq("park_id", carParkItem.getParkId()));
// if (StrUtil.equals(carParkItem.getWay(), "0") //入场
// && carParkItem.getAutoLock() == 1 //开启了锁杆
// && count >= carParkItem.getCarsNum()) { //车位满了
// log.info("车位满了,不记录入场数据");
// return;
// }
//
// String data = new String(message, StandardCharsets.UTF_8);
// JSONObject jsonObject = JSONObject.parseObject(data);
// JSONObject payload = jsonObject.getJSONObject("payload");
// String id = jsonObject.getString("id");
// JSONObject alarmInfoPlate = payload.getJSONObject("AlarmInfoPlate");
// JSONObject result = alarmInfoPlate.getJSONObject("result");
// JSONObject plateResult = result.getJSONObject("PlateResult");
// JSONObject carBrand = plateResult.getJSONObject("car_brand");
// String license = plateResult.getString("license");
// String colorType = plateResult.getString("colorType");
// String str = "";
// if ("5".equals(colorType)) {
// str = AESUtil.decrptyAES_ECB(license, key).substring(0, 20);
// } else {
// str = AESUtil.decrptyAES_ECB(license, key).substring(0, 18);
// }
//
// license = AESUtil.UTF8decode(str);
// log.info("解密前车牌:{},解谜后的车牌:{}", plateResult.getString("license"), license);
//
// //保存通行记录
// CarPassRecord record = new CarPassRecord();
// record.setCarColor(plateResult.getString("carColor"));
// record.setColorType(colorType);
// record.setDirection(plateResult.getString("direction"));
// record.setLicense(license);
// record.setDataType("0");
// Map<String, Object> paramMap = new HashMap<>();
// paramMap.put("url", plateResult.getString("full_image_content"));
// String repose = HttpUtil.createPost(jinjiangUrl + "file/uploadMinioCarBase64")
// .body(JSON.toJSONString(paramMap))
// .execute()
// .body();
// log.info("imgRsp:{}", repose);
// JSONObject imgRsp = JSONObject.parseObject(repose);
// if (null != imgRsp && imgRsp.getInteger("code") == 200) {
// record.setUrl(imgRsp.getJSONObject("data").getString("url"));
// }
// if (plateResult.containsKey("start_time")) {
// log.info("拿到了时间:{}", plateResult.getLong("start_time"));
// record.setPassTime(DateUtil.date(plateResult.getLong("start_time")));
// } else {
// log.info("没有拿到时间,默认当前时间");
// record.setPassTime(DateUtil.date(new Date()));
// }
// record.setSn(sn);
// record.setUniqueNo(id);
// record.setParkId(carParkItem.getParkId());
// record.setTriggerType(plateResult.getString("triggerType"));
// record.setType(carBrand.getString("type"));
// carPassRecordService.save(record);
//
// //保存/删除在场数据
// boolean isExist = carPassGatherService.exists(new QueryWrapper<CarPassGather>().eq("license", license).eq("park_id", carParkItem.getParkId()));
// //入场新增数据
// if (StrUtil.equals(carParkItem.getWay(), "0") && !isExist) {
// CarPassGather carPassGather = new CarPassGather();
// carPassGather.setLicense(license);
// carPassGather.setParkId(carParkItem.getParkId());
// carPassGather.setArea(carParkItem.getArea());
// carPassGather.setJoinTime(record.getPassTime());
// carPassGather.setSn(sn);
// carPassGatherService.save(carPassGather);
//
// //最后一辆车进场
// if (carParkItem.getAutoLock() == 1 && (count + 1) >= carParkItem.getCarsNum()) {
// List<String> snList = carParkItemService.selectSnByParkId(carParkItem.getParkId());
// for (String s : snList) {
// service.locked(s, 2, 0);
// }
// }
// } else if (StrUtil.equals(carParkItem.getWay(), "1") && isExist) {//出场删除数据
// carPassGatherService.deleteByLicense(license);
// //出去一辆车,有空位
// if (carParkItem.getAutoLock() == 1 && count == carParkItem.getCarsNum()) {
// List<String> snList = carParkItemService.selectSnByParkId(carParkItem.getParkId());
// for (String s : snList) {
// service.locked(s, 0, 0);
// }
// }
// }
}
/**
@@ -182,7 +216,7 @@ public class CarMessageListener {
* @param topic
* @param message
*/
@MqttServerFunction("device/${sn}/message/up/ivs_result_offline")
/*@MqttServerFunction("device/${sn}/message/up/ivs_result_offline")
public void ivs_result_offline(String topic, Map<String, String> topicVars, byte[] message) throws Exception {
String sn = topicVars.get("sn");
log.info("接收到车辆离线识别消息 -> Topic: {}", topic);
@@ -225,14 +259,19 @@ public class CarMessageListener {
if (null != imgRsp && imgRsp.getInteger("code") == 200) {
record.setUrl(imgRsp.getJSONObject("data").getString("url"));
}
// record.setPassTime(DateUtil.date(plateResult.getLong("start_time")));
record.setPassTime(new Date());
if (plateResult.containsKey("start_time")) {
log.info("拿到了离线时间:{}", plateResult.getLong("start_time"));
record.setPassTime(DateUtil.date(plateResult.getLong("start_time")));
} else {
log.info("没有拿到离线时间,默认当前时间");
record.setPassTime(DateUtil.date(new Date()));
}
record.setSn(sn);
record.setUniqueNo(id);
record.setTriggerType(plateResult.getString("triggerType"));
record.setType(carBrand.getString("type"));
carPassRecordService.save(record);
}
}*/
/**
* IO输出事件监听
@@ -244,6 +283,7 @@ public class CarMessageListener {
public void gpio_out(String topic, byte[] message) {
log.info("IO输出事件监听消息 -> Topic: {}, message: {}", topic, new String(message));
String data = new String(message, StandardCharsets.UTF_8);
log.info("IO输出事件监听{}", data);
}
@@ -257,9 +297,36 @@ public class CarMessageListener {
public void set_io_lock_status(String topic, byte[] message) {
log.info("IO锁定事件监听消息 -> Topic: {}, message: {}", topic, new String(message));
String data = new String(message, StandardCharsets.UTF_8);
log.info("IO锁定事件监听{}", data);
JSONObject jsonObject = JSONObject.parseObject(data);
}
/**
* 发布获取IO锁定状态监听
*
* @param topic
* @param message
*/
@MqttServerFunction("device/${sn}/message/down/get_io_lock_status/reply")
public void get_io_lock_status(String topic, byte[] message) {
log.info("发布获取IO锁定状态监听消息 -> Topic: {}, message: {}", topic, new String(message));
String data = new String(message, StandardCharsets.UTF_8);
JSONObject jsonObject = JSONObject.parseObject(data);
}
/**
* 发布获取IO状态监听
*
* @param topic
* @param message
*/
@MqttServerFunction("device/${sn}/message/down/get_io_status/reply")
public void get_io_status(String topic, byte[] message) {
log.info("发布获取IO状态监听消息 -> Topic: {}, message: {}", topic, new String(message));
String data = new String(message, StandardCharsets.UTF_8);
JSONObject jsonObject = JSONObject.parseObject(data);
}
/**
* 订阅离线数据数量
*
@@ -272,11 +339,4 @@ public class CarMessageListener {
String data = new String(message, StandardCharsets.UTF_8);
}
@MqttServerFunction("device/${sn}/message/down/set_time/reply")
public void set_time(String topic, byte[] message) {
log.info("订阅离线数据数量 -> Topic: {}, message: {}", topic, new String(message));
String data = new String(message, StandardCharsets.UTF_8);
}
}

View File

@@ -16,13 +16,10 @@
package org.dromara.mica.mqtt.server.listener;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.dromara.mica.mqtt.core.server.event.IMqttConnectStatusListener;
import org.dromara.mica.mqtt.server.constant.CacheConstants;
import org.dromara.mica.mqtt.server.entity.Equipment;
import org.dromara.mica.mqtt.server.enums.FlagEnums;
import org.dromara.mica.mqtt.server.mapper.EquipmentMapper;
import org.dromara.mica.mqtt.server.redis.RedisService;
import org.dromara.mica.mqtt.server.service.IEquipmentService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -30,8 +27,6 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.tio.core.ChannelContext;
import java.util.concurrent.TimeUnit;
/**
* mqtt 连接状态
*

View File

@@ -10,4 +10,5 @@ import java.util.List;
public interface CarInfoMapper extends BaseMapper<CarInfo> {
List<CarInfo> selectCarInfoList(CarInfo car);
}

View File

@@ -12,6 +12,9 @@ import java.util.List;
@Mapper
public interface CarParkItemMapper extends BaseMapper<CarParkItem> {
@Select("SELECT p.id,p.park_id parkId,p.way,p.equipment_id equipmentId,p.area FROM car_park_item p left join sys_equipment e on p.equipment_id = e.id where e.sequence = #{sn} and e.product_id = 4 limit 1")
@Select("SELECT p.id,p.park_id parkId,k.auto_lock autoLock,k.cars_num carsNum,p.way,p.equipment_id equipmentId,p.area FROM car_park_item p left join sys_equipment e on p.equipment_id = e.id LEFT JOIN car_park k on p.park_id = k.id where e.sequence = #{sn} and e.product_id = 4 limit 1")
CarParkItem selectBySn(@Param("sn") String sn);
@Select("SELECT e.sequence FROM car_park_item p left join sys_equipment e on p.equipment_id = e.id where p.park_id = #{parkId} and p.way = 0 and e.product_id = 4")
List<String> selectSnByParkId(@Param("parkId") String parkId);
}

View File

@@ -1,9 +0,0 @@
package org.dromara.mica.mqtt.server.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.mica.mqtt.server.entity.CarPark;
@Mapper
public interface CarParkMapper extends BaseMapper<CarPark> {
}

View File

@@ -4,9 +4,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.mica.mqtt.server.entity.CarParkRecord;
import java.util.List;
@Mapper
public interface CarParkRecordMapper extends BaseMapper<CarParkRecord> {
List<CarParkRecord> selectWithEquipmentSequence();
}

View File

@@ -2,11 +2,8 @@ package org.dromara.mica.mqtt.server.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.dromara.mica.mqtt.server.entity.CarPassGather;
import java.util.List;
@Mapper
public interface CarPassGatherMapper extends BaseMapper<CarPassGather> {

View File

@@ -2,10 +2,9 @@ package org.dromara.mica.mqtt.server.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.mica.mqtt.server.entity.CarInfo;
import org.dromara.mica.mqtt.server.entity.Equipment;
import org.dromara.mica.mqtt.server.entity.IoLockEquipment;
@Mapper
public interface EquipmentMapper extends BaseMapper<Equipment> {
public interface EquipmentMapper extends BaseMapper<IoLockEquipment> {
}

View File

@@ -80,7 +80,11 @@ public class RedisService {
*/
public <T> void setCacheObject(final String key, final T value, final Long timeout, final TimeUnit timeUnit)
{
redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
try {
redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
} catch (Exception e) {
log.error("Redis setCacheObject error: key={}", key, e);
}
}
/**
@@ -127,7 +131,12 @@ public class RedisService {
*/
public Boolean hasKey(String key)
{
return redisTemplate.hasKey(key);
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
log.error("Redis hasKey error: key={}", key, e);
return false;
}
}
/**
@@ -138,8 +147,13 @@ public class RedisService {
*/
public <T> T getCacheObject(final String key)
{
ValueOperations<String, T> operation = redisTemplate.opsForValue();
return operation.get(key);
try {
ValueOperations<String, T> operation = redisTemplate.opsForValue();
return operation.get(key);
} catch (Exception e) {
log.error("Redis getCacheObject error: key={}", key, e);
return null;
}
}
/**

View File

@@ -3,7 +3,11 @@ package org.dromara.mica.mqtt.server.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.mica.mqtt.server.entity.CarParkItem;
import java.util.List;
public interface ICarParkItemService extends IService<CarParkItem> {
CarParkItem selectBySn(String sn);
List<String> selectSnByParkId(String parkId);
}

View File

@@ -3,13 +3,9 @@ package org.dromara.mica.mqtt.server.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.mica.mqtt.server.entity.CarParkRecord;
import java.util.List;
public interface ICarParkRecordService extends IService<CarParkRecord> {
int updateByClientId(CarParkRecord carParkRecord);
void delByClientId(CarParkRecord carParkRecord);
List<CarParkRecord> listSn();
}

View File

@@ -2,13 +2,8 @@ package org.dromara.mica.mqtt.server.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.mica.mqtt.server.entity.CarPassGather;
import org.dromara.mica.mqtt.server.entity.CarPassRecord;
import java.util.List;
public interface ICarPassGatherService extends IService<CarPassGather> {
void deleteByLicense(String license);
void deleteBatchByLicences(List<CarPassRecord> carPassRecords);
}

View File

@@ -3,13 +3,6 @@ package org.dromara.mica.mqtt.server.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.mica.mqtt.server.entity.CarPassRecord;
import java.util.List;
public interface ICarPassRecordService extends IService<CarPassRecord> {
/*
* 批量设置通行记录为历史数据
* */
public void batchUpdate(List<CarPassRecord> carPassRecords);
}

View File

@@ -1,20 +1,15 @@
package org.dromara.mica.mqtt.server.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.mica.mqtt.server.entity.Equipment;
import org.dromara.mica.mqtt.server.entity.IoLockEquipment;
import java.util.List;
import java.util.Map;
public interface IEquipmentService extends IService<Equipment> {
public interface IEquipmentService extends IService<IoLockEquipment> {
Equipment selectEquipmentBySn(String sn);
IoLockEquipment selectEquipmentBySn(String sn);
List<Equipment> selectAllSnFlag();
List<IoLockEquipment> selectAllSnFlag();
void updateFlag(String sn, String flag);
List<Equipment> selectAllOnline();
Map<Long,String> selectIdSnMap();
}

View File

@@ -1,6 +1,5 @@
package org.dromara.mica.mqtt.server.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.dromara.mica.mqtt.server.entity.CarParkItem;
import org.dromara.mica.mqtt.server.mapper.CarParkItemMapper;
@@ -8,6 +7,8 @@ import org.dromara.mica.mqtt.server.service.ICarParkItemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class CarParkItemServiceImpl extends ServiceImpl<CarParkItemMapper, CarParkItem> implements ICarParkItemService {
@@ -18,4 +19,9 @@ public class CarParkItemServiceImpl extends ServiceImpl<CarParkItemMapper, CarPa
public CarParkItem selectBySn(String sn) {
return carParkItemMapper.selectBySn(sn);
}
@Override
public List<String> selectSnByParkId(String parkId) {
return carParkItemMapper.selectSnByParkId(parkId);
}
}

View File

@@ -1,28 +1,19 @@
package org.dromara.mica.mqtt.server.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.dromara.mica.mqtt.server.entity.CarParkRecord;
import org.dromara.mica.mqtt.server.mapper.CarParkRecordMapper;
import org.dromara.mica.mqtt.server.mapper.EquipmentMapper;
import org.dromara.mica.mqtt.server.service.ICarParkRecordService;
import org.dromara.mica.mqtt.server.service.IEquipmentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
@Service
public class CarParkRecordServiceImpl extends ServiceImpl<CarParkRecordMapper, CarParkRecord> implements ICarParkRecordService {
@Autowired
CarParkRecordMapper carParkRecordMapper;
@Autowired
IEquipmentService equipmentService;
@Override
public int updateByClientId(CarParkRecord carParkRecord) {
int id = carParkRecordMapper.update(carParkRecord, new QueryWrapper<CarParkRecord>().eq("client_id", carParkRecord.getClientId()));
@@ -33,10 +24,4 @@ public class CarParkRecordServiceImpl extends ServiceImpl<CarParkRecordMapper, C
public void delByClientId(CarParkRecord carParkRecord) {
carParkRecordMapper.delete(new QueryWrapper<CarParkRecord>().eq("client_id", carParkRecord.getClientId()));
}
@Override
public List<CarParkRecord> listSn() {
List<CarParkRecord> list = carParkRecordMapper.selectWithEquipmentSequence();
return list;
}
}

View File

@@ -1,19 +1,13 @@
package org.dromara.mica.mqtt.server.service.impl;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.dromara.mica.mqtt.server.entity.CarPassGather;
import org.dromara.mica.mqtt.server.entity.CarPassRecord;
import org.dromara.mica.mqtt.server.mapper.CarPassGatherMapper;
import org.dromara.mica.mqtt.server.service.ICarPassGatherService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class CarPassGatherServiceImpl extends ServiceImpl<CarPassGatherMapper, CarPassGather> implements ICarPassGatherService {
@@ -24,15 +18,4 @@ public class CarPassGatherServiceImpl extends ServiceImpl<CarPassGatherMapper, C
public void deleteByLicense(String license) {
carPassGatherMapper.delete(new LambdaQueryWrapper<CarPassGather>().eq(CarPassGather::getLicense, license));
}
@Override
public void deleteBatchByLicences(List<CarPassRecord> carPassRecords) {
if(ObjectUtil.isEmpty(carPassRecords) && carPassRecords.size() == 0) {
return ;
}
List<String> licenses = carPassRecords.stream().map(CarPassRecord::getLicense).collect(Collectors.toList());
LambdaQueryWrapper<CarPassGather> wrapper = Wrappers.lambdaQuery();
wrapper.in(CarPassGather::getLicense, licenses);
carPassGatherMapper.delete(wrapper);
}
}

View File

@@ -2,7 +2,6 @@ package org.dromara.mica.mqtt.server.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.ibatis.executor.BatchResult;
import org.dromara.mica.mqtt.server.entity.CarParkRecord;
import org.dromara.mica.mqtt.server.entity.CarPassRecord;
import org.dromara.mica.mqtt.server.mapper.CarParkRecordMapper;
@@ -12,20 +11,10 @@ import org.dromara.mica.mqtt.server.service.ICarPassRecordService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class CarPassRecordServiceImpl extends ServiceImpl<CarPassRecordMapper, CarPassRecord> implements ICarPassRecordService {
@Autowired
CarPassRecordMapper carPassRecordMapper;
@Override
public void batchUpdate(List<CarPassRecord> carPassRecords) {
carPassRecords.stream().forEach(carPassRecord -> {
carPassRecord.setDataType("1");
});
carPassRecordMapper.updateById(carPassRecords);
}
}

View File

@@ -2,49 +2,34 @@ package org.dromara.mica.mqtt.server.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.dromara.mica.mqtt.server.entity.Equipment;
import org.dromara.mica.mqtt.server.entity.IoLockEquipment;
import org.dromara.mica.mqtt.server.mapper.EquipmentMapper;
import org.dromara.mica.mqtt.server.service.IEquipmentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
public class EquipmentServiceImpl extends ServiceImpl<EquipmentMapper, Equipment> implements IEquipmentService {
public class EquipmentServiceImpl extends ServiceImpl<EquipmentMapper, IoLockEquipment> implements IEquipmentService {
@Autowired
EquipmentMapper equipmentMapper;
@Override
public Equipment selectEquipmentBySn(String sn) {
return equipmentMapper.selectOne(new QueryWrapper<Equipment>().eq("sequence", sn).eq("product_id", 4L).last("limit 1"));
public IoLockEquipment selectEquipmentBySn(String sn) {
return equipmentMapper.selectOne(new QueryWrapper<IoLockEquipment>().eq("sequence", sn).last("limit 1"));
}
@Override
public List<Equipment> selectAllSnFlag() {
return equipmentMapper.selectList(new QueryWrapper<Equipment>().eq("product_id", 4L).select("sequence", "flag"));
public List<IoLockEquipment> selectAllSnFlag() {
return equipmentMapper.selectList(new QueryWrapper<IoLockEquipment>().select("sequence", "flag"));
}
@Override
public void updateFlag(String sn, String flag) {
Equipment equipment = new Equipment();
IoLockEquipment equipment = new IoLockEquipment();
equipment.setFlag(flag);
equipmentMapper.update(equipment, new QueryWrapper<Equipment>().eq("sequence", sn).eq("product_id", 4L));
}
@Override
public List<Equipment> selectAllOnline() {
return equipmentMapper.selectList(new QueryWrapper<Equipment>().eq("product_id", 4L).eq("flag","0").eq("open_need","1"));
}
@Override
public Map<Long, String> selectIdSnMap() {
List<Equipment> list = equipmentMapper.selectList(new QueryWrapper<Equipment>().eq("product_id", 4L));
Map<Long, String> map = list.stream().collect(Collectors.toMap(Equipment::getId, Equipment::getSequence));
return map;
equipmentMapper.update(equipment, new QueryWrapper<IoLockEquipment>().eq("sequence", sn));
}
}

View File

@@ -2,12 +2,9 @@ package org.dromara.mica.mqtt.server.service.impl;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.dromara.mica.mqtt.server.entity.Equipment;
import org.dromara.mica.mqtt.server.pojo.DeviceIOLockRequestPO;
import org.dromara.mica.mqtt.server.pojo.OpenFloodgatePO;
import org.dromara.mica.mqtt.server.service.IEquipmentService;
import org.dromara.mica.mqtt.server.utils.UuidUtil;
import org.dromara.mica.mqtt.spring.server.MqttServerTemplate;
import org.springframework.beans.factory.annotation.Autowired;
@@ -26,9 +23,6 @@ public class ServerService {
@Autowired
private MqttServerTemplate server;
@Autowired
private IEquipmentService equipmentService;
public boolean publish(JSONObject js) {
String sn = js.getString("sn");
String topic = js.getString("topic");
@@ -63,14 +57,12 @@ public class ServerService {
}
public boolean set_time(String body) {
boolean result = server.publish("61e70b04-8e68be6a","device/61e70b04-8e68be6a/message/down/set_time", body.getBytes(StandardCharsets.UTF_8));
boolean result = server.publish("61e70b04-8e68be6a","device/61e70b04-8e68be6a/message/up/offline_record", body.getBytes(StandardCharsets.UTF_8));
log.info("发布离线数据数量body:{},result:{}", body, result);
return result;
}
public boolean openFloodgate(String sn) {
// Equipment equipment = equipmentService.getOne(new LambdaQueryWrapper<Equipment>().eq(Equipment::getName,name));
// String sn = equipment.getSequence();
public boolean gpio_out(String sn, Integer io, Integer value) {
String operUrl = "device/%s/message/down/gpio_out";
String topic = String.format(operUrl, sn);
OpenFloodgatePO openFloodgatePO = new OpenFloodgatePO();
@@ -78,18 +70,26 @@ public class ServerService {
openFloodgatePO.setId(uuid);
openFloodgatePO.setSn(sn);
openFloodgatePO.setName("gpio_out");
LocalDateTime now = LocalDateTime.now();
long timestamp = now.atZone(ZoneId.systemDefault()).toEpochSecond();
openFloodgatePO.setTimestamp(timestamp);
openFloodgatePO.setPayload(this.buildPayloadOpen());
boolean result = server.publish(sn,topic, openFloodgatePO.toString().getBytes(StandardCharsets.UTF_8));
log.info("抬杠设备编码:{},result:{},请求体{}", sn,result,openFloodgatePO);
openFloodgatePO.setTimestamp(System.currentTimeMillis() / 1000);
openFloodgatePO.setPayload(this.buildGpioOut(io, value));
boolean result = server.publish(sn,topic, JSON.toJSONString(openFloodgatePO).getBytes(StandardCharsets.UTF_8));
log.info("gpio_out设备编码:{},result:{},请求体{}", sn,result,openFloodgatePO);
return result;
}
public boolean locked(String sn) {
// Equipment equipment = equipmentService.getOne(new LambdaQueryWrapper<Equipment>().eq(Equipment::getName,name));
// String sn = equipment.getSequence();
private JSONObject buildGpioOut(Integer io, Integer value) {
JSONObject body = new JSONObject();
body.put("delay", 500);
body.put("io", io);
body.put("value", value);
JSONObject payload = new JSONObject();
payload.put("type", "gpio_out");
payload.put("body", body);
return payload;
}
public boolean set_io_lock_status(String sn, Integer status, Integer ioout) {
String operUrl = "device/%s/message/down/set_io_lock_status";
String topic = String.format(operUrl, sn);
DeviceIOLockRequestPO deviceIOLockRequestPO = new DeviceIOLockRequestPO();
@@ -97,30 +97,45 @@ public class ServerService {
deviceIOLockRequestPO.setId(uuid);
deviceIOLockRequestPO.setSn(sn);
deviceIOLockRequestPO.setName("set_io_lock_status");
deviceIOLockRequestPO.setPayload(this.buildPayloadLocked());
boolean result = server.publish(sn,topic, deviceIOLockRequestPO.toString().getBytes(StandardCharsets.UTF_8));
log.info("抬杠解锁:{},result:{},请求体:{}", sn,result,deviceIOLockRequestPO);
return result;
deviceIOLockRequestPO.setPayload(this.buildPayloadLocked(status, ioout));
return server.publish(sn,topic, JSON.toJSONString(deviceIOLockRequestPO).getBytes(StandardCharsets.UTF_8));
}
private JSONObject buildPayloadOpen() {
JSONObject body = new JSONObject();
body.put("delay", 10000);
body.put("io", 0);
body.put("value",2);
private JSONObject buildPayloadLocked(Integer status, Integer ioout) {
JSONObject payload = new JSONObject();
payload.put("type", "gpio_out");
payload.put("type", "set_io_lock_status");
JSONObject body = new JSONObject();
//io口摄像头接线口
body.put("ioout", ioout);
//0解锁 1高电平锁定保持常抬 2低电平锁定保持常关
body.put("status", status);
payload.put("body", body);
return payload;
}
private JSONObject buildPayloadLocked() {
JSONObject payload = new JSONObject();
payload.put("type","set_io_lock_status");
JSONObject body = new JSONObject();
body.put("ioout",1);
body.put("status",0);
payload.put("body", body);
return payload;
public boolean lockOpen(String sn) {
String operUrl = "device/%s/message/down/gpio_out";
String topic = String.format(operUrl, sn);
OpenFloodgatePO openFloodgatePO = new OpenFloodgatePO();
String uuid = "open_" + UuidUtil.getUuid();
openFloodgatePO.setId(uuid);
openFloodgatePO.setSn(sn);
openFloodgatePO.setName("gpio_out");
openFloodgatePO.setTimestamp(System.currentTimeMillis() / 1000);
openFloodgatePO.setPayload(this.buildGpioOut(0, 2));
return server.publish(sn,topic, JSON.toJSONString(openFloodgatePO).getBytes(StandardCharsets.UTF_8));
}
public boolean lockClose(String sn) {
String operUrl = "device/%s/message/down/gpio_out";
String topic = String.format(operUrl, sn);
OpenFloodgatePO openFloodgatePO = new OpenFloodgatePO();
String uuid = "open_" + UuidUtil.getUuid();
openFloodgatePO.setId(uuid);
openFloodgatePO.setSn(sn);
openFloodgatePO.setName("gpio_out");
openFloodgatePO.setTimestamp(System.currentTimeMillis() / 1000);
openFloodgatePO.setPayload(this.buildGpioOut(1, 2));
return server.publish(sn,topic, JSON.toJSONString(openFloodgatePO).getBytes(StandardCharsets.UTF_8));
}
}

View File

@@ -1,11 +1,9 @@
package org.dromara.mica.mqtt.server.task;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.dromara.mica.mqtt.server.constant.CacheConstants;
import org.dromara.mica.mqtt.server.entity.Equipment;
import org.dromara.mica.mqtt.server.entity.IoLockEquipment;
import org.dromara.mica.mqtt.server.enums.FlagEnums;
import org.dromara.mica.mqtt.server.mapper.EquipmentMapper;
import org.dromara.mica.mqtt.server.redis.RedisService;
import org.dromara.mica.mqtt.server.service.IEquipmentService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -32,9 +30,9 @@ public class HeartbeatOnLineTask {
public void run() {
log.info("===========心跳检测=============");
//查询车辆摄像头的编码和在线状态
List<Equipment> equipment = equipmentService.selectAllSnFlag();
List<IoLockEquipment> equipment = equipmentService.selectAllSnFlag();
for (Equipment equip : equipment) {
for (IoLockEquipment equip : equipment) {
//缓存中有该设备心跳key
if (redisService.hasKey(CacheConstants.EQUIPMENT_HEARTBEAT + equip.getSequence())) {
String flag = redisService.getCacheObject(CacheConstants.EQUIPMENT_HEARTBEAT + equip.getSequence());

View File

@@ -4,17 +4,15 @@ import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.dromara.mica.mqtt.core.server.MqttServer;
import org.dromara.mica.mqtt.server.constant.CacheConstants;
import org.dromara.mica.mqtt.server.entity.*;
import org.dromara.mica.mqtt.server.mapper.CarParkMapper;
import org.dromara.mica.mqtt.server.mapper.CarPassRecordMapper;
import org.dromara.mica.mqtt.server.pojo.DeviceIOLockRequestPO;
import org.dromara.mica.mqtt.server.entity.CarInfo;
import org.dromara.mica.mqtt.server.entity.CarParkRecord;
import org.dromara.mica.mqtt.server.pojo.WhiteListOperatorPO;
import org.dromara.mica.mqtt.server.redis.RedisService;
import org.dromara.mica.mqtt.server.service.*;
import org.dromara.mica.mqtt.server.service.ICarInfoService;
import org.dromara.mica.mqtt.server.service.ICarParkRecordService;
import org.dromara.mica.mqtt.server.utils.UuidUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
@@ -26,8 +24,6 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 车牌下发设备定时任务
@@ -45,24 +41,6 @@ public class PlatePublishTask {
@Autowired
ICarParkRecordService carParkRecordService;
@Autowired
CarPassRecordMapper carPassRecordMapper;
@Autowired
ICarPassRecordService carPassRecordService;
@Autowired
ICarPassGatherService carPassGatherService;
@Autowired
CarParkMapper carParkMapper;
@Autowired
IEquipmentService equipmentService;
@Autowired
ICarParkItemService carParkItemService;
@Autowired
RedisService redisService;
@@ -70,240 +48,100 @@ public class PlatePublishTask {
/**
* 定时查询数据库需要下发的车辆进行下发
*/
@Scheduled(fixedDelay = 8 * 1000)
// @Scheduled(fixedDelay = 8 * 1000)
public void run() {
log.info("===== 白名单定时任务开始执行 =====");
String whiteUrl = "device/%s/message/down/white_list_operator";
// 添加异常捕获,防止单个任务异常导致定时任务线程阻塞
try {
String whiteUrl = "device/%s/message/down/white_list_operator";
//删除车牌
List<CarParkRecord> carInfos = carParkRecordService.listSn();
if (CollUtil.isNotEmpty(carInfos)) {
//需要下发的设备
List<String> snList = carInfos.stream().map(CarParkRecord::getSequence).toList();
for (String sn : snList) {
if (!redisService.hasKey(CacheConstants.EQUIPMENT_HEARTBEAT + sn)) {
log.error("删除车牌,设备:{},无心跳", sn);
continue;
}
//车辆授权列表
List<CarParkRecord> carParkRecords = new ArrayList<>();
String topic = String.format(whiteUrl, sn);
WhiteListOperatorPO po = new WhiteListOperatorPO();
String uuid = "del_" + UuidUtil.getUuid();
po.setId(uuid);
po.setSn(sn);
po.setName("white_list_operator");
//构造内层数据
List<String> plates = new ArrayList<>();
for (CarParkRecord carParkRecord : carInfos) {
plates.add(carParkRecord.getPlate());
if (StrUtil.equals(carParkRecord.getSequence(), sn)) {
carParkRecord.setClientId(uuid);
carParkRecords.add(carParkRecord);
//新增&编辑车牌
CarInfo carInfo = new CarInfo();
carInfo.setDelFlag("0");
carInfo.setSync("0");
List<CarInfo> carInfoList = carInfoService.selectCarInfoList(carInfo);
if (CollUtil.isNotEmpty(carInfoList)) {
//向设备下发车辆信息
for (CarInfo car : carInfoList) {
//如果该设备没有心跳,不下发车牌信息
if (!redisService.hasKey(CacheConstants.EQUIPMENT_HEARTBEAT + car.getSn())) {
continue;
}
String topic = String.format(whiteUrl, car.getSn());
WhiteListOperatorPO po = new WhiteListOperatorPO();
String uuid = "add_" + UuidUtil.getUuid();
po.setId(uuid);
po.setSn(car.getSn());
po.setName("white_list_operator");
//构造数据
po.setPayload(buildPayload(car));
//更新car_park_record表的clientId用于回执消息更新下发状态
//必须在发送前更新,不然会导致发送后,还没更新完数据库,回执已经收到,无法更新数据,导致持续下发车牌
CarParkRecord carParkRecord = new CarParkRecord();
carParkRecord.setId(car.getCarParkRecordId());
carParkRecord.setClientId(uuid);
carParkRecordService.updateById(carParkRecord);
//发布车牌到设备(协议只能单条发布)
boolean publish = mqttServer.publish(car.getSn(), topic, JSON.toJSONString(po).getBytes(StandardCharsets.UTF_8));
log.info("定时任务下发车牌topic:{},报文数据:{},发送结果:{}", topic, JSON.toJSONString(po), publish);
}
po.setPayload(buildPayloadDel(plates));
//更新车辆授权信息
if (CollUtil.isNotEmpty(carParkRecords)) {
carParkRecordService.saveOrUpdateBatch(carParkRecords);
}
//删除车牌
CarInfo carDel = new CarInfo();
carDel.setDelFlag("2");
carDel.setSync("1");
List<CarInfo> carInfos = carInfoService.selectCarInfoList(carDel);
if (CollUtil.isNotEmpty(carInfos)) {
//需要下发的设备
List<String> snList = carInfos.stream().map(CarInfo::getSn).distinct().toList();
for (String sn : snList) {
if (!redisService.hasKey(CacheConstants.EQUIPMENT_HEARTBEAT + sn)) {
log.error("删除车牌,设备:{},无心跳", sn);
continue;
}
//车辆授权列表
List<CarParkRecord> carParkRecords = new ArrayList<>();
String topic = String.format(whiteUrl, sn);
WhiteListOperatorPO po = new WhiteListOperatorPO();
String uuid = "del_" + UuidUtil.getUuid();
po.setId(uuid);
po.setSn(sn);
po.setName("white_list_operator");
//构造内层数据
List<String> plates = new ArrayList<>();
for (CarInfo car : carInfos) {
plates.add(car.getPlate());
if (StrUtil.equals(car.getSn(), sn)) {
CarParkRecord carParkRecord = new CarParkRecord();
carParkRecord.setClientId(uuid);
carParkRecord.setId(car.getCarParkRecordId());
carParkRecords.add(carParkRecord);
}
}
po.setPayload(buildPayloadDel(plates));
//更新车辆授权信息
if (CollUtil.isNotEmpty(carParkRecords)) {
carParkRecordService.saveOrUpdateBatch(carParkRecords);
}
//发布车牌到设备
boolean publish = mqttServer.publish(sn, topic, JSON.toJSONString(po).getBytes(StandardCharsets.UTF_8));
log.info("定时任务删除车牌topic:{},报文数据:{},发送结果:{}", topic, JSON.toJSONString(po), publish);
}
//发布车牌到设备
boolean publish = mqttServer.publish(sn, topic, JSON.toJSONString(po).getBytes(StandardCharsets.UTF_8));
log.info("定时任务删除车牌topic:{},报文数据:{},发送结果:{}", topic, JSON.toJSONString(po), publish);
}
} catch (Exception e) {
// 捕获所有异常并记录日志,确保定时任务能继续执行
log.error("PlatePublishTask 定时任务执行异常", e);
}
//新增&编辑车牌
CarInfo carInfo = new CarInfo();
// carInfo.setDelFlag("0");
carInfo.setSync("0");
List<CarInfo> carInfoList = carInfoService.selectCarInfoList(carInfo);
if (CollUtil.isNotEmpty(carInfoList)) {
//向设备下发车辆信息
for (CarInfo car : carInfoList) {
//如果该设备没有心跳,不下发车牌信息
if (!redisService.hasKey(CacheConstants.EQUIPMENT_HEARTBEAT + car.getSn())) {
continue;
}
String topic = String.format(whiteUrl, car.getSn());
WhiteListOperatorPO po = new WhiteListOperatorPO();
String uuid = "add_" + UuidUtil.getUuid();
po.setId(uuid);
po.setSn(car.getSn());
po.setName("white_list_operator");
//构造数据
po.setPayload(buildPayload(car));
//更新car_park_record表的clientId用于回执消息更新下发状态
//必须在发送前更新,不然会导致发送后,还没更新完数据库,回执已经收到,无法更新数据,导致持续下发车牌
CarParkRecord carParkRecord = new CarParkRecord();
carParkRecord.setId(car.getCarParkRecordId());
carParkRecord.setSequence(car.getSn());
carParkRecord.setClientId(uuid);
carParkRecordService.updateById(carParkRecord);
//发布车牌到设备(协议只能单条发布)
boolean publish = mqttServer.publish(car.getSn(), topic, JSON.toJSONString(po).getBytes(StandardCharsets.UTF_8));
log.info("定时任务下发车牌topic:{},报文数据:{},发送结果:{}", topic, JSON.toJSONString(po), publish);
}
}
log.info("白名单定时任务处理结束");
}
/*
* 定时任务 处理通行记录对车位的影响
* */
@Scheduled(cron = "0/30 * * * * ?")
private void handlePassRecord(){
log.info("==========开始对通行记录进行处理==========");
String lockStatusUrl = "device/%s/message/down/set_io_lock_status";
//存放需要上锁的车场id和设备
List<Long> lockedParkList = new ArrayList<>();
List<Equipment> lockedEquipmentList;
//存放需要解锁的车场id和设备
List<Long> unlockParkList = new ArrayList<>();
List<Equipment> unlockEquipmentList;
//获取全部车场对应的剩余车位信息
List<CarPark> carParks = carParkMapper.selectList(Wrappers.lambdaQuery());
//获取车场对应的当前剩余车位
Map<Long,Long> parkIdSpaceMap = carParks.stream().collect(Collectors.toMap(item -> item.getId(), item -> item.getSurplusNum(),(v1,v2)->v1));
//处理非历史数据的离场通行记录
//获取需要处理的出场记录
List<CarPassRecord> carOutPassRecord = carPassRecordMapper.selectList(
Wrappers.lambdaQuery(CarPassRecord.class)
.eq(CarPassRecord::getDirection, "1")
.eq(CarPassRecord::getDataType, "0")
);
Map<String, List<CarPassRecord>> outRecordByParkId = carOutPassRecord.stream()
.filter(record -> record.getParkId() != null)
.collect(Collectors.groupingBy(CarPassRecord::getParkId));
// 遍历每个车场的出场记录,更新剩余车位
outRecordByParkId.forEach((parkId, outRecords) -> {
// 获取该车场当前剩余车位默认0避免空指针
Long currentSurplus = parkIdSpaceMap.getOrDefault(Long.valueOf(parkId), 0L);
Long newSurplus = currentSurplus + outRecords.size();
parkIdSpaceMap.put(Long.valueOf(parkId), newSurplus);
log.info("车场ID:{},处理出场记录{}条,剩余车位从{}更新为{}", parkId, outRecords.size(), currentSurplus, newSurplus);
});
//将出场的车辆从在场记录中删除
carPassGatherService.deleteBatchByLicences(carOutPassRecord);
//将处理过的通行记录标记为历史数据
carPassRecordService.batchUpdate(carOutPassRecord);
carParks.stream().forEach(park -> {
Long currentSurplus = parkIdSpaceMap.get(park.getId());
Long oldSurplus = park.getSurplusNum(); // 原来的剩余车位
if (currentSurplus > 0 && oldSurplus <= 0) unlockParkList.add(park.getId());
});
//先获取全部需解锁的车场对应的绑定列表 只需要管理入场设备
if (unlockParkList.size() > 0) {
List<CarParkItem> parkItemList = carParkItemService.list(Wrappers.lambdaQuery(CarParkItem.class).in(CarParkItem::getParkId, unlockParkList).eq(CarParkItem::getWay,"0"));
List<Long> ids = parkItemList.stream().map(CarParkItem::getEquipmentId).collect(Collectors.toList());
unlockEquipmentList = equipmentService.list(Wrappers.lambdaQuery(Equipment.class).in(Equipment::getId, ids).eq(Equipment::getFlag, "0").eq(Equipment::getLocked, "1"));
//存放成功上锁的设备
List<Equipment> successList = new ArrayList<>();
for (Equipment equipment : unlockEquipmentList) {
String topic = String.format(lockStatusUrl, equipment.getSequence());
DeviceIOLockRequestPO deviceIOLockRequestPO = new DeviceIOLockRequestPO();
String uuid = "unlock_" + UuidUtil.getUuid();
deviceIOLockRequestPO.setId(uuid);
deviceIOLockRequestPO.setSn(equipment.getSequence());
deviceIOLockRequestPO.setName("set_io_lock_status");
JSONObject payload = this.buildPayLoadUnLock();
deviceIOLockRequestPO.setPayload(payload);
boolean publish = mqttServer.publish(equipment.getSequence(), topic, JSON.toJSONString(deviceIOLockRequestPO).getBytes(StandardCharsets.UTF_8));
if (publish) successList.add(equipment);
log.info("定时任务设备解锁topic:{},报文数据:{},发送结果:{}", topic, JSON.toJSONString(deviceIOLockRequestPO), publish);
}
if (successList.size() > 0) {
successList.stream().forEach(equipment -> {
equipment.setLocked("0");
});
equipmentService.updateBatchById(successList);
}
}
//处理非历史数据的进场通行记录
//存放新增的在场记录
List<CarPassGather> insertList = new ArrayList<>();
//存放已经处理的进场通行记录到待处理集合
List<CarPassRecord> handedList = new ArrayList<>();
List<CarPassRecord> carIntoPassRecord = carPassRecordMapper.selectList(
Wrappers.lambdaQuery(CarPassRecord.class)
.eq(CarPassRecord::getDirection, "0")
.eq(CarPassRecord::getDataType, "0")
);
Map<String, List<CarPassRecord>> insertRecordByParkId = carIntoPassRecord.stream()
.filter(record -> record.getParkId() != null)
.collect(Collectors.groupingBy(CarPassRecord::getParkId));
insertRecordByParkId.forEach((parkId, insertRecords) -> {
for (CarPassRecord record : insertRecords) {
Long currentSurplus = parkIdSpaceMap.getOrDefault(Long.valueOf(parkId), 0L);
if (currentSurplus <= 0) continue;
//更新当前车位数
Long currentSpace = currentSurplus-1;
parkIdSpaceMap.put(Long.valueOf(parkId), currentSpace);
//将处理过的进场通行记录加入待处理集合
handedList.add(record);
//生成新地在场记录,并加入待处理集合
CarPassGather carPassGather = new CarPassGather();
carPassGather.setParkId(parkId);
carPassGather.setLicense(record.getLicense());
carPassGather.setSn(record.getSn());
carPassGather.setJoinTime(record.getPassTime());
insertList.add(carPassGather);
}
});
//将待处理的通行记录标记为历史数据
carPassRecordService.batchUpdate(handedList);
//将新生成的在场记录添加到表中
carPassGatherService.saveBatch(insertList);
//更新车场的车位信息
carParks.stream().forEach(park -> {
Long currentSurplus = parkIdSpaceMap.get(park.getId());
Long oldSurplus = park.getSurplusNum();
park.setSurplusNum(currentSurplus);
// 从有车位变为无车位才需要上锁
if (currentSurplus <= 0 && oldSurplus > 0) lockedParkList.add(park.getId());
});
carParkMapper.updateById(carParks);
if (lockedParkList.size() > 0) {
//先获取全部需上锁的车场对应的绑定列表 只需要管理进场设备
List<CarParkItem> parkItemList1 = carParkItemService.list(Wrappers.lambdaQuery(CarParkItem.class).in(CarParkItem::getParkId, lockedParkList).eq(CarParkItem::getWay,"0"));
List<Long> ids1 = parkItemList1.stream().map(CarParkItem::getEquipmentId).collect(Collectors.toList());
lockedEquipmentList = equipmentService.list(Wrappers.lambdaQuery(Equipment.class).in(Equipment::getId, ids1).eq(Equipment::getFlag, "0").eq(Equipment::getLocked, "0"));
//存放成功解锁的设备
List<Equipment> successList = new ArrayList<>();
for (Equipment equipment : lockedEquipmentList) {
String topic = String.format(lockStatusUrl, equipment.getSequence());
DeviceIOLockRequestPO deviceIOLockRequestPO = new DeviceIOLockRequestPO();
String uuid = "locked_" + UuidUtil.getUuid();
deviceIOLockRequestPO.setId(uuid);
deviceIOLockRequestPO.setSn(equipment.getSequence());
deviceIOLockRequestPO.setName("set_io_lock_status");
JSONObject payload = this.buildPayloadLocked();
deviceIOLockRequestPO.setPayload(payload);
boolean publish = mqttServer.publish(equipment.getSequence(), topic, JSON.toJSONString(deviceIOLockRequestPO).getBytes(StandardCharsets.UTF_8));
if (publish) successList.add(equipment);
log.info("定时任务设备上锁topic:{},报文数据:{},发送结果:{}", topic, JSON.toJSONString(deviceIOLockRequestPO), publish);
}
if (successList.size() > 0) {
successList.stream().forEach(equipment -> {
equipment.setLocked("1");
});
equipmentService.updateBatchById(successList);
}
}
log.info("==========通行记录处理结束==========");
}
@@ -348,25 +186,4 @@ public class PlatePublishTask {
return payload;
}
//上锁
private JSONObject buildPayloadLocked() {
JSONObject payload = new JSONObject();
payload.put("type","set_io_lock_status");
JSONObject body = new JSONObject();
body.put("ioout",1);
body.put("status",2);
payload.put("body", body);
return payload;
}
//解锁
private JSONObject buildPayLoadUnLock(){
JSONObject payload = new JSONObject();
payload.put("type","set_io_lock_status");
JSONObject body = new JSONObject();
body.put("ioout",1);
body.put("status",0);
payload.put("body", body);
return payload;
}
}

View File

@@ -1,12 +1,33 @@
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/xa_cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
url: jdbc:mysql://127.0.0.1:3306/td_cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&connectTimeout=5000&socketTimeout=30000
username: root
password: root
# Druid 连接池配置
druid:
max-active: 20
max-wait: 60000
min-idle: 5
initial-size: 5
validation-query: SELECT 1
test-while-idle: true
test-on-borrow: false
test-on-return: false
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
data:
redis:
# host: 127.0.0.1
host: 192.168.2.30
password: redis2025
database: 5
port: 6379
# Redis 超时配置,防止连接阻塞
timeout: 5000ms
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 2
max-wait: 5000ms

View File

@@ -1,28 +1,33 @@
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
#xa
# url: jdbc:mysql://127.0.0.1:3306/xa_cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# username: root
# password: Xahg2024.
#jl
# url: jdbc:mysql://127.0.0.1:3306/jl_cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# username: root
# password: JL202509jj
#td
# url: jdbc:mysql://127.0.0.1:3306/td_cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# username: root
# password: td@JJ2024
#zr
url: jdbc:mysql://192.168.155.42:3306/zr_cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
url: jdbc:mysql://192.168.251.16:3306/td_cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&connectTimeout=5000&socketTimeout=30000
username: root
password: zr202407.J
password: td@JJ2024
# Druid 连接池配置
druid:
max-active: 20
max-wait: 60000
min-idle: 5
initial-size: 5
validation-query: SELECT 1
test-while-idle: true
test-on-borrow: false
test-on-return: false
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
data:
redis:
#zr
host: 192.168.155.42
#xa、jl、td
# host: 127.0.0.1
host: 192.168.251.16
port: 6379
password:
database: 1
database: 2
# Redis 超时配置,防止连接阻塞
timeout: 5000ms
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 2
max-wait: 5000ms

View File

@@ -127,3 +127,8 @@ logging:
server: info # t-io 服务端默认日志
org.tio: info # t-io 服务端默认日志
org.dromara.mica.mqtt: info # mica-mqtt 日志
org.dromara.mica.mqtt.server.task: debug # 定时任务日志级别
# 定时任务线程池配置
scheduler:
pool-size: 5

View File

@@ -5,7 +5,7 @@
<mapper namespace="org.dromara.mica.mqtt.server.mapper.CarInfoMapper">
<resultMap type="org.dromara.mica.mqtt.server.entity.CarInfo" id="CarInfoResult">
<result property="id" column="id" />
<result property="customerId" column="customer_id" />
<result property="enableTime" column="enable_time" />
<result property="overdueTime" column="overdue_time" />
<result property="enable" column="enable" />
@@ -24,19 +24,19 @@
</resultMap>
<sql id="selectCarInfoVo">
select ci.id, ci.enable_time, ci.overdue_time, ci.enable, ci.plate, ci.time_seg_enable, ci.seg_time,
select ci.customer_id, ci.enable_time, ci.overdue_time, ci.enable, ci.plate, ci.time_seg_enable, ci.seg_time,
ci.need_alarm, ci.vehicle_code, ci.vehicle_comment, ci.people_id, ci.del_flag, ci.sync, ci.remark,
ci.create_user, ci.create_time, ci.update_user, ci.update_time, ci.overclock_card
ci.create_by, ci.create_time, ci.update_by, ci.update_time, ci.overclock_card
from car_info ci
left join car_park cp on ci.park_id=cp.id
left join sys_people p on ci.people_id=p.id
left join car_park_record cpr on cpr.customer_id=ci.id
left join car_park_record cpr on cpr.customer_id=ci.customer_id
left join sys_equipment se on cpr.equipment_id=se.id
</sql>
<select id="selectCarInfoList" parameterType="org.dromara.mica.mqtt.server.entity.CarInfo" resultMap="CarInfoResult">
SELECT
ci.id,
ci.customer_id,
ci.enable_time,
ci.overdue_time,
ci.`enable`,
@@ -50,9 +50,9 @@
ci.del_flag,
ci.sync,
ci.remark,
ci.create_user,
ci.create_by,
ci.create_time,
ci.update_user,
ci.update_by,
ci.update_time,
ci.overclock_card,
se.sequence,
@@ -60,7 +60,7 @@
FROM car_info ci
LEFT JOIN car_park cp ON ci.park_id = cp.id
LEFT JOIN sys_people p ON ci.people_id = p.id
LEFT JOIN car_park_record cpr ON cpr.customer_id = ci.id
LEFT JOIN car_park_record cpr ON cpr.customer_id = ci.customer_id
LEFT JOIN sys_equipment se ON cpr.equipment_id = se.id
<where>
<if test="enableTime != null "> and ci.enable_time = #{enableTime}</if>

View File

@@ -1,34 +0,0 @@
<?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="org.dromara.mica.mqtt.server.mapper.CarParkRecordMapper">
<resultMap id="CarParkRecordWithEquipmentResultMap" type="org.dromara.mica.mqtt.server.entity.CarParkRecord">
<id property="id" column="id" />
<result property="equipmentId" column="equipment_id" />
<result property="parkId" column="park_id" />
<result property="plate" column="plate" />
<result property="customerId" column="customer_id" />
<result property="sync" column="sync" />
<result property="clientId" column="client_id" />
<!-- 关联的设备sequence字段 -->
<result property="sequence" column="sequence" />
</resultMap>
<select id="selectWithEquipmentSequence" resultMap="CarParkRecordWithEquipmentResultMap">
SELECT
cpr.id,
cpr.equipment_id,
cpr.park_id,
cpr.plate,
cpr.customer_id,
cpr.sync,
cpr.client_id,
eq.sequence AS sequence
FROM car_park_record cpr
LEFT JOIN sys_equipment eq ON cpr.equipment_id = eq.id
where
cpr.sync = '2'
ORDER BY cpr.id DESC
</select>
</mapper>

View File

@@ -1,7 +0,0 @@
<?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="org.dromara.mica.mqtt.server.mapper.CarPassGatherMapper">
</mapper>