content: package com.alibaba.markovdemo.engine;
  
  
  import com.alibaba.fastjson.JSONArray;
  import com.alibaba.fastjson.JSONObject;
  import com.alibaba.markovdemo.BO.*;
  
  import com.alibaba.markovdemo.engine.AI.FMM.GenerateDictionary;
  import com.alibaba.markovdemo.engine.AI.FMM.Segmentation;
  import com.alibaba.markovdemo.engine.util.Toolkit;
  import com.alibaba.markovdemo.entity.GotFeaturesPool;
  import com.alibaba.markovdemo.entity.GotTestCase;
  import com.alibaba.markovdemo.service.GotFeaturesPoolService;
  import com.alibaba.markovdemo.service.TestcaseService;
  import com.google.gson.Gson;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.stereotype.Service;
  
  import java.io.IOException;
  import java.util.*;
  
  
  @Service
  public class CaseFactoryProcessor {
  
      private static final Logger Logger = LoggerFactory.getLogger(CaseFactoryProcessor.class);
  
      @Autowired
      private GotFeaturesPoolService gotFeaturesPoolService;
      @Autowired
      private TestcaseService testcaseService;
      Gson gson = new Gson();
      Segmentation seg;
  
  
      /**
       * 函数功能:处理FMM处理后的特征
       * 过滤策略:单个字符不算特征,具体的词才算业务特征
       * @param input
       * @return
       */
      public List<String> getFMMFeatures(String input){
  
          List<String> featureList = new ArrayList<>();
          String str = seg.Fmm(input);
          String[] list = str.split("/");
  
          for (String split : list){
  
              if (split.length()>1){
                  featureList.add(split);
              }
          }
          return featureList;
  
      }
  
      /**
       * 函数功能:处理query和数据源的特征
       * 过滤策略:数据源名 ,query用户可根据业务需求自行抽取,本次暂不实现
       * @param input
       * @return
       */
      public List<String> getQueryAndDataFeatures(String input){
  
          List<String> featureList = new ArrayList<>();
  
          try{
  
              JSONObject obj =  JSONObject.parseObject(input);
              List<JSONObject> list = (List<JSONObject>) obj.get("prepareData");
  
              for (JSONObject jObj : list){
  
                  if (jObj.size()>0){
                      for (String dsName : jObj.keySet()){
                          List<JSONObject> dsList = (List<JSONObject>) jObj.get(dsName);
                          for (JSONObject dsObj : dsList){
                              String name = dsObj.getString("dsName");
                              if(!featureList.contains(name)){
                                  featureList.add(name);
                              }
                          }
  
                      }
                  }
              }
  
          }catch (Exception e){
              //格式异常,skip..
          }
  
  
          return featureList;
  
      }
  
  
      /**
       * 特征抽取策略:
       * 1.用例详细描述采用FMM分词.
       * 2.分组单独作为特征.
       * 3.数据源名为特征
       * 其他:其他维度特征用户可根据业务自行补充
       * @param gotTestCase
       * @return
       */
      public List<String>  extractFeatures(GotTestCase gotTestCase){
  
          //用例特征
          List<String> featureList = new ArrayList<>();
          //用例名进行FMM分词
          List<String>  descriptionFeatures = getFMMFeatures(gotTestCase.getDescription());
          //用例描述FMM分词
          List<String> longDescriptionFeatures = getFMMFeatures(gotTestCase.getLongDescription());
          //分组特征
          String caseGroupFeatures = gotTestCase.getCaseGroup();
          //数据源和query特征
          List<String> queryAndDataFeatures = getQueryAndDataFeatures(gotTestCase.getContent());
  
          if (caseGroupFeatures!=null && !"".equals(caseGroupFeatures)){
              featureList.add(caseGroupFeatures);
          }
          featureList.addAll(descriptionFeatures);
          featureList.addAll(longDescriptionFeatures);
          featureList.addAll(queryAndDataFeatures);
  
          return featureList;
      }
  
      /**
       * 函数功能:用例特征并入特征池
       * @param featurePool
       * @param featuresList
       */
      public void putInFeatures(Map<String,Integer>  featurePool,List<String> featuresList){
  
          if (featuresList.size()>0){
              for (String features :  featuresList){
  
                  if (!featurePool.containsKey(features)){
                      featurePool.put(features,1);
                  }
                  else{
                      featurePool.put(features,featurePool.get(features)+1);
                  }
              }
          }
  
      }
  
  
      /**
       * 函数功能:加载字典,分词使用
       * @throws IOException
       */
      public void loadDict() throws IOException {
          //加载词库
          String filename = System.getProperty("user.dir") + "/src/main/java/com/alibaba/markovdemo/engine/AI/FMM/doc/dict.txt";
          HashMap hm = new HashMap();
          HashMap len = new HashMap();
          GenerateDictionary genDic = new GenerateDictionary();
          genDic.genHashDic(filename, hm, len);
          seg = new Segmentation(hm, len);
      }
  
  
  
      /**
       * 函数功能:特征抽取
       * 1.使用FMM算法对用例描述进行分词(用户可根据需要自行采用其他分词算法,本篇采用了经典FMM分词算法)
       * 2.对数据源/发送query进行关键字段抽取
       * 3.用例维度/场景维度的特征入库
       * @param scenarioId
       */
      public void callFeaturesExtract(Long scenarioId) throws IOException {
  
  
          Map<String,Integer> featurePool = new HashMap<>();
          GotFeaturesPool  gotFeaturesPool = new GotFeaturesPool();
  
          //获取所有的用例的业务数据
          List<GotTestCase> gotTestCaseList = testcaseService.findByScenarioId(scenarioId);
          //加载词库
          loadDict();
  
          //遍历用例库
          for (GotTestCase  gotTestCase : gotTestCaseList){
              try{
                  //抽取用例特征集
                  List<String> featuresList = extractFeatures(gotTestCase);
                  //特征池合并
                  putInFeatures(featurePool,featuresList);
                  //单个用例特征入库
                  gotTestCase.setFeatures(gson.toJson(featurePool));
                  testcaseService.update(gotTestCase);
                  System.out.print("用例id:"+ gotTestCase.getId() +"特征抽取完成!\n");
              }
              catch (Exception e){
                  System.out.print("用例id:"+ gotTestCase.getId() +"特征抽取过程出错,请注意!\n");
                  e.printStackTrace();
              }
  
          }
          //特征池入库
          gotFeaturesPool.setId(scenarioId);
          gotFeaturesPool.setScenarioId(scenarioId);
          gotFeaturesPool.setFeatures(gson.toJson(featurePool));
          gotFeaturesPoolService.insert(gotFeaturesPool);
          System.out.print("特征池入库完成!\n");
      }
  
  
      /**
       * 函数功能:异步执行全量用例特征抽取
       * @param scenarioId
       */
      public void featuresExtract(Long scenarioId) throws IOException {
  
          Thread thread = new Thread(new Runnable() {
              @Override
              public void run() {
                  try {
                      Logger.info("提交特征抽取任务");
                      callFeaturesExtract( scenarioId);
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }
          });
          thread.start();
  
      }
  
      // 升序比较器
      Comparator<Map.Entry<String, Double>> valueComparator = new Comparator<Map.Entry<String,Double>>() {
          @Override
          public int compare(Map.Entry<String, Double> o1,
                             Map.Entry<String, Double> o2) {
              // TODO Auto-generated method stub
              return (int) (o1.getValue()-o2.getValue());
          }
      };
  
      // 升序比较器
      Comparator<Map.Entry<GotTestCase, Double>> valueCaseComparator = new Comparator<Map.Entry<GotTestCase,Double>>() {
          @Override
          public int compare(Map.Entry<GotTestCase, Double> o1,
                             Map.Entry<GotTestCase, Double> o2) {
              // TODO Auto-generated method stub
              return (int) (o1.getValue()-o2.getValue());
          }
      };
  
  
      /**
       * 函数功能:获取特征列表
       * 策略:按照特征的出现频率降序返回
       * @param scenarioId
       * @return
       */
      public List<String> getFeaturesList(Long scenarioId){
  
          List<String> featureList = new ArrayList<>();
  
          try{
              List<String> sortList = new ArrayList<>();
              GotFeaturesPool gotFeaturesPool = gotFeaturesPoolService.findByScenarioId(scenarioId);
              // map转换成list进行排序
              Map<String, Double> map = new HashMap<>();
              map = gson.fromJson(gotFeaturesPool.getFeatures(), map.getClass());
  
              List<Map.Entry<String, Double>> list = new ArrayList<>(map.entrySet());
              // 排序
              Collections.sort(list,valueComparator);
              for (Map.Entry<String, Double> entry : list) {
                  sortList.add(entry.getKey());
              }
              return sortList;
          }
          catch (Exception e){
              e.printStackTrace();
          }
  
          return featureList;
  
      }
  
  
      /**
       * 函数功能:匹配特征命中数目
       * @param feature
       * @param featuresList
       * @return
       */
      public Integer getMatchFeatureNum(String feature, List<String> featuresList){
  
          JSONObject jList= JSONObject.parseObject(feature);
          List<String> list = new ArrayList<>();
          for (String str :jList.keySet()){
              list.add(str);
          }
          //交集处理
          list.retainAll(featuresList);
  
          return list.size();
  
      }
  
      /**
       * 函数功能:特征匹配
       * 返回相似度TopN用例集,此处采用简单的特征值命中策略,即交集特征越多,相似度越高
       * @param featureInfo
       * @return
       */
      public JSONObject featuresMatch(FeatureInfo featureInfo) throws IOException {
  
          Date start = new Date();
          JSONObject res = new JSONObject();
          Map<GotTestCase,Double> map = new HashMap<>();
          List<TestCaseInput> sortList = new ArrayList<>();
  
          //对业务描述进行分词,并合并入匹配特征列表
          if(featureInfo.getCaseDesc()!=null){
              loadDict();
              List<String>  descriptionFeatures = getFMMFeatures(featureInfo.getCaseDesc());
              featureInfo.getFeatureList().addAll(descriptionFeatures);
          }
  
          List<GotTestCase> gotTestCaseList = testcaseService.findByScenarioId(featureInfo.getScenarioId());
          //遍历用例库
          for (GotTestCase  gotTestCase : gotTestCaseList){
              try{
                  //命中特征的数目
                  Integer num = getMatchFeatureNum(gotTestCase.getFeatures(),featureInfo.getFeatureList());
                  map.put(gotTestCase, Double.valueOf(num));
                  System.out.print("用例id:"+ gotTestCase.getId() +"特征匹配完成!\n");
              }
              catch (Exception e){
                  System.out.print("用例id:"+ gotTestCase.getId() +"特征匹配过程出错,请注意!\n");
                  map.put(gotTestCase, (double) 0);
              }
          }
  
          List<Map.Entry<GotTestCase, Double>> list = new ArrayList<>(map.entrySet());
          // 降序排序
          Collections.sort(list,valueCaseComparator);
          int cnt = 0;
          for (Map.Entry<GotTestCase, Double> entry : list) {
              cnt++;
              if (cnt<=featureInfo.getTopN()){
                  sortList.add(TestcaseService.formatTestCaseInput(entry.getKey()));
              }
          }
          Date end = new Date();
  
          res.put("caseList",sortList);
          res.put("caseCount",sortList.size());
          res.put("interval",end.getTime() - start.getTime());
          res.put("recommenderName","FMM算法");
  
          return res;
      }
  
      //====以下是用例膨胀===
  
      /**
       * 函数功能:获取可进行参数组合的Feature集
       * 注意:demo中针对请求串中的tesla参数进行用例膨胀,实际使用中,用户可以根据自己的业务自行改造.
       * @param testCaseInput
       * @return
       */
      public JSONArray getExpandFeatures( TestCaseInput testCaseInput){
  
          //对于符合query格式可进行用例膨胀
          try{
              //query
              String query = testCaseInput.getCaseRunStage().get(0).getData().get(0).getInput();
              JSONObject inputObj =  JSONObject.parseObject(query);
              JSONObject paramsObj = (JSONObject) inputObj.get("param_manager");
              JSONObject teslaObj = (JSONObject) paramsObj.get("expand_param");
              return (JSONArray) teslaObj.get("key_value_list");
          }
          catch (Exception e){
              return new JSONArray();
          }
  
      }
  
  
      /**
       * 函数功能:获取膨胀组合
       * 此处的膨胀组合是demo使用,真实场景用户可进行自定义,或者是自行训练出来.
       * @return
       */
      public JSONObject initFeatureCombination(){
  
          Map<String,String> featureCombination = new HashMap<>();
          //bool类型
          featureCombination.put("布尔类型","<true>,<false>");
          //Int32型组合
          featureCombination.put("Int32型组合","<0>,<-2147483648>,<2147483647>,<100>,<-100>");
          //Int型组合
          featureCombination.put("Int16型组合","<0>,<-32768>,<32767>,<100>,<-100>");
          //Long型组合,边界组合
          featureCombination.put("Long型组合","<0>,<-18446744073709551616>,<18446744073709551615>,<100>,<-100>");
          //字符串组合,乱码
          featureCombination.put("字符串组合","<nomalStr>,<?/@#%^^&&>");
  
          //以下用户可自行引入关于具体业务特征抽取算法
          featureCombination.put("线上抽取特征组合","<1/0/1>,<1/0/2>,<2/0/1>,<3/0/1>");
          featureCombination.put("FMM算法抽取特征组合","<猜你喜欢>,<直通车>,<定向>,<pid>");
  
  
          List<JSONObject> list = new ArrayList<>();
          for (String key : featureCombination.keySet()){
  
              JSONObject kv = new JSONObject();
              kv.put("key",key);
              kv.put("value",featureCombination.get(key));
              list.add(kv);
          }
          JSONObject res = new JSONObject();
          res.put("featureInitMap",featureCombination);
          res.put("featureInitList",list);
  
          return res;
      }
  
      /**
       * 函数功能:非递归计算所有膨胀组合
       * @param inputList 所有数组的列表
       * */
      public List<List<String>> calculateCombination(List<List<String>> inputList) {
          List<Integer> combination = new ArrayList<>();
          List<List<String>> outputList = new ArrayList<>();
          List<String> oneOutput ;
          int n=inputList.size();
          for (int i = 0; i < n; i++) {
              combination.add(0);
          }
          int i=0;
          boolean isContinue=false;
          do{
              oneOutput = new ArrayList<>();
              //添加一次循环生成的膨胀组合
              for (int j = 0; j < n; j++) {
                  oneOutput.add(inputList.get(j).get(combination.get(j)));
              }
              outputList.add(oneOutput);
  
              i++;
              combination.set(n-1, i);
              for (int j = n-1; j >= 0; j--) {
                  if (combination.get(j)>=inputList.get(j).size()) {
                      combination.set(j, 0);
                      i=0;
                      if (j-1>=0) {
                          combination.set(j-1, combination.get(j-1)+1);
                      }
                  }
              }
              isContinue=false;
              for (Integer integer : combination) {
                  if (integer != 0) {
                      isContinue=true;
                  }
              }
          }while (isContinue);
  
          return outputList;
      }
  
      /**
       * 函数功能:将需要膨胀的kv集合进行切分
       * @param expandkvList
       * @return
       */
      List<List<String>> getExpandKvGroupList(List<Expandkv> expandkvList){
  
          //首先扫描,expandValue是分隔,如果为null,""
          List<List<String>> teslaCalList = new ArrayList<>();
  
          //转化为参数组
          for(Expandkv expandkv : expandkvList){
              String expandValue = expandkv.getExpandValue();
              String key = expandkv.getKey();
              List<String> teslaCal = new ArrayList<>();
  
              try{
                  if("".equals(expandValue) || expandValue == null){
                      continue;
                  }
                  String[] valList = expandValue.split(">,<");
                  for(String val : valList){
                      val = val.replace("<","");
                      val = val.replace(">","");
                      String kvStr = key + ":" + val;
                      teslaCal.add(kvStr);
                  }
                  teslaCalList.add(teslaCal);
              }catch (Exception e){
                  //不符合格式的则忽略.
              }
  
          }
  
          return calculateCombination(teslaCalList);
  
      }
      /**
       * 函数功能:获取膨胀用例列表
       * 膨胀策略:demo中采用参数进行叉乘组合的方式进行膨胀,更多复杂的策略用户可根据业务进行添加
       * @return
       */
      public List<TestCaseInput> getExpandCases(ExpandInfo expandInfo){
  
          List<TestCaseInput> caseList = new ArrayList<>();
          TestCaseInput testCase = expandInfo.getTestCase();
          List<Expandkv> expandKvList = expandInfo.getExpandKvList();
          List<List<String>> expandKvGroupList = getExpandKvGroupList(expandKvList);
          //遍历膨胀参数列表
          for (List<String> oneExpandKvGroup : expandKvGroupList){
              TestCaseInput expandcase = (TestCaseInput) testCase.clone();
              //修改desc
              String caseDesc = expandcase.getDescription() + ".[用例膨胀组合]:" + Toolkit.implode("|",oneExpandKvGroup);
              expandcase.setDescription(caseDesc);
              //修改input
              String erpc = expandcase.getCaseRunStage().get(0).getData().get(0).getInput();
              JSONObject erpcObj = (JSONObject) JSONObject.parse(erpc);
              JSONObject paramsObj = (JSONObject) erpcObj.get("param_manager");
              JSONObject teslaObj = (JSONObject) paramsObj.get("expand_param");
  
              JSONArray kvObjListclone = new JSONArray();
              for(String onekv : oneExpandKvGroup){
                  String[] kv = onekv.split(":");
                  if(kv.length == 2){
                      JSONObject kvObj = new JSONObject();
                      kvObj.put("key",kv[0]);
                      kvObj.put("value",kv[1]);
                      kvObjListclone.add(kvObj);
                  }
              }
              teslaObj.put("key_value_list",kvObjListclone);
              expandcase.getCaseRunStage().get(0).getData().get(0).setInput(erpcObj.toString());
              caseList.add(expandcase);
          }
  
          return caseList;
      }
  
  
      /**
       * 函数功能:在用户选择了批量膨胀的用例集后,可进行批量保存用例
       * @param data
       * @return
       */
      public AjaxResult saveCaseList( MultiCase data){
          try {
  
              int i = 0;
  
              //遍历待保存的用例集
              for(TestCaseInput testcase :data.getCaseList()){
  
                  testcase.setId(null);
  
                  //如果是用例膨胀,直接保存入库
                  if("expand".equals(data.getMultiInfo().getType())){
                      testcaseService.saveTestCase(testcase);
                  }
                  //如果是推荐用例,可做改造后在保存
                  if("recommend".equals(data.getMultiInfo().getType())){
                      //修改desc
                      String caseDesc = data.getMultiInfo().getCaseDesc();
                      i++;
                      caseDesc =  caseDesc + "[系统推荐" + i + "]";
                      testcase.setDescription(caseDesc);
                      testcase.setTag("系统推荐");
  
                      try{
                          //此处是根据可膨胀的字段进行mock,使用方可自行定义
                          String erpc = testcase.getCaseRunStage().get(0).getData().get(0).getInput();
                          JSONObject erpcObj = (JSONObject) JSONObject.parse(erpc);
                          JSONObject paramsObj = (JSONObject) erpcObj.get("param_manager");
                          JSONObject teslaObj = (JSONObject) paramsObj.get("expand_param");
                          JSONArray kvObjList = (JSONArray) teslaObj.get("key_value_list");
                          kvObjList.clear();
  
                          for(Expandkv onekv : data.getMultiInfo().getExpandkvist()){
                              JSONObject kvObj = new JSONObject();
                              if(onekv.getKey()!= null && onekv.getValue() != null){
                                  kvObj.put("key",onekv.getKey());
                                  kvObj.put("value",onekv.getValue());
                                  kvObjList.add(kvObj);
                              }
  
                          }
                          testcase.getCaseRunStage().get(0).getData().get(0).setInput(erpcObj.toString());
                          testcaseService.saveTestCase(testcase);
                      }
                      catch (Exception e) {
                          //如果请求不符合规范,则不再改造请求串,直接入库保存.
                          testcaseService.saveTestCase(testcase);
                      }
                  }
  
              }
              return AjaxResult.succResult("save ok");
          } catch (Exception e) {
              return AjaxResult.errorResult(e.getMessage());
          }
  
      }
  
  }
  