import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.JsonNodeType;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.stereotype.Service; 
import java.io.IOException;
 import java.util.*;
 import java.util.stream.Collectors; 
/**
  * 解析Json 创建表结构
  */
 @Service
 public class JsonService { 
     /**
      * 根据Json返回字符串创建表语句
      * @param jsonParam
      * @return
      */
     public String createTableSqlByJson(String jsonParam) {
         List<JsonMapper> analysisResult = analysisJson(jsonParam);
         if (CollectionUtils.isNotEmpty(analysisResult)) {
             //Map<String, List<JsonMapper>> mapperByParentName = analysisResult.stream().collect(Collectors.groupingBy(JsonMapper::getParentName));
             Map<String, List<String>> mapperByParentName = analysisResult.stream().collect(Collectors.groupingBy(JsonMapper::getParentName,
                     Collectors.mapping(JsonMapper::getCurrName, Collectors.toList())));
             Iterator iterator = mapperByParentName.entrySet().iterator();
             String sql = "";
             while (iterator.hasNext()) {
                 Map.Entry entry = (Map.Entry) iterator.next();
                 sql = sql + createSql(entry.getKey().toString(), (List<String>) entry.getValue());
             }
             return sql;
         }
         return null;
     } 
    /**
      * Jackson 解析json
      * @param jsonParam
      * @return
      */
     public List<JsonMapper> analysisJson(String jsonParam) {
         try {
             ObjectMapper mapper = new ObjectMapper();
             JsonNode jsonNodeRoot = mapper.readTree(jsonParam);
             List<JsonMapper> tableMapper = new ArrayList<>();
             List<JsonMapper> fieldMapper = new ArrayList<>();
             recursiveAnalysis(tableMapper, fieldMapper, "rootJson",jsonNodeRoot); 
            // 表与表之间
             if (CollectionUtils.isNotEmpty(tableMapper)) {
                 // 合并
                 fieldMapper.addAll(tableMapper);
                 /*for (JsonMapper jsonMapper : tableMapper){
                     System.out.println(jsonMapper.getParentName() + "===111===" +jsonMapper.getCurrName());
                 }*/
             } 
            // 属性与表之间
             if (CollectionUtils.isNotEmpty(fieldMapper)) { 
                // 去重
                 List<JsonMapper> fieldMapperAfter = fieldMapper.stream().collect(Collectors.collectingAndThen(
                         Collectors.toCollection(() -> new TreeSet<>(
                                 Comparator.comparing(o -> o.getParentName() + ";" + o.getCurrName()))), ArrayList::new)); 
                // 排序
                 Collections.sort(fieldMapperAfter, new Comparator<JsonMapper>(){
                     public int compare(JsonMapper o1, JsonMapper o2) {
                         return o1.getParentName().compareTo(o2.getParentName());// 按照父节点升序
                     }
                 });
                 /*for (JsonMapper jsonMapper : fieldMapperAfter){
                     System.out.println(jsonMapper.getParentName() + "======" +jsonMapper.getCurrName());
                 }*/
                 return fieldMapperAfter;
             }
         } catch (IOException e) {
             e.printStackTrace();
         }
         return null;
     } 
    /**
      *  Jackson 递归解析子节点
      * @param tableMapper 表与表之间关联映射
      * @param fieldMapper 字段属性与表之间关联映射
      * @param parentNodeName 父节点名称
      * @param jsonNode 当前节点
      */
     public void recursiveAnalysis(List<JsonMapper> tableMapper,List<JsonMapper> fieldMapper, String parentNodeName,JsonNode jsonNode) {
         if (jsonNode.isContainerNode()){
             if (JsonNodeType.OBJECT == jsonNode.getNodeType()) {
                 Iterator<String> fieldNames = jsonNode.fieldNames();
                 while (fieldNames.hasNext()) {
                     String fieldName = fieldNames.next();
                     if (jsonNode.get(fieldName).isContainerNode()){
                         //System.out.println("父对象:" +parentNodeName +"当前对象的名称:" + fieldName);
                         tableMapper.add(new JsonMapper(parentNodeName, fieldName));
                         recursiveAnalysis(tableMapper, fieldMapper, fieldName, jsonNode.get(fieldName));
                     } else {
                         //System.out.println("父属性:" +parentNodeName +"当前属性的名称:" +fieldName + "===" + jsonNode.get(fieldName).getNodeType());
                         fieldMapper.add(new JsonMapper(parentNodeName, fieldName + ":" + jsonNode.get(fieldName).getNodeType()));
                     }
                 }
             } else if (JsonNodeType.ARRAY == jsonNode.getNodeType()) {
                 Iterator<JsonNode> jsonNodeElements = jsonNode.iterator();
                 while (jsonNodeElements.hasNext()) {
                     JsonNode jsonNodeElement = jsonNodeElements.next();
                     recursiveAnalysis(tableMapper, fieldMapper, parentNodeName, jsonNodeElement);
                 }
             }
         }
     } 
    /**
      * 获取创建表前缀,根据拆分表数字
      * @param tableName
      * @param splitNum
      * @return
      */
     public String getTablePrefix(String tableName, int splitNum) {
         if (splitNum > 0) {
             tableName = tableName + "_" + splitNum;
         }
         StringBuilder tablePrefix = new StringBuilder();
         tablePrefix.append("CREATE TABLE `"+tableName+"` ( \r\n");
         tablePrefix.append("  `id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '主键', \r\n");
         tablePrefix.append("  `orders_id` INT(11) NOT NULL COMMENT '订单ID', \r\n");
         tablePrefix.append("  `user_id` INT(11) DEFAULT NULL, \r\n");
         return tablePrefix.toString();
     } 
    /**
      * 获取创建表后缀
      * @param tableName
      * @return
      */
     public String getTableSuffix(String tableName) {
         StringBuilder tableSuffix = new StringBuilder();
         tableSuffix.append("  `create_time` DATETIME DEFAULT NULL, \r\n");
         tableSuffix.append("PRIMARY KEY (`id`), \r\n");
         tableSuffix.append("KEY `index_user_id` (`user_id`) USING BTREE \r\n");
         tableSuffix.append(") ENGINE=InnoDB DEFAULT CHARSET=UTF8; \r\n");
         tableSuffix.append(" \r\n");
         return tableSuffix.toString();
     }
     /**
      *  根据表名以及列名称创建表结构
      * @param tableName
      * @param columnNames
      * @return
      */
     public String createSql(String tableName, List<String> columnNames) { 
        boolean isSplit = isSplitToTables(columnNames.size());
 
        StringBuilder sqlAll = new StringBuilder();
         sqlAll.append(getTablePrefix(tableName, 0));
         for (int i = 0; i < columnNames.size(); i++) {
             if (columnNames.get(i).contains(":")) {
                 String str[] = columnNames.get(i).split(":");
                 String columnType = buildColumnType(str[1]);
                 sqlAll.append("  `"+str[0]+"` "+columnType+" DEFAULT NULL, \r\n");
             }else {
                 sqlAll.append("  `"+columnNames.get(i)+"` LONGTEXT DEFAULT NULL, \r\n");
             }
             if (isSplit) {
                 if (i > 0 && i % 50 == 0) {
                     sqlAll.append(getTableSuffix(tableName));
                     sqlAll.append(getTablePrefix(tableName, i / 50));
                 }
             }
         }
         sqlAll.append(getTableSuffix(tableName));
         return sqlAll.toString();
     } 
    /**
      * 根据json 字段类型构建数据库字段类型
      * 默认 varchar(20)
      * @param jsonType
      */
     public String buildColumnType(String jsonType) {
         if (JsonNodeType.BOOLEAN.toString().equals(jsonType)) {
             return "CHAR(1)";
         } else if (JsonNodeType.NUMBER.toString().equals(jsonType)) {
             if (jsonType.contains(".")) {
                 return "VARCHAR(20)";
             }
             return "INT(11)";
         } else if (JsonNodeType.STRING.toString().equals(jsonType)) {
             return "VARCHAR(100)";
         } else {
             return "VARCHAR(20)";
         }
     } 
    /**
      * 根据表的列数判断是否需要拆分字段到多个表
      * 当字段 > 80 时,自动创建多表,多表采用单表 50个字段进行分拆
      * @param columns
      * @return
      */
     public boolean isSplitToTables(int columns){
         if (columns >= 80) {
             return true;
         }
         return false;
     }
 }@Data
 public class JsonMapper implements Serializable { 
    private String parentName;
 
    private String currName;
 
    public JsonMapper(String parentName, String currName) {
         this.parentName = parentName;
         this.currName =currName;
     }
 }