websocket通讯
第一步:前段js代码 黄线根据个人所需配置
<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://127.0.0.1:8080/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>
第二步:pom文件引入依赖 websocket
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
第三步:建立文件WebSocketConfig
package com.payease.config; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import org.springframework.web.socket.server.standard.ServerEndpointExporter; /** * Created by liuxiaoming * 2017-12-11 下午03:12 */ @Component public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
第四步:建立后台业务代码
package com.payease.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; /** * @Created By liuxiaoming * @CreateTime 2017/12/5 下午5:55 **/ @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); try { webSocket.session.getBasicRemote().sendText(message); }catch (Exception e){ e.printStackTrace(); } } } }
第五步:Service,controller 调用
package com.payease.service.impl; import com.payease.converter.OrderMaster2OrderDTOConverter; import com.payease.dataobject.OrderDetail; import com.payease.dataobject.OrderMaster; import com.payease.dataobject.ProductInfo; import com.payease.dto.CartDTO; import com.payease.dto.OrderDTO; import com.payease.enums.OrderStatusEnum; import com.payease.enums.PayStatusEnum; import com.payease.enums.ResultEnum; import com.payease.exception.SellException; import com.payease.repository.OrderDetailRepository; import com.payease.repository.OrderMasterRepository; import com.payease.service.OrderService; import com.payease.service.ProductService; import com.payease.service.PushMessageService; import com.payease.service.WebSocket; import com.payease.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 * liuxiaoming * 2017-11-20 */ @Service @Slf4j public class OrderServiceImpl implements OrderService { @Autowired private ProductService productService; @Autowired private OrderDetailRepository orderDetailRepository; @Autowired private OrderMasterRepository orderMasterRepository; @Autowired private WebSocket webSocket; @Autowired private PushMessageService pushMessageService; /** * 创建订单 * liuxiaoming * 2017-11-20 * */ @Override @Transactional public OrderDTO create(OrderDTO orderDTO) { String orderId = KeyUtil.getUniqueKey(); 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.getUniqueKey()); 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. 扣库存 : 循环遍历 OrderDetailList,把OrderDetail封装到CartDTO,最后存入orderDTO中的OrderDetailList 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; } /** * 查询单个订单 * liuxiaoming * 2017-11-21 * */ @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; } /** * 查询订单列表 * liuxiaoming * 2017-11-21 * */ @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()); } /** * 订单取消 * liuxiaoming * 2017-11-23 * */ @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())){ //TODO } return orderDTO; } /** * 完结订单 * liuxiaoming * 2017-11-24 * */ @Override @Transactional public OrderDTO finish(OrderDTO orderDTO) { //判断订单状态 if(!orderDTO.getOrderStatus().equals(OrderStatusEnum.NEW.getCode())){ log.error("【完结订单】订单状态不正确, orderId={}, orderStatus={}", orderDTO.getOrderId()); 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; } /** * 支付订单 * liuxiaoming * 2017-11-24 * */ @Override @Transactional public OrderDTO paid(OrderDTO orderDTO) { //判断订单状态 if(!orderDTO.getOrderStatus().equals(OrderStatusEnum.NEW.getCode())){ log.error("【支付订单】订货状态不正确, orderId={}, orderStatus={}", orderDTO.getOrderId()); 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()); } }
package com.payease.controller; import com.payease.VO.ResultVO; import com.payease.converter.OrderForm2OrderDTOConverter; import com.payease.dto.OrderDTO; import com.payease.enums.ResultEnum; import com.payease.exception.SellException; import com.payease.form.OrderForm; import com.payease.service.BuyerService; import com.payease.service.OrderService; import com.payease.utils.ResultVOUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Created by liuxiaoming * 2017-11-27 23:27 */ @RestController @RequestMapping("/buyer/order") @Slf4j public class BuyerOrderController { @Autowired private OrderService orderService; @Autowired private BuyerService buyerService; //创建订单 @PostMapping("/create") public ResultVO<Map<String, String>> create(@Valid OrderForm orderForm, BindingResult bindingResult) { if (bindingResult.hasErrors()) { log.error("【创建订单】参数不正确, orderForm={}", orderForm); throw new SellException(ResultEnum.PARAM_ERROR.getCode(), bindingResult.getFieldError().getDefaultMessage()); } OrderDTO orderDTO = OrderForm2OrderDTOConverter.convert(orderForm); if (CollectionUtils.isEmpty(orderDTO.getOrderDetailList())) { log.error("【创建订单】购物车不能为空"); throw new SellException(ResultEnum.CART_EMPTY); } OrderDTO createResult = orderService.create(orderDTO); Map<String, String> map = new HashMap<>(); map.put("orderId", createResult.getOrderId()); return ResultVOUtil.success(map); } //订单列表 @GetMapping("/list") public ResultVO<List<OrderDTO>> list(@RequestParam("openid") String openid, @RequestParam(value = "page", defaultValue = "0") Integer page, @RequestParam(value = "size", defaultValue = "10") Integer size) { if (StringUtils.isEmpty(openid)) { log.error("【查询订单列表】openid为空"); throw new SellException(ResultEnum.PARAM_ERROR); } PageRequest request = new PageRequest(page, size); Page<OrderDTO> orderDTOPage = orderService.findList(openid, request); return ResultVOUtil.success(orderDTOPage.getContent()); } //订单详情 @GetMapping("/detail") public ResultVO<OrderDTO> detail(@RequestParam("openid") String openid, @RequestParam("orderId") String orderId) { //OrderDTO orderDTO = orderService.findOne(orderId); OrderDTO orderDTO = buyerService.findOrderOne(openid, orderId); return ResultVOUtil.success(orderDTO); } //取消订单 @PostMapping("/cancel") public ResultVO cancel(@RequestParam("openid") String openid, @RequestParam("orderId") String orderId) { //OrderDTO orderDTO = orderService.findOne(orderId); //orderService.cancel(orderDTO); buyerService.cancelOrder(openid, orderId); return ResultVOUtil.success(); } }
第六步:postman提交
127.0.0.1:8080/sell/buyer/order/create