wangting
2023-08-02 80b00fc662651e4ee20507904364bfcebce2e4ac
Merge remote-tracking branch 'origin/master'
已修改20个文件
已添加4个文件
1665 ■■■■■ 文件已修改
Source/UBCS-WEB/src/components/Master/MasterTransfer.vue 44 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS-WEB/src/components/MasterCrud/VciMasterCrud.vue 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS-WEB/src/components/Theme/ThemeAttrCrud.vue 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS-WEB/src/components/Theme/ThemeClassifyTreeform.vue 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS-WEB/src/components/Theme/ThemeClassifyTrees.vue 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS-WEB/src/components/template/SetPersonnel.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-auth/src/main/java/com/vci/ubcs/auth/service/BladeUserDetailsServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-common/src/main/java/com/vci/ubcs/common/constant/LauncherConstant.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-ops-api/ubcs-flow-api/src/main/java/com/vci/ubcs/flow/core/dto/FlowStatusDTO.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-ops-api/ubcs-flow-api/src/main/java/com/vci/ubcs/flow/core/feign/IFlowClient.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-ops-api/ubcs-flow-api/src/main/java/com/vci/ubcs/flow/core/feign/IFlowClientFallback.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-ops-api/ubcs-flow-api/src/main/java/com/vci/ubcs/flow/core/vo/FlowTaskHisVO.java 345 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-ops/ubcs-flow/src/main/java/com/vci/ubcs/flow/business/feign/FlowClient.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-ops/ubcs-flow/src/main/java/com/vci/ubcs/flow/engine/constant/FlowEngineConstant.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-ops/ubcs-flow/src/main/java/com/vci/ubcs/flow/engine/envent/FlowStatusListener.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-ops/ubcs-flow/src/main/java/com/vci/ubcs/flow/engine/utils/FlowableUtils.java 727 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-service/ubcs-code/src/main/java/com/vci/ubcs/code/controller/CodeClassifyTemplateAttrController.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-service/ubcs-code/src/main/java/com/vci/ubcs/code/mapper/CodeClassifyMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-service/ubcs-code/src/main/java/com/vci/ubcs/code/service/impl/CodeClassifyServiceImpl.java 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-service/ubcs-code/src/main/java/com/vci/ubcs/code/service/impl/CodeClassifyTemplateAttrServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-service/ubcs-code/src/main/java/com/vci/ubcs/code/service/impl/CodeClstemplateServiceImpl.java 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-service/ubcs-code/src/main/java/com/vci/ubcs/code/service/impl/MdmEngineServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-service/ubcs-code/src/main/resources/mapper/CodeCLassifyMapper.xml 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS/ubcs-service/ubcs-omd/src/main/java/com/vci/ubcs/omd/service/impl/BtmAttributeServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Source/UBCS-WEB/src/components/Master/MasterTransfer.vue
@@ -66,29 +66,31 @@
      this.dialogPush = this.visible;
    },
    //表头数据 ç”¨æ¥æ¸²æŸ“穿梭框
    tableHeadData:{
      handler(newval,oldval){
    tableHeadData: {
      handler(newval, oldval) {
        console.log(newval)
       if(newval){
         //excel表头数据转换
         this.tableHeadFindData=newval.map(obj => obj.label);
         this.tableHeadFindData.forEach((city, index) => {
           this.data.push({
             label: city,
             key: index,
           });
         });
         console.log(this.data)
         //excel表格数据转换
         this.option.column[0].children=newval.map(obj => {
           return {
             label: obj.label,
             prop: obj.prop
           }
         })
       }
        if (newval) {
          // æ¸…空data数组
          this.data = [];
          // excel表头数据转换
          this.tableHeadFindData = newval.map(obj => obj.label);
          this.tableHeadFindData.forEach((city, index) => {
            this.data.push({
              label: city,
              key: index,
            });
          });
          console.log(this.data)
          // excel表格数据转换
          this.option.column[0].children = newval.map(obj => {
            return {
              label: obj.label,
              prop: obj.prop
            }
          })
        }
      }
},
    },
    tableData(){
      // å°†å€¼é‡Œé¢çš„true或false改变为是或否
      this.option.data = this.tableData.map(obj => {
Source/UBCS-WEB/src/components/MasterCrud/VciMasterCrud.vue
@@ -234,11 +234,14 @@
        hasDownload: true,
        hasUpload: true,
        height: 100
      }
      },
      result:''
    };
  },
  computed: {},
  created() {
   const index = this.$route.query.id.indexOf('@');
   this.result = this.$route.query.id.substring(0, index);
  },
  mounted() {
  },
