6075 mqtt

This commit is contained in:
2025-11-05 17:55:59 +08:00
parent c4957aa3aa
commit 1dc3386284
19 changed files with 313 additions and 22 deletions

View File

@ -72,10 +72,8 @@ public class AppAuthController {
private final AppLoginService loginService; private final AppLoginService loginService;
private final AppRegisterService registerService; private final AppRegisterService registerService;
private final ISysConfigService configService;
private final ISysTenantService tenantService; private final ISysTenantService tenantService;
private final ISysClientService clientService; private final ISysClientService clientService;
private final ISysDictTypeService dictTypeService;
/** /**

View File

@ -92,4 +92,5 @@ public class AppDeviceController extends BaseController {
public R<AppDeviceVo> getDeviceInfo(String deviceMac) { public R<AppDeviceVo> getDeviceInfo(String deviceMac) {
return R.ok(appDeviceService.getDeviceInfo(deviceMac)); return R.ok(appDeviceService.getDeviceInfo(deviceMac));
} }
} }

View File

@ -44,10 +44,9 @@ import static com.fuyuanshen.common.core.constant.GlobalConstants.DEVICE_SHARE_C
@RequestMapping("/app/deviceShare") @RequestMapping("/app/deviceShare")
public class AppDeviceShareController extends BaseController { public class AppDeviceShareController extends BaseController {
private final IAppDeviceShareService deviceShareService;
private final AppDeviceShareService appDeviceShareService; private final AppDeviceShareService appDeviceShareService;
/** /**
* 分享管理列表 * 分享管理列表
*/ */
@ -95,6 +94,7 @@ public class AppDeviceShareController extends BaseController {
return toAjax(appDeviceShareService.remove(ids)); return toAjax(appDeviceShareService.remove(ids));
} }
/** /**
* 短信验证码 * 短信验证码
* *
@ -116,4 +116,5 @@ public class AppDeviceShareController extends BaseController {
} }
return R.ok(); return R.ok();
} }
} }

View File

@ -25,6 +25,7 @@ public class AppFileController extends BaseController {
private final AppFileService appFileService; private final AppFileService appFileService;
/** /**
* 查询文件列表 * 查询文件列表
*/ */
@ -33,6 +34,7 @@ public class AppFileController extends BaseController {
return R.ok(appFileService.list(bo)); return R.ok(appFileService.list(bo));
} }
/** /**
* 上传文件 * 上传文件
*/ */
@ -52,4 +54,5 @@ public class AppFileController extends BaseController {
} }
return toAjax(appFileService.delete(ids)); return toAjax(appFileService.delete(ids));
} }
} }

View File

@ -25,6 +25,7 @@ public class AppOperationVideoController extends BaseController {
private final IAppOperationVideoService appOperationVideoService; private final IAppOperationVideoService appOperationVideoService;
/** /**
* 查询操作视频列表 * 查询操作视频列表
*/ */
@ -68,4 +69,5 @@ public class AppOperationVideoController extends BaseController {
public R<Void> deleteOperationVideo(@PathVariable Long id) { public R<Void> deleteOperationVideo(@PathVariable Long id) {
return toAjax(appOperationVideoService.deleteWithValidByIds(List.of(id), true)); return toAjax(appOperationVideoService.deleteWithValidByIds(List.of(id), true));
} }
} }

View File

