代码优化

This commit is contained in:
zc
2025-12-11 21:39:02 +08:00
parent 7180eedc29
commit a01da7c9dd
26 changed files with 1050 additions and 70 deletions

View File

@@ -59,7 +59,6 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot4-starter</artifactId>
@@ -94,6 +93,19 @@
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- JSON 解析器和生成器 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.23</version>
</dependency>
<!-- Hutool工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.40</version>
</dependency>
</dependencies>

View File

@@ -1,7 +1,5 @@
package org.dromara.mica.mqtt.server.controller;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.dromara.mica.mqtt.server.service.impl.ServerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
@@ -9,17 +7,35 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "Mqtt::服务端")
@RequestMapping("/mqtt/server")
@RestController
public class ServerController {
@Autowired
private ServerService service;
@Operation(summary = "publish")
@PostMapping("publish")
public boolean publish(@RequestBody String body) {
return service.publish(body);
}
@PostMapping("open")
public boolean open(@RequestBody String body) {
return service.open(body);
}
@PostMapping("open2")
public boolean open2(@RequestBody String body) {
return service.open2(body);
}
@PostMapping("check_offline_record")
public boolean check_offline_record(@RequestBody String body) {
return service.check_offline_record(body);
}
@PostMapping("offline_record")
public boolean offline_record(@RequestBody String body) {
return service.offline_record(body);
}
}

View File

@@ -7,6 +7,7 @@ import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
@Data
@TableName("car_info")
@@ -31,12 +32,21 @@ public class CarInfo implements Serializable {
/** 车牌号 */
private String plate;
/** 车牌号 */
private List<String> plateList;
/** 是否启用时间段 */
private String timeSegEnable;
/** 时间段 */
private String segTime;
/** 时间段 */
private String segTimeStart = "00:00:00";
/** 时间段 */
private String segTimeEnd = "00:00:00";
/** 是否需要报警 */
private String needAlarm;
@@ -57,8 +67,9 @@ public class CarInfo implements Serializable {
private String sn;//设备序列号
private int num;
/** 超频设备识别标签 */
private String overclockCard;
/** car_park_record表的主键id */
private Long carParkRecordId;
}

View File

@@ -0,0 +1,47 @@
package org.dromara.mica.mqtt.server.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 车场设备实体
*
*/
@Data
@TableName("car_park_item")
public class CarParkItem implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* id
*/
private Long id;
/**
* 设备id
*/
private String equipmentId;
/**
* 停车场id
*/
private String parkId;
/**
* 过车行驶方向 0 入场过车 1出场过车
*/
private String way;
/**
* 区域
*/
private String area;
}

View File

@@ -0,0 +1,49 @@
package org.dromara.mica.mqtt.server.entity;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 车辆授权对象 car_park_record
*
*/
@Data
public class CarParkRecord implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* id
*/
private Long id;
/**
* 设备id
*/
private Long equipmentId;
/**
* 车场id
*/
private Long parkId;
/**
* 车辆id
*/
private Long customerId;
/**
* 是否下发0未下发1已下发
*/
private String sync;
/**
* 摄像头mqtt协议的报文id
*/
private String clientId;
}

View File

@@ -0,0 +1,87 @@
package org.dromara.mica.mqtt.server.entity;
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;
import java.util.Date;
/**
* 通行记录对象 car_pass_record
*
*/
@Data
@TableName("car_pass_record")
public class CarPassRecord implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* id
*/
private Long id;
/**
* 停车场id
*/
private String parkId;
/**
* 过车信息唯一标识
*/
private String uniqueNo;
/**
* 过车行驶方向 0 入场过车 1出场过车
*/
private String direction;
/**
* 车牌号码
*/
private String license;
/**
* 通行时间
*/
@JsonFormat(pattern = "yyyy-MM-dd")
private Date passTime;
/**
* 触发类型
*/
private String triggerType;
/**
* 车牌颜色
*/
private String colorType;
/**
* 车辆颜色
*/
private String carColor;
/**
* 车牌图片数据
*/
private String url;
/**
* 数据类型 0 实时数据 1历史数据
*/
private String dataType;
/**
* 设备序列号
*/
private String sn;
private String type;
}

View File

