目录

  • 一、创建项目
  • 二、项目结构
  • 1.目录结构
  • 1.1购物车
  • 1.2订单
  • 2.配置文件
  • 3.Maven依赖
  • 4.启动项目


一、创建项目

首先创建一个SpringBoot项目,具体创建过程和 微服务(三)—— 用户模块(backend-user).一样。

二、项目结构

1.目录结构

商品 订单 支付 架构 订单结构分为三步_List


项目结构就是Controller、Service、Dao三层结构,由于订单和购物车耦合度比较高,所以我就将购物和订单业务放在一个模块了。

1.1购物车

(1)Controller层
主要是提供给前端的增删改查接口,完整的类如下:

package com.aiun.order.controller;
/**
 * 购物车控制层
 * @author lenovo
 */
@Api(tags = "购物车接口")
@RestController
@RequestMapping("/cart/")
public class CartController {
    @Autowired
    private ICartService iCartService;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 添加购物车功能
     * @param productId 产品id
     * @param count 产品个数
     * @return 封装好的购物车 VO
     */
    @GetMapping("add")
    @ApiOperation(value = "购物车添加商品功能")
    public ServerResponse<CartVO> add(HttpServletRequest request, Integer productId, Integer count) {
        ServerResponse hasLogin = loginHasExpired(request);

        if (hasLogin.isSuccess()) {
            User user = (User) hasLogin.getData();
            return iCartService.add(user.getId(), productId, count);
        }

        return hasLogin;
    }
    /**
     * 更新购物车商品功能
     * @param productId 产品 id
     * @param count 产品个数
     * @return 封装好的购物车 VO
     */
    @PostMapping("update")
    @ApiOperation(value = "更新购物车商品功能")
    public ServerResponse<CartVO> update(HttpServletRequest request, Integer productId, Integer count) {
        ServerResponse hasLogin = loginHasExpired(request);

        if (hasLogin.isSuccess()) {
            User user = (User) hasLogin.getData();
            return iCartService.update(user.getId(), productId, count);
        }

        return hasLogin;
    }

    /**
     * 删除购物车商品
     * @param productIds 多个产品的id
     * @return 封装好的购物车 VO
     */
    @DeleteMapping("delete_product")
    @ApiOperation(value = "删除购物车商品")
    public ServerResponse<CartVO> deleteProduct(HttpServletRequest request, String productIds) {
        ServerResponse hasLogin = loginHasExpired(request);

        if (hasLogin.isSuccess()) {
            User user = (User) hasLogin.getData();
            return iCartService.deleteProduct(user.getId(), productIds);
        }

        return hasLogin;
    }

    /**
     * 查询购物车商品列表
     * @return 封装好的购物车 VO
     */
    @PostMapping("list")
    @ApiOperation(value = "查询购物车商品列表")
    public ServerResponse<CartVO> list(HttpServletRequest request) {
        ServerResponse hasLogin = loginHasExpired(request);

        if (hasLogin.isSuccess()) {
            User user = (User) hasLogin.getData();
            return iCartService.list(user.getId());
        }

        return hasLogin;
    }

    /**
     * 全选
     * @return 封装好的购物车 VO
     */
    @PostMapping("select_all")
    @ApiOperation(value = "购物车全选")
    public ServerResponse<CartVO> selectAll(HttpServletRequest request) {
        ServerResponse hasLogin = loginHasExpired(request);

        if (hasLogin.isSuccess()) {
            User user = (User) hasLogin.getData();
            return iCartService.selectOrUnSelect(user.getId(), null, CartConst.Cart.CHECKED);
        }
        return hasLogin;
    }

    /**
     * 反选
     * @return 封装好的购物车 VO
     */
    @PostMapping("un_select_all")
    @ApiOperation(value = "反选")
    public ServerResponse<CartVO> unSelectAll(HttpServletRequest request) {
        ServerResponse hasLogin = loginHasExpired(request);

        if (hasLogin.isSuccess()) {
            User user = (User) hasLogin.getData();
            return iCartService.selectOrUnSelect(user.getId(), null, CartConst.Cart.UN_CHECKED);
        }
        return hasLogin;
    }

    /**
     * 单独选
     * @return 封装好的购物车 VO
     */
    @PostMapping("select")
    @ApiOperation(value = "单独选")
    public ServerResponse<CartVO> select(HttpServletRequest request, Integer productId) {
        ServerResponse hasLogin = loginHasExpired(request);

        if (hasLogin.isSuccess()) {
            User user = (User) hasLogin.getData();
            return iCartService.selectOrUnSelect(user.getId(), productId, CartConst.Cart.CHECKED);
        }
        return hasLogin;
    }

    /**
     * 单独反选
     * @return 封装好的购物车 VO
     */
    @PostMapping("un_select")
    @ResponseBody
    @ApiOperation(value = "单独反选")
    public ServerResponse<CartVO> unSelect(HttpServletRequest request, Integer productId) {
        ServerResponse hasLogin = loginHasExpired(request);

        if (hasLogin.isSuccess()) {
            User user = (User) hasLogin.getData();
            return iCartService.selectOrUnSelect(user.getId(), productId, CartConst.Cart.UN_CHECKED);
        }
        return hasLogin;
    }

    /**
     * 查询当前用户购物车里产品的数量
     * @return 返回产品的数量
     */
    @PostMapping("get_cart_product_count")
    @ResponseBody
    @ApiOperation(value = "查询当前用户购物车里产品的数量")
    public ServerResponse<Integer> getCartProductCount(HttpServletRequest request) {
        ServerResponse hasLogin = loginHasExpired(request);

        if (hasLogin.isSuccess()) {
            User user = (User) hasLogin.getData();
            return iCartService.getCartProductCount(user.getId());
        }
        return hasLogin;
    }

    /**
     * 判断用户登录是否过期
     */
    private ServerResponse<User> loginHasExpired(HttpServletRequest request) {
        String key = request.getHeader(UserConst.AUTHORITY);
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        String value = valueOperations.get(key);
        if (StringUtils.isEmpty(value)) {
            return ServerResponse.createByErrorMessage(ResponseCode.NEED_LOGIN.getCode(), ResponseCode.NEED_LOGIN.getDesc());
        }
        User user = JsonUtils.jsonStr2Object(value, User.class);
        if (!key.equals(user.getUsername())) {
            return ServerResponse.createByErrorMessage(ResponseCode.NEED_LOGIN.getCode(), ResponseCode.NEED_LOGIN.getDesc());
        }
        valueOperations.set(key, value, 1, TimeUnit.HOURS);
        return ServerResponse.createBySuccess(user);
    }
}

该类里面有一个私有的方法,是用于用户权限验证的
(2)Service层
该层是业务的主要实现,下面看一下Service的实现类:

package com.aiun.order.service.impl;
/**
 * @author lenovo
 */
@Service("iCartService")
public class CartServiceImpl implements ICartService {
    @Autowired
    private CartMapper cartMapper;
    @Autowired
    private ProductFeign productFeign;
    @Value("mageHost")
    private String mageHost;

    @Override
    public ServerResponse<CartVO> add(Integer userId, Integer productId, Integer count) {
        if (productId == null || count == null) {
            return ServerResponse.createByErrorMessage(ResponseCode.ILLEGAL_ARGUMENT.getCode(), ResponseCode.ILLEGAL_ARGUMENT.getDesc());
        }

        Cart cart = cartMapper.selectCartByUserIdProductId(userId, productId);
        if (cart == null) {
            //产品不在购物车里,需要新增一个这个产品的记录
            Cart cartItem = new Cart();
            cartItem.setQuantity(count)
                    .setChecked(CartConst.Cart.CHECKED)
                    .setProductId(productId)
                    .setUserId(userId);
            int resultCount = cartMapper.insert(cartItem);
            if (resultCount == 0){
                return ServerResponse.createByErrorMessage("添加购物车失败");
            }
        } else {
            //这个产品已经在购物车里了
            //需要将数量相加
            count = cart.getQuantity() + count;
            cart.setQuantity(count);
            cartMapper.updateByPrimaryKeySelective(cart);
        }
        return this.list(userId);
    }