@ -0,0 +1,277 @@
package com.fuyuanshen.global.mqtt.receiver;
import cn.hutool.core.lang.Dict;
import com.fuyuanshen.common.core.constant.GlobalConstants;
import com.fuyuanshen.common.core.utils.ImageToCArrayConverter;
import com.fuyuanshen.common.json.utils.JsonUtils;
import com.fuyuanshen.common.redis.utils.RedisUtils;
import com.fuyuanshen.global.mqtt.base.MqttRuleContext;
import com.fuyuanshen.global.mqtt.base.MqttRuleEngine;
import com.fuyuanshen.global.mqtt.base.MqttXinghanCommandType;
import com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants;
import com.fuyuanshen.global.queue.MqttMessageQueueConstants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.MessagingException;
import org.springframework.stereotype.Service;
import java.time.Duration;
import java.util.Objects;
import static com.fuyuanshen.global.mqtt.constants.DeviceRedisKeyConstants.DEVICE_KEY_PREFIX;
/**
* @author: 默苍璃
* @date: 2025-11-05 17:41
*/
@Service
@Slf4j
public class DeviceMessageHandler implements MessageHandler {
@Autowired
private MqttRuleEngine ruleEngine;
@Override
public void handleMessage(Message<?> message) throws MessagingException {
Object payload = message.getPayload();
MessageHeaders headers = message.getHeaders();
String receivedTopic = Objects.requireNonNull(headers.get("mqtt_receivedTopic")).toString();
String receivedQos = Objects.requireNonNull(headers.get("mqtt_receivedQos")).toString();
String timestamp = Objects.requireNonNull(headers.get("timestamp")).toString();
log.info("MQTT payload= {} \n receivedTopic = {} \n receivedQos = {} \n timestamp = {}",
payload, receivedTopic, receivedQos, timestamp);
Dict payloadDict = JsonUtils.parseMap(payload.toString());
if (receivedTopic == null || payloadDict == null) {
return;
}
// 解析设备IMEI
String[] subStr = receivedTopic.split("/");
String deviceImei = subStr[1];
// 处理设备在线状态
handleDeviceOnlineStatus(deviceImei);
// 处理不同类型的设备信息
processDeviceInformation(payloadDict, deviceImei);
// 执行规则引擎处理
executeRuleEngine(payloadDict, deviceImei);
}
/**
* 处理设备在线状态
*/
private void handleDeviceOnlineStatus(String deviceImei) {
if (StringUtils.isNotBlank(deviceImei)) {
// 添加去重队列
String queueKey = MqttMessageQueueConstants.MQTT_MESSAGE_QUEUE_KEY;
String dedupKey = MqttMessageQueueConstants.MQTT_MESSAGE_DEDUP_KEY;
RedisUtils.offerDeduplicated(queueKey, dedupKey, deviceImei, Duration.ofSeconds(900));
// 设置设备在线状态
String deviceOnlineStatusRedisKey = GlobalConstants.GLOBAL_REDIS_KEY +
DEVICE_KEY_PREFIX + deviceImei + DeviceRedisKeyConstants.DEVICE_ONLINE_STATUS_KEY_PREFIX;
RedisUtils.setCacheObject(deviceOnlineStatusRedisKey, "1", Duration.ofSeconds(360));
}
}
/**
* 处理不同类型的设备信息
*/
private void processDeviceInformation(Dict payloadDict, String deviceImei) {
// 开机画面
if (payloadDict.containsKey("bootScreen")) {
handleBootScreen(payloadDict, deviceImei);
}
// 人员信息
if (payloadDict.containsKey("personInfo")) {
handlePersonInfo(payloadDict, deviceImei);
}
// 设备信息
if (payloadDict.containsKey("deviceInfo")) {
handleDeviceInfo(payloadDict, deviceImei);
}
// 经纬度
if (payloadDict.containsKey("latitude") && payloadDict.containsKey("longitude")) {
handleLocation(payloadDict, deviceImei);
}
// 电子地图
if (payloadDict.containsKey("mapData")) {
handleMapData(payloadDict, deviceImei);
}
// 电池电量
if (payloadDict.containsKey("batteryLevel")) {
handleBatteryLevel(payloadDict, deviceImei);
}
// 开启/关闭状态
if (payloadDict.containsKey("powerState")) {
handlePowerState(payloadDict, deviceImei);
}
// 海拔高度
if (payloadDict.containsKey("altitude")) {
handleAltitude(payloadDict, deviceImei);
}
// 相对高度
if (payloadDict.containsKey("relativeHeight")) {
handleRelativeHeight(payloadDict, deviceImei);
}
// 群呼/单呼
if (payloadDict.containsKey("callType")) {
handleCallType(payloadDict, deviceImei);
}
// 文字信息
if (payloadDict.containsKey("textMessage")) {
handleTextMessage(payloadDict, deviceImei);
}
}
/**
* 处理开机画面
*/
private void handleBootScreen(Dict payloadDict, String deviceImei) {
log.info("处理设备{}的开机画面信息", deviceImei);
// 实现具体的开机画面处理逻辑
}
/**
* 处理人员信息
*/
private void handlePersonInfo(Dict payloadDict, String deviceImei) {
log.info("处理设备{}的人员信息", deviceImei);
// 实现具体的人员信息处理逻辑
}
/**
* 处理设备信息
*/
private void handleDeviceInfo(Dict payloadDict, String deviceImei) {
log.info("处理设备{}的设备信息", deviceImei);
// 实现具体的设备信息处理逻辑
}
/**
* 处理位置信息
*/
private void handleLocation(Dict payloadDict, String deviceImei) {
log.info("处理设备{}的位置信息: 纬度={}, 经度={}",
deviceImei, payloadDict.getStr("latitude"), payloadDict.getStr("longitude"));
// 实现具体的位置信息处理逻辑
}
/**
* 处理电子地图数据
*/
private void handleMapData(Dict payloadDict, String deviceImei) {
log.info("处理设备{}的电子地图数据", deviceImei);
// 实现具体的电子地图数据处理逻辑
}
/**
* 处理电池电量
*/
private void handleBatteryLevel(Dict payloadDict, String deviceImei) {
log.info("处理设备{}的电池电量: {}", deviceImei, payloadDict.getStr("batteryLevel"));
// 实现具体的电池电量处理逻辑
}
/**
* 处理开关状态
*/
private void handlePowerState(Dict payloadDict, String deviceImei) {
String powerState = payloadDict.getStr("powerState");
log.info("处理设备{}的开关状态: {}", deviceImei, powerState);
// 实现具体的开关状态处理逻辑
}
/**
* 处理海拔高度
*/
private void handleAltitude(Dict payloadDict, String deviceImei) {
log.info("处理设备{}的海拔高度: {}", deviceImei, payloadDict.getStr("altitude"));
// 实现具体的海拔高度处理逻辑
}
/**
* 处理相对高度
*/
private void handleRelativeHeight(Dict payloadDict, String deviceImei) {
log.info("处理设备{}的相对高度: {}", deviceImei, payloadDict.getStr("relativeHeight"));
// 实现具体的相对高度处理逻辑
}
/**
* 处理呼叫类型
*/
private void handleCallType(Dict payloadDict, String deviceImei) {
String callType = payloadDict.getStr("callType");
log.info("处理设备{}的呼叫类型: {}", deviceImei, callType);
// 实现具体的呼叫类型处理逻辑
}
/**
* 处理文字信息
*/
private void handleTextMessage(Dict payloadDict, String deviceImei) {
String textMessage = payloadDict.getStr("textMessage");
log.info("处理设备{}的文字信息: {}", deviceImei, textMessage);
// 实现具体的文字信息处理逻辑
}
/**
* 执行规则引擎处理
*/
private void executeRuleEngine(Dict payloadDict, String deviceImei) {
String state = payloadDict.getStr("state");
Object[] convertArr = ImageToCArrayConverter.convertByteStringToMixedObjectArray(state);
if (convertArr.length > 0) {
Byte val1 = (Byte) convertArr[0];
MqttRuleContext context = new MqttRuleContext();
context.setCommandType(val1);
context.setConvertArr(convertArr);
context.setDeviceImei(deviceImei);
context.setPayloadDict(payloadDict);
boolean ruleExecuted = ruleEngine.executeRule(context);
if (!ruleExecuted) {
log.warn("未找到匹配的规则来处理命令类型: {}", val1);
}
}
/* ===== 追加:根据报文内容识别格式并统一解析 ===== */
int intType = MqttXinghanCommandType.computeVirtualCommandType(payloadDict);
if (intType > 0) {
MqttRuleContext newCtx = new MqttRuleContext();
newCtx.setCommandType((byte) intType);
newCtx.setDeviceImei(deviceImei);
newCtx.setPayloadDict(payloadDict);
boolean ok = ruleEngine.executeRule(newCtx);
if (!ok) {
log.warn("新规则引擎未命中, imei={}", deviceImei);
}
}
}
}

