微信点餐系统技术总结

                                     微信点餐系统技术总结


 

1.项目开发步骤

1.项目设计

1.角色划分

      买家端(手机端)  卖家端(PC)

2.功能模块划分

      详细设计个各个功能模块

        买家         卖家

   商品商品列表数据

   订单订单创建  订单查询 订单取消 ...

   类目订单管理   商品管理 类目管理 ...

   

2.架构和基础框架

 SSH 框架的了解和使用

 Mybatis 的两种配置方法

 Redis 的缓存作用 分布式锁

 

 Aop 身份验证 

 等等

 

       

3.数据库设计

      设计表的成员   表与表之间的关系  确定每个表的主键 设计属性之间的关联...

4开发日志的使用

这里用Slf4j

1.导入依赖

 
    org.projectlombok
    lombok

 

2.加上注解

@Slf4j

 

3..使用

log.error("【创建订单】购物车不能为空");

log.info("【微信支付】发起支付request={}",JsonUtil.toJson(payRequest));

 

5. Logback的配置 

需求:区分info 和error 日志 ,每天产生一个日志文件.

1.application.xml文件的配置

 logging:
  pattern:
    console: 
"%d - %msg%n"
  path: /var/log/tomcat/
  file: /var/log/tomcat/sell.log
  level:
    com.imooc.LoggerTest: 
debug

 

path 和 file 二选一即可

Path:产生文件的位置

File:产生文件的位置和名字

Level: 指定某个类的日志级别

2.Logback-spring.xml配置文件

Resouces目录下建立 Logback-spring.xml配置文件

xml version="1.0" encoding="UTF-8" ?>




    name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
        class="ch.qos.logback.classic.PatternLayout">
            
                %d - %msg%n
            
        
    


    name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">

        class="ch.qos.logback.classic.filter.LevelFilter">
            ERROR
            
        

        
            
                %msg%n
            
        

        
        class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            
            /home/hk/log/tomcat/info.%d.log
        
    



    name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">

        class="ch.qos.logback.classic.filter.ThresholdFilter">
            ERROR
        

        
            
                %msg%n
            
        

        
        class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            
            /home/hk/log/tomcat/error.%d.log
        
    



    level="info">
        ref="consoleLog"/>
        ref="fileInfoLog"/>
        ref="fileErrorLog"/>
    

 

简单的日志需求选择第一种即可,复杂的需要选择第二种配置

注意这里写配置文件时提示不是很友好,不要打错!

6. 买家类目

1.买家类目-dao

1.数据库的连接:

 添加依赖:

 

Mysql的驱动


   mysql
   mysql-connector-java

Jap的驱动


    org.springframework.boot
    spring-boot-starter-data-jpa

配置数据库的信息:

