文章目录
活动时长计算尝试用Lambada 实现责任链的代码优化
记录下一次尝试用Lambada 来替代责任链模式下的各种接口实现的优化代码的处理。
原流程
原代码
主控流程
public ActivityCalcCustomerCostCheckResult calcCustomerCostCheckResult(
ActivityCalcCustomerCostCheckResultDto dto) {
ActivityCalcCustomerCostCheckResult result = new ActivityCalcCustomerCostCheckResult();
//活动时长
Integer activityDuration = dto.getActivityDuration()!=null?dto.getActivityDuration():0;
result.setDuration(activityDuration);
//调整后的时长
Integer adjustedDuration = dto.getAdjustedDuration()!=null?dto.getAdjustedDuration():0;
result.setAdjustedDuration(adjustedDuration);
//调整的时长
result.setAdjustDuration(adjustedDuration-activityDuration);
//计费规则
result.setType(dto.getChargeTypeEnum().value());
//倍率
result.setRate(dto.getRate);
//标准费率时长
Integer standardDuration = calcCustomerCostCheckStandardDuration(dto);
result.setStandardDuration(standardDuration);
//计算结算时长
Integer balanceDuration = standardDuration!=null?
calcBalanceDuration(dto.getChargeTypeEnum(),rate,standardDuration):0;
//结算时长
result.setBalanceDuration(balanceDuration);
//满意度
result.setSatisfaction(dto.getSatisfaction().value());
//报价类型
result.setQuoteType(dto.getQuoteType().value());
return result;
}
计算标准费率时长子过程
/**
* 计算客户费用核对标准时长(分钟)
* @param dto
* @return java.lang.Integer
*/
private Integer calcCustomerCostCheckStandardDuration(ActivityCalcCustomerCostCheckResultDto dto) {
if (dto.getAdjustedDuration()==null || dto.getAdjustedDuration() <=0){
return 0;
}
if(dto.getServiceType()==null){
return null;
}
switch (dto.getServiceType()){
case TELEPHONE_INTERVIEW:
case FACE_INTERVIEW:
return calcCustomerCostCheckStandardDurationForTelephoneInterviewAndFaceInterview(dto);
case ROADSHOW:
return calcCustomerCostCheckStandardDurationForRoadshow(dto);
case QUESTIONNAIRE:
return calcCustomerCostCheckStandardDurationForQuestionnaire(dto);
default:
return null;
}
}
电访和面访活动计算标准费率时长
/**
* 计算电话访谈和面访活动的客户标准费率结算时长
* @param parameter
* @return java.lang.Integer
*/
private Integer calcCustomerCostCheckStandardDurationForTelephoneInterviewAndFaceInterview(
ActivityCalcCustomerCostCheckResultDto parameter) {
if(parameter.getQuoteType()==null){
return null;
}
int duration = parameter.getAdjustedDuration();
switch (parameter.getQuoteType()){
case HOUR:
//按小时
if (BooleanUtils.isTrue(parameter.getIfRelationConf())) {
//是跟进活动 0<h<=15 -> 15
if (duration <= 15) {
return 15;
}else{
// h > 15 -> 按照实际分钟结算
return duration;
}
}else{
if(BooleanUtils.isTrue(parameter.getIfTestCall())){
//测试活动
if(parameter.getTestCallLimitMinute()!=null && duration<=parameter.getTestCallLimitMinute()){
// h<=客户测试活动限定时长 -> 0
return 0;
}
// h > 客户测试活动限定时长 ->按常规活动规则计算
}
//常规活动
if (CustomerSatisfactionEnum.NO.equals(parameter.getSatisfaction())) {
//客户不满意
if (duration < 10L) {
// h <10 -> 0
return 0;
} else if (duration >= 10 && duration <= 30) {
// 10<=h <=30 -> 30
return 30;
} else{
// h > 30 -> 按照实际分钟结算
return duration;
}
} else {
//客户满意/一般
if(duration <= 30){
return 30;
}else{
// h > 30 -> 按照实际分钟结算
return duration;
}
}
}
case SCENE:
//按场次 (电话访谈和面访暂不支持按场次计算时长)
default:
return null;
}
}
路演活动计算标准费率时长
/**
* 计算路演活动的客户标准费率结算时长
* @param parameter
* @return java.lang.Integer
*/
private Integer calcCustomerCostCheckStandardDurationForRoadshow(
ActivityCalcCustomerCostCheckResultDto parameter) {
return parameter.getAdjustedDuration();
}
问卷活动计算标准费率时长
/**
* 计算问卷活动的标准费率时长
* @param parameter
* @return java.lang.Integer
*/
private Integer calcCustomerCostCheckStandardDurationForQuestionnaire(
ActivityCalcCustomerCostCheckResultDto parameter) {
return parameter.getAdjustedDuration();
}
计算结算时长子过程
/**
* 计算结算时长
* @param chargeTypeEnum
* @param rate
* @param duration
* @return java.lang.Integer
*/
private Integer calcBalanceDuration(ActivityCustomerChargeTypeEnum chargeTypeEnum,BigDecimal rate,Integer duration){
switch (chargeTypeEnum){
case PACKAGE_DEDUCTION:
return rate.multiply(new BigDecimal(duration)).setScale(0, RoundingMode.CEILING).intValue();
case FREE:
return 0;
case TRIAL:
case POST_PAID:
return duration;
}
return duration;
}
新流程
新的版本需要调整活动时长计算规则,对于常规的活动标准费率计算增加了起访时长和跳档时长两个参数(其实就是把旧版的默认的这两个值是固定,调整为根据每个客户动态获取的)。并且移除了按场次
还是按时长
这两种报价形式对活动时长计算的影响。
新版本的规则流程图如下:
由于新版本本身就要修改这套活动时长计算代码,因此趁机对这套代码做一次重构优化,优化的思路为:
1、拆分各模式的下具体的计算规则,让各个规则独立。
2、通过用Map组合不同的计算规则
3、通过参数选择Map中不同的规则来计算处理。
4、通过一个活动时长计算上下文来解决参数传递的问题。
新代码
定义活动时长计算上下文类及相关Dto成员类型
活动时长计算上下文类
/**
* 活动计算时长上下文
* @author 徐明龙 XuMingLong 2022-08-04
*/
@Data @AllArgsConstructor @NoArgsConstructor @Builder
public class ActivityCalcDurationContentDto {
/**
* 活动计算时长规则配置
*/
private ActivityCalcDurationRuleConfigDto ruleConfig;
/**
* 活动计算时长输入数据
*/
private ActivityCalcDurationInputDataDto inputData;
/**
* 活动计算时长输出结果
*/
private ActivityCalcDurationOutPutResultDto outPutResult;
}
活动计算时长规则配置Dto
/**
* 活动计算时长规则配置
*/
@Data @AllArgsConstructor @NoArgsConstructor @Builder
public class ActivityCalcDurationRuleConfigDto {
/**
* 起访时长
*/
private int durationOfAtLeast;
/**
* 跳档时长
*/
private int durationOfEachGrade;
/**
* 测试活动限定时长
*/
private Integer durationOfTestCallLimit;
}
活动计算时长输入数据Dto
/**
* 活动计算时长输入数据
*/
@Data @AllArgsConstructor @NoArgsConstructor @Builder
public class ActivityCalcDurationInputDataDto {
/**
* 活动时长
*/
private int activityDuration;
/**
* 调整后的时长
*/
private int adjustedDuration;
/**
* 倍率
*/
private BigDecimal rate;
/**
* 活动类型
*/
private ActivityServiceTypeEnum serviceType;
/**
* 客户满意度
*/
private CustomerSatisfactionEnum satisfaction;
/**
* 报价类型
*/
private CustomerPricingQuoteTypeEnum quoteType;
/**
* 计费规则
*/
private ActivityCustomerChargeTypeEnum chargeType;
/**
* 活动服务目的
*/
private ActivityServicePurposeEnum servicePurpose;
}
活动计算时长输出结果Dto
/**
* 活动计算时长输出结果
* @author 徐明龙 XuMingLong 2022-08-04
*/
@Data
public class ActivityCalcDurationOutPutResultDto {
/**
* 活动时长
*/
private Integer activityDuration;
/**
* 调整后的时长
*/
private Integer adjustedDuration;
/**
* 调整的时长
*/
private Integer adjustDuration;
/**
* 标准费率时长
*/
private Integer standardDuration;
/**
* 结算时长
*/
private Integer balanceDuration;
/**
* 倍率
*/
private BigDecimal rate;
/**
* 活动类型
*/
private ActivityServiceTypeEnum serviceType;
/**
* 客户满意度
*/
private CustomerSatisfactionEnum satisfaction;
/**
* 报价类型
*/
private CustomerPricingQuoteTypeEnum quoteType;
/**
* 计费规则
*/
private ActivityCustomerChargeTypeEnum chargeType;
/**
* 活动服务目的
*/
private ActivityServicePurposeEnum servicePurpose;
}
定义规则存储Map
/**
* 活动类型计算时长责任链Map
*/
private final static Map<ActivityServiceTypeEnum, List<Function<ActivityCalcDurationContentDto,Boolean>>> calcDurationChainMap = new EnumMap<>(ActivityServiceTypeEnum.class);
/**
* 活动服务目的标准费率时长计算方法Map
*/
private final static Map<ActivityServicePurposeEnum, Consumer<ActivityCalcDurationContentDto>> calcStandardDurationFunByServicePurposeMap = new EnumMap<>(ActivityServicePurposeEnum.class);
/**
* 客户满意度标准费率时长计算方法Map
*/
private final static Map<CustomerSatisfactionEnum, Consumer<ActivityCalcDurationContentDto>> calcStandardDurationFunBySatisfactionMap = new EnumMap<>(CustomerSatisfactionEnum.class);
/**
* 活动扣费类型计算结算时长的方法Map
*/
private final static Map<ActivityCustomerChargeTypeEnum,Consumer<ActivityCalcDurationContentDto>> calcBalanceDurationFunMap = new EnumMap<>(ActivityCustomerChargeTypeEnum.class);
各子模式的计算规则实现代码
活动计算时长上下文-预处理器
/**
* 预处理处理器
*/
private final static Function<ActivityCalcDurationContentDto,Boolean> preHandler = (contentDto)->{
//获取输入参数
ActivityCalcDurationInputDataDto inputData = contentDto.getInputData();
ActivityCalcDurationOutPutResultDto outPutResult = new ActivityCalcDurationOutPutResultDto();
outPutResult.setActivityDuration(inputData.getActivityDuration());
outPutResult.setAdjustedDuration(inputData.getAdjustedDuration());
outPutResult.setAdjustDuration(0);
outPutResult.setStandardDuration(0);
outPutResult.setBalanceDuration(0);
outPutResult.setRate(inputData.getRate());
outPutResult.setServiceType(inputData.getServiceType());
outPutResult.setSatisfaction(inputData.getSatisfaction());
outPutResult.setQuoteType(inputData.getQuoteType());
outPutResult.setChargeType(inputData.getChargeType());
outPutResult.setServicePurpose(inputData.getServicePurpose());
contentDto.setOutPutResult(outPutResult);
return true;
};
常规的计算标准费率时长方法
/**
* 常规的计算标准费率时长方法
*/
private final static Consumer<ActivityCalcDurationContentDto> normalCalcStandardDurationFun= (contentDto)->{
//获取计算规则配置
ActivityCalcDurationRuleConfigDto ruleConfig = contentDto.getRuleConfig();
//获取输入参数
ActivityCalcDurationInputDataDto inputData = contentDto.getInputData();
if(inputData.getAdjustedDuration() <=0){
//调整后的时长小于等于0,则标准费率时长也为0
contentDto.getOutPutResult().setStandardDuration(0);
}else if(inputData.getAdjustedDuration() <= ruleConfig.getDurationOfAtLeast()){
//调整后的时长<= 起访时长,标准费率时长 = 起访时长
contentDto.getOutPutResult().setStandardDuration(ruleConfig.getDurationOfAtLeast());
}else{
//否则标准费率时长= 起访时长+ceil((调整后的时长-起访时长)/跳档时长)*跳档时长
// 计算超过起访时长部分的时长
int exceedDuration = inputData.getAdjustedDuration()-ruleConfig.getDurationOfAtLeast();
//计算最终跳档时长
exceedDuration = (int)Math.ceil(exceedDuration*1.0/ruleConfig.getDurationOfEachGrade())*ruleConfig.getDurationOfEachGrade();
//计算标准费率时长
contentDto.getOutPutResult().setStandardDuration(ruleConfig.getDurationOfAtLeast() + exceedDuration);
}
};
计算服务目的处理器
/**
* 计算服务目的处理器
*/
private final static Function<ActivityCalcDurationContentDto,Boolean> calcServicePurposeHandler = (contentDto)->{
//获取输入参数
ActivityCalcDurationInputDataDto inputData = contentDto.getInputData();
if(inputData.getServicePurpose() == ActivityServicePurposeEnum.TEST){
//获取规则配置
ActivityCalcDurationRuleConfigDto ruleConfig = contentDto.getRuleConfig();
//没有测试活动时长限制的配置,则测试活动自动转换为常规活动
if(ruleConfig.getDurationOfTestCallLimit() == null ){
inputData.setServicePurpose(ActivityServicePurposeEnum.CONVENTION);
contentDto.getOutPutResult().setServicePurpose(inputData.getServicePurpose());
return true;
}
//调整后的时长 > 客户测试活动限定时长,则转变为常规活动
if(inputData.getAdjustedDuration() > ruleConfig.getDurationOfTestCallLimit()){
inputData.setServicePurpose(ActivityServicePurposeEnum.CONVENTION);
contentDto.getOutPutResult().setServicePurpose(inputData.getServicePurpose());
return true;
}
}
return true;
};
计算调整时长处理器
/**
* 计算调整时长处理器
*/
private final static Function<ActivityCalcDurationContentDto,Boolean> calcAdjustDurationHandler = (contentDto)->{
//获取输入参数
ActivityCalcDurationInputDataDto inputData = contentDto.getInputData();
//调整时长 = 调整后的时长 - 活动时长
contentDto.getOutPutResult().setAdjustDuration(inputData.getAdjustedDuration()-inputData.getActivityDuration());
return true;
};
计算标准费率时长处理器
/**
* 计算标准费率时长处理器
*/
private final static Function<ActivityCalcDurationContentDto,Boolean> calcStandardDurationHandler = (contentDto)->{
//获取输入参数
ActivityCalcDurationInputDataDto inputData = contentDto.getInputData();
//执行活动服务目的对应的处理方法
calcStandardDurationFunByServicePurposeMap.get(inputData.getServicePurpose()).accept(contentDto);
return true;
};
测试活动计算标准费率时长方法
/**
* 测试活动计算标准费率时长方法
*/
private final static Consumer<ActivityCalcDurationContentDto> calcStandardDurationFunForTest = (contentDto)->{
//测试活动的标准费率时长为0
contentDto.getOutPutResult().setStandardDuration(0);
};
常规活动计算标准费率时长方法
/**
* 常规活动计算标准费率时长方法
*/
private final static Consumer<ActivityCalcDurationContentDto> calcStandardDurationFunForConvention = (contentDto)->{
//获取输入参数
ActivityCalcDurationInputDataDto inputData = contentDto.getInputData();
//执行活动客户满意度对应的处理方法
calcStandardDurationFunBySatisfactionMap.get(inputData.getSatisfaction()).accept(contentDto);
};
跟进活动计算标准费率时长方法
/**
* 跟进活动计算标准费率时长方法
*/
private final static Consumer<ActivityCalcDurationContentDto> calcStandardDurationFunForRelation = (contentDto)->{
//获取输入参数
ActivityCalcDurationInputDataDto inputData = contentDto.getInputData();
if(inputData.getAdjustedDuration() <=0){
//调整后的时长小于等于0,则标准费率时长也为 0
contentDto.getOutPutResult().setStandardDuration(0);
}else if(inputData.getAdjustedDuration() < 15){
//调整后的时长小于15分钟,则标准费率时长= 15
contentDto.getOutPutResult().setStandardDuration(15);
}else{
contentDto.getOutPutResult().setStandardDuration(inputData.getAdjustedDuration());
}
};
客户满意度为满意情况下的计算标准费率时长方法
/**
* 客户满意度为满意情况下的计算标准费率时长方法
*/
private final static Consumer<ActivityCalcDurationContentDto> calcStandardDurationFunWhenSatisfactionYes = (contentDto)->{
//正常的标准费率时长计算处理
normalCalcStandardDurationFun.accept(contentDto);
};
客户满意度为一般情况下的计算标准费率时长方法
/**
* 客户满意度为一般情况下的计算标准费率时长方法
*/
private final static Consumer<ActivityCalcDurationContentDto> calcStandardDurationFunWhenSatisfactionCommon = (contentDto)->{
//正常的标准费率时长计算处理
normalCalcStandardDurationFun.accept(contentDto);
};
客户满意度为不满意的计算标准费率时长方法
/**
* 客户满意度为不满意的计算标准费率时长方法
*/
private final static Consumer<ActivityCalcDurationContentDto> calcStandardDurationFunWhenSatisfactionNo = (contentDto)->{
//获取输入参数
ActivityCalcDurationInputDataDto inputData = contentDto.getInputData();
if(inputData.getAdjustedDuration() <=0){
//调整后的时长小于等于0,则标准费率时长也为0
contentDto.getOutPutResult().setStandardDuration(0);
}else if(inputData.getAdjustedDuration() < 10){
//调整后的时长 < 10 ,标准费率时长 = 0
contentDto.getOutPutResult().setStandardDuration(0);
}else {
//正常的标准费率时长计算处理
normalCalcStandardDurationFun.accept(contentDto);
}
};
计算结算时长处理器
/**
* 计算结算时长处理器
*/
private final static Function<ActivityCalcDurationContentDto,Boolean> calcBalanceDurationHandler = (contentDto)->{
//获取输入参数
ActivityCalcDurationInputDataDto inputData = contentDto.getInputData();
//执行活动扣费类型的对应的处理方法
calcBalanceDurationFunMap.get(inputData.getChargeType()).accept(contentDto);
return true;
};
套餐扣费的计算结算时长的方法
/**
* 套餐扣费的计算结算时长的方法
*/
private final static Consumer<ActivityCalcDurationContentDto> calcBalanceDurationFunByPackageDeduction = (contentDto)->{
//获取输入参数
ActivityCalcDurationInputDataDto inputData = contentDto.getInputData();
//获取输出结果
ActivityCalcDurationOutPutResultDto outPutResult = contentDto.getOutPutResult();
if(outPutResult.getStandardDuration() == 0 ){
outPutResult.setBalanceDuration(0);
}else{
//结算时长 = ceil(报价倍率 * 标准倍率时长)
outPutResult.setBalanceDuration(
inputData.getRate()
.multiply(new BigDecimal(outPutResult.getStandardDuration()))
.setScale(0, RoundingMode.CEILING)
.intValue()
);
}
};
免费的计算结算时长的方法
/**
* 免费的计算结算时长的方法
*/
private final static Consumer<ActivityCalcDurationContentDto> calcBalanceDurationFunByFree = (contentDto)->{
//免费的结算时长为0
contentDto.getOutPutResult().setBalanceDuration(0);
};
优惠试用的计算结算时长的方法
/**
* 优惠试用的计算结算时长的方法
*/
private final static Consumer<ActivityCalcDurationContentDto> calcBalanceDurationFunByTrial = (contentDto)->{
//获取输出结果
ActivityCalcDurationOutPutResultDto outPutResult = contentDto.getOutPutResult();
//优惠的结算时长 = 标准费率时长
outPutResult.setBalanceDuration(outPutResult.getStandardDuration());
};
后付费的计算结算时长的方法
/**
* 后付费的计算结算时长的方法
*/
private final static Consumer<ActivityCalcDurationContentDto> calcBalanceDurationFunByPostPaid = (contentDto)->{
//获取输出结果
ActivityCalcDurationOutPutResultDto outPutResult = contentDto.getOutPutResult();
//后付费的结算时长 = 标准费率时长
outPutResult.setBalanceDuration(outPutResult.getStandardDuration());
};
终结处理器
目前不需要对上下文做额外的处理,保留这个是为了方便后续扩展
/**
* 终结处理器
*/
private final static Function<ActivityCalcDurationContentDto,Boolean> finisherHandler = (contentDto)->{
return true;
};
配置规则代码
目前所有类型的活动都可以使用同一套的规则配置,以后如果业务规则修改,也可以不同的活动类型使用不同的配置。
static{
//配置客户满意度活动标准费率计算方法
calcStandardDurationFunBySatisfactionMap.put(CustomerSatisfactionEnum.YES,
calcStandardDurationFunWhenSatisfactionYes);
calcStandardDurationFunBySatisfactionMap.put(CustomerSatisfactionEnum.COMMON,
calcStandardDurationFunWhenSatisfactionCommon);
calcStandardDurationFunBySatisfactionMap.put(CustomerSatisfactionEnum.NO,
calcStandardDurationFunWhenSatisfactionNo);
//配置服务目的计算方法Map
calcStandardDurationFunByServicePurposeMap.put(ActivityServicePurposeEnum.CONVENTION,calcStandardDurationFunForConvention);
calcStandardDurationFunByServicePurposeMap.put(ActivityServicePurposeEnum.TEST,calcStandardDurationFunForTest);
calcStandardDurationFunByServicePurposeMap.put(ActivityServicePurposeEnum.RELATION,
calcStandardDurationFunForRelation);
//配置活动扣费类型的计算方法Map
calcBalanceDurationFunMap.put(ActivityCustomerChargeTypeEnum.PACKAGE_DEDUCTION,calcBalanceDurationFunByPackageDeduction);
calcBalanceDurationFunMap.put(ActivityCustomerChargeTypeEnum.FREE,calcBalanceDurationFunByFree);
calcBalanceDurationFunMap.put(ActivityCustomerChargeTypeEnum.TRIAL,calcBalanceDurationFunByTrial);
calcBalanceDurationFunMap.put(ActivityCustomerChargeTypeEnum.POST_PAID,calcBalanceDurationFunByPostPaid);
//配置活动的计算规则责任链
List<Function<ActivityCalcDurationContentDto,Boolean>> calcDurationChain = List.of(
//预处理
preHandler,
//计算服务目的处理器
calcServicePurposeHandler,
//计算调整的时长处理器
calcAdjustDurationHandler,
//计算标准费率时长处理器
calcStandardDurationHandler,
//计算结算时长处理器
calcBalanceDurationHandler,
//终结处理器
finisherHandler
);
//配置电话访谈活动的计算时长的责任链
calcDurationChainMap.put(ActivityServiceTypeEnum.TELEPHONE_INTERVIEW, calcDurationChain);
//配置面访活动的计算时长的责任链
calcDurationChainMap.put(ActivityServiceTypeEnum.FACE_INTERVIEW, calcDurationChain);
//配置路演活动的计算时长的责任链
calcDurationChainMap.put(ActivityServiceTypeEnum.ROADSHOW, calcDurationChain);
//配置问卷活动的计算时长的责任链
calcDurationChainMap.put(ActivityServiceTypeEnum.QUESTIONNAIRE, calcDurationChain);
}
主控代码
/**
* 计算时长
* @param contentDto
* @return void
*/
public static void calcDuration(ActivityCalcDurationContentDto contentDto){
//获取责任链
List<Function<ActivityCalcDurationContentDto,Boolean>> handlerChain =
calcDurationChainMap.get(contentDto.getInputData().getServiceType());
for(Function<ActivityCalcDurationContentDto,Boolean> handler:handlerChain){
boolean executeNext = handler.apply(contentDto);
if(!executeNext){
break;
}
}
}