微信点餐系统(十二)-买家与卖家端连通

 章节感悟

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.设置openidcookie,创建一个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();
        }
    }
}
View Code

2.创建SellerAuthorizeException

package com.xiong.sell.exception;

/**
 * @author Xiong YuSong
 * 2019/1/28 16:23
 */
public class SellerAuthorizeException extends RuntimeException {
}
View Code

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");
    }
} 
View Code

微信模板消息推送

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);
}
View Code

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);
        }
    }
}
View Code

3.取消订单则方法调用这个推送

推送成功

 

WebSocket接收并且推送新订单消息

1.引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
View Code

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>
View Code

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();
    }
}
View Code 

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();
            }
        }
    }
}
View Code

5.推送消息订单创建的service方法中插入下面一句话

webSocket.sendMessage("您有新的订单");

 

posted @ 2019-01-28 18:13  寻找梦想的大熊  阅读(1201)  评论(2编辑  收藏  举报