View File

@ -90,4 +90,5 @@ public class ReceiverMessageHandler implements MessageHandler {
} }
} }
} }
} }

View File

@ -63,10 +63,6 @@ import java.util.Map;
@RequestMapping("/mp") @RequestMapping("/mp")
public class MPAuthController { public class MPAuthController {
private final AppLoginService loginService;
private final SysRegisterService registerService;
private final ISysConfigService configService;
private final ISysTenantService tenantService;
private final ISysClientService clientService; private final ISysClientService clientService;
private final MPAuthService mpAuthService; private final MPAuthService mpAuthService;
private final MPService mpService; private final MPService mpService;
@ -74,7 +70,7 @@ public class MPAuthController {
@Operation(summary = "小程序登录授权") @Operation(summary = "小程序登录授权")
@PostMapping(value = "/login") @PostMapping(value = "/login")
public ResponseEntity<Object> login(@RequestBody AuthUserDto authUser, HttpServletRequest request) throws Exception { public ResponseEntity<Object> login(@RequestBody AuthUserDto authUser) throws Exception {
Long phoneNumber = authUser.getPhoneNumber(); Long phoneNumber = authUser.getPhoneNumber();
// 判断小程序用户是否存在,不存在创建 // 判断小程序用户是否存在,不存在创建

View File

@ -60,9 +60,9 @@ import java.util.function.Supplier;
@Service @Service
public class MPAuthService { public class MPAuthService {
private final ISysUserService userService;
private final AppUserService appUserService; private final AppUserService appUserService;
/** /**
* 小程序注册 * 小程序注册
*/ */
@ -128,6 +128,7 @@ public class MPAuthService {
return loginVo; return loginVo;
} }
/** /**
* 构建登录用户 * 构建登录用户
*/ */
@ -160,5 +161,4 @@ public class MPAuthService {
} }
} }

View File

@ -10,11 +10,8 @@ import org.springframework.stereotype.Service;
* *
* @author Lion Li * @author Lion Li
*/ */
public interface MPService { public interface MPService {
/** /**
* 获取小程序用户信息 * 获取小程序用户信息
* *
@ -23,4 +20,5 @@ public interface MPService {
UserApp getMpUser(Long phoneNumber); UserApp getMpUser(Long phoneNumber);
UserApp loadUserByUsername(String username); UserApp loadUserByUsername(String username);
} }

View File

@ -8,5 +8,4 @@ package com.fuyuanshen.mp.service.impl;
public class MPAuthServiceImpl { public class MPAuthServiceImpl {
} }

View File

@ -19,11 +19,9 @@ import org.springframework.stereotype.Service;
@RequiredArgsConstructor @RequiredArgsConstructor
public class MPServiceImpl implements MPService { public class MPServiceImpl implements MPService {
private final AppUserService appUserService; private final AppUserService appUserService;
/** /**
* 获取小程序用户信息 * 获取小程序用户信息
* *

View File

@ -51,6 +51,7 @@ public class CaptchaController {
private final CaptchaProperties captchaProperties; private final CaptchaProperties captchaProperties;
private final MailProperties mailProperties; private final MailProperties mailProperties;
/** /**
* 短信验证码 * 短信验证码
* *

View File

@ -160,4 +160,5 @@ public class DeviceXinghanController extends BaseController {
RedisUtils.setCacheObject(versionKey, json, Duration.ofDays(30)); RedisUtils.setCacheObject(versionKey, json, Duration.ofDays(30));
return R.ok(); return R.ok();
} }
} }

View File

@ -279,11 +279,11 @@ justauth:
# MQTT配置 # MQTT配置
mqtt: mqtt:
username: admin username: admin
password: #YtvpSfCNG password: fys123456
url: tcp://www.cnxhyc.com:2883 url: tcp://47.107.152.87:1883
subClientId: fys_subClient subClientId: fys_subClient
subTopic: A/# subTopic: A/#,status/tenantCode/#,report/tenantCode/#
pubTopic: B/# pubTopic: B/#,command/tenantCode/#
pubClientId: fys_pubClient pubClientId: fys_pubClient

View File

@ -0,0 +1,10 @@
{
"requestId": "123456789",
"imei": "864865082081523",
"timestamp": 1761025080000,
"funcType": "1",
"data": {
"mainLightMode": 1,
"mainLightBrightness": 100
}
}

View File

@ -12,6 +12,7 @@ import lombok.EqualsAndHashCode;
*/ */
@Data @Data
public class AppDeviceBo { public class AppDeviceBo {
/** /**
* 设备IMEI * 设备IMEI
*/ */
@ -25,10 +26,11 @@ public class AppDeviceBo {
/** /**
* 通讯方式 0:4G;1:蓝牙,2 4G&蓝牙 * 通讯方式 0:4G;1:蓝牙,2 4G&蓝牙
*/ */
@NotNull(message = "通讯方式不能为空", groups = { EditGroup.class }) @NotNull(message = "通讯方式不能为空", groups = {EditGroup.class})
private Integer communicationMode; private Integer communicationMode;
private String sendMsg; private String sendMsg;
private Long deviceId; private Long deviceId;
} }

View File

@ -86,4 +86,5 @@ public class AppDeviceVo implements Serializable {
* 设备详情页面 * 设备详情页面
*/ */
private String detailPageUrl; private String detailPageUrl;
} }

View File

@ -227,6 +227,7 @@
FROM device FROM device
WHERE original_device_id = #{originalDeviceId} WHERE original_device_id = #{originalDeviceId}
</select> </select>
<select id="getDeviceInfo" resultType="com.fuyuanshen.equipment.domain.vo.AppDeviceVo"> <select id="getDeviceInfo" resultType="com.fuyuanshen.equipment.domain.vo.AppDeviceVo">
select d.id, select d.id,
d.device_name, d.device_name,
@ -244,6 +245,7 @@
inner join device_type dt on d.device_type = dt.id inner join device_type dt on d.device_type = dt.id
where d.device_mac = #{deviceMac} where d.device_mac = #{deviceMac}
</select> </select>
<select id="queryWebDeviceList" resultType="com.fuyuanshen.equipment.domain.vo.WebDeviceVo"> <select id="queryWebDeviceList" resultType="com.fuyuanshen.equipment.domain.vo.WebDeviceVo">
select * from (select d.id, d.device_name, select * from (select d.id, d.device_name,
d.device_mac, d.device_mac,