package com.artfess.bpm.persistence.util;

import java.io.IOException;
import java.lang.reflect.Method;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.xml.namespace.QName;

import com.artfess.base.feign.SystemConfigFeignService;
import org.springframework.security.core.GrantedAuthority;
import org.w3c.dom.Element;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.artfess.base.exception.WorkFlowException;
import com.artfess.base.util.AppUtil;
import com.artfess.base.util.BeanUtils;
import com.artfess.base.util.JsonUtil;
import com.artfess.base.util.StringUtil;
import com.artfess.base.util.UniqueIdUtil;
import com.artfess.base.util.time.DateFormatUtil;
import com.artfess.base.util.time.TimeUtil;
import com.artfess.bpm.api.cmd.ActionCmd;
import com.artfess.bpm.api.cmd.ProcessInstCmd;
import com.artfess.bpm.api.cmd.TaskFinishCmd;
import com.artfess.bpm.api.constant.ActionType;
import com.artfess.bpm.api.constant.BPMN20ExtConst;
import com.artfess.bpm.api.constant.BpmConstants;
import com.artfess.bpm.api.constant.EventType;
import com.artfess.bpm.api.constant.NodeType;
import com.artfess.bpm.api.constant.OpinionStatus;
import com.artfess.bpm.api.constant.PrivilegeMode;
import com.artfess.bpm.api.constant.TaskActionType;
import com.artfess.bpm.api.constant.TaskType;
import com.artfess.bpm.api.constant.TemplateConstants;
import com.artfess.bpm.api.context.BpmContextUtil;
import com.artfess.bpm.api.context.ContextThreadUtil;
import com.artfess.bpm.api.event.NoExecutorEvent;
import com.artfess.bpm.api.event.NoExecutorModel;
import com.artfess.bpm.api.helper.identity.BpmIdentityExtractService;
import com.artfess.bpm.api.inst.ISkipCondition;
import com.artfess.bpm.api.model.delegate.BpmDelegateTask;
import com.artfess.bpm.api.model.form.FormType;
import com.artfess.bpm.api.model.identity.BpmIdentity;
import com.artfess.bpm.api.model.process.def.BpmDefExtProperties;
import com.artfess.bpm.api.model.process.def.BpmDefinition;
import com.artfess.bpm.api.model.process.def.BpmProcessDef;
import com.artfess.bpm.api.model.process.def.BpmProcessDefExt;
import com.artfess.bpm.api.model.process.def.IGlobalRestfulPluginDef;
import com.artfess.bpm.api.model.process.def.NodeProperties;
import com.artfess.bpm.api.model.process.def.Restful;
import com.artfess.bpm.api.model.process.inst.BpmProcessInstance;
import com.artfess.bpm.api.model.process.nodedef.BpmNodeDef;
import com.artfess.bpm.api.model.process.nodedef.ext.SignNodeDef;
import com.artfess.bpm.api.model.process.nodedef.ext.UserTaskNodeDef;
import com.artfess.bpm.api.model.process.nodedef.ext.extmodel.Button;
import com.artfess.bpm.api.model.process.task.BpmTask;
import com.artfess.bpm.api.model.process.task.SkipResult;
import com.artfess.bpm.api.plugin.core.context.BpmPluginContext;
import com.artfess.bpm.api.plugin.core.def.BpmExecutionPluginDef;
import com.artfess.bpm.api.plugin.core.def.BpmPluginDef;
import com.artfess.bpm.api.plugin.core.def.BpmTaskPluginDef;
import com.artfess.bpm.api.plugin.core.def.TaskActionHandlerDef;
import com.artfess.bpm.api.plugin.core.factory.BpmPluginFactory;
import com.artfess.bpm.api.plugin.core.factory.BpmPluginSessionFactory;
import com.artfess.bpm.api.plugin.core.runtime.BpmExecutionPlugin;
import com.artfess.bpm.api.plugin.core.runtime.BpmTaskPlugin;
import com.artfess.bpm.api.plugin.core.task.TaskActionHandlerConfig;
import com.artfess.bpm.api.service.BpmDefinitionAccessor;
import com.artfess.bpm.api.service.BpmDefinitionService;
import com.artfess.bpm.api.service.BpmFormService;
import com.artfess.bpm.api.service.BpmTaskActionService;
import com.artfess.bpm.api.service.RestfulService;
import com.artfess.bpm.api.service.SignService;
import com.artfess.bpm.defxml.entity.ExtensionElements;
import com.artfess.bpm.defxml.entity.FlowElement;
import com.artfess.bpm.engine.form.BpmFormFactory;
import com.artfess.bpm.engine.task.cmd.DefaultTaskFinishCmd;
import com.artfess.bpm.engine.task.skip.SkipConditionUtil;
import com.artfess.bpm.listener.BusDataUtil;
import com.artfess.bpm.model.form.FormModel;
import com.artfess.bpm.natapi.task.NatTaskService;
import com.artfess.bpm.persistence.dao.BpmExeStackRelationDao;
import com.artfess.bpm.persistence.manager.BpmCheckOpinionManager;
import com.artfess.bpm.persistence.manager.BpmDefinitionManager;
import com.artfess.bpm.persistence.manager.BpmProcessInstanceManager;
import com.artfess.bpm.persistence.manager.BpmTaskDueTimeManager;
import com.artfess.bpm.persistence.manager.BpmTaskManager;
import com.artfess.bpm.persistence.manager.BpmTaskNoticeManager;
import com.artfess.bpm.persistence.model.BpmBusLink;
import com.artfess.bpm.persistence.model.BpmExeStackRelation;
import com.artfess.bpm.persistence.model.BpmTaskDueTime;
import com.artfess.bpm.persistence.model.BpmTaskNotice;
import com.artfess.bpm.persistence.model.DefaultBpmCheckOpinion;
import com.artfess.bpm.persistence.model.DefaultBpmDefinition;
import com.artfess.bpm.persistence.model.DefaultBpmProcessInstance;
import com.artfess.bpm.persistence.model.DefaultBpmTask;
import com.artfess.bpm.plugin.task.tasknotify.helper.NotifyHelper;
import com.artfess.bpm.plugin.task.userassign.def.UserAssignPluginDef;
import com.artfess.bpm.plugin.task.userassign.plugin.UserQueryPlugin;
import com.artfess.bpm.util.PortalDataUtil;
import com.artfess.uc.api.impl.util.ContextUtil;
import com.artfess.uc.api.model.IUser;
import com.artfess.uc.api.service.IUserService;


/**
 * 
 * <pre>
 * 描述：BPM常用解析工具类
 * 构建组：x5-bpmx-core
 * 作者：csx
 * 邮箱:chensx@jee-soft.cn
 * 日期:2014-2-10-下午2:52:16
 * 版权：广州宏天软件有限公司版权所有
 * </pre>
 */
public class BpmUtil {
	
