jiangping
2023-11-02 7e2a837e26aafca3d49d35f9704442659da8654c
硬件协议对接
已修改11个文件
240 ■■■■■ 文件已修改
server/platform/src/main/java/com/doumee/service/impl/MqttSubServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/platform/src/main/java/com/doumee/task/ScheduleTool.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/core/constants/Constants.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/core/model/api/WebLoginUserInfo.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/core/mqtt/service/MqttPushCallback.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/core/mqtt/service/MqttToolService.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/dao/business/model/Sites.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/DeviceSubcribeService.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/DeviceSubscribeServiceImpl.java 138 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/services/src/main/java/com/doumee/service/business/impl/MemberRidesServiceImpl.java 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/自行车mqtt协议.md 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
server/platform/src/main/java/com/doumee/service/impl/MqttSubServiceImpl.java
@@ -32,6 +32,6 @@
    @PostConstruct
    public void startSubcribe() {
        mqttToolService.subscribe(
                new String[]{ Constants.MqttTopic.sub_lockInfo, Constants.MqttTopic.sub_closeLock});
                new String[]{ Constants.MqttTopic.sub_lockInfo, Constants.MqttTopic.sub_closeLock,Constants.MqttTopic.sub_health});
    }
}
server/platform/src/main/java/com/doumee/task/ScheduleTool.java
@@ -77,9 +77,9 @@
     */
    @Scheduled(cron = "0/30 * * * * ? ")
    public void autoRefreshLockStatus() throws Exception {
        log.info("=====================开始每天自动结算=======================");
        log.info("=====================开始刷新骑行中的数充值未开锁失败=======================");
        memberRidesService.autoRefreshLockStatus();
        log.info("=====================结束每天自动结算=======================");
        log.info("=====================开始刷新骑行中的数充值未开锁失败=======================");
    }
@@ -87,7 +87,7 @@
     * ç«™ç‚¹è½¦è¾†æ»¡æž¶çŽ‡é¢„è­¦
     * @throws Exception
     */
    @Scheduled(fixedDelay = 1000L * 60L * 3L)
    @Scheduled(fixedDelay = 1000L * 60L * 10L)
    public void siteReserves() throws Exception {
        log.info("=====================开始 ç«™ç‚¹è½¦è¾†æ»¡æž¶çŽ‡é¢„è­¦=======================");
        sitesService.siteReservesNotice();;
server/services/src/main/java/com/doumee/core/constants/Constants.java
@@ -49,6 +49,9 @@
        String sub_lockInfo = "device/lock/+/+/info";
        //还车锁头(订阅)
        String sub_closeLock = "device/lock/+/+/bike";
        String sub_health= "device/lock/+/health";
        String sub_brokers = "$SYS/brokers/+/clients/#";
        //实时获取锁信息(发布)
        String pub_getLockInfo = "device/lock/{siteId}/{lockId}/getInfo";
    }
