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

Merge branch 'fix/CMS.permissionService_bug' into 'release'

Merge of fix/CMS.permissionService_bug[内容管理]修复了新建内容管理栏目权限以及新建内容管理栏目报错的问题

See merge request o2oa/o2oa!343
李义 5 лет назад
Родитель
Сommit
dd7c341d66
46 измененных файлов с 2267 добавлено и 184 удалено
  1. 2 2
      o2android/app/assets/server.json
  2. 4 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/StartProcessStepOneContract.kt
  3. 26 6
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/StartProcessStepOneFragment.kt
  4. 29 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/StartProcessStepOnePresenter.kt
  5. 4 0
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/StartProcessStepTwoContract.kt
  6. 36 62
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/StartProcessStepTwoFragment.kt
  7. 34 5
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/StartProcessStepTwoPresenter.kt
  8. 38 8
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/TaskWebViewActivity.kt
  9. 2 0
      o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/O2.kt
  10. 18 0
      o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/component/api/APIAddressHelper.kt
  11. 13 0
      o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/component/api/service/ProcessAssembleSurfaceService.kt
  12. 51 0
      o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/api/o2/ProcessDraftWorkData.kt
  13. 10 0
      o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/api/o2/ProcessInfoData.java
  14. 69 10
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QywxAttendanceSyncQueue.java
  15. 165 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QywxPersonStatisticQueue.java
  16. 180 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QywxUnitStatisticQueue.java
  17. 16 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/ThisApplication.java
  18. 294 4
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/DingdingAttendanceFactory.java
  19. 2 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/ActionApplication.java
  20. 10 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/QywxStatisticJaxrsFilter.java
  21. 0 5
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/dingding/ActionListDDAttendanceDetail.java
  22. 0 15
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/dingdingstatistic/ActionTest.java
  23. 1 16
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/dingdingstatistic/DingdingAttendanceStatisticAction.java
  24. 216 33
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/qywx/ActionListQywxAttendanceDetail.java
  25. 6 3
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/qywx/QywxAttendanceAction.java
  26. 45 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/qywxstatistic/ActionPersonStatistic.java
  27. 45 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/qywxstatistic/ActionPersonStatisticWithUnit.java
  28. 49 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/qywxstatistic/ActionUnitStatistic.java
  29. 6 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/qywxstatistic/BaseAction.java
  30. 94 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/qywxstatistic/QywxAttendanceStatisticAction.java
  31. 48 3
      o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/AttendanceQywxDetail.java
  32. 9 0
      o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/PersistenceProperties.java
  33. 213 0
      o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/StatisticQywxPersonForMonth.java
  34. 214 0
      o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/StatisticQywxUnitForDay.java
  35. 201 0
      o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/StatisticQywxUnitForMonth.java
  36. 20 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/DateTools.java
  37. 3 1
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/x_attendance_assemble_control.java
  38. 1 0
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/ThisApplication.java
  39. 3 1
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/ActionApplication.java
  40. 1 1
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/CmsJaxrsCipherFilter.java
  41. 1 1
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/CmsJaxrsFilter.java
  42. 10 1
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/categoryinfo/ActionSave.java
  43. 4 0
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/categoryinfo/ExceptionCategoryInfoProcess.java
  44. 1 1
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/jaxrs/permission/PermissionManagerAction.java
  45. 67 0
      o2server/x_cms_assemble_control/src/main/java/com/x/cms/assemble/control/service/UserManagerService.java
  46. 6 6
      package.json

+ 2 - 2
o2android/app/assets/server.json

@@ -1,7 +1,7 @@
 {
 {
   "id" : "o2CenterServer",
   "id" : "o2CenterServer",
-  "name" : "qywx",
-  "centerHost" : "qywx.o2oa.net",
+  "name" : "develop",
+  "centerHost" : "dev.o2oa.net",
   "centerContext" : "/x_program_center",
   "centerContext" : "/x_program_center",
   "centerPort" : 20030,
   "centerPort" : 20030,
   "httpProtocol" : "http"
   "httpProtocol" : "http"

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

@@ -4,6 +4,7 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenter
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseView
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseView
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.identity.ProcessWOIdentityJson
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.identity.ProcessWOIdentityJson
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ApplicationData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ApplicationData
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessDraftWorkData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessInfoData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessInfoData
 
 
 
 
@@ -17,6 +18,8 @@ object StartProcessStepOneContract {
         fun loadCurrentPersonIdentityFail()
         fun loadCurrentPersonIdentityFail()
         fun startProcessSuccess(workId:String)
         fun startProcessSuccess(workId:String)
         fun startProcessFail(message:String)
         fun startProcessFail(message:String)
+        fun startDraftSuccess(work: ProcessDraftWorkData)
+        fun startDraftFail(message:String)
     }
     }
 
 
     interface Presenter : BasePresenter<View> {
     interface Presenter : BasePresenter<View> {
@@ -24,5 +27,6 @@ object StartProcessStepOneContract {
         fun loadProcessListByAppId(appId:String)
         fun loadProcessListByAppId(appId:String)
         fun loadCurrentPersonIdentityWithProcess(processId: String)
         fun loadCurrentPersonIdentityWithProcess(processId: String)
         fun startProcess(identity: String, processId: String)
         fun startProcess(identity: String, processId: String)
+        fun startDraft(identity: String, processId: String)
     }
     }
 }
 }

+ 26 - 6
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/StartProcessStepOneFragment.kt

@@ -6,6 +6,7 @@ import android.os.Bundle
 import android.support.v7.widget.LinearLayoutManager
 import android.support.v7.widget.LinearLayoutManager
 import android.widget.LinearLayout
 import android.widget.LinearLayout
 import kotlinx.android.synthetic.main.fragment_start_process_step_one.*
 import kotlinx.android.synthetic.main.fragment_start_process_step_one.*
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2CustomStyle
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2CustomStyle
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPFragment
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPFragment
@@ -14,9 +15,11 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.adapter.CommonRecycl
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.adapter.CommonRecyclerViewHolder
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.adapter.CommonRecyclerViewHolder
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.identity.ProcessWOIdentityJson
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.identity.ProcessWOIdentityJson
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ApplicationData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ApplicationData
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessDraftWorkData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessInfoData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessInfoData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XToast
 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.go
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.goThenKill
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.gone
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.gone
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.visible
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.visible
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.CircleImageView
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.widgets.CircleImageView
@@ -115,7 +118,12 @@ class StartProcessStepOneFragment : BaseMVPFragment<StartProcessStepOneContract.
     override fun loadCurrentPersonIdentity(list: List<ProcessWOIdentityJson>) {
     override fun loadCurrentPersonIdentity(list: List<ProcessWOIdentityJson>) {
         if (list.isNotEmpty() ) {
         if (list.isNotEmpty() ) {
             if (list.size == 1) {
             if (list.size == 1) {
-                startProcess(list[0].distinguishedName)
+                //是否走草稿
+                if (clickProcess != null && clickProcess?.defaultStartMode == O2.O2_Process_start_mode_draft) {
+                    startDraft(list[0].distinguishedName)
+                }else {
+                    startProcess(list[0].distinguishedName)
+                }
             }else {
             }else {
                 goToStepTwo()
                 goToStepTwo()
             }
             }
@@ -132,10 +140,7 @@ class StartProcessStepOneFragment : BaseMVPFragment<StartProcessStepOneContract.
 
 
     override fun startProcessSuccess(workId: String) {
     override fun startProcessSuccess(workId: String) {
         hideLoadingDialog()
         hideLoadingDialog()
-        val bundle = Bundle()
-        bundle.putString(TaskWebViewActivity.WORK_WEB_VIEW_WORK, workId)
-        bundle.putString(TaskWebViewActivity.WORK_WEB_VIEW_TITLE, "拟稿")
-        (activity as StartProcessActivity).go<TaskWebViewActivity>(bundle)
+        (activity as StartProcessActivity).go<TaskWebViewActivity>(TaskWebViewActivity.start(workId, "", "拟稿"))
         (activity as StartProcessActivity).finish()
         (activity as StartProcessActivity).finish()
     }
     }
 
 
@@ -144,11 +149,26 @@ class StartProcessStepOneFragment : BaseMVPFragment<StartProcessStepOneContract.
         XToast.toastShort(activity, message)
         XToast.toastShort(activity, message)
     }
     }
 
 
+    override fun startDraftSuccess(work: ProcessDraftWorkData) {
+        hideLoadingDialog()
+        (activity as StartProcessActivity).goThenKill<TaskWebViewActivity>(TaskWebViewActivity.startDraft(work))
+    }
+
+    override fun startDraftFail(message: String) {
+        hideLoadingDialog()
+        XToast.toastShort(activity, message)
+    }
+
     private fun startProcess(identity: String) {
     private fun startProcess(identity: String) {
         //启动流程
         //启动流程
         mPresenter.startProcess(identity, clickProcess!!.id)
         mPresenter.startProcess(identity, clickProcess!!.id)
     }
     }
 
 
+    private fun startDraft(identity: String) {
+        //启动草稿
+        mPresenter.startDraft(identity, clickProcess!!.id)
+    }
+
     private fun onProcessItemClick(processInfoData: ProcessInfoData) {
     private fun onProcessItemClick(processInfoData: ProcessInfoData) {
         clickProcess = processInfoData
         clickProcess = processInfoData
         showLoadingDialog()
         showLoadingDialog()
@@ -158,7 +178,7 @@ class StartProcessStepOneFragment : BaseMVPFragment<StartProcessStepOneContract.
     private fun goToStepTwo() {
     private fun goToStepTwo() {
         hideLoadingDialog()
         hideLoadingDialog()
         if (clickProcess != null) {
         if (clickProcess != null) {
-            val stepTwo = StartProcessStepTwoFragment.newInstance(clickProcess!!.id, clickProcess!!.name)
+            val stepTwo = StartProcessStepTwoFragment.newInstance(clickProcess!!.id, clickProcess!!.name, clickProcess?.defaultStartMode ?: "")
             (activity as StartProcessActivity).addFragment(stepTwo)
             (activity as StartProcessActivity).addFragment(stepTwo)
         }
         }
     }
     }

+ 29 - 0
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/StartProcessStepOnePresenter.kt

@@ -10,6 +10,7 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessInfoData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessStartBo
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessStartBo
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessWorkData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessWorkData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
 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.android.schedulers.AndroidSchedulers
 import rx.schedulers.Schedulers
 import rx.schedulers.Schedulers
 
 
@@ -72,4 +73,32 @@ class StartProcessStepOnePresenter : BasePresenterImpl<StartProcessStepOneContra
                     }))
                     }))
         }
         }
     }
     }
+
+    override fun startDraft(identity: String, processId: String) {
+        if (TextUtils.isEmpty(identity) || TextUtils.isEmpty(processId)) {
+            mView?.startProcessFail("传入参数为空,无法启动流程, identity:$identity,processId:$processId")
+            return
+        }
+        val body = ProcessStartBo()
+        body.title = ""
+        body.identity = identity
+        getProcessAssembleSurfaceServiceAPI(mView?.getContext())?.let { service->
+            service.startDraft(processId, body)
+                    .subscribeOn(Schedulers.io())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .o2Subscribe {
+                        onNext {
+                            if (it.data != null) {
+                                mView?.startDraftSuccess(it.data.work)
+                            }else {
+                                mView?.startDraftFail("打开草稿异常!")
+                            }
+                        }
+                        onError { e, _ ->
+                            XLog.error("", e)
+                            mView?.startDraftFail(e?.message ?: "")
+                        }
+                    }
+        }
+    }
 }
 }

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

@@ -3,6 +3,7 @@ package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.process
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenter
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BasePresenter
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseView
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseView
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.identity.ProcessWOIdentityJson
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.identity.ProcessWOIdentityJson
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessDraftWorkData
 
 
 
 
 object StartProcessStepTwoContract {
 object StartProcessStepTwoContract {
@@ -11,11 +12,14 @@ object StartProcessStepTwoContract {
         fun loadCurrentPersonIdentityFail()
         fun loadCurrentPersonIdentityFail()
         fun startProcessSuccess(workId:String)
         fun startProcessSuccess(workId:String)
         fun startProcessFail(message:String)
         fun startProcessFail(message:String)
+        fun startDraftSuccess(work: ProcessDraftWorkData)
+        fun startDraftFail(message:String)
 
 
     }
     }
 
 
     interface Presenter : BasePresenter<View> {
     interface Presenter : BasePresenter<View> {
         fun loadCurrentPersonIdentityWithProcess(processId: String)
         fun loadCurrentPersonIdentityWithProcess(processId: String)
         fun startProcess(title:String, identity:String, processId:String)
         fun startProcess(title:String, identity:String, processId:String)
+        fun startDraft(title:String, identity: String, processId: String)
     }
     }
 }
 }

+ 36 - 62
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/StartProcessStepTwoFragment.kt

@@ -1,24 +1,20 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.process
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.process
 
 
-import android.graphics.Rect
 import android.os.Bundle
 import android.os.Bundle
 import android.text.TextUtils
 import android.text.TextUtils
-import android.view.View
-import android.view.ViewTreeObserver
-import android.widget.LinearLayout
 import android.widget.RadioButton
 import android.widget.RadioButton
 import android.widget.RadioGroup
 import android.widget.RadioGroup
 import kotlinx.android.synthetic.main.fragment_start_process_step_two.*
 import kotlinx.android.synthetic.main.fragment_start_process_step_two.*
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.DateHelper
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.O2
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPFragment
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPFragment
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview.TaskWebViewActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.o2.webview.TaskWebViewActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.identity.ProcessWOIdentityJson
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.identity.ProcessWOIdentityJson
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessDraftWorkData
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.DateHelper
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
 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.XToast
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.go
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.gone
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.visible
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.extension.goThenKill
 import org.jetbrains.anko.dip
 import org.jetbrains.anko.dip
 
 
 
 
@@ -29,76 +25,48 @@ class StartProcessStepTwoFragment : BaseMVPFragment<StartProcessStepTwoContract.
     override fun layoutResId(): Int = R.layout.fragment_start_process_step_two
     override fun layoutResId(): Int = R.layout.fragment_start_process_step_two
 
 
     companion object {
     companion object {
-        val PROCESS_ID_KEY = "processId"
-        val PROCESS_NAME_KEY = "processName"
-        fun newInstance(processId:String, processName:String): StartProcessStepTwoFragment {
+        const val PROCESS_ID_KEY = "processId"
+        const val PROCESS_NAME_KEY = "processName"
+        const val PROCESS_START_MODE_KEY = "startMode"
+        fun newInstance(processId:String, processName:String, defaultStartMode: String): StartProcessStepTwoFragment {
             val stepTwo = StartProcessStepTwoFragment()
             val stepTwo = StartProcessStepTwoFragment()
             val bundle = Bundle()
             val bundle = Bundle()
             bundle.putString(PROCESS_ID_KEY, processId)
             bundle.putString(PROCESS_ID_KEY, processId)
             bundle.putString(PROCESS_NAME_KEY, processName)
             bundle.putString(PROCESS_NAME_KEY, processName)
+            bundle.putString(PROCESS_START_MODE_KEY, defaultStartMode)
             stepTwo.arguments = bundle
             stepTwo.arguments = bundle
             return stepTwo
             return stepTwo
         }
         }
     }
     }
-    val identityList = ArrayList<ProcessWOIdentityJson>()
-    var processId = ""
-    var processName = ""
-    var identity = ""
-//    var globalLayoutListener: ViewTreeObserver.OnGlobalLayoutListener? = null
+    private val identityList = ArrayList<ProcessWOIdentityJson>()
+    private var processId = ""
+    private var processName = ""
+    private var processStartMode = ""
+    private var identity = ""
     override fun initUI() {
     override fun initUI() {
         val startString = getString(R.string.title_activity_start_process_step_two)
         val startString = getString(R.string.title_activity_start_process_step_two)
         (activity as StartProcessActivity).setToolBarTitle(startString)
         (activity as StartProcessActivity).setToolBarTitle(startString)
         processId = arguments?.getString(PROCESS_ID_KEY) ?: ""
         processId = arguments?.getString(PROCESS_ID_KEY) ?: ""
         processName = arguments?.getString(PROCESS_NAME_KEY) ?: ""
         processName = arguments?.getString(PROCESS_NAME_KEY) ?: ""
+        processStartMode = arguments?.getString(PROCESS_START_MODE_KEY) ?: ""
         tv_start_process_step_two_process_title.text = "$startString-$processName"
         tv_start_process_step_two_process_title.text = "$startString-$processName"
         tv_start_process_step_two_time.text = DateHelper.nowByFormate("yyyy-MM-dd HH:mm")
         tv_start_process_step_two_time.text = DateHelper.nowByFormate("yyyy-MM-dd HH:mm")
-        btn_start_process_step_two_positive.setOnClickListener { startProcess() }
+        btn_start_process_step_two_positive.setOnClickListener {
+            if (processStartMode == O2.O2_Process_start_mode_draft){
+                startDraft()
+            }else {
+                startProcess()
+            }
+        }
         btn_start_process_step_two_cancel.setOnClickListener { (activity as StartProcessActivity).finish() }
         btn_start_process_step_two_cancel.setOnClickListener { (activity as StartProcessActivity).finish() }
-//        edit_start_process_step_two_title.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
-//            override fun onGlobalLayout() {
-//                globalLayoutListener = this
-//                val screenHeight = activity.window.decorView.rootView.height
-//                val screenWidth = activity.window.decorView.rootView.width
-//                val rect = Rect()
-//                activity.window.decorView.getWindowVisibleDisplayFrame(rect)
-//                val height = screenHeight - (rect.bottom -rect.top)
-//                if (height > screenHeight/3) {
-//                    emptySpaceView(true, screenWidth, height)
-//                }else {
-//                    emptySpaceView(false)
-//                }
-//            }
-//        })
-
         mPresenter.loadCurrentPersonIdentityWithProcess(processId)
         mPresenter.loadCurrentPersonIdentityWithProcess(processId)
     }
     }
 
 
     override fun onDestroyView() {
     override fun onDestroyView() {
-//        if (globalLayoutListener!=null) {
-//            edit_start_process_step_two_title.viewTreeObserver.removeOnGlobalLayoutListener(globalLayoutListener)
-//        }
         super.onDestroyView()
         super.onDestroyView()
         XLog.debug("StartProcessStepTwoFragment onDestroyView...............")
         XLog.debug("StartProcessStepTwoFragment onDestroyView...............")
     }
     }
 
 