	/**
	 * 将activi的任务转换成BpmTask对象实例。
	 * 
	 * @param delegateTask
	 * @return BpmTask
	 * @throws Exception 
	 */
	public static BpmTask convertTask(BpmDelegateTask delegateTask) throws Exception {
		String taskId = delegateTask.getId();
		String subject = (String) delegateTask.getVariable(BpmConstants.SUBJECT);

		String instId = (String) delegateTask.getVariable(BpmConstants.PROCESS_INST_ID);
		String bpmnDefId = delegateTask.getBpmnDefId();

		BpmDefinitionService bpmDefinitionService = AppUtil.getBean(BpmDefinitionService.class);

		DefaultBpmDefinition def = (DefaultBpmDefinition) bpmDefinitionService.getByBpmnDefId(bpmnDefId);


		ActionCmd cmd=ContextThreadUtil.getActionCmd(); 

		BpmProcessInstance bpmProcessInstance= (BpmProcessInstance) cmd.getTransitVars(BpmConstants.PROCESS_INST); 


		BpmFormService  bpmFormService =BpmFormFactory.getFormService(FormType.MOBILE);
		FormModel formModel = bpmFormService.getByDefId(def.getId(), delegateTask.getTaskDefinitionKey(), bpmProcessInstance,true);

		int supportMobile=0;

		if(formModel!=null && !formModel.isFormEmpty()){
			supportMobile=1;
		}



		DefaultBpmTask task = new DefaultBpmTask();
		task.setId(taskId);
		task.setSubject(subject);

		task.setTaskId(taskId);
		task.setDueTime(delegateTask.getDueDate());
		task.setSuspendState((short) delegateTask.getSuspensionState());
		task.setExecId(delegateTask.getExecutionId());

		task.setName(delegateTask.getName());
		task.setNodeId(delegateTask.getTaskDefinitionKey());
		task.setProcInstId(instId);
		task.setBpmnInstId(delegateTask.getProcessInstanceId());
		task.setOwnerId(BpmConstants.EmptyUser);
		task.setAssigneeId(BpmConstants.EmptyUser);
		task.setPriority((long) delegateTask.getPriority());
		task.setProcDefId(def.getDefId());
		task.setProcDefKey(def.getDefKey());
		task.setProcDefName(def.getName());
		task.setStatus(TaskType.NORMAL.name());
		task.setBpmnDefId(bpmnDefId);
		task.setTypeId(def.getTypeId());
		task.setSupportMobile(supportMobile);
		task.setCreateTime(delegateTask.getCreateTime());

		if(ActionType.BACK.getKey().equals(cmd.getActionName())||ActionType.BACK_TO_START.getKey().equals(cmd.getActionName())){
			task.setStatus(TaskType.BACK.name());
		}

		return task;

	}

	/**
	 * 根据流程任务对象复制新的任务。
	 * 
	 * <pre>
	 * 1.新的任务的ExecId为空，表示派生的任务。
	 * 2.设定父任务。
	 * 3.设定任务类型。
	 * 4.设定执行人。
	 * </pre>
	 * 
	 * @param task
	 * @param parentTaskId
	 * @param taskType
	 * @param user
	 * @return DefaultBpmTask
	 */
	public static DefaultBpmTask convertTask(DefaultBpmTask task, String parentTaskId, TaskType taskType, IUser user) {
		DefaultBpmTask cloneTask = (DefaultBpmTask) task.clone();
		cloneTask.setId(UniqueIdUtil.getSuid());
		cloneTask.setParentId(parentTaskId);
		cloneTask.setTaskId("");
		cloneTask.setStatus(taskType.name());
		cloneTask.setAssigneeId(user.getUserId());
		cloneTask.setAssigneeName(user.getFullname());;
		cloneTask.setOwnerId(user.getUserId());
		cloneTask.setOwnerName(user.getFullname());
		cloneTask.setCreateTime(LocalDateTime.now());
		
		return cloneTask;

	}

	/**
	 * 根据流程定义ID获取流程的扩展属性。
	 * 
	 * @param bpmnDefId
	 * @return BpmDefExtProperties
	 * @throws Exception 
	 */
	public static BpmDefExtProperties getExtProperties(String bpmnDefId) throws Exception {
		BpmDefinitionService bpmDefinitionService = AppUtil.getBean(BpmDefinitionService.class);
		BpmProcessDef<BpmProcessDefExt> procDef = bpmDefinitionService.getBpmProcessDef(bpmnDefId);
		BpmProcessDefExt procExt = procDef.getProcessDefExt();
		BpmDefExtProperties extProperties = procExt.getExtProperties();
		return extProperties;
	}

	@SuppressWarnings("unchecked")
	private static Object[] getDefProperties(BpmProcessInstance instance, String nodeId) throws Exception {
		Object[] aryObj = new Object[3];
		String defId = instance.getProcDefId();
		BpmDefinitionAccessor bpmDefinitionAccessor = (BpmDefinitionAccessor) AppUtil.getBean("bpmDefinitionAccessor");
		BpmDefinitionManager bpmDefinitionManager = AppUtil.getBean(BpmDefinitionManager.class);
		BpmDefinition bpmDefinition = bpmDefinitionManager.getById(defId);

		BpmNodeDef nodeDef = null;
		BpmProcessDef<BpmProcessDefExt> processDef = null;

		UserTaskNodeDef taskNodeDef = null;

		NodeProperties properties = null;

		String parentId = instance.getParentInstId();
		if (StringUtil.isNotZeroEmpty(parentId)) {
			BpmProcessInstanceManager instanceManager = AppUtil.getBean(BpmProcessInstanceManager.class);
			BpmProcessInstance parentInstance = instanceManager.get(parentId);
			nodeDef = bpmDefinitionAccessor.getBpmNodeDef(parentInstance.getProcDefId(), nodeId);
			if(BeanUtils.isNotEmpty(nodeDef)){
				taskNodeDef = (UserTaskNodeDef) nodeDef;
				properties = taskNodeDef.getLocalProperties();
				processDef = nodeDef.getRootProcessDef();
			}
		}
		if(BeanUtils.isEmpty(nodeDef)){
			nodeDef = bpmDefinitionAccessor.getBpmNodeDef(defId, nodeId);
			taskNodeDef = (UserTaskNodeDef) nodeDef;
			properties = taskNodeDef.getLocalProperties();
			processDef = nodeDef.getRootProcessDef();
		}

		aryObj[0] = properties;
		aryObj[1] = processDef;
		aryObj[2] = bpmDefinition;

		return aryObj;
	}



	/**
	 * 是否允许节点执行人为空。
	 * 
	 * @param bpmnDefId
	 * @param nodeId
	 * @return boolean
	 * @throws Exception 
	 */
	@SuppressWarnings("rawtypes")
	public static boolean isAllowEmptyIdentity(BpmProcessInstance instance, String nodeId) throws Exception {
		boolean isAllowEmpty = false;

		Object[] aryObj = getDefProperties(instance, nodeId);
		NodeProperties properties = (NodeProperties) aryObj[0];
		BpmProcessDef procDef = (BpmProcessDef) aryObj[1];

		if (properties != null) {
			isAllowEmpty = properties.isAllowExecutorEmpty();
		}
		if (!isAllowEmpty) {
			BpmProcessDefExt procExt = procDef.getProcessDefExt();
			BpmDefExtProperties extProperties = procExt.getExtProperties();
			isAllowEmpty = extProperties.isAllowExecutorEmpty();
		}

		return isAllowEmpty;

	}


	/**
	 * 获取跳过第一个节点。
	 * 
	 * @param defId
	 * @return boolean
	 * @throws Exception 
	 */
	public static boolean getSkipFirstNode(String defId) throws Exception {
		BpmDefinitionAccessor bpmDefinitionAccessor = (BpmDefinitionAccessor) AppUtil.getBean("bpmDefinitionAccessor");
		BpmProcessDef<BpmProcessDefExt> procDef = bpmDefinitionAccessor.getBpmProcessDef(defId);
		BpmProcessDefExt procExt = procDef.getProcessDefExt();
		BpmDefExtProperties extProperties = procExt.getExtProperties();
		return extProperties.isSkipFirstNode();
	}

	/**
	 * 根据bpmnDefId获取流程定义。
	 * 
	 * @param bpmnDefId
	 * @return BpmProcessDef&lt;BpmProcessDefExt>
	 * @throws Exception 
	 */
	public static BpmProcessDef<BpmProcessDefExt> getProcessDef(String bpmnDefId) throws Exception {
		BpmDefinitionService bpmDefinitionService = AppUtil.getBean(BpmDefinitionService.class);
		BpmProcessDef<BpmProcessDefExt> procDef = bpmDefinitionService.getBpmProcessDef(bpmnDefId);
		return procDef;
	}

