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")
@RestController
public 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
*/
@Service
public 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)
@Override
public 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...
}