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 {//不同毫秒内,序列号置为0sequence = 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, "优惠券使用失败"),//余额不能小于0SHOP_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;}@Overridepublic 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;}@Overridepublic 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 {@Overridepublic 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 {@Referenceprivate IGoodsService goodsService;@Referenceprivate IUserService userService;@Overridepublic 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{@Autowiredprivate TradeUserMapper userMapper;@Autowiredprivate TradeUserMoneyLogMapper userMoneyLogMapper;@Overridepublic 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 {@Autowiredprivate TradeGoodsMapper goodsMapper;@Autowiredprivate TradeGoodsNumberLogMapper goodsNumberLogMapper;@Overridepublic TradeGoods findOne(Long goodsId) {if (goodsId == null) {CastException.cast(ShopCode.SHOP_REQUEST_PARAMETER_VALID);}return goodsMapper.selectByPrimaryKey(goodsId);}}
上一节关联链接请点击:
# RocketMQ 实战:模拟电商网站场景综合案例(五)
环境搭建:数据库表结构介绍–项目工程初始化 查看 请点击:
# RocketMQ 实战:模拟电商网站场景综合案例(三)
mybatis 逆向工程 生成 POJO 类 源文件 和 mapper 映射文件 查看 请点击:
RocketMQ 实战:模拟电商网站场景综合案例(四)