فهرست منبع

Merge branch 'develop' of https://git.o2oa.net/o2oa/o2oa into develop

Zhou Rui 5 سال پیش
والد
کامیت
3342394ee4
27فایلهای تغییر یافته به همراه991 افزوده شده و 481 حذف شده
  1. 1 1
      gulpfile.js
  2. 1 1
      o2server/x_teamwork_assemble_control/src/main/java/com/x/teamwork/assemble/control/jaxrs/task/ActionSave.java
  3. 192 0
      o2server/x_teamwork_assemble_control/src/main/java/com/x/teamwork/assemble/control/jaxrs/task/ActionTransformAsSubTask.java
  4. 21 1
      o2server/x_teamwork_assemble_control/src/main/java/com/x/teamwork/assemble/control/jaxrs/task/TaskAction.java
  5. 20 0
      o2server/x_teamwork_assemble_control/src/main/java/com/x/teamwork/assemble/control/jaxrs/task/TaskTransformException.java
  6. 46 13
      o2server/x_teamwork_assemble_control/src/main/java/com/x/teamwork/assemble/control/service/DynamicPersistService.java
  7. 18 10
      o2server/x_teamwork_assemble_control/src/main/java/com/x/teamwork/assemble/control/service/DynamicService.java
  8. 29 2
      o2server/x_teamwork_assemble_control/src/main/java/com/x/teamwork/assemble/control/service/TaskPersistService.java
  9. 6 3
      o2server/x_teamwork_assemble_control/src/main/java/com/x/teamwork/assemble/control/service/TaskQueryService.java
  10. 40 0
      o2server/x_teamwork_assemble_control/src/main/webapp/describe/describe.json
  11. 1 1
      o2server/x_teamwork_assemble_control/src/main/webapp/describe/sources/com/x/teamwork/assemble/control/jaxrs/task/ActionSave.java
  12. 21 1
      o2server/x_teamwork_assemble_control/src/main/webapp/describe/sources/com/x/teamwork/assemble/control/jaxrs/task/TaskAction.java
  13. 46 13
      o2server/x_teamwork_assemble_control/src/main/webapp/describe/sources/com/x/teamwork/assemble/control/service/DynamicPersistService.java
  14. 18 10
      o2server/x_teamwork_assemble_control/src/main/webapp/describe/sources/com/x/teamwork/assemble/control/service/DynamicService.java
  15. 29 2
      o2server/x_teamwork_assemble_control/src/main/webapp/describe/sources/com/x/teamwork/assemble/control/service/TaskPersistService.java
  16. 6 3
      o2server/x_teamwork_assemble_control/src/main/webapp/describe/sources/com/x/teamwork/assemble/control/service/TaskQueryService.java
  17. 397 382
      o2web/gulpfile.js
  18. 1 0
      o2web/source/o2_core/o2/xDesktop/Authentication.js
  19. 46 25
      o2web/source/o2_core/o2/xDesktop/WebSocket.js
  20. 30 6
      o2web/source/x_component_Forum/TopNode.js
  21. 6 1
      o2web/source/x_component_process_Application/$Main/reroute.html
  22. 8 3
      o2web/source/x_component_process_Application/WorkExplorer.js
  23. 1 1
      o2web/source/x_component_process_TaskCenter/Main.js
  24. 1 0
      o2web/source/x_component_process_Xform/$Input.js
  25. 0 1
      o2web/source/x_component_process_Xform/Form.js
  26. 3 0
      o2web/source/x_desktop/js/forum.js
  27. 3 1
      o2web/source/x_desktop/js/forumDocMobile.js

+ 1 - 1
gulpfile.js

