package nc.bs.wfengine.engine;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.yonyou.cloud.ncc.NCCSagas;

import nc.bs.dao.BaseDAO;
import nc.bs.dao.DAOException;
import nc.bs.framework.common.InvocationInfoProxy;
import nc.bs.framework.common.NCLocator;
import nc.bs.logging.Logger;
import nc.bs.ml.NCLangResOnserver;
import nc.bs.pf.pub.PFRequestDataCacheProxy;
import nc.bs.pf.pub.PfDataCache;
import nc.bs.pf.pub.cache.CondStringKey;
import nc.bs.pf.pub.cache.IRequestDataCacheKey;
import nc.bs.pub.pf.PfUtilTools;
import nc.bs.pub.taskmanager.TaskManagerDMO;
import nc.bs.pub.taskmanager.WfTaskManager;
import nc.bs.pub.wfengine.impl.ActionEnvironment;
import nc.bs.pub.wfengine.impl.WfDispatchUtils;
import nc.bs.pub.wfengine.status.AbstractFlowStatusService;
import nc.bs.pub.wfengine.status.IFlowStatusService;
import nc.bs.wfengine.engine.persistence.EnginePersistence;
import nc.bs.wfengine.engine.persistence.FlowInstanceDeletionDAO;
import nc.bs.wfengine.engine.utils.EngineServiceUtils;
import nc.impl.uap.pf.PFOrgUnitImpl;
import nc.itf.org.IOrgUnitQryService;
import nc.itf.uap.pf.IPFConfig;
import nc.itf.uap.pf.IWorkflowDefine;
import nc.itf.uap.pf.metadata.IFlowBizItf;
import nc.jdbc.framework.JdbcSession;
import nc.jdbc.framework.PersistenceManager;
import nc.jdbc.framework.SQLParameter;
import nc.jdbc.framework.exception.DbException;
import nc.jdbc.framework.generator.SequenceGenerator;
import nc.jdbc.framework.processor.ArrayListProcessor;
import nc.jdbc.framework.processor.ArrayProcessor;
import nc.jdbc.framework.processor.BaseProcessor;
import nc.jdbc.framework.processor.BeanListProcessor;
import nc.jdbc.framework.processor.ColumnListProcessor;
import nc.jdbc.framework.processor.ColumnProcessor;
import nc.jdbc.framework.processor.MapListProcessor;
import nc.jdbc.framework.processor.ProcessorUtils;
import nc.jdbc.framework.util.InOutUtil;
import nc.pubitf.rbac.IUserPubService;
import nc.uap.pf.metadata.PfMetadataTools;
import nc.vo.cache.CacheManager;
import nc.vo.cache.ICache;
import nc.vo.cache.config.CacheConfig;
import nc.vo.jcom.lang.StringUtil;
import nc.vo.ml.NCLangRes4VoTransl;
import nc.vo.org.OrgVO;
import nc.vo.pf.change.PfUtilBaseTools;
import nc.vo.pf.pub.util.SQLUtil;
import nc.vo.pub.AggregatedValueObject;
import nc.vo.pub.BusinessException;
import nc.vo.pub.billtype.BilltypeVO;
import nc.vo.pub.compiler.PfParameterVO;
import nc.vo.pub.lang.MultiLangText;
import nc.vo.pub.lang.UFBoolean;
import nc.vo.pub.lang.UFDateTime;
import nc.vo.pub.pf.IPfRetCheckInfo;
import nc.vo.pub.pf.NCFunctionConditionVO;
import nc.vo.pub.pf.WfTaskInfo;
import nc.vo.pub.workflownote.WorkflownoteVO;
import nc.vo.trade.sqlutil.IInSqlBatchCallBack;
import nc.vo.trade.sqlutil.InSqlBatchCaller;
import nc.vo.uap.pf.DynamicOrganizeUnit;
import nc.vo.uap.pf.OrganizeUnit;
import nc.vo.uap.pf.OrganizeUnitTypes;
import nc.vo.uap.pf.PFBusinessException;
import nc.vo.uap.pf.PFRuntimeException;
import nc.vo.uap.rbac.ResponsibilityVO;
import nc.vo.uap.wfmonitor.ProcessRouteRes;
import nc.vo.wfengine.core.activity.Activity;
import nc.vo.wfengine.core.activity.GenericActivityEx;
import nc.vo.wfengine.core.activity.SubFlow;
import nc.vo.wfengine.core.application.IWorkflowGadget;
import nc.vo.wfengine.core.application.WfGadgetBodyVO;
import nc.vo.wfengine.core.application.WfGadgetContext;
import nc.vo.wfengine.core.application.WfGadgetControlSource;
import nc.vo.wfengine.core.application.WorkflowgadgetVO;
import nc.vo.wfengine.core.parser.XPDLNames;
import nc.vo.wfengine.core.parser.XPDLParserException;
import nc.vo.wfengine.core.transition.BasicTransitionEx;
import nc.vo.wfengine.core.util.CoreUtilities;
import nc.vo.wfengine.core.workflow.BasicWorkflowProcess;
import nc.vo.wfengine.core.workflow.WorkflowProcess;
import nc.vo.wfengine.definition.ActivityTypeEnum;
import nc.vo.wfengine.definition.NCFunctionXpression;
import nc.vo.wfengine.definition.WfProcessParentMap;
import nc.vo.wfengine.definition.WorkflowDefStatusEnum;
import nc.vo.wfengine.definition.WorkflowDefinitionVO;
import nc.vo.wfengine.definition.WorkflowTypeEnum;
import nc.vo.wfengine.engine.exception.DefinitionNotFound;
import nc.vo.wfengine.engine.exception.EngineException;
import nc.vo.wfengine.pub.WFTask;
import nc.vo.wfengine.pub.WfTaskOrInstanceStatus;
import nc.vo.wfengine.pub.WfTaskType;
import nccloud.impl.platform.common.util.StringUtils;
import nccloud.pubitf.riart.pf.INccPFCrossServiceQuery;

/**
 * 工作流查询DMO
 * 
 * @author wzhy 2005-2
 * @modifier leijun 2005-10-14 适配到NC5
 * @modifier leijun 2006-5-19 删除时将遗留的工作项置为无效
 * @modifier leijun 2008-4 对一些查询新增单据类型条件
 * @modifier leijun 2008-9 增加工作流相关处理
 * @modifier leijun 2008-12 查询用户所属角色的流程定义时，使用批量查询
 */
public class EngineService {
	public EngineService() {
		super();
	}

	/**
	 * 查找某操作员当前可优先启动的审批流或工作流定义
	 * 
	 * @param billOrTranstype
	 * @param pkOrg
	 * @param billmakerId
	 * @param iWorkflowOrApproveflow 主流程类型，见<code>WorkflowTypeEnum.Approveflow</code>和
	 *                               <code>WorkflowTypeEnum.Workflow</code>
	 * @param emendEnum              修订审批流
	 * @return
	 * @throws BusinessException
	 */
	public WorkflowProcess queryWfProcessCanStart2(String billOrTranstype, String pk_group, String pkOrg,
			String billmakerId, int emendEnum, int iWorkflowOrApproveflow) throws BusinessException {
//		IRequestDataCacheKey key = new CondStringKey(IRequestDataCacheKey.CATEGORY_ENGINESERVICE_QUERYWFCANSTART2,
//				new String[] { billOrTranstype, pk_group, pkOrg, billmakerId, String.valueOf(emendEnum),
//						String.valueOf(iWorkflowOrApproveflow) });
//
//		Object cachedObj = PFRequestDataCacheProxy.get(key);
//
//		if (cachedObj == PFRequestDataCacheProxy.NULL) {
//			return null;
//		} else if (cachedObj != null && cachedObj instanceof WorkflowProcess) {
//			return (WorkflowProcess) cachedObj;
//		} else {
			WorkflowProcess wp = queryWfProcessCanStart2_db(billOrTranstype, pk_group, pkOrg, billmakerId, emendEnum,
					iWorkflowOrApproveflow);

//			if (wp == null) {
//				PFRequestDataCacheProxy.put(key, PFRequestDataCacheProxy.NULL);
//				return null;
//			} else {
//				PFRequestDataCacheProxy.put(key, wp);
				return wp;
//			}
	}

	public WorkflowProcess queryWfProcessCanStart2_db(String billOrTranstype, String pk_group, String pkOrg,
			String billmakerId, int emendEnum, int iWorkflowOrApproveflow) throws BusinessException {
		Logger.debug("查询流程定义: billOrTranstype=" + billOrTranstype + ";pkOrg=" + pkOrg + ";userId=" + billmakerId
				+ "iWorkflowtype=" + iWorkflowOrApproveflow + "开始");

		WorkflowProcess wp = queryWfProcessByUser2(billOrTranstype, pk_group, pkOrg, pkOrg, billmakerId, emendEnum,
				iWorkflowOrApproveflow);
		if (wp == null) {
			if (PfUtilBaseTools.isTranstype(billOrTranstype)) {
				String realBilltype = PfUtilBaseTools.getRealBilltype(billOrTranstype);
				// 2011-7-1修改交易类型的审批流未按照交易类型匹配
				// wp = queryWfProcessCanStart2(realBilltype, pk_group, pkOrg,
				// billmakerId,
				wp = queryWfProcessByUser2(realBilltype, pk_group, pkOrg, pkOrg, billmakerId, emendEnum,
						iWorkflowOrApproveflow);
			}
			if (wp == null) {
				if (!StringUtil.isEmptyWithTrim(pkOrg))
					// 查无组织的流程定义
					wp = queryWfProcessCanStartNoOrg(billOrTranstype, pk_group, pkOrg, billmakerId, emendEnum,
							iWorkflowOrApproveflow);
			}
		}
		return wp;
	}

	/**
	 * 匹配没有配组织的流程 由于职责匹配需要单据的组织，所以还是要把pkOrg传进去，但是不用于匹配审批流
	 * 
	 * @return
	 * @throws BusinessException
	 */
	public WorkflowProcess queryWfProcessCanStartNoOrg(String billOrTranstype, String pk_group, String pkOrg,
			String billmakerId, int emendEnum, int iWorkflowOrApproveflow) throws BusinessException {
		Logger.debug("查询无组织流程定义: billOrTranstype=" + billOrTranstype + ";pkOrg=" + pkOrg + ";userId=" + billmakerId
				+ "iWorkflowtype=" + iWorkflowOrApproveflow + "开始");

		WorkflowProcess wp = queryWfProcessByUser2(billOrTranstype, pk_group, null, pkOrg, billmakerId, emendEnum,
				iWorkflowOrApproveflow);
		if (wp == null) {
			if (PfUtilBaseTools.isTranstype(billOrTranstype)) {
				String realBilltype = PfUtilBaseTools.getRealBilltype(billOrTranstype);
				// 2011-7-1修改交易类型的审批流未按照交易类型匹配
				// wp = queryWfProcessCanStart2(realBilltype, pk_group, pkOrg,
				// billmakerId,
				wp = queryWfProcessByUser2(realBilltype, pk_group, null, pkOrg, billmakerId, emendEnum,
						iWorkflowOrApproveflow);
			}
		}
		return wp;
	}

