Procházet zdrojové kódy

Merge branch 'develop' into 'fix/android_ai_update'

# Conflicts:
#   o2android/app/src/main/AndroidManifest.xml
楼国栋 před 5 roky
rodič
revize
8ba40200e0
100 změnil soubory, kde provedl 2153 přidání a 555 odebrání
  1. 52 2
      gulpfile.js
  2. 14 14
      o2android/app/src/main/AndroidManifest.xml
  3. 2 1
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/O2AppUpdateManager.kt
  4. 1 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/attendance/main/AttendanceCheckInNewFragment.kt
  5. 8 2
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/attendance/main/AttendanceCheckInPresenter.kt
  6. 5 4
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/application/CMSCategoryPresenter.kt
  7. 15 6
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/O2InstantMessageActivity.kt
  8. 2 2
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/O2InstantMessageContract.kt
  9. 23 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/O2InstantMessagePresenter.kt
  10. 4 4
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/main/IndexPresenter.kt
  11. 7 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/main/MainPresenter.kt
  12. 38 2
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/security/AccountSecurityActivity.kt
  13. 3 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/security/AccountSecurityContract.kt
  14. 27 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/security/AccountSecurityPresenter.kt
  15. 16 2
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/component/adapter/SwipeRefreshCommonRecyclerViewAdapter.java
  16. 48 0
      o2android/app/src/main/res/layout/activity_account_security.xml
  17. 15 12
      o2android/app/src/main/res/layout/dialog_password_modify.xml
  18. 2 2
      o2android/gradle.properties
  19. 2 1
      o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/O2.kt
  20. 8 0
      o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/component/api/service/OrgAssemblePersonalService.kt
  21. 7 0
      o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/component/api/service/ProcessAssembleSurfaceService.kt
  22. 12 0
      o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/api/cms/CMSDocumentFilter.kt
  23. 13 0
      o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/api/main/person/PersonPwdForm.kt
  24. 45 0
      o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/api/o2/WorkInfoResData.kt
  25. 4 0
      o2ios/O2Platform.xcodeproj/project.pbxproj
  26. 21 21
      o2ios/O2Platform/App/Calendar-日程管理/Controller/OOCalendarEventViewController.swift
  27. 24 34
      o2ios/O2Platform/App/Calendar-日程管理/Controller/OOCalendarViewController.swift
  28. 21 61
      o2ios/O2Platform/App/Calendar-日程管理/calendar.storyboard
  29. 18 1
      o2ios/O2Platform/App/IM-聊天/IMChatViewController.swift
  30. 18 2
      o2ios/O2Platform/App/IM-聊天/IMInstantMessageViewController.swift
  31. 15 0
      o2ios/O2Platform/App/IM-聊天/IMViewModel.swift
  32. 2 4
      o2ios/O2Platform/App/NewAttance-考勤打卡/c/OOAttanceTotalController.swift
  33. 1 1
      o2ios/O2Platform/App/VoiceAI-语音处理/ViewModel/OOAIViewModel.swift
  34. 1 1
      o2ios/O2Platform/App/Work-工作/c/TodoTaskViewController.swift
  35. 10 7
      o2ios/O2Platform/App/Work-工作/c/TodoedTaskViewController.swift
  36. 31 8
      o2ios/O2Platform/App/Work-工作/m/OOTaskModels.swift
  37. 56 0
      o2ios/O2Platform/App/Work-工作/m/WorkInfoResData.swift
  38. 19 21
      o2ios/O2Platform/App/Work-工作/task.storyboard
  39. 17 13
      o2ios/O2Platform/App/meeting-会议/Controller/OOMeetingInforController.swift
  40. 12 2
      o2ios/O2Platform/Framework/O2API/TaskAPI/OOWorkAPI.swift
  41. 2 2
      o2ios/O2Platform/Info.plist
  42. 28 0
      o2server/configSample/cache.json
  43. 2 2
      o2server/configSample/components.json
  44. 3 1
      o2server/configSample/node_127.0.0.1.json
  45. 3 1
      o2server/configSample/processPlatform.json
  46. 1 1
      o2server/console_aix.sh
  47. 1 1
      o2server/console_arm.sh
  48. 1 1
      o2server/console_linux.sh
  49. 1 1
      o2server/console_macos.sh
  50. 1 1
      o2server/console_raspberrypi.sh
  51. 1 1
      o2server/console_risc.sh
  52. 1 1
      o2server/console_windows.bat
  53. 27 0
      o2server/pom.xml
  54. 1 1
      o2server/start_aix.sh
  55. 1 1
      o2server/start_aix_debug.sh
  56. 1 1
      o2server/start_arm.sh
  57. 1 1
      o2server/start_arm_debug.sh
  58. 1 1
      o2server/start_linux.sh
  59. 1 1
      o2server/start_linux_debug.sh
  60. 1 1
      o2server/start_macos.sh
  61. 1 1
      o2server/start_macos_debug.sh
  62. 1 1
      o2server/start_raspberrypi.sh
  63. 1 1
      o2server/start_raspberrypi_debug.sh
  64. 1 1
      o2server/start_risc.sh
  65. 1 1
      o2server/start_risc_debug.sh
  66. 1 1
      o2server/start_windows.bat
  67. 1 1
      o2server/start_windows_debug.bat
  68. 1 1
      o2server/stop_aix.sh
  69. 1 1
      o2server/stop_arm.sh
  70. 1 1
      o2server/stop_linux.sh
  71. 1 1
      o2server/stop_macos.sh
  72. 1 1
      o2server/stop_raspberrypi.sh
  73. 1 1
      o2server/stop_risc.sh
  74. 1 1
      o2server/stop_windows.bat
  75. 258 26
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/AttendanceDetailFactory.java
  76. 5 4
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/DingdingAttendanceFactory.java
  77. 364 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/StatisticPersonForMonthFactory.java
  78. 66 2
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attendancedetail/ActionListNextWithFilter.java
  79. 78 10
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/service/AttendanceStatisticService.java
  80. 10 15
      o2server/x_base_core_project/src/main/java/com/x/base/core/container/EntityManagerContainer.java
  81. 1 1
      o2server/x_base_core_project/src/main/java/com/x/base/core/container/factory/SlicePropertiesBuilder.java
  82. 198 6
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/Applications.java
  83. 7 5
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/build/CreateConfigSample.java
  84. 2 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/Collect.java
  85. 60 62
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/Components.java
  86. 1 1
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/Config.java
  87. 2 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/Dingding.java
  88. 2 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/Person.java
  89. 2 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/Portal.java
  90. 43 36
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/ProcessPlatform.java
  91. 2 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/Qiyeweixin.java
  92. 3 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/Token.java
  93. 24 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/WebServer.java
  94. 33 35
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/http/HttpToken.java
  95. 5 79
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/jaxrs/StandardJaxrsAction.java
  96. 106 7
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/BaseTools.java
  97. 95 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/ClassLoaderTools.java
  98. 43 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/CompressTools.java
  99. 1 1
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/DateTools.java
  100. 10 1
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/JarTools.java

+ 52 - 2
gulpfile.js

@@ -461,6 +461,7 @@ function build_concat_xform(){
         'o2web/source/' + path + '/Textfield.js',
         'o2web/source/' + path + '/Personfield.js',
         'o2web/source/' + path + '/*.js',
+        'o2web/source/x_component_process_Work/Processor.js',
         '!o2web/source/' + path + '/Office.js'
     ];
     var dest = 'target/o2server/servers/webServer/'+path+'/';
@@ -489,10 +490,59 @@ function build_bundle(){
         .pipe(rename({ extname: '.min.js' }))
         .pipe(gulp.dest(dest))
 }
+
+function build_concat_basework() {
+    var src = [
+        'o2web/source/x_desktop/js/base_work_begin.js',
+        'o2web/source/o2_core/o2/widget/Common.js',
+        'o2web/source/o2_core/o2/widget/Dialog.js',
+        'o2web/source/o2_core/o2/widget/UUID.js',
+        'o2web/source/o2_core/o2/widget/Menu.js',
+        'o2web/source/o2_core/o2/widget/Toolbar.js',
+        'o2web/source/o2_core/o2/xDesktop/Common.js',
+        'o2web/source/o2_core/o2/xDesktop/Actions/RestActions.js',
+        'o2web/source/o2_core/o2/xAction/RestActions.js',
+        'o2web/source/o2_core/o2/xDesktop/Access.js',
+        'o2web/source/o2_core/o2/xDesktop/Dialog.js',
+        'o2web/source/o2_core/o2/xDesktop/Menu.js',
+        'o2web/source/o2_core/o2/xDesktop/UserData.js',
+        'o2web/source/x_component_Template/MPopupForm.js',
+        'o2web/source/o2_core/o2/xDesktop/Authentication.js',
+        'o2web/source/o2_core/o2/xDesktop/Dialog.js',
+        'o2web/source/o2_core/o2/xDesktop/Window.js',
+        'o2web/source/x_component_Common/Main.js',
+        'o2web/source/x_component_process_Work/Main.js',
+        'o2web/source/x_component_Selector/package.js',
+        'o2web/source/x_component_Selector/Person.js',
+        'o2web/source/x_component_Selector/Identity.js',
+        'o2web/source/x_component_Selector/Unit.js',
+        'o2web/source/o2_core/o2/xScript/Actions/UnitActions.js',
+        'o2web/source/o2_core/o2/xScript/Actions/ScriptActions.js',
+        'o2web/source/o2_core/o2/xScript/Actions/CMSScriptActions.js',
+        'o2web/source/o2_core/o2/xScript/Actions/PortalScriptActions.js',
+        'o2web/source/o2_core/o2/xScript/Environment.js',
+        'o2web/source/x_component_Template/MTooltips.js',
+        'o2web/source/x_component_Template/MSelector.js',
+        'o2web/source/o2_core/o2/xAction/services/x_organization_assemble_authentication.js',
+        'o2web/source/o2_core/o2/xAction/services/x_processplatform_assemble_surface.js',
+        'o2web/source/o2_core/o2/xAction/services/x_cms_assemble_control.js',
+        'o2web/source/o2_core/o2/xAction/services/x_organization_assemble_control.js',
+        'o2web/source/o2_core/o2/xAction/services/x_query_assemble_surface.js',
+        'o2web/source/x_desktop/js/base_work_end.js',
+        'o2web/source/x_desktop/js/base.js'
+    ];
+    var dest = 'target/o2server/servers/webServer/x_desktop/js/';
+    return gulp.src(src)
+        .pipe(concat('base_work.js'))
+        .pipe(gulp.dest(dest))
+        .pipe(uglify())
+        .pipe(rename({ extname: '.min.js' }))
+        .pipe(gulp.dest(dest));
+}
 // function build_concat(){
 //     return gulp.parallel(build_concat_o2, build_concat_desktop, build_concat_xform);
 // }
-exports.build_concat = gulp.parallel(build_concat_o2, build_concat_desktop, build_concat_xform, build_bundle);
+exports.build_concat = gulp.parallel(build_concat_o2, build_concat_desktop, build_concat_xform, build_bundle, build_concat_basework);
 
 
 function build_web_v_html() {
@@ -584,7 +634,7 @@ function chmod_sh(){
 function chmod_servers(){
     return (shell.task('chmod 777 -R target/o2server/servers'))();
 }
-exports.build_web = gulp.series(build_web_minimize, build_web_move, gulp.parallel(build_concat_o2, build_concat_desktop, build_concat_xform, build_bundle), build_web_v_html, build_web_v_o2);
+exports.build_web = gulp.series(build_web_minimize, build_web_move, gulp.parallel(build_concat_o2, build_concat_desktop, build_concat_xform, build_bundle, build_concat_basework), build_web_v_html, build_web_v_o2);
 if (os.platform().indexOf("win")==-1){
     exports.deploy = gulp.series(deploy_server, chmod_jvm, chmod_commons, chmod_sh, chmod_servers);
 }else{

+ 14 - 14
o2android/app/src/main/AndroidManifest.xml

@@ -56,13 +56,13 @@
         <activity
             android:name=".app.clouddrive.v2.CloudDiskActivity"
             android:label="@string/title_activity_yunpan"
-            
+
             android:theme="@style/XBPMTheme.NoActionBar.Transparent" />
         <activity android:name=".app.o2.organization.ContactPickerActivity" />
         <activity android:name=".app.cms.application.CMSPublishDocumentActivity" />
         <activity
             android:name=".app.o2.webview.LocalImageViewActivity"
-            
+
             android:theme="@style/XBPMTheme.fullscreen" />
         <activity android:name=".app.o2.security.DeviceManagerActivity" />
 
@@ -70,7 +70,7 @@
             android:name=".app.o2.launch.LaunchActivity"
             android:label="@string/app_name"
             android:launchMode="singleTask"
-            
+
             android:theme="@style/XBPMLauncherTheme">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -92,20 +92,20 @@
             android:name=".app.o2.bind.BindPhoneActivity"
             android:label="@string/app_name"
             android:launchMode="singleTask"
-            
+
             android:theme="@style/XBPMClearActivityTheme" />
         <activity
             android:name=".app.o2.login.LoginActivity"
             android:label="@string/app_name"
             android:launchMode="singleTask"
-            
+
             android:theme="@style/XBPMClearActivityTheme"
             android:windowSoftInputMode="adjustUnspecified|stateHidden" />
         <activity
             android:name=".app.o2.main.MainActivity"
             android:label="@string/app_name"
             android:launchMode="singleTask"
-            
+
             android:theme="@style/XBPMClearActivityTheme" />
         <activity
             android:name=".app.o2.group.GroupActivity"
@@ -190,7 +190,7 @@
         <activity
             android:name=".app.bbs.main.BBSMainActivity"
             android:label="@string/bbs"
-            
+
             android:theme="@style/XBPMTheme.NoActionBar.Transparent" />
         <activity
             android:name=".app.bbs.section.BBSSectionActivity"
@@ -199,7 +199,7 @@
         <activity
             android:name=".app.bbs.view.BBSWebViewSubjectActivity"
             android:label="@string/title_activity_bbs_view"
-            
+
             android:windowSoftInputMode="adjustResize|stateHidden" />
         <activity
             android:name=".app.bbs.publish.BBSPublishSubjectActivity"
@@ -212,7 +212,7 @@
         <activity
             android:name=".app.cms.index.CMSIndexActivity"
             android:label="@string/cms"
-            
+
             android:theme="@style/XBPMTheme.NoActionBar.Transparent" />
         <activity
             android:name=".app.cms.application.CMSApplicationActivity"
@@ -225,7 +225,7 @@
         <activity
             android:name=".app.clouddrive.CloudDriveActivity"
             android:label="@string/title_activity_yunpan"
-            
+
             android:theme="@style/XBPMTheme.NoActionBar.Transparent" />
         <activity
             android:name=".app.clouddrive.viewer.PictureViewActivity"
@@ -234,7 +234,7 @@
         <activity
             android:name=".app.meeting.main.MeetingMainActivity"
             android:label="@string/title_activity_meeting"
-            
+
             android:theme="@style/XBPMTheme.NoActionBar.Transparent" />
         <activity
             android:name=".app.meeting.room.MeetingRoomChooseActivity"
@@ -258,7 +258,7 @@
         <activity
             android:name=".app.attendance.main.AttendanceMainActivity"
             android:label="@string/attendance_check_in_title"
-            
+
             android:theme="@style/XBPMTheme.NoActionBar.Transparent" />
         <activity
             android:name=".app.attendance.list.AttendanceListActivity"
@@ -287,14 +287,14 @@
         <activity
             android:name=".app.calendar.CalendarMainActivity"
             android:label="@string/calendar_name"
-            
+
             android:theme="@style/XBPMTheme.NoActionBar.Transparent" />
         <activity android:name=".app.calendar.CreateEventActivity" />
         <activity android:name=".app.calendar.CreateCalendarActivity" />
         <activity android:name=".app.calendar.CalendarStoreActivity" /> <!-- portal -->
         <activity
             android:name=".app.o2.webview.PortalWebViewActivity"
-            
+
             android:theme="@style/XBPMTheme.NoActionBar.Transparent" /> <!-- 换肤 -->
         <activity
             android:name=".app.o2.skin.SkinManagerActivity"

+ 2 - 1
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/O2AppUpdateManager.kt

@@ -30,7 +30,8 @@ class O2AppUpdateManager private constructor() {
     }
 
     //更新的json地址
-    private val o2AppVersionJsonUrl = "https://sample.o2oa.net/app/app.json"
+//    private val o2AppVersionJsonUrl = "https://sample.o2oa.net/app/app.json"
+    private val o2AppVersionJsonUrl = "https://app.o2oa.net/download/app.json"
     private val client = OkHttpClient()
 
 

+ 1 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/attendance/main/AttendanceCheckInNewFragment.kt

@@ -238,6 +238,7 @@ class AttendanceCheckInNewFragment : BaseMVPViewPagerFragment<AttendanceCheckInC
             activity?.let {
                 draw?.setColor(ContextCompat.getColor(it, R.color.disabled))
             }
+            XToast.toastShort(activity, "没有获取到当前用户打卡的信息!")
         }
     }
 

+ 8 - 2
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/attendance/main/AttendanceCheckInPresenter.kt

@@ -24,7 +24,13 @@ class AttendanceCheckInPresenter : BasePresenterImpl<AttendanceCheckInContract.V
             service.listMyRecords().subscribeOn(Schedulers.io())
                     .observeOn(AndroidSchedulers.mainThread())
                     .o2Subscribe {
-                        onNext { mView?.myRecords(it.data) }
+                        onNext {
+                            if (it.data != null) {
+                                mView?.myRecords(it.data)
+                            } else {
+                                mView?.myRecords(null)
+                            }
+                        }
                         onError { e, _ ->
                             XLog.error("", e)
                             mView?.myRecords(null)
@@ -42,7 +48,7 @@ class AttendanceCheckInPresenter : BasePresenterImpl<AttendanceCheckInContract.V
         getAttendanceAssembleControlService(mView?.getContext())?.let { service ->
             service.findAttendanceDetailMobileByPage(body, 1, 100)
                     .subscribeOn(Schedulers.io())
-                    .flatMap { response->
+                    .flatMap { response ->
                         val list = response.data
                         val retList = list?.sortedByDescending { it.signTime } ?: ArrayList()
                         Observable.just(retList)

+ 5 - 4
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/cms/application/CMSCategoryPresenter.kt

@@ -7,6 +7,7 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2App
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2SDKManager
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenterImpl
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.ResponseHandler
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.cms.CMSDocumentFilter
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.cms.CMSDocumentInfoJson
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
 import okhttp3.MediaType
@@ -21,14 +22,14 @@ class CMSCategoryPresenter : BasePresenterImpl<CMSCategoryContract.View>(), CMSC
             mView?.loadFail()
             return
         }
-        val wrapIn = HashMap<String, ArrayList<String>>()
         val category = ArrayList<String>()
         category.add(id)
-        wrapIn["categoryIdList"] = category
         val status = ArrayList<String>()
         status.add("published")
-        wrapIn["statusList"] = status
-        val json = O2SDKManager.instance().gson.toJson(wrapIn)
+        val filter = CMSDocumentFilter()
+        filter.categoryIdList = category
+        filter.statusList = status
+        val json = O2SDKManager.instance().gson.toJson(filter)
         val body = RequestBody.create(MediaType.parse("text/json"), json)
         getCMSAssembleControlService(mView?.getContext())?.let { service ->
             service.filterDocumentList(body, lastId, O2.DEFAULT_PAGE_NUMBER)

+ 15 - 6
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/O2InstantMessageActivity.kt

@@ -25,6 +25,7 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.enums.ApplicationEnu
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.flutter.FlutterConnectActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.InstantMessage
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.DateHelper
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XToast
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.go
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.visible
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.CircleImageView
@@ -84,6 +85,15 @@ class O2InstantMessageActivity : BaseMVPActivity<O2InstantMessageContract.View,
         }
     }
 
+    override fun workIsCompleted(flag: Boolean, workId: String) {
+        hideLoadingDialog()
+        if (!flag) {
+            go<TaskWebViewActivity>(TaskWebViewActivity.start(workId, "", ""))
+        }else {
+            XToast.toastShort(this, "工作已经结束!")
+        }
+    }
+
     private fun messageTypeEvent(textView: TextView, msg: InstantMessage) {
         val type = msg.type
         if (type.startsWith("task_")) {
@@ -92,9 +102,7 @@ class O2InstantMessageActivity : BaseMVPActivity<O2InstantMessageContract.View,
             }
         }else if (type.startsWith("taskCompleted_")) {
             if (!type.contains("_delete")) {
-                setLinkStyle(textView) {
-
-                }
+                openWork(msg, textView)
             }
         }else if (type.startsWith("read_")) {
             if (!type.contains("_delete")) {
@@ -140,10 +148,11 @@ class O2InstantMessageActivity : BaseMVPActivity<O2InstantMessageContract.View,
         val json = JSONTokener(msg.body).nextValue()
         if (json is JSONObject) {
             val work = try {json.getString("work")}catch (e: Exception){null}
-            val workCompleted = try {json.getString("workCompleted")}catch (e: Exception){null}
-            if (!TextUtils.isEmpty(work) || !TextUtils.isEmpty(workCompleted)) {
+            if (!TextUtils.isEmpty(work)) {
                 setLinkStyle(textView) {
-                    go<TaskWebViewActivity>(TaskWebViewActivity.start(work, workCompleted, ""))
+                    //先查询work对象
+                    showLoadingDialog()
+                    mPresenter.getWorkInfo(work!!)
                 }
             }
         }

+ 2 - 2
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/O2InstantMessageContract.kt

@@ -10,9 +10,9 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseView
  */
 object O2InstantMessageContract {
     interface View: BaseView {
-
+        fun workIsCompleted(flag: Boolean, workId: String)
     }
     interface Presenter: BasePresenter<View> {
-
+        fun getWorkInfo(workId: String)
     }
 }

+ 23 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/im/O2InstantMessagePresenter.kt

@@ -1,6 +1,10 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.im
 
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenterImpl
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.o2Subscribe
+import rx.android.schedulers.AndroidSchedulers
+import rx.schedulers.Schedulers
 
 
 /**
@@ -9,5 +13,24 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenterImpl
  */
 
 class O2InstantMessagePresenter : BasePresenterImpl<O2InstantMessageContract.View>(), O2InstantMessageContract.Presenter  {
+    override fun getWorkInfo(workId: String) {
+        val service = getProcessAssembleSurfaceServiceAPI(mView?.getContext())
+        if (service != null) {
+            service.getWorkInfo(workId)
+                    .subscribeOn(Schedulers.io())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .o2Subscribe {
+                        onNext {
+                            mView?.workIsCompleted(false, workId)
+                        }
+                        onError { e, _ ->
+                            XLog.error("", e)
+                            mView?.workIsCompleted(true, workId)
+                        }
+                    }
+        }else {
+            mView?.workIsCompleted(true, workId)
+        }
+    }
 
 }

+ 4 - 4
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/main/IndexPresenter.kt

@@ -8,6 +8,7 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenterImpl
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.ResponseHandler
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.enums.ApplicationEnum
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.realm.RealmDataService
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.cms.CMSDocumentFilter
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.HotPictureOutData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.persistence.MyAppListObject
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
@@ -35,12 +36,11 @@ class IndexPresenter : BasePresenterImpl<IndexContract.View>(), IndexContract.Pr
     }
 
     override fun loadNewsList(lastId: String) {
-        val wrapIn = HashMap<String, List<String>>()
         val status = ArrayList<String>()
         status.add("published")
-        wrapIn["statusList"] = status
-        val json = O2SDKManager.instance().gson.toJson(wrapIn)
-
+        val filter = CMSDocumentFilter()
+        filter.statusList = status
+        val json = O2SDKManager.instance().gson.toJson(filter)
         val body = RequestBody.create(MediaType.parse("text/json"), json)
         getCMSAssembleControlService(mView?.getContext())?.let { service ->
             service.filterDocumentList(body, lastId, O2.DEFAULT_PAGE_NUMBER)

+ 7 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/main/MainPresenter.kt

@@ -46,10 +46,17 @@ class MainPresenter : BasePresenterImpl<MainContract.View>(), MainContract.Prese
                                 O2SDKManager.instance().prefs().edit {
                                     putString(O2.PRE_ATTENDANCE_VERSION_KEY, "1");
                                 }
+                            }else {
+                                O2SDKManager.instance().prefs().edit {
+                                    putString(O2.PRE_ATTENDANCE_VERSION_KEY, "0");
+                                }
                             }
                         }
                         onError { e, _ ->
                             XLog.error("", e)
+                            O2SDKManager.instance().prefs().edit {
+                                putString(O2.PRE_ATTENDANCE_VERSION_KEY, "0");
+                            }
                         }
                     }
         }

+ 38 - 2
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/security/AccountSecurityActivity.kt

@@ -2,11 +2,14 @@ package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.security
 
 
 import android.os.Bundle
+import android.text.TextUtils
+import android.widget.EditText
 import kotlinx.android.synthetic.main.activity_account_security.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.*
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.bind.BindPhoneActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.my.MyInfoActivity
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.im.MessageType
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XToast
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.biometric.BioConstants
@@ -36,8 +39,12 @@ class AccountSecurityActivity : BaseMVPActivity<AccountSecurityContract.View, Ac
             go<MyInfoActivity>()
         }
 
-        val unitName = O2SDKManager.instance().prefs().getString(O2.PRE_BIND_UNIT_KEY, "")
-        tv_account_security_unit_name.text = "当前绑定服务器:$unitName"
+        rl_account_security_password_btn.setOnClickListener {
+            changeMyPassword()
+        }
+
+        val unitHost = O2SDKManager.instance().prefs().getString(O2.PRE_CENTER_HOST_KEY, "")
+        tv_account_security_unit_name.text = "当前绑定服务器:$unitHost"
 
         initBiometryAuthView()
 
@@ -59,6 +66,14 @@ class AccountSecurityActivity : BaseMVPActivity<AccountSecurityContract.View, Ac
         goAndClearBefore<BindPhoneActivity>()
     }
 
+    override fun updateMyPasswordFail(message: String) {
+        XToast.toastLong(this, message)
+    }
+
+    override fun updateMyPasswordSuccess() {
+        XToast.toastShort(this, "修改密码成功!")
+    }
+
     private val bioManager: BiometryManager by lazy { BiometryManager(this) }
     private fun initBiometryAuthView() {
 
@@ -121,6 +136,27 @@ class AccountSecurityActivity : BaseMVPActivity<AccountSecurityContract.View, Ac
         }
     }
 
+    private fun changeMyPassword() {
+        O2DialogSupport.openCustomViewDialog(this, "修改密码", R.layout.dialog_password_modify) { dialog ->
+            val old = dialog.findViewById<EditText>(R.id.dialog_password_old_edit_id).text.toString()
+            if (TextUtils.isEmpty(old)) {
+                XToast.toastShort(this@AccountSecurityActivity, "旧密码不能为空")
+                return@openCustomViewDialog
+            }
+            val newpwd = dialog.findViewById<EditText>(R.id.dialog_password_new_edit_id).text.toString()
+            if (TextUtils.isEmpty(newpwd)) {
+                XToast.toastShort(this@AccountSecurityActivity, "新密码不能为空")
+                return@openCustomViewDialog
+            }
+            val newpwdAgain = dialog.findViewById<EditText>(R.id.dialog_password_confirm_edit_id).text.toString()
+            if (newpwd != newpwdAgain) {
+                XToast.toastShort(this@AccountSecurityActivity, "新密码和确认新密码不一样")
+                return@openCustomViewDialog
+            }
+            mPresenter.updateMyPassword(old, newpwd, newpwdAgain)
+        }
+    }
+
     //如果识别成功 设置结果
     private fun setBioAuthResult() {
         val bioAuthUser = O2SDKManager.instance().prefs().getString(BioConstants.O2_bio_auth_user_id_prefs_key, "") ?: ""

+ 3 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/security/AccountSecurityContract.kt

@@ -8,9 +8,12 @@ object AccountSecurityContract {
     interface View : BaseView {
 
         fun logoutSuccess()
+        fun updateMyPasswordFail(message: String)
+        fun updateMyPasswordSuccess()
     }
 
     interface Presenter : BasePresenter<View> {
         fun logout(deviceId:String)
+        fun updateMyPassword(old: String, newPwd: String, newPwdConfirm: String)
     }
 }

+ 27 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/security/AccountSecurityPresenter.kt

@@ -3,7 +3,11 @@ package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.security
 import net.muliba.accounting.app.ExceptionHandler
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenterImpl
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.ResponseHandler
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.exception.O2ResponseException
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.IdData
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.person.PersonPwdForm
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.o2Subscribe
 import rx.android.schedulers.AndroidSchedulers
 import rx.schedulers.Schedulers
 
@@ -20,4 +24,27 @@ class AccountSecurityPresenter : BasePresenterImpl<AccountSecurityContract.View>
                             ExceptionHandler(mView?.getContext(), { e -> mView?.logoutSuccess()}))
         }
     }
+
+    override fun updateMyPassword(old: String, newPwd: String, newPwdConfirm: String) {
+        val service = getAssemblePersonalApi(mView?.getContext())
+        if (service != null) {
+            val form = PersonPwdForm(old, newPwd, newPwdConfirm)
+            service.modifyCurrentPersonPassword(form)
+                    .subscribeOn(Schedulers.io())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .o2Subscribe {
+                        onNext {
+                            mView?.updateMyPasswordSuccess()
+                        }
+                        onError { e, _ ->
+                            XLog.error("", e)
+                            if (e is O2ResponseException) {
+                                e.message?.let { mView?.updateMyPasswordFail(it) }
+                            }else {
+                                mView?.updateMyPasswordFail("修改失败!")
+                            }
+                        }
+                    }
+        }
+    }
 }

+ 16 - 2
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/component/adapter/SwipeRefreshCommonRecyclerViewAdapter.java

