Kaynağa Gözat

支付宝设备扫码激活、消息推送、微信公众号绑定

谭斌 2 yıl önce
ebeveyn
işleme
bb5b50aace

+ 6 - 0
device-api-service/pom.xml

@@ -50,6 +50,12 @@
             <version>1.0</version>
         </dependency>
 
+        <dependency>
+            <groupId>com.xy</groupId>
+            <artifactId>msg-api</artifactId>
+            <version>1.0</version>
+        </dependency>
+
         <dependency>
             <groupId>com.xy</groupId>
             <artifactId>xy-redis</artifactId>

+ 139 - 0
device-api-service/src/main/java/com/xy/consumer/device/push/msg/DevicePushMsgConsumer.java

@@ -0,0 +1,139 @@
+package com.xy.consumer.device.push.msg;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import com.xy.consumer.MqttConsumer;
+import com.xy.dto.MsgConfigTestDto;
+import com.xy.dto.UserInfoDto;
+import com.xy.dto.be.MercDto;
+import com.xy.dto.be.MercUserDeviceDto;
+import com.xy.entity.DeviceEventMsg;
+import com.xy.entity.DeviceInfo;
+import com.xy.entity.SysCodeConfigureRedis;
+import com.xy.enums.ChannelType;
+import com.xy.service.DeviceInfoServiceImpl;
+import com.xy.service.MsgSendApiService;
+import com.xy.service.UserInfoService;
+import com.xy.service.be.MercFeignService;
+import com.xy.utils.R;
+import com.xy.utils.SysCodeConfigureUtils;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
+
+
+/**
+ * 设备消息推送消费者
+ *
+ * @author 谭斌
+ * @date 2023/06/06
+ */
+@Slf4j
+@Service
+@AllArgsConstructor
+public class DevicePushMsgConsumer implements MqttConsumer {
+
+    private MsgSendApiService msgSendApiService;
+    private DeviceInfoServiceImpl deviceInfoService;
+    private MercFeignService mercFeignService;
+    private UserInfoService userInfoService;
+
+    @Override
+    public boolean message(String topic, String payload) {
+        log.info("-----设备故障消息推送-----:{}", payload);
+        DeviceEventMsg deviceEventMsg = JSONUtil.toBean(payload, DeviceEventMsg.class);
+        if (deviceEventMsg != null) {
+            Long deviceId = deviceEventMsg.getDeviceId();
+            Long mercId = deviceEventMsg.getMercId();
+            LocalDateTime createTime = deviceEventMsg.getCreateTime();
+            String msg = deviceEventMsg.getMsg();
+            SysCodeConfigureRedis sysCodeConfigureRedis = SysCodeConfigureUtils.get(deviceEventMsg.getCode());
+
+            Long configId = 12L;
+            List<MsgConfigTestDto.BizParam> bizParams = R.feignCheckData(msgSendApiService.getBizParamByMsgConfig(new MsgConfigTestDto.MsgConfig().setConfigId(configId)));
+            if (CollUtil.isNotEmpty(bizParams)) {
+                List<MsgConfigTestDto.BizData> bizDataList = BeanUtil.copyToList(bizParams, MsgConfigTestDto.BizData.class);
+                List<MsgConfigTestDto.BizData> sendList = new ArrayList<>();
+                for (MsgConfigTestDto.BizData b : bizDataList) {
+                    String channelType = b.getChannelType();
+                    if (Integer.valueOf(channelType).intValue() == ChannelType.OFFICIAL_ACCOUNT.getCode().intValue()) {
+                        //TODO: 暂无订阅功能。给商户管理员、以及有此设备权限的人推送消息,  查询微信公众号openID
+                        MercDto.Vo merc = R.feignCheckData(mercFeignService.obj(new MercDto.ListDTO().setId(mercId)));
+                        Long userInfoId = merc.getUserInfoId();
+                        List<Long> userInfoIdList = new ArrayList<>();
+                        userInfoIdList.add(userInfoId);
+                        List<MercUserDeviceDto.Vo> mercUserDevices = R.feignCheckData(mercFeignService.mercDeviceUsers(
+                                new MercUserDeviceDto.Vo().setMercId(mercId).setDeviceId(deviceId)));
+                        if (CollUtil.isNotEmpty(mercUserDevices)) {
+                            List<Long> uids = mercUserDevices.stream().map(MercUserDeviceDto.Vo::getUserId).collect(Collectors.toList());
+                            if (CollUtil.isNotEmpty(uids)) {
+                                userInfoIdList.addAll(uids);
+                            }
+                        }
+                        //查询userinfo表的mpOpenId
+                        List<UserInfoDto.Vo> userInfoList = R.feignCheckData(userInfoService.list(new UserInfoDto.SelectListDto().setUserIds(userInfoIdList)));
+
+                        Set<String> mpOpenIds = userInfoList.stream().filter(Objects::nonNull)
+                                .map(UserInfoDto.Vo::getMpOpenid)
+                                .collect(Collectors.toSet());
+                        if (CollUtil.isEmpty(mpOpenIds)) {
+                            continue;
+                        }
+
+                        /**
+                         * {{first.DATA}}
+                         * 设备名称:{{keyword1.DATA}}
+                         * 告警内容:{{keyword2.DATA}}
+                         * 发生时间:{{keyword3.DATA}}
+                         * {{remark.DATA}}
+                         *
+                         */
+                        DeviceInfo deviceInfo = deviceInfoService.getById(deviceId);
+                        String deviceName = deviceInfo.getDeviceName();
+                        if (StrUtil.isEmpty(deviceName)) {
+                            deviceName = deviceId + "";
+                        } else {
+                            deviceName = deviceName + "(" + deviceId + ")";
+                        }
+
+                        //微信公众号
+                        //扩展参数
+                        MsgConfigTestDto.BizExtraParam bizExtraParam = new MsgConfigTestDto.BizExtraParam();
+                        if (ObjectUtil.isNotEmpty(sysCodeConfigureRedis)) {
+                            String propose = sysCodeConfigureRedis.getPropose();
+                            if (StrUtil.isNotEmpty(propose) && propose.contains("http")) {
+                                bizExtraParam.setUrl(propose);
+                            }
+                        }
+                        Map<String, Object> params = MapUtil.newHashMap();
+                        params.put("keyword1", deviceName);
+                        params.put("keyword2", msg);
+                        params.put("keyword3", DateUtil.format(createTime, DatePattern.NORM_DATETIME_PATTERN));
+
+                        //商户管理员发
+                        b.setReceivers(mpOpenIds);
+                        b.setTemplateParams(params);
+                        b.setExtraParam(bizExtraParam);
+                        sendList.add(b);
+                    }
+                }
+                if (CollUtil.isNotEmpty(sendList)) {
+                    msgSendApiService.sendByMsgConfig(new MsgConfigTestDto.SendByMsgConfig().setConfigId(configId).setBizDataList(sendList));
+                }
+
+            }
+        }
+        return true;
+    }
+
+}