	/**
	 * 根据流程DEFID获取流程定义。
	 * 
	 * @param defId
	 * @return BpmProcessDef&lt;BpmProcessDefExt>
	 * @throws Exception 
	 */
	public static BpmProcessDef<BpmProcessDefExt> getProcessDefByDefId(String defId) throws Exception {
		BpmDefinitionAccessor bpmDefinitionAccessor = (BpmDefinitionAccessor) AppUtil.getBean("bpmDefinitionAccessor");
		BpmProcessDef<BpmProcessDefExt> procDef = bpmDefinitionAccessor.getBpmProcessDef(defId);
		return procDef;
	}

	/**
	 * 
	 * 是否允许转办，如果是内部子流程，则会取父流程的参数
	 * 
	 * @param bpmNodeDef
	 * @return
	 */
	public static boolean IsAllowTransTo(BpmNodeDef bpmNodeDef) {
		boolean isAllowTransTo = true;
		if (bpmNodeDef.getBpmProcessDef().getProcessDefExt() == null) {// 子流程
			bpmNodeDef = bpmNodeDef.getParentBpmNodeDef();
		}
		try {
			isAllowTransTo = bpmNodeDef.getBpmProcessDef().getProcessDefExt().getExtProperties().isAllowTransTo();
		} catch (Exception e) {}
		return isAllowTransTo;
	}

	/**
	 * 
	 * 是否使用表单主版本，如果不是则取全局设置，如果全局未设置则取流程启动时的版本
	 * 
	 * @param bpmNodeDef
	 * @return
	 */
	public static String getUseMainForm(BpmNodeDef bpmNodeDef) {
		String useMainForm = "";
		if (bpmNodeDef.getBpmProcessDef().getProcessDefExt() == null) {// 子流程
			bpmNodeDef = bpmNodeDef.getParentBpmNodeDef();
		}
		try {
			useMainForm = bpmNodeDef.getBpmProcessDef().getProcessDefExt().getExtProperties().getUseMainForm();
		} catch (Exception e) {}
		return useMainForm;
	}

	/**
	 * 默认完成任务，在默认跳过时使用。
	 * 
	 * @param taskId
	 *            void
	 * @throws Exception 
	 */
	public static void finishTask(BpmTask bpmTask) throws Exception {

		String taskId = bpmTask.getId();
		// 跳过结果。
		SkipResult result = bpmTask.getSkipResult();

		ActionCmd actionCmd = ContextThreadUtil.getActionCmd();
		// 目标节点
		String destination = actionCmd.getDestination();
		// 节点人员
		Map<String, List<BpmIdentity>> identityMap = actionCmd.getBpmIdentities();

		BpmTaskActionService bpmTaskActionService = AppUtil.getBean(BpmTaskActionService.class);

		Map<String, ObjectNode> boMap = BpmContextUtil.getBoFromContext();

		DefaultTaskFinishCmd cmd = new DefaultTaskFinishCmd();
		//BeanUtils.copyProperties(cmd, actionCmd);
		cmd.addTransitVars(BpmConstants.BO_INST, boMap);
		if (!result.isSkipTask()) {
			cmd.setDestination(destination);
		} else {
			cmd.setDestination(""); 
		}
		cmd.setTaskId(taskId);
		cmd.addTransitVars(BpmConstants.BPM_TASK, bpmTask);
		cmd.setBpmIdentities(identityMap);
		cmd.setBusData(actionCmd.getBusData());
		cmd.setDataMode(actionCmd.getDataMode());

		cmd.setActionName(TaskActionType.AGREE.getKey());
		String skipType = result.getSkipType();
		cmd.addTransitVars(BpmConstants.BPM_SKIP_TYPE, skipType);
		bpmTaskActionService.finishTask(cmd);
	}

	/**
	 * 获取通知类型。
	 * 
	 * <pre>
	 * 	1.如果流程状态为测试，那么就获取流程定义测试状态的通知类型。
	 *  2.先获取节点的通知类型。
	 *  3.获取流程定义的通知类型。
	 * </pre>
	 * 
	 * @param bpmnDefId
	 * @param nodeId
	 * @return String
	 * @throws Exception 
	 */
	@SuppressWarnings("rawtypes")
	public static String getNotifyType(BpmProcessInstance instance, String nodeId) throws Exception {

		Object[] aryObj = getDefProperties(instance, nodeId);
		NodeProperties properties = (NodeProperties) aryObj[0];
		BpmProcessDef procDef = (BpmProcessDef) aryObj[1];
		//BpmDefinition bpmDefinition = (BpmDefinition) aryObj[2];

		BpmProcessDefExt ext = procDef.getProcessDefExt();
		BpmDefExtProperties prop = ext.getExtProperties();

		/*if (BpmDefinition.TEST_STATUS.TEST.equals(bpmDefinition.getTestStatus())) {
			return prop.getTestNotifyType();
		}*/

		String notifyType = "";
		if (properties != null) {
			notifyType = properties.getNotifyType();
		}
		if (StringUtil.isNotEmpty(notifyType))
			return notifyType;
		notifyType = prop.getNotifyType();

		return notifyType;

	}

	/**
	 * 当没有执行人时发布事件。
	 * 
	 * @param model
	 *            void
	 */
	public static void publishNoExecutorEvent(NoExecutorModel model) {
		NoExecutorEvent ev = new NoExecutorEvent(model);
		//AppUtil.getBean(NoExecutorEventListener.class);
		AppUtil.publishEvent(ev);
	}

