策略设计模式-下单

1、定义一个下单context类

通过这类来判断具体使用哪个实现类,可以通过一些枚举或者条件来判断

java 复制代码
import com.alibaba.fastjson.JSON;
import com.tc.common.exception.BusinessException;
import com.tc.common.user.YjkUserDetails;
import com.tc.institution.constant.RedisConstant;
import com.tc.institution.dto.req.order.OrderConfirmReq;
import com.tc.institution.dto.req.order.OrderSubmitReq;
import com.tc.institution.dto.resp.order.OrderConfirmResp;
import com.tc.institution.dto.resp.order.OrderSubmitResp;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * TODO 4.0和4.1代码整合待完成
 * 1集中整合其他策略子类查询到OrderContext上下文,减少互相依赖
 * 2抽取已有共有方法到公共类
 *
 */
@Slf4j
@Service
public class OrderContext {

    @Autowired
    private List<OrderStrategy> orderStrategyList;

    @Resource
    private RedissonClient redissonClient;

    /**
     * 增加策略请维护该注释!!!
     * req.buyDemandOrderId 不为空走BuyDemandOrderStrategy
     * req.bizType = CONSULTATION_FEE(6, "问诊费订单") && req.consultationOrderId 不为空走ConsultationFeeOrderStrategy
     *
     * @param req  请求参数
     * @param user 操作用户
     */
    public OrderConfirmResp confirm(OrderConfirmReq req, YjkUserDetails user) {
        log.info("Function confirm ,req={}", req);
        for (OrderStrategy orderStrategy : orderStrategyList) {
            if (orderStrategy.confirmSupport(req) && orderStrategy.confirmCheck(req)) {
                OrderConfirmResp confirmResp = orderStrategy.confirm(req, user);
                log.info("Function confirm ,resp={}", confirmResp);
                return confirmResp;
            }
        }
        throw new BusinessException("预下单方式异常");
    }

    /**
     * 增加策略请维护该注释!!!
     * req.buyDemandOrderId 不为空走BuyDemandOrderStrategy
     * req.bizType = CONSULTATION_FEE(6, "问诊费订单") && req.consultationOrderId 不为空走ConsultationFeeOrderStrategy
     * req.bizType = CONSULTATION_EXPERT(7, "专家问诊订单") && req.consultationOrderId 不为空走ConsultationFeeOrderStrategy
     *
     * @param req  请求参数
     * @param user 操作用户
     */
    public OrderSubmitResp submit(OrderSubmitReq req, YjkUserDetails user) {
        for (OrderStrategy orderStrategy : orderStrategyList) {
            if (orderStrategy.submitSupport(req) && orderStrategy.submitCheck(req)) {
                return getOrderSubmitResp(req, user, orderStrategy);
            }
        }
        throw new BusinessException("预下单方式异常");
    }

    private OrderSubmitResp getOrderSubmitResp(OrderSubmitReq req, YjkUserDetails user, OrderStrategy orderStrategy) {
        String submitLockKey = orderStrategy.getSubmitLockKey(req, user);
        RLock orderLock = redissonClient.getLock(RedisConstant.ORDER_SUBMIT_LOCK_KEY + submitLockKey);
        try {
            boolean orderLockFlag = orderLock.tryLock(-1, 1, TimeUnit.MINUTES);
            log.info("Function submit orderLockFlag:{} param:{}", orderLockFlag, JSON.toJSONString(req));
            if (orderLockFlag) {
                OrderSubmitResp orderSubmitResp = orderStrategy.submit(req, user);
                log.info("Function submit param:{} result:{}", JSON.toJSONString(req), JSON.toJSONString(orderSubmitResp));
                return orderSubmitResp;
            } else {
                log.info("Function submit get lock fail, key:{}, now exist ...", req.getBuyDemandOrderId());
                throw new BusinessException("订单处理中,请勿重复多次提交");
            }
        } catch (Exception e) {
            log.warn("Function submit getOrderLock fail:", e);
            throw new BusinessException(e.getMessage());
        } finally {
            if (orderLock.isLocked() && orderLock.isHeldByCurrentThread()) {
                orderLock.unlock();
            }
        }
    }
}

2、定义一个订单策略接口,并定义公用入参

java 复制代码
mport com.tc.institution.dto.req.order.OrderConfirmReq;
import com.tc.institution.dto.req.order.OrderSubmitReq;
import com.tc.institution.dto.resp.order.OrderConfirmResp;
import com.tc.institution.dto.resp.order.OrderSubmitResp;

/**
 * 下单通用策略
 */
public interface OrderStrategy<C,S,U> {

    /**
     * 预下单方式检验
     *
     * @param req 预下单请求参数
     * @return
     */
    boolean confirmSupport(OrderConfirmReq req);

    /**
     * 下单方式检验
     *
     * @param req 下单请求参数
     * @return
     */
    boolean submitSupport(OrderSubmitReq req);

    /**
     * 预下单参数校验
     *
     * @param req 预下单请求参数
     * @return
     */
    default boolean confirmCheck(OrderConfirmReq req) {
        return true;
    }

    /**
     * 下单参数校验
     *
     * @param req 下单请求参数
     */
    default boolean submitCheck(OrderSubmitReq req) {
        return true;
    }

    /**
     * 预下单
     *
     * @param req  预下单请求参数
     * @param user 用户数据
     * @return
     */
    OrderConfirmResp confirm(C req, U user);

    /**
     * 提交订单
     *
     * @param req  下单请求参数
     * @param user 用户数据
     * @return
     */
    OrderSubmitResp submit(S req, U user);

    /**
     * 获取提交订单锁
     *
     * @param req  下单请求参数
     * @param user 用户数据
     * @return
     */
    default String getSubmitLockKey(OrderSubmitReq req, U user) {
        return String.valueOf(req.getBuyDemandOrderId());
    }
}

3、定义不同类型下单策略实现类,根据业务可以有多个实现类

java 复制代码
/**
 * 疫苗订单逻辑
 *
 */
@Slf4j
@Service
public class ConsultOrderStrategy implements OrderStrategy<OrderConfirmReq,OrderSubmitReq,YjkUserDetails> {

}
相关推荐
Benaso3 分钟前
Java,Golang,Rust 泛型的大体对比小记
java·golang·rust
程序员清风7 分钟前
什么时候会考虑用联合索引?如果只有一个条件查就没有建联合索引的必要了么?
java·后端·面试
Seven979 分钟前
【设计模式】掌握建造者模式:如何优雅地解决复杂对象创建难题?
java·后端·设计模式
Patrick_Wilson19 分钟前
🔥【全网首篇】30分钟带你从0到1搭建基于Lynx的跨端开发环境
前端·react.js·前端框架
Moment25 分钟前
前端 社招 面筋分享:前端两年都问些啥 ❓️❓️❓️
前端·javascript·面试
Moment25 分钟前
一坤时学习 TS 中的装饰器,让你写 NestJS 不再手软 😏😏😏
前端·javascript·面试
子洋26 分钟前
AnythingLLM + SearXNG 实现私有搜索引擎代理
前端·人工智能·后端
小满zs32 分钟前
React第二十九章(css in js)
前端·react.js
古柳_Deserts_X36 分钟前
Manus官方发布视频的1小时后就开始陆续有人注册了相关网站域名!原因就在于「新词新站」这4个字
前端·程序员·创业
自在如风。39 分钟前
MyBatis-Plus 使用技巧
java·mybatis·mybatis-plus