RocketMQ 实战:模拟电商网站场景综合案例(七)
一、RocketMQ 实战:模拟电商网站场景综合案例–生成预订单流程分析1
二、RocketMQ 实战:模拟电商网站场景综合案例–生成预订单流程分析2
在 shop-order-service 工程模块中,对 OrderServiceImpl.java 下单业务类,预订单 方法,进行分析。
/**
* 生成预订单
*
* @param order
* @return
*/
private Long savePreOrder(TradeOrder order) {
//1. 设置订单状态为不可见
//2. 设置订单ID
//3. 核算订单运费
//4. 核算订单总金额是否合法
//5.判断用户是否使用余额
//6.判断用户是否使用优惠券
//7.核算订单支付金额 订单总金额-余额-优惠券金额
//8.设置下单时间
//9.保存订单到数据库
//10.返回订单ID
}
三、RocketMQ 实战:模拟电商网站场景综合案例–生成预订单流程实现1
在 shop-order-service 工程模块中,修改 OrderServiceImpl.java 下单业务类,生成预订单代码实现 方法。
package com.itheima.shop.service;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.itheima.api.ICouponService;
import com.itheima.api.IGoodsService;
import com.itheima.api.IOrderService;
import com.itheima.api.IUserService;
import com.itheima.constant.ShopCode;
import com.itheima.entity.MQEntity;
import com.itheima.entity.Result;
import com.itheima.exception.CastException;
import com.itheima.shop.mapper.TradeOrderMapper;
import com.itheima.shop.pojo.*;
import com.itheima.utils.IDWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.Date;
@Slf4j
@Component
@Service(interfaceClass = IOrderService.class)
public class OrderServiceImpl implements IOrderService {
@Reference
private IGoodsService goodsService;
@Reference
private IUserService userService;
@Reference
private ICouponService couponService;
@Autowired
private IDWorker idWorker;
@Autowired
private RocketMQTemplate rocketMQTemplate;
@Override
public Result confirmOrder(TradeOrder order) {
//1.校验订单
checkOrder(order);
//2.生成预订单
Long orderId = savePreOrder(order);
try {
//3.扣减库存
//4.扣减优惠券
//5.使用余额
//6.确认订单
//7.返回成功状态
} catch (Exception e) {
//1.确认订单失败,发送消息
//2.返回订单确认失败消息
return null;
}
}
/**
* 生成预订单
*
* @param order
* @return
*/
private Long savePreOrder(TradeOrder order) {
//1. 设置订单状态为不可见
order.setOrderStatus(ShopCode.SHOP_ORDER_NO_CONFIRM.getCode());
//2. 设置订单ID
long orderId = idWorker.nextId();
order.setOrderId(orderId);
//3. 核算订单运费
BigDecimal shippingFee = calculateShippingFee(order.getOrderAmount());
if(order.getShippingFee().compareTo(shippingFee)!=0){
CastException.cast(ShopCode.SHOP_ORDER_SHIPPINGFEE_INVALID);
}
//4. 核算订单总金额是否合法
BigDecimal orderAmount = order.getGoodsPrice().multiply(new BigDecimal(order.getGoodsNumber()));
orderAmount.add(shippingFee);
if(order.getOrderAmount().compareTo(orderAmount)!=0){
CastException.cast(ShopCode.SHOP_ORDERAMOUNT_INVALID);
}
//5.判断用户是否使用余额
BigDecimal moneyPaid = order.getMoneyPaid();
if(moneyPaid!=null){
//5.1 订单中余额是否合法
int r = moneyPaid.compareTo(BigDecimal.ZERO);
//余额小于0
if(r==-1){
CastException.cast(ShopCode.SHOP_MONEY_PAID_LESS_ZERO);
}
//余额大于0
if(r==1){
TradeUser user = userService.findOne(order.getUserId());
if(moneyPaid.compareTo(new BigDecimal(user.getUserMoney()))==1){
CastException.cast(ShopCode.SHOP_MONEY_PAID_INVALID);
}
}
}else{
order.setMoneyPaid(BigDecimal.ZERO);
}
//6.判断用户是否使用优惠券
//7.核算订单支付金额 订单总金额-余额-优惠券金额
//8.设置下单时间
//9.保存订单到数据库
//10.返回订单ID
return null;
}
/**
* 核算运费
* @param orderAmount
* @return
*/
private BigDecimal calculateShippingFee(BigDecimal orderAmount) {
if(orderAmount.compareTo(new BigDecimal(100))==1){
return BigDecimal.ZERO;
}else{
return new BigDecimal(10);
}
}
/**
* 校验订单
*
* @param order
*/
private void checkOrder(TradeOrder order) {
//1.校验订单是否存在
if (order == null) {
CastException.cast(ShopCode.SHOP_ORDER_INVALID);
}
//2.校验订单中的商品是否存在
TradeGoods goods = goodsService.findOne(order.getGoodsId());
if (goods == null) {
CastException.cast(ShopCode.SHOP_GOODS_NO_EXIST);
}
//3.校验下单用户是否存在
TradeUser user = userService.findOne(order.getUserId());
if (user == null) {
CastException.cast(ShopCode.SHOP_USER_NO_EXIST);
}
//4.校验商品单价是否合法
if (order.getGoodsPrice().compareTo(goods.getGoodsPrice()) != 0) {
CastException.cast(ShopCode.SHOP_GOODS_PRICE_INVALID);
}
//5.校验订单商品数量是否合法
if (order.getGoodsNumber() >= goods.getGoodsNumber()) {
CastException.cast(ShopCode.SHOP_GOODS_NUM_NOT_ENOUGH);
}
log.info("校验订单通过");
}
}
四、RocketMQ 实战:模拟电商网站场景综合案例–生成预订单流程实现2
1、在 shop-api 工程模块中,创建 优惠券 接口类 ICouponService.java
package com.itheima.api;
import com.itheima.entity.Result;
import com.itheima.shop.pojo.TradeCoupon;
/**
* 优惠券接口
*/
public interface ICouponService {
/**
* 根据ID查询优惠券对象
* @param coupouId
* @return
*/
public TradeCoupon findOne(Long coupouId);
/**
* 更细优惠券状态
* @param coupon
* @return
*/
Result updateCouponStatus(TradeCoupon coupon);
}
2、在 shop-order-service 工程模块中,修改 OrderServiceImpl.java 下单业务类,完成 预订单代码实现 方法。
package com.itheima.shop.service;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.itheima.api.ICouponService;
import com.itheima.api.IGoodsService;
import com.itheima.api.IOrderService;
import com.itheima.api.IUserService;
import com.itheima.constant.ShopCode;
import com.itheima.entity.MQEntity;
import com.itheima.entity.Result;
import com.itheima.exception.CastException;
import com.itheima.shop.mapper.TradeOrderMapper;
import com.itheima.shop.pojo.*;
import com.itheima.utils.IDWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.Date;
@Slf4j
@Component
@Service(interfaceClass = IOrderService.class)
public class OrderServiceImpl implements IOrderService {
@Reference
private IGoodsService goodsService;
@Reference
private IUserService userService;
@Reference
private ICouponService couponService;
@Autowired
private TradeOrderMapper orderMapper;
@Autowired
private IDWorker idWorker;
@Autowired
private RocketMQTemplate rocketMQTemplate;
@Override
public Result confirmOrder(TradeOrder order) {
//1.校验订单
checkOrder(order);
//2.生成预订单
Long orderId = savePreOrder(order);
try {
//3.扣减库存
//4.扣减优惠券
//5.使用余额
//6.确认订单
//7.返回成功状态
} catch (Exception e) {
//1.确认订单失败,发送消息
//2.返回订单确认失败消息
return null;
}
}
/**
* 生成预订单
*
* @param order
* @return
*/
private Long savePreOrder(TradeOrder order) {
//1. 设置订单状态为不可见
order.setOrderStatus(ShopCode.SHOP_ORDER_NO_CONFIRM.getCode());
//2. 设置订单ID
long orderId = idWorker.nextId();
order.setOrderId(orderId);
//3. 核算订单运费
BigDecimal shippingFee = calculateShippingFee(order.getOrderAmount());
if(order.getShippingFee().compareTo(shippingFee)!=0){
CastException.cast(ShopCode.SHOP_ORDER_SHIPPINGFEE_INVALID);
}
//4. 核算订单总金额是否合法
BigDecimal orderAmount = order.getGoodsPrice().multiply(new BigDecimal(order.getGoodsNumber()));
orderAmount.add(shippingFee);
if(order.getOrderAmount().compareTo(orderAmount)!=0){
CastException.cast(ShopCode.SHOP_ORDERAMOUNT_INVALID);
}
//5.判断用户是否使用余额
BigDecimal moneyPaid = order.getMoneyPaid();
if(moneyPaid!=null){
//5.1 订单中余额是否合法
int r = moneyPaid.compareTo(BigDecimal.ZERO);
//余额小于0
if(r==-1){
CastException.cast(ShopCode.SHOP_MONEY_PAID_LESS_ZERO);
}
//余额大于0
if(r==1){
TradeUser user = userService.findOne(order.getUserId());
if(moneyPaid.compareTo(new BigDecimal(user.getUserMoney()))==1){
CastException.cast(ShopCode.SHOP_MONEY_PAID_INVALID);
}
}
}else{
order.setMoneyPaid(BigDecimal.ZERO);
}
//6.判断用户是否使用优惠券
Long couponId = order.getCouponId();
if(couponId!=null){
TradeCoupon coupon = couponService.findOne(couponId);
//6.1 判断优惠券是否存在
if(coupon==null){
CastException.cast(ShopCode.SHOP_COUPON_NO_EXIST);
}
//6.2 判断优惠券是否已经被使用
if(coupon.getIsUsed().intValue()==ShopCode.SHOP_COUPON_ISUSED.getCode().intValue()){
CastException.cast(ShopCode.SHOP_COUPON_ISUSED);
}
order.setCouponPaid(coupon.getCouponPrice());
}else{
order.setCouponPaid(BigDecimal.ZERO);
}
//7.核算订单支付金额 订单总金额-余额-优惠券金额
BigDecimal payAmount = order.getOrderAmount().subtract(order.getMoneyPaid()).subtract(order.getCouponPaid());
order.setPayAmount(payAmount);
//8.设置下单时间
order.setAddTime(new Date());
//9.保存订单到数据库
orderMapper.insert(order);
//10.返回订单ID
return orderId;
}
/**
* 核算运费
* @param orderAmount
* @return
*/
private BigDecimal calculateShippingFee(BigDecimal orderAmount) {
if(orderAmount.compareTo(new BigDecimal(100))==1){
return BigDecimal.ZERO;
}else{
return new BigDecimal(10);
}
}
/**
* 校验订单
*
* @param order
*/
private void checkOrder(TradeOrder order) {
//1.校验订单是否存在
if (order == null) {
CastException.cast(ShopCode.SHOP_ORDER_INVALID);
}
//2.校验订单中的商品是否存在
TradeGoods goods = goodsService.findOne(order.getGoodsId());
if (goods == null) {
CastException.cast(ShopCode.SHOP_GOODS_NO_EXIST);
}
//3.校验下单用户是否存在
TradeUser user = userService.findOne(order.getUserId());
if (user == null) {
CastException.cast(ShopCode.SHOP_USER_NO_EXIST);
}
//4.校验商品单价是否合法
if (order.getGoodsPrice().compareTo(goods.getGoodsPrice()) != 0) {
CastException.cast(ShopCode.SHOP_GOODS_PRICE_INVALID);
}
//5.校验订单商品数量是否合法
if (order.getGoodsNumber() >= goods.getGoodsNumber()) {
CastException.cast(ShopCode.SHOP_GOODS_NUM_NOT_ENOUGH);
}
log.info("校验订单通过");
}
}
五、RocketMQ 实战:模拟电商网站场景综合案例–扣减库存代码实现1
1、扣减库存:通过 dubbo 调用商品服务完成扣减库存。
/**
* 扣减库存
* @param order
*/
private void reduceGoodsNum(TradeOrder order) {
//订单ID,商品ID,商品数量
TradeGoodsNumberLog goodsNumberLog = new TradeGoodsNumberLog();
goodsNumberLog.setOrderId(order.getOrderId());
goodsNumberLog.setGoodsId(order.getGoodsId());
goodsNumberLog.setGoodsNumber(order.getGoodsNumber());
Result result = goodsService.reduceGoodsNum(goodsNumberLog);
if(result.getSuccess().equals(ShopCode.SHOP_FAIL.getSuccess())){
CastException.cast(ShopCode.SHOP_REDUCE_GOODS_NUM_FAIL);
}
log.info("订单:"+order.getOrderId()+"扣减库存成功");
}
2、在 shop-order-service 工程模块中,修改 OrderServiceImpl.java 下单业务类,完成 扣减库存 方法。
package com.itheima.shop.service;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.itheima.api.ICouponService;
import com.itheima.api.IGoodsService;
import com.itheima.api.IOrderService;
import com.itheima.api.IUserService;
import com.itheima.constant.ShopCode;
import com.itheima.entity.MQEntity;
import com.itheima.entity.Result;
import com.itheima.exception.CastException;
import com.itheima.shop.mapper.TradeOrderMapper;
import com.itheima.shop.pojo.*;
import com.itheima.utils.IDWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.Date;
@Slf4j
@Component
@Service(interfaceClass = IOrderService.class)
public class OrderServiceImpl implements IOrderService {
@Reference
private IGoodsService goodsService;
@Reference
private IUserService userService;
@Reference
private ICouponService couponService;
@Autowired
private TradeOrderMapper orderMapper;
@Autowired
private IDWorker idWorker;
@Autowired
private RocketMQTemplate rocketMQTemplate;
@Override
public Result confirmOrder(TradeOrder order) {
//1.校验订单
checkOrder(order);
//2.生成预订单
Long orderId = savePreOrder(order);
try {
//3.扣减库存
reduceGoodsNum(order);
//4.扣减优惠券
//5.使用余额
//6.确认订单
//7.返回成功状态
} catch (Exception e) {
//1.确认订单失败,发送消息
//2.返回订单确认失败消息
return null;
}
}
/**
* 扣减库存
* @param order
*/
private void reduceGoodsNum(TradeOrder order) {
//订单ID,商品ID,商品数量
TradeGoodsNumberLog goodsNumberLog = new TradeGoodsNumberLog();
goodsNumberLog.setOrderId(order.getOrderId());
goodsNumberLog.setGoodsId(order.getGoodsId());
goodsNumberLog.setGoodsNumber(order.getGoodsNumber());
Result result = goodsService.reduceGoodsNum(goodsNumberLog);
if(result.getSuccess().equals(ShopCode.SHOP_FAIL.getSuccess())){
CastException.cast(ShopCode.SHOP_REDUCE_GOODS_NUM_FAIL);
}
log.info("订单:"+order.getOrderId()+"扣减库存成功");
}
/**
* 生成预订单
*
* @param order
* @return
*/
private Long savePreOrder(TradeOrder order) {
//1. 设置订单状态为不可见
order.setOrderStatus(ShopCode.SHOP_ORDER_NO_CONFIRM.getCode());
//2. 设置订单ID
long orderId = idWorker.nextId();
order.setOrderId(orderId);
//3. 核算订单运费
BigDecimal shippingFee = calculateShippingFee(order.getOrderAmount());
if(order.getShippingFee().compareTo(shippingFee)!=0){
CastException.cast(ShopCode.SHOP_ORDER_SHIPPINGFEE_INVALID);
}
//4. 核算订单总金额是否合法
BigDecimal orderAmount = order.getGoodsPrice().multiply(new BigDecimal(order.getGoodsNumber()));
orderAmount.add(shippingFee);
if(order.getOrderAmount().compareTo(orderAmount)!=0){
CastException.cast(ShopCode.SHOP_ORDERAMOUNT_INVALID);
}
//5.判断用户是否使用余额
BigDecimal moneyPaid = order.getMoneyPaid();
if(moneyPaid!=null){
//5.1 订单中余额是否合法
int r = moneyPaid.compareTo(BigDecimal.ZERO);
//余额小于0
if(r==-1){
CastException.cast(ShopCode.SHOP_MONEY_PAID_LESS_ZERO);
}
//余额大于0
if(r==1){
TradeUser user = userService.findOne(order.getUserId());
if(moneyPaid.compareTo(new BigDecimal(user.getUserMoney()))==1){
CastException.cast(ShopCode.SHOP_MONEY_PAID_INVALID);
}
}
}else{
order.setMoneyPaid(BigDecimal.ZERO);
}
//6.判断用户是否使用优惠券
Long couponId = order.getCouponId();
if(couponId!=null){
TradeCoupon coupon = couponService.findOne(couponId);
//6.1 判断优惠券是否存在
if(coupon==null){
CastException.cast(ShopCode.SHOP_COUPON_NO_EXIST);
}
//6.2 判断优惠券是否已经被使用
if(coupon.getIsUsed().intValue()==ShopCode.SHOP_COUPON_ISUSED.getCode().intValue()){
CastException.cast(ShopCode.SHOP_COUPON_ISUSED);
}
order.setCouponPaid(coupon.getCouponPrice());
}else{
order.setCouponPaid(BigDecimal.ZERO);
}
//7.核算订单支付金额 订单总金额-余额-优惠券金额
BigDecimal payAmount = order.getOrderAmount().subtract(order.getMoneyPaid()).subtract(order.getCouponPaid());
order.setPayAmount(payAmount);
//8.设置下单时间
order.setAddTime(new Date());
//9.保存订单到数据库
orderMapper.insert(order);
//10.返回订单ID
return orderId;
}
/**
* 核算运费
* @param orderAmount
* @return
*/
private BigDecimal calculateShippingFee(BigDecimal orderAmount) {
if(orderAmount.compareTo(new BigDecimal(100))==1){
return BigDecimal.ZERO;
}else{
return new BigDecimal(10);
}
}
/**
* 校验订单
*
* @param order
*/
private void checkOrder(TradeOrder order) {
//1.校验订单是否存在
if (order == null) {
CastException.cast(ShopCode.SHOP_ORDER_INVALID);
}
//2.校验订单中的商品是否存在
TradeGoods goods = goodsService.findOne(order.getGoodsId());
if (goods == null) {
CastException.cast(ShopCode.SHOP_GOODS_NO_EXIST);
}
//3.校验下单用户是否存在
TradeUser user = userService.findOne(order.getUserId());
if (user == null) {
CastException.cast(ShopCode.SHOP_USER_NO_EXIST);
}
//4.校验商品单价是否合法
if (order.getGoodsPrice().compareTo(goods.getGoodsPrice()) != 0) {
CastException.cast(ShopCode.SHOP_GOODS_PRICE_INVALID);
}
//5.校验订单商品数量是否合法
if (order.getGoodsNumber() >= goods.getGoodsNumber()) {
CastException.cast(ShopCode.SHOP_GOODS_NUM_NOT_ENOUGH);
}
log.info("校验订单通过");
}
}
六、RocketMQ 实战:模拟电商网站场景综合案例–扣减库存代码实现2
1、修改 shop-api 工程模块中的 IGoodsService.java 接口类,添加 扣减库存 的方法。
package com.itheima.api;
import com.itheima.entity.Result;
import com.itheima.shop.pojo.TradeGoods;
import com.itheima.shop.pojo.TradeGoodsNumberLog;
public interface IGoodsService {
/**
* 根据ID查询商品对象
* @param goodsId
* @return
*/
TradeGoods findOne(Long goodsId);
/**
* 扣减库存
* @param goodsNumberLog
* @return
*/
Result reduceGoodsNum(TradeGoodsNumberLog goodsNumberLog);
}
2、在 shop-goods-service 工程模块中的 GoodsServiceImpl.java 实现类,添加 扣减库存 的方法。
package com.itheima.shop.service.impl;
import com.alibaba.dubbo.config.annotation.Service;
import com.itheima.api.IGoodsService;
import com.itheima.constant.ShopCode;
import com.itheima.entity.Result;
import com.itheima.exception.CastException;
import com.itheima.shop.mapper.TradeGoodsMapper;
import com.itheima.shop.mapper.TradeGoodsNumberLogMapper;
import com.itheima.shop.pojo.TradeGoods;
import com.itheima.shop.pojo.TradeGoodsNumberLog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
@Service(interfaceClass = IGoodsService.class)
public class GoodsServiceImpl implements IGoodsService {
@Autowired
private TradeGoodsMapper goodsMapper;
@Autowired
private TradeGoodsNumberLogMapper goodsNumberLogMapper;
@Override
public TradeGoods findOne(Long goodsId) {
if (goodsId == null) {
CastException.cast(ShopCode.SHOP_REQUEST_PARAMETER_VALID);
}
return goodsMapper.selectByPrimaryKey(goodsId);
}
@Override //扣减库存
public Result reduceGoodsNum(TradeGoodsNumberLog goodsNumberLog) {
if (goodsNumberLog == null ||
goodsNumberLog.getGoodsNumber() == null ||
goodsNumberLog.getOrderId() == null ||
goodsNumberLog.getGoodsNumber() == null ||
goodsNumberLog.getGoodsNumber().intValue() <= 0) {
CastException.cast(ShopCode.SHOP_REQUEST_PARAMETER_VALID);
}
TradeGoods goods = goodsMapper.selectByPrimaryKey(goodsNumberLog.getGoodsId());
if(goods.getGoodsNumber()<goodsNumberLog.getGoodsNumber()){
//库存不足
CastException.cast(ShopCode.SHOP_GOODS_NUM_NOT_ENOUGH);
}
//减库存
goods.setGoodsNumber(goods.getGoodsNumber()-goodsNumberLog.getGoodsNumber());
goodsMapper.updateByPrimaryKey(goods);
//记录库存操作日志
goodsNumberLog.setGoodsNumber(-(goodsNumberLog.getGoodsNumber()));
goodsNumberLog.setLogTime(new Date());
goodsNumberLogMapper.insert(goodsNumberLog);
return new Result(ShopCode.SHOP_SUCCESS.getSuccess(),ShopCode.SHOP_SUCCESS.getMessage());
}
}
七、RocketMQ 实战:模拟电商网站场景综合案例–更新优惠券
1、扣减优惠券 :通过 dubbo 完成扣减优惠券。
/**
* 使用优惠券
* @param order
*/
private void updateCouponStatus(TradeOrder order) {
if(order.getCouponId()!=null){
TradeCoupon coupon = couponService.findOne(order.getCouponId());
coupon.setOrderId(order.getOrderId());
coupon.setIsUsed(ShopCode.SHOP_COUPON_ISUSED.getCode());
coupon.setUsedTime(new Date());
//更新优惠券状态
Result result = couponService.updateCouponStatus(coupon);
if(result.getSuccess().equals(ShopCode.SHOP_FAIL.getSuccess())){
CastException.cast(ShopCode.SHOP_COUPON_USE_FAIL);
}
log.info("订单:"+order.getOrderId()+",使用优惠券");
}
}
2、在 shop-api 工程模块中,修改 ICouponService 接口类,添加 更新优惠券 方法。
package com.itheima.api;
import com.itheima.entity.Result;
import com.itheima.shop.pojo.TradeCoupon;
/**
* 优惠券接口
*/
public interface ICouponService {
/**
* 根据ID查询优惠券对象
* @param coupouId
* @return
*/
public TradeCoupon findOne(Long coupouId);
/**
* 更细优惠券状态
* @param coupon
* @return
*/
Result updateCouponStatus(TradeCoupon coupon);
}
3、在 shop-coupon-service 工程模块中,创建 CouponServiceImpl.java 实现类。
package com.itheima.shop.service.impl;
import com.alibaba.dubbo.config.annotation.Service;
import com.itheima.api.ICouponService;
import com.itheima.constant.ShopCode;
import com.itheima.entity.Result;
import com.itheima.exception.CastException;
import com.itheima.shop.mapper.TradeCouponMapper;
import com.itheima.shop.pojo.TradeCoupon;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@Service(interfaceClass = ICouponService.class)
public class CouponServiceImpl implements ICouponService{
@Autowired
private TradeCouponMapper couponMapper;
@Override
public TradeCoupon findOne(Long coupouId) {
if(coupouId==null){
CastException.cast(ShopCode.SHOP_REQUEST_PARAMETER_VALID);
}
return couponMapper.selectByPrimaryKey(coupouId);
}
@Override
public Result updateCouponStatus(TradeCoupon coupon) {
if(coupon==null||coupon.getCouponId()==null){
CastException.cast(ShopCode.SHOP_REQUEST_PARAMETER_VALID);
}
//更新优惠券状态
couponMapper.updateByPrimaryKey(coupon);
return new Result(ShopCode.SHOP_SUCCESS.getSuccess(),ShopCode.SHOP_SUCCESS.getMessage());
}
}
4、在 shop-order-service 工程模块中,修改 OrderServiceImpl.java 下单业务类,完成 更新优惠券状态 方法。
package com.itheima.shop.service;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.itheima.api.ICouponService;
import com.itheima.api.IGoodsService;
import com.itheima.api.IOrderService;
import com.itheima.api.IUserService;
import com.itheima.constant.ShopCode;
import com.itheima.entity.MQEntity;
import com.itheima.entity.Result;
import com.itheima.exception.CastException;
import com.itheima.shop.mapper.TradeOrderMapper;
import com.itheima.shop.pojo.*;
import com.itheima.utils.IDWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.Date;
@Slf4j
@Component
@Service(interfaceClass = IOrderService.class)
public class OrderServiceImpl implements IOrderService {
@Reference
private IGoodsService goodsService;
@Reference
private IUserService userService;
@Reference
private ICouponService couponService;
@Autowired
private TradeOrderMapper orderMapper;
@Autowired
private IDWorker idWorker;
@Autowired
private RocketMQTemplate rocketMQTemplate;
@Override
public Result confirmOrder(TradeOrder order) {
//1.校验订单
checkOrder(order);
//2.生成预订单
Long orderId = savePreOrder(order);
try {
//3.扣减库存
reduceGoodsNum(order);
//4.扣减优惠券
updateCouponStatus(order);
//5.使用余额
//6.确认订单
//7.返回成功状态
} catch (Exception e) {
//1.确认订单失败,发送消息
//2.返回订单确认失败消息
}
}
/**
* 使用优惠券
* @param order
*/
private void updateCouponStatus(TradeOrder order) {
if(order.getCouponId()!=null){
TradeCoupon coupon = couponService.findOne(order.getCouponId());
coupon.setOrderId(order.getOrderId());
coupon.setIsUsed(ShopCode.SHOP_COUPON_ISUSED.getCode());
coupon.setUsedTime(new Date());
//更新优惠券状态
Result result = couponService.updateCouponStatus(coupon);
if(result.getSuccess().equals(ShopCode.SHOP_FAIL.getSuccess())){
CastException.cast(ShopCode.SHOP_COUPON_USE_FAIL);
}
log.info("订单:"+order.getOrderId()+",使用优惠券");
}
}
/**
* 扣减库存
* @param order
*/
private void reduceGoodsNum(TradeOrder order) {
//订单ID,商品ID,商品数量
TradeGoodsNumberLog goodsNumberLog = new TradeGoodsNumberLog();
goodsNumberLog.setOrderId(order.getOrderId());
goodsNumberLog.setGoodsId(order.getGoodsId());
goodsNumberLog.setGoodsNumber(order.getGoodsNumber());
Result result = goodsService.reduceGoodsNum(goodsNumberLog);
if(result.getSuccess().equals(ShopCode.SHOP_FAIL.getSuccess())){
CastException.cast(ShopCode.SHOP_REDUCE_GOODS_NUM_FAIL);
}
log.info("订单:"+order.getOrderId()+"扣减库存成功");
}
/**
* 生成预订单
*
* @param order
* @return
*/
private Long savePreOrder(TradeOrder order) {
//1. 设置订单状态为不可见
order.setOrderStatus(ShopCode.SHOP_ORDER_NO_CONFIRM.getCode());
//2. 设置订单ID
long orderId = idWorker.nextId();
order.setOrderId(orderId);
//3. 核算订单运费
BigDecimal shippingFee = calculateShippingFee(order.getOrderAmount());
if(order.getShippingFee().compareTo(shippingFee)!=0){
CastException.cast(ShopCode.SHOP_ORDER_SHIPPINGFEE_INVALID);
}
//4. 核算订单总金额是否合法
BigDecimal orderAmount = order.getGoodsPrice().multiply(new BigDecimal(order.getGoodsNumber()));
orderAmount.add(shippingFee);
if(order.getOrderAmount().compareTo(orderAmount)!=0){
CastException.cast(ShopCode.SHOP_ORDERAMOUNT_INVALID);
}
//5.判断用户是否使用余额
BigDecimal moneyPaid = order.getMoneyPaid();
if(moneyPaid!=null){
//5.1 订单中余额是否合法
int r = moneyPaid.compareTo(BigDecimal.ZERO);
//余额小于0
if(r==-1){
CastException.cast(ShopCode.SHOP_MONEY_PAID_LESS_ZERO);
}
//余额大于0
if(r==1){
TradeUser user = userService.findOne(order.getUserId());
if(moneyPaid.compareTo(new BigDecimal(user.getUserMoney()))==1){
CastException.cast(ShopCode.SHOP_MONEY_PAID_INVALID);
}
}
}else{
order.setMoneyPaid(BigDecimal.ZERO);
}
//6.判断用户是否使用优惠券
Long couponId = order.getCouponId();
if(couponId!=null){
TradeCoupon coupon = couponService.findOne(couponId);
//6.1 判断优惠券是否存在
if(coupon==null){
CastException.cast(ShopCode.SHOP_COUPON_NO_EXIST);
}
//6.2 判断优惠券是否已经被使用
if(coupon.getIsUsed().intValue()==ShopCode.SHOP_COUPON_ISUSED.getCode().intValue()){
CastException.cast(ShopCode.SHOP_COUPON_ISUSED);
}
order.setCouponPaid(coupon.getCouponPrice());
}else{
order.setCouponPaid(BigDecimal.ZERO);
}
//7.核算订单支付金额 订单总金额-余额-优惠券金额
BigDecimal payAmount = order.getOrderAmount().subtract(order.getMoneyPaid()).subtract(order.getCouponPaid());
order.setPayAmount(payAmount);
//8.设置下单时间
order.setAddTime(new Date());
//9.保存订单到数据库
orderMapper.insert(order);
//10.返回订单ID
return orderId;
}
/**
* 核算运费
* @param orderAmount
* @return
*/
private BigDecimal calculateShippingFee(BigDecimal orderAmount) {
if(orderAmount.compareTo(new BigDecimal(100))==1){
return BigDecimal.ZERO;
}else{
return new BigDecimal(10);
}
}
/**
* 校验订单
*
* @param order
*/
private void checkOrder(TradeOrder order) {
//1.校验订单是否存在
if (order == null) {
CastException.cast(ShopCode.SHOP_ORDER_INVALID);
}
//2.校验订单中的商品是否存在
TradeGoods goods = goodsService.findOne(order.getGoodsId());
if (goods == null) {
CastException.cast(ShopCode.SHOP_GOODS_NO_EXIST);
}
//3.校验下单用户是否存在
TradeUser user = userService.findOne(order.getUserId());
if (user == null) {
CastException.cast(ShopCode.SHOP_USER_NO_EXIST);
}
//4.校验商品单价是否合法
if (order.getGoodsPrice().compareTo(goods.getGoodsPrice()) != 0) {
CastException.cast(ShopCode.SHOP_GOODS_PRICE_INVALID);
}
//5.校验订单商品数量是否合法
if (order.getGoodsNumber() >= goods.getGoodsNumber()) {
CastException.cast(ShopCode.SHOP_GOODS_NUM_NOT_ENOUGH);
}
log.info("校验订单通过");
}
}
八、RocketMQ 实战:模拟电商网站场景综合案例–更新余额1
1、扣减用户余额 :通过用户服务完成扣减余额。
/**
* 扣减余额
* @param order
*/
private void reduceMoneyPaid(TradeOrder order) {
if(order.getMoneyPaid()!=null && order.getMoneyPaid().compareTo(BigDecimal.ZERO)==1){
TradeUserMoneyLog userMoneyLog = new TradeUserMoneyLog();
userMoneyLog.setOrderId(order.getOrderId());
userMoneyLog.setUserId(order.getUserId());
userMoneyLog.setUseMoney(order.getMoneyPaid());
userMoneyLog.setMoneyLogType(ShopCode.SHOP_USER_MONEY_PAID.getCode());
Result result = userService.updateMoneyPaid(userMoneyLog);
if(result.getSuccess().equals(ShopCode.SHOP_FAIL.getSuccess())){
CastException.cast(ShopCode.SHOP_USER_MONEY_REDUCE_FAIL);
}
log.info("订单:"+order.getOrderId()+",扣减余额成功");
}
}
2、在 shop-order-service 工程模块中,修改 OrderServiceImpl.java 下单业务类,添加 扣减余额 方法。
package com.itheima.shop.service;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.itheima.api.ICouponService;
import com.itheima.api.IGoodsService;
import com.itheima.api.IOrderService;
import com.itheima.api.IUserService;
import com.itheima.constant.ShopCode;
import com.itheima.entity.MQEntity;
import com.itheima.entity.Result;
import com.itheima.exception.CastException;
import com.itheima.shop.mapper.TradeOrderMapper;
import com.itheima.shop.pojo.*;
import com.itheima.utils.IDWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.Date;
@Slf4j
@Component
@Service(interfaceClass = IOrderService.class)
public class OrderServiceImpl implements IOrderService {
@Reference
private IGoodsService goodsService;
@Reference
private IUserService userService;
@Reference
private ICouponService couponService;
@Autowired
private TradeOrderMapper orderMapper;
@Autowired
private IDWorker idWorker;
@Autowired
private RocketMQTemplate rocketMQTemplate;
@Override
public Result confirmOrder(TradeOrder order) {
//1.校验订单
checkOrder(order);
//2.生成预订单
Long orderId = savePreOrder(order);
try {
//3.扣减库存
reduceGoodsNum(order);
//4.扣减优惠券
updateCouponStatus(order);
//5.使用余额
reduceMoneyPaid(order);
//6.确认订单
//7.返回成功状态
} catch (Exception e) {
//1.确认订单失败,发送消息
//2.返回订单确认失败消息
}
}
/**
* 扣减余额
* @param order
*/
private void reduceMoneyPaid(TradeOrder order) {
if(order.getMoneyPaid()!=null && order.getMoneyPaid().compareTo(BigDecimal.ZERO)==1){
TradeUserMoneyLog userMoneyLog = new TradeUserMoneyLog();
userMoneyLog.setOrderId(order.getOrderId());
userMoneyLog.setUserId(order.getUserId());
userMoneyLog.setUseMoney(order.getMoneyPaid());
userMoneyLog.setMoneyLogType(ShopCode.SHOP_USER_MONEY_PAID.getCode());
Result result = userService.updateMoneyPaid(userMoneyLog);
if(result.getSuccess().equals(ShopCode.SHOP_FAIL.getSuccess())){
CastException.cast(ShopCode.SHOP_USER_MONEY_REDUCE_FAIL);
}
log.info("订单:"+order.getOrderId()+",扣减余额成功");
}
}
/**
* 使用优惠券
* @param order
*/
private void updateCouponStatus(TradeOrder order) {
if(order.getCouponId()!=null){
TradeCoupon coupon = couponService.findOne(order.getCouponId());
coupon.setOrderId(order.getOrderId());
coupon.setIsUsed(ShopCode.SHOP_COUPON_ISUSED.getCode());
coupon.setUsedTime(new Date());
//更新优惠券状态
Result result = couponService.updateCouponStatus(coupon);
if(result.getSuccess().equals(ShopCode.SHOP_FAIL.getSuccess())){
CastException.cast(ShopCode.SHOP_COUPON_USE_FAIL);
}
log.info("订单:"+order.getOrderId()+",使用优惠券");
}
}
/**
* 扣减库存
* @param order
*/
private void reduceGoodsNum(TradeOrder order) {
//订单ID,商品ID,商品数量
TradeGoodsNumberLog goodsNumberLog = new TradeGoodsNumberLog();
goodsNumberLog.setOrderId(order.getOrderId());
goodsNumberLog.setGoodsId(order.getGoodsId());
goodsNumberLog.setGoodsNumber(order.getGoodsNumber());
Result result = goodsService.reduceGoodsNum(goodsNumberLog);
if(result.getSuccess().equals(ShopCode.SHOP_FAIL.getSuccess())){
CastException.cast(ShopCode.SHOP_REDUCE_GOODS_NUM_FAIL);
}
log.info("订单:"+order.getOrderId()+"扣减库存成功");
}
/**
* 生成预订单
*
* @param order
* @return
*/
private Long savePreOrder(TradeOrder order) {
//1. 设置订单状态为不可见
order.setOrderStatus(ShopCode.SHOP_ORDER_NO_CONFIRM.getCode());
//2. 设置订单ID
long orderId = idWorker.nextId();
order.setOrderId(orderId);
//3. 核算订单运费
BigDecimal shippingFee = calculateShippingFee(order.getOrderAmount());
if(order.getShippingFee().compareTo(shippingFee)!=0){
CastException.cast(ShopCode.SHOP_ORDER_SHIPPINGFEE_INVALID);
}
//4. 核算订单总金额是否合法
BigDecimal orderAmount = order.getGoodsPrice().multiply(new BigDecimal(order.getGoodsNumber()));
orderAmount.add(shippingFee);
if(order.getOrderAmount().compareTo(orderAmount)!=0){
CastException.cast(ShopCode.SHOP_ORDERAMOUNT_INVALID);
}
//5.判断用户是否使用余额
BigDecimal moneyPaid = order.getMoneyPaid();
if(moneyPaid!=null){
//5.1 订单中余额是否合法
int r = moneyPaid.compareTo(BigDecimal.ZERO);
//余额小于0
if(r==-1){
CastException.cast(ShopCode.SHOP_MONEY_PAID_LESS_ZERO);
}
//余额大于0
if(r==1){
TradeUser user = userService.findOne(order.getUserId());
if(moneyPaid.compareTo(new BigDecimal(user.getUserMoney()))==1){
CastException.cast(ShopCode.SHOP_MONEY_PAID_INVALID);
}
}
}else{
order.setMoneyPaid(BigDecimal.ZERO);
}
//6.判断用户是否使用优惠券
Long couponId = order.getCouponId();
if(couponId!=null){
TradeCoupon coupon = couponService.findOne(couponId);
//6.1 判断优惠券是否存在
if(coupon==null){
CastException.cast(ShopCode.SHOP_COUPON_NO_EXIST);
}
//6.2 判断优惠券是否已经被使用
if(coupon.getIsUsed().intValue()==ShopCode.SHOP_COUPON_ISUSED.getCode().intValue()){
CastException.cast(ShopCode.SHOP_COUPON_ISUSED);
}
order.setCouponPaid(coupon.getCouponPrice());
}else{
order.setCouponPaid(BigDecimal.ZERO);
}
//7.核算订单支付金额 订单总金额-余额-优惠券金额
BigDecimal payAmount = order.getOrderAmount().subtract(order.getMoneyPaid()).subtract(order.getCouponPaid());
order.setPayAmount(payAmount);
//8.设置下单时间
order.setAddTime(new Date());
//9.保存订单到数据库
orderMapper.insert(order);
//10.返回订单ID
return orderId;
}
/**
* 核算运费
* @param orderAmount
* @return
*/
private BigDecimal calculateShippingFee(BigDecimal orderAmount) {
if(orderAmount.compareTo(new BigDecimal(100))==1){
return BigDecimal.ZERO;
}else{
return new BigDecimal(10);
}
}
/**
* 校验订单
*
* @param order
*/
private void checkOrder(TradeOrder order) {
//1.校验订单是否存在
if (order == null) {
CastException.cast(ShopCode.SHOP_ORDER_INVALID);
}
//2.校验订单中的商品是否存在
TradeGoods goods = goodsService.findOne(order.getGoodsId());
if (goods == null) {
CastException.cast(ShopCode.SHOP_GOODS_NO_EXIST);
}
//3.校验下单用户是否存在
TradeUser user = userService.findOne(order.getUserId());
if (user == null) {
CastException.cast(ShopCode.SHOP_USER_NO_EXIST);
}
//4.校验商品单价是否合法
if (order.getGoodsPrice().compareTo(goods.getGoodsPrice()) != 0) {
CastException.cast(ShopCode.SHOP_GOODS_PRICE_INVALID);
}
//5.校验订单商品数量是否合法
if (order.getGoodsNumber() >= goods.getGoodsNumber()) {
CastException.cast(ShopCode.SHOP_GOODS_NUM_NOT_ENOUGH);
}
log.info("校验订单通过");
}
}
3、在 shop-api 工程模块中,创建 IUserService.java 接口类,添加 更新余额 方法。
package com.itheima.api;
import com.itheima.entity.Result;
import com.itheima.shop.pojo.TradeUser;
import com.itheima.shop.pojo.TradeUserMoneyLog;
public interface IUserService {
TradeUser findOne(Long userId);
//更新余额
Result updateMoneyPaid(TradeUserMoneyLog userMoneyLog);
}
九、RocketMQ 实战:模拟电商网站场景综合案例–更新余额2
1、更新余额分析:
2、在 shop-user-service 工程模块中,修改 UserServiceImpl.java 类,添加 更新余额 的方法。
package com.itheima.shop.service.impl;
import com.alibaba.dubbo.config.annotation.Service;
import com.itheima.api.IUserService;
import com.itheima.constant.ShopCode;
import com.itheima.entity.Result;
import com.itheima.exception.CastException;
import com.itheima.shop.mapper.TradeUserMapper;
import com.itheima.shop.mapper.TradeUserMoneyLogMapper;
import com.itheima.shop.pojo.TradeUser;
import com.itheima.shop.pojo.TradeUserMoneyLog;
import com.itheima.shop.pojo.TradeUserMoneyLogExample;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.Date;
@Component
@Service(interfaceClass = IUserService.class)
public class UserServiceImpl implements IUserService{
@Autowired
private TradeUserMapper userMapper;
@Autowired
private TradeUserMoneyLogMapper userMoneyLogMapper;
@Override
public TradeUser findOne(Long userId) {
if(userId==null){
CastException.cast(ShopCode.SHOP_REQUEST_PARAMETER_VALID);
}
return userMapper.selectByPrimaryKey(userId);
}
//更新余额
@Override
public Result updateMoneyPaid(TradeUserMoneyLog userMoneyLog) {
//1.校验参数是否合法
if(userMoneyLog==null ||
userMoneyLog.getUserId()==null ||
userMoneyLog.getOrderId()==null ||
userMoneyLog.getUseMoney()==null||
userMoneyLog.getUseMoney().compareTo(BigDecimal.ZERO)<=0){
CastException.cast(ShopCode.SHOP_REQUEST_PARAMETER_VALID);
}
//2.查询订单余额使用日志
TradeUserMoneyLogExample userMoneyLogExample = new TradeUserMoneyLogExample();
TradeUserMoneyLogExample.Criteria criteria = userMoneyLogExample.createCriteria();
criteria.andOrderIdEqualTo(userMoneyLog.getOrderId());
criteria.andUserIdEqualTo(userMoneyLog.getUserId());
int r = userMoneyLogMapper.countByExample(userMoneyLogExample);
TradeUser tradeUser = userMapper.selectByPrimaryKey(userMoneyLog.getUserId());
//3.扣减余额...
if(userMoneyLog.getMoneyLogType().intValue()==ShopCode.SHOP_USER_MONEY_PAID.getCode().intValue()){
if(r>0){
//已经付款
CastException.cast(ShopCode.SHOP_ORDER_PAY_STATUS_IS_PAY);
}
//减余额
tradeUser.setUserMoney(new BigDecimal(tradeUser.getUserMoney()).subtract(userMoneyLog.getUseMoney()).longValue());
userMapper.updateByPrimaryKey(tradeUser);
}
//4.回退余额...
if(userMoneyLog.getMoneyLogType().intValue()==ShopCode.SHOP_USER_MONEY_REFUND.getCode().intValue()){
if(r<0){
//如果没有支付,则不能回退余额
CastException.cast(ShopCode.SHOP_ORDER_PAY_STATUS_NO_PAY);
}
//防止多次退款
TradeUserMoneyLogExample userMoneyLogExample2 = new TradeUserMoneyLogExample();
TradeUserMoneyLogExample.Criteria criteria1 = userMoneyLogExample2.createCriteria();
criteria1.andOrderIdEqualTo(userMoneyLog.getOrderId());
criteria1.andUserIdEqualTo(userMoneyLog.getUserId());
criteria1.andMoneyLogTypeEqualTo(ShopCode.SHOP_USER_MONEY_REFUND.getCode());
int r2 = userMoneyLogMapper.countByExample(userMoneyLogExample2);
if(r2>0){
CastException.cast(ShopCode.SHOP_USER_MONEY_REFUND_ALREADY);
}
//退款
tradeUser.setUserMoney(new BigDecimal(tradeUser.getUserMoney()).add(userMoneyLog.getUseMoney()).longValue());
userMapper.updateByPrimaryKey(tradeUser);
}
//5.记录订单余额使用日志
userMoneyLog.setCreateTime(new Date());
userMoneyLogMapper.insert(userMoneyLog);
return new Result(ShopCode.SHOP_SUCCESS.getSuccess(),ShopCode.SHOP_SUCCESS.getMessage());
}
}
十、RocketMQ 实战:模拟电商网站场景综合案例–下单基本流程小结
1、完成 确认订单 的方法:private void updateOrderStatus(TradeOrder order) { … }
/**
* 确认订单
* @param order
*/
private void updateOrderStatus(TradeOrder order) {
order.setOrderStatus(ShopCode.SHOP_ORDER_CONFIRM.getCode());
order.setPayStatus(ShopCode.SHOP_ORDER_PAY_STATUS_NO_PAY.getCode());
order.setConfirmTime(new Date());
int r = orderMapper.updateByPrimaryKey(order);
if(r<=0){
CastException.cast(ShopCode.SHOP_ORDER_CONFIRM_FAIL);
}
log.info("订单:"+order.getOrderId()+"确认订单成功");
}
2、在 shop-order-service 工程模块中,修改 OrderServiceImpl.java 类,添加 确认订单 的方法。
package com.itheima.shop.service;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.itheima.api.ICouponService;
import com.itheima.api.IGoodsService;
import com.itheima.api.IOrderService;
import com.itheima.api.IUserService;
import com.itheima.constant.ShopCode;
import com.itheima.entity.MQEntity;
import com.itheima.entity.Result;
import com.itheima.exception.CastException;
import com.itheima.shop.mapper.TradeOrderMapper;
import com.itheima.shop.pojo.*;
import com.itheima.utils.IDWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.Date;
@Slf4j
@Component
@Service(interfaceClass = IOrderService.class)
public class OrderServiceImpl implements IOrderService {
@Reference
private IGoodsService goodsService;
@Reference
private IUserService userService;
@Reference
private ICouponService couponService;
@Value("${mq.order.topic}")
private String topic;
@Value("${mq.order.tag.cancel}")
private String tag;
@Autowired
private TradeOrderMapper orderMapper;
@Autowired
private IDWorker idWorker;
@Autowired
private RocketMQTemplate rocketMQTemplate;
@Override
public Result confirmOrder(TradeOrder order) {
//1.校验订单
checkOrder(order);
//2.生成预订单
Long orderId = savePreOrder(order);
try {
//3.扣减库存
reduceGoodsNum(order);
//4.扣减优惠券
updateCouponStatus(order);
//5.使用余额
reduceMoneyPaid(order);
//模拟异常抛出
//CastException.cast(ShopCode.SHOP_FAIL);
//6.确认订单
updateOrderStatus(order);
//7.返回成功状态
return new Result(ShopCode.SHOP_SUCCESS.getSuccess(),ShopCode.SHOP_SUCCESS.getMessage());
} catch (Exception e) {
//1.确认订单失败,发送消息
//2.返回订单确认失败消息
}
}
/**
* 确认订单
* @param order
*/
private void updateOrderStatus(TradeOrder order) {
order.setOrderStatus(ShopCode.SHOP_ORDER_CONFIRM.getCode());
order.setPayStatus(ShopCode.SHOP_ORDER_PAY_STATUS_NO_PAY.getCode());
order.setConfirmTime(new Date());
int r = orderMapper.updateByPrimaryKey(order);
if(r<=0){
CastException.cast(ShopCode.SHOP_ORDER_CONFIRM_FAIL);
}
log.info("订单:"+order.getOrderId()+"确认订单成功");
}
/**
* 扣减余额
* @param order
*/
private void reduceMoneyPaid(TradeOrder order) {
if(order.getMoneyPaid()!=null && order.getMoneyPaid().compareTo(BigDecimal.ZERO)==1){
TradeUserMoneyLog userMoneyLog = new TradeUserMoneyLog();
userMoneyLog.setOrderId(order.getOrderId());
userMoneyLog.setUserId(order.getUserId());
userMoneyLog.setUseMoney(order.getMoneyPaid());
userMoneyLog.setMoneyLogType(ShopCode.SHOP_USER_MONEY_PAID.getCode());
Result result = userService.updateMoneyPaid(userMoneyLog);
if(result.getSuccess().equals(ShopCode.SHOP_FAIL.getSuccess())){
CastException.cast(ShopCode.SHOP_USER_MONEY_REDUCE_FAIL);
}
log.info("订单:"+order.getOrderId()+",扣减余额成功");
}
}
/**
* 使用优惠券
* @param order
*/
private void updateCouponStatus(TradeOrder order) {
if(order.getCouponId()!=null){
TradeCoupon coupon = couponService.findOne(order.getCouponId());
coupon.setOrderId(order.getOrderId());
coupon.setIsUsed(ShopCode.SHOP_COUPON_ISUSED.getCode());
coupon.setUsedTime(new Date());
//更新优惠券状态
Result result = couponService.updateCouponStatus(coupon);
if(result.getSuccess().equals(ShopCode.SHOP_FAIL.getSuccess())){
CastException.cast(ShopCode.SHOP_COUPON_USE_FAIL);
}
log.info("订单:"+order.getOrderId()+",使用优惠券");
}
}
/**
* 扣减库存
* @param order
*/
private void reduceGoodsNum(TradeOrder order) {
//订单ID,商品ID,商品数量
TradeGoodsNumberLog goodsNumberLog = new TradeGoodsNumberLog();
goodsNumberLog.setOrderId(order.getOrderId());
goodsNumberLog.setGoodsId(order.getGoodsId());
goodsNumberLog.setGoodsNumber(order.getGoodsNumber());
Result result = goodsService.reduceGoodsNum(goodsNumberLog);
if(result.getSuccess().equals(ShopCode.SHOP_FAIL.getSuccess())){
CastException.cast(ShopCode.SHOP_REDUCE_GOODS_NUM_FAIL);
}
log.info("订单:"+order.getOrderId()+"扣减库存成功");
}
/**
* 生成预订单
*
* @param order
* @return
*/
private Long savePreOrder(TradeOrder order) {
//1. 设置订单状态为不可见
order.setOrderStatus(ShopCode.SHOP_ORDER_NO_CONFIRM.getCode());
//2. 设置订单ID
long orderId = idWorker.nextId();
order.setOrderId(orderId);
//3. 核算订单运费
BigDecimal shippingFee = calculateShippingFee(order.getOrderAmount());
if(order.getShippingFee().compareTo(shippingFee)!=0){
CastException.cast(ShopCode.SHOP_ORDER_SHIPPINGFEE_INVALID);
}
//4. 核算订单总金额是否合法
BigDecimal orderAmount = order.getGoodsPrice().multiply(new BigDecimal(order.getGoodsNumber()));
orderAmount.add(shippingFee);
if(order.getOrderAmount().compareTo(orderAmount)!=0){
CastException.cast(ShopCode.SHOP_ORDERAMOUNT_INVALID);
}
//5.判断用户是否使用余额
BigDecimal moneyPaid = order.getMoneyPaid();
if(moneyPaid!=null){
//5.1 订单中余额是否合法
int r = moneyPaid.compareTo(BigDecimal.ZERO);
//余额小于0
if(r==-1){
CastException.cast(ShopCode.SHOP_MONEY_PAID_LESS_ZERO);
}
//余额大于0
if(r==1){
TradeUser user = userService.findOne(order.getUserId());
if(moneyPaid.compareTo(new BigDecimal(user.getUserMoney()))==1){
CastException.cast(ShopCode.SHOP_MONEY_PAID_INVALID);
}
}
}else{
order.setMoneyPaid(BigDecimal.ZERO);
}
//6.判断用户是否使用优惠券
Long couponId = order.getCouponId();
if(couponId!=null){
TradeCoupon coupon = couponService.findOne(couponId);
//6.1 判断优惠券是否存在
if(coupon==null){
CastException.cast(ShopCode.SHOP_COUPON_NO_EXIST);
}
//6.2 判断优惠券是否已经被使用
if(coupon.getIsUsed().intValue()==ShopCode.SHOP_COUPON_ISUSED.getCode().intValue()){
CastException.cast(ShopCode.SHOP_COUPON_ISUSED);
}
order.setCouponPaid(coupon.getCouponPrice());
}else{
order.setCouponPaid(BigDecimal.ZERO);
}
//7.核算订单支付金额 订单总金额-余额-优惠券金额
BigDecimal payAmount = order.getOrderAmount().subtract(order.getMoneyPaid()).subtract(order.getCouponPaid());
order.setPayAmount(payAmount);
//8.设置下单时间
order.setAddTime(new Date());
//9.保存订单到数据库
orderMapper.insert(order);
//10.返回订单ID
return orderId;
}
/**
* 核算运费
* @param orderAmount
* @return
*/
private BigDecimal calculateShippingFee(BigDecimal orderAmount) {
if(orderAmount.compareTo(new BigDecimal(100))==1){
return BigDecimal.ZERO;
}else{
return new BigDecimal(10);
}
}
/**
* 校验订单
*
* @param order
*/
private void checkOrder(TradeOrder order) {
//1.校验订单是否存在
if (order == null) {
CastException.cast(ShopCode.SHOP_ORDER_INVALID);
}
//2.校验订单中的商品是否存在
TradeGoods goods = goodsService.findOne(order.getGoodsId());
if (goods == null) {
CastException.cast(ShopCode.SHOP_GOODS_NO_EXIST);
}
//3.校验下单用户是否存在
TradeUser user = userService.findOne(order.getUserId());
if (user == null) {
CastException.cast(ShopCode.SHOP_USER_NO_EXIST);
}
//4.校验商品单价是否合法
if (order.getGoodsPrice().compareTo(goods.getGoodsPrice()) != 0) {
CastException.cast(ShopCode.SHOP_GOODS_PRICE_INVALID);
}
//5.校验订单商品数量是否合法
if (order.getGoodsNumber() >= goods.getGoodsNumber()) {
CastException.cast(ShopCode.SHOP_GOODS_NUM_NOT_ENOUGH);
}
log.info("校验订单通过");
}
}
十一、RocketMQ 实战:模拟电商网站场景综合案例–SpringBoot 集成 JUNIT 测试下单基本流程
1、在 shop-coupon-service 工程模块中,创建 启动类 CouponServiceApplication.java
package com.itheima.shop;
import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableDubboConfiguration
public class CouponServiceApplication {
public static void main(String[] args) {
SpringApplication.run(CouponServiceApplication.class,args);
}
}
2、在 shop-goods-service 工程模块中,创建 启动类 GoodsServiceApplication.java
package com.itheima.shop;
import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableDubboConfiguration
public class GoodsServiceApplication {
public static void main(String[] args) {
SpringApplication.run(GoodsServiceApplication.class,args);
}
}
3、在 shop-order-service 工程模块中,创建 启动类 OrderServiceApplication.java
package com.itheima.shop;
import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import com.itheima.utils.IDWorker;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
@EnableDubboConfiguration
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class,args);
}
@Bean
public IDWorker getBean(){
return new IDWorker(1,1);
}
}
4、在 shop-user-service 工程模块中,创建 启动类 UserServiceApplication.java
package com.itheima.shop;
import com.alibaba.dubbo.spring.boot.annotation.EnableDubboConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableDubboConfiguration
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class,args);
}
}
5、在 shop-order-service 工程模块中,完善 OrderServiceImpl.java 类。
package com.itheima.shop.service;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.itheima.api.ICouponService;
import com.itheima.api.IGoodsService;
import com.itheima.api.IOrderService;
import com.itheima.api.IUserService;
import com.itheima.constant.ShopCode;
import com.itheima.entity.MQEntity;
import com.itheima.entity.Result;
import com.itheima.exception.CastException;
import com.itheima.shop.mapper.TradeOrderMapper;
import com.itheima.shop.pojo.*;
import com.itheima.utils.IDWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.Date;
@Slf4j
@Component
@Service(interfaceClass = IOrderService.class)
public class OrderServiceImpl implements IOrderService {
@Reference
private IGoodsService goodsService;
@Reference
private IUserService userService;
@Reference
private ICouponService couponService;
@Value("${mq.order.topic}")
private String topic;
@Value("${mq.order.tag.cancel}")
private String tag;
@Autowired
private TradeOrderMapper orderMapper;
@Autowired
private IDWorker idWorker;
@Autowired
private RocketMQTemplate rocketMQTemplate;
@Override
public Result confirmOrder(TradeOrder order) {
//1.校验订单
checkOrder(order);
//2.生成预订单
Long orderId = savePreOrder(order);
try {
//3.扣减库存
reduceGoodsNum(order);
//4.扣减优惠券
updateCouponStatus(order);
//5.使用余额
reduceMoneyPaid(order);
//模拟异常抛出
//CastException.cast(ShopCode.SHOP_FAIL);
//6.确认订单
updateOrderStatus(order);
//7.返回成功状态
return new Result(ShopCode.SHOP_SUCCESS.getSuccess(),ShopCode.SHOP_SUCCESS.getMessage());
} catch (Exception e) {
//1.确认订单失败,发送消息
MQEntity mqEntity = new MQEntity();
mqEntity.setOrderId(orderId);
mqEntity.setUserId(order.getUserId());
mqEntity.setUserMoney(order.getMoneyPaid());
mqEntity.setGoodsId(order.getGoodsId());
mqEntity.setGoodsNum(order.getGoodsNumber());
mqEntity.setCouponId(order.getCouponId());
//2.返回订单确认失败消息
try {
sendCancelOrder(topic,tag,order.getOrderId().toString(), JSON.toJSONString(mqEntity));
} catch (Exception e1) {
e1.printStackTrace();
}
return new Result(ShopCode.SHOP_FAIL.getSuccess(),ShopCode.SHOP_FAIL.getMessage());
}
}
/**
* 发送订单确认失败消息
* @param topic
* @param tag
* @param keys
* @param body
*/
private void sendCancelOrder(String topic, String tag, String keys, String body) throws InterruptedException, RemotingException, MQClientException, MQBrokerException {
Message message = new Message(topic,tag,keys,body.getBytes());
rocketMQTemplate.getProducer().send(message);
}
/**
* 确认订单
* @param order
*/
private void updateOrderStatus(TradeOrder order) {
order.setOrderStatus(ShopCode.SHOP_ORDER_CONFIRM.getCode());
order.setPayStatus(ShopCode.SHOP_ORDER_PAY_STATUS_NO_PAY.getCode());
order.setConfirmTime(new Date());
int r = orderMapper.updateByPrimaryKey(order);
if(r<=0){
CastException.cast(ShopCode.SHOP_ORDER_CONFIRM_FAIL);
}
log.info("订单:"+order.getOrderId()+"确认订单成功");
}
/**
* 扣减余额
* @param order
*/
private void reduceMoneyPaid(TradeOrder order) {
if(order.getMoneyPaid()!=null && order.getMoneyPaid().compareTo(BigDecimal.ZERO)==1){
TradeUserMoneyLog userMoneyLog = new TradeUserMoneyLog();
userMoneyLog.setOrderId(order.getOrderId());
userMoneyLog.setUserId(order.getUserId());
userMoneyLog.setUseMoney(order.getMoneyPaid());
userMoneyLog.setMoneyLogType(ShopCode.SHOP_USER_MONEY_PAID.getCode());
Result result = userService.updateMoneyPaid(userMoneyLog);
if(result.getSuccess().equals(ShopCode.SHOP_FAIL.getSuccess())){
CastException.cast(ShopCode.SHOP_USER_MONEY_REDUCE_FAIL);
}
log.info("订单:"+order.getOrderId()+",扣减余额成功");
}
}
/**
* 使用优惠券
* @param order
*/
private void updateCouponStatus(TradeOrder order) {
if(order.getCouponId()!=null){
TradeCoupon coupon = couponService.findOne(order.getCouponId());
coupon.setOrderId(order.getOrderId());
coupon.setIsUsed(ShopCode.SHOP_COUPON_ISUSED.getCode());
coupon.setUsedTime(new Date());
//更新优惠券状态
Result result = couponService.updateCouponStatus(coupon);
if(result.getSuccess().equals(ShopCode.SHOP_FAIL.getSuccess())){
CastException.cast(ShopCode.SHOP_COUPON_USE_FAIL);
}
log.info("订单:"+order.getOrderId()+",使用优惠券");
}
}
/**
* 扣减库存
* @param order
*/
private void reduceGoodsNum(TradeOrder order) {
//订单ID,商品ID,商品数量
TradeGoodsNumberLog goodsNumberLog = new TradeGoodsNumberLog();
goodsNumberLog.setOrderId(order.getOrderId());
goodsNumberLog.setGoodsId(order.getGoodsId());
goodsNumberLog.setGoodsNumber(order.getGoodsNumber());
Result result = goodsService.reduceGoodsNum(goodsNumberLog);
if(result.getSuccess().equals(ShopCode.SHOP_FAIL.getSuccess())){
CastException.cast(ShopCode.SHOP_REDUCE_GOODS_NUM_FAIL);
}
log.info("订单:"+order.getOrderId()+"扣减库存成功");
}
/**
* 生成预订单
*
* @param order
* @return
*/
private Long savePreOrder(TradeOrder order) {
//1. 设置订单状态为不可见
order.setOrderStatus(ShopCode.SHOP_ORDER_NO_CONFIRM.getCode());
//2. 设置订单ID
long orderId = idWorker.nextId();
order.setOrderId(orderId);
//3. 核算订单运费
BigDecimal shippingFee = calculateShippingFee(order.getOrderAmount());
if(order.getShippingFee().compareTo(shippingFee)!=0){
CastException.cast(ShopCode.SHOP_ORDER_SHIPPINGFEE_INVALID);
}
//4. 核算订单总金额是否合法
BigDecimal orderAmount = order.getGoodsPrice().multiply(new BigDecimal(order.getGoodsNumber()));
orderAmount.add(shippingFee);
if(order.getOrderAmount().compareTo(orderAmount)!=0){
CastException.cast(ShopCode.SHOP_ORDERAMOUNT_INVALID);
}
//5.判断用户是否使用余额
BigDecimal moneyPaid = order.getMoneyPaid();
if(moneyPaid!=null){
//5.1 订单中余额是否合法
int r = moneyPaid.compareTo(BigDecimal.ZERO);
//余额小于0
if(r==-1){
CastException.cast(ShopCode.SHOP_MONEY_PAID_LESS_ZERO);
}
//余额大于0
if(r==1){
TradeUser user = userService.findOne(order.getUserId());
if(moneyPaid.compareTo(new BigDecimal(user.getUserMoney()))==1){
CastException.cast(ShopCode.SHOP_MONEY_PAID_INVALID);
}
}
}else{
order.setMoneyPaid(BigDecimal.ZERO);
}
//6.判断用户是否使用优惠券
Long couponId = order.getCouponId();
if(couponId!=null){
TradeCoupon coupon = couponService.findOne(couponId);
//6.1 判断优惠券是否存在
if(coupon==null){
CastException.cast(ShopCode.SHOP_COUPON_NO_EXIST);
}
//6.2 判断优惠券是否已经被使用
if(coupon.getIsUsed().intValue()==ShopCode.SHOP_COUPON_ISUSED.getCode().intValue()){
CastException.cast(ShopCode.SHOP_COUPON_ISUSED);
}
order.setCouponPaid(coupon.getCouponPrice());
}else{
order.setCouponPaid(BigDecimal.ZERO);
}
//7.核算订单支付金额 订单总金额-余额-优惠券金额
BigDecimal payAmount = order.getOrderAmount().subtract(order.getMoneyPaid()).subtract(order.getCouponPaid());
order.setPayAmount(payAmount);
//8.设置下单时间
order.setAddTime(new Date());
//9.保存订单到数据库
orderMapper.insert(order);
//10.返回订单ID
return orderId;
}
/**
* 核算运费
* @param orderAmount
* @return
*/
private BigDecimal calculateShippingFee(BigDecimal orderAmount) {
if(orderAmount.compareTo(new BigDecimal(100))==1){
return BigDecimal.ZERO;
}else{
return new BigDecimal(10);
}
}
/**
* 校验订单
*
* @param order
*/
private void checkOrder(TradeOrder order) {
//1.校验订单是否存在
if (order == null) {
CastException.cast(ShopCode.SHOP_ORDER_INVALID);
}
//2.校验订单中的商品是否存在
TradeGoods goods = goodsService.findOne(order.getGoodsId());
if (goods == null) {
CastException.cast(ShopCode.SHOP_GOODS_NO_EXIST);
}
//3.校验下单用户是否存在
TradeUser user = userService.findOne(order.getUserId());
if (user == null) {
CastException.cast(ShopCode.SHOP_USER_NO_EXIST);
}
//4.校验商品单价是否合法
if (order.getGoodsPrice().compareTo(goods.getGoodsPrice()) != 0) {
CastException.cast(ShopCode.SHOP_GOODS_PRICE_INVALID);
}
//5.校验订单商品数量是否合法
if (order.getGoodsNumber() >= goods.getGoodsNumber()) {
CastException.cast(ShopCode.SHOP_GOODS_NUM_NOT_ENOUGH);
}
log.info("校验订单通过");
}
}
6、 在 shop-order-service 工程模块中,创建 测试类 OrderServiceTest 进行测试。
package com.itheima.test;
import com.itheima.api.IOrderService;
import com.itheima.shop.OrderServiceApplication;
import com.itheima.shop.pojo.TradeOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
import java.math.BigDecimal;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = OrderServiceApplication.class)
public class OrderServiceTest {
@Autowired
private IOrderService orderService;
@Test
public void confirmOrder() throws IOException {
Long coupouId = 345988230098857984L;
Long goodsId = 345959443973935104L;
Long userId = 345963634385633280L;
TradeOrder order = new TradeOrder();
order.setGoodsId(goodsId);
order.setUserId(userId);
order.setCouponId(coupouId);
order.setAddress("北京");
order.setGoodsNumber(1);
order.setGoodsPrice(new BigDecimal(1000));
order.setShippingFee(BigDecimal.ZERO);
order.setOrderAmount(new BigDecimal(1000));
order.setMoneyPaid(new BigDecimal(100));
orderService.confirmOrder(order);
System.in.read();
}
}
7、查看 mysql 数据库各项记录,有关订单变化情况。
# RocketMQ 实战:模拟电商网站场景综合案例(六)