	/**
	 * 设置任务是否跳过。
	 * 
	 * @param bpmTask
	 * void
	 * @throws Exception 
	 */
	public static void setTaskSkip(BpmTask bpmTask) throws Exception {
		if (bpmTask.getSkipResult().isHasGetSkip()) return;

		SkipResult skipResult = new SkipResult();
		skipResult.setHasGetSkip(true);

		bpmTask.setSkipResult(skipResult);

		ActionCmd cmd = ContextThreadUtil.getActionCmd();

		BpmProcessDef<BpmProcessDefExt> procDef = BpmUtil.getProcessDefByDefId(bpmTask.getProcDefId());

		BpmDefExtProperties extProperties = procDef.getProcessDefExt().getExtProperties();

		// 发起流程判断
		if (cmd instanceof ProcessInstCmd) {
			// 跳过第一个节点。
			boolean skipFirstNode = extProperties.isSkipFirstNode();
			if (skipFirstNode) {
				skipResult.setSkipTask(true);
				skipResult.setSkipType(SkipResult.SKIP_FIRST);
			}
		}
		//任务审批判断。
		//只要跳转规则满足要求，那么就允许任务直接跳过。
		else{
			String skipRules= extProperties.getSkipRules();
			//当跳转规则为空时，查找当前任务属性设置是否为“执行人为空跳过”，如果是，则将跳转规则设置为“SKIP_EMPTY_USER”
			if(StringUtil.isEmpty(skipRules)){
				try {
					BpmProcessInstance instance = (BpmProcessInstance) cmd.getTransitVars(BpmConstants.PROCESS_INST);
					Object[] aryObj = getDefProperties(instance, bpmTask.getNodeId());
					NodeProperties properties = (NodeProperties) aryObj[0];
					if(properties.isSkipExecutorEmpty()){
						skipRules = SkipResult.SKIP_EMPTY_USER;
					}
				} catch (Exception e) {}
			}
			if(StringUtil.isEmpty(skipRules)) return;

			String[] aryRules=skipRules.split(",");
			for(String rule:aryRules){
				ISkipCondition condition=  SkipConditionUtil.getSkipConditionByType(rule);
				boolean rtn=condition.canSkip(bpmTask);
                //相同执行人跳过时 任务节点配置了审批意见反填到表单控件
                if(rtn && !"reject".equals(cmd.getActionName())) {
                    if (procDef.getBpmnNodeDefs().size() > 0) {
                        for (int i = 0; i < procDef.getBpmnNodeDefs().size(); i++) {
                            if (procDef.getBpmnNodeDefs().get(i).getNodeId().equals(bpmTask.getNodeId())) {
                                if (procDef.getBpmnNodeDefs().get(i).getNodeProperties().size() > 0) {
                                    for (int k = 0; k < procDef.getBpmnNodeDefs().get(i).getNodeProperties().size(); k++) {
                                        if (BeanUtils.isNotEmpty(procDef.getBpmnNodeDefs().get(i).getNodeProperties().get(k).getOpinionField())) {
                                            String opinionField = procDef.getBpmnNodeDefs().get(i).getNodeProperties().get(k).getOpinionField();
                                            ObjectNode boDataZhu = (ObjectNode) JsonUtil.toJsonNode(cmd.getBusData()).get(opinionField.split("\\.")[0]);
                                            if(BeanUtils.isNotEmpty(boDataZhu.get(opinionField.split("\\.")[1]).asText())){
                                                String msg = boDataZhu.get(opinionField.split("\\.")[1]).asText();
                                                msg = msg+"\n\n同意（自动审批）\n"+bpmTask.getOwnerName()+" "+DateFormatUtil.format(LocalDateTime.now(),"yyyy-MM-dd HH:ss:mm")+"";
                                                boDataZhu.put(opinionField.split("\\.")[1],msg);
                                            }else{
                                                boDataZhu.put(opinionField.split("\\.")[1],"同意（自动审批）\n"+bpmTask.getOwnerName()+" "+
                                                        DateFormatUtil.format(LocalDateTime.now(),"yyyy-MM-dd HH:ss:mm"));
                                            }
                                            BusDataUtil.updateBoData(bpmTask.getProcInstId(),bpmTask.getNodeId(),JsonUtil.toJson(boDataZhu));
                                            //审批记录
                                            DefaultBpmCheckOpinion defaultBpmCheckOpinion = new DefaultBpmCheckOpinion();
                                            defaultBpmCheckOpinion.setId(UniqueIdUtil.getSuid());
                                            defaultBpmCheckOpinion.setProcDefId(bpmTask.getProcDefId());
                                            defaultBpmCheckOpinion.setProcInstId(bpmTask.getProcInstId());
                                            defaultBpmCheckOpinion.setTaskId(bpmTask.getTaskId());
                                            defaultBpmCheckOpinion.setTaskKey(bpmTask.getNodeId());
                                            defaultBpmCheckOpinion.setTaskName(bpmTask.getName());
                                            defaultBpmCheckOpinion.setStatus(OpinionStatus.AGREE.getKey());
                                            defaultBpmCheckOpinion.setCreateTime(LocalDateTime.now());
                                            defaultBpmCheckOpinion.setOpinion("同意（自动审批）");
                                            defaultBpmCheckOpinion.setQualfiedNames(bpmTask.getOwnerName());
                                            defaultBpmCheckOpinion.setAuditorName(bpmTask.getOwnerName());
                                            defaultBpmCheckOpinion.setCompleteTime(LocalDateTime.now());
                                            defaultBpmCheckOpinion.setDurMs(TimeUtil.getCurrentTimeMillis() - TimeUtil.getTimeMillis(defaultBpmCheckOpinion.getCreateTime()));
                                            defaultBpmCheckOpinion.setAuditor(bpmTask.getAssigneeId());
                                            defaultBpmCheckOpinion.setFiles("");
                                            defaultBpmCheckOpinion.setIsRead(1);
                                            BpmCheckOpinionManager bpmCheckOpinionManager = AppUtil.getBean(BpmCheckOpinionManager.class);
                                            bpmCheckOpinionManager.create(defaultBpmCheckOpinion);
                                        }
                                    }
                                    break;
                                }
                            }
                        }
                    }
                }
				if(!rtn ) continue;

				skipResult.setSkipTask(true);
				skipResult.setSkipType(condition.getType());
				break;
			}
		}


	}

	/**
	 * 根据用户名和ID获取用户对象。
	 * 
	 * @param userId
	 * @param userName
	 * @return User
	 */
	public static IUser getUser(String userId, String userName) {
		IUser user = new IUser() {
			/**
			 * serialVersionUID
			 * 
			 * @since 1.0.0
			 */
			private static final long serialVersionUID = -3279144470311301256L;

			String userId = "";
			String fullName = "";

			public String getIdentityType() {
				return null;
			}

			public void setUserId(String userId) {
				this.userId = userId;
			}

			public void setFullname(String fullName) {
				this.fullName = fullName;
			}

			public void setAccount(String account) {
			}

			public String getUserId() {
				return this.userId;
			}

			public String getPassword() {
				return null;
			}

			public String getMobile() {
				return null;
			}


			public String getFullname() {
				return this.fullName;
			}

			public String getEmail() {
				return null;
			}

			public String getAccount() {
				return null;
			}

			public void setAttributes(Map<String, String> map) {
			}

			public Map<String, String> getAttributes() {
				return null;
			}

			public boolean isAdmin() {
				return false;
			}


			@Override
			public String getAttrbuite(String key) {
				return "";
			}

			@Override
			public boolean isEnable() {
				// TODO Auto-generated method stub
				return true;
			}

			@Override
			public Collection<GrantedAuthority> getAuthorities() {
				// TODO Auto-generated method stub
				return null;
			}

			@Override
			public String getUsername() {
				// TODO Auto-generated method stub
				return null;
			}

			@Override
			public boolean isAccountNonExpired() {
				// TODO Auto-generated method stub
				return false;
			}

			@Override
			public boolean isAccountNonLocked() {
				// TODO Auto-generated method stub
				return false;
			}

			@Override
			public boolean isCredentialsNonExpired() {
				// TODO Auto-generated method stub
				return false;
			}

			@Override
			public boolean isEnabled() {
				// TODO Auto-generated method stub
				return false;
			}

			@Override
			public Integer getStatus() {
				return 1;
			}

			@Override
			public String getPhoto() {
				return null;
			}

            @Override
            public String getWeixin() {
                return null;
            }

            @Override
            public Integer getHasSyncToWx() {
                return null;
            }

			@Override
			public String getTenantId() {
				// TODO Auto-generated method stub
				return null;
			}

			@Override
			public LocalDateTime getPwdCreateTime() {
				// TODO Auto-generated method stub
				return null;
			}

			@Override
			public String getClientId() {
				// TODO Auto-generated method stub
				return null;
			}

			@Override
			public String getClientToken() {
				// TODO Auto-generated method stub
				return null;
			}

			@Override
			public LocalDate getEntryDate() {
				return null;
			}

			@Override
			public LocalDateTime getLockedTime() {
				return null;
			}

			@Override
			public Integer getLockedStatus() {
				return null;
			}

			@Override
			public Integer getUserType() {
				return null;
			}

			@Override
			public LocalDateTime getLastLoginTime() {
				return null;
			}

			@Override
			public String getSkin() {
				return "light";
			}

		};

		user.setUserId(userId);
		user.setFullname(userName);

		return user;
	}

	/**
	 * 根据用户ID返回用户对象。
	 * 
	 * @param userId
	 * @return User
	 */
	public static IUser getUser(String userId) {
		IUserService userService = AppUtil.getBean(IUserService.class);
		IUser user = userService.getUserById(userId);
		return user;
	}

