SpringBoot-WebSocket

  1. Vue
    1. wsUrl:
    2. Sec-WebSocket-Protocol:
    3. webSocket = new WebSocket(wsUrl, Sec-WebSocket-Protocol)
    4. webSocket.onopen = function(){}
    5. webSocket.onmessage = function(){}
    6. webSocket.onclose = function(){}
    7. webSocket.onerror = function(){}
    8. heartCheck
  2. Nginx
    http {
    
        upstream branchBackup {
            server 117.51.156.xxx:18081;
            server 117.51.156.xxx:28081 backup;
        }
    
        #后端平台管理系统接口
        location ^~ /branch/api-v1/ws/ {
            proxy_pass http://branchBackup;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_read_timeout 86400s;
            proxy_send_timeout 86400s;
        }
    }

     

  3. ApplicationContext发布通知事件
    1. 发布事件
      applicationContext.publishEvent(new ServiceNonstandardCheckEvent(this, id, checkStatus, serviceNonstandardEntity.getOperatorId()));

       

    2. 定义事件实体类(继承ApplicationEvent)
      package com.das.listener.event.notification;
      
      import lombok.Getter;
      import lombok.Setter;
      import org.springframework.context.ApplicationEvent;
      
      /**
       * @Author liangmy
       * @Description:
       * @Create: 2021/1/19 上午11:26
       */
      @Getter
      @Setter
      public class ServiceNonstandardCheckEvent extends ApplicationEvent {
          /**
           * 询价服务唯一标志
           */
          private Long serviceNonstandardId;
          /**
           * 询价服务审核操作
           * true :审核通过
           * false : 审核拒绝
           */
          private Boolean checkStatus;
          /**
           * 通知接收账号唯一标志
           */
          private Long systemUserId;
      
      
          /**
           * Create a new ApplicationEvent.
           *
           * @param source the object on which the event initially occurred (never {@code null})
           */
          public ServiceNonstandardCheckEvent(Object source) {
              super(source);
          }
      
          public ServiceNonstandardCheckEvent(Object source, Long serviceNonstandardId, Boolean checkStatus, Long systemUserId) {
              super(source);
              this.serviceNonstandardId = serviceNonstandardId;
              this.checkStatus = checkStatus;
              this.systemUserId = systemUserId;
          }
      }
      

       

    3. 定义事件监听者(实现ApplicationListener<ApplicationEvent>)
      package com.das.listener.notification;
      
      import com.alibaba.fastjson.JSON;
      import com.das.common.constant.EtcConstant;
      import com.das.common.constant.RedisPrefixConstant;
      import com.das.entity.system.NotificationEntity;
      import com.das.listener.event.notification.ServiceNonstandardCheckEvent;
      import com.das.service.notification.NotificationServiceManager;
      import com.das.service.redis.RedisService;
      import com.das.thirdParty.tim.TimService;
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.context.ApplicationListener;
      import org.springframework.stereotype.Component;
      
      /**
       * @Author liangmy
       * @Description:
       * @Create: 2021/1/19 上午11:33
       */
      @Slf4j
      @Component
      public class ServiceNonstandardCheckListener implements ApplicationListener<ServiceNonstandardCheckEvent> {
      
      
          @Autowired
          private NotificationServiceManager notificationServiceManager;
          @Autowired
          private RedisService redisService;
          @Autowired
          private TimService timService;
      
      
          @Override
          public void onApplicationEvent(ServiceNonstandardCheckEvent event) {
              // 1. 持久化通知信息
              log.info("saveNotificationAndReceiverForServiceNonstandarCheck(Long serviceNonstandardId, Boolean checkStatus, Long systemUserId) : {}, {}, {}", event.getServiceNonstandardId(), event.getCheckStatus(), event.getSystemUserId());
              Long notificationId = notificationServiceManager.saveNotificationAndReceiverForServiceNonstandarCheck(event.getServiceNonstandardId(), event.getCheckStatus(), event.getSystemUserId());
              if (EtcConstant.assertLegalParam(notificationId)) {
                  // 2. 发布消息到 serviceNonstandarCheck 频道
                  NotificationEntity notificationEntity = new NotificationEntity();
                  notificationEntity.setId(notificationId);
                  log.info("convertAndSend(RedisPrefixConstant.SubcribeTopic.serviceNonstandarCheck : {}", JSON.toJSONString(notificationEntity));
                  redisService.convertAndSend(RedisPrefixConstant.SubcribeTopic.serviceNonstandarCheck, notificationEntity);
                  // 3. 发布消息到 IM
                  log.info("sendmsgForServiceNonstandardCheck(Long systemUserId, Long serviceNonstandardId, Boolean checkStatus) : {}, {}, {}", event.getSystemUserId(), event.getServiceNonstandardId(), event.getCheckStatus());
                  timService.sendmsgForServiceNonstandardCheck(event.getSystemUserId(), event.getServiceNonstandardId(), event.getCheckStatus());
              }
          }
      }
      

       

      1. 持久化通知信息
        1. 定义通知信息Subjoin类
          package com.das.entity.system.notification;
          
          import lombok.Getter;
          import lombok.Setter;
          
          /**
           * @Author liangmy
           * @Description:
           * @Create: 2021/1/19 下午2:04
           */
          @Getter
          @Setter
          public class NotificationSubjoinServiceNonstandardCheckEntity {
          
              /**
               * 询价服务唯一标志
               */
              private Long serviceNonstandardId;
              /**
               * 询价服务审核状态
               * true : 审核通过
               * false : 审核拒绝
               */
              private Boolean checkStatus;
              /**
               * 询价服务创建人账号标志
               */
              private Long operatorId;
          
          
          }
          

           

        2. 持久化通知信息逻辑
          /**
               * 持久化通知及通知接收人,并返回通知id(询价服务审核通过/审核拒绝)
               *
               * @param serviceNonstandardId
               * @param checkStatus
               * @param systemUserId         接收通知账号唯一标志
               * @return
               */
              @Override
              public Long saveNotificationAndReceiverForServiceNonstandarCheck(Long serviceNonstandardId, Boolean checkStatus, Long systemUserId) {
                  if (!EtcConstant.assertLegalParam(serviceNonstandardId) || null == checkStatus) {
                      throw DasException.internalError(null, messageSource.getMessage(EtcConstant.MessageCode.param_missing, null, LocaleContextHolder.getLocale()));
                  }
                  NotificationEntity notificationEntity = new NotificationEntity();
          
                  notificationEntity.setType(NotificationTypeEnum.SERVICE_NONSTANDAR_CHECK);
          
                  notificationEntity.setContent(EtcConstant.WsMessage.NotificationConstant.content_service_nonstandar_check);
                  NotificationSubjoinServiceNonstandardCheckEntity notificationSubjoinServiceNonstandardCheckEntity = new NotificationSubjoinServiceNonstandardCheckEntity();
                  notificationSubjoinServiceNonstandardCheckEntity.setServiceNonstandardId(serviceNonstandardId);
                  notificationSubjoinServiceNonstandardCheckEntity.setCheckStatus(checkStatus);
                  notificationEntity.setSubjoin(JSON.toJSONString(notificationSubjoinServiceNonstandardCheckEntity));
                  notificationMapper.save(notificationEntity);
          
                  List<NotificationReceiverEntity> notificationReceiverEntityList = new LinkedList<>();
          
                  NotificationReceiverEntity notificationReceiverEntity = new NotificationReceiverEntity();
                  notificationReceiverEntity.setNotificationId(notificationEntity.getId());
                  SystemUserEntity systemUserEntity = systemUserService.getSysUser(systemUserId);
                  if (null == systemUserEntity) {
                      throw DasException.notAllowed(null, messageSource.getMessage(EtcConstant.MessageCode.system_user_not_exist, null, LocaleContextHolder.getLocale()));
                  }
                  String receiverKey = EtcConstant.getKey(systemUserId, systemUserEntity.getStoreBaseId());
                  notificationReceiverEntity.setReceiverKey(receiverKey);
          
                  notificationReceiverEntityList.add(notificationReceiverEntity);
          
                  notificationReceiverMappper.saveBatch(notificationReceiverEntityList);
                  return notificationEntity.getId();
              }

           

      2. 发布消息到指定频道
        1. 定义Redis 发布订阅频道
              /**
               * Redis 发布订阅频道
               */
              public static class SubcribeTopic {
                  public static final String completeWork = "channel:ws:completeWork";
                  public static final String serviceNonstandarCheck = "channel:ws:serviceNonstandarCheck";
                  public static final String appointmentService = "channel:ws:appointmentService";
                  public static final String carPlanRemind = "channel:ws:carPlanRemind";
              }

           

        2. 发布消息到 serviceNonstandarCheck 频道
                      NotificationEntity notificationEntity = new NotificationEntity();
                      notificationEntity.setId(notificationId);
                      redisService.convertAndSend(RedisPrefixConstant.SubcribeTopic.serviceNonstandarCheck, notificationEntity);

           

  4. Redis 发布订阅模式

    1. 定义观察者RedisReceiver(实现org.springframework.data.redis.connection.MessageListener)
      package com.das.listener.redis;
      
      import com.alibaba.fastjson.JSON;
      import com.das.common.constant.EtcConstant;
      import com.das.common.constant.RedisPrefixConstant;
      import com.das.entity.system.NotificationEntity;
      import com.das.mapper.system.NotificationMapper;
      import com.das.service.ws.WsMessageService;
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.context.ApplicationContext;
      import org.springframework.data.redis.connection.Message;
      import org.springframework.data.redis.connection.MessageListener;
      import org.springframework.stereotype.Component;
      import org.springframework.util.CollectionUtils;
      
      import java.util.LinkedList;
      import java.util.List;
      import java.util.concurrent.TimeUnit;
      
      /**
       * @Author liangmy
       * @Description:
       * @Create: 2020/11/4 下午7:35
       */
      @Slf4j
      @Component
      public class RedisReceiver implements MessageListener {
      
          @Autowired
          private ApplicationContext applicationContext;
          @Autowired
          private NotificationMapper notificationMapper;
          @Autowired
          private WsMessageService wsMessageService;
      
      
          @Override
          public void onMessage(Message message, byte[] bytes) {
              String channel = new String(message.getChannel());
              String body = new String(message.getBody());
              log.info("message.getChannel():{}", channel);
              log.info("message.getBody():{}", body);
              log.info("term:{}", EtcConstant.notificationTerm);
              body = body.substring(1, body.length() - 1);
              log.info("message.getBody():{}", body);
              NotificationEntity notificationEntity = JSON.parseObject(body, NotificationEntity.class);
              log.info("message.getBody()::parseObject:{}", JSON.toJSONString(notificationEntity));
              log.info("notificationEntity.getId() : {}", notificationEntity.getId());
              List<NotificationEntity> notificationEntityPushList = notificationMapper.getNotificationEntityPushList(notificationEntity.getId());
              for (int i = 0; CollectionUtils.isEmpty(notificationEntityPushList) && i < 5; i++) {
                  log.info("notificationMapper.getNotificationEntityPushList(notificationEntity.getId()) is empty, try again. times : {}. notificationEntity.getId() : {}", i, notificationEntity.getId());
                  try {
                      TimeUnit.SECONDS.sleep(2);
                  } catch (InterruptedException e) {
                      log.info("notificationMapper.getNotificationEntityPushList(notificationEntity.getId()) sleep Interrupted. notificationEntity.getId() : {}", notificationEntity.getId());
                  }
                  notificationEntityPushList = notificationMapper.getNotificationEntityPushList(notificationEntity.getId());
              }
              List<String> alarmCountMessageOperatorKeyList = new LinkedList<>();
      
              switch (channel) {
                  case RedisPrefixConstant.SubcribeTopic.completeWork:
                  case RedisPrefixConstant.SubcribeTopic.appointmentService:
                  case RedisPrefixConstant.SubcribeTopic.carPlanRemind:
                  case RedisPrefixConstant.SubcribeTopic.serviceNonstandarCheck:
                      log.info("推送通知:{}", JSON.toJSONString(notificationEntityPushList));
                      wsMessageService.pushNotificationList(notificationEntityPushList);
                      for (NotificationEntity notificationEntityAlarmCount : notificationEntityPushList) {
                          alarmCountMessageOperatorKeyList.add(notificationEntityAlarmCount.getReceiverKey());
                      }
                      wsMessageService.pushAlarmCountMessageList(alarmCountMessageOperatorKeyList);
                      log.info("推送通知数量:{}", JSON.toJSONString(alarmCountMessageOperatorKeyList));
                      break;
                  default:
                      break;
              }
          }
      }
      

       

      1. 推送通知
            @Override
            public void pushNotificationList(List<NotificationEntity> notificationEntityPushList) {
                WsMessageEntity wsMessageEntity = null;
                for (NotificationEntity notificationEntity : notificationEntityPushList) {
                    String receiverKey = notificationEntity.getReceiverKey();
                    log.info("推送通知。获取接受人:{}", receiverKey);
        
                    if (StringUtils.isBlank(receiverKey)) {
                        continue;
                    }
                    log.info("推送通知。获取通知类型:{}", notificationEntity.getType());
                    notificationEntity.setTypeDesc(littleBear.getEnumValue(notificationEntity.getType()));
        
                    log.info("parse Notification::subjoin : {}", notificationEntity.getSubjoin());
                    switch (notificationEntity.getType()) {
                        case SERVICE_NONSTANDAR_CHECK:
                            String downloadUrl = "";
                            NotificationSubjoinServiceNonstandardCheckEntity notificationSubjoinServiceNonstandardCheckEntity = JSON.parseObject(notificationEntity.getSubjoin(), NotificationSubjoinServiceNonstandardCheckEntity.class);
                            if (null != notificationSubjoinServiceNonstandardCheckEntity) {
                                SystemUserEntity systemUserEntity = systemUserMapper.getById(notificationSubjoinServiceNonstandardCheckEntity.getOperatorId());
                                if (null != systemUserEntity) {
                                    downloadUrl = ossService.getDownloadUrl(systemUserEntity.getPortraitResourceId());
                                    notificationSubjoinServiceNonstandardCheckEntity.setOperatorImgUrl(downloadUrl);
                                }
                            }
                            notificationEntity.setOperatorImgUrl(downloadUrl);
                            notificationEntity.setSubjoin(JSON.toJSONString(notificationSubjoinServiceNonstandardCheckEntity));
                            notificationEntity.setSubjoinEntity(notificationSubjoinServiceNonstandardCheckEntity);
                            break;
                        default:
                            break;
                    }
                    WsMessageTermEnum term = WsMessageTermEnum.valueOf(EtcConstant.notificationTerm);
                    String receiverKeyTerm = EtcConstant.getKey(receiverKey, term);
                    wsMessageEntity = new WsMessageEntity(WsMessageTypeEnum.notify, receiverKeyTerm);
                    wsMessageEntity.setBodyClazz(notificationEntity.getClass().getName());
                    wsMessageEntity.setBody(notificationEntity);
        
                    AbstractWebSocketServer.sendMsg(receiverKeyTerm, wsMessageEntity);
                    log.info("通知推送。用户:{}, 通知:{}", receiverKeyTerm, JSON.toJSONString(wsMessageEntity));
                }
        
            }

         

      2. 推送通知数量
            @Override
            public void pushAlarmCountMessageList(List<String> alarmCountMessageOperatorKeyList) {
                for (String receiverKey : alarmCountMessageOperatorKeyList) {
                    if (StringUtils.isBlank(receiverKey)) {
                        continue;
                    }
        
                    // 所有类型通知数量
                    long getNonReadNofificationCount = notificationMapper.getNonReadNofificationCount(receiverKey);
        
                    WsMessageTermEnum term = WsMessageTermEnum.valueOf(EtcConstant.notificationTerm);
                    String receiverKeyTerm = EtcConstant.getKey(receiverKey, term);
                    WsMessageEntity wsMessageEntity = new WsMessageEntity(WsMessageTypeEnum.message, receiverKeyTerm);
        
                    wsMessageEntity.setBody(getNonReadNofificationCount);
                    wsMessageEntity.setBodyClazz(EtcConstant.WsMessage.AlarmCount);
        
                    AbstractWebSocketServer.sendMsg(receiverKeyTerm, wsMessageEntity);
                    // 各个类型通知数量
        
                    List<LabelNonReadNofificationCountDO> labelNonReadNofificationCountList = notificationMapper.getLabelNonReadNofificationCount(receiverKey);
                    Map<String, Integer> labelNonReadNofificationCount = new HashMap<>();
                    for (LabelNonReadNofificationCountDO labelNonReadNofificationCountDO : labelNonReadNofificationCountList) {
                        labelNonReadNofificationCount.put(labelNonReadNofificationCountDO.getType().name(), labelNonReadNofificationCountDO.getAmount());
                    }
        
                    wsMessageEntity.setBody(labelNonReadNofificationCount);
                    wsMessageEntity.setBodyClazz(EtcConstant.WsMessage.labelNonReadNofificationCount);
        
                    AbstractWebSocketServer.sendMsg(receiverKeyTerm, wsMessageEntity);
        
                }
            }

         

    2. RedisConfig(继承CachingConfigurerSupport)
      1. JedisPool
        1. JedisPoolConfig
              @Bean
              public JedisPool redisPoolFactory() {
                  JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
                  jedisPoolConfig.setMaxTotal(maxTotal);
                  jedisPoolConfig.setMaxIdle(maxIdle);
                  jedisPoolConfig.setMinIdle(minIdle);
                  jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
                  return new JedisPool(jedisPoolConfig, host, port, timeout, password);
              }
          

           

      2. RedisTemplate
        1. StringRedisTemplate
          private static final ObjectMapper om = new ObjectMapper();
          
          
              @Bean
              public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
                  StringRedisTemplate stringRedisTemplate = new StringRedisTemplate(factory);
                  Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
                  om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
                  om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
                  jackson2JsonRedisSerializer.setObjectMapper(om);
                  stringRedisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
                  stringRedisTemplate.afterPropertiesSet();
                  return stringRedisTemplate;
              }
          

           

        2. Jackson2JsonRedisSerializer
                  Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
                  om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
                  om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
                  jackson2JsonRedisSerializer.setObjectMapper(om);
          

           

      3. MessageListenerAdapter
      4. RedisMessageListenerContainer
        1. MessageListener
        2. PatternTopic
  5. 定义通知推送的终端
    1. notificationTerm=mech
  6. Filter
    1. 获取WS子协议
      1. String token = request.getHeader("Sec-WebSocket-Protocol");
      2. response.setHeader("Sec-WebSocket-Protocol", token);
  7. AbstractWebSocketService
    1. javax.websocket.Session
    2. @OnOpen
    3. @OnMessage
    4. @OnError
    5. @OnClose
    6. session.getBasicRemote().sendText(String text) throws IOException;
  8. WebSocketServiceImpl
    1. @Component
    2. @ServerEndpoint(value = "/ws/imServer/{term}")
  9. 啦啦啦
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值