@@ -128,7 +128,7 @@ function downloadFile(path, filename, headcb, progresscb, cb){
                     });
                     request.get(options).pipe(str).pipe(stream);
                 } else {
-                    gutil.log(gutil.colors.red("download error"), ":", gutil.colors.red(filename), "statusCode:"+response.statusCode);
+                    gutil.log(gutil.colors.red("download error"), ":", gutil.colors.red(filename), "statusCode:"+res.statusCode);
                 }
             })
             req.on('error', (e) => {

+ 1 - 1
o2server/x_teamwork_assemble_control/src/main/java/com/x/teamwork/assemble/control/jaxrs/task/ActionSave.java

@@ -713,7 +713,7 @@ public class ActionSave extends BaseAction {
 
 	}
 
-public static class Wo extends WoId {
+	public static class Wo extends WoId {
 		
 		@FieldDescribe("操作引起的动态内容")
 		List<WoDynamic> dynamics = new ArrayList<>();

+ 192 - 0
o2server/x_teamwork_assemble_control/src/main/java/com/x/teamwork/assemble/control/jaxrs/task/ActionTransformAsSubTask.java

@@ -0,0 +1,192 @@
+package com.x.teamwork.assemble.control.jaxrs.task;
+
+import com.google.gson.Gson;
+import com.x.base.core.entity.JpaObject;
+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.cache.ApplicationCache;
+import com.x.base.core.project.gson.XGsonBuilder;
+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.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+import com.x.base.core.project.tools.ListTools;
+import com.x.teamwork.assemble.control.service.BatchOperationPersistService;
+import com.x.teamwork.assemble.control.service.BatchOperationProcessService;
+import com.x.teamwork.core.entity.*;
+import net.sf.ehcache.Element;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 将任务转为子任务
+ */
+public class ActionTransformAsSubTask extends BaseAction {
+
+	private static Logger logger = LoggerFactory.getLogger(ActionTransformAsSubTask.class);
+
+	/**
+	 * 将任务转换为子任务的服务
+	 * 1、确认ID是否合法
+	 * 2、将任务的parentId设置为parentId
+	 * 3、保存任务信息
+	 * 4、记录动态信息
+	 *
+	 * @param request
+	 * @param effectivePerson
+	 * @param sourceTaskId
+	 * @param parentId
+	 * @return
+	 * @throws Exception
+	 */
+	protected ActionResult<Wo> execute(HttpServletRequest request, EffectivePerson effectivePerson, String sourceTaskId, String parentId ) throws Exception {
+		ActionResult<Wo> result = new ActionResult<>();
+		Wo wo = new Wo();
+		Task sourceTask = null;
+		Task parentTask = null;
+		TaskDetail taskDetail = null;
+		TaskExtField taskExtField = null;
+		List<ProjectExtFieldRele> extFieldReleList = null;
+		List<TaskTag> tags = null;
+		List<Dynamic> dynamics  = new ArrayList<>();
+		Boolean check = true;
+
+		if ( StringUtils.isEmpty( sourceTaskId ) ) {
+			check = false;
+			Exception exception = new TaskTransformException("需要转换的工作任务ID不允许为空!");
+			result.error( exception );
+		}
+
+		if ( StringUtils.isEmpty( parentId ) ) {
+			check = false;
+			Exception exception = new TaskTransformException("上级任务ID不允许为空!");
+			result.error( exception );
+		}
+
+		if (check) {
+			//查询需要转换为子任务的任务是否存在
+			try {
+				sourceTask = taskQueryService.get( sourceTaskId );
+				if ( sourceTask == null) {
+					check = false;
+					Exception exception = new TaskNotExistsException(sourceTaskId);
+					result.error( exception );
+				}
+			} catch (Exception e) {
+				check = false;
+				Exception exception = new TaskQueryException(e, "根据指定ID查询工作任务信息对象时发生异常。sourceTaskId:" + sourceTaskId );
+				result.error(exception);
+				logger.error(e, effectivePerson, request, null);
+			}
+		}
+
+		if (check) {
+			//查询上级任务是否存在
+			try {
+				parentTask = taskQueryService.get( parentId );
+				if ( parentTask == null) {
+					check = false;
+					Exception exception = new TaskNotExistsException( parentId );
+					result.error( exception );
+				}
+			} catch (Exception e) {
+				check = false;
+				Exception exception = new TaskQueryException(e, "根据指定ID查询工作任务信息对象时发生异常。parentId:" + parentId );
+				result.error(exception);
+				logger.error(e, effectivePerson, request, null);
+			}
+		}
+
+		try {
+			taskPersistService.updateParentId( sourceTask.getId(), parentTask.getId(), effectivePerson );
+		} catch (Exception e) {
+			check = false;
+			Exception exception = new TaskPersistException(e, "工作上级任务ID信息更新时发生异常。");
+			result.error(exception);
+			logger.error(e, effectivePerson, request, null);
+		}
+
+
+		if (check) {
+			//记录工作任务信息变化记录
+			try {
+				dynamics = dynamicPersistService.subTaskTransformDynamic( sourceTask, parentTask, effectivePerson );
+			} catch (Exception e) {
+				logger.error(e, effectivePerson, request, null);
+			}
+		}
+
+		if (check) {
+			try {
+				new BatchOperationPersistService().addOperation(
+						BatchOperationProcessService.OPT_OBJ_TASK,
+						BatchOperationProcessService.OPT_TYPE_PERMISSION,  sourceTask.getId(),  sourceTask.getId(), "刷新文档权限:ID=" +   sourceTask.getId() );
+			} catch (Exception e) {
+				logger.error(e, effectivePerson, request, null);
+			}
+		}
+
+		if (check) {
+			try {
+				new BatchOperationPersistService().addOperation(
+						BatchOperationProcessService.OPT_OBJ_TASK,
+						BatchOperationProcessService.OPT_TYPE_PERMISSION,  parentTask.getId(),  parentTask.getId(), "刷新文档权限:ID=" +   parentTask.getId() );
+			} catch (Exception e) {
+				logger.error(e, effectivePerson, request, null);
+			}
+		}
+
+		if( ListTools.isNotEmpty( dynamics ) ) {
+			wo.setDynamics( WoDynamic.copier.copy( dynamics ) );
+		}
+
+		// 更新缓存
+		ApplicationCache.notify( Task.class );
+		ApplicationCache.notify( TaskList.class );
+		ApplicationCache.notify( TaskView.class );
+		ApplicationCache.notify( Review.class );
+		ApplicationCache.notify( TaskGroup.class );
+		ApplicationCache.notify( Dynamic.class );
+
+		result.setData( wo );
+		return result;
+	}
+
+
+	public static class Wo extends WoId {
+
+		@FieldDescribe("操作引起的动态内容")
+		List<ActionSave.WoDynamic> dynamics = new ArrayList<>();
+
+		public List<ActionSave.WoDynamic> getDynamics() {
+			return dynamics;
+		}
+
+		public void setDynamics(List<ActionSave.WoDynamic> dynamics) {
+			this.dynamics = dynamics;
+		}
+
+	}
+
+	public static class WoDynamic extends Dynamic{
+
+		private static final long serialVersionUID = -5076990764713538973L;
+
+		public static WrapCopier<Dynamic, ActionSave.WoDynamic> copier = WrapCopierFactory.wo( Dynamic.class, ActionSave.WoDynamic.class, null, JpaObject.FieldsInvisible);
+
+		private Long rank = 0L;
+
+		public Long getRank() {
+			return rank;
+		}
+
+		public void setRank(Long rank) {
+			this.rank = rank;
+		}
+	}
+}

+ 21 - 1
o2server/x_teamwork_assemble_control/src/main/java/com/x/teamwork/assemble/control/jaxrs/task/TaskAction.java

@@ -90,7 +90,27 @@ public class TaskAction extends StandardJaxrsAction {
 		}
 		asyncResponse.resume(ResponseFactory.getEntityTagActionResultResponse(request, result));
 	}
-	
+
+	@JaxrsMethodDescribe(value = "将指定的工作转换为子工作.", action = ActionTransformAsSubTask.class)
+	@GET
+	@Path("transform/{tid}/parent/{pid}")
+	@Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public void transformAsSubTask(@Suspended final AsyncResponse asyncResponse,
+								  @Context HttpServletRequest request,
+								  @JaxrsParameterDescribe("指定任务ID") @PathParam("tid") String tid,
+								  @JaxrsParameterDescribe("上级任务ID") @PathParam("pid") String pid) {
+		ActionResult<ActionTransformAsSubTask.Wo> result = new ActionResult<>();
+		EffectivePerson effectivePerson = this.effectivePerson(request);
+		try {
+			result = new ActionTransformAsSubTask().execute( request, effectivePerson, tid, pid );
+		} catch (Exception e) {
+			logger.error(e, effectivePerson, request, null);
+			result.error(e);
+		}
+		asyncResponse.resume(ResponseFactory.getEntityTagActionResultResponse(request, result));
+	}
+
 	@JaxrsMethodDescribe(value = "查询我的项目首页中工作任务视图信息.", action = ActionStatisticMyTaskViews.class)
 	@GET
 	@Path("statitic/group/{projectId}")

+ 20 - 0
o2server/x_teamwork_assemble_control/src/main/java/com/x/teamwork/assemble/control/jaxrs/task/TaskTransformException.java

@@ -0,0 +1,20 @@
+package com.x.teamwork.assemble.control.jaxrs.task;
+
+import com.x.base.core.project.exception.PromptException;
+
+class TaskTransformException extends PromptException {
+
+	private static final long serialVersionUID = 1859164370743532895L;
+
+	TaskTransformException(Throwable e ) {
+		super("系统在转换工作任务为子任务时发生异常。" , e );
+	}
+
+	TaskTransformException(Throwable e, String message ) {
+		super("系统在转换工作任务为子任务时发生异常。Message:" + message, e );
+	}
+
+	TaskTransformException(String message ) {
+		super("系统在转换工作任务为子任务时发生异常。Message:" + message );
+	}
+}

+ 46 - 13
o2server/x_teamwork_assemble_control/src/main/java/com/x/teamwork/assemble/control/service/DynamicPersistService.java

@@ -82,7 +82,6 @@ public class DynamicPersistService {
 	
 	/**
 	 * 保存项目创建或者更新动态信息
-	 * @param old_object
 	 * @param object
 	 * @param effectivePerson
 	 * @param content
@@ -132,9 +131,7 @@ public class DynamicPersistService {
 	 * 保存项目扩展信息保存操作动态信息
 	 * @param object_old
 	 * @param object
-	 * @param optType
 	 * @param effectivePerson
-	 * @param content
 	 * @return
 	 * @throws Exception
 	 */
@@ -179,8 +176,8 @@ public class DynamicPersistService {
 	
 	/**
 	 * 保存动态信息
+	 * @param object_old
 	 * @param object
-	 * @param optType
 	 * @param effectivePerson
 	 * @param content
 	 * @return
@@ -230,7 +227,6 @@ public class DynamicPersistService {
 	 * @param object_old
 	 * @param object_new
 	 * @param effectivePerson
-	 * @param content
 	 * @return
 	 * @throws Exception
 	 */
@@ -257,11 +253,53 @@ public class DynamicPersistService {
 		}
 		return result;
 	}
-	
+
 	/**
-	 * 保存动态信息
-	 * @param dynamic
+	 * 保存转换子工作的动态信息
+	 * @param subTask
+	 * @param parentTask
+	 * @param effectivePerson
+	 * @return
+	 * @throws Exception
+	 */
+	public List<Dynamic> subTaskTransformDynamic( Task subTask, Task parentTask, EffectivePerson effectivePerson ) throws Exception {
+		if ( subTask == null) {
+			throw new Exception("sourceTask is null.");
+		}
+		if ( parentTask == null) {
+			throw new Exception("parentTask is null.");
+		}
+		if ( effectivePerson == null ) {
+			throw new Exception("effectivePerson is null.");
+		}
+		List<Dynamic> result = new ArrayList<>();
+		List<Dynamic> dynamics = null;
+
+		//记录一个添加子任务转换的动态信息
+		result.add(dynamicService.getTaskTransformDynamic( parentTask, subTask, effectivePerson));
+
+		//记录一个为上级任务添加子任务的动态信息
+		result.add(dynamicService.getTaskSplitDynamic( parentTask, subTask, effectivePerson));
+
+		try ( EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+			if( ListTools.isNotEmpty( result )) {
+				for( Dynamic dynamic : result ) {
+					//持久化工作操作动态
+					dynamicService.save( emc, dynamic, "" );
+				}
+			}
+		} catch (Exception e) {
+			throw e;
+		}
+		return result;
+	}
+
+	/**
+	 * 保存项目工作组信息动态
+	 * @param object_old
+	 * @param object
 	 * @param effectivePerson
+	 * @param content
 	 * @return
 	 * @throws Exception
 	 */
@@ -287,7 +325,6 @@ public class DynamicPersistService {
 	 * 保存项目组删除动态信息
 	 * @param object
 	 * @param effectivePerson
-	 * @param content
 	 * @return
 	 * @throws Exception
 	 */
@@ -311,7 +348,6 @@ public class DynamicPersistService {
 	 * 保存工作任务删除动态信息
 	 * @param object
 	 * @param effectivePerson
-	 * @param content
 	 * @return
 	 * @throws Exception
 	 */
@@ -338,7 +374,6 @@ public class DynamicPersistService {
 	 * @param addManagers
 	 * @param removeManagers
 	 * @param effectivePerson
-	 * @param content
 	 * @throws Exception
 	 */
 	public List<Dynamic> taskManagerUpdateDynamic(Task task, List<String> addManagers, List<String> removeManagers, EffectivePerson effectivePerson ) throws Exception {
@@ -421,7 +456,6 @@ public class DynamicPersistService {
 	 * @param addParticipants
 	 * @param removeParticipants
 	 * @param effectivePerson
-	 * @param content
 	 * @throws Exception
 	 */
 	public List<Dynamic> taskParticipantsUpdateDynamic(Task task, List<String> addParticipants, List<String> removeParticipants, EffectivePerson effectivePerson ) throws Exception {
@@ -570,7 +604,6 @@ public class DynamicPersistService {
 	 * 保存工作任务评论删除动态信息
 	 * @param object
 	 * @param effectivePerson
-	 * @param content
 	 * @return
 	 * @throws Exception
 	 */

+ 18 - 10
o2server/x_teamwork_assemble_control/src/main/java/com/x/teamwork/assemble/control/service/DynamicService.java

@@ -103,8 +103,6 @@ class DynamicService {
 	 * @param maxCount
 	 * @param orderField
 	 * @param orderType
-	 * @param projectIds
-	 * @param taskIds
 	 * @return
 	 * @throws Exception
 	 */
@@ -116,7 +114,7 @@ class DynamicService {
 	/**
 	 * 向数据库持久化动态信息
 	 * @param emc
-	 * @param dynamic
+	 * @param object
 	 * @return
 	 * @throws Exception 
 	 */
@@ -480,7 +478,6 @@ class DynamicService {
 	 * 保存和根据项目组信息操作动态
 	 * @param object_old
 	 * @param object
-	 * @param optType
 	 * @param effectivePerson
 	 * @return
 	 */
@@ -734,7 +731,7 @@ class DynamicService {
 	
 	/**
 	 * 更新工作任务管理者信息操作动态
-	 * @param task
+	 * @param object
 	 * @param addManagers
 	 * @param removeManagers
 	 * @param effectivePerson
@@ -768,7 +765,7 @@ class DynamicService {
 	
 	/**
 	 * 更新工作任务参与者操作动态
-	 * @param task
+	 * @param object
 	 * @param addParticipants
 	 * @param removeParticipants
 	 * @param effectivePerson
@@ -805,11 +802,22 @@ class DynamicService {
 		String title =  "工作任务分解";
 		String viewUrl = task.getId();
 		String optType =  "SPLIT";
-		String description = effectivePerson.getName() +"为工作添加了一个子任务:" + task.getName();
+		String description = effectivePerson.getName() +"为工作添加了一个子任务:[" + task.getName() + "]";
 		Dynamic dynamic =  composeNewDynamic( objectType, title, description, viewUrl, optType, parentTask, effectivePerson, false );
 		dynamic.setTarget( parentTask.getExecutor() );		
 		return dynamic;
 	}
+
+	public Dynamic getTaskTransformDynamic(Task parentTask, Task task, EffectivePerson effectivePerson) {
+		String objectType =  "TASK";
+		String title =  "转换为子工作";
+		String viewUrl = task.getId();
+		String optType =  "TRANSFORM";
+		String description = effectivePerson.getName() +"将工作转换为工作[" +parentTask.getName() + "]的一个子任务。";
+		Dynamic dynamic =  composeNewDynamic( objectType, title, description, viewUrl, optType, task, effectivePerson, false );
+		dynamic.setTarget( task.getExecutor() );
+		return dynamic;
+	}
 	
 	public Dynamic subTaskDeleteDynamic(Task parentTask, Task task, EffectivePerson effectivePerson) {
 		String objectType =  "TASK";
@@ -888,7 +896,7 @@ class DynamicService {
 	
 	/**
 	 * 工作任务附件上传操作动态信息
-	 * @param attachment
+	 * @param object
 	 * @param effectivePerson
 	 * @return
 	 */
@@ -903,7 +911,7 @@ class DynamicService {
 	
 	/**
 	 * 工作任务附件下载操作动态信息
-	 * @param attachment
+	 * @param object
 	 * @param effectivePerson
 	 * @return
 	 */
@@ -918,7 +926,7 @@ class DynamicService {
 
 	/**
 	 * 工作任务附件删除操作动态信息
-	 * @param attachment
+	 * @param object
 	 * @param effectivePerson
 	 * @return
 	 */

+ 29 - 2
o2server/x_teamwork_assemble_control/src/main/java/com/x/teamwork/assemble/control/service/TaskPersistService.java

@@ -190,7 +190,34 @@ public class TaskPersistService {
 			throw e;
 		}
 	}
-	
+
+
+	/**
+	 * 更新任务的上级任务ID信息
+	 * @param taskId
+	 * @param parentId
+	 * @param effectivePerson
+	 * @throws Exception
+	 */
+	public void updateParentId( String taskId, String parentId, EffectivePerson effectivePerson) throws Exception {
+
+		if( StringUtils.isEmpty( taskId )) {
+			throw new Exception("taskId can not empty in update parentId.");
+		}
+		try ( EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+			Task task = emc.find( taskId, Task.class );
+			if( task != null ){
+				task.setParent( parentId );
+			}
+			emc.beginTransaction( Task.class );
+			emc.check( task, CheckPersistType.all );
+			emc.commit();
+		} catch (Exception e) {
+			throw e;
+		}
+
+	}
+
 	/**
 	 * 查询用户是否拥有创建工作任务的权限
 	 * @param effectivePerson
@@ -588,5 +615,5 @@ public class TaskPersistService {
 		}
 	}
 
-	
+
 }

+ 6 - 3
o2server/x_teamwork_assemble_control/src/main/java/com/x/teamwork/assemble/control/service/TaskQueryService.java

@@ -217,7 +217,9 @@ public class TaskQueryService {
 					for( TaskListRele rele : taskListReles ) {
 						task = emc.find( rele.getTaskId(), Task.class );
 						//只查询自己负责的任务
-						if( personName.equalsIgnoreCase( task.getExecutor() )) {
+						if( personName.equalsIgnoreCase( task.getExecutor() ) ||
+								( task.getParticipantList() !=null && task.getParticipantList().contains( personName ))
+						) {
 							task.setOrder( rele.getOrder() );
 							resultList.add( task );
 						}
@@ -252,7 +254,9 @@ public class TaskQueryService {
 				if( ListTools.isNotEmpty( taskListTmp )) {
 					for( Task _task : taskListTmp ) {
 						//只查询自己负责的任务
-						if( personName.equalsIgnoreCase( _task.getExecutor() )) {
+						if( personName.equalsIgnoreCase( _task.getExecutor() )||
+								( _task.getParticipantList() !=null && _task.getParticipantList().contains( personName )))
+						{
 							resultList.add( _task );
 						}
 					}
@@ -266,7 +270,6 @@ public class TaskQueryService {
 	
 	/**
 	 * 在人员的可见范围之类,根据指定的工作任务ID,查询子任务列表
-	 * @param project
 	 * @param taskId
 	 * @param effectivePerson
 	 * @return

+ 40 - 0
o2server/x_teamwork_assemble_control/src/main/webapp/describe/describe.json

@@ -7790,6 +7790,46 @@
             }
           ]
         },
+        {
+          "name": "transformAsSubTask",
+          "className": "com.x.teamwork.assemble.control.jaxrs.task.ActionTransformAsSubTask",
+          "description": "将指定的工作转换为子工作.",
+          "type": "GET",
+          "path": "jaxrs/task/transform/{tid}/parent/{pid}",
+          "contentType": "application/json",
+          "resultContentType": "application/json; charset\u003dUTF-8",
+          "useJsonElementParameter": false,
+          "useStringParameter": false,
+          "pathParameters": [
+            {
+              "name": "tid",
+              "type": "String",
+              "description": "指定任务ID"
+            },
+            {
+              "name": "pid",
+              "type": "String",
+              "description": "上级任务ID"
+            }
+          ],
+          "formParameters": [],
+          "queryParameters": [],
+          "ins": [],
+          "outs": [
+            {
+              "name": "dynamics",
+              "type": "List\u003cWoDynamic\u003e",
+              "isCollection": true,
+              "description": "操作引起的动态内容"
+            },
+            {
+              "name": "id",
+              "type": "String",
+              "isCollection": false,
+              "description": "id"
+            }
+          ]
+        },
         {
           "name": "updateManager",
           "className": "com.x.teamwork.assemble.control.jaxrs.task.ActionManagerUpdate",

+ 1 - 1
o2server/x_teamwork_assemble_control/src/main/webapp/describe/sources/com/x/teamwork/assemble/control/jaxrs/task/ActionSave.java

@@ -713,7 +713,7 @@ public class ActionSave extends BaseAction {
 
 	}
 
-public static class Wo extends WoId {
+	public static class Wo extends WoId {
 		
 		@FieldDescribe("操作引起的动态内容")
 		List<WoDynamic> dynamics = new ArrayList<>();

+ 21 - 1
o2server/x_teamwork_assemble_control/src/main/webapp/describe/sources/com/x/teamwork/assemble/control/jaxrs/task/TaskAction.java

@@ -90,7 +90,27 @@ public class TaskAction extends StandardJaxrsAction {
 		}
 		asyncResponse.resume(ResponseFactory.getEntityTagActionResultResponse(request, result));
 	}
-	
+
+	@JaxrsMethodDescribe(value = "将指定的工作转换为子工作.", action = ActionTransformAsSubTask.class)
+	@GET
+	@Path("transform/{tid}/parent/{pid}")
+	@Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public void transformAsSubTask(@Suspended final AsyncResponse asyncResponse,
+								  @Context HttpServletRequest request,
+								  @JaxrsParameterDescribe("指定任务ID") @PathParam("tid") String tid,
+								  @JaxrsParameterDescribe("上级任务ID") @PathParam("pid") String pid) {
+		ActionResult<ActionTransformAsSubTask.Wo> result = new ActionResult<>();
+		EffectivePerson effectivePerson = this.effectivePerson(request);
+		try {
+			result = new ActionTransformAsSubTask().execute( request, effectivePerson, tid, pid );
+		} catch (Exception e) {
+			logger.error(e, effectivePerson, request, null);
+			result.error(e);
+		}
+		asyncResponse.resume(ResponseFactory.getEntityTagActionResultResponse(request, result));
+	}
+
 	@JaxrsMethodDescribe(value = "查询我的项目首页中工作任务视图信息.", action = ActionStatisticMyTaskViews.class)
 	@GET
 	@Path("statitic/group/{projectId}")

+ 46 - 13
o2server/x_teamwork_assemble_control/src/main/webapp/describe/sources/com/x/teamwork/assemble/control/service/DynamicPersistService.java

@@ -82,7 +82,6 @@ public class DynamicPersistService {
 	
 	/**
 	 * 保存项目创建或者更新动态信息
-	 * @param old_object
 	 * @param object
 	 * @param effectivePerson
 	 * @param content
@@ -132,9 +131,7 @@ public class DynamicPersistService {
 	 * 保存项目扩展信息保存操作动态信息
 	 * @param object_old
 	 * @param object
-	 * @param optType
 	 * @param effectivePerson
-	 * @param content
 	 * @return
 	 * @throws Exception
 	 */
@@ -179,8 +176,8 @@ public class DynamicPersistService {
 	
 	/**
 	 * 保存动态信息
+	 * @param object_old
 	 * @param object
-	 * @param optType
 	 * @param effectivePerson
 	 * @param content
 	 * @return
@@ -230,7 +227,6 @@ public class DynamicPersistService {
 	 * @param object_old
 	 * @param object_new
 	 * @param effectivePerson
-	 * @param content
 	 * @return
 	 * @throws Exception
 	 */
@@ -257,11 +253,53 @@ public class DynamicPersistService {
 		}
 		return result;
 	}
-	
+
 	/**
-	 * 保存动态信息
-	 * @param dynamic
+	 * 保存转换子工作的动态信息
+	 * @param subTask
+	 * @param parentTask
+	 * @param effectivePerson
+	 * @return
+	 * @throws Exception
+	 */
+	public List<Dynamic> subTaskTransformDynamic( Task subTask, Task parentTask, EffectivePerson effectivePerson ) throws Exception {
+		if ( subTask == null) {
+			throw new Exception("sourceTask is null.");
+		}
+		if ( parentTask == null) {
+			throw new Exception("parentTask is null.");
+		}
+		if ( effectivePerson == null ) {
+			throw new Exception("effectivePerson is null.");
+		}
+		List<Dynamic> result = new ArrayList<>();
+		List<Dynamic> dynamics = null;
+
+		//记录一个添加子任务转换的动态信息
+		result.add(dynamicService.getTaskTransformDynamic( parentTask, subTask, effectivePerson));
+
+		//记录一个为上级任务添加子任务的动态信息
+		result.add(dynamicService.getTaskSplitDynamic( parentTask, subTask, effectivePerson));
+
+		try ( EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+			if( ListTools.isNotEmpty( result )) {
+				for( Dynamic dynamic : result ) {
+					//持久化工作操作动态
+					dynamicService.save( emc, dynamic, "" );
+				}
+			}
+		} catch (Exception e) {
+			throw e;
+		}
+		return result;
+	}
+
+	/**
+	 * 保存项目工作组信息动态
+	 * @param object_old
+	 * @param object
 	 * @param effectivePerson
+	 * @param content
 	 * @return
 	 * @throws Exception
 	 */
@@ -287,7 +325,6 @@ public class DynamicPersistService {
 	 * 保存项目组删除动态信息
 	 * @param object
 	 * @param effectivePerson
-	 * @param content
 	 * @return
 	 * @throws Exception
 	 */
@@ -311,7 +348,6 @@ public class DynamicPersistService {
 	 * 保存工作任务删除动态信息
 	 * @param object
 	 * @param effectivePerson
-	 * @param content
 	 * @return
 	 * @throws Exception
 	 */
@@ -338,7 +374,6 @@ public class DynamicPersistService {
 	 * @param addManagers
 	 * @param removeManagers
 	 * @param effectivePerson
-	 * @param content
 	 * @throws Exception
 	 */
 	public List<Dynamic> taskManagerUpdateDynamic(Task task, List<String> addManagers, List<String> removeManagers, EffectivePerson effectivePerson ) throws Exception {
@@ -421,7 +456,6 @@ public class DynamicPersistService {
 	 * @param addParticipants
 	 * @param removeParticipants
 	 * @param effectivePerson
-	 * @param content
 	 * @throws Exception
 	 */
 	public List<Dynamic> taskParticipantsUpdateDynamic(Task task, List<String> addParticipants, List<String> removeParticipants, EffectivePerson effectivePerson ) throws Exception {
@@ -570,7 +604,6 @@ public class DynamicPersistService {
 	 * 保存工作任务评论删除动态信息
 	 * @param object
 	 * @param effectivePerson
-	 * @param content
 	 * @return
 	 * @throws Exception
 	 */

+ 18 - 10
o2server/x_teamwork_assemble_control/src/main/webapp/describe/sources/com/x/teamwork/assemble/control/service/DynamicService.java

@@ -103,8 +103,6 @@ class DynamicService {
 	 * @param maxCount
 	 * @param orderField
 	 * @param orderType
-	 * @param projectIds
-	 * @param taskIds
 	 * @return
 	 * @throws Exception
 	 */
@@ -116,7 +114,7 @@ class DynamicService {
 	/**
 	 * 向数据库持久化动态信息
 	 * @param emc
-	 * @param dynamic
+	 * @param object
 	 * @return
 	 * @throws Exception 
 	 */
@@ -480,7 +478,6 @@ class DynamicService {
 	 * 保存和根据项目组信息操作动态
 	 * @param object_old
 	 * @param object
-	 * @param optType
 	 * @param effectivePerson
 	 * @return
 	 */
@@ -734,7 +731,7 @@ class DynamicService {
 	
 	/**
 	 * 更新工作任务管理者信息操作动态
-	 * @param task
+	 * @param object
 	 * @param addManagers
 	 * @param removeManagers
 	 * @param effectivePerson
@@ -768,7 +765,7 @@ class DynamicService {
 	
 	/**
 	 * 更新工作任务参与者操作动态
-	 * @param task
+	 * @param object
 	 * @param addParticipants
 	 * @param removeParticipants
 	 * @param effectivePerson
@@ -805,11 +802,22 @@ class DynamicService {
 		String title =  "工作任务分解";
 		String viewUrl = task.getId();
 		String optType =  "SPLIT";
-		String description = effectivePerson.getName() +"为工作添加了一个子任务:" + task.getName();
+		String description = effectivePerson.getName() +"为工作添加了一个子任务:[" + task.getName() + "]";
 		Dynamic dynamic =  composeNewDynamic( objectType, title, description, viewUrl, optType, parentTask, effectivePerson, false );
 		dynamic.setTarget( parentTask.getExecutor() );		
 		return dynamic;
 	}
+
+	public Dynamic getTaskTransformDynamic(Task parentTask, Task task, EffectivePerson effectivePerson) {
+		String objectType =  "TASK";
+		String title =  "转换为子工作";
+		String viewUrl = task.getId();
+		String optType =  "TRANSFORM";
+		String description = effectivePerson.getName() +"将工作转换为工作[" +parentTask.getName() + "]的一个子任务。";
+		Dynamic dynamic =  composeNewDynamic( objectType, title, description, viewUrl, optType, task, effectivePerson, false );
+		dynamic.setTarget( task.getExecutor() );
+		return dynamic;
+	}
 	
 	public Dynamic subTaskDeleteDynamic(Task parentTask, Task task, EffectivePerson effectivePerson) {
 		String objectType =  "TASK";
@@ -888,7 +896,7 @@ class DynamicService {
 	
 	/**
 	 * 工作任务附件上传操作动态信息
-	 * @param attachment
+	 * @param object
 	 * @param effectivePerson
 	 * @return
 	 */
@@ -903,7 +911,7 @@ class DynamicService {
 	
 	/**
 	 * 工作任务附件下载操作动态信息
-	 * @param attachment
+	 * @param object
 	 * @param effectivePerson
 	 * @return
 	 */
@@ -918,7 +926,7 @@ class DynamicService {
 
 	/**
 	 * 工作任务附件删除操作动态信息
-	 * @param attachment
+	 * @param object
 	 * @param effectivePerson
 	 * @return
 	 */

+ 29 - 2
o2server/x_teamwork_assemble_control/src/main/webapp/describe/sources/com/x/teamwork/assemble/control/service/TaskPersistService.java

@@ -190,7 +190,34 @@ public class TaskPersistService {
 			throw e;
 		}
 	}
-	
+
+
+	/**
+	 * 更新任务的上级任务ID信息
+	 * @param taskId
+	 * @param parentId
+	 * @param effectivePerson
+	 * @throws Exception
+	 */
+	public void updateParentId( String taskId, String parentId, EffectivePerson effectivePerson) throws Exception {
+
+		if( StringUtils.isEmpty( taskId )) {
+			throw new Exception("taskId can not empty in update parentId.");
+		}
+		try ( EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+			Task task = emc.find( taskId, Task.class );
+			if( task != null ){
+				task.setParent( parentId );
+			}
+			emc.beginTransaction( Task.class );
+			emc.check( task, CheckPersistType.all );
+			emc.commit();
+		} catch (Exception e) {
+			throw e;
+		}
+
+	}
+
 	/**
 	 * 查询用户是否拥有创建工作任务的权限
 	 * @param effectivePerson
@@ -588,5 +615,5 @@ public class TaskPersistService {
 		}
 	}
 
-	
+
 }

+ 6 - 3
o2server/x_teamwork_assemble_control/src/main/webapp/describe/sources/com/x/teamwork/assemble/control/service/TaskQueryService.java

@@ -217,7 +217,9 @@ public class TaskQueryService {
 					for( TaskListRele rele : taskListReles ) {
 						task = emc.find( rele.getTaskId(), Task.class );
 						//只查询自己负责的任务
-						if( personName.equalsIgnoreCase( task.getExecutor() )) {
+						if( personName.equalsIgnoreCase( task.getExecutor() ) ||
+								( task.getParticipantList() !=null && task.getParticipantList().contains( personName ))
+						) {
 							task.setOrder( rele.getOrder() );
 							resultList.add( task );
 						}
@@ -252,7 +254,9 @@ public class TaskQueryService {
 				if( ListTools.isNotEmpty( taskListTmp )) {
 					for( Task _task : taskListTmp ) {
 						//只查询自己负责的任务
-						if( personName.equalsIgnoreCase( _task.getExecutor() )) {
+						if( personName.equalsIgnoreCase( _task.getExecutor() )||
+								( _task.getParticipantList() !=null && _task.getParticipantList().contains( personName )))
+						{
 							resultList.add( _task );
 						}
 					}
@@ -266,7 +270,6 @@ public class TaskQueryService {
 	
 	/**
 	 * 在人员的可见范围之类,根据指定的工作任务ID,查询子任务列表
-	 * @param project
 	 * @param taskId
 	 * @param effectivePerson
 	 * @return

+ 397 - 382
o2web/gulpfile.js

@@ -1,382 +1,397 @@
-var gulp = require('gulp'),
-    //var deleted = require('gulp-deleted');
-    del = require('del'),
-    uglify = require('gulp-tm-uglify'),
-    rename = require('gulp-rename'),
-    changed = require('gulp-changed'),
-    gulpif = require('gulp-if'),
-    minimist = require('minimist'),
-    ftp = require('gulp-ftp'),
-    sftp = require('gulp-sftp'),
-    JSFtp = require('jsftp'),
-    gutil = require('gulp-util'),
-    fs = require("fs");
-var through2 = require('through2');
-
-var assetRev = require('gulp-tm-asset-rev');
-var apps = require('./gulpapps.js');
-var ftpconfig = require('./gulpconfig.js');
-
-var o_options = minimist(process.argv.slice(2), {//upload: local ftp or sftp
-    string: ["ev", "upload", "location", "host", "user", "pass", "port", "remotePath", "dest", "src"]
-});
-var options = {};
-
-var uploadOptions = ftpconfig.dev;
-if (o_options.ev && o_options.ev=="dev"){
-    options.ev = "dev";
-    uploadOptions = ftpconfig.dev;
-}else if (o_options.ev && o_options.ev=="release"){
-    options.ev = "release";
-    uploadOptions = ftpconfig.release;
-}else if (o_options.ev && o_options.ev=="wrdp"){
-    options.ev = "wrdp";
-    uploadOptions = ftpconfig.wrdp;
-}else{
-    options.ev = "dev";
-    uploadOptions = ftpconfig.dev;
-}
-
-options.upload = o_options.upload || "";
-options.location = o_options.location || uploadOptions.location;
-options.host = o_options.host || uploadOptions.host;
-options.user = o_options.user || uploadOptions.user;
-options.pass = o_options.pass || uploadOptions.pass;
-options.port = o_options.port || uploadOptions.port;
-options.remotePath = o_options.remotePath || uploadOptions.remotePath;
-options.dest = o_options.dest || uploadOptions.dest || "dest";
-
-var release_options = {};
-release_options.ev = "release";
-release_options.upload = o_options.upload || "";
-release_options.location = o_options.location || ftpconfig.release.location;
-release_options.host = o_options.host || ftpconfig.release.host;
-release_options.user = o_options.user || ftpconfig.release.user;
-release_options.pass = o_options.pass || ftpconfig.release.pass;
-release_options.port = o_options.port || ftpconfig.release.port;
-release_options.remotePath = o_options.remotePath || ftpconfig.release.remotePath;
-release_options.dest = o_options.dest || ftpconfig.release.dest || "dest";
-
-
-var appTasks = [];
-function getAppTask(path, isMin, thisOptions) {
-    return function (cb) {
-        //var srcFile = 'source/' + path + '/**/*';
-        var option = thisOptions || options;
-        var src;
-        var dest = option.dest+'/' + path + '/';
-        if (isMin){
-            var src_min = ['source/' + path + '/**/*.js', '!**/*.spec.js', '!**/test/**'];
-            var src_move = ['source/' + path + '/**/*', '!**/*.spec.js', '!**/test/**'];
-
-            gutil.log("Move-Uglify", ":", gutil.colors.green(gutil.colors.blue(path), gutil.colors.white('->'), dest));
-
-            return gulp.src(src_min)
-                .pipe(changed(dest))
-                .pipe(uglify())
-                .pipe(rename({ extname: '.min.js' }))
-                .pipe(gulpif((option.upload == 'local' && option.location != ''), gulp.dest(option.location + path + '/')))
-                .pipe(gulpif((option.upload == 'ftp' && option.host != ''), ftp({
-                    host: option.host,
-                    user: option.user || 'anonymous',
-                    pass: option.pass || '@anonymous',
-                    port: option.port || 21,
-                    remotePath: (option.remotePath || '/') + path
-                })))
-                .pipe(gulpif((option.upload == 'sftp' && option.host != ''), sftp({
-                    host: option.host,
-                    user: option.user || 'anonymous',
-                    pass: option.pass || null,
-                    port: option.port || 22,
-                    remotePath: (option.remotePath || '/') + path
-                })))
-                .pipe(gulpif((option.ev == "dev" || option.ev == "pro") ,gulp.dest(dest)))
-
-                .pipe(gulp.src(src_move))
-                .pipe(changed(dest))
-                .pipe(gulpif((option.upload == 'local' && option.location != ''), gulp.dest(option.location + path + '/')))
-                .pipe(gulpif((option.upload == 'ftp' && option.host != ''), ftp({
-                    host: option.host,
-                    user: option.user || 'anonymous',
-                    pass: option.pass || '@anonymous',
-                    port: option.port || 21,
-                    remotePath: (option.remotePath || '/') + path
-                })))
-                .pipe(gulpif((option.upload == 'sftp' && option.host != ''), sftp({
-                    host: option.host,
-                    user: option.user || 'anonymous',
-                    pass: option.pass || null,
-                    port: option.port || 22,
-                    remotePath: (option.remotePath || '/') + path
-                })))
-                .pipe(gulp.dest(dest))
-                .pipe(gutil.noop());
-
-
-        }else{
-            src = ['source/' + path + '/**/*', '!**/*.spec.js', '!**/test/**'];
-            gutil.log("Move", ":", gutil.colors.green(gutil.colors.blue(path), gutil.colors.white('->'), dest));
-            return gulp.src(src)
-                .pipe(changed(dest))
-                .pipe(gulpif((option.upload == 'local' && option.location != ''), gulp.dest(option.location + path + '/')))
-                .pipe(gulpif((option.upload == 'ftp' && option.host != ''), ftp({
-                    host: option.host,
-                    user: option.user || 'anonymous',
-                    pass: option.pass || '@anonymous',
-                    port: option.port || 21,
-                    remotePath: (option.remotePath || '/') + path
-                })))
-                .pipe(gulpif((option.upload == 'sftp' && option.host != ''), sftp({
-                    host: option.host,
-                    user: option.user || 'anonymous',
-                    pass: option.pass || null,
-                    port: option.port || 22,
-                    remotePath: (option.remotePath || '/') + path
-                })))
-                .pipe(gulp.dest(dest))
-                .pipe(gutil.noop());
-        }
-    }
-}
-
-//var taskObj = {};
-apps.map(function (app) {
-    var taskName;
-    var isMin = (app.tasks.indexOf("min")!==-1);
-    taskName = app.folder;
-    appTasks.push(taskName);
-    gulp.task(taskName, getAppTask(app.folder, isMin));
-
-    //var isMin = (app.tasks.indexOf("min")!==-1);
-    taskName = app.folder+"_release";
-    //appTasks.push(taskName);
-    gulp.task(taskName, getAppTask(app.folder, isMin, release_options));
-});
-
-// Object.keys(taskObj).map(function(k){
-//     exports[k] = parallel(taskObj[k]);
-// });
-
-//exports[app.folder] = parallel(minTask, moveTask);
-
-function getCleanTask(path) {
-    return function (cb) {
-        if (path){
-            var dest = (path=="/") ? options.dest+"/" : options.dest+'/' + path + '/';
-            gutil.log("Clean", ":", gutil.colors.red(dest));
-            del.sync(dest, cb);
-            cb();
-        }else{
-            cb();
-        }
-    }
-}
-
-function cleanRemoteFtp(f, cb) {
-    var file = options.remotePath + f;
-
-    var ftp = new JSFtp({
-        host: options.host,
-        user: options.user || 'anonymous',
-        pass: options.pass || null,
-        port: options.port || 21
-    });
-
-    ftp.raw('dele ' + file, function (err) {
-        if (err) { cb(); return; }
-        if (file.substring(file.length - 3).toLowerCase() == ".js") {
-            file = file.replace('.js', ".min.js");
-            ftp.raw('dele ' + file, function (err) {
-                if (err) { cb(); return; }
-
-                if (file.indexOf("/") != -1) {
-                    var p = file.substring(0, file.lastIndexOf("/"));
-                    ftp.raw('rmd ' + p, function (err) {
-                        if (err) { cb(); return; }
-
-                        ftp.raw.quit();
-                        cb();
-                    });
-                }
-
-            });
-        } else {
-            if (file.indexOf("/") != -1) {
-                var pPath = file.substring(0, file.lastIndexOf("/"));
-                ftp.raw('rmd ' + pPath, function (err) {
-                    if (err) { cb(); return; }
-                    ftp.raw.quit();
-                    cb();
-                });
-            }
-        }
-    });
-}
-function cleanRemoteLocal(f, cb) {
-    var file = options.location + f;
-    del(file, { force: true, dryRun: true }, function () {
-        if (file.substring(file.length - 3).toLowerCase() == ".js") {
-            var minfile = file.replace('.js', ".min.js");
-            del(minfile, { force: true, dryRun: true }, function () {
-                var p = file.substring(0, file.lastIndexOf("/"));
-                fs.rmdir(p, function (err) {
-                    if (err) { }
-                    cb();
-                })
-            });
-        } else {
-            var p = file.substring(0, file.lastIndexOf("/"));
-            fs.rmdir(p, function (err) {
-                if (err) { }
-                cb();
-            })
-        }
-    });
-}
-
-function getCleanRemoteTask(path) {
-    return function (cb) {
-        if (options.upload) {
-            var file = path.replace(/\\/g, "/");
-            file = file.substring(file.indexOf("source/") + 7);
-
-            if (options.upload == 'local' && options.location != '') cleanRemoteLocal(file, cb);
-            if (options.upload == 'ftp' && options.host != '') cleanRemoteFtp(file, cb);
-        } else {
-            if (cb) cb();
-        }
-    }
-}
-
-function getWatchTask(path) {
-    return (path) ? function (cb) {
-        gutil.log("watch", ":", gutil.colors.green(path, "is watching ..."));
-        gulp.watch(['source/' + path + '/**/*', "!./**/test/**"], { "events": ['addDir', 'add', 'change'] }, gulp.parallel([path]));
-    } : function(cb){cb();};
-}
-
-gulp.task("clean", getCleanTask(options.src))
-gulp.task("watch", getWatchTask(options.src));
-
-gulp.task("index", function () {
-    var src = ['source/favicon.ico', 'source/index.html'];
-    var dest = options.dest;
-    return gulp.src(src)
-        .pipe(changed(dest))
-        .pipe(gulpif((options.upload == 'local' && options.location != ''), gulp.dest(options.location + '/')))
-        .pipe(gulpif((options.upload == 'ftp' && options.host != ''), ftp({
-            host: options.host,
-            user: options.user || 'anonymous',
-            pass: options.pass || '@anonymous',
-            port: options.port || 21,
-            remotePath: (options.remotePath || '/')
-        })))
-        .pipe(gulpif((options.upload == 'sftp' && options.host != ''), ftp({
-            host: options.host,
-            user: options.user || 'anonymous',
-            pass: options.pass || null,
-            port: options.port || 22,
-            remotePath: (options.remotePath || '/')
-        })))
-        .pipe(gulp.dest(dest))
-        .pipe(gutil.noop());
-});
-gulp.task("cleanAll", getCleanTask('/'));
-
-gulp.task("o2:new-v:html", function () {
-    var path = "x_desktop";
-    var src = 'source/x_desktop/*.html';
-    var dest = options.dest + '/x_desktop/';
-    return gulp.src(src)
-        .pipe(assetRev())
-        .pipe(gulpif((options.upload == 'local' && options.location != ''), gulp.dest(options.location + path + '/')))
-        .pipe(gulpif((options.upload == 'ftp' && options.host != ''), ftp({
-            host: options.host,
-            user: options.user || 'anonymous',
-            pass: options.pass || '@anonymous',
-            port: options.port || 21,
-            remotePath: (options.remotePath || '/') + path
-        })))
-        .pipe(gulpif((options.upload == 'sftp' && options.host != ''), sftp({
-            host: options.host,
-            user: options.user || 'anonymous',
-            pass: options.pass || null,
-            port: options.port || 22,
-            remotePath: (options.remotePath || '/') + path
-        })))
-        .pipe(gulp.dest(dest))
-        .pipe(gutil.noop());
-
-});
-gulp.task("o2:new-v:o2", function () {
-    var path = "o2_core";
-    var src = 'source/o2_core/o2.js';
-    var dest = options.dest +'/o2_core/';
-    return gulp.src(src)
-        .pipe(assetRev())
-        .pipe(gulpif((options.upload == 'local' && options.location != ''), gulp.dest(options.location + path + '/')))
-        .pipe(gulpif((options.upload == 'ftp' && options.host != ''), ftp({
-            host: options.host,
-            user: options.user || 'anonymous',
-            pass: options.pass || '@anonymous',
-            port: options.port || 21,
-            remotePath: (options.remotePath || '/') + path
-        })))
-        .pipe(gulpif((options.upload == 'sftp' && options.host != ''), sftp({
-            host: options.host,
-            user: options.user || 'anonymous',
-            pass: options.pass || null,
-            port: options.port || 22,
-            remotePath: (options.remotePath || '/') + path
-        })))
-        .pipe(gulp.dest(dest))
-        .pipe(uglify())
-        .pipe(rename({ extname: '.min.js' }))
-        .pipe(gulpif((options.upload == 'local' && options.location != ''), gulp.dest(options.location + path + '/')))
-        .pipe(gulpif((options.upload == 'ftp' && options.host != ''), ftp({
-            host: options.host,
-            user: options.user || 'anonymous',
-            pass: options.pass || '@anonymous',
-            port: options.port || 21,
-            remotePath: (options.remotePath || '/') + path
-        })))
-        .pipe(gulpif((options.upload == 'sftp' && options.host != ''), sftp({
-            host: options.host,
-            user: options.user || 'anonymous',
-            pass: options.pass || null,
-            port: options.port || 22,
-            remotePath: (options.remotePath || '/') + path
-        })))
-        .pipe(gulp.dest(dest))
-        .pipe(gutil.noop());
-});
-gulp.task("o2:new-v", gulp.parallel("o2:new-v:o2", "o2:new-v:html"));
-
-
-gulp.task("git_clean", function (cb) {
-    var dest = 'D:/O2/github/huqi1980/o2oa/o2web/source/';
-    del(dest, { dryRun: true, force: true }, cb);
-});
-
-gulp.task("git_dest", function () {
-    var dest = "D:/O2/github/huqi1980/o2oa/o2web/source";
-    return gulp.src(["source/**/*", "!./**/test/**"])
-        .pipe(changed(dest))
-        .pipe(gulp.dest(dest))
-});
-gulp.task("git", gulp.series('git_clean', 'git_dest'));
-
-gulp.task("default", gulp.series(gulp.parallel(appTasks, 'index'), "o2:new-v"));
-
-function build(){
-    options.ev = "p";
-    options.upload = o_options.upload || "";
-    options.location = o_options.location || uploadOptions.location;
-    options.host = o_options.host || uploadOptions.host;
-    options.user = o_options.user || uploadOptions.user;
-    options.pass = o_options.pass || uploadOptions.pass;
-    options.port = o_options.port || uploadOptions.port;
-    options.remotePath = o_options.remotePath || uploadOptions.remotePath;
-    options.dest = o_options.dest || uploadOptions.dest || "dest";
-};
-gulp.task("build", gulp.series("clean", gulp.parallel(appTasks, 'index'), "o2:new-v"))
+var gulp = require('gulp'),
+    //var deleted = require('gulp-deleted');
+    del = require('del'),
+    uglify = require('gulp-tm-uglify'),
+    rename = require('gulp-rename'),
+    changed = require('gulp-changed'),
+    gulpif = require('gulp-if'),
+    minimist = require('minimist'),
+    ftp = require('gulp-ftp'),
+    sftp = require('gulp-sftp'),
+    JSFtp = require('jsftp'),
+    gutil = require('gulp-util'),
+    fs = require("fs");
+var through2 = require('through2');
+
+var assetRev = require('gulp-tm-asset-rev');
+var apps = require('./gulpapps.js');
+var ftpconfig = require('./gulpconfig.js');
+
+var o_options = minimist(process.argv.slice(2), {//upload: local ftp or sftp
+    string: ["ev", "upload", "location", "host", "user", "pass", "port", "remotePath", "dest", "src"]
+});
+var options = {};
+
+var uploadOptions = ftpconfig.dev;
+if (o_options.ev && o_options.ev=="dev"){
+    options.ev = "dev";
+    uploadOptions = ftpconfig.dev;
+}else if (o_options.ev && o_options.ev=="release"){
+    options.ev = "release";
+    uploadOptions = ftpconfig.release;
+}else if (o_options.ev && o_options.ev=="wrdp"){
+    options.ev = "wrdp";
+    uploadOptions = ftpconfig.wrdp;
+}else if (o_options.ev && o_options.ev=="develop"){
+    options.ev = "develop";
+    uploadOptions = ftpconfig.develop;
+}else{
+    options.ev = "dev";
+    uploadOptions = ftpconfig.dev;
+}
+
+options.upload = o_options.upload || "";
+options.location = o_options.location || uploadOptions.location;
+options.host = o_options.host || uploadOptions.host;
+options.user = o_options.user || uploadOptions.user;
+options.pass = o_options.pass || uploadOptions.pass;
+options.port = o_options.port || uploadOptions.port;
+options.remotePath = o_options.remotePath || uploadOptions.remotePath;
+options.dest = o_options.dest || uploadOptions.dest || "dest";
+
+var release_options = {};
+release_options.ev = "release";
+release_options.upload = o_options.upload || "";
+release_options.location = o_options.location || ftpconfig.release.location;
+release_options.host = o_options.host || ftpconfig.release.host;
+release_options.user = o_options.user || ftpconfig.release.user;
+release_options.pass = o_options.pass || ftpconfig.release.pass;
+release_options.port = o_options.port || ftpconfig.release.port;
+release_options.remotePath = o_options.remotePath || ftpconfig.release.remotePath;
+release_options.dest = o_options.dest || ftpconfig.release.dest || "dest";
+
+console.log(options.host);
+console.log(options.user);
+console.log(options.pass);
+console.log(options.port);
+
+
+var appTasks = [];
+function getAppTask(path, isMin, thisOptions) {
+    return function (cb) {
+        //var srcFile = 'source/' + path + '/**/*';
+        var option = thisOptions || options;
+        var src;
+        var dest = option.dest+'/' + path + '/';
+        if (isMin){
+            var src_min = ['source/' + path + '/**/*.js', '!**/*.spec.js', '!**/test/**'];
+            var src_move = ['source/' + path + '/**/*', '!**/*.spec.js', '!**/test/**'];
+
+            gutil.log("Move-Uglify", ":", gutil.colors.green(gutil.colors.blue(path), gutil.colors.white('->'), dest));
+
+            var stream = gulp.src(src_min);
+            stream.on("end", function(){
+                let moveStream = gulp.src(src_move);
+                moveStream.on("end", function(){
+                    cb();
+                });
+
+                moveStream.pipe(changed(dest))
+                    .pipe(gulpif((option.upload == 'local' && option.location != ''), gulp.dest(option.location + path + '/')))
+                    .pipe(gulpif((option.upload == 'ftp' && option.host != ''), ftp({
+                        host: option.host,
+                        user: option.user || 'anonymous',
+                        pass: option.pass || '@anonymous',
+                        port: option.port || 21,
+                        remotePath: (option.remotePath || '/') + path
+                    })))
+                    .pipe(gulpif((option.upload == 'sftp' && option.host != ''), sftp({
+                        host: option.host,
+                        user: option.user || 'anonymous',
+                        pass: option.pass || null,
+                        port: option.port || 22,
+                        remotePath: (option.remotePath || '/') + path
+                    })))
+                    .pipe(gulp.dest(dest))
+                    .pipe(gutil.noop());
+
+            });
+            stream.pipe(changed(dest))
+                .pipe(uglify())
+                .pipe(rename({ extname: '.min.js' }))
+                .pipe(gulpif((option.upload == 'local' && option.location != ''), gulp.dest(option.location + path + '/')))
+                .pipe(gulpif((option.upload == 'ftp' && option.host != ''), ftp({
+                    host: option.host,
+                    user: option.user || 'anonymous',
+                    pass: option.pass || '@anonymous',
+                    port: option.port || 21,
+                    remotePath: (option.remotePath || '/') + path
+                })))
+                .pipe(gulpif((option.upload == 'sftp' && option.host != ''), sftp({
+                    host: option.host,
+                    user: option.user || 'anonymous',
+                    pass: option.pass || null,
+                    port: option.port || 22,
+                    remotePath: (option.remotePath || '/') + path
+                })))
+                .pipe(gulpif((option.ev == "dev" || option.ev == "pro") ,gulp.dest(dest)))
+                .pipe(gutil.noop());
+
+
+        }else{
+            src = ['source/' + path + '/**/*', '!**/*.spec.js', '!**/test/**'];
+            gutil.log("Move", ":", gutil.colors.green(gutil.colors.blue(path), gutil.colors.white('->'), dest));
+            return gulp.src(src)
+                .pipe(changed(dest))
+                .pipe(gulpif((option.upload == 'local' && option.location != ''), gulp.dest(option.location + path + '/')))
+                .pipe(gulpif((option.upload == 'ftp' && option.host != ''), ftp({
+                    host: option.host,
+                    user: option.user || 'anonymous',
+                    pass: option.pass || '@anonymous',
+                    port: option.port || 21,
+                    remotePath: (option.remotePath || '/') + path
+                })))
+                .pipe(gulpif((option.upload == 'sftp' && option.host != ''), sftp({
+                    host: option.host,
+                    user: option.user || 'anonymous',
+                    pass: option.pass || null,
+                    port: option.port || 22,
+                    remotePath: (option.remotePath || '/') + path
+                })))
+                .pipe(gulp.dest(dest))
+                .pipe(gutil.noop());
+        }
+    }
+}
+
+//var taskObj = {};
+apps.map(function (app) {
+    var taskName;
+    var isMin = (app.tasks.indexOf("min")!==-1);
+    taskName = app.folder;
+    appTasks.push(taskName);
+    gulp.task(taskName, getAppTask(app.folder, isMin));
+
+    //var isMin = (app.tasks.indexOf("min")!==-1);
+    taskName = app.folder+"_release";
+    //appTasks.push(taskName);
+    gulp.task(taskName, getAppTask(app.folder, isMin, release_options));
+});
+
+// Object.keys(taskObj).map(function(k){
+//     exports[k] = parallel(taskObj[k]);
+// });
+
+//exports[app.folder] = parallel(minTask, moveTask);
+
+function getCleanTask(path) {
+    return function (cb) {
+        if (path){
+            var dest = (path=="/") ? options.dest+"/" : options.dest+'/' + path + '/';
+            gutil.log("Clean", ":", gutil.colors.red(dest));
+            del.sync(dest, cb);
+            cb();
+        }else{
+            cb();
+        }
+    }
+}
+
+function cleanRemoteFtp(f, cb) {
+    var file = options.remotePath + f;
+
+    var ftp = new JSFtp({
+        host: options.host,
+        user: options.user || 'anonymous',
+        pass: options.pass || null,
+        port: options.port || 21
+    });
+
+    ftp.raw('dele ' + file, function (err) {
+        if (err) { cb(); return; }
+        if (file.substring(file.length - 3).toLowerCase() == ".js") {
+            file = file.replace('.js', ".min.js");
+            ftp.raw('dele ' + file, function (err) {
+                if (err) { cb(); return; }
+
+                if (file.indexOf("/") != -1) {
+                    var p = file.substring(0, file.lastIndexOf("/"));
+                    ftp.raw('rmd ' + p, function (err) {
+                        if (err) { cb(); return; }
+
+                        ftp.raw.quit();
+                        cb();
+                    });
+                }
+
+            });
+        } else {
+            if (file.indexOf("/") != -1) {
+                var pPath = file.substring(0, file.lastIndexOf("/"));
+                ftp.raw('rmd ' + pPath, function (err) {
+                    if (err) { cb(); return; }
+                    ftp.raw.quit();
+                    cb();
+                });
+            }
+        }
+    });
+}
+function cleanRemoteLocal(f, cb) {
+    var file = options.location + f;
+    del(file, { force: true, dryRun: true }, function () {
+        if (file.substring(file.length - 3).toLowerCase() == ".js") {
+            var minfile = file.replace('.js', ".min.js");
+            del(minfile, { force: true, dryRun: true }, function () {
+                var p = file.substring(0, file.lastIndexOf("/"));
+                fs.rmdir(p, function (err) {
+                    if (err) { }
+                    cb();
+                })
+            });
+        } else {
+            var p = file.substring(0, file.lastIndexOf("/"));
+            fs.rmdir(p, function (err) {
+                if (err) { }
+                cb();
+            })
+        }
+    });
+}
+
+function getCleanRemoteTask(path) {
+    return function (cb) {
+        if (options.upload) {
+            var file = path.replace(/\\/g, "/");
+            file = file.substring(file.indexOf("source/") + 7);
+
+            if (options.upload == 'local' && options.location != '') cleanRemoteLocal(file, cb);
+            if (options.upload == 'ftp' && options.host != '') cleanRemoteFtp(file, cb);
+        } else {
+            if (cb) cb();
+        }
+    }
+}
+
+function getWatchTask(path) {
+    return (path) ? function (cb) {
+        gutil.log("watch", ":", gutil.colors.green(path, "is watching ..."));
+        gulp.watch(['source/' + path + '/**/*', "!./**/test/**"], { "events": ['addDir', 'add', 'change'] }, gulp.parallel([path]));
+    } : function(cb){cb();};
+}
+
+gulp.task("clean", getCleanTask(options.src))
+gulp.task("watch", getWatchTask(options.src));
+
+gulp.task("index", function () {
+    var src = ['source/favicon.ico', 'source/index.html'];
+    var dest = options.dest;
+    return gulp.src(src)
+        .pipe(changed(dest))
+        .pipe(gulpif((options.upload == 'local' && options.location != ''), gulp.dest(options.location + '/')))
+        .pipe(gulpif((options.upload == 'ftp' && options.host != ''), ftp({
+            host: options.host,
+            user: options.user || 'anonymous',
+            pass: options.pass || '@anonymous',
+            port: options.port || 21,
+            remotePath: (options.remotePath || '/')
+        })))
+        .pipe(gulpif((options.upload == 'sftp' && options.host != ''), ftp({
+            host: options.host,
+            user: options.user || 'anonymous',
+            pass: options.pass || null,
+            port: options.port || 22,
+            remotePath: (options.remotePath || '/')
+        })))
+        .pipe(gulp.dest(dest))
+        .pipe(gutil.noop());
+});
+gulp.task("cleanAll", getCleanTask('/'));
+
+gulp.task("o2:new-v:html", function () {
+    var path = "x_desktop";
+    var src = 'source/x_desktop/*.html';
+    var dest = options.dest + '/x_desktop/';
+    return gulp.src(src)
+        .pipe(assetRev())
+        .pipe(gulpif((options.upload == 'local' && options.location != ''), gulp.dest(options.location + path + '/')))
+        .pipe(gulpif((options.upload == 'ftp' && options.host != ''), ftp({
+            host: options.host,
+            user: options.user || 'anonymous',
+            pass: options.pass || '@anonymous',
+            port: options.port || 21,
+            remotePath: (options.remotePath || '/') + path
+        })))
+        .pipe(gulpif((options.upload == 'sftp' && options.host != ''), sftp({
+            host: options.host,
+            user: options.user || 'anonymous',
+            pass: options.pass || null,
+            port: options.port || 22,
+            remotePath: (options.remotePath || '/') + path
+        })))
+        .pipe(gulp.dest(dest))
+        .pipe(gutil.noop());
+
+});
+gulp.task("o2:new-v:o2", function () {
+    var path = "o2_core";
+    var src = 'source/o2_core/o2.js';
+    var dest = options.dest +'/o2_core/';
+    return gulp.src(src)
+        .pipe(assetRev())
+        .pipe(gulpif((options.upload == 'local' && options.location != ''), gulp.dest(options.location + path + '/')))
+        .pipe(gulpif((options.upload == 'ftp' && options.host != ''), ftp({
+            host: options.host,
+            user: options.user || 'anonymous',
+            pass: options.pass || '@anonymous',
+            port: options.port || 21,
+            remotePath: (options.remotePath || '/') + path
+        })))
+        .pipe(gulpif((options.upload == 'sftp' && options.host != ''), sftp({
+            host: options.host,
+            user: options.user || 'anonymous',
+            pass: options.pass || null,
+            port: options.port || 22,
+            remotePath: (options.remotePath || '/') + path
+        })))
+        .pipe(gulp.dest(dest))
+        .pipe(uglify())
+        .pipe(rename({ extname: '.min.js' }))
+        .pipe(gulpif((options.upload == 'local' && options.location != ''), gulp.dest(options.location + path + '/')))
+        .pipe(gulpif((options.upload == 'ftp' && options.host != ''), ftp({
+            host: options.host,
+            user: options.user || 'anonymous',
+            pass: options.pass || '@anonymous',
+            port: options.port || 21,
+            remotePath: (options.remotePath || '/') + path
+        })))
+        .pipe(gulpif((options.upload == 'sftp' && options.host != ''), sftp({
+            host: options.host,
+            user: options.user || 'anonymous',
+            pass: options.pass || null,
+            port: options.port || 22,
+            remotePath: (options.remotePath || '/') + path
+        })))
+        .pipe(gulp.dest(dest))
+        .pipe(gutil.noop());
+});
+gulp.task("o2:new-v", gulp.parallel("o2:new-v:o2", "o2:new-v:html"));
+
+
+gulp.task("git_clean", function (cb) {
+    var dest = 'D:/O2/github/huqi1980/o2oa/o2web/source/';
+    del(dest, { dryRun: true, force: true }, cb);
+});
+
+gulp.task("git_dest", function () {
+    var dest = "D:/O2/github/huqi1980/o2oa/o2web/source";
+    return gulp.src(["source/**/*", "!./**/test/**"])
+        .pipe(changed(dest))
+        .pipe(gulp.dest(dest))
+});
+gulp.task("git", gulp.series('git_clean', 'git_dest'));
+
+gulp.task("default", gulp.series(gulp.parallel(appTasks, 'index'), "o2:new-v"));
+
+function build(){
+    options.ev = "p";
+    options.upload = o_options.upload || "";
+    options.location = o_options.location || uploadOptions.location;
+    options.host = o_options.host || uploadOptions.host;
+    options.user = o_options.user || uploadOptions.user;
+    options.pass = o_options.pass || uploadOptions.pass;
+    options.port = o_options.port || uploadOptions.port;
+    options.remotePath = o_options.remotePath || uploadOptions.remotePath;
+    options.dest = o_options.dest || uploadOptions.dest || "dest";
+};
+gulp.task("build", gulp.series("clean", gulp.parallel(appTasks, 'index'), "o2:new-v"))

+ 1 - 0
o2web/source/o2_core/o2/xDesktop/Authentication.js

@@ -85,6 +85,7 @@ MWF.xDesktop.Authentication = new Class({
                 this.socket = null;
             }
             Cookie.dispose("x-token");
+            if (layout.session && layout.session.user) layout.session.user.token = "";
             window.location.reload();
         }.bind(this));
     },

+ 46 - 25
o2web/source/o2_core/o2/xDesktop/WebSocket.js

@@ -14,9 +14,12 @@ MWF.xDesktop.WebSocket = new Class({
 
         this.reConnect = true;
         this.checking = false;
-        this.heartTimeout = 30000;
-        this.checkingTimeout = 10000;
+        this.heartTimeout = 60000;
+        this.checkingTimeout = 4000;
         this.heartMsg = "heartbeat";
+        this.maxErrorCount = 10;
+        this.errorCount = 0;
+
 
         // var addressObj = layout.desktop.serviceAddressList["x_collaboration_assemble_websocket"];
         // this.ws = "ws://"+addressObj.host+(addressObj.port==80 ? "" : ":"+addressObj.port)+addressObj.context+"/ws/collaboration";
@@ -35,38 +38,43 @@ MWF.xDesktop.WebSocket = new Class({
         ///*暂时不启用WebSocket了------------
         //this.ws = this.ws+"?x-token="+encodeURIComponent(Cookie.read("x-token"))+"&authorization="+encodeURIComponent(Cookie.read("x-token"));
 
+        this.connect();
+    },
+    connect: function(){
         if (layout.config.webSocketEnable){
-            this.ws = this.ws+"?x-token="+encodeURIComponent(Cookie.read("x-token"));
+            var ws = this.ws+"?x-token="+encodeURIComponent(Cookie.read("x-token"));
 
             try{
-                this.webSocket = new WebSocket(this.ws);
+                this.webSocket = new WebSocket(ws);
 
                 //this.webSocket = new WebSocket(this.ws);
                 this.webSocket.onopen = function (e){this.onOpen(e);}.bind(this);
                 this.webSocket.onclose = function (e){this.onClose(e);}.bind(this);
                 this.webSocket.onmessage = function (e){this.onMessage(e);}.bind(this);
                 this.webSocket.onerror = function (e){this.onError(e);}.bind(this);
-                //---------------------------------*/\
+                //---------------------------------*/
             }catch(e){
                 //WebSocket.close();
                 //this.webSocket = new WebSocket(this.ws);
-                console.log("Unable to connect to the websocket server, will retry in "+(this.heartTimeout/1000)+" seconds");
-                if (this.webSocket){
-                    this.close();
-                    //this.webSocket = new WebSocket(this.ws);
-                }
+                console.log("Unable to connect to the websocket server, will retry in "+(this.checkingTimeout/1000)+" seconds");
+                this.checkRetry();
+                // if (this.webSocket){
+                //     this.close();
+                //     //this.webSocket = new WebSocket(this.ws);
+                // }
             }
-            this.heartbeat();
         }
-
     },
     onOpen: function(e){
+        this.errorCount = 0;
         console.log("websocket is open, You can receive system messages");
+        this.heartbeat();
+
         //MWF.xDesktop.notice("success", {"x": "right", "y": "top"}, "websocket is open ...");
     },
     onClose: function(e){
         console.log("websocket is closed. ");
-        if (this.reConnect) this.initialize();
+        //if (this.reConnect) this.checkRetry();
         //MWF.xDesktop.notice("success", {"x": "right", "y": "top"}, "websocket is closed ...");
     },
     onMessage: function(e){
@@ -152,14 +160,25 @@ MWF.xDesktop.WebSocket = new Class({
         }
     },
     onError: function(e){
-        console.log("websocket is error ...");
+        this.errorCount++;
+        //console.log(e);
+        console.log("Unable to connect to the websocket server, will retry in "+(this.checkingTimeout/1000)+" seconds.");
+        this.checkRetry();
         //MWF.xDesktop.notice("success", {"x": "right", "y": "top"}, "websocket is error ...");
     },
+    checkRetry: function(){
+        if (this.serverCheck) window.clearTimeout(this.serverCheck);
+        if (this.heartbeatCheck) window.clearTimeout(this.heartbeatCheck);
+        if (this.errorCount < this.maxErrorCount) this.serverCheck = window.setTimeout(function(){
+            this.retry();
+        }.bind(this), this.checkingTimeout);
+    },
     retry: function(){
         if (this.webSocket){
             this.close();
         }
-        this.initialize();
+        console.log("Retry connect to websocket server. ("+this.errorCount+"/"+this.maxErrorCount+")");
+        this.connect();
     },
     close: function(){
         this.reConnect = false;
@@ -168,14 +187,15 @@ MWF.xDesktop.WebSocket = new Class({
     },
     send: function(msg){
         if (!this.webSocket || this.webSocket.readyState != 1) {
-            this.initialize();
-        }
-        try{
-            this.webSocket.send(JSON.encode(msg));
-        }catch(e){
-            this.initialize();
-            this.webSocket.send(JSON.encode(msg));
+            if (this.serverCheck) window.clearTimeout(this.serverCheck);
+            this.retry();
         }
+        // try{
+        this.webSocket.send(JSON.encode(msg));
+        // }catch(e){
+        //     this.retry();
+        //     this.webSocket.send(JSON.encode(msg));
+        // }
     },
     heartbeat: function(){
         if (this.serverCheck) window.clearTimeout(this.serverCheck);
@@ -186,15 +206,16 @@ MWF.xDesktop.WebSocket = new Class({
     },
     sendHeartbeat: function(msg){
         if (!this.webSocket || this.webSocket.readyState != 1) {
+            if (this.serverCheck) window.clearTimeout(this.serverCheck);
             this.retry();
         }
         try{
             //console.log("send heartbeat ...");
             this.webSocket.send(msg);
-            this.serverCheck = window.setTimeout(function(){
-                this.retry();
-            }.bind(this), this.checkingTimeout);
+            this.checkRetry();
         }catch(e){
+            //console.log("send heartbeat error !!!");
+            if (this.serverCheck) window.clearTimeout(this.serverCheck);
             this.retry();
             //this.initialize();
         }

+ 30 - 6
o2web/source/x_component_Forum/TopNode.js

@@ -346,11 +346,35 @@ MWF.xApplication.Forum.TopNode = new Class({
         });
     },
     logout: function(){
-        MWF.Actions.get("x_organization_assemble_authentication").logout( function(){
-            layout.desktop.session.user.distinguishedName = "anonymous";
-            this.app.clearContent();
-            this.app.loadApplicationContent();
-            this.openLoginForm();
-        }.bind(this))
+        MWF.Actions.get("x_organization_assemble_authentication").logout(function () {
+            if (this.socket) {
+                this.socket.close();
+                this.socket = null;
+            }
+            Cookie.dispose("x-token");
+            delete layout.desktop.session.user.token;
+            delete layout.desktop.session.user.tokenType;
+            layout.desktop.session.user = {
+                distinguishedName : "anonymous",
+                name : "anonymous",
+                tokenType : "anonymous"
+            }
+
+            if( window.location.href.indexOf("/app.html") > 0 ){
+                window.location.reload();
+            }else{
+                this.app.clearContent();
+                this.app.loadApplicationContent();
+                this.openLoginForm();
+            }
+            //window.location.reload();
+        }.bind(this));
+
+        // MWF.Actions.get("x_organization_assemble_authentication").logout( function(){
+        //     layout.desktop.session.user.distinguishedName = "anonymous";
+        //     this.app.clearContent();
+        //     this.app.loadApplicationContent();
+        //     this.openLoginForm();
+        // }.bind(this))
     }
 });

+ 6 - 1
o2web/source/x_component_process_Application/$Main/reroute.html

@@ -1,5 +1,5 @@
 <div style="padding: 0px 30px; font-size:14px;">
-    <div style="height: 50px; margin-bottom:10px;overflow-y:auto;">
+    <div style="height: 120px; margin-bottom:10px;overflow-y:auto;">
         <div style="line-height: 30px; margin-bottom:10px; overflow: hidden">
             <div style="float: left; width: 70px">调度到:</div>
             <div style="margin-left: 70px">
@@ -7,6 +7,11 @@
                 <!--<div id="resetWork_selPeopleButton" style="font-size:12px; border-radius: 5px; height: 22px; line-height:22px; float:right; margin-top:4px; border:1px solid #666; color: #FFF; background-color: #666; box-shadow: 1px 1px 3px #999; cursor: pointer; padding: 0px 10px;">选择人员</div>
                 <div id="resetWork_selPeopleArea" style="margin-right: 70px; height: 30px"></div>-->
             </div>
+            <div style="float: left; width: 70px; margin-top: 10px">调度给:</div>
+            <div style="margin-left: 70px; margin-top: 10px">
+                <div class="rerouteWork_selPeopleButton" style="font-size:12px; margin-right: 20px; border-radius: 5px; height: 22px; line-height:22px; float:right; margin-top:4px; border:1px solid #666; color: #FFF; background-color: #666; box-shadow: 1px 1px 3px #999; cursor: pointer; padding: 0px 10px;">选择人员</div>
+                <div class="rerouteWork_selPeopleArea" style="margin-right: 70px; height: 30px"></div>
+            </div>
         </div>
         <div style="height: 30px; line-height: 30px; margin-bottom:10px; display:none">
             <div style="float: left; width: 70px; height: 50px; line-height: 50px;">调度意见:</div>

+ 8 - 3
o2web/source/x_component_process_Application/WorkExplorer.js

@@ -889,10 +889,9 @@ MWF.xApplication.process.Application.WorkExplorer.Work = new Class({
         this.readyReroute = true;
 
         MWF.require("MWF.xDesktop.Dialog", function(){
-            var width = 480;
+            var width = 560;
             var height = 260;
             var p = MWF.getCenterPosition(this.explorer.app.content, width, height);
-            var _self = this;
 
             var _self = this;
             var dlg = new MWF.xDesktop.Dialog({
@@ -1082,7 +1081,13 @@ MWF.xApplication.process.Application.WorkExplorer.Work = new Class({
         }.bind(this));
     },
     rerouteWorkToActivity: function(activity, type, opinion, nameArr, success, failure){
-        o2.Actions.load("x_processplatform_assemble_surface").WorkAction.V2Reroute(this.data.id, activity, type, {"manualForceTaskIdentityList": nameArr}, function(){
+        var body = {
+            "activity": activity,
+            "activityType": type,
+            "mergeWork": false,
+            "manualForceTaskIdentityList": nameArr
+        };
+        o2.Actions.load("x_processplatform_assemble_surface").WorkAction.V2Reroute(this.data.id, body, function(){
             if (success) success();
         }.bind(this), function (xhr, text, error) {
             if (failure) failure(xhr, text, error);

+ 1 - 1
o2web/source/x_component_process_TaskCenter/Main.js

@@ -1177,7 +1177,7 @@ MWF.xApplication.process.TaskCenter.Starter = new Class({
                         if (pro.name.indexOf(key)!==-1){
                             var data = Object.clone(pro);
                             data.applicationName = app.name;
-                            new MWF.xApplication.process.TaskCenter.Process(data, {"app": this}, {"name": app}, proListNode);
+                            new MWF.xApplication.process.TaskCenter.Process(data, {"app": this.app, "starter": this}, {"name": app}, proListNode);
                         }
                     }.bind(this));
                 }.bind(this));

+ 1 - 0
o2web/source/x_component_process_Xform/$Input.js

@@ -312,6 +312,7 @@ MWF.xApplication.process.Xform.$Input = MWF.APP$Input =  new Class({
             }
 
             if (!parentNode.isIntoView()) parentNode.scrollIntoView();
+
         }
     },
     showNotValidationMode: function(node){

+ 0 - 1
o2web/source/x_component_process_Xform/Form.js

@@ -3091,7 +3091,6 @@ MWF.xApplication.process.Xform.Form = MWF.APPForm = new Class({
         }.bind(this));
     },
     rerouteWorkToActivity: function (activity, type, opinion, nameArr, success, failure) {
-        debugger;
         var body = {
             "activity": activity,
             "activityType": type,

+ 3 - 0
o2web/source/x_desktop/js/forum.js

@@ -16,6 +16,9 @@ o2.addReady(function(){
 
         MWF.require("MWF.xDesktop.Layout", function(){
             MWF.require("MWF.xDesktop.Authentication", null, false);
+            MWF.require("MWF.xDesktop.Common", null, false);
+            MWF.require("MWF.xAction.RestActions", null, false);
+            MWF.require("MWF.xDesktop.Access", null, false);
             MWF.xDesktop.requireApp("Common", "", null, false);
             (function(){
                 layout.requireApp = function(appNames, callback, clazzName){

+ 3 - 1
o2web/source/x_desktop/js/forumDocMobile.js

@@ -17,7 +17,9 @@ o2.addReady(function(){
 
         MWF.require("MWF.xDesktop.Layout", function(){
             MWF.require("MWF.xDesktop.Authentication", null, false);
-
+            MWF.require("MWF.xDesktop.Common", null, false);
+            MWF.require("MWF.xAction.RestActions", null, false);
+            MWF.require("MWF.xDesktop.Access", null, false);
             (function(){
                 layout.load = function(){
                     if (this.isAuthentication()){