	/**
	 * 根据流程实例数据构建业务表中间数据。
	 * 
	 * @param instance
	 * @return BpmBusLink
	 */
	public static BpmBusLink buildBusLink(BpmProcessInstance instance, ObjectNode result, String saveMode) {
		//获取顶级的流程实例。
		BpmProcessInstanceManager instanceManager = AppUtil.getBean(BpmProcessInstanceManager.class);
		instance = instanceManager.getTopBpmProcessInstance(instance);

		BpmBusLink busLink = buildBusLink(instance);
		busLink.setSaveMode(saveMode);
		busLink.setIsMain(result.get("parentId").asText().equals("0") ? 1 : 0);
		// 设置实体数据名称。
		busLink.setBoDefCode(result.get("boCode").asText());

		busLink.setFormIdentify(result.get("boEnt").get("name").asText());

		if ("number".equals(result.get("boEnt").get("isPkNumber").asText())) {
			busLink.setBusinesskey(new Long(result.get("pk").asText()));
		} else {
			busLink.setBusinesskeyStr(result.get("pk").asText());
		}
		if(StringUtil.isNotEmpty(instance.getSysCode())){
			busLink.setSysCode(instance.getSysCode());
		}
		return busLink;

	}

	/**
	 * 根据流程实例数据构建业务表中间数据。
	 * 
	 * @param instance
	 * @return
	 */
	public static BpmBusLink buildBusLink(BpmProcessInstance instance) {
		BpmBusLink busLink = new BpmBusLink();

		busLink.setId(UniqueIdUtil.getSuid());
		busLink.setDefId(instance.getProcDefId());
		busLink.setProcInstId(instance.getId());

		IUser curUser = ContextUtil.getCurrentUser();

		busLink.setStartId(curUser.getUserId());
		busLink.setStartor(curUser.getFullname());
		busLink.setCreateDate(LocalDateTime.now());
		if(StringUtil.isNotEmpty(instance.getSysCode())){
			busLink.setSysCode(instance.getSysCode());
		}

		return busLink;

	}

	/**
	 * 根据bpmn20流程定义xml文件，流程属性
	 * 
	 * @param flowElement
	 * @param qname
	 * @return
	 */
	public static List<Object> getFlowElementExtension(FlowElement flowElement, QName qname) {
		List<Object> extensions = new ArrayList<Object>();
		ExtensionElements extensionElements = flowElement.getExtensionElements();
		if (extensionElements == null)
			return extensions;
		List<Object> objects = extensionElements.getAny();
		for (Object obj : objects) {
			if (obj instanceof Element) {
				Element el = (Element) obj;// 要相同的命名空间
				QName qn = new QName(el.getNamespaceURI(), el.getLocalName());
				if (qname.equals(qn)) {
					extensions.add(el.getNodeValue());
				}
			}
		}
		return extensions;
	}

	public static Integer getFlowElementOrder(FlowElement flowElement) {
		Integer nodeOrder = 0;
		List<Object> extensions = getFlowElementExtension(flowElement, BPMN20ExtConst._ORDER_QNAME);

		if (BeanUtils.isNotEmpty(extensions)) {
			String s = (String) extensions.get(0);
			if(StringUtil.isNotEmpty(s) && !"null".equals(s)){
				nodeOrder = Integer.parseInt(s);
			}

		}
		return nodeOrder;
	}

	/**
	 * 获取按钮，并按照当前节点和任务状态过滤按钮
	 * 
	 * @param bpmNodeDef
	 *            流程节点
	 * @param task
	 *            任务
	 * @param dataObjects
	 *            数据对象
	 * @return
	 * @throws Exception 
	 */
	public static List<Button> getButtons(BpmNodeDef bpmNodeDef) throws Exception {
		return getButtons(bpmNodeDef, null);
	}

	/**
	 * 获取按钮，并按照当前节点和任务状态过滤按钮
	 * 
	 * @param bpmNodeDef
	 *            流程节点
	 * @param task
	 *            任务
	 * @param dataObjects
	 *            数据对象
	 * @return
	 * @throws Exception 
	 */
	public static List<Button> getButtons(BpmNodeDef bpmNodeDef, DefaultBpmTask task) throws Exception {
		List<Button> buttons = bpmNodeDef.getButtons();

		if(task==null) return buttons;

		List<Button> buttons2remove = new ArrayList<Button>();

		Map<String,Button> buttonMap= convertToMap(buttons);

		String status =task.getStatus();


		BpmDefExtProperties prop;
		if (bpmNodeDef.getBpmProcessDef().getProcessDefExt() == null) {
			// 当为子流程时获取父流程的扩展属性
			prop = bpmNodeDef.getBpmProcessDef().getParentProcessDef().getProcessDefExt().getExtProperties();
		} else {
			prop = bpmNodeDef.getBpmProcessDef().getProcessDefExt().getExtProperties();
		}

		boolean isAllowTransTo = prop.isAllowTransTo();
		//根据配置的按钮和任务状态获取按钮。
		buttons=getCommonButtons( buttons, status);

		//处理代理按钮
		handDelegateButton(buttonMap,buttons2remove, isAllowTransTo);
		// 处理会签任务
		handSignButtons(task.getTaskId(), bpmNodeDef, buttons, buttons2remove);
		// 处理锁定按钮
		handLockButton(task, buttons, buttons2remove);
		// 处理任务延迟按钮
		handTaskDelay(task.getTaskId(),bpmNodeDef,prop,buttonMap,buttons2remove);

		buttons.removeAll(buttons2remove);


		return buttons;
	}



	private static void handTaskDelay(String taskId,BpmNodeDef bpmNodeDef,BpmDefExtProperties prop,Map<String,Button> buttonMap,List<Button> buttons2remove) throws Exception {
		if(!buttonMap.containsKey("taskDelay")){
			return ;
		}
		BpmTaskDueTimeManager bpmTaskDueTimeManager = AppUtil.getBean(BpmTaskDueTimeManager.class);
		SystemConfigFeignService systemConfigFeignService = AppUtil.getBean(SystemConfigFeignService.class);
		BpmTaskDueTime bpmTaskDueTime = bpmTaskDueTimeManager.getByTaskId(taskId);
		if(BeanUtils.isEmpty(bpmTaskDueTime) && buttonMap.containsKey("taskDelay") ){
			buttons2remove.add(buttonMap.get("taskDelay"));
			return;
		}
		int remainingTime=0;
		if("caltime".equals(bpmTaskDueTime.getDateType())){
			// getSecondDiff 秒
			remainingTime = TimeUtil.getSecondDiff(LocalDateTime.now(), bpmTaskDueTime.getStartTime())/60;
		}else{
			// getWorkTimeByUser 毫秒
			ObjectNode params=JsonUtil.getMapper().createObjectNode();
			params.put("userId", bpmTaskDueTime.getUserId());
			params.put("startTime", DateFormatUtil.formaDatetTime(bpmTaskDueTime.getStartTime()));
			params.put("endTime", DateFormatUtil.formaDatetTime(LocalDateTime.now()));
			remainingTime =(int) (systemConfigFeignService.getWorkTimeByUser(params)/60000);
		}
		remainingTime = bpmTaskDueTime.getDueTime() - remainingTime;
		if(remainingTime<=0 && buttonMap.containsKey("taskDelay")){
			buttons2remove.add(buttonMap.get("taskDelay"));
		}

	}

