package cn.myapps.runtime.workflow.controller;

import cn.myapps.authtime.common.service.AuthTimeServiceManager;
import cn.myapps.authtime.user.MyProfileHelper;
import cn.myapps.authtime.user.model.UserVO;
import cn.myapps.authtime.user.service.UserProcess;
import cn.myapps.base.web.WebUser;
import cn.myapps.common.auth.IUser;
import cn.myapps.common.controller.ErrorMessage;
import cn.myapps.common.controller.Resource;
import cn.myapps.common.data.DataPackage;
import cn.myapps.common.data.ParamsTable;
import cn.myapps.common.data.tree.UserNode;
import cn.myapps.common.exception.OBPMValidateException;
import cn.myapps.common.model.workflow.BillDefiVO;
import cn.myapps.common.util.StringUtil;
import cn.myapps.common.util.cache.MemoryCacheUtil;
import cn.myapps.designtime.common.service.DesignTimeServiceManager;
import cn.myapps.designtime.workflow.definition.service.BillDefiDesignTimeService;
import cn.myapps.runtime.common.controller.AbstractRuntimeController;
import cn.myapps.runtime.common.service.RunTimeServiceManager;
import cn.myapps.runtime.common.utils.Pager;
import cn.myapps.runtime.dynaform.document.ejb.Document;
import cn.myapps.runtime.dynaform.document.ejb.DocumentProcess;
import cn.myapps.runtime.dynaform.document.ejb.Item;
import cn.myapps.runtime.dynaform.document.model.IDocument;
import cn.myapps.runtime.dynaform.form.ejb.ValidateMessage;
import cn.myapps.runtime.workflow.FlowType;
import cn.myapps.runtime.workflow.engine.StateMachine;
import cn.myapps.runtime.workflow.engine.StateMachineHelper;
import cn.myapps.runtime.workflow.service.WorkflowRunTimeService;
import cn.myapps.runtime.workflow.storage.runtime.ejb.*;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bcxin.saas.core.exceptions.SaasBadException;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.Option;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;

import java.util.*;

import static com.jayway.jsonpath.JsonPath.parse;

/**
 * 工作流
 * 
 * @author ahan
 *
 */