@@ -1,19 +1,28 @@
package org.dromara.mica.mqtt.server.listener;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.dromara.mica.mqtt.codec.message.MqttPublishMessage;
import org.dromara.mica.mqtt.server.constant.CacheConstants;
import org.dromara.mica.mqtt.server.enums.FlagEnums;
import org.dromara.mica.mqtt.server.pojo.User;
import org.dromara.mica.mqtt.core.annotation.MqttServerFunction;
import org.dromara.mica.mqtt.server.constant.CacheConstants;
import org.dromara.mica.mqtt.server.entity.CarParkItem;
import org.dromara.mica.mqtt.server.entity.CarParkRecord;
import org.dromara.mica.mqtt.server.entity.CarPassRecord;
import org.dromara.mica.mqtt.server.entity.Equipment;
import org.dromara.mica.mqtt.server.enums.FlagEnums;
import org.dromara.mica.mqtt.server.pojo.WhiteListOperatorPO;
import org.dromara.mica.mqtt.server.redis.RedisService;
import org.dromara.mica.mqtt.server.service.ICarParkItemService;
import org.dromara.mica.mqtt.server.service.ICarParkRecordService;
import org.dromara.mica.mqtt.server.service.IEquipmentService;
import org.dromara.mica.mqtt.server.utils.AESUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.tio.core.ChannelContext;
import org.tio.core.Node;
import org.springframework.transaction.annotation.Transactional;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -29,6 +38,23 @@ public class CarMessageListener {
@Autowired
RedisService redisService;
@Autowired
ICarParkRecordService carParkRecordService;
@Autowired
IEquipmentService equipmentService;
@Autowired
ICarParkItemService carParkItemService;
private static String key = "1234567898765432";
/**
* 心跳
* @param topic
* @param topicVars
* @param message
*/
@MqttServerFunction("device/${sn}/message/up/keep_alive")
public void onKeepAliveMessage(String topic, Map<String, String> topicVars, byte[] message) {
String sn = topicVars.get("sn");
@@ -38,13 +64,136 @@ public class CarMessageListener {
redisService.setCacheObject(CacheConstants.EQUIPMENT_HEARTBEAT + sn, FlagEnums.ONLINE.getCode(), CacheConstants.OFFLINE_THRESHOLD, TimeUnit.MILLISECONDS);
}
/**
* 车牌下发结果监听
*
* @param topic
* @param message
*/
@MqttServerFunction("device/${sn}/message/down/white_list_operator/reply")
public void white_list_operator_reply(String topic, byte[] payload) {
log.info("接收到消息 -> Topic: {}, Payload: {}", topic, new String(payload));
// 在这里添加您的业务逻辑,例如:
// 1. 解析 payload
// 2. 更新设备在线状态
// 3. 回复心跳响应等
@Transactional(rollbackFor = Exception.class)
public void white_list_operator_reply(String topic, byte[] message) {
log.info("接收到车牌下发消息 -> Topic: {}, message: {}", topic, new String(message));
String data = new String(message, StandardCharsets.UTF_8);
WhiteListOperatorPO whiteListOperatorPO = JSONObject.parseObject(data, WhiteListOperatorPO.class);
if (null == whiteListOperatorPO || 200 != whiteListOperatorPO.getCode()) {
log.error("white_list_operator发布失败{}", data);
return;
}
CarParkRecord carParkRecord = new CarParkRecord();
carParkRecord.setClientId(whiteListOperatorPO.getId());
//新增修改和删除车牌得回执信息一致通过id区分
if (whiteListOperatorPO.getId().contains("del_")) {
carParkRecord.setSync("2");
} else {
carParkRecord.setSync("1");
}
carParkRecordService.updateByClientId(carParkRecord);
}
/**
* 车牌入场出场识别监听
*
* @param topic
* @param message
*/
@MqttServerFunction("device/${sn}/message/up/ivs_result")
public void ivs_result(String topic, Map<String, String> topicVars, byte[] message) throws Exception {
String sn = topicVars.get("sn");
log.info("接收到车辆识别消息 -> Topic: {}, message: {}", topic, new String(message));
String data = new String(message, StandardCharsets.UTF_8);
JSONObject jsonObject = JSONObject.parseObject(data);
JSONObject payload = jsonObject.getJSONObject("payload");
JSONObject alarmInfoPlate = payload.getJSONObject("AlarmInfoPlate");
JSONObject result = alarmInfoPlate.getJSONObject("result");
JSONObject plateResult = result.getJSONObject("PlateResult");
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);
}
/**
* 车辆离线识别监听
*
* @param topic
* @param message
*/
@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: {}, message: {}", topic, new String(message));
String data = new String(message, StandardCharsets.UTF_8);
log.info("车牌离线识别监听:{}", data);
JSONObject jsonObject = JSONObject.parseObject(data);
JSONObject payload = jsonObject.getJSONObject("payload");
JSONObject alarmInfoPlate = payload.getJSONObject("AlarmInfoPlate");
JSONObject result = alarmInfoPlate.getJSONObject("result");
JSONObject plateResult = result.getJSONObject("PlateResult");
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);
CarParkItem carParkItem = carParkItemService.selectBySn(sn);
CarPassRecord carPassRecord = new CarPassRecord();
carPassRecord.setSn(sn);
carPassRecord.setParkId(carParkItem.getParkId());
}
/**
* IO输出事件监听
*
* @param topic
* @param message
*/
@MqttServerFunction("device/${sn}/message/down/gpio_out/reply")
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);
}
/**
* IO锁定事件监听
*
* @param topic
* @param message
*/
@MqttServerFunction("device/${sn}/message/down/set_io_lock_status/reply")
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);
}
/**
* 订阅离线数据数量
*
* @param topic
* @param message
*/
@MqttServerFunction("device/${sn}/message/up/offline_record")
public void offline_record(String topic, byte[] message) {
log.info("订阅离线数据数量 -> Topic: {}, message: {}", topic, new String(message));
String data = new String(message, StandardCharsets.UTF_8);
}
}