	/**
	 * 处理转交按钮。
	 * @param buttons
	 * @param buttons2remove
	 * @param isAllowTransTo	是否允许转办。
	 */
	private static void handDelegateButton(Map<String,Button> buttonMap,List<Button>  buttons2remove,boolean isAllowTransTo ){
		boolean rtn=buttonMap.containsKey("delegate");
		if(!isAllowTransTo && rtn){
			buttons2remove.add(buttonMap.get("delegate"));
		}
	}


	/**
	 * 根据任务状态和节点按钮定义获取按钮。
	 * @param isAllowTransTo
	 * @param buttonsDef
	 * @param status
	 * @return
	 */
	private static List<Button> getCommonButtons(List<Button> buttonsDef, String status){
		List<Button> buttons=new ArrayList<Button>();
		TaskActionHandlerConfig config = (TaskActionHandlerConfig) AppUtil.getBean("taskActionHandlerConfig");
		if(BeanUtils.isEmpty(config)) {
			return buttons;
		}
		List<? extends TaskActionHandlerDef> listDef= config.getAllActionHandlerDefList();

		List<String> allActions=new ArrayList<String>();
		for(TaskActionHandlerDef def:listDef){
			allActions.add(def.getName());
		}

		//计算根据任务状态获取应该包含的按钮。
		@SuppressWarnings("unchecked")
		Map<String,String> defButtonsMap=(Map<String, String>) AppUtil.getBean("buttonsMap");
		//节点上可以允许的按钮。
		String btnsStr=defButtonsMap.get(status);

		String[] aryDefault = null;
		if(btnsStr!=null){
			aryDefault=btnsStr.split(",");
		}

		//得出自定义的按钮。
		for(Button btn:buttonsDef){
			if(!allActions.contains(btn.getAlias()) || isInDefault(btn.getAlias(),aryDefault)){
				buttons.add(btn);
			}
		}

		return buttons;
	}

	/**
	 * 按钮是否在默认的按钮中。
	 * @param alias
	 * @param aryDefault
	 * @return
	 */
	private static boolean isInDefault(String alias,String[] aryDefault){
		for(String tmp:aryDefault){
			if(tmp.equals(alias))
				return true;
		}
		return false;
	}




	/**
	 * 将按钮列表转换成map。
	 * @param buttons
	 * @return
	 */
	private static Map<String,Button> convertToMap(List<Button> buttons){
		Map<String,Button> btnMap=new HashMap<String, Button>();
		for(Button btn:buttons){
			btnMap.put(btn.getAlias(), btn);
		}
		return btnMap;
	}


	/**
	 * 处理会签按钮。
	 * 
	 * @param task
	 * @param bpmNodeDef
	 * @param buttons
	 * @param buttons2remove
	 * @throws Exception 
	 */
	private static void handSignButtons(String taskId, BpmNodeDef bpmNodeDef, List<Button> buttons, List<Button> buttons2remove) throws Exception {
		if (!bpmNodeDef.getType().equals(NodeType.SIGNTASK)||StringUtil.isEmpty(taskId)) return;
		// 处理会签任务
		NatTaskService natTaskService = (NatTaskService) AppUtil.getBean(NatTaskService.class);
		SignService signService = (SignService) AppUtil.getBean(SignService.class);
		Map<String, Object> variables = natTaskService.getVariables(taskId);
		List<PrivilegeMode> privilege = signService.getPrivilege(ContextUtil.getCurrentUserId(), (SignNodeDef) bpmNodeDef, variables);
		// 即没有全部特权又没有加签特权时移除加签按钮
		if (!privilege.contains(PrivilegeMode.ALL) && !privilege.contains(PrivilegeMode.ALLOW_ADD_SIGN)) {
			for (Button bnt : buttons) {
				if ("addSign".equals(bnt.getAlias())) {
					buttons2remove.add(bnt);
					break;
				}
			}
		}
	}

	/**
	 * 单独处理锁定按钮。
	 * 
	 * @param task
	 * @param buttons
	 * @param buttons2remove
	 */
	private static void handLockButton(DefaultBpmTask task, List<Button> buttons, List<Button> buttons2remove) {
		if (task == null) return;

		String taskId = task.getTaskId();
		//0:任务已经处理,1:可以锁定,2:不需要解锁 ,3:可以解锁，
		// 			4,被其他人锁定,5:这种情况一般是管理员操作，所以不用出锁定按钮。
		BpmTaskManager taskManager = (BpmTaskManager) AppUtil.getBean(BpmTaskManager.class);
		int canLock = taskManager.canLockTask(taskId);
		Button button = null;
		for (Button btn : buttons) {
			if (btn.getAlias().equals("lockUnlock")) {
				button = btn;
			}
		}
		if (button == null) return;
		// 删除锁定按钮
		if (canLock == 0 || canLock == 4 || canLock == 5 || canLock == 2) {
			buttons2remove.add(button);
		}


		if (canLock == 1) {
			button.setName("锁定");
		}
		if (canLock == 3) {
			button.setName("解锁");
		}

	}

	/**
	 * 获取节点的属性设置
	 * 
	 * <pre>
	 * 子流程会获取实例对应父流程的节点属性设置
	 * </pre>
	 * 
	 * @param instance
	 * @param nodeId
	 * @return
	 * @throws Exception 
	 */
	public static NodeProperties getNodeProperties(BpmProcessInstance instance, String nodeId) throws Exception {
		Object[] defProperties = getDefProperties(instance, nodeId);
		Object obj = defProperties[0];
		if (BeanUtils.isNotEmpty(obj) && obj instanceof NodeProperties) {
			return (NodeProperties) obj;
		}
		return null;
	}


	/**
	 * 处理表单意见。
	 * 
	 * <pre>
	 * 1.没有提交表单数据，不做处理。
	 * 2.如果表单数据中不包含表单意见项不做处理。
	 * 3.如果表单中包含意见项，只管一个表单意见
	 * 	设置：
	 *  cmd.setOpinionIdentity(opinionName);
	 *  cmd.setApprovalOpinion(opinion);
	 *  
	 *  {
	 *  __form_opinion:{"caiwu":"意见数据"}
	 *  }
	 * </pre>
	 * 
	 * @param request
	 * @param cmd
	 *            void
	 * @throws IOException 
	 */
	public static void handOpinion(String data, DefaultTaskFinishCmd cmd) throws IOException {
		if (StringUtil.isEmpty(data)) return;
		ObjectNode dataJson = (ObjectNode) JsonUtil.toJsonNode(data);
		if (dataJson.findValue(DefaultBpmCheckOpinion.OPINION_FLAG) == null) return;

		ObjectNode opinionJson = (ObjectNode) dataJson.get(DefaultBpmCheckOpinion.OPINION_FLAG);
		Iterator<Entry<String, JsonNode>> field = opinionJson.fields();
		while (field.hasNext()) {
			Entry<String, JsonNode> ent = field.next();
			String opinion = ent.getKey();
			if (StringUtil.isNotEmpty(opinion)) {
				cmd.setOpinionIdentity(opinion);
				cmd.setApprovalOpinion(ent.getValue().asText());
				break;
			}
		}
		dataJson.remove(DefaultBpmCheckOpinion.OPINION_FLAG);
		data = dataJson.toString();
		cmd.setBusData(data);
	}

	/**
	 * 验证handler输入是否是否有效。
	 * 
	 * <pre>
	 * 	handler 输入规则。
	 *  spring的 serviceId +“." + 方法名称。
	 * </pre>
	 * 
	 * @param handler
	 *            spring 的serviceId + "." + 方法名
	 * @param parameterTypes 
	 * @param args 
	 * @return 0 有效，-1，格式不对，-2 没有找到service类，-3没有找到对应的方法，-4，未知的错误。
	 */
	public static int isHandlerValidNoCmd(String handler, Class<?>[] parameterTypes) {

		if (handler.indexOf(".") == -1)
			return -1;
		String[] aryHandler = handler.split("[.]");
		String beanId = aryHandler[0];
		String method = aryHandler[1];
		Object serviceBean = null;
		try {
			serviceBean = AppUtil.getBean(beanId);
		} catch (Exception ex) {
			return -2;
		}
		if (serviceBean == null)
			return -2;

		try {
			Method invokeMethod = serviceBean.getClass().getMethod(method, parameterTypes);
			if (invokeMethod != null) {
				return 0;
			} else {
				return -3;
			}
		} catch (NoSuchMethodException e) {
			return -3;
		} catch (Exception e) {
			return -4;
		}
	}