-    var emptyView: View ? = null
-
-    private fun emptySpaceView(isShow:Boolean, width:Int=-1, height:Int=-1){
-        XLog.debug("emptySpaceView $isShow, $width, $height")
-        if (isShow) {
-            if (emptyView ==null) {
-                emptyView = View(activity)
-                val param = LinearLayout.LayoutParams(width, height)
-                linear_start_process_step_two_content.addView(emptyView, param)
-            }
-            emptyView?.visible()
-            scroll_start_process_step_two.postDelayed({
-                scroll_start_process_step_two.fullScroll(View.FOCUS_DOWN)
-            }, 200)
-        }else {
-            emptyView?.gone()
-        }
-    }
 
 
     override fun loadCurrentPersonIdentity(list: List<ProcessWOIdentityJson>) {
     override fun loadCurrentPersonIdentity(list: List<ProcessWOIdentityJson>) {
         radio_group_process_step_two_department.removeAllViews()
         radio_group_process_step_two_department.removeAllViews()
@@ -137,8 +105,7 @@ class StartProcessStepTwoFragment : BaseMVPFragment<StartProcessStepTwoContract.
         val bundle = Bundle()
         val bundle = Bundle()
         bundle.putString(TaskWebViewActivity.WORK_WEB_VIEW_WORK, workId)
         bundle.putString(TaskWebViewActivity.WORK_WEB_VIEW_WORK, workId)
         bundle.putString(TaskWebViewActivity.WORK_WEB_VIEW_TITLE, "拟稿")
         bundle.putString(TaskWebViewActivity.WORK_WEB_VIEW_TITLE, "拟稿")
-        (activity as StartProcessActivity).go<TaskWebViewActivity>(bundle)
-        (activity as StartProcessActivity).finish()
+        (activity as StartProcessActivity).goThenKill<TaskWebViewActivity>(bundle)
     }
     }
 
 
     override fun startProcessFail(message:String) {
     override fun startProcessFail(message:String) {
@@ -146,17 +113,24 @@ class StartProcessStepTwoFragment : BaseMVPFragment<StartProcessStepTwoContract.
         hideLoadingDialog()
         hideLoadingDialog()
     }
     }
 
 
+    override fun startDraftSuccess(work: ProcessDraftWorkData) {
+        hideLoadingDialog()
+        (activity as StartProcessActivity).goThenKill<TaskWebViewActivity>(TaskWebViewActivity.startDraft(work))
+    }
 
 
+    override fun startDraftFail(message: String) {
+        hideLoadingDialog()
+        XToast.toastShort(activity, message)
+    }
 
 
     private fun startProcess() {
     private fun startProcess() {
-//        var title = edit_start_process_step_two_title.text.toString()
-//        if (TextUtils.isEmpty(title)) {
-////            XToast.toastShort(activity, "请输入文件标题")
-////            return
-//            title = "无标题"
-//        }
         showLoadingDialog()
         showLoadingDialog()
         mPresenter.startProcess("", identity, processId)
         mPresenter.startProcess("", identity, processId)
     }
     }
 
 
+    private fun startDraft() {
+        showLoadingDialog()
+        mPresenter.startDraft("", identity, processId)
+    }
+
 }
 }

+ 34 - 5
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/process/StartProcessStepTwoPresenter.kt

@@ -8,6 +8,7 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.main.identity.ProcessW
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessStartBo
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessStartBo
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessWorkData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessWorkData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
 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.android.schedulers.AndroidSchedulers
 import rx.schedulers.Schedulers
 import rx.schedulers.Schedulers
 
 
@@ -18,10 +19,10 @@ class StartProcessStepTwoPresenter : BasePresenterImpl<StartProcessStepTwoContra
             service.availableIdentityWithProcess(processId)
             service.availableIdentityWithProcess(processId)
                     .subscribeOn(Schedulers.io())
                     .subscribeOn(Schedulers.io())
                     .observeOn(AndroidSchedulers.mainThread())
                     .observeOn(AndroidSchedulers.mainThread())
-                    .subscribe(ResponseHandler<List<ProcessWOIdentityJson>>({ list ->
+                    .subscribe(ResponseHandler<List<ProcessWOIdentityJson>> { list ->
                         XLog.debug("identities: $list")
                         XLog.debug("identities: $list")
                         mView?.loadCurrentPersonIdentity(list)
                         mView?.loadCurrentPersonIdentity(list)
-                    }),
+                    },
                             ExceptionHandler(mView?.getContext(), { e -> mView?.loadCurrentPersonIdentityFail() }))
                             ExceptionHandler(mView?.getContext(), { e -> mView?.loadCurrentPersonIdentityFail() }))
         }
         }
     }
     }
@@ -38,16 +39,44 @@ class StartProcessStepTwoPresenter : BasePresenterImpl<StartProcessStepTwoContra
                 service.startProcess(processId, body)
                 service.startProcess(processId, body)
                     .subscribeOn(Schedulers.io())
                     .subscribeOn(Schedulers.io())
                     .observeOn(AndroidSchedulers.mainThread())
                     .observeOn(AndroidSchedulers.mainThread())
-                    .subscribe(ResponseHandler<List<ProcessWorkData>>({ list ->
+                    .subscribe(ResponseHandler<List<ProcessWorkData>> { list ->
                         try {
                         try {
                             mView?.startProcessSuccess(list[0].taskList[0].work)
                             mView?.startProcessSuccess(list[0].taskList[0].work)
                         } catch (e: Exception) {
                         } catch (e: Exception) {
                             XLog.error("", e)
                             XLog.error("", e)
                             mView?.startProcessFail("返回数据异常!${e.message}")
                             mView?.startProcessFail("返回数据异常!${e.message}")
                         }
                         }
-                    }), ExceptionHandler(mView?.getContext(), { e ->
+                    }, ExceptionHandler(mView?.getContext()) { e ->
                         mView?.startProcessFail(e.message ?: "")
                         mView?.startProcessFail(e.message ?: "")
-                    }))
+                    })
+        }
+    }
+
+    override fun startDraft(title: String, identity: String, processId: String) {
+        if (TextUtils.isEmpty(identity) || TextUtils.isEmpty(processId)) {
+            mView?.startProcessFail("传入参数为空,无法启动流程, identity:$identity,processId:$processId")
+            return
+        }
+        val body = ProcessStartBo()
+        body.title = ""
+        body.identity = identity
+        getProcessAssembleSurfaceServiceAPI(mView?.getContext())?.let { service->
+            service.startDraft(processId, body)
+                    .subscribeOn(Schedulers.io())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .o2Subscribe {
+                        onNext {
+                            if (it.data != null) {
+                                mView?.startDraftSuccess(it.data.work)
+                            }else {
+                                mView?.startDraftFail("打开草稿异常!")
+                            }
+                        }
+                        onError { e, _ ->
+                            XLog.error("", e)
+                            mView?.startDraftFail(e?.message ?: "")
+                        }
+                    }
         }
         }
     }
     }
 }
 }

+ 38 - 8
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/webview/TaskWebViewActivity.kt

@@ -26,6 +26,7 @@ import com.google.gson.reflect.TypeToken
 import kotlinx.android.synthetic.main.activity_work_web_view.*
 import kotlinx.android.synthetic.main.activity_work_web_view.*
 import net.muliba.fancyfilepickerlibrary.FilePicker
 import net.muliba.fancyfilepickerlibrary.FilePicker
 import net.muliba.fancyfilepickerlibrary.PicturePicker
 import net.muliba.fancyfilepickerlibrary.PicturePicker
+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.O2SDKManager
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.base.BaseMVPActivity
@@ -33,6 +34,7 @@ import net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.tbs.FileReaderActivity
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.APIAddressHelper
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.component.api.APIAddressHelper
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.WorkNewActionItem
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.WorkNewActionItem
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.WorkControl
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.WorkControl
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ProcessDraftWorkData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ReadData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.ReadData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.WorkOpinionData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2.WorkOpinionData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo.O2UploadImageData
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.vo.O2UploadImageData
@@ -52,6 +54,7 @@ import rx.Scheduler
 import rx.android.schedulers.AndroidSchedulers
 import rx.android.schedulers.AndroidSchedulers
 import rx.schedulers.Schedulers
 import rx.schedulers.Schedulers
 import java.io.File
 import java.io.File
+import java.net.URLEncoder
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit
 import java.util.concurrent.TimeUnit
 
 
@@ -66,6 +69,7 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
         val WORK_WEB_VIEW_TITLE = "xbpm.work.web.view.title"
         val WORK_WEB_VIEW_TITLE = "xbpm.work.web.view.title"
         val WORK_WEB_VIEW_WORK = "xbpm.work.web.view.work"
         val WORK_WEB_VIEW_WORK = "xbpm.work.web.view.work"
         val WORK_WEB_VIEW_WORK_COMPLETED = "xbpm.work.web.view.work.completed"
         val WORK_WEB_VIEW_WORK_COMPLETED = "xbpm.work.web.view.work.completed"
+        val WORK_WEB_VIEW_DRAFT = "xbpm.work.web.view.work.draft"
 
 
         fun start(work: String?, workCompleted: String?, title: String?):  Bundle {
         fun start(work: String?, workCompleted: String?, title: String?):  Bundle {
             val bundle = Bundle()
             val bundle = Bundle()
@@ -74,6 +78,12 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
             bundle.putString(WORK_WEB_VIEW_WORK_COMPLETED, workCompleted)
             bundle.putString(WORK_WEB_VIEW_WORK_COMPLETED, workCompleted)
             return bundle
             return bundle
         }
         }
+
+        fun startDraft(draft: ProcessDraftWorkData?):  Bundle {
+            val bundle = Bundle()
+            bundle.putSerializable(WORK_WEB_VIEW_DRAFT, draft)
+            return bundle
+        }
     }
     }
 
 
     private  val WORK_WEB_VIEW_UPLOAD_REQUEST_CODE = 1001
     private  val WORK_WEB_VIEW_UPLOAD_REQUEST_CODE = 1001
@@ -86,6 +96,7 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
     private  var workCompletedId = ""
     private  var workCompletedId = ""
     private  var isWorkCompleted = false
     private  var isWorkCompleted = false
     private  var url = ""
     private  var url = ""
+    private var draft: ProcessDraftWorkData? = null
 
 
     private var control: WorkControl? = null
     private var control: WorkControl? = null
     private var read: ReadData? = null
     private var read: ReadData? = null
@@ -110,15 +121,34 @@ class TaskWebViewActivity : BaseMVPActivity<TaskWebViewContract.View, TaskWebVie
         title = intent.extras?.getString(WORK_WEB_VIEW_TITLE) ?: ""
         title = intent.extras?.getString(WORK_WEB_VIEW_TITLE) ?: ""
         workId = intent.extras?.getString(WORK_WEB_VIEW_WORK) ?: ""
         workId = intent.extras?.getString(WORK_WEB_VIEW_WORK) ?: ""
         workCompletedId = intent.extras?.getString(WORK_WEB_VIEW_WORK_COMPLETED) ?: ""
         workCompletedId = intent.extras?.getString(WORK_WEB_VIEW_WORK_COMPLETED) ?: ""
-
-        isWorkCompleted = !TextUtils.isEmpty(workCompletedId)
-
-        if (isWorkCompleted) {
-            url = APIAddressHelper.instance().getWorkCompletedUrl()
-            url = String.format(url, workCompletedId)
+        draft = if (intent.extras?.getSerializable(WORK_WEB_VIEW_DRAFT) != null){
+            intent.extras?.getSerializable(WORK_WEB_VIEW_DRAFT) as ProcessDraftWorkData
         } else {
         } else {
-            url = APIAddressHelper.instance().getWorkUrlPre()
-            url = String.format(url, workId)
+            null
+        }
+
+        //草稿文档处理
+        if (draft != null) {
+            if (!TextUtils.isEmpty(draft!!.id)){
+                url = APIAddressHelper.instance().getProcessDraftWithIdUrl()
+                url = String.format(url, draft!!.id)
+            }else {
+                url = APIAddressHelper.instance().getProcessDraftUrl()
+                val json = O2SDKManager.instance().gson.toJson(draft)
+                XLog.debug("草稿对象:$json")
+                val enJson = URLEncoder.encode(json, "utf-8")
+                XLog.debug("草稿对象 encode:$enJson")
+                url = String.format(url, enJson)
+            }
+        }else {
+            isWorkCompleted = !TextUtils.isEmpty(workCompletedId)
+            if (isWorkCompleted) {
+                url = APIAddressHelper.instance().getWorkCompletedUrl()
+                url = String.format(url, workCompletedId)
+            } else {
+                url = APIAddressHelper.instance().getWorkUrlPre()
+                url = String.format(url, workId)
+            }
         }
         }
         url += "&time=" + System.currentTimeMillis()
         url += "&time=" + System.currentTimeMillis()
 
 

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