View File

@@ -4,6 +4,11 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.dromara.mica.mqtt.server.entity.CarInfo;
import java.util.List;
@Mapper
public interface CarInfoMapper extends BaseMapper<CarInfo> {
List<CarInfo> selectCarInfoList(CarInfo car);
}

View File

@@ -0,0 +1,17 @@
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.apache.ibatis.annotations.Select;
import org.dromara.mica.mqtt.server.entity.CarInfo;
import org.dromara.mica.mqtt.server.entity.CarParkItem;
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")
CarParkItem selectBySn(@Param("sn") String sn);
}

View File

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

View File

@@ -1,19 +0,0 @@
package org.dromara.mica.mqtt.server.pojo;
import lombok.Data;
@Data
public class User<T> {
private String name;
private T girlfriend;
public static User newUser(){
User<User> user1 = new User();
user1.setName("name1");
User<User> user2 = new User();
user2.setName("name2");
user2.setGirlfriend(user1);
return user2;
}
}

View File

@@ -0,0 +1,47 @@
package org.dromara.mica.mqtt.server.pojo;
import com.alibaba.fastjson2.JSONObject;
import lombok.Data;
/**
* 设备报文数据实体
*/
@Data
public class WhiteListOperatorPO {
/**
* id
*/
private String id;
/**
* 回执的code
*/
private Integer code;
/**
* 设备编码
*/
private String sn;
/**
* 报文名称
*/
private String name;
/**
* 版本
*/
private String version = "1.0";
/**
* 内容
*/
private JSONObject payload;
/**
* 时间戳(精确到秒)
*/
private Long timestamp = System.currentTimeMillis() / 1000;
}

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.CarInfo;
import java.util.List;
public interface ICarInfoService extends IService<CarInfo> {
CarInfo selectCarInfoBySn(String sn);
List<CarInfo> selectCarInfoList(CarInfo car);
}

View File

@@ -0,0 +1,9 @@
package org.dromara.mica.mqtt.server.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.mica.mqtt.server.entity.CarParkItem;
public interface ICarParkItemService extends IService<CarParkItem> {
CarParkItem selectBySn(String sn);
}

View File

@@ -0,0 +1,11 @@
package org.dromara.mica.mqtt.server.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.mica.mqtt.server.entity.CarParkRecord;
public interface ICarParkRecordService extends IService<CarParkRecord> {
int updateByClientId(CarParkRecord carParkRecord);
void delByClientId(CarParkRecord carParkRecord);
}

View File

@@ -8,6 +8,8 @@ import org.dromara.mica.mqtt.server.service.ICarInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class CarInfoServiceImpl extends ServiceImpl<CarInfoMapper, CarInfo> implements ICarInfoService {
@@ -20,4 +22,9 @@ public class CarInfoServiceImpl extends ServiceImpl<CarInfoMapper, CarInfo> impl
return new CarInfo();
}
@Override
public List<CarInfo> selectCarInfoList(CarInfo car) {
return carInfoMapper.selectCarInfoList(car);
}
}

View File

