# RocketMQ 实战:模拟电商网站场景综合案例(六)

RocketMQ 实战:模拟电商网站场景综合案例(六)

一、RocketMQ 实战 :项目公共类介绍

1、ID 生成器 :IDWorker:Twitter 雪花算法。

在 shop-common 工程模块中,IDWorker.java 是 ID 生成器公共类,运用 Twitter 雪花算法,自动生成项目 ID,而不会存在重复现象。

bash 复制代码
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 :异常抛出公共类。

bash 复制代码
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);
    }
}
bash 复制代码
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 :是系统状态公共类。

bash 复制代码
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 :是封装响应状态和响应信息的公共类。

bash 复制代码
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 下单接口类。

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 业务实现类。

基本框架如下:

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 下单业务类,

完成 订单校验方法。

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 接口类。

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 接口类。

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 实现类。

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 实现类。

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 实战:模拟电商网站场景综合案例(四)

相关推荐
懒人w28 分钟前
百万数据量修改数据思路及方法
java·linux·windows
isfox31 分钟前
玩转springboot之springboot多环境配置
spring boot
skywalk81631 小时前
Clojure学习:运行在 Java 虚拟机(JVM)上的动态Lisp编程语言
java·lisp·clojure
一碗谦谦粉1 小时前
Spring之spring的单例bean是线程安全的吗
java·spring
liuliuliuliuyujie1 小时前
学生管理系统
java
ItKevin爱java1 小时前
java八股文面试题
java·开发语言
喜欢猪猪1 小时前
两个线程之间是如何通信的呢?
java·开发语言
co松柏1 小时前
在非 antd pro 项目中使用 umi OpenAPI
java·前端
java6666688881 小时前
Spring Boot中的数据加密与解密
java·spring boot·后端
xintaiideas1 小时前
深入理解 RabbitMQ、RocketMQ等常⽤的消息中间件进⾏消息的异步数据处理
分布式·rabbitmq·rocketmq