目录
订单来源pc端支付,然后不同支付方式和会员等级有不同策略(交叉有相同)
订单来源手机端支付,然后不同支付方式和会员等级有不同策略(交叉有相同)
一、前言
平时的开发中往往需要嵌套的策略去解决一定的业务或底层问题,如果在上一层已经通过工厂模式和策略模式的综合使用_xiaofeng10330111的博客-CSDN博客该方式进行了相关的策略模式的使用,但是在紧接着的下层逻辑上再次使用相同逻辑的策略往往会创建大量的工厂去实现,各策略也需要向工厂写入内容,这个时候我往往会使用注解方式去实现对应的下层策略模式,主要采用方式写三个业务场景来使用展示,以便供大家记录,写的不对的可留言指正。
二、采用简单的注解方式进行业务策略模式
(一)场景举例
假设有业务场景,前期针对现有业务整体上做了通用性配置(例如商家售卖的商品需要符合特定相关资质和业务管控,即:商家是药店,只能售卖一些符合要求的指定药物相关类目,同时具体买那些药物或医疗器械,该商家必须有对应的资质才可以),在指定配置下进行具体商品的售卖,原来线上的配置在业务发展过程中发现有些门店的有些售卖类目可以进行个性化管控(跨境门店可单独售卖一些指定的类目,这些类目符合国家要求)但对原配置不产生影响,我们配置了针对特定门店或品牌等可以进行有自己的个性化业务个性化配置,现要求个性化业务上通过配置对指定门店有不同的策略。假设其他上层策略我们已经实现,就单独个性化配置的控制规则上通过注解实现控制规则的策略实现。
(二)实现方案
1、基本代码准备
业务上个性换配置的主要信息如下
package org.zyf.javabasic.designpatterns.strategy.base;
import lombok.Builder;
import lombok.Data;
import java.util.List;
/**
* @author yanfengzhang
* @description 业务上个性化配置的规则
* @date 2022/3/5 00:33
*/
@Data
@Builder
public class OverrangeBusinessScope {
/**
* 唯一标识
*/
private long id;
/**
* 名称展示
*/
private String name;
/**
* 类型:品牌或门店等,例如:1-品牌;2-门店
*/
private int type;
/**
* 针对类型对应具体的影响范围
* 例如类型为门店,则对应对应需要干预的门店ID:110、112、112、113等
*/
private String effectRanges;
/**
* 控制规则:对原有配置的干预情况
*/
private int controlRule;
/**
* 校验规则:对原有校验结果进行干预
*/
private int validateRule;
/**
* 本次实际配置的类目信息
*/
private List<Long> relationCates;
/**
* 创建时间
*/
private long ctime;
/**
* 更新时间
*/
private long utime;
/**
* 创建人
*/
private String cname;
/**
* 修改人
*/
private String uname;
}
其中控制规则的常量选择如下:(只进行控制规则的,其他规则不做分析)
package org.zyf.javabasic.designpatterns.strategy.base;
/**
* @author yanfengzhang
* @description 超范围相关常量集合
* @date 2022/3/5 00:35
*/
public class OverrangeContants {
/**
* 控制规则
*/
public static class BizScopeControlRule {
/**
* 仅限指定类目
*/
public static final int ONLY = 1;
/**
* 需包含指定类目
*/
public static final int NEED = 2;
/**
* 不得包含指定类目
*/
public static final int NO = 3;
}
} 2、基本功能接口定义
按照不同的控制规则返回实际个性化门店或品牌的实际可以售卖的类目情况,
package org.zyf.javabasic.designpatterns.strategy.base;
import java.util.Collection;
/**
* @author yanfengzhang
* @description 规则控制基本实现接口定义
* @date 2022/3/5 00:37
*/
public interface ControlRuleHandler {
/**
* 根据当前配置类目信息进行分析处理
*
* @param cagegoryDealInfo 需要的处理的类目信息
* @return 最后可售卖的类目信息
* @throws Exception 业务异常
*/
Collection<Long> getCagegoryIds(CagegoryDealInfo cagegoryDealInfo) throws Exception;
}
其中需要入参为超范围经营类目处理整合信息CagegoryDealInfo,需要传入当前平台已配置的通用化可售卖类目情况以及对应业务个性化配置的干预类目信息,具体内容如下:
package org.zyf.javabasic.designpatterns.strategy.base;
import lombok.Builder;
import lombok.Data;
import java.util.Collection;
/**
* @author yanfengzhang
* @description 超范围经营类目处理整合信息
* @date 2022/3/3 00:36
*/
@Data
@Builder
public class CagegoryDealInfo {
/**
* 平台当前配置的后台类目信息
*/
private Collection<Long> bgCategoryIds;
/**
* 指定业务范围配置的后台类目信息
*/
private Collection<Long> bgCategoryIdsForBizScope;
} 3、定义注解与不同的策略实现
基本注解定义
package org.zyf.javabasic.designpatterns.strategy.base;
import org.springframework.stereotype.Service;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author yanfengzhang
* @description 基本控制规则注解
* @date 2022/3/5 00:38
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Service
public @interface ControlRuleHandlerType {
int controlRuleType();
} 策略1:控制规则,要求只返回个性化指定类目作为可售卖类目
package org.zyf.javabasic.designpatterns.strategy.base;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collection;
/**
* @author yanfengzhang
* @description 只返回指定类目信息
* @date 2022/3/5 00:40
*/
@ControlRuleHandlerType(controlRuleType = OverrangeContants.BizScopeControlRule.ONLY)
@Service
public class ControlRuleForDesignatedHandler implements ControlRuleHandler {
@Autowired
private ProductCategoryService productCategoryService;
/**
* 只返回指定类目信息
*
* @param cagegoryDealInfo 需要的处理的类目信息
* @return
*/
@Override
public Collection<Long> getCagegoryIds(CagegoryDealInfo cagegoryDealInfo) throws Exception {
return productCategoryService.fetchAllIdPathByCategoryId(cagegoryDealInfo.getBgCategoryIdsForBizScope());
}
}
策略2:控制规则,要求在原配置上增加个性化指定的一些类目,整体作为可售卖类目的情况
package org.zyf.javabasic.designpatterns.strategy.base;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* @author yanfengzhang
* @description 返回需包含指定类目信息
* @date 2022/3/5 00:42
*/
@ControlRuleHandlerType(controlRuleType = OverrangeContants.BizScopeControlRule.NEED)
@Service
public class ControlRuleForIncludeHandler implements ControlRuleHandler {
@Autowired
private ProductCategoryService productCategoryService;
/**
* 需要在原类目基础上增加指定类目信息
*
* @param cagegoryDealInfo 需要的处理的类目信息
* @return
*/
@Override
public Collection<Long> getCagegoryIds(CagegoryDealInfo cagegoryDealInfo) throws Exception {
Collection<Long> bgCategoryIds = cagegoryDealInfo.getBgCategoryIds();
Collection<Long> bgCategoryIdsForBizScope = cagegoryDealInfo.getBgCategoryIdsForBizScope();
/*若两边没有配置返回为空*/
if (CollectionUtils.isEmpty(bgCategoryIds) && CollectionUtils.isEmpty(bgCategoryIdsForBizScope)) {
return bgCategoryIds;
}
/*将信息整合全部返回*/
Collection<Long> finalCategoryIds = new java.util.ArrayList<Long>(Collections.EMPTY_SET);
/*业务上找到联级关系全部的*/
Set<Long> allCategorysForBizScope = new HashSet<>(productCategoryService.fetchAllIdPathByCategoryId(bgCategoryIdsForBizScope));
finalCategoryIds.addAll(bgCategoryIds);
finalCategoryIds.addAll(allCategorysForBizScope);
return finalCategoryIds;
}
}
策略3:控制规则,要求在原配置上去除个性化指定的一些类目,然后整体作为可售卖类目的情况
package org.zyf.javabasic.designpatterns.strategy.base;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author yanfengzhang
* @description 返回不得包含指定类目信息
* @date 2022/3/5 00:45
*/
@ControlRuleHandlerType(controlRuleType = OverrangeContants.BizScopeControlRule.NO)
@Service
public class ControlRuleForNotContainHandler implements ControlRuleHandler {
@Autowired
private ProductCategoryService productCategoryService;
/**
* 需要在原类目基础上去掉不得包含指定类目信息
*
* @param cagegoryDealInfo 需要的处理的类目信息
* @return
*/
@Override
public Collection<Long> getCagegoryIds(CagegoryDealInfo cagegoryDealInfo) throws Exception {
Collection<Long> bgCategoryIds = cagegoryDealInfo.getBgCategoryIds();
Collection<Long> bgCategoryIdsForBizScope = cagegoryDealInfo.getBgCategoryIdsForBizScope();
/*若两边没有配置返回为空*/
if (CollectionUtils.isEmpty(bgCategoryIds) && CollectionUtils.isEmpty(bgCategoryIdsForBizScope)) {
return bgCategoryIds;
}
if (CollectionUtils.isEmpty(bgCategoryIds)) {
return bgCategoryIds;
}
/*业务上找到联级关系全部的*/
Set<Long> allCategorysForBizScope = new HashSet<>(productCategoryService.fetchAllIdPathByCategoryId(bgCategoryIdsForBizScope));
return bgCategoryIds.stream().filter(bgCategoryId -> !allCategorysForBizScope.contains(bgCategoryId)).collect(Collectors.toList());
}
} 其中设置相关类目级别设置的可能是级层比较低,通过其他service进行补齐,本次不做复杂处理,只简单的为了测试随意写了一个,与实际处理无关,只为了测试。
package org.zyf.javabasic.designpatterns.strategy.base;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
/**
* @author yanfengzhang
* @description
* @date 2022/3/5 00:40
*/
@Service
public class ProductCategoryService {
/**
* 根据当前的类目信息获取对应全链路路径信息 (做一个简单模拟)
*
* @param categoryIds 当前类目信息
* @return
*/
public Collection<Long> fetchAllIdPathByCategoryId(Collection<Long> categoryIds) {
Collection<Long> allIdPath = new ArrayList<Long>(Collections.EMPTY_SET);
if (CollectionUtils.isEmpty(categoryIds)) {
return allIdPath;
}
categoryIds.forEach(categoryId -> {
if (categoryId == 1) {
allIdPath.add(1L);
return;
}
if (categoryId == 2) {
allIdPath.add(2L);
return;
}
if (categoryId == 3) {
allIdPath.add(3L);
return;
}
if (categoryId == 4) {
allIdPath.add(4L);
return;
}
if (categoryId == 5) {
allIdPath.add(5L);
}
});
return allIdPath;
}
}
4、业务实际使用
package org.zyf.javabasic.designpatterns.strategy.base;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.collections.CollectionUtils;
import org.assertj.core.util.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author yanfengzhang
* @description
* @date 2022/3/5 00:55
*/
@Service
@Log4j2
public class BizService {
private Map<Integer, ControlRuleHandler> brandControlRuleHandleMap;
/**
* 启动初始化各种类型的品牌控制规则处理类(通过ControlRuleHandlerType注解)
*
* @param brandControlRuleHandlers
*/
@Autowired
public void setBrandControlRuleHandleMap(List<ControlRuleHandler> brandControlRuleHandlers) {
// 注入各种类型的品牌控制规则处理类
brandControlRuleHandleMap = brandControlRuleHandlers.stream().collect(
Collectors.toMap(orderHandler -> AnnotationUtils.findAnnotation(orderHandler.getClass(), ControlRuleHandlerType.class).controlRuleType(),
v -> v, (v1, v2) -> v1));
}
/**
* 根据控制规则获取实际类目配置
*
* @param overrangeBusinessScope 个性化干预配置
* @param bgCategoryIds 实际现存配置
* @return 最终结果
* @throws Exception 业务异常
*/
public Collection<Long> getCagegoryIdsByControlRule(OverrangeBusinessScope overrangeBusinessScope, Collection<Long> bgCategoryIds) throws Exception {
if (null == overrangeBusinessScope && CollectionUtils.isEmpty(overrangeBusinessScope.getRelationCates())) {
return Lists.newArrayList();
}
/*1.获取指定控制范围内容*/
ControlRuleHandler controlRuleHandler = brandControlRuleHandleMap.get(overrangeBusinessScope.getControlRule());
/*2.分析对应指定类目信息与原类目信息进行处理*/
Collection<Long> bgCategoryIdsForBrand = overrangeBusinessScope.getRelationCates();
CagegoryDealInfo cagegoryDealInfo = CagegoryDealInfo.builder()
.bgCategoryIds(bgCategoryIds)
.bgCategoryIdsForBizScope(bgCategoryIdsForBrand)
.build();
return controlRuleHandler.getCagegoryIds(cagegoryDealInfo);
}
}
5、测试及结果展示
基本测试用例如下
package org.zyf.javabasic.designpatterns.strategy.base;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.zyf.javabasic.ZYFApplication;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/**
* @author yanfengzhang
* @description
* @date 2022/3/5 00:59
*/
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ZYFApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class BaseStrategyTest {
@Autowired
private BizService bizService;
@Test
public void testBizFunction() throws Exception {
log.info("业务场景1---业务原配置类目内容为(1,2,3),现在个性化处理配置需要在原类目基础上增加以下类目(4,5)");
Long[] bgCategoryIdInfo1 = {1L,2L,3L};
Collection<Long> bgCategoryIds1= Arrays.asList(bgCategoryIdInfo1);
Long[] relationCateInfo1 = {4L,5L};
List<Long> relationCates1 = Arrays.asList(relationCateInfo1);
OverrangeBusinessScope overrangeBusinessScope1 = OverrangeBusinessScope.builder()
.controlRule(OverrangeContants.BizScopeControlRule.NEED)
.relationCates(relationCates1).build();
log.info("业务场景1情况下最终得到的业务类目为:{}",bizService.getCagegoryIdsByControlRule(overrangeBusinessScope1,bgCategoryIds1));
log.info("业务场景2---业务原配置类目内容为(1,2,3),现在个性化处理配置需要在原类目基础上不能包含以下类目(1,2)");
Long[] bgCategoryIdInfo2 = {1L,2L,3L};
Collection<Long> bgCategoryIds2= Arrays.asList(bgCategoryIdInfo2);
Long[] relationCateInfo2 = {1L,2L};
List<Long> relationCates2 = Arrays.asList(relationCateInfo2);
OverrangeBusinessScope overrangeBusinessScope2 = OverrangeBusinessScope.builder()
.controlRule(OverrangeContants.BizScopeControlRule.NO)
.relationCates(relationCates2).build();
log.info("业务场景2情况下最终得到的业务类目为:{}",bizService.getCagegoryIdsByControlRule(overrangeBusinessScope2,bgCategoryIds2));
log.info("业务场景3---业务原配置类目内容为(1,2,3, 4, 5),现在个性化处理配置需要不在意原有配置,当前只能售卖类目为(2,5)");
Long[] bgCategoryIdInfo3 = {1L,2L,3L,4L,5L};
Collection<Long> bgCategoryIds3= Arrays.asList(bgCategoryIdInfo3);
Long[] relationCateInfo3 = {2L,5L};
List<Long> relationCates3 = Arrays.asList(relationCateInfo3);
OverrangeBusinessScope overrangeBusinessScope3 = OverrangeBusinessScope.builder()
.controlRule(OverrangeContants.BizScopeControlRule.ONLY)
.relationCates(relationCates3).build();
log.info("业务场景3情况下最终得到的业务类目为:{}",bizService.getCagegoryIdsByControlRule(overrangeBusinessScope3,bgCategoryIds3));
}
}
测试结果展示
三、采用组合的注解方式进行业务策略模式
(一)场景举例
假设我们在某个平台购物的时候,在订单生成后,我们对订单价格营销手段很简单,就是根据订单的来源(手机端或pc端)、支付方式(微信、支付宝或本地钱包)、对应用户的会员等级(新晋会员、白银会员、黄金会员、钻石会员等)等的不同会有不同的策略干预用户可享受的权益,具体如下:
订单来源pc端支付,然后不同支付方式和会员等级有不同策略
订单来源手机端支付,然后不同支付方式和会员等级有不同策略
此时按照注解方式实现相关的策略。以上场景只是为了我后续写代码方便,其实未必有该场景。
(二)实现方案
1、基本代码准备
我们前期先定义一个简单的基本消费订单类Order,基本内容主要包含以下一些基本订单信息:
package org.zyf.javabasic.designpatterns.strategy.combination;
import lombok.Data;
import java.math.BigDecimal;
/**
* @author yanfengzhang
* @description 订单实体类
* @date 2022/3/8 23:10
*/
@Data
public class Order {
/**
* 订单编号
*/
private String code;
/**
* 订单金额
*/
private BigDecimal amount;
/**
* 订单来源:手机端、pc端
*/
private OrderSourceEnum source;
/**
* 支付方式:微信支付、支付宝支付、银行卡支付、本地钱包支付等
*/
private OrderPayMethodEnum payMethod;
/**
* 订单支付会员归类:
*/
private MemberTypeEnum payMember;
}
同时,我们在定义一个实际消费完订单的结果类OrderConsumerResult,基本信息如下:
package org.zyf.javabasic.designpatterns.strategy.combination;
import lombok.Builder;
import lombok.Data;
import java.math.BigDecimal;
/**
* @author yanfengzhang
* @description 订单消费结果
* @date 2022/3/8 23:14
*/
@Data
@Builder
public class OrderConsumerResult {
/**
* 订单编号
*/
private String code;
/**
* 实际支付金额
*/
private BigDecimal amountSpent;
/**
* 本次订单可获取的权益情况 (只做简单测试,所以不做复杂逻辑处理了)
*/
private String right;
}
为了方便注解,相关基本信息我们使用枚举进行处理,相关枚举如下定义:
订单来源枚举
package org.zyf.javabasic.designpatterns.strategy.combination;
/**
* @author yanfengzhang
* @description 订单来源
* @date 2022/3/8 23:16
*/
public enum OrderSourceEnum {
PC(0, "pc端订单"),
MOBILE(1, "手机端订单");
/**
* 订单来源方
*/
private Integer source;
/**
* 描述
*/
private String desc;
public Integer getsource() {
return source;
}
public String getDesc() {
return desc;
}
OrderSourceEnum(Integer source) {
this.source = source;
}
OrderSourceEnum(Integer source, String desc) {
this.desc = desc;
this.source = source;
}
/**
* 根据订单来源类型source获取对应的订单来源类型枚举
*
* @param source 订单来源类型source
* @return
*/
public static OrderSourceEnum getEnumBySource(Integer source) {
for (OrderSourceEnum orderSourceEnum : OrderSourceEnum.values()) {
if (orderSourceEnum.getsource().equals(source)) {
return orderSourceEnum;
}
}
return null;
}
/**
* 根据订单来源类型描述获取对应的订单来源类型枚举
*
* @param desc 订单来源类型描述
* @return
*/
public static OrderSourceEnum getEnumByDesc(String desc) {
for (OrderSourceEnum orderSourceEnum : OrderSourceEnum.values()) {
if (orderSourceEnum.getDesc().equals(desc)) {
return orderSourceEnum;
}
}
return null;
}
/**
* 判断类型是否为指定类型
*
* @param source 订单来源类型source
* @return
*/
public static boolean isAppealTypeEnum(Integer source) {
if (source == null) {
return false;
}
for (OrderSourceEnum tempEnum : OrderSourceEnum.values()) {
if (tempEnum.getsource().equals(source)) {
return true;
}
}
return false;
}
}
订单支付方式枚举
package org.zyf.javabasic.designpatterns.strategy.combination;
/**
* @author yanfengzhang
* @description 订单支付方式枚举
* @date 2022/3/8 23:19
*/
public enum OrderPayMethodEnum {
WEIXIN(0, "微信支付"),
ZHIFUBAO(1, "支付宝支付"),
WALLET(2, "本地钱包支付");
/**
* 订单支付方式
*/
private Integer payType;
/**
* 描述
*/
private String desc;
public Integer getPayType() {
return payType;
}
public String getDesc() {
return desc;
}
OrderPayMethodEnum(Integer payType) {
this.payType = payType;
}
OrderPayMethodEnum(Integer payType, String desc) {
this.desc = desc;
this.payType = payType;
}
/**
* 根据订单支付类型payType获取对应的订单支付类型枚举
*
* @param payType 订单支付类型payType
* @return
*/
public static OrderPayMethodEnum getEnumBypayType(Integer payType) {
for (OrderPayMethodEnum orderPayMethodEnum : OrderPayMethodEnum.values()) {
if (orderPayMethodEnum.getPayType().equals(payType)) {
return orderPayMethodEnum;
}
}
return null;
}
/**
* 根据订单支付类型描述获取对应的订单支付类型枚举
*
* @param desc 订单支付类型描述
* @return
*/
public static OrderPayMethodEnum getEnumByDesc(String desc) {
for (OrderPayMethodEnum orderPayMethodEnum : OrderPayMethodEnum.values()) {
if (orderPayMethodEnum.getDesc().equals(desc)) {
return orderPayMethodEnum;
}
}
return null;
}
/**
* 判断类型是否为指定类型
*
* @param payType 订单支付类型payType
* @return
*/
public static boolean isAppealTypeEnum(Integer payType) {
if (payType == null) {
return false;
}
for (OrderPayMethodEnum tempEnum : OrderPayMethodEnum.values()) {
if (tempEnum.getPayType().equals(payType)) {
return true;
}
}
return false;
}
}
订单会员等级枚举
package org.zyf.javabasic.designpatterns.strategy.combination;
/**
* @author yanfengzhang
* @description 订单会员类型
* @date 2022/3/8 23:22
*/
public enum MemberTypeEnum {
NONACTIVATED(0, "非会员,未激活"),
NEWCOMER(1, "新晋会员"),
SILVER(2, "白银会员"),
GOLD(3, "黄金会员"),
PLATINUM(4, "铂金会员");
/**
* 订单会员类型
*/
private Integer type;
/**
* 描述
*/
private String desc;
public Integer getType() {
return type;
}
public String getDesc() {
return desc;
}
MemberTypeEnum(Integer type) {
this.type = type;
}
MemberTypeEnum(Integer type, String desc) {
this.desc = desc;
this.type = type;
}
/**
* 根据订单会员类型type获取对应的订单会员类型枚举
*
* @param type 订单会员类型type
* @return
*/
public static MemberTypeEnum getEnumBytype(Integer type) {
for (MemberTypeEnum memberTypeEnum : MemberTypeEnum.values()) {
if (memberTypeEnum.getType().equals(type)) {
return memberTypeEnum;
}
}
return null;
}
/**
* 根据订单会员类型描述获取对应的订单会员类型枚举
*
* @param desc 订单会员类型描述
* @return
*/
public static MemberTypeEnum getEnumByDesc(String desc) {
for (MemberTypeEnum memberTypeEnum : MemberTypeEnum.values()) {
if (memberTypeEnum.getDesc().equals(desc)) {
return memberTypeEnum;
}
}
return null;
}
/**
* 判断类型是否为指定类型
*
* @param type 订单会员类型type
* @return
*/
public static boolean isAppealTypeEnum(Integer type) {
if (type == null) {
return false;
}
for (MemberTypeEnum tempEnum : MemberTypeEnum.values()) {
if (tempEnum.getType().equals(type)) {
return true;
}
}
return false;
}
}
2、基本功能接口定义
我们根据当前订单的基本信息来决定最终的消费结果,具体结果定义简单如下:
package org.zyf.javabasic.designpatterns.strategy.combination;
/**
* @author yanfengzhang
* @description 规定处理订单的方法
* @date 2022/3/8 23:25
*/
public interface OrderHandler {
/**
* 处理订单生成消费结果
*
* @param order 订单基本信息
* @return 订单实际消费结果
*/
OrderConsumerResult handle(Order order);
}
3、定义注解与实现
按照当前的基本策略处理可以基本定义注解如下:
package org.zyf.javabasic.designpatterns.strategy.combination;
import org.springframework.stereotype.Service;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author yanfengzhang
* @description 表示订单处理类型
* @date 2022/3/8 23:27
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Service
public @interface OrderHandlerType {
/**
* 指明来源
*/
OrderSourceEnum source();
/**
* 支付方式
*/
OrderPayMethodEnum payMethod();
/**
* 会员类型
*/
MemberTypeEnum memberType();
}
注解本身为接口的语法糖,所以为了组合有效,我们必要实现对应的基本逻辑和相关的hashcode和equals方法等,先简单处理如下展示:
package org.zyf.javabasic.designpatterns.strategy.combination;
import java.lang.annotation.Annotation;
/**
* @author yanfengzhang
* @description 实现注解语法糖的实现类
* @date 2022/3/8 23:33
*/
public class OrderHandlerTypeImpl implements OrderHandlerType {
private OrderSourceEnum source;
private OrderPayMethodEnum payMethod;
private MemberTypeEnum memberType;
OrderHandlerTypeImpl(OrderSourceEnum source, OrderPayMethodEnum payMethod, MemberTypeEnum memberType) {
this.source = source;
this.payMethod = payMethod;
this.memberType = memberType;
}
@Override
public OrderSourceEnum source() {
return source;
}
@Override
public OrderPayMethodEnum payMethod() {
return payMethod;
}
@Override
public MemberTypeEnum memberType() {
return memberType;
}
@Override
public Class<? extends Annotation> annotationType() {
return OrderHandlerType.class;
}
@Override
public int hashCode() {
int hashCode = 0;
hashCode += (127 * "source".hashCode()) ^ source.hashCode();
hashCode += (127 * "payMethod".hashCode()) ^ payMethod.hashCode();
hashCode += (127 * "memberType".hashCode()) ^ memberType.hashCode();
return hashCode;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof OrderHandlerType)) {
return false;
}
OrderHandlerType other = (OrderHandlerType) obj;
return source == other.source()
&& payMethod == other.payMethod()
&& memberType == other.memberType();
}
}
4、各策略实现举例
按原图上来说的话,每一个都需要写对应的策略实现类,由于只是为了我门基本功能的测试,我在这边暂时不会写那么多,只选几种,将其功能简单展示如下:
pc端微信支付,用户为未开通状态
package org.zyf.javabasic.designpatterns.strategy.combination;
import lombok.extern.log4j.Log4j2;
/**
* @author yanfengzhang
* @description pc端微信支付,用户为未开通状态
* @date 2022/3/8 23:35
*/
@Log4j2
@OrderHandlerType(source = OrderSourceEnum.PC, payMethod = OrderPayMethodEnum.WEIXIN, memberType = MemberTypeEnum.NONACTIVATED)
public class PCWXNonactivatedOrderHandler implements OrderHandler {
/**
* 进行原价支付
*
* @param order 基本订单情况
* @return 实际消费结果
*/
@Override
public OrderConsumerResult handle(Order order) {
return OrderConsumerResult.builder()
.code(order.getCode())
.amountSpent(order.getAmount())
.right("该会员不参与其他权益履约适宜").build();
}
}
pc端支付宝支付,用户当前为新晋会员
package org.zyf.javabasic.designpatterns.strategy.combination;
import lombok.extern.log4j.Log4j2;
import java.math.BigDecimal;
/**
* @author yanfengzhang
* @description pc端支付宝支付,用户当前为新晋会员
* @date 2022/3/8 23:37
*/
@Log4j2
@OrderHandlerType(source = OrderSourceEnum.PC, payMethod = OrderPayMethodEnum.ZHIFUBAO, memberType = MemberTypeEnum.NEWCOMER)
public class PCZFBNewcomerOrderHandler implements OrderHandler {
/**
* 支持满120减100优惠,同时赠送新晋套餐
*
* @param order 基本订单情况
* @return 实际消费结果
*/
@Override
public OrderConsumerResult handle(Order order) {
BigDecimal amount = order.getAmount();
BigDecimal basePrice = BigDecimal.valueOf(120);
BigDecimal discountedPrice = BigDecimal.valueOf(100);
if (amount.compareTo(basePrice) < 0) {
return OrderConsumerResult.builder()
.code(order.getCode())
.amountSpent(amount)
.right("当前没有新增权益内容").build();
}
return OrderConsumerResult.builder()
.code(order.getCode())
.amountSpent(amount.subtract(discountedPrice))
.right("赠送新晋套餐").build();
}
}
pc端本地钱包支付支付,用户为未开通状态
package org.zyf.javabasic.designpatterns.strategy.combination;
/**
* @author yanfengzhang
* @description
* @date 2022/3/8 23:38
*/
import lombok.extern.log4j.Log4j2;
/**
* @author yanfengzhang
* @description pc端本地钱包支付支付,用户为未开通状态
* @date 2022/3/8 23:39
*/
@Log4j2
@OrderHandlerType(source = OrderSourceEnum.PC, payMethod = OrderPayMethodEnum.WALLET, memberType = MemberTypeEnum.NONACTIVATED)
public class PCWalletNonactivatedOrderHandler implements OrderHandler {
/**
* 存入钱包50得60,订单满30减30
*
* @param order 基本订单情况
* @return 实际消费结果
*/
@Override
public OrderConsumerResult handle(Order order) {
return OrderConsumerResult.builder()
.code(order.getCode())
.amountSpent(order.getAmount())
.right("存入钱包50得60,订单满30减30").build();
}
}
手机端微信支付,用户为未开通状态
package org.zyf.javabasic.designpatterns.strategy.combination;
import java.math.BigDecimal;
/**
* @author yanfengzhang
* @description 手机端微信支付,用户为未开通状态
* @date 2022/3/8 23:42
*/
@OrderHandlerType(source = OrderSourceEnum.MOBILE, payMethod = OrderPayMethodEnum.WEIXIN, memberType = MemberTypeEnum.NONACTIVATED)
public class MobileWXNonactivatedOrderHandler implements OrderHandler {
/**
* 订单满10减3,送腾讯会员3天
*
* @param order 基本订单情况
* @return 实际消费结果
*/
@Override
public OrderConsumerResult handle(Order order) {
BigDecimal amount = order.getAmount();
BigDecimal basePrice = BigDecimal.valueOf(10);
BigDecimal discountedPrice = BigDecimal.valueOf(3);
if (amount.compareTo(basePrice) < 0) {
return OrderConsumerResult.builder()
.code(order.getCode())
.amountSpent(amount)
.right("当前没有新增权益内容").build();
}
return OrderConsumerResult.builder()
.code(order.getCode())
.amountSpent(amount.subtract(discountedPrice))
.right("送腾讯会员3天").build();
}
}
手机端支付宝支付,用户当前为新晋会员
package org.zyf.javabasic.designpatterns.strategy.combination;
import lombok.extern.log4j.Log4j2;
import java.math.BigDecimal;
/**
* @author yanfengzhang
* @description 手机端支付宝支付,用户当前为新晋会员
* @date 2022/3/8 23:46
*/
@Log4j2
@OrderHandlerType(source = OrderSourceEnum.MOBILE, payMethod = OrderPayMethodEnum.ZHIFUBAO, memberType = MemberTypeEnum.NEWCOMER)
public class MobileZFBNewcomerOrderHandler implements OrderHandler {
/**
* 支持满130减120优惠,同时赠送新晋套餐
*
* @param order 基本订单情况
* @return 实际消费结果
*/
@Override
public OrderConsumerResult handle(Order order) {
BigDecimal amount = order.getAmount();
BigDecimal basePrice = BigDecimal.valueOf(130);
BigDecimal discountedPrice = BigDecimal.valueOf(120);
if (amount.compareTo(basePrice) < 0) {
return OrderConsumerResult.builder()
.code(order.getCode())
.amountSpent(amount)
.right("当前没有新增权益内容").build();
}
return OrderConsumerResult.builder()
.code(order.getCode())
.amountSpent(amount.subtract(discountedPrice))
.right("赠送新晋套餐").build();
}
}
本地钱包支付宝支付,用户当前为黄金会员
package org.zyf.javabasic.designpatterns.strategy.combination;
import lombok.extern.log4j.Log4j2;
import java.math.BigDecimal;
/**
* @author yanfengzhang
* @description 本地钱包支付宝支付,用户当前为黄金会员
* @date 2022/3/8 23:48
*/
@Log4j2
@OrderHandlerType(source = OrderSourceEnum.MOBILE, payMethod = OrderPayMethodEnum.WALLET, memberType = MemberTypeEnum.GOLD)
public class MobileWalletGoldOrderHandler implements OrderHandler {
/**
* 存入钱包200得400,套餐支持满100减20优惠,同时送减免整合套餐和内部优惠无限制满5减5(2张)
*
* @param order 基本订单情况
* @return 实际消费结果
*/
@Override
public OrderConsumerResult handle(Order order) {
BigDecimal amount = order.getAmount();
BigDecimal basePrice = BigDecimal.valueOf(100);
BigDecimal discountedPrice = BigDecimal.valueOf(20);
if (amount.compareTo(basePrice) < 0) {
return OrderConsumerResult.builder()
.code(order.getCode())
.amountSpent(amount)
.right("当前没有新增权益内容").build();
}
return OrderConsumerResult.builder()
.code(order.getCode())
.amountSpent(amount.subtract(discountedPrice))
.right("存入钱包200得400,同时送减免整合套餐和内部优惠无限制满5减5(2张)").build();
}
}
5、业务实际使用
我们写一个实际业务使用的简单例子,实现实际业务订单处理服务定义如下:
package org.zyf.javabasic.designpatterns.strategy.combination;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* @author yanfengzhang
* @description 实际业务订单处理服务
* @date 2022/3/8 23:53
*/
@Service
public class OrderService {
private Map<OrderHandlerType, OrderHandler> orderHandleMap;
@Autowired
public void setOrderHandleMap(List<OrderHandler> orderHandlers) {
// 注入各种类型的订单处理类
orderHandleMap = orderHandlers.stream().collect(
Collectors.toMap(orderHandler -> AnnotationUtils.findAnnotation(orderHandler.getClass(), OrderHandlerType.class),
v -> v, (v1, v2) -> v1));
}
/**
* 只做测试,不进行前置校验或其他订单逻辑,主要就直接按策略看最后的结果
*
* @param order 当前订单信息
* @return 订单处理结果
*/
public OrderConsumerResult orderService(Order order) {
// 通过订单来源、支付方式和会员等级获取对应的handler
OrderHandlerType orderHandlerType = new OrderHandlerTypeImpl(order.getSource(), order.getPayMethod(), order.getPayMember());
OrderHandler orderHandler = orderHandleMap.get(orderHandlerType);
if (Objects.isNull(orderHandler)) {
/*没有匹配的策略直接原价处理并不参与权益处理*/
return OrderConsumerResult.builder()
.code(order.getCode())
.amountSpent(order.getAmount())
.right("该会员不参与其他权益履约适宜").build();
}
return orderHandler.handle(order);
}
}
6、测试及结果展示
基本测试如下,进行了六个场景的模拟:
package org.zyf.javabasic.designpatterns.strategy.combination;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.zyf.javabasic.ZYFApplication;
import java.math.BigDecimal;
import java.util.concurrent.ExecutionException;
/**
* @author yanfengzhang
* @description
* @date 2022/3/8 23:58
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ZYFApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Slf4j
public class OrderServiceTest {
@Autowired
private OrderService orderService;
@Test
public void testCombination() throws ExecutionException {
log.info("测试场景1:(订单编号-000001)用户未开通会员,在pc端通过微信支付,当前实际购买总价为131.7元");
log.info("按该场景实际符合策略:用户需进行原价支付");
Order order1 = new Order();
order1.setCode("000001");
order1.setAmount(new BigDecimal("131.7"));
order1.setSource(OrderSourceEnum.PC);
order1.setPayMethod(OrderPayMethodEnum.WEIXIN);
order1.setPayMember(MemberTypeEnum.NONACTIVATED);
OrderConsumerResult orderConsumerResult1 = orderService.orderService(order1);
log.info("测试场景1结果:用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",
orderConsumerResult1.getCode(), orderConsumerResult1.getAmountSpent(), orderConsumerResult1.getRight());
log.info("测试场景2:(订单编号-000002)用户为新晋会员,在pc端通过支付宝支付,当前实际购买总价为131.7元");
log.info("按该场景实际符合策略:支持满120减100优惠,同时赠送新晋套餐");
Order order2 = new Order();
order2.setCode("000002");
order2.setAmount(new BigDecimal("131.7"));
order2.setSource(OrderSourceEnum.PC);
order2.setPayMethod(OrderPayMethodEnum.ZHIFUBAO);
order2.setPayMember(MemberTypeEnum.NEWCOMER);
OrderConsumerResult orderConsumerResult2 = orderService.orderService(order2);
log.info("测试场景1结果:用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",
orderConsumerResult2.getCode(), orderConsumerResult2.getAmountSpent(), orderConsumerResult2.getRight());
log.info("测试场景3:(订单编号-000003)用户未开通会员,在pc端通过本地钱包支付,当前实际购买总价为131.7元");
log.info("按该场景实际符合策略:存入钱包50得60,订单满30减30");
Order order3 = new Order();
order3.setCode("000003");
order3.setAmount(new BigDecimal("131.7"));
order3.setSource(OrderSourceEnum.PC);
order3.setPayMethod(OrderPayMethodEnum.WALLET);
order3.setPayMember(MemberTypeEnum.NONACTIVATED);
OrderConsumerResult orderConsumerResult3 = orderService.orderService(order3);
log.info("测试场景1结果:用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",
orderConsumerResult3.getCode(), orderConsumerResult3.getAmountSpent(), orderConsumerResult3.getRight());
log.info("测试场景4:(订单编号-000004)用户未开通会员,在手机端通过微信支付,当前实际购买总价为131.7元");
log.info("按该场景实际符合策略:订单满10减3,送腾讯会员3天");
Order order4 = new Order();
order4.setCode("000004");
order4.setAmount(new BigDecimal("131.7"));
order4.setSource(OrderSourceEnum.MOBILE);
order4.setPayMethod(OrderPayMethodEnum.WEIXIN);
order4.setPayMember(MemberTypeEnum.NONACTIVATED);
OrderConsumerResult orderConsumerResult4 = orderService.orderService(order4);
log.info("测试场景1结果:用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",
orderConsumerResult4.getCode(), orderConsumerResult4.getAmountSpent(), orderConsumerResult4.getRight());
log.info("测试场景5:(订单编号-000005)用户为新晋会员,在手机端通过支付宝支付,当前实际购买总价为131.7元");
log.info("按该场景实际符合策略:支持满130减120优惠,同时赠送新晋套餐");
Order order5 = new Order();
order5.setCode("000005");
order5.setAmount(new BigDecimal("131.7"));
order5.setSource(OrderSourceEnum.MOBILE);
order5.setPayMethod(OrderPayMethodEnum.ZHIFUBAO);
order5.setPayMember(MemberTypeEnum.NEWCOMER);
OrderConsumerResult orderConsumerResult5 = orderService.orderService(order5);
log.info("测试场景1结果:用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",
orderConsumerResult5.getCode(), orderConsumerResult5.getAmountSpent(), orderConsumerResult5.getRight());
log.info("测试场景6:(订单编号-000006)用户为黄金会员,在手机端通过本地钱包支付,当前实际购买总价为131.7元");
log.info("按该场景实际符合策略:存入钱包200得400,套餐支持满100减20优惠,同时送减免整合套餐和内部优惠无限制满5减5(2张)");
Order order6 = new Order();
order6.setCode("000006");
order6.setAmount(new BigDecimal("131.7"));
order6.setSource(OrderSourceEnum.MOBILE);
order6.setPayMethod(OrderPayMethodEnum.WALLET);
order6.setPayMember(MemberTypeEnum.GOLD);
OrderConsumerResult orderConsumerResult6 = orderService.orderService(order6);
log.info("测试场景1结果:用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",
orderConsumerResult6.getCode(), orderConsumerResult6.getAmountSpent(), orderConsumerResult6.getRight());
}
}
最后基本测试结果如下展示
四、采用复杂的注解方式进行业务策略模式
(一)场景举例
假设我们在某个平台购物的时候,在订单生成后,我们对订单价格营销手段很简单,就是根据订单的来源(手机端或pc端)、支付方式(微信、支付宝或本地钱包)、对应用户的会员等级(新晋会员、白银会员、黄金会员、钻石会员等)等的不同来干预用户可享受的权益,但是有的组合方式享受的权益内容是一样的,具体如下:
订单来源pc端支付,然后不同支付方式和会员等级有不同策略(交叉有相同)
订单来源手机端支付,然后不同支付方式和会员等级有不同策略(交叉有相同)
此时按照注解方式实现相关的策略,要求别写重复策略代码(因为我知道可以按照上面的组合方式改注解标记来实现,然后copy代码就可以)。以上场景只是为了我后续写代码方便,其实未必有该场景。
(二)实现方案
1、基本代码准备
本部分的基本代码内容和上面三-(二)-1的内容相一致,不在展示。
2、基本功能接口定义
本部分的基本代码内容和上面三-(二)-2的内容相一致,不在展示。
3、定义注解
基本上来看的话,我们当前的需求在支付方式上是有重叠的,所以我们可以将支付方式按数组方式进行定义,具体如下:
package org.zyf.javabasic.designpatterns.strategy.complex;
import org.springframework.stereotype.Service;
import org.zyf.javabasic.designpatterns.strategy.combination.MemberTypeEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderPayMethodEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderSourceEnum;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author yanfengzhang
* @description 订单处理基本注解
* @date 2022/3/11 23:41
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Service
public @interface OrderProcessorType {
/**
* 指明来源
*/
OrderSourceEnum source();
/**
* 支付方式
*/
OrderPayMethodEnum[] payMethod();
/**
* 会员类型
*/
MemberTypeEnum memberType();
}
4、不同策略的实现举例
因为策略比较多,我们依旧选取几个进行展示即可:
pc端微信或支付宝支付,用户当前为新晋会员
package org.zyf.javabasic.designpatterns.strategy.complex;
import lombok.extern.log4j.Log4j2;
import org.zyf.javabasic.designpatterns.strategy.combination.MemberTypeEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.Order;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderConsumerResult;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderHandler;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderPayMethodEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderSourceEnum;
import java.math.BigDecimal;
/**
* @author yanfengzhang
* @description pc端微信或支付宝支付,用户当前为新晋会员
* @date 2022/3/11 23:44
*/
@Log4j2
@OrderProcessorType(source = OrderSourceEnum.PC, payMethod = {OrderPayMethodEnum.ZHIFUBAO, OrderPayMethodEnum.WEIXIN}, memberType = MemberTypeEnum.NEWCOMER)
public class PCWZNewcomerOrderCpxHandler implements OrderHandler {
/**
* 支持满100减50优惠,同时赠送新晋套餐
*
* @param order 基本订单情况
* @return 实际消费结果
*/
@Override
public OrderConsumerResult handle(Order order) {
BigDecimal amount = order.getAmount();
BigDecimal basePrice = BigDecimal.valueOf(100);
BigDecimal discountedPrice = BigDecimal.valueOf(50);
if (amount.compareTo(basePrice) < 0) {
return OrderConsumerResult.builder()
.code(order.getCode())
.amountSpent(amount)
.right("当前没有新增权益内容").build();
}
return OrderConsumerResult.builder()
.code(order.getCode())
.amountSpent(amount.subtract(discountedPrice))
.right("赠送新晋套餐").build();
}
}
pc端支付宝支付,用户当前为黄金会员
package org.zyf.javabasic.designpatterns.strategy.complex;
import lombok.extern.log4j.Log4j2;
import org.zyf.javabasic.designpatterns.strategy.combination.MemberTypeEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.Order;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderConsumerResult;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderHandler;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderPayMethodEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderSourceEnum;
import java.math.BigDecimal;
/**
* @author yanfengzhang
* @description pc端支付宝支付,用户当前为黄金会员
* @date 2022/3/11 23:46
*/
@Log4j2
@OrderProcessorType(source = OrderSourceEnum.PC, payMethod = OrderPayMethodEnum.ZHIFUBAO, memberType = MemberTypeEnum.GOLD)
public class PCZFBGoldOrderCpxHandler implements OrderHandler {
/**
* 套餐支持满100减40优惠,同时送减免整合套餐
*
* @param order 基本订单情况
* @return 实际消费结果
*/
@Override
public OrderConsumerResult handle(Order order) {
BigDecimal amount = order.getAmount();
BigDecimal basePrice = BigDecimal.valueOf(100);
BigDecimal discountedPrice = BigDecimal.valueOf(40);
if (amount.compareTo(basePrice) < 0) {
return OrderConsumerResult.builder()
.code(order.getCode())
.amountSpent(amount)
.right("当前没有新增权益内容").build();
}
return OrderConsumerResult.builder()
.code(order.getCode())
.amountSpent(amount.subtract(discountedPrice))
.right("送减免整合套餐").build();
}
}
pc端本地钱包支付,用户当前为白银会员
package org.zyf.javabasic.designpatterns.strategy.complex;
import lombok.extern.log4j.Log4j2;
import org.zyf.javabasic.designpatterns.strategy.combination.MemberTypeEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.Order;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderConsumerResult;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderHandler;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderPayMethodEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderSourceEnum;
import java.math.BigDecimal;
/**
* @author yanfengzhang
* @description pc端本地钱包支付,用户当前为白银会员
* @date 2022/3/11 23:47
*/
@Log4j2
@OrderProcessorType(source = OrderSourceEnum.PC, payMethod = OrderPayMethodEnum.WALLET, memberType = MemberTypeEnum.SILVER)
public class PCWalletSilverOrderCpxHandler implements OrderHandler {
/**
* 套餐支持满100减10优惠,同时送减免整合套餐和内部优惠无限制满5减5(2张)
*
* @param order 基本订单情况
* @return 实际消费结果
*/
@Override
public OrderConsumerResult handle(Order order) {
BigDecimal amount = order.getAmount();
BigDecimal basePrice = BigDecimal.valueOf(100);
BigDecimal discountedPrice = BigDecimal.valueOf(10);
if (amount.compareTo(basePrice) < 0) {
return OrderConsumerResult.builder()
.code(order.getCode())
.amountSpent(amount)
.right("当前没有新增权益内容").build();
}
return OrderConsumerResult.builder()
.code(order.getCode())
.amountSpent(amount.subtract(discountedPrice))
.right("送减免整合套餐和内部优惠无限制满5减5(2张)").build();
}
}
手机端端微信或支付宝支付,用户当前未开通会员
package org.zyf.javabasic.designpatterns.strategy.complex;
import lombok.extern.log4j.Log4j2;
import org.zyf.javabasic.designpatterns.strategy.combination.MemberTypeEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.Order;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderConsumerResult;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderHandler;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderPayMethodEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderSourceEnum;
import java.math.BigDecimal;
/**
* @author yanfengzhang
* @description 手机端端微信或支付宝支付,用户当前未开通会员
* @date 2022/3/11 23:50
*/
@Log4j2
@OrderProcessorType(source = OrderSourceEnum.MOBILE, payMethod = {OrderPayMethodEnum.ZHIFUBAO, OrderPayMethodEnum.WEIXIN}, memberType = MemberTypeEnum.NONACTIVATED)
public class MobileWZNonactivatedOrderCpxHandler implements OrderHandler {
/**
* 订单满10减3,送腾讯会员7天或爱奇艺会员7天(随机)
*
* @param order 基本订单情况
* @return 实际消费结果
*/
@Override
public OrderConsumerResult handle(Order order) {
BigDecimal amount = order.getAmount();
BigDecimal basePrice = BigDecimal.valueOf(10);
BigDecimal discountedPrice = BigDecimal.valueOf(3);
if (amount.compareTo(basePrice) < 0) {
return OrderConsumerResult.builder()
.code(order.getCode())
.amountSpent(amount)
.right("当前没有新增权益内容").build();
}
return OrderConsumerResult.builder()
.code(order.getCode())
.amountSpent(amount.subtract(discountedPrice))
.right("送腾讯会员7天或爱奇艺会员7天(随机)").build();
}
}
手机端微信支付,用户当前为黄金会员
package org.zyf.javabasic.designpatterns.strategy.complex;
import lombok.extern.log4j.Log4j2;
import org.zyf.javabasic.designpatterns.strategy.combination.MemberTypeEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.Order;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderConsumerResult;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderHandler;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderPayMethodEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderSourceEnum;
/**
* @author yanfengzhang
* @description 手机端微信支付,用户当前为黄金会员
* @date 2022/3/11 23:52
*/
@Log4j2
@OrderProcessorType(source = OrderSourceEnum.MOBILE, payMethod = OrderPayMethodEnum.WEIXIN, memberType = MemberTypeEnum.GOLD)
public class MobileWXGoldOrderCpxHandler implements OrderHandler {
/**
* 套餐支持满160减140优惠、减110、减100各一张优惠,同时送特别礼品(随机)
*
* @param order 基本订单情况
* @return 实际消费结果
*/
@Override
public OrderConsumerResult handle(Order order) {
return OrderConsumerResult.builder()
.code(order.getCode())
.amountSpent(order.getAmount())
.right("本次套餐支持满160减140优惠、减110、减100各一张优惠,同时送特别礼品(随机),后续履约").build();
}
}
手机端本地钱包支付,用户当前为铂金会员
package org.zyf.javabasic.designpatterns.strategy.complex;
import lombok.extern.log4j.Log4j2;
import org.zyf.javabasic.designpatterns.strategy.combination.MemberTypeEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.Order;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderConsumerResult;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderHandler;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderPayMethodEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderSourceEnum;
/**
* @author yanfengzhang
* @description 手机端本地钱包支付,用户当前为铂金会员
* @date 2022/3/11 23:55
*/
@Log4j2
@OrderProcessorType(source = OrderSourceEnum.MOBILE, payMethod = OrderPayMethodEnum.WALLET, memberType = MemberTypeEnum.PLATINUM)
public class MobileWalletPlatinumOrderCpxHandler implements OrderHandler {
/**
* 存入钱包500得800,套餐支持满100减50优惠、减40、减30各一张优惠,同时送特别礼品(随机)和内部优惠无限制满5减5(3张)
*
* @param order 基本订单情况
* @return 实际消费结果
*/
@Override
public OrderConsumerResult handle(Order order) {
return OrderConsumerResult.builder()
.code(order.getCode())
.amountSpent(order.getAmount())
.right("存入钱包500得800,套餐支持满100减50优惠、减40、减30各一张优惠,同时送特别礼品(随机)和内部优惠无限制满5减5(3张),后续履约").build();
}
}
5、业务实际应用
在业务实际调用上,需要去匹配对应的策略,所以我自己先定义分发存储processorRepository,其key为我们以上的相关组装,对应的value为我们实际的策略实现,以下我初步写的基本代码:
策略的基本组合key
package org.zyf.javabasic.designpatterns.strategy.complex;
import lombok.Builder;
import lombok.Data;
import org.zyf.javabasic.designpatterns.strategy.combination.MemberTypeEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderPayMethodEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderSourceEnum;
import java.util.Objects;
/**
* @author yanfengzhang
* @description 策略的基本组合体
* @date 2022/3/11 23:59
*/
@Data
@Builder
public class StrategyInfo {
/**
* 指明来源
*/
OrderSourceEnum source;
/**
* 支付方式
*/
OrderPayMethodEnum payMethod;
/**
* 会员类型
*/
MemberTypeEnum memberType;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof StrategyInfo)) {
return false;
}
StrategyInfo that = (StrategyInfo) o;
return source == that.source &&
payMethod == that.payMethod &&
memberType == that.memberType;
}
@Override
public int hashCode() {
return Objects.hash(source, payMethod, memberType);
}
}
实际业务服务
package org.zyf.javabasic.designpatterns.strategy.complex;
import com.google.common.collect.Maps;
import lombok.extern.log4j.Log4j2;
import org.reflections.Reflections;
import org.springframework.stereotype.Service;
import org.zyf.javabasic.designpatterns.strategy.combination.MemberTypeEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.Order;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderConsumerResult;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderHandler;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderPayMethodEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderSourceEnum;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* @author yanfengzhang
* @description 实际业务订单处理服务
* @date 2022/3/12 00:00
*/
@Service
@Log4j2
public class OrderCpxService {
/**
* 策略表
*/
private static Map<StrategyInfo, Class> processorRepository = Maps.newHashMap();
static {
Reflections reflections = new Reflections("org.zyf.javabasic.designpatterns.strategy.complex");
Set<Class<?>> classSet = reflections.getTypesAnnotatedWith(OrderProcessorType.class);
for (Class<?> cl : classSet) {
Annotation[] annotations = cl.getAnnotations();
if (annotations != null) {
for (Annotation annotation : annotations) {
if (annotation instanceof OrderProcessorType) {
OrderProcessorType dispatch = (OrderProcessorType) annotation;
OrderSourceEnum source = dispatch.source();
OrderPayMethodEnum[] payMethods = dispatch.payMethod();
MemberTypeEnum memberType = dispatch.memberType();
for (OrderPayMethodEnum payMethod : payMethods) {
processorRepository.put(StrategyInfo.builder().source(source).payMethod(payMethod).memberType(memberType).build(), cl);
}
}
}
}
}
}
/**
* 只做测试,不进行前置校验或其他订单逻辑,主要就直接按策略看最后的结果
*
* @param order 当前订单信息
* @return 订单处理结果
*/
public OrderConsumerResult orderService(Order order) throws Exception {
StrategyInfo strategyInfo = StrategyInfo.builder()
.source(order.getSource())
.payMethod(order.getPayMethod())
.memberType(order.getPayMember()).build();
try {
Constructor constructor = processorRepository.get(strategyInfo).getConstructor();
OrderHandler orderHandler = (OrderHandler) constructor.newInstance();
if (Objects.isNull(orderHandler)) {
/*没有匹配的策略直接原价处理并不参与权益处理*/
return OrderConsumerResult.builder()
.code(order.getCode())
.amountSpent(order.getAmount())
.right("该会员不参与其他权益履约适宜").build();
}
return orderHandler.handle(order);
} catch (Exception e) {
log.error("OrderCpxService orderService error,order={}", order);
throw new Exception("依旧当前订单信息处理营销信息失败!");
}
}
}
6、测试及结果展示
基本的测试代码
package org.zyf.javabasic.designpatterns.strategy.complex;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.zyf.javabasic.ZYFApplication;
import org.zyf.javabasic.designpatterns.strategy.combination.MemberTypeEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.Order;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderConsumerResult;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderPayMethodEnum;
import org.zyf.javabasic.designpatterns.strategy.combination.OrderSourceEnum;
import java.math.BigDecimal;
/**
* @author yanfengzhang
* @description
* @date 2022/3/12 00:13
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ZYFApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Slf4j
public class OrderCpxServiceTest {
@Autowired
private OrderCpxService orderCpxService;
@Test
public void testComplex() throws Exception {
log.info("测试场景1:(订单编号-000001)用户为新晋会员,在pc端通过微信或支付宝支付,当前实际购买总价为131.7元");
log.info("按该场景实际符合策略:支持满100减50优惠,同时赠送新晋套餐");
Order order1 = new Order();
order1.setCode("000001");
order1.setAmount(new BigDecimal("131.7"));
order1.setSource(OrderSourceEnum.PC);
order1.setPayMethod(OrderPayMethodEnum.WEIXIN);
order1.setPayMember(MemberTypeEnum.NEWCOMER);
OrderConsumerResult orderConsumerResult11 = orderCpxService.orderService(order1);
log.info("测试场景1结果:(在微信支付)用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",
orderConsumerResult11.getCode(), orderConsumerResult11.getAmountSpent(), orderConsumerResult11.getRight());
order1.setPayMethod(OrderPayMethodEnum.ZHIFUBAO);
OrderConsumerResult orderConsumerResult12 = orderCpxService.orderService(order1);
log.info("测试场景1结果:(在支付宝支付)用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",
orderConsumerResult12.getCode(), orderConsumerResult12.getAmountSpent(), orderConsumerResult12.getRight());
log.info("测试场景2:(订单编号-000002)用户为黄金会员,在pc端通过支付宝支付,当前实际购买总价为131.7元");
log.info("按该场景实际符合策略:套餐支持满100减40优惠,同时送减免整合套餐");
Order order2 = new Order();
order2.setCode("000002");
order2.setAmount(new BigDecimal("131.7"));
order2.setSource(OrderSourceEnum.PC);
order2.setPayMethod(OrderPayMethodEnum.ZHIFUBAO);
order2.setPayMember(MemberTypeEnum.GOLD);
OrderConsumerResult orderConsumerResult2 = orderCpxService.orderService(order2);
log.info("测试场景2结果:用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",
orderConsumerResult2.getCode(), orderConsumerResult2.getAmountSpent(), orderConsumerResult2.getRight());
log.info("测试场景3:(订单编号-000003)用户为白银会员,在pc端通过本地钱包支付,当前实际购买总价为131.7元");
log.info("按该场景实际符合策略:套餐支持满100减10优惠,同时送减免整合套餐和内部优惠无限制满5减5(2张)");
Order order3 = new Order();
order3.setCode("000003");
order3.setAmount(new BigDecimal("131.7"));
order3.setSource(OrderSourceEnum.PC);
order3.setPayMethod(OrderPayMethodEnum.WALLET);
order3.setPayMember(MemberTypeEnum.SILVER);
OrderConsumerResult orderConsumerResult3 = orderCpxService.orderService(order3);
log.info("测试场景3结果:用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",
orderConsumerResult3.getCode(), orderConsumerResult3.getAmountSpent(), orderConsumerResult3.getRight());
log.info("测试场景4:(订单编号-000004)用户未开通会员,在手机端通过微信或支付宝支付,当前实际购买总价为131.7元");
log.info("按该场景实际符合策略:订单满10减3,送腾讯会员7天或爱奇艺会员7天(随机)");
Order order4 = new Order();
order4.setCode("000004");
order4.setAmount(new BigDecimal("131.7"));
order4.setSource(OrderSourceEnum.MOBILE);
order4.setPayMethod(OrderPayMethodEnum.WEIXIN);
order4.setPayMember(MemberTypeEnum.NONACTIVATED);
OrderConsumerResult orderConsumerResult41 = orderCpxService.orderService(order4);
log.info("测试场景4结果:(在微信支付)用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",
orderConsumerResult41.getCode(), orderConsumerResult41.getAmountSpent(), orderConsumerResult41.getRight());
order4.setPayMethod(OrderPayMethodEnum.ZHIFUBAO);
OrderConsumerResult orderConsumerResult42 = orderCpxService.orderService(order4);
log.info("测试场景4结果:(在微信支付)用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",
orderConsumerResult42.getCode(), orderConsumerResult42.getAmountSpent(), orderConsumerResult42.getRight());
log.info("测试场景5:(订单编号-000005)用户为黄金会员,在手机端通过微信支付,当前实际购买总价为131.7元");
log.info("按该场景实际符合策略:套餐支持满160减140优惠、减110、减100各一张优惠,同时送特别礼品(随机)");
Order order5 = new Order();
order5.setCode("000005");
order5.setAmount(new BigDecimal("131.7"));
order5.setSource(OrderSourceEnum.MOBILE);
order5.setPayMethod(OrderPayMethodEnum.WEIXIN);
order5.setPayMember(MemberTypeEnum.GOLD);
OrderConsumerResult orderConsumerResult5 = orderCpxService.orderService(order5);
log.info("测试场景5结果:用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",
orderConsumerResult5.getCode(), orderConsumerResult5.getAmountSpent(), orderConsumerResult5.getRight());
log.info("测试场景6:(订单编号-000006)用户为铂金会员,在手机端通过本地钱包支付,当前实际购买总价为131.7元");
log.info("按该场景实际符合策略:存入钱包500得800,套餐支持满100减50优惠、减40、减30各一张优惠,同时送特别礼品(随机)和内部优惠无限制满5减5(3张)");
Order order6 = new Order();
order6.setCode("000006");
order6.setAmount(new BigDecimal("131.7"));
order6.setSource(OrderSourceEnum.MOBILE);
order6.setPayMethod(OrderPayMethodEnum.WALLET);
order6.setPayMember(MemberTypeEnum.PLATINUM);
OrderConsumerResult orderConsumerResult6 = orderCpxService.orderService(order6);
log.info("测试场景6结果:用户订单为[{}],用户需要支付的金额为{}元,用户享受权益信息:[{}]",
orderConsumerResult6.getCode(), orderConsumerResult6.getAmountSpent(), orderConsumerResult6.getRight());
}
}
测试结果展示













还没有评论,来说两句吧...