Просмотр исходного кода

Merge branch 'feature/passSame' into 'develop'

修改自动流转判断身份改为人员,x-token获取顺序改为 parameter->head->cookie->auth

See merge request o2oa/o2oa!170
o2null 5 лет назад
Родитель
Сommit
f18ec8d9ce

+ 10 - 0
o2server/configSample/externalDataSources.json

@@ -0,0 +1,10 @@
+[
+	{
+		"url":"jdbc:mysql://127.0.0.1:3306/X?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8",      
+		"username" : "root",
+		"password" :"password",
+		"includes": [],
+		"excludes": [],
+		"enable" : true
+	}
+]

+ 10 - 0
o2server/configSample/externalDataSources_db2.json

@@ -0,0 +1,10 @@
+[
+	{
+		"url" : "jdbc:db2://127.0.0.1:50000/X",
+		"username" : "db2admin",
+		"password" :"password",
+		"includes": [],
+		"excludes": [],
+		"enable" : true
+	}
+]

+ 10 - 0
o2server/configSample/externalDataSources_dm.json

@@ -0,0 +1,10 @@
+[
+	{
+		"url" : "jdbc:dm://127.0.0.1/X",
+		"username" : "admin",
+		"password" :"password",
+		"includes": [],
+		"excludes": [],
+		"enable" : true
+	}
+]

+ 10 - 0
o2server/configSample/externalDataSources_informix.json

@@ -0,0 +1,10 @@
+[
+	{
+		"url" : "jdbc:sqlserver://127.0.0.1:1433;DatabaseName=X;selectMethod=cursor;sendStringParametersAsUnicode=false",
+		"username" : "sa",
+		"password" :"password",
+		"includes": [],
+		"excludes": [],
+		"enable" : true
+	}
+]

+ 10 - 0
o2server/configSample/externalDataSources_kingbase.json

@@ -0,0 +1,10 @@
+[
+	{
+		"url" : "jdbc:kingbase://127.0.0.1:54321/X",
+		"username" : "system",
+		"password" :"krms",
+		"includes": [],
+		"excludes": [],
+		"enable" : true
+	}
+]

+ 10 - 0
o2server/configSample/externalDataSources_mysql.json

@@ -0,0 +1,10 @@
+[
+	{
+		"url":"jdbc:mysql://127.0.0.1:3306/X?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8",      
+		"username" : "root",
+		"password" :"password",
+		"includes": [],
+		"excludes": [],
+		"enable" : true
+	}
+]

+ 10 - 0
o2server/configSample/externalDataSources_oracle.json

@@ -0,0 +1,10 @@
+[
+	{
+		"url":"jdbc:oracle:thin:@//127.0.0.1:1521/orcl",
+		"username" : "X",
+		"password" :"password",
+		"includes": [],
+		"excludes": [],
+		"enable" : true
+	}
+]

+ 10 - 0
o2server/configSample/externalDataSources_postgresql.json

@@ -0,0 +1,10 @@
+[
+	{
+		"url" : "jdbc:postgresql://localhost:5432/X",
+		"username" : "postgres",
+		"password" :"password",
+		"includes": [],
+		"excludes": [],
+		"enable" : true
+	}
+]

+ 10 - 0
o2server/configSample/externalDataSources_sqlserver.json

@@ -0,0 +1,10 @@
+[
+	{
+		"url" : "jdbc:sqlserver://127.0.0.1:1433;DatabaseName=X;selectMethod=cursor;sendStringParametersAsUnicode=false",
+		"username" : "sa",
+		"password" :"password",
+		"includes": [],
+		"excludes": [],
+		"enable" : true
+	}
+]

+ 290 - 0
o2server/configSample/externalStorageSources.json