@@ -0,0 +1,21 @@
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;
import org.dromara.mica.mqtt.server.service.ICarParkItemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CarParkItemServiceImpl extends ServiceImpl<CarParkItemMapper, CarParkItem> implements ICarParkItemService {
@Autowired
CarParkItemMapper carParkItemMapper;
@Override
public CarParkItem selectBySn(String sn) {
return carParkItemMapper.selectBySn(sn);
}
}

View File

@@ -0,0 +1,27 @@
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.CarParkRecord;
import org.dromara.mica.mqtt.server.mapper.CarParkRecordMapper;
import org.dromara.mica.mqtt.server.service.ICarParkRecordService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CarParkRecordServiceImpl extends ServiceImpl<CarParkRecordMapper, CarParkRecord> implements ICarParkRecordService {
@Autowired
CarParkRecordMapper carParkRecordMapper;
@Override
public int updateByClientId(CarParkRecord carParkRecord) {
int id = carParkRecordMapper.update(carParkRecord, new QueryWrapper<CarParkRecord>().eq("client_id", carParkRecord.getClientId()));
return id;
}
@Override
public void delByClientId(CarParkRecord carParkRecord) {
carParkRecordMapper.delete(new QueryWrapper<CarParkRecord>().eq("client_id", carParkRecord.getClientId()));
}
}

View File

@@ -17,8 +17,32 @@ public class ServerService {
private MqttServerTemplate server;
public boolean publish(String body) {
boolean result = server.publishAll("/test/123", body.getBytes(StandardCharsets.UTF_8));
log.info("Mqtt publishAll result:{}", result);
boolean result = server.publish("61e70b04-8e68be6a","device/61e70b04-8e68be6a/message/down/white_list_operator", body.getBytes(StandardCharsets.UTF_8));
log.info("测试body:{},result:{}", body, result);
return result;
}
public boolean open(String body) {
boolean result = server.publish("61e70b04-8e68be6a","device/61e70b04-8e68be6a/message/down/gpio_out", body.getBytes(StandardCharsets.UTF_8));
log.info("IO输出测试body:{},result:{}", body, result);
return result;
}
public boolean open2(String body) {
boolean result = server.publish("61e70b04-8e68be6a","device/61e70b04-8e68be6a/message/down/set_io_lock_status", body.getBytes(StandardCharsets.UTF_8));
log.info("IO锁定测试body:{},result:{}", body, result);
return result;
}
public boolean check_offline_record(String body) {
boolean result = server.publish("61e70b04-8e68be6a","device/61e70b04-8e68be6a/message/down/check_offline_record", body.getBytes(StandardCharsets.UTF_8));
log.info("发布离线记录推送body:{},result:{}", body, result);
return result;
}
public boolean offline_record(String body) {
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;
}
}

View File

@@ -20,7 +20,7 @@ import java.util.List;
*/
@Slf4j
@Service
public class HeartbeatOnLine {
public class HeartbeatOnLineTask {
@Autowired
RedisService redisService;

View File

@@ -0,0 +1,186 @@
package org.dromara.mica.mqtt.server.task;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
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.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.utils.UuidUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.tio.utils.hutool.CollUtil;
import org.tio.utils.hutool.StrUtil;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* 车牌下发设备定时任务
*/
@Slf4j
@Service
public class PlatePublishTask {
@Autowired
private MqttServer mqttServer;
@Autowired
ICarInfoService carInfoService;
@Autowired
ICarParkRecordService carParkRecordService;
@Autowired
RedisService redisService;
/**
* 定时查询数据库需要下发的车辆进行下发
*/
@Scheduled(fixedDelay = 8 * 1000)
public void run() {
String whiteUrl = "device/%s/message/down/white_list_operator";
//新增&编辑车牌
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");
//构造数据
if (StrUtil.equals(car.getSn(), car.getSn())) {
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);
}
}
//删除车牌
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) {
if (StrUtil.equals(car.getSn(), car.getSn())) {
plates.add(car.getPlate());
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);
}
}
}
private JSONObject buildPayload(CarInfo carInfo) {
//新增&更新车牌
if (StrUtil.isNotBlank(carInfo.getSegTime())) {
String[] split = carInfo.getSegTime().split("-");
carInfo.setSegTimeStart(split[0]);
carInfo.setSegTimeEnd(split[1]);
}
JSONObject dldbRec = new JSONObject();
dldbRec.put("create_time", DateUtil.format(new Date(), DatePattern.NORM_DATETIME_PATTERN));
dldbRec.put("enable_time", DateUtil.format(carInfo.getEnableTime(), DatePattern.NORM_DATETIME_PATTERN));
dldbRec.put("overdue_time", DateUtil.format(carInfo.getOverdueTime(), DatePattern.NORM_DATETIME_PATTERN));
dldbRec.put("enable", Integer.parseInt(carInfo.getEnable()));
dldbRec.put("plate", carInfo.getPlate());
dldbRec.put("time_seg_enable", Integer.parseInt(carInfo.getTimeSegEnable()));
dldbRec.put("seg_time_start", carInfo.getSegTimeStart() == null ? "00:00:00" : carInfo.getSegTimeStart());
dldbRec.put("seg_time_end", carInfo.getSegTimeEnd() == null ? "00:00:00" : carInfo.getSegTimeEnd());
dldbRec.put("need_alarm", Integer.parseInt(carInfo.getNeedAlarm()));
JSONObject body = new JSONObject();
body.put("operator_type", "update_or_add");
body.put("dldb_rec", dldbRec);
JSONObject payload = new JSONObject();
payload.put("type", "white_list_operator");
payload.put("body", body);
return payload;
}
private JSONObject buildPayloadDel(List<String> plates) {
JSONObject body = new JSONObject();
body.put("operator_type", "delete");
body.put("plate", plates);
JSONObject payload = new JSONObject();
payload.put("type", "white_list_operator");
payload.put("body", body);
return payload;
}
}

