首页> 微信点餐系统技术总结,微信点餐系统有哪些功能?

  • 微信点餐系统技术总结,微信点餐系统有哪些功能?

    —  更新时间:2023-03-13 10:02:46     作者:智铺子
微信点餐系统技术总结

1.项目开发步骤



1.项目设计

1.角色划分

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

2.功能模块划分

详细设计个各个功能模块

买家 卖家

商品: 商品列表数据

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

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

2.架构和基础框架

SSH 框架的了解和使用

Mybatis 的两种配置方法

Redis 的缓存作用 分布式锁

Aop 身份验证

等等

3.数据库设计

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

4开发日志的使用

这里用Slf4j

1.导入依赖

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
2.加上注解

@Slf4j
3..使用

log.error("【创建订单】购物车不能为空");
Home("【微信支付】发起支付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" ?>


<configuration>

    <appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>
                %d - %msg%n
            </pattern>
        </layout>
    </appender>


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

        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <!--<onMatch>DENY</onMatch>
            <onMismatch>ACCEPT</onMismatch>-->
        </filter>

        <encoder>
            <pattern>
                %msg%n
            </pattern>
        </encoder>

        <!--滚动策略-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--路径 -->
            <fileNamePattern>/home/hk/log/tomcat/info.%d.log</fileNamePattern>
        </rollingPolicy>
    </appender>



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

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

        <encoder>
            <pattern>
                %msg%n
            </pattern>
        </encoder>

        <!--滚动策略-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--路径 -->
            <fileNamePattern>/home/hk/log/tomcat/error.%d.log</fileNamePattern>
        </rollingPolicy>
    </appender>



    <root level="info">
        <appender-ref ref="consoleLog"/>
        <appender-ref ref="fileInfoLog"/>
        <appender-ref ref="fileErrorLog"/>
    </root>
</configuration>
简单的日志需求选择第一种即可,复杂的需要选择第二种配置

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

6. 买家类目

1.买家类目-dao

1.数据库的连接:

添加依赖

Mysql的驱动

<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
</dependency>
Jap的驱动

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
配置数据库的信息:

在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 JpaRepository<ProductCategory, Integer>
继承接口 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<ProductInfo> findAll(Pageable pageable);

导入包 import org.springframework.data.domain.Pageable;
传入Pageable pageable对象 返回的时一个 Page<>对象
2.枚举类的使用

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

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

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

package com.imooc.enums;

import lombok.Getter;

/**
 * 商品状态
 */
@Getter
public enum ProductStatusEnum implements CodeEnum {
    UP(0, "在架"),
    DOWN(1, "下架")
    ;

    private Integer code;

    private String message;

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


3.封装返回结果

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

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

package com.imooc.VO;

import lombok.Data;

/**
 * http请求返回的最外层对象
 */
@Data
public class ResultVO<T> {

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

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

    /** 具体内容. */
    private T data;
}
2.中间层(ProductVO): 商品类目信息,包含 类目名字 , 类目编号 , 商品信息 List (里面包含属于这个类目的商品信息).

package com.imooc.VO;

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

import java.util.List;

/**
 * 商品(包含类目)
 */
@Data
public class ProductVO {

    @JsonProperty("name")
    private String categoryName;

    @JsonProperty("type")
    private Integer categoryType;

    @JsonProperty("foods")
    private List<ProductInfoVO> productInfoVOList;
}
3.最里层(ProductInfoVO): 商品详情( 包含用户能看到的商品信息 )

package com.imooc.VO;

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

import java.math.BigDecimal;

/**
 * 商品详情
 */
@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<Integer> categoryTypeList = productInfoList.stream()
        .map(e -> e.getCategoryType())
        .collect(Collectors.toList());
作用是从 对象集合productInfoList中抽取一个属性集合 categoryTypeList

作用等价于:

List<Integer> categoryTypeList = new ArrayList<>();
传统方法
for (ProductInfo productInfo : productInfoList) {
    categoryTypeList.add(productInfo.getCategoryType());
}
根据获得的类目type属性集合来得到类目对象集合

List<ProductCategory> productCategoryList = categoryService.findByCategoryTypeIn(categoryTypeList);
3.数据拼装(将对应的商品对象添加到对应的类目List下去)

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

List<ProductVO> 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<ProductInfoVO> 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(productInfo, productInfoVO);
productInfoVOList.add(productInfoVO);

设置 productVO 的 ProductInfoVOList  的值为 productInfoVOList        

productVO.setProductInfoVOList(productInfoVOList);

将   productVO  添加到  productVOList           

productVOList.add(productVO);


8.买家订单



Dao --- service --- controller



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

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

DAO层



订单表接口的方法

Page<OrderMaster> findByBuyerOpenid(String buyerOpenid, Pageable pageable);
订单详情表接口的方法

List<OrderDetail> findByOrderId(String orderId);
买家订单的service接口的方法



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

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

/** 查询订单列表. */
Page<OrderDTO> findList(String buyerOpenid, Pageable pageable);

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

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

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

/** 查询订单列表. */
Page<OrderDTO> findList(Pageable pageable);
JSON格式转化为 List



导入依赖

<dependency>
   <groupId>com.google.code.gson</groupId>
   <artifactId>gson</artifactId>
</dependency>
Json格式的 orderForm.getItems() 转化为 List<OrderDetail>

Gson gson = new Gson();


orderDetailList = gson.fromJson(orderForm.getItems(),
        new TypeToken<List<OrderDetail>>() {
        }.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引用

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

<dependency>

<groupId>com.github.binarywang</groupId>

<artifactId>(不同模块参考下文)</artifactId>

<version>2.9.0</version>

</dependency>

· 各模块的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<OrderDTO> findList(String buyerOpenid, Pageable pageable);

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

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

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

/** 查询订单列表. */
Page<OrderDTO> 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(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();
    }
}


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语句写在注解里面)

导入依赖

<dependency>
   <groupId>org.mybatis.spring.boot</groupId>
   <artifactId>mybatis-spring-boot-starter</artifactId>
   <version>1.3.1</version>
</dependency>
创建一个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<String,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<ProductCategory> 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 : 接口的位置

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


Type: 操作的实体类的位置

<id 实体类的属性 />



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


Id : 方法名



resuletMap: 上面定义的 resuletMap标签



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



<select 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}
</select>