@@ -0,0 +1,290 @@
+{
+  "file": [
+    {
+      "protocol": "webdav",
+      "username": "admin",
+      "password": "admin",
+      "host": "127.0.0.1",
+      "port": 8080.0,
+      "prefix": "",
+      "enable": true,
+      "weight": 100.0,
+      "name": "251",
+      "deepPath": false,
+      "###protocol": "协议,可选值ftp,webdav###",
+      "###username": "登录用户名.###",
+      "###password": "登录密码.###",
+      "###host": "主机地址.###",
+      "###port": "端口.###",
+      "###prefix": "前缀路径.###",
+      "###enable": "是否启用###",
+      "###weight": "设置权重.###",
+      "###name": "存储节点名,对应存储名称,谨慎修改.###",
+      "###deepPath": "是否使用更深的路径.###"
+    }
+  ],
+  "processPlatform": [
+    {
+      "protocol": "webdav",
+      "username": "admin",
+      "password": "admin",
+      "host": "127.0.0.1",
+      "port": 8080.0,
+      "prefix": "",
+      "enable": true,
+      "weight": 100.0,
+      "name": "251",
+      "deepPath": false,
+      "###protocol": "协议,可选值ftp,webdav###",
+      "###username": "登录用户名.###",
+      "###password": "登录密码.###",
+      "###host": "主机地址.###",
+      "###port": "端口.###",
+      "###prefix": "前缀路径.###",
+      "###enable": "是否启用###",
+      "###weight": "设置权重.###",
+      "###name": "存储节点名,对应存储名称,谨慎修改.###",
+      "###deepPath": "是否使用更深的路径.###"
+    }
+  ],
+  "mind": [
+    {
+      "protocol": "webdav",
+      "username": "admin",
+      "password": "admin",
+      "host": "127.0.0.1",
+      "port": 8080.0,
+      "prefix": "",
+      "enable": true,
+      "weight": 100.0,
+      "name": "251",
+      "deepPath": false,
+      "###protocol": "协议,可选值ftp,webdav###",
+      "###username": "登录用户名.###",
+      "###password": "登录密码.###",
+      "###host": "主机地址.###",
+      "###port": "端口.###",
+      "###prefix": "前缀路径.###",
+      "###enable": "是否启用###",
+      "###weight": "设置权重.###",
+      "###name": "存储节点名,对应存储名称,谨慎修改.###",
+      "###deepPath": "是否使用更深的路径.###"
+    }
+  ],
+  "meeting": [
+    {
+      "protocol": "webdav",
+      "username": "admin",
+      "password": "admin",
+      "host": "127.0.0.1",
+      "port": 8080.0,
+      "prefix": "",
+      "enable": true,
+      "weight": 100.0,
+      "name": "251",
+      "deepPath": false,
+      "###protocol": "协议,可选值ftp,webdav###",
+      "###username": "登录用户名.###",
+      "###password": "登录密码.###",
+      "###host": "主机地址.###",
+      "###port": "端口.###",
+      "###prefix": "前缀路径.###",
+      "###enable": "是否启用###",
+      "###weight": "设置权重.###",
+      "###name": "存储节点名,对应存储名称,谨慎修改.###",
+      "###deepPath": "是否使用更深的路径.###"
+    }
+  ],
+  "calendar": [
+    {
+      "protocol": "webdav",
+      "username": "admin",
+      "password": "admin",
+      "host": "127.0.0.1",
+      "port": 8080.0,
+      "prefix": "",
+      "enable": true,
+      "weight": 100.0,
+      "name": "251",
+      "deepPath": false,
+      "###protocol": "协议,可选值ftp,webdav###",
+      "###username": "登录用户名.###",
+      "###password": "登录密码.###",
+      "###host": "主机地址.###",
+      "###port": "端口.###",
+      "###prefix": "前缀路径.###",
+      "###enable": "是否启用###",
+      "###weight": "设置权重.###",
+      "###name": "存储节点名,对应存储名称,谨慎修改.###",
+      "###deepPath": "是否使用更深的路径.###"
+    }
+  ],
+  "okr": [
+    {
+      "protocol": "webdav",
+      "username": "admin",
+      "password": "admin",
+      "host": "127.0.0.1",
+      "port": 8080.0,
+      "prefix": "",
+      "enable": true,
+      "weight": 100.0,
+      "name": "251",
+      "deepPath": false,
+      "###protocol": "协议,可选值ftp,webdav###",
+      "###username": "登录用户名.###",
+      "###password": "登录密码.###",
+      "###host": "主机地址.###",
+      "###port": "端口.###",
+      "###prefix": "前缀路径.###",
+      "###enable": "是否启用###",
+      "###weight": "设置权重.###",
+      "###name": "存储节点名,对应存储名称,谨慎修改.###",
+      "###deepPath": "是否使用更深的路径.###"
+    }
+  ],
+  "cms": [
+    {
+      "protocol": "webdav",
+      "username": "admin",
+      "password": "admin",
+      "host": "127.0.0.1",
+      "port": 8080.0,
+      "prefix": "",
+      "enable": true,
+      "weight": 100.0,
+      "name": "251",
+      "deepPath": false,
+      "###protocol": "协议,可选值ftp,webdav###",
+      "###username": "登录用户名.###",
+      "###password": "登录密码.###",
+      "###host": "主机地址.###",
+      "###port": "端口.###",
+      "###prefix": "前缀路径.###",
+      "###enable": "是否启用###",
+      "###weight": "设置权重.###",
+      "###name": "存储节点名,对应存储名称,谨慎修改.###",
+      "###deepPath": "是否使用更深的路径.###"
+    }
+  ],
+  "bbs": [
+    {
+      "protocol": "webdav",
+      "username": "admin",
+      "password": "admin",
+      "host": "127.0.0.1",
+      "port": 8080.0,
+      "prefix": "",
+      "enable": true,
+      "weight": 100.0,
+      "name": "251",
+      "deepPath": false,
+      "###protocol": "协议,可选值ftp,webdav###",
+      "###username": "登录用户名.###",
+      "###password": "登录密码.###",
+      "###host": "主机地址.###",
+      "###port": "端口.###",
+      "###prefix": "前缀路径.###",
+      "###enable": "是否启用###",
+      "###weight": "设置权重.###",
+      "###name": "存储节点名,对应存储名称,谨慎修改.###",
+      "###deepPath": "是否使用更深的路径.###"
+    }
+  ],
+  "report": [
+    {
+      "protocol": "webdav",
+      "username": "admin",
+      "password": "admin",
+      "host": "127.0.0.1",
+      "port": 8080.0,
+      "prefix": "",
+      "enable": true,
+      "weight": 100.0,
+      "name": "251",
+      "deepPath": false,
+      "###protocol": "协议,可选值ftp,webdav###",
+      "###username": "登录用户名.###",
+      "###password": "登录密码.###",
+      "###host": "主机地址.###",
+      "###port": "端口.###",
+      "###prefix": "前缀路径.###",
+      "###enable": "是否启用###",
+      "###weight": "设置权重.###",
+      "###name": "存储节点名,对应存储名称,谨慎修改.###",
+      "###deepPath": "是否使用更深的路径.###"
+    }
+  ],
+  "strategyDeploy": [
+    {
+      "protocol": "webdav",
+      "username": "admin",
+      "password": "admin",
+      "host": "127.0.0.1",
+      "port": 8080.0,
+      "prefix": "",
+      "enable": true,
+      "weight": 100.0,
+      "name": "251",
+      "deepPath": false,
+      "###protocol": "协议,可选值ftp,webdav###",
+      "###username": "登录用户名.###",
+      "###password": "登录密码.###",
+      "###host": "主机地址.###",
+      "###port": "端口.###",
+      "###prefix": "前缀路径.###",
+      "###enable": "是否启用###",
+      "###weight": "设置权重.###",
+      "###name": "存储节点名,对应存储名称,谨慎修改.###",
+      "###deepPath": "是否使用更深的路径.###"
+    }
+  ],
+  "teamwork": [
+    {
+      "protocol": "webdav",
+      "username": "admin",
+      "password": "admin",
+      "host": "127.0.0.1",
+      "port": 8080.0,
+      "prefix": "",
+      "enable": true,
+      "weight": 100.0,
+      "name": "251",
+      "deepPath": false,
+      "###protocol": "协议,可选值ftp,webdav###",
+      "###username": "登录用户名.###",
+      "###password": "登录密码.###",
+      "###host": "主机地址.###",
+      "###port": "端口.###",
+      "###prefix": "前缀路径.###",
+      "###enable": "是否启用###",
+      "###weight": "设置权重.###",
+      "###name": "存储节点名,对应存储名称,谨慎修改.###",
+      "###deepPath": "是否使用更深的路径.###"
+    }
+  ],
+  "structure": [
+    {
+      "protocol": "webdav",
+      "username": "admin",
+      "password": "admin",
+      "host": "127.0.0.1",
+      "port": 8080.0,
+      "prefix": "",
+      "enable": true,
+      "weight": 100.0,
+      "name": "251",
+      "deepPath": false,
+      "###protocol": "协议,可选值ftp,webdav###",
+      "###username": "登录用户名.###",
+      "###password": "登录密码.###",
+      "###host": "主机地址.###",
+      "###port": "端口.###",
+      "###prefix": "前缀路径.###",
+      "###enable": "是否启用###",
+      "###weight": "设置权重.###",
+      "###name": "存储节点名,对应存储名称,谨慎修改.###",
+      "###deepPath": "是否使用更深的路径.###"
+    }
+  ]
+}