View File

@@ -1,25 +0,0 @@
package org.dromara.mica.mqtt.server.task;
import org.dromara.mica.mqtt.core.server.MqttServer;
import org.dromara.mica.mqtt.server.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.nio.charset.StandardCharsets;
/**
* @author wsq
*/
@Service
public class PublishAllTask {
@Autowired
private MqttServer mqttServer;
// @Scheduled(fixedDelay = 1000)
// public void run() {
// mqttServer.publishAll("/test/123", "mica最牛皮".getBytes(StandardCharsets.UTF_8));
// mqttServer.publishAll("/test/object", User.newUser());
// }
}

View File

@@ -0,0 +1,217 @@
package org.dromara.mica.mqtt.server.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Random;
/**
* AES加密工具类
*/
public class AESUtil {
/**
* 加密Key 需要16位 可用数字与字母组成
*/
private static String key = "1234567898765432";
/**
* 偏移量 需要16位
*/
private static String iv = "4w2Df1xSj5ff662d";
private static Logger log = LoggerFactory.getLogger(AESUtil.class);
private static Base64.Decoder decoder;
private static Base64.Encoder encoder;
static {
decoder = Base64.getDecoder();
encoder = Base64.getEncoder();
}
public static String getSixteenBitString(){
StringBuffer sb = new StringBuffer();
String[] chars = new String[]{
"1","2","3","4","5","6","7","8","9","a","b",
"c","d","e","f","g","h","i","j","k","l","m",
"n","o","p","q","r","s","t","u","v","w","x",
"y","z","A","B","C","D","E","F","G","H","I",
"J","K","L","M","N","O","P","Q","R","S","T",
"U","V","W","X","Y","Z",
};
int len = chars.length;
Random random = new Random();
for (int i = 0; i < 16; i++) {
sb.append(chars[random.nextInt(len-1)]);
}
return sb.toString();
}
/**
* AES加密
* @param data
* @param key
* @param iv
* @return
* @throws Exception
*/
public static String encryptAES_CBC(String data,String key,String iv) {
Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES/CBC/NoPadding");
int blockSize = cipher.getBlockSize();
byte[] dataBytes = data.getBytes();
int dataLength = dataBytes.length;
if (dataLength % blockSize != 0) {
dataLength = dataLength + (blockSize - (dataLength % blockSize));
}
byte[] plaintext = new byte[dataLength];
System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
byte[] bytes = cipher.doFinal(plaintext);
return encoder.encodeToString(bytes);
}catch (Exception e) {
log.error("AES加密失败");
log.error(e.getMessage());
}
return null;
}
/**
* AES解密
* @param data
* @param key
* @param iv
* @return
* @throws Exception
*/
public static String decrptyAES_CBC(String data,String key,String iv){
try {
byte[] bytes = decoder.decode(data);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
byte[] plainByte = cipher.doFinal(bytes);
return new String(plainByte).trim();
}catch (Exception e){
log.error("AES解密失败");
log.error(e.getMessage());
}
return null;
}
/**
* AES加密
* @param data
* @return
* @throws Exception
*/
public static String encryptAES_ECB(String data,String key) throws Exception{
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
int blockSize = cipher.getBlockSize();
byte[] dataBytes = data.getBytes();
int dataLength = dataBytes.length;
if(dataLength % blockSize != 0){
dataLength = dataLength + (blockSize - (dataLength % blockSize));
}
byte [] plaintext = new byte[dataLength];
System.arraycopy(dataBytes,0,plaintext,0,dataBytes.length);
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(),"AES");
cipher.init(Cipher.ENCRYPT_MODE,secretKey);
byte[] bytes = cipher.doFinal(plaintext);
return encoder.encodeToString(bytes);
}
/**
* AES解密
* @param data
* @return
* @throws Exception
*/
public static String decrptyAES_ECB(String data,String key) throws Exception{
// byte[] bytes = decoder.decode(data);
byte[] bytes =Base64.getDecoder().decode(data);
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding");
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(),"AES");
cipher.init(Cipher.DECRYPT_MODE,secretKey);
byte[] plainByte = cipher.doFinal(bytes);
return bytesToHex(plainByte);
}
private static String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
/**
* gb2312编码
*/
public static String gb2312decode( String string) throws UnsupportedEncodingException{
byte[] bytes = new byte[string.length() / 2];
for(int i = 0; i < bytes.length; i ++){
byte high = Byte.parseByte(string.substring(i * 2, i * 2 + 1), 16);
byte low = Byte.parseByte(string.substring(i * 2 + 1, i * 2 + 2), 16);
bytes[i] = (byte) (high << 4 | low);
}
return new String(bytes, "gb2312");
}
/**
* UTF8编码
*/
public static String UTF8decode( String string) throws UnsupportedEncodingException{
byte[] bytes = new byte[string.length() / 2];
for(int i = 0; i < bytes.length; i ++){
byte high = Byte.parseByte(string.substring(i * 2, i * 2 + 1), 16);
byte low = Byte.parseByte(string.substring(i * 2 + 1, i * 2 + 2), 16);
bytes[i] = (byte) (high << 4 | low);
}
return new String(bytes, StandardCharsets.UTF_8);
}
public static void main(String[] args) throws Exception{
String encrypt = "IcDSUR8fdtJ8gLYlZX9qLw==";
String str = decrptyAES_ECB(encrypt, key);
// String str1 = str.substring(0, 16);
String license = AESUtil.UTF8decode(str);
// System.out.println(gb2312decode(str1));
System.out.println(license);
}
}

