商品规则设计 - 969251639/study GitHub Wiki
每个商品都有自己的规则,那么为了可以快速开发且不影响以前的规则,考虑扩展性和可维护性,设计如下:
整个流程最重要是这个规则树的解析和校验
规则的解析树定义了多个节点,每个节点可以包含各自的子节点(多个节点也可以组成一个规则组),节点与节点之间通过用And或Or的方式串联成单向链表
- 规则树的定义
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
}
}
- 解析规则
假如存储的树形结构如下
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);
}
}
- 解析完后缓存到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);
}
}
- 校验
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);
}
}
- 规则校验的定义
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);
}
}
- 规则实现
@Service("YunShuPackageRule")
public class XxxRule extends AbstractRule {
@Override
public RuleInvalidResult valid(RuleParam ruleParam) {
...
}
}