Controller

  1. package com.shiers.controller;
  2. import com.shiers.enums.OrderStatusEnum;
  3. import com.shiers.enums.PayMethod;
  4. import com.shiers.pojo.OrderStatus;
  5. import com.shiers.pojo.Orders;
  6. import com.shiers.pojo.bo.SubmitOrderBO;
  7. import com.shiers.pojo.vo.MerchantOrdersVO;
  8. import com.shiers.pojo.vo.OrderVO;
  9. import com.shiers.service.OrderService;
  10. import com.shiers.utils.MyJSONResult;
  11. import io.swagger.annotations.Api;
  12. import io.swagger.annotations.ApiOperation;
  13. import org.springframework.beans.factory.annotation.Autowired;
  14. import org.springframework.http.*;
  15. import org.springframework.web.bind.annotation.*;
  16. import org.springframework.web.client.RestTemplate;
  17. import javax.servlet.http.HttpServletRequest;
  18. import javax.servlet.http.HttpServletResponse;
  19. /**
  20. * Demo classO
  21. *
  22. * @author shierS
  23. * @date 2021/5/21
  24. */
  25. @Api(value = "订单相关", tags = {"订单相关API接口"})
  26. @RequestMapping("orders")
  27. @RestController
  28. public class OrdersController extends BaseController {
  29. @Autowired
  30. private OrderService orderService;
  31. @Autowired
  32. private RestTemplate restTemplate;
  33. @ApiOperation(value = "用户下单", notes = "用户下单", httpMethod = "POST")
  34. @PostMapping("/create")
  35. public MyJSONResult create(@RequestBody SubmitOrderBO submitOrderBO,
  36. HttpServletRequest request,
  37. HttpServletResponse response) {
  38. //0.参数合法判断
  39. if (submitOrderBO.getPayMethod() != PayMethod.WEIXIN.type && submitOrderBO.getPayMethod() != PayMethod.ALIPAY.type) {
  40. return MyJSONResult.errorMsg("支付方式不支持!");
  41. }
  42. //1.创建订单
  43. OrderVO orderVO = orderService.createOrder(submitOrderBO);
  44. String orderId = orderVO.getOrderId();
  45. //2.创建订单后,移除购物车中已经算的商品
  46. //TODO 整合redis之后,完善购物车中的已结算商品清除,并且同步到前端ccookie
  47. //CookieUtils.setCookie(request,response,BaseController.FOODIE_SHOPCART,"",true);
  48. //3.向支付中心发送当前订单,用于保存支付中心的数据
  49. MerchantOrdersVO merchantOrdersVO = orderVO.getMerchantOrdersVO();
  50. merchantOrdersVO.setReturnUrl(payReturnUrl); //设置回调地址
  51. // 为了方便测试,所有支付都改为1分钱
  52. merchantOrdersVO.setAmount(1);
  53. HttpHeaders headers = new HttpHeaders();//构建请求
  54. headers.setContentType(MediaType.APPLICATION_JSON); //设置请求类型为JSON
  55. headers.add("imoocUserId", "账号");
  56. headers.add("password", "密码");
  57. HttpEntity<MerchantOrdersVO> entity = new HttpEntity<>(merchantOrdersVO, headers);
  58. // 发起请求
  59. // 远程调用一个 HTTP 我们经常会用到 RestTemplate 这个类。这个类是 Spring 框架提供的一个工具类。RestTemplate 是一个同步的 Rest API 客户端。
  60. ResponseEntity<MyJSONResult> responseEntity =
  61. restTemplate.postForEntity(paymentUrl, entity, MyJSONResult.class);
  62. MyJSONResult paymentResult = responseEntity.getBody();
  63. // 伪支付
  64. paymentResult.setStatus(200);
  65. if (paymentResult.getStatus() != 200) {
  66. return MyJSONResult.errorMsg("支付中心订单创建失败,请联系管理员");
  67. }
  68. return MyJSONResult.ok(orderId);
  69. }
  70. @ApiOperation(value = "更新订单状态", notes = "更新订单状态", httpMethod = "POST")
  71. @PostMapping("notifyMerchantOrderPaid")
  72. public Integer notifyMerchantOrderPaid(String merchantOrderId) {
  73. orderService.updateOrderStatus(merchantOrderId, OrderStatusEnum.WAIT_DELIVER.type);
  74. //返回值为 int 200
  75. return HttpStatus.OK.value();
  76. }
  77. @PostMapping("getPaidOrderInfo")
  78. public MyJSONResult getPaidOrderInfo(String orderId){
  79. OrderStatus orderStatus = orderService.queryOrderStatusInfo(orderId);
  80. return MyJSONResult.ok(orderStatus);
  81. }
  82. @ApiOperation(value = "伪支付:获取订单信息", notes = "伪支付:获取订单信息", httpMethod = "POST")
  83. @PostMapping("getOrderInfo")
  84. public MyJSONResult getOrderInfo(@RequestParam String orderId){
  85. Orders orders = orderService.queryOrdersInfoById(orderId);
  86. System.out.println("=========================================");
  87. System.out.println(orderId);
  88. System.out.println("=========================================");
  89. return MyJSONResult.ok(orders);
  90. }
  91. }

