商品规则设计 - 969251639/study GitHub Wiki

每个商品都有自己的规则,那么为了可以快速开发且不影响以前的规则,考虑扩展性和可维护性,设计如下:
整个流程最重要是这个规则树的解析和校验


规则的解析树定义了多个节点,每个节点可以包含各自的子节点(多个节点也可以组成一个规则组),节点与节点之间通过用And或Or的方式串联成单向链表

实现

  1. 规则树的定义
public class GoodsRuleTree {
	private List<Node> nodes = new ArrayList<Node>();

	public List<Node> getNodes() {
		return nodes;
	}

	public void setNodes(List<Node> nodes) {
		this.nodes = nodes;
	}

	public Node genNode() {
		return new Node();
	}

	class Node {//树的节点
		private Rule rule;//具体的规则
		private String name;//节点名称
		private String remark;//备注
		private String calculateType;//连接方式,AND或OR
		private boolean hasNext;是否有下一个节点
		private GoodsRuleTree goodsRuleTree;//规则树,递归校验
		
		...getter setter
	}
	
	class Rule {
		private String ruleId;//规则主键
		private String ruleDetail;//规则内容
		private String analyzeBean;//实现规则的具体实现类,这里用spring bean name

		...getter setter
	}
	
}
  1. 解析规则
    假如存储的树形结构如下
    ruleDetail = {"groupId": "1"}|AND|{"ruleId":"1"}|AND|{"ruleId":"2"} 那么需要解析出groupId下的所有rule,然后再挨个解析后面的两个ruleId
public class GoodsRuleUtils {
	private static Map<String, GoodsRuleTree> map = new HashMap<String, GoodsRuleTree>();
	public static GoodsRuleTree getGoodsRuleTree(String goodsId) {
		return map.get(goodsId);
	}
	
	public static void setGoodsRuleTree(String goodsId, GoodsRuleTree goodsRuleTree) {
		map.put(goodsId, goodsRuleTree);
	}
	
	public static void removeGoodsRuleTree(String goodsId) {
		map.remove(goodsId);
	}

        	/**
	 * 解析成规则树
	 * @throws Exception 
	 * 
	 */
	public static GoodsRuleTree analyzeRuleTree(String ruleDetail) throws Exception {
		GoodsRuleTree goodsRuleTreeRoot = new GoodsRuleTree();
		if(StringUtils.isNotBlank(ruleDetail)) {
			analyzeRuleTree(goodsRuleTreeRoot, ruleDetail);
		}
		return goodsRuleTreeRoot;
	}
	
	private static void analyzeRuleTree(GoodsRuleTree goodsRuleTree, String ruleDetail) throws Exception {
		//ruleDetail = {"groupId": "cbdf378f29b311e8befa9c5c8e91fb89"}|AND|{"ruleId":"dec57f0b28f111e8befa9c5c8e91fb89"}|AND|{"ruleId":"dec57f0b28f111e8befa9c5c8e91fb89"}
		List<OperatorDetail> operatorList = getRuleOperatorList(ruleDetail);
		List<String> ruleList = getRuleList(operatorList, ruleDetail);
		
		for(int i = 0; i < ruleList.size(); i++) {
			RuleDetail r = JsonUtils.fromJson(ruleList.get(i), RuleDetail.class);
			Node node = goodsRuleTree.genNode();
			if(i < ruleList.size() - 1) {
				node.setCalculateType(operatorList.get(i).getOperator());
				node.setHasNext(true);
			}else {
				node.setHasNext(false);
			}
			genNode(node, r);
			goodsRuleTree.getNodes().add(node);
		}
		
	}
	
