Java使用Websocket向客户端推送消息

喜欢ヅ旅行 2022-04-03 02:47 524阅读 0赞

需求:外卖平台收到订单后,向商家推送消息,有新订单了,并播放音频。

客户端 list.ftl

  1. <html>
  2. <#include "../common/header.ftl">
  3. <body>
  4. <div id="wrapper" class="toggled">
  5. <#--边栏sidebar-->
  6. <#include "../common/nav.ftl">
  7. <#--主要内容content-->
  8. <div id="page-content-wrapper">
  9. <div class="container-fluid">
  10. <div class="row clearfix">
  11. <div class="col-md-12 column">
  12. <table class="table table-bordered table-condensed">
  13. <thead>
  14. <tr>
  15. <th>订单id</th>
  16. <th>姓名</th>
  17. <th>手机号</th>
  18. <th>地址</th>
  19. <th>金额</th>
  20. <th>订单状态</th>
  21. <th>支付状态</th>
  22. <th>创建时间</th>
  23. <th colspan="2">操作</th>
  24. </tr>
  25. </thead>
  26. <tbody>
  27. <#list orderDTOPage.content as orderDTO>
  28. <tr>
  29. <td>${orderDTO.orderId}</td>
  30. <td>${orderDTO.buyerName}</td>
  31. <td>${orderDTO.buyerPhone}</td>
  32. <td>${orderDTO.buyerAddress}</td>
  33. <td>${orderDTO.orderAmount}</td>
  34. <td>${orderDTO.getOrderStatusEnum().message}</td>
  35. <td>${orderDTO.getPayStatusEnum().message}</td>
  36. <td>${orderDTO.createTime}</td>
  37. <td><a href="/sell/seller/order/detail?orderId=${orderDTO.orderId}">详情</a></td>
  38. <td>
  39. <#if orderDTO.getOrderStatusEnum().message == "新订单">
  40. <a href="/sell/seller/order/cancel?orderId=${orderDTO.orderId}">取消</a>
  41. </#if>
  42. </td>
  43. </tr>
  44. </#list>
  45. </tbody>
  46. </table>
  47. </div>
  48. <#--分页-->
  49. <div class="col-md-12 column">
  50. <ul class="pagination pull-right">
  51. <#if currentPage lte 1>
  52. <li class="disabled"><a href="#">上一页</a></li>
  53. <#else>
  54. <li><a href="/sell/seller/order/list?page=${currentPage - 1}&size=${size}">上一页</a></li>
  55. </#if>
  56. <#list 1..orderDTOPage.getTotalPages() as index>
  57. <#if currentPage == index>
  58. <li class="disabled"><a href="#">${index}</a></li>
  59. <#else>
  60. <li><a href="/sell/seller/order/list?page=${index}&size=${size}">${index}</a></li>
  61. </#if>
  62. </#list>
  63. <#if currentPage gte orderDTOPage.getTotalPages()>
  64. <li class="disabled"><a href="#">下一页</a></li>
  65. <#else>
  66. <li><a href="/sell/seller/order/list?page=${currentPage + 1}&size=${size}">下一页</a></li>
  67. </#if>
  68. </ul>
  69. </div>
  70. </div>
  71. </div>
  72. </div>
  73. </div>
  74. <#--弹窗-->
  75. <div class="modal fade" id="myModal" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
  76. <div class="modal-dialog">
  77. <div class="modal-content">
  78. <div class="modal-header">
  79. <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
  80. <h4 class="modal-title" id="myModalLabel">
  81. 提醒
  82. </h4>
  83. </div>
  84. <div class="modal-body">
  85. 你有新的订单
  86. </div>
  87. <div class="modal-footer">
  88. <button onclick="javascript:document.getElementById('notice').pause()" type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
  89. <button onclick="location.reload()" type="button" class="btn btn-primary">查看新的订单</button>
  90. </div>
  91. </div>
  92. </div>
  93. </div>
  94. <#--播放音乐-->
  95. <audio id="notice" loop="loop">
  96. <source src="/sell/mp3/song.mp3" type="audio/mpeg" />
  97. </audio>
  98. <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
  99. <script src="https://cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
  100. <script>
  101. var websocket = null;
  102. if('WebSocket' in window) {
  103. websocket = new WebSocket('ws://域名/sell/webSocket');
  104. }else {
  105. alert('该浏览器不支持websocket!');
  106. }
  107. websocket.onopen = function (event) {
  108. console.log('建立连接');
  109. }
  110. websocket.onclose = function (event) {
  111. console.log('连接关闭');
  112. }
  113. websocket.onmessage = function (event) {
  114. console.log('收到消息:' + event.data)
  115. //弹窗提醒,
  116. $('#myModal').modal('show');
  117. // 播放音乐
  118. document.getElementById('notice').play();
  119. }
  120. websocket.onerror = function () {
  121. alert('websocket通信发生错误!');
  122. }
  123. window.onbeforeunload = function () {
  124. websocket.close();
  125. }
  126. </script>
  127. </body>
  128. </html>