	/**
	 * 批量查询某些启动参与者是否有流程定义 若查询到多条记录，判断是否有明细关系，若有，返回最业务单元的那条数据，否则提示包含多条流程定义。(zhouwba)
	 * 
	 * @param alOrganizeUnitPKs 启动参与者PK数组
	 * @param jdbc
	 * @param billOrTranstype
	 * @param pkOrg
	 * @param iWorkflowtype
	 * @param ouType
	 * @param hsDefPKs
	 * @throws BusinessException
	 */
	private String batchQueryDefPK(ArrayList<String> alOrganizeUnitPKs, final JdbcSession jdbc,
			final String billOrTranstype, final String pk_group, final String pkOrg, final int emendEnum,
			final int iWorkflowtype, final String ouType) throws BusinessException {
		if (alOrganizeUnitPKs.size() > 0) {
			HashSet<String> set = new HashSet<String>();
			for (String s : alOrganizeUnitPKs) {// 收敛一下，避免重复pk
				set.add(s);
			}
			alOrganizeUnitPKs.clear();
			for (String k : set) {
				alOrganizeUnitPKs.add(k);
			}
			try {
				InSqlBatchCaller caller = new InSqlBatchCaller(alOrganizeUnitPKs);
				Object obj = caller.execute(new IInSqlBatchCallBack() {

					public Object doWithInSql(String inSql) throws BusinessException, SQLException {

						Object result = null;
						String key = "BATCHQUERYDEFPK@" + inSql + "@" + iWorkflowtype + "@" + billOrTranstype + "@"
								+ pk_group + "@" + emendEnum + "@" + pkOrg + "@" + ouType;
						if (PFRequestDataCacheProxy.getPublishedFileCache().containsKey(key)) {
							result = PFRequestDataCacheProxy.getPublishedFileCache().get(key);
						} else {
							result = dealSql(inSql);
							PFRequestDataCacheProxy.getPublishedFileCache().put(key, result);
						}
						return result;
					}

					private Object dealSql(String inSql) throws BusinessException, SQLException {
						String sqlReturnPK = "select pk_wf_def from pub_wf_def"
								+ " where workflow_type=? and billtype=? and pk_group=? and ";
						if (emendEnum == -1) {
							// XXX 适配60升级上来的审批流定义 emendenum 字段为null
							sqlReturnPK += " (emendenum =? or isnull(cast(emendenum as char),'~')='~')";
						} else {
							sqlReturnPK += "emendenum =?  ";
						}
						if (pkOrg != null) {
							sqlReturnPK += " and exists (select 1 from pub_wf_def_org wdo where wdo.pk_wf_def = pub_wf_def.pk_wf_def and wdo.pk_org = ?)";
						} else {
							sqlReturnPK += " and orgcount = 0 ";
						}
						sqlReturnPK += " and validity=" + WorkflowDefStatusEnum.Valid.getIntValue()
								+ " and billmaker_type=? and billmaker in" + inSql + " order by priority desc";
						SQLParameter para = new SQLParameter();
						para.addParam(iWorkflowtype);
						para.addParam(billOrTranstype);
						para.addParam(pk_group);
						para.addParam(emendEnum);
						if (pkOrg != null) {
							para.addParam(pkOrg);
						}
						para.addParam(ouType);

						try {
							@SuppressWarnings("unchecked")
							ArrayList<String> alDefPK = (ArrayList<String>) jdbc.executeQuery(sqlReturnPK, para,
									new ColumnListProcessor(1));
							if (alDefPK != null && alDefPK.size() == 1) {// 若只有一条直接返回
								return alDefPK.get(0);
							} else if (alDefPK != null && alDefPK.size() > 1) {// 若有多条，按明细优先取值
								WFDefMatchErrorInfo matchErrorInfo = new WFDefMatchErrorInfo();
								String priorDefPK = tryGetOrgPriorDefPK(alDefPK, matchErrorInfo);
								if (priorDefPK == null) {// 匹配到无法确认的多个流程
									Logger.error("匹配到多个流程，无法区分选择哪个流程定义：" + alDefPK);
									//String orgName = PfUtilUITools.getOrgInfoByPK(pkOrg)[1];
//									IOrgUnitQryService orgService=NCLocator.getInstance().lookup(IOrgUnitQryService.class);
//									OrgVO org=orgService.getOrg(pkOrg);
//									String orgName=org.getName();
									String billTypeName = EngineServiceUtils.getRealBilltypeName(billOrTranstype);
									String conflictMsg = EngineServiceUtils.retConflictDefMsg(matchErrorInfo);
									throw new PFBusinessException(NCLangRes4VoTransl.getNCLangRes().getStrByID(
											"pfworkflow", "EngineService-0006", null,
											new String[] { pkOrg, billTypeName, conflictMsg }));
								} else {
									return priorDefPK;
								}
							}
						} catch (DbException e) {
							Logger.error(e.getMessage(), e);
							throw new PFBusinessException(NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow",
									"UPPpfworkflow-000059")/*
															 * 查询流程定义表发生数据库异常 ：
															 */
									+ e.getMessage(), e);
						}
						return null;
					}
				});
				return (String) obj;
			} catch (SQLException e) {
				Logger.error(e.getMessage(), e);
				throw new PFBusinessException(NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow",
						"UPPpfworkflow-000059")/*
												 * 查询流程定义表发生数据库异常 ：
												 */
						+ e.getMessage(), e);
			}
		}
		return null;
	}

	/**
	 * 尝试获取唯一的业务单元流程定义，存在返回Pk，否则返回null
	 * 
	 * @param defPkList
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private String tryGetOrgPriorDefPK(List<String> defPkList, WFDefMatchErrorInfo matchError)
			throws BusinessException {
		// 获取所属组织为业务单元流程定义PK。
		if (defPkList == null || defPkList.size() == 0) {
			return null;
		}
		StringBuffer sb = new StringBuffer();
		for (String defPK : defPkList) {
			sb.append(",'" + defPK + "'");
		}
		String defPKWhere = sb.toString().substring(1);
		//fengjks 匹配到多个流程，将返回优先级最大的那个，如果优先级相同，随机返回一个。
		String sql = "select pk_wf_def from pub_wf_def,org_orgs where"
				+ " pub_wf_def.pk_org = org_orgs.pk_org and org_orgs.isbusinessunit='Y'" + " and pk_wf_def in("
				+ defPKWhere + ") order by pub_wf_def.priority desc";
		BaseDAO dao = new BaseDAO();
		try {
			List<String> defList = (List<String>) dao.executeQuery(sql, new ColumnListProcessor());
			if (defList == null || defList.size() == 0) {// 全部为集团级流程定义
				// 查看优先级最高的集团级流程
				return tryGetHighestPriorDef(defPkList, matchError);
			} else if (defList.size() == 1) { // 普通组织级别下只有一条流程定义
				return defList.get(0);
			} else if (defList.size() > 1) {// 同一级为组织下有多条流程定义
				//matchError.getConflictDefList().addAll(defList);
				//matchError.setConflictLayer(WFDefMatchErrorInfo.LAYER_ORG);
				Logger.error("In line 369 of EngineService, matching to multiple processes: "+ defList);
				Logger.error("位于EngineService中369行，匹配到多个流程："+ defList);
				return defList.get(0);
			}
		} catch (DAOException e) {
			Logger.error(e.getMessage(), e);
		}
		return null;
	}

	/**
	 * 尝试获取优先级最高的唯一流程定义PK,存在返回Pk，否则返回null
	 * 
	 * @param defPKList
	 * @param errorStack 记录查询到多条时错误信息记录
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private String tryGetHighestPriorDef(List<String> defPkList, WFDefMatchErrorInfo matchError) {
		if (defPkList == null || defPkList.size() == 0) {
			return null;
		}
		StringBuffer sb = new StringBuffer();
		for (String defPK : defPkList) {
			sb.append(",'" + defPK + "'");
		}
		String defPKWhere = sb.toString().substring(1);
		String sql = "select pk_wf_def from pub_wf_def,org_orgs where" + " pub_wf_def.pk_org = org_orgs.pk_org "
				+ " and pk_wf_def in(" + defPKWhere
				+ ") and priority in (select MAX(priority) from pub_wf_def where pk_wf_def in (" + defPKWhere + "))";
		BaseDAO baseDao = new BaseDAO();
		List<String> defList = null;
		try {
			defList = (List<String>) baseDao.executeQuery(sql, new ColumnListProcessor());
			if (defList != null && defList.size() == 1) {
				return defList.get(0);
			} else if (defList != null && defList.size() > 1) {
				matchError.getConflictDefList().addAll(defList);
				matchError.setConflictLayer(WFDefMatchErrorInfo.LAYER_GROUP);
			}
		} catch (DAOException e) {
			Logger.error(e.getMessage(), e);
		}
		return null;
	}

	/**
	 * 查找某操作员当前可启动的流程定义
	 * <li>查找顺序：用户、用户组、角色、角色组、动态组织单元
	 * 
	 * @param billOrTranstype        单据或交易类型
	 * @param pkMatchOrg             匹配审批流用的组织
	 * @param pkBillOrg              实际单据上的组织
	 * @param billmakerId            制单人PK
	 * @param iWorkflowOrApproveflow 主流程类型，见<code>WorkflowTypeEnum.Approveflow</code>和
	 *                               <code>WorkflowTypeEnum.Workflow</code>
	 * 
	 * @param emendEnum              是否修订审批流
	 * @return
	 * @throws BusinessException
	 */
	private WorkflowProcess queryWfProcessByUser2(final String billOrTranstype, final String pk_group,
			final String pkMatchOrg, final String pkBillOrg, String billmakerId, int emendEnum,
			final int iWorkflowOrApproveflow) throws BusinessException {
		// 构造SQL，只查询有效的流程定义
		PersistenceManager persist = null;
		try {
			// 首先判断在组织上是否定义了可用流程，如果没有，直接返回
			if ((!StringUtil.isEmptyWithTrim(pkMatchOrg)
					&& !isHasProcessDefofOrg(pkMatchOrg, iWorkflowOrApproveflow))) {
				return null;
			}
			// 判断在billOrTranstype上是否定义了可用流程，如果没有，直接返回
			if (!isHasProcessDefofBillOrTrans(billOrTranstype, iWorkflowOrApproveflow)) {
				return null;
			}

			// 1.查找用户在某组织单元下是否定义了流程定义
			persist = PersistenceManager.getInstance();
			final JdbcSession jdbc = persist.getJdbcSession();
			ArrayList<String> userids = new ArrayList<String>();
			userids.add(billmakerId);
			String defPk = batchQueryDefPK(userids, jdbc, billOrTranstype, pk_group, pkMatchOrg, emendEnum,
					iWorkflowOrApproveflow, OrganizeUnitTypes.Operator.toString());
			if (!StringUtil.isEmptyWithTrim(defPk)) {
				WorkflowProcess wfProcess = findParsedWfProcess(defPk, null);
				Logger.debug(">>找到用户在该组织的流程定义，返回");
				return wfProcess;
			}

			// 2.查询该用户所属的用户组 是否在某个组织单元下有流程定义
			OrganizeUnit[] usergroupUnits = new PFOrgUnitImpl().findUserGroupUnitsByUser(billmakerId);
			ArrayList<String> alUserGroupPKs = new ArrayList<String>();
			for (int i = 0; i < (usergroupUnits == null ? 0 : usergroupUnits.length); i++) {
				alUserGroupPKs.add(usergroupUnits[i].getPk());
			}

			defPk = batchQueryDefPK(alUserGroupPKs, jdbc, billOrTranstype, pk_group, pkMatchOrg, emendEnum,
					iWorkflowOrApproveflow, OrganizeUnitTypes.UserGroup.toString());
			if (!StringUtil.isEmptyWithTrim(defPk)) {
				WorkflowProcess wfProcess = findParsedWfProcess(defPk, null);
				Logger.debug(">>找到用户所属用户组在该组织的流程定义，返回");
				return wfProcess;
			}

			// 3.查询该用户委派的所有角色 是否在某个组织单元下有流程定义
			OrganizeUnit[] roleUnits = new PFOrgUnitImpl().findRoleUnitsByUser(billmakerId);
			ArrayList<String> alRolePKs = new ArrayList<String>();
			for (int i = 0; i < (roleUnits == null ? 0 : roleUnits.length); i++) {
				alRolePKs.add(roleUnits[i].getPk());
			}

			defPk = batchQueryDefPK(alRolePKs, jdbc, billOrTranstype, pk_group, pkMatchOrg, emendEnum,
					iWorkflowOrApproveflow, OrganizeUnitTypes.Role.toString());
			if (!StringUtil.isEmptyWithTrim(defPk)) {
				WorkflowProcess wfProcess = findParsedWfProcess(defPk, null);
				Logger.debug(">>找到用户所属角色在该组织的流程定义，返回");
				return wfProcess;
			}

			// 4.查询该用户所属的角色组 是否在某个组织单元下有流程定义
			OrganizeUnit[] rolegroupUnits = new PFOrgUnitImpl().findRoleGroupUnitsByUser(billmakerId);
			ArrayList<String> alRoleGroupPKs = new ArrayList<String>();
			for (int i = 0; i < (rolegroupUnits == null ? 0 : rolegroupUnits.length); i++) {
				alRoleGroupPKs.add(rolegroupUnits[i].getPk());
			}

			defPk = batchQueryDefPK(alRoleGroupPKs, jdbc, billOrTranstype, pk_group, pkMatchOrg, emendEnum,
					iWorkflowOrApproveflow, OrganizeUnitTypes.RoleGroup.toString());
			if (!StringUtil.isEmptyWithTrim(defPk)) {
				WorkflowProcess wfProcess = findParsedWfProcess(defPk, null);
				Logger.debug(">>找到用户所属角色组在该组织的流程定义，返回");
				return wfProcess;
			}
			// 5.获取该用户所属的职责是否在某个组织单元下有流程定义。此时职责的组织限定为指定组织，指定的组织是单据组织
			ResponsibilityVO[] responsibilityvos = NCLocator.getInstance().lookup(IUserPubService.class)
					.getRespsRelatedWithUserAndPermOrgs(billmakerId, new String[] { pk_group, pkBillOrg });
			ArrayList<String> alResponisibilityPKs = new ArrayList<String>();
			for (int i = 0; i < (responsibilityvos == null ? 0 : responsibilityvos.length); i++) {
				alResponisibilityPKs.add(responsibilityvos[i].getPrimaryKey());
			}
			defPk = batchQueryDefPK(alResponisibilityPKs, jdbc, billOrTranstype, pk_group, pkMatchOrg, emendEnum,
					iWorkflowOrApproveflow, OrganizeUnitTypes.RESPONSIBILITY.toString());
			if (!StringUtil.isEmptyWithTrim(defPk)) {
				WorkflowProcess wfProcess = findParsedWfProcess(defPk, null);
				Logger.debug(">>找到用户所属职责在该组织的流程定义，返回");
				return wfProcess;
			}

			// 6.获取该用户所属的动态组织，暂时只有岗位、动态组
			// 以及"所有制单人(AllBillMaker)"
			DynamicOrganizeUnit[] dous = new PFOrgUnitImpl().findDynamicOrgUnitsByUser(pkBillOrg, billmakerId);
			ArrayList<String> unitPks = new ArrayList<String>();
			for (int i = 0; i < (dous == null ? 0 : dous.length); i++) {
				// 遍历这些动态组织，是否在该组织有流程定义
				unitPks.add(dous[i].getId());
				defPk = batchQueryDefPK(unitPks, jdbc, billOrTranstype, pk_group, pkMatchOrg, emendEnum,
						iWorkflowOrApproveflow, dous[i].getType());
				if (!StringUtil.isEmptyWithTrim(defPk)) {
					WorkflowProcess wfProcess = findParsedWfProcess(defPk, null);
					Logger.debug(">>找到用户所属动态组织在该组织的流程定义，返回");
					return wfProcess;
				}
			}
		} catch (DbException e) {
			Logger.error(e.getMessage(), e);
			throw new PFBusinessException(
					NCLangResOnserver.getInstance().getStrByID("pfworkflow", "UPPpfworkflow-000006")/*
																									 * 查询流程定义出现数据库异常 ：
																									 */
							+ e.getMessage(),
					e);
		} finally {
			if (persist != null)
				persist.release();
		}
		return null;
	}

	/**
	 * 流程匹配时冲突信息记录
	 * 
	 * @author zhouwba
	 * 
	 */
	public static class WFDefMatchErrorInfo {
		public static final int LAYER_GROUP = 0;// 集团级存在多条流程定义记录
		public static final int LAYER_ORG = 1;// 组织级存在多条流程定义记录
		private List<String> conflictDefList = new ArrayList<String>();// 冲突的流程定义主键列表
		private int conflictLayer = LAYER_GROUP;// 冲突的流程时组织级还是集团下的

		public List<String> getConflictDefList() {
			return conflictDefList;
		}

		public int getConflictLayer() {
			return conflictLayer;
		}

		public void setConflictDefList(List<String> conflictDefList) {
			this.conflictDefList = conflictDefList;
		}

		public void setConflictLayer(int conflictLayer) {
			this.conflictLayer = conflictLayer;
		}
	}

	/**
	 * @since 6.3 判断该单据类型或交易类型上是否定义了流程
	 */
	private boolean isHasProcessDefofBillOrTrans(String billorTransType, int iWorkflowOrApproveflow) {
		String sql = "select pk_wf_def from pub_wf_def where workflow_type =? and billtype =?";
		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();
			SQLParameter para = new SQLParameter();
			para.addParam(iWorkflowOrApproveflow);
			para.addParam(billorTransType);
			Object obj = jdbc.executeQuery(sql, para, new ColumnProcessor(1));
			return obj == null ? false : true;
		} catch (DbException e) {
			Logger.error(e.getMessage());
		} finally {
			if (persist != null)
				persist.release();
		}
		return false;

	}

	/**
	 * @since 6.3 判断组织上是否定义了流程
	 */
	private boolean isHasProcessDefofOrg(String org, int iWorkflowOrApproveflow) {

		String sql = "select pk_wf_def from pub_wf_def where workflow_type =? and exists (select 1 from pub_wf_def_org wdo where wdo.pk_wf_def = pub_wf_def.pk_wf_def and wdo.pk_org = ?)";
		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();
			SQLParameter para = new SQLParameter();
			para.addParam(iWorkflowOrApproveflow);
			para.addParam(org);
			Object obj = jdbc.executeQuery(sql, para, new ColumnProcessor(1));
			return obj == null ? false : true;
		} catch (Exception e) {
			Logger.error(e.getMessage());
		} finally {
			if (persist != null)
				persist.release();
		}
		return false;

	}

	/**
	 * 判断该过程定义是否被实例化过
	 * 
	 * @param proc_defPK 过程定义PK
	 * @return
	 */
	public String hasProcessInstancesOfDefPK(String proc_defPK) throws DbException {
		String sql = "select pk_wf_instance from pub_wf_instance where processdefid =? ";
		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();
			SQLParameter para = new SQLParameter();
			para.addParam(proc_defPK);
			Object obj = jdbc.executeQuery(sql, para, new ColumnProcessor(1));
			return obj == null ? null : String.valueOf(obj);
		} finally {
			if (persist != null)
				persist.release();
		}
	}

	/**
	 * @param proc_defPK
	 * @return
	 * @throws DbException
	 */
	public String hasOngoingProcessInstancesOfDefPK(String proc_defPK) throws DbException {
		String sql = "select pk_wf_instance from pub_wf_instance where processdefid =? and procstatus  != 1 and isnull(procresult,'~')='~'";
		PersistenceManager persist = null;//
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();
			SQLParameter para = new SQLParameter();
			para.addParam(proc_defPK);
			Object obj = jdbc.executeQuery(sql, para, new ColumnProcessor(1));
			return obj == null ? null : String.valueOf(obj);
		} finally {
			if (persist != null)
				persist.release();
		}
	}

	/**
	 * 判断该过程定义是否有正运行中的实例
	 * 
	 * @param proc_defPK 过程定义PK
	 * @param billId
	 * @return
	 * @throws DbException
	 */
	public String hasRunningProcessInstances(String proc_defPK, String billId) throws DbException {
		String sql = "select pk_wf_instance from pub_wf_instance where processdefid =? and procstatus=? and billversionpk=?";
		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();
			SQLParameter para = new SQLParameter();
			para.addParam(proc_defPK);
			para.addParam(WfTaskOrInstanceStatus.Started.getIntValue());
			para.addParam(billId);

			Object obj = jdbc.executeQuery(sql, para, new ColumnProcessor(1));
			return obj == null ? null : String.valueOf(obj);
		} finally {
			if (persist != null)
				persist.release();
		}
	}

	/**
	 * 单据回到自由态后将所有活动实例置为无效，包括工作项和任务
	 * 
	 * @param billid
	 * @param billType
	 * @param iWfType
	 * @throws DbException
	 * @throws BusinessException
	 */
	public void updateAllActivityInstancesToInefficient(String billid, String billType, int iWfType)
			throws DbException, BusinessException {
		String sqlProcInstance = "select pk_wf_instance from pub_wf_instance where billversionpk=? and billtype=? and workflow_type in"
				+ WorkflowTypeEnum.getInstanceTypeInSql(iWfType);
		String sqlTaskQry = "select pk_wf_task from pub_wf_task where pk_wf_instance in (" + sqlProcInstance
				+ ") and tasktype!=" + WfTaskType.Makebill.getIntValue();

		// // 工作项置为无效
		// String sqlWorkitem =
		// "update pub_workflownote set ischeck='X',approvestatus="
		// + WfTaskOrInstanceStatus.Inefficient.getIntValue()
		// +
		// " where pk_wf_task in(select pk_wf_task from pub_wf_task where pk_wf_instance
		// in ("
		// + sqlProcInstance + ") and tasktype!=" +
		// WfTaskType.Makebill.getIntValue() + ")";
		// 任务置为无效
		String sqlTask = "update pub_wf_task  set taskstatus =" + WfTaskOrInstanceStatus.Inefficient.getIntValue()
				+ " where pk_wf_instance in (" + sqlProcInstance + ") and tasktype!="
				+ WfTaskType.Makebill.getIntValue();
		// 活动实例置为无效 XXX:leijun+2008-9
		String sqlActInstance = "update pub_wf_actinstance set actstatus="
				+ WfTaskOrInstanceStatus.Inefficient.getIntValue()
				+ " where pk_wf_actinstance in(select pk_wf_actinstance from pub_wf_task where pk_wf_instance in ("
				+ sqlProcInstance + ") and tasktype!=" + WfTaskType.Makebill.getIntValue() + ")";
		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();
			SQLParameter para = new SQLParameter();
			para.addParam(billid);
			para.addParam(billType);
			ArrayList<String> taskPKs = (ArrayList<String>) jdbc.executeQuery(sqlTaskQry, para,
					new ColumnListProcessor());
			TaskManagerDMO taskDMO = new TaskManagerDMO();
			taskDMO.inefficientWorkitemsByTaskPKs(taskPKs);

			jdbc.executeUpdate(sqlTask, para);
			jdbc.executeUpdate(sqlActInstance, para);
		} finally {
			if (persist != null)
				persist.release();
		}

	}

	/**
	 * 流程结束后将所有未完成的活动实例置为无效，包括工作项和任务
	 * 
	 * @param billid
	 * @param billType
	 * @param iWfType
	 * @throws DbException
	 * @throws BusinessException
	 */
	public void updateUnfinishedActInstancesToInefficient(String billid, String billType, int iWfType)
			throws DbException, BusinessException {
		String sqlProcInstance = "select pk_wf_instance from pub_wf_instance where billversionpk=? and billtype=? and workflow_type in"
				+ WorkflowTypeEnum.getInstanceTypeInSql(iWfType);
		// 未完成的任务
		String sqlTask = "select pk_wf_task from pub_wf_task where pk_wf_instance in (" + sqlProcInstance
				+ ") and taskstatus!=" + WfTaskOrInstanceStatus.Finished.getIntValue() + " and tasktype!="
				+ WfTaskType.Makebill.getIntValue();
		// // 未完成的工作项无效
		// String sqlWorkitem =
		// "update pub_workflownote set ischeck='X',approvestatus="
		// + WfTaskOrInstanceStatus.Inefficient.getIntValue()
		// +
		// " where pk_wf_task in(select pk_wf_task from pub_wf_task where pk_wf_instance
		// in ("
		// + sqlProcInstance + ") and taskstatus!=" +
		// WfTaskOrInstanceStatus.Finished.getIntValue()
		// + " and tasktype!=" + WfTaskType.Makebill.getIntValue() + ")";
		// 未完成的任务无效
		String sqlTaskUpdate = "update pub_wf_task  set taskstatus =" + WfTaskOrInstanceStatus.Inefficient.getIntValue()
				+ " where pk_wf_task in (" + sqlProcInstance + ") and taskstatus!="
				+ WfTaskOrInstanceStatus.Finished.getIntValue() + " and tasktype!=" + WfTaskType.Makebill.getIntValue();
		// 未完成的活动实例无效 leijun+2008-9
		String sqlActInstance = "update pub_wf_actinstance set actstatus="
				+ WfTaskOrInstanceStatus.Inefficient.getIntValue() + " where pk_wf_instance in (" + sqlProcInstance
				+ ") and actstatus!=" + WfTaskOrInstanceStatus.Finished.getIntValue();
		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();
			SQLParameter para = new SQLParameter();
			para.addParam(billid);
			para.addParam(billType);
			ArrayList<String> taskPKs = (ArrayList<String>) jdbc.executeQuery(sqlTask, para, new ColumnListProcessor());
			TaskManagerDMO taskDMO = new TaskManagerDMO();
			taskDMO.inefficientWorkitemsByTaskPKs(taskPKs);

			jdbc.executeUpdate(sqlTaskUpdate, para);
			jdbc.executeUpdate(sqlActInstance, para);
		} finally {
			if (persist != null)
				persist.release();
		}
	}

	/**
	 * @param iWorkflowOrApproveflow 主流程类型，见<code>WorkflowTypeEnum.Approveflow</code>和
	 *                               <code>WorkflowTypeEnum.Workflow</code>
	 * @param tableAlias             实例表的别名
	 * @return
	 */
	public String getWorkflowTypeAndWhere(int iWorkflowOrApproveflow, String tableAlias) {
		String strWfTypeSql = "";
		if (StringUtil.isEmptyWithTrim(tableAlias))
			tableAlias = "";

		strWfTypeSql = " and " + tableAlias + "workflow_type in"
				+ WorkflowTypeEnum.getInstanceTypeInSql(iWorkflowOrApproveflow);

		return strWfTypeSql;
	}

	/**
	 * 查询某人对某个单据 尚未处理的工作项
	 * 
	 * @param billType
	 * @param billId
	 * @param userPK
	 * @param iWorkflowOrApproveflow 主流程类型，见<code>WorkflowTypeEnum.Approveflow</code>和
	 *                               <code>WorkflowTypeEnum.Workflow</code>
	 * @return
	 * @throws DAOException
	 */
	public WorkflownoteVO[] queryWorkitemsNotCheck(String billType, String billId, String userPK,
			int iWorkflowOrApproveflow) throws DAOException {

		String where = "pk_billtype=? and billversionpk=? and checkman=? and ismsgbind='N' "
				+ getWorkflowTypeAndWhere(iWorkflowOrApproveflow, "") + " and actiontype<>'"
				+ WorkflownoteVO.WORKITEM_TYPE_BIZ + "' and approvestatus in("
				+ WfTaskOrInstanceStatus.getUnfinishedStatusSet() + ") order by senddate";

		SQLParameter para = new SQLParameter();
		para.addParam(billType);
		para.addParam(billId);
		para.addParam(userPK);
		Collection<WorkflownoteVO> co = new BaseDAO().retrieveByClause(WorkflownoteVO.class, where, para);
		return co.toArray(new WorkflownoteVO[0]);

	}
	

	/**
	 * 根据pk_checkflow查询某人尚未处理的 工作项
	 * 
	 * @param billType
	 * @param billId
	 * @param userPK
	 * @param iWorkflowOrApproveflow 主流程类型，见<code>WorkflowTypeEnum.Approveflow</code>和
	 *                               <code>WorkflowTypeEnum.Workflow</code>
	 * @return
	 * @throws DAOException
	 */
	public WorkflownoteVO[] queryWorkNotCheckByPkCheckFlow(String pk_checkflow, String userPK,
			int iWorkflowOrApproveflow) throws DAOException {

		String where = "pk_checkflow=? and checkman=? and ismsgbind='N' "
				+ getWorkflowTypeAndWhere(iWorkflowOrApproveflow, "") + " and actiontype<>'"
				+ WorkflownoteVO.WORKITEM_TYPE_BIZ + "' and approvestatus in("
				+ WfTaskOrInstanceStatus.getUnfinishedStatusSet() + ")";
		SQLParameter para = new SQLParameter();
		para.addParam(pk_checkflow);
		para.addParam(userPK);
		Collection<WorkflownoteVO> co = new BaseDAO().retrieveByClause(WorkflownoteVO.class, where, para);
		return co.toArray(new WorkflownoteVO[0]);

	}

	/**
	 * 查询某人对某个单据 尚未处理的工作项，忽略流程类型
	 * 
	 * @param billType
	 * @param billId
	 * @param userPK
	 * @return
	 * @throws DAOException
	 */
	 public WorkflownoteVO[] queryWorkitemsNotCheck(String billType, String billId, String userPK) throws DAOException {
			String where = "pk_billtype=? and billversionpk=? and checkman=? and ismsgbind='N' and actiontype<>'"
					+ WorkflownoteVO.WORKITEM_TYPE_BIZ + "' and approvestatus in("
					+ WfTaskOrInstanceStatus.getUnfinishedStatusSet() + ") order by senddate";

			SQLParameter para = new SQLParameter();
			para.addParam(billType);
			para.addParam(billId);
			para.addParam(userPK);
			Collection<WorkflownoteVO> co = new BaseDAO().retrieveByClause(WorkflownoteVO.class, where, para);
			return co.toArray(new WorkflownoteVO[0]);
		 }
	
	/**
	 * 查询某人对某个单据 尚未处理的工作项，忽略流程类型 去掉'MAKEBILL'
	 * 
	 * @param billType
	 * @param billId
	 * @param userPK
	 * @return
	 * @throws DAOException
	 */
	public WorkflownoteVO[] queryWorkitemsNotCheckNoMAKEBILL(String billType, String billId, String userPK) throws DAOException {

		  String sql = " select REJECTTACHEACTIVITYID  from pub_wf_task   where pk_wf_task in ( select pk_wf_task from pub_workflownote  where pk_billtype=? and billversionpk=? and checkman=? and ismsgbind='N' and actiontype<>'"
				    + WorkflownoteVO.WORKITEM_TYPE_BIZ + "' and approvestatus in("
				    + WfTaskOrInstanceStatus.getUnfinishedStatusSet() + ") )";

				  PersistenceManager persist = null;
				  try {
				   persist = PersistenceManager.getInstance();
				   JdbcSession jdbc = persist.getJdbcSession();
				   SQLParameter para = new SQLParameter();
				   para.addParam(billType);
				   para.addParam(billId);
				   para.addParam(userPK);
				   Object rejectactid = jdbc.executeQuery(sql, para, new ColumnProcessor(1));
				   if(rejectactid==null) {
				    return null;
				   }
				  } catch (DbException e) {
				   throw new DAOException(e);
				  } finally {
				   if (persist != null)
				    persist.release();
				  }
				  return new WorkflownoteVO[] {new WorkflownoteVO()};
	}

	/**
	 * 查询某人对某个单据 尚未处理的工作项，忽略流程类型
	 * 
	 * @param billType
	 * @param billId
	 * @param userPK
	 * @return
	 * @throws DAOException
	 */
	public WorkflownoteVO queryWorkitemsNotCheckForMsg(String billType, String billId, String userPK,
			PfParameterVO paraVo, int iWorkflowOrApproveflow) throws BusinessException, DbException {
		// 查询某用户对某单据的所有待办工作项
		WorkflownoteVO[] uncheckedNoteVOs = queryWorkitemsNotCheck(paraVo.m_billType, paraVo.m_billVersionPK, userPK,
				iWorkflowOrApproveflow);
		if (uncheckedNoteVOs != null && uncheckedNoteVOs.length > 0) {
			// 当前操作员有待办工作项
			WorkflownoteVO noteVO = uncheckedNoteVOs[uncheckedNoteVOs.length - 1];// FIXME:某用户对某个单据可能同时有两个待办工作项吗？
			TaskManagerDMO taskDmo = new TaskManagerDMO();
			WFTask currentTask = taskDmo.getTaskByPK(noteVO.getPk_wf_task());
			if (currentTask == null) {
				throw new PFBusinessException(NCLangResOnserver.getInstance().getStrByID("pfworkflow",
						"UPPpfworkflow-000262")/* 当前操作人待办任务丢失 */);
			} else if ((currentTask.getTaskType() == WfTaskType.Makebill.getIntValue()
					&& currentTask.getRejectTacheActivityID() == null)
					|| currentTask.getTaskType() == WfTaskType.Withdraw.getIntValue())
				throw new PFBusinessException(
						NCLangResOnserver.getInstance().getStrByID("pfworkflow", "UPPpfworkflow-000388")/*
																										 * 没有待办任务。
																										 */);

			// 校验流程实例的状态
			if (hasRunningProcessInstances(currentTask.getWfProcessDefPK(), paraVo.m_billVersionPK) == null) {
				throw new PFBusinessException(NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow",
						"EngineService-0004")/*
												 * 该单据所处的流程实例已被挂起 ！
												 */);
			}
			WorkflowProcess wp = findParsedWfProcess(currentTask.getWfProcessDefPK(),
					currentTask.getWfProcessInstancePK());
			if (wp == null)
				return null; // throw new Exception("没有对应的流程定义");

			currentTask.setBillNO(paraVo.m_billNo);
			currentTask.setBillID(paraVo.m_billId);
			currentTask.setBillversionPK(paraVo.m_billVersionPK);
			// currentTask.setBusiType(paraVo.m_businessType);
			currentTask.setBillType(paraVo.m_billType);
			currentTask.setPk_org(paraVo.m_pkOrg);
			currentTask.setTopic(noteVO.getMessagenote());

			currentTask.setWorkflowType(noteVO.getWorkflow_type());
			// noteVO.setSenddate(new UFDateTime(System.currentTimeMillis()));
			currentTask.setWorknoteVO(noteVO);

			// yanke1 2013-4-18
			// 修改前这里的代码为 currentTask.setSenderman(paraVo.m_operator);
			// paraVo.m_operator通常为当前登录人
			// 使用它作为currentTask的senderMan是不准确的
			// 应当使用noteVO的senderMan
			currentTask.setSenderman(noteVO.getSenderman());

			// 获取该活动后继活动的可指派信息
			WfTaskInfo taskInfo = createTaskInfo(currentTask, wp, paraVo.m_preValueVo);
			noteVO.setTaskInfo(taskInfo);

			// 获得活动的一些扩展属性
			noteVO.getRelaProperties()
					.putAll(findRelaProps(wp.findActivityByID(currentTask.getActivityID()), paraVo.m_billType));

			return noteVO;
		} else {
			// 当前操作员无待办工作项
			HashSet<String> hsRet = hasInstanceOfValid(new String[] { paraVo.m_billVersionPK }, paraVo.m_billType,
					iWorkflowOrApproveflow);
			boolean startup = hsRet.contains(paraVo.m_billVersionPK);
			if (startup) {
				// 该单据有流程实例
				throw new PFBusinessException(
						NCLangResOnserver.getInstance().getStrByID("pfworkflow", "UPPpfworkflow-000263")/*
																										 * 当前操作人没有待办任务 .
																										 */);
			} else {
				// 该单据无流程实例()
				WorkflowProcess wp = null;
				if (StringUtil.isEmptyWithTrim(paraVo.m_flowDefPK))
					wp = queryWfProcessCanStart2(paraVo.m_billType, paraVo.m_pkGroup, paraVo.m_pkOrg,
							/**
							 * 注意此处要按制单人检查，按操作人检查会出现， 新增未提交单据直接点审批时
							 * ，不同人的可能会匹配到流程，也可能匹配不到。而且从流程的含义上将也应该是根据制单人启动流程
							 */
							paraVo.m_makeBillOperator, paraVo.emendEnum, iWorkflowOrApproveflow);
				else
					wp = findParsedWfProcess(paraVo.m_flowDefPK, null);
				if (wp == null) {
					// 该制单人无流程定义，任何人都可审批或执行
					return null;
				} else {
					throw new PFBusinessException(NCLangResOnserver.getInstance().getStrByID("pfworkflow",
							"UPPpfworkflow-000389")/*
													 * 该单据有可启动的流程但未启动，无法流转 。
													 */);
				}
			}
		}

	}

	/**
	 * 查询某人对某个单据的已审工作项
	 * 
	 * @param billType
	 * @param billId
	 * @param userPK
	 * @param iWorkflowOrApproveflow
	 * @return
	 * @throws DAOException
	 */
	public WorkflownoteVO[] queryWorkitemsChecked(String billType, String billId, String userPK,
			int iWorkflowOrApproveflow) throws DAOException {

		// String where =
		// "pk_billtype=? and billid=? and checkman=? and senderman<>? " //wcj+
		// Senderman条件，排除自动审批的问题
		// String where =
		// "pk_billtype=? and billid=? and checkman=? and messagenote not like
		// '##Auto##%' "
		// //xry+
		// Senderman方式判断，不能处理b签收，b审批的，b再弃审找不到工作项，排除自动审批的方法有问题，下一版本改为标志位方式???
		String where = "pk_billtype=? and billversionpk=? and checkman=? " // yanke1+
																			// 2011-9-7
																			// 若此处将自动审批的消息过滤掉，会造成a-b-bc中bc环节的b无法弃审，因此取消过滤自动审批
				+ getWorkflowTypeAndWhere(iWorkflowOrApproveflow, "") + " and approvestatus="
				+ WfTaskOrInstanceStatus.Finished.getIntValue()
				+ " and approveresult is not null and actiontype like 'Z%' " + "order by dealdate desc, senddate desc"; // yanke1+
																														// 此处首先按dealdate降序，再按senddate降序
																														// 以便将最新审批过的工作项放在最前面
		SQLParameter para = new SQLParameter();
		para.addParam(billType);
		para.addParam(billId);
		para.addParam(userPK);
		Collection<WorkflownoteVO> co = new BaseDAO().retrieveByClause(WorkflownoteVO.class, where, para);
		return co.toArray(new WorkflownoteVO[0]);
	}

	/**
	 * 查询某人已审工作项，自动审批也包括(zhouwba)
	 */
	public WorkflownoteVO[] queryWorkitemsCheckedIncludeAutoComplete(String billType, String billId, String userPK,
			int iWorkflowOrApproveflow) throws DAOException {
		/**
		 * @since 6.3 增加ts降序：用于避免流程中有多个自动环节，根据dealdate和senddate无法将最后一个活动排在最前面。从而导致弃审失败 。
		 */
		String sql = "select * from pub_workflownote where pk_billtype=? and billversionpk=? "
				+ getWorkflowTypeAndWhere(iWorkflowOrApproveflow, "") + " and approvestatus="
				+ WfTaskOrInstanceStatus.Finished.getIntValue()
				+ " and approveresult is not null and actiontype like 'Z%' "
				+ "order by dealdate desc, senddate desc, ts desc,dealtimemillis desc";
		;
		SQLParameter para = new SQLParameter();
		para.addParam(billType);
		para.addParam(billId);
		// para.addParam(userPK);
		@SuppressWarnings("unchecked")
		Collection<WorkflownoteVO> co = (Collection<WorkflownoteVO>) new BaseDAO().executeQuery(sql, para,
				new BeanListProcessor(WorkflownoteVO.class));

		/********** 解决审批流包含子审批流是，流程走完后，子审批流还能取消审批的问题 *********/
		UFDateTime endtime = null;
		WorkflownoteVO resultvo = null;
		for (WorkflownoteVO vo : co.toArray(new WorkflownoteVO[co.size()])) {
			if (null != endtime) {
				if (endtime.before(vo.getDealdate())) {
					resultvo = vo;
				}

			} else {
				endtime = vo.getDealdate();
				resultvo = vo;

			}

		}
		/*******************/
		return resultvo == null ? null : new WorkflownoteVO[] { resultvo };
	}

	/**
	 * 查询某人已审工作项，自动审批除外
	 */
	public WorkflownoteVO[] queryWorkitemsCheckedExcludeAutoComplete(String billType, String billId, String userPK,
			int iWorkflowOrApproveflow) throws DAOException {
		/**
		 * @since 6.3 增加ts降序：用于避免流程中有多个自动环节，根据dealdate和senddate无法将最后一个活动排在最前面。从而导致弃审失败 。
		 */
		String sql = "select a.* from pub_workflownote a, pub_wf_task b where a.pk_wf_task =b.pk_wf_task and b.isautocomplete=? "
				+ "and pk_billtype=? and billversionpk=? and checkman=? "
				+ getWorkflowTypeAndWhere(iWorkflowOrApproveflow, "") + " and approvestatus="
				+ WfTaskOrInstanceStatus.Finished.getIntValue()
				+ " and approveresult is not null and actiontype like 'Z%' "
				+ "  and a.messagenote not like'%autoApproved%' " + "order by dealdate desc, senddate desc, a.ts desc";

		SQLParameter para = new SQLParameter();
		para.addParam(UFBoolean.FALSE);
		para.addParam(billType);
		para.addParam(billId);
		para.addParam(userPK);
		Collection<WorkflownoteVO> co = (Collection<WorkflownoteVO>) new BaseDAO().executeQuery(sql, para,
				new BeanListProcessor(WorkflownoteVO.class));
		return co.toArray(new WorkflownoteVO[0]);
	}

	/**
	 * 从pub_function表获取单据函数信息
	 * 
	 * @param expression
	 */
	public NCFunctionConditionVO queryFunctionCondition(NCFunctionXpression expression) throws DbException {
		String sql2 = "select classname,returntype,functionnote,methodname,arguments from pub_function "
				+ "where pk_function=?";
		NCFunctionConditionVO conditionVO = null;

		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();

			// 左值
			SQLParameter para = new SQLParameter();
			para.addParam(expression.getFunctionId());
			Object objLeft = jdbc.executeQuery(sql2, para, new ArrayProcessor());
			if (objLeft != null) {
				Object[] leftAry = (Object[]) objLeft;
				conditionVO = new NCFunctionConditionVO();
				conditionVO.setLeftFunClassName(String.valueOf(leftAry[0]));
				conditionVO.setLeftFunReturnType(String.valueOf(leftAry[1]));
				conditionVO.setLeftFunNote(String.valueOf(leftAry[2]));
				conditionVO.setLeftMethod(String.valueOf(leftAry[3]));
				conditionVO.setLeftParameter(String.valueOf(leftAry[4]));
			}

			// 右值
			SQLParameter paraRight = new SQLParameter();
			paraRight.addParam(expression.getValue());
			Object objRight = jdbc.executeQuery(sql2, paraRight, new ArrayProcessor());
			if (objRight != null) {
				Object[] rightAry = (Object[]) objRight;
				conditionVO.setRightFunClassName(String.valueOf(rightAry[0]));
				conditionVO.setRightFunNote(String.valueOf(rightAry[2]));
				conditionVO.setRightMethod(String.valueOf(rightAry[3]));
				conditionVO.setRightParameter(String.valueOf(rightAry[4]));
				conditionVO.setRightFunReturnType(String.valueOf(rightAry[1]));
			}

			conditionVO.setYsf(expression.getYsf());
			conditionVO.setValue(expression.getValue());
			return conditionVO;
		} finally {
			if (persist != null)
				persist.release();
		}
	}

	/**
	 * 从pub_function表获取单据函数信息
	 * 
	 * @param expression
	 */
	public NCFunctionConditionVO queryFunctionCondition4CustFunc(NCFunctionXpression expression) throws DbException {
		String sql2 = "select classname,returntype,functionnote,methodname,arguments from pub_function "
				+ "where pk_function=?";
		NCFunctionConditionVO conditionVO = new NCFunctionConditionVO();

		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();

			// 右值（只当做右值使用）
			SQLParameter paraRight = new SQLParameter();
			paraRight.addParam(expression.getValue());
			Object objRight = jdbc.executeQuery(sql2, paraRight, new ArrayProcessor());
			if (objRight != null) {
				Object[] rightAry = (Object[]) objRight;
				conditionVO.setRightFunClassName(String.valueOf(rightAry[0]));
				conditionVO.setRightFunNote(String.valueOf(rightAry[2]));
				conditionVO.setRightMethod(String.valueOf(rightAry[3]));
				conditionVO.setRightParameter(String.valueOf(rightAry[4]));
				conditionVO.setRightFunReturnType(String.valueOf(rightAry[1]));
			}

			conditionVO.setYsf(expression.getYsf());
			conditionVO.setValue(expression.getValue());
			return conditionVO;
		} finally {
			if (persist != null)
				persist.release();
		}
	}

	/**
	 * 某单据是否存在一个已经处理完成的工作项
	 * 
	 * @param billId   单据Id
	 * @param billType 单据类型（或交易类型）PK
	 * @param iWftype
	 * @return
	 * @throws DbException
	 */
	public boolean isExistFinishedWorkitem(String billId, String billType, int iWftype) throws DbException {
		String sql1 = "select b.pk_wf_task from pub_workflownote a,pub_wf_task b"
				+ " where a.messagenote not like '%autoApproved%' and a.pk_wf_task=b.pk_wf_task and b.tasktype=" + WfTaskType.Forward.getIntValue()
				+ " and a.approvestatus=" + WfTaskOrInstanceStatus.Finished.getIntValue() + " and actiontype like 'Z%' "// 把签收消息刨除掉，当只剩下签收消息时，就假定没有已完成的工作项了。
				+ " and a.billversionpk=? and a.pk_billtype=?" + getWorkflowTypeAndWhere(iWftype, null);

		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();
			SQLParameter para = new SQLParameter();
			para.addParam(billId);
			para.addParam(billType);
			Object objTaskPK = jdbc.executeQuery(sql1, para, new ColumnProcessor(1));
			return objTaskPK == null ? false : true;
		} finally {
			if (persist != null)
				persist.release();
		}
	}

	/**
	 * 判断该单据是否存在一个审批结果非空的工作项
	 * 
	 * @param billID
	 * @return
	 */
	public boolean isPreserveWorkflownote(String billID) throws DbException {
		String sqlWorkflowNoe = "select billid from pub_workflownote where billversionpk=? and approveresult is not null";

		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();
			SQLParameter para = new SQLParameter();
			para.addParam(billID);
			Object objBillId = jdbc.executeQuery(sqlWorkflowNoe, para, new ColumnProcessor(1));
			return objBillId == null ? false : true;
		} finally {
			if (persist != null)
				persist.release();
		}

	}

	/**
	 * 判断单据ID数组是否存在审批流或工作流实例
	 * <li>处于有效状态的，即启动或完成状态
	 * 
	 * @param billIdAry
	 * @param billType
	 * @param iWorkflowOrApproveflow
	 * @return 存在流程实例的单据ID数组
	 */
	public HashSet<String> hasInstanceOfValid(String[] billIdAry, final String billType,
			final int iWorkflowOrApproveflow) throws BusinessException {
		final HashSet<String> hsRet = new HashSet<String>();
		InSqlBatchCaller caller = new InSqlBatchCaller(billIdAry);
		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			final JdbcSession jdbc = persist.getJdbcSession();
			caller.execute(new IInSqlBatchCallBack() {
				public Object doWithInSql(String inSql) throws BusinessException, SQLException {
					String sql = "select billversionpk from pub_wf_instance where billversionpk in " + inSql
					// +
					// " and (billtype=? or billtype=?) and procstatus!=?"
							+ " and procstatus!=?" + getWorkflowTypeAndWhere(iWorkflowOrApproveflow, "");
					SQLParameter para = new SQLParameter();
					// 传入的是单据类型，表里存的是交易类型
					// para.addParam(billType);
					// if(PfUtilBaseTools.isTranstype(billType)){
					// para.addParam(PfUtilBaseTools.getRealBilltype(billType));
					// }else
					// para.addParam(billType);
					para.addParam(WfTaskOrInstanceStatus.Inefficient.getIntValue());

					try {
						ArrayList alBillId = (ArrayList) jdbc.executeQuery(sql, para, new ColumnListProcessor(1));
						hsRet.addAll(alBillId);
					} catch (DbException e) {
						Logger.error(e.getMessage(), e);
						throw new PFBusinessException(
								NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow", "UPPpfworkflow-000060")/*
																													 * 查询流程实例表发生数据库异常：
																													 */
										+ e.getMessage(),
								e);
					}

					return null;
				}
			});
			return hsRet;
		} catch (DbException e) {
			Logger.error(e.getMessage(), e);
			throw new PFBusinessException(NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow",
					"UPPpfworkflow-000060")/*
											 * 查询流程实例表发生数据库异常 ：
											 */
					+ e.getMessage(), e);
		} catch (SQLException e) {
			Logger.error(e.getMessage(), e);
			throw new PFBusinessException(NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow",
					"UPPpfworkflow-000060")/*
											 * 查询流程实例表发生数据库异常 ：
											 */
					+ e.getMessage(), e);
		} finally {
			if (persist != null)
				persist.release();
		}

	}

	/**
	 * 查询出某个单据走的审批流或工作流定义对象
	 * 
	 * @param billId
	 * @param billType
	 * @param iWorkflowtype
	 * @return
	 * @throws DbException
	 * @throws BusinessException
	 */
	public WorkflowProcess findProcessOfBill(String billId, String billType, int iWorkflowtype)
			throws DbException, BusinessException {
		String sql = "select processdefid from pub_wf_instance where billversionpk=? "
				+ " and billtype=? and procstatus!=?" + getWorkflowTypeAndWhere(iWorkflowtype, "");
		SQLParameter para = new SQLParameter();
		para.addParam(billId);
		para.addParam(billType);
		para.addParam(WfTaskOrInstanceStatus.Inefficient.getIntValue());

		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();
			String strDefPK = (String) jdbc.executeQuery(sql, para, new ColumnProcessor(1));
			if (StringUtil.isEmptyWithTrim(strDefPK))
				return null;
			return findParsedWfProcess(strDefPK, null);
		} finally {
			if (persist != null)
				persist.release();
		}
	}

	/**
	 * 判断某操作员是否是当前单据的审核人（存在有效的审批任务）
	 * 
	 * @param billId   单据ID
	 * @param billType 单据类型PK
	 * @param userId   操作员ID
	 * @return
	 * @throws DbException
	 * @throws BusinessException
	 */
	public boolean isCheckman(String billId, String billType, String userId) throws DbException, BusinessException {
		HashSet<String> hsRet = hasInstanceOfValidByBillID(new String[] { billId }, billType,
				WorkflowTypeEnum.Approveflow.getIntValue());
		boolean bWorkfowStarted = hsRet.contains(billId);
		if (bWorkfowStarted) {
			// 审批流已经启动
			String sql = "select pk_wf_task from pub_workflownote "
					+ "where pk_wf_task !='~' and billversionpk=? and pk_billtype=? and checkman=? and approvestatus in("
					+ WfTaskOrInstanceStatus.getUnfinishedStatusSet() + ")";
			PersistenceManager persist = null;
			try {
				persist = PersistenceManager.getInstance();
				JdbcSession jdbc = persist.getJdbcSession();
				SQLParameter para = new SQLParameter();
				para.addParam(billId);
				para.addParam(billType);
				para.addParam(userId);
				Object obj = jdbc.executeQuery(sql, para, new ColumnProcessor(1));
				if (obj == null)
					// 操作员对该单据没有未完成的工作项，即没有轮到他审批
					return false;
			} finally {
				if (persist != null)
					persist.release();
			}
		}
		// 审批流没启动，则任何人都可直批
		return true;
	}

	/**
	 * 判断是否直接审核通过了？
	 * 
	 * @param billID
	 * @param billType
	 * @return
	 * @throws DbException
	 */
	private boolean isDirectlyCompleted(String billID, String billType) throws DbException {
		// 查询有无 处于完成状态的流程实例
		String sqlWorkflow = "select billid from pub_wf_instance where billversionpk=? and billtype=? and procstatus="
				+ WfTaskOrInstanceStatus.Finished.getIntValue() + " and  procresult is not null";
		// 查询有无 处于完成状态的工作项
		String sqlWorkflowNote = "select billid from pub_workflownote where billversionpk=? and pk_billtype=? and approvestatus="
				+ WfTaskOrInstanceStatus.Finished.getIntValue() + " and approveresult is not null and actiontype<>'"
				+ WorkflownoteVO.WORKITEM_TYPE_MAKEBILL + "'";

		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();
			SQLParameter para = new SQLParameter();
			para.addParam(billID);
			para.addParam(billType);
			Object objBillId = jdbc.executeQuery(sqlWorkflow, para, new ColumnProcessor(1));
			if (objBillId == null)
				return false;
			else {
				Object objBillId2 = jdbc.executeQuery(sqlWorkflowNote, para, new ColumnProcessor(1));
				return objBillId2 == null ? true : false;
			}
		} finally {
			if (persist != null)
				persist.release();
		}
	}

	/**
	 * 判定单据的流程实例状态是否为审批通过
	 * 
	 * @param billId
	 * @param billType
	 * @param iWftype
	 * @return
	 * @throws DbException
	 */
	public boolean isApproveflowPass(String billId, String billType, int iWftype) throws DbException {
		String procResult = queryFlowResult(billId, billType, iWftype);
		if (procResult != null && "Y".equals(procResult))
			return true;
		return false;
	}

	/**
	 * 判定单据的流程实例状态是否结束
	 * 
	 * @param billId                 单据PK
	 * @param billType               单据类型PK
	 * @param iWorkflowOrApproveflow
	 * @return
	 * @throws DbException
	 */
	public boolean isFlowFinished(String billId, String billType, int iWorkflowOrApproveflow) throws DbException {
		String procResult = queryFlowResult(billId, billType, iWorkflowOrApproveflow);
		return procResult == null ? false : true;
	}

	/**
	 * 查询流程实例的结果
	 * 
	 * @param billId
	 * @param billType
	 * @param iWorkflowOrApproveflow
	 * @return
	 * @throws DbException
	 */
	private String queryFlowResult(String billId, String billType, int iWorkflowOrApproveflow) throws DbException {
		String sql = "select procresult from pub_wf_instance "
				+ "where billversionpk=? and billtype=? and isnull(src_pk_actinstance,'~')='~' and procstatus="
				+ WfTaskOrInstanceStatus.Finished.getIntValue() + getWorkflowTypeAndWhere(iWorkflowOrApproveflow, null);

		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();
			SQLParameter para = new SQLParameter();
			para.addParam(billId);
			para.addParam(billType);
			Object objResult = jdbc.executeQuery(sql, para, new ColumnProcessor(1));
			return objResult == null ? null : objResult.toString();
		} finally {
			if (persist != null)
				persist.release();
		}
	}

	/**
	 * 查询流程是否在走但是流程已经禁用.
	 * 
	 * @param billId
	 * @param billType
	 * @param iWorkflowOrApproveflow
	 * @return
	 * @throws DbException
	 */
	public boolean queryIsStepIntoFlow(String billId, String billType, WorkflowTypeEnum em) throws DbException {
		String sql = "select pk_wf_instance from pub_wf_instance "
				+ "where billversionpk=? and billtype=? ";

		if (em != null) {
			sql = sql + getWorkflowTypeAndWhere(em.getIntValue(), null);
		}
		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();
			SQLParameter para = new SQLParameter();
			para.addParam(billId);
			para.addParam(billType);
			Object objResult = jdbc.executeQuery(sql, para, new ColumnProcessor(1));
			return objResult == null ? false : true;
		} finally {
			if (persist != null)
				persist.release();
		}
	}

	public List<Object[]> queryProcessStatus(String billid, String billType, int iWorkflowtype) throws DAOException {
		String sqlInstanceQuery = "select pk_wf_instance, procstatus from pub_wf_instance where billversionpk=? and billtype=?"
				+ getWorkflowTypeAndWhere(iWorkflowtype, "");

		SQLParameter param = new SQLParameter();
		param.addParam(billid);
		param.addParam(billType);

		BaseDAO dao = new BaseDAO();

		return (List<Object[]>) dao.executeQuery(sqlInstanceQuery, param, new ArrayListProcessor());
	}

	/** 挂起、恢复操作 */
	public void updateProcessStatus(String billID, String billType, int iWorkflowtype, int status) throws DbException {
		/** 将有效的流程实例置为挂起 */
		String sqlInstanceQuery = "select pk_wf_instance from pub_wf_instance where billversionpk=? and billtype=?"
				+ getWorkflowTypeAndWhere(iWorkflowtype, "");

		// String sqlTaskUpdate = "update pub_wf_task set taskstatus = " +
		// status
		// + " where pk_wf_instance in ";
		// String sqlActivityUpdate =
		// "update pub_wf_actinstance set actstatus = " + status
		// + " where pk_wf_instance in ";
		String sqlInstanceUpdate = "update pub_wf_instance set procstatus = " + status + " where pk_wf_instance in ("
				+ sqlInstanceQuery + ")";
		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();
			SQLParameter para = new SQLParameter();
			para.addParam(billID);
			para.addParam(billType);

			jdbc.executeUpdate(sqlInstanceUpdate, para);

			// StringBuffer sb = new StringBuffer();
			// List lInstancePKs = (List) jdbc.executeQuery(sqlInstanceQuery,
			// para, new ColumnListProcessor(
			// 1));
			// for (Iterator iter = lInstancePKs.iterator(); iter.hasNext();) {
			// String pk_wf_instance = (String) iter.next();
			// sb.append(",'" + pk_wf_instance + "'");
			// }
			// if (sb.length() > 0) {
			// String pks = sb.substring(1);
			// sqlTaskUpdate += "(" + pks + ")";
			// jdbc.executeUpdate(sqlTaskUpdate);
			// sqlActivityUpdate += "(" + pks + ")";
			// jdbc.executeUpdate(sqlActivityUpdate);
			// sqlInstanceUpdate += "(" + pks + ")";
			// jdbc.executeUpdate(sqlInstanceUpdate);
			// }
		} finally {
			if (persist != null)
				persist.release();
		}

	}

	/**
	 * 回退工作流或审批流
	 * 
	 * @param billID
	 * @param billType
	 * @param billVO
	 * @param iWorkflowOrApproveflow
	 * @throws BusinessException
	 */
	public void rollbackWorkflow(String billID, String billType, AggregatedValueObject billVO,
			int iWorkflowOrApproveflow) throws BusinessException {

		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();
			SQLParameter para = new SQLParameter();
			para.addParam(billID);
			para.addParam(billType);

			String strWfTypeWhere = getWorkflowTypeAndWhere(iWorkflowOrApproveflow, "b.");
			// query valid activity instance
			String sqlActivityQuery = "select a.activitydefid,a.actstatus,b.processdefid from pub_wf_actinstance a left join pub_wf_instance b on a.pk_wf_instance=b.pk_wf_instance"
					+ " where (a.actstatus=" + WfTaskOrInstanceStatus.Started.getIntValue() + " or a.actstatus="
					+ WfTaskOrInstanceStatus.Finished.getIntValue() + ") and b.billversionpk=? and b.billtype=?"
					+ strWfTypeWhere + " order by a.ts desc, a.pk_wf_actinstance desc ";

			ArrayList<ActivityInstance> alActInstance = (ArrayList<ActivityInstance>) jdbc
					.executeQuery(sqlActivityQuery, para, new BaseProcessor() {
						ArrayList<ActivityInstance> al = new ArrayList<ActivityInstance>();

						@Override
						public Object processResultSet(ResultSet rs) throws SQLException {
							while (rs.next()) {
								ActivityInstance ai = new ActivityInstance();
								int index = 1;
								// activitydefid;
								String act_defid = rs.getString(index);
								ai.setActivityID(act_defid == null ? null : act_defid.trim());
								// actstatus;
								index++;
								int status = rs.getInt(index);
								ai.setStatus(status);
								// processdefid
								index++;
								String wf_defid = rs.getString(index);
								ai.setWfProcessDefPK(wf_defid == null ? null : wf_defid.trim());
								al.add(ai);
							}

							return al;
						}
					});

			// rollback all valid activities
			for (int i = 0; i < alActInstance.size(); i++) {
				ActivityInstance ai = alActInstance.get(i);
				WorkflowProcess wp = findParsedWfProcess(ai.getWfProcessDefPK(), ai.getWfProcessInstancePK());
				Activity act = wp.findActivityByID(ai.getActivityID());
				boolean isRunned = false;
				if (ai.getStatus() == WfTaskOrInstanceStatus.Finished.getIntValue()) {
					isRunned = true;
					if (i == alActInstance.size() - 1) {
						// 最后一个要回退的活动，也就是当前活动本身，只需要回滚after组件，不用回滚before组件
						undoGadgetAfter(billID, billType, billVO, act);
						continue;
					}
				} else if (ai.getStatus() == WfTaskOrInstanceStatus.Started.getIntValue()) {
					isRunned = false;
				}
				undoGadget(billID, billType, billVO, act, isRunned);
			}
		} catch (DbException e) {
			Logger.error(e.getMessage(), e);
			throw new PFBusinessException(NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow",
					"EngineService-0000", null, new String[] { e.getMessage() })/* 数据库异常：{0} */, e);
		} finally {
			if (persist != null)
				persist.release();
		}
	}

	/**
	 * 判断服务是否是跨服务 ncc新加 by zhuhlm
	 */
	private synchronized String isCrossService(String className, String billTypeCode) throws BusinessException {
		try {
			INccPFCrossServiceQuery nccPFCrossServiceQueryImpl = NCLocator.getInstance().lookup(INccPFCrossServiceQuery.class);
			return nccPFCrossServiceQueryImpl.isCrossServiceByClassName(className, billTypeCode);
		} catch (Exception e) {
			throw new BusinessException(e);
		}
	}

	/**
	 * 回退某个活动关联的单据组件
	 * 
	 * @param activity 回退的活动
	 * @param isRunned 该回退的活动是否已执行完成
	 * @since 6.0
	 */
	public void undoGadget(String billID, String billType, AggregatedValueObject billVO, Activity activity,
			boolean isRunned) {
		Logger.debug("活动回退时，回退其单据组件=EngineService.undoGadget() called");

		WorkflowgadgetVO foundGadgetVO = findGadget(activity, billType);
		if (foundGadgetVO == null) {
			if (activity != null) {
				Logger.debug("该活动没有配置工作流组件，无需执行。活动名称=" + activity.getName());
			}
			return;
		}
		PfParameterVO paraVO = ActionEnvironment.getInstance().getParaVo(billID);
		// 查询工作流组件注册的类名
		String clzName = foundGadgetVO.getClassname();
		if (StringUtil.isEmptyWithTrim(clzName))
			throw new EngineException(NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow", "EngineService-0001",
					null, new String[] { foundGadgetVO.getPk_workflowgadget() })/*
																				 * 找不到工作流组件对应的类名 ，组件ID={0}
																				 */);
		WfGadgetContext gc = new WfGadgetContext();
		gc.setGadgetVO(foundGadgetVO);
		gc.setBillEntity(billVO);
		gc.setAutoActivity(activity.getActivityType() == ActivityTypeEnum.Auto.getIntValue());
		gc.setActivity(activity);
		// gc.setProcessStatus(isToFinish ?
		// WfTaskOrInstanceStatus.Finished :
		// WfTaskOrInstanceStatus.Started);
		// FIXME:其他属性?
		if (paraVO != null) {
			gc.setUserObj(paraVO.m_userObj);
			gc.setPfParameterVO(paraVO.clone());
		} else {
			gc.setControlSource(WfGadgetControlSource.Manage);
		}
		String [] clazNameArray=clzName.split(",");
		setSplitClzName(clazNameArray,billType,gc,isRunned);
	}
	
	private void setSplitClzName(String [] clazNameArray,String billType,WfGadgetContext gc ,boolean isRunned) {
		for(String clzName:clazNameArray) {
			try {
				String serviceName = isCrossService(clzName, billType);
				//String serviceName="ssc";
				if (StringUtils.isNotBlank(serviceName)) {// 判断跨服务
					NCCSagas.startAddSaga();
					executeCrossUnDoService(clzName,gc,billType,isRunned,serviceName,true);
				} else {
					// 实例化工作流组件并执行回退方法
					Object obj = PfUtilTools.findBizImplOfBilltype(billType, clzName.trim());
					if (obj instanceof IWorkflowGadget) {
						IWorkflowGadget gadgetObj = (IWorkflowGadget) obj;
						if (isRunned) {
							gadgetObj.undoAfterRunned(gc);
							gadgetObj.undoBeforeActive(gc);
						} else {
							gadgetObj.undoBeforeActive(gc);
						}
					} else
						throw new PFBusinessException(
								NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow", "EngineService-0002", null,
										new String[] { (String) obj })/*
																		 * 注册的工作流组件没有实现IWorkflowGadget接口 ={0}
																		 */);
				}

			} catch (BusinessException e) {
				Logger.error(e.getMessage(), e);
				throw new EngineException(NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow", "EngineService-0003",
						null, new String[] { e.getMessage() })/* 回退工作流组件发生异常：{0} */, e);
			}
		}
		
	}
	
	/**
	 * 删除该单据所相关的审批流或工作流记录
	 * 
	 * @param billID
	 * @param billType
	 * @param isPreserveNote         是否保留工作项处理历史
	 * @param iWorkflowOrApproveflow 主流程类型，见<code>WorkflowTypeEnum.Approveflow</code>和
	 *                               <code>WorkflowTypeEnum.Workflow</code>
	 * @throws DbException
	 * @throws BusinessException
	 */
	public void deleteWorkflow(String billID, String billType, boolean isPreserveNote, int iWorkflowOrApproveflow)
			throws DbException, BusinessException {

		String param = "billType=" + billType + ", billi=" + billID;
		Logger.debug("EngineService.deleteWorkflow: begin delete workflow, " + param);

		new FlowInstanceDeletionDAO().deleteWorkflow(billID, billType, isPreserveNote, iWorkflowOrApproveflow);

		// 删除历史驳回标记记录 xuxwb 重新提交时仍然需要驳回标识，不删除
//		IWFRejectReason rr=NCLocator.getInstance().lookup(IWFRejectReason.class);
//		rr.deleteRejectReson(billID);

		Logger.debug("EngineService.deleteWorkflow: end delete workflow, " + param);

		// String sqlCond = "billversionpk='" + billID + "' and pk_billtype='"
		// + SQLTransferMeaningUtil.tmsql(billType) + "'";
		//
		// String strWfTypeWhere =
		// getWorkflowTypeAndWhere(iWorkflowOrApproveflow,
		// null);
		// sqlCond = sqlCond + strWfTypeWhere;
		//
		// BaseDAO dao = new BaseDAO();
		// Collection<WorkflownoteVO> colWorknote = dao.retrieveByClause(
		// WorkflownoteVO.class, sqlCond);
		//
		// if (!isPreserveNote) {
		// dao.deleteVOArray(colWorknote.toArray(new WorkflownoteVO[0]));
		// } else {
		// ArrayList<WorkflownoteVO> aryWorkflownote = new
		// ArrayList<WorkflownoteVO>();
		// for (Iterator iterator = colWorknote.iterator(); iterator.hasNext();)
		// {
		// WorkflownoteVO workflownoteVO = (WorkflownoteVO) iterator
		// .next();
		// if (workflownoteVO.getApprovestatus() ==
		// WfTaskOrInstanceStatus.Started
		// .getIntValue())
		// aryWorkflownote.add(workflownoteVO);
		// }
		// dao.deleteVOList(aryWorkflownote);
		// }
		// PfMessageUtil.deleteMessagesOfWorknote(colWorknote
		// .toArray(new WorkflownoteVO[0]));
		//
		// PersistenceManager persist = null;
		// try {
		// persist = PersistenceManager.getInstance();
		// JdbcSession jdbc = persist.getJdbcSession();
		// SQLParameter para = new SQLParameter();
		// para.addParam(billID);
		// para.addParam(billType);
		//
		// // invalid from pub_workflownote
		// String sqlNoteInvalid = "update pub_workflownote set approvestatus="
		// + WfTaskOrInstanceStatus.Inefficient.getIntValue()
		// + " where billversionpk=? and pk_billtype=?";
		// // delete from pub_wf_task
		// String sqlTaskDelete =
		// "delete from pub_wf_task where pk_wf_instance in ";
		// // delete from pub_wf_actinstancesrc
		// String sqlSrcTargetDelete =
		// "delete from pub_wf_actinstancesrc where src_actinstance"
		// +
		// " in(select pk_wf_actinstance from pub_wf_actinstance where pk_wf_instance in
		// ";
		// // delete from pub_wf_actinstance
		// String sqlActivityDelete =
		// "delete from pub_wf_actinstance where pk_wf_instance in ";
		// // delete from pub_wf_instance_b
		// String sqlProcessbDelete =
		// "delete from pub_wf_instance_b where pk_wf_instance in ";
		// // delete from pub_wf_instance
		// String sqlProcessDelete =
		// "delete from pub_wf_instance where billversionpk=? and billtype=?"
		// + strWfTypeWhere;
		// // delete from pub_messageinfo lj+2006-9-18 删除该单据产生的拉式消息
		// // String sqlMsgInfoDelete =
		// // "delete from pub_messageinfo where billid=? and pk_billtype=?";
		//
		// String sqlInstanceQuery =
		// "select pk_wf_instance from pub_wf_instance where billversionpk=? and
		// billtype=?"
		// + strWfTypeWhere;
		//
		// // execute jdbc
		// jdbc.executeUpdate(sqlNoteInvalid, para);
		//
		// StringBuffer sb = new StringBuffer();
		// List lInstancePKs = (List) jdbc.executeQuery(sqlInstanceQuery,
		// para, new ColumnListProcessor(1));
		// for (Iterator iter = lInstancePKs.iterator(); iter.hasNext();) {
		// String pk_wf_instance = (String) iter.next();
		// sb.append(",'" + pk_wf_instance + "'");
		// }
		//
		// if (sb.length() > 0) {
		// String strInstancePKs = sb.substring(1);
		//
		// // Collection<WorkflowMsgVO> colWorkflowMsg
		// // =dao.retrieveByClause(WorkflowMsgVO.class,
		// // "pk_wf_instance in("+strInstancePKs+")");
		// // PfMessageUtil.deleteMessageOfMsgVO(colWorkflowMsg.toArray(new
		// // WorkflowMsgVO[0]));
		//
		// sqlTaskDelete += "(" + strInstancePKs + ")";
		// jdbc.executeUpdate(sqlTaskDelete);
		//
		// sqlSrcTargetDelete += "(" + strInstancePKs + "))";
		// jdbc.executeUpdate(sqlSrcTargetDelete);
		//
		// sqlActivityDelete += "(" + strInstancePKs + ")";
		// jdbc.executeUpdate(sqlActivityDelete);
		//
		// sqlProcessbDelete += "(" + strInstancePKs + ")";
		// jdbc.executeUpdate(sqlProcessbDelete);
		//
		// jdbc.executeUpdate(sqlProcessDelete, para);
		// // jdbc.executeUpdate(sqlMsgInfoDelete, para);
		// }
		//
		// } finally {
		// if (persist != null)
		// persist.release();
		// }
	}

	/**
	 * 查询[单据类型+业务类型]、[单据类型] 上是否定义了审批流或工作流
	 * 
	 * @param billType               单据类型PK
	 * @param iWorkflowOrApproveflow 流程类型：1=审批流 3=单据
	 * @return true表示定义了；false表示没定义流程
	 * @throws DbException
	 */
	public boolean isBilltypeHasFlowDef(String billType, int iWorkflowOrApproveflow) throws DbException {

		// XXX:查询[单据类型] 上有无流程定义 leijun@2008-10
		String sql = "select billtype from pub_wf_def where billtype=? and validity="
				+ WorkflowDefStatusEnum.Valid.getIntValue() + getWorkflowTypeAndWhere(iWorkflowOrApproveflow, "");
		//
		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();
			SQLParameter para = new SQLParameter();
			para.addParam(billType);
			Object obj = jdbc.executeQuery(sql, para, new ColumnProcessor(1));
			return obj == null ? false : true;
		} finally {
			if (persist != null)
				persist.release();
		}
	}

	/**
	 * 查询活动分配的所有工作项的审批人
	 * 
	 * @param pk_wf_actinstance
	 * @return
	 * @throws DbException
	 */
	public String queryOperatorsByActinstancePK(String pk_wf_actinstance) throws DbException {
		String sql = "select distinct checkman from pub_workflownote a,pub_wf_task b"
				+ " where b.pk_wf_actinstance=? and a.pk_wf_task=b.pk_wf_task";
		//
		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();
			SQLParameter para = new SQLParameter();
			para.addParam(pk_wf_actinstance);
			List lRet = (List) jdbc.executeQuery(sql, para, new ColumnListProcessor(1));
			StringBuffer sb = new StringBuffer();
			for (Iterator iter = lRet.iterator(); iter.hasNext();) {
				String operator = (String) iter.next();
				sb.append("," + operator);
			}
			if (sb.length() > 0)
				return sb.substring(1);
			else
				return null;
		} finally {
			if (persist != null)
				persist.release();
		}
	}

	/**
	 * 找到某流程实例对应的流程定义；如果是子流程，还需递归获取其父流程的定义内容。
	 * <li>返回的链表中，是按照层次关系倒序的。
	 * 
	 * @param procInstancePK 当前流程实例PK
	 * @param activitydefid  当前活动定义ID
	 * @return LinkedList 输出值
	 */
	public LinkedList<WfProcessParentMap> findProcessDefsOfInstance(String procInstancePK, String activitydefid)
			throws DbException {
		// 需要填充的返回值
		LinkedList<WfProcessParentMap> llCascadedDefs = new LinkedList();

		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();
			findProcessDefsByInstancePKRecursive(jdbc, procInstancePK, activitydefid, llCascadedDefs);
		} finally {
			if (persist != null)
				persist.release();
		}

		return llCascadedDefs;
	}

	/**
	 * 根据流程实例PK获取其流程定义的内容, 如果是子流程,还需递归获取父流程的定义内容.
	 * <li>返回的链表中,是按照层次关系倒序的
	 * 
	 * @param jdbc
	 * @param procInstancePK 当前流程实例PK
	 * @param activitydefid  当前活动定义ID
	 * @param outList        输出值
	 * @throws DbException
	 */
	protected void findProcessDefsByInstancePKRecursive(JdbcSession jdbc, String procInstancePK, String activitydefid,
			LinkedList<WfProcessParentMap> outList) throws DbException {
		String sql = "select c.content,b.pk_wf_instance,b.activitydefid,a.processdefid "
				+ "from pub_wf_instance a left outer join pub_wf_actinstance b on a.src_pk_actinstance=b.pk_wf_actinstance "
				+ "left outer join pub_wf_def c on a.processdefid=c.pk_wf_def " + "where a.pk_wf_instance=?";
		//
		SQLParameter para = new SQLParameter();
		para.addParam(procInstancePK);

		List lResult = (List) jdbc.executeQuery(sql, para, new BaseProcessor() {
			public Object processResultSet(ResultSet rs) throws SQLException {
				ArrayList alRS = new ArrayList();
				while (rs.next()) {
					HashMap hm = new HashMap();
					Object def_content = InOutUtil.deserialize(ProcessorUtils.getBlob(rs, 1));
					hm.put("content", def_content);
					hm.put("pk_wf_instance", rs.getString("pk_wf_instance"));
					hm.put("activitydefid", rs.getString("activitydefid"));
					hm.put("processdefid", rs.getString("processdefid"));
					alRS.add(hm);
				}
				return alRS;
			}
		});

		String parentProcInstancePK = null;
		String srcActivityDefIDInParentProcess = null;
		String processDefPK = null;
		for (Iterator iter = lResult.iterator(); iter.hasNext();) {
			HashMap hmap = (HashMap) iter.next();
			Object def_content = hmap.get("content");
			parentProcInstancePK = (String) hmap.get("pk_wf_instance");
			srcActivityDefIDInParentProcess = (String) hmap.get("activitydefid");
			processDefPK = (String) hmap.get("processdefid");

			WfProcessParentMap ppm = new WfProcessParentMap();
			ppm.setProcessInstancePK(procInstancePK);
			ppm.setActivityDefId(activitydefid);
			ppm.setProcessDefPK(processDefPK);
			ppm.setXpdlString(def_content);
			outList.add(ppm);
			// outHashMap.put(activitydefid, def_content);

			// 获取父流程定义PK
			if (parentProcInstancePK != null) {
				String strParentSQL = "select processdefid from pub_wf_instance where pk_wf_instance=?";
				SQLParameter parentPara = new SQLParameter();
				parentPara.addParam(parentProcInstancePK);
				Object obj = jdbc.executeQuery(strParentSQL, parentPara, new ColumnProcessor(1));
				ppm.setParentDefPK(obj == null ? null : String.valueOf(obj));

				// 递归之
				findProcessDefsByInstancePKRecursive(jdbc, parentProcInstancePK, srcActivityDefIDInParentProcess,
						outList);
			}
		}
	}

	/**
	 * 根据单据id和流程实例PK获取其流程定义的内容,还要获取流程中触发过的活动；<br>
	 * 再递归查询其所有子流程。
	 * 
	 * @param billId         单据id
	 * @param billType
	 * @param procInstancePK 流程实例pk,如果为null,表示主流程
	 * @param jdbc           JdbcSession
	 * @param iWorkflowtype  流程类型：1=审批流 3=工作流
	 * 
	 * @modifier yanke1 2011-9-22
	 *           从pub_wf_def中查询流程定义version，赋给ProcessRouteRes的processDefVersion
	 */
	public ProcessRouteRes queryProcessRouteRecursively(String billId, String billType, String procInstancePK,
			JdbcSession jdbc, int iWorkflowtype) throws DbException {
		final ProcessRouteRes processRoute = new ProcessRouteRes();

		// 1.取流程定义
		String sql = "select c.content,a.processdefid,a.pk_wf_instance,a.procstatus, c.version from pub_wf_instance a,pub_wf_def c "
				+ " where a.processdefid=c.pk_wf_def and (a.billversionpk=? or a.billid = ?) and a.billtype=?";
		SQLParameter para = new SQLParameter();
		para.addParam(billId);
		para.addParam(billId);
		para.addParam(billType);
		if (procInstancePK != null) {
			sql += " and a.pk_wf_instance=?";
			para.addParam(procInstancePK);
		} else {
			// 流程实例pk如果为null,表示主流程
			sql += getWorkflowTypeAndWhere(iWorkflowtype, "a.");
			sql += " and isnull(a.src_pk_actinstance,'~')='~'";
		}

		jdbc.executeQuery(sql, para, new BaseProcessor() {
			public Object processResultSet(ResultSet rs) throws SQLException {
				if (rs.next()) {
					Object def_content = InOutUtil.deserialize(ProcessorUtils.getBlob(rs, 1));
					processRoute.setXpdlString(def_content);
					processRoute.setProcessDefPK(rs.getString(2));
					processRoute.setProcInstancePK(rs.getString(3));
					processRoute.setProcStatus(rs.getInt(4));
					processRoute.setProcessDefVersion(rs.getString(5));
				}
				return null;
			}
		});

		if (processRoute.getProcInstancePK() != null) {
			sql = "select content from pub_wf_def_adjust where pk_wf_instance=?";
			para = new SQLParameter();
			para.addParam(processRoute.getProcInstancePK());

			jdbc.executeQuery(sql, para, new BaseProcessor() {
				public Object processResultSet(ResultSet rs) throws SQLException {
					if (rs.next()) {
						String content = (String) InOutUtil.deserialize(ProcessorUtils.getBlob(rs, 1));

						// 设置xpdlContent
						if (!StringUtil.isEmptyWithTrim(content)) {
							processRoute.setXpdlString(content);
						}
					}
					return null;
				}
			});

		}

		if (processRoute.getProcessDefPK() != null) {// 查到了流程定义
			// 2.取流程中触发过的活动

			// yanke1 这是一个select distinct语句，并且根据createtime进行了order by
			// 因此应当在select中加上createtime，否则oracle 10g会报错ORA-01791
			sql = "select distinct pk_wf_actinstance,activitydefid,actstatus,createtime from pub_wf_actinstance "
					+ " where pk_wf_instance=? order by createtime, pk_wf_actinstance";
			para = new SQLParameter();
			para.addParam(processRoute.getProcInstancePK());
			ArrayList alActs = (ArrayList) jdbc.executeQuery(sql, para, new ArrayListProcessor());
			ActivityInstance[] actInstances = null;
			if (alActs != null && alActs.size() > 0) {
				actInstances = new ActivityInstance[alActs.size()];
				Object[] oTemp = null;
				StringBuffer sbActInstancePK = new StringBuffer();
				for (int i = 0; i < alActs.size(); i++) {
					oTemp = (Object[]) alActs.get(i);
					actInstances[i] = new ActivityInstance((String) oTemp[0]);
					sbActInstancePK.append(",'" + oTemp[0] + "'");
					actInstances[i].setActivityID((String) oTemp[1]);
					actInstances[i].setStatus(((Integer) oTemp[2]).intValue());
				}
				processRoute.setActivityInstance(actInstances);

				// 查询流程实例中所有活动的来源关系
				String sqlRela = "select distinct b.activitydefid,c.activitydefid from pub_wf_actinstancesrc a "
						+ "inner join pub_wf_actinstance b on a.src_actinstance=b.pk_wf_actinstance "
						+ "inner join pub_wf_actinstance c on a.target_actinstance=c.pk_wf_actinstance "
						+ "where b.pk_wf_instance=? and c.pk_wf_instance=?  and ((b.actstatus!=c.actstatus )or (b.actstatus=c.actstatus and c.actstatus!=4 ))";
				para.addParam(processRoute.getProcInstancePK());
				ArrayList alActRela = (ArrayList) jdbc.executeQuery(sqlRela, para, new ArrayListProcessor());
				processRoute.setActivityRelations(alActRela);

				// 3.取子流程
				sql = "select distinct pk_wf_instance from pub_wf_instance " + " where src_pk_actinstance in("
						+ sbActInstancePK.substring(1) + ") and procstatus!="
						+ WfTaskOrInstanceStatus.Inefficient.getIntValue();
				ArrayList alSubWfInstance = (ArrayList) jdbc.executeQuery(sql, new ArrayListProcessor());
				if (alSubWfInstance != null && alSubWfInstance.size() > 0) {
					ProcessRouteRes[] subProessRoute = new ProcessRouteRes[alSubWfInstance.size()];
					for (int i = 0; i < alSubWfInstance.size(); i++) {
						oTemp = (Object[]) alSubWfInstance.get(i);
						subProessRoute[i] = queryProcessRouteRecursively(billId, billType, (String) oTemp[0], jdbc,
								iWorkflowtype);// 递归取子流程
					}
					processRoute.setSubProcessRoute(subProessRoute);
				}
			}
		}
		return processRoute;
	}

	// private WorkflownoteVO queryMostSuitedNote(WorkflownoteVO[]
	// checkedNoteVOs) throws DbException{
	// WorkflownoteVO noteVO = checkedNoteVOs[0];
	// TaskManagerDMO taskDmo = new TaskManagerDMO();
	// WFTask currentTask = taskDmo.getTaskByPK(noteVO.getPk_wf_task());
	// if(!currentTask.isAutoCompleted())
	// return noteVO
	// }

	/**
	 * 检查某用户已完成的工作项
	 * <li>如果无已完成工作项，且有流程实例，则抛出异常
	 * 
	 * @param billType
	 * @param billId
	 * @param checkman
	 * @param iWorkflowOrApproveflow
	 * @return
	 * @throws DbException
	 * @throws BusinessException
	 */
	public WorkflownoteVO checkFinishedWorkitem(String billType, String billId, String checkman,
			int iWorkflowOrApproveflow) throws DbException, BusinessException {
		// 查询某用户对某单据的所有已办工作项
		// WorkflownoteVO[] checkedNoteVOs =
		// queryWorkitemsCheckedExcludeAutoComplete(billType,
		// billId, checkman, iWorkflowOrApproveflow);

		/**
		 * @since v65 修改了自动审批逻辑，自动审批时逐级弃审，可以弃审自动审批环节
		 */
		WorkflownoteVO[] checkedNoteVOs = queryWorkitemsCheckedIncludeAutoComplete(billType, billId, checkman,
				iWorkflowOrApproveflow);

		if (checkedNoteVOs != null && checkedNoteVOs.length > 0) {
			Collections.sort(Arrays.asList(checkedNoteVOs), new VoComparator());
			// 当前操作员有已办工作项
			WorkflownoteVO noteVO = findLatestFinishedWorkitem(checkedNoteVOs);
			if (!checkman.equals(noteVO.getCheckman())) {
				throw new PFBusinessException(NCLangResOnserver.getInstance().getStrByID("pfworkflow",
						"UPPpfworkflow-000928")/*
												 * 已经存在后续操作， 请逐级弃审
												 */);
			}
			TaskManagerDMO taskDmo = new TaskManagerDMO();
			WFTask currentTask = taskDmo.getTaskByPK(noteVO.getPk_wf_task());
			
			//checkresult 是共享服务的不重走流程驳回后再次提交后，中间节点还能取消审批问题。UAPNCWEB-4492
			boolean checkresult = checkLatestFinishedWorkitem(currentTask.getWfProcessDefPK(),currentTask.getActivityID(),currentTask.getWfProcessInstancePK());

			// checkresult 是共享服务的不重走流程驳回后再次提交后，中间节点还能取消审批问题。UAPNCWEB-4492
			// boolean checkresult =
			// checkLatestFinishedWorkitem(currentTask.getWfProcessDefPK(),currentTask.getActivityID(),currentTask.getWfProcessInstancePK());

			if (!checkman.equals(noteVO.getCheckman())&&checkresult) {
				throw new PFBusinessException(NCLangResOnserver.getInstance().getStrByID("pfworkflow",
						"UPPpfworkflow-000928")/*
												 * 已经存在后续操作， 请逐级弃审
												 */);
			}

			// 处理自动审批的问题 A-b-c(自动)-b（已经到这里但是未操作）-->之后前面的b反审批
			// autoApprove(checkedNoteVOs,billType,billId);
			// 下级环节有会签的情况，本环节取消审批要看情况
			nextlink(currentTask.getActivityInstancePK());
			if (currentTask == null)
				throw new PFBusinessException(NCLangResOnserver.getInstance().getStrByID("pfworkflow",
						"UPPpfworkflow-000266")/* 当前操作人已办任务丢失 */);
			currentTask.setPk_org(noteVO.getPk_org());
			currentTask.setBillType(noteVO.getPk_billtype());
			currentTask.setBillID(noteVO.getBillid());
			currentTask.setBillversionPK(noteVO.getBillVersionPK());
			currentTask.setBillNO(noteVO.getBillno());
			currentTask.setTopic(noteVO.getMessagenote());

			currentTask.setWorknoteVO(noteVO);
			currentTask.setSenderman(noteVO.getSenderman());
			WfTaskInfo taskInfo = new WfTaskInfo(currentTask);
			noteVO.setTaskInfo(taskInfo);

			// 获得活动的一些扩展属性
			WorkflowProcess wp = findParsedWfProcess(currentTask.getWfProcessDefPK(),
					currentTask.getWfProcessInstancePK());
			if (wp == null)
				return null; // throw new Exception("没有对应的流程定义");
			noteVO.getRelaProperties()
					.putAll(findRelaProps(wp.findActivityByID(currentTask.getActivityID()), billType));
			return noteVO;
		} else {
			// 当前操作员无已办工作项
			HashSet<String> hsRet = hasInstanceOfValid(new String[] { billId }, billType, iWorkflowOrApproveflow);
			boolean isStartup = hsRet.contains(billId);
			if (!isStartup) {
				// 该单据无流程实例
				return null;
			} else {
				// 该单据有流程实例
				boolean isDirectlyCompleted = isDirectlyCompleted(billId, billType);
				if (isDirectlyCompleted) {
					deleteWorkflow(billId, billType, false, iWorkflowOrApproveflow);
					return null;
				} else
					throw new PFBusinessException(NCLangResOnserver.getInstance().getStrByID("pfworkflow",
							"UPPpfworkflow-000267")/* 当前操作人没有已办工作项，无法回退 */);
			}
		}

	}
	
	
	/**
	 * 检查某用户已完成的工作项
	 * <li>如果无已完成工作项，且有流程实例，则抛出异常
	 * 
	 * @param billType
	 * @param billId
	 * @param checkman
	 * @param iWorkflowOrApproveflow
	 * @return
	 * @throws DbException
	 * @throws BusinessException
	 */
	public WorkflownoteVO checkFinishedWorkitemNoCheckMan(String billType, String billId,
			int iWorkflowOrApproveflow) throws DbException, BusinessException {
		// 查询某用户对某单据的所有已办工作项
		// WorkflownoteVO[] checkedNoteVOs =
		// queryWorkitemsCheckedExcludeAutoComplete(billType,
		// billId, checkman, iWorkflowOrApproveflow);

		/**
		 * @since v65 修改了自动审批逻辑，自动审批时逐级弃审，可以弃审自动审批环节
		 */
		WorkflownoteVO[] checkedNoteVOs = queryWorkitemsCheckedIncludeAutoComplete(billType, billId, null,
				iWorkflowOrApproveflow);

		if (checkedNoteVOs != null && checkedNoteVOs.length > 0) {
			Collections.sort(Arrays.asList(checkedNoteVOs), new VoComparator());
			// 当前操作员有已办工作项
			WorkflownoteVO noteVO = findLatestFinishedWorkitem(checkedNoteVOs);
			TaskManagerDMO taskDmo = new TaskManagerDMO();
			WFTask currentTask = taskDmo.getTaskByPK(noteVO.getPk_wf_task());

			// checkresult 是共享服务的不重走流程驳回后再次提交后，中间节点还能取消审批问题。UAPNCWEB-4492
			// boolean checkresult =
			// checkLatestFinishedWorkitem(currentTask.getWfProcessDefPK(),currentTask.getActivityID(),currentTask.getWfProcessInstancePK());
			// 处理自动审批的问题 A-b-c(自动)-b（已经到这里但是未操作）-->之后前面的b反审批
			// autoApprove(checkedNoteVOs,billType,billId);
			// 下级环节有会签的情况，本环节取消审批要看情况
			nextlink(currentTask.getActivityInstancePK());
			if (currentTask == null)
				throw new PFBusinessException(NCLangResOnserver.getInstance().getStrByID("pfworkflow",
						"UPPpfworkflow-000266")/* 当前操作人已办任务丢失 */);
			currentTask.setPk_org(noteVO.getPk_org());
			currentTask.setBillType(noteVO.getPk_billtype());
			currentTask.setBillID(noteVO.getBillid());
			currentTask.setBillversionPK(noteVO.getBillVersionPK());
			currentTask.setBillNO(noteVO.getBillno());
			currentTask.setTopic(noteVO.getMessagenote());

			currentTask.setWorknoteVO(noteVO);
			currentTask.setSenderman(noteVO.getSenderman());
			WfTaskInfo taskInfo = new WfTaskInfo(currentTask);
			noteVO.setTaskInfo(taskInfo);

			// 获得活动的一些扩展属性
			WorkflowProcess wp = findParsedWfProcess(currentTask.getWfProcessDefPK(),
					currentTask.getWfProcessInstancePK());
			if (wp == null)
				return null; // throw new Exception("没有对应的流程定义");
			noteVO.getRelaProperties()
					.putAll(findRelaProps(wp.findActivityByID(currentTask.getActivityID()), billType));
			return noteVO;
		} else {
			// 当前操作员无已办工作项
			HashSet<String> hsRet = hasInstanceOfValid(new String[] { billId }, billType, iWorkflowOrApproveflow);
			boolean isStartup = hsRet.contains(billId);
			if (!isStartup) {
				// 该单据无流程实例
				return null;
			} else {
				// 该单据有流程实例
				boolean isDirectlyCompleted = isDirectlyCompleted(billId, billType);
				if (isDirectlyCompleted) {
					deleteWorkflow(billId, billType, false, iWorkflowOrApproveflow);
					return null;
				} else
					throw new PFBusinessException(NCLangResOnserver.getInstance().getStrByID("pfworkflow",
							"UPPpfworkflow-000267")/* 当前操作人没有已办工作项，无法回退 */);
			}
		}

	}

	/**
	 * 当前活动反审批时，检查下级活动状态,如果又返回值，证明下级活动已经审批，不能反审批 主要为了各种自动审批的用法添加的次方法
	 * 
	 * @Title: autoApprove @Description: TODO(这里用一句话描述这个方法的作用) @param @param
	 *         activityinstancepk @param @throws BusinessException 设定文件 @return void
	 *         返回类型 @throws
	 */
	private void autoApprove(WorkflownoteVO[] checkedNoteVOs, String billType, String billId) throws BusinessException {
		ArrayList<String> list = new ArrayList<String>();
		// for(WorkflownoteVO vo : checkedNoteVOs[0]){
		// list.add(checkedNoteVOs.pk_checkflow);
		// }
		WorkflownoteVO vo = checkedNoteVOs[0];
		list.add(vo.getPk_checkflow());

		BaseDAO dao = new BaseDAO();
		String sqlstr = "SELECT a.pk_checkflow  FROM  PUB_WORKFLOWNOTE a, PUB_WF_TASK b ,PUB_WF_ACTINSTANCE c ,pub_wf_actinstancesrc d "
				+ " where a.pk_billtype='" + billType + "'" + " AND a.billversionpk='" + billId + "'"
				+ " AND a.workflow_type IN(2,3,6)" + " AND a.approvestatus=1" + " AND a.approveresult IS NOT NULL"
				+ " AND a.actiontype LIKE 'Z%'" + " and a.PK_WF_TASK = b.PK_WF_TASK"
				+ " and b.PK_WF_INSTANCE = c.PK_WF_INSTANCE" + " AND b.ACTIVITYDEFID = c.ACTIVITYDEFID"
				+ " and c.PK_WF_ACTINSTANCE = d.SRC_ACTINSTANCE"
				+ " and d.SRC_ACTINSTANCE in( select src.TARGET_ACTINSTANCE from PUB_WORKFLOWNOTE note "
				+ " LEFT JOIN PUB_WF_TASK task ON  note.PK_WF_TASK = task.PK_WF_TASK "
				+ " LEFT JOIN PUB_WF_ACTINSTANCE  act on task.PK_WF_INSTANCE = act.PK_WF_INSTANCE and task.ACTIVITYDEFID = act.ACTIVITYDEFID "
				+ " LEFT JOIN pub_wf_actinstancesrc src on act.PK_WF_ACTINSTANCE = src.SRC_ACTINSTANCE " + " where "
				+ SQLUtil.buildSqlForIn("note.pk_checkflow", list.toArray(new String[list.size()])) + ")";

		ArrayList<String[]> obj = (ArrayList<String[]>) dao.executeQuery(sqlstr, new ArrayListProcessor());
		HashSet<String> set = new HashSet<String>();
		if (obj.size() > 0) {
			for (Object[] s : obj) {
				set.add(s[0].toString());
			}
			set.remove(vo.getPk_checkflow());// 结果中除去自己
			// 如果set还有值那一定是后续完成节点的pk
			if (set.size() > 0) {
				throw new PFBusinessException(NCLangResOnserver.getInstance().getStrByID("pfworkflow",
						"UPPpfworkflow-000928")/*
												 * 已经存在后续操作， 请逐级弃审
												 */);
			}
		}
	}

	/**
	 * 根据当前环节查询下一环节是否有已经审批完成的状态
	 * 
	 * @Title: scr
	 * @Description: TODO(这里用一句话描述这个方法的作用)
	 * @param @param  wfprocessdefpk
	 * @param @param  activityid
	 * @param @param  dao
	 * @param @param  from
	 * @param @throws BusinessException 设定文件
	 * @return void 返回类型
	 * @throws @数据库中取关系
	 */
	@SuppressWarnings("unchecked")
	private void nextlink(String activityinstancepk) throws BusinessException {
		BaseDAO dao = new BaseDAO();
		// act.ACTSTATUS = 0 会签时没有全完成就是0全完成应该是1
		String sqlstr = "select * from  pub_wf_actinstancesrc  src , pub_wf_actinstance act ,PUB_Wf_task  ta ,PUB_WORKFLOWNOTE note"
				+ " where src.SRC_ACTINSTANCE ='" + activityinstancepk + "'"
				+ " and src.TARGET_ACTINSTANCE = act.PK_WF_ACTINSTANCE and act.ACTSTATUS = 0  "
				+ " and  act.PK_WF_ACTINSTANCE = ta.PK_WF_ACTINSTANCE  and  ta.PK_WF_TASK = note.PK_WF_TASK  "
				+ " and  note.APPROVESTATUS = 1 ";
		ArrayList<String[]> obj = (ArrayList<String[]>) dao.executeQuery(sqlstr, new ArrayListProcessor());
		if (obj.size() > 0) {
			throw new PFBusinessException(NCLangResOnserver.getInstance().getStrByID("pfworkflow",
					"UPPpfworkflow-000927")/*
											 * 【会签模式】 已有其他人参与审批 ，不能取消审批
											 */);
		}

	}

	/**
	 * 根据当前环节查询下一环节pk
	 * 
	 * @Title: scr
	 * @Description: TODO(这里用一句话描述这个方法的作用)
	 * @param @param  wfprocessdefpk
	 * @param @param  activityid
	 * @param @param  dao
	 * @param @param  from
	 * @param @throws BusinessException 设定文件
	 * @return void 返回类型
	 * @throws @缓存中取关系
	 */
	@SuppressWarnings("unchecked")
	private void nextlink(String wfprocessdefpk, String activityid, ArrayList<String> al) throws BusinessException {

		BasicWorkflowProcess base = null;
		try {
			// 缓存中读取
			base = PfDataCache.getWorkflowProcess(wfprocessdefpk);

		} catch (XPDLParserException e) {
			// TODO Auto-generated catch block
			Logger.error(e.getMessage(), e);
		}
		// 查询所有转移
		ArrayList<BasicTransitionEx> list = (ArrayList<BasicTransitionEx>) base.findActivityByID(activityid)
				.getWorkflowProcess().getTransitions();
		list.remove(null);
		for (BasicTransitionEx ex : list) {
			if (activityid.equals(ex.getFrom())) { // 指向性判断-->其他活动箭头的来源如果等于当前活动，那么证明这些活动是当前活动的下级活动
				if (ex.getToActivity().getImplementation() instanceof Activity) {// 如果当前环节指向的环节是子流程
					SubFlow sub = (SubFlow) ex.getToActivity().getImplementation();// 子流程流程定义
					String pk = sub.getId();// 得到子流程流程定义的id
					String apk = sub.getPk();// 得到子流程活动的pk
					nextlink(pk, apk, al);// 递归查询子流程
				} else if (ex.getToActivity() instanceof Activity
						&& ex.getToActivity().getActivityType() == ActivityTypeEnum.Route.getIntValue()) {// 虚活动
					Activity act = ex.getToActivity();
					nextlink(act.getWorkflowProcess().getPrimaryKey(), act.getId(), al);// 递归虚节点的下一个节点
				} else {// 不是子流程
					al.add(ex.getTo());
				}
			}
		}
	}

	/**
	 * 根据活动实例执行顺序查询出最后执行的活动实例对应的工作项 给共享服务提供,工作流中的取消审批受不重走流程这个功能影响
	 * 
	 * @param wfprocessdefpk     流程实例
	 * @param activityid         活动id
	 * @param activityInstancePK 活动实例
	 * @return
	 * @throws BusinessException
	 */
	public boolean checkLatestFinishedWorkitem(String wfprocessdefpk, String activityid, String InstancePK)
			throws BusinessException {
		ArrayList<String> to = new ArrayList<String>();// 分支时箭头的指向性
		nextlink(wfprocessdefpk, activityid, to);
		if (to.size() == 0) {
			return false;
		}
		String whereCond = SQLUtil.buildSqlForIn("a.activitydefid", to.toArray(new String[to.size()]));
		String sql = "select actstatus from pub_wf_actinstance a INNER JOIN  pub_wf_task t ON a.Pk_WF_ACTINSTANCE = t.pk_wf_actinstance  where "
				+ "" + whereCond + " and a.PK_WF_INSTANCE = '"+InstancePK+"' and (actstatus = 1 or (actstatus = 4 and taskresult = 'R'))";
		ArrayList<String[]> result = (ArrayList<String[]>) new BaseDAO().executeQuery(sql, new ArrayListProcessor());
		if (null == result || result.size() == 0) {// 查询结果为空
			return false;
		} else {
			return true;
		}

	}

	/**
	 * 根据活动实例执行顺序查询出最后执行的活动实例对应的工作项
	 * 
	 * @param noteVOs
	 * @return
	 */
	public WorkflownoteVO findLatestFinishedWorkitem(WorkflownoteVO[] noteVOs) {
		Set<String> notePks = new HashSet<String>();
		Map<String, WorkflownoteVO> pk_notevo_map = new HashMap<String, WorkflownoteVO>();
		for (WorkflownoteVO noteVO : noteVOs) {
			notePks.add(noteVO.getPk_checkflow());
			pk_notevo_map.put(noteVO.getPk_checkflow(), noteVO);
		}
		List<Map<String, Object>> notepk_actpk_map = findAcitityInstancePks(notePks.toArray(new String[0]));
		Map<String, String> actpk_notepks = new HashMap<String, String>();
		List<String> actpks = new ArrayList<String>();
		for (Map<String, Object> rowMap : notepk_actpk_map) {
			String actpk = (String) rowMap.get("pk_wf_actinstance");
			String notepk = (String) rowMap.get("pk_checkflow");
			actpk_notepks.put(actpk, notepk);
			actpks.add(actpk);
		}
		// 来源-目的单据PK
		List<Map<String, Object>> src_target_actpks = findSrcTargetActivityPks(actpks.toArray(new String[0]));
		List<String> srcActPks = new ArrayList<String>();
		for (Map<String, Object> rowMap : src_target_actpks) {
			String srcActPk = (String) rowMap.get("src_actinstance");
			srcActPks.add(srcActPk);
		}
		VoComparator comparator = new VoComparator();
		WorkflownoteVO lastNoteVO = null;
		for (String actpk : actpks) {
			if (!srcActPks.contains(actpk)) {
				String lastNotePk = actpk_notepks.get(actpk);
				WorkflownoteVO noteVO = pk_notevo_map.get(lastNotePk);
				if (lastNoteVO == null || comparator.compare(noteVO, lastNoteVO) < 0) {
					lastNoteVO = noteVO;
				}
			}
		}
		if (lastNoteVO != null) {
			return lastNoteVO;
		} else {
			return noteVOs[0];
		}
	}

	public List<Map<String, Object>> findAcitityInstancePks(String[] notePks) {
		String whereCond = SQLUtil.buildSqlForIn("note.pk_checkflow", notePks);
		String sql = "select note.pk_checkflow, task.pk_wf_actinstance from pub_wf_task task , pub_workflownote note where note.pk_wf_task = task.pk_wf_task and "
				+ whereCond;
		BaseDAO dao = new BaseDAO();
		List<Map<String, Object>> mapList = null;
		try {
			mapList = (List<Map<String, Object>>) dao.executeQuery(sql, new MapListProcessor());
		} catch (DAOException e) {
			Logger.error(e.getMessage(), e);
		}
		return mapList;
	}

	/**
	 * 根据流程定义id查找子流程列表
	 * 
	 * @param processDefPK
	 * @return
	 */
	public void findProcessSubflow(String processDefPK, String processInstancePK, List<ProcessSubflow> subflowList) {
		String sql = "select def.pk_wf_def,def.processdefid from pub_wfdef_ref ref,pub_wf_def def where ref.processdefid=def.processdefid and ref.pk_wf_def=? ";
		BaseDAO dao = new BaseDAO();
		ProcessSubflow subflow = null;
		try {
			SQLParameter para = new SQLParameter();
			para.addParam(processDefPK);
			List<Map<String, Object>> mapList = (List<Map<String, Object>>) dao.executeQuery(sql, para,
					new MapListProcessor());
			if (mapList != null && mapList.size() != 0) {
				for (Map<String, Object> rowMap : mapList) {
					subflow = new ProcessSubflow(new SequenceGenerator().generate());// 这行代码需要放在for里面，否则两个子流程串行会
					subflow.setSubDefPK((String) rowMap.get("pk_wf_def"));
					subflow.setSubProcessDefPK((String) rowMap.get("processdefid"));
					subflow.setProcessInstance(processInstancePK);
					subflowList.add(subflow);
					findProcessSubflow(subflow.getSubDefPK(), processInstancePK, subflowList);
				}
			}
		} catch (DAOException e) {
			Logger.error(e.getMessage(), e);
		}
	}

	/**
	 * 判断子流程对应的主流程是否已经启动
	 * 
	 * @param processdefPK
	 * @return
	 * @throws DbException
	 */
	public String hasProcessSubflow(String processdefPK) throws DbException {
		String sql = "select pk_wf_instance from pub_instance_def where pk_wf_def =? ";
		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();
			SQLParameter para = new SQLParameter();
			para.addParam(processdefPK);
			Object obj = jdbc.executeQuery(sql, para, new ColumnProcessor(1));
			return obj == null ? null : String.valueOf(obj);
		} finally {
			if (persist != null)
				persist.release();
		}
	}

	private List<Map<String, Object>> findSrcTargetActivityPks(String[] activityPks) {
		String whereCond = SQLUtil.buildSqlForIn("target_actinstance", activityPks);
		String sql = "select src_actinstance,target_actinstance from pub_wf_actinstancesrc where " + whereCond;
		BaseDAO dao = new BaseDAO();
		List<Map<String, Object>> mapList = null;
		try {
			mapList = (List<Map<String, Object>>) dao.executeQuery(sql, new MapListProcessor());
		} catch (DAOException e) {
			Logger.error(e.getMessage(), e);
		}
		return mapList;
	}

	/**
	 * 专为柔性控制使用
	 * 
	 * @param paraVo
	 * @param iWorkflowOrApproveflow 主流程类型，见<code>WorkflowTypeEnum.Approveflow</code>和
	 *                               <code>WorkflowTypeEnum.Workflow</code>
	 * @return WorkflownoteVO
	 */
	public WorkflownoteVO checkUnfinishedWorkitemForRX(PfParameterVO paraVo) throws BusinessException, DbException {
		WorkflownoteVO noteVO = paraVo.m_workFlow;
		TaskManagerDMO taskDmo = new TaskManagerDMO();
		WFTask currentTask = taskDmo.getTaskByPK(noteVO.getPk_wf_task());

		WorkflowProcess wp = findParsedWfProcess(currentTask.getWfProcessDefPK(), currentTask.getWfProcessInstancePK());

		noteVO.getRelaProperties()
				.putAll(findRelaProps(wp.findActivityByID(currentTask.getActivityID()), paraVo.m_billType));

		return noteVO;

	}

	/**
	 * 检查某用户的待办工作项
	 * <li>如果无待办工作项且流程已经启动，则抛出异常
	 * 
	 * @param paraVo
	 * @param iWorkflowOrApproveflow 主流程类型，见<code>WorkflowTypeEnum.Approveflow</code>和
	 *                               <code>WorkflowTypeEnum.Workflow</code>
	 * @return WorkflownoteVO
	 */
	public WorkflownoteVO checkUnfinishedWorkitem(PfParameterVO paraVo, int iWorkflowOrApproveflow)
			throws BusinessException, DbException {
		// 查询某用户对某单据的所有待办工作项
		WorkflownoteVO[] uncheckedNoteVOs = queryWorkitemsNotCheck(paraVo.m_billType, paraVo.m_billVersionPK,
				paraVo.m_operator, iWorkflowOrApproveflow);
		if (uncheckedNoteVOs != null && uncheckedNoteVOs.length > 0) {
			// 当前操作员有待办工作项
			WorkflownoteVO noteVO = uncheckedNoteVOs[uncheckedNoteVOs.length - 1];// FIXME:某用户对某个单据可能同时有两个待办工作项吗？
			TaskManagerDMO taskDmo = new TaskManagerDMO();
			WFTask currentTask = taskDmo.getTaskByPK(noteVO.getPk_wf_task());
			if (currentTask == null) {
				throw new PFBusinessException(NCLangResOnserver.getInstance().getStrByID("pfworkflow",
						"UPPpfworkflow-000262")/* 当前操作人待办任务丢失 */);
			} else if ((currentTask.getTaskType() == WfTaskType.Makebill.getIntValue()
					&& currentTask.getRejectTacheActivityID() == null)
					|| currentTask.getTaskType() == WfTaskType.Withdraw.getIntValue())
				throw new PFBusinessException(
						NCLangResOnserver.getInstance().getStrByID("pfworkflow", "UPPpfworkflow-000388")/*
																										 * 没有待办任务。
																										 */);

			// 校验流程实例的状态
			if (hasRunningProcessInstances(currentTask.getWfProcessDefPK(), paraVo.m_billVersionPK) == null) {
				throw new PFBusinessException(NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow",
						"EngineService-0004")/*
												 * 该单据所处的流程实例已被挂起 ！
												 */);
			}
			WorkflowProcess wp = findParsedWfProcess(currentTask.getWfProcessDefPK(),
					currentTask.getWfProcessInstancePK());
			if (wp == null)
				return null; // throw new Exception("没有对应的流程定义");

			currentTask.setBillNO(paraVo.m_billNo);
			currentTask.setBillID(paraVo.m_billId);
			currentTask.setBillversionPK(paraVo.m_billVersionPK);
			// currentTask.setBusiType(paraVo.m_businessType);
			currentTask.setBillType(paraVo.m_billType);
			currentTask.setPk_org(paraVo.m_pkOrg);
			currentTask.setTopic(noteVO.getMessagenote());

			currentTask.setWorkflowType(noteVO.getWorkflow_type());
			// noteVO.setSenddate(new UFDateTime(System.currentTimeMillis()));
			currentTask.setWorknoteVO(noteVO);

			// yanke1 2013-4-18
			// 修改前这里的代码为 currentTask.setSenderman(paraVo.m_operator);
			// paraVo.m_operator通常为当前登录人
			// 使用它作为currentTask的senderMan是不准确的
			// 应当使用noteVO的senderMan
			currentTask.setSenderman(noteVO.getSenderman());

			// 获取该活动后继活动的可指派信息
			WfTaskInfo taskInfo = createTaskInfo(currentTask, wp, paraVo.m_preValueVo);
			noteVO.setTaskInfo(taskInfo);

			// 获得活动的一些扩展属性
			noteVO.getRelaProperties()
					.putAll(findRelaProps(wp.findActivityByID(currentTask.getActivityID()), paraVo.m_billType));

			return noteVO;
		} else {
			// 当前操作员无待办工作项
			HashSet<String> hsRet = hasInstanceOfValid(new String[] { paraVo.m_billVersionPK }, paraVo.m_billType,
					iWorkflowOrApproveflow);
			boolean startup = hsRet.contains(paraVo.m_billVersionPK);
			if (startup) {
				// 该单据有流程实例
				throw new PFBusinessException(
						NCLangResOnserver.getInstance().getStrByID("pfworkflow", "UPPpfworkflow-000263")/*
																										 * 当前操作人没有待办任务 .
																										 */);
			} else {
				// 该单据无流程实例()
				WorkflowProcess wp = null;
				if (StringUtil.isEmptyWithTrim(paraVo.m_flowDefPK))
					wp = queryWfProcessCanStart2(paraVo.m_billType, paraVo.m_pkGroup, paraVo.m_pkOrg,
							/**
							 * 注意此处要按制单人检查，按操作人检查会出现， 新增未提交单据直接点审批时
							 * ，不同人的可能会匹配到流程，也可能匹配不到。而且从流程的含义上将也应该是根据制单人启动流程
							 */
							paraVo.m_makeBillOperator, paraVo.emendEnum, iWorkflowOrApproveflow);
				else
					wp = findParsedWfProcess(paraVo.m_flowDefPK, null);
				if (wp == null) {
					// 该制单人无流程定义，任何人都可审批或执行
					return null;
				} else {
					throw new PFBusinessException(NCLangResOnserver.getInstance().getStrByID("pfworkflow",
							"UPPpfworkflow-000389")/*
													 * 该单据有可启动的流程但未启动，无法流转 。
													 */);
				}
			}
		}

	}

	/**
	 * 得到活动相关的流程属性，目前有
	 * <li>XPDLNames.WORKFLOW_GADGET - 单据组件及其实参
	 * <li>XPDLNames.EDITABLE_PROPERTIES - 可编辑属性
	 * <li>XPDLNames.ENABLE_BUTTON - 可用按钮
	 * 
	 * @param act
	 * @param strBilltype
	 * @return
	 * @since 6.0
	 */
	private Map findRelaProps(Activity act, String strBilltype) {
		HashMap map = new HashMap();
		// 单据组件及其实参
		map.put(XPDLNames.WORKFLOW_GADGET, findGadget(act, strBilltype));
		// 可编辑属性
		Object editableProps = CoreUtilities.getValueOfExtendedAttr(act, XPDLNames.EDITABLE_PROPERTIES);
		map.put(XPDLNames.EDITABLE_PROPERTIES, editableProps);
		// 可用按钮
		Object enableBtns = CoreUtilities.getValueOfExtendedAttr(act, XPDLNames.ENABLE_BUTTON);
		map.put(XPDLNames.ENABLE_BUTTON, enableBtns);
		// 业务参数
		if (act instanceof GenericActivityEx) {
			map.put(XPDLNames.APPLICATION_ARGS, ((GenericActivityEx) act).getApplicationArgs());
		}
		// 是否可指派
		map.put(XPDLNames.IS_ASSIGN, CoreUtilities.canAssign(act));
		// 是否可改派
		map.put(XPDLNames.CAN_TRANSFER, CoreUtilities.canTransfer(act));
		// 是否可加签
		map.put(XPDLNames.CAN_ADDAPPROVER, CoreUtilities.canAddAprover(act));
		// 是否CA认证
		map.put(XPDLNames.ELECSIGNATURE, ((GenericActivityEx) act).isElectSignature());

		return map;
	}

	/**
	 * 单据"提交"启动审批流时调用
	 * 
	 * @param paraVo 动作执行参数VO
	 * @param status 单据状态：自由态，提交态，未通过
	 * @return
	 * @throws BusinessException
	 * @throws DbException
	 */
	public WorkflownoteVO queryApproveflowOnSave(PfParameterVO paraVo, int status)
			throws BusinessException, DbException {
		WFTask beginTask = null;
		WorkflowProcess wp = null;
		WorkflownoteVO worknoteVO = null;
		WorkflownoteVO[] uncheckedNoteVOs = queryWorkitemsNotCheck(paraVo.m_billType, paraVo.m_billVersionPK,
				paraVo.m_makeBillOperator, WorkflowTypeEnum.Approveflow.getIntValue());
		if (uncheckedNoteVOs != null && uncheckedNoteVOs.length > 0) {
			TaskManagerDMO taskDmo = new TaskManagerDMO();
			WFTask currentTask = taskDmo.getTaskByPK(uncheckedNoteVOs[0].getPk_wf_task());
			if (currentTask != null && WFTask.APPROVERESULT_REJECT.equals(currentTask.getApproveResult())) {
				// NC_USER0000000000000 是系统用户，系统用户制单的驳回后谁都可以提交
//				if (null != paraVo.m_makeBillOperator && null != paraVo.m_operator
//						&& !paraVo.m_makeBillOperator.equals(paraVo.m_operator)
//						&& !paraVo.m_makeBillOperator.equals("NC_USER0000000000000")) {
//					throw new PFBusinessException(
//							NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow", "UPPpfworkflow-000929")
//					/** 驳回的单据只有原制单人才能够再次提交 */
//					);
//				}
			}
			if (currentTask != null && !StringUtil.isEmptyWithTrim(currentTask.getRejectTacheActivityID())) {
				beginTask = currentTask;
				paraVo.m_flowDefPK = currentTask.getWfProcessDefPK();
				WorkflownoteVO[] workflownotes = taskDmo.queryStartedWorkitemsOfTask(currentTask.getTaskPK());
				if (workflownotes != null) {
					worknoteVO = workflownotes[0];
				}
				// 驳回时候生成修单任务，此时说明信息不清空
				if (worknoteVO.getActiontype().equals(WorkflownoteVO.WORKITEM_TYPE_MAKEBILL)) {
					beginTask.setTopic(worknoteVO.getMessagenote());
				}
			}
		}

		if (paraVo.getCustomProperty(PfUtilBaseTools.PARAM_FORCESTART) != null) {
			// 如果指定了forcestart参数
			// 那么继续往下走
		} else {
			switch (status) {
			// 若参数中指定了forcestart
			// 那么即使审批通过状态也要继续走下去
			case IPfRetCheckInfo.COMMIT:
			case IPfRetCheckInfo.NOPASS:
			case IPfRetCheckInfo.NOSTATE:

				break;
			default:
				return null;
			} // /{end switch}
		}

		// 查询可启动的流程定义
		if (StringUtil.isEmptyWithTrim(paraVo.m_flowDefPK)) {
			wp = queryWfProcessCanStart2(paraVo.m_billType, paraVo.m_pkGroup, paraVo.m_pkOrg, paraVo.m_makeBillOperator,
					paraVo.emendEnum, WorkflowTypeEnum.Approveflow.getIntValue());
		} else {
			wp = findParsedWfProcess(paraVo.m_flowDefPK, null);
		}

		if (wp == null) {
			return null;
		}

		// 新建任务要赋 工作流ID,活动ID和单据号
		if (beginTask == null) {
			beginTask = WfTaskManager.getInstance().createTask(paraVo.m_pkOrg,
					WorkflowTypeEnum.Approveflow.getIntValue());
		}

		beginTask.setWfProcessDefPK(wp.getPrimaryKey());
		beginTask.setActivityID(wp.findStartActivity().getId());
		beginTask.setBillNO(paraVo.m_billNo);
		beginTask.setBillID(paraVo.m_billId);
		beginTask.setBillversionPK(paraVo.m_billVersionPK);
		// beginTask.setBusiType(paraVo.m_businessType);
		beginTask.setBillType(paraVo.m_billType);
		beginTask.setPk_org(paraVo.m_pkOrg);

		// yanke1 2013-4-18
		// 修改前这里的原始代码为：beginTask.setSenderman(paraVo.m_operator);
		// 此处的senderMan会放置在流程实例中作为制单人以及后续的上级限定的参数
		// 因此取paraVo.m_operator是不恰当的
		// 因此改为取makeBillOperator
		beginTask.setSenderman(paraVo.m_makeBillOperator);
		// 获得任务所属活动的后继指派信息
		WfTaskInfo taskInfo = createTaskInfo(beginTask, wp, paraVo.m_preValueVo);

		if (worknoteVO == null) {
			worknoteVO = new WorkflownoteVO();
		}
		beginTask.setWorknoteVO(worknoteVO);
		worknoteVO.setTaskInfo(taskInfo);
		worknoteVO.setActiontype(paraVo.m_actionName);
		Activity activity = wp.findStartActivity();
		worknoteVO.getRelaProperties().putAll(findRelaProps(activity, paraVo.m_billType));
		return worknoteVO;
	}

	/**
	 * 获取该活动后继活动的可指派信息
	 * <li>包括该活动后继可手工选择的转移、后继活动的参与者指派信息
	 * 
	 * @param currentTask
	 * @param wp
	 * @param billvo
	 * @return
	 * @throws DbException
	 */
	private static WfTaskInfo createTaskInfo(WFTask currentTask, WorkflowProcess wp, AggregatedValueObject billvo)
			throws DbException, BusinessException {
		// 排除指派的情况
		WfTaskInfo taskInfo = new WfTaskInfo(currentTask);
		
		WorkflownoteVO note=null;
		if(currentTask!=null) {
			note=currentTask.getWorknoteVO();
		}
		
		//加签审批时不需要再重新获取指派相关信息
		if(note!=null&&StringUtils.isNotBlank(note.getActiontype())) {
			if(note.getActiontype().endsWith("_A")) {
				return taskInfo;
			}
		}
		
		if (currentTask.getRejectTacheActivityID() == null) {
			// 获取该活动后继活动的参与者指派信息
			ArrayList alAssignableInfos = WfDispatchUtils.getAfterAssignInfos(currentTask, wp, billvo);

			new ProcessInstance().findActivityInstanceByPK("3ac584d5-7ff8-11e8-b908-77c209b4795a");

			taskInfo.getAssignableInfos().addAll(alAssignableInfos);
			taskInfo.setAssignablePostCondition(WfDispatchUtils.isAssignablePostCondition(wp, currentTask));
		}
		if (wp.getWorkflowType() == WorkflowTypeEnum.Workflow.getIntValue()
				|| wp.getWorkflowType() == WorkflowTypeEnum.SubWorkflow.getIntValue()) {
			// 该活动后继可手工选择的转移信息 leijun+2008-9
			ArrayList alTransitionSelectalbeInfos = WfDispatchUtils.getAfterTransitionSelectableInfos(currentTask, wp,
					billvo);
			taskInfo.getTransitionSelectableInfos().addAll(alTransitionSelectalbeInfos);
		}
		return taskInfo;
	}

	/**
	 * 
	 * @param currentTask
	 * @param wp
	 * @param billvo
	 * @return
	 * @throws DbException
	 * @throws BusinessException
	 */
	public WfTaskInfo createTaskInfo4NCC(WFTask currentTask, AggregatedValueObject billvo)
			throws DbException, BusinessException {
		WorkflowProcess wp = findParsedWfProcess(currentTask.getWfProcessDefPK(), currentTask.getWfProcessInstancePK());

		if (wp == null) {
			throw new BusinessException("请检查流程定义是否被删除");
		}

		return createTaskInfo(currentTask, wp, billvo);
	}

	/**
	 * 判断某操作员是否为单据的审核人（存在有效的审批任务）
	 * 
	 * @param billIdAry
	 * @param billType
	 * @param userId
	 * @return
	 * @throws BusinessException
	 * @throws DbException
	 */
	public String[] isCheckmanAry(String[] billIdAry, String billType, String userId)
			throws BusinessException, DbException {
		ArrayList<String> alRet = new ArrayList<String>();
		// key :billid ;value:IFlowBizItf
		Map<String, IFlowBizItf> idMap = new HashMap<String, IFlowBizItf>();
		IPFConfig qry = (IPFConfig) NCLocator.getInstance().lookup(IPFConfig.class.getName());
		for (String billId : billIdAry) {
			AggregatedValueObject billVO = qry.queryBillDataVO(billType, billId);
			if (billVO == null)
				throw new PFBusinessException(NCLangRes4VoTransl.getNCLangRes().getStrByID("busitype",
						"busitypehint-000063")/*
												 * 错误 ： 根据单据类型和单据ID获取不到单据聚合VO
												 */);
			IFlowBizItf fbi = PfMetadataTools.getBizItfImpl(billVO, IFlowBizItf.class);
			if (fbi == null)
				throw new PFRuntimeException(NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow",
						"EngineService-0005")/* 元数据实体没有提供业务接口IFlowBizItf的实现类 */);
			idMap.put(fbi.getBillVersionPK(), fbi);
		}

		List<String> versionPkList = new ArrayList<String>();
		for (IFlowBizItf itf : idMap.values()) {
			versionPkList.add(itf.getBillVersionPK());
		}
		// 版本PK数组
		String[] billVersionAry = versionPkList.toArray(new String[0]);

		// 查询单据Id数组中哪些拥有有效的审批流实例
		HashSet<String> hsHasInstance = hasInstanceOfValid(billVersionAry, billType,
				WorkflowTypeEnum.Approveflow.getIntValue());

		// 查询该单据类型是否定义了审批流
		String sqlHasDef = "select billtype from pub_wf_def where validity=" + WorkflowDefStatusEnum.Valid.getIntValue()
				+ " and (billtype=? or billtype=?) "
				+ getWorkflowTypeAndWhere(WorkflowTypeEnum.Approveflow.getIntValue(), "");

		// 查询某人可审批的单据VersionPK数组
		String sqlWorkitem = "select billVersionPK from pub_workflownote "
				+ "where (pk_billtype=? or pk_billtype=?) and checkman=? and approvestatus in("
				+ WfTaskOrInstanceStatus.getUnfinishedStatusSet() + ") and actiontype not in('"
				+ WorkflownoteVO.WORKITEM_TYPE_BIZ + "','" + WorkflownoteVO.WORKITEM_TYPE_MAKEBILL + "') ";
		;
		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();
			SQLParameter para = new SQLParameter();
			para.addParam(billType);
			if (PfUtilBaseTools.isTranstype(billType)) {
				para.addParam(PfUtilBaseTools.getRealBilltype(billType));
			} else
				para.addParam(billType);
			Object hasDef = jdbc.executeQuery(sqlHasDef, para, new ColumnProcessor(1));

			para.addParam(userId);
			ArrayList alCanCheckIds = (ArrayList) jdbc.executeQuery(sqlWorkitem, para, new ColumnListProcessor(1));

			for (String strVersion : billVersionAry) {
				if (hsHasInstance.contains(strVersion)) {
					if (alCanCheckIds.contains(strVersion))
						// 如果单据ID启动了审批流，且轮到该用户审批
						alRet.add(idMap.get(strVersion).getBillId());
				} else {
					if (hasDef == null) {
						// 如果单据ID没有审批流实例，且该单据类型没定义审批流，则任何人都可直批
						alRet.add(idMap.get(strVersion).getBillId());
					} else {
						IFlowBizItf fbi = idMap.get(strVersion);
						if (fbi == null)
							continue;
						IWorkflowDefine wfDefine = NCLocator.getInstance().lookup(IWorkflowDefine.class);
						Logger.debug("查询流程定义: billType=" + billType + ";pkOrg=" + fbi.getPkorg() + ";userId="
								+ fbi.getBillMaker() + ";开始");
						boolean hasValidProcessDef = wfDefine.hasValidProcessDef(
								InvocationInfoProxy.getInstance().getGroupId(), billType, fbi.getPkorg(),
								fbi.getBillMaker(), fbi.getEmendEnum(), WorkflowTypeEnum.Approveflow.getIntValue());
						if (!hasValidProcessDef) {
							alRet.add(idMap.get(strVersion).getBillId());
						}
					}
				}
			}
		} finally {
			if (persist != null)
				persist.release();
		}
		return alRet.toArray(new String[0]);
	}

	/**
	 * 判断单据ID数组是否存在审批流或工作流实例
	 * <li>处于有效状态的，即启动或完成状态 根据BillID查询
	 * 
	 * @param billIdAry
	 * @param billType
	 * @param iWorkflowOrApproveflow
	 * @return 存在流程实例的单据ID数组
	 */
	private HashSet<String> hasInstanceOfValidByBillID(String[] billIdAry, final String billType,
			final int iWorkflowOrApproveflow) throws BusinessException {
		final HashSet<String> hsRet = new HashSet<String>();
		InSqlBatchCaller caller = new InSqlBatchCaller(billIdAry);
		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			final JdbcSession jdbc = persist.getJdbcSession();
			caller.execute(new IInSqlBatchCallBack() {
				public Object doWithInSql(String inSql) throws BusinessException, SQLException {
					String sql = "select billid from pub_wf_instance where billid in " + inSql + " and procstatus!=?"
							+ getWorkflowTypeAndWhere(iWorkflowOrApproveflow, "");
					SQLParameter para = new SQLParameter();
					para.addParam(WfTaskOrInstanceStatus.Inefficient.getIntValue());

					try {
						ArrayList alBillId = (ArrayList) jdbc.executeQuery(sql, para,
								new ColumnListProcessor("billid"));
						hsRet.addAll(alBillId);
					} catch (DbException e) {
						Logger.error(e.getMessage(), e);
						throw new PFBusinessException(
								NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow", "UPPpfworkflow-000060")
										/** 查询流程实例表发生数据库异常： */
										+ e.getMessage(),
								e);
					}
					return null;
				}
			});
			return hsRet;
		} catch (DbException e) {
			Logger.error(e.getMessage(), e);
			throw new PFBusinessException(
					NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow", "UPPpfworkflow-000060")
							/** 查询流程实例表发生数据库异常： */
							+ e.getMessage(),
					e);
		} catch (SQLException e) {
			Logger.error(e.getMessage(), e);
			throw new PFBusinessException(
					NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow", "UPPpfworkflow-000060")
							/** 查询流程实例表发生数据库异常： */
							+ e.getMessage(),
					e);
		} finally {
			if (persist != null)
				persist.release();
		}

	}

	/**
	 * 查询单据所处的流程状态
	 * 
	 * @param billId
	 * @param billType
	 * @param result
	 * @return
	 * @throws DbException
	 */
	public int queryFlowStatus(String billId, String billType, int flowType, String taskResult) throws DbException {
		IFlowStatusService flowStatusService = AbstractFlowStatusService.getFlowStatusService(flowType);
		return flowStatusService.queryFlowStatus(billId, billType, taskResult);
	}

	public int queryWorkflowStatus(String billId, String billType) throws DbException {
		return queryFlowStatus(billId, billType, WorkflowTypeEnum.Workflow.getIntValue(), null);
	}

	public int queryApproveflowStatus(String billId, String billType) throws DbException {
		return queryFlowStatus(billId, billType, WorkflowTypeEnum.Approveflow.getIntValue(), null);
	}

	/**
	 * 单据启动工作流时调用
	 * 
	 * @param paraVo 动作执行参数VO
	 * @param status 单据状态：自由态，提交态
	 * @return
	 * @throws BusinessException
	 * @throws DbException
	 */
	public WorkflownoteVO queryWorkflowOnSave(PfParameterVO paraVo, int status) throws BusinessException, DbException {

		WFTask beginTask = null;
		WorkflowProcess wp = null;
		WorkflownoteVO noteVO = null;

		if (paraVo.getCustomProperty(PfUtilBaseTools.PARAM_FORCESTART) != null) {
			// 如果指定了forcestart参数
			// 那么继续执行
		} else {
			// 否则看情况

			switch (status) {
			case IPfRetCheckInfo.COMMIT: // 提交态
			case IPfRetCheckInfo.NOSTATE: // 自由态
				// 提交态和自由态继续往下走
				break;
			default:
				// 否则返回null，不继续走了
				return null;
			} // /{end switch}
		}

		// 查询流程定义
		if (StringUtil.isEmptyWithTrim(paraVo.m_flowDefPK))
			wp = queryWfProcessCanStart2(paraVo.m_billType, paraVo.m_pkGroup, paraVo.m_pkOrg, paraVo.m_makeBillOperator,
					paraVo.emendEnum, WorkflowTypeEnum.Workflow.getIntValue());
		else
			wp = findParsedWfProcess(paraVo.m_flowDefPK, null);

		if (wp == null) {
			// 没有对应的工作流定义，直接抛出异常 XXX:leijun@2008-11
			// return null;
			throw new PFBusinessException(NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow",
					"UPPpfworkflow-000058")/*
											 * 找不到工作流定义， 无法启动工作流
											 */);
		}

		noteVO = new WorkflownoteVO();

		// 新建任务要赋 工作流ID,活动ID和单据号
		beginTask = WfTaskManager.getInstance().createTask(paraVo.m_pkOrg, WorkflowTypeEnum.Workflow.getIntValue());

		beginTask.setWfProcessDefPK(wp.getPrimaryKey());
		beginTask.setActivityID(wp.findStartActivity().getId());
		beginTask.setBillNO(paraVo.m_billNo);
		beginTask.setBillID(paraVo.m_billId);
		beginTask.setBillversionPK(paraVo.m_billVersionPK);
		// beginTask.setBusiType(paraVo.m_businessType);
		beginTask.setBillType(paraVo.m_billType);
		beginTask.setPk_org(paraVo.m_pkOrg);
		beginTask.setTopic(noteVO.getMessagenote());

		beginTask.setWorknoteVO(noteVO);
		beginTask.setSenderman(paraVo.m_operator);

		// 获得任务所属活动的后继指派信息
		WfTaskInfo taskInfo = createTaskInfo(beginTask, wp, paraVo.m_preValueVo);

		noteVO.setTaskInfo(taskInfo);
		noteVO.setActiontype(paraVo.m_actionName);
		// 获得起始活动的一些扩展属性
		noteVO.getRelaProperties().putAll(findRelaProps(wp.findStartActivity(), paraVo.m_billType));

		return noteVO;
	}

	/**
	 * 查询某个活动关联的工作流组件及其实参
	 * 
	 * @param act
	 * @param strBilltype
	 * @return
	 */
	public WorkflowgadgetVO findGadget(Activity act, String strBilltype) {
		Object gadgetValue = CoreUtilities.getValueOfExtendedAttr(act, XPDLNames.WORKFLOW_GADGET);
		if (gadgetValue == null || gadgetValue.toString().length() == 0)
			return null;

		// 查询某个活动关联的工作流组件
		String pk_gadget = gadgetValue.toString();
		Logger.debug("查询活动关联的工作流组件，组件ID=" + pk_gadget);
		WorkflowgadgetVO realGadgetVO = null;
		ArrayList<WorkflowgadgetVO> alGadget = PfDataCache
				.getWorkflowGadget(PfUtilBaseTools.getRealBilltype(strBilltype));
		for (WorkflowgadgetVO formalGadgetVO : alGadget) {
			if (pk_gadget.equalsIgnoreCase(formalGadgetVO.getPk_workflowgadget())) {
				realGadgetVO = WorkflowgadgetVO.copySelfWithBody(formalGadgetVO);
			}
		}
		if (realGadgetVO == null) {
			Logger.debug("后台缓存中找不到工作流组件，组件ID=" + pk_gadget);
			return null;
		}

		// 查询该工作流组件的实参
		Object paramValue = CoreUtilities.getValueOfExtendedAttr(act, XPDLNames.WORKFLOW_GADGET_PARAM);
		if (paramValue != null && paramValue.toString().length() > 0) {
			WfGadgetBodyVO[] existedParams = CoreUtilities.getRealParamFromStr(paramValue.toString());
			HashMap<String, String> hmRet = new HashMap<String, String>();
			for (WfGadgetBodyVO param : existedParams) {
				hmRet.put(param.getVarcode(), param.getValue());
			}

			for (WfGadgetBodyVO realParam : realGadgetVO.getBodys()) {
				String strValue = hmRet.get(realParam.getVarcode());
				realParam.setValue(strValue);
			}
		}

		return realGadgetVO;
	}

	/**
	 * 从流程实例中查询流程发起者，很可能就是制单人
	 * 
	 * @param billId
	 * @param billType
	 * @param iWorkflowtype
	 * @return
	 * @throws BusinessException
	 */
	public String queryBillmakerOfInstance(String billId, String billType, int iWorkflowtype) throws BusinessException {
		String sql = "select billmaker from pub_wf_instance where billversionpk=? and billtype=? and procstatus!=?"
				+ getWorkflowTypeAndWhere(iWorkflowtype, "") + " order by pk_wf_instance";
		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();
			SQLParameter para = new SQLParameter();
			para.addParam(billId);
			para.addParam(billType);
			para.addParam(WfTaskOrInstanceStatus.Inefficient.getIntValue());
			Object obj = jdbc.executeQuery(sql, para, new ColumnProcessor());
			return obj == null ? null : obj.toString();
		} catch (DbException e) {
			Logger.error(e.getMessage(), e);
			throw new PFBusinessException(NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow",
					"UPPpfworkflow-000061")/*
											 * 查询流程实例发生数据库异常 ：
											 */
					+ e.getMessage(), e);
		} finally {
			if (persist != null)
				persist.release();
		}
	}

	/**
	 * 从流程实例中查询流程发起者，很可能就是制单人
	 * 
	 * @param billId
	 * @param billType
	 * @param iWorkflowtype
	 * @return
	 * @throws BusinessException
	 */
	public ProcessInstance queryProcessInstance(String billId, String billType, int iWorkflowtype)
			throws BusinessException {
		String cond = "billversionpk=? and billtype=? and procstatus!=?" + getWorkflowTypeAndWhere(iWorkflowtype, "")
				+ " order by pk_wf_instance";

		SQLParameter param = new SQLParameter();
		param.addParam(billId);
		param.addParam(billType);
		param.addParam(WfTaskOrInstanceStatus.Inefficient.getIntValue());

		BaseDAO dao = new BaseDAO();
		Collection<ProcessInstance> col = dao.retrieveByClause(ProcessInstance.class, ProcessInstance.MAPPING_META,
				cond, param);

		if (col != null && col.size() > 0) {
			return col.iterator().next();
		} else {
			return null;
		}
	}

	/**
	 * 查询流程实例所属的流程定义的主流程
	 * 
	 * @param wfProcessInstancePK 流程实例PK
	 * @return
	 * @throws BusinessException
	 */
	public BasicWorkflowProcess findParsedMainWfProcessByInstancePK(String wfProcessInstancePK)
			throws BusinessException {
		// 查询流程实例是否为主流程
		String sql1 = "select src_pk_actinstance,processdefid from pub_wf_instance where pk_wf_instance=?";

		// 查询活动所属的流程实例
		String sql2 = "select pk_wf_instance from pub_wf_actinstance where pk_wf_actinstance=?";

		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();
			SQLParameter para = new SQLParameter();
			para.addParam(wfProcessInstancePK);
			Object[] objs = (Object[]) jdbc.executeQuery(sql1, para, new ArrayProcessor());
			String strActInstPK = objs[0] == null ? null : objs[0].toString();
			String strDefPK = objs[1] == null ? null : objs[1].toString();
			if (StringUtil.isEmpty(strActInstPK)) {
				// 说明该流程实例为主流程
				return findParsedWfProcess(strDefPK, wfProcessInstancePK);
			} else {
				// 该流程实例为子流程，递归
				SQLParameter para2 = new SQLParameter();
				para2.addParam(strActInstPK);
				String procInst = (String) jdbc.executeQuery(sql2, para2, new ColumnProcessor());
				return findParsedMainWfProcessByInstancePK(procInst);
			}
		} catch (DbException e) {
			Logger.error(e.getMessage(), e);
			throw new PFBusinessException(NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow",
					"UPPpfworkflow-000061")/*
											 * 查询流程实例发生数据库异常 ：
											 */
					+ e.getMessage(), e);
		} finally {
			if (persist != null)
				persist.release();
		}
	}

	public void undoGadgetAfter(String billID, String billType, AggregatedValueObject billVO, Activity activity) {
		Logger.debug("活动回退时，回退其单据组件=EngineService.undoGadget() called");

		WorkflowgadgetVO foundGadgetVO = findGadget(activity, billType);
		if (foundGadgetVO == null) {
			Logger.debug("该活动没有配置工作流组件，无需执行。活动名称=" + activity.getName());
			return;
		}

		PfParameterVO paraVO = ActionEnvironment.getInstance().getParaVo(billID);

		// 查询工作流组件注册的类名
		String clzName = foundGadgetVO.getClassname();
		if (StringUtil.isEmptyWithTrim(clzName))
			throw new EngineException(NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow", "EngineService-0001",
					null, new String[] { foundGadgetVO.getPk_workflowgadget() })/*
																				 * 找不到工作流组件对应的类名 ，组件ID={0}
																				 */);
		WfGadgetContext gc = new WfGadgetContext();
		gc.setGadgetVO(foundGadgetVO);
		gc.setBillEntity(billVO);
		gc.setAutoActivity(activity.getActivityType() == ActivityTypeEnum.Auto.getIntValue());
		gc.setActivity(activity);
		if (paraVO != null) {
			gc.setUserObj(paraVO.m_userObj);
			gc.setPfParameterVO(paraVO.clone());
		} else {
			gc.setControlSource(WfGadgetControlSource.Manage);
		}
        String [] clazNameArray=clzName.split(",");
        setSplitClzName2(clazNameArray,billType,gc);
	}
	
	private void setSplitClzName2(String[] clazNameArray, String billType, WfGadgetContext gc) {
		for (String clzName : clazNameArray) {
			try {
				String serviceName = isCrossService(clzName, billType);
				// String serviceName="ssc";
				if (StringUtils.isNotBlank(serviceName)) {// 判断跨服务
					// executeCrossUnDoAfterService(clzName,gc,billType,serviceName);
					NCCSagas.startAddSaga();
					executeCrossUnDoService(clzName, gc, billType, false, serviceName, false);
				} else {
					// 实例化工作流组件并执行回退方法

					Object obj = PfUtilTools.findBizImplOfBilltype(billType, clzName.trim());
					if (obj instanceof IWorkflowGadget) {
						IWorkflowGadget gadgetObj = (IWorkflowGadget) obj;

						// gc.setProcessStatus(isToFinish ?
						// WfTaskOrInstanceStatus.Finished :
						// WfTaskOrInstanceStatus.Started);
						// FIXME:其他属性?
						gadgetObj.undoAfterRunned(gc);
					} else {
						throw new PFBusinessException(
								NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow", "EngineService-0002", null,
										new String[] { (String) obj })/*
																		 * 注册的工作流组件没有实现IWorkflowGadget接口 ={0}
																		 */);
					}
				}
			} catch (BusinessException e) {
				Logger.error(e.getMessage(), e);
				throw new EngineException(NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow",
						"EngineService-0003", null, new String[] { e.getMessage() })/* 回退工作流组件发生异常：{0} */, e);
			}
		}

	}
	
	/**
	 * 远程跨服务调用
	 */
	private void executeCrossUnDoService(String clzName,WfGadgetContext gc,String billType,boolean isRunned,String serviceName,boolean isBefore) throws BusinessException {
//		INccWorkflowComponentExe exeCom = IrisNCLocator.getInstance().lookup(INccWorkflowComponentExe.class,
//				serviceName);
//		UFBoolean checkFlag=UFBoolean.FALSE;
//		if(isBefore) {
//			checkFlag=exeCom.checkUnDoRunned(clzName.trim(), gc, billType);//同步调用检查
//		}else {
//			checkFlag=exeCom.checkUnDoRunnedAfter(clzName.trim(), gc, billType);//同步调用检查
//		}
//		if(checkFlag.booleanValue()) {
//			Sagas.setDelaySending();
//			exeCom.exeWorkflowComponentUnDo(clzName.trim(), gc, billType,isRunned,isBefore);
//		}
		CacheConfig config = new CacheConfig("workflowCache", false, 0L, 100, 2, 3);
		ICache tempcache = CacheManager.getInstance().getCache(config);
		Object objList = tempcache.get(Thread.currentThread().getId() + "sscList");
		tempcache.put(Thread.currentThread().getId()+"serviceName", serviceName);
		List<Map<String, Object>> list = null;
		if (objList == null) {// 证明是第一次加入缓存
			list = new ArrayList<Map<String, Object>>();
		} else {
			list = (List<Map<String, Object>>) objList;
		}
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("classname", clzName);
		map.put("context", gc);
		map.put("billtype", billType);
		map.put("isRunned", isRunned);
		map.put("isBefore", isBefore);
		map.put("isDo", false);// undoAfter undoBefore
		map.put("serviceName",serviceName);
		list.add(map);
		tempcache.put(Thread.currentThread().getId() + "sscList", list);
	}
	
	public boolean isNextTaskAutoComplete(WFTask startTask) {
		TaskManagerDMO taskDMO = new TaskManagerDMO();
		WFTask nextTask = null;
		try {
			nextTask = taskDMO.getNextTask(startTask);
		} catch (DbException e) {
			Logger.error(e.getMessage(), e);
		}
		//kejl 制单人自动审批
		if( ActionEnvironment.getInstance().getMethodReturn(startTask.getBillID() + "AUTO")!=null){
			return true;
		}
		
		if (nextTask != null
				&& UFBoolean.TRUE.equals(nextTask.getAutoCompleted())) {
			return true;
		}
		
		if(nextTask==null){
			 if(ActionEnvironment.getInstance().getMethodReturn(startTask.getBillID()+"AUTO")!=null){
				 ActionEnvironment.getInstance().putMethodReturn(startTask.getBillID()+"AUTO", null);
				 return true;
			 }
		}
		return false;}

	public boolean isAutoApprove(WFTask task) {
		boolean isAutoApprove = false;
		BasicWorkflowProcess bwp = null;

		// xuxwb 工作流 支持本人自动审批
		// 工作流添加本人自动审批 xuxwb
		boolean isworkflowcanautoapprove = false;
//		 BilltypeVO btvo=PfDataCache.getBillTypeInfo(task.getWorkflowType());

		try {

			BilltypeVO btvo = PfDataCache.getBillTypeInfo(PfUtilBaseTools.getRealBilltype(task.getBillType()));
			if (btvo != null && btvo.getIsworkflowcanautoapprove() != null
					&& btvo.getIsworkflowcanautoapprove().booleanValue()) {
				isworkflowcanautoapprove = true;
			}

			if (task.getWorkflowType() == WorkflowTypeEnum.SubApproveflow.getIntValue()
					|| task.getWorkflowType() == WorkflowTypeEnum.SubWorkApproveflow.getIntValue()) {
				// 子流程
				bwp = findParsedWfProcess(task.getWfProcessDefPK(), task.getWfProcessInstancePK());
			} else if (isworkflowcanautoapprove && (task.getWorkflowType() == WorkflowTypeEnum.Workflow.getIntValue()
					|| task.getWorkflowType() == WorkflowTypeEnum.SubWorkflow.getIntValue())) {
				// 子流程
				bwp = findParsedWfProcess(task.getWfProcessDefPK(), task.getWfProcessInstancePK());

			} else if (!isworkflowcanautoapprove && (task.getWorkflowType() == WorkflowTypeEnum.Workflow.getIntValue()
					|| task.getWorkflowType() == WorkflowTypeEnum.SubWorkflow.getIntValue())) {
				// 工作流不支持本人自动审批
				return false;

			} else {
				bwp = findParsedMainWfProcessByInstancePK(task.getWfProcessInstancePK());
			}

			Activity activity = bwp.findActivityByID(task.getActivityID());
			boolean obj = false;
			if (activity instanceof GenericActivityEx)
				obj = ((GenericActivityEx) activity).getAutoApprove();
			isAutoApprove = obj;

		} catch (BusinessException e) {
			Logger.error(e.getMessage(), e);
			throw new PFRuntimeException(e.getMessage(), e);
		}
		return isAutoApprove;
	}

	public int getSubmitRejectBillMode(WFTask task) {
		BasicWorkflowProcess bwp = null;

		int obj = 0;

//		 BilltypeVO btvo=PfDataCache.getBillTypeInfo(task.getWorkflowType());

		try {

			if (task.getWorkflowType() == WorkflowTypeEnum.SubApproveflow.getIntValue()
					|| task.getWorkflowType() == WorkflowTypeEnum.SubWorkApproveflow.getIntValue()) {
				// 子流程
				bwp = findParsedWfProcess(task.getWfProcessDefPK(), task.getWfProcessInstancePK());
			} else if ((task.getWorkflowType() == WorkflowTypeEnum.Workflow.getIntValue()
					|| task.getWorkflowType() == WorkflowTypeEnum.SubWorkflow.getIntValue())) {
				// 子流程
				bwp = findParsedWfProcess(task.getWfProcessDefPK(), task.getWfProcessInstancePK());

			} else {
				bwp = findParsedMainWfProcessByInstancePK(task.getWfProcessInstancePK());
			}

			Activity activity = bwp.findActivityByID(task.getActivityID());

			if (activity instanceof GenericActivityEx)
				obj = ((GenericActivityEx) activity).getSubmitRejectBillMode();

		} catch (BusinessException e) {
			Logger.error(e.getMessage(), e);
			throw new PFRuntimeException(e.getMessage(), e);
		}
		return obj;
	}

	/**
	 * 判断某个活动实例是否被某操作人自动审批过
	 * 
	 * 判断的依据为： 该活动实例的WFTask是否为自动完成 且 该WFTask的工作任务处理人中是否有某操作人
	 * 
	 * 
	 * 其实还不是很完备 因为目前的数据结构中，没有从工作任务粒度来记录工作任务是否自动完成 而是以WFTask这个更粗的粒度来记录
	 * 
	 * @deprecated 该逻辑导致流程定义为1-2-1的流程，1和2均无法弃审
	 * @param pk_wf_actinstance
	 * @param operator
	 * @return
	 * @throws DAOException
	 */
	public boolean isActInstanceAutoCompleted(String pk_wf_actinstance, String operator) throws DAOException {
		String sql = "select 1 from" + " pub_workflownote n" + " join pub_wf_task k" + " on n.pk_wf_task=k.pk_wf_task"
				+ " where" + " k.pk_wf_actinstance=?" + " and n.checkman=?" + " and isautocomplete=?";

		SQLParameter param = new SQLParameter();
		param.addParam(pk_wf_actinstance);
		param.addParam(operator);
		param.addParam(UFBoolean.TRUE);

		Object obj = new BaseDAO().executeQuery(sql, param, new ColumnProcessor());
		return !(obj == null);
	}

	/**
	 * @since 6.3判断某个活动实例是否为自动审批过
	 * 
	 *        原理：即只要改环节产生的工作项均为
	 */
	public boolean isActInstanceAutoCompleted2(String pk_wf_actinstance) throws DAOException {
		/**
		 * FIXME 用pub_workflownote中的messagenote是否包含‘autoApprove’字段来判断工作项是否为自动完成，
		 * 是否可靠？？加一个标志位？
		 */
		String sql = "select 1 from pub_workflownote n join pub_wf_task k on n.pk_wf_task=k.pk_wf_task where k.pk_wf_actinstance=?  and messagenote not like'"
				+ "%autoApproved%'";
		SQLParameter param = new SQLParameter();
		param.addParam(pk_wf_actinstance);
		Object obj = new BaseDAO().executeQuery(sql, param, new ColumnProcessor());
		return obj == null;
	}

	/**
	 * 根据流程定义pk和流程实例pk（用于加签的情况）从流程定义表查找定义对象 *
	 * 
	 * @param defPK  定义表主键
	 * @param instPk 实例主键
	 * @return
	 */
	public WorkflowProcess queryWfProcess(String defPk, String instPk) {
		WorkflowProcess process = null;
		try {
			process = findParsedWfProcess(defPk, instPk);
		} catch (BusinessException ex) {
			Logger.error(ex.getMessage(), ex);
			throw new EngineException(ex.getMessage(), ex);
		}
		if (process == null)
			throw new DefinitionNotFound(
					NCLangResOnserver.getInstance().getStrByID("pfworkflow", "UPPpfworkflow-000292")/*
																									 * @res "工作流定义不存在："
																									 */
							+ defPk);
		return process;
	}

	private BasicWorkflowProcess findParsedWfProcess(String wfProcessDefPK, String processInstancePK)
			throws BusinessException {
		try {
			return PfDataCache.getWorkflowProcess(wfProcessDefPK, processInstancePK);
		} catch (XPDLParserException e) {
			Logger.error(e.getMessage(), e);
			throw new PFBusinessException(NCLangResOnserver.getInstance().getStrByID("pfworkflow",
					"UPPpfworkflow-000005")/*
											 * 查询流程定义出现XPDL解析异常 ：
											 */
					+ e.getMessage(), e);
		}
	}

	/**
	 * 工作项排序（根据ts以及dealdate，ts有重复的可能性）
	 * 
	 * @author wangjhf
	 */
	private class VoComparator implements Comparator<WorkflownoteVO> {
		@Override
		public int compare(WorkflownoteVO arg0, WorkflownoteVO arg1) {
			int result = 0;
			if (arg0.getTs() == null || arg1.getTs() == null) {
				result = 0;
			} else {
				result = arg1.getTs().compareTo(arg0.getTs());
			}
			if (result == 0) {
				if (arg0.getDealdate() == null || arg1.getDealdate() == null) {
					result = 0;
				} else {
					result = arg1.getDealdate().compareTo(arg0.getDealdate());
				}
			}
			return result;
		}
	}

	/**
	 * 根据流程定义主键取流程定义制单人id
	 * 
	 * @param pk_wf_def 流程定义主键
	 * @return billmaker 制单人
	 */
	public String queryProcessDefByDefPk(String pk_wf_def) throws BusinessException {
		BaseDAO dao = new BaseDAO();
		WorkflowDefinitionVO billmaker = (WorkflowDefinitionVO) dao.retrieveByPK(WorkflowDefinitionVO.class, pk_wf_def);
		return billmaker != null ? billmaker.getBillmaker() : null;
	}

	/**
	 * 根据pk_checkfolw检查某用户的待办工作项
	 * <li>如果无待办工作项且流程已经启动，则抛出异常
	 * 
	 * @param paraVo
	 * @param iWorkflowOrApproveflow 主流程类型，见<code>WorkflowTypeEnum.Approveflow</code>和
	 *                               <code>WorkflowTypeEnum.Workflow</code>
	 * @return WorkflownoteVO
	 * @author wangygf
	 * @since 2014年9月3日10:16:11
	 */
	public WorkflownoteVO checkUnfinishedWorkitemByPKckeckflow(PfParameterVO paraVo, int iWorkflowOrApproveflow,
			String pk_checkflow) throws BusinessException, DbException {
		// 查询某用户对某单据的所有待办工作项
		WorkflownoteVO[] uncheckedNoteVOs = queryWorkNotCheckByPkCheckFlow(pk_checkflow, paraVo.m_operator,
				iWorkflowOrApproveflow);
		if (uncheckedNoteVOs != null && uncheckedNoteVOs.length > 0) {
			// 当前操作员有待办工作项
			WorkflownoteVO noteVO = uncheckedNoteVOs[uncheckedNoteVOs.length - 1];// FIXME:某用户对某个单据可能同时有两个待办工作项吗？
			TaskManagerDMO taskDmo = new TaskManagerDMO();
			WFTask currentTask = taskDmo.getTaskByPK(noteVO.getPk_wf_task());
			if (currentTask == null) {
				throw new PFBusinessException(NCLangResOnserver.getInstance().getStrByID("pfworkflow",
						"UPPpfworkflow-000262")/* 当前操作人待办任务丢失 */);
			} else if (currentTask.getTaskType() == WfTaskType.Makebill.getIntValue()
					|| currentTask.getTaskType() == WfTaskType.Withdraw.getIntValue())
				throw new PFBusinessException(
						NCLangResOnserver.getInstance().getStrByID("pfworkflow", "UPPpfworkflow-000388")/*
																										 * 没有待办任务。
																										 */);

			// 校验流程实例的状态
			if (hasRunningProcessInstances(currentTask.getWfProcessDefPK(), paraVo.m_billVersionPK) == null) {
				throw new PFBusinessException(NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow",
						"EngineService-0004")/*
												 * 该单据所处的流程实例已被挂起 ！
												 */);
			}
			WorkflowProcess wp = findParsedWfProcess(currentTask.getWfProcessDefPK(),
					currentTask.getWfProcessInstancePK());
			if (wp == null)
				return null; // throw new Exception("没有对应的流程定义");

			currentTask.setBillNO(paraVo.m_billNo);
			currentTask.setBillID(paraVo.m_billId);
			currentTask.setBillversionPK(paraVo.m_billVersionPK);
			// currentTask.setBusiType(paraVo.m_businessType);
			currentTask.setBillType(paraVo.m_billType);
			currentTask.setPk_org(paraVo.m_pkOrg);
			currentTask.setTopic(noteVO.getMessagenote());

			currentTask.setWorkflowType(noteVO.getWorkflow_type());
			currentTask.setWorknoteVO(noteVO);

			// yanke1 2013-4-18
			// 修改前这里的代码为 currentTask.setSenderman(paraVo.m_operator);
			// paraVo.m_operator通常为当前登录人
			// 使用它作为currentTask的senderMan是不准确的
			// 应当使用noteVO的senderMan
			currentTask.setSenderman(noteVO.getSenderman());

			// 获取该活动后继活动的可指派信息
			WfTaskInfo taskInfo = createTaskInfo(currentTask, wp, paraVo.m_preValueVo);
			noteVO.setTaskInfo(taskInfo);

			// 获得活动的一些扩展属性
			noteVO.getRelaProperties()
					.putAll(findRelaProps(wp.findActivityByID(currentTask.getActivityID()), paraVo.m_billType));

			return noteVO;
		} else {
			// 当前操作员无待办工作项
			HashSet<String> hsRet = hasInstanceOfValid(new String[] { paraVo.m_billVersionPK }, paraVo.m_billType,
					iWorkflowOrApproveflow);
			boolean startup = hsRet.contains(paraVo.m_billVersionPK);
			if (startup) {
				// 该单据有流程实例
				return null;
			} else {
				// 该单据无流程实例
				WorkflowProcess wp = null;
				if (StringUtil.isEmptyWithTrim(paraVo.m_flowDefPK))
					wp = queryWfProcessCanStart2(paraVo.m_billType, paraVo.m_pkGroup, paraVo.m_pkOrg,
							paraVo.m_makeBillOperator, paraVo.emendEnum, iWorkflowOrApproveflow);
				else
					wp = findParsedWfProcess(paraVo.m_flowDefPK, null);
				if (wp == null) {
					// 该制单人无流程定义，任何人都可审批或执行
					return null;
				} else {
					throw new PFBusinessException(NCLangResOnserver.getInstance().getStrByID("pfworkflow",
							"UPPpfworkflow-000389")/*
													 * 该单据有可启动的流程但未启动，无法流转 。
													 */);
				}
			}
		}

	}

	public void rollbackGadgets(PfParameterVO paraVo) {

		List<ActivityInstance> rollbackActs = new ArrayList<ActivityInstance>();
		ActInstanceCompararot comparator = new ActInstanceCompararot();
		String billID = paraVo.m_billVersionPK;
		String billType = paraVo.m_billType;
		WFTask currentTask = paraVo.m_workFlow.getTaskInfo().getTask();
		String targetActID = currentTask.getJumpToActivity(); // 目标结点
		// boolean flag = currentTask.isSubmit2RjectTache();// 是否不重走工作流
		rollbackActs = ActionEnvironment.getInstance().getParaVo(paraVo.m_billVersionPK).getActInfo();// 所有需要操作结点

		/* 目标结点newInstance结点摘出来单独处理 */
		// zhaojianc 2014-11-6 将newTargetAct设为null
		ActivityInstance newTargetAct = null;
		Iterator<ActivityInstance> iterator = rollbackActs.iterator();
		while (iterator.hasNext()) {
			ActivityInstance temp = iterator.next();
			if (((temp.getActivityID().equals(targetActID))
					&& (temp.getStatus() == WfTaskOrInstanceStatus.Started.getIntValue()))) {
				newTargetAct = temp;
				iterator.remove();
			}
		}

		/* 去掉重复 ，相同的保留后执行的 */
		Collections.sort(rollbackActs, comparator);// 按时间排序
		HashMap<String, ActivityInstance> temMap = new HashMap<String, ActivityInstance>();
		for (ActivityInstance act : rollbackActs) {
			temMap.put(act.getActivityID(), act);
		}
		rollbackActs = new ArrayList<ActivityInstance>(temMap.values());
		Collections.sort(rollbackActs, comparator);
		Collections.reverse(rollbackActs);

		/* 目标结点oldInstance结点摘出来也单独处理 */
		ActivityInstance oldTargetAct = new ActivityInstance();

		Iterator<ActivityInstance> actsIterator = rollbackActs.iterator();
		EnginePersistence  instance = new EnginePersistence();
		
		List<ActivityInstance> oldActIns = paraVo.getOldActIns();// 驳回前所有status为0或1的实例
		Map<String,Integer> actRejionMap=new HashMap<String,Integer>();
		
		for(ActivityInstance actIns:oldActIns) {
			actRejionMap.put(actIns.getActivityInstancePK(), actIns.getReachJoins());
		}
		
		while (actsIterator.hasNext()) {
			ActivityInstance temp = actsIterator.next();
			if (((temp.getActivityID().equals(targetActID))
					&& (temp.getStatus() == WfTaskOrInstanceStatus.Inefficient.getIntValue()))) {
				oldTargetAct = temp;
				actsIterator.remove();
				continue;
			}
			
			if (temp.getStatus() != WfTaskOrInstanceStatus.Inefficient.getIntValue() 
					&&actRejionMap.containsKey(temp.getActivityInstancePK())
					&&actRejionMap.get(temp.getActivityInstancePK())==1) {// 移除非4操作的act 并且是一个来源活动的
				actsIterator.remove();
			}

		}
		/* 应zhaojianc要求，把目标结点的old实例放到最后执行 */
		rollbackActs.add(oldTargetAct);
		List<String> zeroActIDs = new ArrayList<String>();// 状态为0(仅需undo前处理)的结点id的list
		List<String> oneActIDs = new ArrayList<String>();// 状态为1的结点id的list
		for (ActivityInstance act : oldActIns) {
			if (act.getStatus() == WfTaskOrInstanceStatus.Started.getIntValue()) {
				zeroActIDs.add(act.getActivityID());
			} else {
				oneActIDs.add(act.getActivityID());
			}
		}

		// 回滚组件，是否重走工作流都执行 此时rollbackActs；
		// 都是状态操作命令为4的act，且不重复，但可能有不需要回滚的，通过oldActIns的act的状态判断,原来是0或1的需要回滚，其他的不需要回滚
		// （可能包含状态4的不需要执行） ；
		// 此时rollbackActs 中不包括目标结点newInstance

		for (ActivityInstance act : rollbackActs) {
			if (zeroActIDs.contains(act.getActivityID())) {
				undoGadget(billID, billType, paraVo.m_preValueVo, act.getActivity(), false);
			} else if (oneActIDs.contains(act.getActivityID())) {
				undoGadget(billID, billType, paraVo.m_preValueVo, act.getActivity(), true);
			}
		}

		// 驳回到非制单结点时候执行目标结点newInstance的前处理
		// 驳回到制单结点原uap流程执行了newinstance的前处理，但是执行顺序实在其他组件undo前，是否需要修改？在WorkflowRunner.startupActivityInstance中执行
		// zhaojianc 2014-11-6 加入了对newTargetAct是否为null的判断
		if (newTargetAct != null && !newTargetAct.getActivity().isStartActivity()) {
			dobeforGadget(billID, billType, paraVo.m_preValueVo, newTargetAct.getActivity());
		}

	}

	/**
	 * 前处理工作流组件
	 * 
	 * @param billID
	 * @param billType
	 * @param billVO
	 * @param activity
	 */
	private void dobeforGadget(String billID, String billType, AggregatedValueObject billVO, Activity activity) {

		WorkflowgadgetVO foundGadgetVO = findGadget(activity, billType);
		if (foundGadgetVO == null) {
			Logger.debug("该活动没有配置工作流组件，无需执行。活动名称=" + activity.getName());
			return;
		}

		// zhaojian Add Begin
		PfParameterVO paraVO = ActionEnvironment.getInstance().getParaVo(billID);
		// zhaojian Add End

		// 查询工作流组件注册的类名
		String clzName = foundGadgetVO.getClassname();
		if (StringUtil.isEmptyWithTrim(clzName)) {
			// 找不到工作流组件对应的类名
			throw new EngineException(NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow", "EngineService-0001",
					null, new String[] { foundGadgetVO.getPk_workflowgadget() }));
		}

		WfGadgetContext gc = new WfGadgetContext();
		gc.setGadgetVO(foundGadgetVO);
		gc.setBillEntity(billVO);
		gc.setAutoActivity(activity.getActivityType() == ActivityTypeEnum.Auto.getIntValue());
		gc.setActivity(activity);

		// zhaojian Add Begin 将用户对象进行传入，以便对
		// 工作组件操作进行控制
		gc.setUserObj(paraVO.m_userObj);
		gc.setPfParameterVO(paraVO.clone());
		// zhaojian Add End
		String [] clazNameArray=clzName.split(",");
		setSplitDoClzName(clazNameArray,billType,gc);
	}
	
	private void setSplitDoClzName(String [] clazNameArray,String billType,WfGadgetContext gc) {
		for(String clzName:clazNameArray) {
			try {
				// 实例化工作流组件并执行回退方法
				String serviceName = isCrossService(clzName, billType);
				if (StringUtils.isNotBlank(serviceName)) {// 判断跨服务
					NCCSagas.startAddSaga();
					executeCrossService(clzName.trim(),gc,billType,true,serviceName);
				}else {
					Object obj = PfUtilTools.findBizImplOfBilltype(billType, clzName.trim());
					if (obj instanceof IWorkflowGadget) {
						IWorkflowGadget gadgetObj = (IWorkflowGadget) obj;
						// 执行前处理
						gadgetObj.doBeforeActive(gc);
					} else {
						// 注册的工作流组件没有实现IWorkflowGadget接口
						throw new PFBusinessException(NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow",
								"EngineService-0002", null, new String[] { (String) obj }));
					}
				}
				
			} catch (BusinessException e) {
				Logger.error("执行工作流组件发生异常：" + e.getMessage(), e);
				throw new EngineException("执行工作流组件发生异常：" + e.getMessage(), e);
			}
		}
	}

	private void executeCrossService(String clzName, WfGadgetContext gc, String strBilltype, boolean isBefore,
			String serviceName) throws BusinessException {
//		INccWorkflowComponentExe exeCom=IrisNCLocator.getInstance().lookup(INccWorkflowComponentExe.class, serviceName);
//		UFBoolean checkFlag=UFBoolean.FALSE;
//		if(isBefore) {//前置组件同步校验
//			 checkFlag=exeCom.checkDoRunned(clzName.trim(), gc,strBilltype);
//		}else {
//			 checkFlag=exeCom.checkDoRunnedAfter(clzName.trim(), gc,strBilltype);
//		}
//		if(checkFlag.booleanValue()) {//异步调用组件
//			Sagas.setDelaySending();
//			exeCom.exeWorkflowComponentDo(clzName.trim(), gc,strBilltype,isBefore);
//		}
		CacheConfig config = new CacheConfig("workflowCache", false, 0L, 100, 2, 3);
		ICache tempcache = CacheManager.getInstance().getCache(config);
		Object objList = tempcache.get(Thread.currentThread().getId() + "sscList");
		tempcache.put(Thread.currentThread().getId()+"serviceName", serviceName);
		List<Map<String, Object>> list = null;
		if (objList == null) {// 证明是第一次加入缓存
			list = new ArrayList<Map<String, Object>>();
		} else {
			list = (List<Map<String, Object>>) objList;
		}
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("classname", clzName);
		map.put("context", gc);
		map.put("billtype", strBilltype);
		map.put("isBefore", isBefore);
		map.put("isDo", true);// doAfter doBefore
		map.put("serviceName",serviceName);
		list.add(map);
		tempcache.put(Thread.currentThread().getId() + "sscList", list);
	}
	
	
	/**
	 * 根据单据id和流程实例PK获取其流程定义的内容,还要获取流程中触发过的活动；<br>
	 * 再递归查询其所有子流程。
	 * 
	 * @param billId         单据id
	 * @param billType
	 * @param procInstancePK 流程实例pk,如果为null,表示主流程
	 * @param jdbc           JdbcSession
	 * @param iWorkflowtype  流程类型：1=审批流 3=工作流
	 * 
	 * @modifier yanke1 2011-9-22
	 *           从pub_wf_def中查询流程定义version，赋给ProcessRouteRes的processDefVersion
	 */
	public ProcessRouteRes queryProcessRouteRecursivelyForMobile(String billId, String billType, String procInstancePK,
			JdbcSession jdbc, int iWorkflowtype) throws DbException {
		final ProcessRouteRes processRoute = new ProcessRouteRes();

		// 1.取流程定义
		String sql = "select c.content,a.processdefid,a.pk_wf_instance,a.procstatus, c.version, c.node_name ,c.node_name2,c.node_name3,c.node_name4,c.node_name5,c.node_name6 from pub_wf_instance a,pub_wf_def c "
				+ " where a.processdefid=c.pk_wf_def and a.billversionpk=? and a.billtype=?";
		SQLParameter para = new SQLParameter();
		para.addParam(billId);
		para.addParam(billType);
		if (procInstancePK != null) {
			sql += " and a.pk_wf_instance=?";
			para.addParam(procInstancePK);
		} else {
			// 流程实例pk如果为null,表示主流程
			sql += getWorkflowTypeAndWhereForMobile(iWorkflowtype, "a.");
			sql += " and isnull(a.src_pk_actinstance,'~')='~'";
		}

		jdbc.executeQuery(sql, para, new BaseProcessor() {
			public Object processResultSet(ResultSet rs) throws SQLException {
				if (rs.next()) {
					Object def_content = InOutUtil.deserialize(ProcessorUtils.getBlob(rs, 1));
					processRoute.setXpdlString(def_content);
					processRoute.setProcessDefPK(rs.getString(2));
					processRoute.setProcInstancePK(rs.getString(3));
					processRoute.setProcStatus(rs.getInt(4));
					processRoute.setProcessDefVersion(rs.getString(5));
					MultiLangText text = new MultiLangText();
					text.setText(rs.getString(6));
					text.setText2(rs.getString(7));
					text.setText3(rs.getString(8));
					text.setText4(rs.getString(9));
					text.setText5(rs.getString(10));
					text.setText6(rs.getString(11));
					processRoute.setPicName(text.toString());
				}
				return null;
			}
		});

		if (processRoute.getProcInstancePK() != null) {
			sql = "select content from pub_wf_def_adjust where pk_wf_instance=?";
			para = new SQLParameter();
			para.addParam(processRoute.getProcInstancePK());

			jdbc.executeQuery(sql, para, new BaseProcessor() {
				public Object processResultSet(ResultSet rs) throws SQLException {
					if (rs.next()) {
						String content = (String) InOutUtil.deserialize(ProcessorUtils.getBlob(rs, 1));

						// 设置xpdlContent
						if (!StringUtil.isEmptyWithTrim(content)) {
							processRoute.setXpdlString(content);
						}
					}
					return null;
				}
			});

		}

		if (processRoute.getProcessDefPK() != null) {// 查到了流程定义
			// 2.取流程中触发过的活动

			// yanke1 这是一个select distinct语句，并且根据createtime进行了order by
			// 因此应当在select中加上createtime，否则oracle 10g会报错ORA-01791
			sql = "select distinct pk_wf_actinstance,activitydefid,actstatus,createtime from pub_wf_actinstance "
					+ " where pk_wf_instance=? order by createtime, pk_wf_actinstance";
			para = new SQLParameter();
			para.addParam(processRoute.getProcInstancePK());
			ArrayList alActs = (ArrayList) jdbc.executeQuery(sql, para, new ArrayListProcessor());
			ActivityInstance[] actInstances = null;
			if (alActs != null && alActs.size() > 0) {
				actInstances = new ActivityInstance[alActs.size()];
				Object[] oTemp = null;
				StringBuffer sbActInstancePK = new StringBuffer();
				for (int i = 0; i < alActs.size(); i++) {
					oTemp = (Object[]) alActs.get(i);
					actInstances[i] = new ActivityInstance((String) oTemp[0]);
					sbActInstancePK.append(",'" + oTemp[0] + "'");
					actInstances[i].setActivityID((String) oTemp[1]);
					actInstances[i].setStatus(((Integer) oTemp[2]).intValue());
				}
				processRoute.setActivityInstance(actInstances);

				// 查询流程实例中所有活动的来源关系
				String sqlRela = "select distinct b.activitydefid,c.activitydefid from pub_wf_actinstancesrc a "
						+ "inner join pub_wf_actinstance b on a.src_actinstance=b.pk_wf_actinstance "
						+ "inner join pub_wf_actinstance c on a.target_actinstance=c.pk_wf_actinstance "
						+ "where b.pk_wf_instance=? and c.pk_wf_instance=?  and ((b.actstatus!=c.actstatus )or (b.actstatus=c.actstatus and c.actstatus!=4 ))";
				para.addParam(processRoute.getProcInstancePK());
				ArrayList alActRela = (ArrayList) jdbc.executeQuery(sqlRela, para, new ArrayListProcessor());
				processRoute.setActivityRelations(alActRela);

				// 3.取子流程
				sql = "select distinct pk_wf_instance  from pub_wf_instance " + " where src_pk_actinstance in("
						+ sbActInstancePK.substring(1) + ") and procstatus!="
						+ WfTaskOrInstanceStatus.Inefficient.getIntValue();
				ArrayList alSubWfInstance = (ArrayList) jdbc.executeQuery(sql, new ArrayListProcessor());
				if (alSubWfInstance != null && alSubWfInstance.size() > 0) {
					ProcessRouteRes[] subProessRoute = new ProcessRouteRes[alSubWfInstance.size()];
					for (int i = 0; i < alSubWfInstance.size(); i++) {
						oTemp = (Object[]) alSubWfInstance.get(i);
						subProessRoute[i] = queryProcessRouteRecursivelyForMobile(billId, billType, (String) oTemp[0],
								jdbc, iWorkflowtype);// 递归取子流程
					}
					processRoute.setSubProcessRoute(subProessRoute);
				}
			}
		}
		return processRoute;
	}

	/**
	 * @param iWorkflowOrApproveflow 主流程类型，见<code>WorkflowTypeEnum.Approveflow</code>和
	 *                               <code>WorkflowTypeEnum.Workflow</code>
	 * @param tableAlias             实例表的别名
	 * @return
	 */
	public String getWorkflowTypeAndWhereForMobile(int iWorkflowOrApproveflow, String tableAlias) {
		String strWfTypeSql = "";
		if (StringUtil.isEmptyWithTrim(tableAlias))
			tableAlias = "";

		strWfTypeSql = " and " + tableAlias + "workflow_type in"
				+ WorkflowTypeEnum.getInstanceTypeInSql(iWorkflowOrApproveflow);

		return strWfTypeSql;
	}

	/**
	 * 判断单据ID数组是否存在审批流或工作流实例
	 * <li>处于有效状态的，即启动或完成状态
	 * 
	 * @param billIdAry
	 * @param billType
	 * @param iWorkflowOrApproveflow
	 * @return 存在流程实例的单据ID数组
	 */
	public HashSet<String> hasApproveInstance(String billId, final String billType, final int iWorkflowOrApproveflow)
			throws BusinessException {
		final HashSet<String> hsRet = new HashSet<String>();
		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			final JdbcSession jdbc = persist.getJdbcSession();

			String sql = "select billversionpk,workflow_type from pub_wf_instance where billversionpk = ?"
					+ " and procstatus!=? and workflow_type = ? ";
			SQLParameter para = new SQLParameter();
			para.addParam(billId);
			para.addParam(WfTaskOrInstanceStatus.Inefficient.getIntValue());
			para.addParam(iWorkflowOrApproveflow);

			try {
				List<Object[]> lResult = new ArrayList(0);
				lResult = (ArrayList) jdbc.executeQuery(sql, para, new ArrayListProcessor());
				if (!lResult.isEmpty()) {
					hsRet.add(billId);
				}
			} catch (DbException e) {
				Logger.error(e.getMessage(), e);
				throw new PFBusinessException(NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow",
						"UPPpfworkflow-000060")/*
												 * 查询流程实例表发生数据库异常 ：
												 */
						+ e.getMessage(), e);
			}

			return hsRet;
		} catch (DbException e) {
			Logger.error(e.getMessage(), e);
			throw new PFBusinessException(NCLangRes4VoTransl.getNCLangRes().getStrByID("pfworkflow",
					"UPPpfworkflow-000060")/*
											 * 查询流程实例表发生数据库异常 ：
											 */
					+ e.getMessage(), e);
		} finally {
			if (persist != null)
				persist.release();
		}

	}
	

	/**
     * 查询某人已审工作项，自动审批也包括(zhouwba)
     */
    public WorkflownoteVO[] queryWorkitemsCheckedIncludeAutoCompleteSSC(String pk_checkflow) throws DAOException {
        /**
         * @since 6.3 增加ts降序：用于避免流程中有多个自动环节，根据dealdate和senddate无法将最后一个活动排在最前面。从而导致弃审失败 。
         */
        String sql = "select * from pub_workflownote where pk_checkflow=? and approvestatus="
                + WfTaskOrInstanceStatus.Finished.getIntValue()
                + " and approveresult is not null and actiontype like 'Z%' ";
        SQLParameter para = new SQLParameter();
        para.addParam(pk_checkflow);
        // para.addParam(userPK);
        @SuppressWarnings("unchecked")
        Collection<WorkflownoteVO> co = (Collection<WorkflownoteVO>) new BaseDAO().executeQuery(sql, para,
                new BeanListProcessor(WorkflownoteVO.class));
        return co==null?null:co.toArray(new WorkflownoteVO[0]);
    }
    
    