    @Override
    public ServerResponse<CartVO> update(Integer userId, Integer productId, Integer count) {
        if (productId == null || count == null) {
            return ServerResponse.createByErrorMessage(ResponseCode.ILLEGAL_ARGUMENT.getCode(), ResponseCode.ILLEGAL_ARGUMENT.getDesc());
        }
        Cart cart = cartMapper.selectCartByUserIdProductId(userId, productId);
        if (cart != null) {
            cart.setQuantity(count);
        }
        int resoultCount = cartMapper.updateByPrimaryKeySelective(cart);
        if (resoultCount == 0) {
            return ServerResponse.createByErrorMessage("更新购物车失败");
        }
        return this.list(userId);
    }

    @Override
    public ServerResponse<CartVO> deleteProduct(Integer userId, String productIds) {
        List<String> productList = new ArrayList<>();
        String[] productIdStrings = productIds.split(",");
        for(String id : productIdStrings) {
            productList.add(id);
        }
        if (productList.isEmpty()) {
            return ServerResponse.createByErrorMessage(ResponseCode.ILLEGAL_ARGUMENT.getCode(), ResponseCode.ILLEGAL_ARGUMENT.getDesc());
        }
        cartMapper.deleteByUserIdProductIds(userId, productList);
        return this.list(userId);
    }

    @Override
    public ServerResponse<CartVO> list(Integer userId) {
        CartVO cartVo = this.getCartVoLimit(userId);
        return ServerResponse.createBySuccess(cartVo);
    }

    @Override
    public ServerResponse<CartVO> selectOrUnSelect(Integer userId, Integer productId, Integer checked) {
        cartMapper.checkedOrUnCheckedProduct(userId, productId, checked);
        return this.list(userId);
    }

    @Override
    public ServerResponse<Integer> getCartProductCount(Integer userId) {
        if (userId == null) {
            return ServerResponse.createBySuccess(0);
        }

        return ServerResponse.createBySuccess(cartMapper.selectCartProductCount(userId));
    }

    /**
     * 封装前端用到的购物车数据
     * @param userId 用户 id
     * @return 封装的购物车 VO
     */
    private CartVO getCartVoLimit(Integer userId) {
        CartVO cartVo = new CartVO();
        List<Cart> cartList = cartMapper.selectCartByUserId(userId);
        List<CartProductVO> cartProductVOList = Lists.newArrayList();

        BigDecimal cartTotalPrice = new BigDecimal("0");
        if (!cartList.isEmpty()) {
            for (Cart cartItem : cartList) {
                CartProductVO cartProductVo = new CartProductVO();
                cartProductVo.setId(cartItem.getId())
                             .setProductId(cartItem.getProductId())
                             .setUserId(cartItem.getUserId());

                Product product = productFeign.findById(cartItem.getProductId());
                if (product != null) {
                    cartProductVo.setProductMainImage(product.getMainImage())
                                 .setProductName(product.getName())
                                 .setProductPrice(product.getPrice())
                                 .setProductStatus(product.getStatus())
                                 .setProductSubtitle(product.getSubtitle())
                                 .setProductStock(product.getStock());
                    //判断库存
                    int buyLimitCount = 0;
                    if (product.getStock() >= cartItem.getQuantity()) {
                        buyLimitCount = cartItem.getQuantity();
                        cartProductVo.setLimitQuantity(CartConst.LIMIT_NUM_SUCCESS);
                    } else {
                        buyLimitCount = product.getStock();
                        cartProductVo.setLimitQuantity(CartConst.LIMIT_NUM_FAIL);
                        //购物车中更新有效库存
                        Cart cartForQuantity = new Cart();
                        cartForQuantity.setId(cartItem.getId())
                                       .setQuantity(buyLimitCount);
                        cartMapper.updateByPrimaryKeySelective(cartForQuantity);
                    }
                    cartProductVo.setQuantity(buyLimitCount);
                    //计算总价
                    cartProductVo.setProductTotalPrice(BigDecimalUtils.mul(product.getPrice().doubleValue(), cartProductVo.getQuantity()))
                                 .setProductChecked(cartItem.getChecked());
                }
                if (cartItem.getChecked() == CartConst.Cart.CHECKED) {
                    //如果已经勾选,增加到整个购物车的总价中
                    cartTotalPrice = BigDecimalUtils.add(cartTotalPrice.doubleValue(), cartProductVo.getProductTotalPrice().doubleValue());

                }
                cartProductVOList.add(cartProductVo);
            }
        }
        cartVo.setCartTotalPrice(cartTotalPrice)
              .setCartProductVOList(cartProductVOList)
              .setAllChecked(this.getAllCheckedStatus(userId))
              .setImageHost(mageHost);

        return cartVo;
    }

    /**
     * 查询是否是全选中状态
     * @param userId 用户 id
     * @return 产品是否是选中状态
     */
    private boolean getAllCheckedStatus(Integer userId) {
        if (userId == null) {
            return false;
        }
        return cartMapper.selectCartProductCheckedStatusByUserId(userId) == 0;
    }
}

这个类主要是订单的创建,以及查询商品的选中状态,然后计算价格。
计算价格的时候需要注意精度丢失问题,这里选用BigDecimal类,通过字符串的方式构造对象可以防止精度丢失。
(3)Dao层
首先是Mapper接口层

package com.aiun.order.mapper;
@Mapper
@Component
public interface CartMapper {
    /**
     * 通过主键删除购物车
     * @param id 主键
     * @return 影响的记录行
     */
    int deleteByPrimaryKey(Integer id);

    /**
     * 插入购物车信息
     * @param record 购物车信息
     * @return 影响的记录行
     */
    int insert(@Param("record") Cart record);

    /**
     * 有选择的插入购物车信息
     * @param record 购物车信息
     * @return 影响的记录行
     */
    int insertSelective(@Param("record") Cart record);

    /**
     * 通过主键有选择的插入
     * @param id 主键
     * @return 购物车信息
     */
    Cart selectByPrimaryKey(Integer id);

    /**
     * 通过主键有选择的更新购物车数据
     * @param record 购物车
     * @return 影响的记录行
     */
    int updateByPrimaryKeySelective(@Param("record") Cart record);

    /**
     * 通过主键更新购物车数据
     * @param record 购物车信息
     * @return 影响的记录行
     */
    int updateByPrimaryKey(@Param("record") Cart record);

    /**
     * 根据用户Id查询商品选中状态
     * @param userId 用户 id
     * @return 所有购物车信息
     */
    List<Cart> selectCheckedCartByUserId(Integer userId);

    /**
     * 通过用户id和产品id查询购物车
     * @param userId 用户 id
     * @param productId 产品 id
     * @return 购物车信息
     */
    Cart selectCartByUserIdProductId(@Param("userId") Integer userId, @Param("productId") Integer productId);

    /**
     * 通过用户id查询购物车
     * @param userId 用户 id
     * @return 购物车信息
     */
    List<Cart> selectCartByUserId(Integer userId);

    /**
     * 根据用户id和产品id删除
     * @param userId 用户 id
     * @param productIds 需要删除的产品 id
     * @return 影响的记录行
     */
    int deleteByUserIdProductIds(@Param("userId") Integer userId, @Param("productIds") List<String> productIds);

    /**
     * 更新购物车产品的选中状态
     * @param userId 用户 id
     * @param productId 需要选中的产品 id
     * @param checked 选中状态
     * @return 影响的记录行
     */
    int checkedOrUnCheckedProduct(@Param("userId") Integer userId, @Param("productId") Integer productId, @Param("checked") Integer checked);

    /**
     * 获取购物车产品数量
     * @param userId 用户 id
     * @return 影响的记录行
     */
    int selectCartProductCount(@Param("userId") Integer userId);
    /**
     * 根据userId查询产品是否有未被选中的
     * @param userId 用户 id
     * @return 影响的记录行
     */
    int selectCartProductCheckedStatusByUserId(Integer userId);
}