Java 服务端

第一步:在spring boot项目中引入jar包

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-websocket</artifactId>
  4. </dependency>

引入后,右击pom.xml->reimpoit

第二步:编写配置 config/WebSocketConfig.java

  1. package com.imooc.config;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.stereotype.Component;
  4. import org.springframework.web.socket.server.standard.ServerEndpointExporter;
  5. @Component
  6. public class WebSocketConfig {
  7. @Bean
  8. public ServerEndpointExporter serverEndpointExporter() {
  9. return new ServerEndpointExporter();
  10. }
  11. }

第三步:service/WebSocket.java

  1. package com.imooc.service;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.stereotype.Component;
  4. import javax.websocket.OnClose;
  5. import javax.websocket.OnMessage;
  6. import javax.websocket.OnOpen;
  7. import javax.websocket.Session;
  8. import javax.websocket.server.ServerEndpoint;
  9. import java.util.concurrent.CopyOnWriteArraySet;
  10. @Component
  11. @ServerEndpoint("/webSocket")
  12. @Slf4j
  13. public class WebSocket {
  14. private Session session;
  15. private static CopyOnWriteArraySet<WebSocket> webSocketSet = new CopyOnWriteArraySet<>();
  16. @OnOpen
  17. public void onOpen(Session session) {
  18. this.session = session;
  19. webSocketSet.add(this);
  20. log.info("【websocket消息】有新的连接, 总数:{}", webSocketSet.size());
  21. }
  22. @OnClose
  23. public void onClose() {
  24. webSocketSet.remove(this);
  25. log.info("【websocket消息】连接断开, 总数:{}", webSocketSet.size());
  26. }
  27. @OnMessage
  28. public void onMessage(String message) {
  29. log.info("【websocket消息】收到客户端发来的消息:{}", message);
  30. }
  31. public void sendMessage(String message) {
  32. for (WebSocket webSocket: webSocketSet) {
  33. log.info("【websocket消息】广播消息, message={}", message);
  34. try {
  35. webSocket.session.getBasicRemote().sendText(message);
  36. } catch (Exception e) {
  37. e.printStackTrace();
  38. }
  39. }
  40. }
  41. }