Service

  1. package com.shiers.service;
  2. import com.shiers.pojo.OrderStatus;
  3. import com.shiers.pojo.Orders;
  4. import com.shiers.pojo.bo.SubmitOrderBO;
  5. import com.shiers.pojo.vo.OrderVO;
  6. import java.util.List;
  7. /**
  8. * Demo class
  9. *
  10. * @author shierS
  11. * @date 2021/5/29
  12. */
  13. public interface OrderService {
  14. /**
  15. * 创建订单相关信息
  16. * @param submitOrderBO
  17. */
  18. public OrderVO createOrder(SubmitOrderBO submitOrderBO);
  19. /**
  20. * 修改订单状态
  21. * @param orderId
  22. * @param orderStatus
  23. */
  24. public void updateOrderStatus(String orderId,Integer orderStatus);
  25. /**
  26. * 查询订单状态
  27. * @param orderId
  28. * @return
  29. */
  30. public OrderStatus queryOrderStatusInfo(String orderId);
  31. /**
  32. * 获取本地数据库订单信息:用于伪支付
  33. * @param orderId
  34. * @return
  35. */
  36. public Orders queryOrdersInfoById(String orderId);
  37. /**
  38. * 关闭超时未支付订单
  39. */
  40. public void closeOrder();
  41. }
  1. package com.shiers.service.impl;
  2. import com.shiers.enums.OrderStatusEnum;
  3. import com.shiers.enums.YesOrNo;
  4. import com.shiers.mapper.OrderItemsMapper;
  5. import com.shiers.mapper.OrderStatusMapper;
  6. import com.shiers.mapper.OrdersMapper;
  7. import com.shiers.pojo.*;
  8. import com.shiers.pojo.bo.SubmitOrderBO;
  9. import com.shiers.pojo.vo.MerchantOrdersVO;
  10. import com.shiers.pojo.vo.OrderVO;
  11. import com.shiers.service.AddressService;
  12. import com.shiers.service.ItemService;
  13. import com.shiers.service.OrderService;
  14. import com.shiers.utils.DateUtil;
  15. import org.n3r.idworker.Sid;
  16. import org.springframework.beans.factory.annotation.Autowired;
  17. import org.springframework.stereotype.Service;
  18. import org.springframework.transaction.annotation.Propagation;
  19. import org.springframework.transaction.annotation.Transactional;
  20. import java.util.Date;
  21. import java.util.List;
  22. /**
  23. * Demo class
  24. *
  25. * @author shierS
  26. * @date 2021/6/3
  27. */
  28. @Service
  29. public class OrderServiceImpl implements OrderService {
  30. @Autowired
  31. private OrdersMapper ordersMapper;
  32. @Autowired
  33. private AddressService addressService;
  34. @Autowired
  35. private ItemService itemService;
  36. @Autowired
  37. private OrderItemsMapper orderItemsMapper;
  38. @Autowired
  39. private OrderStatusMapper orderStatusMapper;
  40. @Autowired
  41. private Sid sid;
  42. @Transactional(propagation = Propagation.REQUIRED)
  43. @Override
  44. public OrderVO createOrder(SubmitOrderBO submitOrderBO) {
  45. String userId = submitOrderBO.getUserId();
  46. String addressId = submitOrderBO.getAddressId();
  47. String itemSpecIds = submitOrderBO.getItemSpecIds();
  48. Integer payMethod = submitOrderBO.getPayMethod();
  49. String leftMsg = submitOrderBO.getLeftMsg();
  50. //邮费设置为0
  51. Integer postAmount = 0;
  52. //生成订单ID
  53. String orderId = sid.nextShort();
  54. //查询地址信息
  55. UserAddress address = addressService.queryUserAddres(userId, addressId);
  56. //================== 1.新订单数据保存 ==================
  57. Orders newOrder = new Orders();
  58. newOrder.setId(orderId); //订单ID
  59. newOrder.setUserId(userId); //用户ID
  60. newOrder.setReceiverName(address.getReceiver()); //收件人姓名
  61. newOrder.setReceiverMobile(address.getMobile()); //收件人电话
  62. newOrder.setReceiverAddress(address.getProvince() + " " + address.getCity() + " " + address.getDistrict() + " " + address.getDetail()); //地址拼接
  63. newOrder.setPostAmount(postAmount); //邮费
  64. newOrder.setPayMethod(payMethod); //支付方式
  65. newOrder.setLeftMsg(leftMsg); //备注
  66. newOrder.setIsComment(YesOrNo.NO.type); //是否评价
  67. newOrder.setIsDelete(YesOrNo.NO.type); //是否删除
  68. newOrder.setCreatedTime(new Date()); //创建时间
  69. newOrder.setUpdatedTime(new Date()); //更新时间
  70. // ================== 2.循环根据itemSpecIds保存订单商品信息表 ==================
  71. //分隔到的商品id数组
  72. String[] itemSpecIdArr = itemSpecIds.split(",");
  73. Integer totalAmount = 0; //商品原价
  74. Integer realPayAmount = 0; //优惠后的实际支付价格累计
  75. //循环
  76. for (String itemSpecId : itemSpecIdArr) {
  77. //TODO 整合redis后,商品购买的数量重新从redis的购物车中获取
  78. //现在默认数量为1
  79. int buyCounts = 1;
  80. //2.1根据规格id,查询规格的具体信息,主要获取价格
  81. ItemsSpec itemSpec = itemService.queryItemSpecById(itemSpecId);
  82. totalAmount += itemSpec.getPriceNormal() * buyCounts;
  83. realPayAmount += itemSpec.getPriceDiscount() * buyCounts;
  84. //2.2根据规格id,获得商品信息及商品图片
  85. String itemId = itemSpec.getItemId();
  86. //获取商品信息
  87. Items item = itemService.queryItemById(itemId);
  88. //获取商品主图
  89. String imgUrl = itemService.queryItemMainImgById(itemId);
  90. //2.3循环保存子订单数据到数据库
  91. //生成子订单Id
  92. String subOrderId = sid.nextShort();
  93. //创建子订单对象,并填充数据
  94. OrderItems subOrderItem = new OrderItems();
  95. subOrderItem.setId(subOrderId); //id
  96. subOrderItem.setOrderId(orderId); //对应订单Id
  97. subOrderItem.setItemId(itemId); //对应商品id
  98. subOrderItem.setItemName(item.getItemName()); //商品名称
  99. subOrderItem.setItemImg(imgUrl); //商品主图
  100. subOrderItem.setBuyCounts(buyCounts); //所购商品数量
  101. subOrderItem.setItemSpecId(itemSpecId); //商品规格Id
  102. subOrderItem.setItemSpecName(itemSpec.getName()); //商品规格名称
  103. subOrderItem.setPrice(itemSpec.getPriceDiscount()); //优惠价格
  104. //保存子订单到数据库
  105. orderItemsMapper.insert(subOrderItem);
  106. //2.4在用户提交订单后,规格表中扣除库存
  107. itemService.decreaseItemSpecStock(itemSpecId, buyCounts);
  108. }
  109. newOrder.setTotalAmount(totalAmount); //总价
  110. newOrder.setRealPayAmount(realPayAmount); //实际价格
  111. //保存订单到数据库
  112. ordersMapper.insert(newOrder);
  113. // ================== 3.保存订单状态表 ==================
  114. OrderStatus waitPayOrderStatus = new OrderStatus();
  115. waitPayOrderStatus.setOrderId(orderId); //订单状态表ID与订单ID相同
  116. waitPayOrderStatus.setOrderStatus(OrderStatusEnum.WAIT_PAY.type); //设置订单状态
  117. waitPayOrderStatus.setCreatedTime(new Date()); //创建时间
  118. //保存订单状态表到数据库
  119. orderStatusMapper.insert(waitPayOrderStatus);
  120. // ================== 4.构建商户订单,用于传给支付中心 ==================
  121. MerchantOrdersVO merchantOrdersVO = new MerchantOrdersVO();
  122. merchantOrdersVO.setMerchantOrderId(orderId);
  123. merchantOrdersVO.setMerchantUserId(userId);
  124. merchantOrdersVO.setAmount(realPayAmount + postAmount);
  125. merchantOrdersVO.setPayMethod(payMethod);
  126. // ================== 5.构建自定义订单VO ==================
  127. OrderVO orderVO = new OrderVO();
  128. orderVO.setOrderId(orderId);
  129. orderVO.setMerchantOrdersVO(merchantOrdersVO);
  130. return orderVO;
  131. }
  132. @Transactional(propagation = Propagation.REQUIRED)
  133. @Override
  134. public void updateOrderStatus(String orderId, Integer orderStatus) {
  135. OrderStatus paidStatus = new OrderStatus();
  136. paidStatus.setOrderId(orderId);
  137. paidStatus.setOrderStatus(orderStatus);
  138. paidStatus.setPayTime(new Date());
  139. orderStatusMapper.updateByPrimaryKeySelective(paidStatus);
  140. }
  141. @Transactional(propagation = Propagation.SUPPORTS)
  142. @Override
  143. public OrderStatus queryOrderStatusInfo(String orderId) {
  144. return orderStatusMapper.selectByPrimaryKey(orderId);
  145. }
  146. @Transactional(propagation = Propagation.SUPPORTS)
  147. @Override
  148. public Orders queryOrdersInfoById(String orderId) {
  149. return ordersMapper.selectByPrimaryKey(orderId);
  150. }
  151. //关闭超时订单
  152. @Transactional(propagation = Propagation.REQUIRED)
  153. @Override
  154. public void closeOrder() {
  155. //查询所有为付款订单,判断时间是否超时(1天),超时则关闭交易
  156. OrderStatus queryOrder = new OrderStatus();
  157. queryOrder.setOrderStatus(OrderStatusEnum.WAIT_PAY.type);
  158. List<OrderStatus> list = orderStatusMapper.select(queryOrder);
  159. for (OrderStatus os : list){
  160. //获取订单创建时间
  161. Date createdTime = os.getCreatedTime();
  162. //和当前时间进行比较
  163. int days = DateUtil.daysBetween(createdTime, new Date());
  164. if (days >= 1){
  165. //超过1天,关闭订单
  166. doCloseOrder(os.getOrderId());
  167. }
  168. }
  169. }
  170. //关闭订单操作
  171. @Transactional(propagation = Propagation.REQUIRED)
  172. void doCloseOrder(String orderId){
  173. OrderStatus close = new OrderStatus();
  174. close.setOrderId(orderId);
  175. close.setOrderStatus(OrderStatusEnum.CLOSE.type);
  176. close.setCloseTime(new Date());
  177. orderStatusMapper.updateByPrimaryKeySelective(close);
  178. }
  179. }