@@ -386,7 +389,7 @@
            processTS({templateId: this.templateOid, buttonTypeKey: 'PUBLIC'}).then(res => {
              if (res.data.data.records && res.data.data.records.length!=0)  {
                this.userName = localStorage.getItem("username");
                this.parameter.template = this.userName + "-发布" + "[物品]";
                this.parameter.template = this.userName + '-发布['+this.result+'-' + this.selectRow[0].name + ']';
                this.parameter.type = 'PUBLIC';
                this.parameter.code=this.templateOid
                this.title = '流程审批'
@@ -439,7 +442,7 @@
            processTS({ templateId: this.templateOid, buttonTypeKey: 'PUBLIC' }).then(res => {
              if (res.data.records != [] && res.data.data.records.length!=0) {
                this.userName = localStorage.getItem("username");
                this.parameter.template = this.userName + "停用" + "[物品]";
                this.parameter.template = this.userName + '-停用['+this.result+'-' + this.selectRow[0].name + ']';
                this.parameter.type = 'DISABLE';
                this.parameter.code=this.templateOid
                this.title = '停用(冻结)编码数据'
@@ -492,7 +495,7 @@
              if (res.data.data.records && res.data.data.records.length!=0)  {
                this.parameter=res.data.data.records[0]
                this.userName = localStorage.getItem("username");
                this.parameter.template = this.userName + "启用" + "[物品]";
                this.parameter.template = this.userName + '-启用['+this.result+'-' + this.selectRow[0].name + ']';
                this.parameter.type = 'Released';
                this.parameter.code=this.templateOid
                this.title = '启用编码数据'
@@ -551,7 +554,7 @@
            processTS({ templateId: this.templateOid, buttonTypeKey: 'PUBLIC' }).then(res => {
              if (res.data.records != [] && res.data.data.records.length!=0) {
                this.userName = localStorage.getItem("username");
                this.parameter.template = this.userName + "回收" + "[物品]";
                this.parameter.template = this.userName + '-回收['+this.result+'-' + this.selectRow[0].name + ']';
                this.parameter.type = 'TakeBack';
                this.parameter.code=this.templateOid
                this.title = '回收编码数据'
Source/UBCS-WEB/src/components/Theme/ThemeAttrCrud.vue
@@ -88,6 +88,12 @@
      <el-dialog :visible.sync="enumVisible" append-to-body title="枚举注入" :before-close="this.enumBeforeClose">
        <!-- æ¸²æŸ“表格及按钮 -->
        <!-- æ¸²æŸ“表格及按钮 -->
        <el-alert
          title="请新增后点击单元格输入枚举数据"
          type="success"
          style="margin-bottom: 10px;display: inline-block"
          :closable="false">
        </el-alert>
        <div>
          <el-button icon="el-icon-plus" type="primary" @click="addRow">新增</el-button>
          <el-table :data="tableData" style="width: 100%" @cell-click="handleCellClick"
@@ -1799,29 +1805,33 @@
    },
    //枚举注入保存
    enumAddHandle() {
      let hasError = false; // æ·»åŠ ä¸€ä¸ªå˜é‡
      this.tableData.forEach((item, index) => {
        if (item.key === '') {
          this.$message.warning(`第${index + 1}行的选项值不能为空`);
          hasError = true;
          return;
        } else if (item.value === '') {
          this.$message.warning(`第${index + 1}行的选项中文标签不能为空`);
          hasError = true;
          return;
        }
      });
        // ä¿å­˜æ‰§è¡Œé€»è¾‘
      if (!hasError) {
        if (this.CurrentCell) {
          this.$set(this.CurrentCell, 'enumString', JSON.stringify(this.tableData));
          this.enumVisible = false;
        } else {
          this.$set(this.attrSelectList[0], 'enumString', JSON.stringify(this.tableData));
          this.tableData=[];
          this.enumVisible = false;
        }
      }
     if(this.tableData.length>=1){
       let hasError = false; // æ·»åŠ ä¸€ä¸ªå˜é‡
       this.tableData.forEach((item, index) => {
         if (item.key === '') {
           this.$message.warning(`第${index + 1}行的选项值不能为空`);
           hasError = true;
           return;
         } else if (item.value === '') {
           this.$message.warning(`第${index + 1}行的选项中文标签不能为空`);
           hasError = true;
           return;
         }
       });
       // ä¿å­˜æ‰§è¡Œé€»è¾‘
       if (!hasError) {
         if (this.CurrentCell) {
           this.$set(this.CurrentCell, 'enumString', JSON.stringify(this.tableData));
           this.enumVisible = false;
         } else {
           this.$set(this.attrSelectList[0], 'enumString', JSON.stringify(this.tableData));
           this.tableData=[];
           this.enumVisible = false;
         }
       }
     }else {
       this.$message.warning('请添加枚举注入数据!')
     }
    },
    // å°†æ­£åœ¨ç¼–辑的行的状态变为 null,即退出编辑状态
    saveRow() {
Source/UBCS-WEB/src/components/Theme/ThemeClassifyTreeform.vue
@@ -92,7 +92,11 @@
          <el-input style="width: 260px;margin-left: 15px" placeholder="输入值后进行模糊查询" v-model="SelectFInd"></el-input>
          <el-button size="small"  type="primary" plain @click="BtmSelectFindeHandler" style="margin-left: 20px">查询</el-button>
        </template>
        <avue-crud :data="BtmData" :option="masterOption" @select="btmSelect"></avue-crud>
        <avue-crud :data="BtmData" :option="masterOption" @row-click="btmSelect">
          <template slot="radio" slot-scope="{row}">
            <el-radio v-model="masterRow" :label="row.$index" style="padding-left: 10px !important;">{{''}}</el-radio>
          </template>
        </avue-crud>
        <div style="height: 30px">
          <div style="display: inline-block;float: left;border: 1px solid #eee;padding: 5px;margin-top: 5px;font-size: 14px " >已设置的值为:[{{this.loneTreeNewForm.btmTypeName}}]</div>
          <div style="padding-top: 10px;display: flex; justify-content: flex-end;float: right;overflow: hidden" >
@@ -113,6 +117,7 @@
  props: ['loneTreeNewForm','flag','Editclose','TreeFlag','nodeClickList'],
  data() {
    return {
      masterRow:0,
      SelectFInd:"",
      masterName:"",
      masterOid:"",
@@ -166,10 +171,15 @@
        addBtn:false,
        index:true,
        border:true,
        selection:true,
        menu:false,
        height:380,
        column:[
          {
            label: '',
            prop: 'radio',
            width: 60,
            display: false
          },
          {
            label:'英文名称',
            prop:'id'
@@ -567,9 +577,9 @@
    //业务类型多选
    btmSelect(row){
      this.btmSelectList=row;
      this.btmName=row[0].name;
      this.btmOid=row[0].oid;
      this.btmId=row[0].id;
      this.btmName=row.name;
      this.btmOid=row.oid;
      this.btmId=row.id;
    },
    //业务类型接口
    btmdefaultRend(masterParameter){
Source/UBCS-WEB/src/components/Theme/ThemeClassifyTrees.vue
@@ -303,7 +303,11 @@
            <el-input style="width: 260px;margin-left: 15px" placeholder="输入值后进行模糊查询" v-model="SelectFInd"></el-input>
            <el-button size="small"  type="primary" plain @click="BtmSelectFindeHandler" style="margin-left: 20px">查询</el-button>
          </template>
          <avue-crud :data="BtmData" :option="masterOption" @select="btmSelect"></avue-crud>
          <avue-crud :data="BtmData" :option="masterOption" @row-click="btmSelect">
            <template slot="radio" slot-scope="{row}">
              <el-radio v-model="masterRow" :label="row.$index" style="padding-left: 10px !important;">{{''}}</el-radio>
            </template>
          </avue-crud>
          <div style="height: 30px">
            <div style="display: inline-block;float: left;border: 1px solid #eee;padding: 5px;margin-top: 5px;font-size: 14px " >已设置的值为:[{{this.TreeAddform.btmTypeName}}]</div>
            <div style="padding-top: 10px;display: flex; justify-content: flex-end;float: right;overflow: hidden" >
@@ -350,6 +354,7 @@
  inject: ["crudTreeData"],
  data() {
    return {
      masterRow:0,
      AddLoading:false,
      // å®šä¹‰ä¸€ä¸ªå˜é‡æ¥ä¿å­˜æ ‘请求的数量
      requestCount:0,
@@ -389,10 +394,15 @@
        addBtn:false,
        index:true,
        border:true,
        selection:true,
        menu:false,
        height:380,
        column:[
          {
            label: '',
            prop: 'radio',
            width: 60,
            display: false
          },
          {
            label:'英文名称',
            prop:'id'
@@ -956,8 +966,8 @@
    //业务类型多选
    btmSelect(row){
      this.btmSelectList=row;
      this.btmName=row[0].name;
      this.btmOid=row[0].id;
      this.btmName=row.name;
      this.btmOid=row.id;
    },
    //业务类型接口
    btmdefaultRend(masterParameter){
Source/UBCS-WEB/src/components/template/SetPersonnel.vue
@@ -10,7 +10,7 @@
                <el-input placeholder="流程模板" v-model="saveParam.modelName" disabled></el-input>
            </el-form-item>
            <el-form-item label="流程名称" prop="processName">
                <el-input placeholder="流程名称" v-model="saveParam.processName">
                <el-input placeholder="流程名称" v-model="saveParam.template">
                </el-input>
            </el-form-item>
            <el-form-item label="流程描述">
@@ -92,9 +92,10 @@
        },
      parameter:{
          handler(newval,oldval){
            this.saveParam=newval
          },deep:true,
          immediate:true
                  this.saveParam=newval;
          },
        deep:true,
            immediate:true
      }
    },
    data() {
Source/UBCS/ubcs-auth/src/main/java/com/vci/ubcs/auth/service/BladeUserDetailsServiceImpl.java
@@ -31,6 +31,7 @@
import org.springblade.core.jwt.JwtUtil;
import org.springblade.core.jwt.props.JwtProperties;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.support.Kv;
import org.springblade.core.tool.utils.*;
@@ -132,6 +133,7 @@
        if (count >= failCount) {
            throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_TOO_MANY_FAILS);
        }
        // å°†è¯·æ±‚头放进线程中
        //超级管理员配置文件配置账号密码,实现登录, é»˜è®¤ç§Ÿæˆ·id为000000
        if(tenantId.equals(this.tenantId)){
            if (!this.userName.equals(username) && !password.equalsIgnoreCase(this.password)) {
Source/UBCS/ubcs-common/src/main/java/com/vci/ubcs/common/constant/LauncherConstant.java
@@ -47,13 +47,13 @@
     * nacos prod åœ°å€
     */
    //String NACOS_PROD_ADDR = "dev.vci-tech.com:38848";
    String NACOS_PROD_ADDR = "127.0.0.1:8848";
    String NACOS_PROD_ADDR = "192.168.243.130:8848";
    /**
     * nacos test åœ°å€
     */
    //String NACOS_TEST_ADDR = "dev.vci-tech.com:38848";
    String NACOS_TEST_ADDR = "127.0.0.1:8848";
    String NACOS_TEST_ADDR = "192.168.243.130:8848";
    /**
     * sentinel dev åœ°å€
Source/UBCS/ubcs-ops-api/ubcs-flow-api/src/main/java/com/vci/ubcs/flow/core/dto/FlowStatusDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,80 @@
package com.vci.ubcs.flow.core.dto;
import com.vci.ubcs.flow.core.vo.FlowTaskHisVO;
import java.util.List;
import java.util.Map;
/**
 * æµç¨‹çš„æ‰§è¡Œä¿¡æ¯
 * @author dangsn
 * @Date 2023/07/31
 */
public class FlowStatusDTO implements java.io.Serializable{
    /**
     * åºåˆ—化
     */
    private static final long serialVersionUID = 7539176109676429282L;
    /**
     * ä¸šåŠ¡ç±»åž‹
     */
    private String btmType;
    /**
     * æ•°æ®çš„主键
     */
    private List<String> oids;
    /**
     * å˜é‡çš„内容
     */
    private Map<String,Object> variableMap;
    /**
     * æ‰§è¡ŒåŽ†å²
     */
    private List<FlowTaskHisVO> taskHisVOList;
    public String getBtmType() {
        return btmType;
    }
    public void setBtmType(String btmType) {
        this.btmType = btmType;
    }
    public List<String> getOids() {
        return oids;
    }
    public void setOids(List<String> oids) {
        this.oids = oids;
    }
    public Map<String, Object> getVariableMap() {
        return variableMap;
    }
    public void setVariableMap(Map<String, Object> variableMap) {
        this.variableMap = variableMap;
    }
    public List<FlowTaskHisVO> getTaskHisVOList() {
        return taskHisVOList;
    }
    public void setTaskHisVOList(List<FlowTaskHisVO> taskHisVOList) {
        this.taskHisVOList = taskHisVOList;
    }
    @Override
    public String toString() {
        return "FlowStatusDTO{" +
            "btmType='" + btmType + '\'' +
            ", oids=" + oids +
            ", variableMap=" + variableMap +
            ", taskHisVOList=" + taskHisVOList +
            '}';
    }
}
Source/UBCS/ubcs-ops-api/ubcs-flow-api/src/main/java/com/vci/ubcs/flow/core/feign/IFlowClient.java
@@ -16,7 +16,9 @@
 */
package com.vci.ubcs.flow.core.feign;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.vci.ubcs.flow.core.entity.BladeFlow;
import com.vci.ubcs.flow.core.entity.ProcessTemplate;
import org.springblade.core.launch.constant.AppConstant;
import org.springblade.core.tool.api.R;
import org.springframework.cloud.openfeign.FeignClient;
@@ -25,6 +27,7 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
import java.util.Map;
/**
@@ -44,6 +47,8 @@
    String COMPLETE_TASK = API_PREFIX + "/complete-task";
    String TASK_VARIABLE = API_PREFIX + "/task-variable";
    String TASK_VARIABLES = API_PREFIX + "/task-variables";
    String SELECT_BY_WRAPPER = API_PREFIX + "/select-by-wrapper";
    String PROCESS_BATCH_SAVE = API_PREFIX + "/PROCESS-BATCH-SAVE";
    /**
     * å¼€å¯æµç¨‹
@@ -97,4 +102,23 @@
     */
    @GetMapping(TASK_VARIABLES)
    R<Map<String, Object>> taskVariables(@RequestParam("taskId") String taskId);
    /**
     * æŸ¥è¯¢æ•°æ®wrapper方式
     *
     * @param wrapperMap æ¡ä»¶
     * @return R
     */
    @PostMapping(SELECT_BY_WRAPPER)
    R<List<ProcessTemplate>> selectByWrapper(@RequestBody Map<String,Object> wrapperMap);
    /**
     * æ‰¹é‡ä¿å­˜æµç¨‹æ•°æ®
     *
     * @param listProcessTemplate ä¿å­˜æ•°æ®
     * @return R
     */
    @PostMapping(PROCESS_BATCH_SAVE)
    R processBatchSave(@RequestBody List<ProcessTemplate> listProcessTemplate);
}
Source/UBCS/ubcs-ops-api/ubcs-flow-api/src/main/java/com/vci/ubcs/flow/core/feign/IFlowClientFallback.java
@@ -16,10 +16,13 @@
 */
package com.vci.ubcs.flow.core.feign;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.vci.ubcs.flow.core.entity.BladeFlow;
import com.vci.ubcs.flow.core.entity.ProcessTemplate;
import org.springblade.core.tool.api.R;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
/**
@@ -55,4 +58,14 @@
        return R.fail("远程调用失败");
    }
    @Override
    public R<List<ProcessTemplate>> selectByWrapper( Map<String,Object> wrapperMap) {
        return R.fail("远程调用失败");
    }
    @Override
    public R processBatchSave(List<ProcessTemplate> listProcessTemplate) {
        return R.fail("远程调用失败");
    }
}
Source/UBCS/ubcs-ops-api/ubcs-flow-api/src/main/java/com/vci/ubcs/flow/core/vo/FlowTaskHisVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,345 @@
package com.vci.ubcs.flow.core.vo;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
 * æµç¨‹ä»»åŠ¡åŽ†å²æ˜¾ç¤ºå¯¹è±¡
 * @author dangsn
 * @date 2023/07/31
 */
public class FlowTaskHisVO implements java.io.Serializable{
    /**
     * ç¦æ­¢ä¿®æ”¹è¿™ä¸ªå€¼
     */
    private static final long serialVersionUID = -5305339505741718337L;
    /**
     * åŽ†å²è®°å½•ä¸»é”®
     */
    private String oid;
    /**
     * æ´»åЍ䏻键
     */
    private String activityId;
    /**
     * ä»»åŠ¡åç§°
     */
    private String activityName;
    /**
     * ä»»åŠ¡ç±»åž‹
     */
    private String activityType;
    /**
     * æµç¨‹å®šä¹‰ä¸»é”®
     */
    private String processDefinitionId;
    /**
     * æµç¨‹å®žä¾‹ä¸»é”®
     */
    private String processInstanceId;
    /**
     * æµç¨‹çš„名称
     */
    private String processName;
    /**
     * æµç¨‹æ¨¡æ¿åç§°
     */
    private String processDefinitionName;
    /**
     * æ‰§è¡Œä¸»é”®
     */
    private String executionId;
    /**
     * ä»»åС䏻键
     */
    private String taskId;
    /**
     * è°ƒç”¨æµç¨‹ä¸»é”®
     */
    private String calledProcessInstanceId;
    /**
     * æ‰§è¡Œäººè´¦å·
     */
    private String assignee;
    /**
     * æ‰§è¡Œäººå§“名
     */
    private String assigneeName;
    /**
     * å¼€å§‹æ—¶é—´
     */
    private Date startTime;
    /**
     * å®Œæˆæ—¶é—´
     */
    private Date endTime;
    /**
     * è€—æ—¶
     */
    private Long durationInMillis;
    /**
     * åˆ é™¤åŽŸå› 
     */
    private String deleteReason;
    /**
     * ç§Ÿæˆ·ä¸»é”®
     */
    private String tenantId;
    /**
     * å®¡æ‰¹æ„è§
     */
    private String description;
    /**
     * å˜é‡
     */
    private Map<String,Object> variablesMap;
    /**
     * å¤šæ¡æ•°æ®æ—¶å…³è”的业务数据主键
     */
    private List<String> linkBusinessOids;
    /**
     * æ˜¾ç¤ºè·¯å¾„
     */
    private String displayUrl;
    /**
     * å‘起流程的业务类型
     */
    private String btmType;
    public String getProcessName() {
        return processName;
    }
    public void setProcessName(String processName) {
        this.processName = processName;
    }
    public String getProcessDefinitionName() {
        return processDefinitionName;
    }
    public void setProcessDefinitionName(String processDefinitionName) {
        this.processDefinitionName = processDefinitionName;
    }
    public String getBtmType() {
        return btmType;
    }
    public void setBtmType(String btmType) {
        this.btmType = btmType;
    }
    public String getOid() {
        return oid;
    }
    public void setOid(String oid) {
        this.oid = oid;
    }
    public List<String> getLinkBusinessOids() {
        return linkBusinessOids;
    }
    public void setLinkBusinessOids(List<String> linkBusinessOids) {
        this.linkBusinessOids = linkBusinessOids;
    }
    public String getDisplayUrl() {
        return displayUrl;
    }
    public void setDisplayUrl(String displayUrl) {
        this.displayUrl = displayUrl;
    }
    public String getActivityId() {
        return activityId;
    }
    public void setActivityId(String activityId) {
        this.activityId = activityId;
    }
    public String getActivityName() {
        return activityName;
    }
    public void setActivityName(String activityName) {
        this.activityName = activityName;
    }
    public String getActivityType() {
        return activityType;
    }
    public void setActivityType(String activityType) {
        this.activityType = activityType;
    }
    public String getProcessDefinitionId() {
        return processDefinitionId;
    }
    public void setProcessDefinitionId(String processDefinitionId) {
        this.processDefinitionId = processDefinitionId;
    }
    public String getProcessInstanceId() {
        return processInstanceId;
    }
    public void setProcessInstanceId(String processInstanceId) {
        this.processInstanceId = processInstanceId;
    }
    public String getExecutionId() {
        return executionId;
    }
    public void setExecutionId(String executionId) {
        this.executionId = executionId;
    }
    public String getTaskId() {
        return taskId;
    }
    public void setTaskId(String taskId) {
        this.taskId = taskId;
    }
    public String getCalledProcessInstanceId() {
        return calledProcessInstanceId;
    }
    public void setCalledProcessInstanceId(String calledProcessInstanceId) {
        this.calledProcessInstanceId = calledProcessInstanceId;
    }
    public String getAssignee() {
        return assignee;
    }
    public void setAssignee(String assignee) {
        this.assignee = assignee;
    }
    public String getAssigneeName() {
        return assigneeName;
    }
    public void setAssigneeName(String assigneeName) {
        this.assigneeName = assigneeName;
    }
    public Date getStartTime() {
        return startTime;
    }
    public void setStartTime(Date startTime) {
        this.startTime = startTime;
    }
    public Date getEndTime() {
        return endTime;
    }
    public void setEndTime(Date endTime) {
        this.endTime = endTime;
    }
    public Long getDurationInMillis() {
        return durationInMillis;
    }
    public void setDurationInMillis(Long durationInMillis) {
        this.durationInMillis = durationInMillis;
    }
    public String getDeleteReason() {
        return deleteReason;
    }
    public void setDeleteReason(String deleteReason) {
        this.deleteReason = deleteReason;
    }
    public String getTenantId() {
        return tenantId;
    }
    public void setTenantId(String tenantId) {
        this.tenantId = tenantId;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public Map<String, Object> getVariablesMap() {
        return variablesMap;
    }
    public void setVariablesMap(Map<String, Object> variablesMap) {
        this.variablesMap = variablesMap;
    }
    @Override
    public String toString() {
        return "FlowTaskHisVO{" +
            "oid='" + oid + '\'' +
            ", activityId='" + activityId + '\'' +
            ", activityName='" + activityName + '\'' +
            ", activityType='" + activityType + '\'' +
            ", processDefinitionId='" + processDefinitionId + '\'' +
            ", processInstanceId='" + processInstanceId + '\'' +
            ", processName='" + processName + '\'' +
            ", processDefinitionName='" + processDefinitionName + '\'' +
            ", executionId='" + executionId + '\'' +
            ", taskId='" + taskId + '\'' +
            ", calledProcessInstanceId='" + calledProcessInstanceId + '\'' +
            ", assignee='" + assignee + '\'' +
            ", assigneeName='" + assigneeName + '\'' +
            ", startTime=" + startTime +
            ", endTime=" + endTime +
            ", durationInMillis=" + durationInMillis +
            ", deleteReason='" + deleteReason + '\'' +
            ", tenantId='" + tenantId + '\'' +
            ", description='" + description + '\'' +
            ", variablesMap=" + variablesMap +
            ", linkBusinessOids=" + linkBusinessOids +
            ", displayUrl='" + displayUrl + '\'' +
            ", btmType='" + btmType + '\'' +
            '}';
    }
}
Source/UBCS/ubcs-ops/ubcs-flow/src/main/java/com/vci/ubcs/flow/business/feign/FlowClient.java
@@ -16,9 +16,12 @@
 */
package com.vci.ubcs.flow.business.feign;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.vci.ubcs.flow.core.entity.BladeFlow;
import com.vci.ubcs.flow.core.entity.ProcessTemplate;
import com.vci.ubcs.flow.core.feign.IFlowClient;
import com.vci.ubcs.flow.core.utils.TaskUtil;
import com.vci.ubcs.flow.engine.service.ProcessTemplateService;
import lombok.AllArgsConstructor;
import org.flowable.engine.IdentityService;
import org.flowable.engine.RuntimeService;
@@ -34,6 +37,7 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
/**
@@ -49,6 +53,7 @@
    private final RuntimeService runtimeService;
    private final IdentityService identityService;
    private final TaskService taskService;
    private final ProcessTemplateService processTemplateService;
    @Override
    @PostMapping(START_PROCESS_INSTANCE_BY_ID)
@@ -104,4 +109,33 @@
        return R.data(taskService.getVariables(taskId));
    }
    /**
     * æŸ¥è¯¢æ•°æ®wrapper方式
     *
     * @param wrapperMap æ¡ä»¶
     * @return R
     */
    @Override
    @PostMapping(SELECT_BY_WRAPPER)
    public R<List<ProcessTemplate>> selectByWrapper(@RequestBody Map<String,Object> wrapperMap){
        return R.data(processTemplateService.listByMap(wrapperMap));
    }
    /**
     * æ‰¹é‡ä¿å­˜æµç¨‹æ•°æ®
     *
     * @param listProcessTemplate ä¿å­˜æ•°æ®
     * @return R
     */
    @Override
    @PostMapping(PROCESS_BATCH_SAVE)
    public R processBatchSave(@RequestBody List<ProcessTemplate> listProcessTemplate) {
        if(listProcessTemplate.size() == 0){
            return R.fail("为传入数据,请检查!");
        }
        boolean b = processTemplateService.saveBatch(listProcessTemplate);
        return R.data(b);
    }
}
Source/UBCS/ubcs-ops/ubcs-flow/src/main/java/com/vci/ubcs/flow/engine/constant/FlowEngineConstant.java
@@ -51,4 +51,24 @@
    String USR_TASK = "userTask";
    /**
     * çŠ¶æ€å€¼
     */
    String STATUS_VALUE = "statusValue";
    /**
     * è¿œç¨‹è°ƒç”¨çš„地址
     */
    String REMOTE_METHOD = "remoteMethod";
    /**
     * ä¸»é”®
     */
    String OIDS = "oids";
    /**
     * ä¸šåŠ¡ç±»åž‹
     */
    String BTMTYPE = "btmtype";
}
Source/UBCS/ubcs-ops/ubcs-flow/src/main/java/com/vci/ubcs/flow/engine/envent/FlowStatusListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,123 @@
package com.vci.ubcs.flow.engine.envent;
import com.vci.ubcs.flow.core.constant.ProcessConstant;
import com.vci.ubcs.flow.core.dto.FlowStatusDTO;
import com.vci.ubcs.flow.engine.constant.FlowEngineConstant;
import com.vci.ubcs.flow.engine.utils.FlowableUtils;
import com.vci.ubcs.starter.exception.VciBaseException;
import com.vci.ubcs.starter.web.util.LangBaseUtil;
import com.vci.ubcs.starter.web.util.VciBaseUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.engine.HistoryService;
import org.flowable.engine.TaskService;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.engine.impl.el.FixedValue;
import org.flowable.task.service.delegate.DelegateTask;
import org.springblade.core.jwt.JwtUtil;
import org.springblade.core.launch.constant.TokenConstant;
import org.springblade.core.tool.utils.WebUtil;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import java.util.Map;
@Slf4j
@Component
public class FlowStatusListener implements TaskListener, ApplicationContextAware {
    /**
     * è¿œç¨‹è°ƒç”¨åœ°å€ã€‚切记:名称要与流程中定义的一样
     */
    private FixedValue remoteMethod;
    /**
     * çŠ¶æ€å€¼ã€‚åˆ‡è®°ï¼šåç§°è¦ä¸Žæµç¨‹ä¸­å®šä¹‰çš„ä¸€æ ·
     */
    private FixedValue statusValue;
    private static  ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext arg0) throws BeansException {
        applicationContext = arg0;
    }
    @Override
    public void notify(DelegateTask delegateTask) {
        Map taskVariable = delegateTask.getVariables();
        boolean pass = (boolean) taskVariable.get(ProcessConstant.PASS_KEY);
        //获取状态修改信息
        if(pass){
            String restURL = remoteMethod.getExpressionText();
            String status = statusValue.getExpressionText();
            //获取业务数据信息
            String oids = (String) taskVariable.get(FlowEngineConstant.OIDS);
            String btmType = (String) taskVariable.get(FlowEngineConstant.BTMTYPE);
            taskVariable.put(FlowEngineConstant.REMOTE_METHOD,restURL);
            taskVariable.put(FlowEngineConstant.STATUS_VALUE,status);
            if(StringUtils.isEmpty(oids)){
                throw new VciBaseException("执行状态修改事件时,业务数据oid为空!");
            }
            if(StringUtils.isEmpty(btmType)){
                throw new VciBaseException("执行状态修改事件时,业务类型btmType为空!");
            }
            if(StringUtils.isEmpty(restURL)){
                throw new VciBaseException("执行状态修改事件时,远程调用地址为空!");
            }
            if(StringUtils.isEmpty(status)){
                throw new VciBaseException("执行状态修改事件时,状态为空!");
            }
            HistoryService historyService = applicationContext.getBean(HistoryService.class);
            TaskService taskService = applicationContext.getBean(TaskService.class);
            FlowStatusDTO flowStatusDTO = new FlowStatusDTO();
            flowStatusDTO.setBtmType(btmType);
            flowStatusDTO.setOids(VciBaseUtil.str2List(oids));
            flowStatusDTO.setVariableMap(taskVariable);
            flowStatusDTO.setTaskHisVOList(FlowableUtils.listTaskHistory(delegateTask.getProcessInstanceId(),historyService,taskService));
            String token = JwtUtil.getToken(WebUtil.getRequest().getHeader(TokenConstant.HEADER));
            HttpComponentsClientHttpRequestFactory requestFactory=new HttpComponentsClientHttpRequestFactory();
            requestFactory.setReadTimeout(300000);
            requestFactory.setConnectionRequestTimeout(300000);
            requestFactory.setConnectTimeout(300000);
            RestTemplate restTemplate = new RestTemplate(requestFactory);
            HttpHeaders headers = new HttpHeaders();
            headers.add(TokenConstant.HEADER,token);
            headers.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity httpEntity = new HttpEntity<>(flowStatusDTO,headers);
            Map<String, Object> result = null;
            try {
                result = restTemplate.postForObject(restURL, httpEntity, Map.class);
            } catch (HttpClientErrorException e) {
                throw new VciBaseException(LangBaseUtil.getErrorMsg(e),new String[]{},e);
            }catch (Throwable e){
                throw new VciBaseException(LangBaseUtil.getErrorMsg(e),new String[]{},e);
            }
            if(result == null){
                throw new VciBaseException("业务事件时候没有返回值,不确定是否执行成功");
            }
            if(CollectionUtils.isEmpty(result) && !(Boolean) result.get("success")){
                throw new VciBaseException((String) result.get("message"));
            }
        }
    }
}
Source/UBCS/ubcs-ops/ubcs-flow/src/main/java/com/vci/ubcs/flow/engine/utils/FlowableUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,727 @@
package com.vci.ubcs.flow.engine.utils;
import com.vci.ubcs.flow.core.utils.TaskUtil;
import com.vci.ubcs.flow.core.vo.FlowTaskHisVO;
import com.vci.ubcs.flow.engine.constant.FlowEngineConstant;
import com.vci.ubcs.starter.exception.VciBaseException;
import com.vci.ubcs.starter.web.util.VciBaseUtil;
import com.vci.ubcs.system.user.cache.UserCache;
import com.vci.ubcs.system.user.entity.User;
import com.vci.ubcs.system.user.vo.UserVO;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.*;
import org.flowable.engine.HistoryService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
import org.flowable.engine.task.Comment;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.stream.Collectors;
/**
 * @author weidy
 * @date 2021/4/2
 */
public class FlowableUtils {
    /**
     * æ ¹æ®èŠ‚ç‚¹ï¼ŒèŽ·å–å…¥å£è¿žçº¿
     * @param source èŠ‚ç‚¹
     * @return åŒ…含的线
     */
    public static List<SequenceFlow> getElementIncomingFlows(FlowElement source) {
        List<SequenceFlow> sequenceFlows = null;
        if (source instanceof Task) {
            sequenceFlows = ((Task) source).getIncomingFlows();
        } else if (source instanceof Gateway) {
            sequenceFlows = ((Gateway) source).getIncomingFlows();
        } else if (source instanceof SubProcess) {
            sequenceFlows = ((SubProcess) source).getIncomingFlows();
        } else if (source instanceof StartEvent) {
            sequenceFlows = ((StartEvent) source).getIncomingFlows();
        } else if (source instanceof EndEvent) {
            sequenceFlows = ((EndEvent) source).getIncomingFlows();
        }
        return sequenceFlows;
    }
    /**
     * æ ¹æ®èŠ‚ç‚¹ï¼ŒèŽ·å–å‡ºå£è¿žçº¿
     * @param source èŠ‚ç‚¹
     * @return å‡ºå£çš„线
     */
    public static List<SequenceFlow> getElementOutgoingFlows(FlowElement source) {
        List<SequenceFlow> sequenceFlows = null;
        if (source instanceof Task) {
            sequenceFlows = ((Task) source).getOutgoingFlows();
        } else if (source instanceof Gateway) {
            sequenceFlows = ((Gateway) source).getOutgoingFlows();
        } else if (source instanceof SubProcess) {
            sequenceFlows = ((SubProcess) source).getOutgoingFlows();
        } else if (source instanceof StartEvent) {
            sequenceFlows = ((StartEvent) source).getOutgoingFlows();
        } else if (source instanceof EndEvent) {
            sequenceFlows = ((EndEvent) source).getOutgoingFlows();
        }
        return sequenceFlows;
    }
    /**
     * èŽ·å–å…¨éƒ¨èŠ‚ç‚¹åˆ—è¡¨ï¼ŒåŒ…å«å­æµç¨‹èŠ‚ç‚¹
     * @param flowElements èŠ‚ç‚¹
     * @param allElements æ‰€æœ‰çš„节点
     * @return èŠ‚ç‚¹çš„å†…å®¹
     */
    public static Collection<FlowElement> getAllElements(Collection<FlowElement> flowElements, Collection<FlowElement> allElements) {
        allElements = allElements == null ? new ArrayList<>() : allElements;
        for (FlowElement flowElement : flowElements) {
            allElements.add(flowElement);
            if (flowElement instanceof SubProcess) {
                // ç»§ç»­æ·±å…¥å­æµç¨‹ï¼Œè¿›ä¸€æ­¥èŽ·å–å­æµç¨‹
                allElements = FlowableUtils.getAllElements(((SubProcess) flowElement).getFlowElements(), allElements);
            }
        }
        return allElements;
    }
    /**
     * è¿­ä»£èŽ·å–çˆ¶çº§ä»»åŠ¡èŠ‚ç‚¹åˆ—è¡¨ï¼Œå‘å‰æ‰¾
     * @param source èµ·å§‹èŠ‚ç‚¹
     * @param hasSequenceFlow å·²ç»ç»è¿‡çš„连线的 ID,用于判断线路是否重复
     * @param userTaskList å·²æ‰¾åˆ°çš„用户任务节点
     * @return
     */
    public static List<UserTask> iteratorFindParentUserTasks(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
        userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
        hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
        // å¦‚果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
        if (source instanceof StartEvent && source.getSubProcess() != null) {
            userTaskList = iteratorFindParentUserTasks(source.getSubProcess(), hasSequenceFlow, userTaskList);
        }
        // æ ¹æ®ç±»åž‹ï¼ŒèŽ·å–å…¥å£è¿žçº¿
        List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
        if (sequenceFlows != null) {
            // å¾ªçŽ¯æ‰¾åˆ°ç›®æ ‡å…ƒç´ 
            for (SequenceFlow sequenceFlow: sequenceFlows) {
                // å¦‚果发现连线重复,说明循环了,跳过这个循环
                if (hasSequenceFlow.contains(sequenceFlow.getId())) {
                    continue;
                }
                // æ·»åŠ å·²ç»èµ°è¿‡çš„è¿žçº¿
                hasSequenceFlow.add(sequenceFlow.getId());
                // ç±»åž‹ä¸ºç”¨æˆ·èŠ‚ç‚¹ï¼Œåˆ™æ–°å¢žçˆ¶çº§èŠ‚ç‚¹
                if (sequenceFlow.getSourceFlowElement() instanceof UserTask) {
                    userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement());
                    continue;
                }
                // ç±»åž‹ä¸ºå­æµç¨‹ï¼Œåˆ™æ·»åŠ å­æµç¨‹å¼€å§‹èŠ‚ç‚¹å‡ºå£å¤„ç›¸è¿žçš„èŠ‚ç‚¹
                if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) {
                    // èŽ·å–å­æµç¨‹ç”¨æˆ·ä»»åŠ¡èŠ‚ç‚¹
                    List<UserTask> childUserTaskList = findChildProcessUserTasks((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null);
                    // å¦‚果找到节点,则说明该线路找到节点,不继续向下找,反之继续
                    if (childUserTaskList != null && childUserTaskList.size() > 0) {
                        userTaskList.addAll(childUserTaskList);
                        continue;
                    }
                }
                // ç»§ç»­è¿­ä»£
                // æ³¨æ„ï¼šå·²ç»ç»è¿‡çš„节点与连线都应该用浅拷贝出来的对象
                // æ¯”如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据
                userTaskList = iteratorFindParentUserTasks(sequenceFlow.getSourceFlowElement(), new HashSet<>(hasSequenceFlow), userTaskList);
            }
        }
        return userTaskList;
    }
    /**
     * æ ¹æ®æ­£åœ¨è¿è¡Œçš„任务节点,迭代获取子级任务节点列表,向后找
     * @param source èµ·å§‹èŠ‚ç‚¹
     * @param runActiveIdList æ­£åœ¨è¿è¡Œçš„任务 Key,用于校验任务节点是否是正在运行的节点
     * @param hasSequenceFlow å·²ç»ç»è¿‡çš„连线的 ID,用于判断线路是否重复
     * @param flowElementList éœ€è¦æ’¤å›žçš„用户任务列表
     * @return èŠ‚ç‚¹çš„ä¿¡æ¯
     */
    public static List<FlowElement> iteratorFindChildUserTasks(FlowElement source, List<String> runActiveIdList, Set<String> hasSequenceFlow, List<FlowElement> flowElementList) {
        hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
        flowElementList = flowElementList == null ? new ArrayList<>() : flowElementList;
        // å¦‚果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
        if (source instanceof EndEvent && source.getSubProcess() != null) {
            flowElementList = iteratorFindChildUserTasks(source.getSubProcess(), runActiveIdList, hasSequenceFlow, flowElementList);
        }
        // æ ¹æ®ç±»åž‹ï¼ŒèŽ·å–å‡ºå£è¿žçº¿
        List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
        if (sequenceFlows != null) {
            // å¾ªçŽ¯æ‰¾åˆ°ç›®æ ‡å…ƒç´ 
            for (SequenceFlow sequenceFlow: sequenceFlows) {
                // å¦‚果发现连线重复,说明循环了,跳过这个循环
                if (hasSequenceFlow.contains(sequenceFlow.getId())) {
                    continue;
                }
                // æ·»åŠ å·²ç»èµ°è¿‡çš„è¿žçº¿
                hasSequenceFlow.add(sequenceFlow.getId());
                // å¦‚果为用户任务类型,或者为网关
                // æ´»åŠ¨èŠ‚ç‚¹ID åœ¨è¿è¡Œçš„任务中存在,添加
                if ((sequenceFlow.getTargetFlowElement() instanceof UserTask || sequenceFlow.getTargetFlowElement() instanceof Gateway) && runActiveIdList.contains((sequenceFlow.getTargetFlowElement()).getId())) {
                    flowElementList.add(sequenceFlow.getTargetFlowElement());
                    continue;
                }
                // å¦‚果节点为子流程节点情况,则从节点中的第一个节点开始获取
                if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
                    List<FlowElement> childUserTaskList = iteratorFindChildUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), runActiveIdList, hasSequenceFlow, null);
                    // å¦‚果找到节点,则说明该线路找到节点,不继续向下找,反之继续
                    if (childUserTaskList != null && childUserTaskList.size() > 0) {
                        flowElementList.addAll(childUserTaskList);
                        continue;
                    }
                }
                // ç»§ç»­è¿­ä»£
                // æ³¨æ„ï¼šå·²ç»ç»è¿‡çš„节点与连线都应该用浅拷贝出来的对象
                // æ¯”如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据
                flowElementList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runActiveIdList, new HashSet<>(hasSequenceFlow), flowElementList);
            }
        }
        return flowElementList;
    }
    /**
     * è¿­ä»£èŽ·å–å­æµç¨‹ç”¨æˆ·ä»»åŠ¡èŠ‚ç‚¹
     * @param source èµ·å§‹èŠ‚ç‚¹
     * @param hasSequenceFlow å·²ç»ç»è¿‡çš„连线的 ID,用于判断线路是否重复
     * @param userTaskList éœ€è¦æ’¤å›žçš„用户任务列表
     * @return ä»»åŠ¡
     */
    public static List<UserTask> findChildProcessUserTasks(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
        hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
        userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
        // æ ¹æ®ç±»åž‹ï¼ŒèŽ·å–å‡ºå£è¿žçº¿
        List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
        if (sequenceFlows != null) {
            // å¾ªçŽ¯æ‰¾åˆ°ç›®æ ‡å…ƒç´ 
            for (SequenceFlow sequenceFlow: sequenceFlows) {
                // å¦‚果发现连线重复,说明循环了,跳过这个循环
                if (hasSequenceFlow.contains(sequenceFlow.getId())) {
                    continue;
                }
                // æ·»åŠ å·²ç»èµ°è¿‡çš„è¿žçº¿
                hasSequenceFlow.add(sequenceFlow.getId());
                // å¦‚果为用户任务类型,且任务节点的 Key æ­£åœ¨è¿è¡Œçš„任务中存在,添加
                if (sequenceFlow.getTargetFlowElement() instanceof UserTask) {
                    userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement());
                    continue;
                }
                // å¦‚果节点为子流程节点情况,则从节点中的第一个节点开始获取
                if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
                    List<UserTask> childUserTaskList = findChildProcessUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null);
                    // å¦‚果找到节点,则说明该线路找到节点,不继续向下找,反之继续
                    if (childUserTaskList != null && childUserTaskList.size() > 0) {
                        userTaskList.addAll(childUserTaskList);
                        continue;
                    }
                }
                // ç»§ç»­è¿­ä»£
                // æ³¨æ„ï¼šå·²ç»ç»è¿‡çš„节点与连线都应该用浅拷贝出来的对象
                // æ¯”如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据
                userTaskList = findChildProcessUserTasks(sequenceFlow.getTargetFlowElement(), new HashSet<>(hasSequenceFlow), userTaskList);
            }
        }
        return userTaskList;
    }
    /**
     * ä»ŽåŽå‘前寻路,获取所有脏线路上的点
     * @param source èµ·å§‹èŠ‚ç‚¹
     * @param passRoads å·²ç»ç»è¿‡çš„点集合
     * @param hasSequenceFlow å·²ç»ç»è¿‡çš„连线的 ID,用于判断线路是否重复
     * @param targets ç›®æ ‡è„çº¿è·¯ç»ˆç‚¹
     * @param dirtyRoads ç¡®å®šä¸ºè„æ•°æ®çš„点,因为不需要重复,因此使用 set å­˜å‚¨
     * @return è·¯çº¿
     */
    public static Set<String> iteratorFindDirtyRoads(FlowElement source, List<String> passRoads, Set<String> hasSequenceFlow, List<String> targets, Set<String> dirtyRoads) {
        passRoads = passRoads == null ? new ArrayList<>() : passRoads;
        dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads;
        hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
        // å¦‚果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
        if (source instanceof StartEvent && source.getSubProcess() != null) {
            dirtyRoads = iteratorFindDirtyRoads(source.getSubProcess(), passRoads, hasSequenceFlow, targets, dirtyRoads);
        }
        // æ ¹æ®ç±»åž‹ï¼ŒèŽ·å–å…¥å£è¿žçº¿
        List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
        if (sequenceFlows != null) {
            // å¾ªçŽ¯æ‰¾åˆ°ç›®æ ‡å…ƒç´ 
            for (SequenceFlow sequenceFlow: sequenceFlows) {
                // å¦‚果发现连线重复,说明循环了,跳过这个循环
                if (hasSequenceFlow.contains(sequenceFlow.getId())) {
                    continue;
                }
                // æ·»åŠ å·²ç»èµ°è¿‡çš„è¿žçº¿
                hasSequenceFlow.add(sequenceFlow.getId());
                // æ–°å¢žç»è¿‡çš„路线
                passRoads.add(sequenceFlow.getSourceFlowElement().getId());
                // å¦‚果此点为目标点,确定经过的路线为脏线路,添加点到脏线路中,然后找下个连线
                if (targets.contains(sequenceFlow.getSourceFlowElement().getId())) {
                    dirtyRoads.addAll(passRoads);
                    continue;
                }
                // å¦‚果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
                if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) {
                    dirtyRoads = findChildProcessAllDirtyRoad((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, dirtyRoads);
                    // æ˜¯å¦å­˜åœ¨å­æµç¨‹ä¸Šï¼Œtrue æ˜¯ï¼Œfalse å¦
                    Boolean isInChildProcess = dirtyTargetInChildProcess((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, targets, null);
                    if (isInChildProcess) {
                        // å·²åœ¨å­æµç¨‹ä¸Šæ‰¾åˆ°ï¼Œè¯¥è·¯çº¿ç»“束
                        continue;
                    }
                }
                // ç»§ç»­è¿­ä»£
                // æ³¨æ„ï¼šå·²ç»ç»è¿‡çš„节点与连线都应该用浅拷贝出来的对象
                // æ¯”如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据
                dirtyRoads = iteratorFindDirtyRoads(sequenceFlow.getSourceFlowElement(), new ArrayList<>(passRoads), new HashSet<>(hasSequenceFlow), targets, dirtyRoads);
            }
        }
        return dirtyRoads;
    }
    /**
     * è¿­ä»£èŽ·å–å­æµç¨‹è„è·¯çº¿
     * è¯´æ˜Žï¼Œå‡å¦‚回退的点就是子流程,那么也肯定会回退到子流程最初的用户任务节点,因此子流程中的节点全是脏路线
     * @param source èµ·å§‹èŠ‚ç‚¹
     * @param hasSequenceFlow å·²ç»ç»è¿‡çš„连线的 ID,用于判断线路是否重复
     * @param dirtyRoads ç¡®å®šä¸ºè„æ•°æ®çš„点,因为不需要重复,因此使用 set å­˜å‚¨
     * @return è·¯çº¿
     */
    public static Set<String> findChildProcessAllDirtyRoad(FlowElement source, Set<String> hasSequenceFlow, Set<String> dirtyRoads) {
        hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
        dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads;
        // æ ¹æ®ç±»åž‹ï¼ŒèŽ·å–å‡ºå£è¿žçº¿
        List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
        if (sequenceFlows != null) {
            // å¾ªçŽ¯æ‰¾åˆ°ç›®æ ‡å…ƒç´ 
            for (SequenceFlow sequenceFlow: sequenceFlows) {
                // å¦‚果发现连线重复,说明循环了,跳过这个循环
                if (hasSequenceFlow.contains(sequenceFlow.getId())) {
                    continue;
                }
                // æ·»åŠ å·²ç»èµ°è¿‡çš„è¿žçº¿
                hasSequenceFlow.add(sequenceFlow.getId());
                // æ·»åŠ è„è·¯çº¿
                dirtyRoads.add(sequenceFlow.getTargetFlowElement().getId());
                // å¦‚果节点为子流程节点情况,则从节点中的第一个节点开始获取
                if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
                    dirtyRoads = findChildProcessAllDirtyRoad((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, dirtyRoads);
                }
                // ç»§ç»­è¿­ä»£
                // æ³¨æ„ï¼šå·²ç»ç»è¿‡çš„节点与连线都应该用浅拷贝出来的对象
                // æ¯”如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据
                dirtyRoads = findChildProcessAllDirtyRoad(sequenceFlow.getTargetFlowElement(), new HashSet<>(hasSequenceFlow), dirtyRoads);
            }
        }
        return dirtyRoads;
    }
    /**
     * åˆ¤æ–­è„è·¯çº¿ç»“束节点是否在子流程上
     * @param source èµ·å§‹èŠ‚ç‚¹
     * @param hasSequenceFlow å·²ç»ç»è¿‡çš„连线的 ID,用于判断线路是否重复
     * @param targets åˆ¤æ–­è„è·¯çº¿èŠ‚ç‚¹æ˜¯å¦å­˜åœ¨å­æµç¨‹ä¸Šï¼Œåªè¦å­˜åœ¨ä¸€ä¸ªï¼Œè¯´æ˜Žè„è·¯çº¿åªåˆ°å­æµç¨‹ä¸ºæ­¢
     * @param inChildProcess æ˜¯å¦å­˜åœ¨å­æµç¨‹ä¸Šï¼Œtrue æ˜¯ï¼Œfalse å¦
     * @return æ˜¯å¦
     */
    public static Boolean dirtyTargetInChildProcess(FlowElement source, Set<String> hasSequenceFlow, List<String> targets, Boolean inChildProcess) {
        hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
        inChildProcess = inChildProcess == null ? false : inChildProcess;
        // æ ¹æ®ç±»åž‹ï¼ŒèŽ·å–å‡ºå£è¿žçº¿
        List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
        if (sequenceFlows != null && !inChildProcess) {
            // å¾ªçŽ¯æ‰¾åˆ°ç›®æ ‡å…ƒç´ 
            for (SequenceFlow sequenceFlow: sequenceFlows) {
                // å¦‚果发现连线重复,说明循环了,跳过这个循环
                if (hasSequenceFlow.contains(sequenceFlow.getId())) {
                    continue;
                }
                // æ·»åŠ å·²ç»èµ°è¿‡çš„è¿žçº¿
                hasSequenceFlow.add(sequenceFlow.getId());
                // å¦‚果发现目标点在子流程上存在,说明只到子流程为止
                if (targets.contains(sequenceFlow.getTargetFlowElement().getId())) {
                    inChildProcess = true;
                    break;
                }
                // å¦‚果节点为子流程节点情况,则从节点中的第一个节点开始获取
                if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
                    inChildProcess = dirtyTargetInChildProcess((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, targets, inChildProcess);
                }
                // ç»§ç»­è¿­ä»£
                // æ³¨æ„ï¼šå·²ç»ç»è¿‡çš„节点与连线都应该用浅拷贝出来的对象
                // æ¯”如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据
                inChildProcess = dirtyTargetInChildProcess(sequenceFlow.getTargetFlowElement(), new HashSet<>(hasSequenceFlow), targets, inChildProcess);
            }
        }
        return inChildProcess;
    }
    /**
     * è¿­ä»£ä»ŽåŽå‘前扫描,判断目标节点相对于当前节点是否是串行
     * ä¸å­˜åœ¨ç›´æŽ¥å›žé€€åˆ°å­æµç¨‹ä¸­çš„æƒ…况,但存在从子流程出去到父流程情况
     * @param source èµ·å§‹èŠ‚ç‚¹
     * @param isSequential æ˜¯å¦ä¸²è¡Œ
     * @param hasSequenceFlow å·²ç»ç»è¿‡çš„连线的 ID,用于判断线路是否重复
     * @param targetKsy ç›®æ ‡èŠ‚ç‚¹
     * @return æ˜¯å¦
     */
    public static Boolean iteratorCheckSequentialReferTarget(FlowElement source, String targetKsy, Set<String> hasSequenceFlow, Boolean isSequential) {
        isSequential = isSequential == null ? true : isSequential;
        hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
        // å¦‚果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
        if (source instanceof StartEvent && source.getSubProcess() != null) {
            isSequential = iteratorCheckSequentialReferTarget(source.getSubProcess(), targetKsy, hasSequenceFlow, isSequential);
        }
        // æ ¹æ®ç±»åž‹ï¼ŒèŽ·å–å…¥å£è¿žçº¿
        List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
        if (sequenceFlows != null) {
            // å¾ªçŽ¯æ‰¾åˆ°ç›®æ ‡å…ƒç´ 
            for (SequenceFlow sequenceFlow: sequenceFlows) {
                // å¦‚果发现连线重复,说明循环了,跳过这个循环
                if (hasSequenceFlow.contains(sequenceFlow.getId())) {
                    continue;
                }
                // æ·»åŠ å·²ç»èµ°è¿‡çš„è¿žçº¿
                hasSequenceFlow.add(sequenceFlow.getId());
                // å¦‚果目标节点已被判断为并行,后面都不需要执行,直接返回
                if (isSequential == false) {
                    break;
                }
                // è¿™æ¡çº¿è·¯å­˜åœ¨ç›®æ ‡èŠ‚ç‚¹ï¼Œè¿™æ¡çº¿è·¯å®Œæˆï¼Œè¿›å…¥ä¸‹ä¸ªçº¿è·¯
                if (targetKsy.equals(sequenceFlow.getSourceFlowElement().getId())) {
                    continue;
                }
                if (sequenceFlow.getSourceFlowElement() instanceof StartEvent) {
                    isSequential = false;
                    break;
                }
                // å¦åˆ™å°±ç»§ç»­è¿­ä»£
                // æ³¨æ„ï¼šå·²ç»ç»è¿‡çš„节点与连线都应该用浅拷贝出来的对象
                // æ¯”如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据
                isSequential = iteratorCheckSequentialReferTarget(sequenceFlow.getSourceFlowElement(), targetKsy, new HashSet<>(hasSequenceFlow), isSequential);
            }
        }
        return isSequential;
    }
    /**
     * ä»ŽåŽå‘前寻路,获取到达节点的所有路线
     * ä¸å­˜åœ¨ç›´æŽ¥å›žé€€åˆ°å­æµç¨‹ï¼Œä½†æ˜¯å­˜åœ¨å›žé€€åˆ°çˆ¶çº§æµç¨‹çš„æƒ…况
     * @param source èµ·å§‹èŠ‚ç‚¹
     * @param passRoads å·²ç»ç»è¿‡çš„点集合
     * @param roads è·¯çº¿
     * @return ä»»åŠ¡
     */
    public static List<List<UserTask>> findRoad(FlowElement source, List<UserTask> passRoads, Set<String> hasSequenceFlow, List<List<UserTask>> roads) {
        passRoads = passRoads == null ? new ArrayList<>() : passRoads;
        roads = roads == null ? new ArrayList<>() : roads;
        hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
        // å¦‚果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
        if (source instanceof StartEvent && source.getSubProcess() != null) {
            roads = findRoad(source.getSubProcess(), passRoads, hasSequenceFlow, roads);
        }
        // æ ¹æ®ç±»åž‹ï¼ŒèŽ·å–å…¥å£è¿žçº¿
        List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
        if (sequenceFlows != null && sequenceFlows.size() != 0) {
            for (SequenceFlow sequenceFlow: sequenceFlows) {
                // å¦‚果发现连线重复,说明循环了,跳过这个循环
                if (hasSequenceFlow.contains(sequenceFlow.getId())) {
                    continue;
                }
                // æ·»åŠ å·²ç»èµ°è¿‡çš„è¿žçº¿
                hasSequenceFlow.add(sequenceFlow.getId());
                // æ·»åŠ ç»è¿‡è·¯çº¿
                if (sequenceFlow.getSourceFlowElement() instanceof UserTask) {
                    passRoads.add((UserTask) sequenceFlow.getSourceFlowElement());
                }
                // ç»§ç»­è¿­ä»£
                // æ³¨æ„ï¼šå·²ç»ç»è¿‡çš„节点与连线都应该用浅拷贝出来的对象
                // æ¯”如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据
                roads = findRoad(sequenceFlow.getSourceFlowElement(), new ArrayList<>(passRoads), new HashSet<>(hasSequenceFlow), roads);
            }
        } else {
            // æ·»åŠ è·¯çº¿
            roads.add(passRoads);
        }
        return roads;
    }
    /**
     * åŽ†å²èŠ‚ç‚¹æ•°æ®æ¸…æ´—ï¼Œæ¸…æ´—æŽ‰åˆå›žæ»šå¯¼è‡´çš„è„æ•°æ®
     * @param allElements å…¨éƒ¨èŠ‚ç‚¹ä¿¡æ¯
     * @param historicActivityIdList åŽ†å²ä»»åŠ¡å®žä¾‹ä¿¡æ¯ï¼Œæ•°æ®é‡‡ç”¨å¼€å§‹æ—¶é—´å‡åº
     * @return åŽ†å²æ•°æ®
     */
    public static List<String> historicTaskInstanceClean(Collection<FlowElement> allElements, List<HistoricActivityInstance> historicActivityIdList) {
        // ä¼šç­¾èŠ‚ç‚¹æ”¶é›†
        List<String> multiTask = new ArrayList<>();
        allElements.forEach(flowElement -> {
            if (flowElement instanceof UserTask) {
                // å¦‚果该节点的行为为会签行为,说明该节点为会签节点
                if (((UserTask) flowElement).getBehavior() instanceof ParallelMultiInstanceBehavior || ((UserTask) flowElement).getBehavior() instanceof SequentialMultiInstanceBehavior) {
                    multiTask.add(flowElement.getId());
                }
            }
        });
        // å¾ªçŽ¯æ”¾å…¥æ ˆï¼Œæ ˆ LIFO:后进先出
        Stack<HistoricActivityInstance> stack = new Stack<>();
        historicActivityIdList.forEach(item -> stack.push(item));
        // æ¸…洗后的历史任务实例
        List<String> lastHistoricTaskInstanceList = new ArrayList<>();
        // ç½‘关存在可能只走了部分分支情况,且还存在跳转废弃数据以及其他分支数据的干扰,因此需要对历史节点数据进行清洗
        // ä¸´æ—¶ç”¨æˆ·ä»»åŠ¡ key
        StringBuilder userTaskKey = null;
        // ä¸´æ—¶è¢«åˆ æŽ‰çš„任务 key,存在并行情况
        List<String> deleteKeyList = new ArrayList<>();
        // ä¸´æ—¶è„æ•°æ®çº¿è·¯
        List<Set<String>> dirtyDataLineList = new ArrayList<>();
        // ç”±æŸä¸ªç‚¹è·³åˆ°ä¼šç­¾ç‚¹,此时出现多个会签实例对应 1 ä¸ªè·³è½¬æƒ…况,需要把这些连续脏数据都找到
        // ä¼šç­¾ç‰¹æ®Šå¤„理下标
        int multiIndex = -1;
        // ä¼šç­¾ç‰¹æ®Šå¤„理 key
        StringBuilder multiKey = null;
        // ä¼šç­¾ç‰¹æ®Šå¤„理操作标识
        boolean multiOpera = false;
        while (!stack.empty()) {
            // ä»Žè¿™é‡Œå¼€å§‹ userTaskKey éƒ½è¿˜æ˜¯ä¸Šä¸ªæ ˆçš„ key
            // æ˜¯å¦æ˜¯è„æ•°æ®çº¿è·¯ä¸Šçš„点
            final boolean[] isDirtyData = {false};
            for (Set<String> oldDirtyDataLine : dirtyDataLineList) {
                if (oldDirtyDataLine.contains(stack.peek().getActivityId())) {
                    isDirtyData[0] = true;
                }
            }
            // åˆ é™¤åŽŸå› ä¸ä¸ºç©ºï¼Œè¯´æ˜Žä»Žè¿™æ¡æ•°æ®å¼€å§‹å›žè·³æˆ–è€…å›žé€€çš„
            // MI_END:会签完成后,其他未签到节点的删除原因,不在处理范围内
            if (stack.peek().getDeleteReason() != null && !stack.peek().getDeleteReason().equals("MI_END")) {
                // å¯ä»¥ç†è§£ä¸ºè„çº¿è·¯èµ·ç‚¹
                String dirtyPoint = "";
                if (stack.peek().getDeleteReason().indexOf("Change activity to ") >= 0) {
                    dirtyPoint = stack.peek().getDeleteReason().replace("Change activity to ", "");
                }
                // ä¼šç­¾å›žé€€åˆ é™¤åŽŸå› æœ‰ç‚¹ä¸åŒ
                if (stack.peek().getDeleteReason().indexOf("Change parent activity to ") >= 0) {
                    dirtyPoint = stack.peek().getDeleteReason().replace("Change parent activity to ", "");
                }
                FlowElement dirtyTask = null;
                // èŽ·å–å˜æ›´èŠ‚ç‚¹çš„å¯¹åº”çš„å…¥å£å¤„è¿žçº¿
                // å¦‚果是网关并行回退情况,会变成两条脏数据路线,效果一样
                for (FlowElement flowElement : allElements) {
                    if (flowElement.getId().equals(stack.peek().getActivityId())) {
                        dirtyTask = flowElement;
                    }
                }
                // èŽ·å–è„æ•°æ®çº¿è·¯
                Set<String> dirtyDataLine = FlowableUtils.iteratorFindDirtyRoads(dirtyTask, null, null, Arrays.asList(dirtyPoint.split(",")), null);
                // è‡ªå·±æœ¬èº«ä¹Ÿæ˜¯è„çº¿è·¯ä¸Šçš„点,加进去
                dirtyDataLine.add(stack.peek().getActivityId());
                //logger.info(stack.peek().getActivityId() + "点脏路线集合:" + dirtyDataLine);
                // æ˜¯å…¨æ–°çš„需要添加的脏线路
                boolean isNewDirtyData = true;
                for (int i = 0; i < dirtyDataLineList.size(); i++) {
                    // å¦‚果发现他的上个节点在脏线路内,说明这个点可能是并行的节点,或者连续驳回
                    // è¿™æ—¶ï¼Œéƒ½ä»¥ä¹‹å‰çš„脏线路节点为标准,只需合并脏线路即可,也就是路线补全
                    if (dirtyDataLineList.get(i).contains(userTaskKey.toString())) {
                        isNewDirtyData = false;
                        dirtyDataLineList.get(i).addAll(dirtyDataLine);
                    }
                }
                // å·²ç¡®å®šæ—¶å…¨æ–°çš„脏线路
                if (isNewDirtyData) {
                    // deleteKey å•一路线驳回到并行,这种同时生成多个新实例记录情况,这时 deleteKey å…¶å®žæ˜¯ç”±å¤šä¸ªå€¼ç»„成
                    // æŒ‰ç…§é€»è¾‘,回退后立刻生成的实例记录就是回退的记录
                    // è‡³äºŽé©³å›žæ‰€ç”Ÿæˆçš„ Key,直接从删除原因中获取,因为存在驳回到并行的情况
                    deleteKeyList.add(dirtyPoint + ",");
                    dirtyDataLineList.add(dirtyDataLine);
                }
                // æ·»åŠ åŽï¼ŒçŽ°åœ¨è¿™ä¸ªç‚¹å˜æˆè„çº¿è·¯ä¸Šçš„ç‚¹äº†
                isDirtyData[0] = true;
            }
            // å¦‚果不是脏线路上的点,说明是有效数据,添加历史实例 Key
            if (!isDirtyData[0]) {
                lastHistoricTaskInstanceList.add(stack.peek().getActivityId());
            }
            // æ ¡éªŒè„çº¿è·¯æ˜¯å¦ç»“束
            for (int i = 0; i < deleteKeyList.size(); i ++) {
                // å¦‚果发现脏数据属于会签,记录下下标与对应 Key,以备后续比对,会签脏数据范畴开始
                if (multiKey == null && multiTask.contains(stack.peek().getActivityId())
                        && deleteKeyList.get(i).contains(stack.peek().getActivityId())) {
                    multiIndex = i;
                    multiKey = new StringBuilder(stack.peek().getActivityId());
                }
                // ä¼šç­¾è„æ•°æ®å¤„理,节点退回会签清空
                // å¦‚果在会签脏数据范畴中发现 Key改变,说明会签脏数据在上个节点就结束了,可以把会签脏数据删掉
                if (multiKey != null && !multiKey.toString().equals(stack.peek().getActivityId())) {
                    deleteKeyList.set(multiIndex , deleteKeyList.get(multiIndex).replace(stack.peek().getActivityId() + ",", ""));
                    multiKey = null;
                    // ç»“束进行下校验删除
                    multiOpera = true;
                }
                // å…¶ä»–脏数据处理
                // å‘现该路线最后一条脏数据,说明这条脏数据线路处理完了,删除脏数据信息
                // è„æ•°æ®äº§ç”Ÿçš„æ–°å®žä¾‹ä¸­æ˜¯å¦åŒ…含这条数据
                if (multiKey == null && deleteKeyList.get(i).contains(stack.peek().getActivityId())) {
                    // åˆ é™¤åŒ¹é…åˆ°çš„部分
                    deleteKeyList.set(i , deleteKeyList.get(i).replace(stack.peek().getActivityId() + ",", ""));
                }
                // å¦‚果每组中的元素都以匹配过,说明脏数据结束
                if ("".equals(deleteKeyList.get(i))) {
                    // åŒæ—¶åˆ é™¤è„æ•°æ®
                    deleteKeyList.remove(i);
                    dirtyDataLineList.remove(i);
                    break;
                }
            }
            // ä¼šç­¾æ•°æ®å¤„理需要在循环外处理,否则可能导致溢出
            // ä¼šç­¾çš„æ•°æ®è‚¯å®šæ˜¯ä¹‹å‰æ”¾è¿›åŽ»çš„æ‰€ä»¥ç†è®ºä¸Šä¸ä¼šæº¢å‡ºï¼Œä½†è¿˜æ˜¯æ ¡éªŒä¸‹
            if (multiOpera && deleteKeyList.size() > multiIndex && "".equals(deleteKeyList.get(multiIndex))) {
                // åŒæ—¶åˆ é™¤è„æ•°æ®
                deleteKeyList.remove(multiIndex);
                dirtyDataLineList.remove(multiIndex);
                multiIndex = -1;
                multiOpera = false;
            }
            // pop() æ–¹æ³•与 peek() æ–¹æ³•不同,在返回值的同时,会把值从栈中移除
            // ä¿å­˜æ–°çš„ userTaskKey åœ¨ä¸‹ä¸ªå¾ªçŽ¯ä¸­ä½¿ç”¨
            userTaskKey = new StringBuilder(stack.pop().getActivityId());
        }
        //logger.info("清洗后的历史节点数据:" + lastHistoricTaskInstanceList);
        return lastHistoricTaskInstanceList;
    }
    /**
     * èŽ·å–ç›¸å…³çš„åŽ†å²
     * @param processInstanceId æµç¨‹å®žä¾‹çš„主键
     * @param historyService åŽ†å²æœåŠ¡
     * @param taskService ä»»åŠ¡æœåŠ¡
     * @return åŽ†å²çš„ä¿¡æ¯
     */
    public static List<FlowTaskHisVO> listTaskHistory(String processInstanceId, HistoryService historyService, TaskService taskService){
        HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if(processInstance == null || StringUtils.isBlank(processInstance.getId())){
            throw new VciBaseException("流程未找到");
        }
        List<HistoricActivityInstance> hisList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).activityType("userTask").orderByHistoricActivityInstanceEndTime().desc().list();
        Map<String,FlowTaskHisVO> hisVOList = new HashMap<>();
        if(!CollectionUtils.isEmpty(hisList)){
            List<HistoricActivityInstance> allowTaskInstances = new ArrayList<>();
            Map<String,List<Comment>> taskOidCommentsMap = new HashMap<>();
            for(int i = 0 ; i < hisList.size(); i ++){
                HistoricActivityInstance his = hisList.get(i);
                //我们找当时的审批信息,这样可以判断是否为不同意
                List<Comment> comments = taskService.getTaskComments(his.getTaskId());
                if(!CollectionUtils.isEmpty(comments)){
                    taskOidCommentsMap.put(his.getId(),comments);
                    //查询
                }//第一个可能没有
                allowTaskInstances.add(his);
            }
            if(!CollectionUtils.isEmpty(allowTaskInstances)){
                List<HistoricVariableInstance> variableInstances = historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId).list();
                Map<String,Object> variableMap = switchVariable(variableInstances);
                Set<Long> userIds = new HashSet<>();
                Set<String> processInstanceIds = new HashSet<>();
                for(int i = 0 ; i < allowTaskInstances.size() ; i ++){
                    HistoricActivityInstance activityInstance = allowTaskInstances.get(i);
                    if(!hisVOList.containsKey(activityInstance.getId())) {
                        FlowTaskHisVO hisVO = new FlowTaskHisVO();
                        hisVO.setOid(activityInstance.getId());
                        hisVO.setActivityId(activityInstance.getActivityId());
                        hisVO.setActivityName(activityInstance.getActivityName());
                        hisVO.setActivityType(activityInstance.getActivityType());
                        hisVO.setProcessDefinitionId(activityInstance.getProcessDefinitionId());
                        hisVO.setProcessInstanceId(activityInstance.getProcessInstanceId());
                        hisVO.setExecutionId(activityInstance.getExecutionId());
                        hisVO.setTaskId(activityInstance.getTaskId());
                        hisVO.setCalledProcessInstanceId(activityInstance.getCalledProcessInstanceId());
                        hisVO.setAssignee(activityInstance.getAssignee());
                        if (StringUtils.isNotBlank(hisVO.getAssignee())) {
                            userIds.add(TaskUtil.getUserId(hisVO.getAssignee()));
                        }
                        hisVO.setStartTime(activityInstance.getStartTime());
                        hisVO.setEndTime(activityInstance.getEndTime());
                        hisVO.setDurationInMillis(activityInstance.getDurationInMillis());
                        hisVO.setDeleteReason(activityInstance.getDeleteReason());
                        hisVO.setTenantId(activityInstance.getTenantId());
                        hisVO.setVariablesMap(variableMap);
                        hisVO.setBtmType(variableMap.getOrDefault(FlowEngineConstant.BTMTYPE,"").toString());
                        hisVO.setLinkBusinessOids(VciBaseUtil.str2List(variableMap.getOrDefault(FlowEngineConstant.OIDS,"").toString()));
                        List<Comment> comments = taskOidCommentsMap.getOrDefault(activityInstance.getId(),new ArrayList<>());
                        hisVO.setDescription(comments.stream().map(s->{
                            String fullMsg = s.getFullMessage();
                            if(StringUtils.isNotBlank(fullMsg) && fullMsg.contains(":")){
                                fullMsg = fullMsg.substring(fullMsg.indexOf(":")+1);
                            }
                            return fullMsg;
                        }).collect(Collectors.joining(";")));
                        processInstanceIds.add(activityInstance.getProcessInstanceId());
                        hisVO.setProcessName(processInstance.getName());
                        hisVO.setProcessDefinitionName(processInstance.getProcessDefinitionName());
                        hisVOList.put(hisVO.getActivityId(), hisVO);
                    }
                }
                if(!CollectionUtils.isEmpty(userIds) && !CollectionUtils.isEmpty(hisVOList)){
                    List<User> userList = new ArrayList<>();
                    for(Long userId : userIds){
                        userList.add(UserCache.getUser(userId));
                    }
                    if(!CollectionUtils.isEmpty(userList)){
                        Map<Long, User> userVOMap = userList.stream().collect(Collectors.toMap(s -> s.getId(), t -> t, (o1, o2) -> o1));
                        hisVOList.values().forEach(history->{
                            List<String> thisUserIds = VciBaseUtil.str2List(TaskUtil.getTaskUser(history.getAssignee()));
                            List<String> thisUserNames = new ArrayList<>();
                            if(!CollectionUtils.isEmpty(thisUserIds)) {
                                thisUserIds.forEach(userId -> {
                                    thisUserNames.add(userVOMap.getOrDefault(userId, new User()).getName());
                                });
                            }
                            history.setAssigneeName(thisUserNames.stream().collect(Collectors.joining(",")));
                        });
                    }
                }
            }
        }
        return hisVOList.values().stream().collect(Collectors.toList());
    }
    /**
     * è½¬æ¢å˜é‡
     * @param variableInstances å˜é‡çš„实例
     * @return å˜é‡
     */
    public static Map<String,Object> switchVariable(List<HistoricVariableInstance> variableInstances){
        Map<String,Object> variableMap = new HashMap<>();
        if(!CollectionUtils.isEmpty(variableInstances)){
            variableMap = variableInstances.stream().collect(Collectors.toMap(s->s.getVariableName(),t->t.getValue() == null?"":t.getValue()));
        }
        return variableMap;
    }
}
Source/UBCS/ubcs-service/ubcs-code/src/main/java/com/vci/ubcs/code/controller/CodeClassifyTemplateAttrController.java
@@ -145,9 +145,9 @@
        }
        String tenantId = AuthUtil.getTenantId().toString();
        Map<String, String> conditionMap = baseQueryObject.getConditionMap();
        if(Func.isNotEmpty(tenantId)){
        /*if(Func.isNotEmpty(tenantId)){
            conditionMap.put("TENANT_ID",tenantId);
        }
        }*/
        return CodeClstempattrService.gridCodeClassifyTemplateAttr(conditionMap,baseQueryObject.getPageHelper());
    }
Source/UBCS/ubcs-service/ubcs-code/src/main/java/com/vci/ubcs/code/mapper/CodeClassifyMapper.java
@@ -148,4 +148,17 @@
     * @return
     */
    List<CodeClassify>  selectAllParenClassifytByOid(@Param("oid")String oid,@Param("tenantId") String tenantId);
    /**
     * ä¸»é”®æŸ¥è¯¢å½“前节点及其子节点
     * @param oid å½“前节点主键
     * @return æŸ¥è¯¢ç»“æžœ
     */
    List<CodeClassify> selectStartWithCurrentOid(String oid);
    /**
     * æ‰¹é‡æ›´æ–°åˆ†ç±»çŠ¶æ€
     * @param classifyList
     */
    void batchUpdateLcStatus(@Param("records") List<CodeClassify> classifyList);
}
Source/UBCS/ubcs-service/ubcs-code/src/main/java/com/vci/ubcs/code/service/impl/CodeClassifyServiceImpl.java
@@ -359,20 +359,32 @@
    @Override
    public R updateLcStatus(String oid, String lcStatus){
        //查询修改前ts
        CodeClassify codeClassify = codeClassifyMapper.selectById(oid);//主要是为了查询ts
        codeClassify.setLcStatus(lcStatus);
        codeClassify.setTs(new Date());
        codeClassify.setLastModifyTime(new Date());
        codeClassify.setLastModifier(String.valueOf(AuthUtil.getUser().getUserId()));
        List<CodeClassify> classifyList = codeClassifyMapper.selectStartWithCurrentOid(oid);
        Date now = new Date();
        String userId = String.valueOf(AuthUtil.getUserId());
        classifyList = classifyList.stream().map(s -> {
            s.setLcStatus(lcStatus);
            s.setTs(now);
            s.setLastModifier(userId);
            s.setLastModifyTime(now);
            return s;
        }).collect(Collectors.toList());
//        //查询修改前ts
//        CodeClassify codeClassify = codeClassifyMapper.selectById(oid);//主要是为了查询ts
//        codeClassify.setLcStatus(lcStatus);
//        codeClassify.setTs(new Date());
//        codeClassify.setLastModifyTime(new Date());
//        codeClassify.setLastModifier(String.valueOf(AuthUtil.getUser().getUserId()));
        //启用、停用
//        int u = codeClassifyMapper.updateLcStatus(oid,lcStatus);
        int count = codeClassifyMapper.updateById(codeClassify);
//        int count = codeClassifyMapper.updateById(codeClassify);
        codeClassifyMapper.batchUpdateLcStatus(classifyList);
//        //处理数据集成逻辑,成功后执行集成第一步,分类数据特殊处理。
//        if(u!=0) {
//            codeDuckingServiceI.insertCache1(lcStatus,lcStatus,DOCKING_DEFAULT_CLASSIFY, DOCKING_DEFAULT_CLASSIFYOID, oid, codeClassifyDO_old.getTs());
//        }
        return R.data(SqlHelper.retBool(count));
//        return R.data(SqlHelper.retBool(count));
        return R.success("");
    }
    /**
Source/UBCS/ubcs-service/ubcs-code/src/main/java/com/vci/ubcs/code/service/impl/CodeClassifyTemplateAttrServiceImpl.java
@@ -456,10 +456,6 @@
        if(!CollectionUtils.isEmpty(oids)){
            this.removeBatchByIds(oids);
        }
        String tenantId = AuthUtil.getTenantId().toString();
        codeClassifyTemplateAttrDOInsert.parallelStream().forEach(item->{
            item.setTenantId(tenantId);
        });
        //再新增
        if(!CollectionUtils.isEmpty(codeClassifyTemplateAttrDOInsert)){
            this.saveBatch(codeClassifyTemplateAttrDOInsert);
Source/UBCS/ubcs-service/ubcs-code/src/main/java/com/vci/ubcs/code/service/impl/CodeClstemplateServiceImpl.java
@@ -31,6 +31,8 @@
import com.vci.ubcs.code.service.ICodeClstemplateService;
import com.vci.ubcs.code.vo.pagemodel.CodeClassifyTemplateAttrVO;
import com.vci.ubcs.code.vo.pagemodel.CodeClassifyTemplateVO;
import com.vci.ubcs.flow.core.entity.ProcessTemplate;
import com.vci.ubcs.flow.core.feign.IFlowClient;
import com.vci.ubcs.starter.exception.VciBaseException;
import com.vci.ubcs.starter.revision.model.TreeQueryObject;
import com.vci.ubcs.starter.revision.model.TreeWrapperOptions;
@@ -106,6 +108,8 @@
    private CodeClassifyTemplateMapper codeClassifyTemplateMapper;
    @Autowired(required = false)
    private CodePhaseAttrServiceImpl codePhaseattrServiceImpl;
    @Autowired(required = false)
    private IFlowClient iFlowClient;
    @Override
    public IPage<CodeClassifyTemplateVO> selectPlCodeClstemplatePage(IPage<CodeClassifyTemplateVO> page, CodeClassifyTemplateVO plCodeClstemplate) {
@@ -545,7 +549,7 @@
        }
        //复制模板流程
        List<CodeClassifyProcessTemp>  codeClassifyProcessTempDOList = copyTemplateProcess(templateOldOid,templateNewOid);
        List<ProcessTemplate>  codeClassifyProcessTempDOList = copyTemplateProcess(templateOldOid,templateNewOid);
        //复制模板阶段,阶段属性
        Map phase_attrMap = copyTemplatePhase_attr(templateOldOid,templateNewOid);
@@ -557,7 +561,7 @@
        //保存模板流程
        if(!CollectionUtils.isEmpty(codeClassifyProcessTempDOList)) {
            codeClsflowtempServiceImpl.saveBatch(codeClassifyProcessTempDOList);
            iFlowClient.processBatchSave(codeClassifyProcessTempDOList);
        }
        //模板阶段
        if(!CollectionUtils.isEmpty(codeClassifyPhaseDOList)) {
@@ -619,7 +623,7 @@
        List<CodeClassifyTemplateAttr> codeClassifyTemplateAttrDOList = copyTemplateAttr(oldOid,newOid);
        //复制模板流程
        List<CodeClassifyProcessTemp> codeClassifyProcessTempDOList = copyTemplateProcess(oldOid,newOid);
        List<ProcessTemplate> codeClassifyProcessTempDOList = copyTemplateProcess(oldOid,newOid);
        //复制模板阶段,阶段属性
        Map  phase_attrMap = copyTemplatePhase_attr(oldOid,newOid);
@@ -633,7 +637,7 @@
//        baseMapper.insert(codeClassifyTemplateDOList);
        this.saveBatch(codeClassifyTemplateDOList);
        codeTempbuttonServiceImpl.saveBatch(codeClassifyTemplateButtonDOList);
        codeClsflowtempServiceImpl.saveBatch(codeClassifyProcessTempDOList);
        iFlowClient.processBatchSave(codeClassifyProcessTempDOList);
        codeClstempattrService.saveBatch(codeClassifyTemplateAttrDOList);
        codeTempphaseServiceImpl.saveBatch(codeClassifyPhaseDOList);
        codePhaseattrServiceImpl.saveBatch(codePhaseAttrDOList);
@@ -696,22 +700,17 @@
    /**
     * å¤åˆ¶æ¨¡æ¿æµç¨‹
     */
    public List<CodeClassifyProcessTemp> copyTemplateProcess(String templateOldOid,String templateNewOid){
//        VciQueryWrapperForDO processWrapper = new VciQueryWrapperForDO(CodeClassifyProcessTempDO.class);
//        processWrapper.addQueryMap("classifyTemplateOid",templateOldOid);
//        Map<String,Object> condition = new HashMap<>(1);
//        condition.put("classifyTemplateOid",templateOldOid);
        QueryWrapper<CodeClassifyProcessTemp> wrapper = new QueryWrapper<>();
        wrapper.eq("classifyTemplateOid",templateOldOid);
        List<CodeClassifyProcessTemp> codeClsflowtempEntities = codeClsflowtempServiceImpl.list(wrapper);
//        List<CodeClassifyProcessTempDO>  codeClassifyProcessTempDOList = codeClassifyProcessTempDaoI.selectByWrapper(processWrapper);//要保存的新的模板流程
        for (CodeClassifyProcessTemp codeClassifyProcessTempDO:codeClsflowtempEntities){
//            String newOid = VciBaseUtil.getPk();
            codeClassifyProcessTempDO.setOid(null);
            //codeClassifyProcessTempDO.setCodeClassifyOid(templateNewOid);
            codeClassifyProcessTempDO.setClassifyTemplateOid(templateNewOid);
//            codeClsflowtempMapper.insert(codeClassifyProcessTempDO);
    public List<ProcessTemplate> copyTemplateProcess(String templateOldOid,String templateNewOid){
        Map<String,Object> wrapperMap = new HashMap();
        wrapperMap.put("template_id",templateOldOid);
        R<List<ProcessTemplate>> listR = iFlowClient.selectByWrapper(wrapperMap);
        if(!listR.isSuccess()){
            throw new VciBaseException("查询模板流程时出错,请重试!");
        }
        List<ProcessTemplate> codeClsflowtempEntities = listR.getData();
        for (ProcessTemplate codeClassifyProcessTempDO:codeClsflowtempEntities){
            codeClassifyProcessTempDO.setId(null);
            codeClassifyProcessTempDO.setTemplateId(templateNewOid);
            //modify by lihang - @20220406 è®¾ç½®æ¨¡æ¿ä¸»é”®ä½ç½®å‡ºé”™ï¼Œå¯¼è‡´å‡ç‰ˆçš„æ¨¡æ¿ä¸­çš„æµç¨‹æ˜¯ç©ºçš„。
        }
        return codeClsflowtempEntities;
Source/UBCS/ubcs-service/ubcs-code/src/main/java/com/vci/ubcs/code/service/impl/MdmEngineServiceImpl.java
@@ -360,15 +360,10 @@
            throw new VciBaseException("未查询到相关数据。");
        }
        //还需要修改allCode的生命周期
//        Map<String, String> conditionMap = new HashMap<>();
        QueryWrapper<CodeAllCode> allCodeWrapper = new QueryWrapper<>();
        allCodeWrapper.eq("createcodebtm", baseModelDTO.getBtmname());
        allCodeWrapper.in("createcodeoid", oids);
//        conditionMap.put("createcodeoid", QueryOptionConstant.IN + "(" + VciBaseUtil.toInSql(oids.toArray(new String[0])) + ")");
//        conditionMap.put("createcodebtm", baseModelDTO.getBtmname());
        List<CodeAllCode> codeCbos = codeAllCodeService.selectByWrapper(allCodeWrapper);
//        List<ClientBusinessObject> codeCbos = boService.queryCBO(MdmBtmTypeConstant.CODE_ALL_CODE, conditionMap);
        // å›žæ”¶éœ€è¦ä¸šåŠ¡æ•°æ®åˆ é™¤
        List<CodeAllCode> codeCbos = codeAllCodeService.selectByWrapper(allCodeWrapper);// å›žæ”¶éœ€è¦ä¸šåŠ¡æ•°æ®åˆ é™¤
        if (CodeDefaultLC.TASK_BACK.getValue().equals(baseModelDTO.getLcStatus())) {
            R<List<BtmTypeVO>> listR = btmTypeClient.selectByIdCollection(Collections.singletonList(baseModelDTO.getBtmname()));
            if (!listR.isSuccess() || listR.getData().size() == 0) {
@@ -381,8 +376,11 @@
            for (BaseModel baseModel : baseModels) {
                baseModel.setLcStatus(baseModelDTO.getLcStatus());
            }
            updateBatchByBaseModel(baseModelDTO.getBtmname(), baseModels);
        }
            R r = updateBatchByBaseModel(baseModelDTO.getBtmname(), baseModels);
            if(!r.isSuccess()){
                throw new VciBaseException("更新数据出错,重试!"+r.getMsg());
            }
        }
        for (CodeAllCode codeCbo : codeCbos) {
            codeCbo.setLcStatus(baseModelDTO.getLcStatus());
        }
@@ -2359,7 +2357,7 @@
//            conditionMap.put(CODE_CLASSIFY_OID_FIELD, QueryOptionConstant.IN + "(select oid from " + VciBaseUtil.getTableName(MdmBtmTypeConstant.CODE_CLASSIFY)
            conditionMap.put("t." + CODE_CLASSIFY_OID_FIELD, QueryOptionConstant.IN + "(select oid from " +
                    btmTypeClient.selectByIdCollection(Collections.singletonList(MdmBtmTypeConstant.CODE_CLASSIFY))
                    btmTypeClient.selectByIdCollection(Collections.singletonList("classify"))
                            .getData().get(0).getTableName() + " where lcstatus='" + FrameWorkDefaultValueConstant
                    .FRAMEWORK_DATA_ENABLED + "' start with parentCodeClassifyOid = '" + codeClassifyOid +
                    "' CONNECT BY PRIOR OID = parentCodeClassifyOid )");
@@ -3354,9 +3352,9 @@
                                && ("Integer").equals(setter.getParameterTypes()[0].getSimpleName())) {
                            setter.invoke(obj, ((BigDecimal) map.get(property.getName().toUpperCase())).intValue());
                            map.remove(property.getName().toUpperCase());
                        } else if (map.get(property.getName().toUpperCase()) != null) {
                        } else if (map.containsKey(property.getName().toUpperCase())) {
                            if(setter.getParameterTypes()[0].getSimpleName().equals("String")){
                                setter.invoke(obj, String.valueOf(map.get(property.getName().toUpperCase())));
                                setter.invoke(obj, map.get(property.getName().toUpperCase()) == null ? null:String.valueOf(map.get(property.getName().toUpperCase())));
                            }else{
                                setter.invoke(obj, map.get(property.getName().toUpperCase()));
                            }
Source/UBCS/ubcs-service/ubcs-code/src/main/resources/mapper/CodeCLassifyMapper.xml
@@ -39,7 +39,9 @@
        <result column="CODERESEMBLERULEOID" property="codeResembleRuleOid"/>
        <result column="TENANT_ID" property="tenantId"/>
    </resultMap>
    <sql id="tableName">
        pl_code_classify
    </sql>
    <select id="selectPlCodeClassifyPage" resultMap="plCodeClassifyResultMap">
        select * from PL_CODE_CLASSIFY where is_deleted = 0
    </select>
@@ -487,4 +489,23 @@
          </if>
            connect by prior PARENTCODECLASSIFYOID = codeclassify0.oid
    </select>
    <select id="selectStartWithCurrentOid" resultMap="plCodeClassifyResultMap">
        select * from <include refid="tableName"/>
                          start with oid = #{oid}
        connect by prior oid = parentcodeclassifyoid;
    </select>
    <update id="batchUpdateLcStatus" parameterType="java.util.List">
        BEGIN
        <foreach collection="records" item="item" index="index" separator=";">
            update <include refid="tableName"/> set
            lcstatus = #{item.lcStatus},
            ts = #{item.ts},
            lastModifyTime = #{item.lastModifyTime},
            lastModifier = #{item.lastModifier}
            where oid = #{item.oid,jdbcType=VARCHAR}
        </foreach>
        ;END;
    </update>
</mapper>
Source/UBCS/ubcs-service/ubcs-omd/src/main/java/com/vci/ubcs/omd/service/impl/BtmAttributeServiceImpl.java
@@ -215,7 +215,7 @@
                // ç‰ˆæœ¬è§„则字段不能为空
//                vo.setNullableFlag(false);
                vo.setAttributeLength(150);
                if (Arrays.asList("lastr","firstr","lastv","firstv").contains(id)){
                if (Arrays.asList("lastr","firstr","lastv","firstv","revisionseq","versionseq").contains(id)){
                    vo.setAttributeLength(5);
                    vo.setAttrDataType(VciFieldTypeEnum.VTInteger.name());
                }