package com.jfpuhui.anti.service;
import com.jfpuhui.anti.common.StatusCode;
import com.jfpuhui.anti.dao.dto.Edge;
import com.jfpuhui.anti.dao.dto.Graph;
import com.jfpuhui.anti.dao.dto.Node;
import com.jfpuhui.anti.dao.pojo.CustProperty;
import com.jfpuhui.anti.dao.pojo.CustRelationship;
import com.jfpuhui.anti.exception.CustomException;
import com.jfpuhui.anti.mapper.CustPropertyMapper;
import com.jfpuhui.anti.mapper.CustRelationshipMapper;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author Nisus-Liu
* @version 1.0.0
* @email liuhejun108@163.com
* @date 2018-03-21-13:29
*/
public abstract class GraphServiceTemplate {
private Logger log = LoggerFactory.getLogger(this.getClass());
private final static int MAX_DISPLAY_NODES_1D = 37; //一度节点最多36
private final static int MAX_DISPLAY_NODES_2D = 109; //1-6-3
private final static int MAX_DISPLAY_NODES_CORE = 36; //
private final static int MAX_DISPLAY_EDGES = 1998; // 37个点两两相连, 平均每两点间3条边
//private final static int MAX_DISPLAY_EDGES_CORE = 126; // 7个核两两相连, 平均每两点6条边
public Graph getGraph4MultiCores(List<String> nodeIds, CustRelationshipMapper custRelationshipMapper, CustPropertyMapper custPropertyMapper) throws CustomException {
if (nodeIds.size() == 0) {
throw new CustomException("[nodeIds] is empty, will don't excute any SQL");
}
// Map<String, Object> graph = new HashMap<>();
Graph graph = new Graph();
//2 判断身份证号list长度, >36给出提示, 条件太宽, 节点太多; <=36, 继续
if (nodeIds.size() > MAX_DISPLAY_NODES_CORE) { //core node 数目超限
log.info("当前[核]数量大于{}, 将返回null",MAX_DISPLAY_NODES_CORE);
// graph.put("status", StatusCode.TOO_MANY_CORE_NODES);
// graph.put("nodes", null);
// graph.put("edges", null);
graph = new Graph(null, null, StatusCode.TOO_MANY_CORE_NODES);
return graph;
}
// -- 核数量在范围以内 --
//仅一度边
Map<String, Edge> oneDepthEdges = new HashMap<>();
//一度节点, 含中心
Map<String, Node> oneDepthNodes = new HashMap<>();
//创建core node 方便下文区分节点深度
for (String certNo : nodeIds) {
Node node = new Node();
node.setId(certNo);
node.setDepth(0); //core node, 可能是多个core
//装进oneDepthNodes, core node ∈ oneDepth
oneDepthNodes.put(node.getId(), node);
}
//3 继续: 查询核之间、1度的边数据集 --i.e. depth == 0
List<CustRelationship> oneDepthRels = custRelationshipMapper.selectRelByGivenCertNos(nodeIds);
//子类使用具体方法获取
//List<CustRelationship> oneDepthRels = getAll1DRelByGivenNodeIds();
//4 边数不超限, 则继续查二度边数据集
if (oneDepthRels.size() > MAX_DISPLAY_EDGES) {
log.info("当期一度范围内[边]大于{}, 将返回null",MAX_DISPLAY_EDGES);
// graph.put("status", StatusCode.TOO_MANY_1D_EDGES);
// graph.put("nodes", null);
// graph.put("edges", null);
graph = new Graph(null, null, StatusCode.TOO_MANY_1D_EDGES);
return graph;
}
//-- 一度边数量正常 --
//遍历一度边, 封装数据
//Note: 这时oneDepthRels中含所有核之间的边和第一度节点之间的边, 为了区分不同度的边, 这里需要进一步判断:
// 当source node和target node均在oneDepthNodes中时, 表示为核边, 否则是第一度边
for (CustRelationship rel : oneDepthRels) {
//Note: 以身份证为node id
Edge edge = new Edge(rel.getId(), rel.getuCertNo(), rel.getvCertNo(), rel.getContent(), rel.getContentType(), rel.getRelationType());
//判断当前edge的depth属性
if (oneDepthNodes.get(rel.getuCertNo()) != null && oneDepthNodes.get(rel.getvCertNo()) != null) {
edge.setDepth(0); //核间边
} else {
edge.setDepth(1); //一度边
}
oneDepthEdges.put(String.valueOf(edge.getId()), edge);
//region 收集node
gatherNodes(oneDepthNodes, rel, 1);
//endregion
}
//取出一度范围节点的key, 用于下文查二度范围的边
Set<String> oneDepthNodesKeySet = oneDepthNodes.keySet();
//判断一度范围节点个数, 小于阈值才会继续查询二度边
if (oneDepthNodesKeySet.size() > MAX_DISPLAY_NODES_1D) {
log.info("当前一度范围内[节点]个数大于[{}], 将仅返回一度节点和边数据", MAX_DISPLAY_NODES_1D);
List<CustProperty> custProperties = custPropertyMapper.selectByCertNos(oneDepthNodesKeySet);
//获取一度范围的点的属性数据集 子类实现
//List<CustProperty> custProperties = getNodePropertyByNodeIds(oneDepthNodesKeySet);
//封装点属性
dumpCustPropertyData(oneDepthNodes, custProperties, 1);
graph.setNodes(oneDepthNodes);
graph.setEdges(oneDepthEdges);
return graph;
}
// -- 一度范围节点数量在范围以内, 则获取所有二度边 --
//装所有节点
HashMap<String, Node> nodes = (HashMap<String, Node>) ((HashMap<String, Node>) oneDepthNodes).clone();
//装所有的边
HashMap<String, Edge> edges = (HashMap<String, Edge>) ((HashMap<String, Edge>) oneDepthEdges).clone();
List<CustRelationship> allRel = custRelationshipMapper.selectRelByGivenCertNos(oneDepthNodesKeySet);
if (allRel.size() > MAX_DISPLAY_EDGES) {
log.info("当前二度范围内[边]总数大于[{}], 仅返回一度节点和边数据", MAX_DISPLAY_EDGES);
graph.setNodes(oneDepthNodes);
graph.setEdges(oneDepthEdges);
return graph;
}
// -- 二度范围边在范围内, 则加工边数据 --
for (CustRelationship rel : allRel) {
//oneDepthEdges中已有的, 不用再封装了, 没有的, 创建, 封装, 存入edges中, 且depth设为2
if (oneDepthEdges.get(rel.getId()) == null) {
Edge edge = new Edge(rel.getId(), rel.getuCertNo(), rel.getvCertNo(), rel.getContent(), rel.getContentType(), rel.getRelationType());
//oneDepthEdges没有=>depth==2
edge.setDepth(2);
//存入edges
edges.put(String.valueOf(edge.getId()), edge);
}
//遇到新节点, 则存入nodes中, 且深度为2
gatherNodes(nodes, rel, 2);
}
//查询所有属性: 核, 一度, 二度
Set<String> allCertNos = nodes.keySet(); //获取所有身份证号, 查取属性数据
List<CustProperty> custProperties = custPropertyMapper.selectByCertNos(allCertNos);
//封装属性数据
dumpCustPropertyData(nodes, custProperties, 2);
//判断节点数目
if (nodes.size() > MAX_DISPLAY_NODES_2D) {
//0,1,2度节点之和超过阈值, 则只返回一度节点和边
log.info("当前二度范围内[节点]总数大于[{}], 仅返回一度节点和边数据", MAX_DISPLAY_NODES_2D);
graph.setNodes(oneDepthNodes);
graph.setEdges(oneDepthEdges);
} else {
//没有超限, 则返回1,2度全部
log.info("返回全部二度范围内节点和边数据");
graph.setNodes(nodes);
graph.setEdges(edges);
}
return graph;
}
// protected abstract List<C