+ 79 - 0
device-api-service/src/main/java/com/xy/consumer/device/push/msg/DevicePushMsgMqttConfiguration.java

@@ -0,0 +1,79 @@
+package com.xy.consumer.device.push.msg;
+
+import com.xy.configuration.MqttConfigUtils;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.integration.annotation.IntegrationComponentScan;
+import org.springframework.integration.annotation.ServiceActivator;
+import org.springframework.integration.channel.DirectChannel;
+import org.springframework.integration.core.MessageProducer;
+import org.springframework.messaging.MessageChannel;
+import org.springframework.messaging.MessageHandler;
+
+@Configuration
+@IntegrationComponentScan
+public class DevicePushMsgMqttConfiguration {
+
+    /**
+     * topic
+     */
+    public final static String TOPIC = "DevicePushMsgTopic";
+
+    /**
+     * 入站通道名(消费者)订阅的bean名称
+     */
+    public static final String CHANNEL_NAME_IN = TOPIC + "MqttInboundChannel";
+
+    /**
+     * 出站通道名(生产者)发布的bean名称
+     */
+    public static final String CHANNEL_NAME_OUT = TOPIC + "MqttOutboundChannel";
+
+    /*******************************生产者*******************************************/
+
+    /**
+     * MQTT信息通道(生产者)
+     */
+    @Bean(name = CHANNEL_NAME_OUT)
+    public MessageChannel mqttOutboundChannel() {
+        return new DirectChannel();
+    }
+
+    /**
+     * MQTT消息处理器(生产者)
+     */
+    @Bean(name = TOPIC + "MqttOutbound")
+    @ServiceActivator(inputChannel = CHANNEL_NAME_OUT)
+    public MessageHandler mqttOutbound() {
+        return MqttConfigUtils.mqttOutbound(TOPIC);
+    }
+
+
+    /*******************************消费者*******************************************/
+
+    /**
+     * MQTT信息通道(消费者)
+     */
+    @Bean(name = CHANNEL_NAME_IN)
+    public MessageChannel mqttInboundChannel() {
+        return new DirectChannel();
+    }
+
+    /**
+     * MQTT消息订阅绑定(消费者)
+     */
+    @Bean(name = TOPIC + "Inbound")
+    public MessageProducer inbound() {
+        return MqttConfigUtils.inbound(TOPIC, mqttInboundChannel());
+    }
+
+    /**
+     * MQTT消息处理器(消费者)
+     */
+    @Bean(name = TOPIC + "Handler")
+    @ServiceActivator(inputChannel = CHANNEL_NAME_IN)
+    public MessageHandler handler() {
+        return MqttConfigUtils.handler();
+    }
+
+}