	public static void restfulPluginExecut(DefaultBpmTask task,EventType eventType) throws Exception{
		BpmDefinitionAccessor bpmDefinitionAccessor = (BpmDefinitionAccessor) AppUtil.getBean("bpmDefinitionAccessor");
		BpmPluginFactory bpmPluginFactory = (BpmPluginFactory) AppUtil.getBean("bpmPluginFactory");
		RestfulService restfulService = (RestfulService) AppUtil.getBean(RestfulService.class);
		BpmProcessDef<BpmProcessDefExt> bpmProcessDef = bpmDefinitionAccessor.getBpmProcessDef(task.getProcDefId());
		//获取全局的restful接口事件
		List<BpmPluginContext> pluginContextList=bpmProcessDef.getProcessDefExt().getBpmPluginContexts();
		if(BeanUtils.isNotEmpty(pluginContextList)){
			for(BpmPluginContext bpmPluginContext:pluginContextList){
				BpmPluginDef bpmPluginDef =bpmPluginContext.getBpmPluginDef();
				if(bpmPluginDef instanceof BpmExecutionPluginDef){
					BpmExecutionPluginDef bpmExecutionPluginDef = (BpmExecutionPluginDef)bpmPluginDef;
					BpmExecutionPlugin bpmExecutionPlugin = bpmPluginFactory.buildExecutionPlugin(bpmPluginContext, eventType);
					if(bpmExecutionPlugin!=null){
						if(bpmPluginContext.getEventTypes().contains(eventType)){
							if(bpmExecutionPluginDef instanceof IGlobalRestfulPluginDef){
								IGlobalRestfulPluginDef restfulPluginDef = (IGlobalRestfulPluginDef) bpmExecutionPluginDef;
								List<Restful> restfuls = restfulPluginDef.getRestfulList();
								if(BeanUtils.isNotEmpty(restfuls)){
									restfulService.outTaskPluginExecute(task, restfuls,eventType);
								}
							}
						}
					}	
				}
			}
		}

		//执行节点的任务类插件
		BpmNodeDef bpmNodeDef= bpmDefinitionAccessor.getBpmNodeDef( task.getProcDefId(), task.getNodeId());
		for(BpmPluginContext bpmPluginContext:bpmNodeDef.getBpmPluginContexts()){
			//事件为空则跳过。
			if(BeanUtils.isEmpty(bpmPluginContext.getEventTypes())) continue;

			BpmPluginDef bpmPluginDef = bpmPluginContext.getBpmPluginDef();
			if(bpmPluginDef instanceof BpmTaskPluginDef){
				BpmTaskPluginDef bpmTaskPluginDef = (BpmTaskPluginDef)bpmPluginDef;
				BpmTaskPlugin bpmTaskPlugin = bpmPluginFactory.buildTaskPlugin(bpmPluginContext, eventType);
				if(bpmTaskPlugin==null) continue;
				if(bpmPluginContext.getEventTypes().contains(eventType)){
					if(bpmTaskPluginDef instanceof IGlobalRestfulPluginDef){
						IGlobalRestfulPluginDef restfulPluginDef = (IGlobalRestfulPluginDef) bpmTaskPluginDef;
						List<Restful> restfuls = restfulPluginDef.getRestfulList();
						if(BeanUtils.isNotEmpty(restfuls)){
							restfulService.outTaskPluginExecute(task, restfuls,eventType);
						}
					}
				}

			}
		}
	}

	/**
	 * 获取驳回上一步的目标节点
	 * @param taskId
	 * @return "" 不可驳回   ， 非空返回可驳回到上一步节点的名称
	 * @throws Exception 
	 */
	public static String getRejectPreDestination(String taskId) throws Exception{

		boolean canRejectPreAct=true;//是否可以驳回到上一步

		BpmTaskManager bpmTaskManager = (BpmTaskManager) AppUtil.getBean("bpmTaskManager");
		DefaultBpmTask task = bpmTaskManager.get(taskId);

		String defId = task.getProcDefId();
		String nodeId = task.getNodeId();

		boolean canReject = false;
		BpmDefinitionAccessor bpmDefinitionAccessor = (BpmDefinitionAccessor) AppUtil.getBean("bpmDefinitionAccessor"); 
		BpmNodeDef taskNodeDef = bpmDefinitionAccessor.getBpmNodeDef(defId, nodeId);
		List<Button> buttons = BpmUtil.getButtons(taskNodeDef, task);
		for (Button button : buttons) {
			if ("reject".equals(button.getAlias())) 
				canReject = true;
		}

		if(!canReject) return "";

		NodeProperties nodeProperties=taskNodeDef.getLocalProperties();
		String backMode= nodeProperties.getBackMode();
		if(StringUtil.isEmpty(backMode))backMode="normal";
		String procInstId = task.getProcInstId();

		List<BpmNodeDef> listBpmNodeDef = BpmStackRelationUtil.getHistoryListBpmNodeDef(procInstId, task.getNodeId(), "pre");
		// 允许直来直往的节点
		List<BpmNodeDef> bpmExeStacksUserNode = new ArrayList<BpmNodeDef>();
		// 允许按流程图执行的节点
		List<BpmNodeDef> bpmExeStacksGoMapUserNode = new ArrayList<BpmNodeDef>();
		BpmExeStackRelationDao relationDao =  (BpmExeStackRelationDao) AppUtil.getBean("bpmExeStackRelationDaoImpl");
		List<BpmExeStackRelation> relationList= relationDao.getListByProcInstId(procInstId);
		for (BpmNodeDef node : listBpmNodeDef) {
			if ((node.getType().equals(NodeType.USERTASK)||node.getType().equals(NodeType.SIGNTASK))&&!node.getNodeId().equals(nodeId)) {
				bpmExeStacksUserNode.add(node);

				boolean isHavePre = BpmStackRelationUtil.isHaveAndOrGateway(procInstId, node.getNodeId(), "pre",relationList);
				boolean isHaveAfter = BpmStackRelationUtil.isHaveAndOrGateway(procInstId, node.getNodeId(), "after",relationList);
				if (!(isHavePre && isHaveAfter)) {
					bpmExeStacksGoMapUserNode.add(node);
				}else{
					List<BpmNodeDef> incomeNodes = node.getIncomeNodes();
					if(BeanUtils.isNotEmpty(incomeNodes)){
						BpmNodeDef nodeDef = incomeNodes.get(0);
						//如果是从开始网关进入的用户节点，则允许按流程图驳回
						if(node.getType().equals(NodeType.USERTASK) && (nodeDef.getType().equals(NodeType.START)||nodeDef.getType().equals(NodeType.USERTASK))){
							bpmExeStacksGoMapUserNode.add(node);
						}
					}
				}
			}
		}
		canRejectPreAct=bpmExeStacksGoMapUserNode.size()>0||bpmExeStacksUserNode.size()>0;

		if(!canRejectPreAct){
			return "";
		}

		if("direct".equals(backMode)){
			return bpmExeStacksUserNode.get(0).getNodeId();
		}else{
			return  bpmExeStacksGoMapUserNode.get(0).getNodeId();
		}

	}

