• SpringBoot微信点餐项目(三)——后台管理和消息


    iwehdio的博客园:https://www.cnblogs.com/iwehdio/

    代码:https://github.com/iwehdio/SpringBoot-WXDC

    1、卖家订单

    • 请求:

      订单列表: /seller/order/list?page=1&size=10
      取消订单: /seller/order/cancel?orderId=1
      订单详情: /seller/order/detail?orderId=1
      完结订单: /seller/order/finish?orderId=1
      
    • 订单业务层OrderMasterService:

      • 查看订单列表:

        • 控制器:

          @Controller
          @RequestMapping("/seller/order")
          public class SellerOrderController {
              @Autowired
              private OrderMasterService masterService;
              @GetMapping("/list")
              public ModelAndView list(@RequestParam(value = "page",defaultValue = "1") Integer page,
                                       @RequestParam(value = "size",defaultValue = "10") Integer size,
                                       Map<String,Object> map){
                  PageRequest request = new PageRequest(page-1, size);
                  Page<OrderDTO> orderDTOPage = masterService.findAll(request);
                  map.put("orderDTOPage",orderDTOPage);
                  return new ModelAndView("order/list",map);
              }
          }
          
        • 业务层:

          @Override
          public Page<OrderDTO> findAll(Pageable pageable) {
              Page<OrderMaster> masterPage = masterDao.findAll(pageable);
              List<OrderDTO> orderDTOList = OrderMaster2OrderDTOConverter.convert(masterPage.getContent());
              return new PageImpl<OrderDTO>(orderDTOList, pageable, masterPage.getTotalElements());
          }
          
      • 根据数据库中订单状态和支付状态的code获取对应的枚举的message信息:

        • 创建接口,两个状态的枚举都实现该接口:

          public interface CodeEnum {
              Integer getCode();
          }
          
        • 创建工具类:

          public class EnumUtil {
              public static <T extends CodeEnum> T getByCode(Integer code, Class<T> enumClass){
                  for (T enumConstant : enumClass.getEnumConstants()) {
                      if (code.equals(enumConstant.getCode())){
                          return enumConstant;
                      }
                  }
                  return null;
              }
          }
          
        • 在OrderDTO中创建对应方法:

          @JsonIgnore
          public OrderStatusEnum getOrderStatusEnum() {
              return EnumUtil.getByCode(orderStatus, OrderStatusEnum.class);
          }
          @JsonIgnore
          public PayStatusEnum getpayStatusEnum() {
              return EnumUtil.getByCode(payStatus, PayStatusEnum.class);
          }
          
        • 前端使用:

          <#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().getMessage()}</td>
                  <td>${orderDTO.getpayStatusEnum().getMessage()}</td>
                  <td>${orderDTO.createTime}</td>
                  <td>
                      <a href="/sell/seller/order/detail?orderId=${orderDTO.orderId}">详情</a>
                  </td>
                  <td>
                      <#if orderDTO.getOrderStatusEnum().getMessage() =="新订单">
                          <a href="/sell/seller/order/cancel?orderId=${orderDTO.orderId}">取消</a>
                      </#if>
                  </td>
              </tr>
          </#list >
          
      • 翻页效果:

        <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>
        
      • 取消订单按钮:

        • 控制器:

          private String returnUrl = "/sell/seller/order/list";
          @GetMapping("/cancel")
          public ModelAndView cancel(@RequestParam("orderId") String orderId,
                                     Map<String,Object> map){
              OrderDTO orderDTO;
              try {
                  orderDTO = masterService.findOne(orderId);
                  masterService.cancel(orderDTO);
              } catch (SellException e) {
                  map.put("msg", e.getMessage());
                  map.put("url",returnUrl);
                  return new ModelAndView("sellerOrder/error",map);
              }
          
              map.put("msg", "SUCCESS");
              map.put("url",returnUrl);
              return new ModelAndView("sellerOrder/success",map);
          }
          
        • 前端:

          <td>
              <#if orderDTO.getOrderStatusEnum().getMessage() !="已取消">
                  <a href="/sell/seller/order/cancel?orderId=${orderDTO.orderId}">取消</a>
              </#if>
          </td>
          
        • 所要跳转的页面:

          <body>
          <div class="container">
              <div class="row clearfix">
                  <div class="col-md-12 column">
                      <div class="alert alert-dismissable alert-success">
                          <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
                          <h4>
                             成功!
                          </h4> <strong>${msg}</strong><a href="${url}" class="alert-link"> 3s后返回</a>
                      </div>
                  </div>
              </div>
          </div>
          <script>
              setTimeout('location.href="${url}"',3000);
          </script>
          </body>
          
      • 详情按钮:

        • 控制器:

          @GetMapping("/detail")
              public ModelAndView detail(@RequestParam("orderId") String orderId,
                                         Map<String,Object> map) {
                  OrderDTO orderDTO;
                  try {
                      orderDTO = masterService.findOne(orderId);
                  } catch (SellException e) {
                      map.put("msg", e.getMessage());
                      map.put("url",returnUrl);
                      return new ModelAndView("sellerOrder/error",map);
                  }
                  map.put("orderDTO", orderDTO);
                  return new ModelAndView("sellerOrder/detail",map);
              }
          
        • 前端:

          <div class="container">
              <div class="row clearfix">
                  <div class="col-md-12 column">
                      <table class="table table-hover table-bordered">
                          <thead>
                          <tr>
                              <th>订单id</th>
                              <th>订单金额</th>
                          </tr>
                          </thead>
                          <tbody>
                          <tr>
                              <td>${orderDTO.orderId}</td>
                              <td>${orderDTO.orderAmount}</td>
                          </tr>
                          </tbody>
                      </table>
                  </div>
          
                  <div class="col-md-12 column">
                      <table class="table table-hover table-bordered">
                          <thead>
                          <tr>
                              <th>商品id</th>
                              <th>商品名称</th>
                              <th>价格</th>
                              <th>数量</th>
                              <th>总价</th>
                          </tr>
                          </thead>
                          <tbody>
                          <#list orderDTO.orderDetailList as detail>
                          <tr>
                              <td>${detail.productId}</td>
                              <td>${detail.productName}</td>
                              <td>${detail.productPrice}</td>
                              <td>${detail.productQuantity}</td>
                              <td>${detail.productPrice * detail.productQuantity}</td>
                          </tr>
                          </#list>
                          </tbody>
                      </table>
                  </div>
          
                  <div class="col-md-12 column">
                      <#if orderDTO.getOrderStatusEnum().getMessage() != "已取消">
                          <a href="/sell/seller/order/finish?orderId=${orderDTO.orderId}" type="button" class="btn btn-default btn-primary">完结订单</a>
                          <a href="/sell/seller/order/cancel?orderId=${orderDTO.orderId}" type="button" class="btn btn-default btn-danger">取消订单</a>
                      </#if>
                  </div>
          
              </div>
          </div>
          
        • 完结订单:

          @GetMapping("/finish")
          public ModelAndView finish(@RequestParam("orderId") String orderId,
                                     Map<String,Object> map) {
              OrderDTO orderDTO;
              try {
                  orderDTO = masterService.findOne(orderId);
                  masterService.finish(orderDTO);
              } catch (SellException e) {
                  map.put("msg", e.getMessage());
                  map.put("url",returnUrl);
                  return new ModelAndView("sellerOrder/error",map);
              }
              map.put("msg", "SUCCESS");
              map.put("url",returnUrl);
              return new ModelAndView("sellerOrder/success",map);
          }
          

    2、商品列表

    • 请求:

      商品列表: /seller/product/list?page=1&size=10
      商品上架: /seller/product/onSale?productId=1
      商品下架: /seller/product/offSale?productId=1
      商品新增: /seller/product/index
      商品修改: /seller/product/index?productId=1
      
    • 卖家后端侧边栏(需要css样式):

      <nav class="navbar navbar-inverse navbar-fixed-top" id="sidebar-wrapper" role="navigation">
          <ul class="nav sidebar-nav">
              <li class="sidebar-brand">
                  <a href="#">
                      卖家管理系统
                  </a>
              </li>
              <li>
                  <a href="/sell/seller/order/list"><i class="fa fa-fw fa-list-alt"></i> 订单</a>
              </li>
              <li class="dropdown open">
                  <a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="true"><i class="fa fa-fw fa-plus"></i> 商品 <span class="caret"></span></a>
                  <ul class="dropdown-menu" role="menu">
                      <li class="dropdown-header">操作</li>
                      <li><a href="/sell/seller/product/list">列表</a></li>
                      <li><a href="/sell/seller/product/index">新增</a></li>
                  </ul>
              </li>
              <li class="dropdown open">
                  <a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="true"><i class="fa fa-fw fa-plus"></i> 类目 <span class="caret"></span></a>
                  <ul class="dropdown-menu" role="menu">
                      <li class="dropdown-header">操作</li>
                      <li><a href="/sell/seller/category/list">列表</a></li>
                      <li><a href="/sell/seller/category/index">新增</a></li>
                  </ul>
              </li>
      
              <li>
                  <a href="/sell/seller/logout"><i class="fa fa-fw fa-list-alt"></i> 登出</a>
              </li>
          </ul>
      </nav>
      
    • 引入侧边栏:

      <link rel="stylesheet" href="/sell/css/style.css">
      
      <div id="wrapper" class="toggled">
          <!--    侧边栏,这里的引入路径是从templates下开始的绝对路径    -->
          <#include "/sellerOrder/nav.ftl">
      
          <!--    主体内容在这个div中   -->
          <div id="page-content-wrapper">
      
          </div>
      </div>
      
    • 商品列表控制器:

      @Controller
      @RequestMapping("/seller/product")
      public class SellerProductController {
          @Autowired
          private ProductInfoService productService;
          @Autowired
          private ProductCategoryService categoryService;
          @GetMapping("/list")
          public ModelAndView list(@RequestParam(value = "page",defaultValue = "1") Integer page,
                                   @RequestParam(value = "size",defaultValue = "10") Integer size,
                                   Map<String,Object> map){
              PageRequest request = new PageRequest(page-1, size);
              Page<ProductInfo> productInfoPage = productService.findAll(request);
              map.put("productInfoPage",productInfoPage);
              map.put("currentPage",page);
              map.put("size",size);
              return new ModelAndView("sellerProduct/list",map);
          }
      }
      
    • 商品上下架:

      • 商品信息业务层:

        @Override
        public ProductInfo onSale(String pruductId) {
            ProductInfo productInfo = dao.findOne(pruductId);
            if (productInfo == null) {
                throw new SellException(ResultEnum.PRODUCT_NOT_EXIST);
            }
            if (productInfo.getProductStatusEnum() == ProductStatusEnum.UP) {
                throw new SellException(ResultEnum.PRODUCT_STATUS_ERROR);
            }
            productInfo.setProductStatus(ProductStatusEnum.UP.getCode());
            return dao.save(productInfo);
        }
        
      • 控制器:

        private String returnUrl = "/sell/seller/product/list";
        @GetMapping("/onSale")
        public ModelAndView onSale(@RequestParam("productId") String productId,
                                   Map<String,Object> map) {
            try {
                productService.onSale(productId);
            } catch (SellException e) {
                map.put("msg", e.getMessage());
                map.put("url",returnUrl);
                return new ModelAndView("common/error",map);
            }
            map.put("msg", "SUCCESS");
            map.put("url",returnUrl);
            return new ModelAndView("common/success",map);
        }
        
    • 商品新增修改:

      • 跳转商品修改页并回显:

        • 控制器:

          @GetMapping("/index")
          public ModelAndView index(@RequestParam(value = "productId",required = false) String productId,
                                    Map<String,Object> map) {
              if (productId!=null && !productId.isEmpty()) {
                  ProductInfo productInfo = productService.findOne(productId);
                  map.put("productInfo",productInfo);
              }
              List<ProductCategory> categoryList = categoryService.findAll();
              map.put("categoryList", categoryList);
              return new ModelAndView("sellerProduct/index",map);
          
          }
          
        • 前端:

          <html>
          <head>
              <meta charset="utf-8">
              <link rel="stylesheet" href="/sell/css/style.css">
              <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
          </head>
          <body>
          <div id="wrapper" class="toggled">
              <!--    侧边栏    -->
              <#include "common/nav.ftl">
          
              <!--    主体内容在这个div中   -->
              <div id="page-content-wrapper">
                  <div class="container-fluid">
                  <div class="row clearfix">
                      <div class="col-md-12 column">
                          <form role="form" method="post" action="/sell/seller/product/save">
                              <div class="form-group">
                                  <label>名称</label>
                                  <input name="productName" value="${(productInfo.productName)!''}" type="text" class="form-control" >
                              </div>
                              <div class="form-group">
                                  <label>库存</label>
                                  <input name="productName" value="${(productInfo.productStock)!''}" type="number" class="form-control" >
                              </div>
                              <div class="form-group">
                                  <label>描述</label>
                                  <input name="productName" value="${(productInfo.productDescription)!''}" type="text" class="form-control" >
                              </div>
                              <div class="form-group">
                                  <label>图片</label>
                                  <img height="100" width="100" src="${(productInfo.productIcon)!''}" alt="">
                                  <input name="productName" value="${(productInfo.productIcon)!''}" type="text" class="form-control" >
                              </div>
                              <div class="form-group">
                                  <label>类目</label>
                                  <select name="categoryType" class="form-control">
                                      <#list categoryList as category>
                                          <option value="${category.categoryType}"
                                              <#if productInfo.categoryType == category.categoryType>
                                                  selected
                                              </#if>
                                          >${category.categoryName}
                                          </option>
                                      </#list>
                                  </select>
                              </div>
                              <input type="hidden" name="productId" value="${(productInfo.productId)!''}">
                              <button type="submit" class="btn btn-default">提交</button>
                          </form>
                      </div>
                  </div>
              </div>
              </div>
          </div>
          </body>
          </html>
          
      • 修改和新增:

        • 表单类:

          public class ProductForm {
              private String productId;
              private String productName;
              private BigDecimal productPrice;
              private Integer productStock;
              private String productDescription;
              private String productIcon;
              private Integer categoryType;
          }
          
        • 控制器:

          @PostMapping("/save")
          public ModelAndView save(@Valid ProductForm form,
                                   BindingResult bindingResult,
                                   Map<String,Object> map){
              if (bindingResult.hasErrors()) {
                  map.put("msg", bindingResult.getFieldError().getDefaultMessage());
                  map.put("url","/sell/seller/product/index");
                  return new ModelAndView("common/error",map);
              }
              ProductInfo productInfo = new ProductInfo();
              try {
                  if (form.getProductId()!=null && !form.getProductId().isEmpty()) {
                      productInfo = productService.findOne(form.getProductId());
                  }else {
                      form.setProductId(KeyUtil.genUniqueKey());
                      productInfo.setProductStatus(ProductStatusEnum.DOWN.getCode());
                  }
                  BeanUtils.copyProperties(form,productInfo);
                  productService.save(productInfo);
              } catch (Exception e) {
                  map.put("msg", e.getMessage());
                  map.put("url","/sell/seller/product/index");
                  return new ModelAndView("common/error",map);
              }
              map.put("msg", "SUCCESS");
              map.put("url",returnUrl);
              return new ModelAndView("common/success",map);
          }
          

    3、类目列表

    • 请求:

      类名列表: /seller/category/list
      类名新增: /seller/category/index
      类名修改: /seller/category/index?categoryId=1
      
    • 类目列表控制器:

      @Controller
      @RequestMapping("/seller/category")
      public class SellerCategoryController {
          @Autowired
          private ProductCategoryService categoryService;
          @GetMapping("/list")
          public ModelAndView list(Map<String,Object> map) {
              List<ProductCategory> categoryList = categoryService.findAll();
              map.put("categoryList", categoryList);
              return new ModelAndView("sellerCategory/list",map);
          }
      }
      
    • 类目修改控制器:

      @GetMapping("/index")
      public ModelAndView index(@RequestParam(value = "categoryId",required = false) Integer categoryId,
                                Map<String,Object> map) {
          if (categoryId != null) {
              ProductCategory productCategory = categoryService.findOne(categoryId);
              map.put("category",productCategory);
          }
          return new ModelAndView("sellerCategory/index",map);
      }
      
    • 类目保存:

      • 表单类:

        public class CategoryForm {
            private Integer categoryId;
            private String categoryName;
            private Integer categoryType;
        }
        
      • 控制器:

        @PostMapping("/save")
        public ModelAndView save(@Valid CategoryForm form,
                                 BindingResult bindingResult,
                                 Map<String,Object> map) {
            if (bindingResult.hasErrors()) {
                map.put("msg",bindingResult.getFieldError().getDefaultMessage());
                map.put("url","/sell/seller/category/index");
                return new ModelAndView("common/error",map);
            }
            ProductCategory productCategory = new ProductCategory();
            try {
                if (form.getCategoryId() !=null) {
                    productCategory = categoryService.findOne(form.getCategoryId());
                }
                BeanUtils.copyProperties(form,productCategory);
                categoryService.save(productCategory);
            } catch (Exception e) {
                map.put("msg",e.getMessage());
                map.put("url","/sell/seller/category/index");
                return new ModelAndView("common/error",map);
            }
            map.put("msg","SUCCESS");
            map.put("url","/sell/seller/category/list");
            return new ModelAndView("common/success",map);
        }
        
    • 前端页面与之前类似。

    4、登入登出

    • 什么是分布式系统:

      • 旨在支持应用程序和服务的开发,可以利用物理架构由多个自治的处理元素,不共享主内存,但通过网络发送消息合作。
      • 与集群的联系:集群中的节点都是同一个角色,而分布式是不同的功能模块。
      • 水平扩展:多个服务器提供相同的服务。
      • 垂直扩展:多个服务器提供不同的服务,或者说一块大的服务被拆分为多个功能模块。
    • session:

      • 在无状态的Http请求中获取和保存状态。
      • 分布式session,水平或垂直扩展的不同服务器需要保持session统一。
      • 可以使用redis集群,所有的session都存入该集群中。
    • 创建卖家信息表:

      • MySQL:

        CREATE TABLE `seller_info`(
        	`seller_id` VARCHAR(32) PRIMARY KEY,
        	`username` VARCHAR(32) NOT NULL,
        	`password` VARCHAR(32) NOT NULL,
        	`openid` VARCHAR(64) NOT NULL,
        	`create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
        	`update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
        );
        
      • 实体类:

        @Entity
        public class SellerInfo {
            @Id
            private String sellerId;
            private String username;
            private String passWord;
            private String openid;
        }
        
      • dao:

        public interface SellerInfoDao extends JpaRepository<SellerInfo,String> {
            SellerInfo findByOpenid(String openid);
            SellerInfo save(SellerInfo sellerInfo);
        }
        
      • 业务层:

        @Service
        public class SellerInforServiceImpl implements SellerInfoService {
            @Autowired
            private SellerInfoDao dao;
            @Override
            public SellerInfo findSellerInfoByOpenid(String openid) {
                return dao.findByOpenid(openid);
            }
        }
        
    • 微信扫描获取openid:

      • 开发文档:网站应用微信登录。但是需要微信开放平台账户,先搁置。
      • 具体原理:微信扫码登录原理
      • 具体获取openid的流程与之前买家端类似,也是先获取code,再根据code获取openid。
    • 阿里云安装redis:

      docker pull redis:3.2.8-alpine 
      docker run -itd --name redis_wxdc -p 6379:6379 redis:3.2.8-alpine --requirepass 123456
      
    • SpringBoot配置文件:

      Spring: 
      	redis:
              host: ip地址
              port: 6379
              password: 
      
    • 登入:

      • openid与数据库中的数据匹配。如果不匹配就跳转错误页面。
      • 设置token到redis。token由UUID生成,是存储的键,存储的值是openid,并且设置过期时间。
      • 设置token到Cookie。键是"token",值是生成的UUID。
      • 校验登录状态,通过Cookie中的token查询redis中有没有对应的数据。
    • 登出:

      • 从Cookie中获取token,并从Cookie中清除。
      • 根据token清除redis中的openid。
    • AOP实现身份验证:

      • 创建切面类,切入点表达式设置为Seller开头的Controller,且不为登录方法。
      • 创建前置通知,获取请求,查询Cookie和redis。
      • 如果校验不通过则抛出一个登录校验异常。
      • 使用@ControllerAdvice全局异常处理。
    • 简单的账户密码登录:

      • 在SellerInfoDao中添加方法SellerInfo findByUsername(String username)。在业务层也增加对应的方法。

      • 登入:

        @Controller
        @RequestMapping("/seller")
        public class SellerLogController {
            private final String prefix = "token_";
            private final Integer expire = 7200; //过期时间
        
            @Autowired
            private SellerInfoService sellerInfoService;
            @Autowired
            private StringRedisTemplate redisTemplate;
            @GetMapping("/login")
            public ModelAndView login(){
                return new ModelAndView("login/login");
            }
        
            @GetMapping("/loginning")
            public ModelAndView loginning(@RequestParam("username") String username,
                                          @RequestParam("password") String password,
                                          Map<String,Object> map,
                                          HttpServletResponse response){
                SellerInfo sellerInfo = sellerInfoService.findSellerInfoByUsername(username);
                if (sellerInfo==null || sellerInfo.getPassword()==null || !sellerInfo.getPassword().equals(password)) {
                    map.put("msg","登录失败");
                    map.put("url","/sell/seller/login");
                    return new ModelAndView("common/error",map);
                }
                String token = UUID.randomUUID().toString();
                redisTemplate.opsForValue().set(prefix + token, username, expire, TimeUnit.SECONDS);
                CookieUtil.saveCookie(response,prefix, prefix + token);
                return new ModelAndView("redirect:/seller/order/list");
            }
        }
        
      • 登出:

        @GetMapping("/logout")
        public ModelAndView logout(HttpServletRequest request,
                                   HttpServletResponse response,
                                   Map<String,Object> map) {
            String cookie = CookieUtil.removeCookie(request, response,prefix);
            if (cookie!=null) {
                redisTemplate.opsForValue().getOperations().delete(cookie);
            }
            map.put("msg","登出成功");
            map.put("url","#");
            return new ModelAndView("common/success",map);
        }
        
      • Cookie工具类:

        public class CookieUtil {
            public static void saveCookie(HttpServletResponse response,String key,String value) {
                Cookie cookie = new Cookie(key, value);
                response.addCookie(cookie);
            }
            public static Cookie findCookie(HttpServletRequest request,String key){
                Cookie[] cookies = request.getCookies();
                if (cookies!=null) {
                    for (Cookie cookie : cookies) {
                        if (key.equals(cookie.getName())) {
                            return cookie;
                        }
                    }
                }
                return null;
            }
        
            public static String removeCookie(HttpServletRequest request,HttpServletResponse response, String key){
                Cookie cookie = findCookie(request, key);
                if (cookie!=null){
                    cookie.setMaxAge(0);
                    response.addCookie(cookie);
                    return cookie.getValue();
                }
                return null;
            }
        }
        
    • AOP实现身份验证:

      • Aspect切面类:

        @Aspect
        @Component
        public class SellerAuthAspect {
            private final String prefix = "token_";
            @Autowired
            private StringRedisTemplate redisTemplate;
            @Pointcut("execution(public * cn.iwehdio.sell.controller.Seller*.*(..))" +
                    "&& !execution(public * cn.iwehdio.sell.controller.SellerLogController.*(..))")
            public void verify(){}
        
            @Before("verify()")
            public void doVerify(){
                ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                HttpServletRequest request = attributes.getRequest();
                Cookie cookie = CookieUtil.findCookie(request, prefix);
                if (cookie==null){
                    throw new SellerAuthException();
                }
                String s = redisTemplate.opsForValue().get(cookie.getValue());
                if (s==null){
                    throw new SellerAuthException();
                }
            }
        }
        
      • 全局异常处理器:

        @ControllerAdvice
        public class SellerAuthExceptionHandler {
            @ExceptionHandler(SellerAuthException.class)
            public ModelAndView handlerSellerAuth(){
                return new ModelAndView("redirect:/seller/login");
            }
        }
        

    5、消息推送

    • 微信模板消息文档:模板消息接口

    • 设置模板消息接口:

      • 模板id:ScqECQqmMIsSVnHiw8LhlNUY17bq9qa9TkN5zZO1nBc。

      • 模板内容:

        {{first.DATA}} 
        商家名称:{{sellerName.DATA}} 
        订单号:{{orderId.DATA}} 
        状态:{{orderStatus.DATA}} 
        总价:{{orderAmount.DATA}} 
        派送地址:{{orderAddress.DATA}} 
        {{endMsg.DATA}}
        
    • 业务层实现:

      public interface PushMessageService {
          void orderStatus(OrderDTO orderDTO);
      }
      
      @Service
      public class PushMessageServiceImpl implements PushMessageService {
          @Autowired
          private WxMpService wxMpService;
          @Autowired
          private WechatAccountConfig wechatAccountConfig;
          private final String TEMPLATEID = "confirmMessage";
          @Override
          public void orderStatus(OrderDTO orderDTO) {
              WxMpTemplateMessage templateMessage = new WxMpTemplateMessage();
              templateMessage.setTemplateId(wechatAccountConfig.getTemplateIds().get(TEMPLATEID));
              templateMessage.setToUser(orderDTO.getBuyerOpenid());
              List<WxMpTemplateData> data = Arrays.asList(
                      new WxMpTemplateData("first","请确认订单"),
                      new WxMpTemplateData("sellerName","iwehdio"),
                      new WxMpTemplateData("orderId",orderDTO.getOrderId()),
                      new WxMpTemplateData("orderStatus",orderDTO.getOrderStatusEnum().getMessage()),
                      new WxMpTemplateData("orderAmount",orderDTO.getOrderAmount().toString()),
                      new WxMpTemplateData("orderAddress",orderDTO.getBuyerAddress()),
                      new WxMpTemplateData("endMsg","---------------------")
              );
              templateMessage.setData(data);
              try {
                  wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage);
              } catch (WxErrorException e) {
                  e.printStackTrace();
              }
          }
      }
      
    • 效果:

    • WebSocket:

      • 分为客户端和服务端,客户端接收消息,服务端发送消息。

      • 客户端在本项目中就是买家后台管理页面的前端页面。

      • WebSocket配置类:

        @Component
        public class WebSocketConfig {
            @Bean
            public ServerEndpointExporter serverEndpointExporter(){
                return new ServerEndpointExporter();
            }
        }
        
      • 后端发送WebSocket请求:

        @Component
        @ServerEndpoint("/webSocket")
        public class WebSocket {
            private Session session;
            private static CopyOnWriteArraySet<WebSocket> webSockets = new CopyOnWriteArraySet<>();
        
            @OnOpen
            public void onOpen(Session session) {
                this.session = session;
                webSockets.add(this);
            }
            @OnClose
            public void onClose() {
                webSockets.remove(this);
            }
            @OnMessage
            public void onMessage(String message) {
        
            }
        
            public void sendMessage(String message) {
                for (WebSocket webSocket : webSockets) {
                    try {
                        webSocket.session.getBasicRemote().sendText(message);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        
        }
        
      • 前端处理WebSocket消息:

        <div class="modal fade" id="msgmodal" 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" id="modal-body">
                        订单号:
                    </div>
                    <div class="modal-footer">
                        <button 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>
        
        <script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
            <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.5/js/bootstrap.min.js"></script>
        <script>
            var webSocket = null;
            if('WebSocket' in window) {
                webSocket = new WebSocket('ws://n5a8qq.natappfree.cc/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);
                $('#modal-body').text($('#modal-body').text()+ event.data);
                $("#msgmodal").modal('show');
            };
            webSocket.onerror = function (event) {
                console.log('出错');
            };
            window.onbeforeunload = function () {
                webSocket.close();
            };
        </script>
        
      • 效果:

        image-20201212155007395

    iwehdio的博客园:https://www.cnblogs.com/iwehdio/
  • 相关阅读:
    [Leetcode Weekly Contest]285
    [Leetcode Weekly Contest]286
    [Leetcode Weekly Contest]284
    [Leetcode Weekly Contest]287
    阿凡达机器人简介
    PS常用组合键
    PhotoshopCS6 后退多步
    如何卸载Creative Cloud?
    五月实际
    SSO 方案演进
  • 原文地址:https://www.cnblogs.com/iwehdio/p/14125387.html
Copyright © 2020-2023  润新知