+ 26 - 0
device-api-service/src/main/java/com/xy/consumer/device/push/msg/DevicePushMsgProducer.java

@@ -0,0 +1,26 @@
+package com.xy.consumer.device.push.msg;
+
+import org.springframework.integration.annotation.MessagingGateway;
+import org.springframework.integration.mqtt.support.MqttHeaders;
+import org.springframework.messaging.handler.annotation.Header;
+import org.springframework.stereotype.Component;
+
+/**
+ * 生产者接口
+ */
+@Component
+@MessagingGateway(defaultRequestChannel = DevicePushMsgMqttConfiguration.CHANNEL_NAME_OUT)
+public interface DevicePushMsgProducer {
+
+    /**
+     * ata是发送消息的内容
+     * topic是消息发送的主题,就是配置文件的主题
+     * qos是mqtt 对消息处理机制分为0,1,2 其中0表示的是订阅者没收到消息不会再次发送,消息会丢失,1表示的是会尝试重试,一直到接收到消息,但这种情况可能导致订阅者收到多次重复消息,2相比多了一次去重的动作,确保订阅者收到的消息有一次
+     */
+
+    void sendToMqtt(String data);
+
+    void sendToMqtt(String data, @Header(MqttHeaders.TOPIC) String topic);
+
+    void sendToMqtt(String data, @Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos);
+}

+ 55 - 5
device-api-service/src/main/java/com/xy/service/factory/device/impl/alipay/AliPayOpenDeviceFatoryImpl.java

@@ -1,5 +1,6 @@
 package com.xy.service.factory.device.impl.alipay;
 
+import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONObject;
 import com.xy.alipay.SpiDeviceService;
 import com.xy.annotate.Factory;
@@ -15,13 +16,13 @@ import com.xy.dto.spi.DeviceAlarmNotifyDTO;
 import com.xy.dto.spi.DeviceStatusChangeNotifyDTO;
 import com.xy.entity.DeviceInfo;
 import com.xy.entity.DeviceStatus;
-import com.xy.service.AlipayDeviceService;
-import com.xy.service.DeviceEventMsgServiceImpl;
-import com.xy.service.DeviceInfoServiceImpl;
-import com.xy.service.DeviceStatusServiceImpl;
-import com.xy.service.factory.device.impl.open.OpenDeviceFactoryImpl;
+import com.xy.entity.DeviceSysinfo;
+import com.xy.error.CommRuntimeException;
+import com.xy.service.*;
 import com.xy.service.factory.device.DeviceFactory;
+import com.xy.service.factory.device.impl.open.OpenDeviceFactoryImpl;
 import com.xy.utils.*;