在application.yml配置文件中`配置

spring:
  datasource:
    driver-class-name: 
com.mysql.jdbc.Driver
    username: root
    password: 741623
    url: jdbc:mysql://127.0.0.1:3306/food?characterEncoding=utf8&useSSL=false&zeroDateTimeBehavior=convertToNull
  jpa:
    show-sql: true

 

2.建立和数据表映射过来对应的对象

对象类需要加入

@Entity 注解

导入包 import javax.persistence.Entity

 

默认下类名和表明一致

类名中除了第一个字母,其他字母的大写在表中会自动用_加对应的小写字母替代

 表名: product_category 

 类名:ProductCategory

 

也可以加注解来添加映射关系

@Table(name = “ product_category”)

 

对象里面的属性名也必须和数据表里面的属性名对应

否则需要在属性之前添加注解@Column(name = 属性名)

对象的主键用 @Id 标识  导入下面的包

import javax.persistence.Id;

对象自增用  @GeneratedValue 表示

import javax.persistence.GeneratedValue;

 

若类名和属性名都与数据表对应则这个类就和 product_category数据表建立映射关系.

注意建议用默认值 

类名前面添加 @Data

导入包import lombok.Data;

依赖文件和之前日志的依赖一样

这个注解可以省去 get 和 set 方法

 

 

DAO 层的编写

与对象做关联

public interface ProductCategoryRepository extends JpaRepositoryInteger>

继承接口 JpaRepository<>  第一个参数表明对象类的名称 第二个参数表明对象的主键   

 

2.买家类目-service    

 

1.先建立CategoryService接口

在接口中列出可能需要用的方法

再建立CategoryServiceImpl类实现接口 CategoryService

在 CategoryServiceImpl 需要加入注解 @Service

导入包import org.springframework.stereotype.Service;

 

3.买家类目-controller

由于类目不存在单独给买家端的接口,所以不存在 controller

 

7.买家商品

基本步骤和上面买家类目实现一样

注重Service的编写

Dao  ---> Service   ---> Controller

 

------------------技术技巧------------------------------------------------

1.分页查询

查询的时候传入 Pageable pageable对象可以分页查询

Page findAll(Pageable pageable);

导入包 import org.springframework.data.domain.Pageable;

传入Pageable pageable对象 返回的时一个 Page<>对象

 

 

2.枚举类的使用

当用数字表示一些商品的情况或者订单的情况时,一旦数目过多就容易混淆,

这时候可以用到枚举类  Enum, 可以建立一个单独的包来管理各个枚举类

枚举类可以将对应的数字信息和和文字信息结合起来,可以更加方便的了解数字对应的信息

package com.imooc.enums;

import lombok.Getter;

/**
 * 商品状态
 * Created by 廖师兄
 * 2017-05-09 17:33
 */
@Getter
public enum ProductStatusEnum implements CodeEnum {
    UP(0"在架"),
    DOWN(1"下架")
    ;

    private Integer code;

    private String message;

    ProductStatusEnum(Integer codeString message) {
        this.code = code;
        this.message = message;
    }
}

 

 

3.封装返回结果

返回给前端的类目信息分三层结构

 

1. 最外层(ResultVO): 包含code(错误码) , msg(提示信息) , 泛形 T(包含返回的具体信息)

package com.imooc.VO;

import lombok.Data;

/**
 * http请求返回的最外层对象
 * Created by 廖师兄
 * 2017-05-12 14:13
 */
@Data
public class ResultVO<T> {

    /** 错误码. */
    
private Integer code;

    /** 提示信息. */
    
private String msg;

    /** 具体内容. */
    
private data;
}

 

2.中间层(ProductVO): 商品类目信息,包含 类目名字 类目编号 商品信息 List  (里面包含属于这个类目的商品信息).

package com.imooc.VO;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

import java.util.List;

/**
 * 商品(包含类目)
 * Created by 廖师兄
 * 2017-05-12 14:20
 */
@Data
public class ProductVO {

    @JsonProperty("name")
    private String categoryName;

    @JsonProperty("type")
    private Integer categoryType;

    @JsonProperty("foods")
    private List productInfoVOList;
}

 

 

3.最里层(ProductInfoVO): 商品详情包含用户能看到的商品信息 )

package com.imooc.VO;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

import java.math.BigDecimal;

/**
 * 商品详情
 * Created by 廖师兄
 * 2017-05-12 14:25
 */
@Data
public class ProductInfoVO {

    @JsonProperty("id")
    private String productId;

    @JsonProperty("name")
    private String productName;

    @JsonProperty("price")
    private BigDecimal productPrice;

    @JsonProperty("description")
    private String productDescription;

    @JsonProperty("icon")
    private String productIcon;
}

 

 

4.对查询请求的反应和相应的操作

1.查询所有上架商品 

2.查询类目(一次性查询)

这里用到一个 lambda 表达式(从商品对象中获得类目属性)

List categoryTypeList = productInfoList.stream()
        .map(e -> e.getCategoryType())
        .collect(Collectors.toList());

作用是从 对象集合productInfoList中抽取一个属性集合 categoryTypeList

作用等价于:

List categoryTypeList = new ArrayList<>();
传统方法
for (ProductInfo productInfo : productInfoList) {
    categoryTypeList.add(productInfo.getCategoryType());
}

 

根据获得的类目type属性集合来得到类目对象集合

List productCategoryList = categoryService.findByCategoryTypeIn(categoryTypeList);

 

3.数据拼装(将对应的商品对象添加到对应的类目List下去)

新建一个ProductVO(商品类目信息包含 类目名字 类目编号 商品信息 List )的 List

List productVOList = new ArrayList<>();

 

对类目对象集合循环遍历

for (ProductCategory productCategory: productCategoryList) {

新建一个ProductVO对象

设置CategoryType和CategoryName 属性值

ProductVO productVO = new ProductVO();
productVO.setCategoryType(productCategory.getCategoryType());
productVO.setCategoryName(productCategory.getCategoryName());

新建一个 ProductInfoVO 的List --->roductInfoVOList

List productInfoVOList = new ArrayList<>();

 

在类目循环体内部对商品信息金额和循环遍历

for (ProductInfo productInfo: productInfoList) {

If 判断 若满足商品对象的类目属性与当前类目对象的类目属性相同

if (productInfo.getCategoryType().equals(productCategory.getCategoryType())) {

满足上述 if 判断则新建一个 productInfoVO 对象

运用工具类BeanUtils中的copyProperties()方法将productInfo中的属性值拷贝到productInfoVO对象中(拷贝的值包括两个对象中名字和类型一样的属性)

ProductInfoVO productInfoVO = new ProductInfoVO();
BeanUtils.copyProperties(productInfoproductInfoVO);
productInfoVOList.add(productInfoVO);

设置 productVO 的 ProductInfoVOList  的值为 productInfoVOList        

productVO.setProductInfoVOList(productInfoVOList);

将   productVO  添加到  productVOList           

productVOList.add(productVO);

 

8.买家订单

Dao  --- service  --- controller

 

都是相关逻辑的处理  不一一列举了

 

涉及两张数据表(订单用户信息表 和订单商品详情表)

DAO层 

订单表接口的方法

Page findByBuyerOpenid(String buyerOpenidPageable pageable);

 

订单详情表接口的方法

List findByOrderId(String orderId);

 

 

买家订单的service接口的方法

/** 创建订单. */
OrderDTO create(OrderDTO orderDTO);

/** 查询单个订单. */
OrderDTO findOne(String orderId);

/** 查询订单列表. */
Page findList(String buyerOpenidPageable pageable);

/** 取消订单. */
OrderDTO cancel(OrderDTO orderDTO);

/** 完结订单. */
OrderDTO finish(OrderDTO orderDTO);

/** 支付订单. */
OrderDTO paid(OrderDTO orderDTO);

/** 查询订单列表. */
Page findList(Pageable pageable);

 

 

 

JSON格式转化为 List

导入依赖


   com.google.code.gson
   gson

 

Json格式的 orderForm.getItems()     转化为   List 

     

 

Gson gson = new Gson();

orderDetailList = gson.fromJson(orderForm.getItems(),
        new TypeToken>() {
        }.getType());

 

 

 

9.获得微信授权

必须时服务号才能进行微信相关接口开发,订阅号不行

1.手动造轮子

1.设置域名

2.获取 code(code作为换取access_token的票据,每次用户授权带上的code不一样,code只能使用一次,5分钟未使用自动过期).

3.通过code 获得网页受权access_token

2.微信网页授权的第三方SDK

这里是可能是目前最好最全的微信Java开发工具包(SDK

包括微信支付、开放平台、公众号、企业微信、企业号、小程序等

https://github.com/Wechat-Group/weixin-java-tools

 

添加Maven引用

注意:以下为最新正式版,最新测试版本号为 

 

  com.github.binarywang

  (不同模块参考下文)

  2.9.0

· 各模块的artifactId: 

o 微信小程序:weixin-java-miniapp

o 微信支付:weixin-java-pay

o 微信开放平台:weixin-java-open

o 公众号:weixin-java-mp

o 企业号/企业微信:weixin-java-cp

8.微信支付

:由于没有服务号,这一块没有研究

1.微信发起支付(后端)

2.在网页发起支付

3.动态注入参数发起支付

4.微信异步通知

5.微信退款

9.卖家订单

   --->service ----->controller

由于之前买家端写了很多代码了,其中与数据交互的dao层在卖家端不用写了

1.Service层(重用买家订单的service层接口)

/** 创建订单. */
OrderDTO create(OrderDTO orderDTO);

/** 查询单个订单. */
OrderDTO findOne(String orderId);

/** 查询订单列表. */
Page findList(String buyerOpenidPageable pageable);

/** 取消订单. */
OrderDTO cancel(OrderDTO orderDTO);

/** 完结订单. */
OrderDTO finish(OrderDTO orderDTO);

/** 支付订单. */
OrderDTO paid(OrderDTO orderDTO);

/** 查询订单列表. */
Page findList(Pageable pageable);

 

添加一个查询所有用户的订单方法(分页查询)

2.Controller层

(不予具体说明)

10.卖家商品

1. 新增和修改页面

2.修改表单体提交

3.新增功能

4.卖家类目功能开发

11.登录登出

1.分布式下的session

 

1.什么叫分布式系统

旨在支持应用程序和服务的开发,可以利用物理架构由多个自治的处理元素,不共享主内存,但是通过网络发送消息合作.

三个特点: 1.多节点

         2.消息通信(http通信)

         3.不共享内存

三个概念: 1.分布式系统

         2.集群

         3.分布式计算

2.AOP实现身份验证

1.先选取有身份验证的位置做切面

@Pointcut("execution(public * com.imooc.controller.Seller*.*(..))" +
"&& !execution(public * com.imooc.controller.SellerUserController.*(..))")
public void verify() {}

 

 

2.在需要验证的方法需要做什么

@Before("verify()")
public void doVerify() {
    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    HttpServletRequest request = attributes.getRequest();

    //查询cookie
    Cookie cookie = CookieUtil.get(requestCookieConstant.TOKEN);
    if (cookie == null) {
        log.warn("【登录校验】Cookie中查不到token");
        throw new SellerAuthorizeException();
    }

    //redis里查询
    String tokenValue = redisTemplate.opsForValue().get(String.format(RedisConstant.TOKEN_PREFIXcookie.getValue()));
    if (StringUtils.isEmpty(tokenValue)) {
        log.warn("【登录校验】Redis中查不到token");
        throw new SellerAuthorizeException();
    }
}

 

3.拦截登录异常,不正常访问将会跳转到登录界面

//拦截登录异常
//http://sell.natapp4.cc/sell/wechat/qrAuthorize?returnUrl=http://sell.natapp4.cc/sell/seller/login
@ExceptionHandler(value = SellerAuthorizeException.class)
public ModelAndView handlerAuthorizeException() {
    return new ModelAndView("redirect:"
    .concat(projectUrlConfig.getWechatOpenAuthorize())
    .concat("/sell/wechat/qrAuthorize")
    .concat("?returnUrl=")
    .concat(projectUrlConfig.getSell())
    .concat("/sell/seller/login"));
}

12.项目相关其他技术

1.mybatis的使用

1.注解使用方式(sql语句写在注解里面)

导入依赖


   org.mybatis.spring.boot
   mybatis-spring-boot-starter
   1.3.1

 

创建一个mapper 

包下建立对应实体类的sql语句操作类

 

1.通过map的方式插入 sql 语句(一般不用)

//通过 map 的方式
@Insert("insert into product_category(category_name, category_type) values (#{category_name, jdbcType=VARCHAR}, #{category_type, jdbcType=INTEGER})")
int insertByMap(Map,Object>map);

 

2.通过对象的方式插入 sql 语句

//通过对象的方式
//插入
@Insert("insert into product_category(category_name, category_type) values (#{categoryName, jdbcType=VARCHAR}, #{categoryType, jdbcType=INTEGER})")
int insertByObject(ProductCategory productCatrgory);

3.通过类型查询

//通过类型查询
@Select("select *from product_category where category_type = #{categoryType}")
@Results({
        @Result(column "category_type" property "categoryType"),
        @Result(column "category_id" ,   property "categoryId"),
        @Result(column "category_name" property "categoryName")

})
ProductCategory findByCategoryType(Integer categoryType);

 

4.通过名字查询

//通过名字查询
@Select("select *from product_category where category_name = #{categoryName}")
@Results({
        @Result(column "category_type" property "categoryType"),
        @Result(column "category_id" ,   property "categoryId"),
        @Result(column "category_name" property "categoryName")

})
List findByCategoryName(String categoryName);

 

5.更新姓名

(传入姓名和type)

//更新姓名
@Update("update product_category set category_name = #{categoryName} where category_type = #{categoryType}")
int updateByCategoryType(@Param("categoryName") String categoryName@Param("categoryType") Integer categoryType);

(传入对象)

@Update("update product_category set category_name = #{categoryName} where category_type = #{categoryType}")
int updateByObject(ProductCategory productCategory);

 

6.通过categoryType 删除

@Delete("delete from product_category  where category_type = #{categoryType}")
int deleteByCategoryType(Integer categoryType);

 

            

2.xml方式的使用(sql语句写在xml文件里面)

Namespace : 接口的位置

namespace="com.cyg.dataobject.mapper.ProductCategoryMapper">

 

Type: 操作的实体类的位置

实体类的属性 />

id="BaseResultMap" type="com.cyg.dataobject.ProductCategory">
     column="category_id"  property="categoryId" jdbcType="INTEGER" />
     column="category_name"  property="categoryName" jdbcType="VARCHAR" />
     column="category_type"  property="categoryType " jdbcType="INTEGER" />

 

Id : 方法名

resuletMap: 上面定义的 resuletMap标签

parameterType : 写入参的类型   如果入参时个对象,就写入参的路径

id="selectByCategoryType" resultMap="BaseResultMap"  parameterType="java.lang.Integer">
    SELECT category_id,category_name,category_type
    FROM product_category
    WHERE category_type = #{category_type , jdbcType = INTEGER}

 

 

完整xml文件

xml version="1.0" encoding="UTF-8"?>
mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

namespace="com.cyg.dataobject.mapper.ProductCategoryMapper">

    id="BaseResultMap" type="com.cyg.dataobject.ProductCategory">
         column="category_id"  property="categoryId" jdbcType="INTEGER" />
         column="category_name"  property="categoryName" jdbcType="VARCHAR" />
         column="category_type"  property="categoryType" jdbcType="INTEGER" />
    
    
    id="selectByCategoryType" resultMap="BaseResultMap"  parameterType="java.lang.Integer">
        SELECT category_id,category_name,category_type
        FROM product_category
        WHERE category_type = #{category_type,jdbcType = INTEGER}
    

 

 

 

接下来在配置文件里面配置

mybatis:
  mapper-locations: 
classpath:mapper/*.xml

 

在启动类中配置配置文件的位置

@MapperScan(basePackages "com.cyg.dataobject.mapper")

 

 

 

13.redis的使用

推荐一个网站

www.redis.cn

1.redis分布式锁

package com.cyg.service;


import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

/**
 * 
@author cyg
 * 
@date 18-3-17 下午5:14
 **/
@Component
@Slf4j
public class RedisLock {

     @Autowired
     private StringRedisTemplate redisTemplate;


     public boolean lock(String key String value){

         if (redisTemplate.opsForValue().setIfAbsent(key,value)){
             return true;
         }

         String currentValue  = redisTemplate.opsForValue().get(key);
         //如果锁过期
         if(!StringUtils.isEmpty(currentValue)
                 && Long.parseLong(currentValue) < System.currentTimeMillis()){

             //获取上一个锁的时间
             String oldValue = redisTemplate.opsForValue().getAndSet(key value);

             if (!StringUtils.isEmpty(oldValue) && oldValue.equals(System.currentTimeMillis())){
                 return true;
             }

         }
         return false;
     }


     public void unlock(String key String value){

         try {
             String currentValue = redisTemplate.opsForValue(.get(key);
             if (StringUtils.isEmpty(currentValue) && currentValue.equals(value)){
                 redisTemplate.opsForValue().getOperations().delete(key);
             }
         }catch (Exception e){
               log.error("[redis分布式锁解锁异常");
         }
     }


}

 

2.redis缓存的使用

14.项目部署

15.总结

你可能感兴趣的:(开放平台)