public WorkflownoteVO checkFinishedWorkitemSSC(String billType, String billId, String checkman,
            int iWorkflowOrApproveflow,String pk_checkflow) throws DbException, BusinessException {
        // 查询某用户对某单据的所有已办工作项
        // WorkflownoteVO[] checkedNoteVOs =
        // queryWorkitemsCheckedExcludeAutoComplete(billType,
        // billId, checkman, iWorkflowOrApproveflow);

        /**
         * @since v65 修改了自动审批逻辑，自动审批时逐级弃审，可以弃审自动审批环节
         */
        WorkflownoteVO[] checkedNoteVOs = queryWorkitemsCheckedIncludeAutoCompleteSSC(pk_checkflow);
        if (checkedNoteVOs != null && checkedNoteVOs.length > 0) {
            Collections.sort(Arrays.asList(checkedNoteVOs), new VoComparator());
            // 当前操作员有已办工作项
            WorkflownoteVO noteVO = findLatestFinishedWorkitem(checkedNoteVOs);
            if (!checkman.equals(noteVO.getCheckman())) {
                throw new PFBusinessException(NCLangResOnserver.getInstance().getStrByID("pfworkflow",
                        "UPPpfworkflow-000928")/*
                                                 * 已经存在后续操作， 请逐级弃审
                                                 */);
            }
            TaskManagerDMO taskDmo = new TaskManagerDMO();
            WFTask currentTask = taskDmo.getTaskByPK(noteVO.getPk_wf_task());

            // checkresult 是共享服务的不重走流程驳回后再次提交后，中间节点还能取消审批问题。UAPNCWEB-4492
             boolean checkresult =
             checkLatestFinishedWorkitem(currentTask.getWfProcessDefPK(),currentTask.getActivityID(),currentTask.getWfProcessInstancePK());

            if (!checkman.equals(noteVO.getCheckman()) && checkresult) {
                throw new PFBusinessException(NCLangResOnserver.getInstance().getStrByID("pfworkflow",
                        "UPPpfworkflow-000928")/*
                                                 * 已经存在后续操作， 请逐级弃审
                                                 */);
            }

            // 处理自动审批的问题 A-b-c(自动)-b（已经到这里但是未操作）-->之后前面的b反审批
            // autoApprove(checkedNoteVOs,billType,billId);
            // 下级环节有会签的情况，本环节取消审批要看情况
            nextlink(currentTask.getActivityInstancePK());
            if (currentTask == null)
                throw new PFBusinessException(NCLangResOnserver.getInstance().getStrByID("pfworkflow",
                        "UPPpfworkflow-000266")/* 当前操作人已办任务丢失 */);
            currentTask.setPk_org(noteVO.getPk_org());
            currentTask.setBillType(noteVO.getPk_billtype());
            currentTask.setBillID(noteVO.getBillid());
            currentTask.setBillversionPK(noteVO.getBillVersionPK());
            currentTask.setBillNO(noteVO.getBillno());
            currentTask.setTopic(noteVO.getMessagenote());

            currentTask.setWorknoteVO(noteVO);
            currentTask.setSenderman(noteVO.getSenderman());
            WfTaskInfo taskInfo = new WfTaskInfo(currentTask);
            noteVO.setTaskInfo(taskInfo);

            // 获得活动的一些扩展属性
            WorkflowProcess wp = findParsedWfProcess(currentTask.getWfProcessDefPK(),
                    currentTask.getWfProcessInstancePK());
            if (wp == null)
                return null; // throw new Exception("没有对应的流程定义");
            noteVO.getRelaProperties()
                    .putAll(findRelaProps(wp.findActivityByID(currentTask.getActivityID()), billType));
            return noteVO;
        } else {
            // 当前操作员无已办工作项
            HashSet<String> hsRet = hasInstanceOfValid(new String[] { billId }, billType, iWorkflowOrApproveflow);
            boolean isStartup = hsRet.contains(billId);
            if (!isStartup) {
                // 该单据无流程实例
                return null;
            } else {
                // 该单据有流程实例
                boolean isDirectlyCompleted = isDirectlyCompleted(billId, billType);
                if (isDirectlyCompleted) {
                    deleteWorkflow(billId, billType, false, iWorkflowOrApproveflow);
                    return null;
                } else
                    throw new PFBusinessException(NCLangResOnserver.getInstance().getStrByID("pfworkflow",
                            "UPPpfworkflow-000267")/* 当前操作人没有已办工作项，无法回退 */);
            }
        }

    }

	/**
	 * 判定某用户可审批的单据ID数组 逻辑不对，所以重写了一个，保持原来的接口 不判断流程实例状态
	 * 
	 * @param billIdAry
	 * @param billType
	 * @param userId
	 * @return
	 * @throws BusinessException
	 * @throws DbException
	 */
	@SuppressWarnings("unchecked")
	public String[] isCheckmanAryIgnoreInstance(String[] billIdAry, String billType, String userId)
			throws BusinessException, DbException {
		// 查询单据Id数组中哪些拥有有效的审批流实例
		HashSet<String> hsHasInstance = hasInstanceOfValidByBillID(billIdAry, billType,
				WorkflowTypeEnum.Approveflow.getIntValue());

		ArrayList<String> alRet = new ArrayList<String>();
		// 查询该单据类型是否定义了审批流
		String sqlHasDef = "select billtype from pub_wf_def where validity=" + WorkflowDefStatusEnum.Valid.getIntValue()
				+ " and (billtype=? or billtype=?) "
				+ getWorkflowTypeAndWhere(WorkflowTypeEnum.Approveflow.getIntValue(), "");

		// 查询某人可审批的单据VersionPK数组
		String sqlWorkitem = "select billid from pub_workflownote "
				+ "where (pk_billtype=? or pk_billtype=?) and checkman=? and approvestatus in("
				+ WfTaskOrInstanceStatus.getUnfinishedStatusSet() + ") and actiontype not in('"
				+ WorkflownoteVO.WORKITEM_TYPE_BIZ + "','" + WorkflownoteVO.WORKITEM_TYPE_MAKEBILL + "') ";
		;
		PersistenceManager persist = null;
		try {
			persist = PersistenceManager.getInstance();
			JdbcSession jdbc = persist.getJdbcSession();
			SQLParameter para = new SQLParameter();
			para.addParam(billType);
			if (PfUtilBaseTools.isTranstype(billType)) {
				para.addParam(PfUtilBaseTools.getRealBilltype(billType));
			} else
				para.addParam(billType);
			Object hasDef = jdbc.executeQuery(sqlHasDef, para, new ColumnProcessor("billtype"));

			para.addParam(userId);
			List<String> alCanCheckIds = (List<String>) jdbc.executeQuery(sqlWorkitem, para,
					new ColumnListProcessor("billid"));
			for (String billID : billIdAry) {
				// 添加有效地BillID
				if (hsHasInstance.contains(billID)) {
					if (alCanCheckIds.contains(billID)) {
						// 如果单据ID启动了审批流，且轮到该用户审批
						alRet.add(billID);
					}
				} else {
					if (hasDef == null) {
						// 如果单据ID没有审批流实例，且该单据类型没定义审批流，则任何人都可直批
						alRet.add(billID);
					}
				}
			}
		} finally {
			if (persist != null)
				persist.release();
		}
		return alRet.toArray(new String[0]);
	}

	/** @author liningc 20140722 排序类 */
	class ActInstanceCompararot implements Comparator<ActivityInstance> {
		@Override
		public int compare(ActivityInstance o1, ActivityInstance o2) {
			ActivityInstance a1 = o1;
			ActivityInstance a2 = o2;
			int flag = a1.getCreateTime().compareTo(a2.getCreateTime());
			if (flag == 0) {
				return a1.getModifyTime().compareTo(a2.getModifyTime());
			} else {
				return flag;
			}
		}

	}
}