+import com.xy.utils.enums.DeviceActiveStateEnum;
 import com.xy.utils.enums.DeviceErrorRecordTypesEnum;
 import com.xy.vo.DeviceDetailVO;
 import io.swagger.annotations.Api;
@@ -30,6 +31,7 @@ import lombok.AllArgsConstructor;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 
+import java.time.LocalDateTime;
 import java.util.List;
 
 /**
@@ -54,6 +56,8 @@ public class AliPayOpenDeviceFatoryImpl implements DeviceFactory, SpiDeviceServi
 
     private DeviceEventMsgServiceImpl deviceEventMsgService;
 
+    private DeviceSysinfoServiceImpl deviceSysinfoService;
+
     @Override
     public R save(DeviceRegisterDto.Save save) {
         R r = openDeviceFactoryImpl.save(save);
@@ -199,4 +203,50 @@ public class AliPayOpenDeviceFatoryImpl implements DeviceFactory, SpiDeviceServi
         execute.end();
         return R.ok();
     }
+
+    @ApiOperation("设备注册&激活")
+    @PostMapping("deviceActive")
+    public R deviceActive(@RequestBody AlipayDeviceActiveDto dto) {
+        Long deviceId = dto.getDeviceId();
+        String sn = dto.getSn();
+        DeviceInfoDto.Vo deviceInfo = R.feignCheckData(deviceInfoService.obj(new DeviceInfoDto.Obj().setDeviceId(deviceId).setIsSysinfo(Boolean.TRUE)));
+        Long mercId = deviceInfo.getMercId();
+        Integer activeState = deviceInfo.getActiveState();
+        if (DeviceActiveStateEnum.TRUE.getCode().equals(String.valueOf(activeState))) {
+            throw new CommRuntimeException("此设备已经在平台激活,请到货柜操作!");
+        }
+        DeviceSysinfoDto.Vo deviceSysinfo = deviceInfo.getDeviceSysinfo();
+        if (deviceSysinfo != null) {
+            String deviceSn = deviceSysinfo.getDeviceSn();
+            if (StrUtil.isNotEmpty(deviceSn)) {
+                //存在绑定关系了
+                //绑定sn与扫码获取的sn是否一致
+                if (!deviceSn.equals(sn)) {
+                    throw new CommRuntimeException("SN号与平台登记的不一致,请联系客服!");
+                }
+
+            } else {
+                //登记了 but 没有sn ,则启用扫码获取的sn
+                //调用支付宝 注册
+
+            }
+            Boolean regOk = R.feignCheckData(alipayDeviceService.deviceBind(new BindDeviceDTO().setTerminalId(String.valueOf(deviceId)).setBoardSn(sn)));
+            if (regOk) {
+                deviceSysinfoService.updateById(new DeviceSysinfo().setDeviceId(deviceId).setDeviceSn(sn));
+            }
+            String deviceName = deviceInfo.getDeviceName();
+            deviceName = StrUtil.isNotEmpty(deviceInfo.getDeviceName()) ? deviceName : String.valueOf(deviceId);
+            //调用支付宝激活
+            Boolean activeOk = R.feignCheckData(alipayDeviceService.deviceActive(new BindActiveDTO().setTerminalId(String.valueOf(deviceId)).setBoardSn(sn).setDeviceName(deviceName)));
+            if (!activeOk) {
+                throw new CommRuntimeException("设备激活失败,请联系客服!");
+            }
+            //更新激活状态
+            deviceInfoService.updateById(new DeviceInfo().setDeviceId(deviceId).setActiveState(1).setActiveTime(LocalDateTime.now()));
+        } else {
+            throw new CommRuntimeException("此设备尚未在平台登记,请联系客服!");
+        }
+
+        return R.ok();
+    }
 }

+ 8 - 0
device-api-service/src/main/java/com/xy/service/factory/device/impl/open/OpenDeviceFactoryImpl.java

@@ -1,9 +1,12 @@
 package com.xy.service.factory.device.impl.open;
 
 import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.xy.annotate.Factory;
+import com.xy.consumer.device.push.msg.DevicePushMsgMqttConfiguration;
+import com.xy.consumer.device.push.msg.DevicePushMsgProducer;
 import com.xy.device.EnumDeviceActiveStatus;
 import com.xy.device.EnumDeviceBusyStatus;
 import com.xy.device.EnumDeviceCharging;
@@ -55,6 +58,9 @@ public class OpenDeviceFactoryImpl implements DeviceFactory {
 
     private DeviceEventMsgServiceImpl deviceEventMsgService;
 
+    private DevicePushMsgProducer devicePushMsgProducer;
+
+
     @Override
     public R save(DeviceRegisterDto.Save save) {
         DeviceRegister deviceRegister = copy(DeviceRegister.class, save.setDeviceSn(save.getDeviceSn().toUpperCase()));
@@ -221,6 +227,8 @@ public class OpenDeviceFactoryImpl implements DeviceFactory {
                             .setDeviceId(deviceInfo.getDeviceId())
                             .setFaultLevel(Integer.valueOf(sysCodeConfigureRedis.getExpand()));
                     deviceInfoService.updateById(updateDeviceInfo);
+                    //设备故障消息推送
+                    devicePushMsgProducer.sendToMqtt(JSONUtil.toJsonStr(deviceEventMsg), DevicePushMsgMqttConfiguration.TOPIC, 1);
                 }
             }
         }

+ 28 - 0
device-api/src/main/java/com/xy/dto/AlipayDeviceActiveDto.java

@@ -0,0 +1,28 @@
+package com.xy.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+
+/**
+ * 支付宝设备激活
+ *
+ * @author 谭斌
+ * @date 2023/06/09
+ */
+@Data
+@Accessors(chain = true)
+public class AlipayDeviceActiveDto {
+
+    @NotBlank(message = "sn不能为空")
+    @ApiModelProperty(value = "sn编号,扫码获得", required = true)
+    private String sn;
+
+    @NotNull(message = "设备编号不能为空")
+    @ApiModelProperty(value = "设备编号", required = true)
+    private Long deviceId;
+}

