1 Commits

Author SHA1 Message Date
bf8db1d04e 车辆优化 2026-03-02 17:30:28 +08:00
26 changed files with 639 additions and 70 deletions

View File

@@ -3,10 +3,7 @@ package org.dromara.mica.mqtt.server.controller;
import com.alibaba.fastjson2.JSONObject;
import org.dromara.mica.mqtt.server.service.impl.ServerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
@RequestMapping("/mqtt/server")
@RestController
@@ -52,4 +49,13 @@ public class ServerController {
return service.set_time(body);
}
@GetMapping("/openFloodgate/{sn}")
public boolean openFloodgate(@PathVariable String sn){
return service.openFloodgate(sn);
}
@GetMapping("/lock/{sn}")
public boolean lock(@PathVariable String sn){
return service.locked(sn);
}
}

View File

@@ -16,7 +16,7 @@ public class CarInfo implements Serializable {
private static final long serialVersionUID = 1L;
/** customer_id */
private Long customerId;
private Long id;
/** 白名单生效时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")

View File

@@ -0,0 +1,70 @@
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

@@ -27,7 +27,7 @@ public class CarParkItem implements Serializable {
/**
* 设备id
*/
private String equipmentId;
private Long equipmentId;
/**
* 停车场id

View File

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

View File

@@ -45,4 +45,8 @@ public class Equipment implements Serializable {
/** 设备状态(0在线 1离线) */
private String flag;
/* 车辆设备字段 杆是否被锁住 0:未上锁 1: 已上锁 */
private String locked;
}

View File

@@ -58,8 +58,8 @@ public class CarMessageListener {
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/";
// private String jinjiangUrl = "http://192.168.155.42:6609/";
/**
* 心跳
@@ -97,7 +97,7 @@ public class CarMessageListener {
carParkRecord.setClientId(whiteListOperatorPO.getId());
//新增修改和删除车牌得回执信息一致通过id区分
if (whiteListOperatorPO.getId().contains("del_")) {
carParkRecord.setSync("2");
carParkRecord.setSync("3");
} else {
carParkRecord.setSync("1");
}
@@ -244,7 +244,6 @@ 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);
}
@@ -273,4 +272,11 @@ 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

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

View File

@@ -0,0 +1,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.CarPark;
@Mapper
public interface CarParkMapper extends BaseMapper<CarPark> {
}

View File

@@ -4,7 +4,9 @@ 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,8 +2,11 @@ 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

@@ -0,0 +1,45 @@
package org.dromara.mica.mqtt.server.pojo;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
@Data
public class DeviceIOLockRequestPO {
/**
* 消息ID用于关联具体消息
*/
@JsonProperty("id")
private String id;
/**
* 设备序列号
*/
@JsonProperty("sn")
private String sn;
/**
* 消息名称
*/
@JsonProperty("name")
private String name;
/**
* 消息版本目前都填1.0
*/
@JsonProperty("version")
private String version = "1.0";
/**
* 时间戳
*/
@JsonProperty("timestamp")
private long timestamp = System.currentTimeMillis() / 1000;
/**
* 消息负载
*/
@JsonProperty("payload")
private JSONObject payload;
}

View File

@@ -0,0 +1,39 @@
package org.dromara.mica.mqtt.server.pojo;
import lombok.Data;
import com.alibaba.fastjson2.JSONObject;
@Data
public class OpenFloodgatePO {
/**
* 消息ID用于关联具体消息
*/
private String id;
/**
* 设备序列号
*/
private String sn;
/**
* 消息名称固定为gpio_out
*/
private String name;
/**
* 消息版本目前都填1.0
*/
private String version = "1.0";
/**
* 时间戳
*/
private Long timestamp = System.currentTimeMillis() / 1000;
/**
* 消息负载
*/
private JSONObject payload;
}

View File

@@ -3,9 +3,13 @@ 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,8 +2,13 @@ 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,6 +3,13 @@ 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

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.mica.mqtt.server.entity.Equipment;
import java.util.List;
import java.util.Map;
public interface IEquipmentService extends IService<Equipment> {
@@ -12,4 +13,8 @@ public interface IEquipmentService extends IService<Equipment> {
List<Equipment> selectAllSnFlag();
void updateFlag(String sn, String flag);
List<Equipment> selectAllOnline();
Map<Long,String> selectIdSnMap();
}

View File

@@ -1,19 +1,28 @@
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()));
@@ -24,4 +33,10 @@ 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,13 +1,19 @@
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 {
@@ -18,4 +24,15 @@ 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,6 +2,7 @@ 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;
@@ -11,10 +12,20 @@ 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

@@ -8,7 +8,10 @@ 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 {
@@ -32,4 +35,16 @@ public class EquipmentServiceImpl extends ServiceImpl<EquipmentMapper, Equipment
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;
}
}