对应的Mapper.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.aiun.order.mapper.CartMapper" >
  <resultMap id="BaseResultMap" type="com.aiun.order.pojo.Cart" >
    <constructor >
      <idArg column="id" jdbcType="INTEGER" javaType="java.lang.Integer" />
      <arg column="user_id" jdbcType="INTEGER" javaType="java.lang.Integer" />
      <arg column="product_id" jdbcType="INTEGER" javaType="java.lang.Integer" />
      <arg column="quantity" jdbcType="INTEGER" javaType="java.lang.Integer" />
      <arg column="checked" jdbcType="INTEGER" javaType="java.lang.Integer" />
      <arg column="create_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />
      <arg column="update_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />
    </constructor>
  </resultMap>
  <sql id="Base_Column_List" >
    id, user_id, product_id, quantity, checked, create_time, update_time
  </sql>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select
    <include refid="Base_Column_List" />
    from trade_cart
    where id = #{id,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
    delete from trade_cart
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="com.aiun.order.pojo.Cart" >
    insert into trade_cart (id, user_id, product_id,
      quantity, checked, create_time,
      update_time)
    values (#{record.id,jdbcType=INTEGER}, #{record.userId,jdbcType=INTEGER}, #{record.productId,jdbcType=INTEGER},
      #{record.quantity,jdbcType=INTEGER}, #{record.checked,jdbcType=INTEGER}, now(),
      now())
  </insert>
  <insert id="insertSelective" parameterType="com.aiun.order.pojo.Cart" >
    insert into trade_cart
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="record.id != null" >
        id,
      </if>
      <if test="record.userId != null" >
        user_id,
      </if>
      <if test="record.productId != null" >
        product_id,
      </if>
      <if test="record.quantity != null" >
        quantity,
      </if>
      <if test="record.checked != null" >
        checked,
      </if>
      <if test="record.createTime != null" >
        create_time,
      </if>
      <if test="record.updateTime != null" >
        update_time,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="record.id != null" >
        #{record.id,jdbcType=INTEGER},
      </if>
      <if test="record.userId != null" >
        #{record.userId,jdbcType=INTEGER},
      </if>
      <if test="record.productId != null" >
        #{record.productId,jdbcType=INTEGER},
      </if>
      <if test="record.quantity != null" >
        #{record.quantity,jdbcType=INTEGER},
      </if>
      <if test="record.checked != null" >
        #{record.checked,jdbcType=INTEGER},
      </if>
      <if test="record.createTime != null" >
        now(),
      </if>
      <if test="record.updateTime != null" >
        now(),
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.aiun.order.pojo.Cart" >
    update trade_cart
    <set >
      <if test="record.userId != null" >
        user_id = #{record.userId,jdbcType=INTEGER},
      </if>
      <if test="record.productId != null" >
        product_id = #{record.productId,jdbcType=INTEGER},
      </if>
      <if test="record.quantity != null" >
        quantity = #{record.quantity,jdbcType=INTEGER},
      </if>
      <if test="record.checked != null" >
        checked = #{record.checked,jdbcType=INTEGER},
      </if>
      <if test="record.createTime != null" >
        create_time = #{record.createTime,jdbcType=TIMESTAMP},
      </if>
      <if test="record.updateTime != null" >
        update_time = now(),
      </if>
    </set>
    where id = #{record.id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.aiun.order.pojo.Cart" >
    update trade_cart
    set user_id = #{record.userId,jdbcType=INTEGER},
      product_id = #{record.productId,jdbcType=INTEGER},
      quantity = #{record.quantity,jdbcType=INTEGER},
      checked = #{record.checked,jdbcType=INTEGER},
      create_time = #{record.createTime,jdbcType=TIMESTAMP},
      update_time = now()
    where id = #{record.id,jdbcType=INTEGER}
  </update>
  <select id="selectCartByUserIdProductId" resultMap="BaseResultMap" parameterType="map">
    select
    <include refid="Base_Column_List"/>
    from trade_cart
    where user_id = #{userId}
    and product_id = #{productId}
  </select>
  <select id="selectCartByUserId" resultMap="BaseResultMap" parameterType="int">
    select
    <include refid="Base_Column_List"/>
    from trade_cart
    where user_id = #{userId}
  </select>
  <select id="selectCartProductCheckedStatusByUserId" resultType="int" parameterType="int">
    select count(1) from trade_cart where checked = 0 and user_id = #{userId}
  </select>
  <delete id="deleteByUserIdProductIds" parameterType="map">
    delete from trade_cart
    where user_id = #{userId}
    <if test="productIds != null">
      and product_id in
      <foreach collection="productIds" item="item" index="index" open="(" separator="," close=")">
        #{item}
      </foreach>
    </if>
  </delete>
  <update id="checkedOrUnCheckedProduct" parameterType="map">
    update trade_cart
    set checked = #{checked},
    update_time = now()
    where user_id = #{userId}
    <if test="productId != null">
      and product_id = #{productId}
    </if>
  </update>
  <select id="selectCartProductCount" resultType="int" parameterType="int">
    select IFNULL(sum(quantity), 0) as count from trade_cart where user_id = #{userId}
  </select>
  <select id="selectCheckedCartByUserId" resultMap="BaseResultMap" parameterType="int">
    SELECT
    <include refid="Base_Column_List"/>
    FROM trade_cart
    WHERE user_id = #{userId}
    AND checked = 1
  </select>
</mapper>

配置文件和Maven依赖看后面介绍。

1.2订单

(1)Controller层
OrderController类如下:

package com.aiun.order.controller;
/**
 * 订单控制层
 * @author lenovo
 */
@Api(tags = "订单系统相关接口")
@RestController
@FeignClient
@RequestMapping("/order/")
public class OrderController {
    @Autowired
    private IOrderService iOrderService;
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    /**
     * 创建订单
     * @param request 请求
     * @param shippingId 收货地址id
     * @return 结果
     */
    @GetMapping("create")
    @ApiOperation(value = "创建订单")
    public ServerResponse create(HttpServletRequest request, Integer shippingId) {
        ServerResponse hasLogin = loginHasExpired(request);
        if (hasLogin.isSuccess()) {
            User user = (User) hasLogin.getData();
            return iOrderService.createOrder(user.getId(), shippingId);
        }
        return hasLogin;
    }

    /**
     * 取消订单
     * @param request 请求
     * @param orderNo 订单号
     * @return 返回结果
     */
    @PostMapping("cancel")
    @ApiOperation(value = "取消订单")
    public ServerResponse cancel(HttpServletRequest request, Long orderNo) {
        ServerResponse hasLogin = loginHasExpired(request);
        if (hasLogin.isSuccess()) {
            User user = (User) hasLogin.getData();
            return iOrderService.cancel(user.getId(), orderNo);
        }
        return hasLogin;
    }
    /**
     * 获取购物车中已经选中的商品信息
     * @param request 请求
     * @return 返回结果
     */
    @PostMapping("get_order_cart_product")
    @ApiOperation(value = "获取购物车中已经选中的商品信息")
    public ServerResponse getOrderCartProduct(HttpServletRequest request) {
        ServerResponse hasLogin = loginHasExpired(request);
        if (hasLogin.isSuccess()) {
            User user = (User) hasLogin.getData();
            return iOrderService.getOrderCartProduct(user.getId());
        }
        return hasLogin;
    }

    /**
     * 获取订单详细信息
     * @param request 请求
     * @param orderNo 订单号
     * @return 订单详情
     */
    @PostMapping("detail")
    @ApiOperation(value = "获取订单详细信息")
    public ServerResponse detail(HttpServletRequest request, Long orderNo) {
        ServerResponse hasLogin = loginHasExpired(request);
        if (hasLogin.isSuccess()) {
            User user = (User) hasLogin.getData();
            if (user.getRole() == UserConst.Role.ROLE_ADMIN) {
                return iOrderService.getOrderDetail(null, orderNo);
            } else {
                return iOrderService.getOrderDetail(user.getId(), orderNo);
            }
        }
        return hasLogin;
    }

    /**
     * 获取订单列表信息
     * @param request 请求
     * @param pageNum 分页的当前页
     * @param pageSize 页的大小
     * @return 返回订单列表信息
     */
    @PostMapping("list")
    @ApiOperation(value = "获取订单列表信息")
    public ServerResponse list(HttpServletRequest request, @RequestParam(value = "pageNum", defaultValue = "1") int pageNum, @RequestParam(value = "pageSize", defaultValue = "10") int pageSize) {
        ServerResponse hasLogin = loginHasExpired(request);
        if (hasLogin.isSuccess()) {
            User user = (User) hasLogin.getData();
            if (user.getRole() == UserConst.Role.ROLE_ADMIN) {
                return iOrderService.getOrderList(null, pageNum, pageSize);
            } else {
                return iOrderService.getOrderList(user.getId(), pageNum, pageSize);
            }
        }
        return hasLogin;
    }
    /**
     * 订单查询
     * @param request 请求
     * @param orderNo 订单号
     * @param pageNum 当前页
     * @param pageSize 页大小
     * @return 返回结果
     */
    @PostMapping("manage/search")
    @ApiOperation(value = "订单查询")
    public ServerResponse<PageInfo> orderSearch(HttpServletRequest request, Long orderNo, @RequestParam(value = "pageNum", defaultValue = "1") int pageNum, @RequestParam(value = "pageSize", defaultValue = "10") int pageSize) {
        ServerResponse hasLogin = loginHasExpired(request);
        if (hasLogin.isSuccess()) {
            User user = (User) hasLogin.getData();
            if (user.getRole() == UserConst.Role.ROLE_ADMIN) {
                return iOrderService.manageSearch(orderNo, pageNum, pageSize);
            } else {
                return ServerResponse.createByErrorMessage("无权限操作,需要管理员权限");
            }
        }
        return hasLogin;
    }