@@ -2,6 +2,8 @@ package net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.adapter;
 
 import android.content.Context;
 import androidx.recyclerview.widget.RecyclerView;
+
+import android.os.Handler;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -116,7 +118,13 @@ public abstract class SwipeRefreshCommonRecyclerViewAdapter<T> extends RecyclerV
      */
     public void addFooter(View view) {
         footer = view;
-        this.notifyDataSetChanged();
+        new Handler().post(new Runnable() {
+            @Override
+            public void run() {
+                // 刷新操作
+                notifyDataSetChanged();
+            }
+        });
     }
 
     /**
@@ -125,7 +133,13 @@ public abstract class SwipeRefreshCommonRecyclerViewAdapter<T> extends RecyclerV
      */
     public void removeFooter(View view) {
         footer = null;
-        this.notifyDataSetChanged();
+        new Handler().post(new Runnable() {
+            @Override
+            public void run() {
+                // 刷新操作
+                notifyDataSetChanged();
+            }
+        });
     }
 
 

+ 48 - 0
o2android/app/src/main/res/layout/activity_account_security.xml

@@ -88,6 +88,54 @@
                 android:layout_marginLeft="@dimen/spacing_normal"
                 android:layout_marginRight="@dimen/spacing_normal"
                 android:background="@color/z_color_split_line_ddd" />
+
+            <LinearLayout
+                android:orientation="vertical"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content">
+
+                <RelativeLayout
+                    android:id="@+id/rl_account_security_password_btn"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:layout_marginTop="@dimen/spacing_small"
+                    android:layout_marginBottom="@dimen/spacing_small"
+                    android:layout_marginLeft="@dimen/spacing_normal"
+                    android:layout_marginRight="@dimen/spacing_normal">
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:text="登录密码"
+                        android:layout_alignParentStart="true"
+                        android:textSize="15sp"
+                        android:textColor="@color/z_color_text_hint"
+                        android:layout_centerVertical="true" />
+                    <ImageView
+                        android:id="@+id/image_account_security_password_arrow"
+                        android:layout_width="22dp"
+                        android:layout_height="22dp"
+                        android:layout_alignParentEnd="true"
+                        android:layout_centerVertical="true"
+                        android:src="@mipmap/icon_arrow_22dp"/>
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_toStartOf="@id/image_account_security_password_arrow"
+                        android:layout_centerVertical="true"
+                        android:layout_marginEnd="@dimen/spacing_tiny"
+                        android:textSize="13sp"
+                        android:text="修改密码"
+                        android:textColor="@color/z_color_text_primary_dark"/>
+                </RelativeLayout>
+            </LinearLayout>
+
+            <View
+                android:layout_width="match_parent"
+                android:layout_height="1dp"
+                android:layout_marginLeft="@dimen/spacing_normal"
+                android:layout_marginRight="@dimen/spacing_normal"
+                android:background="@color/z_color_split_line_ddd" />
+
             <LinearLayout
                 android:orientation="vertical"
                 android:layout_width="match_parent"

+ 15 - 12
o2android/app/src/main/res/layout/dialog_password_modify.xml

@@ -14,20 +14,21 @@
         android:layout_marginRight="@dimen/spacing_normal">
 
         <TextView
-            android:layout_width="match_parent"
+            android:layout_width="0dp"
             android:layout_height="wrap_content"
+            android:layout_weight="1"
             android:text="原密码"
             android:id="@+id/dialog_password_old_id"
-            android:layout_weight="2"
             android:layout_marginTop="@dimen/spacing_small"
             android:layout_marginBottom="@dimen/spacing_small"
             android:layout_gravity="center_vertical"
+            android:textSize="@dimen/font_small"
             android:gravity="right" />
 
         <EditText
-            android:layout_width="match_parent"
+            android:layout_width="0dp"
             android:layout_height="wrap_content"
-            android:layout_weight="1"
+            android:layout_weight="2"
             android:inputType="textPassword"
             android:id="@+id/dialog_password_old_edit_id"
             android:hint="输入原密码" />
@@ -42,21 +43,22 @@
         android:layout_marginRight="@dimen/spacing_normal">
 
         <TextView
-            android:layout_width="match_parent"
+            android:layout_width="0dp"
             android:layout_height="wrap_content"
+            android:layout_weight="1"
             android:text="新密码"
             android:id="@+id/dialog_password_new_id"
-            android:layout_weight="2"
             android:layout_marginTop="@dimen/spacing_small"
             android:layout_marginBottom="@dimen/spacing_small"
             android:gravity="right"
+            android:textSize="@dimen/font_small"
             android:layout_gravity="center_vertical" />
 
         <EditText
-            android:layout_width="match_parent"
+            android:layout_width="0dp"
             android:layout_height="wrap_content"
+            android:layout_weight="2"
             android:inputType="textPassword"
-            android:layout_weight="1"
             android:id="@+id/dialog_password_new_edit_id"
             android:hint="输入新密码" />
     </LinearLayout>
@@ -71,21 +73,22 @@
         android:layout_marginRight="@dimen/spacing_normal">
 
         <TextView
-            android:layout_width="match_parent"
+            android:layout_width="0dp"
             android:layout_height="wrap_content"
+            android:layout_weight="1"
             android:text="确认新密码"
             android:id="@+id/dialog_password_confirm_id"
-            android:layout_weight="2"
             android:layout_marginTop="@dimen/spacing_small"
             android:layout_marginBottom="@dimen/spacing_small"
             android:gravity="right"
+            android:textSize="@dimen/font_small"
             android:layout_gravity="center_vertical" />
 
         <EditText
-            android:layout_width="match_parent"
+            android:layout_width="0dp"
             android:layout_height="wrap_content"
+            android:layout_weight="2"
             android:inputType="textPassword"
-            android:layout_weight="1"
             android:id="@+id/dialog_password_confirm_edit_id"
             android:hint="输入新密码" />
     </LinearLayout>

+ 2 - 2
o2android/gradle.properties

@@ -23,6 +23,6 @@ android.enableJetifier=true
 
 
 # o2
-o2.versionName=5.1.7
-o2.versionCode=117
+o2.versionName=5.1.8
+o2.versionCode=118
 

+ 2 - 1
o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/O2.kt

@@ -11,7 +11,8 @@ object O2 {
 
 
     val O2_COLLECT_URL = "http://collect.o2oa.net:20080/o2_collect_assemble/"
-    val O2_DOWNLOAD_URL = "https://sample.o2oa.net/app/download.html"
+//    val O2_DOWNLOAD_URL = "https://sample.o2oa.net/app/download.html"
+    val O2_DOWNLOAD_URL = "https://app.o2oa.net/download/download.html"
 
     const val O2_Process_start_mode_draft = "draft"
 

+ 8 - 0
o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/component/api/service/OrgAssemblePersonalService.kt

@@ -4,6 +4,7 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.ApiResponse
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.IdData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.ValueData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.person.PersonJson
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.person.PersonPwdForm
 import okhttp3.MultipartBody
 import retrofit2.http.*
 import rx.Observable
@@ -45,4 +46,11 @@ interface OrgAssemblePersonalService {
      */
     @GET("jaxrs/definition/meetingConfig")
     fun getMeetingConfig() : Observable<ApiResponse<String>>
+
+
+    /**
+     * 更新当前用户密码
+     */
+    @PUT("jaxrs/password")
+    fun modifyCurrentPersonPassword(@Body body: PersonPwdForm): Observable<ApiResponse<ValueData>>
 }

+ 7 - 0
o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/component/api/service/ProcessAssembleSurfaceService.kt

@@ -71,6 +71,13 @@ interface ProcessAssembleSurfaceService {
     @DELETE("jaxrs/work/{workId}")
     fun deleteWorkForm(@Path("workId") workId: String): Observable<ApiResponse<IdData>>
 
+    /**
+     * 获取工作对象
+     * 如果返回500错误就是工作已经结束了
+     */
+    @GET("jaxrs/work/{workId}")
+    fun getWorkInfo(@Path("workId") workId: String): Observable<ApiResponse<WorkInfoResData>>
+
     /**
      * 已阅列表
      * @param lastId

+ 12 - 0
o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/api/cms/CMSDocumentFilter.kt

@@ -0,0 +1,12 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.cms
+
+/**
+ * Created by fancyLou on 2020-09-07.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+
+data class CMSDocumentFilter(
+        var statusList: ArrayList<String> = ArrayList(),
+        var categoryIdList: ArrayList<String> = ArrayList(),
+        var orderField: String = ""
+)

+ 13 - 0
o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/api/main/person/PersonPwdForm.kt

@@ -0,0 +1,13 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.person
+
+/**
+ * Created by fancyLou on 2020-09-03.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+
+
+class PersonPwdForm(
+        var oldPassword: String = "",
+        var newPassword: String = "",
+        var confirmPassword: String = ""
+)

+ 45 - 0
o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/api/o2/WorkInfoResData.kt

@@ -0,0 +1,45 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2
+
+
+/**
+ * Created by fancyLou on 2020-09-04.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+
+data class WorkInfoResData(
+        var work: WorkInfoRes? ,
+        var workLogList: List<WorkLog> = ArrayList()
+)
+
+data class WorkInfoRes(
+    var id: String = "",
+    var job: String = "",
+    var title: String = "",
+    var startTime: String = "",
+    var startTimeMonth: String = "",
+    var creatorPerson: String = "",
+    var creatorIdentity: String = "",
+    var creatorUnit: String = "",
+    var application: String = "",
+    var applicationName: String = "",
+    var applicationAlias: String = "",
+    var process: String = "",
+    var processName: String = "",
+    var processAlias: String = "",
+    var activity: String = "",
+    var activityType: String = "",
+    var activityName: String = "",
+    var activityAlias: String = "",
+    var activityDescription: String = "",
+    var activityToken: String = "",
+    var activityArrivedTime: String = "",
+    var serial: String = "",
+    var workCreateType: String = "",
+    var workStatus: String = "",
+    var manualTaskIdentityText: String = "",
+    var form: String = "",
+    var destinationRoute: String = "",
+    var destinationRouteName: String = "",
+    var destinationActivityType: String = "",
+    var destinationActivity: String = ""
+)

+ 4 - 0
o2ios/O2Platform.xcodeproj/project.pbxproj

@@ -246,6 +246,7 @@
 		B1B0103023586B34002BF874 /* CFFileTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1B0102E23586B34002BF874 /* CFFileTableViewCell.xib */; };
 		B1B110E2223622C400775BEF /* O2BioLocalAuth.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B110AB223622C400775BEF /* O2BioLocalAuth.swift */; };
 		B1B110E422363F7200775BEF /* BioAuthLoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B110E322363F7200775BEF /* BioAuthLoginViewController.swift */; };
+		B1B1303025021EAC003B99DF /* WorkInfoResData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B1302F25021EAC003B99DF /* WorkInfoResData.swift */; };
 		B1B2148F216073B400D9CA7E /* ScanHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B21459216073B400D9CA7E /* ScanHelper.swift */; };
 		B1B214912160A69E00D9CA7E /* Tuling123API.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B214902160A69E00D9CA7E /* Tuling123API.swift */; };
 		B1B214932160A8A300D9CA7E /* TulingPostModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B214922160A8A300D9CA7E /* TulingPostModel.swift */; };
@@ -1414,6 +1415,7 @@
 		B1B0102E23586B34002BF874 /* CFFileTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CFFileTableViewCell.xib; sourceTree = "<group>"; };
 		B1B110AB223622C400775BEF /* O2BioLocalAuth.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = O2BioLocalAuth.swift; sourceTree = "<group>"; };
 		B1B110E322363F7200775BEF /* BioAuthLoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BioAuthLoginViewController.swift; sourceTree = "<group>"; };
+		B1B1302F25021EAC003B99DF /* WorkInfoResData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkInfoResData.swift; sourceTree = "<group>"; };
 		B1B21459216073B400D9CA7E /* ScanHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScanHelper.swift; sourceTree = "<group>"; };
 		B1B214902160A69E00D9CA7E /* Tuling123API.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tuling123API.swift; sourceTree = "<group>"; };
 		B1B214922160A8A300D9CA7E /* TulingPostModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TulingPostModel.swift; sourceTree = "<group>"; };
@@ -4111,6 +4113,7 @@
 				E4B888501D9D48F1002E1A46 /* TodoTask.swift */,
 				B1908E1322685E8F00D75632 /* O2WebViewModels.swift */,
 				B1BA42B724133AC40081CED8 /* TaskCreateData.swift */,
+				B1B1302F25021EAC003B99DF /* WorkInfoResData.swift */,
 			);
 			path = m;
 			sourceTree = "<group>";
@@ -5410,6 +5413,7 @@
 				09E02E8C1F16319600579887 /* Haneke.swift in Sources */,
 				B1E95F7B23750CC2004876B7 /* CloudFilePreviewController.swift in Sources */,
 				E4C24C52208D7EDE00E426B0 /* OOContactUnitHeader.swift in Sources */,
+				B1B1303025021EAC003B99DF /* WorkInfoResData.swift in Sources */,
 				B165CD662242093500373B66 /* OOAlertViewController.swift in Sources */,
 				E428AF5D20AC1CD100D964B9 /* OOAttandanceViewModel.swift in Sources */,
 				E4B8886F1D9D48F1002E1A46 /* ContactHomeViewController.swift in Sources */,

+ 21 - 21
o2ios/O2Platform/App/Calendar-日程管理/Controller/OOCalendarEventViewController.swift

