业务需求背景:有多个不同的数据源(商户信息、商户资产信息),管理人员希望以这些数据源的数据为基础,配置不同的话术、不同的过滤条件。当审查人员审查具体数据时,根据数据命中的条件,展示不同的话术。审查人员根据话术指引,详细检查各项数据,确保正常,然后打个勾,表示做好了检查。当所有的检查项都打上勾了,才能提交。
业务需求技术方案设计:将需求拆分,抽象出多个模块,数据源模块、检查项模块、数据源与检查项的匹配过滤模块。
talk is cheap,show me the code。
一、数据源模块
(1)商户信息数据模型
package com.zyong.zhibo.chapter1.formal;
import lombok.Data;
/**
* 商户信息
*/
@Data
public class MerchantInfo {
/**
* 商户编号
*/
private String merchantNo;
/**
* 商户名称
*/
private String merchantName;
}
(2)商户资产信息数据模型
package com.zyong.zhibo.chapter1.formal;
import lombok.Data;
/**
* 商户资产信息
*/
@Data
public class MerchantAssetInfo {
/**
* 资产ID
*/
private String assetId;
/**
* 资产名称
*/
private String assetName;
/**
* 资产类型
* 1:良性资产
* 2:可疑资产
* 3:坏资产
*/
private String assetType;
}
二、检查项模块
(1)检查项数据模型
package com.zyong.zhibo.chapter1.formal;
import lombok.Data;
import java.util.List;
/**
* 条件配置
*/
@Data
public class ConditionConfig {
/**
* 条件ID
*/
private int conditionId;
/**
* 条件名称、描述
*/
private String conditionName;
/**
* 匹配的数据源类型
*/
private String matchDataType;
/**
* 匹配内容
*/
private String matchContent;
/**
* 配置类型列表
*/
private List<String> matchTypeList;
@Override
public String toString() {
return "ConditionConfig{" +
"conditionId=" + conditionId +
", conditionName='" + conditionName + '\'' +
", matchDataType='" + matchDataType + '\'' +
", matchContent='" + matchContent + '\'' +
", matchTypeList=" + matchTypeList +
'}';
}
}
三、数据源与检查项的匹配过滤模块
(1)检查项服务
package com.zyong.zhibo.chapter1.formal;
import java.util.List;
/**
* 检查项服务
*/
public interface CheckService {
/**
* 检查项匹配
* @param configList
* @return
*/
List<ConditionConfig> matchCheckCondition(List<ConditionConfig> configList);
}
(2)检查项服务抽象类,实现核心执行逻辑。此处使用了模板方法设计模式,核心执行逻辑顺序已确定,并定义了抽象钩子方法,需要子类实现具体执行逻辑。
package com.zyong.zhibo.chapter1.formal;
import java.util.List;
import java.util.Collections;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.collections4.CollectionUtils;
public abstract class AbstractCheckService implements CheckService {
/**
* {@inheritDoc}
* @param configList
* @return
*/
@Override
public List<ConditionConfig> matchCheckCondition(List<ConditionConfig> configList) {
if (CollectionUtils.isEmpty(configList)) {
return Collections.emptyList();
}
// 1、对List<ConditionConfig> configList过滤
List<ConditionConfig> filterConfigList =
configList.stream()
.filter(p -> {
String matchDataType = p.getMatchDataType();
return StringUtils.equals(getDataType(), matchDataType);
})
.collect(Collectors.toList());
// 2、获取数据源以及匹配过滤
filterConfigList = doMatch(configList);
if (CollectionUtils.isEmpty(filterConfigList)) {
return Collections.emptyList();
}
return filterConfigList;
}
/**
* 获取数据源以及匹配逻辑
* @param configList
* @return
*/
abstract protected List<ConditionConfig> doMatch(List<ConditionConfig> configList);
/**
* 获取数据源类型
* @return
*/
abstract protected String getDataType();
}
(3)商户信息检查项服务实现逻辑:以商户信息为基础数据源,匹配过滤配置的检查项数据。涉及的条件有:商户编号。此处使用了策略设计模式,商户信息检查项服务,是检查项服务的其中一种具体实现。
package com.zyong.zhibo.chapter1.formal;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.collections4.CollectionUtils;
public class MerchantCheckService extends AbstractCheckService {
private static final String DATA_TYPE = "Merchant";
/**
* {@inheritDoc}
* @param configList
* @return
*/
@Override
protected List<ConditionConfig> doMatch(List<ConditionConfig> configList) {
if (CollectionUtils.isEmpty(configList)) {
return Collections.emptyList();
}
List<ConditionConfig> resultConfigList = new ArrayList<>();
// 1、获取数据源
MerchantInfo data = wrapMerchantInfo();
// 2、数据匹配过滤
for (ConditionConfig q : configList) {
if (StringUtils.equals(data.getMerchantNo(), q.getMatchContent())) {
resultConfigList.add(q);
}
}
return resultConfigList;
}
/**
* {@inheritDoc}
* @return
*/
@Override
protected String getDataType() {
return DATA_TYPE;
}
/**
* 拼装MerchantInfo数据
* @return
*/
private MerchantInfo wrapMerchantInfo() {
MerchantInfo data = new MerchantInfo();
data.setMerchantNo("0001");
data.setMerchantName("商户0001");
return data;
}
}
(4)商户资产信息检查项服务实现逻辑:以商户资产信息为基础数据源,匹配过滤配置的检查项数据。涉及的条件有:资产ID、资产类型。此处使用了策略设计模式,商户资产信息检查项服务,是检查项服务的其中一种具体实现。
package com.zyong.zhibo.chapter1.formal;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.collections4.CollectionUtils;
public class MerchantAssetCheckService extends AbstractCheckService {
private static final String DATA_TYPE = "MerchantAsset";
/**
* {@inheritDoc}
* @param configList
* @return
*/
@Override
protected List<ConditionConfig> doMatch(List<ConditionConfig> configList) {
if (CollectionUtils.isEmpty(configList)) {
return Collections.emptyList();
}
List<ConditionConfig> resultConfigList = new ArrayList<>();
// 1、获取数据源
List<MerchantAssetInfo> dataList = wrapMerchantAssetList();
if (CollectionUtils.isEmpty(dataList)) {
return Collections.emptyList();
}
// 2、数据匹配过滤
for (MerchantAssetInfo p : dataList) {
for (ConditionConfig q : configList) {
if (StringUtils.equals(p.getAssetId(), q.getMatchContent())) {
if (CollectionUtils.isNotEmpty(q.getMatchTypeList())) {
if (q.getMatchTypeList().contains(p.getAssetType())) {
resultConfigList.add(q);
}
} else {
resultConfigList.add(q);
}
}
}
}
return resultConfigList;
}
/**
* {@inheritDoc}
* @return
*/
@Override
protected String getDataType() {
return DATA_TYPE;
}
/**
* 组装MerchantAssetInfo List数据
* @return
*/
private List<MerchantAssetInfo> wrapMerchantAssetList() {
List<MerchantAssetInfo> dataList = new ArrayList<>();
MerchantAssetInfo data1 = new MerchantAssetInfo();
data1.setAssetId("0001");
data1.setAssetName("资产0001");
data1.setAssetType("2");
MerchantAssetInfo data2 = new MerchantAssetInfo();
data2.setAssetId("0002");
data2.setAssetName("资产0002");
data2.setAssetType("3");
dataList.add(data1);
dataList.add(data2);
return dataList;
}
}
(5)检查项服务执行链服务:执行链使用了过滤器设计模式。
package com.zyong.zhibo.chapter1.formal;
import java.util.*;
import org.apache.commons.collections4.CollectionUtils;
public class CheckCommonChain {
private List<CheckService> checkServiceList;
public CheckCommonChain() {
checkServiceList = new ArrayList<>();
}
public void addCheckService(CheckService checkService) {
checkServiceList.add(checkService);
}
public List<ConditionConfig> matchConfig(List<ConditionConfig> sourceConfigList) {
List<ConditionConfig> resultList = new ArrayList<>();
// 过滤器模式获取所有数据源的过滤结果
for (CheckService p : checkServiceList) {
List<ConditionConfig> matchList = p.matchCheckCondition(sourceConfigList);
if (CollectionUtils.isNotEmpty(matchList)) {
resultList.addAll(matchList);
}
}
if (CollectionUtils.isEmpty(resultList)) {
return Collections.emptyList();
}
// 对结果去重处理
Set<ConditionConfig> configSet = new TreeSet<>(Comparator.comparing(ConditionConfig :: getConditionId));
configSet.addAll(resultList);
return new ArrayList<>(configSet);
}
}
四、测试类:(1)将各数据源检查项服务实现类的实例,添加到执行链的列表中;(2)组装检查项配置数据;(3)执行链执行,输出最终匹配过滤的检查项配置数据。
package com.zyong.zhibo.chapter1.formal;
import java.util.List;
import java.util.ArrayList;
import org.apache.commons.collections4.CollectionUtils;
public class CheckTest {
public static void main(String[] args) {
CheckCommonChain chain = new CheckCommonChain();
chain.addCheckService(new MerchantCheckService());
chain.addCheckService(new MerchantAssetCheckService());
List<ConditionConfig> sourceConfigList = new ArrayList<>();
ConditionConfig config1 = new ConditionConfig();
config1.setConditionId(1);
config1.setConditionName("存在不良资产,需要仔细比对");
config1.setMatchContent("0001");
config1.setMatchDataType("MerchantAsset");
config1.setMatchTypeList(
new ArrayList<String>() {{
add("2");
add("3");
}}
);
ConditionConfig config2 = new ConditionConfig();
config2.setConditionId(2);
config2.setConditionName("商户存在欺诈行为,需要仔细核查");
config2.setMatchContent("0001");
config2.setMatchDataType("Merchant");
ConditionConfig config3 = new ConditionConfig();
config3.setConditionId(3);
config3.setConditionName("商户资质较差");
config3.setMatchContent("0002");
config3.setMatchDataType("Merchant");
sourceConfigList.add(config1);
sourceConfigList.add(config2);
List<ConditionConfig> resultList = chain.matchConfig(sourceConfigList);
if (CollectionUtils.isNotEmpty(resultList)) {
resultList.stream().forEach(p -> {
System.out.println(p.toString());
});
} else {
System.out.println("no data");
}
}
}
五、测试结果:输出的是最终需要展示给审查人员的检查项配置数据。
六、总结
(1)可以根据代码中的数据模型,建立数据库、表,将数据落地到数据库中。
(2)通用检查项功能设计,代码简洁但不简单,灵活运用各类设计模式,代码扩展性强,复用性强。
(3)设计通用的技术方案,完成业务需求的同时,技术能力不断飞升。