Java使用Websocket向客户端推送消息
需求:外卖平台收到订单后,向商家推送消息,有新订单了,并播放音频。
客户端 list.ftl
<html>
<#include "../common/header.ftl">
<body>
<div id="wrapper" class="toggled">
<#--边栏sidebar-->
<#include "../common/nav.ftl">
<#--主要内容content-->
<div id="page-content-wrapper">
<div class="container-fluid">
<div class="row clearfix">
<div class="col-md-12 column">
<table class="table table-bordered table-condensed">
<thead>
<tr>
<th>订单id</th>
<th>姓名</th>
<th>手机号</th>
<th>地址</th>
<th>金额</th>
<th>订单状态</th>
<th>支付状态</th>
<th>创建时间</th>
<th colspan="2">操作</th>
</tr>
</thead>
<tbody>
<#list orderDTOPage.content as orderDTO>
<tr>
<td>${orderDTO.orderId}</td>
<td>${orderDTO.buyerName}</td>
<td>${orderDTO.buyerPhone}</td>
<td>${orderDTO.buyerAddress}</td>
<td>${orderDTO.orderAmount}</td>
<td>${orderDTO.getOrderStatusEnum().message}</td>
<td>${orderDTO.getPayStatusEnum().message}</td>
<td>${orderDTO.createTime}</td>
<td><a href="/sell/seller/order/detail?orderId=${orderDTO.orderId}">详情</a></td>
<td>
<#if orderDTO.getOrderStatusEnum().message == "新订单">
<a href="/sell/seller/order/cancel?orderId=${orderDTO.orderId}">取消</a>
</#if>
</td>
</tr>
</#list>
</tbody>
</table>
</div>
<#--分页-->
<div class="col-md-12 column">
<ul class="pagination pull-right">
<#if currentPage lte 1>
<li class="disabled"><a href="#">上一页</a></li>
<#else>
<li><a href="/sell/seller/order/list?page=${currentPage - 1}&size=${size}">上一页</a></li>
</#if>
<#list 1..orderDTOPage.getTotalPages() as index>
<#if currentPage == index>
<li class="disabled"><a href="#">${index}</a></li>
<#else>
<li><a href="/sell/seller/order/list?page=${index}&size=${size}">${index}</a></li>
</#if>
</#list>
<#if currentPage gte orderDTOPage.getTotalPages()>
<li class="disabled"><a href="#">下一页</a></li>
<#else>
<li><a href="/sell/seller/order/list?page=${currentPage + 1}&size=${size}">下一页</a></li>
</#if>
</ul>
</div>
</div>
</div>
</div>
</div>
<#--弹窗-->
<div class="modal fade" id="myModal" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title" id="myModalLabel">
提醒
</h4>
</div>
<div class="modal-body">
你有新的订单
</div>
<div class="modal-footer">
<button onclick="javascript:document.getElementById('notice').pause()" type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
<button onclick="location.reload()" type="button" class="btn btn-primary">查看新的订单</button>
</div>
</div>
</div>
</div>
<#--播放音乐-->
<audio id="notice" loop="loop">
<source src="/sell/mp3/song.mp3" type="audio/mpeg" />
</audio>
<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script>
var websocket = null;
if('WebSocket' in window) {
websocket = new WebSocket('ws://域名/sell/webSocket');
}else {
alert('该浏览器不支持websocket!');
}
websocket.onopen = function (event) {
console.log('建立连接');
}
websocket.onclose = function (event) {
console.log('连接关闭');
}
websocket.onmessage = function (event) {
console.log('收到消息:' + event.data)
//弹窗提醒,
$('#myModal').modal('show');
// 播放音乐
document.getElementById('notice').play();
}
websocket.onerror = function () {
alert('websocket通信发生错误!');
}
window.onbeforeunload = function () {
websocket.close();
}
</script>
</body>
</html>
Java 服务端
第一步:在spring boot项目中引入jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
引入后,右击pom.xml->reimpoit
第二步:编写配置 config/WebSocketConfig.java
package com.imooc.config;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Component
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
第三步:service/WebSocket.java
package com.imooc.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.CopyOnWriteArraySet;
@Component
@ServerEndpoint("/webSocket")
@Slf4j
public class WebSocket {
private Session session;
private static CopyOnWriteArraySet<WebSocket> webSocketSet = new CopyOnWriteArraySet<>();
@OnOpen
public void onOpen(Session session) {
this.session = session;
webSocketSet.add(this);
log.info("【websocket消息】有新的连接, 总数:{}", webSocketSet.size());
}
@OnClose
public void onClose() {
webSocketSet.remove(this);
log.info("【websocket消息】连接断开, 总数:{}", webSocketSet.size());
}
@OnMessage
public void onMessage(String message) {
log.info("【websocket消息】收到客户端发来的消息:{}", message);
}
public void sendMessage(String message) {
for (WebSocket webSocket: webSocketSet) {
log.info("【websocket消息】广播消息, message={}", message);
try {
webSocket.session.getBasicRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
第四步:实现业务需求 ,在用户创建了订单后,给商家推送有新订单的消息,并播放音频
package com.imooc.service.impl;
import com.imooc.converter.OrderMaster2OrderDTOConverter;
import com.imooc.dataobject.OrderDetail;
import com.imooc.dataobject.OrderMaster;
import com.imooc.dataobject.ProductInfo;
import com.imooc.dto.CartDTO;
import com.imooc.dto.OrderDTO;
import com.imooc.enums.OrderStatusEnum;
import com.imooc.enums.PayStatusEnum;
import com.imooc.enums.ResultEnum;
import com.imooc.exception.SellException;
import com.imooc.repository.OrderDetailRepository;
import com.imooc.repository.OrderMasterRepository;
import com.imooc.service.*;
import com.imooc.utils.KeyUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.stream.Collectors;
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
@Autowired
private ProductService productService;
@Autowired
private OrderDetailRepository orderDetailRepository;
@Autowired
private OrderMasterRepository orderMasterRepository;
@Autowired
private PayService payService;
@Autowired
private PushMessageService pushMessageService;
// 创建订单
@Autowired
private WebSocket webSocket;
@Override
@Transactional
public OrderDTO create(OrderDTO orderDTO) {
String orderId = KeyUtil.genUniqueKey();
BigDecimal orderAmount = new BigDecimal(BigInteger.ZERO);
// List<CartDTO> cartDTOList = new ArrayList<>();
//1. 查询商品(数量, 价格)
for (OrderDetail orderDetail: orderDTO.getOrderDetailList()) {
ProductInfo productInfo = productService.findOne(orderDetail.getProductId());
if (productInfo == null) {
throw new SellException(ResultEnum.PRODUCT_NOT_EXIST);
}
//2. 计算订单总价
orderAmount = productInfo.getProductPrice()
.multiply(new BigDecimal(orderDetail.getProductQuantity()))
.add(orderAmount);
//订单详情入库
orderDetail.setDetailId(KeyUtil.genUniqueKey());
orderDetail.setOrderId(orderId);
BeanUtils.copyProperties(productInfo, orderDetail);
orderDetailRepository.save(orderDetail);
// CartDTO cartDTO = new CartDTO(orderDetail.getProductId(), orderDetail.getProductQuantity());
// cartDTOList.add(cartDTO);
}
//3. 写入订单数据库(orderMaster和orderDetail)
OrderMaster orderMaster = new OrderMaster();
orderDTO.setOrderId(orderId);
BeanUtils.copyProperties(orderDTO, orderMaster);
orderMaster.setOrderAmount(orderAmount);
orderMaster.setOrderStatus(OrderStatusEnum.NEW.getCode());
orderMaster.setPayStatus(PayStatusEnum.WAIT.getCode());
orderMasterRepository.save(orderMaster);
//4. 扣库存
List<CartDTO> cartDTOList = orderDTO.getOrderDetailList().stream().map(e ->
new CartDTO(e.getProductId(), e.getProductQuantity())
).collect(Collectors.toList());
productService.decreaseStock(cartDTOList);
// 发送websocket消息
webSocket.sendMessage(orderDTO.getOrderId());
return orderDTO;
}
@Override
public OrderDTO findOne(String orderId) {
OrderMaster orderMaster = orderMasterRepository.findOne(orderId);
if (orderMaster == null) {
throw new SellException(ResultEnum.ORDER_NOT_EXIST);
}
List<OrderDetail> orderDetailList = orderDetailRepository.findByOrderId(orderId);
if (CollectionUtils.isEmpty(orderDetailList)) {
throw new SellException(ResultEnum.ORDERDETAIL_NOT_EXIST);
}
OrderDTO orderDTO = new OrderDTO();
BeanUtils.copyProperties(orderMaster, orderDTO);
orderDTO.setOrderDetailList(orderDetailList);
return orderDTO;
}
@Override
public Page<OrderDTO> findList(String buyerOpenid, Pageable pageable) {
Page<OrderMaster> orderMasterPage = orderMasterRepository.findByBuyerOpenid(buyerOpenid, pageable);
List<OrderDTO> orderDTOList = OrderMaster2OrderDTOConverter.convert(orderMasterPage.getContent());
return new PageImpl<OrderDTO>(orderDTOList, pageable, orderMasterPage.getTotalElements());
}
@Override
@Transactional
public OrderDTO cancel(OrderDTO orderDTO) {
OrderMaster orderMaster = new OrderMaster();
//判断订单状态
if (!orderDTO.getOrderStatus().equals(OrderStatusEnum.NEW.getCode())) {
log.error("【取消订单】订单状态不正确, orderId={}, orderStatus={}", orderDTO.getOrderId(), orderDTO.getOrderStatus());
throw new SellException(ResultEnum.ORDER_STATUS_ERROR);
}
//修改订单状态
orderDTO.setOrderStatus(OrderStatusEnum.CANCEL.getCode());
BeanUtils.copyProperties(orderDTO, orderMaster);
OrderMaster updateResult = orderMasterRepository.save(orderMaster);
if (updateResult == null) {
log.error("【取消订单】更新失败, orderMaster={}", orderMaster);
throw new SellException(ResultEnum.ORDER_UPDATE_FAIL);
}
//返回库存
if (CollectionUtils.isEmpty(orderDTO.getOrderDetailList())) {
log.error("【取消订单】订单中无商品详情, orderDTO={}", orderDTO);
throw new SellException(ResultEnum.ORDER_DETAIL_EMPTY);
}
List<CartDTO> cartDTOList = orderDTO.getOrderDetailList().stream()
.map(e -> new CartDTO(e.getProductId(), e.getProductQuantity()))
.collect(Collectors.toList());
productService.increaseStock(cartDTOList);
//如果已支付, 需要退款
if (orderDTO.getPayStatus().equals(PayStatusEnum.SUCCESS.getCode())) {
payService.refund(orderDTO);
}
return orderDTO;
}
@Override
@Transactional
public OrderDTO finish(OrderDTO orderDTO) {
//判断订单状态
if (!orderDTO.getOrderStatus().equals(OrderStatusEnum.NEW.getCode())) {
log.error("【完结订单】订单状态不正确, orderId={}, orderStatus={}", orderDTO.getOrderId(), orderDTO.getOrderStatus());
throw new SellException(ResultEnum.ORDER_STATUS_ERROR);
}
//修改订单状态
orderDTO.setOrderStatus(OrderStatusEnum.FINISHED.getCode());
OrderMaster orderMaster = new OrderMaster();
BeanUtils.copyProperties(orderDTO, orderMaster);
OrderMaster updateResult = orderMasterRepository.save(orderMaster);
if (updateResult == null) {
log.error("【完结订单】更新失败, orderMaster={}", orderMaster);
throw new SellException(ResultEnum.ORDER_UPDATE_FAIL);
}
//推送微信模版消息
pushMessageService.orderStatus(orderDTO);
return orderDTO;
}
@Override
@Transactional
public OrderDTO paid(OrderDTO orderDTO) {
//判断订单状态
if (!orderDTO.getOrderStatus().equals(OrderStatusEnum.NEW.getCode())) {
log.error("【订单支付完成】订单状态不正确, orderId={}, orderStatus={}", orderDTO.getOrderId(), orderDTO.getOrderStatus());
throw new SellException(ResultEnum.ORDER_STATUS_ERROR);
}
//判断支付状态
if (!orderDTO.getPayStatus().equals(PayStatusEnum.WAIT.getCode())) {
log.error("【订单支付完成】订单支付状态不正确, orderDTO={}", orderDTO);
throw new SellException(ResultEnum.ORDER_PAY_STATUS_ERROR);
}
//修改支付状态
orderDTO.setPayStatus(PayStatusEnum.SUCCESS.getCode());
OrderMaster orderMaster = new OrderMaster();
BeanUtils.copyProperties(orderDTO, orderMaster);
OrderMaster updateResult = orderMasterRepository.save(orderMaster);
if (updateResult == null) {
log.error("【订单支付完成】更新失败, orderMaster={}", orderMaster);
throw new SellException(ResultEnum.ORDER_UPDATE_FAIL);
}
return orderDTO;
}
@Override
public Page<OrderDTO> findList(Pageable pageable) {
Page<OrderMaster> orderMasterPage = orderMasterRepository.findAll(pageable);
List<OrderDTO> orderDTOList = OrderMaster2OrderDTOConverter.convert(orderMasterPage.getContent());
return new PageImpl<>(orderDTOList, pageable, orderMasterPage.getTotalElements());
}
}
还没有评论,来说两句吧...