微信点餐系统(一)买家商品列表
分类:
IT文章
•
2022-10-28 00:12:58
一、开发环境
- JDK 1.8
- MySQL 5.7
- springboot 2.2.4
- mybatis 1.1.1
- redis 3.2.8
可能有的人的MySQL版本不到5.7,在下面创建数据库的时候可能会报错。我的解决办法使用Docker创建一个合适版本的MySQL容器,参考:使用docker创建MySQL容器,并在springboot中使用。
二、开发工具
- IDEA
- Notepad++
- VirtualBox
- SQLyog
三、项目简单分析
该项目的角色主要分为买家端、买家端。买家端主要实现的功能有商品的查询和订单查询、创建、取消等,而卖家端主要是对订单的管理、商品的管理、类目的管理等。
四、在数据库中创建四张数据表
创建一个sell数据库,并在其中创建四张数据表,创建语句如下:
1 CREATE TABLE product_info(
2 product_id VARCHAR(32) NOT NULL,
3 product_name VARCHAR(64) NOT NULL COMMENT "商品名称",
4 product_price DECIMAL(8,2) NOT NULL COMMENT "单价",
5 product_stock INT NOT NULL COMMENT "库存",
6 product_description VARCHAR(64) COMMENT "描述",
7 product_icon VARCHAR(512) COMMENT "小图",
8 category_type INT NOT NULL COMMENT "类目编号",
9 product_status TINYINT(3),
10 create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT "创建时间",
11 update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT "修改时间",
12 PRIMARY KEY(product_id)
13 ) COMMENT "商品表";
14
15 CREATE TABLE product_category(
16 category_id INT NOT NULL AUTO_INCREMENT,
17 category_name VARCHAR(64) NOT NULL COMMENT '类目名称',
18 category_type INT NOT NULL COMMENT '类目编号',
19 create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
20 update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
21 PRIMARY KEY (category_id),
22 UNIQUE KEY uqe_category_type (category_type)
23 ) COMMENT '类目表';
24
25 CREATE TABLE order_master (
26 order_id VARCHAR(32) NOT NULL,
27 buyer_name VARCHAR(32) NOT NULL COMMENT '买家名字',
28 buyer_phone VARCHAR(32) NOT NULL COMMENT '买家电话',
29 buyer_address VARCHAR(128) NOT NULL COMMENT '买家地址',
30 buyer_openid VARCHAR(64) NOT NULL COMMENT '买家微信openid',
31 order_amount DECIMAL(8,2) NOT NULL COMMENT '订单总金额',
32 order_status TINYINT(3) NOT NULL DEFAULT 0 COMMENT '订单状态,默认0新下单',
33 pay_status TINYINT(3) NOT NULL DEFAULT 0 COMMENT '支付状态,默认0未支付',
34 create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
35 update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
36 PRIMARY KEY (order_id),
37 KEY idx_buyer_openid (buyer_openid)
38 ) COMMENT '订单表';
39
40 CREATE TABLE order_detail (
41 detail_id VARCHAR(32) NOT NULL,
42 order_id VARCHAR(32) NOT NULL,
43 product_id VARCHAR(32) NOT NULL,
44 product_name VARCHAR(64) NOT NULL COMMENT '商品名称',
45 product_price DECIMAL(8,2) NOT NULL COMMENT '商品价格',
46 product_quantity INT NOT NULL COMMENT '商品数量',
47 product_icon VARCHAR(512) COMMENT '商品小图',
48 create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
49 update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
50 PRIMARY KEY (detail_id),
51 KEY idx_order_id (order_id)
52 ) COMMENT '订单详情表';
View Code
还差一张买家信息表,但是暂时还用不到就没创建。本文主要是买家商品list的实现就先介绍其中两张,product_category是商品的类目表,product_info是商品信息表。

