微信点餐系统(十二)-买家与卖家端连通
章节感悟
1.cookie的设置与注销
2.redis的使用,数据插入与删除
3.AOP的使用
4.全局捕获异常
5.微信模板消息推送
6.webSocket消息推送
买家与卖家端连通
卖家信息表dao开发和service开发
1.创建卖家信息表seller_info
create table seller_info( seller_id varchar(32) not null, username varchar(32) not null, password varchar(32) not null, openid varchar(64) not null comment '微信openid', create_time timestamp not null default current_timestamp comment '创建时间', update_time timestamp not null default current_timestamp on update current_timestamp comment '更新时间', primary key(seller_id) ) comment '卖家信息表';
2.DAO实体映射SellerInfo
3.SellerInfoRepository
4.创建SellerService接口
登陆成功
1.获取卖家openid,这是在微信开放平台获取的,由于这里我们没有企业资格,所以这里的openid我们设定为一个固定值oIe231KOhNAGPWEIsE52bdPBA910
2.设置登录页面
3.创建SellerUserController类,里面三个方法,登录页面跳转,登录和登出
4.下载Redis Desktop Manager 可视化工具
5.引入redis依赖,配置redis
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> redis: host: 192.168.1.105 port: 6379
6.设置一个redis常量,创建包constant,创建接口RedisConstant
package com.xiong.sell.constant; /** * @author Xiong YuSong * 2019/1/28 10:55 */ public interface RedisConstant { String TOKEN_PREFIX = "token_%s"; I
7.设置openid到cookie,创建一个cookie工具类,保存和获取cookie,设置cookie常量
8.创建ProductUrlConfig类,获取配置文件中的路径
9.登陆接口”/sell/seller/login”
@PostMapping("/login") public ModelAndView login(@RequestParam("openid") String openid, Map<String, Object> map, HttpServletResponse response) { //1.openid和数据库匹配 SellerInfo sellerInfo = sellerService.findSellerInfoByOpenid(openid); if (sellerInfo == null) { map.put("msg", ResultEnum.LOGIN_FAIL.getMessage()); map.put("url", "/sell/seller/toLogin"); return new ModelAndView("common/error", map); } //2.设置token到redis中 String token = UUID.randomUUID().toString(); Integer expire = RedisConstant.EXPORE; redisTemplate.opsForValue().set(String.format(RedisConstant.TOKEN_PREFIX, token, openid), openid, expire, TimeUnit.SECONDS); //3.设置token到cookie CookieUtil.set(response, CookieConstant.TOKEN, token, CookieConstant.EXPORE); //页面跳转 return new ModelAndView("redirect:" + projectUrlConfig.getSell() + "/sell/seller/order/list"); }
登出成功
1.登出接口”/sell/seller/logout”
@GetMapping("/logout") public ModelAndView logout(HttpServletRequest request, HttpServletResponse response, Map<String, Object> map) { //1.从cookie里面查询 Cookie cookie = CookieUtil.get(request, CookieConstant.TOKEN); if (cookie != null) { //2.清除redis redisTemplate.opsForValue().getOperations().delete((String.format(RedisConstant.TOKEN_PREFIX, cookie.getValue()))); //3.清除cookie,设置过期时间为0 CookieUtil.set(response, CookieConstant.TOKEN, null, 0); } map.put("msg",ResultEnum.LOGOUT_SUCCESS.getMessage()); map.put("url","/sell/seller/toLogin"); return new ModelAndView("common/success",map); }
1.获取cookie,并且注销cookie
2.清除redis
AOP实现身份验证
1.创建SellerAuthorizeAspect类,设置拦截点以及操作,有问题则抛出异常
package com.xiong.sell.aspect; import com.xiong.sell.constant.CookieConstant; import com.xiong.sell.constant.RedisConstant; import com.xiong.sell.exception.SellerAuthorizeException; import com.xiong.sell.utils.CookieUtil; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; /** * @author Xiong YuSong * 2019/1/28 13:00 */ @Aspect @Component @Slf4j public class SellerAuthorizeAspect { @Autowired private StringRedisTemplate redisTemplate; @Pointcut("execution(public * com.xiong.sell.controller.Seller*.*(..))" + " && !execution(public * com.xiong.sell.controller.SellerUserController.*(..))") public void verify() { } @Before("verify()") public void doVerify() { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); //查询cookie Cookie cookie = CookieUtil.get(request, CookieConstant.TOKEN); if (cookie == null) { log.warn("【登录校验】 cookie中没有token"); throw new SellerAuthorizeException(); } //查询redis String tokenValue = redisTemplate.opsForValue().get(String.format(RedisConstant.TOKEN_PREFIX, cookie.getValue())); if(StringUtils.isEmpty(tokenValue)){ log.warn("【登录校验】 redis中没有token"); throw new SellerAuthorizeException(); } } }
2.创建SellerAuthorizeException类
package com.xiong.sell.exception; /** * @author Xiong YuSong * 2019/1/28 16:23 */ public class SellerAuthorizeException extends RuntimeException { }
3.拦截SellerAuthorizeException异常并且给出操作
package com.xiong.sell.handler; import com.xiong.sell.config.ProjectUrlConfig; import com.xiong.sell.exception.SellerAuthorizeException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.ModelAndView; /** * @author Xiong YuSong * 2019/1/28 16:28 */ @ControllerAdvice public class SellExceptionHandler { @Autowired private ProjectUrlConfig projectUrlConfig; /** * 拦截登录异常 * @return */ @ExceptionHandler(value = SellerAuthorizeException.class) public ModelAndView handlerAuthorizeException(){ return new ModelAndView("redirect:" + projectUrlConfig.getSell() + "/sell/seller/toLogin"); } }
微信模板消息推送
1.创建PushMessage接口service
package com.xiong.sell.service; import com.xiong.sell.dto.OrderDTO; /** * @author Xiong YuSong * 2019/1/28 17:01 */ public interface PushMessage { void orderStatus(OrderDTO orderDTO); }
2.实现PushMassage接口
package com.xiong.sell.service.impl; import com.xiong.sell.dto.OrderDTO; import com.xiong.sell.service.PushMessage; import jdk.internal.org.objectweb.asm.tree.TryCatchBlockNode; import lombok.extern.slf4j.Slf4j; import me.chanjar.weixin.common.error.WxErrorException; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.bean.template.WxMpTemplateData; import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; /** * @author Xiong YuSong * 2019/1/28 17:02 */ @Service @Slf4j public class PushMessageImpl implements PushMessage { @Autowired private WxMpService wxMpService; @Override public void orderStatus(OrderDTO orderDTO) { WxMpTemplateMessage templateMessage = new WxMpTemplateMessage(); templateMessage.setTemplateId("sBkdCQcYxaVaIlhQ2wGuejjr_K1I0Rv2HVCZHIaNXdg"); templateMessage.setToUser("oIe231KOhNAGPWEIsE52bdPBA910"); List<WxMpTemplateData> data = new ArrayList<>(); data.add(new WxMpTemplateData("first","这是标题")); data.add(new WxMpTemplateData("keyword1",String.valueOf(orderDTO.getBuyerOpenid()))); data.add(new WxMpTemplateData("remark","这是结尾")); templateMessage.setData(data); try{ wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage); }catch (WxErrorException e){ log.info("【微信模板消息】发送失败,{}",e); } } }
3.取消订单则方法调用这个推送
推送成功
WebSocket接收并且推送新订单消息
1.引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
2.修改order/list.ftl页面的Script
<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-condensed table-hover table-bordered"> <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.orderStatusEnum.message}</td> <td>${orderDTO.payStatusEnum.message}</td> <td>${orderDTO.createTime}</td> <td><a href="/sell/seller/order/detail?orderId=${orderDTO.orderId}">详情</a></td> <td> <#if orderDTO.orderStatusEnum.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"> <#--上一页 小于1则无法显示上一页--> <#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.totalPages 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> <#--下一页 大于orderDTOPage.totalPages则无法显示下一页--> <#if currentPage gte orderDTOPage.totalPages> <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://localhost: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>
3.添加websocket配置
package com.xiong.sell.config; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import org.springframework.web.socket.server.standard.ServerEndpointExporter; /** * @author Xiong YuSong * 2019/1/28 17:32 */ @Component public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
4.建立websocket连接
package com.xiong.sell.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; /** * @author Xiong YuSong * 2019/1/28 17:32 */ @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(); } } } }
5.推送消息订单创建的service方法中插入下面一句话
webSocket.sendMessage("您有新的订单");