@@ -465,11 +465,11 @@ class OOCalendarEventViewController: UITableViewController {
             event.recurrenceRule = rruleEncode()
             event.valarmTime_config = remindValue == "NONE" ? "" : remindValue
             event.comment = remark
-            viewModel.saveCalendarEvent(event: event).then { (result)  in
+            viewModel.saveCalendarEvent(event: event).always{
+                self.hideLoading()
+            }.then { (result)  in
                 DDLogInfo("保存结果:\(result)")
                 self.closeWindow()
-                }.always{
-                    self.hideLoading()
                 }.catch { (error) in
                     DDLogError(error.localizedDescription)
                     self.showError(title: "保存日程错误!")
@@ -526,33 +526,33 @@ class OOCalendarEventViewController: UITableViewController {
     }
     
     private func updateSingle() {
-        viewModel.updateCalendarEventSingle(id: (eventInfo?.id!)!, event: eventInfo!).then { (result)  in
+        viewModel.updateCalendarEventSingle(id: (eventInfo?.id!)!, event: eventInfo!).always {
+            self.hideLoading()
+        }.then { (result)  in
             DDLogInfo("保存结果:\(result)")
             self.closeWindow()
-            }.always{
-                self.hideLoading()
             }.catch { (error) in
                 DDLogError(error.localizedDescription)
                 self.showError(title: "更新单个日程错误!")
         }
     }
     private func updateAfter() {
-        viewModel.updateCalendarEventAfter(id: (eventInfo?.id!)!, event: eventInfo!).then { (result)  in
+        viewModel.updateCalendarEventAfter(id: (eventInfo?.id!)!, event: eventInfo!).always {
+            self.hideLoading()
+        }.then { (result)  in
             DDLogInfo("保存结果:\(result)")
             self.closeWindow()
-            }.always{
-                self.hideLoading()
             }.catch { (error) in
                 DDLogError(error.localizedDescription)
                 self.showError(title: "更新after日程错误!")
         }
     }
     private func updateAll() {
-        viewModel.updateCalendarEventAll(id: (eventInfo?.id!)!, event: eventInfo!).then { (result)  in
+        viewModel.updateCalendarEventAll(id: (eventInfo?.id!)!, event: eventInfo!).always {
+            self.hideLoading()
+        }.then { (result)  in
             DDLogInfo("保存结果:\(result)")
             self.closeWindow()
-            }.always{
-                self.hideLoading()
             }.catch { (error) in
                 DDLogError(error.localizedDescription)
                 self.showError(title: "更新all日程错误!")
@@ -581,31 +581,31 @@ class OOCalendarEventViewController: UITableViewController {
         self.showLoading()
         switch type {
         case 0:
-            viewModel.deleteCalendarEventSingle(id: (eventInfo?.id!)!).then { (result)  in
+            viewModel.deleteCalendarEventSingle(id: (eventInfo?.id!)!).always{
+                self.hideLoading()
+            }.then { (result)  in
                 DDLogInfo("删除结果:\(result)")
                 self.closeWindow()
-                }.always{
-                    self.hideLoading()
                 }.catch { (error) in
                     DDLogError(error.localizedDescription)
                     self.showError(title: "删除Single日程错误!")
             }
         case 1:
-            viewModel.deleteCalendarEventAfter(id: (eventInfo?.id!)!).then { (result)  in
+            viewModel.deleteCalendarEventAfter(id: (eventInfo?.id!)!).always{
+                self.hideLoading()
+            }.then { (result)  in
                 DDLogInfo("删除结果:\(result)")
                 self.closeWindow()
-                }.always{
-                    self.hideLoading()
                 }.catch { (error) in
                     DDLogError(error.localizedDescription)
                     self.showError(title: "删除After日程错误!")
             }
         case 2:
-            viewModel.deleteCalendarEventAll(id: (eventInfo?.id!)!).then { (result)  in
+            viewModel.deleteCalendarEventAll(id: (eventInfo?.id!)!).always {
+                self.hideLoading()
+            }.then { (result)  in
                 DDLogInfo("删除结果:\(result)")
                 self.closeWindow()
-                }.always{
-                    self.hideLoading()
                 }.catch { (error) in
                     DDLogError(error.localizedDescription)
                     self.showError(title: "删除All日程错误!")

+ 24 - 34
o2ios/O2Platform/App/Calendar-日程管理/Controller/OOCalendarViewController.swift

@@ -42,7 +42,6 @@ class OOCalendarViewController: UITableViewController {
     @IBOutlet weak var calendarIsOpenSwitch: UISwitch!
     @IBOutlet weak var calendarColorStackView: UIStackView!
     @IBOutlet weak var calendarRemarkField: UITextField!
-    @IBOutlet weak var calendarDeleteBtn: UIButton!
 
 
     @IBOutlet weak var calendarTypeField: UITextField!
@@ -55,19 +54,11 @@ class OOCalendarViewController: UITableViewController {
 
     @IBOutlet weak var calendarIsOpenBtn: UIButton!
 
-    @IBAction func editRemarkBtn(_ sender: Any) {
-        self.performSegue(withIdentifier: "ShowEditRemark", sender: nil)
-    }
-
+//    @IBAction func editRemarkBtn(_ sender: Any) {
+//        self.performSegue(withIdentifier: "ShowEditRemark", sender: nil)
+//    }
 
 
-
-    @IBAction func deleteBtnTap(_ sender: UIButton) {
-        showDefaultConfirm(title: "删除日历", message: "确定要删除当前日历吗,会同时删除该日历下的日程事件?") { (action) in
-            self.deleteCalendar()
-        }
-    }
-
     //选择是否公开
     @IBAction func selectType(_ sender: Any) {
         let alertController = UIAlertController(title: "请选择类型", message: "", preferredStyle: .actionSheet)
@@ -100,22 +91,18 @@ class OOCalendarViewController: UITableViewController {
     override func viewDidLoad() {
         super.viewDidLoad()
         if calendarInfo != nil && calendarInfo?.id != nil {
-            self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "修改", style: .plain, target: self, action: #selector(tapSave))
+            self.navigationItem.rightBarButtonItems = [
+                UIBarButtonItem(title: "修改保存", style: .plain, target: self, action: #selector(tapSave)),
+                UIBarButtonItem(title: "删除", style: .plain, target: self, action: #selector(tapDelete))
+            ]
             self.navigationItem.title = "修改日历"
+            loadCalendarInfoFromNet()
         } else {
             self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "保存", style: .plain, target: self, action: #selector(tapSave))
             self.navigationItem.title = "新增日历"
-        }
-        self.tableView.tableFooterView = UIView(frame: CGRect.zero)
-
-        if calendarInfo != nil && calendarInfo?.id != nil {
-            loadCalendarInfoFromNet()
-            calendarDeleteBtn.isHidden = false
-        } else {
             self.calendarInfo = OOCalendarInfo.init()
-            calendarDeleteBtn.isHidden = true
         }
-
+        self.tableView.tableFooterView = UIView(frame: CGRect.zero)
 
         //隐藏输入法
         calendarNameField.delegate = self
@@ -145,6 +132,11 @@ class OOCalendarViewController: UITableViewController {
         self.view.endEditing(true)
     }
 
+    @objc func tapDelete() {
+        showDefaultConfirm(title: "删除日历", message: "确定要删除当前日历吗,会同时删除该日历下的日程事件?") { (action) in
+            self.deleteCalendar()
+        }
+    }
     @objc func tapSave() {
         hideKeyboard()
         let name = calendarNameField.text
@@ -164,12 +156,6 @@ class OOCalendarViewController: UITableViewController {
             return
         }
         self.showLoading(title: "正在保存...")
-        /*
-      var calendar = self.calendarInfo
-      if calendarInfo != nil && calendarInfo?.id != nil { // 修改
-          //calendar.id = calendarInfo?.id!
-          calendar = calendarInfo ?? OOCalendarInfo.init()
-      }*/
 
         self.calendarInfo!.name = name
         self.calendarInfo!.isPublic = isopen
@@ -191,11 +177,11 @@ class OOCalendarViewController: UITableViewController {
         }
 
         viewModel.saveCalendar(calendar: self.calendarInfo!)
-            .then { (result) in
+            .always {
+                self.hideLoading()
+            }.then { (result) in
                 DDLogInfo("保存日历成功!!!\(result)")
                 self.closeWindow()
-            }.always {
-                self.hideLoading()
             }.catch { (error) in
                 DDLogError(error.localizedDescription)
                 self.showError(title: "保存日历错误!")
@@ -204,11 +190,13 @@ class OOCalendarViewController: UITableViewController {
     }
     private func deleteCalendar() {
         self.showLoading()
-        viewModel.deleteCalendar(id: (calendarInfo?.id!)!).then { (result) in
+        viewModel.deleteCalendar(id: (calendarInfo?.id!)!)
+        .always {
+            self.hideLoading()
+        }
+        .then { (result) in
             DDLogInfo("删除结果:\(result)")
             self.closeWindow()
-        }.always {
-            self.hideLoading()
         }.catch { (error) in
             DDLogError(error.localizedDescription)
             self.showError(title: "删除日历错误!")
@@ -433,6 +421,7 @@ class OOCalendarViewController: UITableViewController {
 
 
     private func loadCalendarInfoFromNet() {
+        DDLogDebug("loadCalendarInfoFromNet............")
         viewModel.getCalendar(id: (calendarInfo?.id)!)
             .then { (calendar) in
                 self.updateStuffValue(calendar: calendar)
@@ -442,6 +431,7 @@ class OOCalendarViewController: UITableViewController {
     }
 
     private func updateStuffValue(calendar: OOCalendarInfo) {
+        DDLogDebug("updateStuffValue............\(calendar.color)")
         calendarNameField.text = calendar.name
 
         if calendar.type == "UNIT" {

+ 21 - 61
o2ios/O2Platform/App/Calendar-日程管理/calendar.storyboard

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="tKt-3P-uQz">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="tKt-3P-uQz">
     <device id="retina4_7" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
@@ -922,16 +922,15 @@
                                             <autoresizingMask key="autoresizingMask"/>
                                             <subviews>
                                                 <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="请选择类型" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="6Nq-Qx-KK4">
-                                                    <rect key="frame" x="16" y="6" width="291" height="32"/>
+                                                    <rect key="frame" x="16" y="6" width="287" height="32"/>
                                                     <accessibility key="accessibilityConfiguration" identifier="calendarType"/>
                                                     <fontDescription key="fontDescription" type="system" pointSize="14"/>
                                                     <textInputTraits key="textInputTraits"/>
                                                 </textField>
                                                 <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="GjW-u2-fg5">
-                                                    <rect key="frame" x="319" y="6" width="32" height="32"/>
+                                                    <rect key="frame" x="315" y="6" width="44" height="32"/>
                                                     <constraints>
-                                                        <constraint firstAttribute="height" constant="32" id="W9a-KM-Bba"/>
-                                                        <constraint firstAttribute="width" constant="32" id="rno-Bo-Yb6"/>
+                                                        <constraint firstAttribute="width" constant="44" id="rno-Bo-Yb6"/>
                                                     </constraints>
                                                     <state key="normal" title="选择"/>
                                                     <connections>
@@ -942,7 +941,7 @@
                                             <constraints>
                                                 <constraint firstAttribute="bottom" secondItem="6Nq-Qx-KK4" secondAttribute="bottom" constant="6" id="AIZ-wd-7mK"/>
                                                 <constraint firstItem="GjW-u2-fg5" firstAttribute="top" secondItem="EZA-d2-7f1" secondAttribute="top" constant="6" id="MIe-qp-SqM"/>
-                                                <constraint firstAttribute="trailing" secondItem="GjW-u2-fg5" secondAttribute="trailing" constant="24" id="YZn-fx-RPo"/>
+                                                <constraint firstAttribute="trailing" secondItem="GjW-u2-fg5" secondAttribute="trailing" constant="16" id="YZn-fx-RPo"/>
                                                 <constraint firstItem="GjW-u2-fg5" firstAttribute="leading" secondItem="6Nq-Qx-KK4" secondAttribute="trailing" constant="12" id="aCh-YM-UYH"/>
                                                 <constraint firstItem="6Nq-Qx-KK4" firstAttribute="top" secondItem="EZA-d2-7f1" secondAttribute="top" constant="6" id="aY8-2v-BEC"/>
                                                 <constraint firstAttribute="bottom" secondItem="GjW-u2-fg5" secondAttribute="bottom" constant="6" id="dTF-AD-Pg2"/>
@@ -1229,16 +1228,15 @@
                                             <autoresizingMask key="autoresizingMask"/>
                                             <subviews>
                                                 <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="选择所属组织" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="vpF-O7-lvf">
-                                                    <rect key="frame" x="16" y="6" width="291" height="32"/>
+                                                    <rect key="frame" x="16" y="6" width="287" height="32"/>
                                                     <accessibility key="accessibilityConfiguration" identifier="calendarType"/>
                                                     <fontDescription key="fontDescription" type="system" pointSize="14"/>
                                                     <textInputTraits key="textInputTraits"/>
                                                 </textField>
                                                 <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="kae-RP-FyB">
-                                                    <rect key="frame" x="319" y="6" width="32" height="32"/>
+                                                    <rect key="frame" x="315" y="6" width="44" height="32"/>
                                                     <constraints>
-                                                        <constraint firstAttribute="width" constant="32" id="Hwl-ng-8iq"/>
-                                                        <constraint firstAttribute="height" constant="32" id="ZuX-YZ-s8I"/>
+                                                        <constraint firstAttribute="width" constant="44" id="Hwl-ng-8iq"/>
                                                     </constraints>
                                                     <state key="normal" title="选择"/>
                                                     <connections>
@@ -1250,7 +1248,7 @@
                                                 <constraint firstItem="vpF-O7-lvf" firstAttribute="top" secondItem="SSH-zn-oXh" secondAttribute="top" constant="6" id="J2T-eN-he2"/>
                                                 <constraint firstItem="kae-RP-FyB" firstAttribute="top" secondItem="SSH-zn-oXh" secondAttribute="top" constant="6" id="Q0R-ZW-QPM"/>
                                                 <constraint firstAttribute="bottom" secondItem="kae-RP-FyB" secondAttribute="bottom" constant="6" id="Q2f-Im-2VI"/>
-                                                <constraint firstAttribute="trailing" secondItem="kae-RP-FyB" secondAttribute="trailing" constant="24" id="S3M-PM-Qan"/>
+                                                <constraint firstAttribute="trailing" secondItem="kae-RP-FyB" secondAttribute="trailing" constant="16" id="S3M-PM-Qan"/>
                                                 <constraint firstItem="kae-RP-FyB" firstAttribute="leading" secondItem="vpF-O7-lvf" secondAttribute="trailing" constant="12" id="ZdW-3y-eX1"/>
                                                 <constraint firstAttribute="bottom" secondItem="vpF-O7-lvf" secondAttribute="bottom" constant="6" id="gkU-as-WY8"/>
                                                 <constraint firstItem="vpF-O7-lvf" firstAttribute="leading" secondItem="SSH-zn-oXh" secondAttribute="leading" constant="16" id="iuw-nj-CGt"/>
@@ -1265,16 +1263,15 @@
                                             <autoresizingMask key="autoresizingMask"/>
                                             <subviews>
                                                 <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="选择管理者" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="bIB-vr-JmD">
-                                                    <rect key="frame" x="16" y="6" width="291" height="32"/>
+                                                    <rect key="frame" x="16" y="6" width="287" height="32"/>
                                                     <accessibility key="accessibilityConfiguration" identifier="calendarType"/>
                                                     <fontDescription key="fontDescription" type="system" pointSize="14"/>
                                                     <textInputTraits key="textInputTraits"/>
                                                 </textField>
                                                 <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="m4R-gF-vOd">
-                                                    <rect key="frame" x="319" y="6" width="32" height="32"/>
+                                                    <rect key="frame" x="315" y="6" width="44" height="32"/>
                                                     <constraints>
-                                                        <constraint firstAttribute="width" constant="32" id="L1T-hn-46f"/>
-                                                        <constraint firstAttribute="height" constant="32" id="TkP-zZ-3HE"/>
+                                                        <constraint firstAttribute="width" constant="44" id="L1T-hn-46f"/>
                                                     </constraints>
                                                     <state key="normal" title="选择"/>
                                                     <connections>
@@ -1285,7 +1282,7 @@
                                             <constraints>
                                                 <constraint firstItem="bIB-vr-JmD" firstAttribute="leading" secondItem="iOf-91-Mpo" secondAttribute="leading" constant="16" id="4ou-CN-BP7"/>
                                                 <constraint firstAttribute="bottom" secondItem="m4R-gF-vOd" secondAttribute="bottom" constant="6" id="LoN-Hb-nig"/>
-                                                <constraint firstAttribute="trailing" secondItem="m4R-gF-vOd" secondAttribute="trailing" constant="24" id="UfC-cM-iMM"/>
+                                                <constraint firstAttribute="trailing" secondItem="m4R-gF-vOd" secondAttribute="trailing" constant="16" id="UfC-cM-iMM"/>
                                                 <constraint firstItem="m4R-gF-vOd" firstAttribute="top" secondItem="iOf-91-Mpo" secondAttribute="top" constant="6" id="XNp-6R-SxO"/>
                                                 <constraint firstItem="m4R-gF-vOd" firstAttribute="leading" secondItem="bIB-vr-JmD" secondAttribute="trailing" constant="12" id="cd8-WC-7bV"/>
                                                 <constraint firstItem="bIB-vr-JmD" firstAttribute="top" secondItem="iOf-91-Mpo" secondAttribute="top" constant="6" id="yY4-uT-gAH"/>
@@ -1301,16 +1298,15 @@
                                             <autoresizingMask key="autoresizingMask"/>
                                             <subviews>
                                                 <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="选择可见范围" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="5RB-dp-23j">
-                                                    <rect key="frame" x="16" y="6" width="291" height="32"/>
+                                                    <rect key="frame" x="16" y="6" width="287" height="32"/>
                                                     <accessibility key="accessibilityConfiguration" identifier="calendarType"/>
                                                     <fontDescription key="fontDescription" type="system" pointSize="14"/>
                                                     <textInputTraits key="textInputTraits"/>
                                                 </textField>
                                                 <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Yrc-Ws-oZH">
-                                                    <rect key="frame" x="319" y="6" width="32" height="32"/>
+                                                    <rect key="frame" x="315" y="6" width="44" height="32"/>
                                                     <constraints>
-                                                        <constraint firstAttribute="height" constant="32" id="Xvt-5E-w3M"/>
-                                                        <constraint firstAttribute="width" constant="32" id="qzS-jN-n7Y"/>
+                                                        <constraint firstAttribute="width" constant="44" id="qzS-jN-n7Y"/>
                                                     </constraints>
                                                     <state key="normal" title="选择"/>
                                                     <connections>
@@ -1325,7 +1321,7 @@
                                                 <constraint firstAttribute="bottom" secondItem="5RB-dp-23j" secondAttribute="bottom" constant="6" id="ggd-Id-8Ql"/>
                                                 <constraint firstAttribute="bottom" secondItem="Yrc-Ws-oZH" secondAttribute="bottom" constant="6" id="nG1-Sd-yel"/>
                                                 <constraint firstItem="Yrc-Ws-oZH" firstAttribute="top" secondItem="08M-eV-Wtw" secondAttribute="top" constant="6" id="olv-zV-pqA"/>
-                                                <constraint firstAttribute="trailing" secondItem="Yrc-Ws-oZH" secondAttribute="trailing" constant="24" id="zUH-nC-fcT"/>
+                                                <constraint firstAttribute="trailing" secondItem="Yrc-Ws-oZH" secondAttribute="trailing" constant="16" id="zUH-nC-fcT"/>
                                             </constraints>
                                         </tableViewCellContentView>
                                     </tableViewCell>
@@ -1337,16 +1333,15 @@
                                             <autoresizingMask key="autoresizingMask"/>
                                             <subviews>
                                                 <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="选择可新建范围" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="HYK-cG-NOj">
-                                                    <rect key="frame" x="16" y="6" width="291" height="32"/>
+                                                    <rect key="frame" x="16" y="6" width="287" height="32"/>
                                                     <accessibility key="accessibilityConfiguration" identifier="calendarType"/>
                                                     <fontDescription key="fontDescription" type="system" pointSize="14"/>
                                                     <textInputTraits key="textInputTraits"/>
                                                 </textField>
                                                 <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ueU-we-edj">
-                                                    <rect key="frame" x="319" y="6" width="32" height="32"/>
+                                                    <rect key="frame" x="315" y="6" width="44" height="32"/>
                                                     <constraints>
-                                                        <constraint firstAttribute="width" constant="32" id="LGG-fL-DaH"/>
-                                                        <constraint firstAttribute="height" constant="32" id="WoG-4j-BlE"/>
+                                                        <constraint firstAttribute="width" constant="44" id="LGG-fL-DaH"/>
                                                     </constraints>
                                                     <state key="normal" title="选择"/>
                                                     <connections>
@@ -1358,7 +1353,7 @@
                                                 <constraint firstItem="ueU-we-edj" firstAttribute="top" secondItem="uYh-KG-FIU" secondAttribute="top" constant="6" id="0QB-av-9Fw"/>
                                                 <constraint firstAttribute="bottom" secondItem="ueU-we-edj" secondAttribute="bottom" constant="6" id="1Ed-sZ-eSS"/>
                                                 <constraint firstItem="HYK-cG-NOj" firstAttribute="leading" secondItem="uYh-KG-FIU" secondAttribute="leading" constant="16" id="H6R-aE-WPo"/>
-                                                <constraint firstAttribute="trailing" secondItem="ueU-we-edj" secondAttribute="trailing" constant="24" id="JgJ-Gq-FTQ"/>
+                                                <constraint firstAttribute="trailing" secondItem="ueU-we-edj" secondAttribute="trailing" constant="16" id="JgJ-Gq-FTQ"/>
                                                 <constraint firstItem="HYK-cG-NOj" firstAttribute="top" secondItem="uYh-KG-FIU" secondAttribute="top" constant="6" id="XlS-j1-UkW"/>
                                                 <constraint firstItem="ueU-we-edj" firstAttribute="leading" secondItem="HYK-cG-NOj" secondAttribute="trailing" constant="12" id="i3z-wh-vsK"/>
                                                 <constraint firstAttribute="bottom" secondItem="HYK-cG-NOj" secondAttribute="bottom" constant="6" id="wJ6-a2-pUy"/>
@@ -1399,40 +1394,6 @@
                                             </constraints>
                                         </tableViewCellContentView>
                                     </tableViewCell>
-                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="113" id="8mT-v2-Qx8">
-                                        <rect key="frame" x="0.0" y="641" width="375" height="113"/>
-                                        <autoresizingMask key="autoresizingMask"/>
-                                        <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="8mT-v2-Qx8" id="87S-Cm-kNa">
-                                            <rect key="frame" x="0.0" y="0.0" width="375" height="113"/>
-                                            <autoresizingMask key="autoresizingMask"/>
-                                            <subviews>
-                                                <button hidden="YES" opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ceC-iW-RJC">
-                                                    <rect key="frame" x="10" y="30" width="343" height="40"/>
-                                                    <color key="backgroundColor" red="0.98431372549999996" green="0.2784313725" blue="0.2784313725" alpha="1" colorSpace="calibratedRGB"/>
-                                                    <constraints>
-                                                        <constraint firstAttribute="height" constant="40" id="hsv-E0-CNX"/>
-                                                    </constraints>
-                                                    <state key="normal" title="删   除">
-                                                        <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
-                                                    </state>
-                                                    <userDefinedRuntimeAttributes>
-                                                        <userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
-                                                            <real key="value" value="5"/>
-                                                        </userDefinedRuntimeAttribute>
-                                                    </userDefinedRuntimeAttributes>
-                                                    <connections>
-                                                        <action selector="deleteBtnTap:" destination="6hI-Zf-Dol" eventType="touchUpInside" id="Xes-eC-VfF"/>
-                                                        <action selector="tapDeleteBtn:" destination="Vxk-45-vB1" eventType="touchUpInside" id="IFP-6l-1ZD"/>
-                                                    </connections>
-                                                </button>
-                                            </subviews>
-                                            <constraints>
-                                                <constraint firstAttribute="trailing" secondItem="ceC-iW-RJC" secondAttribute="trailing" constant="16" id="Hrn-eU-isu"/>
-                                                <constraint firstItem="ceC-iW-RJC" firstAttribute="leading" secondItem="87S-Cm-kNa" secondAttribute="leading" constant="16" id="clh-qU-E5h"/>
-                                                <constraint firstItem="ceC-iW-RJC" firstAttribute="centerY" secondItem="87S-Cm-kNa" secondAttribute="centerY" id="eix-O3-ypw"/>
-                                            </constraints>
-                                        </tableViewCellContentView>
-                                    </tableViewCell>
                                 </cells>
                             </tableViewSection>
                         </sections>
@@ -1443,7 +1404,6 @@
                     </tableView>
                     <connections>
                         <outlet property="calendarColorStackView" destination="sc4-ds-up5" id="M83-qQ-5Dg"/>
-                        <outlet property="calendarDeleteBtn" destination="ceC-iW-RJC" id="hSl-e9-yib"/>
                         <outlet property="calendarIsOpenBtn" destination="GjW-u2-fg5" id="qt3-Vs-Ni9"/>
                         <outlet property="calendarIsOpenSwitch" destination="fl8-ZU-9Cp" id="B0z-eA-yDa"/>
                         <outlet property="calendarManagerField" destination="bIB-vr-JmD" id="vj2-pN-AIk"/>

+ 18 - 1
o2ios/O2Platform/App/IM-聊天/IMChatViewController.swift

@@ -620,10 +620,27 @@ extension IMChatViewController: IMChatMessageDelegate {
     }
     
     func openWork(workId: String) {
+        self.showLoading()
+        self.viewModel.isWorkCompleted(work: workId).always {
+            self.hideLoading()
+        }.then{ result in
+            if result {
+                self.showMessage(msg: "工作已经完成了!")
+            }else {
+                self.openWorkPage(work: workId)
+            }
+        }.catch {_ in
+            self.showMessage(msg: "工作已经完成了!")
+        }
+        
+        
+    }
+    
+    private func openWorkPage(work: String) {
         let storyBoard = UIStoryboard(name: "task", bundle: nil)
         let destVC = storyBoard.instantiateViewController(withIdentifier: "todoTaskDetailVC") as! TodoTaskDetailViewController
         let json = """
-        {"work":"\(workId)", "workCompleted":"", "title":""}
+        {"work":"\(work)", "workCompleted":"", "title":""}
         """
         let todo = TodoTask(JSONString: json)
         destVC.todoTask = todo

+ 18 - 2
o2ios/O2Platform/App/IM-聊天/IMInstantMessageViewController.swift

@@ -106,10 +106,27 @@ extension IMInstantMessageViewController : IMChatMessageDelegate {
     }
     
     func openWork(workId: String) {
+        self.showLoading()
+        self.viewModel.isWorkCompleted(work: workId).always {
+            self.hideLoading()
+        }.then{ result in
+            if result {
+                self.showMessage(msg: "工作已经完成了!")
+            }else {
+                self.openWorkPage(work: workId)
+            }
+        }.catch {_ in
+            self.showMessage(msg: "工作已经完成了!")
+        }
+        
+        
+    }
+    
+    private func openWorkPage(work: String) {
         let storyBoard = UIStoryboard(name: "task", bundle: nil)
         let destVC = storyBoard.instantiateViewController(withIdentifier: "todoTaskDetailVC") as! TodoTaskDetailViewController
         let json = """
-        {"work":"\(workId)", "workCompleted":"", "title":""}
+        {"work":"\(work)", "workCompleted":"", "title":""}
         """
         let todo = TodoTask(JSONString: json)
         destVC.todoTask = todo
@@ -117,5 +134,4 @@ extension IMInstantMessageViewController : IMChatMessageDelegate {
         self.show(destVC, sender: nil)
     }
     
-    
 }

+ 15 - 0
o2ios/O2Platform/App/IM-聊天/IMViewModel.swift

@@ -16,6 +16,7 @@ class IMViewModel: NSObject {
 
 
     private let communicateAPI = OOMoyaProvider<CommunicateAPI>()
+    private let workAPI = OOMoyaProvider<OOWorkAPI>()
 }
 
 extension IMViewModel {
@@ -206,4 +207,18 @@ extension IMViewModel {
                 })
         }
     }
+    
+    ///判断是否工作已经完成
+    func isWorkCompleted(work: String) -> Promise<Bool> {
+        return Promise { fulfill, reject in
+            self.workAPI.request(.getWork(work), completion: {result in
+                let response = OOResult<BaseModelClass<WorkInfoResData>>(result)
+                if response.isResultSuccess() {
+                    fulfill(false)
+                }else {
+                    fulfill(true)
+                }
+            })
+        }
+    }
 }

+ 2 - 4
o2ios/O2Platform/App/NewAttance-考勤打卡/c/OOAttanceTotalController.swift

@@ -58,6 +58,8 @@ class OOAttanceTotalController: UITableViewController {
         viewModel.getCheckinCycle(self.year, self.month).then { (cycleDetail) -> Promise<(OOAttandanceAnalyze,[OOAttandanceCheckinTotal])> in
                 self.headerView.requestBean = cycleDetail
                return all(self.viewModel.getCheckinAnalyze(cycleDetail), self.viewModel.getCheckinTotal(cycleDetail))
+            }.always {
+                self.hideLoading()
             }.then { (result) in
                 self.headerView.config(withItem: result.0)
                 let list = result.1
@@ -72,14 +74,10 @@ class OOAttanceTotalController: UITableViewController {
                     }
                     self.models.append(contentsOf: newList)
                 }
-                
-            }.always {
-                self.hideLoading()
                 self.tableView.reloadData()
                 if self.tableView.mj_header.isRefreshing() {
                     self.tableView.mj_header.endRefreshing()
                 }
-                
             }.catch { (myError) in
                 let customError = myError as? OOAppError
                 self.showError(title: (customError?.failureReason)!)

+ 1 - 1
o2ios/O2Platform/App/VoiceAI-语音处理/ViewModel/OOAIViewModel.swift

@@ -229,7 +229,7 @@ extension OOAIViewModel {
         let param:[String:String] = ["routeName":routeName, "opinion":""]
         let task = self.taskList[self.currentDealTaskIndex]
         self.taskAPI.request(.taskSaveAndSubmit(task.id!, (param as Dictionary<String, AnyObject>)), completion: { result in
-            let response = OOResult<BaseModelClass<[O2TodoTaskNeural]>>(result)
+            let response = OOResult<BaseModelClass<O2WorkPostResult>>(result)
             if response.isResultSuccess() {
                 self.currentDealTaskIndex = self.currentDealTaskIndex + 1
                 self.speakMessage = "工作处理完成!"

+ 1 - 1
o2ios/O2Platform/App/Work-工作/c/TodoTaskViewController.swift

@@ -324,7 +324,7 @@ class TodoTaskViewController: UITableViewController {
     
     //加载下一页数据
     func loadDataNextByURL() {
-        DDLogDebug("下一页数据   loadDataNextByURL")
+        
         let tv = self.tableView as! ZLBaseTableView
         tv.emptyTitle = self.emptyTexts[AppConfigSettings.shared.taskIndex]
         let todoTask = self.models.last?.sourceObj!

+ 10 - 7
o2ios/O2Platform/App/Work-工作/c/TodoedTaskViewController.swift

@@ -33,7 +33,11 @@ class TodoedTaskViewController: UITableViewController {
     
     override func viewDidLoad() {
         super.viewDidLoad()
-        title = self.todoTask?.title
+        var t = self.todoTask?.title
+        if t == nil || t?.isEmpty == true {
+            t = self.todoTask?.processName ?? ""
+        }
+        title = t
         self.loadTodoedData()
     }
     
@@ -68,14 +72,14 @@ class TodoedTaskViewController: UITableViewController {
         if actionArray != nil {
         for action  in actionArray! {
             if completed {
-                let title = "\(action["title"].stringValue)于\(action["completedTime"].stringValue) 已完成"
+                let title = "\(action["title"].stringValue) 完成于\(action["completedTime"].stringValue)"
                 let id = action["id"].stringValue
                 let workType = "workCompletedList"
                 let actionModel = TodoedActionModel(destText: title, workType: workType, workId: id)
                 self.todoedActions.append(actionModel)
             }else{
 //                %@于%@ 停留在%@",item[@"title"],item[@"updateTime"],item[@"activityName"]
-                let title = "\(action["title"].stringValue)于\(action["updateTime"].stringValue) 停留在\(action["activityName"].stringValue)"
+                let title = "\(action["title"].stringValue) 当前在\(action["activityName"].stringValue)"
                 let id = action["id"].stringValue
                 let workType = "workList"
                 let actionModel = TodoedActionModel(destText: title, workType: workType, workId: id)
@@ -92,7 +96,7 @@ class TodoedTaskViewController: UITableViewController {
             if task.fromActivityType == "begin" {
                 continue
             }
-            let activity = task.arrivedActivityName == nil ? task.fromActivityName:task.arrivedActivityName
+            let activity = task.arrivedActivityName == nil ? task.fromActivityName : "\(task.fromActivityName ?? "") -> \(task.arrivedActivityName ?? "")"
             var identity = ""
             if (task.taskCompletedList ==  nil || task.taskCompletedList!.count == 0) {
                 if (task.taskList!.count > 0 ) {
@@ -103,9 +107,8 @@ class TodoedTaskViewController: UITableViewController {
             }else{
                 identity = (task.taskCompletedList![0] as! NSDictionary)["identity"]! as! String;
             }
-            let status = task.routeName == nil ? "当前节点":task.routeName
-//            text22 = task.arrivedTime == nil ? task.fromTime : task.arrivedTime;
-            let time = task.arrivedTime == nil ? task.fromTime : task.arrivedTime
+            let status = task.routeName == nil ? "正在处理":"选择了【\(task.routeName ?? "")】"
+            let time = task.arrivedTime == nil ? "到达于:\(task.fromTime ?? "")" : "提交于:\(task.arrivedTime ?? "")"
             identity = identity.components(separatedBy: "@").first ?? ""
             let statusModel = TodoedStatusModel(activity: activity, identity: identity, status: status, statusTime: time)
             self.todoedStatus.append(statusModel)

+ 31 - 8
o2ios/O2Platform/App/Work-工作/m/OOTaskModels.swift

@@ -57,21 +57,44 @@ class O2TodoTask:NSObject,DataModel {
 }
 
 class  O2TodoTaskNeural:NSObject,DataModel {
-    var routeName: String?
+    @objc var routeName: String?
     //var workLogList
     required override init() {
         
     }
 }
 
+class O2WorkPostResult:NSObject,DataModel {
+    required override init() {
+           
+       }
+    
+    @objc var id: String?
+    @objc var application: String?
+    @objc var process: String?
+    @objc var job: String?
+    @objc var work: String?
+    var completed: Bool?
+    @objc var fromActivity: String?
+    @objc var fromActivityType: String?
+    @objc var fromActivityName: String?
+    @objc var fromActivityAlias: String?
+    @objc var fromActivityToken: String?
+    @objc var recordTime: String?
+    @objc var person: String?
+    @objc var identity: String?
+    @objc var unit: String?
+    @objc var type: String?
+}
+
 class O2AppProcess: NSObject,DataModel {
-    var id:String?
-    var name:String?
-    var alias:String?
-    var desc:String?
-    var creatorPerson:String?
-    var application:String?
-    var icon:String?
+    @objc var id:String?
+    @objc var name:String?
+    @objc var alias:String?
+    @objc var desc:String?
+    @objc var creatorPerson:String?
+    @objc var application:String?
+    @objc var icon:String?
     
     func mapping(mapper: HelpingMapper) {
         mapper <<<

+ 56 - 0
o2ios/O2Platform/App/Work-工作/m/WorkInfoResData.swift

@@ -0,0 +1,56 @@
+//
+//  WorkInfoResData.swift
+//  O2Platform
+//
+//  Created by FancyLou on 2020/9/4.
+//  Copyright © 2020 zoneland. All rights reserved.
+//
+
+import Foundation
+import HandyJSON
+
+class WorkInfoRes: NSObject,DataModel {
+    @objc var id: String?
+    @objc var job: String?
+    @objc var title: String?
+    @objc var startTime: String?
+    @objc var startTimeMonth: String?
+    @objc var creatorPerson: String?
+    @objc var creatorIdentity: String?
+    @objc var creatorUnit: String?
+    @objc var application: String?
+    @objc var applicationName: String?
+    @objc var applicationAlias: String?
+    @objc var process: String?
+    @objc var processName: String?
+    @objc var processAlias: String?
+    @objc var activity: String?
+    @objc var activityType: String?
+    @objc var activityName: String?
+    @objc var activityAlias: String?
+    @objc var activityDescription: String?
+    @objc var activityToken: String?
+    @objc var activityArrivedTime: String?
+    @objc var serial: String?
+    @objc var workCreateType: String?
+    @objc var workStatus: String?
+    @objc var manualTaskIdentityText: String?
+    @objc var form: String?
+    @objc var destinationRoute: String?
+    @objc var destinationRouteName: String?
+    @objc var destinationActivityType: String?
+    @objc var destinationActivity: String?
+    
+    
+    required override init() {
+        
+    }
+}
+
+class WorkInfoResData: NSObject,DataModel {
+    @objc var work: WorkInfoRes?
+    
+    required override init() {
+        
+    }
+}

+ 19 - 21
o2ios/O2Platform/App/Work-工作/task.storyboard

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Yna-g7-LKp">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Yna-g7-LKp">
     <device id="retina5_5" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
@@ -336,29 +336,29 @@
                                                 <constraint firstAttribute="width" constant="16" id="0v3-hp-qId"/>
                                             </constraints>
                                         </imageView>
-                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="申请人" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ZKr-jv-hUf">
-                                            <rect key="frame" x="59" y="25.000000000000004" width="107" height="15.666666666666668"/>
+                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="申请人 -&gt; 核稿" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ZKr-jv-hUf">
+                                            <rect key="frame" x="59" y="25.000000000000004" width="164" height="15.666666666666668"/>
                                             <constraints>
-                                                <constraint firstAttribute="width" constant="107" id="0av-YQ-zfd"/>
+                                                <constraint firstAttribute="width" constant="164" id="0av-YQ-zfd"/>
                                             </constraints>
                                             <fontDescription key="fontDescription" name="PingFangSC-Regular" family="PingFang SC" pointSize="13"/>
                                             <color key="textColor" red="0.60784313725490191" green="0.60784313725490191" blue="0.60784313725490191" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                             <nil key="highlightedColor"/>
                                         </label>
-                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="时间" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cyz-8F-h4h">
-                                            <rect key="frame" x="199" y="53" width="168" height="21"/>
+                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="提交于:2020-09-08 12:23:00" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cyz-8F-h4h">
+                                            <rect key="frame" x="223" y="72" width="171" height="17"/>
                                             <fontDescription key="fontDescription" name="PingFangSC-Thin" family="PingFang SC" pointSize="12"/>
                                             <color key="textColor" red="0.60784313725490191" green="0.60784313725490191" blue="0.60784313725490191" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                             <nil key="highlightedColor"/>
                                         </label>
-                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="申请人身份" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1sJ-rz-ZFy">
-                                            <rect key="frame" x="174" y="26.000000000000004" width="220" height="15.666666666666668"/>
+                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="申请人身份" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1sJ-rz-ZFy">
+                                            <rect key="frame" x="231" y="22.666666666666671" width="171" height="21"/>
                                             <fontDescription key="fontDescription" name="PingFangSC-Regular" family="PingFang SC" pointSize="15"/>
                                             <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
                                             <nil key="highlightedColor"/>
                                         </label>
-                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="状态" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zrc-o6-jGO">
-                                            <rect key="frame" x="99" y="51" width="161" height="21"/>
+                                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="选择了【送核稿】" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zrc-o6-jGO">
+                                            <rect key="frame" x="59" y="51" width="161" height="21"/>
                                             <constraints>
                                                 <constraint firstAttribute="height" constant="21" id="ddj-1s-uER"/>
                                                 <constraint firstAttribute="width" constant="161" id="iCD-8x-Mpe"/>
@@ -369,24 +369,22 @@
                                         </label>
                                     </subviews>
                                     <constraints>
-                                        <constraint firstItem="1sJ-rz-ZFy" firstAttribute="leading" secondItem="zrc-o6-jGO" secondAttribute="leading" constant="75" id="0Vm-Ud-9rI"/>
-                                        <constraint firstItem="1sJ-rz-ZFy" firstAttribute="trailing" secondItem="FMH-Ke-HJw" secondAttribute="trailingMargin" id="1GM-sD-cLK"/>
+                                        <constraint firstItem="1sJ-rz-ZFy" firstAttribute="trailing" secondItem="FMH-Ke-HJw" secondAttribute="trailingMargin" constant="8" id="1GM-sD-cLK"/>
                                         <constraint firstItem="3Ey-5T-s87" firstAttribute="top" secondItem="FMH-Ke-HJw" secondAttribute="topMargin" constant="14" id="AK1-r8-HqO"/>
                                         <constraint firstAttribute="bottomMargin" secondItem="zrc-o6-jGO" secondAttribute="bottom" constant="17" id="AwQ-o8-1Zc"/>
-                                        <constraint firstAttribute="bottomMargin" secondItem="cyz-8F-h4h" secondAttribute="bottom" constant="17" id="CF9-SN-4fk"/>
+                                        <constraint firstAttribute="bottomMargin" secondItem="cyz-8F-h4h" secondAttribute="bottom" id="CF9-SN-4fk"/>
                                         <constraint firstItem="0rT-J1-BMA" firstAttribute="centerY" secondItem="FMH-Ke-HJw" secondAttribute="centerY" id="DRR-FG-H8r"/>
-                                        <constraint firstItem="1sJ-rz-ZFy" firstAttribute="trailing" secondItem="cyz-8F-h4h" secondAttribute="trailing" id="K0c-Vr-SNO"/>
                                         <constraint firstItem="ZKr-jv-hUf" firstAttribute="leading" secondItem="0rT-J1-BMA" secondAttribute="trailing" constant="21" id="ObS-pD-6ss"/>
                                         <constraint firstItem="1sJ-rz-ZFy" firstAttribute="leading" secondItem="ZKr-jv-hUf" secondAttribute="trailing" constant="8" symbolic="YES" id="PWz-BD-CUF"/>
-                                        <constraint firstItem="1sJ-rz-ZFy" firstAttribute="top" secondItem="FMH-Ke-HJw" secondAttribute="topMargin" constant="15" id="QoB-yM-tFZ"/>
-                                        <constraint firstItem="zrc-o6-jGO" firstAttribute="firstBaseline" secondItem="cyz-8F-h4h" secondAttribute="firstBaseline" constant="1.5" id="XCu-Hm-W7n"/>
+                                        <constraint firstAttribute="trailingMargin" secondItem="cyz-8F-h4h" secondAttribute="trailing" id="Wb8-kc-NPI"/>
+                                        <constraint firstItem="1sJ-rz-ZFy" firstAttribute="firstBaseline" secondItem="ZKr-jv-hUf" secondAttribute="firstBaseline" id="YGd-os-HK4"/>
                                         <constraint firstItem="0rT-J1-BMA" firstAttribute="centerX" secondItem="3Ey-5T-s87" secondAttribute="centerX" id="b5E-eH-NRs"/>
                                         <constraint firstItem="ZKr-jv-hUf" firstAttribute="leading" secondItem="3Ey-5T-s87" secondAttribute="trailing" constant="14" id="bnX-bd-Hkh"/>
                                         <constraint firstAttribute="bottomMargin" secondItem="3Ey-5T-s87" secondAttribute="bottom" constant="53.5" id="eF4-lo-6NE"/>
-                                        <constraint firstItem="cyz-8F-h4h" firstAttribute="top" secondItem="1sJ-rz-ZFy" secondAttribute="bottom" constant="9" id="f1m-rs-12u"/>
                                         <constraint firstItem="cyz-8F-h4h" firstAttribute="leading" secondItem="zrc-o6-jGO" secondAttribute="trailing" constant="32" id="jff-ld-CUs"/>
                                         <constraint firstAttribute="bottomMargin" secondItem="ZKr-jv-hUf" secondAttribute="bottom" constant="48.5" id="mkv-jE-3PC"/>
                                         <constraint firstItem="3Ey-5T-s87" firstAttribute="top" secondItem="ZKr-jv-hUf" secondAttribute="top" id="n7P-R7-wjy"/>
+                                        <constraint firstItem="zrc-o6-jGO" firstAttribute="leading" secondItem="ZKr-jv-hUf" secondAttribute="leading" id="tao-3Q-I1s"/>
                                         <constraint firstItem="0rT-J1-BMA" firstAttribute="top" secondItem="FMH-Ke-HJw" secondAttribute="top" id="u6Z-ov-7cF"/>
                                         <constraint firstItem="3Ey-5T-s87" firstAttribute="leading" secondItem="FMH-Ke-HJw" secondAttribute="leadingMargin" constant="9" id="z0H-ba-Zj0"/>
                                     </constraints>
@@ -416,7 +414,7 @@
                 </tableViewController>
                 <placeholder placeholderIdentifier="IBFirstResponder" id="7Nu-6l-TKM" userLabel="First Responder" sceneMemberID="firstResponder"/>
             </objects>
-            <point key="canvasLocation" x="732" y="1859.8200899550227"/>
+            <point key="canvasLocation" x="730.43478260869574" y="1859.5108695652175"/>
         </scene>
         <!--工作任务列表界面-->
         <scene sceneID="8MY-nj-psY">
@@ -435,7 +433,7 @@
                                     <autoresizingMask key="autoresizingMask"/>
                                     <subviews>
                                         <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="我的报销单" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cNB-vb-QcB">
-                                            <rect key="frame" x="60" y="8.0000000000000018" width="334" height="19.666666666666671"/>
+                                            <rect key="frame" x="60" y="8" width="334" height="20"/>
                                             <fontDescription key="fontDescription" name="PingFangSC-Regular" family="PingFang SC" pointSize="14"/>
                                             <color key="textColor" red="0.070588235294117646" green="0.070588235294117646" blue="0.070588235294117646" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                             <nil key="highlightedColor"/>
@@ -448,7 +446,7 @@
                                             </constraints>
                                         </imageView>
                                         <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="2015.10.10 10:27" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4Sc-3D-x8m">
-                                            <rect key="frame" x="293.33333333333331" y="41" width="88.666666666666686" height="17"/>
+                                            <rect key="frame" x="293" y="41" width="89" height="17"/>
                                             <fontDescription key="fontDescription" name="PingFangSC-Thin" family="PingFang SC" pointSize="12"/>
                                             <color key="textColor" red="0.73333333333333328" green="0.80784313725490198" blue="0.82745098039215681" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                             <nil key="highlightedColor"/>
@@ -661,7 +659,7 @@
         </scene>
     </scenes>
     <inferredMetricsTieBreakers>
-        <segue reference="QJV-sf-Oi5"/>
+        <segue reference="9TQ-Eb-h06"/>
     </inferredMetricsTieBreakers>
     <resources>
         <image name="icon_daiban" width="38" height="38"/>

+ 17 - 13
o2ios/O2Platform/App/meeting-会议/Controller/OOMeetingInforController.swift

@@ -78,19 +78,21 @@ class OOMeetingInforController: UIViewController {
 
     func loadData() {
         self.showLoading()
-        all(viewModel.getMeetingsByYearAndMonth(Date()), viewModel.getMeetingByTheDay(Date())).then { (result) in
+        all(viewModel.getMeetingsByYearAndMonth(Date()), viewModel.getMeetingByTheDay(Date()))
+            .always {
+                self.hideLoading()
+                if self.tableView.mj_header.isRefreshing() {
+                    self.tableView.mj_header.endRefreshing()
+                }
+                //self.tableView.reloadData()
+            }
+        .then { (result) in
             DispatchQueue.main.async {
                 self.headerView.eventsByDate = result.0 as? [String: [OOMeetingInfo]]
                 self.viewModel.theMeetingsByDay.removeAll()
                 self.viewModel.theMeetingsByDay.append(contentsOf: result.1)
                 self.tableView.reloadData()
             }
-        }.always {
-            self.hideLoading()
-            if self.tableView.mj_header.isRefreshing() {
-                self.tableView.mj_header.endRefreshing()
-            }
-            //self.tableView.reloadData()
         }.catch { (myerror) in
             let customError = myerror as! OOAppError
             self.showError(title: customError.failureReason ?? "")
@@ -100,10 +102,12 @@ class OOMeetingInforController: UIViewController {
 
     func loadCurrentMonthCalendar(_ theDate: Date?) {
         self.showLoading()
-        viewModel.getMeetingsByYearAndMonth(theDate ?? Date()).then { (resultDict) in
+        viewModel.getMeetingsByYearAndMonth(theDate ?? Date())
+            .always {
+                self.hideLoading()
+            }
+        .then { (resultDict) in
             self.headerView.eventsByDate = resultDict as? [String: [OOMeetingInfo]]
-        }.always {
-            self.hideLoading()
         }.catch { (myerror) in
             let customError = myerror as! OOAppError
             self.showError(title:  customError.failureReason ?? "")
@@ -112,12 +116,12 @@ class OOMeetingInforController: UIViewController {
 
     func loadtheDayMeetingInfo(_ theDate: Date?) {
         self.showLoading()
-        viewModel.getMeetingByTheDay(theDate ?? Date()).then { (infos) in
+        viewModel.getMeetingByTheDay(theDate ?? Date()).always {
+            self.hideLoading()
+        }.then { (infos) in
             self.viewModel.theMeetingsByDay.removeAll()
             self.viewModel.theMeetingsByDay.append(contentsOf: infos)
             self.tableView.reloadData()
-        }.always {
-            self.hideLoading()
         }.catch { (myerror) in
             let customError = myerror as! OOAppError
             self.showError(title:  customError.failureReason ?? "")

+ 12 - 2
o2ios/O2Platform/Framework/O2API/TaskAPI/OOWorkAPI.swift

@@ -13,6 +13,7 @@ import O2OA_Auth_SDK
 // MARK:- 所有调用的API枚举
 enum OOWorkAPI {
     case createTask(String,Dictionary<String,String>)
+    case getWork(String)//获取工作对象 如果工作已经结束了 500错误
 }
 
 // MARK:- 上下文实现
@@ -41,11 +42,18 @@ extension OOWorkAPI:TargetType {
         switch self {
         case .createTask(let processId,_):
             return "/jaxrs/work/process/\(processId)"
+        case .getWork(let workId):
+            return "/jaxrs/work/\(workId)"
         }
     }
     
     var method: Moya.Method {
-        return .post
+        switch self {
+            case .createTask(_,_):
+                return .post
+            case .getWork(_):
+                return .get
+        }
     }
     
     var sampleData: Data {
@@ -56,7 +64,9 @@ extension OOWorkAPI:TargetType {
         switch self {
         case .createTask(_,let param):
             return .requestParameters(parameters: param, encoding: JSONEncoding.default)
-        }
+        default:
+           return .requestPlain
+       }
     }
     
     var headers: [String : String]? {

+ 2 - 2
o2ios/O2Platform/Info.plist

@@ -23,7 +23,7 @@
 	<key>CFBundlePackageType</key>
 	<string>APPL</string>
 	<key>CFBundleShortVersionString</key>
-	<string>5.1.6</string>
+	<string>5.1.8</string>
 	<key>CFBundleURLTypes</key>
 	<array>
 		<dict>
@@ -38,7 +38,7 @@
 		</dict>
 	</array>
 	<key>CFBundleVersion</key>
-	<string>76</string>
+	<string>78</string>
 	<key>LSRequiresIPhoneOS</key>
 	<true/>
 	<key>NSAppTransportSecurity</key>

+ 28 - 0
o2server/configSample/cache.json

@@ -0,0 +1,28 @@
+{
+  "type": "ehcache",
+  "redis": {
+    "host": "127.0.0.1",
+    "port": 6379.0,
+    "user": "",
+    "password": "",
+    "connectionTimeout": 3000.0,
+    "socketTimeout": 3000.0,
+    "sslEnable": false,
+    "index": 0.0,
+    "###host": "redis服务器地址###",
+    "###port": "redis服务器端口###",
+    "###user": "认证用户###",
+    "###password": "认证口令###",
+    "###connectionTimeout": "连接等待时间###",
+    "###socketTimeout": "response返回等待时间###",
+    "###sslEnable": "是否启用ssl###",
+    "###index": "数据库编号###"
+  },
+  "ehcache": {
+    "jmxEnable": false,
+    "###jmxEnable": "是否启用jmx###"
+  },
+  "###type": "缓存类型:ehcache,type###",
+  "###redis": "redis配置###",
+  "###ehcache": "ehcache配置###"
+}

+ 2 - 2
o2server/configSample/components.json

@@ -81,8 +81,8 @@
       "dentyList": []
     },
     {
-      "name": "AppMarket",
-      "path": "AppMarket",
+      "name": "AppMarketV2",
+      "path": "AppMarketV2",
       "title": "应用市场",
       "iconPath": "appicon.png",
       "orderNumber": 8.0,

+ 3 - 1
o2server/configSample/node_127.0.0.1.json

@@ -78,7 +78,9 @@
     "###dirAllowed": "允许浏览目录,###",
     "###statEnable": "启用统计,默认启用统计.###",
     "###statExclusions": "统计忽略路径,默认忽略*.gif,*.jpg,*.png,*.ico###",
-    "###cacheControlMaxAge": "服务器max-age缓存时间(秒)###"
+    "###cacheControlMaxAge": "服务器max-age缓存时间(秒)###",
+    "###proxyCenterEnable": "是否启用center服务器代理.###",
+    "###proxyApplicationEnable": "是否启用application服务器代理###"
   },
   "data": {
     "enable": true,

+ 3 - 1
o2server/configSample/processPlatform.json

@@ -56,6 +56,7 @@
     "###cron": "定时cron表达式###",
     "###thresholdMinutes": "当工作滞留设定时间后,将尝试触发工作流转,可以自动处理由于人员变动的引起的工作滞留,默认24*60分钟.###"
   },
+  "processingSignalPersistEnable": false,
   "###maintenanceIdentity": "维护身份,当工作发生意外错误,无法找到对应的处理人情况下,先尝试将工作分配给创建身份,如果创建身份也不可获取,那么分配给指定人员,默认情况下这个值为空.###",
   "###formVersionCount": "表单历史版本保留数量,0为不保留.###",
   "###processVersionCount": "流程历史版本保留数量,0为不保留.###",
@@ -92,5 +93,6 @@
     "###intervalMinutes": "提醒间隔(分钟)###",
     "###count": "提醒数量限制.###"
   },
-  "###extensionEvents": "事件扩充.###"
+  "###extensionEvents": "事件扩充.###",
+  "###processingSignalPersistEnable": "是否保存工作处理信号内容,默认false.###"
 }

+ 1 - 1
o2server/console_aix.sh

@@ -1 +1 @@
-$(cd "$(dirname "$0")"; pwd)/jvm/aix/bin/java -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.Shadow
+$(cd "$(dirname "$0")"; pwd)/jvm/aix/bin/java -javaagent:$(cd "$(dirname "$0")"; pwd)/console.jar -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.Shadow

+ 1 - 1
o2server/console_arm.sh

@@ -1 +1 @@
-$(cd "$(dirname "$0")"; pwd)/jvm/arm/bin/java -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.Shadow
+$(cd "$(dirname "$0")"; pwd)/jvm/arm/bin/java -javaagent:$(cd "$(dirname "$0")"; pwd)/console.jar -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.Shadow

+ 1 - 1
o2server/console_linux.sh

@@ -1 +1 @@
-$(cd "$(dirname "$0")"; pwd)/jvm/linux/bin/java -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.Shadow
+$(cd "$(dirname "$0")"; pwd)/jvm/linux/bin/java -javaagent:$(cd "$(dirname "$0")"; pwd)/console.jar -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.Shadow

+ 1 - 1
o2server/console_macos.sh

@@ -1 +1 @@
-sudo $(cd "$(dirname "$0")"; pwd)/jvm/macos/bin/java -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.swapcommand.Exit
+sudo $(cd "$(dirname "$0")"; pwd)/jvm/macos/bin/java -javaagent:$(cd "$(dirname "$0")"; pwd)/console.jar -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.swapcommand.Exit

+ 1 - 1
o2server/console_raspberrypi.sh

@@ -1 +1 @@
-sudo $(cd "$(dirname "$0")"; pwd)/jvm/raspberrypi/bin/java -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.Shadow
+sudo $(cd "$(dirname "$0")"; pwd)/jvm/raspberrypi/bin/java -javaagent:$(cd "$(dirname "$0")"; pwd)/console.jar -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.Shadow

+ 1 - 1
o2server/console_risc.sh

@@ -1 +1 @@
-$(cd "$(dirname "$0")"; pwd)/jvm/risc/bin/java -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.Shadow
+$(cd "$(dirname "$0")"; pwd)/jvm/risc/bin/java -javaagent:$(cd "$(dirname "$0")"; pwd)/console.jar -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.Shadow

+ 1 - 1
o2server/console_windows.bat

@@ -1 +1 @@
-"%~dp0jvm\windows\bin\java" -cp "%~dp0console.jar" com.x.server.console.Shadow
+"%~dp0jvm\windows\bin\java" -javaagent:"%~dp0console.jar" -cp "%~dp0console.jar" com.x.server.console.Shadow

+ 27 - 0
o2server/pom.xml

@@ -128,6 +128,10 @@
 			<groupId>org.apache.commons</groupId>
 			<artifactId>commons-text</artifactId>
 		</dependency>
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-compress</artifactId>
+		</dependency>
 		<dependency>
 			<groupId>org.eclipse.jetty</groupId>
 			<artifactId>jetty-server</artifactId>
@@ -144,6 +148,10 @@
 			<groupId>org.eclipse.jetty</groupId>
 			<artifactId>jetty-quickstart</artifactId>
 		</dependency>
+		<dependency>
+			<groupId>org.eclipse.jetty</groupId>
+			<artifactId>jetty-proxy</artifactId>
+		</dependency>
 		<dependency>
 			<groupId>com.google.zxing</groupId>
 			<artifactId>core</artifactId>
@@ -338,6 +346,10 @@
 			<groupId>org.apache.commons</groupId>
 			<artifactId>commons-email</artifactId>
 		</dependency>
+		<dependency>
+			<groupId>javax.activation</groupId>
+			<artifactId>activation</artifactId>
+		</dependency>
 	</dependencies>
 
 	<build>
@@ -521,6 +533,11 @@
 				<artifactId>maven-model</artifactId>
 				<version>3.0</version>
 			</dependency>
+			<dependency>
+				<groupId>org.apache.commons</groupId>
+				<artifactId>commons-compress</artifactId>
+				<version>1.20</version>
+			</dependency>
 			<dependency>
 				<groupId>org.eclipse.jetty</groupId>
 				<artifactId>jetty-server</artifactId>
@@ -541,6 +558,11 @@
 				<artifactId>jetty-quickstart</artifactId>
 				<version>9.4.20.v20190813</version>
 			</dependency>
+			<dependency>
+				<groupId>org.eclipse.jetty</groupId>
+				<artifactId>jetty-proxy</artifactId>
+				<version>9.4.31.v20200723</version>
+			</dependency>
 			<dependency>
 				<groupId>com.google.zxing</groupId>
 				<artifactId>core</artifactId>
@@ -837,6 +859,11 @@
 				<artifactId>commons-email</artifactId>
 				<version>1.5</version>
 			</dependency>
+			<dependency>
+				<groupId>javax.activation</groupId>
+				<artifactId>activation</artifactId>
+				<version>1.1.1</version>
+			</dependency>
 			<dependency>
 				<groupId>o2oa</groupId>
 				<artifactId>x_base_core_project</artifactId>

+ 1 - 1
o2server/start_aix.sh

@@ -148,4 +148,4 @@ if [ -d ${current_dir}/local/update ]; then
 		rm -Rf ${current_dir}/local/update
 	fi
 fi
-setsid ${current_dir}/jvm/aix/bin/java -server -Djava.awt.headless=true -Xms2g -Xmx8g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar
+setsid ${current_dir}/jvm/aix/bin/java -javaagent:${current_dir}/console.jar -server -Djava.awt.headless=true -Xms2g -Xmx8g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar

+ 1 - 1
o2server/start_aix_debug.sh

@@ -148,4 +148,4 @@ if [ -d ${current_dir}/local/update ]; then
 		rm -Rf ${current_dir}/local/update
 	fi
 fi
-setsid ${current_dir}/jvm/aix/bin/java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=20000 -Djava.awt.headless=true -Xms2g -Xmx8g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar
+setsid ${current_dir}/jvm/aix/bin/java -javaagent:${current_dir}/console.jar -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=20000 -Djava.awt.headless=true -Xms2g -Xmx8g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar

+ 1 - 1
o2server/start_arm.sh

@@ -148,4 +148,4 @@ if [ -d ${current_dir}/local/update ]; then
 		rm -Rf ${current_dir}/local/update
 	fi
 fi
-setsid ${current_dir}/jvm/arm/bin/java -server -Djava.awt.headless=true -Xms2g -Xmx8g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar
+setsid ${current_dir}/jvm/arm/bin/java -javaagent:${current_dir}/console.jar -server -Djava.awt.headless=true -Xms2g -Xmx4g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar

+ 1 - 1
o2server/start_arm_debug.sh

@@ -148,4 +148,4 @@ if [ -d ${current_dir}/local/update ]; then
 		rm -Rf ${current_dir}/local/update
 	fi
 fi
-setsid ${current_dir}/jvm/arm/bin/java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=20000 -Djava.awt.headless=true -Xms2g -Xmx8g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar
+setsid ${current_dir}/jvm/arm/bin/java -javaagent:${current_dir}/console.jar -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=20000 -Djava.awt.headless=true -Xms2g -Xmx4g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar

+ 1 - 1
o2server/start_linux.sh

@@ -148,4 +148,4 @@ if [ -d ${current_dir}/local/update ]; then
 		rm -Rf ${current_dir}/local/update
 	fi
 fi
-setsid ${current_dir}/jvm/linux/bin/java -server -Djava.awt.headless=true -Xms2g -Xmx5g -Duser.timezone=GMT+08 -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar
+setsid ${current_dir}/jvm/linux/bin/java -javaagent:${current_dir}/console.jar -server -Djava.awt.headless=true -Xms2g -Xmx4g -Duser.timezone=GMT+08 -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar

+ 1 - 1
o2server/start_linux_debug.sh

@@ -148,4 +148,4 @@ if [ -d ${current_dir}/local/update ]; then
 		rm -Rf ${current_dir}/local/update
 	fi
 fi
-setsid ${current_dir}/jvm/linux/bin/java -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.authenticate=false -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=20000 -Djava.awt.headless=true -Xms2g -Xmx8g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar
+setsid ${current_dir}/jvm/linux/bin/java -javaagent:${current_dir}/console.jar -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.authenticate=false -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=20000 -Djava.awt.headless=true -Xms2g -Xmx4g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar

+ 1 - 1
o2server/start_macos.sh

@@ -149,4 +149,4 @@ if [ -d ${current_dir}/local/update ]; then
 		rm -Rf ${current_dir}/local/update
 	fi
 fi
-sudo ${current_dir}/jvm/macos/bin/java -server -Djava.awt.headless=true -Xms2g -Xmx8g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar
+sudo ${current_dir}/jvm/macos/bin/java -javaagent:${current_dir}/console.jar -server -Djava.awt.headless=true -Xms2g -Xmx4g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar

+ 1 - 1
o2server/start_macos_debug.sh

@@ -149,4 +149,4 @@ if [ -d ${current_dir}/local/update ]; then
 		rm -Rf ${current_dir}/local/update
 	fi
 fi
-sudo ${current_dir}/jvm/macos/bin/java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=20000 -Djava.awt.headless=true -Xms2g -Xmx8g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar
+sudo ${current_dir}/jvm/macos/bin/java -javaagent:${current_dir}/console.jar -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=20000 -Djava.awt.headless=true -Xms2g -Xmx4g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar

+ 1 - 1
o2server/start_raspberrypi.sh

@@ -148,4 +148,4 @@ if [ -d ${current_dir}/local/update ]; then
 		sudo rm -Rf ${current_dir}/local/update
 	fi
 fi
-sudo setsid ${current_dir}/jvm/raspberrypi/bin/java -server -Xms1g -Xmx1g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar
+sudo setsid ${current_dir}/jvm/raspberrypi/bin/java -javaagent:${current_dir}/console.jar -server -Xms1g -Xmx2g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar

+ 1 - 1
o2server/start_raspberrypi_debug.sh

@@ -148,4 +148,4 @@ if [ -d ${current_dir}/local/update ]; then
 		sudo rm -Rf ${current_dir}/local/update
 	fi
 fi
-sudo setsid ${current_dir}/jvm/raspberrypi/bin/java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=20000 -Djava.awt.headless=true -Xms1g -Xmx1g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar
+sudo setsid ${current_dir}/jvm/raspberrypi/bin/java -javaagent:${current_dir}/console.jar -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=20000 -Djava.awt.headless=true -Xms1g -Xmx2g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar

+ 1 - 1
o2server/start_risc.sh

@@ -148,4 +148,4 @@ if [ -d ${current_dir}/local/update ]; then
 		rm -Rf ${current_dir}/local/update
 	fi
 fi
-setsid ${current_dir}/jvm/risc/bin/java -server -Djava.awt.headless=true -Xms2g -Xmx8g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar
+setsid ${current_dir}/jvm/risc/bin/java -javaagent:${current_dir}/console.jar -server -Djava.awt.headless=true -Xms2g -Xmx4g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar

+ 1 - 1
o2server/start_risc_debug.sh

@@ -148,4 +148,4 @@ if [ -d ${current_dir}/local/update ]; then
 		rm -Rf ${current_dir}/local/update
 	fi
 fi
-setsid ${current_dir}/jvm/risc/bin/java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=20000 -Djava.awt.headless=true -Xms2g -Xmx8g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar
+setsid ${current_dir}/jvm/risc/bin/java -javaagent:${current_dir}/console.jar -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=20000 -Djava.awt.headless=true -Xms2g -Xmx4g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar ${current_dir}/console.jar

+ 1 - 1
o2server/start_windows.bat

@@ -144,5 +144,5 @@ if exist "%~dp0local\update" (
 	)
 )
 @echo on
-"%~dp0jvm\windows\bin\java" -server -Xms2g -Xmx5g -Duser.timezone=GMT+08 -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar "%~dp0console.jar"
+"%~dp0jvm\windows\bin\java" -javaagent:"%~dp0console.jar" -server -Xms2g -Xmx4g -Duser.timezone=GMT+08 -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar "%~dp0console.jar"
 pause

+ 1 - 1
o2server/start_windows_debug.bat

@@ -144,5 +144,5 @@ if exist "%~dp0local\update" (
 	)
 )
 @echo on
-"%~dp0jvm\windows\bin\java" -server -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.authenticate=false -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=20000 -Xms2g -Xmx8g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar "%~dp0console.jar"
+"%~dp0jvm\windows\bin\java" -javaagent:"%~dp0console.jar" -server -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.authenticate=false -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=20000 -Xms2g -Xmx4g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -jar "%~dp0console.jar"
 pause

+ 1 - 1
o2server/stop_aix.sh

@@ -1 +1 @@
-$(cd "$(dirname "$0")"; pwd)/jvm/aix/bin/java -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.swapcommand.Exit
+$(cd "$(dirname "$0")"; pwd)/jvm/aix/bin/java -javaagent:$(cd "$(dirname "$0")"; pwd)/console.jar -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.swapcommand.Exit

+ 1 - 1
o2server/stop_arm.sh

@@ -1 +1 @@
-$(cd "$(dirname "$0")"; pwd)/jvm/arm/bin/java -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.swapcommand.Exit
+$(cd "$(dirname "$0")"; pwd)/jvm/arm/bin/java -javaagent:$(cd "$(dirname "$0")"; pwd)/console.jar -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.swapcommand.Exit

+ 1 - 1
o2server/stop_linux.sh

@@ -1 +1 @@
-$(cd "$(dirname "$0")"; pwd)/jvm/linux/bin/java -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.swapcommand.Exit
+$(cd "$(dirname "$0")"; pwd)/jvm/linux/bin/java -javaagent:$(cd "$(dirname "$0")"; pwd)/console.jar -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.swapcommand.Exit

+ 1 - 1
o2server/stop_macos.sh

@@ -1 +1 @@
-sudo $(cd "$(dirname "$0")"; pwd)/jvm/macos/bin/java -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.swapcommand.Exit
+sudo $(cd "$(dirname "$0")"; pwd)/jvm/macos/bin/java -javaagent:$(cd "$(dirname "$0")"; pwd)/console.jar -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.swapcommand.Exit

+ 1 - 1
o2server/stop_raspberrypi.sh

@@ -1 +1 @@
-sudo $(cd "$(dirname "$0")"; pwd)/jvm/raspberrypi/bin/java -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.swapcommand.Exit
+sudo $(cd "$(dirname "$0")"; pwd)/jvm/raspberrypi/bin/java -javaagent:$(cd "$(dirname "$0")"; pwd)/console.jar -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.swapcommand.Exit

+ 1 - 1
o2server/stop_risc.sh

@@ -1 +1 @@
-$(cd "$(dirname "$0")"; pwd)/jvm/risc/bin/java -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.swapcommand.Exit
+$(cd "$(dirname "$0")"; pwd)/jvm/risc/bin/java -javaagent:$(cd "$(dirname "$0")"; pwd)/console.jar -cp $(cd "$(dirname "$0")"; pwd)/console.jar com.x.server.console.swapcommand.Exit

+ 1 - 1
o2server/stop_windows.bat

@@ -1 +1 @@
-"%~dp0jvm\windows\bin\java" -cp "%~dp0console.jar" com.x.server.console.swapcommand.Exit
+"%~dp0jvm\windows\bin\java" -javaagent:"%~dp0console.jar" -cp "%~dp0console.jar" com.x.server.console.swapcommand.Exit

+ 258 - 26
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/AttendanceDetailFactory.java

@@ -3,6 +3,7 @@ package com.x.attendance.assemble.control.factory;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
+import java.util.stream.Collectors;
 
 import javax.persistence.EntityManager;
 import javax.persistence.Query;
@@ -284,8 +285,10 @@ public class AttendanceDetailFactory extends AbstractFactory {
 			}
 		}
 		
-		cq.distinct(true).select( root.get( AttendanceDetail_.empName ) );
-		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList();
+		/*cq.distinct(true).select( root.get( AttendanceDetail_.empName ) );
+		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList();*/
+		cq.select( root.get( AttendanceDetail_.empName ) ).where(p);
+		return em.createQuery(cq).setMaxResults(20000).getResultList().stream().distinct().collect(Collectors.toList());
 	}
 	
 	/**
@@ -344,8 +347,8 @@ public class AttendanceDetailFactory extends AbstractFactory {
 			}
 		}
 		
-		cq.distinct(true).select( root.get( AttendanceDetail_.empName ) );
-		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList();
+		cq.select( root.get( AttendanceDetail_.empName ) ).where(p);
+		return em.createQuery(cq).setMaxResults(20000).getResultList().stream().distinct().collect(Collectors.toList());
 	}
 	
 	//@MethodDescribe("按指定的开始时间,结束时间列示未被分析的AttendanceDetail信息列表")
@@ -433,11 +436,11 @@ public class AttendanceDetailFactory extends AbstractFactory {
 		CriteriaBuilder cb = em.getCriteriaBuilder();
 		CriteriaQuery<String> cq = cb.createQuery(String.class);
 		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
-		cq.distinct(true).select( root.get(AttendanceDetail_.unitName ));
+		cq.select( root.get(AttendanceDetail_.unitName ));
 		Predicate p = cb.equal( root.get(AttendanceDetail_.recordStatus), 1 );
 		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.yearString), year ));
 		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.monthString), month ));
-		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList();
+		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList().stream().distinct().collect(Collectors.toList());
 	}
 	
 	/**
@@ -482,11 +485,11 @@ public class AttendanceDetailFactory extends AbstractFactory {
 		CriteriaBuilder cb = em.getCriteriaBuilder();
 		CriteriaQuery<String> cq = cb.createQuery(String.class);
 		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
-		cq.distinct(true).select( root.get(AttendanceDetail_.unitName ));
+		cq.select( root.get(AttendanceDetail_.unitName ));
 		Predicate p = cb.equal( root.get(AttendanceDetail_.recordStatus), 1 );
 		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleYear), cycleYear ));
 		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleMonth), cycleMonth ));
-		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList();
+		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList().stream().distinct().collect(Collectors.toList());
 	}
 	
 	//@MethodDescribe("按指定的统计周期年份,月份列示AttendanceDetail信息中涉及的顶层组织名称列表")
@@ -498,11 +501,11 @@ public class AttendanceDetailFactory extends AbstractFactory {
 		CriteriaBuilder cb = em.getCriteriaBuilder();
 		CriteriaQuery<String> cq = cb.createQuery(String.class);
 		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
-		cq.distinct(true).select( root.get(AttendanceDetail_.topUnitName ));
+		cq.select( root.get(AttendanceDetail_.topUnitName ));
 		Predicate p = cb.equal( root.get(AttendanceDetail_.recordStatus), 1 );
 		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.yearString), year ));
 		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.monthString), month ));
-		return em.createQuery(cq.where(p)).setMaxResults(1000).getResultList();
+		return em.createQuery(cq.where(p)).setMaxResults(1000).getResultList().stream().distinct().collect(Collectors.toList());
 	}
 	
 	//@MethodDescribe("按指定的统计周期年份,月份列示AttendanceDetail信息中涉及的员工姓名列表")
@@ -514,11 +517,11 @@ public class AttendanceDetailFactory extends AbstractFactory {
 		CriteriaBuilder cb = em.getCriteriaBuilder();
 		CriteriaQuery<String> cq = cb.createQuery(String.class);
 		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
-		cq.distinct(true).select( root.get(AttendanceDetail_.empName ));
+		cq.select( root.get(AttendanceDetail_.empName ));
 		Predicate p = cb.equal( root.get(AttendanceDetail_.recordStatus), 1 );
 		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleYear), cycleYear ));
 		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleMonth), cycleMonth ));
-		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList();
+		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList().stream().distinct().collect(Collectors.toList());
 	}
 	
 	//@MethodDescribe("按指定的统计周期年份,月份列示AttendanceDetail信息中涉及的顶层组织名称列表")
@@ -530,11 +533,11 @@ public class AttendanceDetailFactory extends AbstractFactory {
 		CriteriaBuilder cb = em.getCriteriaBuilder();
 		CriteriaQuery<String> cq = cb.createQuery(String.class);
 		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
-		cq.distinct(true).select( root.get(AttendanceDetail_.topUnitName ));
+		cq.select( root.get(AttendanceDetail_.topUnitName ));
 		Predicate p = cb.equal( root.get(AttendanceDetail_.recordStatus), 1 );
 		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleYear), cycleYear ));
 		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleMonth), cycleMonth ));
-		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList();
+		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList().stream().distinct().collect(Collectors.toList());
 	}
 	
 	//@MethodDescribe("按指定的统计周期年份,月份列示AttendanceDetail信息中涉及的组织名称列表")
@@ -546,11 +549,11 @@ public class AttendanceDetailFactory extends AbstractFactory {
 		CriteriaBuilder cb = em.getCriteriaBuilder();
 		CriteriaQuery<String> cq = cb.createQuery(String.class);
 		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
-		cq.distinct(true).select( root.get(AttendanceDetail_.unitName ));
+		cq.select( root.get(AttendanceDetail_.unitName ));
 		Predicate p = cb.equal( root.get(AttendanceDetail_.recordStatus), 1 );
 		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleYear), cycleYear ));
 		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleMonth), cycleMonth ));
-		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList();
+		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList().stream().distinct().collect(Collectors.toList());
 	}
 	
 	//@MethodDescribe("按指定的统计周期年份,月份列示AttendanceDetail信息中涉及的组织名称列表")
@@ -562,12 +565,12 @@ public class AttendanceDetailFactory extends AbstractFactory {
 		CriteriaBuilder cb = em.getCriteriaBuilder();
 		CriteriaQuery<String> cq = cb.createQuery(String.class);
 		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
-		cq.distinct(true).select( root.get(AttendanceDetail_.unitName ));
+		cq.select( root.get(AttendanceDetail_.unitName ));
 		Predicate p = cb.equal( root.get(AttendanceDetail_.recordStatus), 1 );
 		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleYear), cycleYear ));
 		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleMonth), cycleMonth ));
 		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.empName), employeeName ));
-		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList();
+		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList().stream().distinct().collect(Collectors.toList());
 	}
 
 
@@ -580,10 +583,10 @@ public class AttendanceDetailFactory extends AbstractFactory {
 		CriteriaBuilder cb = em.getCriteriaBuilder();
 		CriteriaQuery<String> cq = cb.createQuery(String.class);
 		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
-		cq.distinct(true).select( root.get(AttendanceDetail_.id ));
+		cq.select( root.get(AttendanceDetail_.id ));
 		//一般始终为true, id is not null
 		Predicate p = cb.equal( root.get(AttendanceDetail_.batchName), file_id );
-		return em.createQuery(cq.where(p)).getResultList();
+		return em.createQuery(cq.where(p)).getResultList().stream().distinct().collect(Collectors.toList());
 	}
 
 	//@MethodDescribe("按年份月份查询某用户的打卡数据记录列表")
@@ -840,6 +843,131 @@ public class AttendanceDetailFactory extends AbstractFactory {
 		return query.setMaxResults(count).getResultList();
 	}	
 	
+	/**
+	 * 查询下一页的信息数据(排除不参加考勤的人员)
+	 * @param id
+	 * @param count
+	 * @param sequence
+	 * @param wrapIn
+	 * @return
+	 * @throws Exception
+	 */
+	@SuppressWarnings("unchecked")
+	public List<AttendanceDetail> listIdsNextWithFilterUn( String id, Integer count, Object sequence, WrapInFilter wrapIn ,List<String> unUnitNameList,List<String> personNameList) throws Exception {
+		//先获取上一页最后一条的sequence值,如果有值的话,以此sequence值作为依据取后续的count条数据
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		String order = wrapIn.getOrder();//排序方式
+		List<Object> vs = new ArrayList<>();
+		StringBuffer sql_stringBuffer = new StringBuffer();
+		
+		if( order == null || order.isEmpty() ){
+			order = "DESC";
+		}
+		
+		Integer index = 1;
+		sql_stringBuffer.append( "SELECT o FROM "+AttendanceDetail.class.getCanonicalName()+" o where 1=1" );
+
+		if ((null != sequence) ) {
+			sql_stringBuffer.append(" and o.sequence " + (StringUtils.equalsIgnoreCase(order, "DESC") ? "<" : ">") + (" ?" + (index)));
+			vs.add(sequence);
+			index++;
+		}
+		if ((null != wrapIn.getQ_empName()) && (!wrapIn.getQ_empName().isEmpty())) {
+			sql_stringBuffer.append(" and o.empName = ?" + (index));
+			vs.add( wrapIn.getQ_empName() );
+			index++;
+		}
+		if (null != wrapIn.getUnitNames() && wrapIn.getUnitNames().size()>0) {
+			sql_stringBuffer.append(" and o.unitName in ( ?" + (index) + ")");
+			vs.add( wrapIn.getUnitNames() );
+			index++;
+		}
+		if (null != wrapIn.getTopUnitNames() && wrapIn.getTopUnitNames().size() > 0 ) {
+			sql_stringBuffer.append(" and o.topUnitName in ( ?" + (index) + ")");
+			vs.add( wrapIn.getTopUnitNames() );
+			index++;
+		}
+		if ((null != wrapIn.getCycleYear() ) && (!wrapIn.getCycleYear().isEmpty())) {
+			sql_stringBuffer.append(" and o.cycleYear = ?" + (index));
+			vs.add( wrapIn.getCycleYear() );
+			index++;
+		}
+		if ((null != wrapIn.getCycleMonth()) && (!wrapIn.getCycleMonth().isEmpty())) {
+			sql_stringBuffer.append(" and o.cycleMonth = ?" + (index));
+			vs.add( wrapIn.getCycleMonth() );
+			index++;
+		}
+		if ((null != wrapIn.getQ_year() ) && (!wrapIn.getQ_year().isEmpty())) {
+			sql_stringBuffer.append(" and o.yearString = ?" + (index));
+			vs.add( wrapIn.getQ_year() );
+			index++;
+		}
+		if ((null != wrapIn.getQ_month()) && (!wrapIn.getQ_month().isEmpty())) {
+			sql_stringBuffer.append(" and o.monthString = ?" + (index));
+			vs.add( wrapIn.getQ_month() );
+			index++;
+		}
+		if ((null != wrapIn.getQ_date()) && (!wrapIn.getQ_date().isEmpty())) {
+			sql_stringBuffer.append(" and o.recordDateString = ?" + (index));
+			vs.add( wrapIn.getQ_date() );
+			index++;
+		}
+		
+		if ( wrapIn.getRecordStatus() != 999 ) {
+			sql_stringBuffer.append(" and o.recordStatus = ?" + (index));
+			vs.add( wrapIn.getRecordStatus() );
+			index++;
+		}
+		
+		if (wrapIn.getIsAbsent() != null ) {
+			sql_stringBuffer.append(" and o.isAbsent = ?" + (index));
+			vs.add( wrapIn.getIsAbsent() );
+			index++;
+		}
+		
+		if (wrapIn.getIsLate() != null ) {
+			sql_stringBuffer.append(" and o.isLate = ?" + (index));
+			vs.add( wrapIn.getIsLate() );
+			index++;
+		}
+		
+		if (wrapIn.getIsLackOfTime() != null ) {
+			sql_stringBuffer.append(" and o.isLackOfTime = ?" + (index));
+			vs.add( wrapIn.getIsLackOfTime() );
+			index++;
+		}
+		
+		if (wrapIn.getIsLeaveEarlier() != null ) {
+			sql_stringBuffer.append(" and o.isLeaveEarlier = ?" + (index));
+			vs.add( wrapIn.getIsLeaveEarlier() );
+			index++;
+		}
+		
+		if (ListTools.isNotEmpty(unUnitNameList)) {
+			sql_stringBuffer.append(" and o.unitName not in ( ?" + (index) + ")");
+			vs.add( unUnitNameList );
+			index++;
+		}
+		if (ListTools.isNotEmpty(personNameList)) {
+			sql_stringBuffer.append(" and o.empName not in ( ?" + (index) + ")");
+			vs.add( personNameList );
+			index++;
+		}
+		
+		if( StringUtils.isNotEmpty( wrapIn.getKey() )){
+			sql_stringBuffer.append(" order by o."+wrapIn.getKey()+" " + order );
+		}else{
+			sql_stringBuffer.append(" order by o.sequence " + order );
+		}
+		
+		Query query = em.createQuery( sql_stringBuffer.toString(), AttendanceDetail.class );
+		//为查询设置所有的参数值
+		for (int i = 0; i < vs.size(); i++) {
+			query.setParameter(i + 1, vs.get(i));
+		}
+		return query.setMaxResults(count).getResultList();
+	}
+	
 	/**
 	 * 查询上一页的文档信息数据
 	 * @param id
@@ -1046,6 +1174,110 @@ public class AttendanceDetailFactory extends AbstractFactory {
 		}		
 		return (Long) query.getSingleResult();
 	}
+	
+	/**
+	 * 查询符合的文档信息总数(排除不参加考勤的人员)
+	 * @param wrapIn
+	 * @return
+	 * @throws Exception
+	 */
+	public long getCountWithFilterUn( WrapInFilter wrapIn ,List<String> unUnitNameList,List<String> personNameList) throws Exception {
+		//先获取上一页最后一条的sequence值,如果有值的话,以此sequence值作为依据取后续的count条数据
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		List<Object> vs = new ArrayList<>();
+		StringBuffer sql_stringBuffer = new StringBuffer();
+		Integer index = 1;
+		
+		sql_stringBuffer.append( "SELECT count(o.id) FROM "+AttendanceDetail.class.getCanonicalName()+" o where 1=1" );
+		
+		if ((null != wrapIn.getQ_empName()) && (!wrapIn.getQ_empName().isEmpty())) {
+			sql_stringBuffer.append(" and o.empName = ?" + (index));
+			vs.add( wrapIn.getQ_empName() );
+			index++;
+		}
+		if (null != wrapIn.getUnitNames() && wrapIn.getUnitNames().size()>0) {
+			sql_stringBuffer.append(" and o.unitName in ( ?" + (index) + ")");
+			vs.add( wrapIn.getUnitNames() );
+			index++;
+		}
+		if (null != wrapIn.getTopUnitNames() && wrapIn.getTopUnitNames().size() > 0 ) {
+			sql_stringBuffer.append(" and o.topUnitName in ( ?" + (index) + ")");
+			vs.add( wrapIn.getTopUnitNames() );
+			index++;
+		}
+		if ((null != wrapIn.getCycleYear() ) && (!wrapIn.getCycleYear().isEmpty())) {
+			sql_stringBuffer.append(" and o.cycleYear = ?" + (index));
+			vs.add( wrapIn.getCycleYear() );
+			index++;
+		}
+		if ((null != wrapIn.getCycleMonth()) && (!wrapIn.getCycleMonth().isEmpty())) {
+			sql_stringBuffer.append(" and o.cycleMonth = ?" + (index));
+			vs.add( wrapIn.getCycleMonth() );
+			index++;
+		}
+		if ((null != wrapIn.getQ_year() ) && (!wrapIn.getQ_year().isEmpty())) {
+			sql_stringBuffer.append(" and o.yearString = ?" + (index));
+			vs.add( wrapIn.getQ_year() );
+			index++;
+		}
+		if ((null != wrapIn.getQ_month()) && (!wrapIn.getQ_month().isEmpty())) {
+			sql_stringBuffer.append(" and o.monthString = ?" + (index));
+			vs.add( wrapIn.getQ_month() );
+			index++;
+		}
+		if ((null != wrapIn.getQ_date()) && (!wrapIn.getQ_date().isEmpty())) {
+			sql_stringBuffer.append(" and o.recordDateString = ?" + (index));
+			vs.add( wrapIn.getQ_date() );
+			index++;
+		}
+		if ( wrapIn.getRecordStatus() != 999 ) {
+			sql_stringBuffer.append(" and o.recordStatus = ?" + (index));
+			vs.add( wrapIn.getIsAbsent() );
+			index++;
+		}
+		
+		if (wrapIn.getIsAbsent() != null ) {
+			sql_stringBuffer.append(" and o.isAbsent = ?" + (index));
+			vs.add( wrapIn.getIsAbsent() );
+			index++;
+		}
+		
+		if (wrapIn.getIsLate() != null ) {
+			sql_stringBuffer.append(" and o.isLate = ?" + (index));
+			vs.add( wrapIn.getIsLate() );
+			index++;
+		}
+		
+		if (wrapIn.getIsLackOfTime() != null ) {
+			sql_stringBuffer.append(" and o.isLackOfTime = ?" + (index));
+			vs.add( wrapIn.getIsLackOfTime() );
+			index++;
+		}
+		
+		if (wrapIn.getIsLeaveEarlier() != null ) {
+			sql_stringBuffer.append(" and o.isLeaveEarlier = ?" + (index));
+			vs.add( wrapIn.getIsLeaveEarlier() );
+			index++;
+		}
+		
+		if (ListTools.isNotEmpty(unUnitNameList)) {
+			sql_stringBuffer.append(" and o.unitName not in ( ?" + (index) + ")");
+			vs.add( unUnitNameList );
+			index++;
+		}
+		if (ListTools.isNotEmpty(personNameList)) {
+			sql_stringBuffer.append(" and o.empName not in ( ?" + (index) + ")");
+			vs.add( personNameList );
+			index++;
+		}
+		
+		Query query = em.createQuery( sql_stringBuffer.toString(), AttendanceDetail.class );
+		//为查询设置所有的参数值
+		for (int i = 0; i < vs.size(); i++) {
+			query.setParameter(i + 1, vs.get(i));
+		}		
+		return (Long) query.getSingleResult();
+	}
 
 	public List<String> getByUserAndRecordDate(String employeeName, String recordDateStringFormated)  throws Exception{		
 		if( employeeName == null || employeeName.isEmpty() || recordDateStringFormated == null || recordDateStringFormated.isEmpty() ){
@@ -1075,9 +1307,9 @@ public class AttendanceDetailFactory extends AbstractFactory {
 		List<Selection<?>> selectionList = new ArrayList<Selection<?>>();
 		selectionList.add(root.get(AttendanceDetail_.cycleYear ));
 		selectionList.add(root.get(AttendanceDetail_.cycleMonth ));
-		cq.distinct(true).multiselect(selectionList);
+		cq.multiselect(selectionList);
 		
-		return em.createQuery(cq.where(p)).getResultList();
+		return em.createQuery(cq.where(p)).getResultList().stream().distinct().collect(Collectors.toList());
 	}
 	
 	public List<AttendanceCycles> getCyclesFromDetailWithDateSplit( String empName, Date startDate, Date endDate )  throws Exception{
@@ -1093,9 +1325,9 @@ public class AttendanceDetailFactory extends AbstractFactory {
 		List<Selection<?>> selectionList = new ArrayList<Selection<?>>();
 		selectionList.add(root.get(AttendanceDetail_.cycleYear ));
 		selectionList.add(root.get(AttendanceDetail_.cycleMonth ));
-		cq.distinct(true).multiselect(selectionList);
+		cq.multiselect(selectionList);
 		
-		return em.createQuery(cq.where(p)).getResultList();
+		return em.createQuery(cq.where(p)).getResultList().stream().distinct().collect(Collectors.toList());
 	}
 	
 	//@MethodDescribe("查询未归档的条卡记录列表,最大2000条")
@@ -1180,7 +1412,7 @@ public class AttendanceDetailFactory extends AbstractFactory {
 			);
 			p = cb.and( p, p_or );
 		}
-		cq.distinct(true).select(root.get(AttendanceDetail_.empName));
-		return em.createQuery(cq.where(p)).setMaxResults(100000).getResultList();
+		cq.select(root.get(AttendanceDetail_.empName));
+		return em.createQuery(cq.where(p)).setMaxResults(100000).getResultList().stream().distinct().collect(Collectors.toList());
 	}
 }

+ 5 - 4
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/DingdingAttendanceFactory.java

@@ -15,6 +15,7 @@ import javax.persistence.metamodel.SingularAttribute;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.List;
+import java.util.stream.Collectors;
 
 public class DingdingAttendanceFactory extends AbstractFactory {
 
@@ -315,8 +316,8 @@ public class DingdingAttendanceFactory extends AbstractFactory {
         CriteriaQuery<String> query = cb.createQuery(String.class);
         Root<AttendanceDingtalkDetail> root = query.from(AttendanceDingtalkDetail.class);
         Predicate p = cb.between(root.get(AttendanceDingtalkDetail_.userCheckTime), startTime.getTime(), endTime.getTime());
-        query.select(root.get(AttendanceDingtalkDetail_.o2Unit)).where(p).distinct(true);
-        return em.createQuery(query).getResultList();
+        query.select(root.get(AttendanceDingtalkDetail_.o2Unit)).where(p);
+        return em.createQuery(query).getResultList().stream().distinct().collect(Collectors.toList());
     }
 
 
@@ -608,8 +609,8 @@ public class DingdingAttendanceFactory extends AbstractFactory {
         CriteriaQuery<String> query = cb.createQuery(String.class);
         Root<AttendanceQywxDetail> root = query.from(AttendanceQywxDetail.class);
         Predicate p = cb.between(root.get(AttendanceQywxDetail_.checkin_time_date), startTime, endTime);
-        query.select(root.get(AttendanceQywxDetail_.o2Unit)).where(p).distinct(true);
-        return em.createQuery(query).getResultList();
+        query.select(root.get(AttendanceQywxDetail_.o2Unit)).where(p);
+        return em.createQuery(query).getResultList().stream().distinct().collect(Collectors.toList());
     }
 
     /**

+ 364 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/StatisticPersonForMonthFactory.java

@@ -16,6 +16,9 @@ import org.apache.commons.lang3.StringUtils;
 import com.x.attendance.assemble.control.AbstractFactory;
 import com.x.attendance.assemble.control.Business;
 import com.x.attendance.assemble.control.jaxrs.attendancestatistic.WrapInFilterStatisticPersonForMonth;
+import com.x.attendance.assemble.control.service.AttendanceEmployeeConfigServiceAdv;
+import com.x.attendance.assemble.control.service.UserManagerService;
+import com.x.attendance.entity.AttendanceEmployeeConfig;
 import com.x.attendance.entity.StatisticPersonForMonth;
 import com.x.attendance.entity.StatisticPersonForMonth_;
 import com.x.base.core.project.exception.ExceptionWhen;
@@ -416,6 +419,46 @@ public class StatisticPersonForMonthFactory extends AbstractFactory {
 		return em.createQuery(cq.where(p)).getSingleResult();
 	}
 	
+	/**
+	 * 根据组织,统计年月,计算组织内所有员工迟到数总和(排除不参加考勤的员工)
+	 * @param unitName
+	 * @param sYear
+	 * @param sMonth
+	 * @return
+	 * @throws Exception
+	 */
+	public Long sumLateCountByUnitYearAndMonthUn(List<String> unitName,List<String> unUnitNameList,List<String> personNameList, String sYear, String sMonth) throws Exception{
+		if( unitName == null || unitName.size() == 0 ){
+			logger.error( new UnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( StatisticPersonForMonth.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<StatisticPersonForMonth> root = cq.from( StatisticPersonForMonth.class);		
+		//查询总数
+		cq.select( cb.sum( root.get(StatisticPersonForMonth_.lateTimes ) ) );		
+		Predicate p = root.get(StatisticPersonForMonth_.unitName).in( unitName );
+		
+		if(ListTools.isNotEmpty(unUnitNameList)){
+			p = cb.and( p, cb.isNotMember(root.get( StatisticPersonForMonth_.unitName ), cb.literal(unUnitNameList)));
+		}
+		if(ListTools.isNotEmpty(personNameList)){
+			p = cb.and( p, cb.isNotMember(root.get( StatisticPersonForMonth_.employeeName ), cb.literal(personNameList)));
+		}
+		if( sYear == null || sYear.isEmpty() ){
+			logger.error( new StatisticYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get(StatisticPersonForMonth_.statisticYear), sYear));
+		}
+		if( sMonth == null || sMonth.isEmpty() ){
+			logger.error( new StatisticMonthEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get(StatisticPersonForMonth_.statisticMonth), sMonth));
+		}
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
 	
 	/**
 	 * 根据组织列表,统计年月,计算组织内所有员工出勤天数总和
@@ -450,6 +493,45 @@ public class StatisticPersonForMonthFactory extends AbstractFactory {
 		return em.createQuery(cq.where(p)).getSingleResult();
 	}
 	
+	/**
+	 * 根据组织列表,统计年月,计算组织内所有员工出勤天数总和(排除不参加考勤的员工)
+	 * @param unitName
+	 * @param sYear
+	 * @param sMonth
+	 * @return
+	 * @throws Exception
+	 */
+	public Double sumAttendanceDayCountByUnitYearAndMonthUn( List<String> unitName,List<String> unUnitNameList,List<String> personNameList, String sYear, String sMonth) throws Exception{
+		if( unitName == null || unitName.size() == 0 ){
+			logger.error( new UnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( StatisticPersonForMonth.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Double> cq = cb.createQuery(Double.class);
+		Root<StatisticPersonForMonth> root = cq.from( StatisticPersonForMonth.class);		
+		//查询总数
+		cq.select( cb.sum( root.get(StatisticPersonForMonth_.onDutyDayCount) ) );		
+		Predicate p = root.get(StatisticPersonForMonth_.unitName).in( unitName );
+		if(ListTools.isNotEmpty(unUnitNameList)){
+			p = cb.and( p, cb.isNotMember(root.get( StatisticPersonForMonth_.unitName ), cb.literal(unUnitNameList)));
+		}
+		if(ListTools.isNotEmpty(personNameList)){
+			p = cb.and( p, cb.isNotMember(root.get( StatisticPersonForMonth_.employeeName ), cb.literal(personNameList)));
+		}
+		if( sYear == null || sYear.isEmpty() ){
+			logger.error( new StatisticYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get(StatisticPersonForMonth_.statisticYear), sYear));
+		}
+		if( sMonth == null || sMonth.isEmpty() ){
+			logger.error( new StatisticMonthEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get(StatisticPersonForMonth_.statisticMonth), sMonth));
+		}
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
 	/**
 	 * 根据组织列表,统计年月,计算组织内所有员工异常打卡数总和
 	 * @param unitName
@@ -483,6 +565,46 @@ public class StatisticPersonForMonthFactory extends AbstractFactory {
 		return em.createQuery(cq.where(p)).getSingleResult();
 	}
 	
+	/**
+	 * 根据组织列表,统计年月,计算组织内所有员工异常打卡数总和(排除不参加考勤的员工)
+	 * @param unitName
+	 * @param sYear
+	 * @param sMonth
+	 * @return
+	 * @throws Exception
+	 */
+	public Long sumAbNormalDutyCountByUnitYearAndMonthUn( List<String> unitName,List<String> unUnitNameList,List<String> personNameList, String sYear, String sMonth) throws Exception{
+		if( unitName == null || unitName.size() == 0 ){
+			logger.error( new UnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( StatisticPersonForMonth.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<StatisticPersonForMonth> root = cq.from( StatisticPersonForMonth.class);		
+		//查询总数
+		cq.select( cb.sum( root.get(StatisticPersonForMonth_.abNormalDutyCount) ) );		
+		Predicate p = root.get(StatisticPersonForMonth_.unitName).in( unitName );
+		
+		if(ListTools.isNotEmpty(unUnitNameList)){
+			p = cb.and( p, cb.isNotMember(root.get( StatisticPersonForMonth_.unitName ), cb.literal(unUnitNameList)));
+		}
+		if(ListTools.isNotEmpty(personNameList)){
+			p = cb.and( p, cb.isNotMember(root.get( StatisticPersonForMonth_.employeeName ), cb.literal(personNameList)));
+		}
+		if( sYear == null || sYear.isEmpty() ){
+			logger.error( new StatisticYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get(StatisticPersonForMonth_.statisticYear), sYear));
+		}
+		if( sMonth == null || sMonth.isEmpty() ){
+			logger.error( new StatisticMonthEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get(StatisticPersonForMonth_.statisticMonth), sMonth));
+		}
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
 	/**
 	 * 根据组织列表,统计年月,计算组织内所有员工工时不足次数总和
 	 * @param unitName
@@ -516,6 +638,46 @@ public class StatisticPersonForMonthFactory extends AbstractFactory {
 		return em.createQuery(cq.where(p)).getSingleResult();
 	}
 	
+	/**
+	 * 根据组织列表,统计年月,计算组织内所有员工工时不足次数总和(排除不参加考勤的员工)
+	 * @param unitName
+	 * @param sYear
+	 * @param sMonth
+	 * @return
+	 * @throws Exception
+	 */
+	public Long sumLackOfTimeCountByUnitYearAndMonthUn(List<String> unitName,List<String> unUnitNameList,List<String> personNameList, String sYear, String sMonth) throws Exception{
+		if( unitName == null || unitName.size() == 0 ){
+			logger.error( new UnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( StatisticPersonForMonth.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<StatisticPersonForMonth> root = cq.from( StatisticPersonForMonth.class);		
+		//查询总数
+		cq.select( cb.sum( root.get(StatisticPersonForMonth_.lackOfTimeCount) ) );		
+		Predicate p = root.get(StatisticPersonForMonth_.unitName).in( unitName );
+		
+		if(ListTools.isNotEmpty(unUnitNameList)){
+			p = cb.and( p, cb.isNotMember(root.get( StatisticPersonForMonth_.unitName ), cb.literal(unUnitNameList)));
+		}
+		if(ListTools.isNotEmpty(personNameList)){
+			p = cb.and( p, cb.isNotMember(root.get( StatisticPersonForMonth_.employeeName ), cb.literal(personNameList)));
+		}
+		if( sYear == null || sYear.isEmpty() ){
+			logger.error( new StatisticYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get(StatisticPersonForMonth_.statisticYear), sYear));
+		}
+		if( sMonth == null || sMonth.isEmpty() ){
+			logger.error( new StatisticMonthEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get(StatisticPersonForMonth_.statisticMonth), sMonth));
+		}
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
 	/**
 	 * 根据组织列表,统计年月,计算组织内所有员工早退次数总和
 	 * @param unitName
@@ -549,6 +711,46 @@ public class StatisticPersonForMonthFactory extends AbstractFactory {
 		return em.createQuery(cq.where(p)).getSingleResult();
 	}
 	
+	/**
+	 * 根据组织列表,统计年月,计算组织内所有员工早退次数总和(排除不参加考勤的员工)
+	 * @param unitName
+	 * @param sYear
+	 * @param sMonth
+	 * @return
+	 * @throws Exception
+	 */
+	public Long sumLeaveEarlyCountByUnitYearAndMonthUn( List<String> unitName,List<String> unUnitNameList,List<String> personNameList, String sYear, String sMonth) throws Exception{
+		if( unitName == null || unitName.size() == 0 ){
+			logger.error( new UnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( StatisticPersonForMonth.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<StatisticPersonForMonth> root = cq.from( StatisticPersonForMonth.class);		
+		//查询总数
+		cq.select( cb.sum( root.get(StatisticPersonForMonth_.leaveEarlyTimes ) ) );		
+		Predicate p = root.get(StatisticPersonForMonth_.unitName).in( unitName );
+		
+		if(ListTools.isNotEmpty(unUnitNameList)){
+			p = cb.and( p, cb.isNotMember(root.get( StatisticPersonForMonth_.unitName ), cb.literal(unUnitNameList)));
+		}
+		if(ListTools.isNotEmpty(personNameList)){
+			p = cb.and( p, cb.isNotMember(root.get( StatisticPersonForMonth_.employeeName ), cb.literal(personNameList)));
+		}
+		if( sYear == null || sYear.isEmpty() ){
+			logger.error( new StatisticYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get(StatisticPersonForMonth_.statisticYear), sYear));
+		}
+		if( sMonth == null || sMonth.isEmpty() ){
+			logger.error( new StatisticMonthEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get(StatisticPersonForMonth_.statisticMonth), sMonth));
+		}
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
 	/**
 	 * 根据组织列表,统计年月,计算组织内所有员工签退次数总和
 	 * @param unitName
@@ -582,6 +784,46 @@ public class StatisticPersonForMonthFactory extends AbstractFactory {
 		return em.createQuery(cq.where(p)).getSingleResult();
 	}
 	
+	/**
+	 * 根据组织列表,统计年月,计算组织内所有员工签退次数总和
+	 * @param unitName
+	 * @param sYear
+	 * @param sMonth
+	 * @return
+	 * @throws Exception
+	 */
+	public Long sumOffDutyCountByUnitYearAndMonthUn( List<String> unitName,List<String> unUnitNameList,List<String> personNameList, String sYear, String sMonth) throws Exception{
+		if( unitName == null || unitName.size() == 0 ){
+			logger.error( new UnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( StatisticPersonForMonth.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<StatisticPersonForMonth> root = cq.from( StatisticPersonForMonth.class);		
+		//查询总数
+		cq.select( cb.sum( root.get(StatisticPersonForMonth_.offDutyTimes ) ) );		
+		Predicate p = root.get(StatisticPersonForMonth_.unitName).in( unitName );
+		
+		if(ListTools.isNotEmpty(unUnitNameList)){
+			p = cb.and( p, cb.isNotMember(root.get( StatisticPersonForMonth_.unitName ), cb.literal(unUnitNameList)));
+		}
+		if(ListTools.isNotEmpty(personNameList)){
+			p = cb.and( p, cb.isNotMember(root.get( StatisticPersonForMonth_.employeeName ), cb.literal(personNameList)));
+		}
+		if( sYear == null || sYear.isEmpty() ){
+			logger.error( new StatisticYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get(StatisticPersonForMonth_.statisticYear), sYear));
+		}
+		if( sMonth == null || sMonth.isEmpty() ){
+			logger.error( new StatisticMonthEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get(StatisticPersonForMonth_.statisticMonth), sMonth));
+		}
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
 	/**
 	 * 根据组织列表,统计年月,计算组织内所有员工签到退次数总和
 	 * @param unitName
@@ -615,6 +857,46 @@ public class StatisticPersonForMonthFactory extends AbstractFactory {
 		return em.createQuery(cq.where(p)).getSingleResult();
 	}
 	
+	/**
+	 * 根据组织列表,统计年月,计算组织内所有员工签到退次数总和(排除不参加考勤的员工)
+	 * @param unitName
+	 * @param sYear
+	 * @param sMonth
+	 * @return
+	 * @throws Exception
+	 */
+	public Long sumOnDutyCountByUnitYearAndMonthUn( List<String> unitName,List<String> unUnitNameList,List<String> personNameList, String sYear, String sMonth) throws Exception{
+		if( unitName == null || unitName.size() == 0 ){
+			logger.error( new UnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( StatisticPersonForMonth.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<StatisticPersonForMonth> root = cq.from( StatisticPersonForMonth.class);		
+		//查询总数
+		cq.select( cb.sum( root.get(StatisticPersonForMonth_.onDutyTimes ) ) );		
+		Predicate p = root.get(StatisticPersonForMonth_.unitName).in( unitName );
+		
+		if(ListTools.isNotEmpty(unUnitNameList)){
+			p = cb.and( p, cb.isNotMember(root.get( StatisticPersonForMonth_.unitName ), cb.literal(unUnitNameList)));
+		}
+		if(ListTools.isNotEmpty(personNameList)){
+			p = cb.and( p, cb.isNotMember(root.get( StatisticPersonForMonth_.employeeName ), cb.literal(personNameList)));
+		}
+		if( sYear == null || sYear.isEmpty() ){
+			logger.error( new StatisticYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get(StatisticPersonForMonth_.statisticYear), sYear));
+		}
+		if( sMonth == null || sMonth.isEmpty() ){
+			logger.error( new StatisticMonthEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get(StatisticPersonForMonth_.statisticMonth), sMonth));
+		}
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
 	/**
 	 * 根据组织列表,统计年月,计算组织内所有员工请假天数总和
 	 * @param unitName
@@ -648,6 +930,46 @@ public class StatisticPersonForMonthFactory extends AbstractFactory {
 		return em.createQuery(cq.where(p)).getSingleResult();
 	}
 	
+	/**
+	 * 根据组织列表,统计年月,计算组织内所有员工请假天数总和(排除不参加考勤的员工)
+	 * @param unitName
+	 * @param sYear
+	 * @param sMonth
+	 * @return
+	 * @throws Exception
+	 */
+	public Double sumOnSelfHolidayCountByUnitYearAndMonthUn( List<String> unitName, List<String> unUnitNameList,List<String> personNameList,String sYear, String sMonth) throws Exception{
+		if( unitName == null || unitName.size() == 0 ){
+			logger.error( new UnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( StatisticPersonForMonth.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Double> cq = cb.createQuery(Double.class);
+		Root<StatisticPersonForMonth> root = cq.from( StatisticPersonForMonth.class);		
+		//查询总数
+		cq.select( cb.sum( root.get(StatisticPersonForMonth_.onSelfHolidayCount) ) );		
+		Predicate p = root.get(StatisticPersonForMonth_.unitName).in( unitName );
+		
+		if(ListTools.isNotEmpty(unUnitNameList)){
+			p = cb.and( p, cb.isNotMember(root.get( StatisticPersonForMonth_.unitName ), cb.literal(unUnitNameList)));
+		}
+		if(ListTools.isNotEmpty(personNameList)){
+			p = cb.and( p, cb.isNotMember(root.get( StatisticPersonForMonth_.employeeName ), cb.literal(personNameList)));
+		}
+		if( sYear == null || sYear.isEmpty() ){
+			logger.error( new StatisticYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get(StatisticPersonForMonth_.statisticYear), sYear));
+		}
+		if( sMonth == null || sMonth.isEmpty() ){
+			logger.error( new StatisticMonthEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get(StatisticPersonForMonth_.statisticMonth), sMonth));
+		}
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
 	/**
 	 * 根据组织列表,统计年月,计算组织内所有员工缺勤天数总和
 	 * @param unitName
@@ -683,4 +1005,46 @@ public class StatisticPersonForMonthFactory extends AbstractFactory {
 		}
 		return em.createQuery(cq.where(p)).getSingleResult();
 	}
+	
+	/**
+	 * 根据组织列表,统计年月,计算组织内所有员工缺勤天数总和(排除不参加考勤的员工)
+	 * @param unitName
+	 * @param sYear
+	 * @param sMonth
+	 * @return
+	 * @throws Exception
+	 */
+	public Double sumAbsenceDayCountByUnitYearAndMonthUn( List<String> unitName,List<String> unUnitNameList,List<String> personNameList, String sYear, String sMonth) throws Exception{
+		if( unitName == null || unitName.size() == 0 ){
+			logger.error( new UnitNamesEmptyException() );
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get( StatisticPersonForMonth.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Double> cq = cb.createQuery(Double.class);
+		Root<StatisticPersonForMonth> root = cq.from( StatisticPersonForMonth.class);		
+		//查询总数
+		cq.select( cb.sum( root.get(StatisticPersonForMonth_.absenceDayCount) ) );	
+		
+		Predicate p = root.get(StatisticPersonForMonth_.unitName).in( unitName );
+		
+		if(ListTools.isNotEmpty(unUnitNameList)){
+			p = cb.and( p, cb.isNotMember(root.get( StatisticPersonForMonth_.unitName ), cb.literal(unUnitNameList)));
+		}
+		if(ListTools.isNotEmpty(personNameList)){
+			p = cb.and( p, cb.isNotMember(root.get( StatisticPersonForMonth_.employeeName ), cb.literal(personNameList)));
+		}
+		if( sYear == null || sYear.isEmpty() ){
+			logger.error( new StatisticYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get(StatisticPersonForMonth_.statisticYear), sYear));
+		}
+		
+		if( sMonth == null || sMonth.isEmpty() ){
+			logger.error( new StatisticMonthEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get(StatisticPersonForMonth_.statisticMonth), sMonth));
+		}
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
 }

+ 66 - 2
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attendancedetail/ActionListNextWithFilter.java

@@ -16,7 +16,10 @@ import org.apache.commons.lang3.StringUtils;
 import com.google.gson.JsonElement;
 import com.x.attendance.assemble.control.Business;
 import com.x.attendance.assemble.control.ExceptionWrapInConvert;
+import com.x.attendance.assemble.control.service.AttendanceEmployeeConfigServiceAdv;
+import com.x.attendance.assemble.control.service.UserManagerService;
 import com.x.attendance.entity.AttendanceDetail;
+import com.x.attendance.entity.AttendanceEmployeeConfig;
 import com.x.base.core.container.EntityManagerContainer;
 import com.x.base.core.container.factory.EntityManagerContainerFactory;
 import com.x.base.core.entity.JpaObject;
@@ -31,6 +34,8 @@ import com.x.base.core.project.logger.LoggerFactory;
 public class ActionListNextWithFilter extends BaseAction {
 
 	private static  Logger logger = LoggerFactory.getLogger(ActionListNextWithFilter.class);
+	private UserManagerService userManagerService = new UserManagerService();
+	protected AttendanceEmployeeConfigServiceAdv attendanceEmployeeConfigServiceAdv = new AttendanceEmployeeConfigServiceAdv();
 
 	protected ActionResult<List<Wo>> execute(HttpServletRequest request, EffectivePerson effectivePerson, String id,
 			Integer count, JsonElement jsonElement) throws Exception {
@@ -47,6 +52,9 @@ public class ActionListNextWithFilter extends BaseAction {
 		AttendanceScheduleSetting scheduleSetting_top = null;
 		AttendanceScheduleSetting scheduleSetting = null;
 		Boolean check = true;
+		
+		List<String> unUnitNameList = new ArrayList<String>();
+		List<String> personNameList = new ArrayList<String>();
 
 		try {
 			wrapIn = this.convertToWrapIn(jsonElement, Wi.class);
@@ -111,10 +119,14 @@ public class ActionListNextWithFilter extends BaseAction {
 				}
 
 				if (check ) {
+					unUnitNameList = getUnUnitNameList();
+					personNameList = getUnPersonNameList();
 					// 从数据库中查询符合条件的一页数据对象
-					detailList = business.getAttendanceDetailFactory().listIdsNextWithFilter(id, count, sequence, wrapIn);
+					//detailList = business.getAttendanceDetailFactory().listIdsNextWithFilter(id, count, sequence, wrapIn);
+					detailList = business.getAttendanceDetailFactory().listIdsNextWithFilterUn(id, count, sequence, wrapIn,unUnitNameList,personNameList);
 					// 从数据库中查询符合条件的对象总数
-					total = business.getAttendanceDetailFactory().getCountWithFilter(wrapIn);
+					//total = business.getAttendanceDetailFactory().getCountWithFilter(wrapIn);
+					total = business.getAttendanceDetailFactory().getCountWithFilterUn(wrapIn,unUnitNameList,personNameList);
 					// 将所有查询出来的有状态的对象转换为可以输出的过滤过属性的对象
 					wraps = Wo.copier.copy(detailList);
 				}
@@ -192,5 +204,57 @@ public class ActionListNextWithFilter extends BaseAction {
 		public static WrapCopier<AttendanceDetail, Wo> copier = WrapCopierFactory.wo(AttendanceDetail.class, Wo.class,
 				null, JpaObject.FieldsInvisible);
 	}
+	
+	/**
+	 * 获取不需要考勤的组织
+	 * @return
+	 * @throws Exception 
+	 */
+	protected  List<String> getUnUnitNameList() throws Exception {
+		List<String> unUnitNameList = new ArrayList<String>();
+
+		List<AttendanceEmployeeConfig> attendanceEmployeeConfigs = attendanceEmployeeConfigServiceAdv.listByConfigType("NOTREQUIRED");
+
+		if(ListTools.isNotEmpty(attendanceEmployeeConfigs)){
+			for (AttendanceEmployeeConfig attendanceEmployeeConfig : attendanceEmployeeConfigs) {
+				String unitName = attendanceEmployeeConfig.getUnitName();
+				String employeeName = attendanceEmployeeConfig.getEmployeeName();
+
+				if(StringUtils.isEmpty(employeeName) && StringUtils.isNotEmpty(unitName)){
+					unUnitNameList.add(unitName);
+					List<String> tempUnitNameList = userManagerService.listSubUnitNameWithParent(unitName);
+					if(ListTools.isNotEmpty(tempUnitNameList)){
+						for(String tempUnit:tempUnitNameList){
+							if(!ListTools.contains(unUnitNameList, tempUnit)){
+								unUnitNameList.add(tempUnit);
+							}
+						}
+					}
+				}
+			} 
+		}
+		return unUnitNameList;
+	}
+	
+	/**
+	 * 获取不需要考勤的人员
+	 * @return
+	 * @throws Exception 
+	 */
+	protected  List<String> getUnPersonNameList() throws Exception {
+		List<String> personNameList = new ArrayList<String>();
+		List<AttendanceEmployeeConfig> attendanceEmployeeConfigs = attendanceEmployeeConfigServiceAdv.listByConfigType("NOTREQUIRED");
+
+		if(ListTools.isNotEmpty(attendanceEmployeeConfigs)){
+			for (AttendanceEmployeeConfig attendanceEmployeeConfig : attendanceEmployeeConfigs) {
+				String employeeName = attendanceEmployeeConfig.getEmployeeName();
+
+				if(StringUtils.isNotEmpty(employeeName) && !ListTools.contains(personNameList, employeeName)){
+					personNameList.add(employeeName);
+				}
+			}
+		}
+		return personNameList;
+	}
 
 }

+ 78 - 10
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/service/AttendanceStatisticService.java

@@ -5,9 +5,12 @@ import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.commons.lang3.StringUtils;
+
 import com.x.attendance.assemble.control.Business;
 import com.x.attendance.assemble.control.factory.AttendanceDetailStatisticFactory;
 import com.x.attendance.assemble.control.factory.StatisticUnitForMonthFactory;
+import com.x.attendance.entity.AttendanceEmployeeConfig;
 import com.x.attendance.entity.AttendanceStatisticRequireLog;
 import com.x.attendance.entity.AttendanceStatisticalCycle;
 import com.x.attendance.entity.AttendanceWorkDayConfig;
@@ -27,6 +30,7 @@ public class AttendanceStatisticService {
 	
 	private static  Logger logger = LoggerFactory.getLogger( AttendanceStatisticService.class );
 	private UserManagerService userManagerService = new UserManagerService();
+	protected AttendanceEmployeeConfigServiceAdv attendanceEmployeeConfigServiceAdv = new AttendanceEmployeeConfigServiceAdv();
 	
 	/**
 	 * 根据数据统计需求,进行员工每月考勤分析结果统计
@@ -229,7 +233,8 @@ public class AttendanceStatisticService {
 		Object workDayCountForMonth = 0.0, absenceDayCount=0.0, onSelfHolidayCount=0.0;
 		String cycleYear = null, cycleMonth = null;
 		Business business = null;
-		
+		List<String> unUnitNameList = new ArrayList<String>();
+		List<String> personNameList = new ArrayList<String>();
 
 		cycleYear = attendanceStatisticRequireLog.getStatisticYear();
 		cycleMonth = attendanceStatisticRequireLog.getStatisticMonth();
@@ -237,6 +242,8 @@ public class AttendanceStatisticService {
 		query_unitNames.add( unitName );
 		
 		try {
+			unUnitNameList = getUnUnitNameList();
+			personNameList = getUnPersonNameList();
 			business = new Business(emc);
 			statisticUnitForMonth = new StatisticUnitForMonth();
 			statisticUnitForMonth.setUnitName( unitName );
@@ -250,42 +257,51 @@ public class AttendanceStatisticService {
 				logger.warn( "根据组织名称["+unitName+"]未查询到组织信息。" );
 			}
 			//    1.2.1 应出勤天数
-			workDayCountForMonth = business.getStatisticPersonForMonthFactory().sumAttendanceDayCountByUnitYearAndMonth(query_unitNames, cycleYear, cycleMonth);
+			//workDayCountForMonth = business.getStatisticPersonForMonthFactory().sumAttendanceDayCountByUnitYearAndMonth(query_unitNames, cycleYear, cycleMonth);
+			workDayCountForMonth = business.getStatisticPersonForMonthFactory().sumAttendanceDayCountByUnitYearAndMonthUn(query_unitNames,unUnitNameList,personNameList, cycleYear, cycleMonth);
 			double count = 0.0;
 			if( workDayCountForMonth != null ){
 				count = (double)workDayCountForMonth;
 			}
 			statisticUnitForMonth.setOnDutyEmployeeCount( count );
 			//    1.2.3 异常打卡次数
-			abNormalDutyCount = business.getStatisticPersonForMonthFactory().sumAbNormalDutyCountByUnitYearAndMonth( query_unitNames, cycleYear, cycleMonth );
+			//abNormalDutyCount = business.getStatisticPersonForMonthFactory().sumAbNormalDutyCountByUnitYearAndMonth( query_unitNames, cycleYear, cycleMonth );
+			abNormalDutyCount = business.getStatisticPersonForMonthFactory().sumAbNormalDutyCountByUnitYearAndMonthUn( query_unitNames,unUnitNameList,personNameList, cycleYear, cycleMonth );
 			if( abNormalDutyCount == null ){ abNormalDutyCount = 0L;}
 			statisticUnitForMonth.setAbNormalDutyCount((long)abNormalDutyCount);
 			//    1.2.4 工时不足次数
-			lackOfTimeCount = business.getStatisticPersonForMonthFactory().sumLackOfTimeCountByUnitYearAndMonth( query_unitNames, cycleYear, cycleMonth);
+			//lackOfTimeCount = business.getStatisticPersonForMonthFactory().sumLackOfTimeCountByUnitYearAndMonth( query_unitNames, cycleYear, cycleMonth);
+			lackOfTimeCount = business.getStatisticPersonForMonthFactory().sumLackOfTimeCountByUnitYearAndMonthUn( query_unitNames,unUnitNameList,personNameList, cycleYear, cycleMonth);
 			if( lackOfTimeCount == null ){ lackOfTimeCount = 0L;}
 			statisticUnitForMonth.setLackOfTimeCount((long)lackOfTimeCount);
 			//    1.2.5 签到次数
-			onDutyTimes = business.getStatisticPersonForMonthFactory().sumOnDutyCountByUnitYearAndMonth( query_unitNames, cycleYear, cycleMonth);
+			//onDutyTimes = business.getStatisticPersonForMonthFactory().sumOnDutyCountByUnitYearAndMonth( query_unitNames, cycleYear, cycleMonth);
+			onDutyTimes = business.getStatisticPersonForMonthFactory().sumOnDutyCountByUnitYearAndMonthUn( query_unitNames,unUnitNameList,personNameList, cycleYear, cycleMonth);
 			if( onDutyTimes == null ){ onDutyTimes = 0L;}
 			statisticUnitForMonth.setOnDutyCount( (long)onDutyTimes);
 			//    1.2.6 签退次数
-			offDutyTimes = business.getStatisticPersonForMonthFactory().sumOffDutyCountByUnitYearAndMonth( query_unitNames, cycleYear, cycleMonth);
+			//offDutyTimes = business.getStatisticPersonForMonthFactory().sumOffDutyCountByUnitYearAndMonth( query_unitNames, cycleYear, cycleMonth);
+			offDutyTimes = business.getStatisticPersonForMonthFactory().sumOffDutyCountByUnitYearAndMonthUn( query_unitNames, unUnitNameList,personNameList,cycleYear, cycleMonth);
 			if( offDutyTimes == null ){ offDutyTimes = 0L;}
 			statisticUnitForMonth.setOffDutyCount((long)offDutyTimes);
 			//    1.2.7 迟到次数
-			lateTimes = business.getStatisticPersonForMonthFactory().sumLateCountByUnitYearAndMonth( query_unitNames, cycleYear, cycleMonth);
+			//lateTimes = business.getStatisticPersonForMonthFactory().sumLateCountByUnitYearAndMonth( query_unitNames, cycleYear, cycleMonth);
+			lateTimes = business.getStatisticPersonForMonthFactory().sumLateCountByUnitYearAndMonthUn( query_unitNames, unUnitNameList,personNameList,cycleYear, cycleMonth);
 			if( lateTimes == null ){ lateTimes = 0L;}
 			statisticUnitForMonth.setLateCount((long)lateTimes);
 			//    1.2.8 缺勤天数
-			absenceDayCount = business.getStatisticPersonForMonthFactory().sumAbsenceDayCountByUnitYearAndMonth( query_unitNames, cycleYear, cycleMonth);
+			//absenceDayCount = business.getStatisticPersonForMonthFactory().sumAbsenceDayCountByUnitYearAndMonth( query_unitNames, cycleYear, cycleMonth);
+			absenceDayCount = business.getStatisticPersonForMonthFactory().sumAbsenceDayCountByUnitYearAndMonthUn( query_unitNames, unUnitNameList,personNameList,cycleYear, cycleMonth);
 			if( absenceDayCount == null ){ absenceDayCount = 0.0;}
 			statisticUnitForMonth.setAbsenceDayCount((double)absenceDayCount);
 			//    1.2.9 早退次数
-			leaveEarlyTimes = business.getStatisticPersonForMonthFactory().sumLeaveEarlyCountByUnitYearAndMonth( query_unitNames, cycleYear, cycleMonth);
+			//leaveEarlyTimes = business.getStatisticPersonForMonthFactory().sumLeaveEarlyCountByUnitYearAndMonth( query_unitNames, cycleYear, cycleMonth);
+			leaveEarlyTimes = business.getStatisticPersonForMonthFactory().sumLeaveEarlyCountByUnitYearAndMonthUn( query_unitNames, unUnitNameList,personNameList,cycleYear, cycleMonth);
 			if( leaveEarlyTimes == null ){ leaveEarlyTimes = 0L;}
 			statisticUnitForMonth.setLeaveEarlyCount((long)leaveEarlyTimes);
 			//    1.2.10 休假天数
-			onSelfHolidayCount = business.getStatisticPersonForMonthFactory().sumOnSelfHolidayCountByUnitYearAndMonth( query_unitNames, cycleYear, cycleMonth);
+			//onSelfHolidayCount = business.getStatisticPersonForMonthFactory().sumOnSelfHolidayCountByUnitYearAndMonth( query_unitNames, cycleYear, cycleMonth);
+			onSelfHolidayCount = business.getStatisticPersonForMonthFactory().sumOnSelfHolidayCountByUnitYearAndMonthUn( query_unitNames,unUnitNameList,personNameList, cycleYear, cycleMonth);
 			if( onSelfHolidayCount == null ){ onSelfHolidayCount = 0.0;}
 			statisticUnitForMonth.setOnSelfHolidayCount((double)onSelfHolidayCount);
 			
@@ -861,4 +877,56 @@ public class AttendanceStatisticService {
 		Business business =  new Business( emc );
 		return business.getStatisticTopUnitForDayFactory().list( ids );
 	}
+	
+	/**
+	 * 获取不需要考勤的组织
+	 * @return
+	 * @throws Exception 
+	 */
+	protected  List<String> getUnUnitNameList() throws Exception {
+		List<String> unUnitNameList = new ArrayList<String>();
+
+		List<AttendanceEmployeeConfig> attendanceEmployeeConfigs = attendanceEmployeeConfigServiceAdv.listByConfigType("NOTREQUIRED");
+
+		if(ListTools.isNotEmpty(attendanceEmployeeConfigs)){
+			for (AttendanceEmployeeConfig attendanceEmployeeConfig : attendanceEmployeeConfigs) {
+				String unitName = attendanceEmployeeConfig.getUnitName();
+				String employeeName = attendanceEmployeeConfig.getEmployeeName();
+
+				if(StringUtils.isEmpty(employeeName) && StringUtils.isNotEmpty(unitName)){
+					unUnitNameList.add(unitName);
+					List<String> tempUnitNameList = userManagerService.listSubUnitNameWithParent(unitName);
+					if(ListTools.isNotEmpty(tempUnitNameList)){
+						for(String tempUnit:tempUnitNameList){
+							if(!ListTools.contains(unUnitNameList, tempUnit)){
+								unUnitNameList.add(tempUnit);
+							}
+						}
+					}
+				}
+			} 
+		}
+		return unUnitNameList;
+	}
+	
+	/**
+	 * 获取不需要考勤的人员
+	 * @return
+	 * @throws Exception 
+	 */
+	protected  List<String> getUnPersonNameList() throws Exception {
+		List<String> personNameList = new ArrayList<String>();
+		List<AttendanceEmployeeConfig> attendanceEmployeeConfigs = attendanceEmployeeConfigServiceAdv.listByConfigType("NOTREQUIRED");
+
+		if(ListTools.isNotEmpty(attendanceEmployeeConfigs)){
+			for (AttendanceEmployeeConfig attendanceEmployeeConfig : attendanceEmployeeConfigs) {
+				String employeeName = attendanceEmployeeConfig.getEmployeeName();
+
+				if(StringUtils.isNotEmpty(employeeName) && !ListTools.contains(personNameList, employeeName)){
+					personNameList.add(employeeName);
+				}
+			}
+		}
+		return personNameList;
+	}
 }

+ 10 - 15
o2server/x_base_core_project/src/main/java/com/x/base/core/container/EntityManagerContainer.java

@@ -10,6 +10,7 @@ import java.util.Date;
 import java.util.List;
 import java.util.Map.Entry;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
 import javax.persistence.EntityManager;
 import javax.persistence.Query;
@@ -244,7 +245,8 @@ public class EntityManagerContainer extends EntityManagerContainerBasic {
 			CriteriaQuery<T> cq = cb.createQuery(cls);
 			Root<T> root = cq.from(cls);
 			Predicate p = cb.equal(root.get(field.getName()), flag);
-			List<T> list = em.createQuery(cq.select(root).where(p).distinct(true)).setMaxResults(2).getResultList();
+			List<T> list = em.createQuery(cq.select(root).where(p)).setMaxResults(2).getResultList().stream().distinct()
+					.collect(Collectors.toList());
 			switch (list.size()) {
 			case 0:
 				break;
@@ -252,7 +254,7 @@ public class EntityManagerContainer extends EntityManagerContainerBasic {
 				t = list.get(0);
 				break out;
 			case 2:
-				throw new Exception("flag get multiple entity flag:" + flag + ", class:" + cls.getName()
+				throw new IllegalStateException("flag get multiple entity flag:" + flag + ", class:" + cls.getName()
 						+ ", attribute:" + field.getName() + ".");
 			}
 		}
@@ -265,7 +267,7 @@ public class EntityManagerContainer extends EntityManagerContainerBasic {
 
 	private <T extends JpaObject> List<T> flag(List<String> FLAGS, Class<T> cls, List<Field> fields) throws Exception {
 		if (ListTools.isEmpty(fields)) {
-			throw new Exception("attributes can not be empty.");
+			throw new IllegalStateException("attributes can not be empty.");
 		}
 		List<T> list = new ArrayList<>();
 		if (ListTools.isEmpty(FLAGS)) {
@@ -582,9 +584,7 @@ public class EntityManagerContainer extends EntityManagerContainerBasic {
 		CriteriaQuery<T> cq = cb.createQuery(cls);
 		Root<T> root = cq.from(cls);
 		cq.select(root).where(cb.isMember(root.get(attribute), cb.literal(values)));
-		List<T> os = em.createQuery(cq.distinct(true)).getResultList();
-		List<T> list = new ArrayList<>(os);
-		return list;
+		return new ArrayList<>(em.createQuery(cq).getResultList().stream().distinct().collect(Collectors.toList()));
 	}
 
 	public <T extends JpaObject> List<T> listIsMember(Class<T> cls, String attribute, Object value) throws Exception {
@@ -888,9 +888,8 @@ public class EntityManagerContainer extends EntityManagerContainerBasic {
 		CriteriaQuery<String> cq = cb.createQuery(String.class);
 		Root<T> root = cq.from(cls);
 		cq.select(root.get(JpaObject.id_FIELDNAME)).where(cb.isMember(root.get(attribute), cb.literal(values)));
-		List<String> os = em.createQuery(cq.distinct(true)).getResultList();
-		List<String> list = new ArrayList<>(os);
-		return list;
+		return new ArrayList<>(
+				em.createQuery(cq).getResultList().stream().distinct().collect(Collectors.toList()));
 	}
 
 	public <T extends JpaObject, W extends Object> List<String> idsNotIn(Class<T> cls, String attribute,
@@ -900,9 +899,7 @@ public class EntityManagerContainer extends EntityManagerContainerBasic {
 		CriteriaQuery<String> cq = cb.createQuery(String.class);
 		Root<T> root = cq.from(cls);
 		cq.select(root.get(JpaObject.id_FIELDNAME)).where(cb.not(root.get(attribute).in(values)));
-		List<String> os = em.createQuery(cq.distinct(true)).getResultList();
-		List<String> list = new ArrayList<>(os);
-		return list;
+		return new ArrayList<>(em.createQuery(cq).getResultList().stream().distinct().collect(Collectors.toList()));
 	}
 
 	public <T extends JpaObject, W extends Object> List<String> idsEqualAndNotIn(Class<T> cls, String attribute,
@@ -914,9 +911,7 @@ public class EntityManagerContainer extends EntityManagerContainerBasic {
 		Predicate p = cb.not(root.get(attribute).in(values));
 		p = cb.and(p, cb.equal(root.get(otherAttribute), otherValue));
 		cq.select(root.get(JpaObject.id_FIELDNAME)).where(p);
-		List<String> os = em.createQuery(cq.distinct(true)).getResultList();
-		List<String> list = new ArrayList<>(os);
-		return list;
+		return new ArrayList<>(em.createQuery(cq).getResultList().stream().distinct().collect(Collectors.toList()));
 	}
 
 	public <T extends JpaObject> List<String> idsIsMember(Class<T> cls, String attribute, Object value)

+ 1 - 1
o2server/x_base_core_project/src/main/java/com/x/base/core/container/factory/SlicePropertiesBuilder.java

@@ -10,7 +10,7 @@ public class SlicePropertiesBuilder {
 
 	public static String driver_db2 = "com.ibm.db2.jcc.DB2Driver";
 	public static String driver_oracle = "oracle.jdbc.OracleDriver";
-	public static String driver_mysql = "com.mysql.jdbc.Driver";
+	public static String driver_mysql = "com.mysql.cj.jdbc.Driver";
 	public static String driver_postgresql = "org.postgresql.Driver";
 	public static String driver_informix = "com.informix.jdbc.IfxDriver";
 	public static String driver_h2 = "org.h2.Driver";

+ 198 - 6
o2server/x_base_core_project/src/main/java/com/x/base/core/project/Applications.java

@@ -15,6 +15,7 @@ import java.util.stream.Stream;
 import java.util.zip.CRC32;
 
 import com.x.base.core.project.config.Config;
+import com.x.base.core.project.config.Nodes;
 import com.x.base.core.project.connection.ActionResponse;
 import com.x.base.core.project.connection.CipherConnectionAction;
 import com.x.base.core.project.connection.FilePart;
@@ -26,6 +27,7 @@ import com.x.base.core.project.tools.DefaultCharset;
 import com.x.base.core.project.tools.ListTools;
 import com.x.base.core.project.tools.StringTools;
 
+import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.StringUtils;
 
 public class Applications extends ConcurrentHashMap<String, CopyOnWriteArrayList<Application>> {
@@ -109,6 +111,25 @@ public class Applications extends ConcurrentHashMap<String, CopyOnWriteArrayList
 
 	public ActionResponse getQuery(Boolean xdebugger, String applicationName, String uri, String seed)
 			throws Exception {
+		if(applicationName.equalsIgnoreCase("x_program_center")|| applicationName.equalsIgnoreCase(x_program_center.class.getName())) {
+			Nodes nodes = Config.nodes();
+			for (String node : nodes.keySet()) {
+				if (nodes.get(node).getCenter().getEnable()) {
+					Integer port = nodes.get(node).getCenter().getPort();
+					StringBuffer buffer = new StringBuffer();
+					if (BooleanUtils.isTrue(nodes.get(node).getCenter().getSslEnable())) {
+						buffer.append("https://").append(StringUtils.isNotEmpty(node) ? node : "127.0.0.1")
+								.append(":" + port);
+					} else {
+						buffer.append("http://").append(StringUtils.isNotEmpty(node) ? node : "127.0.0.1")
+								.append(":" + port);
+					}
+					buffer.append("/x_program_center/jaxrs/");
+					return CipherConnectionAction.get(xdebugger, buffer.toString()+ CipherConnectionAction.trim(uri));
+
+				}
+			}
+		}
 		String name = this.findApplicationName(applicationName);
 		if (StringUtils.isEmpty(name)) {
 			throw new ExceptionFindApplicationName(applicationName);
@@ -162,6 +183,27 @@ public class Applications extends ConcurrentHashMap<String, CopyOnWriteArrayList
 
 	public byte[] getQueryBinary(Boolean xdebugger, String applicationName, String uri, String seed) throws Exception {
 		String name = this.findApplicationName(applicationName);
+
+		if(applicationName.equalsIgnoreCase("x_program_center")|| applicationName.equalsIgnoreCase(x_program_center.class.getName())) {
+			Nodes nodes = Config.nodes();
+			for (String node : nodes.keySet()) {
+				if (nodes.get(node).getCenter().getEnable()) {
+					Integer port = nodes.get(node).getCenter().getPort();
+					StringBuffer buffer = new StringBuffer();
+					if (BooleanUtils.isTrue(nodes.get(node).getCenter().getSslEnable())) {
+						buffer.append("https://").append(StringUtils.isNotEmpty(node) ? node : "127.0.0.1")
+								.append(":" + port);
+					} else {
+						buffer.append("http://").append(StringUtils.isNotEmpty(node) ? node : "127.0.0.1")
+								.append(":" + port);
+					}
+					buffer.append("/x_program_center/jaxrs/");
+					return CipherConnectionAction.getBinary(xdebugger,
+							buffer.toString() + CipherConnectionAction.trim(uri));
+				}
+			}
+		}
+
 		if (StringUtils.isEmpty(name)) {
 			throw new ExceptionFindApplicationName(applicationName);
 		}
@@ -215,6 +257,26 @@ public class Applications extends ConcurrentHashMap<String, CopyOnWriteArrayList
 
 	public ActionResponse deleteQuery(Boolean xdebugger, String applicationName, String uri, String seed)
 			throws Exception {
+		if(applicationName.equalsIgnoreCase("x_program_center")|| applicationName.equalsIgnoreCase(x_program_center.class.getName())) {
+			Nodes nodes = Config.nodes();
+			for (String node : nodes.keySet()) {
+				if (nodes.get(node).getCenter().getEnable()) {
+					Integer port = nodes.get(node).getCenter().getPort();
+					StringBuffer buffer = new StringBuffer();
+					if (BooleanUtils.isTrue(nodes.get(node).getCenter().getSslEnable())) {
+						buffer.append("https://").append(StringUtils.isNotEmpty(node) ? node : "127.0.0.1")
+								.append(":" + port);
+					} else {
+						buffer.append("http://").append(StringUtils.isNotEmpty(node) ? node : "127.0.0.1")
+								.append(":" + port);
+					}
+					buffer.append("/x_program_center/jaxrs/");
+
+					return CipherConnectionAction.delete(xdebugger,
+							buffer.toString() + CipherConnectionAction.trim(uri));
+				}
+			}
+		}
 		String name = this.findApplicationName(applicationName);
 		if (StringUtils.isEmpty(name)) {
 			throw new ExceptionFindApplicationName(applicationName);
@@ -269,6 +331,26 @@ public class Applications extends ConcurrentHashMap<String, CopyOnWriteArrayList
 
 	public byte[] deleteQueryBinary(Boolean xdebugger, String applicationName, String uri, String seed)
 			throws Exception {
+		if(applicationName.equalsIgnoreCase("x_program_center")|| applicationName.equalsIgnoreCase(x_program_center.class.getName())) {
+			Nodes nodes = Config.nodes();
+			for (String node : nodes.keySet()) {
+				if (nodes.get(node).getCenter().getEnable()) {
+					Integer port = nodes.get(node).getCenter().getPort();
+					StringBuffer buffer = new StringBuffer();
+					if (BooleanUtils.isTrue(nodes.get(node).getCenter().getSslEnable())) {
+						buffer.append("https://").append(StringUtils.isNotEmpty(node) ? node : "127.0.0.1")
+								.append(":" + port);
+					} else {
+						buffer.append("http://").append(StringUtils.isNotEmpty(node) ? node : "127.0.0.1")
+								.append(":" + port);
+					}
+					buffer.append("/x_program_center/jaxrs/");
+
+					return CipherConnectionAction.deleteBinary(xdebugger,
+							buffer.toString()  + CipherConnectionAction.trim(uri));
+				}
+			}
+		}
 		String name = this.findApplicationName(applicationName);
 		if (StringUtils.isEmpty(name)) {
 			throw new ExceptionFindApplicationName(applicationName);
@@ -325,6 +407,26 @@ public class Applications extends ConcurrentHashMap<String, CopyOnWriteArrayList
 
 	public ActionResponse postQuery(Boolean xdebugger, String applicationName, String uri, Object body, String seed)
 			throws Exception {
+		if(applicationName.equalsIgnoreCase("x_program_center")|| applicationName.equalsIgnoreCase(x_program_center.class.getName())) {
+			Nodes nodes = Config.nodes();
+			for (String node : nodes.keySet()) {
+				if (nodes.get(node).getCenter().getEnable()) {
+					Integer port = nodes.get(node).getCenter().getPort();
+					StringBuffer buffer = new StringBuffer();
+					if (BooleanUtils.isTrue(nodes.get(node).getCenter().getSslEnable())) {
+						buffer.append("https://").append(StringUtils.isNotEmpty(node) ? node : "127.0.0.1")
+								.append(":" + port);
+					} else {
+						buffer.append("http://").append(StringUtils.isNotEmpty(node) ? node : "127.0.0.1")
+								.append(":" + port);
+					}
+					buffer.append("/x_program_center/jaxrs/");
+
+					return CipherConnectionAction.post(xdebugger, buffer.toString()  + CipherConnectionAction.trim(uri),
+							body);
+				}
+			}
+		}
 		String name = this.findApplicationName(applicationName);
 		if (StringUtils.isEmpty(name)) {
 			throw new ExceptionFindApplicationName(applicationName);
@@ -381,6 +483,27 @@ public class Applications extends ConcurrentHashMap<String, CopyOnWriteArrayList
 
 	public byte[] postQueryBinary(Boolean xdebugger, String applicationName, String uri, Object body, String seed)
 			throws Exception {
+		if(applicationName.equalsIgnoreCase("x_program_center")|| applicationName.equalsIgnoreCase(x_program_center.class.getName())) {
+			Nodes nodes = Config.nodes();
+			for (String node : nodes.keySet()) {
+				if (nodes.get(node).getCenter().getEnable()) {
+					Integer port = nodes.get(node).getCenter().getPort();
+					StringBuffer buffer = new StringBuffer();
+					if (BooleanUtils.isTrue(nodes.get(node).getCenter().getSslEnable())) {
+						buffer.append("https://").append(StringUtils.isNotEmpty(node) ? node : "127.0.0.1")
+								.append(":" + port);
+					} else {
+						buffer.append("http://").append(StringUtils.isNotEmpty(node) ? node : "127.0.0.1")
+								.append(":" + port);
+					}
+					buffer.append("/x_program_center/jaxrs/");
+
+					return CipherConnectionAction.postBinary(xdebugger,
+							buffer.toString()  + CipherConnectionAction.trim(uri), body);
+				}
+			}
+		}
+
 		String name = this.findApplicationName(applicationName);
 		if (StringUtils.isEmpty(name)) {
 			throw new ExceptionFindApplicationName(applicationName);
@@ -442,6 +565,27 @@ public class Applications extends ConcurrentHashMap<String, CopyOnWriteArrayList
 
 	public byte[] postQueryMultiPartBinary(Boolean xdebugger, String applicationName, String uri,
 			Collection<FormField> formFields, Collection<FilePart> fileParts, String seed) throws Exception {
+		if(applicationName.equalsIgnoreCase("x_program_center")|| applicationName.equalsIgnoreCase(x_program_center.class.getName())) {
+			Nodes nodes = Config.nodes();
+			for (String node : nodes.keySet()) {
+				if (nodes.get(node).getCenter().getEnable()) {
+					Integer port = nodes.get(node).getCenter().getPort();
+					StringBuffer buffer = new StringBuffer();
+					if (BooleanUtils.isTrue(nodes.get(node).getCenter().getSslEnable())) {
+						buffer.append("https://").append(StringUtils.isNotEmpty(node) ? node : "127.0.0.1")
+								.append(":" + port);
+					} else {
+						buffer.append("http://").append(StringUtils.isNotEmpty(node) ? node : "127.0.0.1")
+								.append(":" + port);
+					}
+					buffer.append("/x_program_center/jaxrs/");
+
+					return CipherConnectionAction.postMultiPartBinary(xdebugger,
+							buffer.toString() + CipherConnectionAction.trim(uri), formFields, fileParts);
+				}
+			}
+		}
+
 		String name = this.findApplicationName(applicationName);
 		if (StringUtils.isEmpty(name)) {
 			throw new ExceptionFindApplicationName(applicationName);
@@ -498,6 +642,28 @@ public class Applications extends ConcurrentHashMap<String, CopyOnWriteArrayList
 
 	public ActionResponse putQuery(Boolean xdebugger, String applicationName, String uri, Object body, String seed)
 			throws Exception {
+
+		if(applicationName.equalsIgnoreCase("x_program_center")|| applicationName.equalsIgnoreCase(x_program_center.class.getName())) {
+			Nodes nodes = Config.nodes();
+			for (String node : nodes.keySet()) {
+				if (nodes.get(node).getCenter().getEnable()) {
+					Integer port = nodes.get(node).getCenter().getPort();
+					StringBuffer buffer = new StringBuffer();
+					if (BooleanUtils.isTrue(nodes.get(node).getCenter().getSslEnable())) {
+						buffer.append("https://").append(StringUtils.isNotEmpty(node) ? node : "127.0.0.1")
+								.append(":" + port);
+					} else {
+						buffer.append("http://").append(StringUtils.isNotEmpty(node) ? node : "127.0.0.1")
+								.append(":" + port);
+					}
+					buffer.append("/x_program_center/jaxrs/");
+
+					return CipherConnectionAction.put(xdebugger, buffer.toString() + CipherConnectionAction.trim(uri),
+							body);
+				}
+			}
+		}
+
 		String name = this.findApplicationName(applicationName);
 		if (StringUtils.isEmpty(name)) {
 			throw new ExceptionFindApplicationName(applicationName);
@@ -522,7 +688,10 @@ public class Applications extends ConcurrentHashMap<String, CopyOnWriteArrayList
 				return str;
 			}
 		}
-		return null;
+		if(name.equalsIgnoreCase("x_program_center")|| name.equalsIgnoreCase(x_program_center.class.getName())) {
+			return "x_program_center";
+		}
+			return null;
 	}
 
 	public Application findApplicationWithNode(String className, String node) throws Exception {
@@ -602,12 +771,35 @@ public class Applications extends ConcurrentHashMap<String, CopyOnWriteArrayList
 	}
 
 	public String describeApi(String name) throws Exception {
-		String applicationName = this.findApplicationName(name);
-		if (StringUtils.isEmpty(applicationName)) {
-			throw new Exception("getDescribe can not find application with name:" + name + ".");
+		String urlDescribeApiJson = "";
+		if(name.equalsIgnoreCase("x_program_center")|| name.equalsIgnoreCase(x_program_center.class.getName())) {
+			 Nodes nodes = Config.nodes();
+			for (String node : nodes.keySet()) {
+				if (nodes.get(node).getCenter().getEnable()) {
+					Integer port = nodes.get(node).getCenter().getPort();
+					StringBuffer buffer = new StringBuffer();
+					if (BooleanUtils.isTrue(nodes.get(node).getCenter().getSslEnable())) {
+						buffer.append("https://").append(StringUtils.isNotEmpty(node) ? node : "127.0.0.1")
+								.append(":" + port);
+					} else {
+						buffer.append("http://").append(StringUtils.isNotEmpty(node) ? node : "127.0.0.1")
+								.append(":" + port);
+					}
+					buffer.append("/x_program_center/describe/api.json");
+					urlDescribeApiJson = buffer.toString();
+					break;
+				}
+			}
+		}else {
+			String applicationName = this.findApplicationName(name);
+			if (StringUtils.isEmpty(applicationName)) {
+				throw new Exception("getDescribe can not find application with name:" + name + ".");
+			}
+			Application application = this.randomWithWeight(applicationName);
+			//return HttpConnection.getAsString(application.getUrlDescribeApiJson(), null);
+			urlDescribeApiJson = application.getUrlDescribeApiJson();
 		}
-		Application application = this.randomWithWeight(applicationName);
-		return HttpConnection.getAsString(application.getUrlDescribeApiJson(), null);
+		return HttpConnection.getAsString(urlDescribeApiJson, null);
 	}
 
 }

+ 7 - 5
o2server/x_base_core_project/src/main/java/com/x/base/core/project/build/CreateConfigSample.java

@@ -11,8 +11,14 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.commons.lang3.reflect.MethodUtils;
+
 import com.x.base.core.project.annotation.FieldDescribe;
 import com.x.base.core.project.config.AppStyle;
+import com.x.base.core.project.config.Cache;
 import com.x.base.core.project.config.CenterServer;
 import com.x.base.core.project.config.ClientInit;
 import com.x.base.core.project.config.Collect;
@@ -42,11 +48,6 @@ import com.x.base.core.project.logger.Logger;
 import com.x.base.core.project.logger.LoggerFactory;
 import com.x.base.core.project.tools.DefaultCharset;
 
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.reflect.FieldUtils;
-import org.apache.commons.lang3.reflect.MethodUtils;
-
 public class CreateConfigSample {
 
 	private static Logger logger = LoggerFactory.getLogger(CreateConfigSample.class);
@@ -79,6 +80,7 @@ public class CreateConfigSample {
 		classes.add(Vfs.class);
 		classes.add(WorkTime.class);
 		classes.add(ZhengwuDingding.class);
+		classes.add(Cache.class);
 
 		Collections.sort(classes, new Comparator<Class<?>>() {
 			public int compare(Class<?> c1, Class<?> c2) {

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

@@ -10,6 +10,7 @@ import java.util.Objects;
 
 import com.x.base.core.project.connection.ActionResponse;
 import com.x.base.core.project.http.ActionResult;
+import com.x.base.core.project.tools.BaseTools;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.BooleanUtils;
@@ -245,6 +246,7 @@ public class Collect extends ConfigObject {
 	public void save() throws Exception {
 		File file = new File(Config.base(), Config.PATH_CONFIG_COLLECT);
 		FileUtils.write(file, XGsonBuilder.toJson(this), DefaultCharset.charset);
+		BaseTools.executeSyncFile(Config.PATH_CONFIG_COLLECT);
 	}
 
 	public void setSecret(String secret) {

+ 60 - 62
o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/Components.java

@@ -20,7 +20,7 @@ public class Components extends ConfigObject {
 	public static final String NAME_PORTALEXPLORER = "PortalExplorer";
 	public static final String NAME_DATAEXPLORER = "DataExplorer";
 	public static final String NAME_SERVICEMANAGER = "service.ServiceManager";
-	public static final String NAME_APPMARKET = "AppMarket";
+	public static final String NAME_APPMARKET = "AppMarketV2";
 	public static final String NAME_APPCENTER = "AppCenter";
 	public static final String NAME_LOGVIEWER = "LogViewer";
 	public static final String NAME_PROFILE = "Profile";
@@ -50,66 +50,65 @@ public class Components extends ConfigObject {
 
 	public static Component systemComponent(String name) {
 		switch (name) {
-			case NAME_SETTING:
-				return new Component(NAME_SETTING, NAME_SETTING, "系统设置", APPICON_PNG, 1, Component.TYPE_SYSTEM);
-			case NAME_ORG:
-				return new Component(NAME_ORG, NAME_ORG, "组织管理", APPICON_PNG, 2, Component.TYPE_SYSTEM);
-			case NAME_CMSMANAGER:
-				return new Component(NAME_CMSMANAGER, "cms.Column", "内容管理平台", APPICON_PNG, 3, Component.TYPE_SYSTEM);
-			case NAME_APPLICATIONEXPLORER:
-				return new Component(NAME_APPLICATIONEXPLORER, "process.ApplicationExplorer", "流程管理平台", APPICON_PNG, 4,
-						Component.TYPE_SYSTEM);
-			case NAME_PORTALEXPLORER:
-				return new Component(NAME_PORTALEXPLORER, "portal.PortalExplorer", "门户管理平台", APPICON_PNG, 5,
-						Component.TYPE_SYSTEM);
-			case NAME_DATAEXPLORER:
-				return new Component(NAME_DATAEXPLORER, "query.QueryExplorer", "数据中心平台", APPICON_PNG, 6,
-						Component.TYPE_SYSTEM);
-			case NAME_SERVICEMANAGER:
-				return new Component(NAME_SERVICEMANAGER, NAME_SERVICEMANAGER, "服务管理平台", APPICON_PNG, 7,
-						Component.TYPE_SYSTEM);
-			case NAME_APPMARKET:
-				return new Component(NAME_APPMARKET, NAME_APPMARKET, "应用市场", APPICON_PNG, 8, Component.TYPE_SYSTEM);
-			case NAME_APPCENTER:
-				return new Component(NAME_APPCENTER, NAME_APPCENTER, "应用管理", APPICON_PNG, 9, Component.TYPE_SYSTEM);
-			case NAME_LOGVIEWER:
-				return new Component(NAME_LOGVIEWER, NAME_LOGVIEWER, "日志", APPICON_PNG, 10, Component.TYPE_SYSTEM);
-			case NAME_PROFILE:
-				return new Component(NAME_PROFILE, NAME_PROFILE, "个人设置", APPICON_PNG, 11, Component.TYPE_SYSTEM);
-			case NAME_BAM:
-				return new Component(NAME_BAM, NAME_BAM, "流程监控", APPICON_PNG, 12, Component.TYPE_SYSTEM);
-			case NAME_CMS:
-				return new Component(NAME_CMS, "cms.Index", "信息平台", APPICON_PNG, 12, Component.TYPE_SYSTEM);
-			case NAME_TASKCENTER:
-				return new Component(NAME_TASKCENTER, "process.TaskCenter", "办公中心", APPICON_PNG, 13,
-						Component.TYPE_SYSTEM);
-			case NAME_HOMEPAGE:
-				return new Component(NAME_HOMEPAGE, NAME_HOMEPAGE, "首页", APPICON_PNG, 14, Component.TYPE_SYSTEM);
-			case NAME_HOTARTICLE:
-				return new Component(NAME_HOTARTICLE, NAME_HOTARTICLE, "热点", APPICON_PNG, 15, Component.TYPE_SYSTEM);
-			case NAME_FILE:
-				return new Component(NAME_FILE, NAME_FILE, "云文件", APPICON_PNG, 16, Component.TYPE_SYSTEM);
-			case NAME_NOTE:
-				return new Component(NAME_NOTE, NAME_NOTE, "便签", APPICON_PNG, 17, Component.TYPE_SYSTEM);
-			case NAME_MEETING:
-				return new Component(NAME_MEETING, NAME_MEETING, "会议管理", APPICON_PNG, 18, Component.TYPE_SYSTEM);
-			case NAME_ONLINEMEETING:
-				return new Component(NAME_ONLINEMEETING, NAME_ONLINEMEETING, "网络会议", APPICON_PNG, 19,
-						Component.TYPE_SYSTEM);
-			case NAME_ATTENDANCE:
-				return new Component(NAME_ATTENDANCE, NAME_ATTENDANCE, "考勤管理", APPICON_PNG, 20, Component.TYPE_SYSTEM);
-			case NAME_FORUM:
-				return new Component(NAME_FORUM, NAME_FORUM, "论坛", APPICON_PNG, 21, Component.TYPE_SYSTEM);
-			case NAME_MINDER:
-				return new Component(NAME_MINDER, NAME_MINDER, "脑图编辑器", APPICON_PNG, 22, Component.TYPE_SYSTEM);
-			case NAME_CALENDAR:
-				return new Component(NAME_CALENDAR, NAME_CALENDAR, "日程安排", APPICON_PNG, 23, Component.TYPE_SYSTEM);
-			case NAME_ANN:
-				return new Component(NAME_ANN, NAME_ANN, "神经网络", APPICON_PNG, 24, Component.TYPE_SYSTEM);
-			case NAME_SEARCH:
-				return new Component(NAME_SEARCH, NAME_SEARCH, "搜索", APPICON_PNG, 25, Component.TYPE_SYSTEM);
-			default:
-				return null;
+		case NAME_SETTING:
+			return new Component(NAME_SETTING, NAME_SETTING, "系统设置", APPICON_PNG, 1, Component.TYPE_SYSTEM);
+		case NAME_ORG:
+			return new Component(NAME_ORG, NAME_ORG, "组织管理", APPICON_PNG, 2, Component.TYPE_SYSTEM);
+		case NAME_CMSMANAGER:
+			return new Component(NAME_CMSMANAGER, "cms.Column", "内容管理平台", APPICON_PNG, 3, Component.TYPE_SYSTEM);
+		case NAME_APPLICATIONEXPLORER:
+			return new Component(NAME_APPLICATIONEXPLORER, "process.ApplicationExplorer", "流程管理平台", APPICON_PNG, 4,
+					Component.TYPE_SYSTEM);
+		case NAME_PORTALEXPLORER:
+			return new Component(NAME_PORTALEXPLORER, "portal.PortalExplorer", "门户管理平台", APPICON_PNG, 5,
+					Component.TYPE_SYSTEM);
+		case NAME_DATAEXPLORER:
+			return new Component(NAME_DATAEXPLORER, "query.QueryExplorer", "数据中心平台", APPICON_PNG, 6,
+					Component.TYPE_SYSTEM);
+		case NAME_SERVICEMANAGER:
+			return new Component(NAME_SERVICEMANAGER, NAME_SERVICEMANAGER, "服务管理平台", APPICON_PNG, 7,
+					Component.TYPE_SYSTEM);
+		case NAME_APPMARKET:
+			return new Component(NAME_APPMARKET, NAME_APPMARKET, "应用市场", APPICON_PNG, 8, Component.TYPE_SYSTEM);
+		case NAME_APPCENTER:
+			return new Component(NAME_APPCENTER, NAME_APPCENTER, "应用管理", APPICON_PNG, 9, Component.TYPE_SYSTEM);
+		case NAME_LOGVIEWER:
+			return new Component(NAME_LOGVIEWER, NAME_LOGVIEWER, "日志", APPICON_PNG, 10, Component.TYPE_SYSTEM);
+		case NAME_PROFILE:
+			return new Component(NAME_PROFILE, NAME_PROFILE, "个人设置", APPICON_PNG, 11, Component.TYPE_SYSTEM);
+		case NAME_BAM:
+			return new Component(NAME_BAM, NAME_BAM, "流程监控", APPICON_PNG, 12, Component.TYPE_SYSTEM);
+		case NAME_CMS:
+			return new Component(NAME_CMS, "cms.Index", "信息平台", APPICON_PNG, 12, Component.TYPE_SYSTEM);
+		case NAME_TASKCENTER:
+			return new Component(NAME_TASKCENTER, "process.TaskCenter", "办公中心", APPICON_PNG, 13, Component.TYPE_SYSTEM);
+		case NAME_HOMEPAGE:
+			return new Component(NAME_HOMEPAGE, NAME_HOMEPAGE, "首页", APPICON_PNG, 14, Component.TYPE_SYSTEM);
+		case NAME_HOTARTICLE:
+			return new Component(NAME_HOTARTICLE, NAME_HOTARTICLE, "热点", APPICON_PNG, 15, Component.TYPE_SYSTEM);
+		case NAME_FILE:
+			return new Component(NAME_FILE, NAME_FILE, "云文件", APPICON_PNG, 16, Component.TYPE_SYSTEM);
+		case NAME_NOTE:
+			return new Component(NAME_NOTE, NAME_NOTE, "便签", APPICON_PNG, 17, Component.TYPE_SYSTEM);
+		case NAME_MEETING:
+			return new Component(NAME_MEETING, NAME_MEETING, "会议管理", APPICON_PNG, 18, Component.TYPE_SYSTEM);
+		case NAME_ONLINEMEETING:
+			return new Component(NAME_ONLINEMEETING, NAME_ONLINEMEETING, "网络会议", APPICON_PNG, 19,
+					Component.TYPE_SYSTEM);
+		case NAME_ATTENDANCE:
+			return new Component(NAME_ATTENDANCE, NAME_ATTENDANCE, "考勤管理", APPICON_PNG, 20, Component.TYPE_SYSTEM);
+		case NAME_FORUM:
+			return new Component(NAME_FORUM, NAME_FORUM, "论坛", APPICON_PNG, 21, Component.TYPE_SYSTEM);
+		case NAME_MINDER:
+			return new Component(NAME_MINDER, NAME_MINDER, "脑图编辑器", APPICON_PNG, 22, Component.TYPE_SYSTEM);
+		case NAME_CALENDAR:
+			return new Component(NAME_CALENDAR, NAME_CALENDAR, "日程安排", APPICON_PNG, 23, Component.TYPE_SYSTEM);
+		case NAME_ANN:
+			return new Component(NAME_ANN, NAME_ANN, "神经网络", APPICON_PNG, 24, Component.TYPE_SYSTEM);
+		case NAME_SEARCH:
+			return new Component(NAME_SEARCH, NAME_SEARCH, "搜索", APPICON_PNG, 25, Component.TYPE_SYSTEM);
+		default:
+			return null;
 		}
 	}
 
@@ -245,7 +244,6 @@ public class Components extends ConfigObject {
 		public void setDentyList(List<String> dentyList) {
 			this.dentyList = dentyList;
 		}
- 
 
 		public String getType() {
 			return type;

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

@@ -1113,7 +1113,7 @@ public class Config {
 		}
 		return instance().mq;
 	}
-	
+
 	private Vfs vfs;
 
 	public static Vfs vfs() throws Exception {

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

@@ -4,6 +4,7 @@ import java.io.File;
 import java.util.Calendar;
 import java.util.Date;
 
+import com.x.base.core.project.tools.BaseTools;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -223,6 +224,7 @@ public class Dingding extends ConfigObject {
 	public void save() throws Exception {
 		File file = new File(Config.base(), Config.PATH_CONFIG_DINGDING);
 		FileUtils.write(file, XGsonBuilder.toJson(this), DefaultCharset.charset);
+		BaseTools.executeSyncFile(Config.PATH_CONFIG_DINGDING);
 	}
 
 	public void setEnable(Boolean enable) {

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

@@ -4,6 +4,7 @@ import java.io.File;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import com.x.base.core.project.tools.BaseTools;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -247,6 +248,7 @@ public class Person extends ConfigObject {
 	public void save() throws Exception {
 		File file = new File(Config.base(), Config.PATH_CONFIG_PERSON);
 		FileUtils.write(file, XGsonBuilder.toJson(this), DefaultCharset.charset);
+		BaseTools.executeSyncFile(Config.PATH_CONFIG_PERSON);
 	}
 
 	public void setCodeLogin(Boolean codeLogin) {

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

@@ -5,6 +5,7 @@ import java.util.LinkedHashMap;
 
 import com.x.base.core.project.annotation.FieldDescribe;
 import com.x.base.core.project.gson.XGsonBuilder;
+import com.x.base.core.project.tools.BaseTools;
 import com.x.base.core.project.tools.DefaultCharset;
 
 import org.apache.commons.io.FileUtils;
@@ -141,6 +142,7 @@ public class Portal extends ConfigObject {
 	public void save() throws Exception {
 		File file = new File(Config.base(), Config.PATH_CONFIG_PORTAL);
 		FileUtils.write(file, XGsonBuilder.toJson(this), DefaultCharset.charset);
+		BaseTools.executeSyncFile(Config.PATH_CONFIG_PORTAL);
 	}
 
 	public LinkedHashMap<String, String> getUrlMapping() {

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

@@ -5,16 +5,16 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
 
-import com.x.base.core.project.annotation.FieldDescribe;
-import com.x.base.core.project.gson.XGsonBuilder;
-import com.x.base.core.project.tools.DefaultCharset;
-import com.x.base.core.project.tools.ListTools;
-
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.quartz.CronExpression;
 
+import com.x.base.core.project.annotation.FieldDescribe;
+import com.x.base.core.project.gson.XGsonBuilder;
+import com.x.base.core.project.tools.DefaultCharset;
+import com.x.base.core.project.tools.ListTools;
+
 /**
  * @author Zhou Rui
  */
@@ -48,14 +48,15 @@ public class ProcessPlatform extends ConfigObject {
 
 	public static final Boolean DEFAULT_DELETEAPPLICATIONINUSE = false;
 
-	public final static Boolean DEFAULT_UPDATEDATAPROJECTIONENABLE = false;
+	public static final Boolean DEFAULT_UPDATEDATAPROJECTIONENABLE = false;
+
+	public static final Boolean DEFAULT_PROCESSINGSIGNALPERSISTENABLE = false;
 
 	public static ProcessPlatform defaultInstance() {
 		return new ProcessPlatform();
 	}
 
 	public ProcessPlatform() {
-
 		this.maintenanceIdentity = "";
 		this.formVersionCount = DEFAULT_FORMVERSIONCOUNT;
 		this.processVersionCount = DEFAULT_PROCESSVERSIONCOUNT;
@@ -72,7 +73,7 @@ public class ProcessPlatform extends ConfigObject {
 		this.touchDetained = new TouchDetained();
 		this.deleteDraft = new DeleteDraft();
 		this.passExpired = new PassExpired();
-
+		this.processingSignalPersistEnable = DEFAULT_PROCESSINGSIGNALPERSISTENABLE;
 	}
 
 	public Integer getExecutorCount() {
@@ -165,6 +166,16 @@ public class ProcessPlatform extends ConfigObject {
 	@FieldDescribe("事件扩充.")
 	private ExtensionEvents extensionEvents;
 
+	@FieldDescribe("是否保存工作处理信号内容,默认false.")
+	private Boolean processingSignalPersistEnable;
+
+	public Boolean getProcessingSignalPersistEnable() {
+		if (processingSignalPersistEnable == null) {
+			this.processingSignalPersistEnable = DEFAULT_PROCESSINGSIGNALPERSISTENABLE;
+		}
+		return processingSignalPersistEnable;
+	}
+
 	public ExtensionEvents getExtensionEvents() {
 		if (null == extensionEvents) {
 			this.extensionEvents = new ExtensionEvents();
@@ -224,9 +235,9 @@ public class ProcessPlatform extends ConfigObject {
 			return o;
 		}
 
-		public final static Boolean DEFAULT_ENABLE = false;
+		public static final Boolean DEFAULT_ENABLE = false;
 
-		public final static String DEFAULT_CRON = "30 0/10 8-18 * * ?";
+		public static final String DEFAULT_CRON = "30 0/10 8-18 * * ?";
 
 		@FieldDescribe("是否启用")
 		private Boolean enable = DEFAULT_ENABLE;
@@ -262,9 +273,9 @@ public class ProcessPlatform extends ConfigObject {
 			return o;
 		}
 
-		public final static Boolean DEFAULT_ENABLE = true;
+		public static final Boolean DEFAULT_ENABLE = true;
 
-		public final static String DEFAULT_CRON = "45 0/15 8-18 * * ?";
+		public static final String DEFAULT_CRON = "45 0/15 8-18 * * ?";
 
 		@FieldDescribe("是否启用")
 		private Boolean enable = DEFAULT_ENABLE;
@@ -293,9 +304,9 @@ public class ProcessPlatform extends ConfigObject {
 			return o;
 		}
 
-		public final static Boolean DEFAULT_ENABLE = true;
+		public static final Boolean DEFAULT_ENABLE = true;
 
-		public final static String DEFAULT_CRON = "5 0/5 * * * ?";
+		public static final String DEFAULT_CRON = "5 0/5 * * * ?";
 
 		@FieldDescribe("是否启用")
 		private Boolean enable = DEFAULT_ENABLE;
@@ -324,11 +335,11 @@ public class ProcessPlatform extends ConfigObject {
 			return o;
 		}
 
-		public final static Boolean DEFAULT_ENABLE = false;
+		public static final Boolean DEFAULT_ENABLE = false;
 
-		public final static String DEFAULT_CRON = "30 30 6 * * ?";
+		public static final String DEFAULT_CRON = "30 30 6 * * ?";
 
-		public final static Integer DEFAULT_THRESHOLDDAYS = 365 * 2;
+		public static final Integer DEFAULT_THRESHOLDDAYS = 365 * 2;
 
 		@FieldDescribe("是否启用")
 		private Boolean enable = DEFAULT_ENABLE;
@@ -364,11 +375,11 @@ public class ProcessPlatform extends ConfigObject {
 			return o;
 		}
 
-		public final static String DEFAULT_CRON = "30 30 12 * * ?";
+		public static final String DEFAULT_CRON = "30 30 12 * * ?";
 
-		public final static Boolean DEFAULT_ENABLE = true;
+		public static final Boolean DEFAULT_ENABLE = true;
 
-		public final static Integer DEFAULT_THRESHOLDMINUTES = 60 * 24;
+		public static final Integer DEFAULT_THRESHOLDMINUTES = 60 * 24;
 
 		@FieldDescribe("是否启用")
 		private Boolean enable = DEFAULT_ENABLE;
@@ -404,11 +415,11 @@ public class ProcessPlatform extends ConfigObject {
 			return o;
 		}
 
-		public final static String DEFAULT_CRON = "0 0 20 * * ?";
+		public static final String DEFAULT_CRON = "0 0 20 * * ?";
 
-		public final static Boolean DEFAULT_ENABLE = false;
+		public static final Boolean DEFAULT_ENABLE = false;
 
-		public final static Integer DEFAULT_THRESHOLDMINUTES = 60 * 24 * 60;
+		public static final Integer DEFAULT_THRESHOLDMINUTES = 60 * 24 * 60;
 
 		@FieldDescribe("是否启用")
 		private Boolean enable = DEFAULT_ENABLE;
@@ -444,9 +455,9 @@ public class ProcessPlatform extends ConfigObject {
 			return o;
 		}
 
-		public final static String DEFAULT_CRON = "5 5 8-18 * * ?";
+		public static final String DEFAULT_CRON = "5 5 8-18 * * ?";
 
-		public final static Boolean DEFAULT_ENABLE = true;
+		public static final Boolean DEFAULT_ENABLE = true;
 
 		@FieldDescribe("是否启用")
 		private Boolean enable = DEFAULT_ENABLE;
@@ -474,15 +485,15 @@ public class ProcessPlatform extends ConfigObject {
 			return o;
 		}
 
-		public final static String DEFAULT_CRON = "0 0 4 * * ?";
+		public static final String DEFAULT_CRON = "0 0 4 * * ?";
 
-		public final static Boolean DEFAULT_ENABLE = true;
+		public static final Boolean DEFAULT_ENABLE = true;
 
-		public final static Integer DEFAULT_TASKTHRESHOLDMINUTES = 60 * 24 * 10;
+		public static final Integer DEFAULT_TASKTHRESHOLDMINUTES = 60 * 24 * 10;
 
-		public final static Integer DEFAULT_READTHRESHOLDMINUTES = 60 * 24 * 10;
+		public static final Integer DEFAULT_READTHRESHOLDMINUTES = 60 * 24 * 10;
 
-		public final static Integer DEFAULT_WORKTHRESHOLDMINUTES = 60 * 24 * 10;
+		public static final Integer DEFAULT_WORKTHRESHOLDMINUTES = 60 * 24 * 10;
 
 		@FieldDescribe("是否启用")
 		private Boolean enable = DEFAULT_ENABLE;
@@ -535,9 +546,9 @@ public class ProcessPlatform extends ConfigObject {
 			return o;
 		}
 
-		public final static Integer DEFAULT_INTERVALMINUTES = 10;
+		public static final Integer DEFAULT_INTERVALMINUTES = 10;
 
-		public final static Integer DEFAULT_COUNT = 3;
+		public static final Integer DEFAULT_COUNT = 3;
 
 		@FieldDescribe("提醒间隔(分钟)")
 		private Integer intervalMinutes = DEFAULT_INTERVALMINUTES;
@@ -565,10 +576,6 @@ public class ProcessPlatform extends ConfigObject {
 
 	public static class ExtensionEvents {
 
-		// public static ExtensionEvents defaultInstance() {
-		// return new ExtensionEvents();
-		// }
-
 		@FieldDescribe("工作附件上传.")
 		private WorkExtensionEvents workAttachmentUploadEvents = new WorkExtensionEvents();
 		@FieldDescribe("工作附件下载.")

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

@@ -4,6 +4,7 @@ import java.io.File;
 import java.util.Calendar;
 import java.util.Date;
 
+import com.x.base.core.project.tools.BaseTools;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -447,6 +448,7 @@ public class Qiyeweixin extends ConfigObject {
 	public void save() throws Exception {
 		File file = new File(Config.base(), Config.PATH_CONFIG_QIYEWEIXIN);
 		FileUtils.write(file, XGsonBuilder.toJson(this), DefaultCharset.charset);
+		BaseTools.executeSyncFile(Config.PATH_CONFIG_QIYEWEIXIN);
 	}
 
 

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

@@ -1,12 +1,14 @@
 package com.x.base.core.project.config;
 
 import java.io.File;
+import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 
+import com.x.base.core.project.tools.BaseTools;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang3.StringUtils;
 
@@ -176,6 +178,7 @@ public class Token extends ConfigObject {
 	public void save() throws Exception {
 		File file = new File(Config.base(), Config.PATH_CONFIG_TOKEN);
 		FileUtils.write(file, XGsonBuilder.toJson(this), DefaultCharset.charset);
+        BaseTools.executeSyncFile(Config.PATH_CONFIG_TOKEN);
 	}
 
 	public boolean isInitialManager(String name) {

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

@@ -31,6 +31,8 @@ public class WebServer extends ConfigObject {
 	private static final Boolean DEFAULT_STATENABLE = false;
 	private static final String DEFAULT_STATEXCLUSIONS = "*.gif,*.jpg,*.png,*.ico";
 	private static final Integer DEFAULT_CACHECONTROLMAXAGE = 0;
+	private static final Boolean DEFAULT_PROXYCENTERENABLE = true;
+	private static final Boolean DEFAULT_PROXYAPPLICATIONENABLE = true;
 
 	@FieldDescribe("是否启用")
 	private Boolean enable;
@@ -53,6 +55,20 @@ public class WebServer extends ConfigObject {
 	@FieldDescribe("服务器max-age缓存时间(秒)")
 	private Integer cacheControlMaxAge;
 
+	@FieldDescribe("是否启用center服务器代理.")
+	private Boolean proxyCenterEnable;
+
+	@FieldDescribe("是否启用application服务器代理")
+	private Boolean proxyApplicationEnable;
+
+	public Boolean getProxyCenterEnable() {
+		return proxyCenterEnable == null ? DEFAULT_PROXYCENTERENABLE : this.proxyCenterEnable;
+	}
+
+	public Boolean getProxyApplicationEnable() {
+		return proxyApplicationEnable == null ? DEFAULT_PROXYAPPLICATIONENABLE : this.proxyApplicationEnable;
+	}
+
 	public Integer getCacheControlMaxAge() {
 		if (cacheControlMaxAge == null || cacheControlMaxAge < 0) {
 			return DEFAULT_CACHECONTROLMAXAGE;
@@ -136,4 +152,12 @@ public class WebServer extends ConfigObject {
 		this.weight = weight;
 	}
 
+	public void setProxyApplicationEnable(Boolean proxyApplicationEnable) {
+		this.proxyApplicationEnable = proxyApplicationEnable;
+	}
+
+	public void setProxyCenterEnable(Boolean proxyCenterEnable) {
+		this.proxyCenterEnable = proxyCenterEnable;
+	}
+
 }

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

@@ -1,8 +1,8 @@
 package com.x.base.core.project.http;
 
 import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
 import java.util.Date;
-import java.util.Enumeration;
 import java.util.Objects;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -41,13 +41,14 @@ public class HttpToken {
 		effectivePerson.setRemoteAddress(this.remoteAddress(request));
 		effectivePerson.setUserAgent(this.userAgent(request));
 		effectivePerson.setUri(request.getRequestURI());
-		/* 加入调试标记 */
+		// 加入调试标记
 		Object debugger = request.getHeader(HttpToken.X_Debugger);
-		if (null != debugger && BooleanUtils.toBoolean(Objects.toString(debugger))) {
-			effectivePerson.setDebugger(true);
-		} else {
-			effectivePerson.setDebugger(false);
-		}
+		effectivePerson.setDebugger((null != debugger) && BooleanUtils.toBoolean(Objects.toString(debugger)));
+//		if (null != debugger && BooleanUtils.toBoolean(Objects.toString(debugger))) {
+//			effectivePerson.setDebugger(true);
+//		} else {
+//			effectivePerson.setDebugger(false);
+//		}
 		setAttribute(request, effectivePerson);
 		setToken(request, response, effectivePerson);
 		return effectivePerson;
@@ -69,7 +70,7 @@ public class HttpToken {
 			Pattern pattern = Pattern.compile(RegularExpression_Token, Pattern.CASE_INSENSITIVE);
 			Matcher matcher = pattern.matcher(plain);
 			if (!matcher.find()) {
-				/* 不报错,跳过错误,将用户设置为anonymous */
+				// 不报错,跳过错误,将用户设置为anonymous
 				logger.warn("token format error:{}.", plain);
 				return EffectivePerson.anonymous();
 			}
@@ -79,21 +80,18 @@ public class HttpToken {
 			diff = Math.abs(diff);
 			if (TokenType.user.equals(tokenType) || TokenType.manager.equals(tokenType)) {
 				if (diff > (60000L * Config.person().getTokenExpiredMinutes())) {
-					// throw new Exception("token expired." + token);
-					/* 不报错,跳过错误,将用户设置为anonymous */
-					logger.warn("token expired:{}.", plain);
+					// 不报错,跳过错误,将用户设置为anonymous
+					logger.warn("token expired, user:{}, token:{}.",
+							URLDecoder.decode(matcher.group(3), StandardCharsets.UTF_8.name()), plain);
 					return EffectivePerson.anonymous();
 				}
 			}
-			if (TokenType.cipher.equals(tokenType)) {
-				if (diff > (60000 * 20)) {
-					/* 不报错,跳过错误,将用户设置为anonymous */
-					return EffectivePerson.anonymous();
-				}
+			if (TokenType.cipher.equals(tokenType) && (diff > (60000 * 20))) {
+				// 不报错,跳过错误,将用户设置为anonymous
+				return EffectivePerson.anonymous();
 			}
-			EffectivePerson effectivePerson = new EffectivePerson(URLDecoder.decode(matcher.group(3), "utf-8"),
-					tokenType, key);
-			return effectivePerson;
+			return new EffectivePerson(URLDecoder.decode(matcher.group(3), StandardCharsets.UTF_8.name()), tokenType,
+					key);
 		} catch (Exception e) {
 			e.printStackTrace();
 		}
@@ -112,20 +110,20 @@ public class HttpToken {
 	public void setToken(HttpServletRequest request, HttpServletResponse response, EffectivePerson effectivePerson)
 			throws Exception {
 		switch (effectivePerson.getTokenType()) {
-			case anonymous:
-				// this.deleteToken(request, response);
-				break;
-			case user:
-				this.setResponseToken(request, response, effectivePerson);
-				break;
-			case manager:
-				this.setResponseToken(request, response, effectivePerson);
-				break;
-			case cipher:
-				this.deleteToken(request, response);
-				break;
-			default:
-				break;
+		case anonymous:
+			// this.deleteToken(request, response);
+			break;
+		case user:
+			this.setResponseToken(request, response, effectivePerson);
+			break;
+		case manager:
+			this.setResponseToken(request, response, effectivePerson);
+			break;
+		case cipher:
+			this.deleteToken(request, response);
+			break;
+		default:
+			break;
 		}
 	}
 
@@ -138,8 +136,8 @@ public class HttpToken {
 		}
 	}
 
-	public void setResponseToken(HttpServletRequest request, HttpServletResponse response,
-								  String tokenName, String token) throws Exception {
+	public void setResponseToken(HttpServletRequest request, HttpServletResponse response, String tokenName,
+			String token) throws Exception {
 		if (!StringUtils.isEmpty(token)) {
 			String cookie = tokenName + "=" + token + "; path=/; domain=" + this.domain(request);
 			response.setHeader("Set-Cookie", cookie);

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

@@ -624,7 +624,7 @@ public abstract class StandardJaxrsAction extends AbstractJaxrsAction {
 			ListOrderedMap<String, Collection<?>> notIns, ListOrderedMap<String, Object> members,
 			ListOrderedMap<String, Object> notMembers, boolean andJoin, String order) throws Exception {
 		EntityManager em = emc.get(cls);
-		String str = "SELECT count(distinct o) FROM " + cls.getCanonicalName() + " o";
+		String str = "SELECT count(o) FROM " + cls.getCanonicalName() + " o";
 		/* 预编译的SQL语句的参数序号,必须由1开始 */
 		Integer index = 1;
 		List<String> ps = new ArrayList<>();
@@ -696,80 +696,6 @@ public abstract class StandardJaxrsAction extends AbstractJaxrsAction {
 		return (Long) query.getSingleResult() + 1;
 	}
 
-	// private <T extends JpaObject> Long count(EntityManagerContainer emc, Class<T>
-	// cls, EqualsTerms equals,
-	// NotEqualsTerms notEquals, LikeTerms likes, InTerms ins, NotInTerms notIns,
-	// MemberTerms members,
-	// NotMemberTerms notMembers, boolean andJoin) throws Exception {
-	// EntityManager em = emc.get(cls);
-	// String str = "SELECT count(distinct o) FROM " + cls.getCanonicalName() + "
-	// o";
-	// /* 预编译的SQL语句的参数序号,必须由1开始 */
-	// Integer index = 1;
-	// List<String> ps = new ArrayList<>();
-	// List<Object> vs = new ArrayList<>();
-	// if (null != equals && (!equals.isEmpty())) {
-	// for (Entry<String, Object> en : equals.entrySet()) {
-	// ps.add("o." + en.getKey() + (" = ?" + index));
-	// vs.add(en.getValue());
-	// index++;
-	// }
-	// }
-	// if (null != notEquals && (!notEquals.isEmpty())) {
-	// for (Entry<String, Object> en : notEquals.entrySet()) {
-	// ps.add("(o." + en.getKey() + (" <> ?" + index) + " or o." + en.getKey() + "
-	// is null)");
-	// vs.add(en.getValue());
-	// index++;
-	// }
-	// }
-	// if (null != likes && (!likes.isEmpty())) {
-	// List<String> ors = new ArrayList<>();
-	// for (Entry<String, Object> en : likes.entrySet()) {
-	// ors.add("o." + en.getKey() + (" Like ?" + index));
-	// vs.add("%" + en.getValue() + "%");
-	// index++;
-	// }
-	// ps.add("(" + StringUtils.join(ors, " or ") + ")");
-	// }
-	// if (null != ins && (!ins.isEmpty())) {
-	// for (Entry<String, Collection<?>> en : ins.entrySet()) {
-	// ps.add("o." + en.getKey() + (" in ?" + index));
-	// vs.add(en.getValue());
-	// index++;
-	// }
-	// }
-	// if (null != notIns && (!notIns.isEmpty())) {
-	// for (Entry<String, Collection<?>> en : notIns.entrySet()) {
-	// ps.add("o." + en.getKey() + (" not in ?" + index));
-	// vs.add(en.getValue());
-	// index++;
-	// }
-	// }
-	// if (null != members && (!members.isEmpty())) {
-	// for (Entry<String, Object> en : members.entrySet()) {
-	// ps.add(("?" + index) + (" member of o." + en.getKey()));
-	// vs.add(en.getValue());
-	// index++;
-	// }
-	// }
-	// if (null != notMembers && (!notMembers.isEmpty())) {
-	// for (Entry<String, Object> en : notMembers.entrySet()) {
-	// ps.add(("?" + index) + (" not member of o." + en.getKey()));
-	// vs.add(en.getValue());
-	// index++;
-	// }
-	// }
-	// if (!ps.isEmpty()) {
-	// str += " where " + StringUtils.join(ps, (andJoin ? " and " : " or "));
-	// }
-	// Query query = em.createQuery(str, cls);
-	// for (int i = 0; i < vs.size(); i++) {
-	// query.setParameter(i + 1, vs.get(i));
-	// }
-	// return (Long) query.getSingleResult();
-	// }
-
 	private <T extends JpaObject> Long count(EntityManagerContainer emc, Class<T> cls, EqualsTerms equals,
 			NotEqualsTerms notEquals, LikeTerms likes, InTerms ins, NotInTerms notIns, MemberTerms members,
 			NotMemberTerms notMembers, boolean andJoin) throws Exception {
@@ -1121,7 +1047,7 @@ public abstract class StandardJaxrsAction extends AbstractJaxrsAction {
 				selections.add(root.get(field));
 			}
 
-			List<Tuple> os = em.createQuery(cq.multiselect(selections).distinct(true))
+			List<Tuple> os = em.createQuery(cq.multiselect(selections))
 					.setMaxResults(Math.max(Math.min(count, list_max), list_min)).getResultList();
 
 			List<W> ws = new ArrayList<W>();
@@ -1181,7 +1107,7 @@ public abstract class StandardJaxrsAction extends AbstractJaxrsAction {
 				selections.add(root.get(field));
 			}
 
-			List<Tuple> os = em.createQuery(cq.multiselect(selections).distinct(true))
+			List<Tuple> os = em.createQuery(cq.multiselect(selections))
 					.setMaxResults(Math.max(Math.min(count, list_max), list_min)).getResultList();
 
 			List<W> ws = new ArrayList<W>();
@@ -1236,8 +1162,8 @@ public abstract class StandardJaxrsAction extends AbstractJaxrsAction {
 				cq.orderBy(cb.asc(root.get(sequenceField)));
 			}
 
-			List<T> os = em.createQuery(cq.select(root).distinct(true))
-					.setMaxResults(Math.max(Math.min(count, list_max), list_min)).getResultList();
+			List<T> os = em.createQuery(cq.select(root)).setMaxResults(Math.max(Math.min(count, list_max), list_min))
+					.getResultList();
 
 			ActionResult<List<T>> result = new ActionResult<>();
 			result.setData(new ArrayList<T>(os));

+ 106 - 7
o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/BaseTools.java

@@ -1,13 +1,19 @@
 package com.x.base.core.project.tools;
 
-import java.io.File;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
+import java.io.*;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.Socket;
+import java.util.*;
 import java.util.Map.Entry;
-import java.util.Objects;
 
+import com.x.base.core.project.config.Config;
+import com.x.base.core.project.config.ConfigObject;
+import com.x.base.core.project.config.Nodes;
+import com.x.base.core.project.gson.XGsonBuilder;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang3.StringUtils;
 
@@ -15,7 +21,7 @@ import com.google.gson.Gson;
 import com.google.gson.JsonElement;
 
 public class BaseTools {
-
+	private static Logger logger = LoggerFactory.getLogger(BaseTools.class);
 	public static String getBasePath() throws Exception {
 		return getBaseDirectory().getAbsolutePath();
 	}
@@ -135,4 +141,97 @@ public class BaseTools {
 		}
 		return FileUtils.readFileToString(file, DefaultCharset.charset);
 	}
+
+	public  static boolean executeSyncFile(String syncFilePath) throws Exception {
+
+		boolean Syncflag = false;
+		String localip = getIpAddress();
+		Nodes nodes = Config.nodes();
+		//同步config文件
+		for (String node : nodes.keySet()) {
+			//其他服务器
+			if (!node.equalsIgnoreCase(localip)) {
+				if (nodes.get(node).getApplication().getEnable() || nodes.get(node).getCenter().getEnable()) {
+					Syncflag = executeSyncFile(syncFilePath, node, nodes.get(node).nodeAgentPort());
+				}
+			}
+		}
+		return  Syncflag;
+	}
+
+	private  static boolean executeSyncFile(String syncFilePath , String nodeName ,int nodePort){
+		boolean syncFileFlag = false;
+		File syncFile;
+		InputStream fileInputStream = null;
+
+		try (Socket socket = new Socket(nodeName, nodePort)) {
+
+			syncFile = new File(Config.base(), syncFilePath);
+			fileInputStream= new FileInputStream(syncFile);
+
+			socket.setKeepAlive(true);
+			socket.setSoTimeout(5000);
+			DataOutputStream dos = null;
+			DataInputStream dis  = null;
+			try {
+				dos = new DataOutputStream(socket.getOutputStream());
+				dis = new DataInputStream(socket.getInputStream());
+
+				Map<String, Object> commandObject = new HashMap<>();
+				commandObject.put("command", "syncFile:"+ syncFilePath);
+				commandObject.put("credential", Crypto.rsaEncrypt("o2@", Config.publicKey()));
+				dos.writeUTF(XGsonBuilder.toJson(commandObject));
+				dos.flush();
+
+				dos.writeUTF(syncFilePath);
+				dos.flush();
+
+
+				logger.info("同步文件:"+syncFilePath+" starting...");
+				byte[] bytes = new byte[1024];
+				int length =0;
+				while((length = fileInputStream.read(bytes, 0, bytes.length)) != -1) {
+					dos.write(bytes, 0, length);
+					dos.flush();
+				}
+				logger.info("同步文件:" + syncFilePath +"end.");
+
+			}finally {
+				dos.close();
+				dis.close();
+				socket.close();
+				fileInputStream.close();
+			}
+
+			syncFileFlag = true;
+		} catch (Exception ex) {
+			logger.error(ex);
+			syncFileFlag = false;
+		}
+		return syncFileFlag;
+	}
+
+	public static String getIpAddress() {
+		try {
+			Enumeration<NetworkInterface> allNetInterfaces = NetworkInterface.getNetworkInterfaces();
+			InetAddress ip = null;
+			while (allNetInterfaces.hasMoreElements()) {
+				NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement();
+				if (netInterface.isLoopback() || netInterface.isVirtual() || !netInterface.isUp()) {
+					continue;
+				} else {
+					Enumeration<InetAddress> addresses = netInterface.getInetAddresses();
+					while (addresses.hasMoreElements()) {
+						ip = addresses.nextElement();
+						if (ip != null && ip instanceof Inet4Address) {
+							return ip.getHostAddress();
+						}
+					}
+				}
+			}
+		} catch (Exception e) {
+			System.err.println("IP地址获取失败" + e.toString());
+		}
+		return "";
+	}
 }

+ 95 - 0
o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/ClassLoaderTools.java

@@ -0,0 +1,95 @@
+package com.x.base.core.project.tools;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.x.base.core.project.config.Config;
+
+public class ClassLoaderTools {
+
+	private ClassLoaderTools() {
+		// nothing
+	}
+
+	private static final String CFG = "manifest.cfg";
+	private static final String GIT = ".gitignore";
+	private static final String JAR = ".jar";
+	private static final String ZIP = ".zip";
+
+	public static URLClassLoader urlClassLoader(boolean systemParent, boolean ext, boolean store, boolean custom,
+			boolean dynamic, Path... paths) throws Exception {
+		return urlClassLoader(systemParent, ext, store, custom, dynamic, ListTools.toList(paths));
+	}
+
+	public static URLClassLoader urlClassLoader(boolean systemParent, boolean ext, boolean store, boolean custom,
+			boolean dynamic, List<Path> paths) throws Exception {
+		Set<Path> set = new HashSet<>();
+		if (ext) {
+			set.addAll(dirCfg(Config.dir_commons_ext().toPath()));
+		}
+		if (store) {
+			set.addAll(dirCfg(Config.dir_store_jars().toPath()));
+		}
+		if (custom) {
+			set.addAll(dir(Config.dir_custom_jars().toPath()));
+		}
+		if (dynamic) {
+			set.addAll(dir(Config.dir_dynamic_jars().toPath()));
+		}
+		set.addAll(paths);
+		if (systemParent) {
+			return URLClassLoader.newInstance(toURL(set), ClassLoader.getSystemClassLoader());
+		} else {
+			return URLClassLoader.newInstance(toURL(set));
+		}
+	}
+
+	private static URL[] toURL(Set<Path> set) throws MalformedURLException {
+		URL[] urls = new URL[set.size()];
+		int idx = 0;
+		for (Path p : set) {
+			urls[idx++] = p.toUri().toURL();
+		}
+		return urls;
+	}
+
+	private static List<Path> dirCfg(Path dir) throws IOException {
+		List<Path> paths = new ArrayList<>();
+		Path cfg = dir.resolve(CFG);
+		if (Files.exists(dir) && Files.isDirectory(dir) && Files.exists(cfg) && Files.isRegularFile(cfg)) {
+			List<String> names = Files.readAllLines(cfg);
+			try (Stream<Path> stream = Files.list(dir)) {
+				stream.filter(o -> !(o.getFileName().toString().equalsIgnoreCase(CFG)
+						|| o.getFileName().toString().equalsIgnoreCase(GIT))).forEach(o -> {
+							if (names.remove(o.getFileName().toString())) {
+								paths.add(o);
+							}
+						});
+			}
+		}
+		return paths;
+	}
+
+	private static List<Path> dir(Path dir) throws IOException {
+		List<Path> paths = new ArrayList<>();
+		if (Files.exists(dir) && Files.isDirectory(dir)) {
+			try (Stream<Path> stream = Files.list(dir)) {
+				stream.filter(o -> StringUtils.endsWithAny(StringUtils.lowerCase(o.getFileName().toString()), JAR, ZIP))
+						.forEach(paths::add);
+			}
+		}
+		return paths;
+	}
+
+}

+ 43 - 0
o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/CompressTools.java

@@ -0,0 +1,43 @@
+package com.x.base.core.project.tools;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
+import org.apache.commons.io.IOUtils;
+
+public class CompressTools {
+
+	private CompressTools() {
+	}
+
+	public static void unzip(Path zip, Path dir) throws IOException {
+		try (ZipArchiveInputStream inputStream = new ZipArchiveInputStream(
+				new BufferedInputStream(Files.newInputStream(zip)))) {
+			if (!Files.exists(dir)) {
+				Files.createDirectories(dir);
+			}
+			ZipArchiveEntry entry = null;
+			while ((entry = inputStream.getNextZipEntry()) != null) {
+				Path p = dir.resolve(Paths.get(entry.getName()));
+				if (entry.isDirectory()) {
+					if (!Files.exists(p)) {
+						Files.createDirectories(p);
+					}
+				} else {
+					if (!Files.exists(p)) {
+						Files.createFile(p);
+					}
+					try (OutputStream os = Files.newOutputStream(p)) {
+						IOUtils.copy(inputStream, os);
+					}
+				}
+			}
+		}
+	}
+}

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

@@ -562,7 +562,7 @@ public class DateTools {
 			calendar.add(Calendar.DAY_OF_MONTH, dayAdjust );
 		}
 		if ((null != hourAdjust) && (hourAdjust != 0)) {
-			calendar.add(Calendar.HOUR_OF_DAY, dayAdjust );
+			calendar.add(Calendar.HOUR_OF_DAY, hourAdjust );
 		}
 		if ((null != minuteAdjust) && (minuteAdjust != 0)) {
 			calendar.add(Calendar.MINUTE, minuteAdjust );

+ 10 - 1
o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/JarTools.java

@@ -6,6 +6,7 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.InputStream;
+import java.nio.file.Path;
 import java.util.Enumeration;
 import java.util.List;
 import java.util.jar.JarEntry;
@@ -19,7 +20,11 @@ import org.apache.commons.lang3.StringUtils;
 public class JarTools {
 
 	private JarTools() {
-	};
+	}
+
+	public static void unjar(byte[] bytes, String sub, Path dist, boolean force) {
+		unjar(bytes, sub, dist.toFile(), force);
+	}
 
 	public static void unjar(byte[] bytes, String sub, String dist, boolean force) {
 		unjar(bytes, sub, new File(dist), force);
@@ -66,6 +71,10 @@ public class JarTools {
 		unjar(new File(source), sub, new File(dist), force);
 	}
 
+	public static void unjar(Path source, String sub, Path dist, boolean force) {
+		unjar(source.toFile(), sub, dist.toFile(), force);
+	}
+
 	public static void unjar(File source, String sub, File dist, boolean force) {
 		try (JarFile jarFile = new JarFile(source)) {
 			FileUtils.forceMkdir(dist);

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů