zoukankan      html  css  js  c++  java
  • SpringBoot微信点餐项目(一)——概述、产品和订单业务

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

    1、总体介绍

    • 使用的主要技术:

      • SpringBoot:1.5.2。
      • JDK:1.8。
      • MySQL:5.7.32。
      • Nginx:1.12.2。
      • Redis:3.2.8。
    • 角色划分:

      • 买家(手机端,微信公众号)。
      • 买家(PC端)。
    • 功能分析:

      • 商品。
      • 订单。
      • 类目。
    • 关系图:

    • 部署架构:Nginx + Tomcat + Redis + MySQL。

    • 数据库设计:

      • 商品表product_info,包括商品的相关信息。
      • 类目表product_category,是商品的分类信息,与商品表是一对多的关系。
      • 订单详情表order_detail,订单的具体内容。
      • 订单主表order_master,订单的核心信息,与订单详情表是一对多的关系。
      • 卖家信息表seller_info。

    2、环境搭建

    • 对开发流程还不太熟悉,为了方便,测试期间,在阿里云的Docker上部署MySQL和Redis,在本机上部署Nginx和Tomcat。

    • Win10下环境搭建:

    • 阿里云Docker环境搭建:

    • 创建数据库表:

      CREATE DATABASE WXDC;
      
      CREATE TABLE `product_info` (
      	`product_id` VARCHAR(32) PRIMARY KEY,
      	`product_name` VARCHAR(64) NOT NULL COMMENT '产品名称',
      	`product_price` DECIMAL(8,2) NOT NULL COMMENT '产品单价',
      	`product_stock` INT NOT NULL COMMENT '库存',
      	`product_description` VARCHAR(64) COMMENT '描述',
      	`product_icon` VARCHAR(512) COMMENT '小图',
      	`category_type` INT NOT NULL COMMENT '类目编号',
      	`create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
      	`update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
      ) COMMENT '商品表';
      
      CREATE TABLE `product_category`(
      	`category_id` INT PRIMARY KEY AUTO_INCREMENT,
      	`category_name` VARCHAR(64) NOT NULL COMMENT '类目名字',
      	`category_type` INT NOT NULL UNIQUE KEY COMMENT '类目编号',
      	`create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
      	`update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
      ) COMMENT '类目表';
      
      CREATE TABLE `order_master`(
      	`order_id` VARCHAR(32) PRIMARY KEY,
      	`buyer_name` VARCHAR(32) NOT NULL COMMENT '买家名字',
      	`buyer_phone` VARCHAR(32) NOT NULL COMMENT '买家电话',
      	`buyer_address` VARCHAR(128) NOT NULL COMMENT '买家地址',
      	`buyer_openid` VARCHAR(64) NOT NULL COMMENT '微信openid',
      	`order_amount` DECIMAL(8,2) NOT NULL COMMENT '订单总金额',
      	`order_status` TINYINT(3) NOT NULL DEFAULT 0 COMMENT '订单状态,默认0新下单',
      	`pay_status` TINYINT(3) NOT NULL DEFAULT 0 COMMENT '支付状态,默认0未支付',
      	`create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
      	`update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
      	KEY `idx_buyer_openid` (`buyer_openid`)
      ) COMMENT '订单主表';
      
      CREATE TABLE `order_detail`(
      	`detail_id` VARCHAR(32) PRIMARY KEY,
      	`order_id` VARCHAR(32) NOT NULL,
      	`product_id` VARCHAR(32) NOT NULL,
      	`product_name` VARCHAR(64) NOT NULL COMMENT '商品名称',
      	`product_price` DECIMAL(8,2) NOT NULL COMMENT '商品价格',
      	`product_quantity` INT NOT NULL COMMENT '商品数量',
      	`product_icon` VARCHAR(512) COMMENT '商品小图',
      	`create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
      	`update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
      	KEY `idx_order_id` (`order_id`)
      ) COMMENT '订单详情';
      
    • IDEA创建项目:

      • 使用Spring initializer,勾选Web。

      • SpringBoot 1.x版本下的单元测试:

        import org.junit.Test;
        import org.junit.runner.RunWith;
        import org.springframework.boot.test.context.SpringBootTest;
        import org.springframework.test.context.junit4.SpringRunner;
        
        @RunWith(SpringRunner.class)
        @SpringBootTest
        public class SpringbootesApplicationTests {
            @Test
            public void test() {
        		//方法体
            }
        }
        
      • maven的pom.xml配置:

        <?xml version="1.0" encoding="UTF-8"?>
        <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
            <modelVersion>4.0.0</modelVersion>
            <parent>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>1.5.2.RELEASE</version>
                <relativePath/> <!-- lookup parent from repository -->
            </parent>
            <groupId>cn.iwehdio</groupId>
            <artifactId>sell</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <name>sell</name>
            <description>Demo project for Spring Boot</description>
        
            <properties>
                <java.version>1.8</java.version>
            </properties>
        
            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-web</artifactId>
                </dependency>
        
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-test</artifactId>
                    <scope>test</scope>
                </dependency>
        
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                </dependency>
        
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-data-jpa</artifactId>
                </dependency>
        
                <dependency>
                    <groupId>org.projectlombok</groupId>
                    <artifactId>lombok</artifactId>
                </dependency>
        
                <dependency>
                    <groupId>com.google.code.gson</groupId>
                    <artifactId>gson</artifactId>
                </dependency>
        
                <dependency>
                    <groupId>com.github.binarywang</groupId>
                    <artifactId>weixin-java-mp</artifactId>
                    <version>2.7.0</version>
                </dependency>
        
                <dependency>
                    <groupId>cn.springboot</groupId>
                    <artifactId>best-pay-sdk</artifactId>
                    <version>1.1.0</version>
                </dependency>
        
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-freemarker</artifactId>
                </dependency>
        
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-data-redis</artifactId>
                </dependency>
        
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-websocket</artifactId>
                </dependency>
            </dependencies>
        
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-maven-plugin</artifactId>
                    </plugin>
                </plugins>
            </build>
        
        </project>
        
      • application.yml数据库配置:

        spring:
          datasource:
            driver-class-name: com.mysql.jdbc.Driver
            platform: mysql
            url: jdbc:mysql://ip地址:3306/WXDC?characterEncoding=utf-8&useSSL=false
            username: root
            password: 123456
          jpa:
            show-sql: true
        server:
        	context-path: /sell # url前缀
        
    • 日志:

      • 日志框架的能力:

        • 定制输出目标。
        • 定制输出格式。
        • 携带上下文信息。
        • 运行时选择性输出。
        • 灵活的配置。
        • 优秀的性能。
      • 日志门面SLF4j + 日志实现Logback。

      • 使用:

        private final Logger logger = LoggerFactory.getLogger(当前类.class);
        //输出常量,debug、error等方法
        logger.info("1");
        //输出变量
        String name;
        logger.info("name:{}",name);
        
      • LogBack的配置:

        • application.yml简单配置:

          logging:
            pattern: 
              console: "%d - %msg%n"	#输出格式
            file: 	G:CodeIDEA_codeWXDClogsell.log	#输出日志文件
            level: debug	#输出日志级别
          
        • logback-spring.xml复杂配置(resource下):

        <?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>
            <!--  Info日志,过滤掉Error级别  -->
            <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>%d - %msg%n</pattern>
                </encoder>
                <!--  滚动策略,每天一个日志文件  -->
                <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                    <fileNamePattern>G:CodeIDEA_codeWXDClogsellInfo.%d.log</fileNamePattern>
                </rollingPolicy>
            </appender>
            <!--  Error日志,只指定Error级别  -->
            <appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
                <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                    <level>ERROR</level>
                </filter>
                <encoder>
                    <pattern>%d - %msg%n</pattern>
                </encoder>
                <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                    <fileNamePattern>G:CodeIDEA_codeWXDClogsellError.%d.log</fileNamePattern>
                </rollingPolicy>
            </appender>
        
            <root level="info">
                <appender-ref ref="consoleLog" />
                <appender-ref ref="fileInfoLog" />
                <appender-ref ref="fileErrorLog" />
            </root>
        </configuration>
        
    • 第一部分项目结构(导出方法):

      │  SellApplication.java
      │  
      ├─controller	控制器
      │      BuyerOrderController.java	操作订单
      │		 - create() 创建订单
      │		 - list() 订单列表
      │		 - detail() 订单详情
      │		 - cancel() 取消订单
      │      BuyerProductController.java	操作产品
      │		- list() 查询所有产品列表
      │      
      ├─converter		对象类型转换器
      │      OrderForm2OrderDTOConverter.java
      │      OrderMaster2OrderDTOConverter.java
      │      
      ├─dao	与数据库交互的dao接口,都实现了JpaRepository
      │      OrderDetailDao.java	订单详情
      │		 - findByOrderId()
      │      OrderMasterDao.java	订单主表
      │		 - findByBuyerOpenid()
      │      ProductCategoryDao.java	产品目录
      │		 - findByCategoryTypeIn()
      │      ProductInfoDao.java	产品详情
      │		 - findByProductStatus()
      │      
      ├─dataObject	与数据库直接交互的实体类,不含关联关系
      │      OrderDetail.java		订单详情实体类
      │      OrderMaster.java		订单主表实体类
      │      ProductCategory.java		产品目录实体类
      │      ProductInfo.java		产品信息实体类
      │      
      ├─dto	数据传输对象
      │      CartDTO.java  购物车中的单个对象
      │      OrderDTO.java 包含了订单主表和订单详情列表
      │      
      ├─enums		枚举提高可读性
      │      OrderStatusEnum.java 订单状态枚举
      │      PayStatusEnum.java	支付状态枚举
      │      ProductStatusEnum.java	产品状态枚举
      │      ResultEnum.java	出现的异常种类枚举
      │      
      ├─exception		自定义异常
      │      SellException.java	售卖过程中出现的异常
      │      
      ├─form	前端传回参数封装的对象
      │      OrderForm.java 创建订单时前端传回参数
      │      
      ├─service	业务层,实现业务接口
      │  │  BuyerService.java	核实买家信息的订单操作
      │  │	 - findOneOrder()	查找订单
      │  │	 - cancelOrder()	取消订单
      │  │  OrderMasterService.java	订单操作
      │  │	 - create() 创建
      │  │	 - findOne() 查询单个
      │  │	 - findList() 查询所有
      │  │	 - cancel() 取消订单
      │  │	 - finish() 完结订单
      │  │	 - paid() 支付订单
      │  │  ProductCategoryService.java	产品目录
      │  │	 - findOne() 根据目录id查找
      │  │	 - findAll() 查找所有
      │  │	 - findByCategoryTypeIn() 根据一组目录id查找
      │  │	 - save() 保存一个目录
      │  │  ProductInfoService.java 产品详情
      │  │	 - findOne() 根据产品id查找
      │  │	 - findAll() 查找所有
      │  │	 - findUpAll() 查找所有上架的商品
      │  │	 - save() 保存一个产品
      │  │	 - increaseStock() 增加库存
      │  │	 - decreaseStock() 减少库存
      │  │  
      │  └─impl
      │          BuyerServiceImpl.java
      │          OrderMasterServiceImpl.java
      │          ProductCategoryServiceImpl.java
      │          ProductInfoServiceImpl.java
      │          
      ├─utils	工具类
      │      Date2LongSerializer.java	修改Date的序列化
      │      KeyUtil.java		生成唯一主键,时间+随机数
      │      ProductResultVOUtil.java	封装返回对象
      │      
      └─viewObject	返回给前端的数据封装实体类
              ProductCategoryVO.java		目录对象
              ProductResultVO.java	顶层返回对象
              ProdutInfoVO.java	产品对象
      

    3、商品显示

    • SpringData Jpa,对于数据库中表或者字段,命名为下划线分割的,在类名中自动对应驼峰命名分割的。

    • 类目表(买家类名):

      • 实体类:

        import org.hibernate.annotations.DynamicUpdate;
        import javax.persistence.Entity;
        import javax.persistence.GeneratedValue;
        import javax.persistence.Id;
        import java.util.Date;
        @Entity
        @DynamicUpdate  //使得updateTime动态更新
        public class ProductCategory {
            @Id
            @GeneratedValue
            private Integer categoryId;
            private String categoryName;
            private Integer categoryType;
            private Date createTime;
            private Date updateTime;
            //一定要有无参构造
            public ProductCategory() {
            }
            public ProductCategory(String categoryName, Integer categoryType) {
                this.categoryName = categoryName;
                this.categoryType = categoryType;
            }
            /* getter&setter&toString */
        }
        
      • Dao接口:

        public interface ProductCategoryDao extends JpaRepository<ProductCategory,Integer> {
            List<ProductCategory> findByCategoryTypeIn(List<Integer> categoryTypeList);
        }
        
      • dao测试类:

        import org.junit.Test;
        import org.junit.runner.RunWith;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.boot.test.context.SpringBootTest;
        import org.springframework.test.context.junit4.SpringRunner;
        
        @RunWith(SpringRunner.class)
        @SpringBootTest
        public class ProductCategoryDaoTest {
            @Autowired
            private ProductCategoryDao dao;
            @Test
            public void findOneTest(){
                ProductCategory productCategory = dao.findOne(1);
                System.out.println(productCategory);
            }
            @Test
            public void saveOneTest(){
                ProductCategory productCategory = new ProductCategory("目录2",3);
                dao.save(productCategory);
                System.out.println(productCategory);
            }
            @Test
            public void updateOneTest(){
                ProductCategory productCategory = dao.findOne(2);
                productCategory.setCategoryType(4);
                dao.save(productCategory);
                System.out.println(productCategory);
            }
            @Test
            public void findListTest(){
                List<Integer> findList = Arrays.asList(1,2,3,4);
                List<ProductCategory> byCategoryTypeIn = dao.findByCategoryTypeIn(findList);
                System.out.println(byCategoryTypeIn);
            }
        }
        
      • Service接口:

        public interface ProductCategoryService {
            ProductCategory findOne(Integer categoryId);
            List<ProductCategory> findAll();
            List<ProductCategory> findByCategoryTypeIn(List<Integer> categoryTypeList);
            ProductCategory save(ProductCategory productCategory);
        }
        
      • service实现类:

        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.stereotype.Service;
        
        @Service
        public class ProductCategoryServiceImpl implements ProductCategoryService {
            @Autowired
            private ProductCategoryDao dao;
            @Override
            public ProductCategory findOne(Integer categoryId) {
                return dao.findOne(categoryId);
            }
            @Override
            public List<ProductCategory> findAll() {
                return dao.findAll();
            }
            @Override
            public List<ProductCategory> findByCategoryTypeIn(List<Integer> categoryTypeList) {
                return dao.findByCategoryTypeIn(categoryTypeList);
            }
            @Override
            public ProductCategory save(ProductCategory productCategory) {
                return dao.save(productCategory);
            }
        }
        
      • service测试类:

        @RunWith(SpringRunner.class)
        @SpringBootTest
        public class ProductCategoryServiceImplTest {
            @Autowired
            private ProductCategoryService service;
            @Test
            public void findOne() {
                ProductCategory one = service.findOne(1);
                System.out.println(one);
            }
            @Test
            public void findAll() {
                List<ProductCategory> all = service.findAll();
                System.out.println(all);
            }
            @Test
            public void findByCategoryTypeIn() {
                List<ProductCategory> byCategoryTypeIn = service.findByCategoryTypeIn(Arrays.asList(1, 2, 3, 4));
                System.out.println(byCategoryTypeIn);
            }
            @Test
            public void save() {
                ProductCategory productCategory = new ProductCategory("目录3", 5);
                service.save(productCategory);
        
            }
        }
        
    • 商品表:

      • 实体类:

        @Entity
        @DynamicUpdate
        public class ProductInfo {
            @Id
            private String productId;
            private String productName;
            private BigDecimal productPrice;
            private Integer productStock;
            private String productDescription;
            private String productIcon;
            private Integer productStatus;  //0正常,1下架
            private Integer categoryType;
            private Date createTime;
            private Date updateTime;
            public ProductInfo() {
            }
        
            public ProductInfo(String productId, String productName, BigDecimal productPrice, Integer productStock, String productDescription, String productIcon, Integer productStatus, Integer categoryType) {
                this.productId = productId;
                this.productName = productName;
                this.productPrice = productPrice;
                this.productStock = productStock;
                this.productDescription = productDescription;
                this.productIcon = productIcon;
                this.productStatus = productStatus;
                this.categoryType = categoryType;
            }
            /* getter&setter&toString */
        

      }
      ```

      • Dao接口:

        public interface ProductInfoDao extends JpaRepository<ProductInfo,String> {
            List<ProductInfo> findByProductStatus(Integer productStatus);
        

      }

      
      - dao测试类:
      
      ```java
      @RunWith(SpringRunner.class)
      @SpringBootTest
      public class ProductInfoDaoTest {
          @Autowired
          private ProductInfoDao dao;
          @Test
          public void saveTest(){
              ProductInfo productInfo = new ProductInfo("1", "商品1", new BigDecimal("12.3"), 20, "描述1", "url", 0, 1);
              dao.save(productInfo);
          }
      }
      
      • Service接口:

        public interface ProductInfoService {
            ProductInfo findOne(String productId);
            List<ProductInfo> findUpAll();  //所有上架商品
            Page<ProductInfo> findAll(Pageable pageable);
            ProductInfo save(ProductInfo productInfo);
            //加减库存
        

      }

      
      - service实现类:
      
      ```java
      @Service
      public class ProductInfoServiceImpl implements ProductInfoService {
          @Autowired
          private ProductInfoDao dao;
          @Override
          public ProductInfo findOne(String productId) {
              return dao.findOne(productId);
          }
          @Override
          public List<ProductInfo> findUpAll() {
              return dao.findByProductStatus(ProductStatusEnum.UP.getCode());
          }
          @Override
          public Page<ProductInfo> findAll(Pageable pageable) {
              return dao.findAll(pageable);
          }
          @Override
          public ProductInfo save(ProductInfo productInfo) {
              return dao.save(productInfo);
          }
      }
      
      • service测试类:

        @RunWith(SpringRunner.class)
        @SpringBootTest
        public class ProductInfoServiceImplTest {
            @Autowired
            private ProductInfoService service;
            @Test
            public void findOne() {
                ProductInfo one = service.findOne("12");
                System.out.println(one);
            }
            @Test
            public void findUpAll() {
                List<ProductInfo> upAll = service.findUpAll();
                System.out.println(upAll);
            }
            @Test
            public void findAll() {
                PageRequest request = new PageRequest(0,2);
                Page<ProductInfo> all = service.findAll(request);
                System.out.println(all.getTotalElements());
            }
            @Test
            public void save() {
                ProductInfo productInfo = new ProductInfo("12", "商品12", new BigDecimal("612.3"), 20, "描述12", "url", 0, 1);
                service.save(productInfo);
            }
        }
        
        //枚举增加可读性
        public enum ProductStatusEnum {
            UP(0,"上架"),
            DOWN(1,"下架");
        
            private Integer code;
            private String message;
            ProductStatusEnum(Integer code, String message) {
                this.code = code;
                this.message = message;
            }
            public Integer getCode() {
                return code;
            }
            public String getMessage() {
                return message;
            }
        }
        
    • 商品列表:

      • 请求:

        GET /sell/buyer/product/list
        
      • 返回:

        {
            "code": 0,
            "msg": "成功",
            "data": [	# 类名列表
                {
                    "name": "热榜",
                    "type": 1,
                    "foods": [	# 类目中的商品列表
                        {
                            "id": "123456",
                            "name": "皮蛋粥",
                            "price": 1.2,
                            "description": "好吃的皮蛋粥",
                            "icon": "http://xxx.com",
                        }
                    ]
                },
                {
                    "name": "好吃的",
                    "type": 2,
                    "foods": [
                        {
                            "id": "123457",
                            "name": "慕斯蛋糕",
                            "price": 10.9,
                            "description": "美味爽口",
                            "icon": "http://xxx.com",
                        }
                    ]
                }
            ]
        }
        
      • 返回的对应的实体类:

        • ProductResultVO的设计理由是,要返回前端的JSON格式都是以code+msg+data的形式,其中data是具体的不同的数据对象。
        public class ProductResultVO<T> {
            private Integer code;   //状态码,0正常1错误
            private String msg;
            private List<T> data;
            /* getter&setter&toString */
        }
        
        public class ProductCategoryVO {
            @JsonProperty("name")	//注解用来指定JSON序列化时的项目名
            private String categoryName;
            @JsonProperty("type")
            private Integer categoryType;
            private List<ProdutInfoVO> foods;
            /* getter&setter&toString */
        }
        
        public class ProdutInfoVO {
            @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;
            /* getter&setter&toString */
        }
        
      • 根据要返回的对象封装ProductResultVO:

        public class ProductResultVOUtil {
            public static ProductResultVO success(Object object) {
                ProductResultVO resultVO = new ProductResultVO();
                resultVO.setData(object);
                resultVO.setMsg("success");
                resultVO.setCode(0);
                return resultVO;
            }
            public static ProductResultVO success() {
                return success(null);
            }
        }
        
      • 控制器测试:

        @RestController
        @RequestMapping("/buyer/product")
        public class BuyerProductController {
            @GetMapping("/list")
            public ProductResultVO<ProductCategoryVO> list() {
                ProductResultVO<ProductCategoryVO> resultVO = new ProductResultVO<>();
                ProductCategoryVO categoryVO = new ProductCategoryVO();
                ProdutInfoVO infoVO = new ProdutInfoVO();
        
                categoryVO.setFoods(Arrays.asList(infoVO,infoVO));
                resultVO.setData(Arrays.asList(categoryVO));
                return resultVO;
            }
        }
        
      • 控制器实现业务:

        1. 业务目的:返回所有上架的商品并且按类名显示。
        2. 首先查询所有上架商品。
        3. 通过所有上架商品,遍历查询其类目(此处有优化空间),得到目前有商品的所有类目。
        4. 数据封装。
          • 创建一个最终返回的结果对象。
          • 创建一个列表,元素是某个类目下显示的信息和商品。
          • 遍历获得的类目列表。并添加到上一步的列表。
            • 创建一个列表,元素是商品的展示信息。
            • 遍历上架的商品列表,比较类名是否相同。相同则添加到列表。
          • 设置相关信息并返回最终的结果对象。
        @RestController
        @RequestMapping("/buyer/product")
        public class BuyerProductController {
            @Autowired
            private ProductCategoryService categoryService;
            @Autowired
            private ProductInfoService infoService;
        
            @GetMapping("/list")
            public ProductResultVO<ProductCategoryVO> list() {
                //1、查询所有上架商品
                List<ProductInfo> upAll = infoService.findUpAll();
                //2、查询类名
                List<Integer> categoryTypeList = new ArrayList<>();
                for (ProductInfo productInfo : upAll) {
                    categoryTypeList.add(productInfo.getCategoryType());
                }
                List<ProductCategory> productCategoryList = categoryService.findByCategoryTypeIn(categoryTypeList);
                //3、数据拼装
                ProductResultVO<ProductCategoryVO> resultVO = new ProductResultVO<>();
                List<ProductCategoryVO> categoryVOList = new ArrayList<>();
                for (ProductCategory productCategory : productCategoryList) {
                    ProductCategoryVO categoryVO = new ProductCategoryVO();
                    categoryVO.setCategoryType(productCategory.getCategoryType());
                    categoryVO.setCategoryName(productCategory.getCategoryName());
                    List<ProdutInfoVO> infoVOList = new ArrayList<>();
                    for (ProductInfo productInfo : upAll) {
                        if (productInfo.getCategoryType().equals(productCategory.getCategoryType())){
                            ProdutInfoVO infoVO = new ProdutInfoVO();
                            BeanUtils.copyProperties(productInfo,infoVO);
                            infoVOList.add(infoVO);
                        }
                    }
                    categoryVO.setFoods(infoVOList);
                    categoryVOList.add(categoryVO);
                }
                resultVO.setData(categoryVOList);
                resultVO.setCode(0);
                resultVO.setMsg("success");
                return resultVO;
            }
        }
        
      • 前端联调:

        • 因为还没有连微信,需要手动设置Cookie。

          • 先进入/#/order下。

          • 浏览器检查,在console中设置document.cookie="openid=abc"

    3、创建订单

    • 订单主表:

      • 实体类:

        @Entity
        @DynamicUpdate
        public class OrderMaster {
            @Id
            private String orderId;
            private String buyerName;
            private String buyerPhone;
            private String buyerAddress;
            private String buyerOpenid;
            private BigDecimal orderAmount;
            private Integer orderStatus = OrderStatusEnum.NEW.getCode();
            private Integer payStatus = PayStatusEnum.WAIT.getCode();
            private Date createTime;
            private Date updateTime;
        
            public OrderMaster() {
            }
            public OrderMaster(String orderId, String buyerName, String buyerPhone, String buyerAddress, String buyerOpenid, BigDecimal orderAmount) {
                this.orderId = orderId;
                this.buyerName = buyerName;
                this.buyerPhone = buyerPhone;
                this.buyerAddress = buyerAddress;
                this.buyerOpenid = buyerOpenid;
                this.orderAmount = orderAmount;
            }
            /* g s t */
        }
        
        //枚举提高可读性
        public enum  OrderStatusEnum {
            NEW(0,"新订单"),
            FINISHED(1,"完结"),
            CANCEL(2,"已取消");
        
            private Integer code;
            private String message;
            OrderStatusEnum(Integer code, String message) {
                this.code = code;
                this.message = message;
            }
            public Integer getCode() {
                return code;
            }
            public String getMessage() {
                return message;
            }
        }
        
        public enum PayStatusEnum {
            WAIT(0,"等待支付"),
            SUCCESS(1,"支付成功");
        
            private Integer code;
            private String message;
            PayStatusEnum(Integer code, String message) {
                this.code = code;
                this.message = message;
            }
            public Integer getCode() {
                return code;
            }
            public String getMessage() {
                return message;
            }
        }
        
      • Dao接口:

        public interface OrderMasterDao extends JpaRepository<OrderMaster,String> {
            //根据微信的openid查询订单
            Page<OrderMaster> findByBuyerOpenid(String buyerOpenid, Pageable pageable);
        }
        
      • dao测试类:

        @RunWith(SpringRunner.class)
        @SpringBootTest
        public class OrderMasterDaoTest {
            @Autowired
            private OrderMasterDao dao;
            @Test
            public void saveTest(){
                OrderMaster orderMaster = new OrderMaster("1","订单1","122","CN","123",new BigDecimal("52.2"));
                dao.save(orderMaster);
            }
            @Test
            public void findByBuyerOpenid() {
                PageRequest request = new PageRequest(0,2);
                Page<OrderMaster> byBuyerOpenid = dao.findByBuyerOpenid("123", request);
                System.out.println(byBuyerOpenid.getTotalElements());
            }
        }
        
      • Service接口:

        • 为了数据传输方便,新建一个订单DTO数据传输对象,其中包含了具体订单列表。

          public class OrderDTO {
              private String orderId;
              private String buyerName;
              private String buyerPhone;
              private String buyerAddress;
              private String buyerOpenid;
              private BigDecimal orderAmount;
              private Integer orderStatus = OrderStatusEnum.NEW.getCode();
              private Integer payStatus = PayStatusEnum.WAIT.getCode();
              private Date createTime;
              private Date updateTime;
              private List<OrderDetail> orderDetailList;
          }
          
        • 功能包括:创建、查询单个、查询所有、取消、完结、支付。

          public interface OrderMasterService {
              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);
          }
          
    • 订单细节:

      • 实体类:

        @Entity
        public class OrderDetail {
            @Id
            private String detailId;
            private String orderId;
            private String productId;
            private String productName;
            private BigDecimal productPrice;
            private Integer productQuantity;
            private String productIcon;
        
            public OrderDetail() {
            }
            public OrderDetail(String detailId, String orderId, String productId, String productName, BigDecimal productPrice, Integer productQuantity, String productIcon) {
                this.detailId = detailId;
                this.orderId = orderId;
                this.productId = productId;
                this.productName = productName;
                this.productPrice = productPrice;
                this.productQuantity = productQuantity;
                this.productIcon = productIcon;
            }
            /* g s t */
        }
        
      • Dao接口:

        public interface OrderDetailDao extends JpaRepository<OrderDetail,String> {
            //根据订单号返回所有详细订单
            List<OrderDetail> findByOrderId(String orderId);
        }
        
      • dao测试类:

        @RunWith(SpringRunner.class)
        @SpringBootTest
        public class OrderDetailDaoTest {
            @Autowired
            private OrderDetailDao dao;
            @Test
            public void saveTest(){
                OrderDetail orderDetail = new OrderDetail("1","1","1","商品1",new BigDecimal("46.2"),20,"url");
                dao.save(orderDetail);
            }
            @Test
            public void findByOrderId() {
                List<OrderDetail> byOrderId = dao.findByOrderId("1");
                System.out.println(byOrderId);
            }
        }
        
    • 创建订单:

      • 请求:

        POST /sell/buyer/order/create
        
      • 参数:注意这里参数都不应该带引号,否则数据库中也会带引号。

        name: 张三
        phone: 18868822111
        address: 慕课网总部
        openid: ew3euwhd7sjw9diwkq //用户的微信openid
        items: [{	//购物车
            productId: 1423113435324,
            productQuantity: 2 //购买数量
        }]
        
      • 返回:注意这里的data是一个map,而不是像别的是一个List<map>

        {
          "code": 0,
          "msg": "成功",
          "data": {
              "orderId": "147283992738221" 
          }
        }
        
    • 创建总订单的service实现类:

      • 一些商品的信息需要从数据库中查,另一些从前端获取。

      • 创建订单:

        • 查询商品数量和价格。
        • 计算总价。
        • 写入订单数据库master和detail。
        • 扣库存。
      • 前端传入的购物车信息只有商品id和数量,创建实体类:

        //购物车
        public class CartDTO {
            private String productId;
            private Integer productQuantity;
        }
        
      • 在ProductInfoService中增加加减库存的方法,及其实现:

        • @Transactional事务使得方法运行过程中抛出异常则回滚。
        • 减库存可能会出现超卖,后期用分布式锁防止。
        void increaseStock(List<CartDTO> cartDTOList);
        void decreaseStock(List<CartDTO> cartDTOList);
        
        @Override
        @Transactional
        public void increaseStock(List<CartDTO> cartDTOList) {
            for (CartDTO cartDTO : cartDTOList) {
                ProductInfo productInfo = dao.findOne(cartDTO.getProductId());
                if(productInfo == null) {
                    throw new SellException(ResultEnum.PRODUCT_NOT_EXIST);
                }
                int result = productInfo.getProductStock() + cartDTO.getProductQuantity();
                productInfo.setProductStock(result);
                dao.save(productInfo);
            }
        }
        
        @Override
        @Transactional
        public void decreaseStock(List<CartDTO> cartDTOList) {
            for (CartDTO cartDTO : cartDTOList) {
                ProductInfo productInfo = dao.findOne(cartDTO.getProductId());
                if(productInfo == null) {
                    throw new SellException(ResultEnum.PRODUCT_NOT_EXIST);
                }
                int result = productInfo.getProductStock() - cartDTO.getProductQuantity();
                if (result < 0 ) {
                    throw new SellException(ResultEnum.PRODUCT_STOCK_ERROR);
                }
                productInfo.setProductStock(result);
                dao.save(productInfo);
            }
        }
        
      • 查询商品结果状态的枚举:

        public enum ResultEnum {
            PRODUCT_NOT_EXIST(0,"商品不存在"),
            PRODUCT_STOCK_ERROR(1,"库存错误"),
            ORDER_NOT_EXIST(2,"订单不存在"),
            ORDERDETAIL_NOT_EXIST(3,"订单详情不存在"),
            ORDER_STATUS_ERROR(4,"订单状态不正确"),
            ORDER_UPDATE_FAIL(5,"订单更新失败"),
            ORDER_DETAIL_EMPTY(6,"订单详情为空"),
            ORDER_PAY_STATUS_ERROR(7,"支付状态错误"),
            PARAM_ERROR(8,"参数不正确"),
            CART_EMPTY(9,"购物车为空"),
            ORDER_OWNER_ERROR(10,"操作的不是你的订单"),
            ;
            private Integer code;
            private String message;
        }
        
      • 售卖的异常:

        public class SellException extends RuntimeException {
            private Integer code;
        
            public SellException(ResultEnum resultEnum) {
                super(resultEnum.getMessage());
                this.code = resultEnum.getCode();
            }
        }
        
      • 生成总订单和详细订单唯一主键的工具类:

        public class KeyUtil {
            //生成唯一主键:时间+6位随机数
            public static synchronized String genUniqueKey() {
                Random random = new Random();
                Integer num = random.nextInt(900000) + 100000;
                return System.currentTimeMillis() + String.valueOf(num);
            }
        }
        
      • 创建订单:

        1. 传入的OrderDTO中的OrderDetail只有购物车的信息。
        2. 遍历OrderDetailList,查询商品,计算总价。
        3. 分别写入订单详情表和订单主表。
        @Service
        public class OrderMasterServiceImpl implements OrderMasterService {
            @Autowired
            private ProductInfoService productInfoService;
            @Autowired
            private OrderMasterDao masterDao;
            @Autowired
            private OrderDetailDao detailDao;
            @Override
            @Transactional
            public OrderDTO create(OrderDTO orderDTO) {
                //开始生成订单号,初始化总价和购物车列表
                String orderId = KeyUtil.genUniqueKey();
                BigDecimal orderAmount = new BigDecimal("0");
                List<CartDTO> cartDTOList = new ArrayList<>();
                //1、查询商品数量和价格
                for (OrderDetail orderDetail : orderDTO.getOrderDetailList()) {
                    ProductInfo productInfo = productInfoService.findOne(orderDetail.getProductId());
                    if(productInfo == null) {
                        throw new SellException(ResultEnum.PRODUCT_NOT_EXIST);
                    }
                    //2、计算总价
                    orderAmount = productInfo.getProductPrice()
                        .multiply(new BigDecimal(orderDetail.getProductQuantity()))
                        .add(orderAmount);
                    //3、写入订单数据库detail,注意先拷贝再赋新值
                    BeanUtils.copyProperties(productInfo,orderDetail);
                    orderDetail.setDetailId(KeyUtil.genUniqueKey());
                    orderDetail.setOrderId(orderId);
                    detailDao.save(orderDetail);
        
                    CartDTO cartDTO = new CartDTO(orderDetail.getProductId(), orderDetail.getProductQuantity());
                    cartDTOList.add(cartDTO);
        
                }
                //3、写入订单数据库master
                OrderMaster orderMaster = new OrderMaster();
                BeanUtils.copyProperties(orderDTO,orderMaster);
                orderMaster.setOrderId(orderId);
                orderMaster.setOrderAmount(orderAmount);
                orderMaster.setOrderStatus(OrderStatusEnum.NEW.getCode());
                orderMaster.setPayStatus(PayStatusEnum.WAIT.getCode());
                masterDao.save(orderMaster);
                //4、扣库存
                productInfoService.decreaseStock(cartDTOList);
        
                return orderDTO;
            }
        }
        
      • 测试类:

        @RunWith(SpringRunner.class)
        @SpringBootTest
        public class OrderMasterServiceImplTest {
            @Autowired
            private OrderMasterService service;
            private final String BUYER_OPENID = "aaa";
            @Test
            public void create() {
                OrderDTO orderDTO = new OrderDTO();
                orderDTO.setBuyerName("iwehdio");
                orderDTO.setBuyerAddress("XXX-Add");
                orderDTO.setBuyerPhone("4466");
                orderDTO.setBuyerOpenid(BUYER_OPENID);
        
                List<OrderDetail> orderDetailList = new ArrayList<>();
        
                OrderDetail o1 = new OrderDetail();
                o1.setProductId("1");
                o1.setProductQuantity(2);
                OrderDetail o2 = new OrderDetail();
                o2.setProductId("12");
                o2.setProductQuantity(5);
                orderDetailList.add(o1);
                orderDetailList.add(o2);
                orderDTO.setOrderDetailList(orderDetailList);
                service.create(orderDTO);
            }
        }
        
    • service层的其他订单操作:

      • 查找一个:

        • 订单id和订单详情为空时抛出异常。
        @Override
        public OrderDTO findOne(String orderId) {
            OrderMaster orderMaster = masterDao.findOne(orderId);
            if (orderMaster == null) {
                throw new SellException(ResultEnum.ORDER_NOT_EXIST);
            }
            List<OrderDetail> orderDetailList = detailDao.findByOrderId(orderId);
            if (orderDetailList.isEmpty()){
                throw new SellException(ResultEnum.ORDERDETAIL_NOT_EXIST);
            }
            OrderDTO orderDTO = new OrderDTO();
            BeanUtils.copyProperties(orderMaster,orderDTO);
            orderDTO.setOrderDetailList(orderDetailList);
            return orderDTO;
        }
        
      • 查询某个用户的所有订单:

        @Override
        public Page<OrderDTO> findList(String buyerOpenid, Pageable pageable) {
            Page<OrderMaster> orderMasterPage = masterDao.findByBuyerOpenid(buyerOpenid, pageable);
            List<OrderDTO> orderDTOList = OrderMaster2OrderDTOConverter.convert(orderMasterPage.getContent());
            return new PageImpl<OrderDTO>(orderDTOList,pageable,orderMasterPage.getTotalElements());
        }
        
        //OrderMaster转化为OrderDTO
        public class OrderMaster2OrderDTOConverter {
            public static OrderDTO convert(OrderMaster orderMaster) {
                OrderDTO orderDTO = new OrderDTO();
                BeanUtils.copyProperties(orderMaster,orderDTO);
                return orderDTO;
            }
            public static List<OrderDTO> convert(List<OrderMaster> orderMasterList) {
                List<OrderDTO> orderDTOList = new ArrayList<>();
                for (OrderMaster orderMaster : orderMasterList) {
                    orderDTOList.add(convert(orderMaster));
                }
                return orderDTOList;
            }
        }
        
      • 订单取消:

        1. 判断订单状态,新创建的new才能被取消。
        2. 修改订单状态为取消。
        3. 返还库存。
        @Override
        @Transactional
        public OrderDTO cancel(OrderDTO orderDTO) {
            OrderMaster orderMaster = new OrderMaster();
            //1、判断订单状态,指定状态下才能被取消
            if (!OrderStatusEnum.NEW.getCode().equals(orderDTO.getOrderStatus())){
                logger.error("【取消订单】订单状态不正确,orderId={},orderStatus={}",
                             orderDTO.getOrderId(),orderDTO.getOrderStatus());
                throw new SellException(ResultEnum.ORDER_STATUS_ERROR);
            }
            //2、修改订单状态
            orderDTO.setOrderStatus(OrderStatusEnum.CANCEL.getCode());
            BeanUtils.copyProperties(orderDTO,orderMaster);
            OrderMaster save = masterDao.save(orderMaster);
            if (save ==null){
                logger.error("【取消订单】更新失败,orderMaster={}",orderMaster);
                throw new SellException(ResultEnum.ORDER_UPDATE_FAIL);
            }
            //3、返还库存
            if(orderDTO.getOrderDetailList().isEmpty()) {
                logger.error("【取消订单】详情为空,orderDTO={}",orderDTO);
                throw new SellException(ResultEnum.ORDER_DETAIL_EMPTY);
            }
            List<CartDTO> cartDTOList = new ArrayList<>();
            for (OrderDetail orderDetail : orderDTO.getOrderDetailList()) {
                cartDTOList.add(new CartDTO(orderDetail.getProductId(),orderDetail.getProductQuantity()));
            }
            productInfoService.increaseStock(cartDTOList);
            //4、如果已支付需要退款
            if (PayStatusEnum.SUCCESS.getCode().equals(orderDTO.getPayStatus())){
                //TODO
            }
            return orderDTO;
        }
        
      • 订单完结:

        @Override
        @Transactional
        public OrderDTO finish(OrderDTO orderDTO) {
            OrderMaster orderMaster = new OrderMaster();
            //1、判断订单状态,指定状态下才能被完结
            if (!OrderStatusEnum.NEW.getCode().equals(orderDTO.getOrderStatus())){
                logger.error("【完结订单】订单状态不正确,orderId={},orderStatus={}",
                             orderDTO.getOrderId(),orderDTO.getOrderStatus());
                throw new SellException(ResultEnum.ORDER_STATUS_ERROR);
            }
            //2、修改订单状态
            orderDTO.setOrderStatus(OrderStatusEnum.FINISHED.getCode());
            BeanUtils.copyProperties(orderDTO,orderMaster);
            OrderMaster save = masterDao.save(orderMaster);
            if (save ==null){
                logger.error("【完结订单】更新失败,orderMaster={}",orderMaster);
                throw new SellException(ResultEnum.ORDER_UPDATE_FAIL);
            }
            return orderDTO;
        }
        
      • 订单完成支付:

        @Override
        @Transactional
        public OrderDTO paid(OrderDTO orderDTO) {
            OrderMaster orderMaster = new OrderMaster();
            //1、判断订单状态,指定状态下才能被支付
            if (!OrderStatusEnum.NEW.getCode().equals(orderDTO.getOrderStatus())){
                logger.error("【支付订单】订单状态不正确,orderId={},orderStatus={}",
                             orderDTO.getOrderId(),orderDTO.getOrderStatus());
                throw new SellException(ResultEnum.ORDER_STATUS_ERROR);
            }
            //2、判断订单支付状态
            if (!PayStatusEnum.WAIT.getCode().equals(orderDTO.getPayStatus())){
                logger.error("【支付订单】更新失败,orderDTO={}",orderDTO);
                throw new SellException(ResultEnum.ORDER_PAY_STATUS_ERROR);
            }
            //3、修改订单状态
            orderDTO.setPayStatus(PayStatusEnum.SUCCESS.getCode());
            BeanUtils.copyProperties(orderDTO,orderMaster);
            OrderMaster save = masterDao.save(orderMaster);
            if (save ==null){
                logger.error("【支付订单】更新失败,orderMaster={}",orderMaster);
                throw new SellException(ResultEnum.ORDER_UPDATE_FAIL);
            }
            return orderDTO;
        }
        
      • 测试类:

        private final String BUYER_OPENID = "aaa";
        
        @Test
        public void findOne() {
            OrderDTO orderDTO = service.findOne("1606478486299603884");
            System.out.println(orderDTO);
        }
        
        @Test
        public void findList() {
            PageRequest request = new PageRequest(0,2);
            Page<OrderDTO> list = service.findList(BUYER_OPENID, request);
            System.out.println(list.getContent());
        }
        
        @Test
        public void cancel() {
            OrderDTO orderDTO = service.findOne("1606478486299603884");
            OrderDTO cancel = service.cancel(orderDTO);
            System.out.println(cancel);
        }
        
        @Test
        public void finish() {
            OrderDTO orderDTO = service.findOne("1606478486299603884");
            OrderDTO finish = service.finish(orderDTO);
            System.out.println(finish);
        }
        
        @Test
        public void paid() {
            OrderDTO orderDTO = service.findOne("1606478486299603884");
            OrderDTO paid = service.paid(orderDTO);
            System.out.println(paid);
        }
        
    • 创建总订单的控制器controller层:

      • 按照前端传回参数封装的对象:

        • 相应的做了表单校验。
        • 需要将其转化为OrderDTO,用一个转换类。
        public class OrderForm {
            @NotEmpty(message = "姓名必填")
            private String name;
            @NotEmpty(message = "手机号必填")
            private String phone;
            @NotEmpty(message = "地址必填")
            private String address;
            @NotEmpty(message = "openid必填")
            private String openid;
            @NotEmpty(message = "购物车不能为空")
            private String items;
            /* g s t */
        }
        
        public class OrderForm2OrderDTOConverter {
            private static Logger logger = LoggerFactory.getLogger(OrderForm2OrderDTOConverter.class);
            public static OrderDTO convert(OrderForm orderForm) {
                OrderDTO orderDTO = new OrderDTO();
                orderDTO.setBuyerName(orderForm.getName());
                orderDTO.setBuyerPhone(orderForm.getPhone());
                orderDTO.setBuyerOpenid(orderForm.getOpenid());
                orderDTO.setBuyerAddress(orderForm.getAddress());
        
                Gson gson = new Gson();
                List<OrderDetail> orderDetailList;
                try {
                    orderDetailList = gson.fromJson(orderForm.getItems(),new TypeToken<List<OrderDetail>>(){}.getType());
                } catch (JsonSyntaxException e) {
                    logger.error("【对象转换】错误,string={}",orderForm.getItems());
                    throw new SellException(ResultEnum.PARAM_ERROR);
                }
                orderDTO.setOrderDetailList(orderDetailList);
                return orderDTO;
            }
        }
        
      • 创建订单的控制器实现:

        @RestController
        @RequestMapping("/buyer/order")
        public class BuyerOrderController {
            private final Logger logger = LoggerFactory.getLogger(BuyerOrderController.class);
            @Autowired
            private OrderMasterService masterService;
            @Autowired
            private BuyerService buyerService;
            //创建订单
            @PostMapping("/create")
            public ProductResultVO<Map<String,String>> create(@Valid OrderForm orderForm,
                                                              BindingResult bindingResult) {
                if (bindingResult.hasErrors()) {
                    logger.error("【创建订单】参数不正确,orderForm={}",orderForm);
                    throw new SellException(ResultEnum.PARAM_ERROR,
                            bindingResult.getFieldError().getDefaultMessage());
                }
                OrderDTO orderDTO = OrderForm2OrderDTOConverter.convert(orderForm);
                if(orderDTO.getOrderDetailList().isEmpty()){
                    logger.error("【创建订单】购物车不能为空");
                    throw new SellException(ResultEnum.CART_EMPTY);
                }
                OrderDTO result = masterService.create(orderDTO);
                Map<String,String> map = new HashMap<>();
                map.put("orederId",result.getOrderId());
                return ProductResultVOUtil.success(map);
            }
        }
        
    • 其他功能的请求与返回:

      • 返回前端的数据为null怎么办:

        • 一般来说尽量不要给前端返回null。

        • 第一种方法是如果为null就不返回这项,可在全局配置文件中:

          spring:
              jackson:
                  default-property-inclusion: non_null
          
        • 第二种是设置一个返回的默认值,在实体类中直接初始化成员变量。

      • 订单列表:

        • 请求:

          GET /sell/buyer/order/list
          
        • 参数:

          openid: 18eu2jwk2kse3r42e2e
          page: 0 //从第0页开始
          size: 10
          
        • 返回:

          {
            "code": 0,
            "msg": "成功",
            "data": [
              {
                "orderId": "161873371171128075",
                "buyerName": "张三",
                "buyerPhone": "18868877111",
                "buyerAddress": "慕课网总部",
                "buyerOpenid": "18eu2jwk2kse3r42e2e",
                "orderAmount": 0,
                "orderStatus": 0,
                "payStatus": 0,
                "createTime": 1490171219,
                "updateTime": 1490171219,
                "orderDetailList": null
              },
              {
                "orderId": "161873371171128076",
                "buyerName": "张三",
                "buyerPhone": "18868877111",
                "buyerAddress": "慕课网总部",
                "buyerOpenid": "18eu2jwk2kse3r42e2e",
                "orderAmount": 0,
                "orderStatus": 0,
                "payStatus": 0,
                "createTime": 1490171219,
                "updateTime": 1490171219,
                "orderDetailList": null
              }]
          }
          
      • 订单详情:

        • 请求:

          GET /sell/buyer/order/detail
          
        • 参数:

          openid: 18eu2jwk2kse3r42e2e
          orderId: 161899085773669363
          
        • 返回:

          {
              "code": 0,
              "msg": "成功",
              "data": {
                    "orderId": "161899085773669363",
                    "buyerName": "李四",
                    "buyerPhone": "18868877111",
                    "buyerAddress": "慕课网总部",
                    "buyerOpenid": "18eu2jwk2kse3r42e2e",
                    "orderAmount": 18,
                    "orderStatus": 0,
                    "payStatus": 0,
                    "createTime": 1490177352,
                    "updateTime": 1490177352,
                    "orderDetailList": [
                      {
                          "detailId": "161899085974995851",
                          "orderId": "161899085773669363",
                          "productId": "157875196362360019",
                          "productName": "招牌奶茶",
                          "productPrice": 9,
                          "productQuantity": 2,
                          "productIcon": "http://xxx.com",
                          "productImage": "http://xxx.com"
                      }
                  ]
              }
          }
          
      • 取消订单:

        • 请求:

          POST /sell/buyer/order/cancel
          
        • 参数:

          openid: 18eu2jwk2kse3r42e2e
          orderId: 161899085773669363
          
        • 返回:

          {
              "code": 0,
              "msg": "成功",
              "data": null
          }
          
    • 其他功能的控制器实现:

      • 订单列表:

        @GetMapping("/list")
        public ProductResultVO<List<OrderDTO>> list(@RequestParam("openid") String openid,
                                                    @RequestParam(value = "page",defaultValue = "0") Integer page,
                                                    @RequestParam(value = "size",defaultValue = "10") Integer size){
            if (StringUtils.isEmpty(openid)){
                logger.error("【查询订单列表】openid为空");
                throw new SellException(ResultEnum.PARAM_ERROR);
            }
            PageRequest request = new PageRequest(page,size);
            Page<OrderDTO> list = masterService.findList(openid, request);
            return ProductResultVOUtil.success(list.getContent());
        }
        
        • 返回前端的createTime和updateTime默认被乘以1000转化为了秒。

          • 主要原因是Json对Date的序列化机制。
          • 需要在OrderDTO中日期类型上指定@JsonSerialize(using = Date2LongSerializer.class)
          import com.fasterxml.jackson.core.JsonGenerator;
          import com.fasterxml.jackson.core.JsonProcessingException;
          import com.fasterxml.jackson.databind.JsonSerializer;
          import com.fasterxml.jackson.databind.SerializerProvider;
          
          public class Date2LongSerializer extends JsonSerializer<Date> {
              @Override
              public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
                  gen.writeNumber(value.getTime()/1000);
              }
          }
          
      • 订单详情/取消订单:

        • 订单详情和取消订单的操作都需要判断身份,即同一个访问的openid才能操作他自己的订单。

        • 买家服务:

          public interface BuyerService {
              OrderDTO findOneOrder(String openid,String orderId);
              OrderDTO cancelOrder(String openid,String orderId);
          }
          
          @Service
          public class BuyerServiceImpl implements BuyerService {
              @Autowired
              private OrderMasterService masterService;
              private final Logger logger = LoggerFactory.getLogger(BuyerServiceImpl.class);
          
              @Override
              public OrderDTO findOneOrder(String openid, String orderId) {
                  return checkOrderOwner(openid,orderId);
              }
          
              @Override
              public OrderDTO cancelOrder(String openid, String orderId) {
                  OrderDTO orderDTO = checkOrderOwner(openid, orderId);
                  if (orderDTO == null) {
                      logger.error("【取消订单】查不到该订单,orderId={}",orderId);
                      throw new SellException(ResultEnum.ORDER_NOT_EXIST);
                  }
                  return masterService.cancel(orderDTO);
              }
          
              private OrderDTO checkOrderOwner(String openid,String orderId){
                  OrderDTO orderDTO = masterService.findOne(orderId);
                  if (orderDTO == null) {
                      return null;
                  }
                  if (!orderDTO.getBuyerOpenid().equalsIgnoreCase(openid)) {
                      logger.error("【查询订单】订单的openid不一致,openid={},orderDTO={}",openid,orderDTO);
                      throw new SellException(ResultEnum.ORDER_OWNER_ERROR);
                  }
                  return orderDTO;
              }
          }
          
        • 订单详情控制器方法:

          //订单详情
          @GetMapping("/detail")
          public ProductResultVO<OrderDTO> detail(@RequestParam("openid") String openid,
                                                  @RequestParam("orderId") String orderId){
              OrderDTO orderDTO = buyerService.findOneOrder(openid,orderId);
              return ProductResultVOUtil.success(orderDTO);
          }
          
        • 取消订单控制器方法:

          //取消订单
          @PostMapping("/cancel")
          public ProductResultVO<OrderDTO> cancel(@RequestParam("openid") String openid,
                                                  @RequestParam("orderId") String orderId){
              buyerService.cancelOrder(openid,orderId);
              return ProductResultVOUtil.success();
          }
          

    iwehdio的博客园:https://www.cnblogs.com/iwehdio/
  • 相关阅读:
    Jmeter简单使用
    Linux命令补充
    数据加密
    问题 Can't load AMD 64-bit .dll on a IA 32-bit platform
    需要知道的东西很多还要知道的牢固
    Sqlyog问题
    精神苦难和快乐
    了解一个名词——GTD
    超强记忆力提升九大心法-10连锁记忆法
    Array数组结构底层实现复习
  • 原文地址:https://www.cnblogs.com/iwehdio/p/14055566.html
Copyright © 2011-2022 走看看