	public static void autoTrans(BpmNodeDef bpmNodeDef ,String instId,String taskId){
		//传阅任务
        try {
            List<IUser> iUsers = new ArrayList<>();
            BpmProcessInstanceManager instanceManager = AppUtil.getBean(BpmProcessInstanceManager.class);
            BpmProcessInstance instance = instanceManager.get(instId);
            for(BpmPluginContext bpmPluginContext:bpmNodeDef.getBpmPluginContexts()) {
                if ("传阅用户分配插件".equals(bpmPluginContext.getTitle())) {
                	BpmPluginSessionFactory bpmPluginSessionFactory  = AppUtil.getBean(BpmPluginSessionFactory.class);
                	BpmIdentityExtractService bpmIdentityExtractService  = AppUtil.getBean(BpmIdentityExtractService.class);
                    UserAssignPluginDef userAssignPluginDef = (UserAssignPluginDef) bpmPluginContext.getBpmPluginDef();
                    //调用人员计算插件对人员进行计算。
                    UserQueryPlugin userQueryPlugin = AppUtil.getBean(UserQueryPlugin.class);
                    ActionCmd cmd = ContextThreadUtil.getActionCmd();
                    Map<String, Object> variables = cmd.getVariables();
                    variables.putAll(cmd.getTransitVars());
                    List<BpmIdentity> bpmIdentities = userQueryPlugin.execute(bpmPluginSessionFactory.buildBpmUserCalcPluginSession(variables), userAssignPluginDef);
                    iUsers = bpmIdentityExtractService.extractUser(bpmIdentities);
                    if (BeanUtils.isNotEmpty(iUsers)) {
                    	String notifyType = BpmUtil.getNotifyType(instance, bpmNodeDef.getNodeId());
                    	List<String> copyUsers = new ArrayList<>();
                    	for (IUser entity : iUsers) {
                            String curUserId = ContextUtil.getCurrentUserId();
                            copyUsers.add(entity.getFullname());
                            if (!curUserId.equals(entity.getUserId())) {
                                transToMore(instance, entity.getUserId(), notifyType,taskId);
                            }
                        }
                    	 //审批记录
                        DefaultBpmCheckOpinion defaultBpmCheckOpinion = new DefaultBpmCheckOpinion();
                        defaultBpmCheckOpinion.setId(UniqueIdUtil.getSuid());
                        defaultBpmCheckOpinion.setProcDefId(instance.getBpmnDefId());
                        defaultBpmCheckOpinion.setProcInstId(instance.getId());
                        defaultBpmCheckOpinion.setTaskId(BeanUtils.isEmpty(taskId)?null:taskId);
                        defaultBpmCheckOpinion.setTaskKey(null);
                        defaultBpmCheckOpinion.setTaskName("传阅任务");
                        defaultBpmCheckOpinion.setStatus(OpinionStatus.COPYTO.getKey());
                        defaultBpmCheckOpinion.setCreateTime(LocalDateTime.now());
                        defaultBpmCheckOpinion.setOpinion("系统自动传阅");
                        defaultBpmCheckOpinion.setQualfiedNames(StringUtil.join(copyUsers, ","));
                        defaultBpmCheckOpinion.setAuditorName(ContextUtil.getCurrentUser().getFullname());
                        defaultBpmCheckOpinion.setCompleteTime(LocalDateTime.now());
                        defaultBpmCheckOpinion.setDurMs(TimeUtil.getCurrentTimeMillis() - TimeUtil.getTimeMillis(defaultBpmCheckOpinion.getCreateTime()));
                        defaultBpmCheckOpinion.setAuditor(ContextUtil.getCurrentUser().getUserId());
                        defaultBpmCheckOpinion.setFiles("");
                        defaultBpmCheckOpinion.setIsRead(1);
                        BpmCheckOpinionManager bpmCheckOpinionManager = AppUtil.getBean(BpmCheckOpinionManager.class);
                        bpmCheckOpinionManager.create(defaultBpmCheckOpinion);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        	throw new WorkFlowException("系统自动传阅任务失败");
        }
	}
	
	private static void transToMore(BpmProcessInstance instance,String userId,String messageType,String taskId){
		IUserService userServiceImpl = AppUtil.getBean(IUserService.class);
		IUser user = userServiceImpl.getUserById(userId);
        IUser currentUser = userServiceImpl.getUserById(ContextUtil.getCurrentUserId());
	    //添加知会任务
        BpmTaskNoticeManager noticeManager = AppUtil.getBean(BpmTaskNoticeManager.class);
        BpmTaskNotice taskNotice = new BpmTaskNotice("传阅任务", instance.getSubject(), instance.getId(), instance.getProcDefId(), instance.getProcDefName(), userId, user.getFullname(), TaskType.COPYTO.getKey(),((DefaultBpmProcessInstance) instance).getSupportMobile(),BeanUtils.isNotEmpty(currentUser)?currentUser.getFullname():"系统执行人",BeanUtils.isNotEmpty(currentUser)?currentUser.getUserId():"-1",0,BeanUtils.isEmpty(taskId)?null:taskId,null);
        noticeManager.create(taskNotice);
        //发送消息通知
        try {
        	 NotifyHelper notifyHelper = AppUtil.getBean(NotifyHelper.class);
             List<IUser> receiverUsers = new ArrayList<IUser>();
             receiverUsers.add(user);
             if(StringUtil.isEmpty(messageType)) {
             	messageType = "inner";
             }
             Map<String,Object> variables = new HashMap<String, Object>();
             ActionCmd actionCmd = ContextThreadUtil.getActionCmd();
     		if(actionCmd instanceof TaskFinishCmd){
     			String baseUrl = PortalDataUtil.getPropertyByAlias(TemplateConstants.TEMP_VAR.BASE_URL);
     			variables.put(TemplateConstants.TEMP_VAR.BASE_URL, baseUrl);
     			variables.put(TemplateConstants.TEMP_VAR.INST_ID, actionCmd.getInstId());
     			BpmTask task = (BpmTask) actionCmd.getTransitVars(BpmConstants.BPM_TASK);
     			variables.put(TemplateConstants.TEMP_VAR.TASK_ID, task.getId());
     			// 任务标题
     			variables.put(TemplateConstants.TEMP_VAR.TASK_SUBJECT, task.getSubject());
     			//任务名称
     			variables.put(TemplateConstants.TEMP_VAR.NODE_NAME,task.getName());
     			//流程实例名称
     			variables.put(TemplateConstants.TEMP_VAR.INST_SUBJECT, task.getSubject());
     			//流程发起时间
     			variables.put(TemplateConstants.TEMP_VAR.DATE, instance.getCreateTime());
     			//流程申请人
     			variables.put(TemplateConstants.TEMP_VAR.CREATOR, instance.getCreator());
     			//流程名称
     			variables.put(TemplateConstants.TEMP_VAR.BPMNAME, instance.getProcDefName());
     			if (currentUser != null) {
     				variables.put(TemplateConstants.TEMP_VAR.SENDER, currentUser.getFullname());
     				variables.put(TemplateConstants.TEMP_VAR.DELEGATE,currentUser.getFullname());
     				variables.put(TemplateConstants.TEMP_VAR.AGENT, currentUser.getFullname());
     			}
     			
     			String cause = ((TaskFinishCmd)actionCmd).getApprovalOpinion();
     			// 意见
     			variables.put(TemplateConstants.TEMP_VAR.CAUSE, cause);
     		}
			notifyHelper.notify(receiverUsers, Arrays.asList(messageType.split(",")), TemplateConstants.TYPE_KEY.COPY_TO, variables);
		} catch (Exception e) {
			e.printStackTrace();
		}
    }


}