View File

@@ -1,12 +1,21 @@
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;
import org.springframework.stereotype.Service;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneId;
/**
* @author wsq
@@ -17,6 +26,9 @@ 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");
@@ -51,8 +63,64 @@ public class ServerService {
}
public boolean set_time(String body) {
boolean result = server.publish("61e70b04-8e68be6a","device/61e70b04-8e68be6a/message/up/offline_record", body.getBytes(StandardCharsets.UTF_8));
boolean result = server.publish("61e70b04-8e68be6a","device/61e70b04-8e68be6a/message/down/set_time", 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();
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");
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);
return result;
}
public boolean locked(String sn) {
// Equipment equipment = equipmentService.getOne(new LambdaQueryWrapper<Equipment>().eq(Equipment::getName,name));
// String sn = equipment.getSequence();
String operUrl = "device/%s/message/down/set_io_lock_status";
String topic = String.format(operUrl, sn);
DeviceIOLockRequestPO deviceIOLockRequestPO = new DeviceIOLockRequestPO();
String uuid = "locked_" + UuidUtil.getUuid();
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;
}
private JSONObject buildPayloadOpen() {
JSONObject body = new JSONObject();
body.put("delay", 10000);
body.put("io", 0);
body.put("value",2);
JSONObject payload = new JSONObject();
payload.put("type", "gpio_out");
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;
}
}

View File

@@ -4,15 +4,17 @@ 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.CarInfo;
import org.dromara.mica.mqtt.server.entity.CarParkRecord;
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.pojo.WhiteListOperatorPO;
import org.dromara.mica.mqtt.server.redis.RedisService;
import org.dromara.mica.mqtt.server.service.ICarInfoService;
import org.dromara.mica.mqtt.server.service.ICarParkRecordService;
import org.dromara.mica.mqtt.server.service.*;
import org.dromara.mica.mqtt.server.utils.UuidUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
@@ -24,6 +26,8 @@ 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;
/**
* 车牌下发设备定时任务
@@ -41,6 +45,24 @@ 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;
@@ -50,11 +72,52 @@ public class PlatePublishTask {
*/
@Scheduled(fixedDelay = 8 * 1000)
public void run() {
log.info("===== 白名单定时任务开始执行 =====");
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);
}
}
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);
}
}
//新增&编辑车牌
CarInfo carInfo = new CarInfo();
carInfo.setDelFlag("0");
// carInfo.setDelFlag("0");
carInfo.setSync("0");
List<CarInfo> carInfoList = carInfoService.selectCarInfoList(carInfo);
if (CollUtil.isNotEmpty(carInfoList)) {
@@ -78,65 +141,169 @@ public class PlatePublishTask {
//必须在发送前更新,不然会导致发送后,还没更新完数据库,回执已经收到,无法更新数据,导致持续下发车牌
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);
}
}
//删除车牌
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;
log.info("白名单定时任务处理结束");
}
//车辆授权列表
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");
/*
* 定时任务 处理通行记录对车位的影响
* */
@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")
);
//构造内层数据
List<String> plates = new ArrayList<>();
for (CarInfo car : carInfos) {
plates.add(car.getPlate());
Map<String, List<CarPassRecord>> outRecordByParkId = carOutPassRecord.stream()
.filter(record -> record.getParkId() != null)
.collect(Collectors.groupingBy(CarPassRecord::getParkId));
if (StrUtil.equals(car.getSn(), sn)) {
CarParkRecord carParkRecord = new CarParkRecord();
carParkRecord.setClientId(uuid);
carParkRecord.setId(car.getCarParkRecordId());
carParkRecords.add(carParkRecord);
// 遍历每个车场的出场记录,更新剩余车位
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);
}
}
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);
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("==========通行记录处理结束==========");
}
@@ -181,4 +348,25 @@ 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

@@ -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="customerId" column="customer_id" />
<result property="id" column="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.customer_id, ci.enable_time, ci.overdue_time, ci.enable, ci.plate, ci.time_seg_enable, ci.seg_time,
select ci.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_by, ci.create_time, ci.update_by, ci.update_time, ci.overclock_card
ci.create_user, ci.create_time, ci.update_user, 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.customer_id
left join car_park_record cpr on cpr.customer_id=ci.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.customer_id,
ci.id,
ci.enable_time,
ci.overdue_time,
ci.`enable`,
@@ -50,9 +50,9 @@
ci.del_flag,
ci.sync,
ci.remark,
ci.create_by,
ci.create_user,
ci.create_time,
ci.update_by,
ci.update_user,
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.customer_id
LEFT JOIN car_park_record cpr ON cpr.customer_id = ci.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

@@ -0,0 +1,34 @@
<?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

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.mica.mqtt.server.mapper.CarPassGatherMapper">
</mapper>