Browse Source

稳定版更新

roo00 6 years ago
parent
commit
3b18448a77
100 changed files with 4742 additions and 902 deletions
  1. 5 2
      o2server/x_base_core_project/src/main/java/com/x/base/core/entity/JpaObject.java
  2. 5 1
      o2server/x_base_core_project/src/main/java/com/x/base/core/entity/dataitem/DataItemConverter.java
  3. 1 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/entity/dynamic/DynamicEntity.java
  4. 3 4
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/Context.java
  5. 0 11
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/ProcessPlatform.java
  6. 5 4
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/jaxrs/StandardJaxrsAction.java
  7. 2 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/message/MessageConnector.java
  8. 6 8
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/schedule/JobReportListener.java
  9. 25 23
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/StringTools.java
  10. 1 1
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/x_cms_assemble_control.java
  11. 45 0
      o2server/x_base_core_project/src/test/java/com/x/base/core/project/test/string/TestClient.java
  12. 20 3
      o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/jaxrs/foruminfo/ActionGet.java
  13. 18 2
      o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/jaxrs/foruminfo/ActionGetAll.java
  14. 30 2
      o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/jaxrs/foruminfo/ActionGetAllWithPermission.java
  15. 32 3
      o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/jaxrs/foruminfo/ActionSave.java
  16. 62 1
      o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/jaxrs/sectioninfo/ActionAllListSubSectionByMainSectionId.java
  17. 62 1
      o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/jaxrs/sectioninfo/ActionAllSections.java
  18. 58 1
      o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/jaxrs/sectioninfo/ActionGet.java
  19. 62 1
      o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/jaxrs/sectioninfo/ActionListSubSectionByMainSectionId.java
  20. 62 1
      o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/jaxrs/sectioninfo/ActionListWithForum.java
  21. 68 0
      o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/jaxrs/sectioninfo/ActionSave.java
  22. 79 75
      o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/jaxrs/subjectinfo/ActionSubjectView.java
  23. 1 1
      o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/service/UserPermissionService.java
  24. 88 0
      o2server/x_bbs_core_entity/src/main/java/com/x/bbs/entity/BBSForumInfo.java
  25. 95 0
      o2server/x_bbs_core_entity/src/main/java/com/x/bbs/entity/BBSSectionInfo.java
  26. 0 1
      o2server/x_bbs_core_entity/src/main/java/com/x/bbs/entity/BBSSubjectInfo.java
  27. 13 0
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/MessageFactory.java
  28. 12 5
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/ThisApplication.java
  29. 98 6
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/factory/DocumentFactory.java
  30. 6 0
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/appinfo/ActionSave.java
  31. 40 1
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/categoryinfo/ActionSave.java
  32. 12 0
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/categoryinfo/ExceptionAppInfoNotExists.java
  33. 26 78
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/ActionPersistPublishAndNotify.java
  34. 1 1
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/ActionPersistPublishByWorkFlow.java
  35. 4 3
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/ActionPersistPublishContent.java
  36. 23 3
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/ActionPersistSaveDocument.java
  37. 7 3
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/ActionQueryCountWithFilter.java
  38. 527 0
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/ActionQueryGetControl.java
  39. 0 1
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/ActionQueryGetDocument.java
  40. 1 2
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/ActionQueryListDraftNextWithFilter.java
  41. 156 0
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/ActionQueryListVisiblePersons.java
  42. 185 0
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/ActionQueryListWithFilterPaging.java
  43. 73 5
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/DocumentAction.java
  44. 204 0
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/queue/QueueSendDocumentNotify.java
  45. 1 0
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/service/CmsBatchOperationProcessService.java
  46. 8 1
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/service/DocumentPersistService.java
  47. 51 0
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/service/DocumentQueryService.java
  48. 2 2
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/service/PermissionOperateService.java
  49. 1 1
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/service/ReviewService.java
  50. 22 0
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/timertask/Timertask_CheckDocumentReviewStatus.java
  51. 22 0
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/timertask/Timertask_InitOperationRunning.java
  52. 8 2
      o2server/x_cms_assemble_control/src/main/webapp/jest/describe.js
  53. 15 0
      o2server/x_cms_core_entity/src/main/java/com/x/cms/core/entity/AppInfo.java
  54. 15 0
      o2server/x_cms_core_entity/src/main/java/com/x/cms/core/entity/CategoryInfo.java
  55. 5 2
      o2server/x_cms_core_entity/src/main/java/com/x/cms/core/entity/tools/CriteriaBuilderTools.java
  56. 270 273
      o2server/x_console/src/main/java/com/x/server/console/Main.java
  57. 0 9
      o2server/x_hotpic_assemble_control/src/main/java/com/x/hotpic/assemble/common/date/DateOperation.java
  58. 5 3
      o2server/x_message_assemble_communicate/src/main/java/com/x/message/assemble/communicate/ws/collaboration/ActionCollaboration.java
  59. 1 0
      o2server/x_organization_assemble_control/src/main/java/com/x/organization/assemble/control/Business.java
  60. 1 0
      o2server/x_organization_assemble_control/src/main/java/com/x/organization/assemble/control/jaxrs/identity/ActionCreate.java
  61. 1 0
      o2server/x_organization_assemble_control/src/main/java/com/x/organization/assemble/control/jaxrs/identity/ActionEdit.java
  62. 10 2
      o2server/x_organization_assemble_control/src/main/java/com/x/organization/assemble/control/jaxrs/person/ActionCreate.java
  63. 2 4
      o2server/x_organization_assemble_control/src/main/java/com/x/organization/assemble/control/jaxrs/person/ActionListNext.java
  64. 3 0
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/factory/content/WorkLogFactory.java
  65. 13 1
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/factory/element/RouteFactory.java
  66. 3 9
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/data/ActionUpdateWithWork.java
  67. 3 9
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/data/ActionUpdateWithWorkCompleted.java
  68. 17 14
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/data/BaseAction.java
  69. 2 2
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/data/ExceptionNotJsonObject.java
  70. 0 12
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/data/ExceptionNullData.java
  71. 0 12
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/data/ExceptionPrimitiveData.java
  72. 60 34
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/task/ActionProcessing.java
  73. 14 14
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/task/TaskAction.java
  74. 7 0
      o2server/x_processplatform_core_entity/src/main/java/com/x/processplatform/core/entity/content/Data.java
  75. 12 0
      o2server/x_processplatform_core_entity/src/main/java/com/x/processplatform/core/entity/content/Work.java
  76. 21 25
      o2server/x_processplatform_core_entity/src/main/java/com/x/processplatform/core/entity/element/Manual.java
  77. 71 0
      o2server/x_processplatform_core_entity/src/main/java/com/x/processplatform/core/entity/element/Route.java
  78. 1 1
      o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/factory/ElementFactory.java
  79. 117 0
      o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/jaxrs/task/ActionAppend.java
  80. 8 21
      o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/jaxrs/task/ActionProcessing.java
  81. 18 0
      o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/jaxrs/task/TaskAction.java
  82. 140 0
      o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/jaxrs/work/ActionManualAppendIdentity.java
  83. 13 0
      o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/jaxrs/work/ExceptionNotManual.java
  84. 18 0
      o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/jaxrs/work/WorkAction.java
  85. 0 8
      o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/processor/AbstractProcessor.java
  86. 91 2
      o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/processor/end/AbstractEndProcessor.java
  87. 154 67
      o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/processor/end/EndProcessor.java
  88. 154 0
      o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/processor/end/EndProcessor2.java
  89. 89 0
      o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/processor/manual/AbstractManualProcessor.java
  90. 79 28
      o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/processor/manual/ManualProcessor.java
  91. 616 0
      o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/processor/manual/ManualProcessor2.java
  92. 0 2
      o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/processor/merge/AbstractMergeProcessor.java
  93. 51 75
      o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/processor/merge/MergeProcessor.java
  94. 242 0
      o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/processor/merge/MergeProcessor2.java
  95. 26 0
      o2server/x_processplatform_service_processing/src/test/java/com/x/processplatform/core/processing/test/TestClient.java
  96. 2 2
      o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/schedule/ActionReport.java
  97. 1 0
      o2server/x_program_center/src/main/java/com/x/program/center/schedule/FireSchedule.java
  98. 14 1
      o2server/x_query_assemble_designer/src/main/java/com/x/query/assemble/designer/jaxrs/table/ActionCreate.java
  99. 15 3
      o2server/x_query_assemble_designer/src/main/java/com/x/query/assemble/designer/jaxrs/table/ActionEdit.java
  100. 4 7
      o2server/x_query_assemble_designer/src/main/java/com/x/query/assemble/designer/jaxrs/table/ActionRowInsert.java

+ 5 - 2
o2server/x_base_core_project/src/main/java/com/x/base/core/entity/JpaObject.java

@@ -94,6 +94,11 @@ public abstract class JpaObject extends GsonPropertyObject implements Serializab
 			Arrays.asList(distributeFactor_FIELDNAME, sequence_FIELDNAME, password_FIELDNAME, scratchString_FIELDNAME,
 					scratchBoolean_FIELDNAME, scratchDate_FIELDNAME, scratchInteger_FIELDNAME));
 
+	public static final List<String> FieldsDefault = ListUtils
+			.unmodifiableList(Arrays.asList(id_FIELDNAME, key_FIELDNAME, createTime_FIELDNAME, updateTime_FIELDNAME,
+					sequence_FIELDNAME, distributeFactor_FIELDNAME, password_FIELDNAME, scratchString_FIELDNAME,
+					scratchBoolean_FIELDNAME, scratchDate_FIELDNAME, scratchInteger_FIELDNAME));
+
 	@FieldDescribe("创建时间,自动生成,索引创建在约束中.")
 	@Column(name = ColumnNamePrefix + createTime_FIELDNAME)
 	private Date createTime;
@@ -273,8 +278,6 @@ public abstract class JpaObject extends GsonPropertyObject implements Serializab
 		return ListTools.trim(values, true, true);
 	}
 
-
-
 	public int hashCode() {
 		return 31 + ((this.getId() == null) ? 0 : this.getId().hashCode());
 	}

+ 5 - 1
o2server/x_base_core_project/src/main/java/com/x/base/core/entity/dataitem/DataItemConverter.java

@@ -160,7 +160,11 @@ public class DataItemConverter<T extends DataItem> {
 			// if (!NumberUtils.isNumber(name)) {
 			o.getAsJsonObject().add(name, jsonElement);
 		} else {
-			o.getAsJsonArray().add(jsonElement);
+			try {
+				o.getAsJsonArray().add(jsonElement);
+			} catch (Exception e) {
+				throw new Exception(e.getMessage() + ": name:" + name + ", jsonElement:" + jsonElement + ".", e);
+			}
 		}
 		return root;
 	}

+ 1 - 0
o2server/x_base_core_project/src/main/java/com/x/base/core/entity/dynamic/DynamicEntity.java

@@ -8,6 +8,7 @@ import org.apache.commons.lang3.StringUtils;
 
 import com.x.base.core.entity.JpaObject;
 import com.x.base.core.project.gson.GsonPropertyObject;
+import com.x.base.core.project.tools.ListTools;
 
 public class DynamicEntity extends GsonPropertyObject {
 

+ 3 - 4
o2server/x_base_core_project/src/main/java/com/x/base/core/project/Context.java

@@ -268,13 +268,12 @@ public class Context extends AbstractContext {
 
 	public <T extends AbstractJob> void scheduleLocal(Class<T> cls, Integer delay) throws Exception {
 		/* 需要单独生成一个独立任务,保证group和预约的任务不重复 */
-		String group = StringTools.uniqueToken();
 		JobDataMap jobDataMap = new JobDataMap();
 		jobDataMap.put("context", this);
-		JobDetail jobDetail = JobBuilder.newJob(cls).withIdentity(cls.getName(), group).usingJobData(jobDataMap)
-				.withDescription(Config.node()).build();
+		JobDetail jobDetail = JobBuilder.newJob(cls).withIdentity(cls.getName(), clazz.getName())
+				.usingJobData(jobDataMap).withDescription(Config.node()).build();
 		/* 经过测试0代表不重复,仅运行一次 */
-		Trigger trigger = TriggerBuilder.newTrigger().withIdentity(cls.getName(), group)
+		Trigger trigger = TriggerBuilder.newTrigger().withIdentity(cls.getName(), clazz.getName())
 				.withDescription("scheduleLocal").startAt(DateBuilder.futureDate(delay, IntervalUnit.SECOND))
 				.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).withRepeatCount(0))
 				.build();

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

@@ -26,8 +26,6 @@ public class ProcessPlatform extends ConfigObject {
 
 	public final static String DEFAULT_DOCTOWORDDEFAULTSITE = "$doc";
 
-	public final static Boolean DEFAULT_PROCESSINGTASKSAMEJOBACTIVITYIDENTITY = true;
-
 	public static ProcessPlatform defaultInstance() {
 		return new ProcessPlatform();
 	}
@@ -45,7 +43,6 @@ public class ProcessPlatform extends ConfigObject {
 		this.docToWordType = DEFAULT_DOCTOWORDTYPE;
 		this.docToWordDefaultFileName = DEFAULT_DOCTOWORDDEFAULTFILENAME;
 		this.docToWordDefaultSite = DEFAULT_DOCTOWORDDEFAULTSITE;
-		this.processingTaskSameJobActivityIdentity = DEFAULT_PROCESSINGTASKSAMEJOBACTIVITYIDENTITY;
 	}
 
 	@FieldDescribe("提醒设置,设置提醒间隔.")
@@ -87,14 +84,6 @@ public class ProcessPlatform extends ConfigObject {
 	@FieldDescribe("HTML版式公文转换成Word文件缺省site.")
 	private String docToWordDefaultSite;
 
-	@FieldDescribe("同步处理同一工作在同一环节同一处理身份.")
-	private Boolean processingTaskSameJobActivityIdentity;
-
-	public Boolean getProcessingTaskSameJobActivityIdentity() {
-		return processingTaskSameJobActivityIdentity == null ? DEFAULT_PROCESSINGTASKSAMEJOBACTIVITYIDENTITY
-				: BooleanUtils.isTrue(this.processingTaskSameJobActivityIdentity);
-	}
-
 	public Integer getFormVersionPeriod() {
 		return (formVersionPeriod == null || formVersionPeriod < 1) ? DEFAULT_FORMVERSIONPERIOD
 				: this.formVersionPeriod;

+ 5 - 4
o2server/x_base_core_project/src/main/java/com/x/base/core/project/jaxrs/StandardJaxrsAction.java

@@ -1107,11 +1107,11 @@ public abstract class StandardJaxrsAction extends AbstractJaxrsAction {
 			if (StringUtils.equalsIgnoreCase(order, DESC)) {
 				cq.where(null == sequence ? predicate
 						: cb.and(predicate, cb.greaterThan(root.get(sequenceField), (Comparable) sequence)));
-				cq.orderBy(cb.desc(root.get(sequenceField)));
+				cq.orderBy(cb.asc(root.get(sequenceField)));
 			} else {
 				cq.where(null == sequence ? predicate
 						: cb.and(predicate, cb.lessThan(root.get(sequenceField), (Comparable) sequence)));
-				cq.orderBy(cb.asc(root.get(sequenceField)));
+				cq.orderBy(cb.desc(root.get(sequenceField)));
 			}
 
 			List<Selection<?>> selections = new ArrayList<>();
@@ -1157,6 +1157,7 @@ public abstract class StandardJaxrsAction extends AbstractJaxrsAction {
 					sequence = PropertyUtils.getProperty(t, sequenceField);
 				}
 			}
+
 			EntityManager em = emc.get(cls);
 			CriteriaBuilder cb = em.getCriteriaBuilder();
 			CriteriaQuery<T> cq = cb.createQuery(cls);
@@ -1210,11 +1211,11 @@ public abstract class StandardJaxrsAction extends AbstractJaxrsAction {
 			if (StringUtils.equalsIgnoreCase(order, DESC)) {
 				cq.where(null == sequence ? predicate
 						: cb.and(predicate, cb.greaterThan(root.get(sequenceField), (Comparable) sequence)));
-				cq.orderBy(cb.desc(root.get(sequenceField)));
+				cq.orderBy(cb.asc(root.get(sequenceField)));
 			} else {
 				cq.where(null == sequence ? predicate
 						: cb.and(predicate, cb.lessThan(root.get(sequenceField), (Comparable) sequence)));
-				cq.orderBy(cb.asc(root.get(sequenceField)));
+				cq.orderBy(cb.desc(root.get(sequenceField)));
 			}
 
 			List<T> os = em.createQuery(cq.select(root)).setMaxResults(Math.max(Math.min(count, list_max), list_min))

+ 2 - 0
o2server/x_base_core_project/src/main/java/com/x/base/core/project/message/MessageConnector.java

@@ -105,6 +105,8 @@ public class MessageConnector {
 	public static final String TYPE_TEAMWORK_TASKOVERTIME = "teamwork_taskOvertime";
 	
 	public static final String TYPE_TEAMWORK_CHAT = "teamwork_taskChat";
+	
+	public static final String TYPE_CMS_PUBLISH = "cms_publish";
 
 	public static final String CONSUME_WS = "ws";
 

+ 6 - 8
o2server/x_base_core_project/src/main/java/com/x/base/core/project/schedule/JobReportListener.java

@@ -6,13 +6,13 @@ import org.quartz.JobListener;
 
 import com.x.base.core.project.config.Config;
 import com.x.base.core.project.connection.CipherConnectionAction;
+import com.x.base.core.project.gson.XGsonBuilder;
 import com.x.base.core.project.logger.Logger;
 import com.x.base.core.project.logger.LoggerFactory;
 
 public class JobReportListener implements JobListener {
 
 	private static Logger logger = LoggerFactory.getLogger(JobReportListener.class);
-
 	@Override
 	public String getName() {
 		return this.getClass().getName();
@@ -30,13 +30,11 @@ public class JobReportListener implements JobListener {
 
 	@Override
 	public void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException jobExecutionException) {
-		if (null != jobExecutionException) {
-			ScheduleLogRequest request = new ScheduleLogRequest(jobExecutionContext, jobExecutionException);
-			try {
-				CipherConnectionAction.post(false, Config.url_x_program_center_jaxrs("schedule", "report"), request);
-			} catch (Exception e) {
-				logger.error(e);
-			}
+		ScheduleLogRequest request = new ScheduleLogRequest(jobExecutionContext, jobExecutionException);
+		try {
+			CipherConnectionAction.post(false, Config.url_x_program_center_jaxrs("schedule", "report"), request);
+		} catch (Exception e) {
+			logger.error(e);
 		}
 	}
 

+ 25 - 23
o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/StringTools.java

@@ -323,25 +323,26 @@ public class StringTools {
 	}
 
 	public static boolean matchWildcard(String str, String pattern) {
-		if (StringUtils.isNotEmpty(str) && StringUtils.isNotEmpty(pattern) && StringUtils.contains(pattern, "*")) {
-			if (StringUtils.equals(pattern, "*")) {
-				return true;
-			}
-			if (StringUtils.startsWith(pattern, "*")) {
-				return StringUtils.endsWith(str, StringUtils.substringAfter(pattern, "*"));
-			}
-			if (StringUtils.endsWith(pattern, "*")) {
-				return StringUtils.startsWith(str, StringUtils.substringBeforeLast(pattern, "*"));
-			}
-			String[] parts = StringUtils.split(pattern, "*", 2);
-			if (StringUtils.startsWith(str, parts[0]) && StringUtils.endsWith(str, parts[1])) {
-				return true;
-			} else {
-				return false;
-			}
-		} else {
-			return StringUtils.equals(str, pattern);
-		}
+		return Objects.toString(str, "").matches(Objects.toString(pattern, "").replace("?", ".?").replace("*", ".*?"));
+//		if (StringUtils.isNotEmpty(str) && StringUtils.isNotEmpty(pattern) && StringUtils.contains(pattern, "*")) {
+//			if (StringUtils.equals(pattern, "*")) {
+//				return true;
+//			}
+//			if (StringUtils.startsWith(pattern, "*")) {
+//				return StringUtils.endsWith(str, StringUtils.substringAfter(pattern, "*"));
+//			}
+//			if (StringUtils.endsWith(pattern, "*")) {
+//				return StringUtils.startsWith(str, StringUtils.substringBeforeLast(pattern, "*"));
+//			}
+//			String[] parts = StringUtils.split(pattern, "*", 2);
+//			if (StringUtils.startsWith(str, parts[0]) && StringUtils.endsWith(str, parts[1])) {
+//				return true;
+//			} else {
+//				return false;
+//			}
+//		} else {
+//			return StringUtils.equals(str, pattern);
+//		}
 	}
 
 	public static List<String> includesExcludesWithWildcard(List<String> list, Collection<String> includes,
@@ -386,11 +387,12 @@ public class StringTools {
 		}
 	}
 
-	private static final int[] SIZE_TABLE = new int[]{9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999, 2147483647};
+	private static final int[] SIZE_TABLE = new int[] { 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999,
+			2147483647 };
 
 	public static int sizeOfInt(int x) {
 		int i;
-		for(i = 0; x > SIZE_TABLE[i]; ++i) {
+		for (i = 0; x > SIZE_TABLE[i]; ++i) {
 		}
 
 		return i + 1;
@@ -408,7 +410,7 @@ public class StringTools {
 			if (i < 0) {
 				return true;
 			}
-		} while(Character.isDigit(str.charAt(i)));
+		} while (Character.isDigit(str.charAt(i)));
 
 		return false;
 	}
@@ -416,7 +418,7 @@ public class StringTools {
 	public static boolean equalsNull(String str) {
 		int strLen;
 		if (str != null && (strLen = str.length()) != 0 && !str.equalsIgnoreCase("null")) {
-			for(int i = 0; i < strLen; ++i) {
+			for (int i = 0; i < strLen; ++i) {
 				if (!Character.isWhitespace(str.charAt(i))) {
 					return false;
 				}

+ 1 - 1
o2server/x_base_core_project/src/main/java/com/x/base/core/project/x_cms_assemble_control.java

@@ -5,7 +5,7 @@ import com.x.base.core.project.annotation.Module;
 import com.x.base.core.project.annotation.ModuleCategory;
 import com.x.base.core.project.annotation.ModuleType;
 
-@Module(type = ModuleType.ASSEMBLE, category = ModuleCategory.OFFICIAL, name = "内容管理", packageName = "com.x.cms.assemble.control", containerEntities = {
+@Module( type = ModuleType.ASSEMBLE, category = ModuleCategory.OFFICIAL, name = "内容管理", packageName = "com.x.cms.assemble.control", containerEntities = {
 		"com.x.cms.core.entity.element.AppDict", "com.x.cms.core.entity.element.AppDictItem",
 		"com.x.cms.core.entity.element.Form", "com.x.cms.core.entity.element.FormField",
 		"com.x.cms.core.entity.element.QueryView", "com.x.cms.core.entity.element.Script",

+ 45 - 0
o2server/x_base_core_project/src/test/java/com/x/base/core/project/test/string/TestClient.java

@@ -0,0 +1,45 @@
+package com.x.base.core.project.test.string;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.x.base.core.project.tools.StringTools;
+
+import junit.framework.Assert;
+
+public class TestClient {
+
+	@Test
+	public void test() {
+		List<String> a = new ArrayList<>();
+		a.add("abc");
+		a.add("edf");
+		a.add("a");
+		a.add("bc");
+		a.add("ed");
+		a.add("f");
+		// System.out.println(StringTools.mergeEndsWith(a));
+	}
+
+	@Test
+	public void test1() {
+		System.out.println(StringTools.matchWildcard(null, null));
+		System.out.println(StringTools.matchWildcard("HELLO WORLD", "*ELLO*"));
+		Assert.assertFalse(StringTools.matchWildcard("HELLO WORLD", ""));
+		Assert.assertFalse(StringTools.matchWildcard("", "HELLO WORLD"));
+		Assert.assertFalse(StringTools.matchWildcard("HELLO WORLD", "ELLO"));
+		Assert.assertFalse(StringTools.matchWildcard("HELLO WORLD", "HELLO"));
+		Assert.assertFalse(StringTools.matchWildcard("HELLO WORLD", "*HELLO"));
+		Assert.assertFalse(StringTools.matchWildcard("HELLO WORLD", "HELLO WORLD2"));
+		Assert.assertFalse(StringTools.matchWildcard("HELLO WORLD", "HELLO WORL"));
+		Assert.assertFalse(StringTools.matchWildcard("HELLO WORLD", "hello world"));
+
+		Assert.assertTrue(StringTools.matchWildcard("HELLO WORLD", "*ELLO*"));
+		Assert.assertTrue(StringTools.matchWildcard("HELLO WORLD", "HELLO*"));
+		Assert.assertTrue(StringTools.matchWildcard("HELLO WORLD", "*LLO*"));
+		Assert.assertTrue(StringTools.matchWildcard("HELLO WORLD", "HELLO WORLD"));
+	}
+
+}

+ 20 - 3
o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/jaxrs/foruminfo/ActionGet.java

@@ -77,7 +77,7 @@ public class ActionGet extends BaseAction {
 			if( forumInfo != null ){
 				try {
 					wrap = Wo.copier.copy( forumInfo );
-					
+					wrap.setForumVisibleResult( wrap.getVisiblePermissionList() );
 					//TODO 为了不改变前端的逻辑,此处将List转为String进行输出,逗号分隔
 					wrap.setForumManagerName( wrap.transferStringListToString( wrap.getForumManagerList()) );
 					result.setData( wrap );
@@ -105,7 +105,11 @@ public class ActionGet extends BaseAction {
 		
 		public static WrapCopier< BBSForumInfo, Wo > copier = WrapCopierFactory.wo( BBSForumInfo.class, Wo.class, null, JpaObject.FieldsInvisible);
 		
+		@FieldDescribe("版块访问权限列表,用于接收参数.")
+		private String forumVisibleResult ;
+		
 		//论坛版块列表
+		@FieldDescribe("版块列表.")
 		private List<WoSectionInfo> sections = null;
 
 		public List<WoSectionInfo> getSections() {
@@ -124,14 +128,25 @@ public class ActionGet extends BaseAction {
 			this.forumManagerName = forumManagerName;
 		}
 		
+		public String getForumVisibleResult() {
+			return forumVisibleResult;
+		}
+		public void setForumVisibleResult(String forumVisibleResult) {
+			this.forumVisibleResult = forumVisibleResult;
+		}
+		
+		public void setForumVisibleResult( List<String> list ) {
+			this.forumVisibleResult = transferStringListToString(list);
+		}
+		
 		public String transferStringListToString( List<String> list ) {
 			StringBuffer sb = new StringBuffer();
 			if( ListTools.isNotEmpty( list )) {
 				for( String str : list ) {
 					if( StringUtils.isNotEmpty( sb.toString() )) {
 						sb.append(",");
-						sb.append(str);
 					}
+					sb.append(str);
 				}
 			}
 			return sb.toString();
@@ -143,6 +158,7 @@ public class ActionGet extends BaseAction {
 		private static final long serialVersionUID = -5076990764713538973L;
 		
 		//版块的子版块信息列表
+		@FieldDescribe("子版块列表.")
 		private List<WoSectionInfo> subSections = null;
 
 		public List<WoSectionInfo> getSubSections() {
@@ -150,7 +166,8 @@ public class ActionGet extends BaseAction {
 		}
 		public void setSubSections(List<WoSectionInfo> subSections) {
 			this.subSections = subSections;
-		}	
+		}
+		
 	}
 
 

+ 18 - 2
o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/jaxrs/foruminfo/ActionGetAll.java

@@ -80,6 +80,7 @@ public class ActionGetAll extends BaseAction {
 		if( check ){
 			if( ListTools.isNotEmpty( wraps ) ){
 				for( Wo wo : wraps ) {
+					wo.setForumVisibleResult( wo.getVisiblePermissionList() );
 					wo.setForumManagerName( wo.transferStringListToString( wo.getForumManagerList() ));
 				}
 			}
@@ -98,7 +99,11 @@ public class ActionGetAll extends BaseAction {
 		
 		public static WrapCopier< BBSForumInfo, Wo > copier = WrapCopierFactory.wo( BBSForumInfo.class, Wo.class, null, JpaObject.FieldsInvisible);
 		
+		@FieldDescribe("版块访问权限列表,用于接收参数.")
+		private String forumVisibleResult ;
+		
 		//论坛版块列表
+		@FieldDescribe("版块列表.")
 		private List<WoSectionInfo> sections = null;
 
 		public List<WoSectionInfo> getSections() {
@@ -108,7 +113,7 @@ public class ActionGetAll extends BaseAction {
 		public void setSections(List<WoSectionInfo> sections) {
 			this.sections = sections;
 		}
-		
+
 		public String getForumManagerName() {
 			return forumManagerName;
 		}
@@ -117,14 +122,25 @@ public class ActionGetAll extends BaseAction {
 			this.forumManagerName = forumManagerName;
 		}
 		
+		public String getForumVisibleResult() {
+			return forumVisibleResult;
+		}
+		public void setForumVisibleResult(String forumVisibleResult) {
+			this.forumVisibleResult = forumVisibleResult;
+		}
+		
+		public void setForumVisibleResult( List<String> list ) {
+			this.forumVisibleResult = transferStringListToString(list);
+		}
+		
 		public String transferStringListToString( List<String> list ) {
 			StringBuffer sb = new StringBuffer();
 			if( ListTools.isNotEmpty( list )) {
 				for( String str : list ) {
 					if( StringUtils.isNotEmpty( sb.toString() )) {
 						sb.append(",");
-						sb.append(str);
 					}
+					sb.append(str);
 				}
 			}
 			return sb.toString();

+ 30 - 2
o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/jaxrs/foruminfo/ActionGetAllWithPermission.java

@@ -18,6 +18,7 @@ import com.x.base.core.project.logger.LoggerFactory;
 import com.x.base.core.project.tools.ListTools;
 import com.x.bbs.assemble.control.ThisApplication;
 import com.x.bbs.assemble.control.jaxrs.MethodExcuteResult;
+import com.x.bbs.assemble.control.jaxrs.foruminfo.ActionGetAll.WoSectionInfo;
 import com.x.bbs.assemble.control.jaxrs.foruminfo.exception.ExceptionForumInfoProcess;
 import com.x.bbs.entity.BBSForumInfo;
 
@@ -125,6 +126,7 @@ public class ActionGetAllWithPermission extends BaseAction {
 		if( check ){
 			if( ListTools.isNotEmpty( wraps ) ){
 				for( Wo wo : wraps ) {
+					wo.setForumVisibleResult( wo.getVisiblePermissionList() );
 					wo.setForumManagerName( wo.transferStringListToString( wo.getForumManagerList() ));
 				}
 			}
@@ -146,7 +148,7 @@ public class ActionGetAllWithPermission extends BaseAction {
 		return sb.toString();
 	}
 
-public static class Wo extends BBSForumInfo{
+	public static class Wo extends BBSForumInfo{
 		
 		@FieldDescribe("字符串形式输出的管理员信息,逗号(,)分隔.")
 		private String forumManagerName = null;
@@ -155,6 +157,21 @@ public static class Wo extends BBSForumInfo{
 		
 		public static WrapCopier< BBSForumInfo, Wo > copier = WrapCopierFactory.wo( BBSForumInfo.class, Wo.class, null, JpaObject.FieldsInvisible);
 		
+		@FieldDescribe("版块访问权限列表,用于接收参数.")
+		private String forumVisibleResult ;
+		
+		//论坛版块列表
+		@FieldDescribe("版块列表.")
+		private List<WoSectionInfo> sections = null;
+
+		public List<WoSectionInfo> getSections() {
+			return sections;
+		}
+
+		public void setSections(List<WoSectionInfo> sections) {
+			this.sections = sections;
+		}
+
 		public String getForumManagerName() {
 			return forumManagerName;
 		}
@@ -163,14 +180,25 @@ public static class Wo extends BBSForumInfo{
 			this.forumManagerName = forumManagerName;
 		}
 		
+		public String getForumVisibleResult() {
+			return forumVisibleResult;
+		}
+		public void setForumVisibleResult(String forumVisibleResult) {
+			this.forumVisibleResult = forumVisibleResult;
+		}
+		
+		public void setForumVisibleResult( List<String> list ) {
+			this.forumVisibleResult = transferStringListToString(list);
+		}
+		
 		public String transferStringListToString( List<String> list ) {
 			StringBuffer sb = new StringBuffer();
 			if( ListTools.isNotEmpty( list )) {
 				for( String str : list ) {
 					if( StringUtils.isNotEmpty( sb.toString() )) {
 						sb.append(",");
-						sb.append(str);
 					}
+					sb.append(str);
 				}
 			}
 			return sb.toString();

+ 32 - 3
o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/jaxrs/foruminfo/ActionSave.java

@@ -1,6 +1,7 @@
 package com.x.bbs.assemble.control.jaxrs.foruminfo;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 import javax.servlet.http.HttpServletRequest;
@@ -9,6 +10,7 @@ import org.apache.commons.lang3.StringUtils;
 
 import com.google.gson.JsonElement;
 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;
@@ -151,8 +153,22 @@ public class ActionSave extends BaseAction {
 		}
 		
 		if (check) {
-			try {
-				forumInfo = Wi.copier.copy(wrapIn);
+			forumInfo = Wi.copier.copy(wrapIn);
+			
+			List<String> arrayList = new ArrayList<>(); 
+			if( StringUtils.equals( wrapIn.getForumVisible(), "根据权限" )) {
+				if( StringUtils.isNotEmpty( wrapIn.getForumVisibleResult())) {
+					arrayList.clear();
+					Collections.addAll(arrayList, wrapIn.getForumVisibleResult().split( "," ));
+					forumInfo.setVisiblePermissionList( arrayList );
+				}
+			}else {
+				forumInfo.setVisiblePermissionList( new ArrayList<>() );
+			}
+		}
+		
+		if (check) {
+			try {				
 				forumInfo.setForumManagerList( forumManagerList );
 				if( StringUtils.isNotEmpty( wrapIn.getId() )) {
 					forumInfo.setId( wrapIn.getId() );
@@ -219,8 +235,12 @@ public class ActionSave extends BaseAction {
 
 	public static class Wi extends BBSForumInfo {
 		
+		@FieldDescribe("论坛管理员.")
 		private String forumManagerName = null;
 
+		@FieldDescribe("论坛可见范围.")
+		private String forumVisibleResult ;
+		
 		private static final long serialVersionUID = -5076990764713538973L;
 
 		public static WrapCopier<Wi, BBSForumInfo> copier = WrapCopierFactory.wi(Wi.class, BBSForumInfo.class, null,
@@ -232,7 +252,16 @@ public class ActionSave extends BaseAction {
 
 		public void setForumManagerName(String forumManagerName) {
 			this.forumManagerName = forumManagerName;
-		}		
+		}
+
+		public String getForumVisibleResult() {
+			return forumVisibleResult;
+		}
+
+		public void setForumVisibleResult(String forumVisibleResult) {
+			this.forumVisibleResult = forumVisibleResult;
+		}
+		
 	}
 
 	public static class Wo extends WoId {

+ 62 - 1
o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/jaxrs/sectioninfo/ActionAllListSubSectionByMainSectionId.java

@@ -5,13 +5,17 @@ import java.util.List;
 
 import javax.servlet.http.HttpServletRequest;
 
+import org.apache.commons.lang3.StringUtils;
+
 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.http.ActionResult;
 import com.x.base.core.project.http.EffectivePerson;
 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.bbs.assemble.control.jaxrs.sectioninfo.exception.ExceptionSectionIdEmpty;
 import com.x.bbs.assemble.control.jaxrs.sectioninfo.exception.ExceptionSectionInfoProcess;
 import com.x.bbs.assemble.control.jaxrs.sectioninfo.exception.ExceptionSectionNotExists;
@@ -68,6 +72,13 @@ public class ActionAllListSubSectionByMainSectionId extends BaseAction {
 			if( sectionInfoList != null && sectionInfoList.size() > 0 ){
 				try {
 					wraps = Wo.copier.copy( sectionInfoList );
+					if( ListTools.isNotEmpty( wraps )) {
+						for( Wo wrap : wraps ) {
+							wrap.setSectionVisibleResult( wrap.getVisiblePermissionList() );
+							wrap.setSubjectPublishResult( wrap.getPublishPermissionList() );
+							wrap.setReplyPublishResult( wrap.getReplyPermissionList() );
+						}	
+					}
 					result.setData(wraps);
 				} catch (Exception e) {
 					Exception exception = new ExceptionSectionInfoProcess( e, "系统在转换所有BBS版块信息为输出对象时发生异常." );
@@ -86,7 +97,17 @@ public class ActionAllListSubSectionByMainSectionId extends BaseAction {
 		
 		public static WrapCopier< BBSSectionInfo, Wo > copier = WrapCopierFactory.wo( BBSSectionInfo.class, Wo.class, null, JpaObject.FieldsInvisible);
 		
+		@FieldDescribe("版块访问权限列表,用于接收参数.")
+		private String sectionVisibleResult ;
+		
+		@FieldDescribe("版块访问权限列表,用于接收参数.")
+		private String replyPublishResult ;
+		
+		@FieldDescribe("版块访问权限列表,用于接收参数.")
+		private String subjectPublishResult ;
+		
 		//版块的子版块信息列表
+		@FieldDescribe("子版块列表.")
 		private List<Wo> subSections = null;
 
 		public List<Wo> getSubSections() {
@@ -94,6 +115,46 @@ public class ActionAllListSubSectionByMainSectionId extends BaseAction {
 		}
 		public void setSubSections(List<Wo> subSections) {
 			this.subSections = subSections;
-		}	
+		}
+		public String getSectionVisibleResult() {
+			return sectionVisibleResult;
+		}
+		public void setSectionVisibleResult(String sectionVisibleResult) {
+			this.sectionVisibleResult = sectionVisibleResult;
+		}
+		public String getReplyPublishResult() {
+			return replyPublishResult;
+		}
+		public void setReplyPublishResult(String replyPublishResult) {
+			this.replyPublishResult = replyPublishResult;
+		}
+		public String getSubjectPublishResult() {
+			return subjectPublishResult;
+		}
+		public void setSubjectPublishResult(String subjectPublishResult) {
+			this.subjectPublishResult = subjectPublishResult;
+		}
+		
+		public void setSubjectPublishResult(List<String> list) {
+			this.subjectPublishResult = transferStringListToString(list);
+		}
+		public void setReplyPublishResult(List<String> list) {
+			this.replyPublishResult = transferStringListToString(list);
+		}
+		public void setSectionVisibleResult(List<String> list) {
+			this.sectionVisibleResult = transferStringListToString(list);
+		}
+		public String transferStringListToString( List<String> list ) {
+			StringBuffer sb = new StringBuffer();
+			if( ListTools.isNotEmpty( list )) {
+				for( String str : list ) {
+					if( StringUtils.isNotEmpty( sb.toString() )) {
+						sb.append(",");
+					}
+					sb.append(str);
+				}
+			}
+			return sb.toString();
+		}
 	}
 }

+ 62 - 1
o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/jaxrs/sectioninfo/ActionAllSections.java

@@ -5,13 +5,17 @@ import java.util.List;
 
 import javax.servlet.http.HttpServletRequest;
 
+import org.apache.commons.lang3.StringUtils;
+
 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.http.ActionResult;
 import com.x.base.core.project.http.EffectivePerson;
 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.bbs.assemble.control.jaxrs.sectioninfo.exception.ExceptionSectionInfoProcess;
 import com.x.bbs.entity.BBSSectionInfo;
 
@@ -42,6 +46,13 @@ public class ActionAllSections extends BaseAction {
 			if( sectionInfoList != null && sectionInfoList.size() > 0 ){
 				try {
 					wraps = Wo.copier.copy( sectionInfoList );
+					if( ListTools.isNotEmpty( wraps )) {
+						for( Wo wrap : wraps ) {
+							wrap.setSectionVisibleResult( wrap.getVisiblePermissionList() );
+							wrap.setSubjectPublishResult( wrap.getPublishPermissionList() );
+							wrap.setReplyPublishResult( wrap.getReplyPermissionList() );
+						}	
+					}
 					result.setData(wraps);
 				} catch (Exception e) {
 					Exception exception = new ExceptionSectionInfoProcess( e, "系统在转换所有BBS版块信息为输出对象时发生异常." );
@@ -61,7 +72,17 @@ public class ActionAllSections extends BaseAction {
 		
 		public static WrapCopier< BBSSectionInfo, Wo > copier = WrapCopierFactory.wo( BBSSectionInfo.class, Wo.class, null, JpaObject.FieldsInvisible);
 		
+		@FieldDescribe("版块访问权限列表,用于接收参数.")
+		private String sectionVisibleResult ;
+		
+		@FieldDescribe("版块访问权限列表,用于接收参数.")
+		private String replyPublishResult ;
+		
+		@FieldDescribe("版块访问权限列表,用于接收参数.")
+		private String subjectPublishResult ;
+		
 		//版块的子版块信息列表
+		@FieldDescribe("子版块列表.")
 		private List<Wo> subSections = null;
 
 		public List<Wo> getSubSections() {
@@ -69,7 +90,47 @@ public class ActionAllSections extends BaseAction {
 		}
 		public void setSubSections(List<Wo> subSections) {
 			this.subSections = subSections;
-		}	
+		}
+		public String getSectionVisibleResult() {
+			return sectionVisibleResult;
+		}
+		public void setSectionVisibleResult(String sectionVisibleResult) {
+			this.sectionVisibleResult = sectionVisibleResult;
+		}
+		public String getReplyPublishResult() {
+			return replyPublishResult;
+		}
+		public void setReplyPublishResult(String replyPublishResult) {
+			this.replyPublishResult = replyPublishResult;
+		}
+		public String getSubjectPublishResult() {
+			return subjectPublishResult;
+		}
+		public void setSubjectPublishResult(String subjectPublishResult) {
+			this.subjectPublishResult = subjectPublishResult;
+		}
+		
+		public void setSubjectPublishResult(List<String> list) {
+			this.subjectPublishResult = transferStringListToString(list);
+		}
+		public void setReplyPublishResult(List<String> list) {
+			this.replyPublishResult = transferStringListToString(list);
+		}
+		public void setSectionVisibleResult(List<String> list) {
+			this.sectionVisibleResult = transferStringListToString(list);
+		}
+		public String transferStringListToString( List<String> list ) {
+			StringBuffer sb = new StringBuffer();
+			if( ListTools.isNotEmpty( list )) {
+				for( String str : list ) {
+					if( StringUtils.isNotEmpty( sb.toString() )) {
+						sb.append(",");
+					}
+					sb.append(str);
+				}
+			}
+			return sb.toString();
+		}
 	}
 
 }

+ 58 - 1
o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/jaxrs/sectioninfo/ActionGet.java

@@ -5,13 +5,17 @@ import java.util.List;
 
 import javax.servlet.http.HttpServletRequest;
 
+import org.apache.commons.lang3.StringUtils;
+
 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.http.ActionResult;
 import com.x.base.core.project.http.EffectivePerson;
 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.bbs.assemble.control.jaxrs.sectioninfo.exception.ExceptionSectionIdEmpty;
 import com.x.bbs.assemble.control.jaxrs.sectioninfo.exception.ExceptionSectionInfoProcess;
 import com.x.bbs.assemble.control.jaxrs.sectioninfo.exception.ExceptionSectionNotExists;
@@ -72,6 +76,9 @@ public class ActionGet extends BaseAction {
 			if( sectionInfo != null ){
 				try {
 					wrap = Wo.copier.copy( sectionInfo );
+					wrap.setSectionVisibleResult( wrap.getVisiblePermissionList() );
+					wrap.setSubjectPublishResult( wrap.getPublishPermissionList() );
+					wrap.setReplyPublishResult( wrap.getReplyPermissionList() );
 					result.setData( wrap );
 				} catch (Exception e) {
 					check = false;
@@ -95,7 +102,17 @@ public class ActionGet extends BaseAction {
 		
 		public static WrapCopier< BBSSectionInfo, Wo > copier = WrapCopierFactory.wo( BBSSectionInfo.class, Wo.class, null, JpaObject.FieldsInvisible);
 		
+		@FieldDescribe("版块访问权限列表,用于接收参数.")
+		private String sectionVisibleResult ;
+		
+		@FieldDescribe("版块访问权限列表,用于接收参数.")
+		private String replyPublishResult ;
+		
+		@FieldDescribe("版块访问权限列表,用于接收参数.")
+		private String subjectPublishResult ;
+		
 		//版块的子版块信息列表
+		@FieldDescribe("子版块列表.")
 		private List<Wo> subSections = null;
 
 		public List<Wo> getSubSections() {
@@ -103,6 +120,46 @@ public class ActionGet extends BaseAction {
 		}
 		public void setSubSections(List<Wo> subSections) {
 			this.subSections = subSections;
-		}	
+		}
+		public String getSectionVisibleResult() {
+			return sectionVisibleResult;
+		}
+		public void setSectionVisibleResult(String sectionVisibleResult) {
+			this.sectionVisibleResult = sectionVisibleResult;
+		}
+		public String getReplyPublishResult() {
+			return replyPublishResult;
+		}
+		public void setReplyPublishResult(String replyPublishResult) {
+			this.replyPublishResult = replyPublishResult;
+		}
+		public String getSubjectPublishResult() {
+			return subjectPublishResult;
+		}
+		public void setSubjectPublishResult(String subjectPublishResult) {
+			this.subjectPublishResult = subjectPublishResult;
+		}
+		
+		public void setSubjectPublishResult(List<String> list) {
+			this.subjectPublishResult = transferStringListToString(list);
+		}
+		public void setReplyPublishResult(List<String> list) {
+			this.replyPublishResult = transferStringListToString(list);
+		}
+		public void setSectionVisibleResult(List<String> list) {
+			this.sectionVisibleResult = transferStringListToString(list);
+		}
+		public String transferStringListToString( List<String> list ) {
+			StringBuffer sb = new StringBuffer();
+			if( ListTools.isNotEmpty( list )) {
+				for( String str : list ) {
+					if( StringUtils.isNotEmpty( sb.toString() )) {
+						sb.append(",");
+					}
+					sb.append(str);
+				}
+			}
+			return sb.toString();
+		}
 	}
 }

+ 62 - 1
o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/jaxrs/sectioninfo/ActionListSubSectionByMainSectionId.java

@@ -5,13 +5,17 @@ import java.util.List;
 
 import javax.servlet.http.HttpServletRequest;
 
+import org.apache.commons.lang3.StringUtils;
+
 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.http.ActionResult;
 import com.x.base.core.project.http.EffectivePerson;
 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.bbs.assemble.control.jaxrs.MethodExcuteResult;
 import com.x.bbs.assemble.control.jaxrs.sectioninfo.exception.ExceptionSectionIdEmpty;
 import com.x.bbs.assemble.control.jaxrs.sectioninfo.exception.ExceptionSectionInfoProcess;
@@ -83,6 +87,13 @@ public class ActionListSubSectionByMainSectionId extends BaseAction {
 			if( sectionInfoList != null && sectionInfoList.size() > 0 ){
 				try {
 					wraps = Wo.copier.copy( sectionInfoList );
+					if( ListTools.isNotEmpty( wraps )) {
+						for( Wo wrap : wraps ) {
+							wrap.setSectionVisibleResult( wrap.getVisiblePermissionList() );
+							wrap.setSubjectPublishResult( wrap.getPublishPermissionList() );
+							wrap.setReplyPublishResult( wrap.getReplyPermissionList() );
+						}	
+					}
 					result.setData(wraps);
 				} catch (Exception e) {
 					Exception exception = new ExceptionSectionInfoProcess( e, "系统在转换所有BBS版块信息为输出对象时发生异常." );
@@ -102,7 +113,17 @@ public class ActionListSubSectionByMainSectionId extends BaseAction {
 		
 		public static WrapCopier< BBSSectionInfo, Wo > copier = WrapCopierFactory.wo( BBSSectionInfo.class, Wo.class, null, JpaObject.FieldsInvisible);
 		
+		@FieldDescribe("版块访问权限列表,用于接收参数.")
+		private String sectionVisibleResult ;
+		
+		@FieldDescribe("版块访问权限列表,用于接收参数.")
+		private String replyPublishResult ;
+		
+		@FieldDescribe("版块访问权限列表,用于接收参数.")
+		private String subjectPublishResult ;
+		
 		//版块的子版块信息列表
+		@FieldDescribe("子版块列表.")
 		private List<Wo> subSections = null;
 
 		public List<Wo> getSubSections() {
@@ -110,6 +131,46 @@ public class ActionListSubSectionByMainSectionId extends BaseAction {
 		}
 		public void setSubSections(List<Wo> subSections) {
 			this.subSections = subSections;
-		}	
+		}
+		public String getSectionVisibleResult() {
+			return sectionVisibleResult;
+		}
+		public void setSectionVisibleResult(String sectionVisibleResult) {
+			this.sectionVisibleResult = sectionVisibleResult;
+		}
+		public String getReplyPublishResult() {
+			return replyPublishResult;
+		}
+		public void setReplyPublishResult(String replyPublishResult) {
+			this.replyPublishResult = replyPublishResult;
+		}
+		public String getSubjectPublishResult() {
+			return subjectPublishResult;
+		}
+		public void setSubjectPublishResult(String subjectPublishResult) {
+			this.subjectPublishResult = subjectPublishResult;
+		}
+		
+		public void setSubjectPublishResult(List<String> list) {
+			this.subjectPublishResult = transferStringListToString(list);
+		}
+		public void setReplyPublishResult(List<String> list) {
+			this.replyPublishResult = transferStringListToString(list);
+		}
+		public void setSectionVisibleResult(List<String> list) {
+			this.sectionVisibleResult = transferStringListToString(list);
+		}
+		public String transferStringListToString( List<String> list ) {
+			StringBuffer sb = new StringBuffer();
+			if( ListTools.isNotEmpty( list )) {
+				for( String str : list ) {
+					if( StringUtils.isNotEmpty( sb.toString() )) {
+						sb.append(",");
+					}
+					sb.append(str);
+				}
+			}
+			return sb.toString();
+		}
 	}
 }

+ 62 - 1
o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/jaxrs/sectioninfo/ActionListWithForum.java

@@ -5,13 +5,17 @@ import java.util.List;
 
 import javax.servlet.http.HttpServletRequest;
 
+import org.apache.commons.lang3.StringUtils;
+
 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.http.ActionResult;
 import com.x.base.core.project.http.EffectivePerson;
 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.bbs.assemble.control.jaxrs.sectioninfo.exception.ExceptionForumIdEmpty;
 import com.x.bbs.assemble.control.jaxrs.sectioninfo.exception.ExceptionForumInfoNotExists;
 import com.x.bbs.assemble.control.jaxrs.sectioninfo.exception.ExceptionSectionInfoProcess;
@@ -70,6 +74,13 @@ public class ActionListWithForum extends BaseAction {
 			if( sectionInfoList != null && sectionInfoList.size() > 0 ){
 				try {
 					wraps = Wo.copier.copy( sectionInfoList );
+					if( ListTools.isNotEmpty( wraps )) {
+						for( Wo wrap : wraps ) {
+							wrap.setSectionVisibleResult( wrap.getVisiblePermissionList() );
+							wrap.setSubjectPublishResult( wrap.getPublishPermissionList() );
+							wrap.setReplyPublishResult( wrap.getReplyPermissionList() );
+						}	
+					}
 					result.setData(wraps);
 				} catch (Exception e) {
 					Exception exception = new ExceptionSectionInfoProcess( e, "系统在转换所有BBS版块信息为输出对象时发生异常." );
@@ -89,7 +100,17 @@ public class ActionListWithForum extends BaseAction {
 		
 		public static WrapCopier< BBSSectionInfo, Wo > copier = WrapCopierFactory.wo( BBSSectionInfo.class, Wo.class, null, JpaObject.FieldsInvisible);
 		
+		@FieldDescribe("版块访问权限列表,用于接收参数.")
+		private String sectionVisibleResult ;
+		
+		@FieldDescribe("版块访问权限列表,用于接收参数.")
+		private String replyPublishResult ;
+		
+		@FieldDescribe("版块访问权限列表,用于接收参数.")
+		private String subjectPublishResult ;
+		
 		//版块的子版块信息列表
+		@FieldDescribe("子版块列表.")
 		private List<Wo> subSections = null;
 
 		public List<Wo> getSubSections() {
@@ -97,6 +118,46 @@ public class ActionListWithForum extends BaseAction {
 		}
 		public void setSubSections(List<Wo> subSections) {
 			this.subSections = subSections;
-		}	
+		}
+		public String getSectionVisibleResult() {
+			return sectionVisibleResult;
+		}
+		public void setSectionVisibleResult(String sectionVisibleResult) {
+			this.sectionVisibleResult = sectionVisibleResult;
+		}
+		public String getReplyPublishResult() {
+			return replyPublishResult;
+		}
+		public void setReplyPublishResult(String replyPublishResult) {
+			this.replyPublishResult = replyPublishResult;
+		}
+		public String getSubjectPublishResult() {
+			return subjectPublishResult;
+		}
+		public void setSubjectPublishResult(String subjectPublishResult) {
+			this.subjectPublishResult = subjectPublishResult;
+		}
+		
+		public void setSubjectPublishResult(List<String> list) {
+			this.subjectPublishResult = transferStringListToString(list);
+		}
+		public void setReplyPublishResult(List<String> list) {
+			this.replyPublishResult = transferStringListToString(list);
+		}
+		public void setSectionVisibleResult(List<String> list) {
+			this.sectionVisibleResult = transferStringListToString(list);
+		}
+		public String transferStringListToString( List<String> list ) {
+			StringBuffer sb = new StringBuffer();
+			if( ListTools.isNotEmpty( list )) {
+				for( String str : list ) {
+					if( StringUtils.isNotEmpty( sb.toString() )) {
+						sb.append(",");
+					}
+					sb.append(str);
+				}
+			}
+			return sb.toString();
+		}
 	}
 }

+ 68 - 0
o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/jaxrs/sectioninfo/ActionSave.java

@@ -1,6 +1,7 @@
 package com.x.bbs.assemble.control.jaxrs.sectioninfo;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 
@@ -10,6 +11,7 @@ import org.apache.commons.lang3.StringUtils;
 
 import com.google.gson.JsonElement;
 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;
@@ -219,6 +221,39 @@ public class ActionSave extends BaseAction {
 			}
 		}
 
+		if (check) {
+			List<String> arrayList = new ArrayList<>(); 
+			if( StringUtils.equals( wrapIn.getSectionVisible(), "根据权限" )) {
+				if( StringUtils.isNotEmpty( wrapIn.getSectionVisibleResult() )) {
+					arrayList.clear();
+					Collections.addAll(arrayList, wrapIn.getSectionVisibleResult().split( "," ));
+					sectionInfo.setVisiblePermissionList( arrayList );
+				}
+			}else {
+				sectionInfo.setVisiblePermissionList( new ArrayList<>() );
+			}
+			
+			if( StringUtils.equals( wrapIn.getReplyPublishAble(), "根据权限" )) {
+				if( StringUtils.isNotEmpty( wrapIn.getReplyPublishResult() )) {
+					arrayList.clear();
+					Collections.addAll(arrayList, wrapIn.getReplyPublishResult().split( "," ));
+					sectionInfo.setReplyPermissionList( arrayList );
+				}
+			}else {
+				sectionInfo.setReplyPermissionList( new ArrayList<>() );
+			}
+
+			if( StringUtils.equals( wrapIn.getSubjectPublishAble(), "根据权限" )) {
+				if( StringUtils.isNotEmpty( wrapIn.getSubjectPublishResult() )) {
+					arrayList.clear();
+					Collections.addAll(arrayList, wrapIn.getSubjectPublishResult().split( "," ));
+					sectionInfo.setPublishPermissionList( arrayList );
+				}
+			}else {
+				sectionInfo.setPublishPermissionList( new ArrayList<>() );
+			}
+		}
+		
 		if (check) {
 			try {
 				if( sectionInfo.getCreateTime() == null ) {
@@ -340,6 +375,39 @@ public class ActionSave extends BaseAction {
 		public static List<String> Excludes = new ArrayList<String>();
 		
 		public static WrapCopier< Wi, BBSSectionInfo > copier = WrapCopierFactory.wi( Wi.class, BBSSectionInfo.class, null, JpaObject.FieldsUnmodify);
+		
+		@FieldDescribe("版块访问权限列表,用于接收参数.")
+		private String sectionVisibleResult ;
+		
+		@FieldDescribe("版块访问权限列表,用于接收参数.")
+		private String replyPublishResult ;
+		
+		@FieldDescribe("版块访问权限列表,用于接收参数.")
+		private String subjectPublishResult ;
+
+		public String getSectionVisibleResult() {
+			return sectionVisibleResult;
+		}
+
+		public void setSectionVisibleResult(String sectionVisibleResult) {
+			this.sectionVisibleResult = sectionVisibleResult;
+		}
+
+		public String getReplyPublishResult() {
+			return replyPublishResult;
+		}
+
+		public void setReplyPublishResult(String replyPublishResult) {
+			this.replyPublishResult = replyPublishResult;
+		}
+
+		public String getSubjectPublishResult() {
+			return subjectPublishResult;
+		}
+
+		public void setSubjectPublishResult(String subjectPublishResult) {
+			this.subjectPublishResult = subjectPublishResult;
+		}
 	}
 
 	

+ 79 - 75
o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/jaxrs/subjectinfo/ActionSubjectView.java

@@ -48,7 +48,7 @@ public class ActionSubjectView extends BaseAction {
 		}
 		
 		if( check ){
-			String cacheKey = "subject#view#" + id;
+			String cacheKey = "subject#view#"+ id;
 			Element element = cache.get( cacheKey );
 			if ((null != element) && (null != element.getObjectValue())) {
 				ActionResult<Wo> result_cache = (ActionResult<Wo>) element.getObjectValue();
@@ -56,11 +56,87 @@ public class ActionSubjectView extends BaseAction {
 				result.setCount( 1L);
 			} else {
 				//继续进行数据查询
-				result = getSubjectViewQueryResult( id, request, effectivePerson );
+				result = getSubjectViewQueryResult( id, request, effectivePerson );				
 				cache.put(new Element(cacheKey, result ));
 			}
 		}
 		
+		if( check && result.getData() != null ) {
+			WoBBSSubjectInfo currentSubject = result.getData().getCurrentSubject();
+			if (check) {
+				if( currentSubject != null ){//获取该主题的投票选项组
+					List<BBSVoteOption> voteOptionList = null;
+					List<BBSVoteOptionGroup> voteOptionGroupList = null;
+					List<WoBBSVoteOptionGroup> wrapOutSubjectVoteOptionGroupList = null;
+					List<WoBBSVoteOption> wrapOutSubjectVoteOptionList = null;
+					
+					currentSubject.setVoted( false );
+					
+					try {
+						voteOptionGroupList = subjectVoteService.listVoteOptionGroup( id );
+						if( ListTools.isNotEmpty( voteOptionGroupList ) ){
+							wrapOutSubjectVoteOptionGroupList = WoBBSVoteOptionGroup.copier.copy( voteOptionGroupList );
+							for( WoBBSVoteOptionGroup group : wrapOutSubjectVoteOptionGroupList ){
+								voteOptionList = subjectVoteService.listVoteOptionByGroupId( group.getId() );
+								if( ListTools.isNotEmpty( voteOptionList ) ){
+									try {
+										wrapOutSubjectVoteOptionList = WoBBSVoteOption.copier.copy( voteOptionList );
+										for( WoBBSVoteOption wrapOutBBSVoteOption: wrapOutSubjectVoteOptionList ){
+											wrapOutBBSVoteOption.setVoted( false );
+											try {
+												if( subjectVoteService.optionHasVoted( effectivePerson, wrapOutBBSVoteOption.getId() )){
+													//判断是否已经投过票
+													wrapOutBBSVoteOption.setVoted( true );
+													currentSubject.setVoted( true );
+												}
+											} catch (Exception e) {
+												check = false;
+												Exception exception = new ExceptionVoteResultQueryById( e, id );
+												result.error( exception );
+												logger.error( e, effectivePerson, request, null);
+											}
+										}
+										group.setVoteOptions( wrapOutSubjectVoteOptionList );
+									} catch (Exception e) {
+										check = false;
+										Exception exception = new ExceptionSubjectWrapOut( e );
+										result.error( exception );
+										logger.error( e, effectivePerson, request, null);
+									}
+								}
+							}
+							currentSubject.setVoteOptionGroupList( wrapOutSubjectVoteOptionGroupList );
+						}
+					} catch (Exception e) {
+						check = false;
+						Exception exception = new ExceptionVoteOptionListById( e, id );
+						result.error( exception );
+						logger.error( e, effectivePerson, request, null);
+					}
+					try {
+						//查询投票总人数
+						Long voteCount = subjectVoteService.countVoteRecordForSubject( id, null );
+						currentSubject.setVoteCount( voteCount );
+					}catch (Exception e) {
+						check = false;
+						Exception exception = new ExceptionVoteOptionListById( e, id );
+						result.error( exception );
+						logger.error( e, effectivePerson, request, null);
+					}
+					
+					try {
+						voteOptionList = subjectVoteService.listVoteOption( id );
+					} catch (Exception e) {
+						check = false;
+						Exception exception = new ExceptionVoteOptionListById( e, id );
+						result.error( exception );
+						logger.error( e, effectivePerson, request, null);
+					}
+					result.getData().setCurrentSubject(currentSubject);
+				}
+			}
+		}
+		
 		if( check ){
 			try {
 				// 查看次数+1
@@ -84,10 +160,6 @@ public class ActionSubjectView extends BaseAction {
 		WoBBSSubjectInfo currentSubject = null;
 		WoBBSSubjectInfo nextSubject = null;
 		BBSSubjectInfo subjectInfo = null;
-		List<BBSVoteOption> voteOptionList = null;
-		List<BBSVoteOptionGroup> voteOptionGroupList = null;
-		List<WoBBSVoteOptionGroup> wrapOutSubjectVoteOptionGroupList = null;
-		List<WoBBSVoteOption> wrapOutSubjectVoteOptionList = null;
 		String subjectContent = null;
 		Boolean check = true;
 		
@@ -185,75 +257,7 @@ public class ActionSubjectView extends BaseAction {
 				nextSubject.setTitle( subjectInfo.getTitle() );
 				wrapOutNearSubjectInfo.setNextSubject( nextSubject );
 			}
-		}
-		
-		if (check) {
-			if( currentSubject != null ){//获取该主题的投票选项
-				try {
-					voteOptionList = subjectVoteService.listVoteOption( id );
-				} catch (Exception e) {
-					check = false;
-					Exception exception = new ExceptionVoteOptionListById( e, id );
-					result.error( exception );
-					logger.error( e, effectivePerson, request, null);
-				}
-			}
-		}
-		
-		if (check) {
-			if( currentSubject != null ){//获取该主题的投票选项组
-				try {
-					voteOptionGroupList = subjectVoteService.listVoteOptionGroup( id );
-					if( ListTools.isNotEmpty( voteOptionGroupList ) ){
-						wrapOutSubjectVoteOptionGroupList = WoBBSVoteOptionGroup.copier.copy( voteOptionGroupList );
-						for( WoBBSVoteOptionGroup group : wrapOutSubjectVoteOptionGroupList ){
-							voteOptionList = subjectVoteService.listVoteOptionByGroupId( group.getId() );
-							if( ListTools.isNotEmpty( voteOptionList ) ){
-								try {
-									wrapOutSubjectVoteOptionList = WoBBSVoteOption.copier.copy( voteOptionList );
-									for( WoBBSVoteOption wrapOutBBSVoteOption: wrapOutSubjectVoteOptionList ){
-										try {
-											if( subjectVoteService.optionHasVoted( effectivePerson, wrapOutBBSVoteOption.getId() )){
-												wrapOutBBSVoteOption.setVoted( true );
-												currentSubject.setVoted( true );
-											}
-										} catch (Exception e) {
-											check = false;
-											Exception exception = new ExceptionVoteResultQueryById( e, id );
-											result.error( exception );
-											logger.error( e, effectivePerson, request, null);
-										}
-									}
-									group.setVoteOptions( wrapOutSubjectVoteOptionList );
-								} catch (Exception e) {
-									check = false;
-									Exception exception = new ExceptionSubjectWrapOut( e );
-									result.error( exception );
-									logger.error( e, effectivePerson, request, null);
-								}
-							}
-						}
-						currentSubject.setVoteOptionGroupList( wrapOutSubjectVoteOptionGroupList );
-					}
-				} catch (Exception e) {
-					check = false;
-					Exception exception = new ExceptionVoteOptionListById( e, id );
-					result.error( exception );
-					logger.error( e, effectivePerson, request, null);
-				}
-				try {
-					//查询投票总人数
-					Long voteCount = subjectVoteService.countVoteRecordForSubject( id, null );
-					currentSubject.setVoteCount( voteCount );
-				}catch (Exception e) {
-					check = false;
-					Exception exception = new ExceptionVoteOptionListById( e, id );
-					result.error( exception );
-					logger.error( e, effectivePerson, request, null);
-				}
-			}
-		}
-		
+		}		
 		//将带@形式的人员标识修改为人员的姓名并且赋值到xxShort属性里
 		cutPersonNames( wrapOutNearSubjectInfo.getCurrentSubject() );
 		

+ 1 - 1
o2server/x_bbs_assemble_control/src/main/java/com/x/bbs/assemble/control/service/UserPermissionService.java

@@ -271,7 +271,7 @@ public class UserPermissionService {
 			}
 			if ( methodExcuteResult.getSuccess() ) {// 获取用户可以访问的论坛相关权限
 				try {
-					forumViewPermissionList = permissionInfoService.filterPermissionListByPermissionFunction("FORUM_VIEW", permissionList);
+					forumViewPermissionList = permissionInfoService.filterPermissionListByPermissionFunction("FORUM_VIEW", permissionList );
 				} catch (Exception e) {
 					methodExcuteResult.setSuccess(false);
 					methodExcuteResult.error(e);

+ 88 - 0
o2server/x_bbs_core_entity/src/main/java/com/x/bbs/entity/BBSForumInfo.java

@@ -21,6 +21,7 @@ import org.apache.openjpa.persistence.jdbc.ElementColumn;
 import org.apache.openjpa.persistence.jdbc.ElementIndex;
 import org.apache.openjpa.persistence.jdbc.Index;
 
+import com.x.base.core.entity.AbstractPersistenceProperties;
 import com.x.base.core.entity.JpaObject;
 import com.x.base.core.entity.SliceJpaObject;
 import com.x.base.core.entity.annotation.CheckPersist;
@@ -105,6 +106,17 @@ public class BBSForumInfo extends SliceJpaObject {
 	@Index(name = TABLE + IndexNameMiddle + forumVisible_FIELDNAME)
 	@CheckPersist(allowEmpty = false)
 	private String forumVisible = "所有人";
+	
+	public static final String visiblePermissionList_FIELDNAME = "visiblePermissionList";
+	@FieldDescribe("版块可见范围")
+	@PersistentCollection( fetch = FetchType.EAGER )
+	@OrderColumn( name = ORDERCOLUMNCOLUMN )
+	@ContainerTable(name = TABLE + ContainerTableNameMiddle + visiblePermissionList_FIELDNAME, joinIndex = @Index( name = TABLE
+			+ IndexNameMiddle + visiblePermissionList_FIELDNAME + JoinIndexNameSuffix ))
+	@ElementColumn( length = AbstractPersistenceProperties.organization_name_length, name = ColumnNamePrefix + visiblePermissionList_FIELDNAME )
+	@ElementIndex( name = TABLE + IndexNameMiddle + visiblePermissionList_FIELDNAME + ElementIndexNameSuffix )
+	@CheckPersist( allowEmpty = true )
+	private List<String> visiblePermissionList;
 
 	public static final String subjectPublishAble_FIELDNAME = "subjectPublishAble";
 	@FieldDescribe("版块发贴权限:所有人(默认)|根据权限")
@@ -112,12 +124,34 @@ public class BBSForumInfo extends SliceJpaObject {
 	@Index(name = TABLE + IndexNameMiddle + subjectPublishAble_FIELDNAME)
 	@CheckPersist(allowEmpty = false)
 	private String subjectPublishAble = "所有人";
+	
+	public static final String publishPermissionList_FIELDNAME = "publishPermissionList";
+	@FieldDescribe("可发布范围")
+	@PersistentCollection( fetch = FetchType.EAGER )
+	@OrderColumn( name = ORDERCOLUMNCOLUMN )
+	@ContainerTable(name = TABLE + ContainerTableNameMiddle + publishPermissionList_FIELDNAME, joinIndex = @Index( name = TABLE
+			+ IndexNameMiddle + publishPermissionList_FIELDNAME + JoinIndexNameSuffix ))
+	@ElementColumn( length = AbstractPersistenceProperties.organization_name_length, name = ColumnNamePrefix + publishPermissionList_FIELDNAME )
+	@ElementIndex( name = TABLE + IndexNameMiddle + publishPermissionList_FIELDNAME + ElementIndexNameSuffix )
+	@CheckPersist( allowEmpty = true )
+	private List<String> publishPermissionList;
 
 	public static final String replyPublishAble_FIELDNAME = "replyPublishAble";
 	@FieldDescribe("版块回复权限:所有人(默认)|根据权限")
 	@Column(length = JpaObject.length_16B, name = ColumnNamePrefix + replyPublishAble_FIELDNAME)
 	@CheckPersist(allowEmpty = false)
 	private String replyPublishAble = "所有人";
+	
+	public static final String replyPermissionList_FIELDNAME = "replyPermissionList";
+	@FieldDescribe("可回复范围")
+	@PersistentCollection( fetch = FetchType.EAGER )
+	@OrderColumn( name = ORDERCOLUMNCOLUMN )
+	@ContainerTable(name = TABLE + ContainerTableNameMiddle + replyPermissionList_FIELDNAME, joinIndex = @Index( name = TABLE
+			+ IndexNameMiddle + replyPermissionList_FIELDNAME + JoinIndexNameSuffix ))
+	@ElementColumn( length = AbstractPersistenceProperties.organization_name_length, name = ColumnNamePrefix + replyPermissionList_FIELDNAME )
+	@ElementIndex( name = TABLE + IndexNameMiddle + replyPermissionList_FIELDNAME + ElementIndexNameSuffix )
+	@CheckPersist( allowEmpty = true )
+	private List<String> replyPermissionList;
 
 	public static final String indexListStyle_FIELDNAME = "indexListStyle";
 	@FieldDescribe("首页列表样式:经典|简单矩形|图片矩形")
@@ -540,4 +574,58 @@ public class BBSForumInfo extends SliceJpaObject {
 		}
 		return this.forumManagerList;
 	}
+
+	public List<String> getVisiblePermissionList() {
+		return visiblePermissionList;
+	}
+
+	public void setVisiblePermissionList(List<String> visiblePermissionList) {
+		this.visiblePermissionList = visiblePermissionList;
+	}
+	
+	public List<String> addVisiblePermission(String permissoin) {
+		if (this.visiblePermissionList == null) {
+			this.visiblePermissionList = new ArrayList<>();
+		}
+		if (!this.visiblePermissionList.contains(permissoin)) {
+			this.visiblePermissionList.add(permissoin);
+		}
+		return this.visiblePermissionList;
+	}
+
+	public List<String> getPublishPermissionList() {
+		return publishPermissionList;
+	}
+
+	public void setPublishPermissionList(List<String> publishPermissionList) {
+		this.publishPermissionList = publishPermissionList;
+	}
+
+	public List<String> addPublishPermission(String permissoin) {
+		if (this.publishPermissionList == null) {
+			this.publishPermissionList = new ArrayList<>();
+		}
+		if (!this.publishPermissionList.contains(permissoin)) {
+			this.publishPermissionList.add(permissoin);
+		}
+		return this.publishPermissionList;
+	}
+	
+	public List<String> getReplyPermissionList() {
+		return replyPermissionList;
+	}
+
+	public void setReplyPermissionList(List<String> replyPermissionList) {
+		this.replyPermissionList = replyPermissionList;
+	}
+	
+	public List<String> addReplyPermission(String permissoin) {
+		if (this.replyPermissionList == null) {
+			this.replyPermissionList = new ArrayList<>();
+		}
+		if (!this.replyPermissionList.contains(permissoin)) {
+			this.replyPermissionList.add(permissoin);
+		}
+		return this.replyPermissionList;
+	}
 }

+ 95 - 0
o2server/x_bbs_core_entity/src/main/java/com/x/bbs/entity/BBSSectionInfo.java

@@ -1,5 +1,8 @@
 package com.x.bbs.entity;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import javax.persistence.Basic;
 import javax.persistence.Column;
 import javax.persistence.Entity;
@@ -11,8 +14,14 @@ import javax.persistence.Lob;
 import javax.persistence.Table;
 import javax.persistence.UniqueConstraint;
 
+import org.apache.openjpa.persistence.PersistentCollection;
+import org.apache.openjpa.persistence.jdbc.ContainerTable;
+import org.apache.openjpa.persistence.jdbc.ElementColumn;
+import org.apache.openjpa.persistence.jdbc.ElementIndex;
 import org.apache.openjpa.persistence.jdbc.Index;
+import org.apache.openjpa.persistence.jdbc.OrderColumn;
 
+import com.x.base.core.entity.AbstractPersistenceProperties;
 import com.x.base.core.entity.JpaObject;
 import com.x.base.core.entity.SliceJpaObject;
 import com.x.base.core.entity.annotation.CheckPersist;
@@ -134,18 +143,51 @@ public class BBSSectionInfo extends SliceJpaObject {
 	@Index(name = TABLE + IndexNameMiddle + sectionVisible_FIELDNAME)
 	@CheckPersist(allowEmpty = false)
 	private String sectionVisible = "所有人";
+	
+	public static final String visiblePermissionList_FIELDNAME = "visiblePermissionList";
+	@FieldDescribe("版块可见范围")
+	@PersistentCollection( fetch = FetchType.EAGER )
+	@OrderColumn( name = ORDERCOLUMNCOLUMN )
+	@ContainerTable(name = TABLE + ContainerTableNameMiddle + visiblePermissionList_FIELDNAME, joinIndex = @Index( name = TABLE
+			+ IndexNameMiddle + visiblePermissionList_FIELDNAME + JoinIndexNameSuffix ))
+	@ElementColumn( length = AbstractPersistenceProperties.organization_name_length, name = ColumnNamePrefix + visiblePermissionList_FIELDNAME )
+	@ElementIndex( name = TABLE + IndexNameMiddle + visiblePermissionList_FIELDNAME + ElementIndexNameSuffix )
+	@CheckPersist( allowEmpty = true )
+	private List<String> visiblePermissionList;
 
 	public static final String subjectPublishAble_FIELDNAME = "subjectPublishAble";
 	@FieldDescribe("版块发贴权限:所有人(默认)|根据权限")
 	@Column(length = JpaObject.length_16B, name = ColumnNamePrefix + subjectPublishAble_FIELDNAME)
 	@CheckPersist(allowEmpty = false)
 	private String subjectPublishAble = "所有人";
+	
+	public static final String publishPermissionList_FIELDNAME = "publishPermissionList";
+	@FieldDescribe("版块可发表范围")
+	@PersistentCollection( fetch = FetchType.EAGER )
+	@OrderColumn( name = ORDERCOLUMNCOLUMN )
+	@ContainerTable(name = TABLE + ContainerTableNameMiddle + publishPermissionList_FIELDNAME, joinIndex = @Index( name = TABLE
+			+ IndexNameMiddle + publishPermissionList_FIELDNAME + JoinIndexNameSuffix ))
+	@ElementColumn( length = AbstractPersistenceProperties.organization_name_length, name = ColumnNamePrefix + publishPermissionList_FIELDNAME )
+	@ElementIndex( name = TABLE + IndexNameMiddle + publishPermissionList_FIELDNAME + ElementIndexNameSuffix )
+	@CheckPersist( allowEmpty = true )
+	private List<String> publishPermissionList;
 
 	public static final String replyPublishAble_FIELDNAME = "replyPublishAble";
 	@FieldDescribe("版块回复权限:所有人(默认)|根据权限")
 	@Column(length = JpaObject.length_16B, name = ColumnNamePrefix + replyPublishAble_FIELDNAME)
 	@CheckPersist(allowEmpty = false)
 	private String replyPublishAble = "所有人";
+	
+	public static final String replyPermissionList_FIELDNAME = "replyPermissionList";
+	@FieldDescribe("版块可回复范围")
+	@PersistentCollection( fetch = FetchType.EAGER )
+	@OrderColumn( name = ORDERCOLUMNCOLUMN )
+	@ContainerTable(name = TABLE + ContainerTableNameMiddle + replyPermissionList_FIELDNAME, joinIndex = @Index( name = TABLE
+			+ IndexNameMiddle + replyPermissionList_FIELDNAME + JoinIndexNameSuffix ))
+	@ElementColumn( length = AbstractPersistenceProperties.organization_name_length, name = ColumnNamePrefix + replyPermissionList_FIELDNAME )
+	@ElementIndex( name = TABLE + IndexNameMiddle + replyPermissionList_FIELDNAME + ElementIndexNameSuffix )
+	@CheckPersist( allowEmpty = true )
+	private List<String> replyPermissionList;
 
 	public static final String moderatorNames_FIELDNAME = "moderatorNames";
 	@FieldDescribe("版主姓名:可多值,默认为创建者")
@@ -454,4 +496,57 @@ public class BBSSectionInfo extends SliceJpaObject {
 		this.typeCategory = typeCategory;
 	}
 
+	public List<String> getVisiblePermissionList() {
+		return visiblePermissionList;
+	}
+
+	public void setVisiblePermissionList(List<String> visiblePermissionList) {
+		this.visiblePermissionList = visiblePermissionList;
+	}
+
+	public List<String> addVisitPermission(String permissoin) {
+		if (this.visiblePermissionList == null) {
+			this.visiblePermissionList = new ArrayList<>();
+		}
+		if (!this.visiblePermissionList.contains(permissoin)) {
+			this.visiblePermissionList.add(permissoin);
+		}
+		return this.visiblePermissionList;
+	}
+	
+	public List<String> getPublishPermissionList() {
+		return publishPermissionList;
+	}
+
+	public void setPublishPermissionList(List<String> publishPermissionList) {
+		this.publishPermissionList = publishPermissionList;
+	}
+
+	public List<String> addPublishPermission(String permissoin) {
+		if (this.publishPermissionList == null) {
+			this.publishPermissionList = new ArrayList<>();
+		}
+		if (!this.publishPermissionList.contains(permissoin)) {
+			this.publishPermissionList.add(permissoin);
+		}
+		return this.publishPermissionList;
+	}
+	
+	public List<String> getReplyPermissionList() {
+		return replyPermissionList;
+	}
+
+	public void setReplyPermissionList(List<String> replyPermissionList) {
+		this.replyPermissionList = replyPermissionList;
+	}
+	
+	public List<String> addReplyPermission(String permissoin) {
+		if (this.replyPermissionList == null) {
+			this.replyPermissionList = new ArrayList<>();
+		}
+		if (!this.replyPermissionList.contains(permissoin)) {
+			this.replyPermissionList.add(permissoin);
+		}
+		return this.replyPermissionList;
+	}
 }

+ 0 - 1
o2server/x_bbs_core_entity/src/main/java/com/x/bbs/entity/BBSSubjectInfo.java

@@ -22,7 +22,6 @@ import org.apache.openjpa.persistence.jdbc.ElementColumn;
 import org.apache.openjpa.persistence.jdbc.ElementIndex;
 import org.apache.openjpa.persistence.jdbc.Index;
 
-import com.x.base.core.entity.AbstractPersistenceProperties;
 import com.x.base.core.entity.JpaObject;
 import com.x.base.core.entity.SliceJpaObject;
 import com.x.base.core.entity.annotation.CheckPersist;

+ 13 - 0
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/MessageFactory.java

@@ -0,0 +1,13 @@
+package com.x.cms.assemble.control;
+
+import com.x.base.core.project.message.MessageConnector;
+import com.x.cms.assemble.control.queue.QueueSendDocumentNotify.MessageWo;
+
+public class MessageFactory {
+
+	public static void cms_publish(String person, MessageWo messageWo ) throws Exception {
+		String title = "新信息发布:" + messageWo.getTitle();
+		System.out.println("CMS推送消息:" +  title + ", 目标用户:" + person );
+		MessageConnector.send(MessageConnector.TYPE_CMS_PUBLISH,  title, person, messageWo);
+	}
+}

+ 12 - 5
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/ThisApplication.java

@@ -3,14 +3,17 @@ package com.x.cms.assemble.control;
 import java.util.concurrent.ConcurrentHashMap;
 
 import com.x.base.core.project.Context;
+import com.x.base.core.project.message.MessageConnector;
 import com.x.cms.assemble.control.queue.DataImportStatus;
 import com.x.cms.assemble.control.queue.QueueBatchOperation;
 import com.x.cms.assemble.control.queue.QueueDataRowImport;
 import com.x.cms.assemble.control.queue.QueueDocumentDelete;
 import com.x.cms.assemble.control.queue.QueueDocumentUpdate;
 import com.x.cms.assemble.control.queue.QueueDocumentViewCountUpdate;
-import com.x.cms.assemble.control.service.CmsBatchOperationPersistService;
+import com.x.cms.assemble.control.queue.QueueSendDocumentNotify;
 import com.x.cms.assemble.control.timertask.Timertask_BatchOperationTask;
+import com.x.cms.assemble.control.timertask.Timertask_CheckDocumentReviewStatus;
+import com.x.cms.assemble.control.timertask.Timertask_InitOperationRunning;
 import com.x.cms.assemble.control.timertask.Timertask_LogRecordCheckTask;
 
 public class ThisApplication {
@@ -23,7 +26,7 @@ public class ThisApplication {
 	public static QueueDocumentUpdate queueDocumentUpdate;
 	public static QueueDocumentViewCountUpdate queueDocumentViewCountUpdate;
 	public static QueueBatchOperation queueBatchOperation;
-	private static CmsBatchOperationPersistService cmsBatchOperationPersistService;
+	public static QueueSendDocumentNotify queueSendDocumentNotify;
 	private static ConcurrentHashMap<String, DataImportStatus> importStatus = new ConcurrentHashMap<>();
 	
 	public static Context context() {
@@ -31,7 +34,6 @@ public class ThisApplication {
 	}
 	
 	public static void init() throws Exception {
-		cmsBatchOperationPersistService = new CmsBatchOperationPersistService();	
 		//执行数据库中的批处理操作
 		queueBatchOperation = new QueueBatchOperation();
 		//Document删除时也需要检查一下热点图片里的数据是否已经删除掉了
@@ -42,18 +44,23 @@ public class ThisApplication {
 		queueDocumentUpdate = new QueueDocumentUpdate();
 		//Document被访问时,需要将总的访问量更新到item的document中,便于视图使用,在队列里异步修改
 		queueDocumentViewCountUpdate = new QueueDocumentViewCountUpdate();
+		//Document发布时,向所有阅读者推送通知
+		queueSendDocumentNotify = new QueueSendDocumentNotify();
+		
+		MessageConnector.start(context());
 		
 		context().startQueue( queueBatchOperation );
 		context().startQueue( queueDocumentDelete );
 		context().startQueue( queueDataRowImport );
 		context().startQueue( queueDocumentUpdate );
 		context().startQueue( queueDocumentViewCountUpdate );
+		context().startQueue( queueSendDocumentNotify );
 
 		// 每天凌晨2点执行一次
 		context.schedule( Timertask_LogRecordCheckTask.class, "0 0 2 * * ?" );
 		context.schedule( Timertask_BatchOperationTask.class, "0 */5 * * * ?" );
-		//cmsBatchOperationPersistService.initOperationRunning();
-		//cmsBatchOperationPersistService.checkDocumentReviewStatus();
+		context.scheduleLocal( Timertask_CheckDocumentReviewStatus.class, 1200 );
+		context.scheduleLocal( Timertask_InitOperationRunning.class, 150 );
 	}
 
 	public static void destroy() {

+ 98 - 6
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/factory/DocumentFactory.java

@@ -4,12 +4,10 @@ import java.util.ArrayList;
 import java.util.List;
 
 import javax.persistence.EntityManager;
-import javax.persistence.criteria.CriteriaBuilder;
-import javax.persistence.criteria.CriteriaQuery;
-import javax.persistence.criteria.Order;
-import javax.persistence.criteria.Predicate;
-import javax.persistence.criteria.Root;
+import javax.persistence.criteria.*;
 
+import com.x.cms.core.entity.Review;
+import com.x.cms.core.entity.Review_;
 import org.apache.commons.lang3.StringUtils;
 
 import com.x.base.core.project.exception.ExceptionWhen;
@@ -239,7 +237,6 @@ public class DocumentFactory extends AbstractFactory {
 	 * @param sequenceFieldValue
 	 * @param orderField
 	 * @param orderType
-	 * @param personName
 	 * @param queryFilter
 	 * @return
 	 * @throws Exception
@@ -445,5 +442,100 @@ public class DocumentFactory extends AbstractFactory {
 		return em.createQuery(cq).getSingleResult();
 	}
 
+	/**
+	 * 根据条件分页查询符合条件的文档信息列表
+	 * @param orderField
+	 * @param orderType
+	 * @param queryFilter
+	 * @param adjustPage
+	 * @param adjustPageSize
+	 * @return
+	 * @throws SecurityException
+	 * @throws NoSuchFieldException
+	 */
+	public List<Document> listPagingWithCondition( String personName, String orderField, String orderType, QueryFilter queryFilter, Integer adjustPage,
+												   Integer adjustPageSize ) throws Exception {
+		EntityManager em = this.entityManagerContainer().get( Document.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		EntityManager em1 = this.entityManagerContainer().get( Review.class );
+		CriteriaBuilder cb1 = em1.getCriteriaBuilder();
+		CriteriaQuery<Document> cq = cb.createQuery(Document.class);
+		Root<Document> root = cq.from(Document.class);
+		Predicate p = CriteriaBuilderTools.composePredicateWithQueryFilter( Document_.class, cb, null, root, queryFilter );
+
+		if(StringUtils.isNotBlank(personName)){
+			Subquery<Review> subquery = cq.subquery(Review.class);
+			Root<Review> root2 = subquery.from(em1.getMetamodel().entity(Review.class));
+			subquery.select(root2);
+			Predicate p_permission = cb1.conjunction();
+			p_permission = cb1.and(p_permission,
+					cb1.or(cb1.equal( root2.get( Review_.permissionObj), "*"),
+							cb1.equal( root2.get( Review_.permissionObj ), personName )));
+			p_permission = cb1.and(p_permission, cb1.equal(root.get(Document_.id), root2.get(Review_.docId)));
+			subquery.where(p_permission);
+			p = cb.and(p, cb.exists(subquery));
+		}
+		cq.select(root).where(p);
+
+		//排序,添加排序列,默认使用sequence
+		List<Order> orders = new ArrayList<>();
+		if( !Document.isTop_FIELDNAME.equals( orderField )) {
+			Order isTopOrder = CriteriaBuilderTools.getOrder( cb, root, Document_.class, Document.isTop_FIELDNAME, "desc" );
+			if( isTopOrder != null ){
+				orders.add( isTopOrder );
+			}
+		}
+
+		Order orderWithField = CriteriaBuilderTools.getOrder( cb, root, Document_.class, orderField, orderType );
+		if( orderWithField != null ){
+			orders.add( orderWithField );
+		}
+
+		if( !Document.isFieldInSequence(orderField)) {
+			//如果是其他的列,很可能排序值不唯一,所以使用多一列排序列来确定每次查询的顺序
+			orderWithField = CriteriaBuilderTools.getOrder( cb, root, Document_.class, Document.id_FIELDNAME, orderType );
+			if( orderWithField != null ){
+				orders.add( orderWithField );
+			}
+		}
+		if( ListTools.isNotEmpty(  orders )){
+			cq.orderBy( orders );
+		}
+		return em.createQuery(cq).setFirstResult((adjustPage - 1) * adjustPageSize).setMaxResults(adjustPageSize)
+				.getResultList();
+	}
+
+	/**
+	 * 根据条件统计文档数目
+	 * @param personName
+	 * @param queryFilter
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countWithCondition( String personName, QueryFilter queryFilter) throws Exception {
+		EntityManager em = this.entityManagerContainer().get( Document.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		EntityManager em1 = this.entityManagerContainer().get( Review.class );
+		CriteriaBuilder cb1 = em1.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<Document> root = cq.from(Document.class);
+		Predicate p = CriteriaBuilderTools.composePredicateWithQueryFilter( Document_.class, cb, null, root, queryFilter );
+
+		if(StringUtils.isNotBlank(personName)){
+			Subquery<Review> subquery = cq.subquery(Review.class);
+			Root<Review> root2 = subquery.from(em1.getMetamodel().entity(Review.class));
+			subquery.select(root2);
+			Predicate p_permission = cb1.conjunction();
+			p_permission = cb1.and(p_permission,
+					cb1.or(cb1.equal( root2.get( Review_.permissionObj), "*"),
+							cb1.equal( root2.get( Review_.permissionObj ), personName )));
+			p_permission = cb1.and(p_permission, cb1.equal(root.get(Document_.id), root2.get(Review_.docId)));
+			subquery.where(p_permission);
+			p = cb.and(p, cb.exists(subquery));
+		}
+
+		return em.createQuery(cq.select(cb.count(root)).where(p)).getSingleResult();
+	}
+
 
 }

+ 6 - 0
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/appinfo/ActionSave.java

@@ -146,6 +146,12 @@ public class ActionSave extends BaseAction {
 			wi.setCreatorPerson(effectivePerson.getDistinguishedName());
 			wi.setCreatorUnitName( unitName );
 			wi.setCreatorTopUnitName( topUnitName );
+
+			if( StringUtils.equals( "信息", wi.getDocumentType() ) && wi.getSendNotify() == null ) {
+				wi.setSendNotify( true );
+			}else {
+				wi.setSendNotify( false );
+			}
 			
 			try {
 				appInfo = appInfoServiceAdv.save( wi, effectivePerson );

+ 40 - 1
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/categoryinfo/ActionSave.java

@@ -37,6 +37,7 @@ public class ActionSave extends BaseAction {
 		String unitName = null;
 		String topUnitName = null;
 		Wi wi = null;
+		AppInfo appInfo = null;
 		CategoryInfo old_categoryInfo = null;
 		CategoryInfo categoryInfo = null;
 		Boolean check = true;
@@ -74,6 +75,14 @@ public class ActionSave extends BaseAction {
 			}
 		}
 		
+		if( check ) {
+			if ( StringUtils.isEmpty( wi.getAppId() ) ) {
+				check = false;
+				Exception exception = new ExceptionAppIdEmpty();
+				result.error(exception);
+			}
+		}
+		
 		if ( check && !"xadmin".equals( identityName ) ) {
 			try {
 				unitName = userManagerService.getUnitNameByIdentity(identityName);
@@ -95,7 +104,21 @@ public class ActionSave extends BaseAction {
 				logger.error(e, effectivePerson, request, null);
 			}
 		}
-		
+		if( check ){
+			try {
+				appInfo = appInfoServiceAdv.getWithFlag( wi.getAppId() );
+				if( appInfo == null ){
+					check = false;
+					Exception exception = new ExceptionAppInfoNotExists( wi.getAppId() );
+					result.error( exception );
+				}
+			} catch (Exception e) {
+				check = false;
+				Exception exception = new ExceptionCategoryInfoProcess( e, "根据指定flag查询应用栏目信息对象时发生异常。flag:" + wi.getAppId() );
+				result.error( exception );
+				logger.error( e, effectivePerson, request, null);
+			}
+		}
 		if (check) {
 			if( StringUtils.isEmpty( wi.getId() )) {
 				wi.setId( CategoryInfo.createId() );
@@ -117,6 +140,22 @@ public class ActionSave extends BaseAction {
 			wi.setCreatorUnitName(unitName);
 			wi.setCreatorTopUnitName(topUnitName);
 			
+			if( StringUtils.equals( "信息", wi.getDocumentType() ) ) {
+				if( wi.getSendNotify() == null ) {
+					if( appInfo.getSendNotify() == null) {
+						wi.setSendNotify( true );
+					}else {
+						//继承栏目的通知配置
+						wi.setSendNotify( appInfo.getSendNotify() );
+					}
+				}
+			}else {
+				//数据就默认为false,不通知
+				if( wi.getSendNotify() == null ) {
+					wi.setSendNotify( false );
+				}
+			}
+
 			try {
 				categoryInfo = categoryInfoServiceAdv.save( wi, wi.getExtContent(), effectivePerson );
 

+ 12 - 0
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/categoryinfo/ExceptionAppInfoNotExists.java

@@ -0,0 +1,12 @@
+package com.x.cms.assemble.control.jaxrs.categoryinfo;
+
+import com.x.base.core.project.exception.PromptException;
+
+class ExceptionAppInfoNotExists extends PromptException {
+
+	private static final long serialVersionUID = 1859164370743532895L;
+
+	ExceptionAppInfoNotExists( String id ) {
+		super("ID为{}的栏目信息不存在。", id );
+	}
+}

+ 26 - 78
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/ActionPersistPublishDocument.java → o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/ActionPersistPublishAndNotify.java

@@ -3,10 +3,11 @@ package com.x.cms.assemble.control.jaxrs.document;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
-import java.util.Map;
 
 import javax.servlet.http.HttpServletRequest;
 
+import org.apache.commons.lang3.StringUtils;
+
 import com.google.gson.JsonElement;
 import com.x.base.core.container.EntityManagerContainer;
 import com.x.base.core.container.factory.EntityManagerContainerFactory;
@@ -22,12 +23,13 @@ import com.x.base.core.project.logger.Logger;
 import com.x.base.core.project.logger.LoggerFactory;
 import com.x.cms.assemble.control.ThisApplication;
 import com.x.cms.assemble.control.jaxrs.permission.element.PermissionInfo;
+import com.x.cms.core.entity.CategoryInfo;
 import com.x.cms.core.entity.Document;
 import com.x.cms.core.entity.FileInfo;
 
-public class ActionPersistPublishDocument extends BaseAction {
+public class ActionPersistPublishAndNotify extends BaseAction {
 
-	private static  Logger logger = LoggerFactory.getLogger(ActionPersistPublishDocument.class);
+	private static  Logger logger = LoggerFactory.getLogger(ActionPersistPublishAndNotify.class);
 
 	protected ActionResult<Wo> execute(HttpServletRequest request, String id, EffectivePerson effectivePerson, JsonElement jsonElement ) throws Exception {
 		ActionResult<Wo> result = new ActionResult<>();
@@ -61,10 +63,9 @@ public class ActionPersistPublishDocument extends BaseAction {
 				logger.error(e, effectivePerson, request, null);
 			}
 		}
-
 		if (check) {
 			try {
-				modifyDocStatus(id, "published", effectivePerson.getDistinguishedName());
+				modifyDocStatus( id, "published", effectivePerson.getDistinguishedName() );
 				document.setDocStatus("published");
 				document.setPublishTime(new Date());
 				
@@ -174,7 +175,7 @@ public class ActionPersistPublishDocument extends BaseAction {
 		}
 
 		//将读者以及作者信息持久化到数据库中
-		if( !wi.getSkipPermission() ) {
+		if( wi.getSkipPermission() ) {
 			try {
 				documentPersistService.refreshDocumentPermission( id, wi.getReaderList(), wi.getAuthorList() );
 			} catch (Exception e) {
@@ -184,35 +185,33 @@ public class ActionPersistPublishDocument extends BaseAction {
 				logger.error(e, effectivePerson, request, null);
 			}
 		}
+		
+		//判断是否需要发送通知消息
+		if (check) {
+			try {
+				CategoryInfo categoryInfo = categoryInfoServiceAdv.getWithFlag( document.getCategoryId() );
+				if( categoryInfo != null ){
+					//如果分类配置为需要推送通知,或者(分类配置为空,但是文档为信息文档时),推送通知
+					if( categoryInfo.getSendNotify() || (categoryInfo.getSendNotify() == null && StringUtils.equals("信息", categoryInfo.getDocumentType()))){
+						ThisApplication.queueSendDocumentNotify.send( document );
+					}
+				}
+			} catch (Exception e) {
+				check = false;
+				Exception exception = new ExceptionDocumentInfoProcess( e, "根据ID查询分类信息对象时发生异常。Flag:" + document.getCategoryId()  );
+				result.error( exception );
+				logger.error( e, effectivePerson, request, null);
+			}
+		}
 		ApplicationCache.notify(Document.class);
 
 		return result;
 	}
 
-	public static class Wi extends Document {
-		
-		private static final long serialVersionUID = -5076990764713538973L;
+	public static class Wi {
 		
 		public static WrapCopier<Wi, Document> copier = WrapCopierFactory.wi( Wi.class, Document.class, null, JpaObject.FieldsUnmodify );
 		
-		@FieldDescribe( "文档操作者身份." )
-		private String identity = null;
-		
-		@FieldDescribe( "数据的路径列表." )
-		private String[] dataPaths = null;
-		
-		@FieldDescribe( "启动流程的JobId." )
-		private String wf_jobId = null;
-		
-		@FieldDescribe( "启动流程的WorkId." )
-		private String wf_workId = null;
-		
-		@FieldDescribe( "启动流程的附件列表." )
-		private String[] wf_attachmentIds = null;	
-		
-		@FieldDescribe( "文档数据." )
-		private Map<?, ?> docData = null;
-		
 		@FieldDescribe( "文档读者." )
 		private List<PermissionInfo> readerList = null;
 		
@@ -233,14 +232,6 @@ public class ActionPersistPublishDocument extends BaseAction {
 			this.skipPermission = skipPermission;
 		}
 
-		public String getIdentity() {
-			return identity;
-		}
-
-		public void setIdentity(String identity) {
-			this.identity = identity;
-		}
-
 		public List<PermissionInfo> getReaderList() {
 			return readerList;
 		}
@@ -257,49 +248,6 @@ public class ActionPersistPublishDocument extends BaseAction {
 			this.authorList = authorList;
 		}
 
-		public String[] getDataPaths() {
-			if( dataPaths != null && dataPaths.length == 1 && dataPaths[0].equals("null")){
-				return null;
-			}
-			return dataPaths;
-		}
-
-		public void setDataPaths(String[] dataPaths) {
-			this.dataPaths = dataPaths;
-		}
-
-		public Map<?, ?> getDocData() {
-			return docData;
-		}
-
-		public void setDocData(Map<?, ?> docData) {
-			this.docData = docData;
-		}
-
-		public String getWf_jobId() {
-			return wf_jobId;
-		}
-
-		public String getWf_workId() {
-			return wf_workId;
-		}
-
-		public String[] getWf_attachmentIds() {
-			return wf_attachmentIds;
-		}
-
-		public void setWf_jobId(String wf_jobId) {
-			this.wf_jobId = wf_jobId;
-		}
-
-		public void setWf_workId(String wf_workId) {
-			this.wf_workId = wf_workId;
-		}
-
-		public void setWf_attachmentIds(String[] wf_attachmentIds) {
-			this.wf_attachmentIds = wf_attachmentIds;
-		}
-
 		public List<String> getCloudPictures() {
 			return cloudPictures;
 		}

+ 1 - 1
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/ActionPersistPublishByWorkFlow.java

@@ -182,7 +182,7 @@ public class ActionPersistPublishByWorkFlow extends BaseAction {
 
 		if (check) {
 			try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
-				if (wi.getCreatorIdentity() != null) {
+				if ( StringUtils.isNotEmpty( wi.getCreatorIdentity() )) {
 					wi.setCreatorPerson( userManagerService.getPersonNameWithIdentity( wi.getCreatorIdentity() ) );
 					wi.setCreatorUnitName( userManagerService.getUnitNameByIdentity( wi.getCreatorIdentity() ) );
 					wi.setCreatorTopUnitName( userManagerService.getTopUnitNameByIdentity( wi.getCreatorIdentity() ) );

+ 4 - 3
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/ActionPersistPublishContent.java

@@ -179,7 +179,7 @@ public class ActionPersistPublishContent extends BaseAction {
 
 		if (check) {
 			try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
-				if (wi.getCreatorIdentity() != null) {
+				if (StringUtils.isNotEmpty( wi.getCreatorIdentity() )) {
 					wi.setCreatorPerson( userManagerService.getPersonNameWithIdentity( wi.getCreatorIdentity() ) );
 					wi.setCreatorUnitName( userManagerService.getUnitNameByIdentity( wi.getCreatorIdentity() ) );
 					wi.setCreatorTopUnitName( userManagerService.getTopUnitNameByIdentity( wi.getCreatorIdentity() ) );
@@ -209,8 +209,9 @@ public class ActionPersistPublishContent extends BaseAction {
 			try {
 				JsonElement docData = XGsonBuilder.instance().toJsonTree(wi.getDocData(), Map.class);
 				wi.setDocStatus("published");
-				wi.setPublishTime(new Date());
-			
+				if( wi.getPublishTime() == null ) {
+					wi.setPublishTime(new Date());
+				}			
 				document = documentPersistService.save(wi, docData );
 			} catch (Exception e) {
 				check = false;

+ 23 - 3
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/ActionPersistSaveDocument.java

@@ -1,6 +1,7 @@
 package com.x.cms.assemble.control.jaxrs.document;
 
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
@@ -189,13 +190,13 @@ public class ActionPersistSaveDocument extends BaseAction {
 
 		if (check) {
 			try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
-				if ( identity != null) {
+				if ( StringUtils.isNotEmpty(identity)) {
 					document.setCreatorIdentity( identity );
 					document.setCreatorPerson( userManagerService.getPersonNameWithIdentity( identity ) );
 					document.setCreatorUnitName( userManagerService.getUnitNameByIdentity( identity ) );
 					document.setCreatorTopUnitName( userManagerService.getTopUnitNameByIdentity( identity ) );
 				} else {
-					if ("xadmin".equalsIgnoreCase(effectivePerson.getDistinguishedName())) {
+					if ("xadmin".equalsIgnoreCase( effectivePerson.getDistinguishedName() )) {
 						document.setCreatorIdentity("xadmin");
 						document.setCreatorPerson("xadmin");
 						document.setCreatorUnitName("xadmin");
@@ -203,7 +204,7 @@ public class ActionPersistSaveDocument extends BaseAction {
 					} else {
 						//取第一个身份
 						identity = userManagerService.getIdentityWithPerson(effectivePerson.getDistinguishedName());
-						if(StringUtils.isNotEmpty(identity)) {
+						if( StringUtils.isNotEmpty(identity) ) {
 							document.setCreatorIdentity( identity );
 							document.setCreatorPerson( effectivePerson.getDistinguishedName() );
 							document.setCreatorUnitName( userManagerService.getUnitNameByIdentity( identity ) );
@@ -220,6 +221,14 @@ public class ActionPersistSaveDocument extends BaseAction {
 			}
 		}
 
+		if (check) {
+			if( StringUtils.equals( wi.getDocStatus(), "published")) {
+				if( document.getPublishTime() == null ) {
+					document.setPublishTime( new Date() );
+				}
+			}
+		}
+		
 		if (check) {
 			try {
 				JsonElement dataJson = null;
@@ -351,6 +360,9 @@ public class ActionPersistSaveDocument extends BaseAction {
 
 		@FieldDescribe("文档状态: published | draft | checking | error,非必填,默认为draft")
 		private String docStatus = "draft";
+		
+		@FieldDescribe("文档发布时间")
+		private Date publishTime;
 
 		@FieldDescribe("首页图片列表,非必填")
 		private List<String> pictureList = null;		
@@ -514,6 +526,14 @@ public class ActionPersistSaveDocument extends BaseAction {
 		public void setSkipPermission(Boolean skipPermission) {
 			this.skipPermission = skipPermission;
 		}
+
+		public Date getPublishTime() {
+			return publishTime;
+		}
+
+		public void setPublishTime(Date publishTime) {
+			this.publishTime = publishTime;
+		}
 	}
 
 	public static class Wo extends WoId {

+ 7 - 3
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/ActionQueryCountWithFilter.java

@@ -25,21 +25,21 @@ public class ActionQueryCountWithFilter extends BaseAction {
 		ActionResult<Wo> result = new ActionResult<>();
 		
 		Long total = 0L;
-		WrapInDocumentFilter wi = null;
+		Wi wi = null;
 		Wo wo = new Wo();		
 		Boolean check = true;		
 		String personName = effectivePerson.getDistinguishedName();
 		QueryFilter queryFilter = null;
 		
 		try {
-			wi = this.convertToWrapIn( jsonElement, WrapInDocumentFilter.class );
+			wi = this.convertToWrapIn( jsonElement, Wi.class );
 		} catch (Exception e ) {
 			check = false;
 			Exception exception = new ExceptionDocumentInfoProcess( e, "系统在将JSON信息转换为对象时发生异常。JSON:" + jsonElement.toString() );
 			result.error( exception );
 			logger.error( e, effectivePerson, request, null);
 		}
-		if ( wi == null ) { wi = new WrapInDocumentFilter(); }
+		if ( wi == null ) { wi = new Wi(); }
 		
 		if( StringUtils.isEmpty( wi.getDocumentType() )) {
 			wi.setDocumentType( "信息" );
@@ -114,6 +114,10 @@ public class ActionQueryCountWithFilter extends BaseAction {
 		return result;
 	}
 	
+	public static class Wi extends WrapInDocumentFilter{
+		
+	}
+
 	public static class Wo {
 		
 		@FieldDescribe( "查询到的文档数量" )

+ 527 - 0
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/ActionQueryGetControl.java

@@ -0,0 +1,527 @@
+package com.x.cms.assemble.control.jaxrs.document;
+
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang3.StringUtils;
+
+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.GsonPropertyObject;
+import com.x.base.core.project.http.ActionResult;
+import com.x.base.core.project.http.EffectivePerson;
+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.cms.core.entity.AppInfo;
+import com.x.cms.core.entity.CategoryInfo;
+import com.x.cms.core.entity.Document;
+
+import net.sf.ehcache.Element;
+
+public class ActionQueryGetControl extends BaseAction {
+
+	private static  Logger logger = LoggerFactory.getLogger(ActionQueryGetControl.class);
+
+	/**
+	 * "control": {
+      "allowVisit": true,
+      "allowReadProcessing": false,
+      "allowDelete": true
+    },
+	 * @param request
+	 * @param id
+	 * @param effectivePerson
+	 * @return
+	 * @throws Exception
+	 */
+	protected ActionResult<Wo> execute(HttpServletRequest request, String id, EffectivePerson effectivePerson)
+			throws Exception {
+		ActionResult<Wo> result = new ActionResult<>();
+		Wo wo = new Wo();
+		WoControl woControl = new WoControl();
+		Long reviewCount = null;
+		Document document = null;
+		Boolean isAppAdmin = false;
+		Boolean isCategoryAdmin = false;
+		Boolean isManager = false;
+		Boolean isCreator = false;
+		Boolean check = true;
+		List<String> unitNames = null;
+		List<String> groupNames = null;
+		String personName = effectivePerson.getDistinguishedName();
+		
+		if ( StringUtils.isEmpty(id)) {
+			check = false;
+			Exception exception = new ExceptionDocumentIdEmpty();
+			result.error(exception);
+		}
+		
+		if (check) {
+			try {
+				if (effectivePerson.isManager()) {
+					isManager = true;
+				}
+			} catch (Exception e) {
+				check = false;
+				Exception exception = new ExceptionDocumentInfoProcess(e, "判断用户是否是系统管理员时发生异常!user:" + personName);
+				result.error(exception);
+				logger.error(e, effectivePerson, request, null);
+			}
+		}
+		
+		String cacheKey = ApplicationCache.concreteCacheKey( id, "getControl", isManager, effectivePerson.getDistinguishedName() );
+		Element element = cache.get(cacheKey);
+		if ((null != element) && (null != element.getObjectValue())) {
+			document = wo = (Wo) element.getObjectValue();
+			result.setData(wo);
+		} else {
+			if (check) {
+				try {
+					document = documentQueryService.get(id);
+					if (document == null) {
+						check = false;
+						Exception exception = new ExceptionDocumentNotExists(id);
+						result.error(exception);
+					} else {
+						try {
+							wo = Wo.copier.copy(document);
+						} catch (Exception e) {
+							check = false;
+							Exception exception = new ExceptionDocumentInfoProcess(e, "将查询出来的文档信息对象转换为可输出的数据信息时发生异常。");
+							result.error(exception);
+							logger.error(e, effectivePerson, request, null);
+						}
+					}
+				} catch (Exception e) {
+					check = false;
+					Exception exception = new ExceptionDocumentInfoProcess(e, "文档信息获取操作时发生异常。Id:" + id + ", Name:" + personName);
+					result.error(exception);
+					logger.error(e, effectivePerson, request, null);
+				}
+			}
+			
+			if (check) {
+				try {					
+					reviewCount = documentQueryService.getViewableReview(id, personName);
+					if (reviewCount > 0 ) {
+						woControl.setAllowVisit(true);
+					}
+				} catch (Exception e) {
+					check = false;
+					Exception exception = new ExceptionDocumentInfoProcess(e, "文档信息获取操作时发生异常。Id:" + id + ", Name:" + personName);
+					result.error(exception);
+					logger.error(e, effectivePerson, request, null);
+				}
+			}
+			
+			//判断用户是否是文档的创建者,创建者是有权限编辑文档的
+			if (check) {
+				if (wo != null && StringUtils.equals( personName, wo.getCreatorPerson())) {
+					isCreator = true;
+					woControl.setAllowVisit(true);
+				}
+			}
+			
+			if (check) {
+				wo.setControl(woControl);
+				cache.put(new Element(cacheKey, wo));
+			}
+		}
+		
+		/////////////////////////////////////////////////////////////
+		//不管是从缓存还是数据库查出来,都要重新进行处理权限判断
+		/////////////////////////////////////////////////////////////
+		if (check) {
+			try {
+				unitNames = userManagerService.listUnitNamesWithPerson( personName );
+				groupNames = userManagerService.listGroupNamesByPerson( personName );
+			} catch (Exception e) {
+				check = false;
+				Exception exception = new ExceptionDocumentInfoProcess(e, "查询用户所有的组织和群组信息时发生异常!user:" + personName);
+				result.error(exception);
+				logger.error(e, effectivePerson, request, null);
+			}
+		}
+		
+		AppInfo appInfo = null;
+		CategoryInfo categoryInfo = null;
+		
+		if (check) {
+			try {
+				appInfo = appInfoServiceAdv.get( document.getAppId() );
+				if( appInfo == null ) {
+					check = false;
+					Exception exception = new ExceptionAppInfoNotExists( document.getAppId()  );
+					result.error(exception);
+				}
+			} catch (Exception e) {
+				check = false;
+				Exception exception = new ExceptionDocumentInfoProcess(e, "根据ID查询栏目信息对象时发生异常。ID:" + document.getAppId());
+				result.error(exception);
+				logger.error(e, effectivePerson, request, null);
+			}
+		}
+		
+		if ( check ) {
+			try {
+				categoryInfo = categoryInfoServiceAdv.get(document.getCategoryId());
+				if( categoryInfo == null ) {
+					check = false;
+					Exception exception = new ExceptionCategoryInfoNotExists( document.getCategoryId() );
+					result.error(exception);
+				}
+			} catch (Exception e) {
+				check = false;
+				Exception exception = new ExceptionDocumentInfoProcess(e, "根据ID查询分类信息对象时发生异常。ID:" + document.getCategoryId());
+				result.error(exception);
+				logger.error(e, effectivePerson, request, null);
+			}
+		}
+		
+		//判断用户是否是分类的管理者,分类管理者是有权限编辑文档的
+		if (check) {
+			try {
+				if ( categoryInfoServiceAdv.isCategoryInfoManager( categoryInfo, personName, unitNames, groupNames ) ) {
+					isCategoryAdmin = true;
+					woControl.setAllowEdit(true);
+					woControl.setAllowDelete(true);
+				}
+			} catch (Exception e) {
+				check = false;
+				Exception exception = new ExceptionDocumentInfoProcess(e, "判断用户是否是分类管理员时发生异常!user:" + personName);
+				result.error(exception);
+				logger.error(e, effectivePerson, request, null);
+			}
+		}
+
+		//判断用户是否是栏目的管理者,栏目管理者是有权限编辑文档的
+		if (check) {
+			try {
+				if ( appInfoServiceAdv.isAppInfoManager( appInfo, personName, unitNames, groupNames )) {
+					isAppAdmin = true;
+					woControl.setAllowEdit(true);
+					woControl.setAllowDelete(true);
+				}
+			} catch (Exception e) {
+				check = false;
+				Exception exception = new ExceptionDocumentInfoProcess(e, "判断用户是否是栏目管理员时发生异常!user:" + personName);
+				result.error(exception);
+				logger.error(e, effectivePerson, request, null);
+			}
+		}
+
+		if (check) {
+			if (isManager || isAppAdmin || isCategoryAdmin || isCreator) {
+				woControl.setAllowEdit(true);
+				woControl.setAllowDelete(true);
+			} else {
+				// 判断当前登录者是不是该文档的可编辑者
+				try {
+					if( ListTools.isNotEmpty( document.getAuthorPersonList() )) {
+						if ( wo.getAuthorPersonList().contains( personName )) {
+							woControl.setAllowVisit(true);
+							woControl.setAllowEdit(true);
+						}
+						if( ListTools.containsAny( unitNames , wo.getAuthorUnitList() )) {
+							woControl.setAllowVisit(true);
+							woControl.setAllowEdit(true);
+						}
+						if( ListTools.containsAny( groupNames , wo.getAuthorGroupList() )) {
+							woControl.setAllowVisit(true);
+							woControl.setAllowEdit(true);
+						}
+					}
+				} catch (Exception e) {
+					check = false;
+					Exception exception = new ExceptionDocumentInfoProcess(e, "判断用户是否可编辑文档时发生异常!user:" + personName);
+					result.error(exception);
+					logger.error(e, effectivePerson, request, null);
+				}
+			}
+		}
+		wo.setControl(woControl);
+		result.setData(wo);
+		return result;
+	}
+
+//	private List<String> composeAuthorUnitsWithAppAndCagetory(AppInfo appInfo, CategoryInfo category) {
+//		List<String> authorUnits = new ArrayList<>();
+//		if( ListTools.isNotEmpty( appInfo.getManageableUnitList() )) {
+//			for( String name : appInfo.getManageableUnitList() ) {
+//				if( !authorUnits.contains( name )) {
+//					authorUnits.add( name );
+//				}
+//			}
+//		}
+//		if( ListTools.isNotEmpty( appInfo.getPublishableUnitList() )) {
+//			for( String name : appInfo.getPublishableUnitList() ) {
+//				if( !authorUnits.contains( name )) {
+//					authorUnits.add( name );
+//				}
+//			}
+//		}
+//		if( ListTools.isNotEmpty( category.getManageableUnitList() )) {
+//			for( String name : category.getManageableUnitList() ) {
+//				if( !authorUnits.contains( name )) {
+//					authorUnits.add( name );
+//				}
+//			}
+//		}
+//		if( ListTools.isNotEmpty( category.getPublishableUnitList() )) {
+//			for( String name : category.getPublishableUnitList() ) {
+//				if( !authorUnits.contains( name )) {
+//					authorUnits.add( name );
+//				}
+//			}
+//		}
+//		return authorUnits;
+//	}
+//
+//	private List<String> composeAuthorGroupsWithAppAndCagetory(AppInfo appInfo, CategoryInfo category) {
+//		List<String> authorGroups = new ArrayList<>();
+//		if( ListTools.isNotEmpty( appInfo.getManageableGroupList() )) {
+//			for( String name : appInfo.getManageableGroupList() ) {
+//				if( !authorGroups.contains( name )) {
+//					authorGroups.add( name );
+//				}
+//			}
+//		}
+//		if( ListTools.isNotEmpty( appInfo.getPublishableGroupList() )) {
+//			for( String name : appInfo.getPublishableGroupList() ) {
+//				if( !authorGroups.contains( name )) {
+//					authorGroups.add( name );
+//				}
+//			}
+//		}
+//		if( ListTools.isNotEmpty( category.getManageableGroupList() )) {
+//			for( String name : category.getManageableGroupList() ) {
+//				if( !authorGroups.contains( name )) {
+//					authorGroups.add( name );
+//				}
+//			}
+//		}
+//		if( ListTools.isNotEmpty( category.getPublishableGroupList() )) {
+//			for( String name : category.getPublishableGroupList() ) {
+//				if( !authorGroups.contains( name )) {
+//					authorGroups.add( name );
+//				}
+//			}
+//		}
+//		return authorGroups;
+//	}
+//
+//	private List<String> composeAuthorPersonsWithAppAndCagetory(AppInfo appInfo, CategoryInfo category) {
+//		List<String> authorPersons = new ArrayList<>();
+//		if( ListTools.isNotEmpty( appInfo.getManageablePersonList() )) {
+//			for( String name : appInfo.getManageablePersonList() ) {
+//				if( !authorPersons.contains( name )) {
+//					authorPersons.add( name );
+//				}
+//			}
+//		}
+//		if( ListTools.isNotEmpty( appInfo.getPublishablePersonList() )) {
+//			for( String name : appInfo.getPublishablePersonList() ) {
+//				if( !authorPersons.contains( name )) {
+//					authorPersons.add( name );
+//				}
+//			}
+//		}
+//		if( ListTools.isNotEmpty( category.getManageablePersonList() )) {
+//			for( String name : category.getManageablePersonList() ) {
+//				if( !authorPersons.contains( name )) {
+//					authorPersons.add( name );
+//				}
+//			}
+//		}
+//		if( ListTools.isNotEmpty( category.getPublishablePersonList() )) {
+//			for( String name : category.getPublishablePersonList() ) {
+//				if( !authorPersons.contains( name )) {
+//					authorPersons.add( name );
+//				}
+//			}
+//		}
+//		return authorPersons;
+//	}
+
+	
+
+	public static class Wo extends Document {
+
+		private static final long serialVersionUID = -5076990764713538973L;
+
+		public static WrapCopier<Document, Wo> copier = WrapCopierFactory.wo(Document.class, Wo.class, null, JpaObject.FieldsInvisible);
+
+		private WoControl control;
+
+		public WoControl getControl() {
+			return control;
+		}
+
+		public void setControl(WoControl control) {
+			this.control = control;
+		}		
+	}
+
+	public static class WoControl extends GsonPropertyObject {
+
+		@FieldDescribe("是否允许查看.")
+		private Boolean allowVisit = false;
+
+		@FieldDescribe("是否允许编辑.")
+		private Boolean allowEdit = false;
+
+		@FieldDescribe("是否允许删除.")
+		private Boolean allowDelete = false;
+
+		public Boolean getAllowVisit() {
+			return allowVisit;
+		}
+
+		public void setAllowVisit(Boolean allowVisit) {
+			this.allowVisit = allowVisit;
+		}
+
+		public Boolean getAllowEdit() {
+			return allowEdit;
+		}
+
+		public void setAllowEdit(Boolean allowEdit) {
+			this.allowEdit = allowEdit;
+		}
+
+		public Boolean getAllowDelete() {
+			return allowDelete;
+		}
+
+		public void setAllowDelete(Boolean allowDelete) {
+			this.allowDelete = allowDelete;
+		}		
+	}
+	
+//	public static class WoFileInfo extends FileInfo {
+//
+//		private static final long serialVersionUID = -5076990764713538973L;
+//
+//		public static List<String> Excludes = new ArrayList<String>();
+//
+//		private WoControl control = new WoControl();
+//
+//		public WoControl getControl() {
+//			return control;
+//		}
+//
+//		public void setControl(WoControl control) {
+//			this.control = control;
+//		}
+//		
+//		public static WrapCopier<FileInfo, WoFileInfo> copier = WrapCopierFactory.wo(FileInfo.class, WoFileInfo.class,
+//				null, JpaObject.FieldsInvisible);
+//
+//		private Long referencedCount;
+//
+//		public Long getReferencedCount() {
+//			return referencedCount;
+//		}
+//
+//		public void setReferencedCount(Long referencedCount) {
+//			this.referencedCount = referencedCount;
+//		}
+//	}
+//
+//	public static class WoLog extends Log {
+//
+//		private static final long serialVersionUID = -5076990764713538973L;
+//
+//		public static List<String> Excludes = new ArrayList<String>();
+//	}
+//
+//	public static class WoForm extends Form {
+//
+//		private static final long serialVersionUID = -5076990764713538973L;
+//
+//		public static List<String> Excludes = new ArrayList<String>();
+//	}
+	
+//	public static class WoControl extends GsonPropertyObject {
+//
+//		private Boolean allowRead = false;
+//		private Boolean allowEdit = false;
+//		private Boolean allowControl = false;
+//
+//		public Boolean getAllowRead() {
+//			return allowRead;
+//		}
+//
+//		public void setAllowRead(Boolean allowRead) {
+//			this.allowRead = allowRead;
+//		}
+//
+//		public Boolean getAllowEdit() {
+//			return allowEdit;
+//		}
+//
+//		public void setAllowEdit(Boolean allowEdit) {
+//			this.allowEdit = allowEdit;
+//		}
+//
+//		public Boolean getAllowControl() {
+//			return allowControl;
+//		}
+//
+//		public void setAllowControl(Boolean allowControl) {
+//			this.allowControl = allowControl;
+//		}
+//	}
+	
+//	private boolean read( WoFileInfo woFileInfo, EffectivePerson effectivePerson, List<String> identities, List<String> units) throws Exception {
+//		boolean value = false;
+//		if (effectivePerson.isPerson(woFileInfo.getCreatorUid())) {
+//			value = true;
+//		} else if (ListTools.isEmpty(woFileInfo.getReadIdentityList()) && ListTools.isEmpty(woFileInfo.getReadUnitList())) {
+//			value = true;
+//		} else if (ListTools.containsAny(identities, woFileInfo.getReadIdentityList()) || ListTools.containsAny(units, woFileInfo.getReadUnitList())) {
+//			value = true;
+//		} else if (ListTools.containsAny(identities, woFileInfo.getEditIdentityList()) || ListTools.containsAny(units, woFileInfo.getEditUnitList())) {
+//			value = true;
+//		} else {
+//			if (ListTools.containsAny(identities, woFileInfo.getControllerIdentityList()) || ListTools.containsAny(units, woFileInfo.getControllerUnitList() )) {
+//				value = true;
+//			}
+//		}
+//		return value;
+//	}
+//
+//	private boolean edit( WoFileInfo woFileInfo, EffectivePerson effectivePerson, List<String> identities, List<String> units) throws Exception {
+//		boolean value = false;
+//		if (effectivePerson.isPerson(woFileInfo.getCreatorUid())) {
+//			value = true;
+//		} else if (ListTools.isEmpty(woFileInfo.getEditIdentityList()) && ListTools.isEmpty(woFileInfo.getEditUnitList())) {
+//			value = true;
+//		} else {
+//			if (ListTools.containsAny(identities, woFileInfo.getEditIdentityList()) || ListTools.containsAny(units, woFileInfo.getEditUnitList())) {
+//				value = true;
+//			}
+//		}
+//		return value;
+//	}
+//
+//	private boolean control( WoFileInfo woFileInfo, EffectivePerson effectivePerson, List<String> identities, List<String> units)
+//			throws Exception {
+//		boolean value = false;
+//		if (effectivePerson.isPerson(woFileInfo.getCreatorUid())) {
+//			value = true;
+//		} else if (ListTools.isEmpty(woFileInfo.getControllerUnitList()) && ListTools.isEmpty(woFileInfo.getControllerIdentityList())) {
+//			value = true;
+//		} else {
+//			if (ListTools.containsAny(identities, woFileInfo.getControllerIdentityList()) || ListTools.containsAny(units, woFileInfo.getControllerUnitList())) {
+//				value = true;
+//			}
+//		}
+//		return value;
+//	}
+}

+ 0 - 1
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/ActionQueryGetDocument.java

@@ -76,7 +76,6 @@ public class ActionQueryGetDocument extends BaseAction {
 			wrapOutDocument = wo.getDocument();
 			result.setData(wo);
 		} else {
-			logger.debug(">>>>>>>>>>>>>get document '"+id+"' in database!" );			
 			if (check) {
 				try {
 					document = documentQueryService.get(id);

+ 1 - 2
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/ActionQueryListDraftNextWithFilter.java

@@ -24,8 +24,7 @@ public class ActionQueryListDraftNextWithFilter extends BaseAction {
 	private static  Logger logger = LoggerFactory.getLogger(ActionQueryListDraftNextWithFilter.class);
 
 	@SuppressWarnings("unchecked")
-	protected ActionResult<List<Wo>> execute(HttpServletRequest request, String id, Integer count,
-			JsonElement jsonElement, EffectivePerson effectivePerson) {
+	protected ActionResult<List<Wo>> execute(HttpServletRequest request, String id, Integer count, JsonElement jsonElement, EffectivePerson effectivePerson) {
 		ActionResult<List<Wo>> result = new ActionResult<>();
 		List<Wo> wos = null;
 		List<Document> documentList = null;

+ 156 - 0
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/ActionQueryListVisiblePersons.java

@@ -0,0 +1,156 @@
+package com.x.cms.assemble.control.jaxrs.document;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.x.base.core.project.cache.ApplicationCache;
+import com.x.base.core.project.http.ActionResult;
+import com.x.base.core.project.http.EffectivePerson;
+import com.x.base.core.project.jaxrs.WrapStringList;
+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.cms.assemble.control.service.ReviewService;
+import com.x.cms.assemble.control.service.UserManagerService;
+import com.x.cms.core.entity.AppInfo;
+import com.x.cms.core.entity.CategoryInfo;
+import com.x.cms.core.entity.Document;
+
+import net.sf.ehcache.Element;
+
+public class ActionQueryListVisiblePersons extends BaseAction {
+
+	private static  Logger logger = LoggerFactory.getLogger(ActionQueryListVisiblePersons.class);
+
+	/**
+	 * 根据文档ID,获取该文档所有的可见者列表
+	 * @param request
+	 * @param id
+	 * @param effectivePerson
+	 * @return
+	 * @throws Exception
+	 */
+	protected ActionResult<Wo> execute(HttpServletRequest request, String id, EffectivePerson effectivePerson)
+			throws Exception {
+		ActionResult<Wo> result = new ActionResult<>();
+		Wo wo = new Wo();
+		AppInfo appInfo = null;
+		CategoryInfo categoryInfo = null;
+		Document document = null;
+		Boolean check = true;
+		String personName = effectivePerson.getDistinguishedName();
+		
+		if ( StringUtils.isEmpty(id)) {
+			check = false;
+			Exception exception = new ExceptionDocumentIdEmpty();
+			result.error(exception);
+		}
+		
+		String cacheKey = ApplicationCache.concreteCacheKey( id, "ActionQueryListVisiblePersons" );
+		Element element = cache.get(cacheKey);
+		if ((null != element) && (null != element.getObjectValue())) {
+			wo = (Wo) element.getObjectValue();
+			result.setData(wo);
+		} else {
+			if (check) {
+				try {
+					document = documentQueryService.get(id);
+					if (document == null) {
+						check = false;
+						Exception exception = new ExceptionDocumentNotExists(id);
+						result.error(exception);
+					}
+				} catch (Exception e) {
+					check = false;
+					Exception exception = new ExceptionDocumentInfoProcess(e, "文档信息获取操作时发生异常。Id:" + id + ", Name:" + personName);
+					result.error(exception);
+					logger.error(e, effectivePerson, request, null);
+				}
+			}
+			if (check) {
+				try {
+					categoryInfo = categoryInfoServiceAdv.get( document.getCategoryId() );
+					if (categoryInfo == null) {
+						check = false;
+						Exception exception = new ExceptionCategoryInfoNotExists(document.getCategoryId());
+						result.error(exception);
+					}
+				} catch (Exception e) {
+					check = false;
+					Exception exception = new ExceptionDocumentInfoProcess(e, "系统在根据ID查询分类信息时发生异常!ID:" + document.getCategoryId());
+					result.error(exception);
+					logger.error(e, effectivePerson, request, null);
+				}
+			}
+
+			if (check) {
+				try {
+					appInfo = appInfoServiceAdv.get( document.getAppId() );
+					if (appInfo == null) {
+						check = false;
+						Exception exception = new ExceptionAppInfoNotExists(document.getAppId());
+						result.error(exception);
+					}
+				} catch (Exception e) {
+					check = false;
+					Exception exception = new ExceptionDocumentInfoProcess(e, "系统在根据ID查询应用栏目信息时发生异常!ID:" + document.getAppId());
+					result.error(exception);
+					logger.error(e, effectivePerson, request, null);
+				}
+			}
+			
+			if (check) {
+				//计算该文档有多少阅读者
+				ReviewService reviewService = new ReviewService();
+				List<String> persons = reviewService.listPermissionPersons( appInfo, categoryInfo, document );
+				if( ListTools.isNotEmpty( persons )) {
+					//有可能是*, 一般是所有的人员标识列表
+					if( persons.contains( "*" )) {
+						List<String> allPersons = listPersonWithUnit( document.getCreatorTopUnitName() );
+						if( ListTools.isNotEmpty( allPersons )) {
+							for( String person : persons ) {
+								if( StringUtils.equals( "*" , person ) && allPersons.contains( person )) {
+									allPersons.add( person );
+								}
+							}
+						}
+						persons = allPersons;
+					}
+				}
+				if( persons == null ) {
+					persons = new ArrayList<>();
+				}
+				wo.setValueList(persons);
+				result.setData(wo);
+				result.setCount( Long.parseLong( persons.size() + ""));
+				cache.put(new Element(cacheKey, wo));
+			}
+		}
+		return result;
+	}
+	
+	/**
+	 * 根据组织名称,获取该组织下所有的人员标识
+	 * @param unitName
+	 * @return
+	 */
+	private List<String> listPersonWithUnit(String unitName) {
+		UserManagerService  userManagerService = new UserManagerService();
+		List<String> persons = null;
+		try {
+			persons = userManagerService.listPersonWithUnit(unitName);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return persons;
+	}
+
+	public static class Wo extends WrapStringList {
+
+	}
+
+}

+ 185 - 0
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/ActionQueryListWithFilterPaging.java

@@ -0,0 +1,185 @@
+package com.x.cms.assemble.control.jaxrs.document;
+
+import com.google.gson.JsonElement;
+import com.x.base.core.entity.JpaObject;
+import com.x.base.core.project.bean.WrapCopier;
+import com.x.base.core.project.bean.WrapCopierFactory;
+import com.x.base.core.project.http.ActionResult;
+import com.x.base.core.project.http.EffectivePerson;
+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.cms.core.entity.Document;
+import com.x.cms.core.entity.tools.filter.QueryFilter;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ActionQueryListWithFilterPaging extends BaseAction {
+
+	private static  Logger logger = LoggerFactory.getLogger(ActionQueryListWithFilterPaging.class);
+
+	protected ActionResult<List<Wo>> execute( HttpServletRequest request, Integer page, Integer size, JsonElement jsonElement, EffectivePerson effectivePerson ) {
+		ActionResult<List<Wo>> result = new ActionResult<>();		
+		Long total = 0L;
+		Wi wi = null;
+		List<Wo> wos = new ArrayList<>();
+		List<Document> searchResultList = new ArrayList<>();
+		Boolean check = true;
+		Boolean isManager = false;
+		String personName = effectivePerson.getDistinguishedName();
+		QueryFilter queryFilter = null;
+		
+		try {
+			wi = this.convertToWrapIn( jsonElement, Wi.class );
+		} catch (Exception e ) {
+			check = false;
+			Exception exception = new ExceptionDocumentInfoProcess( e, "系统在将JSON信息转换为对象时发生异常。JSON:" + jsonElement.toString() );
+			result.error( exception );
+			logger.error( e, effectivePerson, request, null);
+		}
+		if ( wi == null ) { wi = new Wi(); }
+		
+		if( StringUtils.isEmpty( wi.getDocumentType() )) {
+			wi.setDocumentType( "信息" );
+		}
+		
+		if( StringUtils.isNotEmpty( wi.getOrderField() )) {
+			wi.setOrderField( "createTime" );
+		}
+		
+		if( StringUtils.isNotEmpty( wi.getOrderType() )) {
+			wi.setOrderField( "DESC" );
+		}
+		
+		if( ListTools.isNotEmpty( wi.getStatusList() )) {
+			List<String> status = new ArrayList<>();
+			status.add( "published" );
+			wi.setStatusList( status );
+		}
+		
+		if (check) {
+			try {
+				queryFilter = wi.getQueryFilter();
+			} catch (Exception e) {
+				check = false;
+				Exception exception = new ExceptionDocumentInfoProcess(e, "系统在获取查询条件信息时发生异常。");
+				result.error(exception);
+				logger.error(e, effectivePerson, request, null);
+			}
+		}
+		
+		if( check ) {
+			try {
+				if( effectivePerson.isManager() || userManagerService.isHasPlatformRole( effectivePerson.getDistinguishedName(), "CMSManager" )) {
+					isManager = true;
+				}
+			} catch (Exception e) {
+				check = false;
+				Exception exception = new ExceptionDocumentInfoProcess(e, "系统在判断用户是否是管理时发生异常。");
+				result.error(exception);
+				logger.error(e, effectivePerson, request, null);
+			}
+		}
+		
+		if (check) {
+			// 从Review表中查询符合条件的对象总数
+			try {
+				if( isManager ) {
+					personName = null;
+				}
+				total = documentQueryService.countWithCondition( personName, queryFilter );
+			} catch (Exception e) {
+				check = false;
+				Exception exception = new ExceptionDocumentInfoProcess(e, "系统在获取用户可查询到的文档数据条目数量时发生异常。");
+				result.error(exception);
+				logger.error(e, effectivePerson, request, null);
+			}
+		}
+		
+		if (check) {
+			//document和Review除了sequence还有5个排序列支持title, appAlias, categoryAlias, categoryName, creatorUnitName的分页查询
+			//除了sequence和title, appAlias, categoryAlias, categoryName, creatorUnitName之外,其他的列排序全部在内存进行分页
+			try {
+				if( isManager ) {
+					personName = null;
+				}
+				searchResultList = documentQueryService.listPagingWithCondition( personName, wi.getOrderField(), wi.getOrderType(), queryFilter, page, size );
+			} catch (Exception e) {
+				check = false;
+				Exception exception = new ExceptionDocumentInfoProcess(e, "系统在根据用户可访问的文档ID列表对文档进行分页查询时发生异常。");
+				result.error(exception);
+				logger.error(e, effectivePerson, request, null);
+			}
+		}
+		
+		if (check) {
+			if ( searchResultList != null ) {
+				Wo wo = null;
+				for( Document document : searchResultList ) {					
+					try {
+						wo = Wo.copier.copy( document );						
+						if( wo.getCreatorPerson() != null && !wo.getCreatorPerson().isEmpty() ) {
+							wo.setCreatorPersonShort( wo.getCreatorPerson().split( "@" )[0]);
+						}
+						if( wo.getCreatorUnitName() != null && !wo.getCreatorUnitName().isEmpty() ) {
+							wo.setCreatorUnitNameShort( wo.getCreatorUnitName().split( "@" )[0]);
+						}
+						if( wo.getCreatorTopUnitName() != null && !wo.getCreatorTopUnitName().isEmpty() ) {
+							wo.setCreatorTopUnitNameShort( wo.getCreatorTopUnitName().split( "@" )[0]);
+						}
+						if( wi.getNeedData() ) {
+							//需要组装数据
+							wo.setData( documentQueryService.getDocumentData( document ) );
+						}
+					} catch (Exception e) {
+						check = false;
+						Exception exception = new ExceptionDocumentInfoProcess(e, "系统获取文档数据内容信息时发生异常。Id:" + document.getCategoryId());
+						result.error(exception);
+						logger.error(e, effectivePerson, request, null);
+					}
+					wos.add( wo );
+				}
+			}
+		}
+		result.setCount(total);
+		result.setData(wos);
+		return result;
+	}	
+
+	public class DocumentCacheForFilter {
+
+		private Long total = 0L;		
+		private List<Wo> documentList = null;
+
+		public Long getTotal() {
+			return total;
+		}
+
+		public void setTotal(Long total) {
+			this.total = total;
+		}
+
+		public List<Wo> getDocumentList() {
+			return documentList;
+		}
+
+		public void setDocumentList(List<Wo> documentList) {
+			this.documentList = documentList;
+		}	
+	}
+	
+	public static class Wi extends WrapInDocumentFilter{
+		
+	}
+	
+	public static class Wo extends WrapOutDocumentList {
+		
+		public static List<String> Excludes = new ArrayList<String>();
+		
+		public static WrapCopier<Document, Wo> copier = WrapCopierFactory.wo( Document.class, Wo.class, null,JpaObject.FieldsInvisible);
+		
+	}
+}

+ 73 - 5
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/document/DocumentAction.java

@@ -274,20 +274,20 @@ public class DocumentAction extends StandardJaxrsAction{
 		asyncResponse.resume(ResponseFactory.getDefaultActionResultResponse(result));
 	}
 	
-	@JaxrsMethodDescribe(value = "根据ID修改信息发布文档状态为已发布.", action = ActionPersistPublishDocument.class)
+	@JaxrsMethodDescribe(value = "根据文档ID正式发布文档,并且使用Message通知所有的阅读者。", action = ActionPersistPublishAndNotify.class)
 	@PUT
 	@Path("publish/{id}")
 	@Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
 	@Consumes(MediaType.APPLICATION_JSON)
-	public void persist_publish( @Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request, 
-			@JaxrsParameterDescribe("信息文档ID") @PathParam("id") String id, JsonElement jsonElement ) {		
+	public void persist_publishAndNotify( @Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request, 
+			@JaxrsParameterDescribe("文档ID") @PathParam("id") String id, JsonElement jsonElement ) {		
 		EffectivePerson effectivePerson = this.effectivePerson( request );
-		ActionResult<ActionPersistPublishDocument.Wo> result = new ActionResult<>();
+		ActionResult<ActionPersistPublishAndNotify.Wo> result = new ActionResult<>();
 		Boolean check = true;
 
 		if( check ){
 			try {
-				result = new ActionPersistPublishDocument().execute( request, id, effectivePerson, jsonElement );
+				result = new ActionPersistPublishAndNotify().execute( request, id, effectivePerson, jsonElement );
 			} catch (Exception e) {
 				result = new ActionResult<>();
 				result.error( e );
@@ -566,4 +566,72 @@ public class DocumentAction extends StandardJaxrsAction{
 		}
 		asyncResponse.resume(ResponseFactory.getDefaultActionResultResponse(result));
 	}
+
+	@JaxrsMethodDescribe(value = "列示符合过滤条件的已发布的信息内容, 下一页.", action = ActionQueryListWithFilterPaging.class)
+	@PUT
+	@Path("filter/list/{page}/size/{size}")
+	@Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public void query_listWithFilterPaging( @Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request,
+											@JaxrsParameterDescribe("分页") @PathParam("page") Integer page,
+											@JaxrsParameterDescribe("数量") @PathParam("size") Integer size, JsonElement jsonElement) {
+		EffectivePerson effectivePerson = this.effectivePerson( request );
+		ActionResult<List<ActionQueryListWithFilterPaging.Wo>> result = new ActionResult<>();
+		Boolean check = true;
+
+		if( check ){
+			try {
+				result = new ActionQueryListWithFilterPaging().execute( request, page, size, jsonElement, effectivePerson );
+			} catch (Exception e) {
+				result = new ActionResult<>();
+				result.error( e );
+				logger.error( e, effectivePerson, request, null);
+			}
+		}
+		asyncResponse.resume(ResponseFactory.getDefaultActionResultResponse(result));
+	}
+	
+	@JaxrsMethodDescribe(value = "获取文件基础信息和访问控制信息.", action = ActionQueryGetControl.class)
+	@GET
+	@Path("{id}/control")
+	@Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public void query_getControl( @Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request, 
+			@JaxrsParameterDescribe("信息文档ID") @PathParam("id") String id ) {
+		EffectivePerson effectivePerson = this.effectivePerson( request );
+		ActionResult<ActionQueryGetControl.Wo> result = new ActionResult<>();
+		Boolean check = true;
+		if( check ){
+			try {
+				result = new ActionQueryGetControl().execute( request, id, effectivePerson );
+			} catch (Exception e) {
+				result = new ActionResult<>();
+				result.error( e );
+				logger.error( e, effectivePerson, request, null);
+			}
+		}
+		asyncResponse.resume(ResponseFactory.getDefaultActionResultResponse(result));
+	}
+	
+	@JaxrsMethodDescribe(value = "获取文件可见范围内的所有人员.", action = ActionQueryListVisiblePersons.class)
+	@GET
+	@Path("{id}/persons")
+	@Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public void query_getVisiblePersons( @Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request, 
+			@JaxrsParameterDescribe("信息文档ID") @PathParam("id") String id ) {
+		EffectivePerson effectivePerson = this.effectivePerson( request );
+		ActionResult<ActionQueryListVisiblePersons.Wo> result = new ActionResult<>();
+		Boolean check = true;
+		if( check ){
+			try {
+				result = new ActionQueryListVisiblePersons().execute( request, id, effectivePerson );
+			} catch (Exception e) {
+				result = new ActionResult<>();
+				result.error( e );
+				logger.error( e, effectivePerson, request, null);
+			}
+		}
+		asyncResponse.resume(ResponseFactory.getDefaultActionResultResponse(result));
+	}
 }

+ 204 - 0
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/queue/QueueSendDocumentNotify.java

@@ -0,0 +1,204 @@
+package com.x.cms.assemble.control.queue;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.x.base.core.container.EntityManagerContainer;
+import com.x.base.core.container.factory.EntityManagerContainerFactory;
+import com.x.base.core.entity.JpaObject;
+import com.x.base.core.project.annotation.FieldDescribe;
+import com.x.base.core.project.bean.WrapCopier;
+import com.x.base.core.project.bean.WrapCopierFactory;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+import com.x.base.core.project.queue.AbstractQueue;
+import com.x.base.core.project.tools.ListTools;
+import com.x.cms.assemble.control.MessageFactory;
+import com.x.cms.assemble.control.service.ReviewService;
+import com.x.cms.assemble.control.service.UserManagerService;
+import com.x.cms.core.entity.AppInfo;
+import com.x.cms.core.entity.CategoryInfo;
+import com.x.cms.core.entity.Document;
+
+/**
+ * Document正式发布后,向所有的阅读者推送消息通知
+ */
+public class QueueSendDocumentNotify extends AbstractQueue<Document> {
+	
+	private static  Logger logger = LoggerFactory.getLogger( QueueSendDocumentNotify.class );
+
+	public void execute( Document document ) throws Exception {
+		if( document == null ) {
+			return;
+		}
+		logger.debug("send publish notify for new document:" + document.getTitle() );
+		try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+			AppInfo appInfo = emc.find( document.getAppId(), AppInfo.class );
+			CategoryInfo category = emc.find( document.getCategoryId(), CategoryInfo.class );
+			if( appInfo != null && category != null ) {
+				//计算该文档有多少阅读者
+				ReviewService reviewService = new ReviewService();
+				List<String> persons = reviewService.listPermissionPersons( appInfo, category, document );
+				if( ListTools.isNotEmpty( persons )) {
+					//有可能是*, 一般是所有的人员标识列表
+					if( persons.contains( "*" )) {
+						List<String> allPersons = listPersonWithUnit( document.getCreatorTopUnitName() );
+						if( ListTools.isNotEmpty( allPersons )) {
+							for( String person : persons ) {
+								if( StringUtils.equals( "*" , person ) && allPersons.contains( person )) {
+									allPersons.add( person );
+								}
+							}
+						}
+						persons = allPersons;
+					}
+				}
+				if( ListTools.isNotEmpty( persons )) {
+					MessageWo wo = MessageWo.copier.copy(document);
+					for( String person : persons ) {
+						if( !StringUtils.equals( "*", person  )) {
+							MessageFactory.cms_publish(person, wo);
+						}
+					}
+					logger.debug("send total count:" + persons.size()  );
+				}
+				logger.debug("send publish notify for new document completed! " );
+			}
+		}
+	}
+
+	/**
+	 * 根据组织名称,获取该组织下所有的人员标识
+	 * @param unitName
+	 * @return
+	 */
+	private List<String> listPersonWithUnit(String unitName) {
+		UserManagerService  userManagerService = new UserManagerService();
+		List<String> persons = null;
+		try {
+			persons = userManagerService.listPersonWithUnit(unitName);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return persons;
+	}
+	
+	public static class MessageWo{
+
+		public static List<String> Excludes = new ArrayList<String>();
+
+		public static WrapCopier<Document, MessageWo> copier = WrapCopierFactory.wo(Document.class, MessageWo.class, null, JpaObject.FieldsInvisible);
+
+		@FieldDescribe("数据库主键,自动生成.")
+		private String id;
+
+		@FieldDescribe("文档摘要")
+		private String summary;
+
+		@FieldDescribe("文档标题")
+		private String title;
+
+		@FieldDescribe("文档类型,跟随分类类型,信息 | 数据")
+		private String documentType = "信息";
+
+		@FieldDescribe("栏目ID")
+		private String appId;
+
+		@FieldDescribe("栏目名称")
+		private String appName;
+
+		@FieldDescribe("分类ID")
+		private String categoryId;
+
+		@FieldDescribe("分类名称")
+		private String categoryName;
+
+		@FieldDescribe("创建人,可能为空,如果由系统创建。")
+		private String creatorPerson;
+
+		@FieldDescribe("文档发布时间")
+		private Date publishTime;
+
+		public String getId() {
+			return id;
+		}
+
+		public void setId(String id) {
+			this.id = id;
+		}
+
+		public String getSummary() {
+			return summary;
+		}
+
+		public void setSummary(String summary) {
+			this.summary = summary;
+		}
+
+		public String getTitle() {
+			return title;
+		}
+
+		public void setTitle(String title) {
+			this.title = title;
+		}
+
+		public String getDocumentType() {
+			return documentType;
+		}
+
+		public void setDocumentType(String documentType) {
+			this.documentType = documentType;
+		}
+
+		public String getAppId() {
+			return appId;
+		}
+
+		public void setAppId(String appId) {
+			this.appId = appId;
+		}
+
+		public String getAppName() {
+			return appName;
+		}
+
+		public void setAppName(String appName) {
+			this.appName = appName;
+		}
+
+		public String getCategoryId() {
+			return categoryId;
+		}
+
+		public void setCategoryId(String categoryId) {
+			this.categoryId = categoryId;
+		}
+
+		public String getCategoryName() {
+			return categoryName;
+		}
+
+		public void setCategoryName(String categoryName) {
+			this.categoryName = categoryName;
+		}
+
+		public String getCreatorPerson() {
+			return creatorPerson;
+		}
+
+		public void setCreatorPerson(String creatorPerson) {
+			this.creatorPerson = creatorPerson;
+		}
+
+		public Date getPublishTime() {
+			return publishTime;
+		}
+
+		public void setPublishTime(Date publishTime) {
+			this.publishTime = publishTime;
+		}
+	}
+}

+ 1 - 0
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/service/CmsBatchOperationProcessService.java

@@ -11,6 +11,7 @@ import com.x.base.core.entity.annotation.CheckRemoveType;
 import com.x.base.core.project.cache.ApplicationCache;
 import com.x.base.core.project.logger.Logger;
 import com.x.base.core.project.logger.LoggerFactory;
+import com.x.base.core.project.message.MessageConnector;
 import com.x.base.core.project.tools.ListTools;
 import com.x.cms.assemble.control.Business;
 import com.x.cms.core.entity.AppInfo;

+ 8 - 1
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/service/DocumentPersistService.java

@@ -237,8 +237,15 @@ public class DocumentPersistService {
 		try ( EntityManagerContainer emc = EntityManagerContainerFactory.instance().create() ) {
 			Document document = emc.find( id, Document.class );
 			if( document != null ) {
+//				emc.beginTransaction( Item.class );
 				emc.beginTransaction( Document.class );
-				document.setIsTop( true );
+				document.setIsTop( true );			
+				
+				DocumentDataHelper documentDataHelper = new DocumentDataHelper( emc, document );
+				Data data = documentDataHelper.get();
+				data.setDocument( document );
+				documentDataHelper.update( data );
+				
 				emc.commit();
 			}
 			

+ 51 - 0
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/service/DocumentQueryService.java

@@ -44,6 +44,17 @@ public class DocumentQueryService {
 		}
 	}
 	
+	public Long getViewableReview( String id , String personName ) throws Exception {
+		if( StringUtils.isEmpty( id ) ){
+			throw new Exception("id is null!");
+		}
+		try ( EntityManagerContainer emc = EntityManagerContainerFactory.instance().create() ) {
+			return reviewService.countViewableWithFilter(emc, personName, new QueryFilter() );
+		} catch ( Exception e ) {
+			throw e;
+		}
+	}
+	
 	public String getSequence(String id) throws Exception {
 		if( StringUtils.isEmpty( id ) ){
 			return null;
@@ -384,6 +395,46 @@ public class DocumentQueryService {
 			throw e;
 		}
 	}
+
+	/**
+	 * 对Document信息进行分页查询(包含权限)
+	 * @param personName
+	 * @param orderField
+	 * @param orderType
+	 * @param queryFilter
+	 * @param adjustPage
+	 * @param pageSize
+	 * @return
+	 * @throws Exception
+	 */
+	public List<Document> listPagingWithCondition( String personName, String orderField, String orderType, QueryFilter queryFilter, Integer adjustPage,
+																  Integer pageSize ) throws Exception {
+		if( pageSize == 0 ) { pageSize = 20; }
+		//按正常逻辑根据序列进行分页查询
+		try ( EntityManagerContainer emc = EntityManagerContainerFactory.instance().create() ) {
+			Business business = new Business(emc);
+			return business.getDocumentFactory().listPagingWithCondition(personName, orderField, orderType, queryFilter, adjustPage, pageSize);
+		} catch ( Exception e ) {
+			throw e;
+		}
+	}
+
+	/**
+	 * 根据条件统计Document信息(包含权限)
+	 * @param personName
+	 * @param queryFilter
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countWithCondition( String personName, QueryFilter queryFilter) throws Exception {
+		//按正常逻辑根据序列进行分页查询
+		try ( EntityManagerContainer emc = EntityManagerContainerFactory.instance().create() ) {
+			Business business = new Business(emc);
+			return business.getDocumentFactory().countWithCondition(personName, queryFilter);
+		} catch ( Exception e ) {
+			throw e;
+		}
+	}
 	
 	/**
 	 * 根据条件按指定的排序方式查询指定数量的文档信息列表

+ 2 - 2
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/service/PermissionOperateService.java

@@ -52,7 +52,7 @@ public class PermissionOperateService {
 			permissionList.add( new PermissionInfo( PermissionName.READER, "所有人", "所有人", "所有人" ) );
 		} else {
 			for( PermissionInfo p : readerList ) {
-				System.out.println(">>>>>readerList:" + p.getPermissionObjectCode());
+//				System.out.println(">>>>>readerList:" + p.getPermissionObjectCode());
 				new_permissionInfo = createPermissionInfo( PermissionName.READER, p.getPermissionObjectType(), p.getPermissionObjectName(), p.getPermissionObjectName() );
 				if( new_permissionInfo != null ) {
 					new_permissionInfo.setPermissionObjectCode( new_permissionInfo.getPermissionObjectName() );
@@ -64,7 +64,7 @@ public class PermissionOperateService {
 		//将所有的作者都添加到阅读者里去
 		if ( authorList != null && !authorList.isEmpty() ) {
 			for( PermissionInfo p : authorList ) {
-				System.out.println(">>>>>authorList:" + p.getPermissionObjectCode());
+//				System.out.println(">>>>>authorList:" + p.getPermissionObjectCode());
 				new_permissionInfo = createPermissionInfo( PermissionName.READER, p.getPermissionObjectType(), p.getPermissionObjectName(), p.getPermissionObjectName() );
 				if( new_permissionInfo != null ) {
 					new_permissionInfo.setPermissionObjectCode( new_permissionInfo.getPermissionObjectName() );

+ 1 - 1
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/service/ReviewService.java

@@ -145,7 +145,7 @@ public class ReviewService {
 	 * @return
 	 * @throws Exception 
 	 */
-	private List<String> listPermissionPersons( AppInfo appInfo, CategoryInfo categoryInfo, Document document ) throws Exception {
+	public List<String> listPermissionPersons( AppInfo appInfo, CategoryInfo categoryInfo, Document document ) throws Exception {
 		List<String> permissionObjs = new ArrayList<>();
 		Boolean documentHasPermissionControl = documentViewPermissionExists(document);
 		Boolean categoryHasPermissionControl = categoryPermissionExists(categoryInfo);

+ 22 - 0
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/timertask/Timertask_CheckDocumentReviewStatus.java

@@ -0,0 +1,22 @@
+package com.x.cms.assemble.control.timertask;
+
+import org.quartz.JobExecutionContext;
+
+import com.x.base.core.project.schedule.AbstractJob;
+import com.x.cms.assemble.control.service.CmsBatchOperationPersistService;
+
+/**
+ * 检查文档是否已经review过了
+ *
+ */
+public class Timertask_CheckDocumentReviewStatus extends AbstractJob {
+	
+	private CmsBatchOperationPersistService cmsBatchOperationPersistService;
+
+	@Override
+	public void schedule(JobExecutionContext jobExecutionContext) throws Exception {
+		cmsBatchOperationPersistService = new CmsBatchOperationPersistService();
+		cmsBatchOperationPersistService.checkDocumentReviewStatus();
+	}
+
+}

+ 22 - 0
o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/timertask/Timertask_InitOperationRunning.java

@@ -0,0 +1,22 @@
+package com.x.cms.assemble.control.timertask;
+
+import org.quartz.JobExecutionContext;
+
+import com.x.base.core.project.schedule.AbstractJob;
+import com.x.cms.assemble.control.service.CmsBatchOperationPersistService;
+
+/**
+ * 检查是否有未执行的批处理任务
+ *
+ */
+public class Timertask_InitOperationRunning extends AbstractJob {
+	
+	private CmsBatchOperationPersistService cmsBatchOperationPersistService;
+
+	@Override
+	public void schedule(JobExecutionContext jobExecutionContext) throws Exception {
+		cmsBatchOperationPersistService = new CmsBatchOperationPersistService();
+		cmsBatchOperationPersistService.initOperationRunning();
+	}
+
+}

+ 8 - 2
o2server/x_cms_assemble_control/src/main/webapp/jest/describe.js

@@ -407,7 +407,10 @@ Describe.prototype = {
 																data[i.name] = $('#' + i.name, '#ins').val();
 															}
 														} else {
-															data[i.name] = $.parseJSON($('#' + i.name, '#ins').val());
+															if( $('#' + i.name, '#ins').val() != "" ) {
+																data[i.name] = $.parseJSON($('#' + i.name, '#ins').val());
+															}
+															
 														}
 													}
 												});
@@ -431,7 +434,10 @@ Describe.prototype = {
 																data[i.name] = $('#' + i.name, '#ins').val();
 															}
 														} else {
-															data[i.name] = $.parseJSON($('#' + i.name, '#ins').val());
+															if( $('#' + i.name, '#ins').val() != "" ) {
+																data[i.name] = $.parseJSON($('#' + i.name, '#ins').val());
+															}
+															
 														}
 													}
 												});

+ 15 - 0
o2server/x_cms_core_entity/src/main/java/com/x/cms/core/entity/AppInfo.java

@@ -187,6 +187,13 @@ public class AppInfo extends SliceJpaObject {
 	@Index(name = TABLE + IndexNameMiddle + allPeoplePublish_FIELDNAME)
 	private Boolean allPeoplePublish = true;
 	
+	public static final String sendNotify_FIELDNAME = "sendNotify";
+	@FieldDescribe("发布文档后是否通知阅读者:true | false")
+	@Column( name = ColumnNamePrefix + sendNotify_FIELDNAME)
+	@CheckPersist(allowEmpty = true)
+	@Index(name = TABLE + IndexNameMiddle + sendNotify_FIELDNAME)
+	private Boolean sendNotify = true;
+	
 	public static final String categoryList_FIELDNAME = "categoryList";
 	@FieldDescribe("分类列表")
 	@PersistentCollection(fetch = FetchType.EAGER)
@@ -738,4 +745,12 @@ public class AppInfo extends SliceJpaObject {
 		this.allPeoplePublish = allPeoplePublish;
 	}
 
+	public Boolean getSendNotify() {
+		return sendNotify;
+	}
+
+	public void setSendNotify(Boolean sendNotify) {
+		this.sendNotify = sendNotify;
+	}
+
 }

+ 15 - 0
o2server/x_cms_core_entity/src/main/java/com/x/cms/core/entity/CategoryInfo.java

@@ -280,6 +280,13 @@ public class CategoryInfo extends SliceJpaObject {
 	@CheckPersist(allowEmpty = true)
 	@Index(name = TABLE + IndexNameMiddle + anonymousAble_FIELDNAME)
 	private Boolean anonymousAble = true;
+	
+	public static final String sendNotify_FIELDNAME = "sendNotify";
+	@FieldDescribe("发布文档后是否通知阅读者:true | false")
+	@Column( name = ColumnNamePrefix + sendNotify_FIELDNAME)
+	@CheckPersist(allowEmpty = true)
+	@Index(name = TABLE + IndexNameMiddle + sendNotify_FIELDNAME)
+	private Boolean sendNotify = true;
 
 	public static final String viewablePersonList_FIELDNAME = "viewablePersonList";
 	@FieldDescribe("发布可见人员")
@@ -970,4 +977,12 @@ public class CategoryInfo extends SliceJpaObject {
 		this.anonymousAble = anonymousAble;
 	}
 
+	public Boolean getSendNotify() {
+		return sendNotify;
+	}
+
+	public void setSendNotify(Boolean sendNotify) {
+		this.sendNotify = sendNotify;
+	}
+
 }

+ 5 - 2
o2server/x_cms_core_entity/src/main/java/com/x/cms/core/entity/tools/CriteriaBuilderTools.java

@@ -87,6 +87,9 @@ public class CriteriaBuilderTools {
 	 */
 	public static <T extends JpaObject, T_ extends SliceJpaObject_> Predicate composePredicateWithQueryFilter( 
 			Class<T_> cls_, CriteriaBuilder cb, Predicate p, Root<T> root,  QueryFilter queryFilter ) throws NoSuchFieldException, SecurityException {
+		if( queryFilter == null ) {
+			queryFilter = new QueryFilter();
+		}
 		//组装条件
 		if( ListTools.isNotEmpty( queryFilter.getEqualsTerms() )) {
 			for( EqualsTerm term : queryFilter.getEqualsTerms() ) {
@@ -167,9 +170,9 @@ public class CriteriaBuilderTools {
 					continue;
 				}
 				if( "and".equalsIgnoreCase( queryFilter.getJoinType() )) {
-					p = CriteriaBuilderTools.predicate_and( cb, p, cb.like( root.get( cls_.getDeclaredField( term.getName() ).getName() ), term.getValue() ));
+					p = CriteriaBuilderTools.predicate_and( cb, p, cb.like( root.get( cls_.getDeclaredField( term.getName() ).getName() ), "%"+term.getValue()+"%" ));
 				} else if( "or".equalsIgnoreCase( queryFilter.getJoinType() )) {
-					p = CriteriaBuilderTools.predicate_or( cb, p, cb.like( root.get( cls_.getDeclaredField( term.getName() ).getName() ), term.getValue() ));
+					p = CriteriaBuilderTools.predicate_or( cb, p, cb.like( root.get( cls_.getDeclaredField( term.getName() ).getName() ), "%"+term.getValue()+"%" ));
 				}
 			}
 		}

+ 270 - 273
o2server/x_console/src/main/java/com/x/server/console/Main.java

@@ -4,8 +4,6 @@ import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileReader;
 import java.io.InputStreamReader;
-import java.io.PipedInputStream;
-import java.io.PipedOutputStream;
 import java.io.RandomAccessFile;
 import java.lang.reflect.Method;
 import java.net.URL;
@@ -19,6 +17,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
+import java.util.concurrent.LinkedBlockingQueue;
 import java.util.regex.Matcher;
 
 import org.apache.commons.io.FileUtils;
@@ -76,303 +75,301 @@ public class Main {
 		if (null == Config.currentNode()) {
 			throw new Exception("无法找到当前节点,请检查config/node_{name}.json与local/node.cfg文件内容中的名称是否一致.");
 		}
-		try (PipedInputStream pipedInput = new PipedInputStream();
-				PipedOutputStream pipedOutput = new PipedOutputStream(pipedInput)) {
-			new Thread() {
-				/* 文件中的命令输出到解析器 */
-				public void run() {
-					try (RandomAccessFile raf = new RandomAccessFile(Config.base() + "/command.swap", "rw")) {
-						FileChannel fc = raf.getChannel();
-						MappedByteBuffer mbb = fc.map(MapMode.READ_WRITE, 0, 256);
-						byte[] fillBytes = new byte[256];
-						byte[] readBytes = new byte[256];
-						Arrays.fill(fillBytes, (byte) 0);
+		final LinkedBlockingQueue<String> commandQueue = new LinkedBlockingQueue<>();
+//		try (PipedInputStream pipedInput = new PipedInputStream();
+//				PipedOutputStream pipedOutput = new PipedOutputStream(pipedInput)) {
+		new Thread() {
+			/* 文件中的命令输出到解析器 */
+			public void run() {
+				try (RandomAccessFile raf = new RandomAccessFile(Config.base() + "/command.swap", "rw")) {
+					FileChannel fc = raf.getChannel();
+					MappedByteBuffer mbb = fc.map(MapMode.READ_WRITE, 0, 256);
+					byte[] fillBytes = new byte[256];
+					byte[] readBytes = new byte[256];
+					Arrays.fill(fillBytes, (byte) 0);
+					mbb.put(fillBytes);
+					FileLock flock = null;
+					String cmd = "";
+					while (true) {
+						flock = fc.lock();
+						mbb.position(0);
+						mbb.get(readBytes, 0, 256);
+						mbb.position(0);
 						mbb.put(fillBytes);
-						FileLock flock = null;
-						String cmd = "";
-						while (true) {
-							flock = fc.lock();
-							mbb.position(0);
-							mbb.get(readBytes, 0, 256);
-							mbb.position(0);
-							mbb.put(fillBytes);
-							flock.release();
-							if (!Arrays.equals(readBytes, fillBytes)) {
-								cmd = StringUtils.trim(new String(readBytes, DefaultCharset.charset));
-								System.out.println("read command:" + cmd);
-								pipedOutput.write((cmd + StringUtils.LF).getBytes(DefaultCharset.name));
-							}
-							Thread.sleep(4000);
+						flock.release();
+						if (!Arrays.equals(readBytes, fillBytes)) {
+							cmd = StringUtils.trim(new String(readBytes, DefaultCharset.charset));
+							System.out.println("read command:" + cmd);
+							commandQueue.put(cmd);
 						}
-					} catch (Exception e) {
-						e.printStackTrace();
+						Thread.sleep(1000);
 					}
+				} catch (Exception e) {
+					e.printStackTrace();
 				}
-			}.start();
-			new Thread() {
-				/* 将屏幕命令输出到解析器 */
-				public void run() {
-					try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
-						String cmd = "";
-						while (null != cmd) {
-							cmd = reader.readLine();
-							/** 在linux环境中当前端console窗口关闭后会导致可以立即read到一个null的input值 */
-							if (null != cmd) {
-								cmd = cmd + StringUtils.LF;
-								pipedOutput.write(cmd.getBytes(DefaultCharset.name));
-								pipedOutput.flush();
-							}
-							Thread.sleep(1000);
+			}
+		}.start();
+		new Thread() {
+			/* 将屏幕命令输出到解析器 */
+			public void run() {
+				try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
+					String cmd = "";
+					while (null != cmd) {
+						cmd = reader.readLine();
+						/** 在linux环境中当前端console窗口关闭后会导致可以立即read到一个null的input值 */
+						if (null != cmd) {
+							commandQueue.put(cmd);
 						}
-						System.out.println("console input closed!");
-					} catch (Exception e) {
-						e.printStackTrace();
+						Thread.sleep(4000);
 					}
+				} catch (Exception e) {
+					System.out.println("console input closed!");
 				}
-			}.start();
-			/* 启动NodeAgent */
-			if (BooleanUtils.isTrue(Config.currentNode().nodeAgentEnable())) {
-				NodeAgent nodeAgent = new NodeAgent();
-				nodeAgent.start();
 			}
+		}.start();
+		/* 启动NodeAgent */
+		if (BooleanUtils.isTrue(Config.currentNode().nodeAgentEnable())) {
+			NodeAgent nodeAgent = new NodeAgent();
+			nodeAgent.start();
+		}
 
-			SchedulerBuilder schedulerBuilder = new SchedulerBuilder();
-			Scheduler scheduler = schedulerBuilder.start();
+		SchedulerBuilder schedulerBuilder = new SchedulerBuilder();
+		Scheduler scheduler = schedulerBuilder.start();
 
-			if (Config.currentNode().autoStart()) {
-				startAll();
-			}
-
-			Matcher matcher = null;
-			try (BufferedReader reader = new BufferedReader(new InputStreamReader(pipedInput))) {
-				String cmd = "";
-				while (true) {
-					try {
-						cmd = reader.readLine();
-					} catch (Exception e) {
-						continue;
-					}
-					if (StringUtils.isBlank(cmd)) {
-						continue;
-					}
-
-					matcher = CommandFactory.test_pattern.matcher(cmd);
-					if (matcher.find()) {
-						test();
-						continue;
-					}
+		if (Config.currentNode().autoStart()) {
+			startAll();
+		}
 
-					matcher = CommandFactory.show_os_pattern.matcher(cmd);
-					if (matcher.find()) {
-						showOs(matcher.group(1), matcher.group(2));
-						continue;
-					}
+		Matcher matcher = null;
+		// try (BufferedReader reader = new BufferedReader(new
+		// InputStreamReader(pipedInput))) {
+		String cmd = "";
+		while (true) {
+			try {
+				cmd = commandQueue.take();
+			} catch (Exception e) {
+				e.printStackTrace();
+				continue;
+			}
+			if (StringUtils.isBlank(cmd)) {
+				continue;
+			}
+			matcher = CommandFactory.test_pattern.matcher(cmd);
+			if (matcher.find()) {
+				test();
+				continue;
+			}
 
-					matcher = CommandFactory.show_cpu_pattern.matcher(cmd);
-					if (matcher.find()) {
-						showCpu(matcher.group(1), matcher.group(2));
-						continue;
-					}
+			matcher = CommandFactory.show_os_pattern.matcher(cmd);
+			if (matcher.find()) {
+				showOs(matcher.group(1), matcher.group(2));
+				continue;
+			}
 
-					matcher = CommandFactory.show_memory_pattern.matcher(cmd);
-					if (matcher.find()) {
-						showMemory(matcher.group(1), matcher.group(2));
-						continue;
-					}
+			matcher = CommandFactory.show_cpu_pattern.matcher(cmd);
+			if (matcher.find()) {
+				showCpu(matcher.group(1), matcher.group(2));
+				continue;
+			}
 
-					matcher = CommandFactory.show_thread_pattern.matcher(cmd);
-					if (matcher.find()) {
-						showThread(matcher.group(1), matcher.group(2));
-						continue;
-					}
+			matcher = CommandFactory.show_memory_pattern.matcher(cmd);
+			if (matcher.find()) {
+				showMemory(matcher.group(1), matcher.group(2));
+				continue;
+			}
 
-					matcher = CommandFactory.show_dataSource_pattern.matcher(cmd);
-					if (matcher.find()) {
-						showDataSource(matcher.group(1), matcher.group(2));
-						continue;
-					}
+			matcher = CommandFactory.show_thread_pattern.matcher(cmd);
+			if (matcher.find()) {
+				showThread(matcher.group(1), matcher.group(2));
+				continue;
+			}
 
-					matcher = CommandFactory.start_pattern.matcher(cmd);
-					if (matcher.find()) {
-						switch (matcher.group(1)) {
-						case "application":
-							startApplicationServer();
-							break;
-						case "center":
-							startCenterServer();
-							break;
-						case "web":
-							startWebServer();
-							break;
-						case "storage":
-							startStorageServer();
-							break;
-						case "data":
-							startDataServer();
-							break;
-						default:
-							startAll();
-							break;
-						}
-						continue;
-					}
-					matcher = CommandFactory.stop_pattern.matcher(cmd);
-					if (matcher.find()) {
-						switch (matcher.group(1)) {
-						case "application":
-							stopApplicationServer();
-							break;
-						case "center":
-							stopCenterServer();
-							break;
-						case "web":
-							stopWebServer();
-							break;
-						case "storage":
-							stopStorageServer();
-							break;
-						case "data":
-							stopDataServer();
-							break;
-						default:
-							stopAll();
-							break;
-						}
-						continue;
-					}
-					matcher = CommandFactory.dump_path_pattern.matcher(cmd);
-					if (matcher.find()) {
-						switch (matcher.group(1)) {
-						case "data":
-							dumpData(matcher.group(2), matcher.group(3));
-							break;
-						case "storage":
-							dumpStorage(matcher.group(2), matcher.group(3));
-							break;
-						default:
-							break;
-						}
-						continue;
-					}
-					matcher = CommandFactory.dump_pattern.matcher(cmd);
-					if (matcher.find()) {
-						switch (matcher.group(1)) {
-						case "data":
-							dumpData("", matcher.group(2));
-							break;
-						case "storage":
-							dumpStorage("", matcher.group(2));
-							break;
-						default:
-							break;
-						}
-						continue;
-					}
-					matcher = CommandFactory.restore_path_pattern.matcher(cmd);
-					if (matcher.find()) {
-						switch (matcher.group(1)) {
-						case "data":
-							resotreDataPath(matcher.group(2), matcher.group(3));
-							break;
-						case "storage":
-							resotreStoragePath(matcher.group(2), matcher.group(3));
-							break;
-						default:
-							break;
-						}
-						continue;
-					}
-					matcher = CommandFactory.restore_pattern.matcher(cmd);
-					if (matcher.find()) {
-						switch (matcher.group(1)) {
-						case "data":
-							resotreData(matcher.group(2), matcher.group(3));
-							break;
-						case "storage":
-							resotreStorage(matcher.group(2), matcher.group(3));
-							break;
-						default:
-							break;
-						}
-						continue;
-					}
-					matcher = CommandFactory.help_pattern.matcher(cmd);
-					if (matcher.find()) {
-						CommandFactory.printHelp();
-						continue;
-					}
+			matcher = CommandFactory.show_dataSource_pattern.matcher(cmd);
+			if (matcher.find()) {
+				showDataSource(matcher.group(1), matcher.group(2));
+				continue;
+			}
 
-					matcher = CommandFactory.version_pattern.matcher(cmd);
-					if (matcher.find()) {
-						version();
-						continue;
-					}
+			matcher = CommandFactory.start_pattern.matcher(cmd);
+			if (matcher.find()) {
+				switch (matcher.group(1)) {
+				case "application":
+					startApplicationServer();
+					break;
+				case "center":
+					startCenterServer();
+					break;
+				case "web":
+					startWebServer();
+					break;
+				case "storage":
+					startStorageServer();
+					break;
+				case "data":
+					startDataServer();
+					break;
+				default:
+					startAll();
+					break;
+				}
+				continue;
+			}
+			matcher = CommandFactory.stop_pattern.matcher(cmd);
+			if (matcher.find()) {
+				switch (matcher.group(1)) {
+				case "application":
+					stopApplicationServer();
+					break;
+				case "center":
+					stopCenterServer();
+					break;
+				case "web":
+					stopWebServer();
+					break;
+				case "storage":
+					stopStorageServer();
+					break;
+				case "data":
+					stopDataServer();
+					break;
+				default:
+					stopAll();
+					break;
+				}
+				continue;
+			}
+			matcher = CommandFactory.dump_path_pattern.matcher(cmd);
+			if (matcher.find()) {
+				switch (matcher.group(1)) {
+				case "data":
+					dumpData(matcher.group(2), matcher.group(3));
+					break;
+				case "storage":
+					dumpStorage(matcher.group(2), matcher.group(3));
+					break;
+				default:
+					break;
+				}
+				continue;
+			}
+			matcher = CommandFactory.dump_pattern.matcher(cmd);
+			if (matcher.find()) {
+				switch (matcher.group(1)) {
+				case "data":
+					dumpData("", matcher.group(2));
+					break;
+				case "storage":
+					dumpStorage("", matcher.group(2));
+					break;
+				default:
+					break;
+				}
+				continue;
+			}
+			matcher = CommandFactory.restore_path_pattern.matcher(cmd);
+			if (matcher.find()) {
+				switch (matcher.group(1)) {
+				case "data":
+					resotreDataPath(matcher.group(2), matcher.group(3));
+					break;
+				case "storage":
+					resotreStoragePath(matcher.group(2), matcher.group(3));
+					break;
+				default:
+					break;
+				}
+				continue;
+			}
+			matcher = CommandFactory.restore_pattern.matcher(cmd);
+			if (matcher.find()) {
+				switch (matcher.group(1)) {
+				case "data":
+					resotreData(matcher.group(2), matcher.group(3));
+					break;
+				case "storage":
+					resotreStorage(matcher.group(2), matcher.group(3));
+					break;
+				default:
+					break;
+				}
+				continue;
+			}
+			matcher = CommandFactory.help_pattern.matcher(cmd);
+			if (matcher.find()) {
+				CommandFactory.printHelp();
+				continue;
+			}
 
-					matcher = CommandFactory.updateFile_pattern.matcher(cmd);
-					if (matcher.find()) {
-						if (updateFile(matcher.group(1), matcher.group(2), matcher.group(3))) {
-							stopAll();
-							System.exit(0);
-						} else {
-							continue;
-						}
-					}
+			matcher = CommandFactory.version_pattern.matcher(cmd);
+			if (matcher.find()) {
+				version();
+				continue;
+			}
 
-					matcher = CommandFactory.setPassword_pattern.matcher(cmd);
-					if (matcher.find()) {
-						setPassword(matcher.group(1), matcher.group(2));
-						if (config()) {
-							break;
-						} else {
-							continue;
-						}
-					}
+			matcher = CommandFactory.updateFile_pattern.matcher(cmd);
+			if (matcher.find()) {
+				if (updateFile(matcher.group(1), matcher.group(2), matcher.group(3))) {
+					stopAll();
+					System.exit(0);
+				} else {
+					continue;
+				}
+			}
 
-					matcher = CommandFactory.erase_content_pattern.matcher(cmd);
-					if (matcher.find()) {
-						switch (matcher.group(1)) {
-						case "pp":
-							eraseContentProcessPlatform(matcher.group(2));
-							break;
-						case "cms":
-							eraseContentCms(matcher.group(2));
-							break;
-						case "log":
-							eraseContentLog(matcher.group(2));
-							break;
-						case "bbs":
-							eraseContentBbs(matcher.group(2));
-							break;
-
-						default:
-							break;
-						}
-						continue;
-					}
+			matcher = CommandFactory.setPassword_pattern.matcher(cmd);
+			if (matcher.find()) {
+				setPassword(matcher.group(1), matcher.group(2));
+				if (config()) {
+					break;
+				} else {
+					continue;
+				}
+			}
 
-					matcher = CommandFactory.compact_data_pattern.matcher(cmd);
-					if (matcher.find()) {
-						compactData(matcher.group(1));
-						continue;
-					}
+			matcher = CommandFactory.erase_content_pattern.matcher(cmd);
+			if (matcher.find()) {
+				switch (matcher.group(1)) {
+				case "pp":
+					eraseContentProcessPlatform(matcher.group(2));
+					break;
+				case "cms":
+					eraseContentCms(matcher.group(2));
+					break;
+				case "log":
+					eraseContentLog(matcher.group(2));
+					break;
+				case "bbs":
+					eraseContentBbs(matcher.group(2));
+					break;
+
+				default:
+					break;
+				}
+				continue;
+			}
 
-					matcher = CommandFactory.create_encrypt_key_pattern.matcher(cmd);
-					if (matcher.find()) {
-						createEncryptKey(matcher.group(1));
-						continue;
-					}
+			matcher = CommandFactory.compact_data_pattern.matcher(cmd);
+			if (matcher.find()) {
+				compactData(matcher.group(1));
+				continue;
+			}
 
-					matcher = CommandFactory.exit_pattern.matcher(cmd);
-					if (matcher.find()) {
-						exit();
-					}
+			matcher = CommandFactory.create_encrypt_key_pattern.matcher(cmd);
+			if (matcher.find()) {
+				createEncryptKey(matcher.group(1));
+				continue;
+			}
 
-					System.out.println("unknown command:" + cmd);
-				}
+			matcher = CommandFactory.exit_pattern.matcher(cmd);
+			if (matcher.find()) {
+				exit();
 			}
-			/* 关闭定时器 */
-			scheduler.shutdown();
+
+			System.out.println("unknown command:" + cmd);
 		}
+		/* 关闭定时器 */
+		scheduler.shutdown();
+		// }
 		SystemOutErrorSideCopyBuilder.stop();
 	}
 

+ 0 - 9
o2server/x_hotpic_assemble_control/src/main/java/com/x/hotpic/assemble/common/date/DateOperation.java

@@ -10,15 +10,6 @@ import java.util.List;
 import com.x.base.core.project.logger.Logger;
 import com.x.base.core.project.logger.LoggerFactory;
 
-/**
- * 
- * @项目名 :OKR
- * @包  名 :com.x.okr.assemble.common.date
- * @文件名 :DateOperation.java
- * @作  者 :李  义
- * @单  位 :浙江兰德纵横网络技术股份有限公司
- * @日  期 :2016-06-07
- */
 public class DateOperation {
 	
 	private Logger logger =  LoggerFactory.getLogger( DateOperation.class);

+ 5 - 3
o2server/x_message_assemble_communicate/src/main/java/com/x/message/assemble/communicate/ws/collaboration/ActionCollaboration.java

@@ -74,12 +74,14 @@ public class ActionCollaboration {
 
 	@OnMessage
 	public void message(String input, Session session) throws Exception {
-		logger.info("@OnMessage receive message {}",input);
+		EffectivePerson effectivePerson = (EffectivePerson) session.getUserProperties().get(HttpToken.X_Person);
+		logger.debug("@OnMessage1 receive: message {}, person:{}, ip:{}, client:{} .", input,
+				effectivePerson.getDistinguishedName(), effectivePerson.getRemoteAddress(), effectivePerson.getUserAgent());
 		if (StringUtils.isBlank(input)) {
 			return;
 		}
-		//建立心跳,维持websocket链接
-		if(input.equalsIgnoreCase("heartbeat")){
+		// 建立心跳,维持websocket链接
+		if (input.equalsIgnoreCase("heartbeat")) {
 			session.getBasicRemote().sendText("heartbeat");
 		}
 	}

+ 1 - 0
o2server/x_organization_assemble_control/src/main/java/com/x/organization/assemble/control/Business.java

@@ -473,6 +473,7 @@ public class Business {
 		List<Identity> identities = emc.listEqual(Identity.class, Identity.person_FIELDNAME, person.getId());
 		List<String> unitIds = new ArrayList<>();
 		for (String id : ListTools.extractField(identities, Identity.unit_FIELDNAME, String.class, true, true)) {
+			unitIds.add(id);
 			unitIds.addAll(unit().listSupNested(id));
 		}
 		unitIds = ListTools.trim(unitIds, true, true);

+ 1 - 0
o2server/x_organization_assemble_control/src/main/java/com/x/organization/assemble/control/jaxrs/identity/ActionCreate.java

@@ -91,6 +91,7 @@ class ActionCreate extends BaseAction {
 			emc.commit();
 
 			ApplicationCache.notify(Identity.class);
+			ApplicationCache.notify(Person.class);
 			Wo wo = new Wo();
 			wo.setId(identity.getId());
 			result.setData(wo);

+ 1 - 0
o2server/x_organization_assemble_control/src/main/java/com/x/organization/assemble/control/jaxrs/identity/ActionEdit.java

@@ -73,6 +73,7 @@ class ActionEdit extends BaseAction {
 			emc.check(person, CheckPersistType.all);
 			emc.commit();
 			ApplicationCache.notify(Identity.class);
+			ApplicationCache.notify(Person.class);
 			Wo wo = new Wo();
 			wo.setId(identity.getId());
 			result.setData(wo);

+ 10 - 2
o2server/x_organization_assemble_control/src/main/java/com/x/organization/assemble/control/jaxrs/person/ActionCreate.java

@@ -20,6 +20,7 @@ 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.organization.OrganizationDefinition;
 import com.x.base.core.project.tools.ListTools;
 import com.x.organization.assemble.control.Business;
 import com.x.organization.assemble.control.jaxrs.person.ActionGet.Wo;
@@ -38,12 +39,19 @@ class ActionCreate extends BaseAction {
 			Business business = new Business(emc);
 			Person person = new Person();
 			Wi.copier.copy(wi, person);
-			if (!this.editable(business, effectivePerson, person)) {
+
+			if ((!business.hasAnyRole(effectivePerson, OrganizationDefinition.OrganizationManager,
+					OrganizationDefinition.PersonManager, OrganizationDefinition.Manager))
+					&& (!effectivePerson.isManager()) && (!effectivePerson.isCipher())) {
 				throw new ExceptionAccessDenied(effectivePerson);
 			}
-			if (!Config.token().isInitialManager(effectivePerson.getDistinguishedName())) {
+
+			if ((!Config.token().isInitialManager(effectivePerson.getDistinguishedName()))
+					&& (!effectivePerson.isCipher())) {
 				Person current = business.person().pick(effectivePerson.getDistinguishedName());
 				person.setTopUnitList(current.getTopUnitList());
+			} else {
+				person.setTopUnitList(new ArrayList<String>());
 			}
 			this.checkName(business, person.getName(), person.getId());
 			this.checkMobile(business, person.getMobile(), person.getId());

+ 2 - 4
o2server/x_organization_assemble_control/src/main/java/com/x/organization/assemble/control/jaxrs/person/ActionListNext.java

@@ -41,10 +41,8 @@ class ActionListNext extends BaseAction {
 					}
 					id = o.getId();
 				}
-				System.out.println("!!!!!!!");
-				System.out.println(business.personPredicateWithTopUnit(effectivePerson));
-				System.out.println("!!!!!!!");
-				result = this.standardListPrev(Wo.copier, id, count, JpaObject.sequence_FIELDNAME, DESC,
+	
+				result = this.standardListNext(Wo.copier, id, count, JpaObject.sequence_FIELDNAME, DESC,
 						business.personPredicateWithTopUnit(effectivePerson));
 
 				Co co = new Co(result.getData(), result.getCount());

+ 3 - 0
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/factory/content/WorkLogFactory.java

@@ -16,6 +16,7 @@ import org.apache.commons.collections4.list.SetUniqueList;
 import org.apache.commons.lang3.StringUtils;
 
 import com.x.base.core.project.exception.ExceptionWhen;
+import com.x.base.core.project.gson.XGsonBuilder;
 import com.x.base.core.project.tools.ListTools;
 import com.x.processplatform.assemble.surface.AbstractFactory;
 import com.x.processplatform.assemble.surface.Business;
@@ -51,6 +52,8 @@ public class WorkLogFactory extends AbstractFactory {
 		} else if (list.size() == 1) {
 			return list.get(0);
 		} else {
+			System.out.println("!!!!!!!!!!!!!!!!!!!!!!");
+			System.out.println(XGsonBuilder.toJson(list));
 			throw new Exception("find multiple workLog point to arrivedActivityToken{value:" + activityToken
 					+ "}, is not impossible.");
 		}

+ 13 - 1
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/factory/element/RouteFactory.java

@@ -1,5 +1,6 @@
 package com.x.processplatform.assemble.surface.factory.element;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import com.x.base.core.project.exception.ExceptionWhen;
@@ -18,7 +19,18 @@ public class RouteFactory extends ElementFactory {
 	}
 
 	public Route pick(String flag, ExceptionWhen exceptionWhen) throws Exception {
-		return this.pick(flag, Route.class );
+		return this.pick(flag, Route.class);
+	}
+
+	public List<Route> pick(List<String> flags) throws Exception {
+		List<Route> list = new ArrayList<>();
+		for (String flag : flags) {
+			Route o = this.pick(flag, Route.class);
+			if (null != o) {
+				list.add(o);
+			}
+		}
+		return list;
 	}
 
 	public List<Route> listWithProcess(Process process) throws Exception {

+ 3 - 9
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/data/ActionUpdateWithWork.java

@@ -16,16 +16,10 @@ class ActionUpdateWithWork extends BaseAction {
 	ActionResult<Wo> execute(EffectivePerson effectivePerson, String id, JsonElement jsonElement) throws Exception {
 		try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
 			/** 防止提交空数据清空data */
-			if (jsonElement.isJsonNull()) {
-				throw new ExceptionNullData();
+			if (null == jsonElement || (!jsonElement.isJsonObject())) {
+				throw new ExceptionNotJsonObject();
 			}
-			if (jsonElement.isJsonArray()) {
-				throw new ExceptionArrayData();
-			}
-			if (jsonElement.isJsonPrimitive()) {
-				throw new ExceptionPrimitiveData();
-			}
-			if (jsonElement.isJsonObject() && jsonElement.getAsJsonObject().entrySet().isEmpty()) {
+			if (jsonElement.getAsJsonObject().entrySet().isEmpty()) {
 				throw new ExceptionEmptyData();
 			}
 			ActionResult<Wo> result = new ActionResult<>();

+ 3 - 9
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/data/ActionUpdateWithWorkCompleted.java

@@ -20,16 +20,10 @@ class ActionUpdateWithWorkCompleted extends BaseAction {
 	ActionResult<Wo> execute(EffectivePerson effectivePerson, String id, JsonElement jsonElement) throws Exception {
 		try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
 			/** 防止提交空数据清空data */
-			if (jsonElement.isJsonNull()) {
-				throw new ExceptionNullData();
+			if (null == jsonElement || (!jsonElement.isJsonObject())) {
+				throw new ExceptionNotJsonObject();
 			}
-			if (jsonElement.isJsonArray()) {
-				throw new ExceptionArrayData();
-			}
-			if (jsonElement.isJsonPrimitive()) {
-				throw new ExceptionPrimitiveData();
-			}
-			if (jsonElement.isJsonObject() && jsonElement.getAsJsonObject().entrySet().isEmpty()) {
+			if (jsonElement.getAsJsonObject().entrySet().isEmpty()) {
 				throw new ExceptionEmptyData();
 			}
 			ActionResult<Wo> result = new ActionResult<>();

+ 17 - 14
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/data/BaseAction.java

@@ -27,6 +27,7 @@ import com.x.base.core.project.tools.ListTools;
 import com.x.processplatform.assemble.surface.Business;
 import com.x.processplatform.core.entity.content.Attachment;
 import com.x.processplatform.core.entity.content.Data;
+import com.x.processplatform.core.entity.content.Data.DataWork;
 import com.x.processplatform.core.entity.content.Read;
 import com.x.processplatform.core.entity.content.ReadCompleted;
 import com.x.processplatform.core.entity.content.Review;
@@ -129,18 +130,19 @@ abstract class BaseAction extends StandardJaxrsAction {
 	}
 
 	void updateData(Business business, Work work, JsonElement jsonElement, String... paths) throws Exception {
-		if (jsonElement.isJsonObject()) {
-			JsonObject jsonObject = jsonElement.getAsJsonObject();
-
-			jsonObject.add(Data.WORK_PROPERTY, gson.toJsonTree(Data.DataWork.workCopier.copy(work)));
+		JsonObject jsonObject = jsonElement.getAsJsonObject();
+		if (paths.length == 0) {
+			DataWork dataWork = DataWork.workCopier.copy(work);
+			dataWork.setWorkId(work.getId());
+			dataWork.setWorkCompletedId("");
+			dataWork.setCompleted(false);
+			jsonObject.add(Data.WORK_PROPERTY, gson.toJsonTree(dataWork));
 			jsonObject.add(Data.ATTACHMENTLIST_PROPERTY,
 					gson.toJsonTree(this.listDataAttachment(business, work.getJob())));
-
-			jsonElement = jsonObject;
 		}
 		DataItemConverter<Item> converter = new DataItemConverter<>(Item.class);
 		List<Item> exists = business.item().listWithJobWithPath(work.getJob(), paths);
-		List<Item> currents = converter.disassemble(jsonElement, paths);
+		List<Item> currents = converter.disassemble(jsonObject, paths);
 		List<Item> removes = converter.subtract(exists, currents);
 		List<Item> adds = converter.subtract(currents, exists);
 		if ((!removes.isEmpty()) || (!adds.isEmpty())) {
@@ -162,18 +164,19 @@ abstract class BaseAction extends StandardJaxrsAction {
 
 	void updateData(Business business, WorkCompleted workCompleted, JsonElement jsonElement, String... paths)
 			throws Exception {
-		if (jsonElement.isJsonObject()) {
-			JsonObject jsonObject = jsonElement.getAsJsonObject();
-
-			jsonObject.add(Data.WORK_PROPERTY, gson.toJsonTree(Data.DataWork.workCompletedCopier.copy(workCompleted)));
+		JsonObject jsonObject = jsonElement.getAsJsonObject();
+		if (paths.length == 0) {
+			DataWork dataWork = DataWork.workCompletedCopier.copy(workCompleted);
+			dataWork.setWorkCompletedId(workCompleted.getId());
+			dataWork.setWorkId(workCompleted.getWork());
+			dataWork.setCompleted(true);
+			jsonObject.add(Data.WORK_PROPERTY, gson.toJsonTree(dataWork));
 			jsonObject.add(Data.ATTACHMENTLIST_PROPERTY,
 					gson.toJsonTree(this.listDataAttachment(business, workCompleted.getJob())));
-
-			jsonElement = jsonObject;
 		}
 		DataItemConverter<Item> converter = new DataItemConverter<>(Item.class);
 		List<Item> exists = business.item().listWithJobWithPath(workCompleted.getJob(), paths);
-		List<Item> currents = converter.disassemble(jsonElement, paths);
+		List<Item> currents = converter.disassemble(jsonObject, paths);
 		List<Item> removes = converter.subtract(exists, currents);
 		List<Item> adds = converter.subtract(currents, exists);
 		if ((!removes.isEmpty()) || (!adds.isEmpty())) {

+ 2 - 2
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/data/ExceptionArrayData.java → o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/data/ExceptionNotJsonObject.java

@@ -2,11 +2,11 @@ package com.x.processplatform.assemble.surface.jaxrs.data;
 
 import com.x.base.core.project.exception.PromptException;
 
-class ExceptionArrayData extends PromptException {
+class ExceptionNotJsonObject extends PromptException {
 
 	private static final long serialVersionUID = -665095222445791960L;
 
-	ExceptionArrayData( ) {
+	ExceptionNotJsonObject( ) {
 		super("更新的数据不能为数组.");
 	}
 }

+ 0 - 12
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/data/ExceptionNullData.java

@@ -1,12 +0,0 @@
-package com.x.processplatform.assemble.surface.jaxrs.data;
-
-import com.x.base.core.project.exception.PromptException;
-
-class ExceptionNullData extends PromptException {
-
-	private static final long serialVersionUID = -665095222445791960L;
-
-	ExceptionNullData() {
-		super("更新的数据不能为null.");
-	}
-}

+ 0 - 12
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/data/ExceptionPrimitiveData.java

@@ -1,12 +0,0 @@
-package com.x.processplatform.assemble.surface.jaxrs.data;
-
-import com.x.base.core.project.exception.PromptException;
-
-class ExceptionPrimitiveData extends PromptException {
-
-	private static final long serialVersionUID = -665095222445791960L;
-
-	ExceptionPrimitiveData() {
-		super("更新的数据不能为Primitive.");
-	}
-}

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

@@ -1,6 +1,5 @@
 package com.x.processplatform.assemble.surface.jaxrs.task;
 
-import java.net.URLEncoder;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -12,17 +11,16 @@ import com.google.gson.JsonElement;
 import com.x.base.core.container.EntityManagerContainer;
 import com.x.base.core.container.factory.EntityManagerContainerFactory;
 import com.x.base.core.entity.JpaObject;
+import com.x.base.core.project.Applications;
 import com.x.base.core.project.x_processplatform_service_processing;
 import com.x.base.core.project.annotation.FieldDescribe;
 import com.x.base.core.project.bean.WrapCopier;
 import com.x.base.core.project.bean.WrapCopierFactory;
-import com.x.base.core.project.config.Config;
 import com.x.base.core.project.exception.ExceptionAccessDenied;
 import com.x.base.core.project.exception.ExceptionEntityNotExist;
 import com.x.base.core.project.gson.GsonPropertyObject;
 import com.x.base.core.project.http.ActionResult;
 import com.x.base.core.project.http.EffectivePerson;
-import com.x.base.core.project.tools.DefaultCharset;
 import com.x.base.core.project.tools.ListTools;
 import com.x.processplatform.assemble.surface.Business;
 import com.x.processplatform.assemble.surface.ThisApplication;
@@ -30,6 +28,8 @@ import com.x.processplatform.core.entity.content.ProcessingType;
 import com.x.processplatform.core.entity.content.Task;
 import com.x.processplatform.core.entity.content.TaskCompleted;
 import com.x.processplatform.core.entity.content.WorkLog;
+import com.x.processplatform.core.entity.element.Manual;
+import com.x.processplatform.core.entity.element.Route;
 
 class ActionProcessing extends BaseAction {
 
@@ -38,8 +38,9 @@ class ActionProcessing extends BaseAction {
 		ActionResult<List<Wo>> result = new ActionResult<>();
 		Wi wi = this.convertToWrapIn(jsonElement, Wi.class);
 		Task task = null;
-		List<String> sameJobActivityIdentityTasks = new ArrayList<>();
+		boolean appendTask = false;
 		try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+			Business business = new Business(emc);
 			task = emc.find(id, Task.class);
 			if (null == task) {
 				throw new ExceptionEntityNotExist(id, Task.class);
@@ -47,11 +48,6 @@ class ActionProcessing extends BaseAction {
 			if (!StringUtils.equalsIgnoreCase(task.getPerson(), effectivePerson.getDistinguishedName())) {
 				throw new ExceptionAccessDenied(effectivePerson, task);
 			}
-			if (Config.processPlatform().getProcessingTaskSameJobActivityIdentity()) {
-				sameJobActivityIdentityTasks = emc.idsEqualAndEqualAndEqualAndNotEqual(Task.class, Task.job_FIELDNAME,
-						task.getJob(), Task.activity_FIELDNAME, task.getActivity(), Task.identity_FIELDNAME,
-						task.getIdentity(), Task.id_FIELDNAME, task.getId());
-			}
 			emc.beginTransaction(Task.class);
 			/* 如果有输入新的路由决策覆盖原有决策 */
 			if (StringUtils.isNotEmpty(wi.getRouteName())) {
@@ -64,47 +60,51 @@ class ActionProcessing extends BaseAction {
 			/* 强制覆盖多媒体意见 */
 			task.setMediaOpinion(wi.getMediaOpinion());
 			emc.commit();
+
+			appendTask = this.appendTask(business, task, wi);
 		}
-		/* processing task */
+
+		if (appendTask) {
+			ReqAppendTask req = new ReqAppendTask();
+			req.setIdentityList(wi.getAppendTaskIdentityList());
+			ThisApplication.context().applications().putQuery(x_processplatform_service_processing.class,
+					Applications.joinQueryUri("task", task.getId(), "append"), req);
+		}
+
 		ProcessingRequest processingRequest = new ProcessingRequest();
 		processingRequest.setRouteData(wi.getRouteData());
 		ThisApplication.context().applications().putQuery(x_processplatform_service_processing.class,
-				"task/" + URLEncoder.encode(task.getId(), DefaultCharset.name) + "/processing", processingRequest);
+				Applications.joinQueryUri("task", task.getId(), "processing"), processingRequest);
+
+		ThisApplication.context().applications().putQuery(effectivePerson.getDebugger(),
+				x_processplatform_service_processing.class,
+				Applications.joinQueryUri("work", task.getWork(), "processing"), null);
+
 		try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
 			Business business = new Business(emc);
 			List<Wo> wos = this.referenceWorkLog(business, task);
 			result.setData(wos);
 		}
 
-		other(wi, sameJobActivityIdentityTasks);
-
 		return result;
 	}
 
-	private void other(Wi wi, List<String> sameJobActivityIdentityTasks) throws Exception {
-		for (String str : sameJobActivityIdentityTasks) {
-			try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
-				Task other = emc.find(str, Task.class);
-				if (null != other) {
-					emc.beginTransaction(Task.class);
-					/* 如果有输入新的路由决策覆盖原有决策 */
-					if (StringUtils.isNotEmpty(wi.getRouteName())) {
-						other.setRouteName(wi.getRouteName());
-					}
-					/* 如果有新的流程意见那么覆盖原有流程意见 */
-					if (StringUtils.isNotEmpty(wi.getOpinion())) {
-						other.setOpinion(wi.getOpinion());
-					}
-					/* 强制覆盖多媒体意见 */
-					other.setMediaOpinion(wi.getMediaOpinion());
-					emc.commit();
-					ProcessingRequest request = new ProcessingRequest();
-					request.setRouteData(wi.getRouteData());
-					ThisApplication.context().applications().putQuery(x_processplatform_service_processing.class,
-							"task/" + URLEncoder.encode(other.getId(), DefaultCharset.name) + "/processing", request);
+	private boolean appendTask(Business business, Task task, Wi wi) throws Exception {
+		Manual manual = business.manual().pick(task.getActivity());
+		if (null != manual) {
+			Route route = null;
+			for (Route o : business.route().pick(manual.getRouteList())) {
+				if (StringUtils.equals(o.getName(), task.getRouteName())) {
+					route = o;
+					break;
 				}
 			}
+			if ((null != route) && (StringUtils.equals(route.getType(), Route.TYPE_APPENDTASK))
+					&& StringUtils.equals(manual.getId(), route.getActivity())) {
+				return true;
+			}
 		}
+		return false;
 	}
 
 	private List<Wo> referenceWorkLog(Business business, Task task) throws Exception {
@@ -207,6 +207,21 @@ class ActionProcessing extends BaseAction {
 
 	}
 
+	public static class ReqAppendTask extends GsonPropertyObject {
+
+		@FieldDescribe("添加的待办身份.")
+		private List<String> identityList;
+
+		public List<String> getIdentityList() {
+			return identityList;
+		}
+
+		public void setIdentityList(List<String> identityList) {
+			this.identityList = identityList;
+		}
+
+	}
+
 	public static class Wi extends GsonPropertyObject {
 
 		@FieldDescribe("路由名称")
@@ -221,6 +236,17 @@ class ActionProcessing extends BaseAction {
 		@FieldDescribe("路由数据")
 		private JsonElement routeData;
 
+		@FieldDescribe("新添加的待办处理人")
+		private List<String> appendTaskIdentityList;
+
+		public List<String> getAppendTaskIdentityList() {
+			return appendTaskIdentityList;
+		}
+
+		public void setAppendTaskIdentityList(List<String> appendTaskIdentityList) {
+			this.appendTaskIdentityList = appendTaskIdentityList;
+		}
+
 		public String getRouteName() {
 			return routeName;
 		}

+ 14 - 14
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/task/TaskAction.java

@@ -316,7 +316,7 @@ public class TaskAction extends StandardJaxrsAction {
 		asyncResponse.resume(ResponseFactory.getDefaultActionResultResponse(result));
 	}
 
-	@JaxrsMethodDescribe(value = "保存并继续流转.返回是最新的WorkLog", action = ActionProcessing.class)
+	@JaxrsMethodDescribe(value = "保存待办意见并继续流转.返回是最新的WorkLog", action = ActionProcessing.class)
 	@POST
 	@Path("{id}/processing")
 	@Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
@@ -558,8 +558,8 @@ public class TaskAction extends StandardJaxrsAction {
 	@Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
 	@Consumes(MediaType.APPLICATION_JSON)
 	public void manageListNext(@Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request,
-						 @JaxrsParameterDescribe("标识") @PathParam("id") String id,
-						 @JaxrsParameterDescribe("数量") @PathParam("count") Integer count) {
+			@JaxrsParameterDescribe("标识") @PathParam("id") String id,
+			@JaxrsParameterDescribe("数量") @PathParam("count") Integer count) {
 		ActionResult<List<ActionManageListNext.Wo>> result = new ActionResult<>();
 		EffectivePerson effectivePerson = this.effectivePerson(request);
 		try {
@@ -577,8 +577,8 @@ public class TaskAction extends StandardJaxrsAction {
 	@Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
 	@Consumes(MediaType.APPLICATION_JSON)
 	public void manageListPrev(@Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request,
-						 @JaxrsParameterDescribe("标识") @PathParam("id") String id,
-						 @JaxrsParameterDescribe("数量") @PathParam("count") Integer count) {
+			@JaxrsParameterDescribe("标识") @PathParam("id") String id,
+			@JaxrsParameterDescribe("数量") @PathParam("count") Integer count) {
 		ActionResult<List<ActionManageListPrev.Wo>> result = new ActionResult<>();
 		EffectivePerson effectivePerson = this.effectivePerson(request);
 		try {
@@ -595,9 +595,9 @@ public class TaskAction extends StandardJaxrsAction {
 	@Path("list/{id}/next/{count}/filter/manage")
 	@Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
 	@Consumes(MediaType.APPLICATION_JSON)
-	public void manageListNextWithFilter(@Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request,
-								   @JaxrsParameterDescribe("标识") @PathParam("id") String id,
-								   @JaxrsParameterDescribe("数量") @PathParam("count") Integer count, JsonElement jsonElement) {
+	public void manageListNextWithFilter(@Suspended final AsyncResponse asyncResponse,
+			@Context HttpServletRequest request, @JaxrsParameterDescribe("标识") @PathParam("id") String id,
+			@JaxrsParameterDescribe("数量") @PathParam("count") Integer count, JsonElement jsonElement) {
 		ActionResult<List<ActionManageListNextFilter.Wo>> result = new ActionResult<>();
 		EffectivePerson effectivePerson = this.effectivePerson(request);
 		try {
@@ -614,9 +614,9 @@ public class TaskAction extends StandardJaxrsAction {
 	@Path("list/{id}/prev/{count}/filter/manage")
 	@Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
 	@Consumes(MediaType.APPLICATION_JSON)
-	public void manageListPrevWithFilter(@Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request,
-								   @JaxrsParameterDescribe("标识") @PathParam("id") String id,
-								   @JaxrsParameterDescribe("数量") @PathParam("count") Integer count, JsonElement jsonElement) {
+	public void manageListPrevWithFilter(@Suspended final AsyncResponse asyncResponse,
+			@Context HttpServletRequest request, @JaxrsParameterDescribe("标识") @PathParam("id") String id,
+			@JaxrsParameterDescribe("数量") @PathParam("count") Integer count, JsonElement jsonElement) {
 		ActionResult<List<ActionManageListPrevFilter.Wo>> result = new ActionResult<>();
 		EffectivePerson effectivePerson = this.effectivePerson(request);
 		try {
@@ -633,9 +633,9 @@ public class TaskAction extends StandardJaxrsAction {
 	@Path("list/filter/{page}/size/{size}/manage")
 	@Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
 	@Consumes(MediaType.APPLICATION_JSON)
-	public void manageListFilterPaging(@Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request,
-								   @JaxrsParameterDescribe("分页") @PathParam("page") Integer page,
-								   @JaxrsParameterDescribe("数量") @PathParam("size") Integer size, JsonElement jsonElement) {
+	public void manageListFilterPaging(@Suspended final AsyncResponse asyncResponse,
+			@Context HttpServletRequest request, @JaxrsParameterDescribe("分页") @PathParam("page") Integer page,
+			@JaxrsParameterDescribe("数量") @PathParam("size") Integer size, JsonElement jsonElement) {
 		ActionResult<List<ActionManageListFilterPaging.Wo>> result = new ActionResult<>();
 		EffectivePerson effectivePerson = this.effectivePerson(request);
 		try {

+ 7 - 0
o2server/x_processplatform_core_entity/src/main/java/com/x/processplatform/core/entity/content/Data.java

@@ -17,6 +17,7 @@ import com.x.base.core.entity.JpaObject;
 import com.x.base.core.project.bean.WrapCopier;
 import com.x.base.core.project.bean.WrapCopierFactory;
 import com.x.base.core.project.gson.GsonPropertyObject;
+import com.x.base.core.project.gson.XGsonBuilder;
 import com.x.base.core.project.tools.ListTools;
 
 public class Data extends ListOrderedMap<String, Object> {
@@ -440,4 +441,10 @@ public class Data extends ListOrderedMap<String, Object> {
 				.count() == 0;
 	}
 
+	@Override
+	/*需要重载,前端toString需要这个方法.*/
+	public String toString() {
+		return XGsonBuilder.toJson(this);
+	}
+
 }

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

@@ -326,6 +326,18 @@ public class Work extends SliceJpaObject {
 	@CheckPersist(allowEmpty = true)
 	private List<String> manualTaskIdentityList;
 
+//	public static final String manualReadIdentityList_FIELDNAME = "manualReadIdentityList";
+//	@FieldDescribe("预期的待阅人")
+//	@PersistentCollection(fetch = FetchType.EAGER)
+//	@OrderColumn(name = ORDERCOLUMNCOLUMN)
+//	@ContainerTable(name = TABLE + ContainerTableNameMiddle
+//			+ manualReadIdentityList_FIELDNAME, joinIndex = @Index(name = TABLE + IndexNameMiddle
+//					+ manualReadIdentityList_FIELDNAME + JoinIndexNameSuffix))
+//	@ElementColumn(length = length_255B, name = ColumnNamePrefix + manualReadIdentityList_FIELDNAME)
+//	@ElementIndex(name = TABLE + IndexNameMiddle + manualReadIdentityList_FIELDNAME + ElementIndexNameSuffix)
+//	@CheckPersist(allowEmpty = true)
+//	private List<String> manualReadIdentityList;
+
 	public static final String manualTaskIdentityText_FIELDNAME = "manualTaskIdentityText";
 	@FieldDescribe("当前处理人身份合并文本,用','分割,超长截断,此字段仅用于显示当前工作的处理人,不索引.")
 	@Column(length = JpaObject.length_255B, name = ColumnNamePrefix + manualTaskIdentityText_FIELDNAME)

+ 21 - 25
o2server/x_processplatform_core_entity/src/main/java/com/x/processplatform/core/entity/element/Manual.java

@@ -63,8 +63,6 @@ public class Manual extends Activity {
 
 	/* 更新运行方法 */
 
-	// public static String[] FLA GS = new String[] { "id", "alias" };
-
 	/* flag标志位 */
 	/* Entity 默认字段结束 */
 
@@ -455,14 +453,6 @@ public class Manual extends Activity {
 	@CheckPersist(allowEmpty = true)
 	private List<String> taskDataPathList;
 
-	public static final String manualMode_FIELDNAME = "manualMode";
-	@FieldDescribe("人工节点的处理方式.")
-	@Enumerated(EnumType.STRING)
-	@Column(length = ManualMode.length, name = ColumnNamePrefix + manualMode_FIELDNAME)
-	@Index(name = TABLE + IndexNameMiddle + manualMode_FIELDNAME)
-	@CheckPersist(allowEmpty = true)
-	private ManualMode manualMode;
-
 	public static final String allowReset_FIELDNAME = "allowReset";
 	@FieldDescribe("允许重置待办")
 	@CheckPersist(allowEmpty = true)
@@ -517,6 +507,14 @@ public class Manual extends Activity {
 	@Column(name = ColumnNamePrefix + resetCount_FIELDNAME)
 	private Integer resetCount;
 
+	public static final String manualMode_FIELDNAME = "manualMode";
+	@FieldDescribe("人工节点的处理方式.")
+	@Enumerated(EnumType.STRING)
+	@Column(length = ManualMode.length, name = ColumnNamePrefix + manualMode_FIELDNAME)
+	@Index(name = TABLE + IndexNameMiddle + manualMode_FIELDNAME)
+	@CheckPersist(allowEmpty = true)
+	private ManualMode manualMode;
+
 	public static final String manualBeforeTaskScript_FIELDNAME = "manualBeforeTaskScript";
 	@IdReference(Script.class)
 	@FieldDescribe("待办执行前脚本.")
@@ -565,25 +563,24 @@ public class Manual extends Activity {
 	@CheckPersist(allowEmpty = true)
 	private String manualStayScriptText;
 
-//	public static final String manualPressScript_FIELDNAME = "manualPressScript";
-//	@IdReference(Script.class)
-//	@FieldDescribe("提醒内容脚本.")
-//	@Column(length = length_255B, name = ColumnNamePrefix + manualPressScript_FIELDNAME)
-//	@CheckPersist(allowEmpty = true)
-//	private String manualPressScript;
-//
-//	public static final String manualPressScriptText_FIELDNAME = "manualPressScriptText";
-//	@FieldDescribe("提醒内容脚本文本.")
-//	@Lob
-//	@Basic(fetch = FetchType.EAGER)
-//	@Column(length = JpaObject.length_1M, name = ColumnNamePrefix + manualPressScriptText_FIELDNAME)
-//	@CheckPersist(allowEmpty = true)
-//	private String manualPressScriptText;
+	public static final String manualMergeSameJobActivity_FIELDNAME = "manualMergeSameJobActivity";
+	@FieldDescribe("合并相同job相同活动的工作.")
+	@CheckPersist(allowEmpty = true)
+	@Column(name = ColumnNamePrefix + manualMergeSameJobActivity_FIELDNAME)
+	private Boolean manualMergeSameJobActivity;
 
 	public String getName() {
 		return name;
 	}
 
+	public Boolean getManualMergeSameJobActivity() {
+		return manualMergeSameJobActivity;
+	}
+
+	public void setManualMergeSameJobActivity(Boolean manualMergeSameJobActivity) {
+		this.manualMergeSameJobActivity = manualMergeSameJobActivity;
+	}
+
 	public void setName(String name) {
 		this.name = name;
 	}
@@ -1152,5 +1149,4 @@ public class Manual extends Activity {
 		this.opinionGroup = opinionGroup;
 	}
 
-
 }

+ 71 - 0
o2server/x_processplatform_core_entity/src/main/java/com/x/processplatform/core/entity/element/Route.java

@@ -16,6 +16,7 @@ import javax.persistence.UniqueConstraint;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.openjpa.persistence.jdbc.Index;
 
+import com.x.base.core.entity.AbstractPersistenceProperties;
 import com.x.base.core.entity.JpaObject;
 import com.x.base.core.entity.SliceJpaObject;
 import com.x.base.core.entity.annotation.CheckPersist;
@@ -34,6 +35,12 @@ import com.x.processplatform.core.entity.PersistenceProperties;
 @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
 public class Route extends SliceJpaObject {
 
+	public static final String TYPE_NORMAL = "normal";
+	public static final String TYPE_APPENDTASK = "appendTask";
+
+	public static final String APPENDTASKIDENTITYTYPE_SCRIPT = "script";
+	public static final String APPENDTASKIDENTITYTYPE_CONFIG = "config";
+
 	private static final long serialVersionUID = -1151288890276589956L;
 	private static final String TABLE = PersistenceProperties.Element.Route.table;
 
@@ -214,6 +221,54 @@ public class Route extends SliceJpaObject {
 	@CheckPersist(allowEmpty = true)
 	private String selectConfig;
 
+	public static final String type_FIELDNAME = "type";
+	@FieldDescribe("路由类型")
+	@Column(length = JpaObject.length_64B, name = ColumnNamePrefix + type_FIELDNAME)
+	@CheckPersist(allowEmpty = true)
+	private String type;
+
+	public static final String appendTaskIdentityType_FIELDNAME = "appendTaskIdentityType";
+	@FieldDescribe("添加待办方式.")
+	@Column(length = JpaObject.length_64B, name = ColumnNamePrefix + appendTaskIdentityType_FIELDNAME)
+	@CheckPersist(allowEmpty = true)
+	private String appendTaskIdentityType;
+
+	public static final String appendTaskIdentityScript_FIELDNAME = "appendTaskIdentityScript";
+	@IdReference(Script.class)
+	@FieldDescribe("添加待办人脚本.")
+	@Column(length = AbstractPersistenceProperties.processPlatform_name_length, name = ColumnNamePrefix
+			+ appendTaskIdentityScript_FIELDNAME)
+	@CheckPersist(allowEmpty = true)
+	private String appendTaskIdentityScript;
+
+	public static final String appendTaskIdentityScriptText_FIELDNAME = "appendTaskIdentityScriptText";
+	@FieldDescribe("添加待办人脚本文本.")
+	@Lob
+	@Basic(fetch = FetchType.EAGER)
+	@Column(length = JpaObject.length_1M, name = ColumnNamePrefix + appendTaskIdentityScriptText_FIELDNAME)
+	@CheckPersist(allowEmpty = true)
+	private String appendTaskIdentityScriptText;
+
+	public String getAppendTaskIdentityType() {
+		return appendTaskIdentityType;
+	}
+
+	public void setAppendTaskIdentityType(String appendTaskIdentityType) {
+		this.appendTaskIdentityType = appendTaskIdentityType;
+	}
+
+	public String getAppendTaskIdentityScript() {
+		return appendTaskIdentityScript;
+	}
+
+	public void setAppendTaskIdentityScript(String appendTaskIdentityScript) {
+		this.appendTaskIdentityScript = appendTaskIdentityScript;
+	}
+
+	public static String getAppendtaskidentityscriptFieldname() {
+		return appendTaskIdentityScript_FIELDNAME;
+	}
+
 	public String getName() {
 		return name;
 	}
@@ -390,4 +445,20 @@ public class Route extends SliceJpaObject {
 		this.selectConfig = selectConfig;
 	}
 
+	public String getType() {
+		return type;
+	}
+
+	public void setType(String type) {
+		this.type = type;
+	}
+
+	public String getAppendTaskIdentityScriptText() {
+		return appendTaskIdentityScriptText;
+	}
+
+	public void setAppendTaskIdentityScriptText(String appendTaskIdentityScriptText) {
+		this.appendTaskIdentityScriptText = appendTaskIdentityScriptText;
+	}
+
 }

+ 1 - 1
o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/factory/ElementFactory.java

@@ -396,7 +396,7 @@ public class ElementFactory extends AbstractFactory {
 		default:
 			break;
 		}
-		return list;
+		return ListTools.trim(list, true, true);
 	}
 
 	public List<String> listFormWithProcess(Process process) throws Exception {

+ 117 - 0
o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/jaxrs/task/ActionAppend.java

@@ -0,0 +1,117 @@
+package com.x.processplatform.service.processing.jaxrs.task;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.google.gson.JsonElement;
+import com.x.base.core.container.EntityManagerContainer;
+import com.x.base.core.container.factory.EntityManagerContainerFactory;
+import com.x.base.core.project.annotation.FieldDescribe;
+import com.x.base.core.project.exception.ExceptionEntityNotExist;
+import com.x.base.core.project.gson.GsonPropertyObject;
+import com.x.base.core.project.http.ActionResult;
+import com.x.base.core.project.http.EffectivePerson;
+import com.x.base.core.project.jaxrs.WrapBoolean;
+import com.x.base.core.project.tools.ListTools;
+import com.x.processplatform.core.entity.content.Data;
+import com.x.processplatform.core.entity.content.Task;
+import com.x.processplatform.core.entity.content.Work;
+import com.x.processplatform.core.entity.element.ActivityType;
+import com.x.processplatform.core.entity.element.Manual;
+import com.x.processplatform.core.entity.element.Route;
+import com.x.processplatform.service.processing.Business;
+import com.x.processplatform.service.processing.ScriptHelper;
+import com.x.processplatform.service.processing.ScriptHelperFactory;
+import com.x.processplatform.service.processing.WorkDataHelper;
+
+class ActionAppend extends BaseAction {
+
+	ActionResult<Wo> execute(EffectivePerson effectivePerson, String id, JsonElement jsonElement) throws Exception {
+
+		try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+
+			Business business = new Business(emc);
+
+			ActionResult<Wo> result = new ActionResult<>();
+
+			Wi wi = this.convertToWrapIn(jsonElement, Wi.class);
+
+			Task task = emc.find(id, Task.class);
+
+			if (null == task) {
+				throw new ExceptionEntityNotExist(id, Task.class);
+			}
+
+			Work work = emc.find(task.getWork(), Work.class);
+
+			if (null == work) {
+				throw new ExceptionEntityNotExist(task.getWork(), Work.class);
+			}
+
+			Manual manual = (Manual) business.element().get(task.getActivity(), ActivityType.manual);
+			Route route = this.getRoute(business, task, manual);
+			List<String> identities = new ArrayList<>();
+			identities.addAll(work.getManualTaskIdentityList());
+			if (ListTools.isNotEmpty(wi.getIdentityList())) {
+				identities.addAll(wi.getIdentityList());
+			}
+			if ((null != manual) && (route != null)) {
+				if (StringUtils.equals(route.getType(), Route.TYPE_APPENDTASK)
+						&& StringUtils.equals(manual.getId(), route.getActivity())) {
+					if (StringUtils.equals(route.getAppendTaskIdentityType(), Route.APPENDTASKIDENTITYTYPE_SCRIPT)) {
+						Data data = new Data();
+						WorkDataHelper workDataHelper = new WorkDataHelper(business.entityManagerContainer(), work);
+						data = workDataHelper.get();
+						ScriptHelper scriptHelper = ScriptHelperFactory.createWithTask(business, work, data, manual,
+								task);
+						List<String> os = scriptHelper.evalExtrectDistinguishedName(work.getApplication(),
+								route.getAppendTaskIdentityScript(), route.getAppendTaskIdentityScriptText());
+						if (ListTools.isNotEmpty(os)) {
+							identities.addAll(os);
+						}
+					}
+				}
+			}
+			identities = business.organization().identity().list(ListTools.trim(identities, true, true));
+			identities.remove(task.getIdentity());
+			emc.beginTransaction(Work.class);
+			work.setManualTaskIdentityList(identities);
+			emc.commit();
+			Wo wo = new Wo();
+			wo.setValue(true);
+			result.setData(wo);
+			return result;
+		}
+	}
+
+	private Route getRoute(Business business, Task task, Manual manual) throws Exception {
+		for (Route o : business.element().listRouteWithManual(manual.getId())) {
+			if (StringUtils.equals(task.getRouteName(), o.getName())) {
+				return o;
+			}
+		}
+		return null;
+	}
+
+	public static class Wi extends GsonPropertyObject {
+
+		@FieldDescribe("添加的待办身份.")
+		private List<String> identityList;
+
+		public List<String> getIdentityList() {
+			return identityList;
+		}
+
+		public void setIdentityList(List<String> identityList) {
+			this.identityList = identityList;
+		}
+
+	}
+
+	public static class Wo extends WrapBoolean {
+
+	}
+
+}

+ 8 - 21
o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/jaxrs/task/ActionProcessing.java

@@ -3,7 +3,6 @@ package com.x.processplatform.service.processing.jaxrs.task;
 import java.util.Date;
 import java.util.Objects;
 
-import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.StringUtils;
 
 import com.google.gson.JsonElement;
@@ -29,8 +28,6 @@ import com.x.processplatform.core.entity.element.ActivityType;
 import com.x.processplatform.core.entity.element.Manual;
 import com.x.processplatform.service.processing.Business;
 import com.x.processplatform.service.processing.MessageFactory;
-import com.x.processplatform.service.processing.Processing;
-import com.x.processplatform.service.processing.ProcessingAttributes;
 import com.x.processplatform.service.processing.ScriptHelper;
 import com.x.processplatform.service.processing.ScriptHelperFactory;
 import com.x.processplatform.service.processing.WorkDataHelper;
@@ -112,14 +109,12 @@ class ActionProcessing extends BaseAction {
 				}
 			}
 			MessageFactory.task_to_taskCompleted(taskCompleted);
-//			MessageFactory.taskCompleted_create(taskCompleted);
-//			MessageFactory.task_delete(task);
-			if (BooleanUtils.isNotFalse(wi.getFinallyProcessingWork())) {
-				ProcessingAttributes processingAttributes = new ProcessingAttributes();
-				processingAttributes.setDebugger(effectivePerson.getDebugger());
-				Processing processing = new Processing(processingAttributes);
-				processing.processing(task.getWork());
-			}
+//			if (BooleanUtils.isNotFalse(wi.getFinallyProcessingWork())) {
+//				ProcessingAttributes processingAttributes = new ProcessingAttributes();
+//				processingAttributes.setDebugger(effectivePerson.getDebugger());
+//				Processing processing = new Processing(processingAttributes);
+//				processing.processing(task.getWork());
+//			}
 			Wo wo = new Wo();
 			wo.setId(task.getId());
 			result.setData(wo);
@@ -135,8 +130,8 @@ class ActionProcessing extends BaseAction {
 		@FieldDescribe("流转类型.")
 		private ProcessingType processingType;
 
-		@FieldDescribe("最后是否触发work的流转,默认流转.")
-		private Boolean finallyProcessingWork;
+//		@FieldDescribe("最后是否触发work的流转,默认流转.")
+//		private Boolean finallyProcessingWork;
 
 		@FieldDescribe("路由数据.")
 		private JsonElement routeData;
@@ -149,14 +144,6 @@ class ActionProcessing extends BaseAction {
 			this.processingType = processingType;
 		}
 
-		public Boolean getFinallyProcessingWork() {
-			return finallyProcessingWork;
-		}
-
-		public void setFinallyProcessingWork(Boolean finallyProcessingWork) {
-			this.finallyProcessingWork = finallyProcessingWork;
-		}
-
 		public JsonElement getRouteData() {
 			return routeData;
 		}

+ 18 - 0
o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/jaxrs/task/TaskAction.java

@@ -123,4 +123,22 @@ public class TaskAction extends StandardJaxrsAction {
 		}
 		asyncResponse.resume(ResponseFactory.getDefaultActionResultResponse(result));
 	}
+
+	@JaxrsMethodDescribe(value = "添加待办.", action = ActionAppend.class)
+	@PUT
+	@Path("{id}/append")
+	@Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public void append(@Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request,
+			@JaxrsParameterDescribe("标识") @PathParam("id") String id, JsonElement jsonElement) {
+		ActionResult<ActionAppend.Wo> result = new ActionResult<>();
+		EffectivePerson effectivePerson = this.effectivePerson(request);
+		try {
+			result = new ActionAppend().execute(effectivePerson, id, jsonElement);
+		} catch (Exception e) {
+			logger.error(e, effectivePerson, request, jsonElement);
+			result.error(e);
+		}
+		asyncResponse.resume(ResponseFactory.getDefaultActionResultResponse(result));
+	}
 }

+ 140 - 0
o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/jaxrs/work/ActionManualAppendIdentity.java

@@ -0,0 +1,140 @@
+package com.x.processplatform.service.processing.jaxrs.work;
+
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.commons.collections4.ListUtils;
+
+import com.google.gson.JsonElement;
+import com.x.base.core.container.EntityManagerContainer;
+import com.x.base.core.container.factory.EntityManagerContainerFactory;
+import com.x.base.core.entity.annotation.CheckPersistType;
+import com.x.base.core.project.annotation.FieldDescribe;
+import com.x.base.core.project.exception.ExceptionEntityNotExist;
+import com.x.base.core.project.gson.GsonPropertyObject;
+import com.x.base.core.project.http.ActionResult;
+import com.x.base.core.project.http.EffectivePerson;
+import com.x.base.core.project.jaxrs.WrapStringList;
+import com.x.processplatform.core.entity.content.Work;
+import com.x.processplatform.core.entity.element.ActivityType;
+import com.x.processplatform.service.processing.Business;
+
+class ActionManualAppendIdentity extends BaseAction {
+
+	ActionResult<Wo> execute(EffectivePerson effectivePerson, String id, JsonElement jsonElement) throws Exception {
+
+		try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+
+			Business business = new Business(emc);
+
+			ActionResult<Wo> result = new ActionResult<>();
+
+			Wi wi = this.convertToWrapIn(jsonElement, Wi.class);
+
+			Work work = emc.find(id, Work.class);
+
+			if (null == work) {
+				throw new ExceptionEntityNotExist(id, Work.class);
+			}
+
+			if (!Objects.equals(ActivityType.manual, work.getActivityType())) {
+				throw new ExceptionNotManual(work.getActivity());
+			}
+
+			List<String> taskIdentities = business.organization().identity().list(wi.getTaskIdentityList());
+
+			taskIdentities = ListUtils.subtract(taskIdentities, work.getManualTaskIdentityList());
+
+			work.setManualTaskIdentityList(ListUtils.sum(work.getManualTaskIdentityList(), wi.getTaskIdentityList()));
+
+			emc.beginTransaction(Work.class);
+
+			emc.check(work, CheckPersistType.all);
+
+			emc.commit();
+
+			Wo wo = new Wo();
+
+			wo.setTaskIdentityList(taskIdentities);
+
+			result.setData(wo);
+
+			return result;
+		}
+	}
+
+	public static class Wi extends GsonPropertyObject {
+
+		@FieldDescribe("添加的待办身份.")
+		private List<String> taskIdentityList;
+
+		@FieldDescribe("添加的待阅身份.")
+		private List<String> readIdentityList;
+
+		@FieldDescribe("添加的参阅身份.")
+		private List<String> reviewIdentityList;
+
+		public List<String> getTaskIdentityList() {
+			return taskIdentityList;
+		}
+
+		public void setTaskIdentityList(List<String> taskIdentityList) {
+			this.taskIdentityList = taskIdentityList;
+		}
+
+		public List<String> getReadIdentityList() {
+			return readIdentityList;
+		}
+
+		public void setReadIdentityList(List<String> readIdentityList) {
+			this.readIdentityList = readIdentityList;
+		}
+
+		public List<String> getReviewIdentityList() {
+			return reviewIdentityList;
+		}
+
+		public void setReviewIdentityList(List<String> reviewIdentityList) {
+			this.reviewIdentityList = reviewIdentityList;
+		}
+
+	}
+
+	public static class Wo extends WrapStringList {
+
+		@FieldDescribe("添加的待办身份.")
+		private List<String> taskIdentityList;
+
+		@FieldDescribe("添加的待阅身份.")
+		private List<String> readIdentityList;
+
+		@FieldDescribe("添加的参阅身份.")
+		private List<String> reviewIdentityList;
+
+		public List<String> getTaskIdentityList() {
+			return taskIdentityList;
+		}
+
+		public void setTaskIdentityList(List<String> taskIdentityList) {
+			this.taskIdentityList = taskIdentityList;
+		}
+
+		public List<String> getReadIdentityList() {
+			return readIdentityList;
+		}
+
+		public void setReadIdentityList(List<String> readIdentityList) {
+			this.readIdentityList = readIdentityList;
+		}
+
+		public List<String> getReviewIdentityList() {
+			return reviewIdentityList;
+		}
+
+		public void setReviewIdentityList(List<String> reviewIdentityList) {
+			this.reviewIdentityList = reviewIdentityList;
+		}
+
+	}
+
+}

+ 13 - 0
o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/jaxrs/work/ExceptionNotManual.java

@@ -0,0 +1,13 @@
+package com.x.processplatform.service.processing.jaxrs.work;
+
+import com.x.base.core.project.exception.PromptException;
+
+class ExceptionNotManual extends PromptException {
+
+	private static final long serialVersionUID = -7038279889683420366L;
+
+	ExceptionNotManual(String activityId) {
+		super("非人工节点:{}.", activityId);
+	}
+
+}

+ 18 - 0
o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/jaxrs/work/WorkAction.java

@@ -159,4 +159,22 @@ public class WorkAction extends StandardJaxrsAction {
 		asyncResponse.resume(ResponseFactory.getDefaultActionResultResponse(result));
 	}
 
+	@JaxrsMethodDescribe(value = "添加人工环节处理人,不会自动产生,需要processing之后才会执行.", action = ActionManualAppendIdentity.class)
+	@PUT
+	@Path("{id}/manual/append/identity")
+	@Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public void manualAppendIdentity(@Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request,
+			@JaxrsParameterDescribe("工作标识") @PathParam("id") String id, JsonElement jsonElement) {
+		ActionResult<ActionManualAppendIdentity.Wo> result = new ActionResult<>();
+		EffectivePerson effectivePerson = this.effectivePerson(request);
+		try {
+			result = new ActionManualAppendIdentity().execute(effectivePerson, id, jsonElement);
+		} catch (Exception e) {
+			logger.error(e, effectivePerson, request, null);
+			result.error(e);
+		}
+		asyncResponse.resume(ResponseFactory.getDefaultActionResultResponse(result));
+	}
+
 }

+ 0 - 8
o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/processor/AbstractProcessor.java

@@ -220,14 +220,6 @@ public abstract class AbstractProcessor extends AbstractBaseProcessor {
 	}
 
 	private void callBeforeExecuteScript(AeiObjects aeiObjects) throws Exception {
-		System.out.println(StringUtils.isNotEmpty(aeiObjects.getActivity().get(BES, String.class)));
-		System.out.println(aeiObjects.getActivity().get("beforeExecuteScriptText", String.class));
-
-		System.out.println(aeiObjects.getActivity().get("beforeExecuteScriptText"));
-		System.out.println(aeiObjects.getActivity().get("form"));
-		System.out.println(aeiObjects.getActivity());
-		System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
-		System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
 		if (aeiObjects.getActivityProcessingConfigurator().getCallBeforeExecuteScript()) {
 			if (this.hasBeforeExecuteScript(aeiObjects.getActivity())) {
 				ScriptHelper scriptHelper = ScriptHelperFactory.create(aeiObjects);

+ 91 - 2
o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/processor/end/AbstractEndProcessor.java

@@ -2,17 +2,21 @@ package com.x.processplatform.service.processing.processor.end;
 
 import java.util.List;
 
+import org.apache.commons.lang3.StringUtils;
+
 import com.x.base.core.container.EntityManagerContainer;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
 import com.x.processplatform.core.entity.content.Work;
 import com.x.processplatform.core.entity.element.End;
 import com.x.processplatform.core.entity.element.Route;
 import com.x.processplatform.service.processing.processor.AbstractProcessor;
 import com.x.processplatform.service.processing.processor.AeiObjects;
-import com.x.processplatform.service.processing.processor.AeiObjects;
-import com.x.processplatform.service.processing.processor.AeiObjects;
 
 public abstract class AbstractEndProcessor extends AbstractProcessor {
 
+	private static Logger logger = LoggerFactory.getLogger(AbstractEndProcessor.class);
+
 	protected AbstractEndProcessor(EntityManagerContainer entityManagerContainer) throws Exception {
 		super(entityManagerContainer);
 	}
@@ -64,4 +68,89 @@ public abstract class AbstractEndProcessor extends AbstractProcessor {
 	protected abstract void executingCommitted(AeiObjects aeiObjects, End end) throws Exception;
 
 	protected abstract void inquiringCommitted(AeiObjects aeiObjects, End end) throws Exception;
+
+	protected void mergeTaskCompleted(AeiObjects aeiObjects, Work work, Work oldest) {
+		try {
+			aeiObjects.getTaskCompleteds().stream().filter(o -> StringUtils.equals(o.getWork(), work.getId()))
+					.forEach(o -> {
+						o.setWork(oldest.getId());
+						aeiObjects.getUpdateTaskCompleteds().add(o);
+					});
+		} catch (Exception e) {
+			logger.error(e);
+		}
+	}
+
+	protected void mergeRead(AeiObjects aeiObjects, Work work, Work target) {
+		try {
+			aeiObjects.getReads().stream().filter(o -> StringUtils.equals(o.getWork(), work.getId())).forEach(o -> {
+				o.setWork(target.getId());
+				aeiObjects.getUpdateReads().add(o);
+			});
+		} catch (Exception e) {
+			logger.error(e);
+		}
+	}
+
+	protected void mergeReadCompleted(AeiObjects aeiObjects, Work work, Work target) {
+		try {
+			aeiObjects.getReadCompleteds().stream().filter(o -> StringUtils.equals(o.getWork(), work.getId()))
+					.forEach(o -> {
+						o.setWork(target.getId());
+						aeiObjects.getUpdateReadCompleteds().add(o);
+					});
+		} catch (Exception e) {
+			logger.error(e);
+		}
+	}
+
+	protected void mergeReview(AeiObjects aeiObjects, Work work, Work target) {
+		try {
+			aeiObjects.getReviews().stream().filter(o -> StringUtils.equals(o.getWork(), work.getId())).forEach(o -> {
+				o.setWork(target.getId());
+				aeiObjects.getUpdateReviews().add(o);
+			});
+		} catch (Exception e) {
+			logger.error(e);
+		}
+	}
+
+	protected void mergeHint(AeiObjects aeiObjects, Work work, Work target) {
+		try {
+			aeiObjects.getHints().stream().filter(o -> StringUtils.equals(o.getWork(), work.getId())).forEach(o -> {
+				o.setWork(target.getId());
+				aeiObjects.getUpdateHints().add(o);
+			});
+		} catch (Exception e) {
+			logger.error(e);
+		}
+	}
+
+	protected void mergeAttachment(AeiObjects aeiObjects, Work work, Work target) {
+		try {
+			aeiObjects.getAttachments().stream().filter(o -> StringUtils.equals(o.getWork(), work.getId()))
+					.forEach(o -> {
+						o.setWork(target.getId());
+						aeiObjects.getUpdateAttachments().add(o);
+					});
+		} catch (Exception e) {
+			logger.error(e);
+		}
+	}
+
+	protected void mergeWorkLog(AeiObjects aeiObjects, Work work, Work target) {
+		try {
+			aeiObjects.getWorkLogs().stream()
+					.filter(o -> StringUtils.equals(work.getActivityToken(), o.getArrivedActivityToken())
+							&& StringUtils.equals(o.getWork(), work.getId()))
+					.forEach(o -> {
+						o.setWork(target.getId());
+						// o.setArrivedActivityToken(target.getActivityToken());
+						aeiObjects.getUpdateWorkLogs().add(o);
+					});
+		} catch (Exception e) {
+			logger.error(e);
+		}
+	}
+
 }

+ 154 - 67
o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/processor/end/EndProcessor.java

@@ -16,7 +16,6 @@ import com.x.base.core.project.logger.LoggerFactory;
 import com.x.base.core.project.tools.ListTools;
 import com.x.processplatform.core.entity.content.Work;
 import com.x.processplatform.core.entity.content.WorkCompleted;
-import com.x.processplatform.core.entity.element.ActivityType;
 import com.x.processplatform.core.entity.element.End;
 import com.x.processplatform.core.entity.element.Form;
 import com.x.processplatform.core.entity.element.Route;
@@ -43,75 +42,163 @@ public class EndProcessor extends AbstractEndProcessor {
 
 	@Override
 	protected List<Work> executing(AeiObjects aeiObjects, End end) throws Exception {
+
 		List<Work> results = new ArrayList<>();
-		if (!aeiObjects.getWorks().stream().allMatch(o -> Objects.equals(o.getActivityType(), ActivityType.end))) {
-			/* 如果还有多个副本没有到达end节点那么不能路由 */
-			return results;
+
+		Work other = aeiObjects.getWorks().stream().filter(o -> {
+			return o != aeiObjects.getWork();
+		}).sorted(Comparator.comparing(Work::getCreateTime)).findFirst().orElse(null);
+
+		if (null != other) {
+			aeiObjects.getUpdateWorks().add(other);
+			aeiObjects.getDeleteWorks().add(aeiObjects.getWork());
+			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.mergeHint(aeiObjects, aeiObjects.getWork(), other);
+			this.mergeAttachment(aeiObjects, aeiObjects.getWork(), other);
+			this.mergeWorkLog(aeiObjects, aeiObjects.getWork(), other);
+			aeiObjects.getWorkLogs().stream()
+					.filter(p -> StringUtils.equals(p.getFromActivityToken(), aeiObjects.getWork().getActivityToken()))
+					.forEach(obj -> {
+						aeiObjects.getDeleteWorkLogs().add(obj);
+					});
+		} else {
+			WorkCompleted workCompleted = this.createWorkCompleted(aeiObjects.getWork());
+			workCompleted.setAllowRollback(end.getAllowRollback());
+			aeiObjects.getCreateWorkCompleteds().add(workCompleted);
+			aeiObjects.getTasks().stream().forEach(o -> aeiObjects.getDeleteTasks().add(o));
+			aeiObjects.getHints().stream().forEach(o -> aeiObjects.getDeleteHints().add(o));
+			aeiObjects.getTaskCompleteds().stream().forEach(o -> {
+				/* 已办的完成时间是不需要更新的 */
+				o.setCompleted(true);
+				o.setWorkCompleted(workCompleted.getId());
+				/* 加入到更新队列保证事务开启 */
+				aeiObjects.getUpdateTaskCompleteds().add(o);
+			});
+			aeiObjects.getReads().stream().forEach(o -> {
+				/* 待阅的完成时间是不需要更新的 */
+				o.setCompleted(true);
+				o.setWorkCompleted(workCompleted.getId());
+				/* 加入到更新队列保证事务开启 */
+				aeiObjects.getUpdateReads().add(o);
+			});
+			aeiObjects.getReadCompleteds().stream().forEach(o -> {
+				/* 已阅的完成时间是不需要更新的 */
+				o.setCompleted(true);
+				o.setWorkCompleted(workCompleted.getId());
+				/* 加入到更新队列保证事务开启 */
+				aeiObjects.getUpdateReadCompleteds().add(o);
+			});
+			aeiObjects.getReviews().stream().forEach(o -> {
+				o.setCompleted(true);
+				o.setWorkCompleted(workCompleted.getId());
+				o.setCompletedTime(workCompleted.getCompletedTime());
+				o.setCompletedTimeMonth(workCompleted.getCompletedTimeMonth());
+				/* 加入到更新队列保证事务开启 */
+				aeiObjects.getUpdateReviews().add(o);
+			});
+			aeiObjects.getWorkLogs().stream().forEach(o -> {
+				o.setSplitting(false);
+				o.setSplitToken("");
+				o.setSplitTokenList(new ArrayList<String>());
+				o.setSplitValue("");
+				o.setCompleted(true);
+				o.setWorkCompleted(workCompleted.getId());
+				/* 加入到更新队列保证事务开启 */
+				aeiObjects.getUpdateWorkLogs().add(o);
+				/* 删除未连接的WorkLog */
+				if (BooleanUtils.isNotTrue(o.getConnected())) {
+					aeiObjects.getDeleteWorkLogs().add(o);
+				}
+			});
+			aeiObjects.getAttachments().stream().forEach(o -> {
+				o.setCompleted(true);
+				o.setWorkCompleted(workCompleted.getId());
+				/* 加入到更新队列保证事务开启 */
+				aeiObjects.getUpdateAttachments().add(o);
+			});
+			/* 已workCompleted数据为准进行更新 */
+			aeiObjects.getData().setWork(workCompleted);
+			aeiObjects.getData().setAttachmentList(aeiObjects.getAttachments());
+			aeiObjects.getDeleteWorks().addAll(aeiObjects.getWorks());
 		}
-		/* 删除所有待办 */
-		Work oldest = aeiObjects.getWorks().stream()
-				.sorted(Comparator.comparing(Work::getCreateTime, Comparator.nullsLast(Date::compareTo))).findFirst()
-				.get();
-		WorkCompleted workCompleted = this.createWorkCompleted(oldest);
-
-		workCompleted.setAllowRollback(end.getAllowRollback());
-		aeiObjects.getCreateWorkCompleteds().add(workCompleted);
-		aeiObjects.getTasks().stream().forEach(o -> aeiObjects.getDeleteTasks().add(o));
-		aeiObjects.getHints().stream().forEach(o -> aeiObjects.getDeleteHints().add(o));
-		aeiObjects.getTaskCompleteds().stream().forEach(o -> {
-			/* 已办的完成时间是不需要更新的 */
-			o.setCompleted(true);
-			o.setWorkCompleted(workCompleted.getId());
-			/* 加入到更新队列保证事务开启 */
-			aeiObjects.getUpdateTaskCompleteds().add(o);
-		});
-		aeiObjects.getReads().stream().forEach(o -> {
-			/* 待阅的完成时间是不需要更新的 */
-			o.setCompleted(true);
-			o.setWorkCompleted(workCompleted.getId());
-			/* 加入到更新队列保证事务开启 */
-			aeiObjects.getUpdateReads().add(o);
-		});
-		aeiObjects.getReadCompleteds().stream().forEach(o -> {
-			/* 已阅的完成时间是不需要更新的 */
-			o.setCompleted(true);
-			o.setWorkCompleted(workCompleted.getId());
-			/* 加入到更新队列保证事务开启 */
-			aeiObjects.getUpdateReadCompleteds().add(o);
-		});
-		aeiObjects.getReviews().stream().forEach(o -> {
-			o.setCompleted(true);
-			o.setWorkCompleted(workCompleted.getId());
-			o.setCompletedTime(workCompleted.getCompletedTime());
-			o.setCompletedTimeMonth(workCompleted.getCompletedTimeMonth());
-			/* 加入到更新队列保证事务开启 */
-			aeiObjects.getUpdateReviews().add(o);
-		});
-		aeiObjects.getWorkLogs().stream().forEach(o -> {
-			o.setSplitting(false);
-			o.setSplitToken("");
-			o.setSplitTokenList(new ArrayList<String>());
-			o.setSplitValue("");
-			o.setCompleted(true);
-			o.setWorkCompleted(workCompleted.getId());
-			/* 加入到更新队列保证事务开启 */
-			aeiObjects.getUpdateWorkLogs().add(o);
-			/* 删除未连接的WorkLog */
-			if (BooleanUtils.isNotTrue(o.getConnected())) {
-				aeiObjects.getDeleteWorkLogs().add(o);
-			}
-		});
-		aeiObjects.getAttachments().stream().forEach(o -> {
-			o.setCompleted(true);
-			o.setWorkCompleted(workCompleted.getId());
-			/* 加入到更新队列保证事务开启 */
-			aeiObjects.getUpdateAttachments().add(o);
-		});
-		/* 已workCompleted数据为准进行更新 */
-		aeiObjects.getData().setWork(workCompleted);
-		aeiObjects.getData().setAttachmentList(aeiObjects.getAttachments());
-		aeiObjects.getDeleteWorks().addAll(aeiObjects.getWorks());
+
 		return results;
+
+//		if (aeiObjects.getWorks().stream().count() > 1) {
+//			aeiObjects.getDeleteWorks().add(aeiObjects.getWork());
+//		}
+//
+//		if (!aeiObjects.getWorks().stream().allMatch(o -> Objects.equals(o.getActivityType(), ActivityType.end))) {
+//			/* 如果还有多个副本没有到达end节点那么不能路由 */
+//			return results;
+//		}
+//		/* 删除所有待办 */
+//		Work oldest = aeiObjects.getWorks().stream()
+//				.sorted(Comparator.comparing(Work::getCreateTime, Comparator.nullsLast(Date::compareTo))).findFirst()
+//				.get();
+//		WorkCompleted workCompleted = this.createWorkCompleted(oldest);
+//
+//		workCompleted.setAllowRollback(end.getAllowRollback());
+//		aeiObjects.getCreateWorkCompleteds().add(workCompleted);
+//		aeiObjects.getTasks().stream().forEach(o -> aeiObjects.getDeleteTasks().add(o));
+//		aeiObjects.getHints().stream().forEach(o -> aeiObjects.getDeleteHints().add(o));
+//		aeiObjects.getTaskCompleteds().stream().forEach(o -> {
+//			/* 已办的完成时间是不需要更新的 */
+//			o.setCompleted(true);
+//			o.setWorkCompleted(workCompleted.getId());
+//			/* 加入到更新队列保证事务开启 */
+//			aeiObjects.getUpdateTaskCompleteds().add(o);
+//		});
+//		aeiObjects.getReads().stream().forEach(o -> {
+//			/* 待阅的完成时间是不需要更新的 */
+//			o.setCompleted(true);
+//			o.setWorkCompleted(workCompleted.getId());
+//			/* 加入到更新队列保证事务开启 */
+//			aeiObjects.getUpdateReads().add(o);
+//		});
+//		aeiObjects.getReadCompleteds().stream().forEach(o -> {
+//			/* 已阅的完成时间是不需要更新的 */
+//			o.setCompleted(true);
+//			o.setWorkCompleted(workCompleted.getId());
+//			/* 加入到更新队列保证事务开启 */
+//			aeiObjects.getUpdateReadCompleteds().add(o);
+//		});
+//		aeiObjects.getReviews().stream().forEach(o -> {
+//			o.setCompleted(true);
+//			o.setWorkCompleted(workCompleted.getId());
+//			o.setCompletedTime(workCompleted.getCompletedTime());
+//			o.setCompletedTimeMonth(workCompleted.getCompletedTimeMonth());
+//			/* 加入到更新队列保证事务开启 */
+//			aeiObjects.getUpdateReviews().add(o);
+//		});
+//		aeiObjects.getWorkLogs().stream().forEach(o -> {
+//			o.setSplitting(false);
+//			o.setSplitToken("");
+//			o.setSplitTokenList(new ArrayList<String>());
+//			o.setSplitValue("");
+//			o.setCompleted(true);
+//			o.setWorkCompleted(workCompleted.getId());
+//			/* 加入到更新队列保证事务开启 */
+//			aeiObjects.getUpdateWorkLogs().add(o);
+//			/* 删除未连接的WorkLog */
+//			if (BooleanUtils.isNotTrue(o.getConnected())) {
+//				aeiObjects.getDeleteWorkLogs().add(o);
+//			}
+//		});
+//		aeiObjects.getAttachments().stream().forEach(o -> {
+//			o.setCompleted(true);
+//			o.setWorkCompleted(workCompleted.getId());
+//			/* 加入到更新队列保证事务开启 */
+//			aeiObjects.getUpdateAttachments().add(o);
+//		});
+//		/* 已workCompleted数据为准进行更新 */
+//		aeiObjects.getData().setWork(workCompleted);
+//		aeiObjects.getData().setAttachmentList(aeiObjects.getAttachments());
+//		aeiObjects.getDeleteWorks().addAll(aeiObjects.getWorks());
+//		return results;
 	}
 
 	@Override

+ 154 - 0
o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/processor/end/EndProcessor2.java

@@ -0,0 +1,154 @@
+package com.x.processplatform.service.processing.processor.end;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import com.x.base.core.container.EntityManagerContainer;
+import com.x.base.core.project.config.Config;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+import com.x.base.core.project.tools.ListTools;
+import com.x.processplatform.core.entity.content.Work;
+import com.x.processplatform.core.entity.content.WorkCompleted;
+import com.x.processplatform.core.entity.element.ActivityType;
+import com.x.processplatform.core.entity.element.End;
+import com.x.processplatform.core.entity.element.Form;
+import com.x.processplatform.core.entity.element.Route;
+import com.x.processplatform.service.processing.ScriptHelper;
+import com.x.processplatform.service.processing.ScriptHelperFactory;
+import com.x.processplatform.service.processing.processor.AeiObjects;
+
+public class EndProcessor2 extends AbstractEndProcessor {
+
+	private static Logger logger = LoggerFactory.getLogger(EndProcessor2.class);
+
+	public EndProcessor2(EntityManagerContainer entityManagerContainer) throws Exception {
+		super(entityManagerContainer);
+	}
+
+	@Override
+	protected Work arriving(AeiObjects aeiObjects, End end) throws Exception {
+		return aeiObjects.getWork();
+	}
+
+	@Override
+	protected void arrivingCommitted(AeiObjects aeiObjects, End end) throws Exception {
+	}
+
+	@Override
+	protected List<Work> executing(AeiObjects aeiObjects, End end) throws Exception {
+		List<Work> results = new ArrayList<>();
+		if (!aeiObjects.getWorks().stream().allMatch(o -> Objects.equals(o.getActivityType(), ActivityType.end))) {
+			/* 如果还有多个副本没有到达end节点那么不能路由 */
+			return results;
+		}
+		/* 删除所有待办 */
+		Work oldest = aeiObjects.getWorks().stream()
+				.sorted(Comparator.comparing(Work::getCreateTime, Comparator.nullsLast(Date::compareTo))).findFirst()
+				.get();
+		WorkCompleted workCompleted = this.createWorkCompleted(oldest);
+
+		workCompleted.setAllowRollback(end.getAllowRollback());
+		aeiObjects.getCreateWorkCompleteds().add(workCompleted);
+		aeiObjects.getTasks().stream().forEach(o -> aeiObjects.getDeleteTasks().add(o));
+		aeiObjects.getHints().stream().forEach(o -> aeiObjects.getDeleteHints().add(o));
+		aeiObjects.getTaskCompleteds().stream().forEach(o -> {
+			/* 已办的完成时间是不需要更新的 */
+			o.setCompleted(true);
+			o.setWorkCompleted(workCompleted.getId());
+			/* 加入到更新队列保证事务开启 */
+			aeiObjects.getUpdateTaskCompleteds().add(o);
+		});
+		aeiObjects.getReads().stream().forEach(o -> {
+			/* 待阅的完成时间是不需要更新的 */
+			o.setCompleted(true);
+			o.setWorkCompleted(workCompleted.getId());
+			/* 加入到更新队列保证事务开启 */
+			aeiObjects.getUpdateReads().add(o);
+		});
+		aeiObjects.getReadCompleteds().stream().forEach(o -> {
+			/* 已阅的完成时间是不需要更新的 */
+			o.setCompleted(true);
+			o.setWorkCompleted(workCompleted.getId());
+			/* 加入到更新队列保证事务开启 */
+			aeiObjects.getUpdateReadCompleteds().add(o);
+		});
+		aeiObjects.getReviews().stream().forEach(o -> {
+			o.setCompleted(true);
+			o.setWorkCompleted(workCompleted.getId());
+			o.setCompletedTime(workCompleted.getCompletedTime());
+			o.setCompletedTimeMonth(workCompleted.getCompletedTimeMonth());
+			/* 加入到更新队列保证事务开启 */
+			aeiObjects.getUpdateReviews().add(o);
+		});
+		aeiObjects.getWorkLogs().stream().forEach(o -> {
+			o.setSplitting(false);
+			o.setSplitToken("");
+			o.setSplitTokenList(new ArrayList<String>());
+			o.setSplitValue("");
+			o.setCompleted(true);
+			o.setWorkCompleted(workCompleted.getId());
+			/* 加入到更新队列保证事务开启 */
+			aeiObjects.getUpdateWorkLogs().add(o);
+			/* 删除未连接的WorkLog */
+			if (BooleanUtils.isNotTrue(o.getConnected())) {
+				aeiObjects.getDeleteWorkLogs().add(o);
+			}
+		});
+		aeiObjects.getAttachments().stream().forEach(o -> {
+			o.setCompleted(true);
+			o.setWorkCompleted(workCompleted.getId());
+			/* 加入到更新队列保证事务开启 */
+			aeiObjects.getUpdateAttachments().add(o);
+		});
+		/* 已workCompleted数据为准进行更新 */
+		aeiObjects.getData().setWork(workCompleted);
+		aeiObjects.getData().setAttachmentList(aeiObjects.getAttachments());
+		aeiObjects.getDeleteWorks().addAll(aeiObjects.getWorks());
+		return results;
+	}
+
+	@Override
+	protected void executingCommitted(AeiObjects aeiObjects, End end) throws Exception {
+		if (StringUtils.isNotEmpty(aeiObjects.getProcess().getAfterEndScript())
+				|| StringUtils.isNotEmpty(aeiObjects.getProcess().getAfterEndScriptText())) {
+			ScriptHelper scriptHelper = ScriptHelperFactory.create(aeiObjects);
+			scriptHelper.eval(aeiObjects.getWork().getApplication(),
+					Objects.toString(aeiObjects.getProcess().getAfterEndScript()),
+					Objects.toString(aeiObjects.getProcess().getAfterEndScriptText()));
+		}
+	}
+
+	@Override
+	protected List<Route> inquiring(AeiObjects aeiObjects, End end) throws Exception {
+		return new ArrayList<Route>();
+	}
+
+	@Override
+	protected void inquiringCommitted(AeiObjects aeiObjects, End end) throws Exception {
+	}
+
+	/* 根据work和data创建最终保存的workCompleted */
+	private WorkCompleted createWorkCompleted(Work work) throws Exception {
+		Date completedTime = new Date();
+		Long duration = Config.workTime().betweenMinutes(work.getStartTime(), completedTime);
+		String formString = "";
+		String formMobileString = "";
+		if (StringUtils.isNotEmpty(work.getForm())) {
+			Form form = this.entityManagerContainer().fetch(work.getForm(), Form.class,
+					ListTools.toList(Form.data_FIELDNAME, Form.mobileData_FIELDNAME));
+			if (null != form) {
+				formString = form.getData();
+				formMobileString = form.getMobileData();
+			}
+		}
+		WorkCompleted workCompleted = new WorkCompleted(work, completedTime, duration, formString, formMobileString);
+		return workCompleted;
+	}
+}

+ 89 - 0
o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/processor/manual/AbstractManualProcessor.java

@@ -7,6 +7,8 @@ import org.apache.commons.beanutils.PropertyUtils;
 import org.apache.commons.lang3.StringUtils;
 
 import com.x.base.core.container.EntityManagerContainer;
+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.processplatform.core.entity.content.Work;
 import com.x.processplatform.core.entity.element.Activity;
@@ -22,6 +24,8 @@ import com.x.processplatform.service.processing.processor.AeiObjects;
  */
 public abstract class AbstractManualProcessor extends AbstractProcessor {
 
+	private static Logger logger = LoggerFactory.getLogger(AbstractManualProcessor.class);
+
 	protected AbstractManualProcessor(EntityManagerContainer entityManagerContainer) throws Exception {
 		super(entityManagerContainer);
 	}
@@ -91,4 +95,89 @@ public abstract class AbstractManualProcessor extends AbstractProcessor {
 		return StringUtils.isNotEmpty(activity.get(Manual.manualStayScript_FIELDNAME, String.class))
 				|| StringUtils.isNotEmpty(activity.get(Manual.manualStayScriptText_FIELDNAME, String.class));
 	}
+
+	protected void mergeTaskCompleted(AeiObjects aeiObjects, Work work, Work oldest) {
+		try {
+			aeiObjects.getTaskCompleteds().stream().filter(o -> StringUtils.equals(o.getWork(), work.getId()))
+					.forEach(o -> {
+						o.setWork(oldest.getId());
+						// o.setActivityToken(oldest.getActivityToken());
+						aeiObjects.getUpdateTaskCompleteds().add(o);
+					});
+		} catch (Exception e) {
+			logger.error(e);
+		}
+	}
+
+	protected void mergeRead(AeiObjects aeiObjects, Work work, Work target) {
+		try {
+			aeiObjects.getReads().stream().filter(o -> StringUtils.equals(o.getWork(), work.getId())).forEach(o -> {
+				o.setWork(target.getId());
+				aeiObjects.getUpdateReads().add(o);
+			});
+		} catch (Exception e) {
+			logger.error(e);
+		}
+	}
+
+	protected void mergeReadCompleted(AeiObjects aeiObjects, Work work, Work target) {
+		try {
+			aeiObjects.getReadCompleteds().stream().filter(o -> StringUtils.equals(o.getWork(), work.getId()))
+					.forEach(o -> {
+						o.setWork(target.getId());
+						aeiObjects.getUpdateReadCompleteds().add(o);
+					});
+		} catch (Exception e) {
+			logger.error(e);
+		}
+	}
+
+	protected void mergeReview(AeiObjects aeiObjects, Work work, Work target) {
+		try {
+			aeiObjects.getReviews().stream().filter(o -> StringUtils.equals(o.getWork(), work.getId())).forEach(o -> {
+				o.setWork(target.getId());
+				aeiObjects.getUpdateReviews().add(o);
+			});
+		} catch (Exception e) {
+			logger.error(e);
+		}
+	}
+
+	protected void mergeHint(AeiObjects aeiObjects, Work work, Work target) {
+		try {
+			aeiObjects.getHints().stream().filter(o -> StringUtils.equals(o.getWork(), work.getId())).forEach(o -> {
+				o.setWork(target.getId());
+				aeiObjects.getUpdateHints().add(o);
+			});
+		} catch (Exception e) {
+			logger.error(e);
+		}
+	}
+
+	protected void mergeAttachment(AeiObjects aeiObjects, Work work, Work target) {
+		try {
+			aeiObjects.getAttachments().stream().filter(o -> StringUtils.equals(o.getWork(), work.getId()))
+					.forEach(o -> {
+						o.setWork(target.getId());
+						aeiObjects.getUpdateAttachments().add(o);
+					});
+		} catch (Exception e) {
+			logger.error(e);
+		}
+	}
+
+	protected void mergeWorkLog(AeiObjects aeiObjects, Work work, Work target) {
+		try {
+			aeiObjects.getWorkLogs().stream()
+					.filter(o -> StringUtils.equals(work.getActivityToken(), o.getArrivedActivityToken())
+							&& StringUtils.equals(o.getWork(), work.getId()))
+					.forEach(o -> {
+						o.setWork(target.getId());
+						// o.setArrivedActivityToken(target.getActivityToken());
+						aeiObjects.getUpdateWorkLogs().add(o);
+					});
+		} catch (Exception e) {
+			logger.error(e);
+		}
+	}
 }

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

@@ -7,6 +7,7 @@ import java.util.List;
 import java.util.Objects;
 import java.util.stream.Collectors;
 
+import org.apache.commons.collections4.ListUtils;
 import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.time.DateUtils;
@@ -17,7 +18,6 @@ import com.x.base.core.project.config.Config;
 import com.x.base.core.project.logger.Logger;
 import com.x.base.core.project.logger.LoggerFactory;
 import com.x.base.core.project.organization.EmpowerLog;
-import com.x.base.core.project.organization.Empower;
 import com.x.base.core.project.tools.DateTools;
 import com.x.base.core.project.tools.ListTools;
 import com.x.base.core.project.tools.NumberTools;
@@ -49,11 +49,17 @@ public class ManualProcessor extends AbstractManualProcessor {
 
 	@Override
 	protected Work arriving(AeiObjects aeiObjects, Manual manual) throws Exception {
-		/*
-		 * 先记录所有的待办人,必须在这里先进行计算,否则返回值中无法提示下一个处理人.
-		 * 这里先计算处理人存放在manualTaskIdentityList中主要是为了脚本中可以预先知道可能的处理人进行业务处理
-		 */
+		/* 启用同类工作相同活动节点合并,如果有合并的工作,那么直接返回这个工作. */
+		if (BooleanUtils.isTrue(manual.getManualMergeSameJobActivity())) {
+			Work other = this.arriving_mergeSameJobActivity(aeiObjects, manual);
+			if (null != other) {
+				return other;
+			}
+		}
+
+		/* 根据manual计算出来的活动处理人 */
 		List<String> identities = TranslateTaskIdentityTools.translate(aeiObjects, manual);
+
 		if (identities.isEmpty()) {
 			/* 如果活动没有找到任何可用的处理人,那么强制设置处理人为文档创建者,或者配置的 maintenanceIdentity */
 			String effectiveCreatorIdentity = aeiObjects.business().organization().identity()
@@ -66,8 +72,8 @@ public class ManualProcessor extends AbstractManualProcessor {
 				effectiveCreatorIdentity = aeiObjects.business().organization().identity()
 						.get(Config.processPlatform().getMaintenanceIdentity());
 				if (StringUtils.isNotEmpty(effectiveCreatorIdentity)) {
-					logger.info("人工活动到达未能找到指定的处理人, 标题:{}, id:{}, 强制指定处理人为系统维护身份:{}.", aeiObjects.getWork().getTitle(),
-							aeiObjects.getWork().getId(), effectiveCreatorIdentity);
+					logger.info("人工活动到达未能找到指定的处理人, 标题:{}, id:{}, 也没有能找到活动创建人,  强制指定处理人为系统维护身份:{}.",
+							aeiObjects.getWork().getTitle(), aeiObjects.getWork().getId(), effectiveCreatorIdentity);
 					identities.add(effectiveCreatorIdentity);
 				} else {
 					throw new ExceptionExpectedEmpty(aeiObjects.getWork().getTitle(), aeiObjects.getWork().getId(),
@@ -76,6 +82,7 @@ public class ManualProcessor extends AbstractManualProcessor {
 			}
 			aeiObjects.createHint(Hint.EmptyTaskIdentityOnManual(aeiObjects.getWork(), manual));
 		}
+
 		aeiObjects.getWork().setManualTaskIdentityList(identities);
 		/* 查找是否有passSameTarget设置 */
 		Route passSameTargetRoute = aeiObjects.getRoutes().stream()
@@ -147,13 +154,10 @@ public class ManualProcessor extends AbstractManualProcessor {
 
 	@Override
 	protected List<Work> executing(AeiObjects aeiObjects, Manual manual) throws Exception {
+
 		List<Work> results = new ArrayList<>();
 		boolean passThrough = false;
-		/*
-		 * 如果采用List<String> identities =
-		 * TranslateTaskIdentityTools.translate(aeiObjects, manual)
-		 * 将全部重新计算人员,那么重置处理人将被去掉
-		 */
+
 		TaskIdentities taskIdentities = new TaskIdentities(
 				aeiObjects.business().organization().identity().list(aeiObjects.getWork().getManualTaskIdentityList()));
 		if (taskIdentities.isEmpty()) {
@@ -272,11 +276,12 @@ public class ManualProcessor extends AbstractManualProcessor {
 
 	private boolean single(AeiObjects aeiObjects, Manual manual, TaskIdentities taskIdentities) throws Exception {
 		boolean passThrough = false;
-		/* 找到所有的已办 */
+		List<String> identities = taskIdentities.identities();
 		Long count = aeiObjects.getTaskCompleteds().stream().filter(o -> {
 			if (StringUtils.equals(aeiObjects.getWork().getActivityToken(), o.getActivityToken())
 					&& (!o.getProcessingType().equals(ProcessingType.retract))
-					&& (!o.getProcessingType().equals(ProcessingType.reset))) {
+					&& (!o.getProcessingType().equals(ProcessingType.reset))
+					&& (identities.contains(o.getIdentity()))) {
 				return true;
 			} else {
 				return false;
@@ -289,7 +294,7 @@ public class ManualProcessor extends AbstractManualProcessor {
 			}).forEach(o -> {
 				aeiObjects.deleteTask(o);
 			});
-			/** 所有预计的处理人中已经有已办,这个环节已经产生了已办,可以离开换个环节。 */
+			/* 所有预计的处理人中已经有已办,这个环节已经产生了已办,可以离开换个环节。 */
 			passThrough = true;
 		} else {
 			/* 取到期望的待办人员,由于要进行处理需要转换成可读写List */
@@ -324,7 +329,8 @@ public class ManualProcessor extends AbstractManualProcessor {
 	private boolean parallel(AeiObjects aeiObjects, Manual manual, TaskIdentities taskIdentities) throws Exception {
 		boolean passThrough = false;
 		/** 取得本环节已经处理的已办 */
-		List<TaskCompleted> taskCompleteds = this.listEffectiveTaskCompleted(aeiObjects.getWork(), taskIdentities);
+
+		List<TaskCompleted> taskCompleteds = this.listEffectiveTaskCompleted(aeiObjects, taskIdentities);
 		if (ListTools.isEmpty(taskIdentities)) {
 			if (ListTools.isNotEmpty(taskCompleteds)) {
 				/** 预计的处理人全部不存在,且已经有人处理过了 */
@@ -375,7 +381,7 @@ public class ManualProcessor extends AbstractManualProcessor {
 			throw new ExceptionExpectedEmpty(aeiObjects.getWork().getTitle(), aeiObjects.getWork().getId(),
 					manual.getName(), manual.getId());
 		}
-		List<TaskCompleted> done = this.listEffectiveTaskCompleted(aeiObjects.getWork(), taskIdentities);
+		List<TaskCompleted> done = this.listEffectiveTaskCompleted(aeiObjects, taskIdentities);
 		/** 将已经处理的人从期望值中移除 */
 		for (TaskCompleted o : done) {
 			taskIdentities.removeIdentity(o.getIdentity());
@@ -409,17 +415,24 @@ public class ManualProcessor extends AbstractManualProcessor {
 	}
 
 	/** 所有有效的已办,去除 reset,retract */
-	private List<TaskCompleted> listEffectiveTaskCompleted(Work work, TaskIdentities taskIdentities) throws Exception {
-		List<String> ids = this.business().taskCompleted().listWithActivityTokenInIdentityList(work.getActivityToken(),
-				taskIdentities.identities());
-		List<TaskCompleted> list = new ArrayList<>();
-		for (TaskCompleted o : this.business().entityManagerContainer().list(TaskCompleted.class, ids)) {
-			if ((!o.getProcessingType().equals(ProcessingType.retract))
-					&& (!o.getProcessingType().equals(ProcessingType.reset))) {
-				list.add(o);
-			}
-		}
-		return list;
+	private List<TaskCompleted> listEffectiveTaskCompleted(AeiObjects aeiObjects, TaskIdentities taskIdentities)
+			throws Exception {
+//		final List<String> ids = this.business().taskCompleted()
+//				.listWithActivityTokenInIdentityList(work.getActivityToken(), taskIdentities.identities());
+//		List<TaskCompleted> list = new ArrayList<>();
+//		for (TaskCompleted o : this.business().entityManagerContainer().list(TaskCompleted.class, ids)) {
+//			if ((!o.getProcessingType().equals(ProcessingType.retract))
+//					&& (!o.getProcessingType().equals(ProcessingType.reset))) {
+//				list.add(o);
+//			}
+//		}
+//		return list;
+		return aeiObjects.getTaskCompleteds().stream().filter(o -> {
+			return StringUtils.equals(aeiObjects.getWork().getActivityToken(), o.getActivityToken())
+					&& taskIdentities.identities().contains(o.getIdentity())
+					&& (!o.getProcessingType().equals(ProcessingType.retract))
+					&& (!o.getProcessingType().equals(ProcessingType.reset));
+		}).collect(Collectors.toList());
 	}
 
 	@Override
@@ -575,6 +588,44 @@ public class ManualProcessor extends AbstractManualProcessor {
 		return empowerLog;
 	}
 
+	private Work arriving_mergeSameJobActivity(AeiObjects aeiObjects, Manual manual) throws Exception {
+		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) {
+			return null;
+		} else {
+			/* 将新增加的待办人添加到待办人 */
+			List<String> identities = TranslateTaskIdentityTools.translate(aeiObjects, manual);
+			other.setManualTaskIdentityList(ListUtils.sum(identities, other.getManualTaskIdentityList()));
+			ListUtils.subtract(identities, other.getManualTaskIdentityList());
+			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.mergeHint(aeiObjects, aeiObjects.getWork(), other);
+			this.mergeAttachment(aeiObjects, aeiObjects.getWork(), other);
+			this.mergeWorkLog(aeiObjects, aeiObjects.getWork(), other);
+//			try {
+//				aeiObjects.getWorkLogs().stream().filter(
+//						p -> StringUtils.equals(p.getFromActivityToken(), aeiObjects.getWork().getActivityToken()))
+//						.forEach(obj -> {
+//							System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!###########");
+//							System.out.println(obj);
+//							System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!###########");
+//							aeiObjects.getDeleteWorkLogs().add(obj);
+//						});
+//			} catch (Exception e) {
+//				logger.error(e);
+//			}
+			aeiObjects.getUpdateWorks().add(other);
+			aeiObjects.getDeleteWorks().add(aeiObjects.getWork());
+			return other;
+		}
+	}
+
 	public class ExpireScriptResult {
 		Integer hour;
 		Integer workHour;

+ 616 - 0
o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/processor/manual/ManualProcessor2.java

@@ -0,0 +1,616 @@
+package com.x.processplatform.service.processing.processor.manual;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.time.DateUtils;
+
+import com.x.base.core.container.EntityManagerContainer;
+import com.x.base.core.entity.JpaObject;
+import com.x.base.core.project.config.Config;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+import com.x.base.core.project.organization.EmpowerLog;
+import com.x.base.core.project.organization.Empower;
+import com.x.base.core.project.tools.DateTools;
+import com.x.base.core.project.tools.ListTools;
+import com.x.base.core.project.tools.NumberTools;
+import com.x.base.core.project.utils.time.WorkTime;
+import com.x.processplatform.core.entity.content.Hint;
+import com.x.processplatform.core.entity.content.ProcessingType;
+import com.x.processplatform.core.entity.content.Task;
+import com.x.processplatform.core.entity.content.TaskCompleted;
+import com.x.processplatform.core.entity.content.Work;
+import com.x.processplatform.core.entity.content.WorkLog;
+import com.x.processplatform.core.entity.element.ActivityType;
+import com.x.processplatform.core.entity.element.Manual;
+import com.x.processplatform.core.entity.element.Route;
+import com.x.processplatform.core.entity.element.util.WorkLogTree;
+import com.x.processplatform.service.processing.BindingPair;
+import com.x.processplatform.service.processing.MessageFactory;
+import com.x.processplatform.service.processing.ScriptHelper;
+import com.x.processplatform.service.processing.ScriptHelperFactory;
+import com.x.processplatform.service.processing.processor.AeiObjects;
+import com.x.processplatform.service.processing.processor.TranslateTaskIdentityTools;
+
+public class ManualProcessor2 extends AbstractManualProcessor {
+
+	private static Logger logger = LoggerFactory.getLogger(ManualProcessor2.class);
+
+	public ManualProcessor2(EntityManagerContainer entityManagerContainer) throws Exception {
+		super(entityManagerContainer);
+	}
+
+	@Override
+	protected Work arriving(AeiObjects aeiObjects, Manual manual) throws Exception {
+		/*
+		 * 先记录所有的待办人,必须在这里先进行计算,否则返回值中无法提示下一个处理人.
+		 * 这里先计算处理人存放在manualTaskIdentityList中主要是为了脚本中可以预先知道可能的处理人进行业务处理
+		 */
+		List<String> identities = TranslateTaskIdentityTools.translate(aeiObjects, manual);
+		if (identities.isEmpty()) {
+			/* 如果活动没有找到任何可用的处理人,那么强制设置处理人为文档创建者,或者配置的 maintenanceIdentity */
+			String effectiveCreatorIdentity = aeiObjects.business().organization().identity()
+					.get(aeiObjects.getWork().getCreatorIdentity());
+			if (StringUtils.isNotEmpty(effectiveCreatorIdentity)) {
+				logger.info("人工活动到达未能找到指定的处理人, 标题:{}, id:{}, 强制指定处理人为活动的创建身份:{}.", aeiObjects.getWork().getTitle(),
+						aeiObjects.getWork().getId(), effectiveCreatorIdentity);
+				identities.add(effectiveCreatorIdentity);
+			} else {
+				effectiveCreatorIdentity = aeiObjects.business().organization().identity()
+						.get(Config.processPlatform().getMaintenanceIdentity());
+				if (StringUtils.isNotEmpty(effectiveCreatorIdentity)) {
+					logger.info("人工活动到达未能找到指定的处理人, 标题:{}, id:{}, 强制指定处理人为系统维护身份:{}.", aeiObjects.getWork().getTitle(),
+							aeiObjects.getWork().getId(), effectiveCreatorIdentity);
+					identities.add(effectiveCreatorIdentity);
+				} else {
+					throw new ExceptionExpectedEmpty(aeiObjects.getWork().getTitle(), aeiObjects.getWork().getId(),
+							manual.getName(), manual.getId());
+				}
+			}
+			aeiObjects.createHint(Hint.EmptyTaskIdentityOnManual(aeiObjects.getWork(), manual));
+		}
+		aeiObjects.getWork().setManualTaskIdentityList(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())))
+				&& (BooleanUtils.isNotTrue(aeiObjects.getWork().getForceRouteArriveCurrentActivity()))) {
+			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.getTaskCompleteds().stream()
+						.filter(o -> aeiObjects.getWork().getManualTaskIdentityList().contains(o.getIdentity())
+								&& StringUtils.equals(o.getActivityToken(), arriveActivityToken))
+						.forEach(o -> {
+							TaskCompleted obj = new TaskCompleted(aeiObjects.getWork(), manual, passSameTargetRoute, o);
+							try {
+								obj.setProcessingType(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();
+							}
+						});
+			}
+		}
+		return aeiObjects.getWork();
+	}
+
+	private WorkLog findPassSameTargetWorkLog(AeiObjects aeiObjects) throws Exception {
+		WorkLogTree tree = new WorkLogTree(aeiObjects.getWorkLogs());
+		List<WorkLog> parents = tree.parents(aeiObjects.getArriveWorkLog(aeiObjects.getWork()));
+		logger.debug("pass same target rollback parents:{}.", parents);
+		WorkLog workLog = null;
+		for (WorkLog o : parents) {
+			if (Objects.equals(ActivityType.manual, o.getArrivedActivityType())) {
+				workLog = o;
+				break;
+			} else if (Objects.equals(ActivityType.choice, o.getArrivedActivityType())) {
+				continue;
+			} else if (Objects.equals(ActivityType.agent, o.getArrivedActivityType())) {
+				continue;
+			} else if (Objects.equals(ActivityType.invoke, o.getArrivedActivityType())) {
+				continue;
+			} else if (Objects.equals(ActivityType.service, o.getArrivedActivityType())) {
+				continue;
+			} else {
+				break;
+			}
+		}
+		logger.debug("pass same target find workLog:{}.", workLog);
+		return workLog;
+	}
+
+	@Override
+	protected void arrivingCommitted(AeiObjects aeiObjects, Manual manual) throws Exception {
+	}
+
+	@Override
+	protected List<Work> executing(AeiObjects aeiObjects, Manual manual) throws Exception {
+		List<Work> results = new ArrayList<>();
+		boolean passThrough = false;
+		/*
+		 * 如果采用List<String> identities =
+		 * TranslateTaskIdentityTools.translate(aeiObjects, manual)
+		 * 将全部重新计算人员,那么重置处理人将被去掉
+		 */
+		TaskIdentities taskIdentities = new TaskIdentities(
+				aeiObjects.business().organization().identity().list(aeiObjects.getWork().getManualTaskIdentityList()));
+		if (taskIdentities.isEmpty()) {
+			List<String> identities = TranslateTaskIdentityTools.translate(aeiObjects, manual);
+			taskIdentities.addIdentities(identities);
+			logger.info("工作设置的处理人已经全部无效,重新计算当前环节所有处理人进行处理,标题:{}, id:{}, 设置的处理人:{}.", aeiObjects.getWork().getTitle(),
+					aeiObjects.getWork().getId(), identities);
+		}
+		if (taskIdentities.isEmpty()) {
+			/* 如果活动没有找到任何可用的处理人,那么强制设置处理人为文档创建者,或者配置的 maintenanceIdentity */
+			String effectiveCreatorIdentity = aeiObjects.business().organization().identity()
+					.get(aeiObjects.getWork().getCreatorIdentity());
+			if (StringUtils.isNotEmpty(effectiveCreatorIdentity)) {
+				logger.info("人工活动执行未找到指定的处理身份, 标题:{}, id:{}, 强制指定为工作的创建身份:{}.", aeiObjects.getWork().getTitle(),
+						aeiObjects.getWork().getId(), effectiveCreatorIdentity);
+				taskIdentities.addIdentity(effectiveCreatorIdentity);
+			} else {
+				effectiveCreatorIdentity = aeiObjects.business().organization().identity()
+						.get(Config.processPlatform().getMaintenanceIdentity());
+				if (StringUtils.isNotEmpty(effectiveCreatorIdentity)) {
+					logger.info("人工活动执行未找到指定的处理身份, 标题:{}, id:{}, 强制指定为系统维护身份:{}.", aeiObjects.getWork().getTitle(),
+							aeiObjects.getWork().getId(), effectiveCreatorIdentity);
+					taskIdentities.addIdentity(effectiveCreatorIdentity);
+				} else {
+					throw new ExceptionExpectedEmpty(aeiObjects.getWork().getTitle(), aeiObjects.getWork().getId(),
+							manual.getName(), manual.getId());
+				}
+			}
+			aeiObjects.createHint(Hint.EmptyTaskIdentityOnManual(aeiObjects.getWork(), manual));
+		}
+
+		aeiObjects.getWork().setManualTaskIdentityList(taskIdentities.identities());
+
+		/* 更新授权 */
+		taskIdentities.update(aeiObjects.business().organization().empower().listWithIdentityObject(
+				aeiObjects.getWork().getApplication(), aeiObjects.getWork().getProcess(),
+				aeiObjects.getWork().getManualTaskIdentityList()));
+
+		switch (manual.getManualMode()) {
+		case single:
+			passThrough = this.single(aeiObjects, manual, taskIdentities);
+			break;
+		case parallel:
+			passThrough = this.parallel(aeiObjects, manual, taskIdentities);
+			break;
+		case queue:
+			passThrough = this.queue(aeiObjects, manual, taskIdentities);
+			break;
+		case grab:
+			passThrough = this.single(aeiObjects, manual, taskIdentities);
+			break;
+		default:
+			throw new ExceptionManualModeError(manual.getId());
+		}
+		if (passThrough) {
+			results.add(aeiObjects.getWork());
+		}
+		return results;
+	}
+
+	@Override
+	protected void executingCommitted(AeiObjects aeiObjects, Manual manual) throws Exception {
+
+	}
+
+	@Override
+	protected List<Route> inquiring(AeiObjects aeiObjects, Manual manual) throws Exception {
+		List<Route> results = new ArrayList<>();
+		/* 仅有单条路由 */
+		if (aeiObjects.getRoutes().size() == 1) {
+			results.add(aeiObjects.getRoutes().get(0));
+		} else if (aeiObjects.getRoutes().size() > 1) {
+			/* 存在多条路由 */
+			List<TaskCompleted> taskCompletedList = aeiObjects.getTaskCompleteds().stream()
+					.filter(o -> StringUtils.equals(o.getActivityToken(), aeiObjects.getWork().getActivityToken())
+							&& aeiObjects.getWork().getManualTaskIdentityList().contains(o.getIdentity())
+							&& (!Objects.equals(o.getProcessingType(), ProcessingType.retract))
+							&& (!Objects.equals(o.getProcessingType(), ProcessingType.reset)))
+					.collect(Collectors.toList());
+			String name = this.choiceRouteName(taskCompletedList, aeiObjects.getRoutes());
+			for (Route o : aeiObjects.getRoutes()) {
+				if (o.getName().equalsIgnoreCase(name)) {
+					results.add(o);
+					break;
+				}
+			}
+		}
+		return results;
+	}
+
+	/* 通过已办存根选择某条路由 */
+	private String choiceRouteName(List<TaskCompleted> list, List<Route> routes) throws Exception {
+		String result = "";
+		List<String> os = new ArrayList<>();
+		ListTools.trim(list, false, false).stream()
+				.filter(o -> (!Objects.equals(o.getProcessingType(), ProcessingType.reset))
+						&& (!Objects.equals(o.getProcessingType(), ProcessingType.retract)))
+				.forEach(o -> {
+					/* 跳过重置处理人的路由 */
+					os.add(o.getRouteName());
+				});
+		/* 进行独占路由的判断 */
+		Route soleRoute = routes.stream().filter(o -> BooleanUtils.isTrue(o.getSole())).findFirst().orElse(null);
+		if ((null != soleRoute) && os.contains(soleRoute.getName())) {
+			result = soleRoute.getName();
+		} else {
+			/* 进行默认的策略,选择占比多的 */
+			result = ListTools.maxCountElement(os);
+		}
+		if (StringUtils.isEmpty(result)) {
+			throw new ExceptionChoiceRouteNameError(
+					ListTools.extractProperty(list, JpaObject.id_FIELDNAME, String.class, false, false));
+		}
+		return result;
+	}
+
+	private boolean single(AeiObjects aeiObjects, Manual manual, TaskIdentities taskIdentities) throws Exception {
+		boolean passThrough = false;
+		/* 找到所有的已办 */
+		Long count = aeiObjects.getTaskCompleteds().stream().filter(o -> {
+			if (StringUtils.equals(aeiObjects.getWork().getActivityToken(), o.getActivityToken())
+					&& (!o.getProcessingType().equals(ProcessingType.retract))
+					&& (!o.getProcessingType().equals(ProcessingType.reset))) {
+				return true;
+			} else {
+				return false;
+			}
+		}).count();
+		if (count > 0) {
+			/* 已经确定要通过此节点,清除可能是多余的待办 */
+			aeiObjects.getTasks().stream().filter(o -> {
+				return StringUtils.equals(aeiObjects.getWork().getId(), o.getWork());
+			}).forEach(o -> {
+				aeiObjects.deleteTask(o);
+			});
+			/** 所有预计的处理人中已经有已办,这个环节已经产生了已办,可以离开换个环节。 */
+			passThrough = true;
+		} else {
+			/* 取到期望的待办人员,由于要进行处理需要转换成可读写List */
+			if (ListTools.isEmpty(taskIdentities)) {
+				throw new ExceptionExpectedEmpty(aeiObjects.getWork().getTitle(), aeiObjects.getWork().getId(),
+						manual.getName(), manual.getId());
+			}
+			/* 删除多余的待办 */
+			aeiObjects.getTasks().stream()
+					.filter(o -> StringUtils.equals(aeiObjects.getWork().getActivityToken(), o.getActivityToken())
+							&& (!ListTools.contains(taskIdentities.identities(), o.getIdentity())))
+					.forEach(o -> {
+						aeiObjects.deleteTask(o);
+					});
+			/* 将待办已经产生的人从预期值中删除 */
+			aeiObjects.getTasks().stream()
+					.filter(o -> StringUtils.equals(aeiObjects.getWork().getActivityToken(), o.getActivityToken())
+							&& (ListTools.contains(taskIdentities.identities(), o.getIdentity())))
+					.forEach(o -> {
+						taskIdentities.removeIdentity(o.getIdentity());
+					});
+			/* 这里剩余的应该是没有生成待办的人员 */
+			if (!taskIdentities.isEmpty()) {
+				for (TaskIdentity taskIdentity : taskIdentities) {
+					aeiObjects.createTask(this.createTask(aeiObjects, manual, taskIdentity));
+				}
+			}
+		}
+		return passThrough;
+	}
+
+	private boolean parallel(AeiObjects aeiObjects, Manual manual, TaskIdentities taskIdentities) throws Exception {
+		boolean passThrough = false;
+		/** 取得本环节已经处理的已办 */
+		List<TaskCompleted> taskCompleteds = this.listEffectiveTaskCompleted(aeiObjects.getWork(), taskIdentities);
+		if (ListTools.isEmpty(taskIdentities)) {
+			if (ListTools.isNotEmpty(taskCompleteds)) {
+				/** 预计的处理人全部不存在,且已经有人处理过了 */
+				passThrough = true;
+			} else {
+				/** 即没有预计的处理人也没有已经办理过的记录那么只能报错 */
+				throw new ExceptionExpectedEmpty(aeiObjects.getWork().getTitle(), aeiObjects.getWork().getId(),
+						manual.getName(), manual.getId());
+			}
+		}
+		/* 将已经处理的人从期望值中移除 */
+		aeiObjects.getTaskCompleteds().stream().filter(o -> {
+			return StringUtils.equals(aeiObjects.getWork().getActivityToken(), o.getActivityToken());
+		}).forEach(o -> {
+			taskIdentities.removeIdentity(o.getIdentity());
+		});
+		/* 清空可能的多余的待办 */
+		aeiObjects.getTasks().stream().filter(o -> {
+			return StringUtils.equals(aeiObjects.getWork().getActivityToken(), o.getActivityToken())
+					&& (!ListTools.contains(taskIdentities.identities(), o.getIdentity()));
+		}).forEach(o -> {
+			aeiObjects.deleteTask(o);
+		});
+		if (taskIdentities.isEmpty()) {
+			/* 所有人已经处理完成。 */
+			passThrough = true;
+		} else {
+			passThrough = false;
+			/* 先清空已经有待办的身份 */
+			aeiObjects.getTasks().stream().filter(o -> {
+				return StringUtils.equals(aeiObjects.getWork().getActivityToken(), o.getActivityToken());
+			}).forEach(o -> {
+				taskIdentities.removeIdentity(o.getIdentity());
+			});
+			/* 这里剩余的应该是没有生成待办的人员 */
+			if (!taskIdentities.isEmpty()) {
+				for (TaskIdentity taskIdentity : taskIdentities) {
+					aeiObjects.createTask(this.createTask(aeiObjects, manual, taskIdentity));
+				}
+			}
+		}
+		return passThrough;
+	}
+
+	private boolean queue(AeiObjects aeiObjects, Manual manual, TaskIdentities taskIdentities) throws Exception {
+		boolean passThrough = false;
+		if (taskIdentities.isEmpty()) {
+			throw new ExceptionExpectedEmpty(aeiObjects.getWork().getTitle(), aeiObjects.getWork().getId(),
+					manual.getName(), manual.getId());
+		}
+		List<TaskCompleted> done = this.listEffectiveTaskCompleted(aeiObjects.getWork(), taskIdentities);
+		/** 将已经处理的人从期望值中移除 */
+		for (TaskCompleted o : done) {
+			taskIdentities.removeIdentity(o.getIdentity());
+		}
+		if (taskIdentities.isEmpty()) {
+			/** 所有人已经处理完成。 */
+			passThrough = true;
+		} else {
+			passThrough = false;
+			TaskIdentity taskIdentity = taskIdentities.get(0);
+			/** 还有人没有处理,开始判断待办,取到本环节的所有待办,理论上只能有一条待办 */
+			List<Task> existed = this.entityManagerContainer().fetch(
+					this.business().task().listWithActivityToken(aeiObjects.getWork().getActivityToken()), Task.class,
+					ListTools.toList(Task.identity_FIELDNAME));
+			/** 理论上只能有一条待办 */
+			boolean find = false;
+			for (Task _o : existed) {
+				if (!StringUtils.equals(_o.getIdentity(), taskIdentity.getIdentity())) {
+					this.entityManagerContainer().delete(Task.class, _o.getId());
+					MessageFactory.task_delete(_o);
+				} else {
+					find = true;
+				}
+			}
+			/** 当前处理人没有待办 */
+			if (!find) {
+				aeiObjects.createTask(this.createTask(aeiObjects, manual, taskIdentity));
+			}
+		}
+		return passThrough;
+	}
+
+	/** 所有有效的已办,去除 reset,retract */
+	private List<TaskCompleted> listEffectiveTaskCompleted(Work work, TaskIdentities taskIdentities) throws Exception {
+		List<String> ids = this.business().taskCompleted().listWithActivityTokenInIdentityList(work.getActivityToken(),
+				taskIdentities.identities());
+		List<TaskCompleted> list = new ArrayList<>();
+		for (TaskCompleted o : this.business().entityManagerContainer().list(TaskCompleted.class, ids)) {
+			if ((!o.getProcessingType().equals(ProcessingType.retract))
+					&& (!o.getProcessingType().equals(ProcessingType.reset))) {
+				list.add(o);
+			}
+		}
+		return list;
+	}
+
+	@Override
+	protected void inquiringCommitted(AeiObjects aeiObjects, Manual manual) throws Exception {
+	}
+
+	private void calculateExpire(AeiObjects aeiObjects, Manual manual, Task task) throws Exception {
+		if (null != 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;
+			}
+		}
+		/** 如果work有截至时间 */
+		if (null != aeiObjects.getWork().getExpireTime()) {
+			if (null == task.getExpireTime()) {
+				task.setExpireTime(aeiObjects.getWork().getExpireTime());
+			} else {
+				if (task.getExpireTime().after(aeiObjects.getWork().getExpireTime())) {
+					task.setExpireTime(aeiObjects.getWork().getExpireTime());
+				}
+			}
+		}
+		/** 已经有过期时间了,那么设置催办时间 */
+		if (null != task.getExpireTime()) {
+			task.setUrgeTime(DateUtils.addHours(task.getExpireTime(), -2));
+		} else {
+			task.setExpired(false);
+			task.setUrgeTime(null);
+			task.setUrged(false);
+		}
+	}
+
+	/*
+	 * 从不过期
+	 */
+	private void expireNever(Task task) {
+		task.setExpireTime(null);
+	}
+
+	private void expireAppoint(Manual manual, Task task) throws Exception {
+		if (BooleanUtils.isTrue(manual.getTaskExpireWorkTime())) {
+			this.expireAppointWorkTime(task, manual);
+		} else {
+			this.expireAppointNaturalDay(task, manual);
+		}
+	}
+
+	private void expireAppointWorkTime(Task task, Manual manual) throws Exception {
+		Integer m = 0;
+		WorkTime wt = new WorkTime();
+		if (NumberTools.greaterThan(manual.getTaskExpireDay(), 0)) {
+			m += manual.getTaskExpireDay() * wt.minutesOfWorkDay();
+		}
+		if (NumberTools.greaterThan(manual.getTaskExpireHour(), 0)) {
+			m += manual.getTaskExpireHour() * 60;
+		}
+		if (m > 0) {
+			Date expire = wt.forwardMinutes(new Date(), m);
+			task.setExpireTime(expire);
+		} else {
+			task.setExpireTime(null);
+		}
+	}
+
+	private void expireAppointNaturalDay(Task task, Manual manual) throws Exception {
+		Integer m = 0;
+		if (NumberTools.greaterThan(manual.getTaskExpireDay(), 0)) {
+			m += manual.getTaskExpireDay() * 60 * 24;
+		}
+		if (NumberTools.greaterThan(manual.getTaskExpireHour(), 0)) {
+			m += manual.getTaskExpireHour() * 60;
+		}
+		if (m > 0) {
+			Calendar cl = Calendar.getInstance();
+			cl.add(Calendar.MINUTE, m);
+			task.setExpireTime(cl.getTime());
+		} else {
+			task.setExpireTime(null);
+		}
+	}
+
+	private void expireScript(AeiObjects aeiObjects, Manual manual, Task task) throws Exception {
+		ExpireScriptResult expire = new ExpireScriptResult();
+		ScriptHelper sh = ScriptHelperFactory.create(aeiObjects, new BindingPair("task", task),
+				new BindingPair("expire", expire));
+		sh.eval(aeiObjects.getWork().getApplication(), manual.getTaskExpireScript(), manual.getTaskExpireScriptText());
+
+		if (NumberTools.greaterThan(expire.getWorkHour(), 0)) {
+			Integer m = 0;
+			m += expire.getWorkHour() * 60;
+			if (m > 0) {
+				WorkTime wt = new WorkTime();
+				task.setExpireTime(wt.forwardMinutes(new Date(), m));
+			} else {
+				task.setExpireTime(null);
+			}
+		} else if (NumberTools.greaterThan(expire.getHour(), 0)) {
+			Integer m = 0;
+			m += expire.getHour() * 60;
+			if (m > 0) {
+				Calendar cl = Calendar.getInstance();
+				cl.add(Calendar.MINUTE, m);
+				task.setExpireTime(cl.getTime());
+			} else {
+				task.setExpireTime(null);
+			}
+		} else if (null != expire.getDate()) {
+			task.setExpireTime(expire.getDate());
+		} else {
+			task.setExpireTime(null);
+		}
+	}
+
+	private Task createTask(AeiObjects aeiObjects, Manual manual, TaskIdentity taskIdentity) throws Exception {
+		String person = aeiObjects.business().organization().person().getWithIdentity(taskIdentity.getIdentity());
+		String unit = aeiObjects.business().organization().unit().getWithIdentity(taskIdentity.getIdentity());
+		Task task = new Task(aeiObjects.getWork(), taskIdentity.getIdentity(), person, unit,
+				taskIdentity.getFromIdentity(), new Date(), null, aeiObjects.getRoutes(), manual.getAllowRapid());
+		/* 是第一条待办,进行标记 */
+		if (ListTools.isEmpty(aeiObjects.getTaskCompleteds())) {
+			task.setFirst(true);
+		} else {
+			task.setFirst(false);
+		}
+		this.calculateExpire(aeiObjects, manual, task);
+		if (StringUtils.isNotEmpty(taskIdentity.getFromIdentity())) {
+			aeiObjects.business().organization().empowerLog()
+					.log(this.createEmpowerLog(aeiObjects.getWork(), taskIdentity));
+			task.setTrustIdentity(taskIdentity.getFromIdentity());
+		}
+		return task;
+	}
+
+	private EmpowerLog createEmpowerLog(Work work, TaskIdentity taskIdentity) {
+		EmpowerLog empowerLog = new EmpowerLog().setApplication(work.getApplication())
+				.setApplicationAlias(work.getApplicationAlias()).setApplicationName(work.getApplicationName())
+				.setProcess(work.getProcess()).setProcessAlias(work.getProcessAlias())
+				.setProcessName(work.getProcessName()).setTitle(work.getTitle()).setWork(work.getId())
+				.setJob(work.getJob()).setFromIdentity(taskIdentity.getFromIdentity())
+				.setToIdentity(taskIdentity.getIdentity()).setActivity(work.getActivity())
+				.setActivityAlias(work.getActivityAlias()).setActivityName(work.getActivityName())
+				.setTrustTime(new Date());
+		return empowerLog;
+	}
+
+	public class ExpireScriptResult {
+		Integer hour;
+		Integer workHour;
+		Date date;
+
+		public Integer getHour() {
+			return hour;
+		}
+
+		public void setHour(Integer hour) {
+			this.hour = hour;
+		}
+
+		public Integer getWorkHour() {
+			return workHour;
+		}
+
+		public void setWorkHour(Integer workHour) {
+			this.workHour = workHour;
+		}
+
+		public Date getDate() {
+			return date;
+		}
+
+		public void setDate(Date date) {
+			this.date = date;
+		}
+
+		public void setDate(String str) {
+			try {
+				this.date = DateTools.parse(str);
+			} catch (Exception e) {
+				logger.error(e);
+			}
+		}
+
+	}
+}

+ 0 - 2
o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/processor/merge/AbstractMergeProcessor.java

@@ -8,8 +8,6 @@ import com.x.processplatform.core.entity.element.Merge;
 import com.x.processplatform.core.entity.element.Route;
 import com.x.processplatform.service.processing.processor.AbstractProcessor;
 import com.x.processplatform.service.processing.processor.AeiObjects;
-import com.x.processplatform.service.processing.processor.AeiObjects;
-import com.x.processplatform.service.processing.processor.AeiObjects;
 
 public abstract class AbstractMergeProcessor extends AbstractProcessor {
 

+ 51 - 75
o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/processor/merge/MergeProcessor.java

@@ -2,10 +2,7 @@ package com.x.processplatform.service.processing.processor.merge;
 
 import java.util.ArrayList;
 import java.util.Comparator;
-import java.util.Date;
 import java.util.List;
-import java.util.Objects;
-import java.util.stream.Collectors;
 
 import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -13,9 +10,7 @@ import org.apache.commons.lang3.StringUtils;
 import com.x.base.core.container.EntityManagerContainer;
 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.processplatform.core.entity.content.Work;
-import com.x.processplatform.core.entity.element.ActivityType;
 import com.x.processplatform.core.entity.element.Merge;
 import com.x.processplatform.core.entity.element.Route;
 import com.x.processplatform.service.processing.processor.AeiObjects;
@@ -45,78 +40,61 @@ public class MergeProcessor extends AbstractMergeProcessor {
 			results.add(aeiObjects.getWork());
 			return results;
 		}
-		String splitToken = null;
-		List<Work> waitMerges = new ArrayList<>();
-		for (String str : aeiObjects.getWork().getSplitTokenList()) {
-			if (StringUtils.isNotEmpty(str)) {
-				waitMerges = aeiObjects.getWorks().stream()
-						.filter(o -> ListTools.contains(o.getSplitTokenList(), str)
-								&& (!Objects.equals(ActivityType.end, o.getActivityType())))
-						.collect(Collectors.toList());
-				if (!waitMerges.isEmpty()) {
-					if (waitMerges.stream()
-							.allMatch(o -> StringUtils.equals(o.getActivity(), aeiObjects.getWork().getActivity()))) {
-						splitToken = str;
-						break;
-					}
+		String join = StringUtils.join(aeiObjects.getWork().getSplitTokenList(), ",");
+
+		/* 查找同级 */
+		Work other = aeiObjects.getWorks().stream().filter(o -> {
+			if (BooleanUtils.isTrue(o.getSplitting()) && (o != aeiObjects.getWork())) {
+				if (StringUtils.equals(StringUtils.join(o.getSplitTokenList(), ","), join)) {
+					return true;
 				}
 			}
+			return false;
+		}).sorted((o1, o2) -> {
+			return o1.getCreateTime().compareTo(o2.getCreateTime());
+		}).findFirst().orElse(null);
+
+		/* 找不到同级找上层 */
+		if (null == other) {
+			other = aeiObjects.getWorks().stream().filter(o -> {
+				if (BooleanUtils.isTrue(o.getSplitting()) && (o != aeiObjects.getWork())) {
+					if (StringUtils.startsWith(join, StringUtils.join(o.getSplitTokenList(), ","))) {
+						return true;
+					}
+				}
+				return false;
+			}).sorted((o1, o2) -> {
+				int compare = o2.getSplitTokenList().size() - o1.getSplitTokenList().size();
+				if (compare == 0) {
+					return o2.getCreateTime().compareTo(o1.getCreateTime());
+				}
+				return compare;
+			}).findFirst().orElse(null);
 		}
-		/* 没有找到需要合并的work,直接返回 */
-		if (StringUtils.isEmpty(splitToken)) {
-			return results;
-		}
-		/* 已经找到了需要合并的work,找到最老的作为基准work */
-		Work oldest = waitMerges.stream()
-				.sorted(Comparator.comparing(Work::getCreateTime, Comparator.nullsLast(Date::compareTo))).findFirst()
-				.get();
-		aeiObjects.getUpdateWorks().add(oldest);
-		waitMerges.stream().filter(o -> (!Objects.equals(o, oldest))).forEach(o -> {
-			/* 排除oldest文档不进行删除 */
-			aeiObjects.getDeleteWorks().add(o);
-			/* 是不可能有待办的 */
-			/* 将已办归并到最早work */
-			this.mergeTaskCompleted(aeiObjects, o, oldest);
-			/* 将待阅归并到最早work */
-			this.mergeRead(aeiObjects, o, oldest);
-			/* 将已阅归并到最早work */
-			this.mergeReadCompleted(aeiObjects, o, oldest);
-			/* 将参阅归并到最早work */
-			this.mergeReview(aeiObjects, o, oldest);
-			/* 将提示归并到最早work */
-			this.mergeHint(aeiObjects, o, oldest);
-			/* 将附件归并到当前work */
-			this.mergeAttachment(aeiObjects, o, oldest);
-			/* 将工作日志归并到当前work */
-			this.mergeWorkLog(aeiObjects, o, oldest);
-			/* 将要合并的工作上当前为连接的workLog删除 */
-			try {
-				aeiObjects.getWorkLogs().stream()
-						.filter(p -> StringUtils.equals(p.getFromActivityToken(), o.getActivityToken()))
-						.forEach(obj -> {
-							aeiObjects.getDeleteWorkLogs().add(obj);
-						});
-			} catch (Exception e) {
-				logger.error(e);
-			}
-		});
-		List<String> splitTokens = new ArrayList<>();
-		for (String str : oldest.getSplitTokenList()) {
-			if (StringUtils.equals(str, splitToken)) {
-				break;
-			}
-			splitTokens.add(str);
-		}
-		oldest.setSplitTokenList(splitTokens);
-		if (splitTokens.isEmpty()) {
-			oldest.setSplitValue("");
-			oldest.setSplitting(false);
-			oldest.setSplitToken("");
+
+		/* 完全找不到合并的文档,唯一一份 */
+		if (null == other) {
+			aeiObjects.getWork().setSplitting(false);
+			aeiObjects.getWork().setSplitToken("");
+			aeiObjects.getWork().setSplitTokenList(new ArrayList<String>());
+			aeiObjects.getWork().setSplitValue("");
+			results.add(aeiObjects.getWork());
 		} else {
-			oldest.setSplitting(true);
-			oldest.setSplitToken(splitTokens.get(splitTokens.size() - 1));
+			aeiObjects.getUpdateWorks().add(other);
+			aeiObjects.getDeleteWorks().add(aeiObjects.getWork());
+			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.mergeHint(aeiObjects, aeiObjects.getWork(), other);
+			this.mergeAttachment(aeiObjects, aeiObjects.getWork(), other);
+			this.mergeWorkLog(aeiObjects, aeiObjects.getWork(), other);
+			aeiObjects.getWorkLogs().stream()
+					.filter(p -> StringUtils.equals(p.getFromActivityToken(), aeiObjects.getWork().getActivityToken()))
+					.forEach(obj -> {
+						aeiObjects.getDeleteWorkLogs().add(obj);
+					});
 		}
-		results.add(oldest);
 		return results;
 	}
 
@@ -125,7 +103,6 @@ public class MergeProcessor extends AbstractMergeProcessor {
 			aeiObjects.getTaskCompleteds().stream().filter(o -> StringUtils.equals(o.getWork(), work.getId()))
 					.forEach(o -> {
 						o.setWork(oldest.getId());
-						// o.setActivityToken(oldest.getActivityToken());
 						aeiObjects.getUpdateTaskCompleteds().add(o);
 					});
 		} catch (Exception e) {
@@ -137,7 +114,6 @@ public class MergeProcessor extends AbstractMergeProcessor {
 		try {
 			aeiObjects.getReads().stream().filter(o -> StringUtils.equals(o.getWork(), work.getId())).forEach(o -> {
 				o.setWork(oldest.getId());
-				// o.setActivityToken(oldest.getActivityToken());
 				aeiObjects.getUpdateReads().add(o);
 			});
 		} catch (Exception e) {
@@ -199,7 +175,7 @@ public class MergeProcessor extends AbstractMergeProcessor {
 							&& StringUtils.equals(o.getWork(), work.getId()))
 					.forEach(o -> {
 						o.setWork(oldest.getId());
-						o.setArrivedActivityToken(oldest.getActivityToken());
+						// o.setArrivedActivityToken(oldest.getActivityToken());
 						aeiObjects.getUpdateWorkLogs().add(o);
 					});
 		} catch (Exception e) {

+ 242 - 0
o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/processor/merge/MergeProcessor2.java

@@ -0,0 +1,242 @@
+package com.x.processplatform.service.processing.processor.merge;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import com.x.base.core.container.EntityManagerContainer;
+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.processplatform.core.entity.content.Work;
+import com.x.processplatform.core.entity.element.ActivityType;
+import com.x.processplatform.core.entity.element.Merge;
+import com.x.processplatform.core.entity.element.Route;
+import com.x.processplatform.service.processing.processor.AeiObjects;
+
+public class MergeProcessor2 extends AbstractMergeProcessor {
+
+	private static Logger logger = LoggerFactory.getLogger(MergeProcessor2.class);
+
+	public MergeProcessor2(EntityManagerContainer entityManagerContainer) throws Exception {
+		super(entityManagerContainer);
+	}
+
+	@Override
+	protected Work arriving(AeiObjects aeiObjects, Merge merge) throws Exception {
+		return aeiObjects.getWork();
+	}
+
+	@Override
+	protected void arrivingCommitted(AeiObjects aeiObjects, Merge merge) throws Exception {
+	}
+
+	@Override
+	protected List<Work> executing(AeiObjects aeiObjects, Merge merge) throws Exception {
+		List<Work> results = new ArrayList<>();
+		if (BooleanUtils.isNotTrue(aeiObjects.getWork().getSplitting())) {
+			/* 如果不是一个拆分文档,直接通过 */
+			results.add(aeiObjects.getWork());
+			return results;
+		}
+		String splitToken = null;
+		List<Work> waitMerges = new ArrayList<>();
+		for (String str : aeiObjects.getWork().getSplitTokenList()) {
+			if (StringUtils.isNotEmpty(str)) {
+				waitMerges = aeiObjects.getWorks().stream()
+						.filter(o -> ListTools.contains(o.getSplitTokenList(), str)
+								&& (!Objects.equals(ActivityType.end, o.getActivityType())))
+						.collect(Collectors.toList());
+				if (!waitMerges.isEmpty()) {
+					if (waitMerges.stream()
+							.allMatch(o -> StringUtils.equals(o.getActivity(), aeiObjects.getWork().getActivity()))) {
+						splitToken = str;
+						break;
+					}
+				}
+			}
+		}
+		/* 没有找到需要合并的work,直接返回 */
+		if (StringUtils.isEmpty(splitToken)) {
+			return results;
+		}
+		/* 已经找到了需要合并的work,找到最老的作为基准work */
+		Work oldest = waitMerges.stream()
+				.sorted(Comparator.comparing(Work::getCreateTime, Comparator.nullsLast(Date::compareTo))).findFirst()
+				.get();
+		aeiObjects.getUpdateWorks().add(oldest);
+		waitMerges.stream().filter(o -> (!Objects.equals(o, oldest))).forEach(o -> {
+			/* 排除oldest文档不进行删除 */
+			aeiObjects.getDeleteWorks().add(o);
+			/* 是不可能有待办的 */
+			/* 将已办归并到最早work */
+			this.mergeTaskCompleted(aeiObjects, o, oldest);
+			/* 将待阅归并到最早work */
+			this.mergeRead(aeiObjects, o, oldest);
+			/* 将已阅归并到最早work */
+			this.mergeReadCompleted(aeiObjects, o, oldest);
+			/* 将参阅归并到最早work */
+			this.mergeReview(aeiObjects, o, oldest);
+			/* 将提示归并到最早work */
+			this.mergeHint(aeiObjects, o, oldest);
+			/* 将附件归并到当前work */
+			this.mergeAttachment(aeiObjects, o, oldest);
+			/* 将工作日志归并到当前work */
+			this.mergeWorkLog(aeiObjects, o, oldest);
+			/* 将要合并的工作上当前为连接的workLog删除 */
+			try {
+				aeiObjects.getWorkLogs().stream()
+						.filter(p -> StringUtils.equals(p.getFromActivityToken(), o.getActivityToken()))
+						.forEach(obj -> {
+							aeiObjects.getDeleteWorkLogs().add(obj);
+						});
+			} catch (Exception e) {
+				logger.error(e);
+			}
+		});
+		List<String> splitTokens = new ArrayList<>();
+		for (String str : oldest.getSplitTokenList()) {
+			if (StringUtils.equals(str, splitToken)) {
+				break;
+			}
+			splitTokens.add(str);
+		}
+		oldest.setSplitTokenList(splitTokens);
+		if (splitTokens.isEmpty()) {
+			oldest.setSplitValue("");
+			oldest.setSplitting(false);
+			oldest.setSplitToken("");
+		} else {
+			oldest.setSplitting(true);
+			oldest.setSplitToken(splitTokens.get(splitTokens.size() - 1));
+		}
+		results.add(oldest);
+		return results;
+	}
+
+	private void mergeTaskCompleted(AeiObjects aeiObjects, Work work, Work oldest) {
+		try {
+			aeiObjects.getTaskCompleteds().stream().filter(o -> StringUtils.equals(o.getWork(), work.getId()))
+					.forEach(o -> {
+						o.setWork(oldest.getId());
+						// o.setActivityToken(oldest.getActivityToken());
+						aeiObjects.getUpdateTaskCompleteds().add(o);
+					});
+		} catch (Exception e) {
+			logger.error(e);
+		}
+	}
+
+	private void mergeRead(AeiObjects aeiObjects, Work work, Work oldest) {
+		try {
+			aeiObjects.getReads().stream().filter(o -> StringUtils.equals(o.getWork(), work.getId())).forEach(o -> {
+				o.setWork(oldest.getId());
+				// o.setActivityToken(oldest.getActivityToken());
+				aeiObjects.getUpdateReads().add(o);
+			});
+		} catch (Exception e) {
+			logger.error(e);
+		}
+	}
+
+	private void mergeReadCompleted(AeiObjects aeiObjects, Work work, Work oldest) {
+		try {
+			aeiObjects.getReadCompleteds().stream().filter(o -> StringUtils.equals(o.getWork(), work.getId()))
+					.forEach(o -> {
+						o.setWork(oldest.getId());
+						// o.setActivityToken(oldest.getActivityToken());
+						aeiObjects.getUpdateReadCompleteds().add(o);
+					});
+		} catch (Exception e) {
+			logger.error(e);
+		}
+	}
+
+	private void mergeReview(AeiObjects aeiObjects, Work work, Work oldest) {
+		try {
+			aeiObjects.getReviews().stream().filter(o -> StringUtils.equals(o.getWork(), work.getId())).forEach(o -> {
+				o.setWork(oldest.getId());
+				aeiObjects.getUpdateReviews().add(o);
+			});
+		} catch (Exception e) {
+			logger.error(e);
+		}
+	}
+
+	private void mergeHint(AeiObjects aeiObjects, Work work, Work oldest) {
+		try {
+			aeiObjects.getHints().stream().filter(o -> StringUtils.equals(o.getWork(), work.getId())).forEach(o -> {
+				o.setWork(oldest.getId());
+				aeiObjects.getUpdateHints().add(o);
+			});
+		} catch (Exception e) {
+			logger.error(e);
+		}
+	}
+
+	private void mergeAttachment(AeiObjects aeiObjects, Work work, Work oldest) {
+		try {
+			aeiObjects.getAttachments().stream().filter(o -> StringUtils.equals(o.getWork(), work.getId()))
+					.forEach(o -> {
+						o.setWork(oldest.getId());
+						aeiObjects.getUpdateAttachments().add(o);
+					});
+		} catch (Exception e) {
+			logger.error(e);
+		}
+	}
+
+	private void mergeWorkLog(AeiObjects aeiObjects, Work work, Work oldest) {
+		try {
+			aeiObjects.getWorkLogs().stream()
+					.filter(o -> StringUtils.equals(work.getActivityToken(), o.getArrivedActivityToken())
+							&& StringUtils.equals(o.getWork(), work.getId()))
+					.forEach(o -> {
+						o.setWork(oldest.getId());
+						o.setArrivedActivityToken(oldest.getActivityToken());
+						aeiObjects.getUpdateWorkLogs().add(o);
+					});
+		} catch (Exception e) {
+			logger.error(e);
+		}
+	}
+
+	@Override
+	protected void executingCommitted(AeiObjects aeiObjects, Merge merge) throws Exception {
+	}
+
+	@Override
+	protected List<Route> inquiring(AeiObjects aeiObjects, Merge merge) throws Exception {
+		List<Route> results = new ArrayList<>();
+		results.add(aeiObjects.getRoutes().get(0));
+		return results;
+	}
+
+	@Override
+	protected void arriveCommitted(AeiObjects aeiObjects) throws Exception {
+		Merge merge = (Merge) aeiObjects.getActivity();
+		this.arrivingCommitted(aeiObjects, merge);
+	}
+
+	@Override
+	protected void executeCommitted(AeiObjects aeiObjects) throws Exception {
+		Merge merge = (Merge) aeiObjects.getActivity();
+		this.executingCommitted(aeiObjects, merge);
+	}
+
+	@Override
+	protected void inquireCommitted(AeiObjects aeiObjects) throws Exception {
+		Merge merge = (Merge) aeiObjects.getActivity();
+		this.inquiringCommitted(aeiObjects, merge);
+	}
+
+	@Override
+	protected void inquiringCommitted(AeiObjects aeiObjects, Merge merge) throws Exception {
+	}
+}

+ 26 - 0
o2server/x_processplatform_service_processing/src/test/java/com/x/processplatform/core/processing/test/TestClient.java

@@ -31,8 +31,34 @@ public class TestClient {
 		b.add("33");
 		b.add("44");
 		System.out.println(ListUtils.sum(a, b));
+		System.out.println(ListUtils.intersection(a, b));
+		System.out.println(ListUtils.union(a, b));
 	}
 
+	@Test
+	public void test2() {
+		List<String> aaa = new ArrayList<>();
+
+		aaa.add("aaa");
+		aaa.add("bbb");
+		aaa.add("ccc");
+
+		List<String> bbb = new ArrayList<>();
+
+		bbb.add("111");
+		bbb.add("222");
+
+		List<List<String>> list = new ArrayList<>();
 
+		list.add(aaa);
+		list.add(bbb);
+
+		list = list.stream().sorted((o1, o2) -> {
+			return o2.size() - o1.size();
+		}).collect(Collectors.toList());
+
+		System.out.println(list);
+
+	}
 
 }

+ 2 - 2
o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/schedule/ActionReport.java

@@ -14,7 +14,7 @@ import com.x.base.core.project.jaxrs.WrapBoolean;
 import com.x.program.center.core.entity.ScheduleLog;
 
 class ActionReport extends BaseAction {
-	
+
 	ActionResult<Wo> execute(EffectivePerson effectivePerson, JsonElement jsonElement) throws Exception {
 		try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
 			Wi wi = this.convertToWrapIn(jsonElement, Wi.class);
@@ -37,7 +37,7 @@ class ActionReport extends BaseAction {
 
 		private static final long serialVersionUID = 1996856138701159925L;
 		static WrapCopier<Wi, ScheduleLog> copier = WrapCopierFactory.wi(Wi.class, ScheduleLog.class, null,
-				JpaObject.FieldsInvisible);
+				JpaObject.FieldsUnmodify);
 	}
 
 	public static class Wo extends WrapBoolean {

+ 1 - 0
o2server/x_program_center/src/main/java/com/x/program/center/schedule/FireSchedule.java

@@ -79,6 +79,7 @@ public class FireSchedule extends BaseAction {
 
 	private Date getLastStartTime(ScheduleRequest request) throws Exception {
 		Date lastStartTime = request.getLastStartTime();
+		// Date lastStartTime = null;
 		if (null == lastStartTime) {
 			try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
 				EntityManager em = emc.get(ScheduleLog.class);

+ 14 - 1
o2server/x_query_assemble_designer/src/main/java/com/x/query/assemble/designer/jaxrs/table/ActionCreate.java

@@ -10,6 +10,7 @@ import com.x.base.core.container.factory.EntityManagerContainerFactory;
 import com.x.base.core.entity.JpaObject;
 import com.x.base.core.entity.annotation.CheckPersistType;
 import com.x.base.core.entity.dynamic.DynamicEntity;
+import com.x.base.core.entity.dynamic.DynamicEntity.Field;
 import com.x.base.core.project.bean.WrapCopier;
 import com.x.base.core.project.bean.WrapCopierFactory;
 import com.x.base.core.project.cache.ApplicationCache;
@@ -48,8 +49,20 @@ class ActionCreate extends BaseAction {
 			if (StringUtils.isNotEmpty(emc.conflict(Table.class, table))) {
 				throw new ExceptionDuplicateFlag(Table.class, emc.conflict(Table.class, table));
 			}
+
+			DynamicEntity dynamicEntity = XGsonBuilder.instance().fromJson(table.getDraftData(), DynamicEntity.class);
+			if (ListTools.isEmpty(dynamicEntity.getFieldList())) {
+				throw new ExceptionFieldEmpty();
+			}
+
+			for (Field field : dynamicEntity.getFieldList()) {
+				if (JpaObject.FieldsDefault.stream().filter(o -> StringUtils.equalsIgnoreCase(o, field.getName()))
+						.count() > 0) {
+					throw new ExceptionFieldName(field.getName());
+				}
+			}
+
 			emc.beginTransaction(Table.class);
-			XGsonBuilder.instance().fromJson(table.getData(), DynamicEntity.class);
 			table.setCreatorPerson(effectivePerson.getDistinguishedName());
 			table.setLastUpdatePerson(effectivePerson.getDistinguishedName());
 			table.setLastUpdateTime(new Date());

+ 15 - 3
o2server/x_query_assemble_designer/src/main/java/com/x/query/assemble/designer/jaxrs/table/ActionEdit.java

@@ -10,10 +10,10 @@ import com.x.base.core.container.factory.EntityManagerContainerFactory;
 import com.x.base.core.entity.JpaObject;
 import com.x.base.core.entity.annotation.CheckPersistType;
 import com.x.base.core.entity.dynamic.DynamicEntity;
+import com.x.base.core.entity.dynamic.DynamicEntity.Field;
 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.exception.ExceptionAccessDenied;
 import com.x.base.core.project.exception.ExceptionDuplicateFlag;
 import com.x.base.core.project.exception.ExceptionEntityFieldEmpty;
 import com.x.base.core.project.exception.ExceptionEntityNotExist;
@@ -23,7 +23,6 @@ import com.x.base.core.project.http.EffectivePerson;
 import com.x.base.core.project.jaxrs.WoId;
 import com.x.base.core.project.tools.ListTools;
 import com.x.query.assemble.designer.Business;
-import com.x.query.core.entity.Query;
 import com.x.query.core.entity.schema.Statement;
 import com.x.query.core.entity.schema.Table;
 
@@ -45,8 +44,21 @@ class ActionEdit extends BaseAction {
 			if (StringUtils.isNotEmpty(emc.conflict(Table.class, table))) {
 				throw new ExceptionDuplicateFlag(Table.class, emc.conflict(Table.class, table));
 			}
+
+			DynamicEntity dynamicEntity = XGsonBuilder.instance().fromJson(table.getDraftData(), DynamicEntity.class);
+
+			if (ListTools.isEmpty(dynamicEntity.getFieldList())) {
+				throw new ExceptionFieldEmpty();
+			}
+
+			for (Field field : dynamicEntity.getFieldList()) {
+				if (JpaObject.FieldsDefault.stream().filter(o -> StringUtils.equalsIgnoreCase(o, field.getName()))
+						.count() > 0) {
+					throw new ExceptionFieldName(field.getName());
+				}
+			}
+
 			emc.beginTransaction(Table.class);
-			XGsonBuilder.instance().fromJson(table.getData(), DynamicEntity.class);
 			table.setLastUpdatePerson(effectivePerson.getDistinguishedName());
 			table.setLastUpdateTime(new Date());
 			table.setData("");

+ 4 - 7
o2server/x_query_assemble_designer/src/main/java/com/x/query/assemble/designer/jaxrs/table/ActionRowInsert.java

@@ -9,11 +9,10 @@ import com.x.base.core.container.factory.EntityManagerContainerFactory;
 import com.x.base.core.entity.JpaObject;
 import com.x.base.core.entity.annotation.CheckPersistType;
 import com.x.base.core.entity.dynamic.DynamicEntity;
-import com.x.base.core.project.exception.ExceptionAccessDenied;
 import com.x.base.core.project.exception.ExceptionEntityNotExist;
 import com.x.base.core.project.http.ActionResult;
 import com.x.base.core.project.http.EffectivePerson;
-import com.x.base.core.project.jaxrs.WrapBoolean;
+import com.x.base.core.project.jaxrs.WrapStringList;
 import com.x.query.assemble.designer.Business;
 import com.x.query.core.entity.schema.Table;
 
@@ -46,17 +45,15 @@ class ActionRowInsert extends BaseAction {
 			}
 			emc.commit();
 			Wo wo = new Wo();
-			if (os.isEmpty()) {
-				wo.setValue(false);
-			} else {
-				wo.setValue(true);
+			for (Object o : os) {
+				wo.addValue(((JpaObject)o).getId(), true);
 			}
 			result.setData(wo);
 			return result;
 		}
 	}
 
-	public static class Wo extends WrapBoolean {
+	public static class Wo extends WrapStringList {
 
 	}
 

Some files were not shown because too many files changed in this diff