	private static void genNode(Node node, RuleDetail r) throws Exception {
		if(StringUtils.isNotBlank(r.getRuleId())) {
			Rule rule = node.genRule();
			IGoodsRuleServComponent goodsRuleServComponent = (GoodsRuleServComponentImpl)SpringContainerTools.getBean("GoodsRuleServComponentImpl");
			GoodsRuleVo goodsRuleVo = goodsRuleServComponent.queryGoodsRuleById(r.getRuleId());
			rule.setAnalyzeBean(goodsRuleVo.getAnalyzeBean());
			rule.setRuleDetail(goodsRuleVo.getRuleDetail());
			node.setRule(rule);
			node.setName(goodsRuleVo.getRuleName());
			node.setRemark(goodsRuleVo.getRemark());
		}else {
			//递归构造出子节点
			IGoodsRuleGroupServComponent goodsRuleGroupServComponent = (GoodsRuleGroupServComponentImpl)SpringContainerTools.getBean("GoodsRuleGroupServComponentImpl");
			GoodsRuleGroupVo goodsRuleGroupVo = goodsRuleGroupServComponent.queryGoodsRuleGroupById(r.getGroupId());
			String ruleDetail = goodsRuleGroupVo.getRuleDetail();
			GoodsRuleTree goodsRuleTree = new GoodsRuleTree();
			analyzeRuleTree(goodsRuleTree, ruleDetail);
			node.setGoodsRuleTree(goodsRuleTree);
			node.setName(goodsRuleGroupVo.getGroupName());
			node.setRemark(goodsRuleGroupVo.getRemark());
		}
	}
	
	private static List<OperatorDetail> getRuleOperatorList(String ruleDetail) {
		List<OperatorDetail> operatorList = new ArrayList<OperatorDetail>();
		splitOperator(operatorList, ruleDetail, 0);
		return operatorList;
	}
	
	private static List<String> getRuleList(List<OperatorDetail> operatorList, String ruleDetail) {
		List<String> rules = new ArrayList<String>();
		int size = operatorList.size();
		if(size == 0) {
			rules.add(ruleDetail);
		}else {
			for(int i = 0; i < size; i++) {
				if(i == 0) {
					String ruleText = ruleDetail.substring(0, operatorList.get(i).getFirstIndex());
					rules.add(ruleText);
				}
				if(i + 1 < size) {
					String ruleText = ruleDetail.substring(operatorList.get(i).getSecondIndex() + 1, operatorList.get(i + 1).getFirstIndex());
					rules.add(ruleText);
				}else {
					String ruleText = ruleDetail.substring(operatorList.get(i).getSecondIndex() + 1);
					rules.add(ruleText);
				}
			}
		}
		return rules;
	}
	
	private static void splitOperator(List<OperatorDetail> operatorList, String ruleDetail, int index) {
		int firstIndex = ruleDetail.indexOf("|", index);
		int secondIndex = ruleDetail.indexOf("|", firstIndex + 1);
		if(firstIndex != -1 && secondIndex != -1) {
			String operator = ruleDetail.substring(firstIndex + 1, secondIndex);
			OperatorDetail operatorDetail = new OperatorDetail();
			operatorDetail.setOperator(operator);
			operatorDetail.setFirstIndex(firstIndex);
			operatorDetail.setSecondIndex(secondIndex);
			operatorList.add(operatorDetail);
			splitOperator(operatorList, ruleDetail, secondIndex + 1);
		}
	}
}

class OperatorDetail {//控制操作规则
	private int firstIndex;
	private int secondIndex;
	private String operator;
	public int getFirstIndex() {
		return firstIndex;
	}
	public void setFirstIndex(int firstIndex) {
		this.firstIndex = firstIndex;
	}
	public int getSecondIndex() {
		return secondIndex;
	}
	public void setSecondIndex(int secondIndex) {
		this.secondIndex = secondIndex;
	}
	public String getOperator() {
		return operator;
	}
	public void setOperator(String operator) {
		this.operator = operator;
	}
	@Override
	public String toString() {
		return JsonUtils.toJson(this);
	}
	
}
  1. 解析完后缓存到map中
@Service("RuleServComponentImpl")
public class RuleServComponentImpl implements IRuleServComponent {
	@Resource
	private IGoodsRuleRelationshipServComponent goodsRuleRelationshipServComponent ;
	