View File

@@ -0,0 +1,40 @@
package org.dromara.mica.mqtt.server.utils;
import java.util.UUID;
public class UuidUtil {
public static String[] chars = new String[] { "a", "b", "c", "d", "e", "f",
"g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
"t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I",
"J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
"W", "X", "Y", "Z" };
/**
* 获取短UUID
* @return
*/
public static String getShortUuid() {
StringBuffer shortBuffer = new StringBuffer();
String uuid = UuidUtil.getUuid();
for (int i = 0; i < 8; i++) {
String str = uuid.substring(i * 4, i * 4 + 4);
int x = Integer.parseInt(str, 16);
shortBuffer.append(chars[x % 0x3E]); // 对62取余
}
return shortBuffer.toString();
}
/**
* 获得32位UUID
*/
public static String getUuid(){
String uuid = UUID.randomUUID().toString();
//去掉“-”符号
return uuid.replaceAll("-", "");
}
}

View File

@@ -1,7 +1,7 @@
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/xa_cloud?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
url: jdbc:mysql://127.0.0.1:3306/xa_cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: root
data:

View File

@@ -19,6 +19,8 @@
<result property="delFlag" column="del_flag" />
<result property="sync" column="sync" />
<result property="overclockCard" column="overclock_card" />
<result property="sn" column="sequence" />
<result property="carParkRecordId" column="carParkRecordId" />
</resultMap>
<sql id="selectCarInfoVo">
@@ -33,7 +35,33 @@
</sql>
<select id="selectCarInfoList" parameterType="org.dromara.mica.mqtt.server.entity.CarInfo" resultMap="CarInfoResult">
<include refid="selectCarInfoVo"/>
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_by,
ci.create_time,
ci.update_by,
ci.update_time,
ci.overclock_card,
se.sequence,
cpr.id as carParkRecordId
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 sys_equipment se ON cpr.equipment_id = se.id
<where>
<if test="enableTime != null "> and ci.enable_time = #{enableTime}</if>
<if test="overdueTime != null "> and ci.overdue_time = #{overdueTime}</if>