@@ -13,6 +13,8 @@ object O2 {
     val O2_COLLECT_URL = "http://collect.o2oa.net:20080/o2_collect_assemble/"
     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"
 
 
+    const val O2_Process_start_mode_draft = "draft"
+
     /**
     /**
      * 项目文件存储路径
      * 项目文件存储路径
      * 根目录
      * 根目录

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

@@ -92,6 +92,24 @@ class APIAddressHelper private constructor() {
         } ?: ""
         } ?: ""
     }
     }
 
 
+    /**
+     * 流程草稿打开的url
+     */
+    fun getProcessDraftUrl(): String {
+        return webServerData?.let {
+            "$httpHead${webServerData?.host}:${webServerData?.port}/x_desktop/workmobilewithaction.html?draft=%s"
+        } ?: ""
+    }
+
+    /**
+     * 流程草稿打开的url
+     */
+    fun getProcessDraftWithIdUrl(): String {
+        return webServerData?.let {
+            "$httpHead${webServerData?.host}:${webServerData?.port}/x_desktop/workmobilewithaction.html?draftid=%s"
+        } ?: ""
+    }
+
     /**
     /**
      * 查看帖子的url
      * 查看帖子的url
      * @param subjectId 帖子id
      * @param subjectId 帖子id

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

@@ -244,6 +244,19 @@ interface ProcessAssembleSurfaceService {
     fun getTask(@Path("taskId") taskId: String): Observable<ApiResponse<TaskData>>
     fun getTask(@Path("taskId") taskId: String): Observable<ApiResponse<TaskData>>
 
 
 
 
+
+    /**
+     * 启动草稿
+     * @param processId
+     * *
+     * @param body
+     * *
+     * @return
+     */
+    @Headers("Content-Type:application/json;charset=UTF-8")
+    @POST("jaxrs/draft/process/{processId}")
+    fun startDraft(@Path("processId") processId: String, @Body body: ProcessStartBo): Observable<ApiResponse<ProcessDraftData>>
+
     /**
     /**
      * 启动流程
      * 启动流程
      * @param processId
      * @param processId

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

@@ -0,0 +1,51 @@
+package net.zoneland.x.bpm.mobile.v1.zoneXBPM.model.bo.api.o2
+
+import java.io.Serializable
+
+
+/**
+ * Created by fancyLou on 2020-04-21.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+
+/**
+ *  "work": {
+"id": "",
+"title": "流程开发测试-无标题",
+"creatorPerson": "楼国栋@louguodong@P",
+"creatorIdentity": "楼国栋@6fef54b9-e195-4609-b030-aa446a4164f3@I",
+"creatorUnit": "开发部@22@U",
+"application": "d18a3210-22f2-431c-af29-dbb4f6e8ae82",
+"applicationName": "流程开发",
+"applicationAlias": "",
+"process": "4be0e389-a69b-466d-a725-97f9a189d7ff",
+"processName": "流程开发测试",
+"processAlias": "",
+"workCreateType": "surface",
+"form": "7d0d8ac2-7c8c-4bcc-9e2f-6d436a7a39b5",
+"properties": {
+"manualForceTaskIdentityList": [],
+"manualEmpowerMap": {},
+"serviceValue": {}
+}
+}
+ */
+class ProcessDraftWorkData (
+    var id: String = "",
+    var title: 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 workCreateType: String = "",
+    var form: String = ""
+): Serializable
+
+class ProcessDraftData(
+        var work: ProcessDraftWorkData = ProcessDraftWorkData()
+)

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

@@ -10,6 +10,8 @@ public class ProcessInfoData {
     private String alias;
     private String alias;
     private String description;
     private String description;
 
 
+    private String defaultStartMode;	//默认启动方式,draft,instance
+
     public String getId() {
     public String getId() {
         return id;
         return id;
     }
     }
@@ -41,4 +43,12 @@ public class ProcessInfoData {
     public void setDescription(String description) {
     public void setDescription(String description) {
         this.description = description;
         this.description = description;
     }
     }
+
+    public String getDefaultStartMode() {
+        return defaultStartMode;
+    }
+
+    public void setDefaultStartMode(String defaultStartMode) {
+        this.defaultStartMode = defaultStartMode;
+    }
 }
 }

+ 69 - 10
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QywxAttendanceSyncQueue.java

@@ -1,7 +1,9 @@
 package com.x.attendance.assemble.control;
 package com.x.attendance.assemble.control;
 
 
 import com.x.attendance.assemble.control.exception.DingDingRequestException;
 import com.x.attendance.assemble.control.exception.DingDingRequestException;
-import com.x.attendance.entity.*;
+import com.x.attendance.entity.AttendanceQywxDetail;
+import com.x.attendance.entity.AttendanceQywxDetail_;
+import com.x.attendance.entity.DingdingQywxSyncRecord;
 import com.x.base.core.container.EntityManagerContainer;
 import com.x.base.core.container.EntityManagerContainer;
 import com.x.base.core.container.factory.EntityManagerContainerFactory;
 import com.x.base.core.container.factory.EntityManagerContainerFactory;
 import com.x.base.core.entity.JpaObject;
 import com.x.base.core.entity.JpaObject;
@@ -14,11 +16,11 @@ import com.x.base.core.project.gson.GsonPropertyObject;
 import com.x.base.core.project.logger.Logger;
 import com.x.base.core.project.logger.Logger;
 import com.x.base.core.project.logger.LoggerFactory;
 import com.x.base.core.project.logger.LoggerFactory;
 import com.x.base.core.project.organization.Person;
 import com.x.base.core.project.organization.Person;
+import com.x.base.core.project.organization.Unit;
 import com.x.base.core.project.queue.AbstractQueue;
 import com.x.base.core.project.queue.AbstractQueue;
+import com.x.base.core.project.tools.DateTools;
 import com.x.base.core.project.tools.ListTools;
 import com.x.base.core.project.tools.ListTools;
-import com.x.base.core.project.tools.StringTools;
 import com.x.base.core.project.x_organization_assemble_control;
 import com.x.base.core.project.x_organization_assemble_control;
-import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.StringUtils;
 
 
 import javax.persistence.EntityManager;
 import javax.persistence.EntityManager;
@@ -26,8 +28,10 @@ import javax.persistence.criteria.CriteriaBuilder;
 import javax.persistence.criteria.CriteriaQuery;
 import javax.persistence.criteria.CriteriaQuery;
 import javax.persistence.criteria.Predicate;
 import javax.persistence.criteria.Predicate;
 import javax.persistence.criteria.Root;
 import javax.persistence.criteria.Root;
+import java.util.Calendar;
 import java.util.Date;
 import java.util.Date;
 import java.util.List;
 import java.util.List;
+import java.util.Optional;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
 public class QywxAttendanceSyncQueue  extends AbstractQueue<DingdingQywxSyncRecord> {
 public class QywxAttendanceSyncQueue  extends AbstractQueue<DingdingQywxSyncRecord> {
@@ -76,8 +80,8 @@ public class QywxAttendanceSyncQueue  extends AbstractQueue<DingdingQywxSyncReco
 
 
                 if(ListTools.isNotEmpty(qywxUsers)){
                 if(ListTools.isNotEmpty(qywxUsers)){
                     QywxPost post = new QywxPost();
                     QywxPost post = new QywxPost();
-                    post.setStarttime(fromDate.getTime());
-                    post.setEndtime(toDate.getTime());
+                    post.setStarttime(DateTools.toUnixTimeStamp(fromDate.getTime()));
+                    post.setEndtime(DateTools.toUnixTimeStamp(toDate.getTime()));
                     post.setUseridlist(qywxUsers);
                     post.setUseridlist(qywxUsers);
                     post.setOpencheckindatatype(3);//全部打卡信息
                     post.setOpencheckindatatype(3);//全部打卡信息
                     logger.info("企业微信 post :" + post.toString());
                     logger.info("企业微信 post :" + post.toString());
@@ -85,7 +89,7 @@ public class QywxAttendanceSyncQueue  extends AbstractQueue<DingdingQywxSyncReco
                     logger.info("返回结果:"+result.toString());
                     logger.info("返回结果:"+result.toString());
                     if (result.errcode == 0) {
                     if (result.errcode == 0) {
                         List<QywxResultItem> resultList = result.getCheckindata();
                         List<QywxResultItem> resultList = result.getCheckindata();
-                        saveQywxAttendance(resultList);
+                        saveQywxAttendance(resultList, list);
                         saveNumber += resultList.size();
                         saveNumber += resultList.size();
                     } else {
                     } else {
                         //请求结果异常 结束
                         //请求结果异常 结束
@@ -112,16 +116,54 @@ public class QywxAttendanceSyncQueue  extends AbstractQueue<DingdingQywxSyncReco
         }
         }
         logger.info("结束 插入:"+saveNumber+" 条");
         logger.info("结束 插入:"+saveNumber+" 条");
 
 
+        boolean hasNextDate = true;
+        Date statisticDate = fromDate;
+        while (hasNextDate) {
+            logger.info("发起企业微信考勤数据统计, date:"+ DateTools.format(statisticDate));
+            ThisApplication.personQywxStatisticQueue.send(statisticDate);
+            ThisApplication.unitQywxStatisticQueue.send(statisticDate);
+            if (!isSameDay(statisticDate, toDate)) {
+                statisticDate = DateTools.addDay(statisticDate, 1);
+            }else {
+                hasNextDate = false;
+            }
+        }
+        logger.info("发起数据统计程序 完成。。。。。。。。。。");
+
+    }
 
 
+    private boolean isSameDay(Date startDate, Date endDate) {
+        Calendar start = Calendar.getInstance();
+        start.setTime( startDate );
+        Calendar end = Calendar.getInstance();
+        end.setTime(endDate);
+        return (start.get(Calendar.YEAR) == end.get(Calendar.YEAR) && start.get(Calendar.DAY_OF_YEAR) == end.get(Calendar.DAY_OF_YEAR));
     }
     }
 
 
-    private void saveQywxAttendance(List<QywxResultItem> resultList) throws Exception {
+    private void saveQywxAttendance(List<QywxResultItem> resultList, List<Person> personList) throws Exception {
         if (resultList != null && !resultList.isEmpty()) {
         if (resultList != null && !resultList.isEmpty()) {
             try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
             try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+                Business business = new Business(emc);
                 emc.beginTransaction(AttendanceQywxDetail.class);
                 emc.beginTransaction(AttendanceQywxDetail.class);
                 for (int i = 0; i < resultList.size(); i++) {
                 for (int i = 0; i < resultList.size(); i++) {
                     QywxResultItem item = resultList.get(i);
                     QywxResultItem item = resultList.get(i);
                     AttendanceQywxDetail detail = QywxResultItem.copier.copy(item);
                     AttendanceQywxDetail detail = QywxResultItem.copier.copy(item);
+                    if (detail.getCheckin_time() > 0) {
+                        long timestamp = DateTools.toTimestamp(detail.getCheckin_time());
+                        detail.setCheckin_time_date(new Date(timestamp));
+                    }
+                    //添加o2组织和用户
+                    Optional<Person> first = personList.stream().filter(p -> item.userid.equals(p.getQiyeweixinId())).findFirst();
+                    if (first.isPresent()) {
+                        Person person = first.get();
+                        String unit = getUnitWithPerson(person.getDistinguishedName(), business);
+                        detail.setO2Unit(unit);
+                        detail.setO2User(person.getDistinguishedName());
+                    }
+                    //设置正常的类型
+                    if (StringUtils.isEmpty(detail.getException_type())){
+                        detail.setException_type(AttendanceQywxDetail.EXCEPTION_TYPE_NORMAL);
+                    }
                     emc.persist(detail);
                     emc.persist(detail);
                 }
                 }
                 emc.commit();
                 emc.commit();
@@ -129,6 +171,25 @@ public class QywxAttendanceSyncQueue  extends AbstractQueue<DingdingQywxSyncReco
         }
         }
     }
     }
 
 
+    private String getUnitWithPerson(String person, Business business) throws Exception {
+        String result = null;
+        Integer level = 0;
+        Unit unit = null;
+        List<String> unitNames = business.organization().unit().listWithPerson( person );
+        if( ListTools.isNotEmpty( unitNames ) ) {
+            for( String unitName : unitNames ) {
+                if( StringUtils.isNotEmpty( unitName ) && !"null".equals( unitName ) ) {
+                    unit = business.organization().unit().getObject( unitName );
+                    if( level < unit.getLevel() ) {
+                        level = unit.getLevel();
+                        result = unitName;
+                    }
+                }
+            }
+        }
+        return result;
+    }
+
     private void deleteQywxAttendance(Date fromDate, Date toDate) throws Exception {
     private void deleteQywxAttendance(Date fromDate, Date toDate) throws Exception {
         try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
         try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
             //先删除 再同步
             //先删除 再同步
@@ -136,9 +197,7 @@ public class QywxAttendanceSyncQueue  extends AbstractQueue<DingdingQywxSyncReco
             CriteriaBuilder cb = em.getCriteriaBuilder();
             CriteriaBuilder cb = em.getCriteriaBuilder();
             CriteriaQuery<AttendanceQywxDetail> query = cb.createQuery(AttendanceQywxDetail.class);
             CriteriaQuery<AttendanceQywxDetail> query = cb.createQuery(AttendanceQywxDetail.class);
             Root<AttendanceQywxDetail> root = query.from(AttendanceQywxDetail.class);
             Root<AttendanceQywxDetail> root = query.from(AttendanceQywxDetail.class);
-            long start = fromDate.getTime();
-            long end = toDate.getTime();
-            Predicate p = cb.between(root.get(AttendanceQywxDetail_.checkin_time), start, end);
+            Predicate p = cb.between(root.get(AttendanceQywxDetail_.checkin_time_date), fromDate, toDate);
             query.select(root).where(p);
             query.select(root).where(p);
             List<AttendanceQywxDetail> detailList = em.createQuery(query).getResultList();
             List<AttendanceQywxDetail> detailList = em.createQuery(query).getResultList();
             //先删除
             //先删除

+ 165 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QywxPersonStatisticQueue.java

@@ -0,0 +1,165 @@
+package com.x.attendance.assemble.control;
+
+import com.x.attendance.entity.AttendanceDingtalkDetail;
+import com.x.attendance.entity.AttendanceQywxDetail;
+import com.x.attendance.entity.StatisticQywxPersonForMonth;
+import com.x.base.core.container.EntityManagerContainer;
+import com.x.base.core.container.factory.EntityManagerContainerFactory;
+import com.x.base.core.project.Application;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+import com.x.base.core.project.organization.Person;
+import com.x.base.core.project.organization.Unit;
+import com.x.base.core.project.queue.AbstractQueue;
+import com.x.base.core.project.tools.DateTools;
+import com.x.base.core.project.tools.ListTools;
+import com.x.base.core.project.x_organization_assemble_control;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * Created by fancyLou on 2020-04-05.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class QywxPersonStatisticQueue extends AbstractQueue<Date> {
+    private static final Logger logger = LoggerFactory.getLogger(QywxPersonStatisticQueue.class);
+
+    @Override
+    protected void execute(Date date) throws Exception {
+        logger.info("开始执行人员企业微信考勤统计。。。time:"+DateTools.format(date));
+        try ( EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+            Business business = new Business(emc);
+            saveStatisticPersonForMonth(business, emc, date);
+        }
+    }
+
+
+    private void saveStatisticPersonForMonth(Business business, EntityManagerContainer emc, Date date) throws Exception {
+        String dateString = DateTools.format(date, DateTools.format_yyyyMMdd);
+        String year = dateString.substring(0, 4);
+        String month = dateString.substring(5, 7);
+
+        Application app = ThisApplication.context().applications().randomWithWeight(x_organization_assemble_control.class.getName());
+        //开始分页查询人员
+        boolean hasNextPerson = true;
+        int personPageSize = 50;
+        //人员查询地址
+        String uri = "person/list/(0)/next/50";
+        while (hasNextPerson) {
+            List<Person> list = ThisApplication.context().applications()
+                    .getQuery(false, app, uri).getDataAsList(Person.class);
+            if (list != null && list.size() > 0) {
+                for (Person person : list) {
+                    List<String> ids = business.dingdingAttendanceFactory()
+                            .getQywxStatPersonForMonthIds(year, month, person.getDistinguishedName());
+                    emc.beginTransaction(StatisticQywxPersonForMonth.class);
+                    if (ids != null && ids.size() > 0) {
+                        for (String item : ids) {
+                            StatisticQywxPersonForMonth personForMonth_temp = emc.find(item, StatisticQywxPersonForMonth.class);
+                            emc.remove(personForMonth_temp);
+                        }
+                    }
+                    StatisticQywxPersonForMonth personForMonth = new StatisticQywxPersonForMonth();
+                    personForMonth.setStatisticYear(year);
+                    personForMonth.setStatisticMonth(month);
+                    personForMonth.setO2Unit(getUnitWithPerson(person.getDistinguishedName(), business));
+                    personForMonth.setO2User(person.getDistinguishedName());
+                    List<AttendanceQywxDetail> details = business.dingdingAttendanceFactory().qywxPersonForMonthList(year, month, person.getDistinguishedName());
+                    long workDayCount = 0L;
+                    long onDutyTimes = 0L;
+                    long offDutyTimes = 0L;
+                    long outsideDutyTimes = 0L;
+                    long resultNormal = 0L;
+                    long lateTimes = 0L;
+                    long leaveEarlyTimes = 0L;
+                    long absenteeismTimes = 0L;
+                    long notSignedCount = 0L;
+                    if ( details!=null && !details.isEmpty()) {
+                        workDayCount = (long) (details.size() / 2);
+                        onDutyTimes = details.stream().filter(d -> d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_ON)).count();
+                        offDutyTimes = details.stream().filter(d -> d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_OFF)).count();
+                        outsideDutyTimes = details.stream().filter(d -> d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_OUTSIDE)).count();
+                        resultNormal = details.stream().filter(d -> d.getException_type().equals(AttendanceQywxDetail.EXCEPTION_TYPE_NORMAL)).count();
+                        lateTimes = details.stream().filter(d ->
+                                (d.getException_type().contains(AttendanceQywxDetail.EXCEPTION_TYPE_TIME) &&
+                                        d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_ON))).count();
+                        leaveEarlyTimes = details.stream().filter(d ->
+                                (d.getException_type().contains(AttendanceQywxDetail.EXCEPTION_TYPE_TIME) &&
+                                        d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_OFF))).count();
+                        List<AttendanceQywxDetail> ondutys = details.stream().filter(d->
+                                d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_ON)).collect(Collectors.toList());
+                        List<AttendanceQywxDetail> offdutys = details.stream().filter(d->
+                                d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_OFF)).collect(Collectors.toList());
+                        for (int i = 0; i < ondutys.size(); i++) {
+                            AttendanceQywxDetail detail = ondutys.get(i);
+                            if (detail.getException_type().contains(AttendanceQywxDetail.EXCEPTION_TYPE_NOSIGN)) {
+                                notSignedCount++;
+                                String checkDate = DateTools.format(detail.getCheckin_time_date(), DateTools.format_yyyyMMdd);
+                                Optional<AttendanceQywxDetail> op = offdutys.stream().filter(d->
+                                        checkDate.equals(DateTools.format(d.getCheckin_time_date(), DateTools.format_yyyyMMdd))).findFirst();
+                                if (op.isPresent()) {
+                                    AttendanceQywxDetail detail1 = op.get();
+                                    if(detail1.getException_type().contains(AttendanceQywxDetail.EXCEPTION_TYPE_NOSIGN)) {
+                                        notSignedCount++;
+                                        absenteeismTimes++;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    personForMonth.setOnDutyTimes(onDutyTimes);
+                    personForMonth.setOffDutyTimes(offDutyTimes);
+                    personForMonth.setOutsideDutyTimes(outsideDutyTimes);
+                    personForMonth.setWorkDayCount(workDayCount);
+                    personForMonth.setResultNormal(resultNormal);
+                    personForMonth.setLateTimes(lateTimes);
+                    personForMonth.setLeaveEarlyTimes(leaveEarlyTimes);
+                    personForMonth.setAbsenteeismTimes(absenteeismTimes);
+                    personForMonth.setNotSignedCount(notSignedCount);
+                    emc.persist(personForMonth);
+                    emc.commit();
+                }
+
+                //是否还有更多用户
+                if (list.size() < personPageSize) {
+                    logger.info("统计企业微信考勤个人数据 结束。。。。。。。。。。。。。。。");
+                    hasNextPerson = false;
+                } else {
+                    //还有更多用户继续查询
+                    uri = "person/list/" + list.get(list.size() - 1).getDistinguishedName() + "/next/50";
+                }
+            }else {
+                //没有用户查询到结束
+                logger.info("统计企业微信考勤个人数据 结束。。。。。。。。。。。。。。。");
+                hasNextPerson = false;
+            }
+        }
+    }
+
+    private String getUnitWithPerson(String person, Business business) throws Exception {
+        String result = null;
+        Integer level = 0;
+        Unit unit = null;
+        List<String> unitNames = business.organization().unit().listWithPerson( person );
+        if( ListTools.isNotEmpty( unitNames ) ) {
+            for( String unitName : unitNames ) {
+                if( StringUtils.isNotEmpty( unitName ) && !"null".equals( unitName ) ) {
+                    unit = business.organization().unit().getObject( unitName );
+                    if( level < unit.getLevel() ) {
+                        level = unit.getLevel();
+                        result = unitName;
+                    }
+                }
+            }
+        }
+        return result;
+    }
+
+
+
+
+}

+ 180 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QywxUnitStatisticQueue.java

@@ -0,0 +1,180 @@
+package com.x.attendance.assemble.control;
+
+import com.x.attendance.entity.*;
+import com.x.base.core.container.EntityManagerContainer;
+import com.x.base.core.container.factory.EntityManagerContainerFactory;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+import com.x.base.core.project.queue.AbstractQueue;
+import com.x.base.core.project.tools.DateTools;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * Created by fancyLou on 2020-04-05.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class QywxUnitStatisticQueue extends AbstractQueue<Date> {
+    private static final Logger logger = LoggerFactory.getLogger(QywxUnitStatisticQueue.class);
+
+    @Override
+    protected void execute(Date date) throws Exception {
+        logger.info("开始执行组织企业微信考勤统计。。。time:"+DateTools.format(date));
+        try ( EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+            Business business = new Business(emc);
+            saveStatisticUnitForDay(business, emc, date);
+        }
+    }
+
+
+
+    /**
+     * 单位 日统计
+     * @param business
+     * @param emc
+     * @param date
+     * @throws Exception
+     */
+    private void saveStatisticUnitForDay(Business business, EntityManagerContainer emc, Date date) throws Exception{
+        String dateString = DateTools.format(date, DateTools.format_yyyyMMdd);
+        String year = dateString.substring(0, 4);
+        String month = dateString.substring(5, 7);
+        String day = dateString.substring(8, 10);
+        List<String> units = business.dingdingAttendanceFactory().qywxUnitDistinct(dateString);
+        if (units != null && !units.isEmpty()) {
+            for (String unit : units) {
+                if (StringUtils.isEmpty(unit)){
+                    continue;
+                }
+                List<String> ids = business.dingdingAttendanceFactory().getQywxStatUnitForDayIds(year, month, day, unit);
+                emc.beginTransaction(StatisticQywxUnitForDay.class);
+                if (ids != null && ids.size() > 0) {
+                    for (String item : ids) {
+                        StatisticQywxUnitForDay statisticTopUnitForDay_tmp = emc.find(item, StatisticQywxUnitForDay.class);
+                        emc.remove(statisticTopUnitForDay_tmp);
+                    }
+                }
+                //for day
+                StatisticQywxUnitForDay unitForDay = new StatisticQywxUnitForDay();
+                unitForDay.setO2Unit(unit);
+                unitForDay.setStatisticYear(year);
+                unitForDay.setStatisticMonth(month);
+                unitForDay.setStatisticDate(day);
+                List<AttendanceQywxDetail> details = business.dingdingAttendanceFactory().qywxUnitForDayList(year, month, day, unit);
+                long workDayCount = 0L;
+                long onDutyTimes = 0L;
+                long offDutyTimes = 0L;
+                long outsideDutyTimes = 0L;
+                long resultNormal = 0L;
+                long lateTimes = 0L;
+                long leaveEarlyTimes = 0L;
+                long absenteeismTimes = 0L;
+                long notSignedCount = 0L;
+                if ( details!=null && !details.isEmpty()) {
+                    workDayCount = (long) (details.size() / 2);
+                    onDutyTimes = details.stream().filter(d -> d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_ON)).count();
+                    offDutyTimes = details.stream().filter(d -> d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_OFF)).count();
+                    outsideDutyTimes = details.stream().filter(d -> d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_OUTSIDE)).count();
+                    resultNormal = details.stream().filter(d -> d.getException_type().equals(AttendanceQywxDetail.EXCEPTION_TYPE_NORMAL)).count();
+                    lateTimes = details.stream().filter(d ->
+                            (d.getException_type().contains(AttendanceQywxDetail.EXCEPTION_TYPE_TIME) &&
+                                    d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_ON))).count();
+                    leaveEarlyTimes = details.stream().filter(d ->
+                            (d.getException_type().contains(AttendanceQywxDetail.EXCEPTION_TYPE_TIME) &&
+                                    d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_OFF))).count();
+                    List<AttendanceQywxDetail> ondutys = details.stream().filter(d->
+                            d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_ON)).collect(Collectors.toList());
+                    List<AttendanceQywxDetail> offdutys = details.stream().filter(d->
+                            d.getCheckin_type().equals(AttendanceQywxDetail.CHECKIN_TYPE_OFF)).collect(Collectors.toList());
+                    for (int i = 0; i < ondutys.size(); i++) {
+                        AttendanceQywxDetail detail = ondutys.get(i);
+                        if (detail.getException_type().contains(AttendanceQywxDetail.EXCEPTION_TYPE_NOSIGN)) {
+                            notSignedCount++;
+                            String checkDate = DateTools.format(detail.getCheckin_time_date(), DateTools.format_yyyyMMdd);
+                            Optional<AttendanceQywxDetail> op = offdutys.stream().filter(d->
+                                    checkDate.equals(DateTools.format(d.getCheckin_time_date(), DateTools.format_yyyyMMdd))).findFirst();
+                            if (op.isPresent()) {
+                                AttendanceQywxDetail detail1 = op.get();
+                                if(detail1.getException_type().contains(AttendanceQywxDetail.EXCEPTION_TYPE_NOSIGN)) {
+                                    notSignedCount++;
+                                    absenteeismTimes++;
+                                }
+                            }
+                        }
+                    }
+                }
+                unitForDay.setOnDutyTimes(onDutyTimes);
+                unitForDay.setOffDutyTimes(offDutyTimes);
+                unitForDay.setOutsideDutyTimes(outsideDutyTimes);
+                unitForDay.setWorkDayCount(workDayCount);
+                unitForDay.setResultNormal(resultNormal);
+                unitForDay.setLateTimes(lateTimes);
+                unitForDay.setLeaveEarlyTimes(leaveEarlyTimes);
+                unitForDay.setAbsenteeismTimes(absenteeismTimes);
+                unitForDay.setNotSignedCount(notSignedCount);
+                emc.persist(unitForDay);
+                emc.commit();
+            }
+            saveStatisticUnitForMonth(business, emc, units, year, month);
+        }
+
+
+    }
+
+    /**
+     * 单位月统计
+     * @param business
+     * @param emc
+     * @param units
+     * @param year
+     * @param month
+     * @throws Exception
+     */
+    private void saveStatisticUnitForMonth(Business business, EntityManagerContainer emc, List<String> units,
+                                           String year, String month) throws Exception {
+
+        for (String unit : units) {
+            if (StringUtils.isEmpty(unit)){
+                continue;
+            }
+            Long workDay = business.dingdingAttendanceFactory().sumQywxWorkDayUnitForDayWithMonth(year, month, unit);
+            Long onduty = business.dingdingAttendanceFactory().sumQywxOndutyUnitForDayWithMonth(year, month, unit);
+            Long offDuty = business.dingdingAttendanceFactory().sumQywxOffDutyUnitForDayWithMonth(year, month, unit);
+            Long outside = business.dingdingAttendanceFactory().sumQywxOutsideUnitForDayWithMonth(year, month, unit);
+            Long normal = business.dingdingAttendanceFactory().sumQywxResultNormalUnitForDayWithMonth(year, month, unit);
+            Long late = business.dingdingAttendanceFactory().sumQywxLatetimeUnitForDayWithMonth(year, month, unit);
+            Long leaveearly = business.dingdingAttendanceFactory().sumQywxLeaveEarlyUnitForDayWithMonth(year, month, unit);
+            Long notSign = business.dingdingAttendanceFactory().sumQywxNotSignUnitForDayWithMonth(year, month, unit);
+            Long absenteeism = business.dingdingAttendanceFactory().sumQywxAbsenteeismUnitForDayWithMonth(year, month, unit);
+
+            List<String> list = business.dingdingAttendanceFactory().getQywxStatUnitForMonthIds(year, month, unit);
+            emc.beginTransaction(StatisticQywxUnitForMonth.class);
+            if (list != null && list.size() > 0) {
+                for (String item : list) {
+                    StatisticQywxUnitForMonth statisticTopUnitForMonth_tmp = emc.find(item, StatisticQywxUnitForMonth.class);
+                    emc.remove(statisticTopUnitForMonth_tmp);
+                }
+            }
+            StatisticQywxUnitForMonth unitForMonth = new StatisticQywxUnitForMonth();
+            unitForMonth.setO2Unit(unit);
+            unitForMonth.setStatisticYear(year);
+            unitForMonth.setStatisticMonth(month);
+            unitForMonth.setWorkDayCount(workDay);
+            unitForMonth.setOnDutyTimes(onduty);
+            unitForMonth.setOffDutyTimes(offDuty);
+            unitForMonth.setResultNormal(normal);
+            unitForMonth.setLateTimes(late);
+            unitForMonth.setLeaveEarlyTimes(leaveearly);
+            unitForMonth.setNotSignedCount(notSign);
+            unitForMonth.setAbsenteeismTimes(absenteeism);
+            unitForMonth.setOutsideDutyTimes(outside);
+            emc.persist(unitForMonth);
+            emc.commit();
+        }
+
+    }
+}