	@Override
	public RuleInvalidResult goodsRuleValid(RuleParam param) throws Exception {
		String goodsId = param.getParam("goodsId", String.class);
		//获取解析树
		GoodsRuleTree goodsRuleTree = GoodsRuleUtils.getGoodsRuleTree(goodsId);
		if(goodsRuleTree == null) {
			GoodsRuleRelationshipQueryBean goodsRuleRelationshipQueryBean = new GoodsRuleRelationshipQueryBean();
			goodsRuleRelationshipQueryBean.setGoodsId(goodsId);
			GoodsRuleRelationshipVo goodsRuleRelationshipVo = goodsRuleRelationshipServComponent.queryGoodsRuleRelationship(goodsRuleRelationshipQueryBean);
			if(goodsRuleRelationshipVo == null) {//获取不到则说明该商品无购买规则
				return new RuleInvalidResult().success("success");
			}else {
				String ruleDetail = goodsRuleRelationshipVo.getRuleDetail();
				goodsRuleTree = GoodsRuleUtils.analyzeRuleTree(ruleDetail);
				GoodsRuleUtils.setGoodsRuleTree(goodsId, goodsRuleTree);
			}
		}
		if(goodsRuleTree.getNodes().size() == 0) {//获取不到则说明该商品无购买规则
			return new RuleInvalidResult().success("success");
		}
		RuleInvalidResult result = GoodsRuleUtils.valid(goodsRuleTree, param);
		return result;
	}

	@Override
	public RuleInvalidResult refresh(RuleParam param) throws Exception {
		String goodsId = param.getParam("goodsId", String.class);
		GoodsRuleUtils.removeGoodsRuleTree(goodsId);
		return goodsRuleValid(param);
	}
}
  1. 校验
public class GoodsRuleUtils {
	public static RuleInvalidResult valid(GoodsRuleTree goodsRuleTree, RuleParam param) {
		List<Node> list = goodsRuleTree.getNodes();
		RuleInvalidResult ruleInvalidResult = null;
		for(Node node : list) {
			if(node.getGoodsRuleTree() != null) {
				ruleInvalidResult = valid(node.getGoodsRuleTree(), param);
			}else {
				String analyzeBean = node.getRule().getAnalyzeBean();
				org.sunshine.dcda.rule.Rule rule = (org.sunshine.dcda.rule.Rule)SpringContainerTools.getBean(analyzeBean);
				if(!rule.validParam(param)) {//先验证参数输入
					ruleInvalidResult = new RuleInvalidResult();
					ruleInvalidResult.setCode(RuleInvalidResultConstants.VALID_FAIL);
					ruleInvalidResult.setErrMsg("参数异常");
					break;
				}
				ruleInvalidResult = rule.valid(param);
				if(StringUtils.isNotBlank(node.getRule().getRuleDetail())) {
					ruleInvalidResult.setData(node.getRule().getRuleDetail());
				}
			}
			
			if(!node.isHasNext()) {//如果没有下一个验证器,跳出
				break;
			}
			
			String calculateType = node.getCalculateType();
			if(RuleConstants.OR.equals(calculateType)) {
				if(ruleInvalidResult.getCode().equals(RuleInvalidResultConstants.SUCCESS)) {//前面的条件验证成功后,直接返回成功
					return ruleInvalidResult;
				}
			}
			if(RuleConstants.AND.equals(calculateType)) {
				if(!ruleInvalidResult.getCode().equals(RuleInvalidResultConstants.SUCCESS)) {//前面的条件验证失败后,直接返回失败
					return ruleInvalidResult;
				}
			}
			
		}
		return ruleInvalidResult;
	}
}

public class RuleInvalidResult implements Serializable {
	private static final long serialVersionUID = 1L;
	private String code;
	private Object data;
	private String errMsg;

	public String getCode() {
		return code;
	}

	public void setCode(String code) {
		this.code = code;
	}

	public Object getData() {
		return data;
	}

	public void setData(Object data) {
		this.data = data;
	}

	public String getErrMsg() {
		return errMsg;
	}

	public void setErrMsg(String errMsg) {
		this.errMsg = errMsg;
	}