第四步:实现业务需求 ,在用户创建了订单后,给商家推送有新订单的消息,并播放音频

  1. package com.imooc.service.impl;
  2. import com.imooc.converter.OrderMaster2OrderDTOConverter;
  3. import com.imooc.dataobject.OrderDetail;
  4. import com.imooc.dataobject.OrderMaster;
  5. import com.imooc.dataobject.ProductInfo;
  6. import com.imooc.dto.CartDTO;
  7. import com.imooc.dto.OrderDTO;
  8. import com.imooc.enums.OrderStatusEnum;
  9. import com.imooc.enums.PayStatusEnum;
  10. import com.imooc.enums.ResultEnum;
  11. import com.imooc.exception.SellException;
  12. import com.imooc.repository.OrderDetailRepository;
  13. import com.imooc.repository.OrderMasterRepository;
  14. import com.imooc.service.*;
  15. import com.imooc.utils.KeyUtil;
  16. import lombok.extern.slf4j.Slf4j;
  17. import org.springframework.beans.BeanUtils;
  18. import org.springframework.beans.factory.annotation.Autowired;
  19. import org.springframework.data.domain.Page;
  20. import org.springframework.data.domain.PageImpl;
  21. import org.springframework.data.domain.Pageable;
  22. import org.springframework.stereotype.Service;
  23. import org.springframework.transaction.annotation.Transactional;
  24. import org.springframework.util.CollectionUtils;
  25. import java.math.BigDecimal;
  26. import java.math.BigInteger;
  27. import java.util.List;
  28. import java.util.stream.Collectors;
  29. @Service
  30. @Slf4j
  31. public class OrderServiceImpl implements OrderService {
  32. @Autowired
  33. private ProductService productService;
  34. @Autowired
  35. private OrderDetailRepository orderDetailRepository;
  36. @Autowired
  37. private OrderMasterRepository orderMasterRepository;
  38. @Autowired
  39. private PayService payService;
  40. @Autowired
  41. private PushMessageService pushMessageService;
  42. // 创建订单
  43. @Autowired
  44. private WebSocket webSocket;
  45. @Override
  46. @Transactional
  47. public OrderDTO create(OrderDTO orderDTO) {
  48. String orderId = KeyUtil.genUniqueKey();
  49. BigDecimal orderAmount = new BigDecimal(BigInteger.ZERO);
  50. // List<CartDTO> cartDTOList = new ArrayList<>();
  51. //1. 查询商品(数量, 价格)
  52. for (OrderDetail orderDetail: orderDTO.getOrderDetailList()) {
  53. ProductInfo productInfo = productService.findOne(orderDetail.getProductId());
  54. if (productInfo == null) {
  55. throw new SellException(ResultEnum.PRODUCT_NOT_EXIST);
  56. }
  57. //2. 计算订单总价
  58. orderAmount = productInfo.getProductPrice()
  59. .multiply(new BigDecimal(orderDetail.getProductQuantity()))
  60. .add(orderAmount);
  61. //订单详情入库
  62. orderDetail.setDetailId(KeyUtil.genUniqueKey());
  63. orderDetail.setOrderId(orderId);
  64. BeanUtils.copyProperties(productInfo, orderDetail);
  65. orderDetailRepository.save(orderDetail);
  66. // CartDTO cartDTO = new CartDTO(orderDetail.getProductId(), orderDetail.getProductQuantity());
  67. // cartDTOList.add(cartDTO);
  68. }
  69. //3. 写入订单数据库(orderMaster和orderDetail)
  70. OrderMaster orderMaster = new OrderMaster();
  71. orderDTO.setOrderId(orderId);
  72. BeanUtils.copyProperties(orderDTO, orderMaster);
  73. orderMaster.setOrderAmount(orderAmount);
  74. orderMaster.setOrderStatus(OrderStatusEnum.NEW.getCode());
  75. orderMaster.setPayStatus(PayStatusEnum.WAIT.getCode());
  76. orderMasterRepository.save(orderMaster);
  77. //4. 扣库存
  78. List<CartDTO> cartDTOList = orderDTO.getOrderDetailList().stream().map(e ->
  79. new CartDTO(e.getProductId(), e.getProductQuantity())
  80. ).collect(Collectors.toList());
  81. productService.decreaseStock(cartDTOList);
  82. // 发送websocket消息
  83. webSocket.sendMessage(orderDTO.getOrderId());
  84. return orderDTO;
  85. }
  86. @Override
  87. public OrderDTO findOne(String orderId) {
  88. OrderMaster orderMaster = orderMasterRepository.findOne(orderId);
  89. if (orderMaster == null) {
  90. throw new SellException(ResultEnum.ORDER_NOT_EXIST);
  91. }
  92. List<OrderDetail> orderDetailList = orderDetailRepository.findByOrderId(orderId);
  93. if (CollectionUtils.isEmpty(orderDetailList)) {
  94. throw new SellException(ResultEnum.ORDERDETAIL_NOT_EXIST);
  95. }
  96. OrderDTO orderDTO = new OrderDTO();
  97. BeanUtils.copyProperties(orderMaster, orderDTO);
  98. orderDTO.setOrderDetailList(orderDetailList);
  99. return orderDTO;
  100. }
  101. @Override
  102. public Page<OrderDTO> findList(String buyerOpenid, Pageable pageable) {
  103. Page<OrderMaster> orderMasterPage = orderMasterRepository.findByBuyerOpenid(buyerOpenid, pageable);
  104. List<OrderDTO> orderDTOList = OrderMaster2OrderDTOConverter.convert(orderMasterPage.getContent());
  105. return new PageImpl<OrderDTO>(orderDTOList, pageable, orderMasterPage.getTotalElements());
  106. }
  107. @Override
  108. @Transactional
  109. public OrderDTO cancel(OrderDTO orderDTO) {
  110. OrderMaster orderMaster = new OrderMaster();
  111. //判断订单状态
  112. if (!orderDTO.getOrderStatus().equals(OrderStatusEnum.NEW.getCode())) {
  113. log.error("【取消订单】订单状态不正确, orderId={}, orderStatus={}", orderDTO.getOrderId(), orderDTO.getOrderStatus());
  114. throw new SellException(ResultEnum.ORDER_STATUS_ERROR);
  115. }
  116. //修改订单状态
  117. orderDTO.setOrderStatus(OrderStatusEnum.CANCEL.getCode());
  118. BeanUtils.copyProperties(orderDTO, orderMaster);
  119. OrderMaster updateResult = orderMasterRepository.save(orderMaster);
  120. if (updateResult == null) {
  121. log.error("【取消订单】更新失败, orderMaster={}", orderMaster);
  122. throw new SellException(ResultEnum.ORDER_UPDATE_FAIL);
  123. }
  124. //返回库存
  125. if (CollectionUtils.isEmpty(orderDTO.getOrderDetailList())) {
  126. log.error("【取消订单】订单中无商品详情, orderDTO={}", orderDTO);
  127. throw new SellException(ResultEnum.ORDER_DETAIL_EMPTY);
  128. }
  129. List<CartDTO> cartDTOList = orderDTO.getOrderDetailList().stream()
  130. .map(e -> new CartDTO(e.getProductId(), e.getProductQuantity()))
  131. .collect(Collectors.toList());
  132. productService.increaseStock(cartDTOList);
  133. //如果已支付, 需要退款
  134. if (orderDTO.getPayStatus().equals(PayStatusEnum.SUCCESS.getCode())) {
  135. payService.refund(orderDTO);
  136. }
  137. return orderDTO;
  138. }
  139. @Override
  140. @Transactional
  141. public OrderDTO finish(OrderDTO orderDTO) {
  142. //判断订单状态
  143. if (!orderDTO.getOrderStatus().equals(OrderStatusEnum.NEW.getCode())) {
  144. log.error("【完结订单】订单状态不正确, orderId={}, orderStatus={}", orderDTO.getOrderId(), orderDTO.getOrderStatus());
  145. throw new SellException(ResultEnum.ORDER_STATUS_ERROR);
  146. }
  147. //修改订单状态
  148. orderDTO.setOrderStatus(OrderStatusEnum.FINISHED.getCode());
  149. OrderMaster orderMaster = new OrderMaster();
  150. BeanUtils.copyProperties(orderDTO, orderMaster);
  151. OrderMaster updateResult = orderMasterRepository.save(orderMaster);
  152. if (updateResult == null) {
  153. log.error("【完结订单】更新失败, orderMaster={}", orderMaster);
  154. throw new SellException(ResultEnum.ORDER_UPDATE_FAIL);
  155. }
  156. //推送微信模版消息
  157. pushMessageService.orderStatus(orderDTO);
  158. return orderDTO;
  159. }
  160. @Override
  161. @Transactional
  162. public OrderDTO paid(OrderDTO orderDTO) {
  163. //判断订单状态
  164. if (!orderDTO.getOrderStatus().equals(OrderStatusEnum.NEW.getCode())) {
  165. log.error("【订单支付完成】订单状态不正确, orderId={}, orderStatus={}", orderDTO.getOrderId(), orderDTO.getOrderStatus());
  166. throw new SellException(ResultEnum.ORDER_STATUS_ERROR);
  167. }
  168. //判断支付状态
  169. if (!orderDTO.getPayStatus().equals(PayStatusEnum.WAIT.getCode())) {
  170. log.error("【订单支付完成】订单支付状态不正确, orderDTO={}", orderDTO);
  171. throw new SellException(ResultEnum.ORDER_PAY_STATUS_ERROR);
  172. }
  173. //修改支付状态
  174. orderDTO.setPayStatus(PayStatusEnum.SUCCESS.getCode());
  175. OrderMaster orderMaster = new OrderMaster();
  176. BeanUtils.copyProperties(orderDTO, orderMaster);
  177. OrderMaster updateResult = orderMasterRepository.save(orderMaster);
  178. if (updateResult == null) {
  179. log.error("【订单支付完成】更新失败, orderMaster={}", orderMaster);
  180. throw new SellException(ResultEnum.ORDER_UPDATE_FAIL);
  181. }
  182. return orderDTO;
  183. }
  184. @Override
  185. public Page<OrderDTO> findList(Pageable pageable) {
  186. Page<OrderMaster> orderMasterPage = orderMasterRepository.findAll(pageable);
  187. List<OrderDTO> orderDTOList = OrderMaster2OrderDTOConverter.convert(orderMasterPage.getContent());
  188. return new PageImpl<>(orderDTOList, pageable, orderMasterPage.getTotalElements());
  189. }
  190. }

发表评论

表情:
评论列表 (有 0 条评论,524人围观)

还没有评论,来说两句吧...

相关阅读