zhourui 5 лет назад
Родитель
Сommit
d6e8c8895c
12 измененных файлов с 988 добавлено и 73 удалено
  1. 175 0
      o2server/configSample/node.json
  2. 27 1
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/ProcessPlatform.java
  3. 56 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/executor/Signal.java
  4. 114 38
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/task/ActionProcessing.java
  5. 480 0
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/task/ActionProcessing2.java
  6. 1 1
      o2server/x_processplatform_core_entity/src/main/java/com/x/processplatform/core/entity/content/Work.java
  7. 19 0
      o2server/x_processplatform_core_express/src/main/java/com/x/processplatform/core/express/ProcessingAttributes.java
  8. 52 0
      o2server/x_processplatform_core_express/src/main/java/com/x/processplatform/core/express/ProcessingSignal.java
  9. 6 12
      o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/Processing.java
  10. 5 3
      o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/jaxrs/test/ActionTest1.java
  11. 46 17
      o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/jaxrs/work/ActionProcessing.java
  12. 7 1
      o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/processor/split/SplitProcessor.java

+ 175 - 0
o2server/configSample/node.json

@@ -0,0 +1,175 @@
+{
+  "enable": true,
+  "isPrimaryCenter": true,
+  "center": {
+    "enable": true,
+    "order": 0.0,
+    "sslEnable": false,
+    "redeploy": true,
+    "port": 20030.0,
+    "httpProtocol": "",
+    "proxyHost": "",
+    "proxyPort": 20030.0,
+    "scanInterval": 0.0,
+    "configApiEnable": true,
+    "statEnable": true,
+    "statExclusions": "*.js,*.gif,*.jpg,*.png,*.css,*.ico",
+    "maxFormContent": 20.0,
+    "###enable": "是否启用###",
+    "###order": "center节点顺序,顺序排列0,1,2...###",
+    "###sslEnable": "是否启用ssl传输加密,如果启用将使用config/keystore文件作为密钥文件.使用config/token.json文件中的sslKeyStorePassword字段为密钥密码,sslKeyManagerPassword为管理密码.###",
+    "###redeploy": "每次启动是否重新部署所有应用.###",
+    "###port": "端口,center服务器端口,默认20030###",
+    "###httpProtocol": "对外http访问协议,http/https###",
+    "###proxyHost": "代理主机,当服务器是通过apache/nginx等代理服务器映射到公网或者通过路由器做端口映射,在这样的情况下需要设置此地址以标明公网访问地址.###",
+    "###proxyPort": "代理端口,当服务器是通过apache/nginx等代理服务器映射到公网或者通过路由器做端口映射,在这样的情况下需要设置此地址以标明公网访问端口.###",
+    "###scanInterval": "重新扫描war包时间间隔(秒)###",
+    "###config": "其他参数###",
+    "###configApiEnable": "允许通过Api修改config###",
+    "###statEnable": "启用统计,默认启用统计.###",
+    "###statExclusions": "统计忽略路径,默认忽略*.js,*.gif,*.jpg,*.png,*.css,*.ico###",
+    "###maxFormContent": "最大提交数据限制(M),限制有所上传的内容大小,包括附件.###"
+  },
+  "application": {
+    "enable": true,
+    "port": 20020.0,
+    "sslEnable": false,
+    "proxyHost": "",
+    "proxyPort": 20020.0,
+    "redeploy": true,
+    "scanInterval": 0.0,
+    "includes": [],
+    "excludes": [],
+    "weights": [],
+    "scheduleWeights": [],
+    "statEnable": true,
+    "statExclusions": "*.js,*.gif,*.jpg,*.png,*.css,*.ico",
+    "maxFormContent": 20.0,
+    "###enable": "是否启用###",
+    "###port": "http/https端口,负责向前端提供数据访问接口.默认为20020端口.###",
+    "###sslEnable": "是否启用ssl传输加密,如果启用将使用config/keystore文件作为密钥文件.使用config/token.json文件中的sslKeyStorePassword字段为密钥密码,sslKeyManagerPassword为管理密码.###",
+    "###proxyHost": "代理主机,当服务器是通过apache/nginx等代理服务器映射到公网或者通过路由器做端口映射,在这样的情况下需要设置此地址以标明公网访问地址.###",
+    "###proxyPort": "代理端口,当服务器是通过apache/nginx等代理服务器映射到公网或者通过路由器做端口映射,在这样的情况下需要设置此地址以标明公网访问端口.###",
+    "###redeploy": "每次启动是否重载全部应用.###",
+    "###scanInterval": "应用reload扫描间隔,\u003c0 表示不会reload应用,扫描到应用文件发生了变化.###",
+    "###includes": "承载的应用,在集群环境下可以选择仅承载部分应用以降低服务器负载,可以使用*作为通配符.###",
+    "###excludes": "选择不承载的应用,和includes的值配合使用可以选择或者排除承载的应用,可以使用*作为通配符.###",
+    "###weights": "设置应用的Web访问权重,在集群环境中,一个应用可以部署多个实例提供负载均衡.通过合计占比来分配应用占比.###",
+    "###scheduleWeights": "设置应用的定时任务权重,在集群环境中,一个应用可以部署多个实例提供负载均衡.通过合计占比来分配应用占比.###",
+    "###statEnable": "启用统计,默认启用统计.###",
+    "###statExclusions": "统计忽略路径,默认忽略*.js,*.gif,*.jpg,*.png,*.css,*.ico###",
+    "###maxFormContent": "最大提交数据限制(M),限制有所上传的内容大小,包括附件.###"
+  },
+  "web": {
+    "enable": true,
+    "sslEnable": false,
+    "proxyHost": "",
+    "weight": 100.0,
+    "dirAllowed": false,
+    "statEnable": false,
+    "statExclusions": "*.gif,*.jpg,*.png,*.ico",
+    "cacheControlMaxAge": 0.0,
+    "###enable": "是否启用###",
+    "###port": "http/https端口,用户输入网址后实际访问的第一个端口.http协议默认为80端口,https默认为443端口.###",
+    "###sslEnable": "是否启用ssl传输加密,如果启用将使用config/keystore文件作为密钥文件.使用config/token.json文件中的sslKeyStorePassword字段为密钥密码,sslKeyManagerPassword为管理密码.###",
+    "###proxyHost": "代理主机,当服务器是通过apache/nginx等代理服务器映射到公网或者通过路由器做端口映射,在这样的情况下需要设置此地址以标明公网访问地址.###",
+    "###proxyPort": "代理端口,当服务器是通过apache/nginx等代理服务器映射到公网或者通过路由器做端口映射,在这样的情况下需要设置此地址以标明公网访问端口.###",
+    "###weight": "设置权重.当前没有作用,###",
+    "###dirAllowed": "允许浏览目录,###",
+    "###statEnable": "启用统计,默认启用统计.###",
+    "###statExclusions": "统计忽略路径,默认忽略*.gif,*.jpg,*.png,*.ico###",
+    "###cacheControlMaxAge": "服务器max-age缓存时间(秒)###",
+    "###proxyCenterEnable": "是否启用center服务器代理.###",
+    "###proxyApplicationEnable": "是否启用application服务器代理###"
+  },
+  "data": {
+    "enable": true,
+    "tcpPort": 20050.0,
+    "webPort": 20051.0,
+    "includes": [],
+    "excludes": [],
+    "jmxEnable": false,
+    "cacheSize": 512.0,
+    "logLevel": "WARN",
+    "maxTotal": 50.0,
+    "maxIdle": 0.0,
+    "statEnable": true,
+    "statFilter": "mergeStat",
+    "slowSqlMillis": 2000.0,
+    "lockTimeout": 120000.0,
+    "###enable": "是否启用,如果没有可用的externalDataSources.json文件,那么默认会在节点中启用本地的H2数据库作为默认的数据库.###",
+    "###tcpPort": "H2数据库jdbc连接端口,登录的用户名:sa,密码为xadmin的密码.数据库创建在/o2server/local/repository/data/X.mv.db,一旦数据库文件被创建,那么该数据库的密码被创建.###",
+    "###webPort": "H2数据库web端口,H2提供一个web端的client,此端口为web端client的访问端口.用户名sa,密码为xadmin数据库初始创建的密码.###",
+    "###includes": "设置此数据库存储的类,默认情况下存储所有类型,如果需要对每个类进行单独的控制以达到高性能,可以将不同的类存储到不同的节点上提高性能.可以使用通配符*###",
+    "###excludes": "在此节点上不存储的类,和includes一起设置实际存储的类,可以使用通配符*###",
+    "###jmxEnable": "是否启动jmx,如果启用,可以通过本地的jmx客户端进行访问,不支持远程jmx客户端.###",
+    "###cacheSize": "H2数据库缓存大小,设置H2用于作为缓存的内存大小,以M作为单位,这里默认为512M.###",
+    "###logLevel": "默认日志级别###",
+    "###maxTotal": "最大使用连接数###",
+    "###maxIdle": "最大空闲连接数###",
+    "###statEnable": "启用统计,默认启用###",
+    "###statFilter": "统计方式配置,默认mergeStat###",
+    "###slowSqlMillis": "执行缓慢sql毫秒数,默认2000毫秒,执行缓慢的sql将被单独记录.###",
+    "###lockTimeout": "默认锁超时时间()毫秒).###"
+  },
+  "storage": {
+    "enable": true,
+    "port": 20040.0,
+    "sslEnable": false,
+    "name": "251",
+    "accounts": [],
+    "prefix": "",
+    "deepPath": false,
+    "###enable": "是否启用,对于二进制流文件,比如附件,图片等存储在单独的文件服务器中,可以支持多种文件服务器,默认情况下使用ftp服务器作为文件服务器,每个节点可以启动一个文件服务器以提供高性能.###",
+    "###port": "ftp服务器端口,此端口可以不对外开放,仅有ApplicationServer进行访问,并不直接对用户提供服务.###",
+    "###sslEnable": "是否启用ssl传输加密,如果启用将使用config/keystore文件作为密钥文件.使用config/token.json文件中的sslKeyStorePassword字段为密钥密码,sslKeyManagerPassword为管理密码.###",
+    "###name": "名称,多个节点中不能重名,默认为251.###",
+    "###accounts": "二进制流文件是分多个账号分段存储的,可以单独设置每个分类的存储配置,一般不需要设置.###",
+    "###passivePorts": "ftp传输有主动和被动之分,如果使用了被动传输,设置被动端口范围,默认为29000-30000.###",
+    "###prefix": "路径前缀.###",
+    "###deepPath": "使用更深的路径###"
+  },
+  "logLevel": "warn",
+  "dumpData": {
+    "enable": true,
+    "cron": "",
+    "size": 7.0,
+    "path": "",
+    "###enable": "是否启用,默认每天凌晨2点进行备份.###",
+    "###cron": "定时任务cron表达式###",
+    "###size": "最大保留份数,超过将自动删除最久的数据.###",
+    "###path": "备份路径###"
+  },
+  "restoreData": {
+    "enable": false,
+    "cron": "",
+    "path": "",
+    "###enable": "是否启用.###",
+    "###cron": "定时任务cron表达式###",
+    "###path": "恢复路径###"
+  },
+  "nodeAgentEnable": true,
+  "nodeAgentPort": 20010.0,
+  "nodeAgentEncrypt": true,
+  "quickStartWebApp": false,
+  "autoStart": true,
+  "###enable": "是否启用###",
+  "###isPrimaryCenter": "是否是center节点,仅允许存在一个center节点###",
+  "###center": "Center服务器配置###",
+  "###application": "Application服务器配置###",
+  "###web": "Web服务器配置###",
+  "###data": "Data服务器配置###",
+  "###storage": "Storage服务器配置###",
+  "###logLevel": "日志级别,默认当前节点的slf4j日志级别,通过系统变量\"org.slf4j.simpleLogger.defaultLogLevel\"设置到当前jvm中.###",
+  "###dumpData": "定时数据导出配置###",
+  "###restoreData": "定时数据导入配置###",
+  "###logSize": "日志文件保留天数.###",
+  "###auditLogSize": "审计日志文件保留天数.###",
+  "###nodeAgentEnable": "是否启用节点代理###",
+  "###nodeAgentPort": "是否启用节点端口###",
+  "###nodeAgentEncrypt": "是否启用节点代理加密###",
+  "###quickStartWebApp": "是否使用快速应用部署###",
+  "###banner": "服务器控制台启动标识###",
+  "###autoStart": "是否自动启动###",
+  "###eraseContentEnable": "是否允许使用擦除数据功能###"
+}