    /**
     * 发货
     * @param request 请求
     * @param orderNo 订单号
     * @return 发货信息
     */
    @PostMapping("send_goods")
    @ApiOperation(value = "发货")
    public ServerResponse<String> orderSendGoods(HttpServletRequest request, Long orderNo) {
        ServerResponse hasLogin = loginHasExpired(request);
        if (hasLogin.isSuccess()) {
            User user = (User) hasLogin.getData();
            if (user.getRole() == UserConst.Role.ROLE_ADMIN) {
                return iOrderService.manageSendGoods(orderNo);
            } else {
                return ServerResponse.createByErrorMessage("无权限操作,需要管理员权限");
            }
        }
        return hasLogin;
    }
    /**
     * 判断用户登录是否过期
     */
    private ServerResponse<User> loginHasExpired(HttpServletRequest request) {
        String key = request.getHeader(UserConst.AUTHORITY);
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        String value = valueOperations.get(key);
        if (StringUtils.isEmpty(value)) {
            return ServerResponse.createByErrorMessage(ResponseCode.NEED_LOGIN.getCode(), ResponseCode.NEED_LOGIN.getDesc());
        }
        User user = JsonUtils.jsonStr2Object(value, User.class);
        if (!key.equals(user.getUsername())) {
            return ServerResponse.createByErrorMessage(ResponseCode.NEED_LOGIN.getCode(), ResponseCode.NEED_LOGIN.getDesc());
        }
        valueOperations.set(key, value, 1, TimeUnit.HOURS);
        return ServerResponse.createBySuccess(user);
    }
}

Controller层没什么业务逻辑,比较简单。
(2)Service层
看一下Service层的实现类:

package com.aiun.order.service.impl;
/**
 * 订单服务层实现类
 * @author lenovo
 */
@Service("iOrderService")
public class OrderServiceImpl implements IOrderService {
    @Value("${image.localhost}")
    private String mageHost;
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private OrderItemMapper orderItemMapper;
    @Autowired
    private CartMapper cartMapper;
    @Autowired
    private ProductFeign productFeign;
    @Autowired
    private ShippingFeign shippingFeign;

    @Override
    public ServerResponse<OrderVO> createOrder(Integer userId, Integer shippingId) {
        //从购物车中获取数据
        List<Cart> cartList = cartMapper.selectCheckedCartByUserId(userId);

        //计算订单的总价
        ServerResponse serverResponse = getCartOrderItem(userId, cartList);
        if (!serverResponse.isSuccess()) {
            return serverResponse;
        }
        List<OrderItem> orderItemList = (List<OrderItem>) serverResponse.getData();
        if (CollectionUtils.isEmpty(orderItemList)) {
            return ServerResponse.createByErrorMessage("购物车为空");
        }
        //计算订单总价
        BigDecimal payment = getOrderTotalPrice(orderItemList);
        //生成订单
        Order order = assembleOrder(userId, shippingId, payment);
        if (order == null) {
            return ServerResponse.createByErrorMessage("生成订单错误");
        }

        orderItemList.forEach(e->e.setOrderNo(order.getOrderNo()));

        //mybatis批量插入
        orderItemMapper.batchInsert(orderItemList);
        //生成成功,减少产品的库存
        reduceProductStock(orderItemList);
        //清空购物车
        cleanCart(cartList);

        //返回数据给前端
        OrderVO orderVo = assembleOrderVo(order, orderItemList);

        return ServerResponse.createBySuccess(orderVo);
    }

    @Override
    public ServerResponse<String> cancel(Integer userId, Long orderNo) {
        Order order = orderMapper.selectByUserIdAndOrderNo(userId, orderNo);
        if (order == null) {
            return ServerResponse.createByErrorMessage("该用户没有此订单");
        }
        if (order.getStatus() != OrderStatusEnum.NO_PAY.getCode()) {
            return ServerResponse.createByErrorMessage("订单已付款,无法取消!");
        }

        Order updateOrder = new Order();
        updateOrder.setId(order.getId())
                   .setStatus(OrderStatusEnum.CANCELED.getCode());

        int rowCount = orderMapper.updateByPrimaryKeySelective(updateOrder);
        if (rowCount > 0) {
            return ServerResponse.createBySuccess();
        }
        return ServerResponse.createByError();
    }

    @Override
    public ServerResponse getOrderCartProduct(Integer userId) {
        OrderProductVO orderProductVo = new OrderProductVO();
        //从购物车中获取数据
        List<Cart> cartList = cartMapper.selectCheckedCartByUserId(userId);
        ServerResponse serverResponse = getCartOrderItem(userId, cartList);
        if (!serverResponse.isSuccess()) {
            return serverResponse;
        }
        List<OrderItem> orderItemList = (List<OrderItem>) serverResponse.getData();
        List<OrderItemVO> orderItemVOList = Lists.newArrayList();
        BigDecimal payment = new BigDecimal("0");

        for (OrderItem orderItem : orderItemList) {
            payment = BigDecimalUtils.add(payment.doubleValue(), orderItem.getTotalPrice().doubleValue());
            orderItemVOList.add(assembleOrderItemVo(orderItem));
        }

        orderProductVo.setProductTotalPrice(payment)
                      .setOrderItemVOList(orderItemVOList)
                      .setImageHost(mageHost);

        return ServerResponse.createBySuccess(orderProductVo);
    }

    @Override
    public ServerResponse<OrderVO> getOrderDetail(Integer userId, Long orderNo) {
        Order order;
        // userId == null 表示管理员
        if (userId == null) {
            order = orderMapper.selectByOrderNo(orderNo);
        } else {
            order = orderMapper.selectByUserIdAndOrderNo(userId, orderNo);
        }
        List<OrderItem> orderItemList;
        if (order != null) {
            orderItemList = (userId == null ? orderItemMapper.getByOrderNo(orderNo) : orderItemMapper.getByOrderNoAndUserId(orderNo, userId));
            OrderVO orderVo = assembleOrderVo(order, orderItemList);
            return ServerResponse.createBySuccess(orderVo);
        }

        return ServerResponse.createByErrorMessage("没有找到该订单");
    }

    @Override
    public ServerResponse<PageInfo> getOrderList(Integer userId, int pageNum, int pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<OrderVO> orderVOList;
        List<Order> orderList;
        // 管理员
        if (userId == null) {
            orderList = orderMapper.selectAllOrder();
            orderVOList = assembleOrderVoList(orderList, userId);
        } else { // 普通用户
            orderList = orderMapper.selectByUserId(userId);
            orderVOList = assembleOrderVoList(orderList, userId);
        }
        PageInfo pageInfo = new PageInfo(orderList);
        pageInfo.setList(orderVOList);

        return ServerResponse.createBySuccess(pageInfo);
    }

    @Override
    public ServerResponse<String> manageSendGoods(Long orderNo) {
        Order order = orderMapper.selectByOrderNo(orderNo);
        if (order != null) {
            if (order.getStatus() == OrderStatusEnum.PAID.getCode()) {
                order.setStatus(OrderStatusEnum.SHIPPED.getCode())
                     .setSendTime(new Date());
                orderMapper.updateByPrimaryKeySelective(order);
                return ServerResponse.createBySuccessMessage("发货成功");
            }
        }
        return ServerResponse.createByErrorMessage("订单不存在");
    }

    @Override
    public ServerResponse<PageInfo> manageSearch(Long orderNo, int pageNum, int pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        Order order = orderMapper.selectByOrderNo(orderNo);
        if (order != null) {
            List<OrderItem> orderItemList = orderItemMapper.getByOrderNo(orderNo);
            OrderVO orderVo = assembleOrderVo(order, orderItemList);

            PageInfo pageInfo = new PageInfo(Lists.newArrayList(order));
            pageInfo.setList(Lists.newArrayList(orderVo));
            return ServerResponse.createBySuccess(pageInfo);
        }

        return ServerResponse.createByErrorMessage("订单不存在");
    }