使用乐观锁较少库存decreaseItemSpecStock

  1. @Transactional(propagation = Propagation.REQUIRED)
  2. @Override
  3. public void decreaseItemSpecStock(String specId, int buyCounts) {
  4. //库存是一个高并发操作,处理不当可能会出现库存为负数,导致数据的不一致性
  5. //解决方案:
  6. //1、synchronized 不推荐使用,集器下无用,性能低下
  7. //2、锁数据库:不推荐,导致数据库性能低下
  8. //3、分布式锁:推荐, 设计到zookeeper redis 后续添加
  9. //4、乐观锁,目前单体应用使用该方式
  10. // lockUtil.getLock(); --加锁
  11. // //1.查询库存
  12. // int stock = 10;
  13. //
  14. // //2.判断库存,是否能够减少到0以下
  15. // if (stock - buyCounts < 0) {
  16. // //提示用户库存不够
  17. // }
  18. // lockUtil.unLock(); --解锁
  19. //========🔥🔥🔥乐观锁实现:在SQL语句上加乐观锁判断条件🔥🔥🔥========
  20. // and
  21. // stock >= #{pendingCounts}
  22. //这样在库存满足情况下会执行更新操作返回 1 ,否则返回 0
  23. //通过返回值判断库存是否满足,并执行相关操作
  24. //================================================================
  25. int result = itemsMapperCustom.decreaseItemSpecStock(specId, buyCounts);
  26. if(result != 1){
  27. throw new RuntimeException("订单创建失败,原因:库存不足");
  28. }
  29. }
  1. <update id="decreaseItemSpecStock">
  2. update
  3. items_spec
  4. set
  5. stock = stock - #{pendingCounts}
  6. where
  7. id = #{specId}
  8. and
  9. stock >= #{pendingCounts} 👈乐观锁实现
  10. </update>