+ 16 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/ThisApplication.java

@@ -18,9 +18,13 @@ public class ThisApplication {
 
 
 	public static DingdingAttendanceQueue dingdingQueue = new DingdingAttendanceQueue();
 	public static DingdingAttendanceQueue dingdingQueue = new DingdingAttendanceQueue();
 	public static QywxAttendanceSyncQueue qywxQueue = new QywxAttendanceSyncQueue();
 	public static QywxAttendanceSyncQueue qywxQueue = new QywxAttendanceSyncQueue();
+	public static QywxUnitStatisticQueue unitQywxStatisticQueue = new QywxUnitStatisticQueue();
+	public static QywxPersonStatisticQueue personQywxStatisticQueue = new QywxPersonStatisticQueue();
+
 	public static DingdingPersonStatisticQueue personStatisticQueue = new DingdingPersonStatisticQueue();
 	public static DingdingPersonStatisticQueue personStatisticQueue = new DingdingPersonStatisticQueue();
 	public static DingdingUnitStatisticQueue unitStatisticQueue = new DingdingUnitStatisticQueue();
 	public static DingdingUnitStatisticQueue unitStatisticQueue = new DingdingUnitStatisticQueue();
 
 
+
 	public static void init() throws Exception {
 	public static void init() throws Exception {
 		try {
 		try {
 			new AttendanceSettingService().initAllSystemConfig();
 			new AttendanceSettingService().initAllSystemConfig();
@@ -37,6 +41,8 @@ public class ThisApplication {
 			}
 			}
 			if (BooleanUtils.isTrue(Config.qiyeweixin().getAttendanceSyncEnable())) {
 			if (BooleanUtils.isTrue(Config.qiyeweixin().getAttendanceSyncEnable())) {
 				qywxQueue.start();
 				qywxQueue.start();
+				unitQywxStatisticQueue.start();
+				personQywxStatisticQueue.start();
 				context.schedule(QywxAttendanceSyncScheduleTask.class, "0 0 1 * * ?");
 				context.schedule(QywxAttendanceSyncScheduleTask.class, "0 0 1 * * ?");
 			}
 			}
 		} catch (Exception e) {
 		} catch (Exception e) {
@@ -75,5 +81,15 @@ public class ThisApplication {
 		} catch (Exception e) {
 		} catch (Exception e) {
 			e.printStackTrace();
 			e.printStackTrace();
 		}
 		}
+		try {
+			unitQywxStatisticQueue.stop();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		try {
+			personQywxStatisticQueue.stop();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
 	}
 	}
 }
 }

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

@@ -116,9 +116,7 @@ public class DingdingAttendanceFactory extends AbstractFactory {
 
 
         Predicate p = null;
         Predicate p = null;
         if (startTime != null && endTime != null) {
         if (startTime != null && endTime != null) {
-            long start = startTime.getTime();
-            long end = endTime.getTime();
-            p = cb.between(root.get(AttendanceQywxDetail_.checkin_time), start, end);
+            p = cb.between(root.get(AttendanceQywxDetail_.checkin_time_date), startTime, endTime);
         }
         }
         if (userId != null && !userId.isEmpty()) {
         if (userId != null && !userId.isEmpty()) {
             if (p != null) {
             if (p != null) {
@@ -127,7 +125,7 @@ public class DingdingAttendanceFactory extends AbstractFactory {
                 p = cb.equal(root.get(AttendanceQywxDetail_.userid), userId);
                 p = cb.equal(root.get(AttendanceQywxDetail_.userid), userId);
             }
             }
         }
         }
-        query.select(root).where(p).orderBy(cb.desc(root.get(AttendanceQywxDetail_.checkin_time)));
+        query.select(root).where(p).orderBy(cb.desc(root.get(AttendanceQywxDetail_.checkin_time_date)));
         return em.createQuery(query).getResultList();
         return em.createQuery(query).getResultList();
 
 
     }
     }
@@ -464,9 +462,301 @@ public class DingdingAttendanceFactory extends AbstractFactory {
         return em.createQuery(query).getSingleResult();
         return em.createQuery(query).getSingleResult();
     }
     }
 
 