    /**
     * 购物车内数据的封装
     * @param userId 用户id
     * @param cartList 该用户的购物车列表
     * @return 操作结果
     */
    private ServerResponse getCartOrderItem(Integer userId, List<Cart> cartList) {
        List<OrderItem> orderItemList = Lists.newArrayList();
        if (CollectionUtils.isEmpty(cartList)) {
            ServerResponse.createByErrorMessage("购物车为空");
        }
        //校验购物车的数据,包括产品的状态和数量
        for(Cart cartItem : cartList) {
            OrderItem orderItem = new OrderItem();
            Product product = productFeign.findById(cartItem.getProductId());
            if (ProductStatusEnum.ON_SALE.getCode() != product.getStatus()) {
                return ServerResponse.createByErrorMessage("产品" + product.getName() + "不是在线售卖状态");
            }

            //校验库存
            if (cartItem.getQuantity() > product.getStock()) {
                return ServerResponse.createByErrorMessage("产品" + product.getName() + "库存数量不足");
            }

            orderItem.setUserId(userId)
                     .setProductId(product.getId())
                     .setProductImage(product.getMainImage())
                     .setProductName(product.getName())
                     .setCurrentUnitPrice(product.getPrice())
                     .setQuantity(cartItem.getQuantity())
                     .setTotalPrice(BigDecimalUtils.mul(product.getPrice().doubleValue(), cartItem.getQuantity().doubleValue()));
            orderItemList.add(orderItem);
        }
        //TODO 用 Lambda 表达式

        return ServerResponse.createBySuccess(orderItemList);
    }

    /**
     * 计算购物车内购买商品总价
     * @param orderItemList 用户所有购买的商品
     * @return 支付金额
     */
    private BigDecimal getOrderTotalPrice(List<OrderItem> orderItemList) {
        BigDecimal payment = new BigDecimal("0");
        for(OrderItem orderItem : orderItemList) {
            payment = BigDecimalUtils.add(payment.doubleValue(), orderItem.getTotalPrice().doubleValue());
        }
        return payment;
    }

    /**
     * 生成订单
     * @param userId 用户id
     * @param shippingId 地址id
     * @param payment 支付金额
     * @return 订单
     */
    private Order assembleOrder(Integer userId, Integer shippingId, BigDecimal payment) {
        Order order = new Order();
        long orderNo = generateOrderNo();
        order.setOrderNo(orderNo)
             .setStatus(OrderStatusEnum.NO_PAY.getCode())
             .setPostage(0)
             .setPaymentType(PaymentTypeEnum.ONLINE_PAY.getCode())
             .setPayment(payment)
             .setUserId(userId)
             .setShippingId(shippingId);
        //发货时间,付款时间,后面会填充
        int rowCount = orderMapper.insert(order);
        if (rowCount > 0) {
            return order;
        }
        return null;
    }

    /**
     * 生成订单号
     * @return 订单号
     */
    private long generateOrderNo() {
        long currentTime = System.currentTimeMillis();
        //并发量高的会造成订单重复
        return currentTime + new Random().nextInt(100);
    }

    /**
     * 减少商品库存
     * @param orderItemList 订单详情列表
     */
    private void reduceProductStock(List<OrderItem> orderItemList) {
        orderItemList.forEach(e->{
            Product product = productFeign.findById(e.getProductId());
            productFeign.updateStockById(e.getProductId(), product.getStock() - e.getQuantity());
        });
    }

    /**
     * 清空购物车
     * @param cartList 购物车列表
     */
    private void cleanCart(List<Cart> cartList) {
        cartList.forEach(e->{
            cartMapper.deleteByPrimaryKey(e.getId());
        });
    }

    /**
     * 封装订单数据
     * @param order 订单
     * @param orderItemList 订单详情列表
     * @return 封装的订单对象
     */
    private OrderVO assembleOrderVo(Order order, List<OrderItem> orderItemList) {
        OrderVO orderVo = new OrderVO();
        orderVo.setOrderNo(order.getOrderNo())
               .setPayment(order.getPayment())
               .setPaymentType(order.getPaymentType())
               .setPaymentTypeDesc(PaymentTypeEnum.codeOf(order.getPaymentType()).getValue())
               .setPostage(order.getPostage())
               .setStatus(order.getStatus())
               .setStatusDesc(OrderStatusEnum.codeOf(order.getStatus()).getValue())
               .setShippingId(order.getShippingId());
        Shipping shipping = shippingFeign.findById(order.getShippingId());
        if (shipping != null) {
            orderVo.setReceiverName(shipping.getReceiverName())
                   .setShippingVo(assembleShippingVo(shipping));
        }
        orderVo.setPaymentTime(DateUtils.dateToString(order.getPaymentTime()))
               .setSendTime(DateUtils.dateToString(order.getSendTime()))
               .setCreateTime(DateUtils.dateToString(order.getCreateTime()))
               .setCloseTime(DateUtils.dateToString(order.getCloseTime()))
               .setEndTime(DateUtils.dateToString(order.getEndTime()))
               .setImageHost(mageHost);

        List<OrderItemVO> orderItemVOList = Lists.newArrayList();

        orderItemList.forEach(e->{
            OrderItemVO orderItemVo = assembleOrderItemVo(e);
            orderItemVOList.add(orderItemVo);
        });

        orderVo.setOrderItemVOList(orderItemVOList);
        return orderVo;
    }

    /**
     * 给前端返回的地址数据
     * @param shipping 地址实体类
     * @return 地址信息封装 VO
     */
    private ShippingVo assembleShippingVo(Shipping shipping) {
        ShippingVo shippingVo = new ShippingVo();
        shippingVo.setReceiverAddress(shipping.getReceiverAddress())
                  .setReceiverCity(shipping.getReceiverCity())
                  .setReceiverDistrict(shipping.getReceiverDistrict())
                  .setReceiverMobile(shipping.getReceiverMobile())
                  .setReceiverName(shipping.getReceiverName())
                  .setReceiverProvince(shipping.getReceiverProvince())
                  .setReceiverPhone(shipping.getReceiverPhone())
                  .setReceiverZip(shipping.getReceiverZip());

        return shippingVo;
    }

    /**
     * 封装订单详情前端数据
     * @param orderItem 订单详情
     * @return 订单详情信息封装 VO
     */
    private OrderItemVO assembleOrderItemVo(OrderItem orderItem) {
        OrderItemVO orderItemVo = new OrderItemVO();
        orderItemVo.setOrderNo(orderItem.getOrderNo())
                   .setProductId(orderItem.getProductId())
                   .setProductName(orderItem.getProductName())
                   .setProductImage(orderItem.getProductImage())
                   .setTotalPrice(orderItem.getTotalPrice())
                   .setCurrentUnitPrice(orderItem.getCurrentUnitPrice())
                   .setQuantity(orderItem.getQuantity())
                   .setCreateTime(DateUtils.dateToString(orderItem.getCreateTime()));
        return orderItemVo;
    }

    /**
     * 封装订单 vo 信息
     * @param orderList 订单列表
     * @param userId 用户 id
     * @return 订单展示列表
     */
    private List<OrderVO> assembleOrderVoList(List<Order> orderList, Integer userId) {
        List<OrderVO> orderVOList = Lists.newArrayList();
        for (Order order : orderList) {
            List<OrderItem> orderItemList = Lists.newArrayList();
            if (userId == null) {
                //管理员不需要userId
                orderItemList = orderItemMapper.getByOrderNo(order.getOrderNo());
            } else {
                orderItemList = orderItemMapper.getByOrderNoAndUserId(order.getOrderNo(), userId);
            }
            OrderVO orderVo = assembleOrderVo(order, orderItemList);
            orderVOList.add(orderVo);
        }
        return orderVOList;
    }
}

(3)Dao层
Mapper层有两个接口OrderMapper和OrderItemMapper

package com.aiun.order.mapper;
/**
 * order模块映射接口
 * @author lenovo
 */
@Mapper
@Component
public interface OrderMapper {
    /**
     * 通过主键删除订单
     * @param id 主键
     * @return 影响行数
     */
    int deleteByPrimaryKey(Integer id);

    /**
     * 创建订单
     * @param record 订单信息
     * @return 影响行数
     */
    int insert(@Param("record") Order record);

