1. 程式人生 > 實用技巧 >SpringBoot微信點餐專案(三)——後臺管理和訊息

SpringBoot微信點餐專案(三)——後臺管理和訊息

iwehdio的部落格園:https://www.cnblogs.com/iwehdio/

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>
      
    • 效果:


iwehdio的部落格園:https://www.cnblogs.com/iwehdio/