	public RuleInvalidResult success(Object data) {
		this.setCode(RuleInvalidResultConstants.SUCCESS);
		this.setData(data);
		return this;
	}

	public RuleInvalidResult error(String msg) {
		return this.error(RuleInvalidResultConstants.FAIL, msg, null);
	}

	public RuleInvalidResult error(String code, String msg) {
		return this.error(code, msg, null);
	}

	public RuleInvalidResult error(String code, String msg, Object obj) {
		this.setCode(code);
		this.setErrMsg(msg);
		this.setData(obj);
		return this;
	}
	
	public RuleInvalidResult serviceError(String msg) {
		return this.error(RuleInvalidResultConstants.SERVICE_FAIL, msg, null);
	}
	
	public RuleInvalidResult serviceError(String msg, Object obj) {
		return this.error(RuleInvalidResultConstants.SERVICE_FAIL, msg, obj);
	}

	@Override
	public String toString() {
		return JsonUtils.toJson(this);
	}
	
}
  1. 规则校验的定义
public interface Rule {

	public RuleInvalidResult valid(RuleParam ruleParam);
	public boolean validParam(RuleParam param);
}

ublic abstract class AbstractRule implements Rule {
	private static final Logger logger = LoggerFactory.getLogger(AbstractRule.class);
	/**
	 * 如果需要校验参数,可以重写该方法,默认只校验是否存在customerId的key
	 * @param param
	 * @return
	 */
	@Override
	public boolean validParam(RuleParam param) {
		String customerId = param.getParam("customerId", String.class);
		if(StringUtils.isBlank(customerId)) {
			logger.error("未能获取key: customerId的参数");
			return false;
		}
		return true;
	}
	
	protected boolean validParams(RuleParam param, String... keys) {
		for(String key : keys) {
			Object obj = param.getParam(key);
			try {
				String s = String.valueOf(obj);
				if(s == null) {
					logger.error(String.format("未能获取key: %s的参数", key));
					return false;
				}
				
				if(obj instanceof String) {
					if(StringUtils.isBlank(key)) {
						logger.error(String.format("未能获取key: %s的参数", key));
						return false;
					}
				}else if(obj instanceof Boolean) {
					Boolean.parseBoolean(s);
				}else if(obj instanceof Integer) {
					Integer.parseInt(s);
				}else if(obj instanceof Double) {
					Double.parseDouble(s);
				}else if(obj instanceof Float) {
					Float.parseFloat(s);
				}else if(obj instanceof Long) {
					Long.parseLong(s);
				}else if(obj instanceof Short) {
					Short.parseShort(s);
				}
			}catch(Exception e) {
				logger.error("验证错误, key: %s, value: %s");
				return false;
			}
		}
		return true;
	}
}

public class RuleParam implements Serializable {
	private static final long serialVersionUID = -1890741515056148161L;
	
	private static final Logger logger = LoggerFactory.getLogger(RuleParam.class);
	private Map<String, Object> map = new HashMap<String, Object>();
	
	private RuleParam(){}
	
	public static RuleParam getInstance() {
		return new RuleParam();
	}
	
	public void addParam(String key, Object data) {
		map.put(key, data);
	}
	
	public void addParams(Map<String, Object> m) {
		map.putAll(m);
	}
	
	public Object getParam(String key) {
		return map.get(key);
	}
	
	@SuppressWarnings("unchecked")
	public <T> T getParam(String key, Class<T> clazz) {
		try {
			return (T) map.get(key);
		} catch (Exception e) {
			logger.error(String.format("获取参数错误,不能转换成对应%s类型,key:%s", clazz.getName(), key), e);
			return null;
		}
	}

	@Override
	public String toString() {
		return JsonUtils.toJson(map);
	}
	
}
  1. 规则实现
@Service("YunShuPackageRule")
public class XxxRule extends AbstractRule {
    @Override
    public RuleInvalidResult valid(RuleParam ruleParam) {
        ...
    }
}
⚠️ **GitHub.com Fallback** ⚠️