+    /*********************企业微信统计 ******************************/
+
+
+    /**
+     * 部门统计数据
+     * @param unit
+     * @param year
+     * @param month
+     * @return
+     * @throws Exception
+     */
+    public List<StatisticQywxUnitForMonth> findQywxUnitStatistic(String unit, String year, String month) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxUnitForMonth.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<StatisticQywxUnitForMonth> query = cb.createQuery(StatisticQywxUnitForMonth.class);
+        Root<StatisticQywxUnitForMonth> root = query.from(StatisticQywxUnitForMonth.class);
+        Predicate p = cb.equal(root.get(StatisticQywxUnitForMonth_.o2Unit), unit);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForMonth_.statisticYear), year));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForMonth_.statisticMonth), month));
+
+        query.select(root).where(p);
+        return em.createQuery(query).getResultList();
+    }
+
+    /**
+     * 人员统计数据
+     * @param unit
+     * @param year
+     * @param month
+     * @return
+     * @throws Exception
+     */
+    public List<StatisticQywxPersonForMonth> findQywxPersonStatisticWithUnit(String unit, String year, String month) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxPersonForMonth.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<StatisticQywxPersonForMonth> query = cb.createQuery(StatisticQywxPersonForMonth.class);
+        Root<StatisticQywxPersonForMonth> root = query.from(StatisticQywxPersonForMonth.class);
+        Predicate p = cb.equal(root.get(StatisticQywxPersonForMonth_.o2Unit), unit);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxPersonForMonth_.statisticYear), year));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxPersonForMonth_.statisticMonth), month));
+
+        query.select(root).where(p);
+        return em.createQuery(query).getResultList();
+    }
+
+
+
+    /**
+     * 人员统计数据
+     * @param person
+     * @param year
+     * @param month
+     * @return
+     * @throws Exception
+     */
+    public List<StatisticQywxPersonForMonth> findQywxPersonStatistic(String person, String year, String month) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxPersonForMonth.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<StatisticQywxPersonForMonth> query = cb.createQuery(StatisticQywxPersonForMonth.class);
+        Root<StatisticQywxPersonForMonth> root = query.from(StatisticQywxPersonForMonth.class);
+        Predicate p = cb.equal(root.get(StatisticQywxPersonForMonth_.o2User), person);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxPersonForMonth_.statisticYear), year));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxPersonForMonth_.statisticMonth), month));
+
+        query.select(root).where(p);
+        return em.createQuery(query).getResultList();
+    }
+
+    /**
+     * 个人 月份 所有数据
+     * @param year
+     * @param month
+     * @param person
+     * @return
+     * @throws Exception
+     */
+    public List<AttendanceQywxDetail> qywxPersonForMonthList(String year, String month, String person)  throws Exception {
+        Date start = monthFirstDay(year, month);
+        Date end = monthLastDay(year, month);
 
 
+        EntityManager em = this.entityManagerContainer().get(AttendanceQywxDetail.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<AttendanceQywxDetail> query = cb.createQuery(AttendanceQywxDetail.class);
+        Root<AttendanceQywxDetail> root = query.from(AttendanceQywxDetail.class);
+        Predicate p = cb.between(root.get(AttendanceQywxDetail_.checkin_time_date), start, end);
+        p = cb.and(p, cb.equal(root.get(AttendanceQywxDetail_.o2User), person));
+        query.select(root).where(p);
+        return em.createQuery(query).getResultList();
+    }
+    /**
+     * StatisticDingdingPersonForMonth ids
+     * @param year
+     * @param month
+     * @param person
+     * @return
+     * @throws Exception
+     */
+    public List<String> getQywxStatPersonForMonthIds(String year, String month, String person) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxPersonForMonth.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<String> query = cb.createQuery(String.class);
+        Root<StatisticQywxPersonForMonth> root = query.from(StatisticQywxPersonForMonth.class);
+        Predicate p = cb.equal(root.get(StatisticQywxPersonForMonth_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxPersonForMonth_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxPersonForMonth_.o2User), person));
+        query.select(root.get(StatisticQywxPersonForMonth_.id)).where(p);
+        return em.createQuery(query).getResultList();
+    }
 
 
 
 
+    /**
+     * 单位 日期 所有数据
+     * @param year
+     * @param month
+     * @param day
+     * @param unit
+     * @return
+     * @throws Exception
+     */
+    public List<AttendanceQywxDetail> qywxUnitForDayList(String year, String month, String day,  String unit)  throws Exception {
+        Date startTime = DateTools.parse(year+"-"+month+"-"+day);
+        startTime = startOneDate(startTime);
+        Date endTime = endOneDate(startTime);
+        EntityManager em = this.entityManagerContainer().get(AttendanceQywxDetail.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<AttendanceQywxDetail> query = cb.createQuery(AttendanceQywxDetail.class);
+        Root<AttendanceQywxDetail> root = query.from(AttendanceQywxDetail.class);
+        Predicate p = cb.between(root.get(AttendanceQywxDetail_.checkin_time_date), startTime, endTime);
+        p = cb.and(p, cb.equal(root.get(AttendanceQywxDetail_.o2Unit), unit));
+        query.select(root).where(p);
+        return em.createQuery(query).getResultList();
+    }
+
+    /**
+     * 查询所有有数据的组织 去重的
+     * @param date
+     * @return
+     * @throws Exception
+     */
+    public List<String> qywxUnitDistinct(String date) throws Exception {
+        Date startTime = DateTools.parse(date);
+        startTime = startOneDate(startTime);
+        Date endTime = endOneDate(startTime);
+        EntityManager em = this.entityManagerContainer().get(AttendanceQywxDetail.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        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();
+    }
+
+    /**
+     * 查询单位 日期 统计数据的id 删除用的
+     * @param year
+     * @param month
+     * @param day
+     * @param unit
+     * @return
+     * @throws Exception
+     */
+    public List<String> getQywxStatUnitForDayIds(String year, String month, String day, String unit) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxUnitForDay.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<String> query = cb.createQuery(String.class);
+        Root<StatisticQywxUnitForDay> root = query.from(StatisticQywxUnitForDay.class);
+        Predicate p = cb.equal(root.get(StatisticQywxUnitForDay_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.statisticDate), day));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.o2Unit), unit));
+        query.select(root.get(StatisticQywxUnitForDay_.id)).where(p);
+        return em.createQuery(query).getResultList();
+    }
+
+    /**
+     * 查询单位 月份统计数据的id 删除用的
+     * @param year
+     * @param month
+     * @param unit
+     * @return
+     * @throws Exception
+     */
+    public List<String> getQywxStatUnitForMonthIds(String year, String month, String unit) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxUnitForMonth.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<String> query = cb.createQuery(String.class);
+        Root<StatisticQywxUnitForMonth> root = query.from(StatisticQywxUnitForMonth.class);
+        Predicate p = cb.equal(root.get(StatisticQywxUnitForMonth_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForMonth_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForMonth_.o2Unit), unit));
+        query.select(root.get(StatisticQywxUnitForMonth_.id)).where(p);
+        return em.createQuery(query).getResultList();
+    }
+
+    public Long sumQywxWorkDayUnitForDayWithMonth(String year, String month, String unit) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxUnitForDay.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> query = cb.createQuery(Long.class);
+        Root<StatisticQywxUnitForDay> root = query.from(StatisticQywxUnitForDay.class);
+        Predicate p = cb.equal(root.get(StatisticQywxUnitForDay_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.o2Unit), unit));
+        query.select(cb.sum(root.get(StatisticQywxUnitForDay_.workDayCount))).where(p);
+        return em.createQuery(query).getSingleResult();
+    }
+
+    public Long sumQywxOndutyUnitForDayWithMonth(String year, String month, String unit) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxUnitForDay.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> query = cb.createQuery(Long.class);
+        Root<StatisticQywxUnitForDay> root = query.from(StatisticQywxUnitForDay.class);
+        Predicate p = cb.equal(root.get(StatisticQywxUnitForDay_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.o2Unit), unit));
+        query.select(cb.sum(root.get(StatisticQywxUnitForDay_.onDutyTimes))).where(p);
+        return em.createQuery(query).getSingleResult();
+    }
+    public Long sumQywxOffDutyUnitForDayWithMonth(String year, String month, String unit) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxUnitForDay.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> query = cb.createQuery(Long.class);
+        Root<StatisticQywxUnitForDay> root = query.from(StatisticQywxUnitForDay.class);
+        Predicate p = cb.equal(root.get(StatisticQywxUnitForDay_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.o2Unit), unit));
+        query.select(cb.sum(root.get(StatisticQywxUnitForDay_.offDutyTimes))).where(p);
+        return em.createQuery(query).getSingleResult();
+    }
+    public Long sumQywxOutsideUnitForDayWithMonth(String year, String month, String unit) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxUnitForDay.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> query = cb.createQuery(Long.class);
+        Root<StatisticQywxUnitForDay> root = query.from(StatisticQywxUnitForDay.class);
+        Predicate p = cb.equal(root.get(StatisticQywxUnitForDay_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.o2Unit), unit));
+        query.select(cb.sum(root.get(StatisticQywxUnitForDay_.outsideDutyTimes))).where(p);
+        return em.createQuery(query).getSingleResult();
+    }
+    public Long sumQywxResultNormalUnitForDayWithMonth(String year, String month, String unit) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxUnitForDay.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> query = cb.createQuery(Long.class);
+        Root<StatisticQywxUnitForDay> root = query.from(StatisticQywxUnitForDay.class);
+        Predicate p = cb.equal(root.get(StatisticQywxUnitForDay_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.o2Unit), unit));
+        query.select(cb.sum(root.get(StatisticQywxUnitForDay_.resultNormal))).where(p);
+        return em.createQuery(query).getSingleResult();
+    }
+    public Long sumQywxLatetimeUnitForDayWithMonth(String year, String month, String unit) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxUnitForDay.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> query = cb.createQuery(Long.class);
+        Root<StatisticQywxUnitForDay> root = query.from(StatisticQywxUnitForDay.class);
+        Predicate p = cb.equal(root.get(StatisticQywxUnitForDay_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.o2Unit), unit));
+        query.select(cb.sum(root.get(StatisticQywxUnitForDay_.lateTimes))).where(p);
+        return em.createQuery(query).getSingleResult();
+    }
+    public Long sumQywxLeaveEarlyUnitForDayWithMonth(String year, String month, String unit) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxUnitForDay.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> query = cb.createQuery(Long.class);
+        Root<StatisticQywxUnitForDay> root = query.from(StatisticQywxUnitForDay.class);
+        Predicate p = cb.equal(root.get(StatisticQywxUnitForDay_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.o2Unit), unit));
+        query.select(cb.sum(root.get(StatisticQywxUnitForDay_.leaveEarlyTimes))).where(p);
+        return em.createQuery(query).getSingleResult();
+    }
+    public Long sumQywxAbsenteeismUnitForDayWithMonth(String year, String month, String unit) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxUnitForDay.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> query = cb.createQuery(Long.class);
+        Root<StatisticQywxUnitForDay> root = query.from(StatisticQywxUnitForDay.class);
+        Predicate p = cb.equal(root.get(StatisticQywxUnitForDay_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.o2Unit), unit));
+        query.select(cb.sum(root.get(StatisticQywxUnitForDay_.absenteeismTimes))).where(p);
+        return em.createQuery(query).getSingleResult();
+    }
+    public Long sumQywxNotSignUnitForDayWithMonth(String year, String month, String unit) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(StatisticQywxUnitForDay.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<Long> query = cb.createQuery(Long.class);
+        Root<StatisticQywxUnitForDay> root = query.from(StatisticQywxUnitForDay.class);
+        Predicate p = cb.equal(root.get(StatisticQywxUnitForDay_.statisticYear), year);
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.statisticMonth), month));
+        p = cb.and(p, cb.equal(root.get(StatisticQywxUnitForDay_.o2Unit), unit));
+        query.select(cb.sum(root.get(StatisticQywxUnitForDay_.notSignedCount))).where(p);
+        return em.createQuery(query).getSingleResult();
+    }
+
     private Date monthLastDay(String year, String month) throws Exception {
     private Date monthLastDay(String year, String month) throws Exception {
         Calendar cal = Calendar.getInstance();
         Calendar cal = Calendar.getInstance();
         int yearInt = Integer.parseInt(year);
         int yearInt = Integer.parseInt(year);

+ 2 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/ActionApplication.java

@@ -22,6 +22,7 @@ import com.x.attendance.assemble.control.jaxrs.dingding.DingdingAttendanceAction
 import com.x.attendance.assemble.control.jaxrs.dingdingstatistic.DingdingAttendanceStatisticAction;
 import com.x.attendance.assemble.control.jaxrs.dingdingstatistic.DingdingAttendanceStatisticAction;
 import com.x.attendance.assemble.control.jaxrs.fileimport.AttendanceDetailFileImportAction;
 import com.x.attendance.assemble.control.jaxrs.fileimport.AttendanceDetailFileImportAction;
 import com.x.attendance.assemble.control.jaxrs.qywx.QywxAttendanceAction;
 import com.x.attendance.assemble.control.jaxrs.qywx.QywxAttendanceAction;
+import com.x.attendance.assemble.control.jaxrs.qywxstatistic.QywxAttendanceStatisticAction;
 import com.x.attendance.assemble.control.jaxrs.selfholiday.AttendanceSelfHolidayAction;
 import com.x.attendance.assemble.control.jaxrs.selfholiday.AttendanceSelfHolidayAction;
 import com.x.attendance.assemble.control.jaxrs.selfholiday.AttendanceSelfHolidaySimpleAction;
 import com.x.attendance.assemble.control.jaxrs.selfholiday.AttendanceSelfHolidaySimpleAction;
 import com.x.attendance.assemble.control.jaxrs.uuid.UUIDAction;
 import com.x.attendance.assemble.control.jaxrs.uuid.UUIDAction;
@@ -54,6 +55,7 @@ public class ActionApplication extends AbstractActionApplication {
 		this.classes.add(DingdingAttendanceAction.class);
 		this.classes.add(DingdingAttendanceAction.class);
 		this.classes.add(QywxAttendanceAction.class);
 		this.classes.add(QywxAttendanceAction.class);
 		this.classes.add(DingdingAttendanceStatisticAction.class);
 		this.classes.add(DingdingAttendanceStatisticAction.class);
+		this.classes.add(QywxAttendanceStatisticAction.class);
 		return this.classes;
 		return this.classes;
 	}
 	}
 
 

+ 10 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/QywxStatisticJaxrsFilter.java

@@ -0,0 +1,10 @@
+package com.x.attendance.assemble.control.jaxrs;
+
+import com.x.base.core.project.jaxrs.ManagerUserJaxrsFilter;
+
+import javax.servlet.annotation.WebFilter;
+
+
+@WebFilter(urlPatterns = "/jaxrs/qywxstatistic/*", asyncSupported = true)
+public class QywxStatisticJaxrsFilter extends ManagerUserJaxrsFilter {
+}

+ 0 - 5
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/dingding/ActionListDDAttendanceDetail.java

@@ -40,7 +40,6 @@ public class ActionListDDAttendanceDetail extends BaseAction {
         try ( EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
         try ( EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
             Business business = new Business(emc);
             Business business = new Business(emc);
             Wi wi = this.convertToWrapIn(jsonElement , Wi.class);
             Wi wi = this.convertToWrapIn(jsonElement , Wi.class);
-            logger.info("传入参数:"+wi.toString());
             if (StringUtils.isEmpty(wi.getYear())) {
             if (StringUtils.isEmpty(wi.getYear())) {
                 throw new TimeEmptyException();
                 throw new TimeEmptyException();
             }
             }
@@ -61,11 +60,8 @@ public class ActionListDDAttendanceDetail extends BaseAction {
                     endDay = getEndDay(wi.getYear(), wi.getMonth(), wi.getDay());
                     endDay = getEndDay(wi.getYear(), wi.getMonth(), wi.getDay());
                 }
                 }
             }
             }
-            logger.info("startDay:"+DateTools.format(startDay));
-            logger.info("endDay:"+DateTools.format(endDay));
             BetweenTerms betweenTerms = new BetweenTerms();
             BetweenTerms betweenTerms = new BetweenTerms();
             betweenTerms.put("userCheckTime", ListTools.toList(startDay.getTime(), endDay.getTime()));
             betweenTerms.put("userCheckTime", ListTools.toList(startDay.getTime(), endDay.getTime()));
-            logger.info("between :"+betweenTerms.toString());
             String id = EMPTY_SYMBOL;
             String id = EMPTY_SYMBOL;
             /** 如果不是空位标志位 */
             /** 如果不是空位标志位 */
             if (!StringUtils.equals(EMPTY_SYMBOL, flag)) {
             if (!StringUtils.equals(EMPTY_SYMBOL, flag)) {
@@ -80,7 +76,6 @@ public class ActionListDDAttendanceDetail extends BaseAction {
                 if (isTimeResultEnable(wi.getTimeResult())) {
                 if (isTimeResultEnable(wi.getTimeResult())) {
                     equals.put("timeResult", wi.getTimeResult());
                     equals.put("timeResult", wi.getTimeResult());
                 }
                 }
-                logger.info("equals :"+equals.toString());
                 result = this.standardListNext(Wo.copier, id, count, JpaObject.sequence_FIELDNAME, equals, null,
                 result = this.standardListNext(Wo.copier, id, count, JpaObject.sequence_FIELDNAME, equals, null,
                         null, null, null, null, null, betweenTerms, true, DESC);
                         null, null, null, null, null, betweenTerms, true, DESC);
             }
             }

+ 0 - 15
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/dingdingstatistic/ActionTest.java

@@ -1,15 +0,0 @@
-package com.x.attendance.assemble.control.jaxrs.dingdingstatistic;
-
-import com.x.base.core.project.http.ActionResult;
-import com.x.base.core.project.jaxrs.WrapBoolean;
-
-/**
- * Created by fancyLou on 2020-04-05.
- * Copyright © 2020 O2. All rights reserved.
- */
-public class ActionTest extends BaseAction {
-
-    ActionResult<WrapBoolean> execute() throws Exception {
-        return new ActionResult<>();
-    }
-}

+ 1 - 16
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/dingdingstatistic/DingdingAttendanceStatisticAction.java

@@ -30,22 +30,7 @@ public class DingdingAttendanceStatisticAction extends StandardJaxrsAction {
     private static final Logger logger = LoggerFactory.getLogger(DingdingAttendanceStatisticAction.class);
     private static final Logger logger = LoggerFactory.getLogger(DingdingAttendanceStatisticAction.class);
 
 
 
 
-    @JaxrsMethodDescribe(value = "测试", action = ActionTest.class)
-    @GET
-    @Path("demo")
-    @Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
-    @Consumes(MediaType.APPLICATION_JSON)
-    public void syncData(@Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request) {
-        ActionResult<WrapBoolean> result = new ActionResult<>();
-        EffectivePerson effectivePerson = this.effectivePerson(request);
-        try {
-            result = new ActionTest().execute();
-        }catch (Exception e) {
-            logger.error(e, effectivePerson, request, null);
-            result.error(e);
-        }
-        asyncResponse.resume(ResponseFactory.getEntityTagActionResultResponse(request, result));
-    }
+
 
 
     @JaxrsMethodDescribe(value = "人员月份统计查询", action = ActionPersonStatistic.class)
     @JaxrsMethodDescribe(value = "人员月份统计查询", action = ActionPersonStatistic.class)
     @GET
     @GET

+ 216 - 33
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/qywx/ActionListQywxAttendanceDetail.java

@@ -2,7 +2,11 @@ package com.x.attendance.assemble.control.jaxrs.qywx;
 
 
 import com.google.gson.JsonElement;
 import com.google.gson.JsonElement;
 import com.x.attendance.assemble.control.Business;
 import com.x.attendance.assemble.control.Business;
+import com.x.attendance.assemble.control.jaxrs.dingding.ActionListDDAttendanceDetail;
 import com.x.attendance.assemble.control.jaxrs.dingding.BaseAction;
 import com.x.attendance.assemble.control.jaxrs.dingding.BaseAction;
+import com.x.attendance.assemble.control.jaxrs.dingding.exception.SearchArgEmptyException;
+import com.x.attendance.assemble.control.jaxrs.dingding.exception.TimeEmptyException;
+import com.x.attendance.entity.AttendanceDingtalkDetail;
 import com.x.attendance.entity.AttendanceQywxDetail;
 import com.x.attendance.entity.AttendanceQywxDetail;
 import com.x.base.core.container.EntityManagerContainer;
 import com.x.base.core.container.EntityManagerContainer;
 import com.x.base.core.container.factory.EntityManagerContainerFactory;
 import com.x.base.core.container.factory.EntityManagerContainerFactory;
@@ -12,11 +16,18 @@ import com.x.base.core.project.bean.WrapCopier;
 import com.x.base.core.project.bean.WrapCopierFactory;
 import com.x.base.core.project.bean.WrapCopierFactory;
 import com.x.base.core.project.gson.GsonPropertyObject;
 import com.x.base.core.project.gson.GsonPropertyObject;
 import com.x.base.core.project.http.ActionResult;
 import com.x.base.core.project.http.ActionResult;
+import com.x.base.core.project.jaxrs.BetweenTerms;
+import com.x.base.core.project.jaxrs.EqualsTerms;
+import com.x.base.core.project.jaxrs.InTerms;
 import com.x.base.core.project.logger.Logger;
 import com.x.base.core.project.logger.Logger;
 import com.x.base.core.project.logger.LoggerFactory;
 import com.x.base.core.project.logger.LoggerFactory;
 import com.x.base.core.project.organization.Person;
 import com.x.base.core.project.organization.Person;
 import com.x.base.core.project.tools.DateTools;
 import com.x.base.core.project.tools.DateTools;
+import com.x.base.core.project.tools.ListTools;
+import org.apache.commons.lang3.StringUtils;
 
 
+import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.Date;
 import java.util.Date;
 import java.util.List;
 import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
@@ -25,60 +36,126 @@ public class ActionListQywxAttendanceDetail extends BaseAction {
 
 
     private static final Logger logger = LoggerFactory.getLogger(ActionListQywxAttendanceDetail.class);
     private static final Logger logger = LoggerFactory.getLogger(ActionListQywxAttendanceDetail.class);
 
 
-    public ActionResult<List<Wo>> execute(JsonElement jsonElement) throws Exception {
+    public ActionResult<List<Wo>> execute(String flag, Integer count, JsonElement jsonElement) throws Exception {
         ActionResult<List<Wo>> result = new ActionResult<>();
         ActionResult<List<Wo>> result = new ActionResult<>();
         try ( EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
         try ( EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
             Business business = new Business(emc);
             Business business = new Business(emc);
             Wi wi = this.convertToWrapIn(jsonElement , Wi.class);
             Wi wi = this.convertToWrapIn(jsonElement , Wi.class);
-            Date start = DateTools.parseDateTime(wi.getStartTime());
-            Date end = DateTools.parseDateTime(wi.getEndTime());
-            String qywxUser = null;
-            //转化成企业微信的id
-            if (wi.getPerson() != null && !wi.getPerson().isEmpty()) {
-                Person person = business.organization().person().getObject(wi.getPerson());
-                qywxUser = person.getQiyeweixinId();
+            if (StringUtils.isEmpty(wi.getYear())) {
+                throw new TimeEmptyException();
             }
             }
-            List<AttendanceQywxDetail> list = business.dingdingAttendanceFactory().findQywxAttendanceDetail(start, end, qywxUser);
-            if (list != null && !list.isEmpty()) {
-                List<Wo> wos = list.stream().map(detail -> {
-                    Wo wo = new Wo();
-                    try {
-                        wo = Wo.copier.copy(detail, wo);
-                        wo.formatDateTime();
-                    }catch (Exception e) {
-                        logger.error(e);
-                    }
-                    return wo;
-                }).collect(Collectors.toList());
-                result.setData(wos);
+            if (StringUtils.isEmpty(wi.getPerson()) && StringUtils.isEmpty(wi.getUnit()) && StringUtils.isEmpty(wi.getTopUnit())) {
+                throw new SearchArgEmptyException();
             }
             }
+            Date startDay  ;
+            Date endDay;
+            if (StringUtils.isEmpty(wi.getMonth())) {
+                startDay = getDay(wi.getYear(), "1", "1");
+                endDay = getDay(wi.getYear(), "12", "31");
+            }else {
+                if (StringUtils.isEmpty(wi.getDay())) {
+                    startDay = getDay(wi.getYear(), wi.getMonth(), "1");
+                    endDay = getMonthLastDay(wi.getYear(), wi.getMonth());
+                }else {
+                    startDay = getDay(wi.getYear(), wi.getMonth(), wi.getDay());
+                    endDay = getEndDay(wi.getYear(), wi.getMonth(), wi.getDay());
+                }
+            }
+            BetweenTerms betweenTerms = new BetweenTerms();
+            betweenTerms.put("checkin_time_date", ListTools.toList(startDay, endDay));
+            String id = EMPTY_SYMBOL;
+            /** 如果不是空位标志位 */
+            if (!StringUtils.equals(EMPTY_SYMBOL, flag)) {
+                id = flag;
+            }
+            if (StringUtils.isNotEmpty(wi.getPerson())) {
+                EqualsTerms equals = new EqualsTerms();
+                equals.put("o2User", wi.getPerson());
+                if (isCheckTypeEnable(wi.getCheckType())){
+                    equals.put("checkin_type", wi.getCheckType());
+                }
+                if (isExceptionTypeEnable(wi.getExceptionType())) {
+                    equals.put("exception_type", wi.getExceptionType());
+                }
+                result = this.standardListNext(Wo.copier, id, count, JpaObject.sequence_FIELDNAME, equals, null,
+                        null, null, null, null, null, betweenTerms, true, DESC);
+            }
+            if (StringUtils.isNotEmpty(wi.getUnit())) {
+                EqualsTerms equals = new EqualsTerms();
+                equals.put("o2Unit", wi.getUnit());
+                if (isCheckTypeEnable(wi.getCheckType())){
+                    equals.put("checkin_type", wi.getCheckType());
+                }
+                if (isExceptionTypeEnable(wi.getExceptionType())) {
+                    equals.put("exception_type", wi.getExceptionType());
+                }
+                result = this.standardListNext(Wo.copier, id, count, JpaObject.sequence_FIELDNAME, equals, null,
+                        null, null, null, null, null, betweenTerms, true, DESC);
+            }
+            if (StringUtils.isNotEmpty(wi.getTopUnit())) {
+                EqualsTerms equals = new EqualsTerms();
+                if (isCheckTypeEnable(wi.getCheckType())){
+                    equals.put("checkin_type", wi.getCheckType());
+                }
+                if (isExceptionTypeEnable(wi.getExceptionType())) {
+                    equals.put("exception_type", wi.getExceptionType());
+                }
+                InTerms ins = new InTerms();
+                List<String> subUnits = business.organization().unit().listWithUnitSubNested( wi.getTopUnit() );
+                if (subUnits == null || subUnits.isEmpty()) {
+                    subUnits = new ArrayList<>();
+                }
+                subUnits.add(wi.getTopUnit());
+                ins.put("o2Unit", subUnits);
+                result = this.standardListNext(Wo.copier, id, count, JpaObject.sequence_FIELDNAME, equals, null,
+                        null, ins, null, null, null, betweenTerms, true, DESC);
+            }
+
         }
         }
         return result;
         return result;
     }
     }
 
 
     public static class Wi extends GsonPropertyObject {
     public static class Wi extends GsonPropertyObject {
-        @FieldDescribe("开始时间:yyyy-MM-dd HH:mm:ss")
-        private String startTime;
-        @FieldDescribe("结束时间:yyyy-MM-dd HH:mm:ss")
-        private String endTime;
+        @FieldDescribe("年份")
+        private String year;
+        @FieldDescribe("月份")
+        private String month;
+        @FieldDescribe("日期")
+        private String day;
         @FieldDescribe("人员")
         @FieldDescribe("人员")
         private String person;
         private String person;
+        @FieldDescribe("部门")
+        private String unit;
+        @FieldDescribe("顶级部门,会及联查询下级部门")
+        private String topUnit;
+        @FieldDescribe("打卡类型:上班打卡,下班打卡,外出打卡")
+        private String checkType;
+        @FieldDescribe("打卡结果:时间异常,地点异常,未打卡,wifi异常,非常用设备")
+        private String exceptionType;
 
 
 
 
-        public String getStartTime() {
-            return startTime;
+        public String getYear() {
+            return year;
         }
         }
 
 
-        public void setStartTime(String startTime) {
-            this.startTime = startTime;
+        public void setYear(String year) {
+            this.year = year;
         }
         }
 
 
-        public String getEndTime() {
-            return endTime;
+        public String getMonth() {
+            return month;
         }
         }
 
 
-        public void setEndTime(String endTime) {
-            this.endTime = endTime;
+        public void setMonth(String month) {
+            this.month = month;
+        }
+
+        public String getDay() {
+            return day;
+        }
+
+        public void setDay(String day) {
+            this.day = day;
         }
         }
 
 
         public String getPerson() {
         public String getPerson() {
@@ -88,6 +165,38 @@ public class ActionListQywxAttendanceDetail extends BaseAction {
         public void setPerson(String person) {
         public void setPerson(String person) {
             this.person = person;
             this.person = person;
         }
         }
+
+        public String getUnit() {
+            return unit;
+        }
+
+        public void setUnit(String unit) {
+            this.unit = unit;
+        }
+
+        public String getTopUnit() {
+            return topUnit;
+        }
+
+        public void setTopUnit(String topUnit) {
+            this.topUnit = topUnit;
+        }
+
+        public String getCheckType() {
+            return checkType;
+        }
+
+        public void setCheckType(String checkType) {
+            this.checkType = checkType;
+        }
+
+        public String getExceptionType() {
+            return exceptionType;
+        }
+
+        public void setExceptionType(String exceptionType) {
+            this.exceptionType = exceptionType;
+        }
     }
     }
 
 
     public static class Wo extends AttendanceQywxDetail {
     public static class Wo extends AttendanceQywxDetail {
@@ -96,7 +205,16 @@ public class ActionListQywxAttendanceDetail extends BaseAction {
 
 
         @FieldDescribe("实际打卡时间")
         @FieldDescribe("实际打卡时间")
         private Date checkTimeFormat;
         private Date checkTimeFormat;
+        @FieldDescribe("排序号")
+        private Long rank;
 
 
+        public Long getRank() {
+            return rank;
+        }
+
+        public void setRank(Long rank) {
+            this.rank = rank;
+        }
 
 
         public void formatDateTime() {
         public void formatDateTime() {
             if (checkTimeFormat == null) {
             if (checkTimeFormat == null) {
@@ -114,4 +232,69 @@ public class ActionListQywxAttendanceDetail extends BaseAction {
             this.checkTimeFormat = checkTimeFormat;
             this.checkTimeFormat = checkTimeFormat;
         }
         }
     }
     }
+
+
+    private boolean isCheckTypeEnable(String type) {
+        if (StringUtils.isEmpty(type) || (!AttendanceQywxDetail.CHECKIN_TYPE_OFF.equals(type) && !AttendanceQywxDetail.CHECKIN_TYPE_ON.equals(type) && !AttendanceQywxDetail.CHECKIN_TYPE_OUTSIDE.equals(type))) {
+            return false;
+        }
+        return true;
+    }
+
+    private boolean isExceptionTypeEnable(String result) {
+        if (StringUtils.isEmpty(result) ||
+                (!AttendanceQywxDetail.EXCEPTION_TYPE_NORMAL.equals(result)
+                        && !AttendanceQywxDetail.EXCEPTION_TYPE_ADDRESS.equals(result)
+                        && !AttendanceQywxDetail.EXCEPTION_TYPE_NOSIGN.equals(result)
+                        && !AttendanceQywxDetail.EXCEPTION_TYPE_TIME.equals(result)
+                        && !AttendanceQywxDetail.EXCEPTION_TYPE_UNKOWN_DEVICE.equals(result)
+                        && !AttendanceQywxDetail.EXCEPTION_TYPE_WIFI.equals(result))) {
+            return false;
+        }
+        return true;
+    }
+
+    private static Date getMonthLastDay(String year, String month) throws Exception {
+        Calendar cal = Calendar.getInstance();
+        int yearInt = Integer.parseInt(year);
+        cal.set(Calendar.YEAR, yearInt);
+        int monthInt = Integer.parseInt(month);
+        cal.set(Calendar.MONTH, monthInt);
+        cal.set(Calendar.DAY_OF_MONTH, 1);
+        cal.set(Calendar.HOUR_OF_DAY, 23);
+        cal.set(Calendar.MINUTE, 59);
+        cal.set(Calendar.SECOND, 59);
+        cal.set(Calendar.MILLISECOND, 0);
+        cal.add(Calendar.DAY_OF_MONTH, -1);
+        return cal.getTime();
+    }
+
+    private static Date getDay(String year, String month, String day) throws Exception {
+        Calendar cal = Calendar.getInstance();
+        int yearInt = Integer.parseInt(year);
+        cal.set(Calendar.YEAR, yearInt);
+        int monthInt = Integer.parseInt(month);
+        cal.set(Calendar.MONTH, monthInt-1);
+        int dayInt = Integer.parseInt(day);
+        cal.set(Calendar.DATE, dayInt);
+        cal.set(Calendar.HOUR_OF_DAY, 0);
+        cal.set(Calendar.MINUTE, 0);
+        cal.set(Calendar.SECOND, 0);
+        cal.set(Calendar.MILLISECOND, 0);
+        return cal.getTime();
+    }
+    private static Date getEndDay(String year, String month, String day) throws Exception {
+        Calendar cal = Calendar.getInstance();
+        int yearInt = Integer.parseInt(year);
+        cal.set(Calendar.YEAR, yearInt);
+        int monthInt = Integer.parseInt(month);
+        cal.set(Calendar.MONTH, monthInt-1);
+        int dayInt = Integer.parseInt(day);
+        cal.set(Calendar.DATE, dayInt);
+        cal.set(Calendar.HOUR_OF_DAY, 23);
+        cal.set(Calendar.MINUTE, 59);
+        cal.set(Calendar.SECOND, 59);
+        cal.set(Calendar.MILLISECOND, 0);
+        return cal.getTime();
+    }
 }
 }

+ 6 - 3
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/qywx/QywxAttendanceAction.java

@@ -86,14 +86,17 @@ public class QywxAttendanceAction  extends StandardJaxrsAction {
 
 
     @JaxrsMethodDescribe(value = "查询企业微信打卡结果", action = ActionListQywxAttendanceDetail.class)
     @JaxrsMethodDescribe(value = "查询企业微信打卡结果", action = ActionListQywxAttendanceDetail.class)
     @PUT
     @PUT
-    @Path("attendance/list")
+    @Path("attendance/list/{id}/next/{count}")
     @Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
     @Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
     @Consumes(MediaType.APPLICATION_JSON)
     @Consumes(MediaType.APPLICATION_JSON)
-    public void listDingdingAttendance(@Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request, JsonElement jsonElement) {
+    public void listDingdingAttendance(@Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request,
+                                       @JaxrsParameterDescribe("最后一条数据ID") @PathParam("id") String id,
+                                       @JaxrsParameterDescribe("每页显示的条目数量") @PathParam("count") Integer count,
+                                       JsonElement jsonElement) {
         ActionResult<List<ActionListQywxAttendanceDetail.Wo>> result = new ActionResult<>();
         ActionResult<List<ActionListQywxAttendanceDetail.Wo>> result = new ActionResult<>();
         EffectivePerson effectivePerson = this.effectivePerson(request);
         EffectivePerson effectivePerson = this.effectivePerson(request);
         try {
         try {
-            result = new ActionListQywxAttendanceDetail().execute(jsonElement);
+            result = new ActionListQywxAttendanceDetail().execute(id, count, jsonElement);
         }catch (Exception e) {
         }catch (Exception e) {
             logger.error(e, effectivePerson, request, null);
             logger.error(e, effectivePerson, request, null);
             result.error(e);
             result.error(e);

+ 45 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/qywxstatistic/ActionPersonStatistic.java

@@ -0,0 +1,45 @@
+package com.x.attendance.assemble.control.jaxrs.qywxstatistic;
+
+import com.x.attendance.assemble.control.Business;
+import com.x.attendance.assemble.control.jaxrs.dingdingstatistic.BaseAction;
+import com.x.attendance.assemble.control.jaxrs.dingdingstatistic.EmptyArgsException;
+import com.x.attendance.entity.StatisticDingdingPersonForMonth;
+import com.x.attendance.entity.StatisticQywxPersonForMonth;
+import com.x.base.core.container.EntityManagerContainer;
+import com.x.base.core.container.factory.EntityManagerContainerFactory;
+import com.x.base.core.entity.JpaObject;
+import com.x.base.core.project.bean.WrapCopier;
+import com.x.base.core.project.bean.WrapCopierFactory;
+import com.x.base.core.project.http.ActionResult;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.List;
+
+/**
+ * Created by fancyLou on 2020-04-07.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class ActionPersonStatistic extends BaseAction {
+
+
+    ActionResult<List<Wo>> execute(String person, String year, String month) throws Exception {
+        ActionResult<List<Wo>> result = new ActionResult<>();
+        if (StringUtils.isEmpty(person) || StringUtils.isEmpty(year) || StringUtils.isEmpty(month)) {
+            throw new EmptyArgsException();
+        }
+        try ( EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+            Business business = new Business(emc);
+            List<StatisticQywxPersonForMonth> list = business.dingdingAttendanceFactory().findQywxPersonStatistic(person, year, month);
+            result.setData(Wo.copier.copy(list));
+        }
+        return result;
+    }
+
+
+
+    public static class Wo extends StatisticQywxPersonForMonth {
+        static final WrapCopier<StatisticQywxPersonForMonth, Wo> copier = WrapCopierFactory.wo(StatisticQywxPersonForMonth.class, Wo.class,
+                null, JpaObject.FieldsInvisible);
+
+    }
+}

+ 45 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/qywxstatistic/ActionPersonStatisticWithUnit.java

@@ -0,0 +1,45 @@
+package com.x.attendance.assemble.control.jaxrs.qywxstatistic;
+
+import com.x.attendance.assemble.control.Business;
+import com.x.attendance.assemble.control.jaxrs.dingdingstatistic.BaseAction;
+import com.x.attendance.assemble.control.jaxrs.dingdingstatistic.EmptyArgsException;
+import com.x.attendance.entity.StatisticDingdingPersonForMonth;
+import com.x.attendance.entity.StatisticQywxPersonForMonth;
+import com.x.base.core.container.EntityManagerContainer;
+import com.x.base.core.container.factory.EntityManagerContainerFactory;
+import com.x.base.core.entity.JpaObject;
+import com.x.base.core.project.bean.WrapCopier;
+import com.x.base.core.project.bean.WrapCopierFactory;
+import com.x.base.core.project.http.ActionResult;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.List;
+
+/**
+ * Created by fancyLou on 2020-04-07.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class ActionPersonStatisticWithUnit extends BaseAction {
+
+
+    ActionResult<List<Wo>> execute(String unit, String year, String month) throws Exception {
+        ActionResult<List<Wo>> result = new ActionResult<>();
+        if (StringUtils.isEmpty(unit) || StringUtils.isEmpty(year) || StringUtils.isEmpty(month)) {
+            throw new EmptyArgsException();
+        }
+        try ( EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+            Business business = new Business(emc);
+            List<StatisticQywxPersonForMonth> list = business.dingdingAttendanceFactory().findQywxPersonStatisticWithUnit(unit, year, month);
+            result.setData(Wo.copier.copy(list));
+        }
+        return result;
+    }
+
+
+
+    public static class Wo extends StatisticQywxPersonForMonth {
+        static final WrapCopier<StatisticQywxPersonForMonth, Wo> copier = WrapCopierFactory.wo(StatisticQywxPersonForMonth.class, Wo.class,
+                null, JpaObject.FieldsInvisible);
+
+    }
+}

+ 49 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/qywxstatistic/ActionUnitStatistic.java

@@ -0,0 +1,49 @@
+package com.x.attendance.assemble.control.jaxrs.qywxstatistic;
+
+import com.x.attendance.assemble.control.Business;
+import com.x.attendance.assemble.control.jaxrs.dingdingstatistic.BaseAction;
+import com.x.attendance.assemble.control.jaxrs.dingdingstatistic.EmptyArgsException;
+import com.x.attendance.entity.StatisticDingdingUnitForMonth;
+import com.x.attendance.entity.StatisticQywxUnitForMonth;
+import com.x.base.core.container.EntityManagerContainer;
+import com.x.base.core.container.factory.EntityManagerContainerFactory;
+import com.x.base.core.entity.JpaObject;
+import com.x.base.core.project.bean.WrapCopier;
+import com.x.base.core.project.bean.WrapCopierFactory;
+import com.x.base.core.project.http.ActionResult;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.List;
+
+/**
+ * Created by fancyLou on 2020-04-07.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class ActionUnitStatistic extends BaseAction {
+
+
+    ActionResult<Wo> execute(String unit, String year, String month) throws Exception {
+        ActionResult<Wo> result = new ActionResult<>();
+        if (StringUtils.isEmpty(unit) || StringUtils.isEmpty(year) || StringUtils.isEmpty(month)) {
+            throw new EmptyArgsException();
+        }
+        try ( EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+            Business business = new Business(emc);
+            List<StatisticQywxUnitForMonth> list = business.dingdingAttendanceFactory().findQywxUnitStatistic(unit, year, month);
+            if (list!=null && !list.isEmpty()) {
+                result.setData(Wo.copier.copy(list.get(0)));
+            }else {
+                result.setData(new Wo());
+            }
+        }
+        return result;
+    }
+
+
+
+    public static class Wo extends StatisticQywxUnitForMonth {
+        static final WrapCopier<StatisticQywxUnitForMonth, Wo> copier = WrapCopierFactory.wo(StatisticQywxUnitForMonth.class, Wo.class,
+                null, JpaObject.FieldsInvisible);
+
+    }
+}

+ 6 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/qywxstatistic/BaseAction.java

@@ -0,0 +1,6 @@
+package com.x.attendance.assemble.control.jaxrs.qywxstatistic;
+
+import com.x.base.core.project.jaxrs.StandardJaxrsAction;
+
+public class BaseAction extends StandardJaxrsAction {
+}

+ 94 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/qywxstatistic/QywxAttendanceStatisticAction.java

@@ -0,0 +1,94 @@
+package com.x.attendance.assemble.control.jaxrs.qywxstatistic;
+
+import com.x.base.core.project.annotation.JaxrsDescribe;
+import com.x.base.core.project.annotation.JaxrsMethodDescribe;
+import com.x.base.core.project.annotation.JaxrsParameterDescribe;
+import com.x.base.core.project.http.ActionResult;
+import com.x.base.core.project.http.EffectivePerson;
+import com.x.base.core.project.http.HttpMediaType;
+import com.x.base.core.project.jaxrs.ResponseFactory;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.*;
+import javax.ws.rs.container.AsyncResponse;
+import javax.ws.rs.container.Suspended;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import java.util.List;
+
+/**
+ * Created by fancyLou on 2020-04-21.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+
+@Path("qywxstatistic")
+@JaxrsDescribe("企业微信打卡数据统计管理")
+public class QywxAttendanceStatisticAction extends BaseAction {
+
+    private static final Logger logger = LoggerFactory.getLogger(QywxAttendanceStatisticAction.class);
+
+
+
+    @JaxrsMethodDescribe(value = "人员月份统计查询", action = ActionPersonStatistic.class)
+    @GET
+    @Path("person/{person}/{year}/{month}")
+    @Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void personMonth(@Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request,
+                            @JaxrsParameterDescribe("人员") @PathParam("person") String person,
+                            @JaxrsParameterDescribe("年份: yyyy") @PathParam("year") String year,
+                            @JaxrsParameterDescribe("月份: MM") @PathParam("month") String month) {
+        ActionResult<List<ActionPersonStatistic.Wo>> result = new ActionResult<>();
+        EffectivePerson effectivePerson = this.effectivePerson(request);
+        try {
+            result = new ActionPersonStatistic().execute(person, year, month);
+        }catch (Exception e) {
+            logger.error(e, effectivePerson, request, null);
+            result.error(e);
+        }
+        asyncResponse.resume(ResponseFactory.getEntityTagActionResultResponse(request, result));
+    }
+
+    @JaxrsMethodDescribe(value = "根据部门查询人员月份统计", action = ActionPersonStatisticWithUnit.class)
+    @GET
+    @Path("person/unit/{unit}/{year}/{month}")
+    @Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void personMonthWithUnit(@Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request,
+                                    @JaxrsParameterDescribe("部门") @PathParam("unit") String unit,
+                                    @JaxrsParameterDescribe("年份: yyyy") @PathParam("year") String year,
+                                    @JaxrsParameterDescribe("月份: MM") @PathParam("month") String month) {
+        ActionResult<List<ActionPersonStatisticWithUnit.Wo>> result = new ActionResult<>();
+        EffectivePerson effectivePerson = this.effectivePerson(request);
+        try {
+            result = new ActionPersonStatisticWithUnit().execute(unit, year, month);
+        }catch (Exception e) {
+            logger.error(e, effectivePerson, request, null);
+            result.error(e);
+        }
+        asyncResponse.resume(ResponseFactory.getEntityTagActionResultResponse(request, result));
+    }
+
+    @JaxrsMethodDescribe(value = "部门月份统计查询", action = ActionUnitStatistic.class)
+    @GET
+    @Path("unit/{unit}/{year}/{month}")
+    @Produces(HttpMediaType.APPLICATION_JSON_UTF_8)
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void unitMonth(@Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request,
+                          @JaxrsParameterDescribe("部门") @PathParam("unit") String unit,
+                          @JaxrsParameterDescribe("年份: yyyy") @PathParam("year") String year,
+                          @JaxrsParameterDescribe("月份: MM") @PathParam("month") String month) {
+        ActionResult<ActionUnitStatistic.Wo> result = new ActionResult<>();
+        EffectivePerson effectivePerson = this.effectivePerson(request);
+        try {
+            result = new ActionUnitStatistic().execute(unit, year, month);
+        }catch (Exception e) {
+            logger.error(e, effectivePerson, request, null);
+            result.error(e);
+        }
+        asyncResponse.resume(ResponseFactory.getEntityTagActionResultResponse(request, result));
+    }
+
+}

+ 48 - 3
o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/AttendanceQywxDetail.java

@@ -7,6 +7,7 @@ import com.x.base.core.entity.annotation.ContainerEntity;
 import com.x.base.core.project.annotation.FieldDescribe;
 import com.x.base.core.project.annotation.FieldDescribe;
 
 
 import javax.persistence.*;
 import javax.persistence.*;
+import java.util.Date;
 
 
 @ContainerEntity
 @ContainerEntity
 @Entity
 @Entity
@@ -44,6 +45,14 @@ public class AttendanceQywxDetail extends SliceJpaObject  {
      */
      */
 
 
 
 
+    @FieldDescribe("O2用户")
+    @Column(name = ColumnNamePrefix + "o2User", length = length_128B)
+    private String o2User;
+
+    @FieldDescribe("O2用户所在的组织")
+    @Column(name = ColumnNamePrefix + "o2Unit", length = length_128B)
+    private String o2Unit;
+
     @FieldDescribe("用户id")
     @FieldDescribe("用户id")
     @Column(name = ColumnNamePrefix + "userid", length = length_96B)
     @Column(name = ColumnNamePrefix + "userid", length = length_96B)
     private String userid;
     private String userid;
@@ -52,6 +61,11 @@ public class AttendanceQywxDetail extends SliceJpaObject  {
     @Column(name = ColumnNamePrefix + "groupname", length = length_128B)
     @Column(name = ColumnNamePrefix + "groupname", length = length_128B)
     private String groupname;
     private String groupname;
 
 
+    //打卡类型
+    public static final String CHECKIN_TYPE_ON = "上班打卡";
+    public static final String CHECKIN_TYPE_OFF = "下班打卡";
+    public static final String CHECKIN_TYPE_OUTSIDE = "外出打卡";
+
     @FieldDescribe("打卡类型。字符串,目前有:上班打卡,下班打卡,外出打卡")
     @FieldDescribe("打卡类型。字符串,目前有:上班打卡,下班打卡,外出打卡")
     @Column(name = ColumnNamePrefix + "checkin_type", length = length_128B)
     @Column(name = ColumnNamePrefix + "checkin_type", length = length_128B)
     private String checkin_type;
     private String checkin_type;
@@ -60,6 +74,17 @@ public class AttendanceQywxDetail extends SliceJpaObject  {
     @Column(name = ColumnNamePrefix + "checkin_time")
     @Column(name = ColumnNamePrefix + "checkin_time")
     private long checkin_time;
     private long checkin_time;
 
 
+    @FieldDescribe("实际打卡时间,  用Date格式存储")
+    @Column(name = ColumnNamePrefix + "checkin_time_date")
+    private Date checkin_time_date;
+
+    //异常类型
+    public static final String EXCEPTION_TYPE_TIME = "时间异常";
+    public static final String EXCEPTION_TYPE_ADDRESS = "地点异常";
+    public static final String EXCEPTION_TYPE_WIFI = "wifi异常";
+    public static final String EXCEPTION_TYPE_UNKOWN_DEVICE = "非常用设备";
+    public static final String EXCEPTION_TYPE_NOSIGN = "未打卡";
+    public static final String EXCEPTION_TYPE_NORMAL = "正常";
     @FieldDescribe("异常类型,字符串,包括:时间异常,地点异常,未打卡,wifi异常,非常用设备。如果有多个异常,以分号间隔")
     @FieldDescribe("异常类型,字符串,包括:时间异常,地点异常,未打卡,wifi异常,非常用设备。如果有多个异常,以分号间隔")
     @Column(name = ColumnNamePrefix + "exception_type", length = length_255B)
     @Column(name = ColumnNamePrefix + "exception_type", length = length_255B)
     private String exception_type;
     private String exception_type;
@@ -80,10 +105,30 @@ public class AttendanceQywxDetail extends SliceJpaObject  {
     @Column(name = ColumnNamePrefix + "notes", length = length_255B)
     @Column(name = ColumnNamePrefix + "notes", length = length_255B)
     private String notes;
     private String notes;
 
 
-//    @FieldDescribe("用户id")
-//    @Column(name = ColumnNamePrefix + "userid", length = length_96B)
-//    private String wifimac;
 
 
+    public String getO2User() {
+        return o2User;
+    }
+
+    public void setO2User(String o2User) {
+        this.o2User = o2User;
+    }
+
+    public String getO2Unit() {
+        return o2Unit;
+    }
+
+    public void setO2Unit(String o2Unit) {
+        this.o2Unit = o2Unit;
+    }
+
+    public Date getCheckin_time_date() {
+        return checkin_time_date;
+    }
+
+    public void setCheckin_time_date(Date checkin_time_date) {
+        this.checkin_time_date = checkin_time_date;
+    }
 
 
     public String getUserid() {
     public String getUserid() {
         return userid;
         return userid;

+ 9 - 0
o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/PersistenceProperties.java

@@ -81,12 +81,21 @@ public final class PersistenceProperties extends AbstractPersistenceProperties {
 	public static class StatisticDingdingPersonForMonth {
 	public static class StatisticDingdingPersonForMonth {
 		public static final String table = "ATDC_STATISTIC_DD_PERSON_FORMONTH";
 		public static final String table = "ATDC_STATISTIC_DD_PERSON_FORMONTH";
 	}
 	}
+	public static class StatisticQywxPersonForMonth {
+		public static final String table = "ATDC_STATISTIC_QYWX_PERSON_FORMONTH";
+	}
 	public static class StatisticDingdingUnitForMonth {
 	public static class StatisticDingdingUnitForMonth {
 		public static final String table = "ATDC_STATISTIC_DD_UNIT_FORMONTH";
 		public static final String table = "ATDC_STATISTIC_DD_UNIT_FORMONTH";
 	}
 	}
+	public static class StatisticQywxUnitForMonth {
+		public static final String table = "ATDC_STATISTIC_QYWX_UNIT_FORMONTH";
+	}
 	public static class StatisticDingdingUnitForDay {
 	public static class StatisticDingdingUnitForDay {
 		public static final String table = "ATDC_STATISTIC_DD_UNIT_FORDAY";
 		public static final String table = "ATDC_STATISTIC_DD_UNIT_FORDAY";
 	}
 	}
+	public static class StatisticQywxUnitForDay {
+		public static final String table = "ATDC_STATISTIC_QYWX_UNIT_FORDAY";
+	}
 	
 	
 	public static class AttendanceWorkPlace {
 	public static class AttendanceWorkPlace {
 		public static final String table = "ATDC_WORK_PLACE";
 		public static final String table = "ATDC_WORK_PLACE";

+ 213 - 0
o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/StatisticQywxPersonForMonth.java

@@ -0,0 +1,213 @@
+package com.x.attendance.entity;
+
+import com.x.base.core.entity.JpaObject;
+import com.x.base.core.entity.SliceJpaObject;
+import com.x.base.core.entity.annotation.CheckPersist;
+import com.x.base.core.entity.annotation.ContainerEntity;
+import com.x.base.core.project.annotation.FieldDescribe;
+
+import javax.persistence.*;
+
+@ContainerEntity
+@Entity
+@Table(name = PersistenceProperties.StatisticQywxPersonForMonth.table, uniqueConstraints = {
+		@UniqueConstraint(name = PersistenceProperties.StatisticQywxPersonForMonth.table + JpaObject.IndexNameMiddle
+				+ JpaObject.DefaultUniqueConstraintSuffix, columnNames = { JpaObject.IDCOLUMN,
+						JpaObject.CREATETIMECOLUMN, JpaObject.UPDATETIMECOLUMN, JpaObject.SEQUENCECOLUMN }) })
+@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
+public class StatisticQywxPersonForMonth extends SliceJpaObject {
+
+
+
+	private static final String TABLE = PersistenceProperties.StatisticQywxPersonForMonth.table;
+	private static final long serialVersionUID = 2006440116216111693L;
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	@FieldDescribe("数据库主键,自动生成.")
+	@Id
+	@Column(length = length_id, name = ColumnNamePrefix + id_FIELDNAME)
+	private String id = createId();
+
+	public void onPersist() throws Exception {
+	}
+	/*
+	 * =============================================================================
+	 * ===== 以上为 JpaObject 默认字段
+	 * =============================================================================
+	 * =====
+	 */
+
+	/*
+	 * =============================================================================
+	 * ===== 以下为具体不同的业务及数据表字段要求
+	 * =============================================================================
+	 * =====
+	 */
+	@FieldDescribe("O2用户")
+	@Column(name = ColumnNamePrefix + "o2User", length = length_128B)
+	@CheckPersist(allowEmpty = false)
+	private String o2User;
+
+	@FieldDescribe("O2用户所在的组织")
+	@Column(name = ColumnNamePrefix + "o2Unit", length = length_128B)
+	@CheckPersist(allowEmpty = false)
+	private String o2Unit;
+
+
+	@FieldDescribe("统计年份")
+	@Column(name = "xstatisticYear", length = JpaObject.length_16B)
+	@CheckPersist(allowEmpty = false)
+	private String statisticYear;
+
+	@FieldDescribe("统计月份")
+	@Column(name = "xstatisticMonth", length = JpaObject.length_16B)
+	@CheckPersist(allowEmpty = false)
+	private String statisticMonth;
+
+	@FieldDescribe("出勤天数")
+	@Column(name = "xworkDayCount")
+	private Long workDayCount;
+
+	@FieldDescribe("上班签到次数")
+	@Column(name = "xonDutyTimes")
+	private Long onDutyTimes;
+
+	@FieldDescribe("下班签到次数")
+	@Column(name = "xoffDutyTimes")
+	private Long offDutyTimes;
+
+	@FieldDescribe("外出签到次数")
+	@Column(name = "xoutsideDutyTimes")
+	private Long outsideDutyTimes;
+
+	@FieldDescribe("正常签到次数")
+	@Column(name = "xresultNormal")
+	private Long resultNormal;
+
+	@FieldDescribe("迟到次数")
+	@Column(name = "xlateTimes")
+	private Long lateTimes;
+
+	@FieldDescribe("早退次数")
+	@Column(name = "xleaveEarlyTimes")
+	private Long leaveEarlyTimes;
+
+	@FieldDescribe("旷工次数")
+	@Column(name = "xAbsenteeismTimes")
+	private Long absenteeismTimes;
+
+	@FieldDescribe("未打卡次数")
+	@Column(name = "xNotSignedCount")
+	private Long notSignedCount;
+
+	public Long getOutsideDutyTimes() {
+		return outsideDutyTimes;
+	}
+
+	public void setOutsideDutyTimes(Long outsideDutyTimes) {
+		this.outsideDutyTimes = outsideDutyTimes;
+	}
+
+	public Long getResultNormal() {
+		return resultNormal;
+	}
+
+	public void setResultNormal(Long resultNormal) {
+		this.resultNormal = resultNormal;
+	}
+
+	public String getO2User() {
+		return o2User;
+	}
+
+	public void setO2User(String o2User) {
+		this.o2User = o2User;
+	}
+
+	public String getO2Unit() {
+		return o2Unit;
+	}
+
+	public void setO2Unit(String o2Unit) {
+		this.o2Unit = o2Unit;
+	}
+
+	public String getStatisticYear() {
+		return statisticYear;
+	}
+
+	public void setStatisticYear(String statisticYear) {
+		this.statisticYear = statisticYear;
+	}
+
+	public String getStatisticMonth() {
+		return statisticMonth;
+	}
+
+	public void setStatisticMonth(String statisticMonth) {
+		this.statisticMonth = statisticMonth;
+	}
+
+	public Long getWorkDayCount() {
+		return workDayCount;
+	}
+
+	public void setWorkDayCount(Long workDayCount) {
+		this.workDayCount = workDayCount;
+	}
+
+	public Long getOnDutyTimes() {
+		return onDutyTimes;
+	}
+
+	public void setOnDutyTimes(Long onDutyTimes) {
+		this.onDutyTimes = onDutyTimes;
+	}
+
+	public Long getOffDutyTimes() {
+		return offDutyTimes;
+	}
+
+	public void setOffDutyTimes(Long offDutyTimes) {
+		this.offDutyTimes = offDutyTimes;
+	}
+
+	public Long getLateTimes() {
+		return lateTimes;
+	}
+
+	public void setLateTimes(Long lateTimes) {
+		this.lateTimes = lateTimes;
+	}
+	
+	public Long getLeaveEarlyTimes() {
+		return leaveEarlyTimes;
+	}
+
+	public void setLeaveEarlyTimes(Long leaveEarlyTimes) {
+		this.leaveEarlyTimes = leaveEarlyTimes;
+	}
+
+	public Long getAbsenteeismTimes() {
+		return absenteeismTimes;
+	}
+
+	public void setAbsenteeismTimes(Long absenteeismTimes) {
+		this.absenteeismTimes = absenteeismTimes;
+	}
+
+	public Long getNotSignedCount() {
+		return notSignedCount;
+	}
+
+	public void setNotSignedCount(Long notSignedCount) {
+		this.notSignedCount = notSignedCount;
+	}
+}

+ 214 - 0
o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/StatisticQywxUnitForDay.java

@@ -0,0 +1,214 @@
+package com.x.attendance.entity;
+
+import com.x.base.core.entity.JpaObject;
+import com.x.base.core.entity.SliceJpaObject;
+import com.x.base.core.entity.annotation.CheckPersist;
+import com.x.base.core.entity.annotation.ContainerEntity;
+import com.x.base.core.project.annotation.FieldDescribe;
+
+import javax.persistence.*;
+
+@ContainerEntity
+@Entity
+@Table(name = PersistenceProperties.StatisticQywxUnitForDay.table, uniqueConstraints = {
+		@UniqueConstraint(name = PersistenceProperties.StatisticQywxUnitForDay.table + JpaObject.IndexNameMiddle
+				+ JpaObject.DefaultUniqueConstraintSuffix, columnNames = { JpaObject.IDCOLUMN,
+						JpaObject.CREATETIMECOLUMN, JpaObject.UPDATETIMECOLUMN, JpaObject.SEQUENCECOLUMN }) })
+@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
+public class StatisticQywxUnitForDay extends SliceJpaObject {
+
+
+
+	private static final String TABLE = PersistenceProperties.StatisticQywxUnitForDay.table;
+	private static final long serialVersionUID = 2090817422412907325L;
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	@FieldDescribe("数据库主键,自动生成.")
+	@Id
+	@Column(length = length_id, name = ColumnNamePrefix + id_FIELDNAME)
+	private String id = createId();
+
+	public void onPersist() throws Exception {
+	}
+	/*
+	 * =============================================================================
+	 * ===== 以上为 JpaObject 默认字段
+	 * =============================================================================
+	 * =====
+	 */
+
+	/*
+	 * =============================================================================
+	 * ===== 以下为具体不同的业务及数据表字段要求
+	 * =============================================================================
+	 * =====
+	 */
+
+	@FieldDescribe("O2用户所在的组织")
+	@Column(name = ColumnNamePrefix + "o2Unit", length = length_128B)
+	@CheckPersist(allowEmpty = false)
+	private String o2Unit;
+
+	@FieldDescribe("统计年份")
+	@Column(name = "xstatisticYear", length = JpaObject.length_16B)
+	@CheckPersist(allowEmpty = false)
+	private String statisticYear;
+
+	@FieldDescribe("统计月份")
+	@Column(name = "xstatisticMonth", length = JpaObject.length_16B)
+	@CheckPersist(allowEmpty = false)
+	private String statisticMonth;
+
+	@FieldDescribe("统计日期")
+	@Column(name = "xstatisticDate", length = JpaObject.length_16B)
+	@CheckPersist(allowEmpty = false)
+	private String statisticDate;
+
+	@FieldDescribe("出勤天数")
+	@Column(name = "xworkDayCount")
+	private Long workDayCount;
+
+	@FieldDescribe("上班签到次数")
+	@Column(name = "xonDutyTimes")
+	private Long onDutyTimes;
+
+	@FieldDescribe("下班签到次数")
+	@Column(name = "xoffDutyTimes")
+	private Long offDutyTimes;
+
+	@FieldDescribe("外出签到次数")
+	@Column(name = "xoutsideDutyTimes")
+	private Long outsideDutyTimes;
+
+	@FieldDescribe("正常签到次数")
+	@Column(name = "xresultNormal")
+	private Long resultNormal;
+
+	@FieldDescribe("迟到次数")
+	@Column(name = "xlateTimes")
+	private Long lateTimes;
+
+	@FieldDescribe("早退次数")
+	@Column(name = "xleaveEarlyTimes")
+	private Long leaveEarlyTimes;
+
+	@FieldDescribe("旷工次数")
+	@Column(name = "xAbsenteeismTimes")
+	private Long absenteeismTimes;
+
+	@FieldDescribe("未打卡次数")
+	@Column(name = "xNotSignedCount")
+	private Long notSignedCount;
+
+
+	public Long getResultNormal() {
+		return resultNormal;
+	}
+
+	public void setResultNormal(Long resultNormal) {
+		this.resultNormal = resultNormal;
+	}
+
+	public String getStatisticDate() {
+		return statisticDate;
+	}
+
+	public void setStatisticDate(String statisticDate) {
+		this.statisticDate = statisticDate;
+	}
+
+	public String getO2Unit() {
+		return o2Unit;
+	}
+
+	public void setO2Unit(String o2Unit) {
+		this.o2Unit = o2Unit;
+	}
+
+	public String getStatisticYear() {
+		return statisticYear;
+	}
+
+	public void setStatisticYear(String statisticYear) {
+		this.statisticYear = statisticYear;
+	}
+
+	public String getStatisticMonth() {
+		return statisticMonth;
+	}
+
+	public void setStatisticMonth(String statisticMonth) {
+		this.statisticMonth = statisticMonth;
+	}
+
+	public Long getWorkDayCount() {
+		return workDayCount;
+	}
+
+	public void setWorkDayCount(Long workDayCount) {
+		this.workDayCount = workDayCount;
+	}
+
+	public Long getOnDutyTimes() {
+		return onDutyTimes;
+	}
+
+	public void setOnDutyTimes(Long onDutyTimes) {
+		this.onDutyTimes = onDutyTimes;
+	}
+
+	public Long getOffDutyTimes() {
+		return offDutyTimes;
+	}
+
+	public void setOffDutyTimes(Long offDutyTimes) {
+		this.offDutyTimes = offDutyTimes;
+	}
+
+	public Long getLateTimes() {
+		return lateTimes;
+	}
+
+	public void setLateTimes(Long lateTimes) {
+		this.lateTimes = lateTimes;
+	}
+
+	public Long getOutsideDutyTimes() {
+		return outsideDutyTimes;
+	}
+
+	public void setOutsideDutyTimes(Long outsideDutyTimes) {
+		this.outsideDutyTimes = outsideDutyTimes;
+	}
+
+	public Long getLeaveEarlyTimes() {
+		return leaveEarlyTimes;
+	}
+
+	public void setLeaveEarlyTimes(Long leaveEarlyTimes) {
+		this.leaveEarlyTimes = leaveEarlyTimes;
+	}
+
+	public Long getAbsenteeismTimes() {
+		return absenteeismTimes;
+	}
+
+	public void setAbsenteeismTimes(Long absenteeismTimes) {
+		this.absenteeismTimes = absenteeismTimes;
+	}
+
+	public Long getNotSignedCount() {
+		return notSignedCount;
+	}
+
+	public void setNotSignedCount(Long notSignedCount) {
+		this.notSignedCount = notSignedCount;
+	}
+}

+ 201 - 0
o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/StatisticQywxUnitForMonth.java

@@ -0,0 +1,201 @@
+package com.x.attendance.entity;
+
+import com.x.base.core.entity.JpaObject;
+import com.x.base.core.entity.SliceJpaObject;
+import com.x.base.core.entity.annotation.CheckPersist;
+import com.x.base.core.entity.annotation.ContainerEntity;
+import com.x.base.core.project.annotation.FieldDescribe;
+
+import javax.persistence.*;
+
+@ContainerEntity
+@Entity
+@Table(name = PersistenceProperties.StatisticQywxUnitForMonth.table, uniqueConstraints = {
+		@UniqueConstraint(name = PersistenceProperties.StatisticQywxUnitForMonth.table + JpaObject.IndexNameMiddle
+				+ JpaObject.DefaultUniqueConstraintSuffix, columnNames = { JpaObject.IDCOLUMN,
+						JpaObject.CREATETIMECOLUMN, JpaObject.UPDATETIMECOLUMN, JpaObject.SEQUENCECOLUMN }) })
+@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
+public class StatisticQywxUnitForMonth extends SliceJpaObject {
+
+
+
+	private static final String TABLE = PersistenceProperties.StatisticQywxUnitForMonth.table;
+	private static final long serialVersionUID = 2831416127767736230L;
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	@FieldDescribe("数据库主键,自动生成.")
+	@Id
+	@Column(length = length_id, name = ColumnNamePrefix + id_FIELDNAME)
+	private String id = createId();
+
+	public void onPersist() throws Exception {
+	}
+	/*
+	 * =============================================================================
+	 * ===== 以上为 JpaObject 默认字段
+	 * =============================================================================
+	 * =====
+	 */
+
+	/*
+	 * =============================================================================
+	 * ===== 以下为具体不同的业务及数据表字段要求
+	 * =============================================================================
+	 * =====
+	 */
+
+	@FieldDescribe("O2用户所在的组织")
+	@Column(name = ColumnNamePrefix + "o2Unit", length = length_128B)
+	@CheckPersist(allowEmpty = false)
+	private String o2Unit;
+
+
+	@FieldDescribe("统计年份")
+	@Column(name = "xstatisticYear", length = JpaObject.length_16B)
+	@CheckPersist(allowEmpty = false)
+	private String statisticYear;
+
+	@FieldDescribe("统计月份")
+	@Column(name = "xstatisticMonth", length = JpaObject.length_16B)
+	@CheckPersist(allowEmpty = false)
+	private String statisticMonth;
+
+	@FieldDescribe("出勤天数")
+	@Column(name = "xworkDayCount")
+	private Long workDayCount;
+
+	@FieldDescribe("上班签到次数")
+	@Column(name = "xonDutyTimes")
+	private Long onDutyTimes;
+
+	@FieldDescribe("下班签到次数")
+	@Column(name = "xoffDutyTimes")
+	private Long offDutyTimes;
+
+	@FieldDescribe("外出签到次数")
+	@Column(name = "xoutsideDutyTimes")
+	private Long outsideDutyTimes;
+
+	@FieldDescribe("正常签到次数")
+	@Column(name = "xresultNormal")
+	private Long resultNormal;
+
+	@FieldDescribe("迟到次数")
+	@Column(name = "xlateTimes")
+	private Long lateTimes;
+
+	@FieldDescribe("早退次数")
+	@Column(name = "xleaveEarlyTimes")
+	private Long leaveEarlyTimes;
+
+	@FieldDescribe("旷工次数")
+	@Column(name = "xAbsenteeismTimes")
+	private Long absenteeismTimes;
+
+	@FieldDescribe("未打卡次数")
+	@Column(name = "xNotSignedCount")
+	private Long notSignedCount;
+
+	public Long getResultNormal() {
+		return resultNormal;
+	}
+
+	public void setResultNormal(Long resultNormal) {
+		this.resultNormal = resultNormal;
+	}
+
+	public String getO2Unit() {
+		return o2Unit;
+	}
+
+	public void setO2Unit(String o2Unit) {
+		this.o2Unit = o2Unit;
+	}
+
+	public String getStatisticYear() {
+		return statisticYear;
+	}
+
+	public void setStatisticYear(String statisticYear) {
+		this.statisticYear = statisticYear;
+	}
+
+	public String getStatisticMonth() {
+		return statisticMonth;
+	}
+
+	public void setStatisticMonth(String statisticMonth) {
+		this.statisticMonth = statisticMonth;
+	}
+
+	public Long getWorkDayCount() {
+		return workDayCount;
+	}
+
+	public void setWorkDayCount(Long workDayCount) {
+		this.workDayCount = workDayCount;
+	}
+
+	public Long getOnDutyTimes() {
+		return onDutyTimes;
+	}
+
+	public void setOnDutyTimes(Long onDutyTimes) {
+		this.onDutyTimes = onDutyTimes;
+	}
+
+	public Long getOffDutyTimes() {
+		return offDutyTimes;
+	}
+
+	public void setOffDutyTimes(Long offDutyTimes) {
+		this.offDutyTimes = offDutyTimes;
+	}
+
+	public Long getLateTimes() {
+		return lateTimes;
+	}
+
+	public void setLateTimes(Long lateTimes) {
+		this.lateTimes = lateTimes;
+	}
+
+	public Long getOutsideDutyTimes() {
+		return outsideDutyTimes;
+	}
+
+	public void setOutsideDutyTimes(Long outsideDutyTimes) {
+		this.outsideDutyTimes = outsideDutyTimes;
+	}
+
+	public Long getLeaveEarlyTimes() {
+		return leaveEarlyTimes;
+	}
+
+	public void setLeaveEarlyTimes(Long leaveEarlyTimes) {
+		this.leaveEarlyTimes = leaveEarlyTimes;
+	}
+
+	public Long getAbsenteeismTimes() {
+		return absenteeismTimes;
+	}
+
+	public void setAbsenteeismTimes(Long absenteeismTimes) {
+		this.absenteeismTimes = absenteeismTimes;
+	}
+
+	public Long getNotSignedCount() {
+		return notSignedCount;
+	}
+
+	public void setNotSignedCount(Long notSignedCount) {
+		this.notSignedCount = notSignedCount;
+	}
+}

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

@@ -625,6 +625,26 @@ public class DateTools {
 		return date;
 		return date;
 	}
 	}
 
 
+
+	/**
+	 * 时间戳转Unix时间戳
+	 * @param timestamp
+	 * @return
+	 */
+	public static long toUnixTimeStamp(long timestamp){
+		return timestamp/1000;
+	}
+
+	/**
+	 * Unix时间戳转时间戳
+	 * @param unixTimeStamp
+	 * @return
+	 */
+	public static long toTimestamp(long unixTimeStamp){
+		return unixTimeStamp*1000;
+	}
+
+
 	public static void main(String[] args) {
 	public static void main(String[] args) {
 		try {
 		try {
 			Date today = new Date();
 			Date today = new Date();

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

@@ -16,7 +16,9 @@ import com.x.base.core.project.annotation.ModuleType;
 		"com.x.attendance.entity.StatisticUnitForDay", "com.x.attendance.entity.StatisticUnitForMonth",
 		"com.x.attendance.entity.StatisticUnitForDay", "com.x.attendance.entity.StatisticUnitForMonth",
 		"com.x.attendance.entity.AttendanceDingtalkDetail", "com.x.attendance.entity.AttendanceQywxDetail",
 		"com.x.attendance.entity.AttendanceDingtalkDetail", "com.x.attendance.entity.AttendanceQywxDetail",
 		"com.x.attendance.entity.DingdingQywxSyncRecord", "com.x.attendance.entity.StatisticDingdingPersonForMonth",
 		"com.x.attendance.entity.DingdingQywxSyncRecord", "com.x.attendance.entity.StatisticDingdingPersonForMonth",
-		"com.x.attendance.entity.StatisticDingdingUnitForDay", "com.x.attendance.entity.StatisticDingdingUnitForMonth"}, storeJars = {
+		"com.x.attendance.entity.StatisticDingdingUnitForDay", "com.x.attendance.entity.StatisticDingdingUnitForMonth",
+		"com.x.attendance.entity.StatisticQywxPersonForMonth", "com.x.attendance.entity.StatisticQywxUnitForDay",
+		"com.x.attendance.entity.StatisticQywxUnitForMonth"}, storeJars = {
 				"x_attendance_core_entity", "x_organization_core_express", "x_organization_core_entity" })
 				"x_attendance_core_entity", "x_organization_core_express", "x_organization_core_entity" })
 public class x_attendance_assemble_control extends Deployable {
 public class x_attendance_assemble_control extends Deployable {
 }
 }

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

@@ -12,6 +12,7 @@ public class ThisApplication {
 	protected static Context context;
 	protected static Context context;
 	
 	
 	public static final String ROLE_CMSManager = "CMSManager@CMSManagerSystemRole@R";
 	public static final String ROLE_CMSManager = "CMSManager@CMSManagerSystemRole@R";
+	public static final String ROLE_Manager = "Manager@ManagerSystemRole@R";
 	public static QueueDataRowImport queueDataRowImport;
 	public static QueueDataRowImport queueDataRowImport;
 	public static QueueDocumentDelete queueDocumentDelete;
 	public static QueueDocumentDelete queueDocumentDelete;
 	public static QueueDocumentUpdate queueDocumentUpdate;
 	public static QueueDocumentUpdate queueDocumentUpdate;

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

@@ -53,8 +53,11 @@ public class ActionApplication extends AbstractActionApplication {
 		this.classes.add(DataAction.class);
 		this.classes.add(DataAction.class);
 		this.classes.add(DocumentAction.class);
 		this.classes.add(DocumentAction.class);
 		this.classes.add(DocumentCipherAction.class);
 		this.classes.add(DocumentCipherAction.class);
+
+		this.classes.add(PermissionAction.class);
 		this.classes.add(PermissionForDocumentAction.class);
 		this.classes.add(PermissionForDocumentAction.class);
 		this.classes.add(PermissionManagerAction.class);
 		this.classes.add(PermissionManagerAction.class);
+
 		this.classes.add(DocumentViewRecordAction.class);
 		this.classes.add(DocumentViewRecordAction.class);
 		this.classes.add(FileInfoAction.class);
 		this.classes.add(FileInfoAction.class);
 		this.classes.add(FileAction.class);
 		this.classes.add(FileAction.class);
@@ -73,7 +76,6 @@ public class ActionApplication extends AbstractActionApplication {
 		this.classes.add(SearchFilterAction.class);
 		this.classes.add(SearchFilterAction.class);
 		this.classes.add(InputAction.class);
 		this.classes.add(InputAction.class);
 		this.classes.add(OutputAction.class);
 		this.classes.add(OutputAction.class);
-		this.classes.add(PermissionAction.class);
 		
 		
 		this.classes.add(AppInfoAnonymousAction.class);
 		this.classes.add(AppInfoAnonymousAction.class);
 		this.classes.add(AppDictAnonymousAction.class);
 		this.classes.add(AppDictAnonymousAction.class);

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

@@ -12,7 +12,7 @@ import javax.servlet.annotation.WebFilter;
  */
  */
 @WebFilter(urlPatterns = { 
 @WebFilter(urlPatterns = { 
 		"/jaxrs/document/cipher/*",
 		"/jaxrs/document/cipher/*",
-		"/jaxrs/permission/manager*"
+		"/jaxrs/permission/management/*"
 }, asyncSupported = true )
 }, asyncSupported = true )
 public class CmsJaxrsCipherFilter extends CipherManagerJaxrsFilter {
 public class CmsJaxrsCipherFilter extends CipherManagerJaxrsFilter {
 
 

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

@@ -31,13 +31,13 @@ import javax.servlet.annotation.WebFilter;
         "/jaxrs/surface/appdict/*",
         "/jaxrs/surface/appdict/*",
         "/jaxrs/script/*",
         "/jaxrs/script/*",
         "/jaxrs/uuid/*",
         "/jaxrs/uuid/*",
-        "/jaxrs/docpermission/*",
         "/jaxrs/viewrecord/*",
         "/jaxrs/viewrecord/*",
         "/jaxrs/searchfilter/*",
         "/jaxrs/searchfilter/*",
         "/jaxrs/templateform/*",
         "/jaxrs/templateform/*",
         "/jaxrs/input/*",
         "/jaxrs/input/*",
         "/jaxrs/output/*",
         "/jaxrs/output/*",
         "/jaxrs/permission/*",
         "/jaxrs/permission/*",
+        "/jaxrs/docpermission/*",
         "/jaxrs/comment/*",
         "/jaxrs/comment/*",
         "/servlet/*"
         "/servlet/*"
 }, asyncSupported = true)
 }, asyncSupported = true)

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

@@ -51,7 +51,16 @@ public class ActionSave extends BaseAction {
 			result.error(exception);
 			result.error(exception);
 			logger.error(e, effectivePerson, request, null);
 			logger.error(e, effectivePerson, request, null);
 		}
 		}
-		
+
+		//判断用户是否有权限来进行分类的管理
+		if (check) {
+			if( !userManagerService.hasCategoryManagerPermission( effectivePerson, wi.getAppId() ) ){
+				check = false;
+				Exception exception = new ExceptionCategoryInfoProcess("用户操作权限不足,无法在此栏目中管理分类信息。" );
+				result.error(exception);
+			}
+		}
+
 		if (check) {
 		if (check) {
 			if ( StringUtils.isEmpty( identityName ) && !"xadmin".equalsIgnoreCase(effectivePerson.getDistinguishedName())) {
 			if ( StringUtils.isEmpty( identityName ) && !"xadmin".equalsIgnoreCase(effectivePerson.getDistinguishedName())) {
 				try {
 				try {

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

@@ -9,4 +9,8 @@ class ExceptionCategoryInfoProcess extends PromptException {
 	ExceptionCategoryInfoProcess( Throwable e, String message ) {
 	ExceptionCategoryInfoProcess( Throwable e, String message ) {
 		super( message, e );
 		super( message, e );
 	}
 	}
+
+	ExceptionCategoryInfoProcess( String message ) {
+		super( message );
+	}
 }
 }

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

@@ -19,7 +19,7 @@ import javax.ws.rs.container.Suspended;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MediaType;
 
 
-@Path("permission/manager")
+@Path("permission/management")
 @JaxrsDescribe("文档权限操作服务(管理员)")
 @JaxrsDescribe("文档权限操作服务(管理员)")
 public class PermissionManagerAction extends StandardJaxrsAction {
 public class PermissionManagerAction extends StandardJaxrsAction {
 
 

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

@@ -3,6 +3,7 @@ package com.x.cms.assemble.control.service;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
 
 
+import com.x.cms.core.entity.AppInfo;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.StringUtils;
 
 
 import com.x.base.core.container.EntityManagerContainer;
 import com.x.base.core.container.EntityManagerContainer;
@@ -530,4 +531,70 @@ public class UserManagerService {
 		}
 		}
 		return false;
 		return false;
 	}
 	}
+
+	public boolean hasCategoryManagerPermission( EffectivePerson person, String appId) throws Exception {
+		//xadmin或者Cipher
+		if( person.isManager() || person.isCipher() ){
+			return true;
+		}
+		if( StringUtils.equalsIgnoreCase("xadmin", person.getName() ) || StringUtils.equalsIgnoreCase("xadmin", person.getDistinguishedName() ) ){
+			return true;
+		}
+		UserManagerService userManagerService = new UserManagerService();
+		//Manager管理员
+		if( userManagerService.isHasPlatformRole( person.getDistinguishedName(), ThisApplication.ROLE_Manager )){
+			return true;
+		}
+		//CMS管理员
+		if( userManagerService.isHasPlatformRole( person.getDistinguishedName(), ThisApplication.ROLE_CMSManager )){
+			return true;
+		}
+
+		//查询用户是否为该栏目的管理者
+		try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+			AppInfo appInfo = emc.find( appId, AppInfo.class );
+			//是管理员
+			if( ListTools.isNotEmpty(appInfo.getManageablePersonList()) && ListTools.contains( appInfo.getManageablePersonList(), person.getDistinguishedName() )){
+				return true;
+			}
+			if( ListTools.isNotEmpty( appInfo.getManageableUnitList() )){
+				List<String> unitNames = userManagerService.listUnitNamesWithPerson( person.getDistinguishedName() );
+				if( ListTools.isNotEmpty( unitNames )){
+					unitNames.retainAll( appInfo.getManageableUnitList() );
+					if( ListTools.isNotEmpty( unitNames )){
+						return true;
+					}
+				}
+			}
+			if( ListTools.isNotEmpty( appInfo.getManageableGroupList() )){
+				List<String> groupNames = userManagerService.listGroupNamesByPerson( person.getDistinguishedName() );
+				if( ListTools.isNotEmpty( groupNames )){
+					groupNames.retainAll( appInfo.getManageableGroupList() );
+					if( ListTools.isNotEmpty( groupNames )){
+						return true;
+					}
+				}
+			}
+		} catch (Exception e) {
+			throw e;
+		}
+
+		return false;
+	}
+
+	public boolean hasAppInfoManagerPermission( EffectivePerson person ) throws Exception {
+		//系统管理员
+		if( person.isManager() || person.isCipher() ){
+			return true;
+		}
+		if( StringUtils.equalsIgnoreCase("xadmin", person.getName() ) || StringUtils.equalsIgnoreCase("xadmin", person.getDistinguishedName() ) ){
+			return true;
+		}
+		//CMS管理员
+		UserManagerService userManagerService = new UserManagerService();
+		if( userManagerService.isHasPlatformRole( person.getDistinguishedName(), ThisApplication.ROLE_CMSManager )){
+			return true;
+		}
+		return false;
+	}
 }
 }

+ 6 - 6
package.json

@@ -26,13 +26,13 @@
                 "deploy:rpi": "gulp deploy --e raspberrypi",
                 "deploy:rpi": "gulp deploy --e raspberrypi",
                 "typeInfor": "type welcome && echo Your server is build success: target/o2server/",
                 "typeInfor": "type welcome && echo Your server is build success: target/o2server/",
                 "clear": "gulp clear_build",
                 "clear": "gulp clear_build",
-                "build": "npm run clear  && npm run build_parallel && npm run deploy && npm run typeInfor",
-                "build:win": "npm run clear  && npm run build_parallel && npm run deploy:win && npm run typeInfor",
+                "build": "npm run clear && npm run build_parallel && npm run deploy && npm run typeInfor",
+                "build:win": "npm run clear && npm run build_parallel && npm run deploy:win && npm run typeInfor",
                 "build:linux": "npm run clear  && npm run build_parallel && npm run deploy:linux && npm run typeInfor",
                 "build:linux": "npm run clear  && npm run build_parallel && npm run deploy:linux && npm run typeInfor",
-                "build:aix": "npm run clear && npm run build_parallel && npm run deploy:aix && npm run typeInfor",
-                "build:arm": "npm run clear && npm run build_parallel && npm run deploy:arm && npm run typeInfor",
-                "build:macos": "npm run clear &&  npm run build_parallel && npm run deploy:macos && npm run typeInfor",
-                "build:risc": "npm run clear && npm run build_parallel && npm run deploy:risc && npm run typeInfor",
+                "build:aix": "npm run clear &&  npm run build_parallel && npm run deploy:aix && npm run typeInfor",
+                "build:arm": "npm run clear &&  npm run build_parallel && npm run deploy:arm && npm run typeInfor",
+                "build:macos": "npm run clear  && npm run build_parallel && npm run deploy:macos && npm run typeInfor",
+                "build:risc": "npm run clear &&  npm run build_parallel && npm run deploy:risc && npm run typeInfor",
                 "build:rpi": "npm run clear &&  npm run build_parallel && npm run deploy:rpi && npm run typeInfor",
                 "build:rpi": "npm run clear &&  npm run build_parallel && npm run deploy:rpi && npm run typeInfor",
                 "build_ci": "npm run clear && npm run preperation && npm run build_parallel && npm run deploy && npm run typeInfor",
                 "build_ci": "npm run clear && npm run preperation && npm run build_parallel && npm run deploy && npm run typeInfor",
                 "build_ci:win": "npm run clear && npm run preperation:win && npm run build_parallel && npm run deploy:win && npm run typeInfor",
                 "build_ci:win": "npm run clear && npm run preperation:win && npm run build_parallel && npm run deploy:win && npm run typeInfor",