    /**
     * 有选择的创建订单
     * @param record 订单信息
     * @return 影响行数
     */
    int insertSelective(@Param("record") Order record);

    /**
     * 有选择的通过主键创建订单
     * @param id 主键
     * @return 订单
     */
    Order selectByPrimaryKey(Integer id);

    /**
     * 有选择的通过主键更新订单
     * @param record 订单信息
     * @return 影响行数
     */
    int updateByPrimaryKeySelective(@Param("record") Order record);

    /**
     * 修改订单
     * @param record 订单信息
     * @return 影响行数
     */
    int updateByPrimaryKey(@Param("record") Order record);

    /**
     * 通过用户id和订单和查询订单
     * @param userId 用户id
     * @param orderNo 订单号
     * @return 订单
     */
    Order selectByUserIdAndOrderNo(@Param("userId") Integer userId, @Param("orderNo") Long orderNo);

    /**
     * 通过订单号查询订单
     * @param orderNo 订单号
     * @return 订单
     */
    Order selectByOrderNo(Long orderNo);

    /**
     * 通过用户 id 查询订单
     * @param userId 用户 id
     * @return 返回用户所有的订单
     */
    List<Order> selectByUserId(Integer userId);

    /**
     * 查询所有的订单
     * @return 返回订单列表
     */
    List<Order> selectAllOrder();
}
package com.aiun.order.mapper;
/**
 * 订单详情映射接口
 * @author lenovo
 */
@Mapper
@Component
public interface OrderItemMapper {
    /**
     * 通过主键删除订单详情
     * @param id 主键
     * @return 影响行数
     */
    int deleteByPrimaryKey(Integer id);

    /**
     * 插入订单详情信息
     * @param record 订单详情信息
     * @return 影响行数
     */
    int insert(@Param("record") OrderItem record);

    /**
     *
     * @param record 订单详情信息
     * @return 影响行数
     */
    int insertSelective(@Param("record") OrderItem record);

    /**
     * 通过主键查询订单详情
     * @param id 主键
     * @return 订单详情信息
     */
    OrderItem selectByPrimaryKey(Integer id);

    /**
     * 通过主键有选择的更新订单详情信息
     * @param record 订单详情信息
     * @return 影响行数
     */
    int updateByPrimaryKeySelective(@Param("record") OrderItem record);

    /**
     * 通过主键更新订单详情
     * @param record 订单详情信息
     * @return 影响行数
     */
    int updateByPrimaryKey(@Param("record") OrderItem record);

    /**
     * 通过订单号获取订单详情
     * @param orderNo 订单号
     * @return 订单详情信息列表
     */
    List<OrderItem> getByOrderNo(@Param("orderNo") Long orderNo);

    /**
     * 通过订单号和用户 id 获取订单详情
     * @param orderNo 订单号
     * @param userId 用户 id
     * @return 订单详情信息列表
     */
    List<OrderItem> getByOrderNoAndUserId(@Param("orderNo") Long orderNo, @Param("userId") Integer userId);

    /**
     * 订单详情批量插入
     * @param orderItemList 订单信息列表
     */
    void batchInsert(@Param("orderItemList") List<OrderItem> orderItemList);
}

订单我创建了订单表和订单详情表,所以需要两个Mapper,每个Mapper操作一个表。
下面看一下OrderMapper.xml和OrderItemMapper.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.aiun.order.mapper.OrderMapper" >
  <resultMap id="BaseResultMap" type="com.aiun.order.pojo.Order" >
    <constructor >
      <idArg column="id" jdbcType="INTEGER" javaType="java.lang.Integer" />
      <arg column="order_no" jdbcType="BIGINT" javaType="java.lang.Long" />
      <arg column="user_id" jdbcType="INTEGER" javaType="java.lang.Integer" />
      <arg column="shipping_id" jdbcType="INTEGER" javaType="java.lang.Integer" />
      <arg column="payment" jdbcType="DECIMAL" javaType="java.math.BigDecimal" />
      <arg column="payment_type" jdbcType="INTEGER" javaType="java.lang.Integer" />
      <arg column="postage" jdbcType="INTEGER" javaType="java.lang.Integer" />
      <arg column="status" jdbcType="INTEGER" javaType="java.lang.Integer" />
      <arg column="payment_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />
      <arg column="send_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />
      <arg column="end_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />
      <arg column="close_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />
      <arg column="create_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />
      <arg column="update_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />
    </constructor>
  </resultMap>
  <sql id="Base_Column_List" >
    id, order_no, user_id, shipping_id, payment, payment_type, postage, status, payment_time,
    send_time, end_time, close_time, create_time, update_time
  </sql>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select
    <include refid="Base_Column_List" />
    from trade_order
    where id = #{id,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
    delete from trade_order
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="com.aiun.order.pojo.Order" >
    insert into trade_order (id, order_no, user_id,
      shipping_id, payment, payment_type,
      postage, status, payment_time,
      send_time, end_time, close_time,
      create_time, update_time)
    values (#{record.id,jdbcType=INTEGER}, #{record.orderNo,jdbcType=BIGINT}, #{record.userId,jdbcType=INTEGER},
      #{record.shippingId,jdbcType=INTEGER}, #{record.payment,jdbcType=DECIMAL}, #{record.paymentType,jdbcType=INTEGER},
      #{record.postage,jdbcType=INTEGER}, #{record.status,jdbcType=INTEGER}, #{record.paymentTime,jdbcType=TIMESTAMP},
      #{record.sendTime,jdbcType=TIMESTAMP}, #{record.endTime,jdbcType=TIMESTAMP}, #{record.closeTime,jdbcType=TIMESTAMP},
      now(), now())
  </insert>
  <insert id="insertSelective" parameterType="com.aiun.order.pojo.Order" >
    insert into trade_order
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="record.id != null" >
        id,
      </if>
      <if test="record.orderNo != null" >
        order_no,
      </if>
      <if test="record.userId != null" >
        user_id,
      </if>
      <if test="record.shippingId != null" >
        shipping_id,
      </if>
      <if test="record.payment != null" >
        payment,
      </if>
      <if test="record.paymentType != null" >
        payment_type,
      </if>
      <if test="record.postage != null" >
        postage,
      </if>
      <if test="record.status != null" >
        status,
      </if>
      <if test="record.paymentTime != null" >
        payment_time,
      </if>
      <if test="record.sendTime != null" >
        send_time,
      </if>
      <if test="record.endTime != null" >
        end_time,
      </if>
      <if test="record.closeTime != null" >
        close_time,
      </if>
      <if test="record.createTime != null" >
        create_time,
      </if>
      <if test="record.updateTime != null" >
        update_time,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="record.id != null" >
        #{record.id,jdbcType=INTEGER},
      </if>
      <if test="record.orderNo != null" >
        #{record.orderNo,jdbcType=BIGINT},
      </if>
      <if test="record.userId != null" >
        #{record.userId,jdbcType=INTEGER},
      </if>
      <if test="record.shippingId != null" >
        #{record.shippingId,jdbcType=INTEGER},
      </if>
      <if test="record.payment != null" >
        #{record.payment,jdbcType=DECIMAL},
      </if>
      <if test="record.paymentType != null" >
        #{record.paymentType,jdbcType=INTEGER},
      </if>
      <if test="record.postage != null" >
        #{record.postage,jdbcType=INTEGER},
      </if>
      <if test="record.status != null" >
        #{record.status,jdbcType=INTEGER},
      </if>
      <if test="record.paymentTime != null" >
        #{record.paymentTime,jdbcType=TIMESTAMP},
      </if>
      <if test="record.sendTime != null" >
        #{record.sendTime,jdbcType=TIMESTAMP},
      </if>
      <if test="record.endTime != null" >
        #{record.endTime,jdbcType=TIMESTAMP},
      </if>
      <if test="record.closeTime != null" >
        #{record.closeTime,jdbcType=TIMESTAMP},
      </if>
      <if test="record.createTime != null" >
        #{record.createTime,jdbcType=TIMESTAMP},
      </if>
      <if test="record.updateTime != null" >
        now(),
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.aiun.order.pojo.Order" >
    update trade_order
    <set >
      <if test="record.orderNo != null" >
        order_no = #{record.orderNo,jdbcType=BIGINT},
      </if>
      <if test="record.userId != null" >
        user_id = #{record.userId,jdbcType=INTEGER},
      </if>
      <if test="record.shippingId != null" >
        shipping_id = #{record.shippingId,jdbcType=INTEGER},
      </if>
      <if test="record.payment != null" >
        payment = #{record.payment,jdbcType=DECIMAL},
      </if>
      <if test="record.paymentType != null" >
        payment_type = #{record.paymentType,jdbcType=INTEGER},
      </if>
      <if test="record.postage != null" >
        postage = #{record.postage,jdbcType=INTEGER},
      </if>
      <if test="record.status != null" >
        status = #{record.status,jdbcType=INTEGER},
      </if>
      <if test="record.paymentTime != null" >
        payment_time = #{record.paymentTime,jdbcType=TIMESTAMP},
      </if>
      <if test="record.sendTime != null" >
        send_time = #{record.sendTime,jdbcType=TIMESTAMP},
      </if>
      <if test="record.endTime != null" >
        end_time = #{record.endTime,jdbcType=TIMESTAMP},
      </if>
      <if test="record.closeTime != null" >
        close_time = #{record.closeTime,jdbcType=TIMESTAMP},
      </if>
      <if test="record.createTime != null" >
        create_time = #{record.createTime,jdbcType=TIMESTAMP},
      </if>
      <if test="record.updateTime != null" >
        update_time = now(),
      </if>
    </set>
    where id = #{record.id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.aiun.order.pojo.Order" >
    update trade_order
    set order_no = #{record.orderNo,jdbcType=BIGINT},
      user_id = #{record.userId,jdbcType=INTEGER},
      shipping_id = #{record.shippingId,jdbcType=INTEGER},
      payment = #{record.payment,jdbcType=DECIMAL},
      payment_type = #{record.paymentType,jdbcType=INTEGER},
      postage = #{record.postage,jdbcType=INTEGER},
      status = #{record.status,jdbcType=INTEGER},
      payment_time = #{record.paymentTime,jdbcType=TIMESTAMP},
      send_time = #{record.sendTime,jdbcType=TIMESTAMP},
      end_time = #{record.endTime,jdbcType=TIMESTAMP},
      close_time = #{record.closeTime,jdbcType=TIMESTAMP},
      create_time = #{record.createTime,jdbcType=TIMESTAMP},
      update_time = now()
    where id = #{record.id,jdbcType=INTEGER}
  </update>
  <select id="selectByUserIdAndOrderNo" resultMap="BaseResultMap" parameterType="map">
    select
    <include refid="Base_Column_List"/>
    from trade_order
    where order_no = #{orderNo}
    and user_id = #{userId}
  </select>
  <select id="selectByOrderNo" resultMap="BaseResultMap" parameterType="long">
    select
    <include refid="Base_Column_List"/>
    from trade_order
    where order_no = #{orderNo}
  </select>
  <select id="selectByUserId" resultMap="BaseResultMap" parameterType="int">
    SELECT
    <include refid="Base_Column_List"/>
    FROM trade_order
    WHERE user_id = #{userId}
    ORDER BY create_time DESC
  </select>
  <select id="selectAllOrder" resultMap="BaseResultMap">
    SELECT
    <include refid="Base_Column_List"/>
    FROM trade_order
    ORDER BY create_time DESC
  </select>