+ 29 - 28
o2server/x_base_core_project/src/main/java/com/x/base/core/project/http/HttpToken.java

@@ -2,6 +2,7 @@ package com.x.base.core.project.http;
 
 
 import java.net.URLDecoder;
 import java.net.URLDecoder;
 import java.util.Date;
 import java.util.Date;
+import java.util.Enumeration;
 import java.util.Objects;
 import java.util.Objects;
 import java.util.regex.Matcher;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.regex.Pattern;
@@ -74,7 +75,7 @@ public class HttpToken {
 			}
 			}
 			Date date = DateUtils.parseDate(matcher.group(2), DateTools.formatCompact_yyyyMMddHHmmss);
 			Date date = DateUtils.parseDate(matcher.group(2), DateTools.formatCompact_yyyyMMddHHmmss);
 			TokenType tokenType = TokenType.valueOf(matcher.group(1));
 			TokenType tokenType = TokenType.valueOf(matcher.group(1));
-			long diff = (new Date().getTime() - date.getTime());
+			long diff = (System.currentTimeMillis() - date.getTime());
 			diff = Math.abs(diff);
 			diff = Math.abs(diff);
 			if (TokenType.user.equals(tokenType) || TokenType.manager.equals(tokenType)) {
 			if (TokenType.user.equals(tokenType) || TokenType.manager.equals(tokenType)) {
 				if (diff > (60000L * Config.person().getTokenExpiredMinutes())) {
 				if (diff > (60000L * Config.person().getTokenExpiredMinutes())) {
@@ -111,20 +112,20 @@ public class HttpToken {
 	public void setToken(HttpServletRequest request, HttpServletResponse response, EffectivePerson effectivePerson)
 	public void setToken(HttpServletRequest request, HttpServletResponse response, EffectivePerson effectivePerson)
 			throws Exception {
 			throws Exception {
 		switch (effectivePerson.getTokenType()) {
 		switch (effectivePerson.getTokenType()) {
-		case anonymous:
-			// this.deleteToken(request, response);
-			break;
-		case user:
-			this.setResponseToken(request, response, effectivePerson);
-			break;
-		case manager:
-			this.setResponseToken(request, response, effectivePerson);
-			break;
-		case cipher:
-			this.deleteToken(request, response);
-			break;
-		default:
-			break;
+			case anonymous:
+				// this.deleteToken(request, response);
+				break;
+			case user:
+				this.setResponseToken(request, response, effectivePerson);
+				break;
+			case manager:
+				this.setResponseToken(request, response, effectivePerson);
+				break;
+			case cipher:
+				this.deleteToken(request, response);
+				break;
+			default:
+				break;
 		}
 		}
 	}
 	}
 
 
@@ -139,27 +140,27 @@ public class HttpToken {
 
 
 	public String getToken(HttpServletRequest request) throws Exception {
 	public String getToken(HttpServletRequest request) throws Exception {
 		String token = null;
 		String token = null;
-		if (null != request.getCookies()) {
-			for (Cookie c : request.getCookies()) {
-				if (StringUtils.equals(X_Token, c.getName())) {
-					token = c.getValue();
-					break;
-				}
-			}
-		}
+		token = URLTools.getQueryStringParameter(request.getQueryString(), X_Token);
 		if (StringUtils.isEmpty(token)) {
 		if (StringUtils.isEmpty(token)) {
 			token = request.getHeader(X_Token);
 			token = request.getHeader(X_Token);
 		}
 		}
 		if (StringUtils.isEmpty(token)) {
 		if (StringUtils.isEmpty(token)) {
-			token = request.getHeader(X_Authorization);
+			if (null != request.getCookies()) {
+				for (Cookie c : request.getCookies()) {
+					if (StringUtils.equals(X_Token, c.getName())) {
+						token = c.getValue();
+						break;
+					}
+				}
+			}
 		}
 		}
 		if (StringUtils.isEmpty(token)) {
 		if (StringUtils.isEmpty(token)) {
-			token = URLTools.getQueryStringParameter(request.getQueryString(), X_Token);
+			token = request.getHeader(X_Authorization);
 		}
 		}
 		// 此代码将导致input被关闭.
 		// 此代码将导致input被关闭.
-//		if (StringUtils.isEmpty(token)) {
-//			token = request.getParameter(X_Token);
-//		}
+		// if (StringUtils.isEmpty(token)) {
+		// token = request.getParameter(X_Token);
+		// }
 		return token;
 		return token;
 	}
 	}
 
 

+ 5 - 3
o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/Crypto.java

@@ -26,10 +26,12 @@ public class Crypto {
 
 
 	private final static String DES = "DES";
 	private final static String DES = "DES";
 	
 	
-	private final static String cipher_init = "DES";
+	//private final static String CIPHER_INIT = "DES";
 
 
 	private final static String RSA = "RSA";
 	private final static String RSA = "RSA";
 
 
+	//private final static SecureRandom sr = new SecureRandom();
+
 	public static String encrypt(String data, String key) throws Exception {
 	public static String encrypt(String data, String key) throws Exception {
 		byte[] bt = encrypt(data.getBytes(), key.getBytes());
 		byte[] bt = encrypt(data.getBytes(), key.getBytes());
 		String str = Base64.encodeBase64URLSafeString(bt);
 		String str = Base64.encodeBase64URLSafeString(bt);
@@ -45,7 +47,7 @@ public class Crypto {
 		SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
 		SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
 		SecretKey securekey = keyFactory.generateSecret(dks);
 		SecretKey securekey = keyFactory.generateSecret(dks);
 		// Cipher对象实际完成加密操作
 		// Cipher对象实际完成加密操作
-		Cipher cipher = Cipher.getInstance(cipher_init);
+		Cipher cipher = Cipher.getInstance(DES);
 		// 用密钥初始化Cipher对象
 		// 用密钥初始化Cipher对象
 		cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);
 		cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);
 		return cipher.doFinal(data);
 		return cipher.doFinal(data);
@@ -70,7 +72,7 @@ public class Crypto {
 		SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
 		SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
 		SecretKey securekey = keyFactory.generateSecret(dks);
 		SecretKey securekey = keyFactory.generateSecret(dks);
 		// Cipher对象实际完成解密操作
 		// Cipher对象实际完成解密操作
-		Cipher cipher = Cipher.getInstance(cipher_init);
+		Cipher cipher = Cipher.getInstance(DES);
 		// 用密钥初始化Cipher对象
 		// 用密钥初始化Cipher对象
 		cipher.init(Cipher.DECRYPT_MODE, securekey, sr);
 		cipher.init(Cipher.DECRYPT_MODE, securekey, sr);
 		return cipher.doFinal(data);
 		return cipher.doFinal(data);

+ 218 - 102
o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/processor/manual/ManualProcessor.java

@@ -40,6 +40,9 @@ import com.x.processplatform.core.entity.element.util.WorkLogTree.Node;
 import com.x.processplatform.service.processing.Business;
 import com.x.processplatform.service.processing.Business;
 import com.x.processplatform.service.processing.processor.AeiObjects;
 import com.x.processplatform.service.processing.processor.AeiObjects;
 
 
+/**
+ * @author Zhou Rui
+ */
 public class ManualProcessor extends AbstractManualProcessor {
 public class ManualProcessor extends AbstractManualProcessor {
 
 
 	private static Logger logger = LoggerFactory.getLogger(ManualProcessor.class);
 	private static Logger logger = LoggerFactory.getLogger(ManualProcessor.class);
@@ -53,78 +56,188 @@ public class ManualProcessor extends AbstractManualProcessor {
 		/* 根据manual计算出来的活动处理人 */
 		/* 根据manual计算出来的活动处理人 */
 		List<String> identities = calculateTaskIdentities(aeiObjects, manual);
 		List<String> identities = calculateTaskIdentities(aeiObjects, manual);
 		/* 启用同类工作相同活动节点合并,如果有合并的工作,那么直接返回这个工作. */
 		/* 启用同类工作相同活动节点合并,如果有合并的工作,那么直接返回这个工作. */
-		if (BooleanUtils.isTrue(manual.getManualMergeSameJobActivity())) {
-			List<String> exists = this.arriving_sameJobActivityExistIdentities(aeiObjects, manual);
-			if (ListTools.isNotEmpty(exists)) {
-				Work other = aeiObjects.getWorks().stream().filter(o -> {
-					return StringUtils.equals(aeiObjects.getWork().getJob(), o.getJob())
-							&& StringUtils.equals(aeiObjects.getWork().getActivity(), o.getActivity())
-							&& (!Objects.equals(aeiObjects.getWork(), o));
-				}).findFirst().orElse(null);
-				if (null != other) {
-					identities.removeAll(exists);
-					if (ListTools.isEmpty(identities)) {
-						this.mergeTaskCompleted(aeiObjects, aeiObjects.getWork(), other);
-						this.mergeRead(aeiObjects, aeiObjects.getWork(), other);
-						this.mergeReadCompleted(aeiObjects, aeiObjects.getWork(), other);
-						this.mergeReview(aeiObjects, aeiObjects.getWork(), other);
-						this.mergeAttachment(aeiObjects, aeiObjects.getWork(), other);
-						this.mergeWorkLog(aeiObjects, aeiObjects.getWork(), other);
-						if (ListTools.size(aeiObjects.getWork().getSplitTokenList()) > ListTools
-								.size(other.getSplitTokenList())) {
-							other.setSplitTokenList(aeiObjects.getWork().getSplitTokenList());
-							other.setSplitToken(aeiObjects.getWork().getSplitToken());
-							other.setSplitValue(aeiObjects.getWork().getSplitValue());
-							other.setSplitting(true);
-						}
-						aeiObjects.getUpdateWorks().add(other);
-						aeiObjects.getDeleteWorks().add(aeiObjects.getWork());
-						return other;
+		Work merge = this.arrivingMergeSameJob(aeiObjects, manual, identities);
+		if (null != merge) {
+			return merge;
+		}
+		this.arrivingPassSame(aeiObjects, identities);
+		// if (BooleanUtils.isTrue(manual.getManualMergeSameJobActivity())) {
+		// List<String> exists =
+		// this.arriving_sameJobActivityExistIdentities(aeiObjects, manual);
+		// if (ListTools.isNotEmpty(exists)) {
+		// Work other = aeiObjects.getWorks().stream().filter(o -> {
+		// return StringUtils.equals(aeiObjects.getWork().getJob(), o.getJob())
+		// && StringUtils.equals(aeiObjects.getWork().getActivity(), o.getActivity())
+		// && (!Objects.equals(aeiObjects.getWork(), o));
+		// }).findFirst().orElse(null);
+		// if (null != other) {
+		// identities.removeAll(exists);
+		// if (ListTools.isEmpty(identities)) {
+		// this.mergeTaskCompleted(aeiObjects, aeiObjects.getWork(), other);
+		// this.mergeRead(aeiObjects, aeiObjects.getWork(), other);
+		// this.mergeReadCompleted(aeiObjects, aeiObjects.getWork(), other);
+		// this.mergeReview(aeiObjects, aeiObjects.getWork(), other);
+		// this.mergeAttachment(aeiObjects, aeiObjects.getWork(), other);
+		// this.mergeWorkLog(aeiObjects, aeiObjects.getWork(), other);
+		// if (ListTools.size(aeiObjects.getWork().getSplitTokenList()) > ListTools
+		// .size(other.getSplitTokenList())) {
+		// other.setSplitTokenList(aeiObjects.getWork().getSplitTokenList());
+		// other.setSplitToken(aeiObjects.getWork().getSplitToken());
+		// other.setSplitValue(aeiObjects.getWork().getSplitValue());
+		// other.setSplitting(true);
+		// }
+		// aeiObjects.getUpdateWorks().add(other);
+		// aeiObjects.getDeleteWorks().add(aeiObjects.getWork());
+		// return other;
+		// }
+		// }
+		// }
+		// }
+		aeiObjects.getWork().setManualTaskIdentityList(new ArrayList<String>(identities));
+		// /* 查找是否有passSameTarget设置 */
+		// Route passSameTargetRoute = aeiObjects.getRoutes().stream()
+		// .filter(o ->
+		// BooleanUtils.isTrue(o.getPassSameTarget())).findFirst().orElse(null);
+		// /* 如果有passSameTarget,有到达ArriveWorkLog,不是调度到这个节点的 */
+		// if ((null != passSameTargetRoute) && ((null !=
+		// aeiObjects.getArriveWorkLog(aeiObjects.getWork())))
+		// && (!aeiObjects.getProcessingAttributes().ifForceJoinAtArrive())) {
+		// logger.debug("pass same target work:{}.", aeiObjects.getWork());
+		// WorkLog rollbackWorkLog = findPassSameTargetWorkLog(aeiObjects);
+		// logger.debug("pass same target workLog:{}.", rollbackWorkLog);
+		// // if (null != rollbackWorkLog) {
+		// // final String arriveActivityToken =
+		// rollbackWorkLog.getArrivedActivityToken();
+		// // aeiObjects.getJoinInquireTaskCompleteds().stream()
+		// // .filter(o ->
+		// // aeiObjects.getWork().getManualTaskIdentityList().contains(o.getIdentity())
+		// // && StringUtils.equals(o.getActivityToken(), arriveActivityToken))
+		// // .forEach(o -> {
+		// // TaskCompleted obj = new TaskCompleted(aeiObjects.getWork(),
+		// // passSameTargetRoute, o);
+		// // try {
+		// // obj.setProcessingType(TaskCompleted.PROCESSINGTYPE_SAMETARGET);
+		// // obj.setRouteName(passSameTargetRoute.getName());
+		// // Date now = new Date();
+		// // obj.setStartTime(now);
+		// // obj.setStartTimeMonth(DateTools.format(now, DateTools.format_yyyyMM));
+		// // obj.setCompletedTime(now);
+		// // obj.setCompletedTimeMonth(DateTools.format(now, DateTools.format_yyyyMM));
+		// // obj.setDuration(0L);
+		// // obj.setExpired(false);
+		// // obj.setExpireTime(null);
+		// // obj.setTask(null);
+		// // obj.setLatest(true);
+		// // aeiObjects.getCreateTaskCompleteds().add(obj);
+		// // } catch (Exception e) {
+		// // e.printStackTrace();
+		// // }
+		// // });
+		// // }
+		// if (null != rollbackWorkLog) {
+		// for (TaskCompleted o : aeiObjects.getJoinInquireTaskCompleteds()) {
+		// if (StringUtils.equals(o.getActivityToken(),
+		// rollbackWorkLog.getArrivedActivityToken())) {
+		// List<String> values =
+		// ListUtils.union(aeiObjects.getWork().getManualTaskIdentityList(),
+		// aeiObjects.business().organization().identity().listWithPerson(o.getPerson()));
+		// if (!values.isEmpty()) {
+		// TaskCompleted obj = new TaskCompleted(aeiObjects.getWork(),
+		// passSameTargetRoute, o);
+		// obj.setIdentity(values.get(0));
+		// obj.setProcessingType(TaskCompleted.PROCESSINGTYPE_SAMETARGET);
+		// obj.setRouteName(passSameTargetRoute.getName());
+		// Date now = new Date();
+		// obj.setStartTime(now);
+		// obj.setStartTimeMonth(DateTools.format(now, DateTools.format_yyyyMM));
+		// obj.setCompletedTime(now);
+		// obj.setCompletedTimeMonth(DateTools.format(now, DateTools.format_yyyyMM));
+		// obj.setDuration(0L);
+		// obj.setExpired(false);
+		// obj.setExpireTime(null);
+		// obj.setTask(null);
+		// obj.setLatest(true);
+		// aeiObjects.getCreateTaskCompleteds().add(obj);
+		// }
+		// }
+		// }
+		// }
+		// }
+		return aeiObjects.getWork();
+	}
+
+	private Work arrivingMergeSameJob(AeiObjects aeiObjects, Manual manual, List<String> identities) throws Exception {
+		if (!BooleanUtils.isTrue(manual.getManualMergeSameJobActivity())) {
+			return null;
+		}
+		List<String> exists = this.arriving_sameJobActivityExistIdentities(aeiObjects, manual);
+		if (ListTools.isNotEmpty(exists)) {
+			Work other = aeiObjects.getWorks().stream().filter(o -> {
+				return StringUtils.equals(aeiObjects.getWork().getJob(), o.getJob())
+						&& StringUtils.equals(aeiObjects.getWork().getActivity(), o.getActivity())
+						&& (!Objects.equals(aeiObjects.getWork(), o));
+			}).findFirst().orElse(null);
+			if (null != other) {
+				identities.removeAll(exists);
+				if (ListTools.isEmpty(identities)) {
+					this.mergeTaskCompleted(aeiObjects, aeiObjects.getWork(), other);
+					this.mergeRead(aeiObjects, aeiObjects.getWork(), other);
+					this.mergeReadCompleted(aeiObjects, aeiObjects.getWork(), other);
+					this.mergeReview(aeiObjects, aeiObjects.getWork(), other);
+					this.mergeAttachment(aeiObjects, aeiObjects.getWork(), other);
+					this.mergeWorkLog(aeiObjects, aeiObjects.getWork(), other);
+					if (ListTools.size(aeiObjects.getWork().getSplitTokenList()) > ListTools
+							.size(other.getSplitTokenList())) {
+						other.setSplitTokenList(aeiObjects.getWork().getSplitTokenList());
+						other.setSplitToken(aeiObjects.getWork().getSplitToken());
+						other.setSplitValue(aeiObjects.getWork().getSplitValue());
+						other.setSplitting(true);
 					}
 					}
+					aeiObjects.getUpdateWorks().add(other);
+					aeiObjects.getDeleteWorks().add(aeiObjects.getWork());
+					return other;
 				}
 				}
 			}
 			}
 		}
 		}
+		return null;
+	}
 
 
-		aeiObjects.getWork().setManualTaskIdentityList(new ArrayList<String>(identities));
+	private void arrivingPassSame(AeiObjects aeiObjects, List<String> identities) throws Exception {
 		/* 查找是否有passSameTarget设置 */
 		/* 查找是否有passSameTarget设置 */
-		Route passSameTargetRoute = aeiObjects.getRoutes().stream()
-				.filter(o -> BooleanUtils.isTrue(o.getPassSameTarget())).findFirst().orElse(null);
+		Route route = aeiObjects.getRoutes().stream().filter(o -> BooleanUtils.isTrue(o.getPassSameTarget()))
+				.findFirst().orElse(null);
 		/* 如果有passSameTarget,有到达ArriveWorkLog,不是调度到这个节点的 */
 		/* 如果有passSameTarget,有到达ArriveWorkLog,不是调度到这个节点的 */
-//		if ((null != passSameTargetRoute) && ((null != aeiObjects.getArriveWorkLog(aeiObjects.getWork())))
-//				&& (BooleanUtils.isNotTrue(aeiObjects.getWork().getForceRouteArriveCurrentActivity()))) {
-		if ((null != passSameTargetRoute) && ((null != aeiObjects.getArriveWorkLog(aeiObjects.getWork())))
+		if ((null != route) && ((null != aeiObjects.getArriveWorkLog(aeiObjects.getWork())))
 				&& (!aeiObjects.getProcessingAttributes().ifForceJoinAtArrive())) {
 				&& (!aeiObjects.getProcessingAttributes().ifForceJoinAtArrive())) {
-			logger.debug("pass same target work:{}.", aeiObjects.getWork());
-			WorkLog rollbackWorkLog = findPassSameTargetWorkLog(aeiObjects);
-			logger.debug("pass same target workLog:{}.", rollbackWorkLog);
-			if (null != rollbackWorkLog) {
-				final String arriveActivityToken = rollbackWorkLog.getArrivedActivityToken();
-				aeiObjects.getJoinInquireTaskCompleteds().stream()
-						.filter(o -> aeiObjects.getWork().getManualTaskIdentityList().contains(o.getIdentity())
-								&& StringUtils.equals(o.getActivityToken(), arriveActivityToken))
-						.forEach(o -> {
-							TaskCompleted obj = new TaskCompleted(aeiObjects.getWork(), passSameTargetRoute, o);
-							try {
-								obj.setProcessingType(TaskCompleted.PROCESSINGTYPE_SAMETARGET);
-								obj.setRouteName(passSameTargetRoute.getName());
-								Date now = new Date();
-								obj.setStartTime(now);
-								obj.setStartTimeMonth(DateTools.format(now, DateTools.format_yyyyMM));
-								obj.setCompletedTime(now);
-								obj.setCompletedTimeMonth(DateTools.format(now, DateTools.format_yyyyMM));
-								obj.setDuration(0L);
-								obj.setExpired(false);
-								obj.setExpireTime(null);
-								obj.setTask(null);
-								obj.setLatest(true);
-								aeiObjects.getCreateTaskCompleteds().add(obj);
-							} catch (Exception e) {
-								e.printStackTrace();
-							}
-						});
+			WorkLog workLog = findPassSameTargetWorkLog(aeiObjects);
+			logger.debug("pass same target work:{}, workLog:{}.", aeiObjects.getWork(), workLog);
+			if (null == workLog) {
+				return;
+			}
+			for (TaskCompleted o : aeiObjects.getJoinInquireTaskCompleteds()) {
+				if (StringUtils.equals(o.getActivityToken(), workLog.getArrivedActivityToken())) {
+					List<String> values = ListUtils.union(identities,
+							aeiObjects.business().organization().identity().listWithPerson(o.getPerson()));
+					if (!values.isEmpty()) {
+						TaskCompleted obj = new TaskCompleted(aeiObjects.getWork(), route, o);
+						obj.setIdentity(values.get(0));
+						obj.setProcessingType(TaskCompleted.PROCESSINGTYPE_SAMETARGET);
+						obj.setRouteName(route.getName());
+						Date now = new Date();
+						obj.setStartTime(now);
+						obj.setStartTimeMonth(DateTools.format(now, DateTools.format_yyyyMM));
+						obj.setCompletedTime(now);
+						obj.setCompletedTimeMonth(DateTools.format(now, DateTools.format_yyyyMM));
+						obj.setDuration(0L);
+						obj.setExpired(false);
+						obj.setExpireTime(null);
+						obj.setTask(null);
+						obj.setLatest(true);
+						aeiObjects.getCreateTaskCompleteds().add(obj);
+					}
+				}
 			}
 			}
 		}
 		}
-		return aeiObjects.getWork();
 	}
 	}
 
 
 	/* 计算处理人 */
 	/* 计算处理人 */
@@ -275,20 +388,20 @@ public class ManualProcessor extends AbstractManualProcessor {
 		}
 		}
 
 
 		switch (manual.getManualMode()) {
 		switch (manual.getManualMode()) {
-		case single:
-			passThrough = this.single(aeiObjects, manual, identities);
-			break;
-		case parallel:
-			passThrough = this.parallel(aeiObjects, manual, identities);
-			break;
-		case queue:
-			passThrough = this.queue(aeiObjects, manual, identities);
-			break;
-		case grab:
-			passThrough = this.single(aeiObjects, manual, identities);
-			break;
-		default:
-			throw new ExceptionManualModeError(manual.getId());
+			case single:
+				passThrough = this.single(aeiObjects, manual, identities);
+				break;
+			case parallel:
+				passThrough = this.parallel(aeiObjects, manual, identities);
+				break;
+			case queue:
+				passThrough = this.queue(aeiObjects, manual, identities);
+				break;
+			case grab:
+				passThrough = this.single(aeiObjects, manual, identities);
+				break;
+			default:
+				throw new ExceptionManualModeError(manual.getId());
 		}
 		}
 
 
 		if (passThrough) {
 		if (passThrough) {
@@ -515,17 +628,17 @@ public class ManualProcessor extends AbstractManualProcessor {
 	private void calculateExpire(AeiObjects aeiObjects, Manual manual, Task task) throws Exception {
 	private void calculateExpire(AeiObjects aeiObjects, Manual manual, Task task) throws Exception {
 		if (null != manual.getTaskExpireType()) {
 		if (null != manual.getTaskExpireType()) {
 			switch (manual.getTaskExpireType()) {
 			switch (manual.getTaskExpireType()) {
-			case never:
-				this.expireNever(task);
-				break;
-			case appoint:
-				this.expireAppoint(manual, task);
-				break;
-			case script:
-				this.expireScript(aeiObjects, manual, task);
-				break;
-			default:
-				break;
+				case never:
+					this.expireNever(task);
+					break;
+				case appoint:
+					this.expireAppoint(manual, task);
+					break;
+				case script:
+					this.expireScript(aeiObjects, manual, task);
+					break;
+				default:
+					break;
 			}
 			}
 		}
 		}
 		/* 如果work有截至时间 */
 		/* 如果work有截至时间 */
@@ -597,21 +710,24 @@ public class ManualProcessor extends AbstractManualProcessor {
 		}
 		}
 	}
 	}
 
 
-//	private void expireScript(AeiObjects aeiObjects, Manual manual, Task task) throws Exception {
-//		ScriptContext scriptContext = aeiObjects.scriptContext();
-//		Bindings bindings = scriptContext.getBindings(ScriptContext.ENGINE_SCOPE);
-//		bindings.put(ScriptFactory.BINDING_NAME_TASK, task);
-//		Object objectValue = aeiObjects.business().element()
-//				.getCompiledScript(aeiObjects.getWork().getApplication(), manual, Business.EVENT_MANUALTASKEXPIRE)
-//				.eval(scriptContext);
-//		WorkTime wt = new WorkTime();
-//		if (NumberUtils.isCreatable(objectValue.toString())) {
-//			task.setExpireTime(wt.forwardMinutes(new Date(), NumberUtils.toInt(ScriptFactory.asString(objectValue))));
-//		} else {
-//			task.setExpireTime(null);
-//		}
-//
-//	}
+	// private void expireScript(AeiObjects aeiObjects, Manual manual, Task task)
+	// throws Exception {
+	// ScriptContext scriptContext = aeiObjects.scriptContext();
+	// Bindings bindings = scriptContext.getBindings(ScriptContext.ENGINE_SCOPE);
+	// bindings.put(ScriptFactory.BINDING_NAME_TASK, task);
+	// Object objectValue = aeiObjects.business().element()
+	// .getCompiledScript(aeiObjects.getWork().getApplication(), manual,
+	// Business.EVENT_MANUALTASKEXPIRE)
+	// .eval(scriptContext);
+	// WorkTime wt = new WorkTime();
+	// if (NumberUtils.isCreatable(objectValue.toString())) {
+	// task.setExpireTime(wt.forwardMinutes(new Date(),
+	// NumberUtils.toInt(ScriptFactory.asString(objectValue))));
+	// } else {
+	// task.setExpireTime(null);
+	// }
+	//
+	// }
 
 
 	private void expireScript(AeiObjects aeiObjects, Manual manual, Task task) throws Exception {
 	private void expireScript(AeiObjects aeiObjects, Manual manual, Task task) throws Exception {
 		ExpireScriptResult expire = new ExpireScriptResult();
 		ExpireScriptResult expire = new ExpireScriptResult();