五.根据API文档在vo包下创建需要返回数据的类
商品列表的API文档如下:
1 ###商品列表
2
3 ```
4 GET /sell/buyer/product/list
5 ```
6
7 参数
8
9 ```
10 无
11 ```
12
13 返回
14
15 ```
16 {
17 "code": 0,
18 "msg": "成功",
19 "data": [
20 {
21 "name": "热榜",
22 "type": 1,
23 "foods": [
24 {
25 "id": "123456",
26 "name": "皮蛋粥",
27 "price": 1.2,
28 "description": "好吃的皮蛋粥",
29 "icon": "http://xxx.com",
30 }
31 ]
32 },
33 {
34 "name": "好吃的",
35 "type": 2,
36 "foods": [
37 {
38 "id": "123457",
39 "name": "慕斯蛋糕",
40 "price": 10.9,
41 "description": "美味爽口",
42 "icon": "http://xxx.com",
43 }
44 ]
45 }
46 ]
47 }
48 ```
根据商品列表文档可知需要返回数据最外层有"code","msg","data"字段,由于各个API"data"内内容不一致,将"data"类型指定为泛型T
因此创建一个ResultVO类
1 package club.nipengfei.VO;
2
3 import lombok.Data;
4
5 @Data
6 public class ResultVO<T> {
7
8 /** 错误码 */
9 private Integer code;
10
11 /** 提示信息 */
12 private String msg;
13
14 /** 具体内容 */
15 private T data;
16
17 }
根据上述的文档可知"data"内是一个列表,其中一个元素的"name"表示"category_name","type"表示"category_type","foods"表示商品信息列表。
根据这些字段在vo包下创建一个ProductVO类。@Data注解:lombok插件的一个注解自动生成get,set等方法。@JsonProperty注解:可以让返回前端例如"categoryName"变成"name",如果字段直接使用"name"不能方便获取该字段含义。
1 package club.nipengfei.VO;
2
3 import com.fasterxml.jackson.annotation.JsonProperty;
4 import lombok.Data;
5
6 import java.util.List;
7
8 /**
9 * 商品(包含类目)
10 */
11 @Data
12 public class ProductVO {
13
14 @JsonProperty("name")
15 private String categoryName;
16
17 @JsonProperty("type")
18 private Integer categoryType;
19
20 @JsonProperty("foods")
21 private List<ProductInfoVO> productInfoVOList;
22
23 }
"foods"列表中表示属于该类目"category"的商品,使用ProductInfoVO类封装。类型BigDecimal对应数据表中的"DECIMAL"一般钱都用这个类型。
1 package club.nipengfei.VO;
2
3 import com.fasterxml.jackson.annotation.JsonProperty;
4 import lombok.Data;
5
6 import java.math.BigDecimal;
7
8 /**
9 * 商品详情
10 */
11 @Data
12 public class ProductInfoVO {
13
14 @JsonProperty("id")
15 private String productId;
16
17 @JsonProperty("name")
18 private String productName;
19
20 @JsonProperty("price")
21 private BigDecimal productPrice;
22
23 @JsonProperty("description")
24 private String productDescription;
25
26 @JsonProperty("icon")
27 private String productIcon;
28 }
六、开发repository层(dao层)
在这之前需要根据数据库中的数据表建立相应映射类,ProductCategory类、ProductInfo类。
1 package club.nipengfei.dataobject;
2
3 import lombok.Data;
4
5 import java.util.Date;
6
7 /**
8 * 类目
9 */
10 @Data
11 public class ProductCategory {
12
13 /**
14 * 类目id
15 */
16 private Integer category_id;
17
18 /**
19 * 类目名字
20 */
21 private String category_name;
22
23 /**
24 * 类目编号
25 */
26 private Integer category_type;
27
28 private Date create_time;
29
30 private Date update_time;
31
32 }
View Code
1 package club.nipengfei.dataobject;
2
3 import lombok.Data;
4
5 import java.math.BigDecimal;
6
7 @Data
8 public class ProductInfo {
9
10 private String product_id;
11
12 private String product_name;
13
14 private BigDecimal product_price;
15
16 private Integer product_stock;
17
18 private String product_description;
19
20 private String product_icon;
21
22 /** 状态,0正常 1下架*/
23 private Integer product_status;
24
25 /** 类目编号*/
26 private Integer category_type;
27 }
View Code
在pom文件中引入相应依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
View Code
并在配置文件中配置数据库的信息
1 spring.datasource.url=jdbc:mysql://192.168.1.199/sell?characterEncoding=utf-8&useSSL=FALSE
2 spring.datasource.username=root
3 spring.datasource.password=123456
View Code
在ProductInfoRepository接口内写一个findUpAll抽象方法,将product_info表中上架的商品全部查出来,放入List<ProductInfo>中。然后在ProductCategoryRepository接口中写一个findByCategoryTypeIn抽象方法,根据List<Integer>参数查询出List<ProductCategory>。
@Mapper注解会将注解的接口交给Mybatis处理,如果觉得麻烦可以在启动类上加上@MapperScan("club.nipengfei.repository")代替,该注解可以自动扫描repository包下的类
@Repository注解将其交给spring处理,自动注册为spring bean
1 package club.nipengfei.repository;
2
3 import club.nipengfei.dataobject.ProductInfo;
4 import com.github.pagehelper.Page;
5 import org.apache.ibatis.annotations.Insert;
6 import org.apache.ibatis.annotations.Mapper;
7 import org.apache.ibatis.annotations.Select;
8 import org.apache.ibatis.annotations.Update;
9 import org.springframework.stereotype.Repository;
10
11 import java.util.List;
12
13 @Repository
14 @Mapper
15 public interface ProductInfoRepository {
16
17 @Select("select * from product_info where product_status=0")
18 List<ProductInfo> findUpAll();
19
20 }
下面的select语句中使用了mybatis的动态SQL,参考:https://mybatis.org/mybatis-3/zh/dynamic-sql.html
注意在注解中使用动态SQL需要使用<script>
1 package club.nipengfei.repository;
2
3 import club.nipengfei.dataobject.ProductCategory;
4 import org.apache.ibatis.annotations.Insert;
5 import org.apache.ibatis.annotations.Mapper;
6 import org.apache.ibatis.annotations.Param;
7 import org.apache.ibatis.annotations.Select;
8 import org.springframework.stereotype.Repository;
9
10 import java.util.List;
11
12 @Mapper
13 @Repository
14 public interface ProductCategoryRepository {
15
16
17 @Select("<script>select * from product_category " +
18 "<where>" +
19 "<if test='list != null and list.size()>0' >"+
20 "<foreach collection='list' open='and category_type in (' close=')' item='id' separator=','> #{id}</foreach>"+
21 "</if>"+
22 "</where>"+
23 "</script>")
24 List<ProductCategory> findByCategoryTypeIn(@Param("list") List<Integer> list);
25 }
七、service层开发
@Service注解与上面的@Repository一样将其注册为spring bean
@Autowired注解,bean的自动注入
1 package club.nipengfei.service;
2
3 import club.nipengfei.dataobject.ProductInfo;
4 import club.nipengfei.dto.CartDTO;
5 import com.github.pagehelper.Page;
6
7 import java.util.List;
8
9 public interface ProductService {
10
11 /**
12 * 查询所有在架商品列表
13 * @return
14 */
15 List<ProductInfo> findUpAll();
16
17 }
View Code
1 package club.nipengfei.service.impl;
2
3 import club.nipengfei.dataobject.ProductInfo;
4 import club.nipengfei.dto.CartDTO;
5 import club.nipengfei.enums.ResultEnum;
6 import club.nipengfei.exception.SellException;
7 import club.nipengfei.repository.ProductInfoRepository;
8 import club.nipengfei.service.ProductService;
9 import com.github.pagehelper.Page;
10 import org.springframework.beans.factory.annotation.Autowired;
11 import org.springframework.stereotype.Service;
12 import org.springframework.transaction.annotation.Transactional;
13
14 import java.util.List;
15
16 @Service
17 public class ProductServiceImpl implements ProductService {
18
19 @Autowired
20 private ProductInfoRepository repository;
21
22 @Override
23 public List<ProductInfo> findUpAll() {
24 return repository.findUpAll();
25 }
26
27 }
View Code
1 package club.nipengfei.service;
2
3 import club.nipengfei.dataobject.ProductCategory;
4
5 import java.util.List;
6
7 public interface CategoryService {
8
9 List<ProductCategory> findByCategoryTypeIn(List<Integer> categoryTypeList);
10
11 }
View Code
1 package club.nipengfei.service.impl;
2
3 import club.nipengfei.dataobject.ProductCategory;
4 import club.nipengfei.repository.ProductCategoryRepository;
5 import club.nipengfei.service.CategoryService;
6 import org.springframework.beans.factory.annotation.Autowired;
7 import org.springframework.stereotype.Service;
8
9 import java.util.List;
10
11 @Service
12 public class CategoryServiceImpl implements CategoryService {
13
14 @Autowired
15 private ProductCategoryRepository repository;
16
17 @Override
18 public List<ProductCategory> findByCategoryTypeIn(List<Integer> categoryTypeList) {
19 return repository.findByCategoryTypeIn(categoryTypeList);
20 }
21
22 }
View Code
八、controller层的开发
根据API文档请求方法和路径,在配置文件中写入一个项目路径"/sell",新建一个BuyerProductController类在这上面加上注解@RequestMapping指定值为"/buyer/product",在list方法上使用@GetMapping注解,指定值为"list"。@RestController注解@ResponseBody和@Controller的组合注解,@ResponseBody注解是返回json,@Controller注解处理http请求。
1 server.servlet.context-path=/sell
1 package club.nipengfei.controller;
2
3 import club.nipengfei.VO.ProductInfoVO;
4 import club.nipengfei.VO.ProductVO;
5 import club.nipengfei.VO.ResultVO;
6 import club.nipengfei.dataobject.ProductCategory;
7 import club.nipengfei.dataobject.ProductInfo;
8 import club.nipengfei.service.CategoryService;
9 import club.nipengfei.service.ProductService;
10 import club.nipengfei.utils.ResultVOUtil;
11 import org.springframework.beans.BeanUtils;
12 import org.springframework.beans.factory.annotation.Autowired;
13 import org.springframework.web.bind.annotation.GetMapping;
14 import org.springframework.web.bind.annotation.RequestMapping;
15 import org.springframework.web.bind.annotation.RestController;
16
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.List;
20
21 @RestController
22 @RequestMapping("/buyer/product")
23 public class BuyerProductController {
24
25 @Autowired
26 private ProductService productService;
27
28 @Autowired
29 private CategoryService categoryService;
30
31
32 @GetMapping("/list")
33 public ResultVO list() {
34
35 // 1.查询所有上架商品
36 List<ProductInfo> productInfoList = productService.findUpAll();
37
38 // 2.查询类目(一次查询)
39 List<Integer> categoryTypeList = new ArrayList<>();
40 for (ProductInfo productInfo : productInfoList) {
41 categoryTypeList.add(productInfo.getCategory_type());
42 }
43 /** 当categoryTypeList为空时,即商品都没有上架,程序会报错 */
44 List<ProductCategory> productCategoryList = categoryService.findByCategoryTypeIn( categoryTypeList);
45
46 // 3.数据拼接
47 List<ProductVO> productVOList = new ArrayList<>();
48 for (ProductCategory productCategory : productCategoryList) {
49 ProductVO productVO = new ProductVO();
50 productVO.setCategoryType(productCategory.getCategory_type());
51 productVO.setCategoryName(productCategory.getCategory_name());
52
53 List<ProductInfoVO> productInfoVOList = new ArrayList<>();
54 for (ProductInfo productInfo : productInfoList) {
55 if (productInfo.getCategory_type().equals(productCategory.getCategory_type())){
56 ProductInfoVO productInfoVO = new ProductInfoVO();
57
58 // BeanUtils.copyProperties(productInfo,productInfoVO);
59 // 由于我的productInfo和productInfoVO的字段名不一致,不能使用上面的工具,而使用下面的
60 productInfoVO.setProductId(productInfo.getProduct_id());
61 productInfoVO.setProductName(productInfo.getProduct_name());
62 productInfoVO.setProductPrice(productInfo.getProduct_price());
63 productInfoVO.setProductDescription(productInfo.getProduct_description());
64 productInfoVO.setProductIcon(productInfo.getProduct_icon());
65
66 productInfoVOList.add(productInfoVO);
67 }
68 }
69 productVO.setProductInfoVOList(productInfoVOList);
70 productVOList.add(productVO);
71 }
72
73 return ResultVOUtil.success(productVOList);
74 }
75 }
View Code
1 package club.nipengfei.utils;
2
3 import club.nipengfei.VO.ResultVO;
4
5 public class ResultVOUtil {
6
7 public static ResultVO success(Object object){
8 ResultVO resultVO = new ResultVO();
9 resultVO.setData(object);
10 resultVO.setCode(0);
11 resultVO.setMsg("成功");
12 return resultVO;
13 }
14
15 public static ResultVO success(){
16 return success(null);
17 }
18
19 public static ResultVO error(Integer code,String msg){
20 ResultVO resultVO = new ResultVO();
21 resultVO.setCode(code);
22 resultVO.setMsg(msg);
23 return resultVO;
24 }
25 }
View Code
在本地浏览器或者Postman中输入http://127.0.0.1:8080/sell/buyer/product/list,就可以发现返回json格式与API文档一致

九、存在的问题
@Mapper,@Repository,@Autowired,@RestController等注解具体是怎么工作的还是不太了解。mybatis中动态sql使用有点困难。