+ 5 - 4
device-start/src/main/resources/bootstrap-prod.yml

@@ -1,9 +1,10 @@
 spring:
   mqtt:
     topics:
-      - {topic: "disConnectedNotify", handler: com.xy.consumer.disconnect.DisconnectedConsumer}
-      - {topic: "connectedNotify", handler: com.xy.consumer.connected.ConnectedConsumer}
-      - {topic: "deviceDataNotify", handler: com.xy.consumer.data.DataConsumer}
+      - { topic: "disConnectedNotify", handler: com.xy.consumer.disconnect.DisconnectedConsumer }
+      - { topic: "connectedNotify", handler: com.xy.consumer.connected.ConnectedConsumer }
+      - { topic: "deviceDataNotify", handler: com.xy.consumer.data.DataConsumer }
+      - { topic: "DevicePushMsgTopic", handler: com.xy.consumer.device.push.msg.DevicePushMsgConsumer }
 
 #微服务相关配置
 cloud:
@@ -25,4 +26,4 @@ xxl:
 workerId: 10
 
 mysql:
-  doc: true
+  doc: true

+ 5 - 4
device-start/src/main/resources/bootstrap-uat.yml

@@ -1,9 +1,10 @@
 spring:
   mqtt:
     topics:
-      - {topic: "disConnectedNotify", handler: com.xy.consumer.disconnect.DisconnectedConsumer}
-      - {topic: "connectedNotify", handler: com.xy.consumer.connected.ConnectedConsumer}
-      - {topic: "deviceDataNotify", handler: com.xy.consumer.data.DataConsumer}
+      - { topic: "disConnectedNotify", handler: com.xy.consumer.disconnect.DisconnectedConsumer }
+      - { topic: "connectedNotify", handler: com.xy.consumer.connected.ConnectedConsumer }
+      - { topic: "deviceDataNotify", handler: com.xy.consumer.data.DataConsumer }
+      - { topic: "DevicePushMsgTopic", handler: com.xy.consumer.device.push.msg.DevicePushMsgConsumer }
 
 #微服务相关配置
 cloud:
@@ -20,4 +21,4 @@ xxl:
       port: 9061
 
 #雪花ID唯一标识
-workerId: 10
+workerId: 10