</mapper>
<?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.aiun.order.mapper.OrderItemMapper" >
  <resultMap id="BaseResultMap" type="com.aiun.order.pojo.OrderItem" >
    <constructor >
      <idArg column="id" jdbcType="INTEGER" javaType="java.lang.Integer" />
      <arg column="user_id" jdbcType="INTEGER" javaType="java.lang.Integer" />
      <arg column="order_no" jdbcType="BIGINT" javaType="java.lang.Long" />
      <arg column="product_id" jdbcType="INTEGER" javaType="java.lang.Integer" />
      <arg column="product_name" jdbcType="VARCHAR" javaType="java.lang.String" />
      <arg column="product_image" jdbcType="VARCHAR" javaType="java.lang.String" />
      <arg column="current_unit_price" jdbcType="DECIMAL" javaType="java.math.BigDecimal" />
      <arg column="quantity" jdbcType="INTEGER" javaType="java.lang.Integer" />
      <arg column="total_price" jdbcType="DECIMAL" javaType="java.math.BigDecimal" />
      <arg column="create_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />
      <arg column="update_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />
    </constructor>
  </resultMap>
  <sql id="Base_Column_List" >
    id, user_id, order_no, product_id, product_name, product_image, current_unit_price,
    quantity, total_price, create_time, update_time
  </sql>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select
    <include refid="Base_Column_List" />
    from trade_order_item
    where id = #{id,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
    delete from trade_order_item
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="com.aiun.order.pojo.OrderItem" >
    insert into trade_order_item (id, user_id, order_no,
      product_id, product_name, product_image,
      current_unit_price, quantity, total_price,
      create_time, update_time)
    values (#{record.id,jdbcType=INTEGER}, #{record.userId,jdbcType=INTEGER}, #{record.orderNo,jdbcType=BIGINT},
      #{record.productId,jdbcType=INTEGER}, #{record.productName,jdbcType=VARCHAR}, #{record.productImage,jdbcType=VARCHAR},
      #{record.currentUnitPrice,jdbcType=DECIMAL}, #{record.quantity,jdbcType=INTEGER}, #{record.totalPrice,jdbcType=DECIMAL},
      now(), now())
  </insert>
  <insert id="insertSelective" parameterType="com.aiun.order.pojo.OrderItem" >
    insert into trade_order_item
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="record.id != null" >
        id,
      </if>
      <if test="record.userId != null" >
        user_id,
      </if>
      <if test="record.orderNo != null" >
        order_no,
      </if>
      <if test="record.productId != null" >
        product_id,
      </if>
      <if test="record.productName != null" >
        product_name,
      </if>
      <if test="record.productImage != null" >
        product_image,
      </if>
      <if test="record.currentUnitPrice != null" >
        current_unit_price,
      </if>
      <if test="record.quantity != null" >
        quantity,
      </if>
      <if test="record.totalPrice != null" >
        total_price,
      </if>
      <if test="record.createTime != null" >
        create_time,
      </if>
      <if test="record.updateTime != null" >
        update_time,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="record.id != null" >
        #{record.id,jdbcType=INTEGER},
      </if>
      <if test="record.userId != null" >
        #{record.userId,jdbcType=INTEGER},
      </if>
      <if test="record.orderNo != null" >
        #{record.orderNo,jdbcType=BIGINT},
      </if>
      <if test="record.productId != null" >
        #{record.productId,jdbcType=INTEGER},
      </if>
      <if test="record.productName != null" >
        #{record.productName,jdbcType=VARCHAR},
      </if>
      <if test="record.productImage != null" >
        #{record.productImage,jdbcType=VARCHAR},
      </if>
      <if test="record.currentUnitPrice != null" >
        #{record.currentUnitPrice,jdbcType=DECIMAL},
      </if>
      <if test="record.quantity != null" >
        #{record.quantity,jdbcType=INTEGER},
      </if>
      <if test="record.totalPrice != null" >
        #{record.totalPrice,jdbcType=DECIMAL},
      </if>
      <if test="record.createTime != null" >
        #{record.createTime,jdbcType=TIMESTAMP},
      </if>
      <if test="record.updateTime != null" >
        now(),
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.aiun.order.pojo.OrderItem" >
    update trade_order_item
    <set >
      <if test="record.userId != null" >
        user_id = #{record.userId,jdbcType=INTEGER},
      </if>
      <if test="record.orderNo != null" >
        order_no = #{record.orderNo,jdbcType=BIGINT},
      </if>
      <if test="record.productId != null" >
        product_id = #{record.productId,jdbcType=INTEGER},
      </if>
      <if test="record.productName != null" >
        product_name = #{record.productName,jdbcType=VARCHAR},
      </if>
      <if test="record.productImage != null" >
        product_image = #{record.productImage,jdbcType=VARCHAR},
      </if>
      <if test="record.currentUnitPrice != null" >
        current_unit_price = #{record.currentUnitPrice,jdbcType=DECIMAL},
      </if>
      <if test="record.quantity != null" >
        quantity = #{record.quantity,jdbcType=INTEGER},
      </if>
      <if test="record.totalPrice != null" >
        total_price = #{record.totalPrice,jdbcType=DECIMAL},
      </if>
      <if test="record.createTime != null" >
        create_time = #{record.createTime,jdbcType=TIMESTAMP},
      </if>
      <if test="record.updateTime != null" >
        update_time = now(),
      </if>
    </set>
    where id = #{record.id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.aiun.order.pojo.OrderItem" >
    update trade_order_item
    set user_id = #{record.userId,jdbcType=INTEGER},
      order_no = #{record.orderNo,jdbcType=BIGINT},
      product_id = #{record.productId,jdbcType=INTEGER},
      product_name = #{record.productName,jdbcType=VARCHAR},
      product_image = #{record.productImage,jdbcType=VARCHAR},
      current_unit_price = #{record.currentUnitPrice,jdbcType=DECIMAL},
      quantity = #{record.quantity,jdbcType=INTEGER},
      total_price = #{record.totalPrice,jdbcType=DECIMAL},
      create_time = #{record.createTime,jdbcType=TIMESTAMP},
      update_time = now()
    where id = #{record.id,jdbcType=INTEGER}
  </update>
  <select id="getByOrderNo" resultMap="BaseResultMap" parameterType="long">
    SELECT
    <include refid="Base_Column_List"/>
    FROM trade_order_item
    WHERE order_no = #{orderNo}
  </select>
  <select id="getByOrderNoAndUserId" resultMap="BaseResultMap" parameterType="map">
    select
    <include refid="Base_Column_List"/>
    from trade_order_item
    where order_no = #{orderNo}
    and user_id = #{userId}
  </select>
  <insert id="batchInsert" parameterType="list">
    insert into trade_order_item (id, order_no, user_id,
    product_id, product_name, product_image,
    current_unit_price, quantity, total_price,
    create_time, update_time)
    values
    <foreach collection="orderItemList" index="index" item="item" separator=",">
      (
      #{item.id},#{item.orderNo},#{item.userId},#{item.productId},#{item.productName},#{item.productImage},#{item.currentUnitPrice},#{item.quantity},#{item.totalPrice},now(),now()
      )
    </foreach>
  </insert>
