Controller
package com.shiers.controller;import com.shiers.enums.OrderStatusEnum;import com.shiers.enums.PayMethod;import com.shiers.pojo.OrderStatus;import com.shiers.pojo.Orders;import com.shiers.pojo.bo.SubmitOrderBO;import com.shiers.pojo.vo.MerchantOrdersVO;import com.shiers.pojo.vo.OrderVO;import com.shiers.service.OrderService;import com.shiers.utils.MyJSONResult;import io.swagger.annotations.Api;import io.swagger.annotations.ApiOperation;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.*;import org.springframework.web.bind.annotation.*;import org.springframework.web.client.RestTemplate;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/** * Demo classO * * @author shierS * @date 2021/5/21 */@Api(value = "订单相关", tags = {"订单相关API接口"})@RequestMapping("orders")@RestControllerpublic class OrdersController extends BaseController { @Autowired private OrderService orderService; @Autowired private RestTemplate restTemplate; @ApiOperation(value = "用户下单", notes = "用户下单", httpMethod = "POST") @PostMapping("/create") public MyJSONResult create(@RequestBody SubmitOrderBO submitOrderBO, HttpServletRequest request, HttpServletResponse response) { //0.参数合法判断 if (submitOrderBO.getPayMethod() != PayMethod.WEIXIN.type && submitOrderBO.getPayMethod() != PayMethod.ALIPAY.type) { return MyJSONResult.errorMsg("支付方式不支持!"); } //1.创建订单 OrderVO orderVO = orderService.createOrder(submitOrderBO); String orderId = orderVO.getOrderId(); //2.创建订单后,移除购物车中已经算的商品 //TODO 整合redis之后,完善购物车中的已结算商品清除,并且同步到前端ccookie //CookieUtils.setCookie(request,response,BaseController.FOODIE_SHOPCART,"",true); //3.向支付中心发送当前订单,用于保存支付中心的数据 MerchantOrdersVO merchantOrdersVO = orderVO.getMerchantOrdersVO(); merchantOrdersVO.setReturnUrl(payReturnUrl); //设置回调地址 // 为了方便测试,所有支付都改为1分钱 merchantOrdersVO.setAmount(1); HttpHeaders headers = new HttpHeaders();//构建请求 headers.setContentType(MediaType.APPLICATION_JSON); //设置请求类型为JSON headers.add("imoocUserId", "账号"); headers.add("password", "密码"); HttpEntity<MerchantOrdersVO> entity = new HttpEntity<>(merchantOrdersVO, headers); // 发起请求 // 远程调用一个 HTTP 我们经常会用到 RestTemplate 这个类。这个类是 Spring 框架提供的一个工具类。RestTemplate 是一个同步的 Rest API 客户端。 ResponseEntity<MyJSONResult> responseEntity = restTemplate.postForEntity(paymentUrl, entity, MyJSONResult.class); MyJSONResult paymentResult = responseEntity.getBody(); // 伪支付 paymentResult.setStatus(200); if (paymentResult.getStatus() != 200) { return MyJSONResult.errorMsg("支付中心订单创建失败,请联系管理员"); } return MyJSONResult.ok(orderId); } @ApiOperation(value = "更新订单状态", notes = "更新订单状态", httpMethod = "POST") @PostMapping("notifyMerchantOrderPaid") public Integer notifyMerchantOrderPaid(String merchantOrderId) { orderService.updateOrderStatus(merchantOrderId, OrderStatusEnum.WAIT_DELIVER.type); //返回值为 int 200 return HttpStatus.OK.value(); } @PostMapping("getPaidOrderInfo") public MyJSONResult getPaidOrderInfo(String orderId){ OrderStatus orderStatus = orderService.queryOrderStatusInfo(orderId); return MyJSONResult.ok(orderStatus); } @ApiOperation(value = "伪支付:获取订单信息", notes = "伪支付:获取订单信息", httpMethod = "POST") @PostMapping("getOrderInfo") public MyJSONResult getOrderInfo(@RequestParam String orderId){ Orders orders = orderService.queryOrdersInfoById(orderId); System.out.println("========================================="); System.out.println(orderId); System.out.println("========================================="); return MyJSONResult.ok(orders); }}
Service
package com.shiers.service;import com.shiers.pojo.OrderStatus;import com.shiers.pojo.Orders;import com.shiers.pojo.bo.SubmitOrderBO;import com.shiers.pojo.vo.OrderVO;import java.util.List;/** * Demo class * * @author shierS * @date 2021/5/29 */public interface OrderService { /** * 创建订单相关信息 * @param submitOrderBO */ public OrderVO createOrder(SubmitOrderBO submitOrderBO); /** * 修改订单状态 * @param orderId * @param orderStatus */ public void updateOrderStatus(String orderId,Integer orderStatus); /** * 查询订单状态 * @param orderId * @return */ public OrderStatus queryOrderStatusInfo(String orderId); /** * 获取本地数据库订单信息:用于伪支付 * @param orderId * @return */ public Orders queryOrdersInfoById(String orderId); /** * 关闭超时未支付订单 */ public void closeOrder();}
package com.shiers.service.impl;import com.shiers.enums.OrderStatusEnum;import com.shiers.enums.YesOrNo;import com.shiers.mapper.OrderItemsMapper;import com.shiers.mapper.OrderStatusMapper;import com.shiers.mapper.OrdersMapper;import com.shiers.pojo.*;import com.shiers.pojo.bo.SubmitOrderBO;import com.shiers.pojo.vo.MerchantOrdersVO;import com.shiers.pojo.vo.OrderVO;import com.shiers.service.AddressService;import com.shiers.service.ItemService;import com.shiers.service.OrderService;import com.shiers.utils.DateUtil;import org.n3r.idworker.Sid;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;import java.util.Date;import java.util.List;/** * Demo class * * @author shierS * @date 2021/6/3 */@Servicepublic class OrderServiceImpl implements OrderService { @Autowired private OrdersMapper ordersMapper; @Autowired private AddressService addressService; @Autowired private ItemService itemService; @Autowired private OrderItemsMapper orderItemsMapper; @Autowired private OrderStatusMapper orderStatusMapper; @Autowired private Sid sid; @Transactional(propagation = Propagation.REQUIRED) @Override public OrderVO createOrder(SubmitOrderBO submitOrderBO) { String userId = submitOrderBO.getUserId(); String addressId = submitOrderBO.getAddressId(); String itemSpecIds = submitOrderBO.getItemSpecIds(); Integer payMethod = submitOrderBO.getPayMethod(); String leftMsg = submitOrderBO.getLeftMsg(); //邮费设置为0 Integer postAmount = 0; //生成订单ID String orderId = sid.nextShort(); //查询地址信息 UserAddress address = addressService.queryUserAddres(userId, addressId); //================== 1.新订单数据保存 ================== Orders newOrder = new Orders(); newOrder.setId(orderId); //订单ID newOrder.setUserId(userId); //用户ID newOrder.setReceiverName(address.getReceiver()); //收件人姓名 newOrder.setReceiverMobile(address.getMobile()); //收件人电话 newOrder.setReceiverAddress(address.getProvince() + " " + address.getCity() + " " + address.getDistrict() + " " + address.getDetail()); //地址拼接 newOrder.setPostAmount(postAmount); //邮费 newOrder.setPayMethod(payMethod); //支付方式 newOrder.setLeftMsg(leftMsg); //备注 newOrder.setIsComment(YesOrNo.NO.type); //是否评价 newOrder.setIsDelete(YesOrNo.NO.type); //是否删除 newOrder.setCreatedTime(new Date()); //创建时间 newOrder.setUpdatedTime(new Date()); //更新时间 // ================== 2.循环根据itemSpecIds保存订单商品信息表 ================== //分隔到的商品id数组 String[] itemSpecIdArr = itemSpecIds.split(","); Integer totalAmount = 0; //商品原价 Integer realPayAmount = 0; //优惠后的实际支付价格累计 //循环 for (String itemSpecId : itemSpecIdArr) { //TODO 整合redis后,商品购买的数量重新从redis的购物车中获取 //现在默认数量为1 int buyCounts = 1; //2.1根据规格id,查询规格的具体信息,主要获取价格 ItemsSpec itemSpec = itemService.queryItemSpecById(itemSpecId); totalAmount += itemSpec.getPriceNormal() * buyCounts; realPayAmount += itemSpec.getPriceDiscount() * buyCounts; //2.2根据规格id,获得商品信息及商品图片 String itemId = itemSpec.getItemId(); //获取商品信息 Items item = itemService.queryItemById(itemId); //获取商品主图 String imgUrl = itemService.queryItemMainImgById(itemId); //2.3循环保存子订单数据到数据库 //生成子订单Id String subOrderId = sid.nextShort(); //创建子订单对象,并填充数据 OrderItems subOrderItem = new OrderItems(); subOrderItem.setId(subOrderId); //id subOrderItem.setOrderId(orderId); //对应订单Id subOrderItem.setItemId(itemId); //对应商品id subOrderItem.setItemName(item.getItemName()); //商品名称 subOrderItem.setItemImg(imgUrl); //商品主图 subOrderItem.setBuyCounts(buyCounts); //所购商品数量 subOrderItem.setItemSpecId(itemSpecId); //商品规格Id subOrderItem.setItemSpecName(itemSpec.getName()); //商品规格名称 subOrderItem.setPrice(itemSpec.getPriceDiscount()); //优惠价格 //保存子订单到数据库 orderItemsMapper.insert(subOrderItem); //2.4在用户提交订单后,规格表中扣除库存 itemService.decreaseItemSpecStock(itemSpecId, buyCounts); } newOrder.setTotalAmount(totalAmount); //总价 newOrder.setRealPayAmount(realPayAmount); //实际价格 //保存订单到数据库 ordersMapper.insert(newOrder); // ================== 3.保存订单状态表 ================== OrderStatus waitPayOrderStatus = new OrderStatus(); waitPayOrderStatus.setOrderId(orderId); //订单状态表ID与订单ID相同 waitPayOrderStatus.setOrderStatus(OrderStatusEnum.WAIT_PAY.type); //设置订单状态 waitPayOrderStatus.setCreatedTime(new Date()); //创建时间 //保存订单状态表到数据库 orderStatusMapper.insert(waitPayOrderStatus); // ================== 4.构建商户订单,用于传给支付中心 ================== MerchantOrdersVO merchantOrdersVO = new MerchantOrdersVO(); merchantOrdersVO.setMerchantOrderId(orderId); merchantOrdersVO.setMerchantUserId(userId); merchantOrdersVO.setAmount(realPayAmount + postAmount); merchantOrdersVO.setPayMethod(payMethod); // ================== 5.构建自定义订单VO ================== OrderVO orderVO = new OrderVO(); orderVO.setOrderId(orderId); orderVO.setMerchantOrdersVO(merchantOrdersVO); return orderVO; } @Transactional(propagation = Propagation.REQUIRED) @Override public void updateOrderStatus(String orderId, Integer orderStatus) { OrderStatus paidStatus = new OrderStatus(); paidStatus.setOrderId(orderId); paidStatus.setOrderStatus(orderStatus); paidStatus.setPayTime(new Date()); orderStatusMapper.updateByPrimaryKeySelective(paidStatus); } @Transactional(propagation = Propagation.SUPPORTS) @Override public OrderStatus queryOrderStatusInfo(String orderId) { return orderStatusMapper.selectByPrimaryKey(orderId); } @Transactional(propagation = Propagation.SUPPORTS) @Override public Orders queryOrdersInfoById(String orderId) { return ordersMapper.selectByPrimaryKey(orderId); } //关闭超时订单 @Transactional(propagation = Propagation.REQUIRED) @Override public void closeOrder() { //查询所有为付款订单,判断时间是否超时(1天),超时则关闭交易 OrderStatus queryOrder = new OrderStatus(); queryOrder.setOrderStatus(OrderStatusEnum.WAIT_PAY.type); List<OrderStatus> list = orderStatusMapper.select(queryOrder); for (OrderStatus os : list){ //获取订单创建时间 Date createdTime = os.getCreatedTime(); //和当前时间进行比较 int days = DateUtil.daysBetween(createdTime, new Date()); if (days >= 1){ //超过1天,关闭订单 doCloseOrder(os.getOrderId()); } } } //关闭订单操作 @Transactional(propagation = Propagation.REQUIRED) void doCloseOrder(String orderId){ OrderStatus close = new OrderStatus(); close.setOrderId(orderId); close.setOrderStatus(OrderStatusEnum.CLOSE.type); close.setCloseTime(new Date()); orderStatusMapper.updateByPrimaryKeySelective(close); }}
使用乐观锁较少库存decreaseItemSpecStock
@Transactional(propagation = Propagation.REQUIRED)@Overridepublic void decreaseItemSpecStock(String specId, int buyCounts) { //库存是一个高并发操作,处理不当可能会出现库存为负数,导致数据的不一致性 //解决方案: //1、synchronized 不推荐使用,集器下无用,性能低下 //2、锁数据库:不推荐,导致数据库性能低下 //3、分布式锁:推荐, 设计到zookeeper redis 后续添加 //4、乐观锁,目前单体应用使用该方式 // lockUtil.getLock(); --加锁 // //1.查询库存 // int stock = 10; // // //2.判断库存,是否能够减少到0以下 // if (stock - buyCounts < 0) { // //提示用户库存不够 // } // lockUtil.unLock(); --解锁 //========🔥🔥🔥乐观锁实现:在SQL语句上加乐观锁判断条件🔥🔥🔥======== // and // stock >= #{pendingCounts} //这样在库存满足情况下会执行更新操作返回 1 ,否则返回 0 //通过返回值判断库存是否满足,并执行相关操作 //================================================================ int result = itemsMapperCustom.decreaseItemSpecStock(specId, buyCounts); if(result != 1){ throw new RuntimeException("订单创建失败,原因:库存不足"); }}
<update id="decreaseItemSpecStock"> update items_spec set stock = stock - #{pendingCounts} where id = #{specId} and stock >= #{pendingCounts} 👈乐观锁实现</update>
BO
用于创建订单对象的—SubmitOrderBO
package com.shiers.pojo.bo;/** * 用于创建订单对象的BO * * @author shierS * @date 2021/6/3 */public class SubmitOrderBo { private String userId; //用户id private String itemSpecIds; //所购商品id的拼接 private String addressId; //地址id private Integer payMethod; //支付方式 private String leftMsg; //备注 //get/set...}
VO
商户订单——MerchantOrdersVO
package com.shiers.pojo.vo;/** * 商户订单 */public class MerchantOrdersVO { private String merchantOrderId; // 商户订单号 private String merchantUserId; // 商户方的发起用户的用户主键id private Integer amount; // 实际支付总金额(包含商户所支付的订单费邮费总额) private Integer payMethod; // 支付方式 1:微信 2:支付宝 private String returnUrl; // 支付成功后的回调地址(学生自定义) //get/set...}
创建订单时service返回给controller层的VO——OrderVO
package com.shiers.pojo.vo;/** * 用于创建订单时,service返回给controller层的VO * * @author shierS * @date 2021/6/5 */public class OrderVO { private String orderId; private MerchantOrdersVO merchantOrdersVO; //get/set...}