完整xml文件

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

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

    <resultMap id="BaseResultMap" type="com.cyg.dataobject.ProductCategory">
         <id column="category_id"  property="categoryId" jdbcType="INTEGER" />
         <id column="category_name"  property="categoryName" jdbcType="VARCHAR" />
         <id column="category_type"  property="categoryType" jdbcType="INTEGER" />
    </resultMap>
    
    <select 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}
    </select>

</mapper>为


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

mybatis:
  mapper-locations: classpath:mapper/*.xml
在启动类中配置配置文件的位置

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


13.redis的使用

推荐一个网站

redis中文官方网站



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.总结


微信点餐系统有两种,一种是微信公众号点餐系统,另一种是微信小程序点餐系统。这两种点餐系统都可以实现微信扫码点餐,也是各有优势。公众号点餐系统能更好的聚集粉丝、与粉丝互动以及文章推送,而小程序点餐系统能更好的线上推广拓客,可以倚靠微信12亿流量红利,将线上流量引导到店内消费。

基于微信小程序点餐系统具体功能都有哪些?
①扫桌面二维码点餐,“到店顾客”就坐后,使用微信,扫描桌面二维码,选择餐品并在线付款,等待服务员上菜。
②自助点餐,预约堂食/自提,消费者通过餐饮店点餐小程序自助下单,预约好进餐时间;餐饮门店加工、顾客到点再堂食/自提。
③点外卖,等送餐上门,通过餐饮店点餐小程序,用户选好喜欢的菜品之后,用户直接下单、支付,还可以预约送达时间,查看订单状态和订单详情。
微信点餐系统有哪些推广的渠道
①微信公众号点餐系统推广:二维码是最直接的线上入口,如餐厅海报、菜单广告上印上二维码,本地论坛发帖带上二维码等,公众号名片分享也是很常见的一种方式,如好友分享、群分享等等。
②公众号文章分享这种一般用会选择优惠活动来做文章。比如什么优惠活动,转发到朋友圈集齐多少个赞,免费送一个特色菜。这种免费的方式吸引力还是有的,尤其是学生,格外喜欢这种方式。现在餐厅一般都是提供免费wifi的,微信公众号开通门店功能后,可以设置关注公众号连wifi等。
③通过位置标注,打开-发现-小程序,你会看到在附近的小程序能看到,首页会显示自己所在位置附近5公里以内的商家,顶部菜单侧显示了各行各业的小程序,餐桌上的小程序码,是最直接的线上入口,顾客扫一下就成了小程序的会员。附近5km的人群均可以在微信附近小程序列表中看到餐厅的小程序;分享转发同公众号类似,但是小程序会永久留存在“聊天小程序”栏目中。智铺子科技在外卖点餐系统开发方面有着丰富的经验,既可以定制开发又可以模板开发。网上搜索智铺子即可!
相关标签: 微信扫码点餐小程序    扫码点餐小程序    
新用户申请

请填写您的联系方式,我们会主动与您联系

*号标识为必填信息
智铺子,专注微信外卖系统,微信订餐系统,外卖小程序