@@ -63,11 +66,12 @@
        int partful = 3;
  }
    public interface LockStatus{
      //  //状态,0闭合, 1打开,2运行中, 3异常
      //  //状态,0闭合, 1打开,2运行中, 3异常 -1自检
        int closed =0;
        int open =1;
        int running =2;
        int error= 3;
        int checking= -1;
    }
    public interface goodsorderStatus{
        int waitPay =0;
server/services/src/main/java/com/doumee/core/model/api/WebLoginUserInfo.java
@@ -46,6 +46,6 @@
    @ApiModelProperty(value = "总积分")
    private Integer total_integral         ;//
    @ApiModelProperty(value = "总提交数量")
    private Integer    submission_number      ;//
    private Integer    submission_number ;//
}
server/services/src/main/java/com/doumee/core/mqtt/service/MqttPushCallback.java
@@ -20,7 +20,6 @@
        //接收消息回调
        @Override
        public void connectionLost(Throwable cause) {
            // è¿žæŽ¥ä¸¢å¤±åŽï¼Œä¸€èˆ¬åœ¨è¿™é‡Œé¢è¿›è¡Œé‡è¿ž
            System.out.println("连接断开,重连中");
            try {
server/services/src/main/java/com/doumee/core/mqtt/service/MqttToolService.java
@@ -14,7 +14,6 @@
    private MqttConfig config;
    @Autowired
    private MqttPushCallback callBack ;
    /**
     * è®¢é˜…消息,启动加载一次
     * @param topics
@@ -25,6 +24,11 @@
            int[] Qos = new int[topics.length];//0:最多一次 ã€1:最少一次 ã€2:只有一次
            for (int i = 0; i < Qos.length; i++) {
                Qos[i] = 1;
                /*if(i ==2){
                    Qos[i] = 2;
                }else{
                    Qos[i] = 1;
                }*/
            }
            MqttClientInit.getSubInstance(config,callBack).subscribe(topics, Qos);
        } catch (Exception e) {
@@ -39,7 +43,7 @@
    public  int pubMessage(String message,String topic){
        MqttMessage mess = new MqttMessage();
        mess.setQos(1);
        mess.setRetained(true);
        mess.setRetained(false);
        mess.setPayload(message.getBytes());
        try {
            MqttClientInit.getInstance(config).publish(topic, mess);
server/services/src/main/java/com/doumee/dao/business/model/Sites.java
@@ -37,6 +37,10 @@
    @ExcelColumn(name="编辑时间")
    //@JsonFormat(pattern = "yyyy-MM-dd")
    private Date editDate;
    @ApiModelProperty(value = "最后通讯时间")
    @ExcelColumn(name="最后通讯时间")
    //@JsonFormat(pattern = "yyyy-MM-dd")
    private Date lastLinkDate;
    @ApiModelProperty(value = "编辑人")
    @ExcelColumn(name="编辑人")
@@ -59,9 +63,12 @@
    @ExcelColumn(name="编号")
    private String code;
    @ApiModelProperty(value = "状态 0正常 1禁用", example = "1")
    @ApiModelProperty(value = "状态 0正常 1禁用 ", example = "1")
    @ExcelColumn(name="状态 0正常 1禁用")
    private Integer status;
    @ApiModelProperty(value = "状态 0在线 1离线 ", example = "1")
    @ExcelColumn(name="状态 0在线 1离线")
    private Integer online;
    @ApiModelProperty(value = "锁头数量", example = "1")
    @ExcelColumn(name="锁头数量")
server/services/src/main/java/com/doumee/service/business/DeviceSubcribeService.java
@@ -1,14 +1,11 @@
package com.doumee.service.business;
import com.doumee.dao.business.model.Locks;
/**
 * ä¸Žç¡¬ä»¶å¯¹æŽ¥æœåŠ¡
 * @author æ±Ÿè¹„蹄
 * @date 2023/10/09 18:06
 */
public interface DeviceSubcribeService {
    /**
     * è®¾å¤‡ä¸ŠæŠ¥æ•°æ®ï¼ˆä¸ŠæŠ¥ï¼‰
     * @param param ä¸ŠæŠ¥å‚æ•°
server/services/src/main/java/com/doumee/service/business/impl/DeviceSubscribeServiceImpl.java
@@ -6,11 +6,10 @@
import com.doumee.core.exception.BusinessException;
import com.doumee.core.mqtt.config.MqttConfig;
import com.doumee.core.mqtt.service.MqttToolService;
import com.doumee.dao.business.MemberRidesMapper;
import com.doumee.dao.business.MqttLogMapper;
import com.doumee.dao.business.model.Bikes;
import com.doumee.dao.business.model.Locks;
import com.doumee.dao.business.model.MemberRides;
import com.doumee.dao.business.model.MqttLog;
import com.doumee.dao.business.SitesMapper;
import com.doumee.dao.business.model.*;
import com.doumee.service.business.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@@ -32,6 +31,9 @@
    @Autowired
    @Lazy
    MemberRidesService memberRidesService;
    @Lazy
    @Autowired
    SitesMapper sitesMapper;
    @Autowired
    private MqttLogMapper mqttLogMapper;
    @Autowired
@@ -41,24 +43,21 @@
       log.info("mqtt消息订阅==================="+param);
        String info = Constants.MqttTopic.sub_lockInfo.substring(Constants.MqttTopic.sub_lockInfo.lastIndexOf("/")+1) ;
        String closeLock = Constants.MqttTopic.sub_closeLock.substring(Constants.MqttTopic.sub_closeLock.lastIndexOf("/")+1) ;
        if(topic.indexOf(Constants.MqttTopic.topic_index)!=0
                ||topic.split("/").length < 5
                || (!StringUtils.contains(topic, info)
                   &&!StringUtils.contains(topic,closeLock))){
            log.error("mqtt消息订阅===========无效数据========"+param);
            return;
        }
        String[] ss = topic.split("/");
        String siteid =  ss[2];//站点编码
        String lockid =  ss[3];//锁头编码
        if(StringUtils.isBlank(siteid)||StringUtils.isBlank(lockid)){
            //如果锁头编码为空
            log.error("mqtt消息订阅==============无效数据====="+param);
            return;
        String health = Constants.MqttTopic.sub_health.substring(Constants.MqttTopic.sub_health.lastIndexOf("/")+1) ;
        String brokers = "brokers";
        if(!StringUtils.contains(topic, brokers)){
            if(topic.indexOf(Constants.MqttTopic.topic_index)!=0
                    ||topic.split("/").length < 4
                    || (!StringUtils.contains(topic, info)
                    &&!StringUtils.contains(topic, health)
                    &&!StringUtils.contains(topic,closeLock))){
                log.error("mqtt消息订阅===========无效数据========"+param);
                return;
            }
        }
        MqttLog mqttLog = new MqttLog();
        mqttLog.setMsgId(msgId);
        mqttLog.setTopic(topic);
        int msgCount  =mqttLogMapper.selectCount(new QueryWrapper<MqttLog>().lambda().eq(MqttLog::getMsg, param).eq(MqttLog::getType, Constants.ZERO));
        if(msgCount>0){
            log.error("mqtt消息订阅==============已消费数据====="+param);
@@ -68,27 +67,86 @@
        String logInfo = "";
        int result =0;
        try {
            if(StringUtils.contains(topic, info)){
                //如果锁头信息上报
                Locks locks  = JSONObject.parseObject(param, Locks.class);
                locks.setSiteId(siteid);
                locks.setCode(lockid);
                locks.setInfo(logId);
                result = memberRidesService.mqttLockInfoEvent(locks);
                logInfo = "mqtt消息订阅锁头信息";
                log.info("mqtt消息订阅=========锁信息==========成功");
            }
            if(StringUtils.contains(topic, closeLock)){
                //如果还车上报
                JSONObject pjson  = JSONObject.parseObject(param);
                MemberRides bikes = new MemberRides();
                bikes.setBikeCode(pjson.getString("bikeCode"));
                bikes.setBackLockId( lockid);
                bikes.setBackSiteId( siteid);
                bikes.setBackCommondId(logId);
                result = memberRidesService.mqttCloseBikeEvent(bikes);
                logInfo = "mqtt消息订阅还车消息";
                log.info("mqtt消息订阅=========还车==========成功");
            if(!topic.contains(brokers)){
                String[] ss = topic.split("/");
                String siteid =  ss[2];//站点编码
                if(StringUtils.isBlank(siteid) ){
                    //如果锁头编码为空
                    log.error("mqtt消息订阅==============无效数据====="+topic+param);
                    return;
                }
                if(StringUtils.contains(topic, info)){
                    //如果锁头信息上报
                    String lockid =  ss[3];//锁头编码
                    if( StringUtils.isBlank(lockid)){
                        //如果锁头编码为空
                        log.error("mqtt消息订阅==============无效数据====="+topic+param);
                        return;
                    }
                    Locks locks  = JSONObject.parseObject(param, Locks.class);
                    locks.setSiteId(siteid);
                    locks.setCode(lockid);
                    locks.setInfo(logId);
                    result = memberRidesService.mqttLockInfoEvent(locks);
                    logInfo = "mqtt消息订阅锁头信息";
                    log.info("mqtt消息订阅=========锁信息==========成功");
                }else if(StringUtils.contains(topic, closeLock)){
                    //如果还车上报
                    String lockid =  ss[3];//锁头编码
                    if( StringUtils.isBlank(lockid)){
                        //如果锁头编码为空
                        log.error("mqtt消息订阅==============无效数据====="+topic+param);
                        return;
                    }
                    JSONObject pjson  = JSONObject.parseObject(param);
                    MemberRides bikes = new MemberRides();
                    bikes.setBikeCode(pjson.getString("bikeCode"));
                    bikes.setBackLockId( lockid);
                    bikes.setBackSiteId( siteid);
                    bikes.setBackCommondId(logId);
                    result = memberRidesService.mqttCloseBikeEvent(bikes);
                    logInfo = "mqtt消息订阅还车消息";
                    log.info("mqtt消息订阅=========还车==========成功");
                }else if(StringUtils.contains(topic, health)){
                    //心跳消息=
                    Sites site = new Sites();
                    site.setId(siteid);
                    site.setOnline(Constants.ZERO);
                    site.setLastLinkDate(new Date());
                    sitesMapper.updateById(site);//更新站点状态
                    logInfo = "mqtt消息订阅心跳消息";
                    log.info("mqtt消息订阅=========心跳消息==========成功");
                }
            }else {
                //如果站点上下线消息
                JSONObject pjson = JSONObject.parseObject(param);
                String clientId = String.valueOf(pjson.get("clientid"));
                if(clientId.contains("doumee")){
                    log.error("mqtt消息订阅==============无效订阅状态====="+topic+param);
                    return;
                }
                Sites site = sitesMapper.selectById(clientId);
                if(site == null){
                    log.error("mqtt消息订阅==============无效订阅状态====="+topic+param);
                    return;
                }
                if (topic.endsWith("disconnected")) {
                    if(Constants.formatIntegerNum(site.getOnline()) == Constants.ONE){
                        log.error("mqtt消息订阅==============已订阅状态掉线====="+param);
                        return;
                    }
                    site.setOnline(Constants.ONE);
                } else {
                    if(Constants.formatIntegerNum(site.getOnline()) == Constants.ZERO){
                        log.error("mqtt消息订阅==============已订阅状态上线====="+param);
                        return;
                    }
                    site.setOnline(Constants.ZERO);
                }
                site.setLastLinkDate(new Date());
                sitesMapper.updateById(site);//更新站点状态
                log.info("mqtt消息订阅=========站点上下线==========成功");
            }
        }catch (BusinessException e){
            e.printStackTrace();
server/services/src/main/java/com/doumee/service/business/impl/MemberRidesServiceImpl.java
@@ -449,14 +449,26 @@
                .eq(Locks::getIsdeleted, Constants.ZERO)
                .last("limit 1"));
        if (Objects.isNull(locks)) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "扫码无效,未查询到锁头信息");
//            locks =new Locks();
//            locks.setCode(codes[1]);
//            locks.setSiteId(codes[0]);
//            deviceService.getLockInfo(locks);
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "扫码无效,该站点锁头暂不支持借出业务哦!");
        }
        //查询锁头是否存在车辆 ä»¥åŠæ˜¯å¦æ­£å¸¸
        if (Constants.formatIntegerNum(locks.getStatus())!=Constants.ZERO) {
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "当前扫码锁头状态错误,无法进行开锁");
            locks =new Locks();
            locks.setCode(codes[1]);
            locks.setSiteId(codes[0]);
            deviceService.getLockInfo(locks);
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "当前扫码锁头状态错误,已尝试更新锁头状态,请稍后重试!");
        }
        if (StringUtils.isBlank(locks.getBikeCode())) {
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "当前扫码锁头无车辆信息,无法进行开锁");
            locks =new Locks();
            locks.setCode(codes[1]);
            locks.setSiteId(codes[0]);
            deviceService.getLockInfo(locks);
            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(), "当前扫码锁头无车辆信息,已尝试更新锁头状态,请稍后重试!");
        }
        MemberRides memberRides = new MemberRides();
        //根据车型查询计价方案
@@ -698,6 +710,7 @@
            sites.setStatus(Constants.ZERO);
            sites.setEditDate(date);
            sites.setLockNum(1);
            sites.setLastLinkDate(date);
            //新增锁头
            sitesMapper.insert(sites);
        }
@@ -751,13 +764,23 @@
    @Override
    @Transactional(rollbackFor = {BusinessException.class,Exception.class})
    public  int mqttCloseBikeEvent(MemberRides bikes){
        Locks locks  = new Locks();
        locks.setSiteId(bikes.getBackSiteId());
        locks.setCode(bikes.getBackLockId());
        locks.setBikeCode(bikes.getBikeCode());
        locks.setInfo(bikes.getBackCommondId());
        //处理锁头数据
         mqttLockInfoEvent(locks);
        //免费骑行时长查询,数据字典配置
        if(StringUtils.isBlank(bikes.getBikeCode()) ||StringUtils.isBlank(bikes.getBackLockId())||StringUtils.isBlank(bikes.getBackSiteId())){
          throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(),"还车上报参数错误!");
        }
        QueryWrapper<MemberRides> wrapper = new QueryWrapper<>();
        List<Integer> statusList = new ArrayList<>();
        statusList.add(Constants.MEMBER_RIDES_STATUS.RIDES_RUNNING.getKey());
        statusList.add(Constants.MEMBER_RIDES_STATUS.LOCKING.getKey());
        wrapper.lambda().eq(MemberRides::getBikeCode, bikes.getBikeCode());
        wrapper.lambda().eq(MemberRides::getStatus, Constants.MEMBER_RIDES_STATUS.RIDES_RUNNING.getKey());
        wrapper.lambda().in(MemberRides::getStatus, statusList);
        wrapper.lambda().eq(MemberRides::getIsdeleted, Constants.ZERO);
        //根据车辆编码查询骑行中的骑行记录信息,如果有进行还车操作
        MemberRides memberRides = memberRidesMapper.selectOne(wrapper.last("limit  1"));
server/×ÔÐгµmqttЭÒé.md
@@ -3,47 +3,49 @@
## sub: device/lock/{siteId}/{lockId}/info {"bikeCode":"1234567890","lockId":"kjflksjlkfsjdlk","siteId": 456,"status":0,"code":"789"}
## sub: device/lock/{siteId}/{lockId}/bike  {"bikeCode":"1234567890","lockId":789,"siteId": 456,"time":"2023-10-13 10:12:90"}
# è‡ªè¡Œè½¦ mqtt åè®®æ–‡æ¡£
## é€šä¿¡è§„范
- æ¯ä¸ªç«™ç‚¹ä¸€ä¸ª mqtt è¿žæŽ¥ï¼ŒclientId ä¸º SITE_站点编号
- ä¸€ä¸ªç«™ç‚¹ç”±éƒ¨ç½²åœ¨ä¸Šä½æœºä¸Šçš„软件通过can总线与多个锁通信,并与服务器通过mqtt做消息转发
- siteId + lockId ä¸ºé”çš„唯一索引,在同一站点下,lockId不重复
## sub: device/lock/{siteId}/{lockId}/info
> **锁信息,在初始化、状态变更时会推送锁的完整状态**
## sub: device/lock/{siteId}/{lockId}/info qos=1
> **锁信息,在站点软件启动初始化、getInfo主题,unlock主题时时会推送锁的完整状态**
- æ•°æ®
```json
{
    "siteId": "1015", // ç«™ç‚¹ç¼–号,同主题{siteId}
    "lockId": 2, // é”ç¼–号,同主题{lockId}
    "status": 1, // çŠ¶æ€ï¼Œ0闭合, 1打开,2运行中, 3异常, -1自检中
    "bikeCode": "12345678" // è‡ªè¡Œè½¦ic卡号,8位数字,如:10000012,无车为空
    "bikeCode": "12345678", // è‡ªè¡Œè½¦ic卡号,8位数字,如:10000012,无车为空
    "timestamp": 1234567823423 // æ—¶é—´æˆ³ï¼Œ ms值
}
```
## pub: device/lock/{siteId}/{lockId}/getInfo
> å®žæ—¶èŽ·å–é”ä¿¡æ¯
## pub: device/lock/{siteId}/{lockId}/getInfo qos=0
> å®žæ—¶èŽ·å–é”ä¿¡æ¯, ç«™ç‚¹åœ¨æ”¶åˆ°æ­¤æ¶ˆæ¯æ—¶ï¼Œå‘送指定获取到锁的实时消息,并发布到info主题
- æ•°æ®
```json
{}
```
## pub: device/lock/{siteId}/{lockId}/unlock
## pub: device/lock/{siteId}/{lockId}/unlock qos=0
> å¼€é”ï¼ŒæˆåŠŸå¤±è´¥å¯å…³æ³¨info消息推送
- æ•°æ®
```json
{}
```
## sub: device/lock/{siteId}/{lockId}/bike
## sub: device/lock/{siteId}/{lockId}/bike qos=1
> è¿˜è½¦, è¿˜è½¦æˆåŠŸæ—¶ï¼ŒèŽ·å¾—æ‰€è¿˜è½¦è¾†ic卡号推送
- æ•°æ®
```json
{
    "bikeCode": "12345678",
    "lockId": 2,
    "siteId": "1050",
    "time": "2023-10-13 10:12:90" // ç«™ç‚¹ä¸Šä½æœºæ”¶åˆ°è¿˜è½¦æŒ‡ä»¤çš„æ—¶é—´ï¼Œä»…做参考,请以服务器时间为准
    "siteId": "1015", // ç«™ç‚¹ç¼–号,同主题{siteId}
    "lockId": 2, // é”ç¼–号,同主题{lockId}
    "status": 1, // çŠ¶æ€ï¼Œ0闭合, 1打开,2运行中, 3异常, -1自检中
    "bikeCode": "12345678", // è‡ªè¡Œè½¦ic卡号,8位数字,如:10000012,无车为空
    "timestamp": 1234567823423 // æ—¶é—´æˆ³ï¼Œ ms值,站点上位机收到还车指令的时间,仅做参考,请以服务器时间为准
}
```
```