@Api(tags = "工作流执行模块")
@Component
@RequestMapping(path = {"/api/runtime/{applicationId}", "/api/authtime/{applicationId}"}, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class WorkflowController extends AbstractRuntimeController {

	@Autowired
	private WorkflowRunTimeService workflowService;

	/**
	 * 获取流程状态
	 * @param applicationId 软件id
	 * @param instanceId 流程实例id
	 * @return
	 * @throws Exception
	 */
	@GetMapping("/workflows/{instanceId}/states")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = " 获取流程状态", notes = " 获取流程状态")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "instanceId",value = "流程实例id",required = true,paramType = "path",dataType = "string")
	})
	public Resource queryWorkflow(@PathVariable String applicationId, @PathVariable String instanceId)
			throws Exception {
		JSONObject result = workflowService.queryWorkflow(applicationId, instanceId);
		return success("ok", result);
	}

	/**
	 * 获取文档的流程历史
	 * @param applicationId
	 * 			软件id
	 * @param docId
	 * 			文档id
	 * @return
	 * 			流程历史对象的集合
	 * @throws Exception
	 */
	@GetMapping("/documents/{docId}/workflows/flowhistorys")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "获取文档的流程历史", notes = "获取文档的流程历史")
	@ApiImplicitParams({
			@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
			@ApiImplicitParam(name = "docId",value = "文档id",required = true,paramType = "path",dataType = "string")
	})
	public Resource getHistorys(@PathVariable String applicationId, @PathVariable String docId,@RequestParam String content) throws Exception {
		Collection<FlowHistoryVO> result = new ArrayList<>();
		List<FlowHistoryVO> flowhiss = workflowService.getHistorys(applicationId, docId);
		if(!StringUtil.isBlank(content)){
			content = content.toLowerCase();
			for (FlowHistoryVO his:flowhiss){
				if(his.getStartNodeName().toLowerCase().contains(content) ||
						his.getTargetNodeName().toLowerCase().contains(content) ||
						his.getAuditorName().toLowerCase().contains(content) ||
						his.getAgentAuditorName().toLowerCase().contains(content) ||
						his.getAttitude().toLowerCase().contains(content)){
					result.add(his);
				}
			}
		}else {
			result = flowhiss;
		}
		return success("ok", result);
	}

	/**
	 * 获取文档的流程图
	 * @param applicationId
	 * 			软件id
	 * @param docId
	 * 			文档id
	 * @param instanceId
	 * 			流程实例id
	 * @return
	 * 			文档的流程图
	 * @throws Exception
	 */
	@GetMapping("/documents/{docId}/workflows/{instanceId}/flowchart")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "获取文档的流程图", notes = "获取文档的流程图")
	@ApiImplicitParams({
			@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
			@ApiImplicitParam(name = "docId",value = "文档id",required = true,paramType = "path",dataType = "string"),
			@ApiImplicitParam(name = "instanceId",value = "流程实例id",required = true,paramType = "path",dataType = "string")
	})
	public Resource getWorkflowChart(@PathVariable String applicationId, @PathVariable String docId, @PathVariable String instanceId) throws Exception {
		JSONObject result = workflowService.getWorkflowChart(applicationId, docId, instanceId);
		return success("ok", result);
	}

	/**
	 * 发起流程
	 * @param applicationId 软件id
	 * @param docid 文档id
	 * @param content 请求包体
	 * @return
	 * @throws Exception
	 */
	@PostMapping("/documents/{docid}/workflows/initiate")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "发起流程", notes = "发起流程")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "docid",value = "文档id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "content",value = "请求包体",required = true,paramType = "body",dataType = "string")
	})
	public Resource initiate(@PathVariable String applicationId, @PathVariable String docid,
			@RequestBody String content) throws Exception {
		Configuration configuration = Configuration.defaultConfiguration();
		configuration = configuration.addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL);
		
		DocumentContext parse = parse(content, configuration);
		
		ParamsTable params = getParams();
		Document doc = this.prepareDocument(content, applicationId, "$.document", params);
		

		//获取流程定义模板id
		String flowId = parse.read("$.flowId");
		//获取下个节点定义对象的id
		String nextId = parse.read("$.nextId");
		//获取操作按钮id
		String actId = parse.read("$.actId");

		//调用流程服务发起流程(请求的参数+domainid+_pagelines,webuser,文档,流程定义模板的id,操作按钮id,下个节点定义对象的id,应用id)
		String result = workflowService.initiate(getParams(), getUser(), doc, flowId, actId, nextId, applicationId);
		return success("ok", result);
	}

	/**
	 * 获取文档的流程催办历史
	 * @param applicationId
	 * 			软件id
	 * @param instanceId
	 * 			流程实例id
	 * @return
	 * 			流程催办历史对象的集合
	 * @throws Exception
	 */
	@GetMapping("/documents/{docId}/workflows/remind-historys")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "获取文档的流程催办历史", notes = "获取文档的流程催办历史")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "docId",value = "文档id",required = true,paramType = "path",dataType = "string")
	})
	public Resource getRemindHistorys(@PathVariable String applicationId, @PathVariable String docId)
			throws Exception {
		List<FlowReminderHistory> result = workflowService.getRemindHistorys(applicationId, docId);
		return success("ok", result);
	}

	/**
	 * 获取文档的流程提交面板
	 * @param applicationId 软件id
	 * @param docId 文档id
	 * @return
	 * @throws Exception
	 */
	@PostMapping("/documents/{docId}/panels/submission")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "获取文档的流程提交面板", notes = "获取文档的流程提交面板")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "docId",value = "文档id",required = true,paramType = "path",dataType = "string"),
			@ApiImplicitParam(name = "content",value = "请求包体",required = true,paramType = "body",dataType = "string")
	})
	public Resource submissionPanels(@PathVariable String applicationId, @PathVariable String docId, @RequestBody String content) throws Exception {
		ParamsTable params = getParams();
		Document doc = this.prepareDocument(content, applicationId, "$.document", params);
		JSONObject result = workflowService.submissionPanels(applicationId, doc, params, getUser());
		return success("ok", result);
	}

	/**
	 * 获取文档的流程发起面板
	 * @param applicationId 软件id
	 * @param docid 文档id
	 * @return
	 * @throws Exception
	 */
	@PostMapping("/documents/{docid}/panels/initiate")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "获取文档的流程发起面板", notes = "获取文档的流程发起面板")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "docid",value = "文档id",required = true,paramType = "path",dataType = "string"),
			@ApiImplicitParam(name = "content",value = "请求包体",required = true,paramType = "body",dataType = "string")
	})
	public Resource initiatePanels(@PathVariable String applicationId, @PathVariable String docid, @RequestBody String content) throws Exception {
		ParamsTable params = getParams();
		Document doc = this.prepareDocument(content, applicationId, "$.document", params);
		
		JSONArray result = workflowService.initiatePanels(applicationId, doc, getUser(), getParams());
		return success("ok", result);
	}

	/**
	 * 提交流程
	 * @param applicationId 软件id
	 * @param docid 文档id
	 * @param content 请求包体内容
	 * @return
	 * @throws Exception
	 */
	@PutMapping("/documents/{docId}/workflows/submit")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "提交流程", notes = "提交流程")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "docId",value = "文档id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "content",value = "请求包体内容",required = true,paramType = "body",dataType = "string")
	})
	public Resource submitWorkflow(@PathVariable String applicationId, @PathVariable String docId,
			@RequestBody String content) throws Exception {
		Configuration configuration = Configuration.defaultConfiguration();
		configuration = configuration.addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL);
		DocumentContext parse = parse(content, configuration);

		ParamsTable params = getParams();
		Document doc =null;
		try {
			doc = this.prepareDocument(content, applicationId, "$.document", params);

			//String parentId = parse.read("$.document.parentid");
			//获取流程操作类型 参考FlowType类
			//获取流程定义模板对象id
			String flowId = parse.read("$.flowId");
			//获取当前节点的节点定义对象id
			String currentNodeId = parse.read("$.currentNodeId");
			//获取下一个节点的节点对象集合的id (当前节点的下个节点可能有多个所以这里使用集合)
			List<String> nextNodeIds = parse.read("$.nextNodeIds");
			//是否有多个当前节点审批人为同一人
			String isMoreCurrentNode = parse.read("$.isMoreCurrentNode")==null?null:parse.read("$.isMoreCurrentNode").toString();
			String signature = parse.read("$.signatureJson");

			//检验nextNodeIds是否为空
			if (nextNodeIds == null || nextNodeIds.isEmpty()) {
				return error(4001, "请选择审批节点!", null);
			}
			if (!StringUtil.isBlank(signature)) {
				params.setParameter("_signature", signature);
			}

			//获取审批时填写的审批意见
			String attitude = parse.read("$.attitude");
			if(!StringUtil.isBlank(attitude))
				params.setParameter("attitude", attitude);


			//获取审批时填写的审批附件
			List<Object> fileList = parse.read("$.fileList");
			if(!StringUtil.isBlank(attitude))
				params.setParameter("fileList", fileList);

			if (!StringUtil.isBlank(isMoreCurrentNode)) {
				if (isMoreCurrentNode.equals("true")) {
					params.setParameter("isMoreCurrentNode", true);
				} else {
					params.setParameter("isMoreCurrentNode", false);
				}
			}

			params.setParameter("_flowid", flowId);
			List<Map<String, String>> submitTo = parse.read("$.submitTo");
			if (submitTo != null && !submitTo.isEmpty()) {
				List<JSONObject> submitToList = new ArrayList<JSONObject>();
				for (Map<String, String> map : submitTo) {
					JSONObject json = new JSONObject();
					json.put("nodeid", map.get("nodeid").toString());
					json.put("isToPerson", "true");
					String userIdStr = (String) map.get("userids");
					String newUserIdStr = userIdStr.replaceAll(";", "','");
					String userIds = "['" + newUserIdStr + "']";
					json.put("userids", userIds);
					submitToList.add(json);
				}
				params.setParameter("submitTo", submitToList.toString());
			}
			
			String circulatorInfoStr = parse.read("$.circulatorInfo");
			if (!StringUtil.isBlank(circulatorInfoStr)) {
				List<String> circulatorInfo = new ArrayList<String>();
				String[] userIds = circulatorInfoStr.split(";");
				JSONObject json = new JSONObject();
				json.put("circulator", userIds);
				circulatorInfo.add(json.toString());
				params.setParameter("_circulatorInfo", circulatorInfo.toString());
			}

			//子流程指定审批人
			String subFlowApprover = parse.read("subFlowApprover")==null?null:parse.read("subFlowApprover").toString();
			if(!StringUtil.isBlank(subFlowApprover)){
				params.setParameter("_subFlowApproverInfo", subFlowApprover);
			}

			if(doc.getState()==null) {
				throw new SaasBadException("无效文档数据,doc.getState审批数据不应该为空");
			}

			if(!doc.getState().isTemp()) {
				NodeRT nodert =
						StateMachine.getCurrUserNodeRT(doc, getUser(), currentNodeId);
				if (nodert == null || !nodert.getNodeid().equals(currentNodeId)) {
					return error(4001, "审批人已经没有审批权限，请勿重复提交", null);
				}
			}

			String[] nextNodeIdsArray = nextNodeIds.toArray(new String[0]);
			IUser webUser = getUser();
			Collection<Document> subDocuments = doc.getFrontSubDocuments();
			Collection<ErrorMessage> errors = validateDocument(params, doc, applicationId, webUser);
			if (errors.isEmpty()) {
				DocumentProcess dProcess = RunTimeServiceManager.documentProcess( applicationId);
				if(subDocuments != null){
					List<Document> copySubDocuments = new ArrayList<Document>();
					copySubDocuments.addAll(doc.getSubDocuments());
					for (Document document : copySubDocuments) {
						//保存子表
						if(document.isDelete()){
							dProcess.doRemove(document.getId());
							doc.removeSubDocument(document.getId());
						} else if(document.isEdit()) {
							Collection<ErrorMessage> subErrors = validateDocument(params, document, applicationId, getUser());
							if(!subErrors.isEmpty()){
								return error(4001, "子表单校验不通过", subErrors);
							}
							document.setVersions(-1);
							dProcess.doCreateOrUpdate(document, getUser());
						}
					}
					//在保存完子表之后重计算一下主表
					doc = doc.getForm().recalculateDocument(doc, params, getUser());
				}

				String result = workflowService.submitWorkflow(params, doc, applicationId, webUser, currentNodeId, attitude, nextNodeIdsArray);

				return success("ok", result);
			} else {
				return error(4001, "表单校验不通过", errors);
			}
		}catch (Exception e){
			String message ="";
			if(e instanceof OBPMValidateException) {
				message = ((OBPMValidateException) e).getValidateMessage();
			}else if (e.getMessage().toLowerCase().indexOf("data too long for column") > -1){
				int index = e.getMessage().indexOf("ITEM_");
				int off = e.getMessage().lastIndexOf("'");
				String fieldName = e.getMessage().substring(index,off);
				message = new StringBuffer().append(fieldName).append("字段过长！").toString();
			}else {
				message =e.getMessage();
			}
			Collection<ErrorMessage> errmsgs = new ArrayList<ErrorMessage>();
			ErrorMessage msg = new ErrorMessage();
			msg.setErrcode(40001);
			msg.setErrmsg(message);
			msg.setField("");
			errmsgs.add(msg);
			e.printStackTrace();
			return error(4001, "流程校验不通过", errmsgs);
		}
	}

	/**
	 * 获取文档的流程回退面板
	 * @param applicationId 软件id
	 * @param docId 文档id
	 * @return
	 * @throws Exception
	 */
	@PostMapping("/documents/{docId}/panels/back")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "获取文档的流程回退面板", notes = "获取文档的流程回退面板")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "docId",value = "文档id",required = true,paramType = "path",dataType = "string"),
			@ApiImplicitParam(name = "content",value = "请求包体",required = true,paramType = "body",dataType = "string")
	})
	public Resource backPanels(@PathVariable String applicationId, @PathVariable String docId,@RequestBody String content) throws Exception {
		ParamsTable params = getParams();
		Document doc = this.prepareDocument(content, applicationId, "$.document", params);
		JSONObject result = workflowService.backPanels(applicationId, doc, getUser());
		return success("ok", result);
	}



	/**
	 * 获取文档的流程跳转面板
	 * @param applicationId 软件id
	 * @param docId 文档id
	 * @return
	 * @throws Exception
	 */
	@PostMapping("/documents/{docId}/panels/jump")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "获取文档的流程回退面板", notes = "获取文档的流程回退面板")
	@ApiImplicitParams({
			@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
			@ApiImplicitParam(name = "docId",value = "文档id",required = true,paramType = "path",dataType = "string"),
			@ApiImplicitParam(name = "content",value = "请求包体",required = true,paramType = "body",dataType = "string")
	})
	public Resource jumpPanels(@PathVariable String applicationId, @PathVariable String docId,@RequestBody String content) throws Exception {
		ParamsTable params = getParams();
		Document doc = this.prepareDocument(content, applicationId, "$.document", params);
		JSONObject result = workflowService.jumpPanels(applicationId, doc, getUser());
		return success("ok", result);
	}

	/**
	 * 回退流程
	 * @param applicationId 软件id
	 * @param docid 文档id
	 * @param content 请求包体内容
	 * @return
	 * @throws Exception
	 */
	@PutMapping("/documents/{docid}/workflows/back")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "回退流程", notes = "回退流程")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "docid",value = "文档id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "content",value = "请求包体内容",required = true,paramType = "body",dataType = "string")
	})
	public Resource backWorkflow(@PathVariable String applicationId, @PathVariable String docid,
			@RequestBody String content) throws Exception {
		Configuration configuration = Configuration.defaultConfiguration();
		configuration = configuration.addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL);
		DocumentContext parse = parse(content, configuration);
		
		ParamsTable params = getParams();
		Document doc = this.prepareDocument(content, applicationId, "$.document", params);
		
		String currentNodeId = parse.read("$.currentNodeId");
		String flowId = parse.read("$.flowId");
		String attitude = parse.read("$.attitude");
		if(!StringUtil.isBlank(attitude))
			params.setParameter("attitude", attitude);
		List<String> nextNodeIds = parse.read("$.nextNodeIds");
		String[] nextNodeIdsArray = nextNodeIds.toArray(new String[0]);
		//手写签名
		String signature = parse.read("$.signatureJson");
		if(!StringUtil.isBlank(signature)){
			params.setParameter("_signature", signature);
		}

		params.setParameter("_flowid", flowId);

		Collection<Document> subDocuments = doc.getFrontSubDocuments();
		if(subDocuments != null){
			DocumentProcess dProcess = RunTimeServiceManager.documentProcess( applicationId);
			List<Document> copySubDocuments = new ArrayList<Document>();
			copySubDocuments.addAll(doc.getSubDocuments());
			for (Document document : copySubDocuments) {
				//保存子表
				if(document.isDelete()){
					dProcess.doRemove(document.getId());
					doc.removeSubDocument(document.getId());
				} else if(document.isEdit()) {
					Collection<ErrorMessage> subErrors = validateDocument(params, document, applicationId, getUser());
					if(!subErrors.isEmpty()){
						return error(4001, "子表单校验不通过", subErrors);
					}
					document.setVersions(-1);
					dProcess.doCreateOrUpdate(document, getUser());
				}
			}
		}
		String result = workflowService.backWorkflow(getUser(), applicationId, doc, params, currentNodeId,
				nextNodeIdsArray, attitude);

		return success("ok", result);
	}


	/**
	 * 回退流程历史节点指定历史用户
	 * @param applicationId 软件id
	 * @param docid 文档id
	 * @param content 请求包体内容
	 * @return
	 * @throws Exception
	 */
	@PutMapping("/documents/{docid}/workflows/jump")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "回退流程历史节点指定历史用户", notes = "回退流程历史节点指定历史用户")
	@ApiImplicitParams({
			@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
			@ApiImplicitParam(name = "docid",value = "文档id",required = true,paramType = "path",dataType = "string"),
			@ApiImplicitParam(name = "content",value = "请求包体内容",required = true,paramType = "body",dataType = "string")
	})
	public Resource jumpWorkflow(@PathVariable String applicationId, @PathVariable String docid,
								 @RequestBody String content) throws Exception {
		Configuration configuration = Configuration.defaultConfiguration();
		configuration = configuration.addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL);
		DocumentContext parse = parse(content, configuration);

		ParamsTable params = getParams();
		Document doc = this.prepareDocument(content, applicationId, "$.document", params);

		String currentNodeId = parse.read("$.currentNodeId");
		String flowId = parse.read("$.flowId");
		String attitude = parse.read("$.attitude");
		if(!StringUtil.isBlank(attitude))
			params.setParameter("attitude", attitude);
		List<String> nextNodeIds = parse.read("$.nextNodeIds");
		String[] nextNodeIdsArray = nextNodeIds.toArray(new String[0]);
		//手写签名
		String signature = parse.read("$.signatureJson");
		if(!StringUtil.isBlank(signature)){
			params.setParameter("_signature", signature);
		}

		//指定回退人
		List<Map<String, String>> jumpTo = parse.read("$.jumpToPerson");
		if(jumpTo != null && !jumpTo.isEmpty()){
			List<JSONObject> jumpToList = new ArrayList<JSONObject>();
			for (Map<String, String> map : jumpTo) {
				JSONObject json = new JSONObject();
				json.put("nodeid", map.get("nodeid").toString());
				json.put("isToPerson", "true");
				String userIdStr  = (String) map.get("userids");
				String newUserIdStr = userIdStr.replaceAll(";", "','");
				String userIds =  newUserIdStr;
				json.put("userids", userIds);
				jumpToList.add(json);
			}
			params.setParameter("jumpToPerson", jumpToList.toString());
		}

		params.setParameter("_flowid", flowId);

		Collection<Document> subDocuments = doc.getFrontSubDocuments();
		if(subDocuments != null){
			DocumentProcess dProcess = RunTimeServiceManager.documentProcess( applicationId);
			List<Document> copySubDocuments = new ArrayList<Document>();
			copySubDocuments.addAll(doc.getSubDocuments());
			for (Document document : copySubDocuments) {
				//保存子表
				if(document.isDelete()){
					dProcess.doRemove(document.getId());
					doc.removeSubDocument(document.getId());
				} else if(document.isEdit()) {
					Collection<ErrorMessage> subErrors = validateDocument(params, document, applicationId, getUser());
					if(!subErrors.isEmpty()){
						return error(4001, "子表单校验不通过", subErrors);
					}
					document.setVersions(-1);
					dProcess.doCreateOrUpdate(document, getUser());
				}
			}
		}
		String	result = workflowService.backWorkflow(getUser(), applicationId, doc, params, currentNodeId,
					nextNodeIdsArray, attitude);

		return success("ok", result);
	}


	/**
	 * 撤回流程
	 * @param applicationId 软件id
	 * @param docid 文档id
	 * @return
	 * @throws Exception
	 */
	@PutMapping("/documents/{docid}/workflows/retracement")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "撤回流程", notes = "撤回流程")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "docid",value = "文档id",required = true,paramType = "path",dataType = "string")
	})
	public Resource retracementWorkflow(@PathVariable String applicationId, @PathVariable String docid) throws Exception { 
		DocumentProcess dProcess = RunTimeServiceManager.documentProcess(
				applicationId);
		IDocument doc = (IDocument) dProcess.doView(docid);

		String result = workflowService.retracementWorkflow(getParams(), getUser(), doc, applicationId);
		return success("ok", result);
	}

	/**
	 * 挂起流程
	 * @param applicationId 软件id
	 * @param docid 文档id
	 * @return
	 * @throws Exception
	 */
	@PutMapping("/documents/{docid}/workflows/suspend")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "挂起流程", notes = "挂起流程")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "docid",value = "文档id",required = true,paramType = "path",dataType = "string")
	})
	public Resource suspendWorkflow(@PathVariable String applicationId, @PathVariable String docid) throws Exception {
		DocumentProcess dProcess = RunTimeServiceManager.documentProcess(
				applicationId);
		IDocument doc = (IDocument) dProcess.doView(docid);

		String result = workflowService.suspendWorkflow(getParams(), getUser(), applicationId, doc);
		return success("ok", result);
	}

	/**
	 * 恢复流程
	 * @param applicationId 软件id
	 * @param docid 文档id
	 * @return
	 * @throws Exception
	 */
	@PutMapping("/documents/{docid}/workflows/recovery")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "恢复流程", notes = "恢复流程")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "docid",value = "文档id",required = true,paramType = "path",dataType = "string")
	})
	public Resource recoveryWorkflow(@PathVariable String applicationId, @PathVariable String docid) throws Exception {
		DocumentProcess dProcess = RunTimeServiceManager.documentProcess(
				applicationId);
		IDocument doc = (IDocument) dProcess.doView(docid);

		String result = workflowService.recoveryWorkflow(getParams(), getUser(), applicationId, doc);
		return success("ok", result);
	}

	/**
	 * 终止流程
	 * @param applicationId 软件id
	 * @param docid 文档id
	 * @param attitude 意见
	 * @param signature 签名
	 * @return
	 * @throws Exception
	 */
	@PutMapping("/documents/{docid}/workflows/termination")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "终止流程", notes = "终止流程")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "docid",value = "文档id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "attitude",value = "意见",required = false,paramType = "query",dataType = "string"),
		@ApiImplicitParam(name = "signature",value = "签名",required = false,paramType = "query",dataType = "string")
	})
	public Resource terminateWorkflow(@PathVariable String applicationId, @PathVariable String docid, @RequestParam(required=false) String attitude, @RequestParam(required=false) String signature) throws Exception {
		DocumentProcess dProcess = RunTimeServiceManager.documentProcess(
				applicationId);
		Document doc = (Document) dProcess.doView(docid);

		String result = workflowService.terminateWorkflow(getParams(), getUser(), applicationId, attitude , signature, doc);
		return success("ok", result);
	}
	
	/**
	 * 流程催办
	 * @param applicationId 软件id
	 * @param docid 文档id
	 * @param content 请求包体
	 * @return
	 * @throws Exception
	 */
	@PutMapping("/documents/{docid}/workflows/remind")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "流程催办", notes = "流程催办")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "docid",value = "文档id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "content",value = "请求包体",required = true,paramType = "body",dataType = "string")
	})
	public Resource reminderWorkflow(@PathVariable String applicationId, @PathVariable String docid,
			@RequestBody String content) throws Exception{
		Configuration configuration = Configuration.defaultConfiguration();
		configuration = configuration.addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL);
		DocumentContext parse = parse(content, configuration);
		
		String reminderContent = parse.read("$.attitude");
		List<String> nodertIds = parse.read("$.nodertIds");
		String[] nodertIdsArray = nodertIds.toArray(new String[0]);
		if(nodertIdsArray.length==0){
			return error(4001, "请选择催办节点!", null);
		}
		String signature = parse.read("$.signatureJson");
		
		String result = workflowService.reminderWorkflow(getUser(), applicationId, docid, nodertIdsArray, reminderContent, signature);
		return success("ok", result);
	}
	
	/**
	 * 自由流程提交
	 * @param docid 文档id
	 * @param applicationId 软件id
	 * @param content 请求包体
	 * @return
	 * @throws Exception
	 */
	@PutMapping("/documents/{docid}/freeflows/submit")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "自由流程提交", notes = "自由流程提交")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "docid",value = "文档id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "content",value = "请求包体",required = true,paramType = "body",dataType = "string")
	})
	public Resource submitFreeFlows(@PathVariable String docid, @PathVariable String applicationId, @RequestBody String content) throws Exception{
		Configuration configuration = Configuration.defaultConfiguration();
		configuration = configuration.addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL);
		DocumentContext parse = parse(content, configuration);
		
		ParamsTable params = getParams();
		Document doc = this.prepareDocument(content, applicationId, "$.document", params);
		
		String nextUserId = parse.read("$.nextUserId");
		if(StringUtil.isBlank(nextUserId)){
			return error(4001, "{*[cn.myapps.runtime.workflow.choose_specify_auditor]*}",new ArrayList<>());
		}
		String attitude = parse.read("$.attitude");
		if(!StringUtil.isBlank(attitude))
			params.setParameter("attitude", attitude);
		String signature = parse.read("$.signatureJson");
		if(!StringUtil.isBlank(signature)){
			params.setParameter("_signature", signature);
		}

		IUser webUser = getUser();
		Collection<Document> subDocuments = doc.getFrontSubDocuments();
		Collection<ErrorMessage> errors = validateDocument(params, doc, applicationId, webUser);
		
		if(errors.isEmpty()){
			DocumentProcess dProcess = RunTimeServiceManager.documentProcess( applicationId);
			if(subDocuments != null){
				List<Document> copySubDocuments = new ArrayList<Document>();
				copySubDocuments.addAll(doc.getSubDocuments());
				for (Document document : copySubDocuments) {
					//保存子表
					if(document.isDelete()){
						dProcess.doRemove(document.getId());
						doc.removeSubDocument(document.getId());
					} else if(document.isEdit()) {
						Collection<ErrorMessage> subErrors = validateDocument(params, document, applicationId, getUser());
						if(!subErrors.isEmpty()){
							return error(4001, "子表单校验不通过", subErrors);
						}
						document.setVersions(-1);
						dProcess.doCreateOrUpdate(document, getUser());
					}
				}
				//在保存完子表之后重计算一下主表
				doc = doc.getForm().recalculateDocument(doc, params, getUser());
			}
			String result = workflowService.submitFreeFlows(doc, webUser, params, applicationId, attitude, nextUserId);
			return success("ok", result);
		}else{
			return error(4001, "表单校验不通过", errors);
		}
		
	}
	
	/**
	 * 自由流程回退
	 * @param docid 文档id
	 * @param applicationId 软件id
	 * @param content 请求包体
	 * @return
	 * @throws Exception
	 */
	@PutMapping("/documents/{docid}/freeflows/back")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "自由流程回退", notes = "自由流程回退")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "docid",value = "文档id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "content",value = "请求包体",required = true,paramType = "body",dataType = "string")
	})
	public Resource backFreeFlows(@PathVariable String docid, @PathVariable String applicationId, @RequestBody String content) throws Exception{
		Configuration configuration = Configuration.defaultConfiguration();
		configuration = configuration.addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL);
		DocumentContext parse = parse(content, configuration);
		
		ParamsTable params = getParams();
		Document doc = this.prepareDocument(content, applicationId, "$.document", params);

		String nextUserId = parse.read("$.nextUserId");
		String attitude = parse.read("$.attitude");
		if(!StringUtil.isBlank(attitude))
			params.setParameter("attitude", attitude);
		//手写签名
		String signature = parse.read("$.signatureJson");
		if(!StringUtil.isBlank(signature)){
			params.setParameter("_signature", signature);
		}
		
		IUser webUser = getUser();
		Collection<Document> subDocuments = doc.getFrontSubDocuments();
		Collection<ErrorMessage> errors = validateDocument(params, doc, applicationId, webUser);
		if(errors.isEmpty()){
			DocumentProcess dProcess = RunTimeServiceManager.documentProcess( applicationId);
			if(subDocuments != null){
				List<Document> copySubDocuments = new ArrayList<Document>();
				copySubDocuments.addAll(doc.getSubDocuments());
				for (Document document : copySubDocuments) {
					//保存子表
					if(document.isDelete()){
						dProcess.doRemove(document.getId());
						doc.removeSubDocument(document.getId());
					} else if(document.isEdit()) {
						Collection<ErrorMessage> subErrors = validateDocument(params, document, applicationId, getUser());
						if(!subErrors.isEmpty()){
							return error(4001, "子表单校验不通过", subErrors);
						}
						document.setVersions(-1);
						dProcess.doCreateOrUpdate(document, getUser());
					}
				}
			}
			String result = workflowService.backFreeFlows(doc, getUser(), params, applicationId, attitude, nextUserId);
			return success("ok", result);
		}else{
			return error(4001, "表单校验不通过", errors);
		}
	}
	
	/**
	 * 自由流程发起
	 * @param docid 文档id
	 * @param applicationId 软件id
	 * @param content 请求包体
	 * @return
	 * @throws Exception
	 */
	@PostMapping("/documents/{docid}/freeflows/initiate")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "自由流程发起", notes = "自由流程发起")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "docid",value = "文档id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "content",value = "请求包体",required = true,paramType = "body",dataType = "string")
	})
	public Resource initiateFreeFlows(@PathVariable String docid, @PathVariable String applicationId, @RequestBody String content) throws Exception{
		Configuration configuration = Configuration.defaultConfiguration();
		configuration = configuration.addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL);
		DocumentContext parse = parse(content, configuration);

		String nextUserId = parse.read("$.nextUserId");
		String attitude = parse.read("$.attitude");

		Map<String, Object> items = parse.read("$.document.items");

		DocumentProcess dProcess = RunTimeServiceManager.documentProcess(
				applicationId);
		Document doc = (Document) MemoryCacheUtil.getFromPrivateSpace(docid, getUser());
		if (doc == null) {
			doc = (Document) dProcess.doView(docid);
		}

		for (Map.Entry<String, Object> entry : items.entrySet()) {
			String key = entry.getKey();
			Object value = entry.getValue();
			Item item = doc.findItem(key);
			if (item == null)
				throw new Exception(key + " 值不存在");
			item.setValue(value);
		}
		
		String result = workflowService.initiateFreeFlows(doc, getUser(), getParams(), applicationId, attitude, nextUserId);
		return success("ok", result);
	}
	
	/**
	 * 自由流程结束
	 * @param docid 文档id 
	 * @param applicationId 软件id
	 * @param content 请求包体
	 * @return
	 * @throws Exception
	 */
	@PutMapping("/documents/{docid}/freeflows/complete")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "自由流程结束", notes = "自由流程结束")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "docid",value = "文档id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "content",value = "请求包体",required = true,paramType = "body",dataType = "string")
	})
	public Resource completeFreeFlows(@PathVariable String docid, @PathVariable String applicationId, @RequestBody String content) throws Exception{
		Configuration configuration = Configuration.defaultConfiguration();
		configuration = configuration.addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL);
		DocumentContext parse = parse(content, configuration);
		
		ParamsTable params = getParams();
		Document doc = this.prepareDocument(content, applicationId, "$.document", params);

		String attitude = parse.read("$.attitude");
		if(!StringUtil.isBlank(attitude))
			params.setParameter("attitude", attitude);
		//手写签名
		String signature = parse.read("$.signatureJson");
		if(!StringUtil.isBlank(signature)){
			params.setParameter("_signature", signature);
		}
		String result = workflowService.completeFreeFlows(doc, getUser(), params, applicationId, attitude);
		return success("ok", result);
	}
	
	/**
	 * 更新流程节点审批人
	 * @param docid
	 * 			文档id
	 * @param applicationId
	 * 			软件id
	 * @param content 请求包体
	 * @return
	 * @throws Exception
	 */
	@PutMapping("/documents/{docid}/workflows/approvers")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "更新流程节点审批人", notes = "更新流程节点审批人")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "docid",value = "文档id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "content",value = "请求包体",required = true,paramType = "body",dataType = "string")
	})
	public Resource updateApprovers(@PathVariable String docid, @PathVariable String applicationId, @RequestBody String content) throws Exception{
		Configuration configuration = Configuration.defaultConfiguration();
		configuration = configuration.addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL);
		DocumentContext parse = parse(content, configuration);
		
		ParamsTable params = getParams();
		List<String> auditorList = parse.read("$.auditorList");
		
		params.setParameter("auditorList", auditorList);

		DocumentProcess dProcess = RunTimeServiceManager.documentProcess(
				applicationId);
		Document doc = (Document) dProcess.doView(docid);
		String result = workflowService.updateApprovers(params, getUser(), applicationId, doc);
		return success("ok", result);
	}
	
	/**
	 * 自由流程模式回退获取历史处理人
	 * @param instanceId
	 * 			流程实例id
	 * @param applicationId
	 * 			软件id
	 * @param content 请求包体
	 * @return
	 * @throws Exception
	 */
	@GetMapping("/workflows/{instanceId}/hisActorsFreeFlow")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "自由流程模式回退获取历史处理人", notes = "自由流程模式回退获取历史处理人")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "instanceId",value = "流程实例id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string")
	})
	public Resource getHisActorsFreeFlow(@PathVariable String applicationId, @PathVariable String instanceId) throws Exception{
		String _userId = getUser().getId();

		UserProcess userProcess = AuthTimeServiceManager.userRuntimeService();
		FlowHistoryService hisProcess = new FlowHistoryServiceImpl(applicationId);
		FlowRuntimeService runtimeService = new FlowRuntimeServiceImpl(applicationId);
		FlowStateRT instance = runtimeService.findFlowStateRT(instanceId);
		Collection<FlowHistoryVO> list = hisProcess.getFlowHistorysByDocId(instance.getDocid());
		
		//去除重复的处理人
		Map<String , UserVO> userMap = new HashMap<String , UserVO>();
		for(FlowHistoryVO his : list){
			String userId = his.getAuditorId();
			if(_userId.equals(userId)){//回退指定审批人不应包含自己
				continue;
			}
			if(!userMap.containsKey(userId)){
				UserVO userVO = (UserVO) userProcess.doView(userId);
				userMap.put(userId, userVO);
			}
		}
		Collection<UserNode> userList = new ArrayList<UserNode>(); 
		for(String key : userMap.keySet()){
			WebUser webUser = new WebUser(userMap.get(key));
			UserNode u = MyProfileHelper.buildProfile(webUser);
			userList.add(u);
		}
		return success("ok", userList);
	}

	/**
	 * 获取流程指定审批人用户选择框列表
	 * @param docId
	 * 			文档id
	 * @param flowId
	 * 			流程id
	 * @param applicationId
	 * 			软件id
	 * @param nodeId
	 * 			节点id
	 * @param type
	 * 			类型（3:查询;2:角色;1:部门;）
	 * @param selectId
	 * 			点击角色或者部门时选中id
	 * @return
	 * @throws Exception
	 */
	@GetMapping("/documents/{docId}/workflows/{flowId}/selectApprovers")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "获取流程指定审批人用户选择框列表", notes = "获取流程指定审批人用户选择框列表")
	@ApiImplicitParams({
			@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
			@ApiImplicitParam(name = "docId",value = "文档id",required = true,paramType = "path",dataType = "string"),
			@ApiImplicitParam(name = "flowId",value = "流程id",required = true,paramType = "path",dataType = "string"),
			@ApiImplicitParam(name = "nodeId",value = "节点id",required = true,paramType = "query",dataType = "string"),
			@ApiImplicitParam(name = "type",value = "类型",required = true,paramType = "query",dataType = "string"),
			@ApiImplicitParam(name = "selectId",value = "选择id",required = false,paramType = "query",dataType = "string"),
			@ApiImplicitParam(name = "pageSize",value = "每页显示数据数",required = false,paramType = "query",dataType = "string",defaultValue="10"),
			@ApiImplicitParam(name = "pageNum",value = "当前页",required = false,paramType = "query",dataType = "string",defaultValue="1")
	})
	public Resource doSelectByFlow(@PathVariable String applicationId, @PathVariable String docId, @PathVariable String flowId, @RequestParam String nodeId, @RequestParam String type, @RequestParam(required=false) String selectId, @RequestParam(required=false,defaultValue="10") String pageSize, @RequestParam(required=false,defaultValue="1") String pageNum) throws Exception {
		List<UserVO> users = StateMachineHelper.getPrincipalList(docId, getUser(), nodeId, request, flowId, Integer.valueOf(type), selectId);
		List<UserNode> result = new ArrayList<UserNode>();
		for(UserVO userVO : users){
			if(userVO.getStatus()==1){
				UserNode u = MyProfileHelper.buildProfileUser(userVO);
				result.add(u);
			}
		}
		result = sort(result);
		int newPageSzie = Integer.valueOf(pageSize);
		int newPageNum = Integer.valueOf(pageNum);

		//分页
		Pager<UserNode> pager = Pager.create(result, newPageSzie);
		List<UserNode> datas = pager.getPagedList(newPageNum);
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("datas", datas);
		map.put("pageCount", pager.getPageCount());
		map.put("linesPerPage", newPageSzie);
		map.put("rowCount", result.size());
		map.put("pageNum", newPageNum);
		return success("ok", map);
	}

	/**
	 * 获取流程列表
	 * @return
	 * @throws Exception
	 */
	@GetMapping("/workflows/defi")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "获取流程定义列表", notes = "获取流程定义列表")
	@ApiImplicitParams({
			@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
			@ApiImplicitParam(name = "pageSize",value = "每页显示数据数",required = false,paramType = "query",dataType = "string",defaultValue="10"),
			@ApiImplicitParam(name = "pageNum",value = "当前页",required = false,paramType = "query",dataType = "string",defaultValue="1")
	})
	public Resource getFlowDefs(@PathVariable String applicationId, @RequestParam String type, @RequestParam(required=false,defaultValue="10") String pageSize, @RequestParam(required=false,defaultValue="1") String pageNum) throws Exception {
		BillDefiDesignTimeService billDefiService = DesignTimeServiceManager.billDefiDesignTimeService();

		int newPageSzie = Integer.valueOf(pageSize);
		int newPageNum = Integer.valueOf(pageNum);
		
		DataPackage<BillDefiVO> dpks = billDefiService.query(applicationId, "", newPageNum, newPageSzie);
//		String moduleName = "";
//		String subjectName = "";
//		DataPackage<BillDefiVO> dpks = billDefiService.queryByApplicationIdAndModuleNameAndSubjectName(applicationId,
//				moduleName, subjectName, newPageNum, newPageSzie);
		//分页
		Map<String, Object> map = new HashMap<String, Object>();
		List<JSONObject> list = new ArrayList<JSONObject>();
		
		for (BillDefiVO def : dpks.datas) {
			JSONObject obj = new JSONObject();
			obj.put("id", def.getId());
			obj.put("name", def.getName());
			obj.put("subject", def.getSubject());
			list.add(obj);
		}
		
		map.put("datas", list);
		map.put("pageCount", dpks.getPageCount());
		map.put("linesPerPage", dpks.linesPerPage);
		map.put("rowCount", dpks.rowCount);
		map.put("pageNum", newPageNum);
		return success("ok", map);
	}
	
	/**
	 * 获取流程指定抄送人用户选择框列表
	 * @param docId
	 * 			文档id
	 * @param flowId
	 * 			流程id
	 * @param applicationId
	 * 			软件id
	 * @param nodeId
	 * 			节点id
	 * @param type
	 * 			类型
	 * @param selectId
	 * 			点击角色或者部门时选中id
	 * @return
	 * @throws Exception
	 */
	@GetMapping("/documents/{docId}/workflows/{flowId}/selectCirculators")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "获取流程指定抄送人用户选择框列表", notes = "获取流程指定抄送人用户选择框列表")
	@ApiImplicitParams({
			@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
			@ApiImplicitParam(name = "docId",value = "文档id",required = true,paramType = "path",dataType = "string"),
			@ApiImplicitParam(name = "flowId",value = "流程id",required = true,paramType = "path",dataType = "string"),
			@ApiImplicitParam(name = "nodeId",value = "节点id",required = true,paramType = "query",dataType = "string"),
			@ApiImplicitParam(name = "type",value = "类型",required = true,paramType = "query",dataType = "string"),
			@ApiImplicitParam(name = "selectId",value = "选择id",required = false,paramType = "query",dataType = "string"),
			@ApiImplicitParam(name = "pageSize",value = "每页显示数据数",required = false,paramType = "query",dataType = "string",defaultValue="10"),
			@ApiImplicitParam(name = "pageNum",value = "当前页",required = false,paramType = "query",dataType = "string",defaultValue="1")
	})
	public Resource doSelectCirculatorByFlow(@PathVariable String applicationId, @PathVariable String docId, @PathVariable String flowId, @RequestParam String nodeId, @RequestParam String type, @RequestParam(required=false) String selectId, @RequestParam(required=false,defaultValue="10") String pageSize, @RequestParam(required=false,defaultValue="1") String pageNum) throws Exception {
		Collection<UserVO> users = StateMachineHelper.getCirculatorList(docId, getUser(), nodeId, request, flowId,Integer.valueOf(type), selectId);
		List<UserNode> result = new ArrayList<UserNode>();
		for(UserVO userVO : users){
			if(userVO.getStatus()==1){
				UserNode u = MyProfileHelper.buildProfileUser(userVO);
				result.add(u);
			}
		}
		result = sort(result);
		int newPageSzie = Integer.valueOf(pageSize);
		int newPageNum = Integer.valueOf(pageNum);

		//分页
		Pager<UserNode> pager = Pager.create(result, newPageSzie);
		List<UserNode> datas = pager.getPagedList(newPageNum);
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("datas", datas);
		map.put("pageCount", pager.getPageCount());
		map.put("linesPerPage", newPageSzie);
		map.put("rowCount", result.size());
		map.put("pageNum", newPageNum);
		return success("ok", map);
	}
	/**
	 * 添加/补签意见
	 * @param applicationId
	 * 			软件id
	 * @param instanceId
	 * 			流程实例id
	 * @param comments
	 * 			补签意见
	 * @return
	 * @throws Exception
	 */
	@PutMapping("/workflows/{instanceId}/supplement")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "添加/补签意见", notes = "添加/补签意见")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "instanceId",value = "流程实例id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "comments",value = "补签意见",required = true,paramType = "query",dataType = "string")
	})
	public Resource doFlowSupplement(@PathVariable String applicationId, @PathVariable String instanceId, @RequestParam String comments) throws Exception {
		String result = workflowService.doFlowSupplement(applicationId, instanceId, comments, getUser());
		return success("ok", result);
	}

	/**
	 * 流程协办
	 * @param applicationId
	 * 			软件id
	 * @param instanceId
	 * 			流程实例id
	 * @param attitude
	 * 			协办意见
	 * @param nodeId
	 * 			节点id
	 * @return
	 * @throws Exception
	 */
	@PutMapping("/workflows/{instanceId}/assist")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "流程协办", notes = "流程协办")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "instanceId",value = "流程实例id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "attitude",value = "协办意见",required = true,paramType = "query",dataType = "string")
	})
	public Resource doFlowAssist(@PathVariable String applicationId, @PathVariable String instanceId, @RequestParam String attitude) throws Exception {
		String result = workflowService.doFlowAssist(applicationId, instanceId, attitude, getUser());
		return success("ok", result);
	}
	/**
	 * 流程协办加签
	 * @param applicationId
	 * 			软件id
	 * @param docId
	 * 			文档id
	 * @param userIds
	 * 			加签用户集合
	 * @return
	 * @throws Exception
	 */
	@PutMapping("/documents/{docId}/workflows/assist/addition")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "流程协办加签", notes = "流程协办加签")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "docId",value = "文档id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "userIds",value = "请求包体(用户id集合)",required = true,paramType = "body",dataType = "string")
	})
	public Resource addCoAuditor(@PathVariable String applicationId, @PathVariable String docId, @RequestBody String userIds) throws Exception {
		List<String> idList = parse(userIds).json();
		String result = workflowService.addCoAuditor(applicationId, docId, idList, getUser());
		return success("ok", result);
	}
	/**
	 * 流程主办加签
	 * @param applicationId
	 * 			软件id
	 * @param docId
	 * 			文档id
	 * @param userIds
	 * 			加签用户集合
	 * @return
	 * @throws Exception
	 */
	@PutMapping("/documents/{docId}/workflows/approver/addition")
	@ResponseStatus(HttpStatus.OK)
	@ApiOperation(value = "流程协办加签", notes = "流程协办加签")
	@ApiImplicitParams({
		@ApiImplicitParam(name = "applicationId",value = "软件id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "docId",value = "文档id",required = true,paramType = "path",dataType = "string"),
		@ApiImplicitParam(name = "userIds",value = "请求包体(用户id集合)",required = true,paramType = "body",dataType = "string")
	})
	public Resource addAuditor(@PathVariable String applicationId, @PathVariable String docId, @RequestBody String userIds) throws Exception {
		List<String> idList = parse(userIds).json();
		String result = workflowService.addAuditor(applicationId, docId, idList, getUser());
		return success("ok", result);
	}
	/**
	 * 校验方法
	 *
	 * @param params
	 * @param doc
	 * @param applicationId
	 * @param webUser
	 * @return
	 * @throws Exception
	 */
	private Collection<ErrorMessage> validateDocument(ParamsTable params, Document doc, String applicationId, IUser webUser) throws Exception {
		Collection<ErrorMessage> errmsgs = new ArrayList<ErrorMessage>();
		doc.setDomainid(getDomain());
		//doc = DocumentHelper.rebuildDocument(doc, params, webUser);
		String _flowType = params.getParameterAsString("_flowType");
		boolean isWithoutValidate = params
				.getParameterAsBoolean("isWithoutValidate");
		if (isWithoutValidate)
			return null;
		if ("retracement".equals(_flowType)) {
			_flowType = "85";
		}
		DocumentProcess proxy = RunTimeServiceManager.documentProcess(applicationId);
		if ((_flowType != null && _flowType
				.equals(FlowType.RUNNING2RUNNING_RETRACEMENT))
				|| proxy.isDocSaveUser(doc, params, webUser)) {
			Collection<ValidateMessage> errors = proxy.doValidate(doc,
					params, webUser);
			if (errors != null && errors.size() > 0) {
				for (Iterator<ValidateMessage> iter = errors.iterator(); iter
						.hasNext();) {
					ValidateMessage err = (ValidateMessage) iter.next();
					ErrorMessage msg = new ErrorMessage();
					msg.setErrcode(40001);
					msg.setErrmsg(err.getErrmessage());
					msg.setField(err.getFieldname());
					errmsgs.add(msg);
				}
			}
		}
		return errmsgs;
	}

	private List<UserNode> sort(List<UserNode> list){
		Collections.sort(list, new Comparator<UserNode>() {
			@Override
			public int compare(UserNode o1, UserNode o2) {
				if(o1.getOrderByNo()>o2.getOrderByNo()){
					return 1;
				}
				if (o1.getOrderByNo()==o2.getOrderByNo()){
					return 0;
				}
				return -1;
			}
		});
		return list;
	}
}