</mapper>

(4)数据库建表语句

CREATE TABLE `trade_order` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '订单id',
  `order_no` bigint(20) DEFAULT NULL COMMENT '订单号',
  `user_id` int(11) DEFAULT NULL COMMENT '用户id',
  `shipping_id` int(11) DEFAULT NULL COMMENT '订单的地址',
  `payment` decimal(20,2) DEFAULT NULL COMMENT '实际付款金额,单位是元,保留两位小数',
  `payment_type` int(4) DEFAULT NULL COMMENT '支付类型,1-在线支付',
  `postage` int(10) DEFAULT NULL COMMENT '运费,单位是元',
  `status` int(10) DEFAULT NULL COMMENT '订单状态:0-已取消-10-未付款,20-已付款,40-已发货,50-交易成功,60-交易关闭',
  `payment_time` datetime DEFAULT NULL COMMENT '支付时间',
  `send_time` datetime DEFAULT NULL COMMENT '发货时间',
  `end_time` datetime DEFAULT NULL COMMENT '交易完成时间',
  `close_time` datetime DEFAULT NULL COMMENT '交易关闭时间',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `order_no_index` (`order_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `trade_order_item` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '订单子表id',
  `user_id` int(11) DEFAULT NULL,
  `order_no` bigint(20) DEFAULT NULL COMMENT '订单号',
  `product_id` int(11) DEFAULT NULL COMMENT '商品id',
  `product_name` varchar(100) DEFAULT NULL COMMENT '商品名称',
  `product_image` varchar(500) DEFAULT NULL COMMENT '商品图片地址',
  `current_unit_price` decimal(20,2) DEFAULT NULL COMMENT '生成订单时的商品单价,单位是元,保留两位小数',
  `quantity` int(10) DEFAULT NULL COMMENT '商品数量',
  `total_price` decimal(20,2) DEFAULT NULL COMMENT '商品总价,单位是元,保留两位小数',
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `order_no_index` (`order_no`) USING BTREE,
  KEY `order_no_user_id_index` (`user_id`,`order_no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

订单表和订单详情表

2.配置文件

看一下application.yml文件

# Spring
spring:
 # 服务应用名称
 application:
  name: backend-order
  # 数据源
 datasource:
  driver-class-name: com.mysql.cj.jdbc.Driver
  url: jdbc:mysql://localhost:3306/trade?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
  username: root
  password: root
  # 指定druid 连接池以及druid 连接池配置
  type: com.alibaba.druid.pool.DruidDataSource
  druid:
   initial-size: 1          # 初始连接数
   max-active: 20           # 最大连接数
   max-idle: 20             # 最大空闲
   min-idle: 1              # 最小空闲
   max-wait: 60000          # 最长等待时间

server:
 port: 8082                  # 项目访问端口
 servlet:
  context-path: /backend-order   # 项目访问路径

# 产品图片存放地址
image:
  localhost: F:/Work/imgHost/

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
  instance:
    hostname: localhost

# MyBatis
mybatis:
 # 配置 MyBatis 数据返回类型别名(默认别名是类名)
 type-aliases-package: com.aiun.order.pojo
 # 配置 MyBatis Mapper 映射文件
 mapper-locations: classpath:/mappers/*.xml

# MyBatis SQL 打印(方法接口所在的包,不是 Mapper.xml 所在的包)

 # pagehelper
 pagehelper:
   helperDialect: sqlite #postgresql
   reasonable: true
   supportMethodsArguments: true
   params: countSql
   count: countSql
   returnPageInfo: check

 # 记录日志
 logging:
   config: classpath:logback-spring.xml

和前面介绍的配置选项一样。

3.Maven依赖

下面看一下Maven项目的POM文件
首先要添加父依赖

<parent>
    <groupId>com.aiun</groupId>
    <artifactId>BackendManageSystem</artifactId>
    <version>1.0-SNAPSHOT</version>
</parent>

还需要添加公共类的依赖

<!--backend common 依赖-->
<dependency>
    <groupId>com.aiun</groupId>
    <artifactId>backend-common</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

因为会用到用户实体类,所以加了一个user的依赖

<!-- user api 依赖 -->
<dependency>
    <groupId>com.aiun</groupId>
    <artifactId>backend-service-user-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

还会用到product 和shipping 依赖

<!-- product api 依赖 -->
<dependency>
    <groupId>com.aiun</groupId>
    <artifactId>backend-service-product-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
<!-- shipping api 依赖 -->
<dependency>
    <groupId>com.aiun</groupId>
    <artifactId>backend-service-shipping-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

下面是完整的pom文件

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>BackendManageSystem</artifactId>
        <groupId>com.aiun</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>backend-order</artifactId>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!--backend common 依赖-->
        <dependency>
            <groupId>com.aiun</groupId>
            <artifactId>backend-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!-- user api 依赖 -->
        <dependency>
            <groupId>com.aiun</groupId>
            <artifactId>backend-service-user-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!-- product api 依赖 -->
        <dependency>
            <groupId>com.aiun</groupId>
            <artifactId>backend-service-product-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!-- shipping api 依赖 -->
        <dependency>
            <groupId>com.aiun</groupId>
            <artifactId>backend-service-shipping-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--config client 依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-client</artifactId>
        </dependency>
        <!--feign依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!-- eureka client 依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--spring boot web 依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- mybatis 依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <!-- pagehelper 分页依赖 -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
        </dependency>
        <!--mysql 依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--druid 连接池依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <!-- lombok 依赖 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- swagger 依赖-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${swagger.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${swagger.version}</version>
        </dependency>
        <!--springboot test依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
</project>

4.启动项目

启动类OrderApplication

package com.aiun.order;
/**
 * 后台启动类
 * @author lenovo
 */
@EnableSwagger2
@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients(basePackages = {"com.aiun.product.feign", "com.aiun.user.feign", "com.aiun.shipping.feign"})
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}

这里启动类多了一个@EnableFeignClients注解,这个注解主要是通过 Feign 进行微服务不同子系统之间的通信的,详细内容看后面
还是一样,先启动注册中心,然后启动用户模块,因为该模块还需要使用到收货地址和产品信息,所以还要启动产品模块和收货地址模块。
通过Google插件Talend API Test进行购物车和订单接口的测试

商品 订单 支付 架构 订单结构分为三步_User_02

成功加入购物车,购物车接口没问题,下面测试订单接口:

商品 订单 支付 架构 订单结构分为三步_微服务_03

创建订单成功!!!

到这里订单模块就完成了。