RocketMQ 实战:模拟电商网站场景综合案例(六)
一、RocketMQ 实战 :项目公共类介绍
1、ID 生成器 :IDWorker:Twitter 雪花算法。
在 shop-common 工程模块中,IDWorker.java 是 ID 生成器公共类,运用 Twitter 雪花算法,自动生成项目 ID,而不会存在重复现象。
package com.itheima.utils;
public class IDWorker {
/**
* 起始的时间戳
*/
private final static long START_STMP = 1480166465631L;
/**
* 每一部分占用的位数
*/
private final static long SEQUENCE_BIT = 12; //序列号占用的位数
private final static long MACHINE_BIT = 5; //机器标识占用的位数
private final static long DATACENTER_BIT = 5;//数据中心占用的位数
/**
* 每一部分的最大值
*/
private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
/**
* 每一部分向左的位移
*/
private final static long MACHINE_LEFT = SEQUENCE_BIT;
private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
private long datacenterId; //数据中心
private long machineId; //机器标识
private long sequence = 0L; //序列号
private long lastStmp = -1L;//上一次时间戳
public IDWorker(long datacenterId, long machineId) {
if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
}
if (machineId > MAX_MACHINE_NUM || machineId < 0) {
throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
}
this.datacenterId = datacenterId;
this.machineId = machineId;
}
/**
* 产生下一个ID
*
* @return
*/
public synchronized long nextId() {
long currStmp = getNewstmp();
if (currStmp < lastStmp) {
throw new RuntimeException("Clock moved backwards. Refusing to generate id");
}
if (currStmp == lastStmp) {
//相同毫秒内,序列号自增
sequence = (sequence + 1) & MAX_SEQUENCE;
//同一毫秒的序列数已经达到最大
if (sequence == 0L) {
currStmp = getNextMill();
}
} else {
//不同毫秒内,序列号置为0
sequence = 0L;
}
lastStmp = currStmp;
return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分
| datacenterId << DATACENTER_LEFT //数据中心部分
| machineId << MACHINE_LEFT //机器标识部分
| sequence; //序列号部分
}
private long getNextMill() {
long mill = getNewstmp();
while (mill <= lastStmp) {
mill = getNewstmp();
}
return mill;
}
private long getNewstmp() {
return System.currentTimeMillis();
}
public static void main(String[] args) {
IDWorker idWorker = new IDWorker(2, 3);
for (int i = 0; i < 10; i++) {
System.out.println(idWorker.nextId());
}
}
}
2、异常处理类:
在 shop-common 工程模块中,
CustomerException.java :自定义异常公共类,CastException.java :异常抛出公共类。
package com.itheima.exception;
import com.itheima.constant.ShopCode;
import lombok.extern.slf4j.Slf4j;
/**
* 异常抛出类
*/
@Slf4j
public class CastException {
public static void cast(ShopCode shopCode) {
log.error(shopCode.toString());
throw new CustomerException(shopCode);
}
}
package com.itheima.exception;
import com.itheima.constant.ShopCode;
/**
* 自定义异常
*/
public class CustomerException extends RuntimeException{
private ShopCode shopCode;
public CustomerException(ShopCode shopCode) {
this.shopCode = shopCode;
}
}
3、常量类 : ShopCode:系统状态类
在 shop-common 工程模块中,ShopCode.java :是系统状态公共类。
package com.itheima.constant;
/**
* @author Think
*/
public enum ShopCode {
//正确
SHOP_SUCCESS(true, 1, "正确"),
//错误
SHOP_FAIL(false, 0, "错误"),
//付款
SHOP_USER_MONEY_PAID(true, 1, "付款"),
//退款
SHOP_USER_MONEY_REFUND(true, 2, "退款"),
//订单未确认
SHOP_ORDER_NO_CONFIRM(false, 0, "订单未确认"),
//订单已确认
SHOP_ORDER_CONFIRM(true, 1, "订单已经确认"),
//订单已取消
SHOP_ORDER_CANCEL(false, 2, "订单已取消"),
//订单已取消
SHOP_ORDER_INVALID(false, 3, "订单无效"),
//订单已取消
SHOP_ORDER_RETURNED(false, 4, "订单已退货"),
//订单已付款
SHOP_ORDER_PAY_STATUS_NO_PAY(true,0,"订单未付款"),
//订单已付款
SHOP_ORDER_PAY_STATUS_PAYING(true,1,"订单正在付款"),
//订单已付款
SHOP_ORDER_PAY_STATUS_IS_PAY(true,2,"订单已付款"),
//消息正在处理
SHOP_MQ_MESSAGE_STATUS_PROCESSING(true, 0, "消息正在处理"),
//消息处理成功
SHOP_MQ_MESSAGE_STATUS_SUCCESS(true, 1, "消息处理成功"),
//消息处理失败
SHOP_MQ_MESSAGE_STATUS_FAIL(false, 2, "消息处理失败"),
//请求参数有误
SHOP_REQUEST_PARAMETER_VALID(false, -1, "请求参数有误"),
//优惠券已经使用
SHOP_COUPON_ISUSED(true, 1, "优惠券已经使用"),
//优惠券未使用
SHOP_COUPON_UNUSED(false, 0, "优惠券未使用"),
//快递运费不正确
SHOP_ORDER_STATUS_UPDATE_FAIL(false, 10001, "订单状态修改失败"),
//快递运费不正确
SHOP_ORDER_SHIPPINGFEE_INVALID(false, 10002, "订单运费不正确"),
//订单总价格不合法
SHOP_ORDERAMOUNT_INVALID(false, 10003, "订单总价格不正确"),
//订单保存失败
SHOP_ORDER_SAVE_ERROR(false, 10004, "订单保存失败"),
//订单确认失败
SHOP_ORDER_CONFIRM_FAIL(false, 10005, "订单确认失败"),
//商品不存在
SHOP_GOODS_NO_EXIST(false, 20001, "商品不存在"),
//订单价格非法
SHOP_GOODS_PRICE_INVALID(false, 20002, "商品价格非法"),
//商品库存不足
SHOP_GOODS_NUM_NOT_ENOUGH(false, 20003, "商品库存不足"),
//扣减库存失败
SHOP_REDUCE_GOODS_NUM_FAIL(false, 20004, "扣减库存失败"),
//库存记录为空
SHOP_REDUCE_GOODS_NUM_EMPTY(false, 20005, "扣减库存失败"),
//用户账号不能为空
SHOP_USER_IS_NULL(false, 30001, "用户账号不能为空"),
//用户信息不存在
SHOP_USER_NO_EXIST(false, 30002, "用户不存在"),
//余额扣减失败
SHOP_USER_MONEY_REDUCE_FAIL(false, 30003, "余额扣减失败"),
//已经退款
SHOP_USER_MONEY_REFUND_ALREADY(true, 30004, "订单已经退过款"),
//优惠券不不存在
SHOP_COUPON_NO_EXIST(false, 40001, "优惠券不存在"),
//优惠券不合法
SHOP_COUPON_INVALIED(false, 40002, "优惠券不合法"),
//优惠券使用失败
SHOP_COUPON_USE_FAIL(false, 40003, "优惠券使用失败"),
//余额不能小于0
SHOP_MONEY_PAID_LESS_ZERO(false, 50001, "余额不能小于0"),
//余额非法
SHOP_MONEY_PAID_INVALID(false, 50002, "余额非法"),
//Topic不能为空
SHOP_MQ_TOPIC_IS_EMPTY(false, 60001, "Topic不能为空"),
//消息体不能为空
SHOP_MQ_MESSAGE_BODY_IS_EMPTY(false, 60002, "消息体不能为空"),
//消息发送失败
SHOP_MQ_SEND_MESSAGE_FAIL(false,60003,"消息发送失败"),
//支付订单未找到
SHOP_PAYMENT_NOT_FOUND(false,70001,"支付订单未找到"),
//支付订单已支付
SHOP_PAYMENT_IS_PAID(false,70002,"支付订单已支付"),
//订单付款失败
SHOP_PAYMENT_PAY_ERROR(false,70002,"订单支付失败");
Boolean success;
Integer code;
String message;
ShopCode() {
}
ShopCode(Boolean success, Integer code, String message) {
this.success = success;
this.code = code;
this.message = message;
}
public Boolean getSuccess() {
return success;
}
public void setSuccess(Boolean success) {
this.success = success;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "ShopCode{" +
"success=" + success +
", code=" + code +
", message='" + message + '\'' +
'}';
}
}
4、响应实体类 :Result:封装响应状态和响应信息
在 shop-pojo 工程模块中,Result.java :是封装响应状态和响应信息的公共类。
package com.itheima.entity;
import java.io.Serializable;
/**
* 结果实体类
*/
public class Result implements Serializable {
private Boolean success;
private String message;
public Result() {
}
public Result(Boolean success, String message) {
this.success = success;
this.message = message;
}
public Boolean getSuccess() {
return success;
}
public void setSuccess(Boolean success) {
this.success = success;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "Result{" +
"success=" + success +
", message='" + message + '\'' +
'}';
}
}
二、RocketMQ 实战:模拟电商网站场景综合案例 – 下单功能时序图
三、RocketMQ 实战:下单接口定义和编码步骤分析
1、下单基本流程:接口定义 IOrderService.java
在 shop-api 工程模块中,创建 IOrderService.java 下单接口类。
package com.itheima.api;
import com.itheima.entity.Result;
import com.itheima.shop.pojo.TradeOrder;
public interface IOrderService {
/**
* 下单接口
* @param order
* @return
*/
public Result confirmOrder(TradeOrder order);
}
2、下单基本流程:业务类实现 OrderServiceImpl.java
在 shop-order-service 工程模块中,创建 OrderServiceImpl.java 业务实现类。
基本框架如下:
@Slf4j
@Component
@Service(interfaceClass = IOrderService.class)
public class OrderServiceImpl implements IOrderService {
@Override
public Result confirmOrder(TradeOrder order) {
//1.校验订单
//2.生成预订单
try {
//3.扣减库存
//4.扣减优惠券
//5.使用余额
//6.确认订单
//7.返回成功状态
} catch (Exception e) {
//1.确认订单失败,发送消息
//2.返回失败状态
}
}
}
四、RocketMQ 实战:模拟电商网站场景综合案例–校验订单流程分析图
五、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;
@Override
public Result confirmOrder(TradeOrder order) {
//1.校验订单
checkOrder(order);
//2.生成预订单
try {
//3.扣减库存
//4.扣减优惠券
//5.使用余额
//6.确认订单
//7.返回成功状态
} catch (Exception e) {
//1.确认订单失败,发送消息
//2.返回订单确认失败消息
return null;
}
}
/**
* 校验订单
*
* @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("校验订单通过");
}
}
2、在 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);
}
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);
}
4、在 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);
}
}
5、在 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);
}
}
上一节关联链接请点击:
# RocketMQ 实战:模拟电商网站场景综合案例(五)
环境搭建:数据库表结构介绍–项目工程初始化 查看 请点击:
# RocketMQ 实战:模拟电商网站场景综合案例(三)
mybatis 逆向工程 生成 POJO 类 源文件 和 mapper 映射文件 查看 请点击:
RocketMQ 实战:模拟电商网站场景综合案例(四)