diff --git a/pjcloud-common/pjcloud-common-core/src/main/java/com/bsd/pjcloud/common/core/constant/ServiceNameConstants.java b/pjcloud-common/pjcloud-common-core/src/main/java/com/bsd/pjcloud/common/core/constant/ServiceNameConstants.java index 80dad6595..fe01e2747 100644 --- a/pjcloud-common/pjcloud-common-core/src/main/java/com/bsd/pjcloud/common/core/constant/ServiceNameConstants.java +++ b/pjcloud-common/pjcloud-common-core/src/main/java/com/bsd/pjcloud/common/core/constant/ServiceNameConstants.java @@ -86,4 +86,9 @@ public interface ServiceNameConstants { * 硬件模块 */ String HARDWARE_SERVICE = "pjcloud-hardware-biz"; + + /** + * mqtt模块 + */ + String MQTT_SERVICE = "pjcloud-mqtt-biz"; } diff --git a/pjcloud-hardware/pjcloud-hardware-biz/pom.xml b/pjcloud-hardware/pjcloud-hardware-biz/pom.xml index ee6079f26..0b5789b9e 100644 --- a/pjcloud-hardware/pjcloud-hardware-biz/pom.xml +++ b/pjcloud-hardware/pjcloud-hardware-biz/pom.xml @@ -127,6 +127,12 @@ 2.1.0 compile + + com.bsd + pjcloud-mqtt-api + 5.7.0 + compile + diff --git a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/common/config/MqttConfig.java b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/mqtt/config/MqttConfig.java similarity index 93% rename from pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/common/config/MqttConfig.java rename to pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/mqtt/config/MqttConfig.java index 802bb6e79..b96045c5a 100644 --- a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/common/config/MqttConfig.java +++ b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/mqtt/config/MqttConfig.java @@ -1,4 +1,4 @@ -package com.bsd.pjcloud.hard.common.config; +package com.bsd.pjcloud.hard.mqtt.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; diff --git a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/HardManagerServiceImpl.java b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/HardManagerServiceImpl.java index a9857db11..d5019e0be 100644 --- a/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/HardManagerServiceImpl.java +++ b/pjcloud-hardware/pjcloud-hardware-biz/src/main/java/com/bsd/pjcloud/hard/service/impl/HardManagerServiceImpl.java @@ -16,8 +16,9 @@ import com.bsd.pjcloud.hard.api.entity.HardManagerEntity; import com.bsd.pjcloud.hard.api.vo.HardManagerVo; import com.bsd.pjcloud.hard.mapper.HardManagerMapper; import com.bsd.pjcloud.hard.service.HardManagerService; -import com.bsd.pjcloud.hard.common.config.MqttConfig; +import com.bsd.pjcloud.hard.mqtt.config.MqttConfig; import com.bsd.pjcloud.hard.utils.MqTopicUtil; +import com.bsd.pjcloud.mqtt.api.feign.RemoteMqttEventService; import com.bsd.pjcloud.wallet.api.feign.RemoteMchService; import com.bsd.pjcloud.wallet.api.utils.WalletDataUtil; import com.bsd.pjcloud.wallet.api.vo.MchManageVo; @@ -46,6 +47,7 @@ public class HardManagerServiceImpl extends ServiceImpl pageJoin(Page page, HardManagerDto dto) { @@ -99,6 +101,8 @@ public class HardManagerServiceImpl extends ServiceImpl + + 4.0.0 + + com.bsd + pjcloud-mqtt + 5.7.0 + + + pjcloud-mqtt-api + + + + + com.github.yulichang + mybatis-plus-join-annotation + + + + com.bsd + pjcloud-common-core + + + + com.baomidou + mybatis-plus-spring + + + + com.bsd + pjcloud-common-feign + + + + com.bsd + pjcloud-common-excel + + + diff --git a/pjcloud-mqtt/pjcloud-mqtt-api/src/main/java/com/bsd/pjcloud/mqtt/api/feign/RemoteMqttEventService.java b/pjcloud-mqtt/pjcloud-mqtt-api/src/main/java/com/bsd/pjcloud/mqtt/api/feign/RemoteMqttEventService.java new file mode 100644 index 000000000..ca77a61d1 --- /dev/null +++ b/pjcloud-mqtt/pjcloud-mqtt-api/src/main/java/com/bsd/pjcloud/mqtt/api/feign/RemoteMqttEventService.java @@ -0,0 +1,62 @@ +package com.bsd.pjcloud.mqtt.api.feign; + +import com.bsd.pjcloud.common.core.constant.ServiceNameConstants; +import com.bsd.pjcloud.common.core.util.R; +import com.bsd.pjcloud.common.feign.annotation.NoToken; +import com.bsd.pjcloud.mqtt.dto.MqttBaseDto; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; + +@FeignClient(contextId = "remoteMqttEventService", value = ServiceNameConstants.MQTT_SERVICE) +public interface RemoteMqttEventService { + + @NoToken + @GetMapping("/mqtt/send-default-msg") + R sendDefaultMsg(@RequestBody MqttBaseDto dto); + + /** + * 设置变化 + */ + @NoToken + @GetMapping("/mqtt/send-config-change-msg") + R sendConfigChangeMsg(@RequestParam Long mchDeptId); + + /** + * 食堂商品变化 + */ + @NoToken + @GetMapping("/mqtt/send-canteen-product-change-msg") + R sendCanteenProductChangeMsg(@RequestParam Long mchDeptId); + + /** + * 食堂分类变化 + */ + @NoToken + @GetMapping("/mqtt/send-canteen-classify-change-msg") + R sendCanteenClassifyChangeMsg(@RequestParam Long mchDeptId); + + /** + * 超市商品变化 + */ + @NoToken + @GetMapping("/mqtt/send-supermarket-product-change-msg") + R sendSupermarketProductChangeMsg(@RequestParam Long mchDeptId); + + /** + * 超市分类变化 + */ + @NoToken + @GetMapping("/mqtt/send-supermarket-classify-change-msg") + R sendSupermarketClassifyChangeMsg(@RequestParam Long mchDeptId); + + /** + * 人脸变化 + */ + @NoToken + @GetMapping("/mqtt/send-face-change-msg") + R sendFaceChangeMsg(@RequestParam Long mchDeptId); +} diff --git a/pjcloud-mqtt/pjcloud-mqtt-api/src/main/java/com/bsd/pjcloud/mqtt/constant/MqttActionEnum.java b/pjcloud-mqtt/pjcloud-mqtt-api/src/main/java/com/bsd/pjcloud/mqtt/constant/MqttActionEnum.java new file mode 100644 index 000000000..1d394e21a --- /dev/null +++ b/pjcloud-mqtt/pjcloud-mqtt-api/src/main/java/com/bsd/pjcloud/mqtt/constant/MqttActionEnum.java @@ -0,0 +1,21 @@ +package com.bsd.pjcloud.mqtt.constant; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * mq action + */ +@AllArgsConstructor +@Getter +public enum MqttActionEnum { + CONFIG("设置变化"), + CANTEEN_PRODUCT("食堂商品变化"), + CANTEEN_CLASSIFY("食堂分类变化"), + SUPERMARKET_PRODUCT("超市商品变化"), + SUPERMARKET_CLASSIFY("超市分类变化"), + PRINT("打印变化"), + FACE("人脸变化"), + ; + private final String description; +} \ No newline at end of file diff --git a/pjcloud-mqtt/pjcloud-mqtt-api/src/main/java/com/bsd/pjcloud/mqtt/dto/MqttBaseDto.java b/pjcloud-mqtt/pjcloud-mqtt-api/src/main/java/com/bsd/pjcloud/mqtt/dto/MqttBaseDto.java new file mode 100644 index 000000000..f1dc10c1a --- /dev/null +++ b/pjcloud-mqtt/pjcloud-mqtt-api/src/main/java/com/bsd/pjcloud/mqtt/dto/MqttBaseDto.java @@ -0,0 +1,35 @@ +package com.bsd.pjcloud.mqtt.dto; + +import cn.hutool.core.util.ObjectUtil; +import com.bsd.pjcloud.mqtt.constant.MqttActionEnum; +import lombok.Data; + +@Data +public class MqttBaseDto { + // 商户id + private Long merchantDeptId; + + // 硬件id + private Long hardwareId; + + private MqttActionEnum action; + + private Integer qos = 1; + + private T data; + + public Boolean isExistMchDeptId() { + return ObjectUtil.isNotEmpty(merchantDeptId); + } + + public String getTopic() { + if (ObjectUtil.isAllNotEmpty(merchantDeptId, hardwareId)) { + return String.format("topic/android/%s/%s", merchantDeptId, hardwareId); + } + if (ObjectUtil.isAllNotEmpty(merchantDeptId)) { + return String.format("topic/android/%s", merchantDeptId); + } + return "topic/android"; + } + +} diff --git a/pjcloud-mqtt/pjcloud-mqtt-api/src/main/java/com/bsd/pjcloud/mqtt/dto/MqttHardBaseMsg.java b/pjcloud-mqtt/pjcloud-mqtt-api/src/main/java/com/bsd/pjcloud/mqtt/dto/MqttHardBaseMsg.java new file mode 100644 index 000000000..a37dd5875 --- /dev/null +++ b/pjcloud-mqtt/pjcloud-mqtt-api/src/main/java/com/bsd/pjcloud/mqtt/dto/MqttHardBaseMsg.java @@ -0,0 +1,14 @@ +package com.bsd.pjcloud.mqtt.dto; + +import com.bsd.pjcloud.mqtt.constant.MqttActionEnum; +import lombok.Data; + +/** + * 向硬件发送消息 + */ +@Data +public class MqttHardBaseMsg { + + private MqttActionEnum action; + +} diff --git a/pjcloud-mqtt/pjcloud-mqtt-api/src/main/resources/META-INF/spring/org.springframework.cloud.openfeign.FeignClient.imports b/pjcloud-mqtt/pjcloud-mqtt-api/src/main/resources/META-INF/spring/org.springframework.cloud.openfeign.FeignClient.imports new file mode 100644 index 000000000..4ac0b5c6a --- /dev/null +++ b/pjcloud-mqtt/pjcloud-mqtt-api/src/main/resources/META-INF/spring/org.springframework.cloud.openfeign.FeignClient.imports @@ -0,0 +1 @@ +com.bsd.pjcloud.mqtt.api.feign.RemoteMqttEventService \ No newline at end of file diff --git a/pjcloud-mqtt/pjcloud-mqtt-biz/pom.xml b/pjcloud-mqtt/pjcloud-mqtt-biz/pom.xml new file mode 100644 index 000000000..126dc1db5 --- /dev/null +++ b/pjcloud-mqtt/pjcloud-mqtt-biz/pom.xml @@ -0,0 +1,119 @@ + + + 4.0.0 + + com.bsd + pjcloud-mqtt + 5.7.0 + + + pjcloud-mqtt-biz + + + + + org.springframework.integration + spring-integration-mqtt + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + com.bsd + pjcloud-common-data + + + + com.bsd + pjcloud-common-security + + + + com.bsd + pjcloud-common-xss + + + + com.bsd + pjcloud-common-sentinel + + + + com.bsd + pjcloud-common-feign + + + + com.bsd + pjcloud-mqtt-api + 5.7.0 + + + + com.bsd + pjcloud-common-log + + + + org.springframework.boot + spring-boot-starter-undertow + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + + + + + cloud + + true + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + CLASSIC + + + + + + io.fabric8 + docker-maven-plugin + + false + + + + + + + boot + + + diff --git a/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/MqttApp.java b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/MqttApp.java new file mode 100644 index 000000000..1ca7a5a41 --- /dev/null +++ b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/MqttApp.java @@ -0,0 +1,17 @@ +package com.bsd.pjcloud.mqtt; + +import com.bsd.pjcloud.common.feign.annotation.EnablePjcloudFeignClients; +import com.bsd.pjcloud.common.security.annotation.EnablePjcloudResourceServer; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; + +@EnablePjcloudFeignClients +@EnableDiscoveryClient +@EnablePjcloudResourceServer +@SpringBootApplication +public class MqttApp { + public static void main(String[] args) { + SpringApplication.run(MqttApp.class, args); + } +} diff --git a/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/config/MqttConfig.java b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/config/MqttConfig.java new file mode 100644 index 000000000..3471b9b6c --- /dev/null +++ b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/config/MqttConfig.java @@ -0,0 +1,43 @@ +package com.bsd.pjcloud.mqtt.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Data +@ConfigurationProperties(prefix = "mqtt") +@Component +public class MqttConfig { + /** + * 是否使用本地配置文件 + */ + private Boolean isLocal = false; + /** + * 证书地址 + */ + private String crtPath; + /** + * 用户名 + */ + private String username; + /** + * 密码 + */ + private String password; + /** + * 连接地址 + */ + private String hostUrl; + /** + * clientId + */ + private String clientId; + /** + * 安卓连接地址 + */ + private String androidAddress; + /** + * 安卓连接端口 + */ + private String androidPort; +} diff --git a/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/controller/MqttEventController.java b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/controller/MqttEventController.java new file mode 100644 index 000000000..fbdccc096 --- /dev/null +++ b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/controller/MqttEventController.java @@ -0,0 +1,101 @@ +package com.bsd.pjcloud.mqtt.controller; + +import com.bsd.pjcloud.common.core.util.R; +import com.bsd.pjcloud.common.security.annotation.Inner; +import com.bsd.pjcloud.mqtt.constant.MqttActionEnum; +import com.bsd.pjcloud.mqtt.dto.MqttBaseDto; +import com.bsd.pjcloud.mqtt.service.MqttEventService; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpHeaders; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/mqtt") +@Tag(description = "hard-invoice", name = "发票记录管理") +@SecurityRequirement(name = HttpHeaders.AUTHORIZATION) +public class MqttEventController { + + private final MqttEventService mqttEventService; + + @Inner + @PostMapping("/send-default-msg") + public R sendDefaultMsg(@RequestBody MqttBaseDto dto) { + return R.ok(mqttEventService.defaultAction(dto)); + } + + /** + * 设置变化 + */ + @Inner + @GetMapping("/send-config-change-msg") + public R sendConfigChangeMsg(@RequestParam Long mchDeptId) { + MqttBaseDto dto = new MqttBaseDto<>(); + dto.setAction(MqttActionEnum.CONFIG); + dto.setMerchantDeptId(mchDeptId); + return R.ok(mqttEventService.defaultAction(dto)); + } + + /** + * 食堂商品变化 + */ + @Inner + @GetMapping("/send-canteen-product-change-msg") + public R sendCanteenProductChangeMsg(@RequestParam Long mchDeptId) { + MqttBaseDto dto = new MqttBaseDto<>(); + dto.setMerchantDeptId(mchDeptId); + dto.setAction(MqttActionEnum.CANTEEN_PRODUCT); + return R.ok(mqttEventService.defaultAction(dto)); + } + + /** + * 食堂分类变化 + */ + @Inner + @GetMapping("/send-canteen-classify-change-msg") + public R sendCanteenClassifyChangeMsg(@RequestParam Long mchDeptId) { + MqttBaseDto dto = new MqttBaseDto<>(); + dto.setMerchantDeptId(mchDeptId); + dto.setAction(MqttActionEnum.CANTEEN_CLASSIFY); + return R.ok(mqttEventService.defaultAction(dto)); + } + + /** + * 超市商品变化 + */ + @Inner + @GetMapping("/send-supermarket-product-change-msg") + public R sendSupermarketProductChangeMsg(@RequestParam Long mchDeptId) { + MqttBaseDto dto = new MqttBaseDto<>(); + dto.setMerchantDeptId(mchDeptId); + dto.setAction(MqttActionEnum.SUPERMARKET_PRODUCT); + return R.ok(mqttEventService.defaultAction(dto)); + } + + /** + * 超市分类变化 + */ + @Inner + @GetMapping("/send-supermarket-classify-change-msg") + public R sendSupermarketClassifyChangeMsg(@RequestParam Long mchDeptId) { + MqttBaseDto dto = new MqttBaseDto<>(); + dto.setMerchantDeptId(mchDeptId); + dto.setAction(MqttActionEnum.SUPERMARKET_CLASSIFY); + return R.ok(mqttEventService.defaultAction(dto)); + } + + /** + * 人脸变化 + */ + @Inner + @GetMapping("/send-face-change-msg") + public R sendFaceChangeMsg(@RequestParam Long mchDeptId) { + MqttBaseDto dto = new MqttBaseDto<>(); + dto.setMerchantDeptId(mchDeptId); + dto.setAction(MqttActionEnum.FACE); + return R.ok(mqttEventService.defaultAction(dto)); + } + +} diff --git a/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/inner/MqttInner.java b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/inner/MqttInner.java new file mode 100644 index 000000000..19c994999 --- /dev/null +++ b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/inner/MqttInner.java @@ -0,0 +1,21 @@ +package com.bsd.pjcloud.mqtt.inner; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import com.bsd.pjcloud.mqtt.service.MqttService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class MqttInner { + + private final MqttService mqttService; + + @PostConstruct + public void initMqtt() { + mqttService.connect(); + mqttService.subscribeAll(); + } + +} diff --git a/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/service/MqttCallBackService.java b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/service/MqttCallBackService.java new file mode 100644 index 000000000..e21e8fbfc --- /dev/null +++ b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/service/MqttCallBackService.java @@ -0,0 +1,54 @@ +package com.bsd.pjcloud.mqtt.service; + +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSON; +import jakarta.annotation.Resource; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; +import org.eclipse.paho.client.mqttv3.MqttCallback; +import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.nio.charset.StandardCharsets; + +@RequiredArgsConstructor +@Slf4j +@Component +public class MqttCallBackService implements MqttCallback { + + static final String TAG = "mqtt回调"; + + @Lazy + private final MqttService mqttService; + + + // 断开连接的回调 + @Override + public void connectionLost(Throwable e) { + log.debug("mqtt断开连接:{}", JSON.toJSON(e)); + // 重新连接 + mqttService.connect(); + } + + // 监听消息的回调 + @Override + public void messageArrived(String topic, MqttMessage msg) throws Exception { + log.debug("mqtt收到消息topic:{};msg:{};", topic, JSON.toJSON(msg)); + if (ObjectUtil.isEmpty(topic)) return; + if (!topic.contains("service")) return; + byte[] payload = msg.getPayload(); + String json = new String(payload, StandardCharsets.UTF_8); + } + + @Override + public void deliveryComplete(IMqttDeliveryToken token) { + try { + log.info("|{}|mqtt证书校验成功:|{}|", TAG, token.getTopics()); + } catch (Exception e) { + log.error("|{}|mqtt证书校验失败:|{}|", TAG, JSON.toJSON(e)); + } + } + +} diff --git a/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/service/MqttEventService.java b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/service/MqttEventService.java new file mode 100644 index 000000000..8a121ab8e --- /dev/null +++ b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/service/MqttEventService.java @@ -0,0 +1,23 @@ +package com.bsd.pjcloud.mqtt.service; + +import com.alibaba.fastjson.JSON; +import com.bsd.pjcloud.mqtt.dto.MqttBaseDto; +import com.bsd.pjcloud.mqtt.dto.MqttHardBaseMsg; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@RequiredArgsConstructor +@Slf4j +@Component +public class MqttEventService { + + private final MqttService mqttService; + + public Boolean defaultAction(T dto) { + MqttHardBaseMsg msg = new MqttHardBaseMsg(); + msg.setAction(dto.getAction()); + mqttService.publish(dto.getQos(), false, dto.getTopic(), JSON.toJSONString(msg)); + return true; + } +} diff --git a/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/service/MqttService.java b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/service/MqttService.java new file mode 100644 index 000000000..0e34d06da --- /dev/null +++ b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/service/MqttService.java @@ -0,0 +1,123 @@ +package com.bsd.pjcloud.mqtt.service; + +import cn.hutool.core.util.ObjectUtil; +import com.bsd.pjcloud.mqtt.config.MqttConfig; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import com.bsd.pjcloud.mqtt.utils.SSLUtils; +import org.eclipse.paho.client.mqttv3.*; +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; +import org.springframework.stereotype.Component; +import org.springframework.validation.annotation.Validated; + +import java.util.Objects; + +@Validated +@Slf4j +@RequiredArgsConstructor +@Component +public class MqttService { + + static final String TAG = "mqtt服务"; + + private final MqttConfig mqttConfig; + + private final MqttCallBackService mqttCallBack; + + static private MqttClient client; + + private static MqttClient getClient() { + return client; + } + + private static void setClient(MqttClient client) { + MqttService.client = client; + } + + public void connect() { + MqttClient client; + if (ObjectUtil.isNotEmpty(MqttService.getClient()) && MqttService.getClient().isConnected()) return; + try { + client = new MqttClient(mqttConfig.getHostUrl(), mqttConfig.getClientId(), new MemoryPersistence()); + MqttConnectOptions options = new MqttConnectOptions(); + options.setCleanSession(true); + options.setUserName(mqttConfig.getUsername()); + options.setPassword(mqttConfig.getPassword().toCharArray()); + crtAuth(options); + client.setCallback(mqttCallBack); + client.connect(options); + if (!client.isConnected()) { + log.error("|{}|mqtt连接失败;", TAG); + } else { + log.info("|{}|mqtt连接成功;", TAG); + } + setClient(client); + } catch (Exception e) { + log.error("mqtt连接错误:{};", e.getMessage()); + } + } + + public void crtAuth(MqttConnectOptions options) { + try { + String path; + if (!mqttConfig.getIsLocal() && ObjectUtil.isNotEmpty(mqttConfig.getCrtPath())) { + path = mqttConfig.getCrtPath(); + } else { + path = getClass().getClassLoader().getResource("crt/emqxsl-ca.crt").getPath(); + } + options.setSocketFactory(SSLUtils.getSocketFactory(path)); + } catch (Exception e) { + log.error("mqtt 证书错误 {}", e.getMessage()); + } + } + + private boolean checkMqttConnect() { + if (Objects.isNull(MqttService.getClient()) || !client.isConnected()) { + log.error("|{}|mqtt连接错误", TAG); + return false; + } + return true; + } + + // 订阅所有主题 + public void subscribeAll() { + if (!checkMqttConnect()) return; + String topic = "topic/#"; + subscribe(topic, 1); + } + + // 发送消息 + public void publish(int qos, boolean retained, String topic, String pushMessage) { + if (!checkMqttConnect()) return; + MqttMessage message = new MqttMessage(); + message.setQos(qos); + message.setRetained(retained); + message.setPayload(pushMessage.getBytes()); + MqttTopic mTopic = MqttService.getClient().getTopic(topic); + MqttDeliveryToken token; + try { + token = mTopic.publish(message); + token.waitForCompletion(); + publishLog(qos, retained, topic, pushMessage); + } catch (MqttException e) { + log.error("|{}|mqtt发送消息错误:|{}|", TAG, e.getMessage()); + } + } + + public void subscribe(String topic, int qos) { + try { + MqttService.getClient().subscribe(topic, qos); + subscribeLog(topic, qos); + } catch (MqttException e) { + log.error("|{}|mqtt订阅主题错误:|{}|", TAG, e.getMessage()); + } + } + + private void publishLog(int qos, boolean retained, String topic, String pushMessage) { + log.info("|{}|发布消息成功topic:|{}|qos:|{}|是否保留:|{}|消息:|{}|", TAG, topic, qos, retained, pushMessage); + } + + private void subscribeLog(String topic, int qos) { + log.debug("|{}|订阅主题成功topic:{};qos:{};", TAG, topic, qos); + } +} diff --git a/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/utils/SSLUtils.java b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/utils/SSLUtils.java new file mode 100644 index 000000000..1c8e35563 --- /dev/null +++ b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/java/com/bsd/pjcloud/mqtt/utils/SSLUtils.java @@ -0,0 +1,33 @@ +package com.bsd.pjcloud.mqtt.utils; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManagerFactory; +import java.io.FileInputStream; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; + +public class SSLUtils { + + public static SSLSocketFactory getSocketFactory(String caCertPath) throws Exception { + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + + // load CA certificate into keystore to authenticate server + Certificate caCert = certFactory.generateCertificate(new FileInputStream(caCertPath)); + X509Certificate x509CaCert = (X509Certificate) caCert; + + KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + caKeyStore.load(null, null); + caKeyStore.setCertificateEntry("cacert", x509CaCert); + + TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmFactory.init(caKeyStore); + + SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); + sslContext.init(null, tmFactory.getTrustManagers(), null); + + return sslContext.getSocketFactory(); + } +} \ No newline at end of file diff --git a/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/resources/application.yml b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/resources/application.yml new file mode 100644 index 000000000..f4bb02b48 --- /dev/null +++ b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/resources/application.yml @@ -0,0 +1,18 @@ +server: + port: 8010 + +spring: + application: + name: @artifactId@ + cloud: + nacos: + username: @nacos.username@ + password: @nacos.password@ + discovery: + server-addr: ${NACOS_HOST:pjcloud-register}:${NACOS_PORT:8848} + config: + server-addr: ${spring.cloud.nacos.discovery.server-addr} + config: + import: + - optional:nacos:application-@profiles.active@.yml + - optional:nacos:${spring.application.name}-@profiles.active@.yml diff --git a/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/resources/crt/apiclient_key.pem b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/resources/crt/apiclient_key.pem new file mode 100644 index 000000000..fd0262cbe --- /dev/null +++ b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/resources/crt/apiclient_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC/dA1s6RUO8ZMy +0exwGp34jgcUY5//dbeXusXmkwqLPO24jtBjfglVoOQF9PpUWFsk8UY2pOP26sq5 +pXrCpLYA7nuROVk4ulmU/JDfsi8YK0DpDMT8neAO/uy+kXoIGogW7scVz9ZNkKFI +9g4YBjh8plNb0KaT3piYgxmSccVV9vwWDvFoVFZkHqCCY6PScKm2v2WnINNgXbuO +X3PBjSJKJ/AnaONiwp7F67+LE1meBu+m1vCg+pDry8s2H2vxf3TlzXygG32owsru +bqj/aN5+wr3YcheX1L2TQcTeGOMNoylD/0lzMi+az6IMNM2sV/BOdKbVq2/g238Z +SyFM0xQTAgMBAAECggEAKbgXSNlNrnDz24olFJSoQcZpQocnEVrMg/K+Ny1picfg +IWx1eoi3bgJeTfIBLqoGSnmNDFFIH8EwJd4qbwnHY1KUeKPfjl4FMVMMGsLQQlsC +jQYsUsPwVDQ20JwQ1H07lIu5tEYS89oFW0nK7m9Eld0EoyhHdEd4RxuiUega0U1d +R2TdILJsA2WT/StRuthmbpR/0PJ8GwLF294ARP+ibmk4H1ZuMShf5kHnFrLG1+0Z +g/kfQQfXMNIbLK/i4KxGh3IdaTxFl0ITQq86tX+pZp1d36f1Evnsb3PxdCymBHM8 +/TFlZ6llpjN/JLWTr6t93CpYm7DGwcvg99Pada1QKQKBgQDhRGd6+x9pwWrowoC+ +wnerxXYe1/MecMXzsVptX7/cj9dieqQnwgsbEGt8Heb3Zbh/IheFEB3LHB4tC80n +1WgVi78OY3fbaEBSnwrkdbYxoiSn8FBoi6NkzitbcAQhzEMu/1GM3XQ9RAD9bNpg +E2Pb/USvO4dd66uGD4UbnQFUzQKBgQDZkq2Htw05QREfcAVVD4EUnoFub3wPXP83 +MJMoNU+6v1J/boURiUJ1DVwHZlP9f+K2++hlDleURSQypj37UgupyQGZJS4K+847 +x1K/vRW8U6+Ey8syMd+B8F8xVnPwJdYMOOmQCbnoYSXYsY2U4vv5pkcq2gWfuTtS ++k6BRscMXwKBgGhqtrSwtozk1NNzZQ4HQkVi0C/X1Z+kkn01Fj9ghKSOID5Lrs+n +CFLRN6FekvLWOcDkYpaRM94z+GDU+ZGfQBHMpxDaVnJjTn19ryTa00sKZ4dYw0zS +fTh6wGov8BpifjnIq1pqRDkpznOqA0dJdZ5BNZcsNsD4PpdzljWIf1d5AoGAPgBi +my47AnncW00c9+R3MF0LHWF/xREHWb7p8KdglSM4a0Afg6PN4HVyKFmszazH+sVG +sKWpp4MckaJ2P0xeLoAtiGrj+BVR7A8dMa68pvJiLkG1IhS8UNcjCXhNib3+hXUi +ef2LkQE+O8Y9RpR4oegUl82RlLbAhSD3hjeZOZkCgYEAsPHXxe4r3KpXBoWhkZIs +Xd00tYeTzzN1WulIIM6RUx3pNfy+bAne9fEieTZ3frw+b2Hbo+bUgS40hn8gdaxX +HJfwXTsnJ9yw5PnJ8VAk+Te07G2RczbJwl6G4vEifBAZaYqbuK37n7EwQdl6P2Db +5Tg5eEAXoE4XnOWPj5ivIMc= +-----END PRIVATE KEY----- diff --git a/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/resources/crt/emqxsl-ca.crt b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/resources/crt/emqxsl-ca.crt new file mode 100644 index 000000000..8f3aea9d5 --- /dev/null +++ b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/resources/crt/emqxsl-ca.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/resources/logback-spring.xml b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/resources/logback-spring.xml new file mode 100644 index 000000000..964ca8735 --- /dev/null +++ b/pjcloud-mqtt/pjcloud-mqtt-biz/src/main/resources/logback-spring.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + ${CONSOLE_LOG_PATTERN} + + + + + + ${log.path}/debug.log + + ${log.path}/%d{yyyy-MM, aux}/debug.%d{yyyy-MM-dd}.%i.log.gz + 50MB + 30 + + + %date [%thread] %-5level [%logger{50}] %file:%line - %msg%n + + + + + + ${log.path}/error.log + + ${log.path}/%d{yyyy-MM}/error.%d{yyyy-MM-dd}.%i.log.gz + 50MB + 30 + + + %date [%thread] %-5level [%logger{50}] %file:%line - %msg%n + + + ERROR + + + + + + + + + + + + + + + + + diff --git a/pjcloud-mqtt/pom.xml b/pjcloud-mqtt/pom.xml new file mode 100644 index 000000000..7d6aa9f9d --- /dev/null +++ b/pjcloud-mqtt/pom.xml @@ -0,0 +1,21 @@ + + + + 4.0.0 + + + com.bsd + pjcloud + 5.7.0 + + + pjcloud-mqtt + pom + + + + pjcloud-mqtt-api + pjcloud-mqtt-biz + + diff --git a/pom.xml b/pom.xml index bcb840dd3..d5020e7f5 100644 --- a/pom.xml +++ b/pom.xml @@ -16,9 +16,7 @@ ~ this software without specific prior written permission. ~ Author: pjcloud ~ - --> - + --> 4.0.0 com.bsd @@ -125,7 +123,8 @@ pjcloud-wallet pjcloud-supermarket pjcloud-hardware - + pjcloud-mqtt +