策略模式实战
前言
说一下场景,我做的是拍卖相关的业务,因而有的订单不能够即时去生成,就用到了定时器去扫表,下面是重头戏,涉及到了不同订单状态的生成,如下:
1. 待付定金订单
2. 待付尾款的订单
3. 待成交订单
4. 流拍订单
根据定金的状态以及参拍人出价与保留价的差值进行三种订单状态的处理,一开始三种状态写到了一起,上周学了下策略模式,这代码看得我心累,写的自己能没看懂,后面的人估计要开骂了,典型的问题代码,趁着项目没上线必须优化掉,老代码如下:
@KafkaListener(topics = {ORDER_TOPIC})
@Transactional(rollbackFor = Exception.class)
public synchronized void data(ConsumerRecord consumerRecord) {
// 里面的值为Product活动对象
String productId = (String) consumerRecord.value();
int status = 0;
if (null != productId) {
TOrder order = new TOrder();
TAuctionProduct product = productMapper.selectList(new QueryWrapper<TAuctionProduct>()
.lambda().eq(TAuctionProduct::getId, productId)).get(0);
TAuctionActivity activity = activityMapper.selectOne(new QueryWrapper<TAuctionActivity>().lambda()
.eq(TAuctionActivity::getCode, product.getAuctionCode()));
// 1. 查询订单记录表, 将userId拿到
List<TAuctionRecord> recordList = recordService.list(new QueryWrapper<TAuctionRecord>()
.eq("product_id", product.getId()));
/****** 没有竞价记录的车辆进入流拍状态 ******/
if (CollectionUtil.isEmpty(recordList)) {
order.setActivityCode(product.getAuctionCode());
order.setProductId(NumberUtil.parseInt(productId));
// 流拍订单用户id 为null
order.setUserId(null);
order.setCode(UUID.randomUUID().toString());
order.setOrderStatus(LIU_PAI);
order.setCarIds(product.getCarIds());
// 需要将当前的车辆释放掉
// 结束时间以生成订单时间为准
order.setEndTime(new Date());
order.setLiuPaiTime(new Date());
order.setBasePrice(product.getBasePrice());
order.setFinalPrice(BigDecimal.valueOf(-1L));
// 保留价
order.setRemainPrice(product.getReservePrice());
// 佣金
order.setCommission(BigDecimal.valueOf(Constants.COMMISSION));
// 服务费
order.setDealServicePrice(product.getDealServicePrice());
// 对外展示,门店或集团名称
order.setDisplayShopName(product.getDisplayShopName());
// 用户手机号
order.setMobile("");
// 设置成交时间
order.setDealTime(null);
// 设置定金
order.setEarnestPrice(product.getEarnestPrice());
// 设置订单已结束
product.setAuctionStatus("END_BIDDING");
// 设置集团id
order.setGroupId(NumberUtil.parseInt(activity.getGroupId()));
// 设置当前订单转存门店或集团id
order.setBelGroupId(NumberUtil.parseInt(activity.getGroupId()));
boolean save = orderService.save(order);
product.setStatus(1);
int i = productMapper.updateById(product);
// 保证金解冻 todo
// 消息推送
// kafkaTemplate.send(MESSAGE_TOPIC, productId);
kafkaTemplate.send(CAR_TOPIC_ROLLBACK,productId);
} else {
// 2. 对比final_price 取到最终价
List<TAuctionRecord> matchList = recordList.stream()
.filter(finalRes -> finalRes.getAuctionPrice().intValue() - product.getFinalPrice().intValue() == 0).collect(Collectors.toList());
// 3. 生成订单 TODO 需要重构,考虑消息发送失败
if (CollectionUtil.isNotEmpty(matchList)) {
TAuctionRecord record = matchList.get(0);
order.setActivityCode(record.getAuctionCode());
order.setProductId(NumberUtil.parseInt(record.getProductId()));
order.setUserId(record.getUserId());
order.setCode(UUID.randomUUID().toString());
if (product.getEarnestPrice().doubleValue() > 0) {
order.setOrderStatus(PRE_PAYMENT);
} else {
order.setOrderStatus(PRE_FINAL_PAYMENT);
order.setNewAddTime(new Date());
}
order.setCarIds(product.getCarIds());
// 结束时间以生成订单时间为准
order.setEndTime(new Date());
order.setBasePrice(product.getBasePrice());
order.setFinalPrice(product.getFinalPrice());
// 保留价
order.setRemainPrice(product.getReservePrice());
// 佣金
order.setCommission(BigDecimal.valueOf(Constants.COMMISSION));
// 服务费
order.setDealServicePrice(product.getDealServicePrice());
// 对外展示,门店或集团名称
order.setDisplayShopName(product.getDisplayShopName());
// 用户手机号
order.setMobile(record.getMobile());
// 设置订单转态
if (product.getEarnestPrice().doubleValue() > 0.0) {
order.setOrderStatus(OrderEnum.PRE_PAYMENT.getName());
} else {
order.setOrderStatus(OrderEnum.PRE_FINAL_PAYMENT.getName());
//当定金为0时,付定金时间为当前order新增时间
order.setEarnestPriceTime(new Date());
}
// 设置成交时间
order.setDealTime(new Date());
// 设置定金
order.setEarnestPrice(product.getEarnestPrice());
// 设置订单已结束
product.setAuctionStatus("END_BIDDING");
// 设置集团id
// order.setGroupId(NumberUtil.parseInt(activity.getGroupId()));
// 设置集团id
order.setGroupId(NumberUtil.parseInt(activity.getGroupId()));
// 设置当前订单转存门店或集团id
order.setBelGroupId(NumberUtil.parseInt(activity.getGroupId()));
boolean save = orderService.save(order);
product.setStatus(1);
int i = productMapper.updateById(product);
// 消息推送
kafkaTemplate.send(MESSAGE_TOPIC, productId);
// 保证金解冻 todo 待定
}
}
}
}
针对上面的代码,我们自己来进行分析,之后进行代码的优化
分析如下:
1. 冗余代码,一眼看得出来的问题
2. 不易于拓展,新增加一种新类型的订单在来一个if else 判断?
3. 这个是一个活脱脱的策略模式拿来练手的案例,此时不动,更待何时?留给别人去操作,然后泡走你的妹子打你的娃?我们为何不能泡别人的妹子打别人的娃呢?所以,自己动吧
4. 再多说一句,最近在看spring源码,看得我真是头皮发毛,spring源码我已经停留在1.5层了,并不是简单的第一层,能不能突破第三层就看第二遍怎么看了!
5. 学习目的不纯
我们接下来要做的就是,首先抽取出一个方法,判断出,当前订单的状态属于以下哪一种?
1. 待付定金订单
2. 待付尾款的订单
3. 待成交订单
4. 流拍订单
分析下以上订单的条件:
1. 待付订金订单: 定金是成交价的百分之30
2. 待付尾款订单: 订金为:0,不需要付定金
3. 待成交订单: 最终竞价fianlPrice是否大于remainPrice
4. 流拍: 当前无人出价(生成订单时,流拍只有一种情况那就是无人竞价)
ps: finalPrice: 最终竞价, basePrice: 起拍价, remainPrice: 保留价
为什么要用策略模式,每种订单有不同的处理逻辑, 按照自己的思路一点点向策略模式去靠拢
我们最终的目的是将上述代码优化为: 简单工厂 + 策略
简单工厂: 供我们去选择对应处理算法(这里面指的是处理不同的订单状态)
策略模式: 指不同的算法,这里面指处理不同的订单
下面我们把优化的代码粘贴进来:
1. 生成订单的主要入口
@Component
@Slf4j
public class OrderConstructor {
// 订单状态的推送TOPIC
private static final String ORDER_TOPIC = "order-topic";
@Autowired
private ITAuctionRecordService recordService;
@Autowired
private TAuctionProductMapper productMapper;
@KafkaListener(topics = {ORDER_TOPIC})
@Transactional(rollbackFor = Exception.class)
public synchronized void data(ConsumerRecord consumerRecord) {
// 里面的值为Product活动对象
String productId = (String) consumerRecord.value();
// 如果条件不成立,则订单不生成
if (StringUtils.isBlank(productId)) return;
// 订单状态
String orderStatus = imitateUserAuction(productId);
// 应用简单工厂 + 策略实现代码的解耦,可拓展性,下面我们
IExecuteOrder order = OrderFactory.getOrderImpl(orderStatus);
// 生成订单
order.execute(productId);
}
/**
* 模拟用户请求,返回订单所属的状态
*
* @param productId 订单活动id
*/
private String imitateUserAuction(String productId) {
// 根据条件判断出用户行为信息
String orderStatus = "";
List<TAuctionRecord> recordList = recordService.list(new QueryWrapper<TAuctionRecord>()
.eq("product_id", productId));
TAuctionProduct product = productMapper.selectList(new QueryWrapper<TAuctionProduct>()
.lambda().eq(TAuctionProduct::getId, productId)).get(0);
// 流拍 --->即不存在任何竞价记录
if (CollectionUtil.isEmpty(recordList)) {
orderStatus = OrderEnum.LIU_PAI.getName();
// 待成交订单 ---> 有人出价,但是价格小于保留价,即: finalPrice < remainPrice
} else if (product.getFinalPrice().doubleValue() - product.getReservePrice().doubleValue() < 0) {
orderStatus = OrderEnum.PRE_DEAL.getName();
// 待付订金的
} else if (product.getEarnestPrice().doubleValue() > 0.0) {
orderStatus = OrderEnum.PRE_PAYMENT.getName();
// 待付尾款
} else {
orderStatus = OrderEnum.PRE_FINAL_PAYMENT.getName();
}
return orderStatus;
}
}
2. 策略模式定义的接口
public interface IExecuteOrder {
/**
* 生成订单
*/
void execute(String productId);
}
3. 简单工厂: 获取特定的执行类
/**
* @author xxx
* @since 2020/7/24
*/
public class OrderFactory {
private static Map<String, IExecuteOrder> ORDERS = new HashMap<>();
static {
ORDERS.put(OrderEnum.PRE_PAYMENT.getName(), new PrePaymentImpl());
ORDERS.put(OrderEnum.PRE_FINAL_PAYMENT.getName(), new PreFinalPaymentImpl());
ORDERS.put(OrderEnum.LIU_PAI.getName(), new LiuPaiImpl());
ORDERS.put(OrderEnum.PRE_DEAL.getName(), new PreDealImpl());
}
private static IExecuteOrder prePayment;
private OrderFactory() {
}
public static IExecuteOrder getOrderImpl(String orderStatus) {
IExecuteOrder order = ORDERS.get(orderStatus);
if (null == order) {
throw new ApiException("无法创建当前订单类型, 请仔细核对。");
}
return order;
}
public static Set<String> getOrders() {
return ORDERS.keySet();
}
}
4. 具体的执行类: 待付订金逻辑,这里面我感觉有点问题,但是不知道如何优化,大家帮忙看下
/**
* @author xxx
* @since 2020/7/24
*/
public class PrePaymentImpl implements IExecuteOrder {
// 消息推送TOPIC
private static final String MESSAGE_TOPIC = "message-topic";
@Autowired
private ITAuctionRecordService recordService;
@Autowired
private ITOrderService orderService;
@Autowired
private TAuctionProductMapper productMapper;
@Autowired
private TAuctionActivityMapper activityMapper;
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
@Override
@Transactional
public void execute(String productId) {
// 订单信息
TAuctionProduct product = productMapper.selectList(new QueryWrapper<TAuctionProduct>()
.lambda().eq(TAuctionProduct::getId, productId)).get(0);
// 活动信息
TAuctionActivity activity = activityMapper.selectOne(new QueryWrapper<TAuctionActivity>().lambda()
.eq(TAuctionActivity::getCode, product.getAuctionCode()));
TOrder order = new TOrder();
// 1. 查询订单记录表, 将userId拿到
List<TAuctionRecord> recordList = recordService.list(new QueryWrapper<TAuctionRecord>()
.eq("product_id", product.getId()));
// 2. 对比final_price 取到最终价
List<TAuctionRecord> matchList = recordList.stream()
.filter(finalRes -> finalRes.getAuctionPrice().intValue() - product.getFinalPrice().intValue() == 0).collect(Collectors.toList());
if (CollectionUtil.isNotEmpty(matchList)) {
TAuctionRecord record = matchList.get(0);
order.setActivityCode(record.getAuctionCode());
order.setProductId(NumberUtil.parseInt(record.getProductId()));
order.setUserId(record.getUserId());
order.setCode(UUID.randomUUID().toString());
if (product.getEarnestPrice().doubleValue() > 0) {
order.setOrderStatus(PRE_PAYMENT);
} else {
order.setOrderStatus(PRE_FINAL_PAYMENT);
order.setNewAddTime(new Date());
}
order.setCarIds(product.getCarIds());
// 结束时间以生成订单时间为准
order.setEndTime(new Date());
order.setBasePrice(product.getBasePrice());
order.setFinalPrice(product.getFinalPrice());
// 保留价
order.setRemainPrice(product.getReservePrice());
// 佣金
order.setCommission(BigDecimal.valueOf(Constants.COMMISSION));
// 服务费
order.setDealServicePrice(product.getDealServicePrice());
// 对外展示,门店或集团名称
order.setDisplayShopName(product.getDisplayShopName());
// 用户手机号
order.setMobile(record.getMobile());
// 设置订单转态
order.setOrderStatus(OrderEnum.PRE_PAYMENT.getName());
// 设置成交时间
order.setDealTime(new Date());
// 设置定金
order.setEarnestPrice(product.getEarnestPrice());
// 设置订单已结束
product.setAuctionStatus("END_BIDDING");
// 设置集团id
order.setGroupId(NumberUtil.parseInt(activity.getGroupId()));
// 设置当前订单转存门店或集团id
order.setBelGroupId(NumberUtil.parseInt(activity.getGroupId()));
orderService.save(order);
product.setStatus(1);
productMapper.updateById(product);
// 消息推送
kafkaTemplate.send(MESSAGE_TOPIC, productId);
// 保证金解冻 todo 待定
}
}
}
5. 具体的执行类: 待付尾款逻辑
/**
* @author xxx
* @since 2020/7/24
*/
public class PreFinalPaymentImpl implements IExecuteOrder {
// 消息推送TOPIC
private static final String MESSAGE_TOPIC = "message-topic";
@Autowired
private ITAuctionRecordService recordService;
@Autowired
private ITOrderService orderService;
@Autowired
private TAuctionProductMapper productMapper;
@Autowired
private TAuctionActivityMapper activityMapper;
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
@Override
public void execute(String productId) {
// 订单信息
TAuctionProduct product = productMapper.selectList(new QueryWrapper<TAuctionProduct>()
.lambda().eq(TAuctionProduct::getId, productId)).get(0);
// 活动信息
TAuctionActivity activity = activityMapper.selectOne(new QueryWrapper<TAuctionActivity>().lambda()
.eq(TAuctionActivity::getCode, product.getAuctionCode()));
TOrder order = new TOrder();
// 1. 查询订单记录表, 将userId拿到
List<TAuctionRecord> recordList = recordService.list(new QueryWrapper<TAuctionRecord>()
.eq("product_id", product.getId()));
// 2. 对比final_price 取到最终价
List<TAuctionRecord> matchList = recordList.stream()
.filter(finalRes -> finalRes.getAuctionPrice().intValue() - product.getFinalPrice().intValue() == 0).collect(Collectors.toList());
if (CollectionUtil.isNotEmpty(matchList)) {
TAuctionRecord record = matchList.get(0);
order.setActivityCode(record.getAuctionCode());
order.setProductId(NumberUtil.parseInt(record.getProductId()));
order.setUserId(record.getUserId());
order.setCode(UUID.randomUUID().toString());
if (product.getEarnestPrice().doubleValue() > 0) {
order.setOrderStatus(PRE_PAYMENT);
} else {
order.setOrderStatus(PRE_FINAL_PAYMENT);
order.setNewAddTime(new Date());
}
order.setCarIds(product.getCarIds());
// 结束时间以生成订单时间为准
order.setEndTime(new Date());
order.setBasePrice(product.getBasePrice());
order.setFinalPrice(product.getFinalPrice());
// 保留价
order.setRemainPrice(product.getReservePrice());
// 佣金
order.setCommission(BigDecimal.valueOf(Constants.COMMISSION));
// 服务费
order.setDealServicePrice(product.getDealServicePrice());
// 对外展示,门店或集团名称
order.setDisplayShopName(product.getDisplayShopName());
// 用户手机号
order.setMobile(record.getMobile());
// 设置订单转态
order.setOrderStatus(OrderEnum.PRE_FINAL_PAYMENT.getName());
//当定金为0时,付定金时间为当前order新增时间
order.setEarnestPriceTime(new Date());
// 设置成交时间
order.setDealTime(new Date());
// 设置定金
order.setEarnestPrice(product.getEarnestPrice());
// 设置订单已结束
product.setAuctionStatus("END_BIDDING");
// 设置集团id
order.setGroupId(NumberUtil.parseInt(activity.getGroupId()));
// 设置当前订单转存门店或集团id
order.setBelGroupId(NumberUtil.parseInt(activity.getGroupId()));
orderService.save(order);
product.setStatus(1);
productMapper.updateById(product);
// 消息推送
kafkaTemplate.send(MESSAGE_TOPIC, productId);
// 保证金解冻 todo 待定
}
}
}
6. 具体的执行类: 待成交逻辑
/**
* @author xxx
* @since 2020/7/24
*/
public class PreDealImpl implements IExecuteOrder {
// 消息推送TOPIC
private static final String MESSAGE_TOPIC = "message-topic";
@Autowired
private ITAuctionRecordService recordService;
@Autowired
private ITOrderService orderService;
@Autowired
private TAuctionProductMapper productMapper;
@Autowired
private TAuctionActivityMapper activityMapper;
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
@Override
public void execute(String productId) {
/****** 待成交订单 ---> 有用户出价,但是最终竞价 < 订单保留价 ******/
// 订单信息
TAuctionProduct product = productMapper.selectList(new QueryWrapper<TAuctionProduct>()
.lambda().eq(TAuctionProduct::getId, productId)).get(0);
// 活动信息
TAuctionActivity activity = activityMapper.selectOne(new QueryWrapper<TAuctionActivity>().lambda()
.eq(TAuctionActivity::getCode, product.getAuctionCode()));
TOrder order = new TOrder();
// 查询订单记录表, 将userId拿到
List<TAuctionRecord> recordList = recordService.list(new QueryWrapper<TAuctionRecord>()
.eq("product_id", product.getId()));
// 对比final_price 取到最终价
List<TAuctionRecord> matchList = recordList.stream()
.filter(finalRes -> finalRes.getAuctionPrice().intValue() - product.getFinalPrice().intValue() == 0).collect(Collectors.toList());
if (CollectionUtil.isNotEmpty(matchList)) {
TAuctionRecord record = matchList.get(0);
order.setActivityCode(record.getAuctionCode());
order.setProductId(NumberUtil.parseInt(record.getProductId()));
order.setUserId(record.getUserId());
order.setCode(UUID.randomUUID().toString());
order.setOrderStatus(PRE_DEAL);
order.setCarIds(product.getCarIds());
// 结束时间以生成订单时间为准
order.setEndTime(new Date());
order.setBasePrice(product.getBasePrice());
order.setFinalPrice(product.getFinalPrice());
// 保留价
order.setRemainPrice(product.getReservePrice());
// 佣金
order.setCommission(BigDecimal.valueOf(Constants.COMMISSION));
// 服务费
order.setDealServicePrice(product.getDealServicePrice());
// 对外展示,门店或集团名称
order.setDisplayShopName(product.getDisplayShopName());
// 用户手机号
order.setMobile(record.getMobile());
// 待成交订单
order.setOrderStatus(PRE_DEAL);
// 设置成交时间
order.setDealTime(new Date());
// 设置定金
order.setEarnestPrice(product.getEarnestPrice());
product.setAuctionStatus(null);
// 设置集团id
order.setGroupId(NumberUtil.parseInt(activity.getGroupId()));
// 设置当前订单转存门店或集团id
order.setBelGroupId(NumberUtil.parseInt(activity.getGroupId()));
orderService.save(order);
product.setStatus(1);
productMapper.updateById(product);
// 消息推送
kafkaTemplate.send(MESSAGE_TOPIC, productId);
// 保证金解冻 todo 待定
}
}
}
7. 具体的执行类: 流拍逻辑
/**
* @author xxx
* @since 2020/7/24
*/
public class LiuPaiImpl implements IExecuteOrder {
// 流拍车辆释放消息推送TOPIC
private static final String CAR_TOPIC_ROLLBACK = "car-topic-rollback";
// 消息推送TOPIC
private static final String MESSAGE_TOPIC = "message-topic";
@Autowired
private ITOrderService orderService;
@Autowired
private TAuctionProductMapper productMapper;
@Autowired
private TAuctionActivityMapper activityMapper;
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
@Override
public void execute(String productId) {
// 订单信息
TAuctionProduct product = productMapper.selectList(new QueryWrapper<TAuctionProduct>()
.lambda().eq(TAuctionProduct::getId, productId)).get(0);
// 活动信息
TAuctionActivity activity = activityMapper.selectOne(new QueryWrapper<TAuctionActivity>().lambda()
.eq(TAuctionActivity::getCode, product.getAuctionCode()));
TOrder order = new TOrder();
// 流拍需要把订单设置为流拍状态
order.setActivityCode(product.getAuctionCode());
order.setProductId(NumberUtil.parseInt(productId));
// 流拍订单用户id 为null
order.setUserId(null);
order.setCode(UUID.randomUUID().toString());
// 流拍订单
order.setOrderStatus(LIU_PAI);
order.setCarIds(product.getCarIds());
// 结束时间以生成订单时间为准
order.setEndTime(new Date());
order.setLiuPaiTime(new Date());
order.setBasePrice(product.getBasePrice());
order.setFinalPrice(BigDecimal.valueOf(-1L));
// 保留价
order.setRemainPrice(product.getReservePrice());
// 佣金
order.setCommission(BigDecimal.valueOf(Constants.COMMISSION));
// 服务费
order.setDealServicePrice(product.getDealServicePrice());
// 对外展示,门店或集团名称
order.setDisplayShopName(product.getDisplayShopName());
// 用户手机号
order.setMobile("");
// 设置成交时间
order.setDealTime(null);
// 设置定金
order.setEarnestPrice(product.getEarnestPrice());
// 设置订单已结束
product.setAuctionStatus("END_BIDDING");
// 设置集团id
order.setGroupId(NumberUtil.parseInt(activity.getGroupId()));
// 设置当前订单转存门店或集团id
order.setBelGroupId(NumberUtil.parseInt(activity.getGroupId()));
orderService.save(order);
product.setStatus(1);
productMapper.updateById(product);
// 保证金解冻 todo
// 消息推送
kafkaTemplate.send(MESSAGE_TOPIC, productId);
// 将车辆的状态设置为流拍状态
kafkaTemplate.send(CAR_TOPIC_ROLLBACK, productId);
}
}
上面是,项目中我优化的代码,其中就我的认知整体思维是没错的,最后的策略,出现了大量的冗余代码,有没有什么办法呢?欢迎留言