BO

用于创建订单对象的—SubmitOrderBO

  1. package com.shiers.pojo.bo;
  2. /**
  3. * 用于创建订单对象的BO
  4. *
  5. * @author shierS
  6. * @date 2021/6/3
  7. */
  8. public class SubmitOrderBo {
  9. private String userId; //用户id
  10. private String itemSpecIds; //所购商品id的拼接
  11. private String addressId; //地址id
  12. private Integer payMethod; //支付方式
  13. private String leftMsg; //备注
  14. //get/set...
  15. }

VO

商户订单——MerchantOrdersVO

  1. package com.shiers.pojo.vo;
  2. /**
  3. * 商户订单
  4. */
  5. public class MerchantOrdersVO {
  6. private String merchantOrderId; // 商户订单号
  7. private String merchantUserId; // 商户方的发起用户的用户主键id
  8. private Integer amount; // 实际支付总金额(包含商户所支付的订单费邮费总额)
  9. private Integer payMethod; // 支付方式 1:微信 2:支付宝
  10. private String returnUrl; // 支付成功后的回调地址(学生自定义)
  11. //get/set...
  12. }

创建订单时service返回给controller层的VO——OrderVO

  1. package com.shiers.pojo.vo;
  2. /**
  3. * 用于创建订单时,service返回给controller层的VO
  4. *
  5. * @author shierS
  6. * @date 2021/6/5
  7. */
  8. public class OrderVO {
  9. private String orderId;
  10. private MerchantOrdersVO merchantOrdersVO;
  11. //get/set...
  12. }