+ 27 - 1
o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/ProcessPlatform.java

@@ -72,7 +72,7 @@ public class ProcessPlatform extends ConfigObject {
 		this.touchDetained = new TouchDetained();
 		this.deleteDraft = new DeleteDraft();
 		this.passExpired = new PassExpired();
-
+		this.processingSignal = new ProcessingSignal();
 	}
 
 	public Integer getExecutorCount() {
@@ -165,6 +165,9 @@ public class ProcessPlatform extends ConfigObject {
 	@FieldDescribe("事件扩充.")
 	private ExtensionEvents extensionEvents;
 
+	@FieldDescribe("流程处理型号设置.")
+	private ProcessingSignal processingSignal;
+
 	public ExtensionEvents getExtensionEvents() {
 		if (null == extensionEvents) {
 			this.extensionEvents = new ExtensionEvents();
@@ -212,6 +215,13 @@ public class ProcessPlatform extends ConfigObject {
 		return maintenanceIdentity;
 	}
 
+	public ProcessingSignal getProcessingSignal() {
+		if (this.processingSignal == null) {
+			this.processingSignal = new ProcessingSignal();
+		}
+		return this.processingSignal;
+	}
+
 	public void save() throws Exception {
 		File file = new File(Config.base(), Config.PATH_CONFIG_PROCESSPLATFORM);
 		FileUtils.write(file, XGsonBuilder.toJson(this), DefaultCharset.charset);
@@ -797,4 +807,20 @@ public class ProcessPlatform extends ConfigObject {
 
 	}
 
+	public static class ProcessingSignal extends ConfigObject {
+
+		public static final Integer DEFAULT_SPLITTHRESHOLD = 5;
+
+		private Integer splitThreshold = DEFAULT_SPLITTHRESHOLD;
+
+		public Integer getSplitThreshold() {
+			return splitThreshold == null ? DEFAULT_SPLITTHRESHOLD : this.splitThreshold;
+		}
+
+		public void setSplitThreshold(Integer splitThreshold) {
+			this.splitThreshold = splitThreshold;
+		}
+
+	}
+
 }

+ 56 - 0
o2server/x_base_core_project/src/main/java/com/x/base/core/project/executor/Signal.java

@@ -0,0 +1,56 @@
+package com.x.base.core.project.executor;
+
+import java.io.Closeable;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+
+import org.apache.commons.io.IOUtils;
+
+public class Signal implements Closeable {
+
+	private PipedInputStream input;
+	private PipedOutputStream output;
+	private volatile boolean completed = false;
+
+	public Signal() {
+		try {
+			this.output = new PipedOutputStream();
+			this.input = new PipedInputStream();
+			input.connect(output);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	public void write(String text) {
+		try {
+			if (!completed) {
+				this.output.write(Objects.toString(text, "").getBytes(StandardCharsets.UTF_8));
+				this.output.close();
+				completed = true;
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	public String read() {
+		String value = null;
+		try {
+			value = IOUtils.toString(this.input, StandardCharsets.UTF_8);
+			input.close();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return value;
+	}
+
+	@Override
+	public void close() {
+		IOUtils.closeQuietly(output, null);
+		IOUtils.closeQuietly(input, null);
+	}
+
+}

+ 114 - 38
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/task/ActionProcessing.java

@@ -5,6 +5,7 @@ import java.util.Date;
 import java.util.List;
 import java.util.stream.Collectors;
 
+import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.StringUtils;
 
 import com.google.gson.JsonElement;
@@ -19,6 +20,7 @@ import com.x.base.core.project.bean.WrapCopierFactory;
 import com.x.base.core.project.config.Config;
 import com.x.base.core.project.exception.ExceptionAccessDenied;
 import com.x.base.core.project.exception.ExceptionEntityNotExist;
+import com.x.base.core.project.executor.Signal;
 import com.x.base.core.project.gson.GsonPropertyObject;
 import com.x.base.core.project.http.ActionResult;
 import com.x.base.core.project.http.EffectivePerson;
@@ -42,6 +44,7 @@ import com.x.processplatform.core.entity.content.WorkLog;
 import com.x.processplatform.core.entity.element.Manual;
 import com.x.processplatform.core.entity.element.Route;
 import com.x.processplatform.core.express.ProcessingAttributes;
+import com.x.processplatform.core.express.ProcessingSignal;
 import com.x.processplatform.core.express.service.processing.jaxrs.task.WrapAppend;
 import com.x.processplatform.core.express.service.processing.jaxrs.task.WrapProcessing;
 import com.x.processplatform.core.express.service.processing.jaxrs.task.WrapUpdatePrevTaskIdentity;
@@ -63,6 +66,8 @@ class ActionProcessing extends BaseAction {
 	private EffectivePerson effectivePerson;
 	private List<TaskCompleted> taskCompleteds = new ArrayList<>();
 	private List<Task> newTasks = new ArrayList<>();
+	private Exception exception = null;
+	private Signal signal = new Signal();
 
 	private Record record;
 	private String series = StringTools.uniqueToken();
@@ -104,7 +109,23 @@ class ActionProcessing extends BaseAction {
 			}
 			this.type = this.type(business, task, wi);
 		}
-		this.processing();
+		new Thread(() -> {
+			if (StringUtils.equals(type, TYPE_APPENDTASK)) {
+				exception = processingAppendTask();
+			} else {
+				exception = processingTask();
+			}
+			signal.close();
+		}, String.format("%s:%s", ActionProcessing.class.getName(), id)).start();
+		if (exception != null) {
+			throw exception;
+		}
+		String value = signal.read();
+		if (StringUtils.isNotBlank(value)) {
+			Wo wo = gson.fromJson(value, Wo.class);
+			result.setData(wo);
+			return result;
+		}
 		audit.log(null, "任务处理");
 		Wo wo = Wo.copier.copy(record);
 		result.setData(wo);
@@ -142,26 +163,20 @@ class ActionProcessing extends BaseAction {
 		return TYPE_TASK;
 	}
 
-	private void processing() throws Exception {
-		switch (type) {
-		case TYPE_APPENDTASK:
-			this.processingAppendTask();
-			break;
-		default:
-			this.processingTask();
-			break;
+	private Exception processingAppendTask() {
+		try {
+			this.processingAppendTask_append();
+			this.taskCompletedId = this.processing_processingTask(TaskCompleted.PROCESSINGTYPE_APPENDTASK);
+			this.processing_processingWork(ProcessingAttributes.TYPE_APPENDTASK);
+			this.processing_record(Record.TYPE_APPENDTASK);
+			this.processing_updateTaskCompleted();
+			this.processing_updateTask();
+			return null;
+		} catch (Exception e) {
+			return e;
 		}
 	}
 
-	private void processingAppendTask() throws Exception {
-		this.processingAppendTask_append();
-		this.taskCompletedId = this.processing_processingTask(TaskCompleted.PROCESSINGTYPE_APPENDTASK);
-		this.processing_processingWork(ProcessingAttributes.TYPE_APPENDTASK);
-		this.processing_record(Record.TYPE_APPENDTASK);
-		this.processing_updateTaskCompleted();
-		this.processing_updateTask();
-	}
-
 	private void processingAppendTask_append() throws Exception {
 		WrapAppend req = new WrapAppend();
 		req.setIdentityList(this.wi.getAppendTaskIdentityList());
@@ -171,25 +186,30 @@ class ActionProcessing extends BaseAction {
 				.getData(WrapStringList.class);
 	}
 
-	private void processingTask() throws Exception {
-		this.taskCompletedId = this.processing_processingTask(TaskCompleted.PROCESSINGTYPE_TASK);
-		this.processing_processingWork(ProcessingAttributes.TYPE_TASK);
-		// 流程流转到取消环节,此时工作已被删除
-		boolean flag = true;
-		try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
-			if ((emc.countEqual(Work.class, Work.job_FIELDNAME, task.getJob()) == 0)
-					&& (emc.countEqual(WorkCompleted.class, WorkCompleted.job_FIELDNAME, task.getJob()) == 0)) {
-				flag = false;
+	private Exception processingTask() {
+		try {
+			this.taskCompletedId = this.processing_processingTask(TaskCompleted.PROCESSINGTYPE_TASK);
+			this.processing_processingWork(ProcessingAttributes.TYPE_TASK);
+			// 流程流转到取消环节,此时工作已被删除
+			boolean flag = true;
+			try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+				if ((emc.countEqual(Work.class, Work.job_FIELDNAME, task.getJob()) == 0)
+						&& (emc.countEqual(WorkCompleted.class, WorkCompleted.job_FIELDNAME, task.getJob()) == 0)) {
+					flag = false;
+				}
 			}
-		}
-		if (flag) {
-			this.processing_record(Record.TYPE_TASK);
-			this.processing_updateTaskCompleted();
-			this.processing_updateTask();
-		} else {
-			record = new Record(workLog, task);
-			record.setCompleted(true);
-			record.setType(Record.TYPE_TASK);
+			if (flag) {
+				this.processing_record(Record.TYPE_TASK);
+				this.processing_updateTaskCompleted();
+				this.processing_updateTask();
+			} else {
+				record = new Record(workLog, task);
+				record.setCompleted(true);
+				record.setType(Record.TYPE_TASK);
+			}
+			return null;
+		} catch (Exception e) {
+			return e;
 		}
 	}
 
@@ -215,10 +235,13 @@ class ActionProcessing extends BaseAction {
 		req.setSeries(this.series);
 		req.setPerson(task.getPerson());
 		req.setIdentity(task.getIdentity());
-		WoId resp = ThisApplication.context().applications()
+		RespOfProcessWork resp = ThisApplication.context().applications()
 				.putQuery(effectivePerson.getDebugger(), x_processplatform_service_processing.class,
 						Applications.joinQueryUri("work", task.getWork(), "processing"), req, task.getJob())
-				.getData(WoId.class);
+				.getData(RespOfProcessWork.class);
+		if (BooleanUtils.isTrue(resp.getOccurProcessingSignal())) {
+			signal.write(gson.toJson(resp));
+		}
 		if (StringUtils.isBlank(resp.getId())) {
 			throw new ExceptionWorkProcessing(task.getId());
 		}
@@ -395,6 +418,27 @@ class ActionProcessing extends BaseAction {
 		private static final long serialVersionUID = -1771383649634969945L;
 		static WrapCopier<Record, Wo> copier = WrapCopierFactory.wo(Record.class, Wo.class, null,
 				JpaObject.FieldsInvisible);
+
+		private ProcessingSignal processingSignal;
+
+		private Boolean occurProcessingSignal;
+
+		public ProcessingSignal getProcessingSignal() {
+			return processingSignal;
+		}
+
+		public void setProcessingSignal(ProcessingSignal processingSignal) {
+			this.processingSignal = processingSignal;
+		}
+
+		public Boolean getOccurProcessingSignal() {
+			return occurProcessingSignal;
+		}
+
+		public void setOccurProcessingSignal(Boolean occurProcessingSignal) {
+			this.occurProcessingSignal = occurProcessingSignal;
+		}
+
 	}
 
 	public static class WoTask extends Task {
@@ -408,6 +452,38 @@ class ActionProcessing extends BaseAction {
 
 	}
 
+	public static class RespOfProcessWork {
+
+		private String id;
+		private Boolean occurProcessingSignal;
+		private ProcessingSignal processingSignal;
+
+		public Boolean getOccurProcessingSignal() {
+			return occurProcessingSignal;
+		}
+
+		public void setOccurProcessingSignal(Boolean occurProcessingSignal) {
+			this.occurProcessingSignal = occurProcessingSignal;
+		}
+
+		public ProcessingSignal getProcessingSignal() {
+			return processingSignal;
+		}
+
+		public void setProcessingSignal(ProcessingSignal processingSignal) {
+			this.processingSignal = processingSignal;
+		}
+
+		public String getId() {
+			return id;
+		}
+
+		public void setId(String id) {
+			this.id = id;
+		}
+
+	}
+
 	public static class Wi extends GsonPropertyObject {
 
 		@FieldDescribe("路由名称")

+ 480 - 0
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/task/ActionProcessing2.java

@@ -0,0 +1,480 @@
+package com.x.processplatform.assemble.surface.jaxrs.task;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.google.gson.JsonElement;
+import com.x.base.core.container.EntityManagerContainer;
+import com.x.base.core.container.factory.EntityManagerContainerFactory;
+import com.x.base.core.entity.JpaObject;
+import com.x.base.core.project.Applications;
+import com.x.base.core.project.x_processplatform_service_processing;
+import com.x.base.core.project.annotation.FieldDescribe;
+import com.x.base.core.project.bean.WrapCopier;
+import com.x.base.core.project.bean.WrapCopierFactory;
+import com.x.base.core.project.config.Config;
+import com.x.base.core.project.exception.ExceptionAccessDenied;
+import com.x.base.core.project.exception.ExceptionEntityNotExist;
+import com.x.base.core.project.gson.GsonPropertyObject;
+import com.x.base.core.project.http.ActionResult;
+import com.x.base.core.project.http.EffectivePerson;
+import com.x.base.core.project.jaxrs.WoId;
+import com.x.base.core.project.jaxrs.WrapBoolean;
+import com.x.base.core.project.jaxrs.WrapStringList;
+import com.x.base.core.project.logger.Audit;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+import com.x.base.core.project.tools.ListTools;
+import com.x.base.core.project.tools.StringTools;
+import com.x.processplatform.assemble.surface.Business;
+import com.x.processplatform.assemble.surface.ThisApplication;
+import com.x.processplatform.core.entity.content.Record;
+import com.x.processplatform.core.entity.content.RecordProperties.NextManual;
+import com.x.processplatform.core.entity.content.Task;
+import com.x.processplatform.core.entity.content.TaskCompleted;
+import com.x.processplatform.core.entity.content.Work;
+import com.x.processplatform.core.entity.content.WorkCompleted;
+import com.x.processplatform.core.entity.content.WorkLog;
+import com.x.processplatform.core.entity.element.Manual;
+import com.x.processplatform.core.entity.element.Route;
+import com.x.processplatform.core.express.ProcessingAttributes;
+import com.x.processplatform.core.express.service.processing.jaxrs.task.WrapAppend;
+import com.x.processplatform.core.express.service.processing.jaxrs.task.WrapProcessing;
+import com.x.processplatform.core.express.service.processing.jaxrs.task.WrapUpdatePrevTaskIdentity;
+import com.x.processplatform.core.express.service.processing.jaxrs.task.WrapUpdatePrevTaskIdentity.PrevTask;
+import com.x.processplatform.core.express.service.processing.jaxrs.taskcompleted.WrapUpdateNextTaskIdentity;
+
+class ActionProcessing2 extends BaseAction {
+
+	private static Logger logger = LoggerFactory.getLogger(ActionProcessing2.class);
+
+	private ActionResult<Wo> result = new ActionResult<>();
+
+	private Wi wi;
+	private Task task;
+	private WorkLog workLog;
+	private Work work;
+	private String taskCompletedId;
+	private String type;
+	private EffectivePerson effectivePerson;
+	private List<TaskCompleted> taskCompleteds = new ArrayList<>();
+	private List<Task> newTasks = new ArrayList<>();
+
+	private Record record;
+	private String series = StringTools.uniqueToken();
+
+	private static final String TYPE_APPENDTASK = "appendTask";
+	private static final String TYPE_TASK = "task";
+
+	ActionResult<Wo> execute(EffectivePerson effectivePerson, String id, JsonElement jsonElement) throws Exception {
+		Audit audit = logger.audit(effectivePerson);
+		this.effectivePerson = effectivePerson;
+		wi = this.convertToWrapIn(jsonElement, Wi.class);
+		try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+			Business business = new Business(emc);
+			this.task = emc.find(id, Task.class);
+			if (null == this.task) {
+				throw new ExceptionEntityNotExist(id, Task.class);
+			}
+			// 获取当前环节已经完成的待办
+			this.taskCompleteds = emc.listEqual(TaskCompleted.class, TaskCompleted.activityToken_FIELDNAME,
+					task.getActivityToken());
+			this.workLog = emc.firstEqualAndEqual(WorkLog.class, WorkLog.job_FIELDNAME, task.getJob(),
+					WorkLog.fromActivityToken_FIELDNAME, task.getActivityToken());
+
+			if (null == workLog) {
+				throw new ExceptionEntityNotExist(WorkLog.class);
+			}
+			this.work = emc.find(this.task.getWork(), Work.class);
+			if (null == this.work) {
+				throw new ExceptionEntityNotExist(this.task.getWork(), Work.class);
+			}
+			if ((!effectivePerson.isCipher()) && effectivePerson.isNotPerson(this.task.getPerson())) {
+				throw new ExceptionAccessDenied(effectivePerson, this.task);
+			}
+			if (StringUtils.isNotEmpty(wi.getRouteName()) || StringUtils.isNotEmpty(wi.getOpinion())
+					|| (!StringUtils.equals(this.task.getMediaOpinion(), wi.getMediaOpinion()))) {
+				emc.beginTransaction(Task.class);
+				this.updateRouteNameOpinionMediaOpinion(task, wi);
+				emc.commit();
+			}
+			this.type = this.type(business, task, wi);
+		}
+		this.processing();
+		audit.log(null, "任务处理");
+		Wo wo = Wo.copier.copy(record);
+		result.setData(wo);
+		return result;
+	}
+
+	private void updateRouteNameOpinionMediaOpinion(Task task, Wi wi) {
+		/* 如果有输入新的路由决策覆盖原有决策 */
+		if (StringUtils.isNotEmpty(wi.getRouteName())) {
+			task.setRouteName(wi.getRouteName());
+		}
+		/* 如果有新的流程意见那么覆盖原有流程意见 */
+		if (StringUtils.isNotEmpty(wi.getOpinion())) {
+			task.setOpinion(wi.getOpinion());
+		}
+		/* 强制覆盖多媒体意见 */
+		task.setMediaOpinion(wi.getMediaOpinion());
+	}
+
+	private String type(Business business, Task task, Wi wi) throws Exception {
+		Manual manual = business.manual().pick(task.getActivity());
+		if (null != manual) {
+			Route route = null;
+			for (Route o : business.route().pick(manual.getRouteList())) {
+				if (StringUtils.equals(o.getName(), task.getRouteName())) {
+					route = o;
+					break;
+				}
+			}
+			if ((null != route) && (StringUtils.equals(route.getType(), Route.TYPE_APPENDTASK))
+					&& StringUtils.equals(manual.getId(), route.getActivity())) {
+				return TYPE_APPENDTASK;
+			}
+		}
+		return TYPE_TASK;
+	}
+
+	private void processing() throws Exception {
+		switch (type) {
+		case TYPE_APPENDTASK:
+			this.processingAppendTask();
+			break;
+		default:
+			this.processingTask();
+			break;
+		}
+	}
+
+	private void processingAppendTask() throws Exception {
+		this.processingAppendTask_append();
+		this.taskCompletedId = this.processing_processingTask(TaskCompleted.PROCESSINGTYPE_APPENDTASK);
+		this.processing_processingWork(ProcessingAttributes.TYPE_APPENDTASK);
+		this.processing_record(Record.TYPE_APPENDTASK);
+		this.processing_updateTaskCompleted();
+		this.processing_updateTask();
+	}
+
+	private void processingAppendTask_append() throws Exception {
+		WrapAppend req = new WrapAppend();
+		req.setIdentityList(this.wi.getAppendTaskIdentityList());
+		ThisApplication.context().applications()
+				.putQuery(x_processplatform_service_processing.class,
+						Applications.joinQueryUri("task", this.task.getId(), "append"), req, this.task.getJob())
+				.getData(WrapStringList.class);
+	}
+
+	private void processingTask() throws Exception {
+		this.taskCompletedId = this.processing_processingTask(TaskCompleted.PROCESSINGTYPE_TASK);
+		this.processing_processingWork(ProcessingAttributes.TYPE_TASK);
+		// 流程流转到取消环节,此时工作已被删除
+		boolean flag = true;
+		try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+			if ((emc.countEqual(Work.class, Work.job_FIELDNAME, task.getJob()) == 0)
+					&& (emc.countEqual(WorkCompleted.class, WorkCompleted.job_FIELDNAME, task.getJob()) == 0)) {
+				flag = false;
+			}
+		}
+		if (flag) {
+			this.processing_record(Record.TYPE_TASK);
+			this.processing_updateTaskCompleted();
+			this.processing_updateTask();
+		} else {
+			record = new Record(workLog, task);
+			record.setCompleted(true);
+			record.setType(Record.TYPE_TASK);
+		}
+	}
+
+	private String processing_processingTask(String processType) throws Exception {
+		WrapProcessing req = new WrapProcessing();
+		req.setProcessingType(processType);
+		WoId resp = ThisApplication.context().applications()
+				.putQuery(x_processplatform_service_processing.class,
+						Applications.joinQueryUri("task", task.getId(), "processing"), req, task.getJob())
+				.getData(WoId.class);
+		if (StringUtils.isBlank(resp.getId())) {
+			throw new ExceptionTaskProcessing(task.getId());
+		} else {
+			/* 获得已办id */
+			return resp.getId();
+		}
+	}
+
+	private void processing_processingWork(String type) throws Exception {
+		ProcessingAttributes req = new ProcessingAttributes();
+		req.setIgnoreEmpowerIdentityList(wi.getIgnoreEmpowerIdentityList());
+		req.setType(ProcessingAttributes.TYPE_TASK);
+		req.setSeries(this.series);
+		req.setPerson(task.getPerson());
+		req.setIdentity(task.getIdentity());
+		WoId resp = ThisApplication.context().applications()
+				.putQuery(effectivePerson.getDebugger(), x_processplatform_service_processing.class,
+						Applications.joinQueryUri("work", task.getWork(), "processing"), req, task.getJob())
+				.getData(WoId.class);
+		if (StringUtils.isBlank(resp.getId())) {
+			throw new ExceptionWorkProcessing(task.getId());
+		}
+	}
+
+	private void processing_record(String type) throws Exception {
+		try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+			final List<String> nextTaskIdentities = new ArrayList<>();
+			record = new Record(workLog, task);
+			// 校验workCompleted,如果存在,那么说明工作已经完成,标识状态为已经完成.
+			WorkCompleted workCompleted = emc.firstEqual(WorkCompleted.class, WorkCompleted.job_FIELDNAME,
+					task.getJob());
+			if (null != workCompleted) {
+				record.setCompleted(true);
+				record.setWorkCompleted(workCompleted.getId());
+			}
+			record.getProperties().setElapsed(
+					Config.workTime().betweenMinutes(record.getProperties().getStartTime(), record.getRecordTime()));
+			record.setType(type);
+			List<Task> list = emc.fetchEqualAndEqual(Task.class,
+					ListTools.toList(Task.person_FIELDNAME, Task.identity_FIELDNAME, Task.unit_FIELDNAME,
+							Task.job_FIELDNAME, Task.work_FIELDNAME, Task.activity_FIELDNAME,
+							Task.activityAlias_FIELDNAME, Task.activityName_FIELDNAME, Task.activityToken_FIELDNAME,
+							Task.activityType_FIELDNAME, Task.empowerFromIdentity_FIELDNAME),
+					Task.job_FIELDNAME, task.getJob(), Task.series_FIELDNAME, this.series);
+			list.stream().collect(Collectors.groupingBy(Task::getActivity, Collectors.toList())).entrySet().stream()
+					.forEach(o -> {
+						NextManual nextManual = new NextManual();
+						nextManual.setActivity(o.getValue().get(0).getActivity());
+						nextManual.setActivityAlias(o.getValue().get(0).getActivityAlias());
+						nextManual.setActivityName(o.getValue().get(0).getActivityName());
+						nextManual.setActivityToken(o.getValue().get(0).getActivityToken());
+						nextManual.setActivityType(o.getValue().get(0).getActivityType());
+						for (Task t : o.getValue()) {
+							nextManual.getTaskIdentityList().add(t.getIdentity());
+							this.newTasks.add(t);
+							nextTaskIdentities.add(t.getIdentity());
+						}
+						record.getProperties().getNextManualList().add(nextManual);
+					});
+			// 去重
+			record.getProperties().setNextManualTaskIdentityList(ListTools.trim(nextTaskIdentities, true, true));
+			TaskCompleted taskCompleted = emc.find(taskCompletedId, TaskCompleted.class);
+			if (null != taskCompleted) {
+				// 处理完成后在重新写入待办信息
+				record.getProperties().setOpinion(taskCompleted.getOpinion());
+				record.getProperties().setRouteName(taskCompleted.getRouteName());
+				record.getProperties().setMediaOpinion(taskCompleted.getMediaOpinion());
+			}
+		}
+		WoId resp = ThisApplication.context().applications()
+				.postQuery(effectivePerson.getDebugger(), x_processplatform_service_processing.class,
+						Applications.joinQueryUri("record", "job", this.work.getJob()), record, this.task.getJob())
+				.getData(WoId.class);
+		if (StringUtils.isBlank(resp.getId())) {
+			throw new ExceptionWorkProcessing(this.work.getId());
+		}
+	}
+
+	private void processing_updateTaskCompleted() throws Exception {
+		// 记录下一处理人信息
+		WrapUpdateNextTaskIdentity req = new WrapUpdateNextTaskIdentity();
+		req.getTaskCompletedList().add(taskCompletedId);
+		req.setNextTaskIdentityList(record.getProperties().getNextManualTaskIdentityList());
+		ThisApplication.context().applications()
+				.putQuery(effectivePerson.getDebugger(), x_processplatform_service_processing.class,
+						Applications.joinQueryUri("taskcompleted", "next", "task", "identity"), req, task.getJob())
+				.getData(WrapBoolean.class);
+	}
+
+	private void processing_updateTask() throws Exception {
+		// 记录上一处理人信息
+		if (ListTools.isNotEmpty(newTasks)) {
+			WrapUpdatePrevTaskIdentity req = new WrapUpdatePrevTaskIdentity();
+			req.setTaskList(ListTools.extractProperty(newTasks, JpaObject.id_FIELDNAME, String.class, true, true));
+			this.taskCompleteds.stream().forEach(o -> {
+				PrevTask prevTask = new PrevTask();
+				prevTask.setCompletedTime(o.getCompletedTime());
+				prevTask.setStartTime(o.getStartTime());
+				prevTask.setOpinion(o.getOpinion());
+				prevTask.setPerson(o.getPerson());
+				prevTask.setIdentity(o.getIdentity());
+				prevTask.setUnit(o.getUnit());
+				prevTask.setRouteName(o.getRouteName());
+				req.getPrevTaskIdentityList().add(prevTask.getIdentity());
+				req.getPrevTaskList().add(prevTask);
+			});
+			PrevTask prevTask = new PrevTask();
+			prevTask.setCompletedTime(new Date());
+			prevTask.setStartTime(task.getStartTime());
+			prevTask.setOpinion(task.getOpinion());
+			prevTask.setPerson(task.getPerson());
+			prevTask.setIdentity(task.getIdentity());
+			prevTask.setUnit(task.getUnit());
+			prevTask.setRouteName(task.getRouteName());
+			req.getPrevTaskIdentityList().add(prevTask.getIdentity());
+			req.setPrevTaskIdentity(prevTask.getIdentity());
+			req.getPrevTaskList().add(prevTask);
+			req.setPrevTask(prevTask);
+			// 去重
+			req.setPrevTaskIdentityList(ListTools.trim(req.getPrevTaskIdentityList(), true, true));
+			ThisApplication.context().applications()
+					.putQuery(effectivePerson.getDebugger(), x_processplatform_service_processing.class,
+							Applications.joinQueryUri("task", "prev", "task", "identity"), req, task.getJob())
+					.getData(WrapBoolean.class);
+		}
+
+		List<Task> empowerTasks = new ArrayList<>();
+		for (Task o : newTasks) {
+			if (StringUtils.isNotEmpty(o.getEmpowerFromIdentity())
+					&& (!StringUtils.equals(o.getEmpowerFromIdentity(), o.getIdentity()))) {
+				empowerTasks.add(o);
+			}
+		}
+
+		if (!empowerTasks.isEmpty()) {
+			List<Record> empowerRecords = new ArrayList<>();
+			try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+				Business business = new Business(emc);
+				for (Task o : empowerTasks) {
+					empowerRecords.add(createEmpowerRecord(business, o));
+				}
+			}
+			for (Record r : empowerRecords) {
+				WoId resp = ThisApplication.context().applications()
+						.postQuery(effectivePerson.getDebugger(), x_processplatform_service_processing.class,
+								Applications.joinQueryUri("record", "job", this.work.getJob()), r, this.task.getJob())
+						.getData(WoId.class);
+				if (StringUtils.isBlank(resp.getId())) {
+					throw new ExceptionWorkProcessing(this.work.getId());
+				}
+			}
+		}
+	}
+
+	private Record createEmpowerRecord(Business business, Task task) throws Exception {
+		Record o = new Record();
+		o.setType(Record.TYPE_EMPOWER);
+		o.setApplication(task.getApplication());
+		o.setProcess(task.getProcess());
+		o.setJob(task.getJob());
+		o.setCompleted(false);
+		o.setWork(task.getWork());
+		o.setFromActivity(task.getActivity());
+		o.setFromActivityAlias(task.getActivityAlias());
+		o.setFromActivityName(task.getActivityName());
+		o.setFromActivityToken(task.getActivityToken());
+		o.setFromActivityType(task.getActivityType());
+		o.setArrivedActivity(task.getActivity());
+		o.setArrivedActivityAlias(task.getActivityAlias());
+		o.setArrivedActivityName(task.getActivityName());
+		o.setArrivedActivityToken(task.getActivityToken());
+		o.setArrivedActivityType(task.getActivityType());
+		o.getProperties().setEmpowerToPerson(task.getPerson());
+		o.getProperties().setEmpowerToIdentity(task.getIdentity());
+		o.getProperties().setEmpowerToUnit(task.getUnit());
+		o.setIdentity(task.getEmpowerFromIdentity());
+		o.setPerson(business.organization().person().getWithIdentity(o.getIdentity()));
+		o.setUnit(business.organization().unit().getWithIdentity(o.getIdentity()));
+		o.getProperties().setElapsed(0L);
+		NextManual nextManual = new NextManual();
+		nextManual.setActivity(task.getActivity());
+		nextManual.setActivityAlias(task.getActivityAlias());
+		nextManual.setActivityName(task.getActivityName());
+		nextManual.setActivityToken(task.getActivityToken());
+		nextManual.setActivityType(task.getActivityType());
+		o.getProperties().getNextManualList().add(nextManual);
+		o.getProperties().getNextManualTaskIdentityList().add(task.getIdentity());
+		return o;
+	}
+
+	public static class Wo extends Record {
+
+		private static final long serialVersionUID = -1771383649634969945L;
+		static WrapCopier<Record, Wo> copier = WrapCopierFactory.wo(Record.class, Wo.class, null,
+				JpaObject.FieldsInvisible);
+	}
+
+	public static class WoTask extends Task {
+
+		private static final long serialVersionUID = 2702712453822143654L;
+
+		static WrapCopier<Task, WoTask> copier = WrapCopierFactory.wo(Task.class, WoTask.class,
+				ListTools.toList(JpaObject.id_FIELDNAME, Task.activity_FIELDNAME, Task.activityName_FIELDNAME,
+						Task.person_FIELDNAME, Task.identity_FIELDNAME, Task.unit_FIELDNAME),
+				null);
+
+	}
+
+	public static class Wi extends GsonPropertyObject {
+
+		@FieldDescribe("路由名称")
+		private String routeName;
+
+		@FieldDescribe("意见")
+		private String opinion;
+
+		@FieldDescribe("多媒体意见")
+		private String mediaOpinion;
+
+		@FieldDescribe("路由数据")
+		private JsonElement routeData;
+
+		@FieldDescribe("新添加的待办处理人")
+		private List<String> appendTaskIdentityList;
+
+		@FieldDescribe("忽略授权身份")
+		private List<String> ignoreEmpowerIdentityList;
+
+		public List<String> getAppendTaskIdentityList() {
+			return appendTaskIdentityList;
+		}
+
+		public void setAppendTaskIdentityList(List<String> appendTaskIdentityList) {
+			this.appendTaskIdentityList = appendTaskIdentityList;
+		}
+
+		public String getRouteName() {
+			return routeName;
+		}
+
+		public void setRouteName(String routeName) {
+			this.routeName = routeName;
+		}
+
+		public String getOpinion() {
+			return opinion;
+		}
+
+		public void setOpinion(String opinion) {
+			this.opinion = opinion;
+		}
+
+		public String getMediaOpinion() {
+			return mediaOpinion;
+		}
+
+		public void setMediaOpinion(String mediaOpinion) {
+			this.mediaOpinion = mediaOpinion;
+		}
+
+		public JsonElement getRouteData() {
+			return routeData;
+		}
+
+		public void setRouteData(JsonElement routeData) {
+			this.routeData = routeData;
+		}
+
+		public List<String> getIgnoreEmpowerIdentityList() {
+			return ignoreEmpowerIdentityList;
+		}
+
+		public void setIgnoreEmpowerIdentityList(List<String> ignoreEmpowerIdentityList) {
+			this.ignoreEmpowerIdentityList = ignoreEmpowerIdentityList;
+		}
+	}
+
+}

+ 1 - 1
o2server/x_processplatform_core_entity/src/main/java/com/x/processplatform/core/entity/content/Work.java

@@ -97,8 +97,8 @@ public class Work extends SliceJpaObject implements ProjectionInterface {
 	public void postLoad() {
 		if ((null != this.properties) && StringUtils.isNotEmpty(this.getProperties().getTitle())) {
 			this.title = this.getProperties().getTitle();
-			this.splitValueList = this.getProperties().getSplitValueList();
 		}
+		this.splitValueList = this.getProperties().getSplitValueList();
 	}
 
 	/* 更新运行方法 */

+ 19 - 0
o2server/x_processplatform_core_express/src/main/java/com/x/processplatform/core/express/ProcessingAttributes.java

@@ -5,7 +5,9 @@ import java.util.List;
 
 import com.google.gson.JsonElement;
 import com.x.base.core.project.annotation.FieldDescribe;
+import com.x.base.core.project.executor.Signal;
 import com.x.base.core.project.gson.GsonPropertyObject;
+import com.x.base.core.project.gson.XGsonBuilder;
 import com.x.base.core.project.tools.StringTools;
 
 public class ProcessingAttributes extends GsonPropertyObject {
@@ -20,6 +22,8 @@ public class ProcessingAttributes extends GsonPropertyObject {
 	public static final String TYPE_SERVICE = "service";
 	private Integer loop = 1;
 
+	private transient Signal signal = null;
+
 	@FieldDescribe("强制从arrive开始")
 	private Boolean forceJoinAtArrive;
 
@@ -37,6 +41,11 @@ public class ProcessingAttributes extends GsonPropertyObject {
 
 	public ProcessingAttributes() {
 		this.series = StringTools.uniqueToken();
+		this.signal = new Signal();
+	}
+
+	public Signal signal() {
+		return this.signal;
 	}
 
 	private Boolean debugger = false;
@@ -166,4 +175,14 @@ public class ProcessingAttributes extends GsonPropertyObject {
 		this.identity = identity;
 	}
 
+	public ProcessingAttributes copyInstanceButSameSignal() {
+		ProcessingAttributes p = XGsonBuilder.convert(this, ProcessingAttributes.class);
+		p.signal(this.signal);
+		return p;
+	}
+
+	private void signal(Signal signal) {
+		this.signal = signal;
+	}
+
 }

+ 52 - 0
o2server/x_processplatform_core_express/src/main/java/com/x/processplatform/core/express/ProcessingSignal.java

@@ -0,0 +1,52 @@
+package com.x.processplatform.core.express;
+
+import java.util.List;
+
+import com.x.base.core.project.gson.GsonPropertyObject;
+
+public class ProcessingSignal extends GsonPropertyObject {
+
+	public ProcessingSignal() {
+
+	}
+
+	public static final String TYPE_SPLIT = "split";
+
+	private String type = "";
+
+	public String getType() {
+		return type;
+	}
+
+	private Split split = new Split();
+
+	public Split getSplit() {
+		if (null == split) {
+			this.split = new Split();
+		}
+		return this.split;
+	}
+
+	public static class Split {
+
+		List<String> splitValueList;
+
+		public List<String> getSplitValueList() {
+			return splitValueList;
+		}
+
+		public void setSplitValueList(List<String> splitValueList) {
+			this.splitValueList = splitValueList;
+		}
+	}
+
+	public static ProcessingSignal splitSignal(List<String> splitValues) {
+		ProcessingSignal p = new ProcessingSignal();
+		p.type = TYPE_SPLIT;
+		Split split = new Split();
+		split.setSplitValueList(splitValues);
+		p.split = split;
+		return p;
+	}
+
+}

+ 6 - 12
o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/Processing.java

@@ -36,20 +36,12 @@ public class Processing extends BaseProcessing {
 		this.entityManagerContainer = EntityManagerContainerFactory.instance().create();
 	}
 
-//	public Processing(ProcessingAttributes processingAttributes, EntityManagerContainer entityManagerContainer)
-//			throws Exception {
-//		this.processingAttributes = processingAttributes;
-//		this.entityManagerContainer = entityManagerContainer;
-//		if (this.processingAttributes.getLoop() > 64) {
-//			throw new Exception("processing too many.");
-//		}
-//	}
-
 	public void processing(String workId) throws Exception {
 		this.processing(workId, new ProcessingConfigurator());
 	}
 
 	public void processing(String workId, ProcessingConfigurator processingConfigurator) throws Exception {
+		System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!11" + Processing.class);
 		try {
 			Work work = null;
 			work = this.entityManagerContainer().fetch(workId, Work.class, ListTools.toList(Work.workStatus_FIELDNAME));
@@ -101,9 +93,10 @@ public class Processing extends BaseProcessing {
 				if (StringUtils.isNotEmpty(str)) {
 					if (processingConfigurator.getContinueLoop()) {
 //						new Processing(processingAttributes, this.entityManagerContainer()).processing(str);
-						/* clone processingAttributes 对象 */
-						new Processing(XGsonBuilder.convert(processingAttributes, ProcessingAttributes.class))
-								.processing(str);
+//						new Processing(XGsonBuilder.convert(processingAttributes, ProcessingAttributes.class))
+//								.processing(str);
+						// clone processingAttributes 对象
+						new Processing(processingAttributes.copyInstanceButSameSignal()).processing(str);
 					}
 				}
 			}
@@ -111,6 +104,7 @@ public class Processing extends BaseProcessing {
 			throw new Exception("processing fialure.", e);
 		} finally {
 			this.entityManagerContainer().close();
+			processingAttributes.signal().close();
 		}
 	}
 

+ 5 - 3
o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/jaxrs/test/ActionTest1.java

@@ -17,7 +17,7 @@ class ActionTest1 extends BaseAction {
 	ActionResult<Wo> execute(EffectivePerson effectivePerson) throws Exception {
 		ActionResult<Wo> result = new ActionResult<>();
 		CallableAction action = new CallableAction();
-		ProcessPlatformExecutorFactory.get(unique).submit(action).get();
+		ProcessPlatformExecutorFactory.get(unique).submit(action);
 		return result;
 	}
 
@@ -26,9 +26,11 @@ class ActionTest1 extends BaseAction {
 	}
 
 	public class CallableAction implements Callable<String> {
+
 		public String call() throws Exception {
-			ThisApplication.context().applications().getQuery(x_processplatform_service_processing.class,
-					Applications.joinQueryUri("test", "test1"), unique);
+			System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!Callable start!" + unique);
+			Thread.sleep(5000);
+			System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!Callable start, again!" + unique);
 			return "";
 		}
 	};

+ 46 - 17
o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/jaxrs/work/ActionProcessing.java

@@ -2,9 +2,12 @@ package com.x.processplatform.service.processing.jaxrs.work;
 
 import java.util.concurrent.Callable;
 
+import org.apache.commons.lang3.StringUtils;
+
 import com.google.gson.JsonElement;
 import com.x.base.core.container.EntityManagerContainer;
 import com.x.base.core.container.factory.EntityManagerContainerFactory;
+import com.x.base.core.project.annotation.FieldDescribe;
 import com.x.base.core.project.exception.ExceptionEntityNotExist;
 import com.x.base.core.project.executor.ProcessPlatformExecutorFactory;
 import com.x.base.core.project.http.ActionResult;
@@ -12,6 +15,7 @@ import com.x.base.core.project.http.EffectivePerson;
 import com.x.base.core.project.jaxrs.WoId;
 import com.x.processplatform.core.entity.content.Work;
 import com.x.processplatform.core.express.ProcessingAttributes;
+import com.x.processplatform.core.express.ProcessingSignal;
 import com.x.processplatform.service.processing.Processing;
 
 class ActionProcessing extends BaseAction {
@@ -36,38 +40,40 @@ class ActionProcessing extends BaseAction {
 
 		}
 
-		// Callable<ActionResult<Wo>> callable = new Callable<ActionResult<Wo>>() {
-		// public ActionResult<Wo> call() throws Exception {
-		// Processing processing = new Processing(wi);
-		// processing.processing(id);
-		// ActionResult<Wo> result = new ActionResult<>();
-		// Wo wo = new Wo();
-		// wo.setId(id);
-		// result.setData(wo);
-		// return result;
-		// }
-		// };
+		ActionResult<Wo> result = new ActionResult<>();
+
+		result.setData(new Wo());
+
+		ProcessPlatformExecutorFactory.get(job).submit(new CallableExecute(wi, id, result));
 
-		return ProcessPlatformExecutorFactory.get(job).submit(new CallableExecute(wi, id)).get();
+		String value = wi.signal().read();
+
+		if (StringUtils.isNotBlank(value)) {
+			result.getData().setId(id);
+			result.getData().setOccurProcessingSignal(true);
+			result.getData().setProcessingSignal(gson.fromJson(value, ProcessingSignal.class));
+			return result;
+		} else {
+			return result;
+		}
 
 	}
 
 	private class CallableExecute implements Callable<ActionResult<Wo>> {
 		private Wi wi;
 		private String id;
+		private ActionResult<Wo> result;
 
-		private CallableExecute(Wi wi, String id) {
+		private CallableExecute(Wi wi, String id, ActionResult<Wo> result) {
 			this.wi = wi;
 			this.id = id;
+			this.result = result;
 		}
 
 		public ActionResult<Wo> call() throws Exception {
 			Processing processing = new Processing(wi);
 			processing.processing(id);
-			ActionResult<Wo> result = new ActionResult<>();
-			Wo wo = new Wo();
-			wo.setId(id);
-			result.setData(wo);
+			result.getData().setId(id);
 			return result;
 		}
 	}
@@ -77,6 +83,29 @@ class ActionProcessing extends BaseAction {
 	}
 
 	public static class Wo extends WoId {
+
+		@FieldDescribe("是否发生流程信号.")
+		private Boolean occurProcessingSignal = false;
+
+		@FieldDescribe("流程信号.")
+		private ProcessingSignal processingSignal;
+
+		public Boolean getOccurProcessingSignal() {
+			return occurProcessingSignal;
+		}
+
+		public void setOccurProcessingSignal(Boolean occurProcessingSignal) {
+			this.occurProcessingSignal = occurProcessingSignal;
+		}
+
+		public ProcessingSignal getProcessingSignal() {
+			return processingSignal;
+		}
+
+		public void setProcessingSignal(ProcessingSignal processingSignal) {
+			this.processingSignal = processingSignal;
+		}
+
 	}
 
 }

+ 7 - 1
o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/processor/split/SplitProcessor.java

@@ -7,6 +7,7 @@ import java.util.Optional;
 import org.apache.commons.lang3.StringUtils;
 
 import com.x.base.core.container.EntityManagerContainer;
+import com.x.base.core.project.config.Config;
 import com.x.base.core.project.logger.Logger;
 import com.x.base.core.project.logger.LoggerFactory;
 import com.x.base.core.project.script.ScriptFactory;
@@ -16,6 +17,7 @@ import com.x.processplatform.core.entity.content.Work;
 import com.x.processplatform.core.entity.content.WorkLog;
 import com.x.processplatform.core.entity.element.Route;
 import com.x.processplatform.core.entity.element.Split;
+import com.x.processplatform.core.express.ProcessingSignal;
 import com.x.processplatform.service.processing.Business;
 import com.x.processplatform.service.processing.processor.AeiObjects;
 
@@ -40,7 +42,7 @@ public class SplitProcessor extends AbstractSplitProcessor {
 	@Override
 	protected List<Work> executing(AeiObjects aeiObjects, Split split) throws Exception {
 		List<Work> results = new ArrayList<>();
-		/* 标志拆分状态 */
+		// 标志拆分状态
 		aeiObjects.getWork().setSplitting(true);
 		aeiObjects.getWork().setSplitToken(StringTools.uniqueToken());
 		aeiObjects.getWork().getSplitTokenList().add(aeiObjects.getWork().getSplitToken());
@@ -49,6 +51,10 @@ public class SplitProcessor extends AbstractSplitProcessor {
 			throw new ExceptionSplitEmptySplitValue(split.getName(), aeiObjects.getWork().getTitle(),
 					aeiObjects.getWork().getId(), aeiObjects.getWork().getJob());
 		}
+		// 如果发送值超过阈值那么发送ProcessingSingal
+		if (splitValues.size() >= Config.processPlatform().getProcessingSignal().getSplitThreshold()) {
+			aeiObjects.getProcessingAttributes().signal().write(gson.toJson(ProcessingSignal.splitSignal(splitValues)));
+		}
 		// 先将当前文档标志拆分值
 		aeiObjects.getWork().setSplitValue(splitValues.get(0));
 		aeiObjects.getWork().getSplitValueList().add(splitValues.get(0));