dongzaixing 1 month ago
commit
62685e1008
100 changed files with 13616 additions and 0 deletions
  1. 186 0
      .gitignore
  2. 661 0
      LICENSE
  3. 195 0
      README.md
  4. 411 0
      build.xml
  5. 64 0
      changelog.md
  6. 1249 0
      gulpfile.js
  7. 14 0
      o2android/README.md
  8. 14 0
      o2ios/README.md
  9. 3 0
      o2server/.gitignore
  10. 1 0
      o2server/console_arm.sh
  11. 1 0
      o2server/console_linux.sh
  12. 1 0
      o2server/console_macosarm.sh
  13. 1 0
      o2server/console_macosx64.sh
  14. 1 0
      o2server/console_mips.sh
  15. 1 0
      o2server/console_raspi.sh
  16. 1 0
      o2server/console_sw.sh
  17. 1 0
      o2server/console_windows.bat
  18. 1 0
      o2server/index.html
  19. 1 0
      o2server/localSample/node.cfg
  20. 1326 0
      o2server/pom.xml
  21. 9 0
      o2server/restart_arm.sh
  22. 9 0
      o2server/restart_linux.sh
  23. 9 0
      o2server/restart_macosarm.sh
  24. 9 0
      o2server/restart_macosx64.sh
  25. 9 0
      o2server/restart_mips.sh
  26. 9 0
      o2server/restart_raspi.sh
  27. 9 0
      o2server/restart_sw.sh
  28. 36 0
      o2server/service_linux.sh
  29. 21 0
      o2server/service_windows.bat
  30. 44 0
      o2server/start_arm.sh
  31. 44 0
      o2server/start_arm_debug.sh
  32. 44 0
      o2server/start_linux.sh
  33. 44 0
      o2server/start_linux_debug.sh
  34. 49 0
      o2server/start_linux_min.sh
  35. 45 0
      o2server/start_macosarm.sh
  36. 45 0
      o2server/start_macosarm_debug.sh
  37. 45 0
      o2server/start_macosx64.sh
  38. 45 0
      o2server/start_macosx64_debug.sh
  39. 44 0
      o2server/start_mips.sh
  40. 44 0
      o2server/start_mips_debug.sh
  41. 44 0
      o2server/start_raspi.sh
  42. 44 0
      o2server/start_raspi_debug.sh
  43. 44 0
      o2server/start_sw.sh
  44. 44 0
      o2server/start_sw_debug.sh
  45. 40 0
      o2server/start_windows.bat
  46. 40 0
      o2server/start_windows_debug.bat
  47. 18 0
      o2server/stop_arm.sh
  48. 18 0
      o2server/stop_linux.sh
  49. 18 0
      o2server/stop_macosarm.sh
  50. 18 0
      o2server/stop_macosx64.sh
  51. 18 0
      o2server/stop_mips.sh
  52. 18 0
      o2server/stop_raspi.sh
  53. 18 0
      o2server/stop_sw.sh
  54. 2 0
      o2server/stop_windows.bat
  55. 24 0
      o2server/store/jars/manifest.cfg
  56. 28 0
      o2server/store/manifest.cfg
  57. 138 0
      o2server/x_attendance_assemble_control/pom.xml
  58. 807 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/common/date/DateOperation.java
  59. 275 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/common/excel/reader/Excel2003Reader.java
  60. 239 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/common/excel/reader/Excel2007Reader.java
  61. 58 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/common/excel/reader/ExcelReaderUtil.java
  62. 13 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/common/excel/reader/IRowReader.java
  63. 23 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/common/excel/reader/ImportExcelReader.java
  64. 225 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/common/excel/reader/UserModelEventListener.java
  65. 215 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/common/excel/writer/AbstractExcel2007Writer.java
  66. 60 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/common/excel/writer/Excel2003Writer.java
  67. 44 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/common/excel/writer/Excel2007WriterImpl.java
  68. 48 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/common/excel/writer/XMLEncoder.java
  69. 24 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/AbstractFactory.java
  70. 33 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/ApplicationServletContextListener.java
  71. 279 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/Business.java
  72. 16 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/CacheUtil.java
  73. 58 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/CriteriaQueryTools.java
  74. 12 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/ExceptionDingDingRequest.java
  75. 9 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/ExceptionDingdingFindNoArgumentError.java
  76. 16 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/ExceptionPersonHasNoIdentity.java
  77. 9 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/ExceptionQywxFindNoArgumentError.java
  78. 13 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/ExceptionWrapInConvert.java
  79. 7 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/MimeTypeDefinition.java
  80. 121 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QueueAttendanceDetailStatistic.java
  81. 460 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QueueDingdingAttendance.java
  82. 131 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QueueDingdingPersonStatistic.java
  83. 147 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QueueDingdingUnitStatistic.java
  84. 53 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QueuePersonAttendanceDetailAnalyse.java
  85. 397 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QueueQywxAttendanceSync.java
  86. 165 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QueueQywxPersonStatistic.java
  87. 183 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QueueQywxUnitStatistic.java
  88. 135 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/ThisApplication.java
  89. 53 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/AttendanceAdminFactory.java
  90. 703 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/AttendanceAppealInfoFactory.java
  91. 1313 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/AttendanceDetailFactory.java
  92. 183 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/AttendanceDetailMobileFactory.java
  93. 960 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/AttendanceDetailStatisticFactory.java
  94. 67 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/AttendanceEmployeeConfigFactory.java
  95. 53 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/AttendanceImportFileInfoFactory.java
  96. 97 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/AttendanceScheduleSettingFactory.java
  97. 310 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/AttendanceSelfHolidayFactory.java
  98. 85 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/AttendanceSettingFactory.java
  99. 124 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/AttendanceStatisticRequireLogFactory.java
  100. 73 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/AttendanceStatisticalCycleFactory.java

+ 186 - 0
.gitignore

@@ -0,0 +1,186 @@
+#o2oa
+**/command.swap
+**/version.o2
+**/bin/
+**/target/
+/target/**
+/o2server/webroot/
+/o2server/servers/
+/o2server/store/jars/*.jar
+/o2server/store/*.war
+/o2server/config/
+/o2server/custom/
+/o2server/dynamic/
+/o2server/commons/
+/o2server/jvm/
+/o2server/local/
+/o2server/configSample/
+!/o2server/configSample/externalDataSources*.json
+!/o2server/configSample/externalStorageSources.json
+!/o2server/configSample/manifest.cfg
+!/o2server/configSample/messageSendRule.js
+!/o2server/configSample/web.json
+/o2server/*/src/main/webapp/describe/
+/o2server/*/src/main/webapp/WEB-INF/
+# mnist图片资源
+!/o2server/x_program_center/src/main/webapp/WEB-INF/
+**/.settings/
+**/.classpath
+**/.project
+**/.VSCodeCounter
+/o2custom/
+/o2cloud/
+/.svn/
+*.hprof
+/o2web/gulpconfig.js
+/node_modules/
+/o2web/ftpconfig.js
+**/.DS_Store
+.DS_Store
+**/.idea
+/.idea/
+# Windows thumbnail cache files
+Thumbs.db
+Thumbs.db:encryptable
+ehthumbs.db
+ehthumbs_vista.db
+# Dump file
+*.stackdump
+# Folder config file
+[Dd]esktop.ini
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+# Windows shortcuts
+*.lnk
+# Compiled class file
+*.class
+# Log file
+*.log
+# BlueJ files
+*.ctxt
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+# Package Files #
+**/*.jar
+!/o2server/**/ext/*.jar
+**/*.war
+**/*.nar
+**/*.ear
+**/*.zip
+**/*.tar.gz
+**/*.rar
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+#jpa enhance class
+*_.java
+*.iml
+# for node
+package-lock.json
+# Logs
+logs
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# TypeScript v1 declaration files
+typings/
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+
+# next.js build output
+.next
+
+# nuxt.js build output
+.nuxt
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless/
+
+# FuseBox cache
+.fusebox/
+
+#DynamoDB Local files
+.dynamodb/
+
+#vscode
+**/.factorypath
+**/.vscode/
+
+**/pmd.xml
+**/cpd.xml
+**/pmd.html
+**/cpd.html
+
+*.swp
+
+#pom.xml.versionsBackup
+*.versionsBackup
+*netbean
+**/nbproject/
+*.lock
+*.checkstyle
+o2server/test.sh
+**/dest/
+**/dest_*/
+**/dist/
+*_bak.*
+*.bak

+ 661 - 0
LICENSE

@@ -0,0 +1,661 @@
+                    GNU AFFERO GENERAL PUBLIC LICENSE
+                       Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+  A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate.  Many developers of free software are heartened and
+encouraged by the resulting cooperation.  However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+  The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community.  It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server.  Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+  An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals.  This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU Affero General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Remote Network Interaction; Use with the GNU General Public License.
+
+  Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software.  This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time.  Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published
+    by the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source.  For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code.  There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<https://www.gnu.org/licenses/>.

+ 195 - 0
README.md

@@ -0,0 +1,195 @@
+<p align="center">
+	<a target="_blank" href="https://github.com/o2oa/o2oa/blob/develop/LICENSE"><img alt="GitHub license" src="https://img.shields.io/github/license/o2oa/o2oa"></a>
+	<a target="_blank" href="https://www.oracle.com/technetwork/java/javase/downloads/index.html">
+		<img src="https://img.shields.io/badge/JDK-11-green" />
+	</a>
+	<a target="_blank" href="https://gitee.com/o2oa/O2OA/stargazers">
+		<img src="https://gitee.com/o2oa/O2OA/badge/star.svg?theme=dark" alt='gitee star'/>
+	</a>
+	<a target="_blank" href="https://github.com/o2oa/o2oa/stargazers"><img alt="GitHub stars" src="https://img.shields.io/github/stars/o2oa/o2oa?style=social"></a>
+</p>
+
+
+----------------------------------------------------------------------------
+
+# O2OA(翱途)开发平台(100%开源的OA与协同办公解决方案)
+
+O2OA(翱途)低代码开发平台,100%开源企业协同办公定制平台,提供完整的前后端API和模块定制能力。平台基于JavaEE分布式架构,具备流程引擎、表单定制、页面定制能力及业务数据服务能力,支持跨平台移动办公,有效提升工作效率。
+
+平台通过国产信创认证,支持白标二次开发,提供高度灵活的协同办公解决方案,低成本满足企业办公需求。低代码开发大幅降低技术门槛,加速系统构建与定制,赋能企业快速响应业务实现,确保信息安全与自主可控。
+
+![o2oa](https://www.o2oa.net/v3/img/home/pic_zonghe@2x.png)
+
+
+
+# 官方网站:
+
+开源主页 : https://www.oschina.net/p/o2oa
+
+官方网站 : http://www.o2oa.net
+
+官方论坛 : https://www.o2oa.net/forum/
+
+
+# 关于正式环境数据安全相关的建议:
+
+O2OA自带的H2数据库是一个内嵌式的内存数据库,适合用于开发环境、功能演示环境,并不适合用作正式环境。
+
+如果作为正式环境使用,建议您使用拥有更高性能,更加稳定的商用级别数据库。如Mysql8,Oracle12C,SQLServer 2012等。
+
+另外,O2OA提供数据定期备份和恢复的能力,建议您开启正式环境的数据定期备份的功能,以确保数据库异常时可以进行数据恢复。
+
+
+
+# 支持操作系统:
+
+```shell
+Windows 64Bit, Linux 64Bit[CentOS, RedHat, Ubuntu等], MacOS, AIX, Raspberrypi(树莓派), ARM_Linux, MIPS_Linux, UOS,麒麟等国产操作系统
+```
+
+
+
+# 支持数据库:
+
+O2OA通过openjpa默认支持以下数据库:
+
+| Database Name                     | Database Version            | JDBC Driver Name                  | JDBC Driver Version |
+| --------------------------------- | --------------------------- | --------------------------------- | ------------------- |
+| Apache Derby                      | 10.1.2.1                    | Apache Derby Embedded JDBC Driver | 10.1.2.1            |
+| Borland Interbase                 | 7.1.0.202                   | Interclient                       | 4.5.1               |
+| Borland JDataStore                | 6.0                         | Borland JDataStore                | 6.0                 |
+| DB2                               | 8.1                         | IBM DB2 JDBC Universal Driver     | 1.0.581             |
+| Empress                           | 8.62                        | Empress Category 2 JDBC Driver    | 8.62                |
+| Firebird                          | 1.5                         | JayBird JCA/JDBC driver           | 1.0.1               |
+| H2 Database Engine                | 1.0                         | H2                                | 1.0                 |
+| Hypersonic Database Engine        | 1.8.0                       | Hypersonic                        | 1.8.0               |
+| Informix Dynamic Server           | 9.30.UC10                   | Informix JDBC driver              | 2.21.JC2            |
+| InterSystems Cache                | 5.0                         | Cache JDBC Driver                 | 5.0                 |
+| Microsoft Access                  | 9.0 (a.k.a. "2000")         | DataDirect SequeLink              | 5.4.0038            |
+| Microsoft SQL Server              | 9.00.1399 (SQL Server 2005) | SQLServer                         | 1.0.809.102         |
+| Microsoft Visual FoxPro           | 7.0                         | DataDirect SequeLink              | 5.4.0038            |
+| MySQL                             | 3.23.43-log                 | MySQL Driver                      | 3.0.14              |
+| MySQL                             | 5.0.26                      | MySQL Driver                      | 3.0.14              |
+| Oracle                            | 8.1,9.2,10.1                | Oracle JDBC driver                | 10.2.0.1.0          |
+| Pointbase                         | 4.4                         | Pointbase JDBC driver             | 4.4 (4.4)           |
+| PostgreSQL                        | 7.2.1                       | PostgreSQL Native Driver          | 8.1                 |
+| PostgreSQL                        | 8.1.5                       | PostgreSQL Native Driver          | 8.1                 |
+| Sybase Adaptive Server Enterprise | 12.5                        | jConnect                          | 5.5 (5.5)           |
+
+主流数据库都包含在内.
+
+
+
+# 国产数据库支持
+
+对与国产数据库的支持是对不同的数据库编写不同的适配方言来实现的,也就是通过定制DBDictionary来实现对接。
+
+目前我们已经成功适配的国产数据库如下:
+
+| 数据库                 | 方言                                                   |
+| ---------------------- | ------------------------------------------------------ |
+| 达梦                   | com.x.base.core.openjpa.jdbc.sql.DMDictionary          |
+| 南大通用8s             | com.x.base.core.openjpa.jdbc.sql.GBaseDictionary       |
+| 南大通用华库(mysql5)   | com.x.base.core.openjpa.jdbc.sql.GBaseMySQL5Dictionary |
+| 南大通用华库(mysql8)   | com.x.base.core.openjpa.jdbc.sql.GBaseMySQLDictionary  |
+| 人大金仓V7             | com.x.base.core.openjpa.jdbc.sql.KingbaseDictionary    |
+| 人大金仓V8             | com.x.base.core.openjpa.jdbc.sql.Kingbase8Dictionary   |
+| 人大金仓V8R6           | com.x.base.core.openjpa.jdbc.sql.Kingbase8R6Dictionary |
+| 神通数据库             | com.x.base.core.openjpa.jdbc.sql.OscarDictionary       |
+| 各种PostgreSQL改造版本 | org.apache.openjpa.jdbc.sql.PostgresDictionary         |
+
+
+
+# 平台使用手册:
+
+https://www.o2oa.net/handbook.html
+
+
+
+# 源码编译教程:
+
+https://www.o2oa.net/cms/source/335.html
+
+
+
+# 服务器部署教程:
+
+Windows环境:https://www.o2oa.net/cms/serverdeployment/467.html
+
+Linux环境:https://www.o2oa.net/cms/serverdeployment/468.html
+
+
+
+# How to Start
+
+## windows
+
+1.下载o2server. yyyyMMddHHmmss_ windows.zip程序包。
+
+2.解压下载后的压缩包到任意目录。
+
+3.确认开通服务器的80、20020、20030端口。
+
+4.打开o2server文件夹,选择start_ windows.bat双击打开。
+
+5.启动服务,等待相关服务自动完成。
+
+6.自动完成后打开浏览器访问http://127.0.0.1。
+
+7.输入用户名xadmin密码o2oa@2022登陆系统。
+
+## linux
+
+1.下载o2server. yyyyMMddHHmmss_linux.zip程序包。
+
+2.确认开通服务器的80、20020、20030端口。
+
+3."unzip o2server. syyyMMddHHmmss linux.zip" 解压程序包。
+
+4."cd o2server. yyyMMddHHmmss_ linux" 进入解压目录。
+
+5."cd o2server"进入程序目录。
+
+6."./start. linux.sh" 回车启动服务器控制台。
+
+7.启动服务,等待相关服务自动完成。
+
+8.自动完成后打开浏览器访问http://127.0.0.1。
+
+9.输入用户名xadmin密码o2oa@2022登陆系统。
+
+
+
+#### 若开发者学习研究O2OA,企业在O2OA应用开发平台上建设内部使用的办公系统,不闭源分发版本,不参与商业项目的使用行为不会构成侵权风险。
+
+#### 如果需要进行转售,闭源分发或者在商业项目中作为项目的一部分使用,请主动联系兰德网络公司购买商用许可。
+
+商用许可与支持服务:https://www.o2oa.net/service/gs.html
+
+
+
+# 协议
+
+[AGPL-3.0 开源协议。](./LICENSE)
+
+
+
+# 关于
+
+[![img](https://www.o2oa.net/v3/img/common/logo_all@2x.png)](./assets/O2OA-logo.jpg)
+
+浙江兰德纵横网络技术股份有限 公司成立于1999年,是浙江省高新技术企业、浙江省信息产业厅认定的软件企业、浙江省信息产业重点企业、浙江省软件业五强企业, 总部拥有5300多平方米的办公场所,员工总数超过600人,在北京、上海、深圳、天津、南京、合肥、郑州、重庆、沈阳、长春、哈尔滨、呼和浩特、济南、南昌等省会城市设立办事处, 服务网络覆盖中国及东南亚部分国家和地区。公司已通过ISO9001:2008国际质量体系认证,并取得了国家系统集成三级资质认证和CMI三级认证的证书。
+
+兰德网络是信息技术应用与服务提供商,为客户的计算机信息系统、企业级通信系统的建设提供解决方案和专业化服务。 业务包括:项目咨询、方案设计、应用系统开发、企业信息系统等服务。
+
+兰德网络在电信运营企业中拥有良好的品牌形象,我们的通信软件系列产品已广泛应用于中国电信、中国移动、中国联通和中国网通等广大电信运营商的通信系统。 兰德网络已成为中国无线信息服务产业中优秀的商业机构及系统提供商代表,并将成为信息产业、互联网和数据通信服务产业中杰出的系统与服务提供商代表。
+
+面向未来,兰德网络的愿景是“致力于更加美好的协作”。我们相信人类的协作一定会有更加美好的图景,等待我们去探索和描绘!
+
+
+
+O2OA(翱途)开发平台是由 **浙江兰德纵横网路技术股份有限公司** 建立和维护的。O2OA(翱途)的名字和标志是属于 **浙江兰德纵横网路技术股份有限公司** 的注册商标。
+
+我们 ❤️ 开源软件!看一下[我们的其他开源项目](https://github.com/o2oa),瞅一眼[我们的博客](https://my.oschina.net/o2oa)。
+
+

+ 411 - 0
build.xml

@@ -0,0 +1,411 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project basedir="." name="main" default="default">
+	<property name="VERSION" value="4.3.6" />
+	<property name="preName" value="o2server"/>
+	<property name="targetdir" value="target"/>
+	<target name="default" depends="md5code" />
+	<target name="md5code" depends="default_zip">
+		<checksum file="${preName}-${VERSION}-windows-x64.zip" property="build_win.sha" algorithm="SHA-256" />
+		<length file="${preName}-${VERSION}-windows-x64.zip" property="length_win"/>
+		<script language="javascript">
+			<![CDATA[
+			var length_win_bytes = project.getProperty("length_win");
+			var length_win_kbytes = Math.round((length_win_bytes / 1024) * Math.pow(10,2))/ Math.pow(10,2);
+			var length_win_mbytes = Math.round((length_win_kbytes / 1024) * Math.pow(10,2))/ Math.pow(10,2);
+			project.setNewProperty("length_win_mb", length_win_mbytes);
+		]]>
+		</script>
+		<checksum file="${preName}-${VERSION}-linux-x64.zip" property="build_linux.sha" algorithm="SHA-256" />
+		<length file="${preName}-${VERSION}-linux-x64.zip" property="length_linux"/>
+		<script language="javascript">
+			<![CDATA[
+			var length_linux_bytes = project.getProperty("length_linux");
+			var length_linux_kbytes = Math.round((length_linux_bytes / 1024) * Math.pow(10,2))/ Math.pow(10,2);
+			var length_linux_mbytes = Math.round((length_linux_kbytes / 1024) * Math.pow(10,2))/ Math.pow(10,2);
+			project.setNewProperty("length_linux_mb", length_linux_mbytes);
+		]]>
+		</script>
+		<checksum file="${preName}-${VERSION}-macosx64.zip" property="build_macosx64.sha" algorithm="SHA-256" />
+		<length file="${preName}-${VERSION}-macosx64.zip" property="length_macosx64"/>
+		<script language="javascript">
+			<![CDATA[
+			var length_macosx64_bytes = project.getProperty("length_macosx64");
+			var length_macosx64_kbytes = Math.round((length_macosx64_bytes / 1024) * Math.pow(10,2))/ Math.pow(10,2);
+			var length_macosx64_mbytes = Math.round((length_macosx64_kbytes / 1024) * Math.pow(10,2))/ Math.pow(10,2);
+			project.setNewProperty("length_macosx64_mb", length_macosx64_mbytes);
+		]]>
+		</script>
+		<checksum file="${preName}-${VERSION}-macosarm.zip" property="build_macosarm.sha" algorithm="SHA-256" />
+		<length file="${preName}-${VERSION}-macosarm.zip" property="length_macosarm"/>
+		<script language="javascript">
+			<![CDATA[
+			var length_macosarm_bytes = project.getProperty("length_macosarm");
+			var length_macosarm_kbytes = Math.round((length_macosarm_bytes / 1024) * Math.pow(10,2))/ Math.pow(10,2);
+			var length_macosarm_mbytes = Math.round((length_macosarm_kbytes / 1024) * Math.pow(10,2))/ Math.pow(10,2);
+			project.setNewProperty("length_macosarm_mb", length_macosarm_mbytes);
+		]]>
+		</script>
+		<checksum file="${preName}-${VERSION}-linux-mips.zip" property="build_mips.sha" algorithm="SHA-256" />
+		<length file="${preName}-${VERSION}-linux-mips.zip" property="length_mips"/>
+		<script language="javascript">
+			<![CDATA[
+			var length_mips_bytes = project.getProperty("length_mips");
+			var length_mips_kbytes = Math.round((length_mips_bytes / 1024) * Math.pow(10,2))/ Math.pow(10,2);
+			var length_mips_mbytes = Math.round((length_mips_kbytes / 1024) * Math.pow(10,2))/ Math.pow(10,2);
+			project.setNewProperty("length_mips_mb", length_mips_mbytes);
+		]]>
+		</script>
+		<checksum file="${preName}-${VERSION}-linux-arm.zip" property="build_arm.sha" algorithm="SHA-256" />
+		<length file="${preName}-${VERSION}-linux-arm.zip" property="length_arm"/>
+		<script language="javascript">
+			<![CDATA[
+			var length_arm_bytes = project.getProperty("length_arm");
+			var length_arm_kbytes = Math.round((length_arm_bytes / 1024) * Math.pow(10,2))/ Math.pow(10,2);
+			var length_arm_mbytes = Math.round((length_arm_kbytes / 1024) * Math.pow(10,2))/ Math.pow(10,2);
+			project.setNewProperty("length_arm_mb", length_arm_mbytes);
+		]]>
+		</script>
+		<checksum file="${preName}-${VERSION}-sw.zip" property="build_sw.sha" algorithm="SHA-256" />
+		<length file="${preName}-${VERSION}-sw.zip" property="length_sw"/>
+		<script language="javascript">
+			<![CDATA[
+			var length_sw_bytes = project.getProperty("length_sw");
+			var length_sw_kbytes = Math.round((length_sw_bytes / 1024) * Math.pow(10,2))/ Math.pow(10,2);
+			var length_sw_mbytes = Math.round((length_sw_kbytes / 1024) * Math.pow(10,2))/ Math.pow(10,2);
+			project.setNewProperty("length_sw_mb", length_sw_mbytes);
+		]]>
+		</script>
+		<tstamp>
+			<format property="TODAY" pattern="yyyy-MM-dd HH:mm:ss"/>
+		</tstamp>
+		<echo file="download_preview.jsonp">callback(
+			{
+			"windows": {
+			"system": "windows",
+			"name": "o2server-${VERSION}-windows-x64",
+			"fileName": "o2server-${VERSION}-windows-x64.zip",
+			"fileSize": "${length_win_mb}MB",
+			"updateTime": "${TODAY}",
+			"url": "/download/o2server-${VERSION}-windows-x64.zip",
+			"sha256": "${build_win.sha}"
+			},
+			"linux": {
+			"system": "linux",
+			"name": o2server-${VERSION}-linux-x64",
+			"fileName": "o2server-${VERSION}-linux-x64.zip",
+			"fileSize": "${length_linux_mb}MB",
+			"updateTime": "${TODAY}",
+			"url": "/download/o2server-${VERSION}-linux-x64.zip",
+			"sha256": "${build_linux.sha}"
+			},
+			"macosx64": {
+			"system": "macosx64",
+			"name": "o2server-${VERSION}-macosx64",
+			"fileName": "o2server-${VERSION}-macosx64.zip",
+			"fileSize": "${length_macosx64_mb}MB",
+			"updateTime": "${TODAY}",
+			"url": "/download/o2server-${VERSION}-macosx64.zip",
+			"sha256": "${build_macosx64.sha}"
+			},
+			"macosarm": {
+			"system": "macosarm",
+			"name": "o2server-${VERSION}-macosarm",
+			"fileName": "o2server-${VERSION}-macosarm.zip",
+			"fileSize": "${length_macosarm_mb}MB",
+			"updateTime": "${TODAY}",
+			"url": "/download/o2server-${VERSION}-macosarm.zip",
+			"sha256": "${build_macosarm.sha}"
+			},
+			"raspi": {
+			"system": "raspberrypi",
+			"name": "o2server-${VERSION}-raspi.zip",
+			"fileName": "o2server-${VERSION}-raspi.zip",
+			"fileSize": "${length_raspberrypi_mb}MB",
+			"updateTime": "${TODAY}",
+			"url": "/download/o2server-${VERSION}-raspi.zip",
+			"sha256": "${build_raspi.sha}"
+			},
+			"mips": {
+			"system": "mips",
+			"name": "o2server-${VERSION}-linux-mips",
+			"fileName": "o2server-${VERSION}-linux-mips.zip",
+			"fileSize": "${length_mips_mb}MB",
+			"updateTime": "${TODAY}",
+			"url": "/download/o2server-${VERSION}-linux-mips.zip",
+			"sha256": "${build_mips.sha}"
+			},
+			"arm": {
+			"system": "arm",
+			"name": "o2server-${VERSION}-linux-arm",
+			"fileName": "o2server-${VERSION}-linux-arm.zip",
+			"fileSize": "${length_arm_mb}MB",
+			"updateTime": "${TODAY}",
+			"url": "/download/o2server-${VERSION}-linux-arm.zip",
+			"sha256": "${build_arm.sha}"
+			},
+			"sw": {
+			"system": "sw",
+			"name": "o2server-${VERSION}-sw",
+			"fileName": "o2server-${VERSION}-sw.zip",
+			"fileSize": "${length_sw_mb}MB",
+			"updateTime": "${TODAY}",
+			"url": "/download/o2server-${VERSION}-sw.zip",
+			"sha256": "${build_sw.sha}"
+			},
+			"jvm_windows": {}
+			}
+			)</echo>
+		<echo file="download-pro.json">{
+			"title": "O2OA V${VERSION}",
+			"publishTime": "${TODAY}",
+			"windows": {
+			"system": "windows",
+			"name": "o2server-${VERSION}-windows-x64",
+			"fileName": "o2server-${VERSION}-windows-x64.zip",
+			"fileSize": "${length_win_mb}MB",
+			"updateTime": "${TODAY}",
+			"url": "/download/o2server-${VERSION}-windows-x64.zip",
+			"sha256": "${build_win.sha}"
+			},
+			"linux": {
+			"system": "linux",
+			"name": "o2server-${VERSION}-linux-x64",
+			"fileName": "o2server-${VERSION}-linux-x64.zip",
+			"fileSize": "${length_linux_mb}MB",
+			"updateTime": "${TODAY}",
+			"url": "/download/o2server-${VERSION}-linux-x64.zip",
+			"sha256": "${build_linux.sha}"
+			},
+			"macosx64": {
+			"system": "macosx64",
+			"name": "o2server-${VERSION}-macosx64",
+			"fileName": "o2server-${VERSION}-macosx64.zip",
+			"fileSize": "${length_macosx64_mb}MB",
+			"updateTime": "${TODAY}",
+			"url": "/download/o2server-${VERSION}-macosx64.zip",
+			"sha256": "${build_macosx64.sha}"
+			},
+			"macosarm": {
+			"system": "macosarm",
+			"name": "o2server-${VERSION}-macosarm",
+			"fileName": "o2server-${VERSION}-macosarm.zip",
+			"fileSize": "${length_macosarm_mb}MB",
+			"updateTime": "${TODAY}",
+			"url": "/download/o2server-${VERSION}-macosarm.zip",
+			"sha256": "${build_macosarm.sha}"
+			},
+			"raspi": {
+			"system": "raspi",
+			"name": "o2server-${VERSION}-raspi.zip",
+			"fileName": "o2server-${VERSION}-raspi.zip",
+			"fileSize": "${length_raspi_mb}MB",
+			"updateTime": "${TODAY}",
+			"url": "/download/o2server-${VERSION}-raspi.zip",
+			"sha256": "${build_raspi.sha}"
+			},
+			"mips": {
+			"system": "mips",
+			"name": "o2server-${VERSION}-linux-mips",
+			"fileName": "o2server-${VERSION}-linux-mips.zip",
+			"fileSize": "${length_mips_mb}MB",
+			"updateTime": "${TODAY}",
+			"url": "/download/o2server-${VERSION}-linux-mips.zip",
+			"sha256": "${build_mips.sha}"
+			},
+			"arm": {
+			"system": "arm",
+			"name": "o2server-${VERSION}-linux-arm",
+			"fileName": "o2server-${VERSION}-linux-arm.zip",
+			"fileSize": "${length_arm_mb}MB",
+			"updateTime": "${TODAY}",
+			"url": "/download/o2server-${VERSION}-linux-arm.zip",
+			"sha256": "${build_arm.sha}"
+			},
+			"sw": {
+			"system": "sw",
+			"name": "o2server-${VERSION}-sw",
+			"fileName": "o2server-${VERSION}-sw.zip",
+			"fileSize": "${length_sw_mb}MB",
+			"updateTime": "${TODAY}",
+			"url": "/download/o2server-${VERSION}-sw.zip",
+			"sha256": "${build_sw.sha}"
+			},
+			"source":{
+			"name":"o2oa-master",
+			"updateTime":"${TODAY}",
+			"fileSize":"990MB",
+			"url":"https://github.com/o2oa/o2oa"
+			}
+			}</echo>
+	</target>
+	<target name="default_zip" depends="version_o2,zip_windows,zip_linux,zip_macosx64,zip_macosarm,zip_raspi,zip_mips,zip_arm,zip_sw" />
+	<target name="version_o2">
+		<tstamp>
+			<format property="TODAY" pattern="yyyy-MM-dd HH:mm:ss"/>
+		</tstamp>
+		<echo file="${targetdir}/o2server/version.o2">{"version":"${VERSION}","date":"${TODAY}"}</echo>
+	</target>
+	<target name="zip_windows">
+		<zip encoding="utf-8" destfile="${preName}-${VERSION}-windows-x64.zip" update="false">
+			<zipfileset dir="${targetdir}/o2server" filemode="777" dirmode="777" encoding="utf-8" prefix="o2server">
+				<include name="commons/"/>
+				<exclude name="commons/ext/" />
+				<include name="configSample/"/>
+				<include name="localSample/"/>
+				<include name="servers/"/>
+				<include name="store/"/>
+				<include name="console.jar"/>
+				<include name="index.html"/>
+				<include name="version.o2"/>
+				<include name="jvm/windows_java11/"/>
+				<include name="service_windows.bat"/>
+				<include name="start_windows.bat"/>
+				<include name="start_windows_debug.bat"/>
+				<include name="console_windows.bat"/>
+				<include name="stop_windows.bat"/>
+			</zipfileset>
+		</zip>
+	</target>
+	<target name="zip_linux">
+		<zip encoding="utf-8" destfile="${preName}-${VERSION}-linux-x64.zip" update="false">
+			<zipfileset dir="${targetdir}/o2server" filemode="777" dirmode="777" encoding="utf-8" prefix="o2server">
+				<include name="commons/"/>
+				<exclude name="commons/ext/" />
+				<include name="configSample/"/>
+				<include name="localSample/"/>
+				<include name="servers/"/>
+				<include name="store/"/>
+				<include name="console.jar"/>
+				<include name="index.html"/>
+				<include name="version.o2"/>
+				<include name="jvm/linux_java11/"/>
+				<include name="start_linux.sh"/>
+				<include name="start_linux_min.sh"/>
+				<include name="start_linux_debug.sh"/>
+				<include name="console_linux.sh"/>
+				<include name="stop_linux.sh"/>
+				<include name="service_linux.sh"/>
+			</zipfileset>
+		</zip>
+	</target>
+	<target name="zip_macosx64">
+		<zip encoding="utf-8" destfile="${preName}-${VERSION}-macosx64.zip" update="false">
+			<zipfileset dir="${targetdir}/o2server" filemode="777" dirmode="777" encoding="utf-8" prefix="o2server">
+				<include name="commons/"/>
+				<exclude name="commons/ext/" />
+				<include name="configSample/"/>
+				<include name="localSample/"/>
+				<include name="servers/"/>
+				<include name="store/"/>
+				<include name="console.jar"/>
+				<include name="index.html"/>
+				<include name="version.o2"/>
+				<include name="jvm/macosx64_java11/"/>
+				<include name="start_macosx64.sh"/>
+				<include name="start_macosx64_debug.sh"/>
+				<include name="console_macosx64.sh"/>
+				<include name="stop_macosx64.sh"/>
+			</zipfileset>
+		</zip>
+	</target>
+	<target name="zip_macosarm">
+		<zip encoding="utf-8" destfile="${preName}-${VERSION}-macosarm.zip" update="false">
+			<zipfileset dir="${targetdir}/o2server" filemode="777" dirmode="777" encoding="utf-8" prefix="o2server">
+				<include name="commons/"/>
+				<exclude name="commons/ext/" />
+				<include name="configSample/"/>
+				<include name="localSample/"/>
+				<include name="servers/"/>
+				<include name="store/"/>
+				<include name="console.jar"/>
+				<include name="index.html"/>
+				<include name="version.o2"/>
+				<include name="jvm/macosarm_java11/"/>
+				<include name="start_macosarm.sh"/>
+				<include name="start_macosarm_debug.sh"/>
+				<include name="console_macosarm.sh"/>
+				<include name="stop_macosarm.sh"/>
+			</zipfileset>
+		</zip>
+	</target>
+	<target name="zip_raspi">
+		<zip encoding="utf-8" destfile="${preName}-${VERSION}-raspi.zip" update="false">
+			<zipfileset dir="${targetdir}/o2server" filemode="777" dirmode="777" encoding="utf-8" prefix="o2server">
+				<include name="commons/"/>
+				<exclude name="commons/ext/" />
+				<include name="configSample/"/>
+				<include name="localSample/"/>
+				<include name="servers/"/>
+				<include name="store/"/>
+				<include name="console.jar"/>
+				<include name="index.html"/>
+				<include name="version.o2"/>
+				<include name="jvm/raspi_java11/"/>
+				<include name="start_raspi.sh"/>
+				<include name="start_raspi_debug.sh"/>
+				<include name="console_raspi.sh"/>
+				<include name="stop_raspi.sh"/>
+			</zipfileset>
+		</zip>
+	</target>
+	<target name="zip_mips">
+		<zip encoding="utf-8" destfile="${preName}-${VERSION}-linux-mips.zip" update="false">
+			<zipfileset dir="${targetdir}/o2server" filemode="777" dirmode="777" encoding="utf-8" prefix="o2server">
+				<include name="commons/"/>
+				<exclude name="commons/ext/" />
+				<include name="configSample/"/>
+				<include name="localSample/"/>
+				<include name="servers/"/>
+				<include name="store/"/>
+				<include name="console.jar"/>
+				<include name="index.html"/>
+				<include name="version.o2"/>
+				<include name="jvm/mips_java11/"/>
+				<include name="start_mips.sh"/>
+				<include name="start_mips_debug.sh"/>
+				<include name="console_mips.sh"/>
+				<include name="stop_mips.sh"/>
+			</zipfileset>
+		</zip>
+	</target>
+	<target name="zip_arm">
+		<zip encoding="utf-8" destfile="${preName}-${VERSION}-linux-arm.zip" update="false">
+			<zipfileset dir="${targetdir}/o2server" filemode="777" dirmode="777" encoding="utf-8" prefix="o2server">
+				<include name="commons/"/>
+				<exclude name="commons/ext/" />
+				<include name="configSample/"/>
+				<include name="localSample/"/>
+				<include name="servers/"/>
+				<include name="store/"/>
+				<include name="console.jar"/>
+				<include name="index.html"/>
+				<include name="version.o2"/>
+				<include name="jvm/arm_java11/"/>
+				<include name="start_arm.sh"/>
+				<include name="start_arm_debug.sh"/>
+				<include name="console_arm.sh"/>
+				<include name="stop_arm.sh"/>
+			</zipfileset>
+		</zip>
+	</target>
+	<target name="zip_sw">
+		<zip encoding="utf-8" destfile="${preName}-${VERSION}-sw.zip" update="false">
+			<zipfileset dir="${targetdir}/o2server" filemode="777" dirmode="777" encoding="utf-8" prefix="o2server">
+				<include name="commons/"/>
+				<exclude name="commons/ext/" />
+				<include name="configSample/"/>
+				<include name="localSample/"/>
+				<include name="servers/"/>
+				<include name="store/"/>
+				<include name="console.jar"/>
+				<include name="index.html"/>
+				<include name="version.o2"/>
+				<include name="jvm/sw_java11/"/>
+				<include name="start_sw.sh"/>
+				<include name="start_sw_debug.sh"/>
+				<include name="console_sw.sh"/>
+				<include name="stop_sw.sh"/>
+			</zipfileset>
+		</zip>
+	</target>
+</project>

+ 64 - 0
changelog.md

@@ -0,0 +1,64 @@
+最新版本 v7.0.x:
+功能新增
+[容器]发布o2server smartbi 集成docker版本
+[容器]发布OnlyOffice集成版O2OA服务器Docker镜像
+[平台架构]新增了附件阿里云存储OSS支持(也可定制支持其他云存储)的功能
+[平台架构]新增了支持第三方短信通道发送验证码(支持阿里云、腾讯云、华为云、百度云)的功能
+[平台架构]新增了systemd注册脚本,实现开机o2server服务自启动
+[消息处理]改写消息处理机制,新增对kafka、activemq、restful、mail、api、jdbc、table、hadoop的支持
+[应用市场]新版应用市场上线
+[流程平台]新增了流程结束时将数据同步到自建表的功能
+[流程平台]新增了流程结束时将数据同步到Hadoop的功能
+[流程平台]表单组件新增了pdf和ofd控件
+[人员组织]新增了根据组织类型查询组织的接口
+[数据中心]新增了自建表立即生效,无需重启服务器的功能
+功能优化
+[平台架构]优化了登陆验证码,将默认值由true改为false
+[平台架构]优化了默认数据库索引
+[平台架构]优化了响应的etag设置使得附件下载支持etag
+[平台架构]优化了html转图片的接口,支持图片宽高设置
+[API]this.Table增加了addRow方法以支持单条插入
+[API]增加this.form.shareToIMChat分享工作到聊天消息
+[API]增加this.form.startIM 创建聊天并发送工作消息
+[论坛]优化了论坛,允许支持使用昵称
+[人员组织]优化了系统角色,限制系统角色名称不允许修改
+[流程平台]优化了通过word导入表单的功能,新增了支持docx格式
+[版式公文]优化了公文编辑器,支持从word拷贝段落编号
+[版式公文]优化了公文编辑器的页面位置计算,取消双页,增加pagePosition事件
+[内容管理]优化了内容管理文档评论,允许指定用户、内容管理分页列表中增加首页图片信息
+[内容管理]优化了内容管理消息发送配置
+[门户管理]优化了部件、部件元素,使其可以跨应用选择,页面保存部件可以也可以跨应用
+[数据中心]优化了视图和查询视图,为其增加隐藏分页和搜索的配置
+[服务管理接口]优化了invoke接口,支持get方式调用,并运行通过url传递参数
+[IM聊聊]优化了消息类型,增加流程工作消息
+[移动开发]优化了app自助打包程序的表单验证,提高使用成功率
+[移动开发]优化了移动app消息类型,增加流程工作消息
+[移动开发]优化了移动app会议管理,增加业务字段
+[移动开发]优化了Android端扫码识别率
+[移动开发]Android程序SDK升级
+[移动开发]优化了企业微信移动端和pc端适配
+问题修复
+[平台架构]修复了启动脚本可能定位不到程序目录导致无法获取log4j2配置文件的问题
+[平台架构]修复了在cpu64核以上环境中线程数不足的问题
+[平台架构]修复了调用PRC样式webService失败的问题
+[平台架构]修复了脚本执行中对gson 内建类解析的错误
+[平台架构]修复了附件名称有空格下载后变加号的问题
+[消息]修复了收到待办消息后打开新版的办公中心
+[流程平台]修复了回滚没有正确计算表单的问题
+[流程平台]修复了移动端身份选择后重新打开选项未被选中的问题
+[流程平台]修复了数据表格的附件移动端不能正常上传的问题
+[流程平台]修复了提交表单会执行主表单事件的问题
+[流程平台]修复了待阅根据应用统计接口返回错误的问题
+[版式公文]修复了公文编辑器对正文赋值后,载入页面冲突的问题
+[版式公文]修复了转换word时修改了页边距导致页码和版记部分重叠的问题
+[表单设计]修复了附件框拖拽错误上传的问题
+[表单设计]修复了校验时不能准确获取当前组件值的问题
+[数据中心]修复了查询语句部分情况下参数映射不成功的问题
+[数据中心]修复了windows自建表编译后不生效的问题
+[数据中心]修复了数据中心数据表查询权限错误的问题
+[数据中心]修复了查询语句一个条件多个值的问题
+[数据中心]修复了查询视图搜索条件为数字时报错的问题
+[数据中心]修复了查询视图值为0的时候不显示的问题
+[数据中心]修复了视图和查询视图switchView分页丢失的问题
+[内容管理]修复了内容管理发布文档指定身份不生效的问题
+[移动开发]修复了app已完成工作附件下载不了的问题

+ 1249 - 0
gulpfile.js

@@ -0,0 +1,1249 @@
+var gulp = require('gulp'),
+    gutil = require('gulp-util'),
+    del = require('del'),
+    fs = require("fs"),
+    minimist = require('minimist'),
+    targz = require('targz'),
+    slog = require('single-line-log').stdout,
+    dateFormat = require('dateformat'),
+    progress = require('progress-stream'),
+    request = require("request"),
+    // uglify = require('gulp-uglify-es').default,
+    uglify = require('gulp-terser'),
+    rename = require('gulp-rename'),
+    changed = require('gulp-changed'),
+    gulpif = require('gulp-if'),
+    http = require('http');
+const ora = require('ora');
+concat = require('gulp-concat');
+var fg = require('fast-glob');
+var logger = require('gulp-logger');
+var assetRev = require('gulp-o2oa-asset-rev');
+const os = require('os');
+var through2 = require('through2');
+var path = require('path');
+
+var git = require('gulp-git');
+const sourcemaps = require('gulp-sourcemaps');
+
+var supportedLanguage = ["zh-cn", "en", "es"];
+var translateLanguage = {
+    "en": "en",
+    "es": "spa"
+};
+var {generate} = require('@o2oa/language-tools');
+function check_language_pack(token){
+    return generate(null, translateLanguage, token).then(()=>{
+        return generate("o2_core", translateLanguage, token);
+    });
+}
+
+var downloadHost = "git.o2oa.net";
+var protocol = "https";
+var commonUrl = "/o2oa/evn-o2server-commons/-/archive/8.3/evn-o2server-commons-8.3.tar.gz?path=commons";
+var jvmUrls = {
+    "all": "/o2oa/evn-o2server-jvm/-/archive/master/evn-o2server-jvm-master.tar.gz?path=jvm",
+    "linux_java11": "/o2oa/evn-o2server-jvm/-/archive/master/evn-o2server-jvm-master.tar.gz?path=jvm/linux_java11",
+    "aix_java11": "/o2oa/evn-o2server-jvm/-/archive/master/evn-o2server-jvm-master.tar.gz?path=jvm/aix_java11",
+    "arm_java11": "/o2oa/evn-o2server-jvm/-/archive/master/evn-o2server-jvm-master.tar.gz?path=jvm/arm_java11",
+    "macosx64_java11": "/o2oa/evn-o2server-jvm/-/archive/master/evn-o2server-jvm-master.tar.gz?path=jvm/macosx64_java11",
+    "macosarm_java11": "/o2oa/evn-o2server-jvm/-/archive/master/evn-o2server-jvm-master.tar.gz?path=jvm/macosarm_java11",
+    "mips_java11": "/o2oa/evn-o2server-jvm/-/archive/master/evn-o2server-jvm-master.tar.gz?path=jvm/mips_java11",
+    "raspi_java11": "/o2oa/evn-o2server-jvm/-/archive/master/evn-o2server-jvm-master.tar.gz?path=jvm/raspi_java11",
+    "windows_java11": "/o2oa/evn-o2server-jvm/-/archive/master/evn-o2server-jvm-master.tar.gz?path=jvm/windows_java11",
+    "sw_java11": "/o2oa/evn-o2server-jvm/-/archive/master/evn-o2server-jvm-master.tar.gz?path=jvm/sw_java11"
+};
+
+var scripts = {
+    "all": ["o2server/*.sh", "o2server/*.jar", "o2server/*.html", "o2server/*.bat", "o2server/version.o2"],
+    "linux": ["o2server/*linux*", "o2server/*.jar", "o2server/*.html", "o2server/version.o2"],
+    "aix": ["o2server/*aix*", "o2server/*.jar", "o2server/*.html", "o2server/version.o2"],
+    "arm": ["o2server/*arm*", "o2server/*.jar", "o2server/*.html", "o2server/version.o2"],
+    "macosx64": ["o2server/*macosx64*", "o2server/*.jar", "o2server/*.html", "o2server/version.o2"],
+    "macosarm": ["o2server/*macosarm*", "o2server/*.jar", "o2server/*.html", "o2server/version.o2"],
+    "mips": ["o2server/*mips*", "o2server/*.jar", "o2server/*.html", "o2server/version.o2"],
+    "raspi": ["o2server/*raspi*", "o2server/*.jar", "o2server/*.html", "o2server/version.o2"],
+    "windows": ["o2server/*windows*", "o2server/*.jar", "o2server/*.html", "o2server/version.o2"],
+    "sw": ["o2server/*sw*", "o2server/*.jar", "o2server/*.html", "o2server/version.o2"]
+};
+
+var o_options = minimist(process.argv.slice(2), {//upload: local ftp or sftp
+    string: ["e", "lp", "w", "m", "d"]
+});
+var options = {};
+options.ev = o_options.e || "all";
+options.lp = o_options.lp || "zh-cn";
+options.webSite = o_options.w || "https://www.o2oa.net";
+options.mirrorSite = o_options.m || "http://mirror1.o2oa.net";
+options.downloadSite = o_options.d || "https://download.o2oa.net";
+var jvmUrl = jvmUrls[options.ev];
+var scriptSource = scripts[options.ev];
+
+function ProgressBar(description, bar_length){
+    this.description = description || 'Progress';
+    this.length = bar_length || 50;
+
+    this.render = function (opts){
+        var percent = (opts.completed / opts.total).toFixed(4);
+        var cell_num = Math.floor(percent * this.length);
+
+        var speed = "";
+        if (opts.time){
+            speed = (opts.completed/1024/1024)/(opts.time/1000);
+            speed = speed.toFixed(2);
+            speed = speed+"M/S";
+        }
+        var count = "";
+        if (opts.count){
+            count = "["+opts.count+"/"+opts.total+"]"
+        }
+
+        var cell = '';
+        for (var i=0;i<cell_num;i++) { cell += '>'; }
+
+        var empty = '';
+        for (var i=0;i<this.length-cell_num;i++) { empty += '='; }
+
+        if (opts.completed <= opts.total){
+            var d = new Date();
+            var cmdText = "["+dateFormat(d, "HH:MM:ss")+"]"+" "+this.description + ': ' + cell + empty + ' ' + (100*percent).toFixed(2) + '% '+speed+count+'\n';
+            slog(cmdText);
+        }
+    };
+}
+
+function downloadFile_progress(path, filename, headcb, progresscb, cb){
+    var dest = `o2server/${filename}`;
+
+    let stream = fs.createWriteStream(dest);
+    var options = { url:protocol+"://"+downloadHost+path };
+    var fileHost = downloadHost;
+    var filePath =  path;
+    stream.on('finish', () => {
+        cb();
+    });
+    stream.on('error', (err) => {
+        gutil.log(gutil.colors.red("download error"), ":", gutil.colors.red(filename), err);
+    });
+    var req = http.request({
+        host:fileHost,
+        path:filePath,
+        method:'HEAD'
+    },function (res){
+        if (res.statusCode == 200) {
+            res.setEncoding(null);
+            var time = 0;
+            var l = res.headers['content-length'];
+            var str = progress({
+                length: l,
+                time: 100 /* ms */
+            });
+            headcb(l);
+
+            str.on('progress', function(progress) {
+                if (pb){
+                    progresscb(progress);
+                    pb.render({ completed: currentLength, total: totalLength, time: time+=100 });
+                }
+
+            });
+            request.get(options).pipe(str).pipe(stream);
+        } else {
+            downloadFile(path, filename, headcb, progresscb, cb)
+        }
+    })
+    req.on('error', (e) => {
+        downloadFile(path, filename, headcb, progresscb, cb)
+    });
+    req.end();
+    //    }
+    //});
+}
+function downloadFile(path, filename, headcb, progresscb, cb){
+    var dest = `o2server/${filename}`;
+
+    const spinner = ora({
+        'prefixText': 'Downloading '+filename+' ...',
+        'spinner': {
+            interval: 80, // Optional
+            frames: ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏']
+        }
+    }).start();
+
+    let stream = fs.createWriteStream(dest);
+    var options = { url:protocol+"://"+downloadHost+path };
+    var fileHost = downloadHost;
+    var filePath =  path;
+    stream.on('finish', () => {
+        spinner.stop();
+        spinner.succeed(filename + ' Downloaded!');
+        cb();
+    });
+    stream.on('error', (err) => {
+        gutil.log(gutil.colors.red("download error"), ":", gutil.colors.red(filename), err);
+    });
+
+    request.get(options).pipe(stream);
+}
+
+var commonsLength = 0;
+var jvmLenght = 0;
+var totalLength = 0;
+var currentLength = 0;
+var commonsCurrentLength = 0;
+var jvmCurrentLength = 0;
+
+var pb = null;
+function initProgress(){
+    if (commonsLength && jvmLenght){
+        totalLength = +commonsLength + jvmLenght;
+        var t = (totalLength/1024/1024).toFixed(2);
+        pb = new ProgressBar('total: '+t+"M", 50);
+
+    }
+}
+
+function download_commons_and_jvm(cb){
+    gutil.log(gutil.colors.green("begin download commons and jvm"));
+    console.log(`---------------------------------------------------------------------
+  . Start to download the dependencies needed for compilation ...
+---------------------------------------------------------------------`);
+    var downloader = new Promise((resolve, reject) => {
+        var commonLoaded = false;
+        var jvmLoaded = false;
+        downloadFile_progress(commonUrl, "commons_git.tar.gz", (length)=>{
+            commonsLength = +length;
+            initProgress();
+        }, (progress)=>{
+            commonsCurrentLength = progress.transferred;
+            currentLength = +commonsCurrentLength+jvmCurrentLength;
+        }, ()=>{
+            commonLoaded = true;
+            if (jvmLoaded && commonLoaded) resolve();
+        });
+        downloadFile_progress(jvmUrl, "jvm_git.tar.gz", (length)=>{
+            jvmLenght = +length;
+            initProgress();
+        }, (progress)=>{
+            jvmCurrentLength = progress.transferred;
+            currentLength = +commonsCurrentLength+jvmCurrentLength;
+        }, ()=>{
+            jvmLoaded = true;
+            if (jvmLoaded && commonLoaded) resolve();
+        });
+    });
+    downloader.then(()=>{
+        gutil.log(gutil.colors.green("download commons and jvm completed"));
+        cb();
+    });
+}
+
+function decompress_commons_and_jvm(cb){
+    console.log(`---------------------------------------------------------------------
+  . Start to decompress the dependencies needed for compilation ...
+---------------------------------------------------------------------`);
+    gutil.log(gutil.colors.green("begin decompress commons and jvm"));
+    var count =0;
+    var decompressor = new Promise((resolve, reject) => {
+        var commonUnziped = false;
+        var jvmUnziped = false;
+        targz.decompress({
+            src: 'o2server/commons_git.tar.gz',
+            dest: 'o2server/tmp',
+            tar: {map: function(header){
+                    count++;
+                    var d = new Date();
+                    slog("["+dateFormat(d, "HH:MM:ss")+"] " + count +" "+ header.name+" ...");
+                }}
+        }, function(err){
+            if(err) {
+                gutil.log(gutil.colors.red("decompress error"), ":", gutil.colors.red("common.tar.gz "), err);
+            } else {
+                commonUnziped = true;
+                if (jvmUnziped && commonUnziped) resolve();
+            }
+        });
+        targz.decompress({
+            src: 'o2server/jvm_git.tar.gz',
+            dest: 'o2server/tmp',
+            tar: {map: function(header){
+                    count++;
+                    var d = new Date();
+                    slog("["+dateFormat(d, "HH:MM:ss")+"] " + count +" "+ header.name+" ...");
+                }}
+        }, function(err){
+            if(err) {
+                gutil.log(gutil.colors.red("decompress error"), ":", gutil.colors.red("jvm.tar.gz "), err);
+            } else {
+                jvmUnziped = true;
+                if (jvmUnziped && commonUnziped) resolve();
+            }
+        });
+    });
+    decompressor.then(()=>{
+        gutil.log(gutil.colors.green("decompress commons and jvm completed. " + count+" files"));
+        cb();
+    });
+}
+function move_commons(){
+    console.log(`---------------------------------------------------------------------
+  . move commons files to o2server/commons ...
+---------------------------------------------------------------------`);
+    return gulp.src("o2server/tmp/evn-o2server-commons-8.3-commons/commons/**/*")
+        .pipe(gulp.dest("o2server/commons/"));
+}
+function move_jvm(){
+    console.log(`---------------------------------------------------------------------
+  . move jvm files to o2server/jvm ...
+---------------------------------------------------------------------`);
+    var path;
+    if (options.ev=="all"){
+        path = "o2server/tmp/evn-o2server-jvm-master-jvm/jvm/**/*"
+    }else{
+        path = "o2server/tmp/evn-o2server-jvm-master-jvm-"+options.ev+"/jvm/**/*"
+    }
+    return gulp.src(path)
+        .pipe(gulp.dest("o2server/jvm/"));
+}
+async function clear_commons_git(cb) {
+    var dest = ['o2server/tmp/evn-o2server-commons-8.3-commons/', 'o2server/commons_git.tar.gz'];
+    await del(dest, {force: true});
+    cb();
+}
+async function clear_jvm_git(cb){
+    await del(['o2server/tmp/', 'o2server/jvm_git.tar.gz'], { force: true });
+    cb();
+}
+
+function build_web_language_pack(cb){
+    if (fs.existsSync('./gulpconfig.js')){
+        const {token} = require('./gulpconfig.js');
+        return check_language_pack(token);
+    }
+    cb();
+}
+
+exports.build_web_language_pack = build_web_language_pack
+
+var moduleFolder = [];
+async function build_web_module() {
+    var dest = 'target/o2server/servers/webServer/';
+    var srcPath = 'o2web/source';
+    const fp = fs.promises;
+
+    return fp.readdir(srcPath).then((files)=>{
+        let statP = [];
+        files.forEach((file) => {
+            let p = path.resolve(srcPath, file)
+            statP.push(fp.stat(p).then((s)=>{
+                if (s.isDirectory()){
+                    var pkgPath = path.resolve(srcPath, p, 'package.json');
+                    if (fs.existsSync(pkgPath)){
+                        var pkg = require(pkgPath);
+                        if (pkg.scripts['o2-build']){
+                            moduleFolder.push(file);
+                        }
+                    }
+                }
+            }));
+        });
+        const shelljs = require('shelljs');
+        return Promise.all(statP).then(()=>{
+            moduleFolder.forEach((f)=>{
+                shelljs.config.verbose = true;
+                shelljs.exec('npm install && npm run o2-build', {cwd: path.resolve(srcPath, f)});
+            });
+        });
+    });
+}
+
+function build_web_minimize(cb) {
+    console.log(`---------------------------------------------------------------------
+  . Start compiling the web ...
+---------------------------------------------------------------------`);
+
+    var dest = 'target/o2server/servers/webServer/';
+    var lpFiles = supportedLanguage.join('|');
+
+    var src_min = ['o2web/source/**/*.js', '!**/lp/!('+lpFiles+').js', '!o2web/source/o2_core/o2.js', '!**/*.spec.js', '!**/test/**', '!o2web/source/o2_lib/**/*', '!**/node_modules/**/*', '!**/dist/**/*'];
+    moduleFolder.forEach((f)=>{
+        src_min.push('!o2web/source/'+f+'/**/*');
+    })
+
+    var entries = fg.sync(src_min, { dot: false});
+    var size = entries.length;
+
+    var pb = new ProgressBar('', 50);
+    var doCount = 0;
+
+    var stream = gulp.src(src_min);
+
+    return stream.pipe(uglify())
+        .pipe(rename({ extname: '.min.js' }))
+        .pipe(gulp.dest(dest))
+        .pipe(logger(function(){
+            doCount++;
+            if (doCount <= size){pb.render({ completed: doCount, total: size, count: doCount})};
+        }))
+        .pipe(gutil.noop());
+}
+
+function build_web_move() {
+    var dest = 'target/o2server/servers/webServer/';
+
+    var lpFiles = supportedLanguage.join('|');
+
+    var src_move = ['o2web/source/**/*', '!**/lp/!('+lpFiles+').js', '!o2web/source/o2_core/o2.js', '!**/*.spec.js', '!**/test/**', '!**/node_modules/**/*', '!**/dist/**/*'];
+    moduleFolder.forEach((f)=>{
+        src_move.push('!o2web/source/'+f+'/**/*');
+    })
+
+    var entries = fg.sync(src_move, { dot: false});
+    var size = entries.length;
+    var pb = new ProgressBar('', 50);
+    var doCount = 0;
+
+    var stream = gulp.src(src_move);
+
+    return stream.pipe(gulp.dest(dest))
+        .pipe(logger(function(){
+            doCount++;
+            if (doCount <= size) {pb.render({ completed: doCount, total: size, count: doCount})};
+        }))
+        .pipe(gutil.noop());
+}
+
+function build_concat_o2(){
+    var src = [
+        'o2web/source/o2_core/polyfill.js',
+        'o2web/source/o2_lib/mootools/mootools-1.6.0_all.js',
+        'o2web/source/o2_lib/mootools/plugin/mBox.js',
+        'o2web/source/o2_core/o2.js'
+    ];
+    var dest = 'target/o2server/servers/webServer/o2_core/';
+    return gulp.src(src)
+        .pipe(concat('o2.js'))
+        .pipe(gulp.dest(dest))
+        .pipe(sourcemaps.init())
+        .pipe(concat('o2.min.js'))
+        .pipe(uglify())
+        .pipe(sourcemaps.write("./"))
+        .pipe(gulp.dest(dest))
+}
+function build_concat_base(){
+    var src = [
+        'o2web/source/x_desktop/js/base.js',
+        'o2web/source/o2_core/o2/xScript/PageEnvironment.js',
+        'o2web/source/o2_core/o2/framework.js',
+        'o2web/source/x_desktop/js/base_loader.js'
+    ];
+    var dest = 'target/o2server/servers/webServer/x_desktop/js/';
+    return gulp.src(src)
+        .pipe(concat('base.js'))
+        .pipe(gulp.dest(dest))
+        .pipe(sourcemaps.init())
+        .pipe(concat('base.min.js'))
+        .pipe(uglify())
+        .pipe(sourcemaps.write("./"))
+        .pipe(gulp.dest(dest));
+}
+function build_concat_desktop(){
+    let path = "o2_core";
+    var src = [
+        'o2web/source/'+path+'/o2/widget/Common.js',
+        'o2web/source/'+path+'/o2/widget/Dialog.js',
+        'o2web/source/'+path+'/o2/widget/UUID.js',
+        'o2web/source/'+path+'/o2/xDesktop/Common.js',
+        'o2web/source/'+path+'/o2/xDesktop/Actions/RestActions.js',
+        'o2web/source/'+path+'/o2/xAction/RestActions.js',
+        'o2web/source/'+path+'/o2/xDesktop/Access.js',
+        'o2web/source/'+path+'/o2/xDesktop/Dialog.js',
+        'o2web/source/'+path+'/o2/xDesktop/Menu.js',
+        'o2web/source/'+path+'/o2/xDesktop/UserData.js',
+        'o2web/source/x_component_Template/MPopupForm.js',
+        'o2web/source/'+path+'/o2/xDesktop/Authentication.js',
+        'o2web/source/'+path+'/o2/xDesktop/Dialog.js',
+        'o2web/source/'+path+'/o2/xDesktop/Window.js',
+        'o2web/source/x_component_Common/Main.js'
+    ];
+    var dest = 'target/o2server/servers/webServer/o2_core/o2/xDesktop/';
+    return gulp.src(src)
+        .pipe(concat('$all.js'))
+        .pipe(gulp.dest(dest))
+        .pipe(sourcemaps.init())
+        .pipe(concat('$all.min.js'))
+        .pipe(uglify())
+        .pipe(sourcemaps.write("./"))
+        .pipe(gulp.dest(dest))
+}
+function build_concat_xform(){
+    let path = "x_component_process_Xform";
+    var src = [
+        'o2web/source/o2_core/o2/widget/AttachmentController.js',
+        'o2web/source/o2_core/o2/xScript/Macro.js',
+        'o2web/source/o2_core/o2/widget/Tab.js',
+        'o2web/source/o2_core/o2/widget/O2Identity.js',
+        'o2web/source/' + path + '/Form.js',
+        'o2web/source/' + path + '/$Module.js',
+        'o2web/source/' + path + '/$Input.js',
+        'o2web/source/' + path + '/Div.js',
+        'o2web/source/' + path + '/Combox.js',
+        'o2web/source/' + path + '/DatagridMobile.js',
+        'o2web/source/' + path + '/DatagridPC.js',
+        'o2web/source/' + path + '/Textfield.js',
+        'o2web/source/' + path + '/Personfield.js',
+        'o2web/source/' + path + '/Button.js',
+        'o2web/source/' + path + '/ViewSelector.js',
+        'o2web/source/' + path + '/*.js',
+        'o2web/source/x_component_process_Work/Processor.js',
+        '!o2web/source/' + path + '/Documenteditor.js ',
+        '!o2web/source/' + path + '/Office.js',
+        '!o2web/source/' + path + '/WpsOffice.js',
+        '!o2web/source/' + path + '/WpsOffice2.js',
+        '!o2web/source/' + path + '/YozoOffice.js',
+        '!o2web/source/' + path + '/IWebOffice.js',
+        '!o2web/source/' + path + '/OnlyOffice.js',
+        '!o2web/source/' + path + '/TinyMCEEditor.js'
+    ];
+    var dest = 'target/o2server/servers/webServer/'+path+'/';
+    return gulp.src(src)
+        .pipe(concat('$all.js'))
+        .pipe(gulp.dest(dest))
+        .pipe(sourcemaps.init())
+        .pipe(concat('$all.min.js'))
+        .pipe(uglify())
+        .pipe(sourcemaps.write("./"))
+        .pipe(gulp.dest(dest))
+}
+
+function build_concat_cms_xform(){
+    let processPath = "x_component_process_Xform";
+    let path = "x_component_cms_Xform";
+    var src = [
+        'o2web/source/o2_core/o2/widget/AttachmentController.js',
+        // 'source/o2_core/o2/xScript/CMSEnvironment.js',
+        'o2web/source/o2_core/o2/xScript/CMSMacro.js',
+        'o2web/source/o2_core/o2/widget/Tab.js',
+        'o2web/source/o2_core/o2/widget/O2Identity.js',
+        'o2web/source/' + processPath + '/Form.js',
+        'o2web/source/' + processPath + '/$Module.js',
+        'o2web/source/' + processPath + '/$Input.js',
+        'o2web/source/' + processPath + '/Div.js',
+        //'source/' + processPath + '/Combox.js',
+        'o2web/source/' + processPath + '/DatagridMobile.js',
+        'o2web/source/' + processPath + '/DatagridPC.js',
+        'o2web/source/' + processPath + '/Textfield.js',
+        //'source/' + processPath + '/Personfield.js',
+        'o2web/source/' + processPath + '/Button.js',
+        //'source/' + processPath + '/ViewSelector.js',
+        'o2web/source/' + processPath + '/Org.js',
+        // 'source/' + processPath + '/*.js',
+        'o2web/source/' + processPath + '/Actionbar.js',
+        //'source/' + processPath + '/Address.js',
+        'o2web/source/' + processPath + '/Attachment.js',
+        'o2web/source/' + processPath + '/Calendar.js',
+        'o2web/source/' + processPath + '/Checkbox.js',
+        'o2web/source/' + processPath + '/Datagrid.js',
+        'o2web/source/' + processPath + '/Htmleditor.js',
+        //'source/' + processPath + '/Iframe.js',
+        'o2web/source/' + processPath + '/Label.js',
+        'o2web/source/' + processPath + '/Number.js',
+        'o2web/source/' + processPath + '/Common.js',
+        'o2web/source/' + processPath + '/Image.js',
+        'o2web/source/' + processPath + '/ImageClipper.js',
+        'o2web/source/' + processPath + '/Html.js',
+        'o2web/source/' + processPath + '/Radio.js',
+        'o2web/source/' + processPath + '/Select.js',
+        //'source/' + processPath + '/Stat.js',
+        //'source/' + processPath + '/Statement.js',
+        //'source/' + processPath + '/StatementSelector.js',
+        //'source/' + processPath + '/Subform.js',
+        'o2web/source/' + processPath + '/Tab.js',
+        'o2web/source/' + processPath + '/Table.js',
+        'o2web/source/' + processPath + '/Textarea.js',
+        //'source/' + processPath + '/Tree.js',
+        //'source/' + processPath + '/View.js',
+        // 'source/x_component_process_Work/Processor.js',
+        // '!source/' + processPath + '/Office.js'
+
+
+        'o2web/source/o2_core/o2/widget/SimpleToolbar.js',
+        'o2web/source/' + path + '/ModuleImplements.js',
+        'o2web/source/' + path + '/Package.js',
+        'o2web/source/' + path + '/Form.js',
+        //'source/' + path + '/widget/Comment.js',
+        'o2web/source/' + path + '/widget/Log.js',
+        'o2web/source/' + path + '/Org.js',
+        'o2web/source/' + path + '/Author.js',
+        'o2web/source/' + path + '/Reader.js',
+        'o2web/source/' + path + '/Textfield.js',
+        'o2web/source/' + path + '/Actionbar.js',
+        'o2web/source/' + path + '/Attachment.js',
+        'o2web/source/' + path + '/Button.js',
+        'o2web/source/' + path + '/Calendar.js',
+        'o2web/source/' + path + '/Checkbox.js',
+        'o2web/source/' + path + '/Datagrid.js',
+        'o2web/source/' + path + '/Htmleditor.js',
+        'o2web/source/' + path + '/ImageClipper.js',
+        'o2web/source/' + path + '/Label.js',
+        'o2web/source/' + path + '/Number.js',
+        'o2web/source/' + path + '/Radio.js',
+        'o2web/source/' + path + '/Select.js',
+        'o2web/source/' + path + '/Tab.js',
+        'o2web/source/' + path + '/Table.js',
+        'o2web/source/' + path + '/Textarea.js'
+        //'source/' + path + '/Personfield.js',
+        //'source/' + path + '/Readerfield.js',
+        //'source/' + path + '/Authorfield.js',
+        //'source/' + path + '/Orgfield.js',
+        // 'source/' + path + '/*.js',
+        // '!source/' + path + '/Office.js'
+    ];
+    var dest = 'target/o2server/servers/webServer/'+path+'/';
+    return gulp.src(src)
+        .pipe(concat('$all.js'))
+        .pipe(gulp.dest(dest))
+        .pipe(sourcemaps.init())
+        .pipe(concat('$all.min.js'))
+        .pipe(uglify())
+        .pipe(sourcemaps.write("./"))
+        .pipe(gulp.dest(dest))
+}
+
+function build_bundle(){
+    let path = "o2_core";
+    var src = [
+        'o2web/source/o2_core/polyfill.js',
+        'o2web/source/o2_lib/mootools/mootools-1.6.0_all.js',
+        'o2web/source/o2_lib/mootools/plugin/mBox.js',
+        'o2web/source/o2_core/o2.js',
+        'o2web/source/x_desktop/js/base.js',
+        'o2web/source/x_desktop/js/base_loader.js',
+        'o2web/source/o2_core/o2/xScript/PageEnvironment.js',
+        "o2web/source/o2_core/o2/framework.js"
+    ];
+    var dest = 'target/o2server/servers/webServer/'+path+'/';
+    return gulp.src(src)
+        .pipe(concat('bundle.js'))
+        .pipe(gulp.dest(dest))
+        .pipe(sourcemaps.init())
+        .pipe(concat('bundle.min.js'))
+        .pipe(uglify())
+        .pipe(sourcemaps.write("./"))
+        .pipe(gulp.dest(dest))
+}
+
+
+function concat_Actions(){
+    return through2.obj(function (file, enc, cb) {
+        if (file.isNull()) {
+            this.push(file);
+            return cb();
+        }
+
+        if (file.isStream()) {
+            this.emit('error', new gutil.PluginError(PLUGIN_NAME, 'Streaming not supported'));
+            return cb();
+        }
+        var content = file.contents.toString();
+
+        var o = path.parse(file.path);
+        var name = o.name;
+        content = "var actionJson = "+content;
+        content = content+"\nif (!o2.xAction.RestActions.Action[\""+name+"\"]) o2.xAction.RestActions.Action[\""+name+"\"] = new Class({Extends: o2.xAction.RestActions.Action});";
+        content = content+"\no2.Actions.actions[\""+name+"\"] = new o2.xAction.RestActions.Action[\""+name+"\"](\""+name+"\", actionJson);";
+
+        file.contents = new Buffer.from(content);
+        this.push(file);
+        cb();
+    });
+}
+function concat_Style(){
+    return through2.obj(function (file, enc, cb) {
+        if (file.isNull()) {
+            this.push(file);
+            return cb();
+        }
+
+        if (file.isStream()) {
+            this.emit('error', new gutil.PluginError(PLUGIN_NAME, 'Streaming not supported'));
+            return cb();
+        }
+        var content = file.contents.toString();
+        var name = file.path.replace(process.cwd(), "").replace(/\\/g, "/")
+        name = ".."+name.substring(name.indexOf("/source")+7);
+        content = "var csskey = encodeURIComponent(\""+name+"\");\no2.widget.css[csskey]="+content;
+
+        file.contents = new Buffer.from(content);
+        this.push(file);
+        cb();
+    });
+}
+
+function build_concat_basework_style(){
+    return gulp.src([
+        "o2web/source/x_component_process_Work/$Main/default/css.wcss",
+        "o2web/source/x_component_process_Xform/$Form/default/css.wcss",
+        "o2web/source/o2_core/o2/widget/$Tab/mobileForm/css.wcss",
+        "o2web/source/o2_core/o2/widget/$Menu/tab/css.wcss",
+        "o2web/source/o2_core/o2/widget/$Tab/form/css.wcss",
+        "o2web/source/x_component_process_Xform/$Form/default/doc.wcss",
+        "o2web/source/o2_core/o2/widget/$Toolbar/documentEdit/css.wcss",
+        "o2web/source/o2_core/o2/widget/$Toolbar/documentEdit_side/css.wcss",
+        "o2web/source/x_component_process_Xform/$Form/default/css.wcss"
+    ])
+        .pipe(concat_Style())
+        .pipe(concat('js/base_work_style_temp.js'))
+        .pipe(gulp.dest('o2web/source/x_desktop/'));
+}
+
+function build_concat_basework_action(){
+    return gulp.src([
+        "o2web/source/o2_core/o2/xAction/services/x_organization_assemble_authentication.json",
+        "o2web/source/o2_core/o2/xAction/services/x_processplatform_assemble_surface.json",
+        "o2web/source/o2_core/o2/xAction/services/x_organization_assemble_control.json",
+        "o2web/source/o2_core/o2/xAction/services/x_query_assemble_surface.json",
+        "o2web/source/o2_core/o2/xAction/services/x_cms_assemble_control.json",
+        "o2web/source/o2_core/o2/xAction/services/x_program_center.json",
+        "o2web/source/o2_core/o2/xAction/services/x_organization_assemble_personal.json"
+    ])
+        .pipe(concat_Actions())
+        .pipe(concat('js/base_work_actions_temp.js'))
+        .pipe(gulp.dest('o2web/source/x_desktop/'));
+}
+
+function build_concat_basework_clean(cb) {
+    var dest = [
+        'o2web/source/x_desktop/js/base_work_actions_temp.js',
+        'o2web/source/x_desktop/js/base_work_style_temp.js'
+    ];
+    return del(dest, cb);
+}
+
+
+
+function build_concat_lp(cb) {
+    var lpTasks = [];
+    supportedLanguage.forEach(function(lp){
+        var src = [
+            'o2web/source/o2_core/o2/lp/'+(lp)+'.js',
+            'o2web/source/x_component_process_Work/lp/'+(lp)+'.js',
+            'o2web/source/x_component_process_Xform/lp/'+(lp)+'.js',
+            'o2web/source/x_component_Selector/lp/'+(lp)+'.js',
+            'o2web/source/x_component_Template/lp/'+(lp)+'.js',
+            'o2web/source/x_component_portal_Portal/lp/'+(lp)+'.js',
+            'o2web/source/x_component_cms_Document/lp/'+(lp)+'.js',
+            'o2web/source/x_component_cms_Xform/lp/'+(lp)+'.js',
+        ];
+        var dest = 'target/o2server/servers/webServer/x_desktop/js/';
+        var stream = gulp.src(src, {"allowEmpty": true});
+        lpTasks.push(new Promise((resolve)=>{
+            stream.on("end", ()=>{  resolve(); });
+        }));
+        stream.pipe(concat('base_lp_' + lp + '.js'))
+            .pipe(gulp.dest(dest))
+            .pipe(sourcemaps.init())
+            .pipe(concat('base_lp_' + lp + '.min.js'))
+            .pipe(uglify())
+            .pipe(sourcemaps.write('./'))
+            .pipe(gulp.dest(dest));
+    });
+
+    return Promise.all(lpTasks);
+}
+
+
+
+function build_concat_basework_body() {
+    var src = [
+        'o2web/source/x_desktop/js/base_concat_head.js',
+        //'o2web/source/o2_core/o2/lp/'+(options.lp || 'zh-cn')+'.js',
+
+        'o2web/source/x_desktop/js/base_work_style_temp.js',
+
+        'o2web/source/o2_core/o2/widget/Common.js',
+        'o2web/source/o2_core/o2/widget/Dialog.js',
+        'o2web/source/o2_core/o2/widget/UUID.js',
+        'o2web/source/o2_core/o2/widget/Menu.js',
+        'o2web/source/o2_core/o2/widget/Toolbar.js',
+        'o2web/source/o2_core/o2/xDesktop/Common.js',
+        'o2web/source/o2_core/o2/xDesktop/Actions/RestActions.js',
+        'o2web/source/o2_core/o2/xAction/RestActions.js',
+        'o2web/source/o2_core/o2/xDesktop/Access.js',
+        'o2web/source/o2_core/o2/xDesktop/Dialog.js',
+        'o2web/source/o2_core/o2/xDesktop/Menu.js',
+        'o2web/source/o2_core/o2/xDesktop/UserData.js',
+        'o2web/source/x_component_Template/MPopupForm.js',
+        'o2web/source/o2_core/o2/xDesktop/Authentication.js',
+        'o2web/source/o2_core/o2/xDesktop/Dialog.js',
+        'o2web/source/o2_core/o2/xDesktop/Window.js',
+        'o2web/source/x_component_Common/Main.js',
+
+        // 'o2web/source/o2_core/o2/lp/'+(options.lp || 'zh-cn')+'.js',
+        // 'o2web/source/x_component_process_Work/lp/'+(options.lp || 'zh-cn')+'.js',
+        // 'o2web/source/x_component_process_Xform/lp/'+(options.lp || 'zh-cn')+'.js',
+        // 'o2web/source/x_component_Selector/lp/'+(options.lp || 'zh-cn')+'.js',
+
+        'o2web/source/x_component_process_Work/Main.js',
+        'o2web/source/x_component_Selector/package.js',
+        // 'o2web/source/x_component_Selector/Person.js',
+        // 'o2web/source/x_component_Selector/Identity.js',
+        // 'o2web/source/x_component_Selector/Unit.js',
+        // 'o2web/source/x_component_Selector/IdentityWidthDuty.js',
+        // 'o2web/source/x_component_Selector/IdentityWidthDutyCategoryByUnit.js',
+        // 'o2web/source/x_component_Selector/UnitWithType.js',
+        'o2web/source/o2_core/o2/xScript/Actions/UnitActions.js',
+        'o2web/source/o2_core/o2/xScript/Actions/ScriptActions.js',
+        'o2web/source/o2_core/o2/xScript/Actions/CMSScriptActions.js',
+        'o2web/source/o2_core/o2/xScript/Actions/PortalScriptActions.js',
+        'o2web/source/o2_core/o2/xScript/Environment.js',
+        'o2web/source/x_component_Template/MTooltips.js',
+        'o2web/source/x_component_Template/MSelector.js',
+
+        'o2web/source/o2_core/o2/xAction/services/x_organization_assemble_authentication.js',
+        'o2web/source/o2_core/o2/xAction/services/x_processplatform_assemble_surface.js',
+        'o2web/source/o2_core/o2/xAction/services/x_cms_assemble_control.js',
+        'o2web/source/o2_core/o2/xAction/services/x_organization_assemble_control.js',
+        'o2web/source/o2_core/o2/xAction/services/x_query_assemble_surface.js',
+        'o2web/source/o2_core/o2/xAction/services/x_organization_assemble_personal.js',
+
+        'o2web/source/x_desktop/js/base_work_actions_temp.js',
+
+        'o2web/source/x_desktop/js/base.js',
+        'o2web/source/x_desktop/js/base_loader.js'
+    ];
+    var dest = 'target/o2server/servers/webServer/x_desktop/js/';
+    return gulp.src(src)
+        .pipe(concat('base_work.js'))
+        .pipe(gulp.dest(dest))
+        .pipe(sourcemaps.init())
+        .pipe(concat('base_work.min.js'))
+        .pipe(uglify())
+        .pipe(sourcemaps.write("./"))
+        .pipe(gulp.dest(dest));
+}
+
+function build_concat_baseportal_style(){
+    return gulp.src([
+        "o2web/source/x_component_process_Work/$Main/default/css.wcss",
+        "o2web/source/x_component_portal_Portal/$Main/default/css.wcss",
+        "o2web/source/x_component_process_Xform/$Form/default/css.wcss",
+        "o2web/source/o2_core/o2/widget/$Tab/mobileForm/css.wcss",
+        "o2web/source/o2_core/o2/widget/$Menu/tab/css.wcss",
+    ])
+        .pipe(concat_Style())
+        .pipe(concat('js/base_portal_style_temp.js'))
+        .pipe(gulp.dest('o2web/source/x_desktop/'));
+}
+
+function build_concat_baseportal_action(){
+    return gulp.src([
+        "o2web/source/o2_core/o2/xAction/services/x_organization_assemble_authentication.json",
+        "o2web/source/o2_core/o2/xAction/services/x_portal_assemble_surface.json",
+        "o2web/source/o2_core/o2/xAction/services/x_processplatform_assemble_surface.json",
+        "o2web/source/o2_core/o2/xAction/services/x_organization_assemble_control.json",
+        "o2web/source/o2_core/o2/xAction/services/x_query_assemble_surface.json",
+        "o2web/source/o2_core/o2/xAction/services/x_cms_assemble_control.json",
+        "o2web/source/o2_core/o2/xAction/services/x_program_center.json",
+        "o2web/source/o2_core/o2/xAction/services/x_organization_assemble_personal.json"
+    ])
+        .pipe(concat_Actions())
+        .pipe(concat('js/base_portal_actions_temp.js'))
+        .pipe(gulp.dest('o2web/source/x_desktop/'));
+}
+
+function build_concat_baseportal_clean(cb) {
+    var dest = [
+        'o2web/source/x_desktop/js/base_portal_actions_temp.js',
+        'o2web/source/x_desktop/js/base_portal_style_temp.js'
+    ];
+    return del(dest, cb);
+}
+
+function build_concat_baseportal_body() {
+    var src = [
+        'o2web/source/x_desktop/js/base_concat_head.js',
+        //'o2web/source/o2_core/o2/lp/'+(options.lp || 'zh-cn')+'.js',
+
+        'o2web/source/x_desktop/js/base_portal_style_temp.js',
+
+        'o2web/source/o2_core/o2/widget/Common.js',
+        'o2web/source/o2_core/o2/widget/Dialog.js',
+        'o2web/source/o2_core/o2/widget/UUID.js',
+        'o2web/source/o2_core/o2/widget/Menu.js',
+        'o2web/source/o2_core/o2/widget/Toolbar.js',
+        'o2web/source/o2_core/o2/xDesktop/Common.js',
+        'o2web/source/o2_core/o2/xDesktop/Actions/RestActions.js',
+        'o2web/source/o2_core/o2/xAction/RestActions.js',
+        'o2web/source/o2_core/o2/xDesktop/Access.js',
+        'o2web/source/o2_core/o2/xDesktop/Dialog.js',
+        'o2web/source/o2_core/o2/xDesktop/Menu.js',
+        'o2web/source/o2_core/o2/xDesktop/UserData.js',
+        'o2web/source/x_component_Template/MPopupForm.js',
+        'o2web/source/o2_core/o2/xDesktop/Authentication.js',
+        'o2web/source/o2_core/o2/xDesktop/Window.js',
+
+        'o2web/source/x_component_Common/Main.js',
+
+        // 'o2web/source/x_component_process_Work/lp/'+(options.lp || 'zh-cn')+'.js',
+        // 'o2web/source/x_component_portal_Portal/lp/'+(options.lp || 'zh-cn')+'.js',
+        // 'o2web/source/x_component_process_Xform/lp/'+(options.lp || 'zh-cn')+'.js',
+        // 'o2web/source/x_component_Selector/lp/'+(options.lp || 'zh-cn')+'.js',
+
+        'o2web/source/x_component_portal_Portal/Main.js',
+
+        'o2web/source/x_component_Selector/package.js',
+        'o2web/source/x_component_Selector/Person.js',
+        'o2web/source/x_component_Selector/Identity.js',
+        'o2web/source/x_component_Selector/Unit.js',
+        'o2web/source/x_component_Selector/IdentityWidthDuty.js',
+        'o2web/source/x_component_Selector/IdentityWidthDutyCategoryByUnit.js',
+        'o2web/source/x_component_Selector/UnitWithType.js',
+
+        'o2web/source/o2_core/o2/xScript/Actions/UnitActions.js',
+        'o2web/source/o2_core/o2/xScript/Actions/ScriptActions.js',
+        'o2web/source/o2_core/o2/xScript/Actions/CMSScriptActions.js',
+        'o2web/source/o2_core/o2/xScript/Actions/PortalScriptActions.js',
+        'o2web/source/o2_core/o2/xScript/PageEnvironment.js',
+
+        'o2web/source/o2_core/o2/xAction/services/x_organization_assemble_authentication.js',
+        'o2web/source/o2_core/o2/xAction/services/x_processplatform_assemble_surface.js',
+        'o2web/source/o2_core/o2/xAction/services/x_cms_assemble_control.js',
+        'o2web/source/o2_core/o2/xAction/services/x_organization_assemble_control.js',
+        'o2web/source/o2_core/o2/xAction/services/x_query_assemble_surface.js',
+        'o2web/source/o2_core/o2/xAction/services/x_organization_assemble_personal.js',
+
+        'o2web/source/x_desktop/js/base_portal_actions_temp.js',
+
+        'o2web/source/x_desktop/js/base.js',
+        'o2web/source/x_desktop/js/base_loader.js'
+    ];
+    var dest = 'target/o2server/servers/webServer/x_desktop/js/';
+    return gulp.src(src)
+        .pipe(concat('base_portal.js'))
+        .pipe(gulp.dest(dest))
+        .pipe(sourcemaps.init())
+        .pipe(concat('base_portal.min.js'))
+        .pipe(uglify())
+        .pipe(sourcemaps.write('./'))
+        .pipe(gulp.dest(dest));
+}
+
+
+function build_concat_basedocument_style(){
+    return gulp.src([
+        "o2web/source/x_component_cms_Document/$Main/default/css.wcss",
+        "o2web/source/x_component_cms_Xform/$Form/default/css.wcss",
+        "o2web/source/o2_core/o2/widget/$AttachmentController/default/css.wcss"
+    ])
+        .pipe(concat_Style())
+        .pipe(concat('js/base_document_style_temp.js'))
+        .pipe(gulp.dest('o2web/source/x_desktop/'));
+}
+
+function build_concat_basedocument_action(){
+    return gulp.src([
+        "o2web/source/o2_core/o2/xAction/services/x_organization_assemble_authentication.json",
+        "o2web/source/o2_core/o2/xAction/services/x_organization_assemble_control.json",
+        "o2web/source/o2_core/o2/xAction/services/x_cms_assemble_control.json",
+        "o2web/source/o2_core/o2/xAction/services/x_program_center.json",
+        "o2web/source/o2_core/o2/xAction/services/x_organization_assemble_personal.json"
+    ])
+        .pipe(concat_Actions())
+        .pipe(concat('js/base_document_actions_temp.js'))
+        .pipe(gulp.dest('o2web/source/x_desktop/'));
+}
+
+function build_concat_basedocument_clean(cb) {
+    var dest = [
+        'o2web/source/x_desktop/js/base_document_actions_temp.js',
+        'o2web/source/x_desktop/js/base_document_style_temp.js'
+    ];
+    return del(dest, cb);
+}
+
+function build_concat_basedocument_body() {
+    var src = [
+        'o2web/source/x_desktop/js/base_concat_head.js',
+        //'o2web/source/o2_core/o2/lp/'+(options.lp || 'zh-cn')+'.js',
+
+        'o2web/source/x_desktop/js/base_document_style_temp.js',
+
+        'o2web/source/o2_core/o2/widget/Common.js',
+        'o2web/source/o2_core/o2/widget/Dialog.js',
+        'o2web/source/o2_core/o2/widget/UUID.js',
+        'o2web/source/o2_core/o2/widget/Menu.js',
+        'o2web/source/o2_core/o2/widget/Mask.js',
+        'o2web/source/o2_core/o2/xDesktop/Common.js',
+        'o2web/source/o2_core/o2/xDesktop/Actions/RestActions.js',
+        'o2web/source/o2_core/o2/xAction/RestActions.js',
+        'o2web/source/o2_core/o2/xDesktop/Access.js',
+        'o2web/source/o2_core/o2/xDesktop/Dialog.js',
+        'o2web/source/o2_core/o2/xDesktop/Menu.js',
+        'o2web/source/o2_core/o2/xDesktop/UserData.js',
+        'o2web/source/x_component_Template/MPopupForm.js',
+        'o2web/source/o2_core/o2/xDesktop/Authentication.js',
+        'o2web/source/o2_core/o2/xDesktop/Window.js',
+
+        'o2web/source/x_component_Common/Main.js',
+
+        // 'o2web/source/x_component_cms_Document/lp/'+(options.lp || 'zh-cn')+'.js',
+        // 'o2web/source/x_component_process_Xform/lp/'+(options.lp || 'zh-cn')+'.js',
+        // 'o2web/source/x_component_Selector/lp/'+(options.lp || 'zh-cn')+'.js',
+        // 'o2web/source/x_component_cms_Xform/lp/'+(options.lp || 'zh-cn')+'.js',
+
+        'o2web/source/x_component_cms_Document/Main.js',
+
+        'o2web/source/x_component_Selector/package.js',
+
+        'o2web/source/o2_core/o2/xScript/Actions/UnitActions.js',
+        'o2web/source/o2_core/o2/xScript/Actions/CMSScriptActions.js',
+        'o2web/source/o2_core/o2/xScript/CMSEnvironment.js',
+
+        'o2web/source/o2_core/o2/xAction/services/x_organization_assemble_authentication.js',
+        'o2web/source/o2_core/o2/xAction/services/x_cms_assemble_control.js',
+        'o2web/source/o2_core/o2/xAction/services/x_organization_assemble_control.js',
+        'o2web/source/o2_core/o2/xAction/services/x_organization_assemble_personal.js',
+
+        'o2web/source/x_desktop/js/base_document_actions_temp.js',
+
+        'o2web/source/x_desktop/js/base.js',
+        'o2web/source/x_desktop/js/base_loader.js'
+    ];
+    var dest = 'target/o2server/servers/webServer/x_desktop/js/';
+    return gulp.src(src)
+        .pipe(concat('base_document.js'))
+        .pipe(gulp.dest(dest))
+        .pipe(sourcemaps.init())
+        .pipe(concat('base_document.min.js'))
+        .pipe(uglify())
+        .pipe(sourcemaps.write('./'))
+        .pipe(gulp.dest(dest));
+}
+
+function getGitV(){
+    var tagPromise = new Promise(function(s, f){
+        git.exec({args : 'describe --tag'}, function (err, stdout) {
+            if (err){
+                f(err);
+            }
+            var v = stdout.substring(0, stdout.lastIndexOf("-"));
+            s(v);
+        });
+    });
+    var revPromise = new Promise(function(s, f){
+        git.exec({args : 'rev-parse --short HEAD'}, function (err, hash) {
+            if (err){
+                f(err);
+            }
+            s(hash.trim());
+        });
+    });
+    return Promise.all([tagPromise,revPromise])
+}
+
+function build_web_v_html() {
+    var src = 'o2web/source/x_desktop/*.html';
+    var dest = 'target/o2server/servers/webServer/x_desktop/';
+
+    return getGitV().then(function(arr){
+        return gulp.src(src)
+            .pipe(assetRev({"verConnecter": arr[0], "md5": arr[1]}))
+            .pipe(gulp.dest(dest))
+            .pipe(gutil.noop());
+    }, function(){
+        return gulp.src(src)
+            .pipe(assetRev())
+            .pipe(gulp.dest(dest))
+            .pipe(gutil.noop());
+    });
+}
+function build_web_api() {
+    var src = 'o2web/api/**/*';
+    var dest = 'target/o2server/servers/webServer/api/';
+    return gulp.src(src)
+        .pipe(gulp.dest(dest))
+}
+
+function build_doc(){
+    return getGitV().then(function(arr){
+        return (shell.task('jsdoc -c o2web/jsdoc.conf.json -q version='+arr[0]+'-'+arr[1]+''))();
+    }, function(){
+        return (shell.task('jsdoc -c o2web/jsdoc.conf.json -q version='))();
+    });
+}
+exports.build_api = gulp.series(build_doc, build_web_api);
+
+
+function build_web_v_o2() {
+    var src = [
+        'target/o2server/servers/webServer/o2_core/o2.js',
+        'target/o2server/servers/webServer/o2_core/o2.min.js'
+    ];
+    var dest = 'target/o2server/servers/webServer/o2_core/';
+
+    return getGitV().then(function(arr){
+        debugger;
+        return gulp.src(src)
+            .pipe(assetRev({"verConnecter": arr[0], "md5": arr[1], "replace": true}))
+            .pipe(gulp.dest(dest))
+            .pipe(gutil.noop());
+    }, function(){
+        return gulp.src(src)
+            .pipe(assetRev())
+            .pipe(gutil.noop());
+    });
+}
+exports.build_version = gulp.parallel(build_web_v_o2, build_web_v_html);
+
+async function clear_build(cb) {
+    console.log(`---------------------------------------------------------------------
+  . clear old build ...
+---------------------------------------------------------------------`);
+    var dest = 'target';
+    await del(dest, {force: true});
+    cb();
+}
+async function clear_deploy(cb) {
+    console.log(`---------------------------------------------------------------------
+  . clear old deploy ...
+---------------------------------------------------------------------`);
+    var dest = ["target/o2server/store/", "target/o2server/commons/", "target/o2server/jvm/", "target/o2server/configSample/", "target/o2server/localSample/", "target/o2server/servers/"];
+    dest = dest.concat(["target/o2server/*.sh", "target/o2server/*.jar", "target/o2server/*.html", "target/o2server/*.bat", "target/o2server/version.o2"]);
+    await del(dest, {force: true});
+    cb();
+}
+exports.clear_build = clear_build;
+exports.clear_deploy = clear_deploy;
+
+
+
+function deploy_server(){
+    console.log(`---------------------------------------------------------------------
+  . deploy to target ...
+---------------------------------------------------------------------`);
+    var source = ["o2server/*store/**/*", "o2server/*commons/**/*", "o2server/*jvm/**/*", "o2server/*configSample/**/*", "o2server/*localSample/**/*"];
+    source = source.concat(scriptSource);
+    console.log(source)
+    var dest = "target/o2server/"
+
+    var entries = fg.sync(source, { dot: false});
+    var size = entries.length;
+    var pb = new ProgressBar('', 50);
+    var doCount = 0;
+
+    var stream = gulp.src(source);
+
+    return stream.pipe(gulp.dest(dest))
+        .pipe(logger(function(){
+            doCount++;
+            if (doCount <= size) {pb.render({ completed: doCount, total: size, count: doCount})};
+        }));
+}
+
+exports.preperation =  gulp.series(download_commons_and_jvm, decompress_commons_and_jvm, move_commons, move_jvm, clear_commons_git, clear_jvm_git);
+
+var shell = require('gulp-shell')
+const shelljs = require("shelljs");
+exports.build_server = function(){
+    console.log(`---------------------------------------------------------------------
+  . Start compiling the server ...
+---------------------------------------------------------------------`);
+    return (shell.task('npm run build_server_script'))();
+};
+function chmod_jvm(){
+    return (shell.task('chmod 777 -R target/o2server/jvm'))();
+}
+function chmod_commons(){
+    return (shell.task('chmod 777 -R target/o2server/commons'))();
+}
+function chmod_sh(){
+    return (shell.task('chmod 777 target/o2server/*.sh'))();
+}
+function chmod_servers(){
+    return (shell.task('chmod 777 -R target/o2server/servers'))();
+}
+exports.build_web = gulp.series(
+    build_web_language_pack,
+    build_web_module,
+    build_web_minimize,
+    build_web_move,
+    gulp.parallel(
+        build_concat_o2,
+        build_concat_base,
+        build_concat_desktop,
+        build_concat_xform,
+        build_concat_cms_xform,
+        build_concat_lp,
+        gulp.series(build_concat_basework_style, build_concat_basework_action, build_concat_basework_body,build_concat_basework_clean),
+        gulp.series(build_concat_baseportal_style, build_concat_baseportal_action, build_concat_baseportal_body,build_concat_baseportal_clean),
+        gulp.series(build_concat_basedocument_style, build_concat_basedocument_action, build_concat_basedocument_body,build_concat_basedocument_clean),
+        build_bundle
+    ),
+    build_web_v_html,
+    build_web_v_o2,
+);
+
+if (os.platform().indexOf("win")==-1){
+    exports.deploy = gulp.series(deploy_server, chmod_jvm, chmod_commons, chmod_sh, chmod_servers);
+}else{
+    exports.deploy = gulp.series(deploy_server);
+}
+
+function createHistoryJsonFile(url, fileName, host){
+    const fp = fs.promises;
+    return new Promise(function(resolve){
+        request.get({'url': url}, function(error, response, body) {
+            if (!error && response.statusCode == 200) {
+                const historyJsons = JSON.parse(body);
+                fp.readFile(path.resolve(process.cwd(), 'download-pro.json'), 'utf8').then(function(str){
+                    const downloadJson = JSON.parse(str);
+                    downloadJson.windows.url = host+downloadJson.windows.url;
+                    downloadJson.linux.url = host+downloadJson.linux.url
+                    downloadJson.macosx64.url = host+downloadJson.macosx64.url
+                    downloadJson.macosarm.url = host+downloadJson.macosarm.url
+                    // downloadJson.aix.url = host+downloadJson.aix.url
+                    downloadJson.raspi.url = host+downloadJson.raspi.url
+                    downloadJson.mips.url = host+downloadJson.mips.url
+                    downloadJson.arm.url = host+downloadJson.arm.url
+                    downloadJson.sw.url = host+downloadJson.sw.url;
+                    let append = true;
+                    for (var i=0; i<historyJsons.length; i++){
+                        var o = historyJsons[i];
+                        if (o.title == downloadJson.title){
+                            historyJsons.splice(i, 1, downloadJson)
+                            append = false;
+                            break;
+                        }
+                    }
+                    if (append){
+                        historyJsons.unshift(downloadJson);
+                    }
+                    const jsonStr = JSON.stringify(historyJsons, null, '\t');
+                    fp.writeFile(path.resolve(process.cwd(), fileName), jsonStr).then(()=>{resolve();});
+                });
+            }
+        });
+    });
+}
+
+async function createHistroyJson(cb) {
+    const host = options.webSite;
+    const mirrorHost = options.mirrorSite;
+    const downloadHost = options.downloadSite
+
+    if (host) {
+        const url = host + "/website/history.json?t=" + (new Date()).getTime();
+        const mirrorUrl = mirrorHost + "/download/download-history.json?t=" + (new Date()).getTime();
+
+        var doneWebSite = false;
+        var doneMirror = false;
+        var check = function () {
+            if (doneWebSite && doneMirror) cb();
+        };
+        await createHistoryJsonFile(url, 'history.json', downloadHost);
+        await createHistoryJsonFile(mirrorUrl, 'download-history.json', mirrorHost);
+    }
+    cb();
+}
+exports.build_historyJson = createHistroyJson;

+ 14 - 0
o2android/README.md

@@ -0,0 +1,14 @@
+# O2OA Android 源码
+
+
+
+Android端源码已经迁移,目前这里不再更新。
+
+
+
+新地址:
+
+Gitee -> [https://gitee.com/o2oa/o2oa-android](https://gitee.com/o2oa/o2oa-android)
+
+Github -> [https://github.com/o2oa/o2oa-android](https://github.com/o2oa/o2oa-android)
+

+ 14 - 0
o2ios/README.md

@@ -0,0 +1,14 @@
+# O2OA iOS 源码
+
+
+
+iOS端源码已经迁移,目前这里不再更新。
+
+
+
+新地址:
+
+Gitee -> [https://gitee.com/o2oa/o2oa-ios](https://gitee.com/o2oa/o2oa-ios)
+
+Github -> [https://github.com/o2oa/o2oa-ios](https://github.com/o2oa/o2oa-ios)
+

+ 3 - 0
o2server/.gitignore

@@ -0,0 +1,3 @@
+/graalvm.sh
+/test.sh
+/webroot

+ 1 - 0
o2server/console_arm.sh

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

+ 1 - 0
o2server/console_linux.sh

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

+ 1 - 0
o2server/console_macosarm.sh

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

+ 1 - 0
o2server/console_macosx64.sh

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

+ 1 - 0
o2server/console_mips.sh

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

+ 1 - 0
o2server/console_raspi.sh

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

+ 1 - 0
o2server/console_sw.sh

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

+ 1 - 0
o2server/console_windows.bat

@@ -0,0 +1 @@
+"%~dp0jvm\windows_java11\bin\java" -javaagent:"%~dp0console.jar=shadow" -cp "%~dp0console.jar" com.x.server.console.Shadow

+ 1 - 0
o2server/index.html

@@ -0,0 +1 @@
+<!DOCTYPE html><html><head><meta charset="UTF-8"><title>o2 index</title></head><body><a href="http://127.0.0.1">http://127.0.0.1</a><br/></body></html>

+ 1 - 0
o2server/localSample/node.cfg

@@ -0,0 +1 @@
+127.0.0.1

+ 1326 - 0
o2server/pom.xml

@@ -0,0 +1,1326 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>net.o2oa</groupId>
+	<artifactId>o2server</artifactId>
+	<version>9.0</version>
+	<packaging>pom</packaging>
+	<description>O2OA企业应用开发平台 OpenSource OA Platform</description>
+	<url>https://github.com/o2oa/o2oa</url>
+	<licenses>
+		<license>
+			<name>GNU Affero General Public License v3.0</name>
+			<url>https://www.gnu.org/licenses/agpl-3.0.html</url>
+			<distribution>repo</distribution>
+		</license>
+	</licenses>
+	<developers>
+		<developer>
+			<name>o2oa</name>
+			<email>admin@o2oa.net</email>
+			<organization>o2oa.net</organization>
+		</developer>
+	</developers>
+	<scm>
+		<tag>o2oa</tag>
+		<url>https://github.com/o2oa/o2oa</url>
+		<connection>scm:git:https://github.com/o2oa/o2oa.git</connection>
+		<developerConnection>scm:git:https://github.com/o2oa/o2oa.git</developerConnection>
+	</scm>
+	<properties>
+		<maven.compiler.source>11</maven.compiler.source>
+		<maven.compiler.target>11</maven.compiler.target>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<project.reporting.outputEncoding>UTF-8
+		</project.reporting.outputEncoding>
+		<graalvm.version>22.3.4</graalvm.version>
+	</properties>
+	<modules>
+		<module>x_base_core_project</module>
+		<!-- 顺序 -->
+		<module>x_query_core_entity</module>
+		<module>x_attendance_core_entity</module>
+		<module>x_bbs_core_entity</module>
+		<module>x_calendar_core_entity</module>
+		<module>x_cms_core_entity</module>
+		<module>x_component_core_entity</module>
+		<module>x_file_core_entity</module>
+		<module>x_general_core_entity</module>
+		<module>x_hotpic_core_entity</module>
+		<module>x_jpush_core_entity</module>
+		<module>x_meeting_core_entity</module>
+		<module>x_message_core_entity</module>
+		<module>x_mind_core_entity</module>
+		<module>x_organization_core_entity</module>
+		<module>x_portal_core_entity</module>
+		<module>x_processplatform_core_entity</module>
+		<module>x_correlation_core_entity</module>
+		<module>x_program_center_core_entity</module>
+		<module>x_organization_core_express</module>
+		<module>x_query_core_express</module>
+		<module>x_processplatform_core_express</module>
+		<module>x_correlation_core_express</module>
+		<module>x_bbs_assemble_control</module>
+		<module>x_calendar_assemble_control</module>
+		<module>x_cms_core_express</module>
+		<module>x_cms_assemble_control</module>
+		<module>x_attendance_assemble_control</module>
+		<module>x_component_assemble_control</module>
+		<module>x_file_assemble_control</module>
+		<module>x_general_assemble_control</module>
+		<module>x_hotpic_assemble_control</module>
+		<module>x_jpush_assemble_control</module>
+		<module>x_meeting_assemble_control</module>
+		<module>x_message_assemble_communicate</module>
+		<module>x_mind_assemble_control</module>
+		<module>x_organization_assemble_authentication</module>
+		<module>x_organization_assemble_control</module>
+		<module>x_organization_assemble_express</module>
+		<module>x_organization_assemble_personal</module>
+		<module>x_portal_assemble_designer</module>
+		<module>x_portal_assemble_surface</module>
+		<module>x_processplatform_assemble_bam</module>
+		<module>x_processplatform_assemble_designer</module>
+		<module>x_processplatform_assemble_surface</module>
+		<module>x_processplatform_service_processing</module>
+		<module>x_correlation_service_processing</module>
+		<module>x_query_assemble_designer</module>
+		<module>x_query_assemble_surface</module>
+		<module>x_query_service_processing</module>
+		<module>x_program_center</module>
+		<module>x_program_init</module>
+		<module>x_console</module>
+	</modules>
+	<dependencies>
+		<dependency>
+			<groupId>javax</groupId>
+			<artifactId>javaee-api</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.google.code.gson</groupId>
+			<artifactId>gson</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.openjpa</groupId>
+			<artifactId>openjpa</artifactId>
+			<exclusions>
+				<exclusion>
+					<groupId>org.apache.geronimo.specs</groupId>
+					<artifactId>geronimo-jms_1.1_spec</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-lang3</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>commons-cli</groupId>
+			<artifactId>commons-cli</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>commons-beanutils</groupId>
+			<artifactId>commons-beanutils</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>commons-net</groupId>
+			<artifactId>commons-net</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-math3</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-collections4</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>commons-codec</groupId>
+			<artifactId>commons-codec</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>commons-io</groupId>
+			<artifactId>commons-io</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-vfs2-jackrabbit2</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-pool2</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-text</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-compress</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.eclipse.jetty</groupId>
+			<artifactId>jetty-server</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.eclipse.jetty</groupId>
+			<artifactId>jetty-deploy</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.eclipse.jetty</groupId>
+			<artifactId>jetty-annotations</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.eclipse.jetty</groupId>
+			<artifactId>jetty-quickstart</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.eclipse.jetty</groupId>
+			<artifactId>jetty-proxy</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.google.zxing</groupId>
+			<artifactId>core</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.ftpserver</groupId>
+			<artifactId>ftpserver-core</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.ftpserver</groupId>
+			<artifactId>ftplet-api</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.cxf</groupId>
+			<artifactId>cxf-core</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.cxf</groupId>
+			<artifactId>cxf-rt-frontend-jaxws</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.cxf</groupId>
+			<artifactId>cxf-rt-frontend-simple</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.cxf</groupId>
+			<artifactId>cxf-rt-wsdl</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.poi</groupId>
+			<artifactId>poi</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.poi</groupId>
+			<artifactId>poi-ooxml</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.poi</groupId>
+			<artifactId>poi-scratchpad</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>fr.opensagres.xdocreport</groupId>
+			<artifactId>fr.opensagres.poi.xwpf.converter.xhtml</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>fr.opensagres.xdocreport</groupId>
+			<artifactId>xdocreport</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.tika</groupId>
+			<artifactId>tika-core</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.maven</groupId>
+			<artifactId>maven-model</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>io.github.classgraph</groupId>
+			<artifactId>classgraph</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.glassfish.jersey.core</groupId>
+			<artifactId>jersey-server</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.glassfish.jersey.media</groupId>
+			<artifactId>jersey-media-multipart</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.dom4j</groupId>
+			<artifactId>dom4j</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>net.sf.ehcache</groupId>
+			<artifactId>ehcache</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.quartz-scheduler</groupId>
+			<artifactId>quartz</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.imgscalr</groupId>
+			<artifactId>imgscalr-lib</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.github.stuxuhai</groupId>
+			<artifactId>jpinyin</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.hankcs</groupId>
+			<artifactId>hanlp</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.hankcs.nlp</groupId>
+			<artifactId>hanlp-lucene-plugin</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>de.vandermeer</groupId>
+			<artifactId>asciitable</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.h2database</groupId>
+			<artifactId>h2</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.mnode.ical4j</groupId>
+			<artifactId>ical4j</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.xbean</groupId>
+			<artifactId>xbean-asm8-shaded</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.squareup</groupId>
+			<artifactId>javapoet</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.github.neuroph</groupId>
+			<artifactId>neuroph-core</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.github.neuroph</groupId>
+			<artifactId>neuroph-imgrec</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.github.neuroph</groupId>
+			<artifactId>neuroph-ocr</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.github.neuroph</groupId>
+			<artifactId>neuroph-contrib</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>net.sourceforge.tess4j</groupId>
+			<artifactId>tess4j</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>druid</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.itextpdf</groupId>
+			<artifactId>html2pdf</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.itextpdf</groupId>
+			<artifactId>font-asian</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>redis.clients</groupId>
+			<artifactId>jedis</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.sun.mail</groupId>
+			<artifactId>javax.mail</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.commons</groupId>
+			<artifactId>commons-email</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>commons-fileupload</groupId>
+			<artifactId>commons-fileupload</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>javax.activation</groupId>
+			<artifactId>activation</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>net.lingala.zip4j</groupId>
+			<artifactId>zip4j</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.github.xuwei-k</groupId>
+			<artifactId>html2image</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.microsoft.playwright</groupId>
+			<artifactId>playwright</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>javax.cache</groupId>
+			<artifactId>cache-api</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.jsr107.ri</groupId>
+			<artifactId>cache-ri-impl</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.github.penggle</groupId>
+			<artifactId>kaptcha</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.hadoop</groupId>
+			<artifactId>hadoop-client</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.logging.log4j</groupId>
+			<artifactId>log4j-api</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.logging.log4j</groupId>
+			<artifactId>log4j-core</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>io.swagger.core.v3</groupId>
+			<artifactId>swagger-annotations</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>io.swagger.core.v3</groupId>
+			<artifactId>swagger-core</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>io.swagger.core.v3</groupId>
+			<artifactId>swagger-jaxrs2</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.eclipse.jgit</groupId>
+			<artifactId>org.eclipse.jgit</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.ldaptive</groupId>
+			<artifactId>ldaptive-jldap</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.jsoup</groupId>
+			<artifactId>jsoup</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.lucene</groupId>
+			<artifactId>lucene-core</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.lucene</groupId>
+			<artifactId>lucene-queryparser</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.lucene</groupId>
+			<artifactId>lucene-highlighter</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.lucene</groupId>
+			<artifactId>lucene-grouping</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.lucene</groupId>
+			<artifactId>lucene-luke</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.solr</groupId>
+			<artifactId>solr-hdfs</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.solr</groupId>
+			<artifactId>solr-solrj</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.github.jsqlparser</groupId>
+			<artifactId>jsqlparser</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.junit.jupiter</groupId>
+			<artifactId>junit-jupiter-api</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.junit.jupiter</groupId>
+			<artifactId>junit-jupiter-engine</artifactId>
+		</dependency>
+			<dependency>
+				<groupId>org.graalvm.sdk</groupId>
+				<artifactId>graal-sdk</artifactId>
+			</dependency>
+			<dependency>
+				<groupId>org.graalvm.js</groupId>
+				<artifactId>js</artifactId>
+			</dependency>
+			<dependency>
+				<groupId>org.graalvm.js</groupId>
+				<artifactId>js-scriptengine</artifactId>
+			</dependency>
+			<dependency>
+				<groupId>org.graalvm.tools</groupId>
+				<artifactId>profiler</artifactId>
+			</dependency>
+			<dependency>
+				<groupId>org.graalvm.tools</groupId>
+				<artifactId>chromeinspector</artifactId>
+			</dependency>
+	</dependencies>
+	<build>
+		<finalName>${project.artifactId}</finalName>
+		<pluginManagement>
+			<plugins>
+				<plugin>
+					<artifactId>maven-clean-plugin</artifactId>
+					<version>3.1.0</version>
+					<configuration>
+						<filesets>
+							<fileset>
+								<directory>${basedir}</directory>
+								<includes>
+									<include>**/*_.java</include>
+								</includes>
+							</fileset>
+						</filesets>
+					</configuration>
+				</plugin>
+				<plugin>
+					<groupId>org.codehaus.mojo</groupId>
+					<artifactId>exec-maven-plugin</artifactId>
+					<version>1.6.0</version>
+				</plugin>
+				<plugin>
+					<groupId>org.apache.maven.plugins</groupId>
+					<artifactId>maven-resources-plugin</artifactId>
+					<version>3.1.0</version>
+				</plugin>
+				<plugin>
+					<groupId>org.apache.maven.plugins</groupId>
+					<artifactId>maven-compiler-plugin</artifactId>
+					<version>3.8.1</version>
+					<configuration>
+						<target>11</target>
+					</configuration>
+				</plugin>
+				<plugin>
+					<groupId>org.eclipse.m2e</groupId>
+					<artifactId>lifecycle-mapping</artifactId>
+					<version>1.0.0</version>
+					<configuration>
+						<lifecycleMappingMetadata>
+							<pluginExecutions>
+								<pluginExecution>
+									<pluginExecutionFilter>
+										<groupId>
+											org.codehaus.mojo
+										</groupId>
+										<artifactId>
+											exec-maven-plugin
+										</artifactId>
+										<versionRange>
+											[1.6.0,)
+										</versionRange>
+										<goals>
+											<goal>java</goal>
+										</goals>
+									</pluginExecutionFilter>
+									<action>
+										<ignore></ignore>
+									</action>
+								</pluginExecution>
+							</pluginExecutions>
+						</lifecycleMappingMetadata>
+					</configuration>
+				</plugin>
+				<!--				源码插件-->
+				<plugin>
+					<groupId>org.apache.maven.plugins</groupId>
+					<artifactId>maven-source-plugin</artifactId>
+					<version>3.1.0</version>
+					<executions>
+						<execution>
+							<phase>package</phase>
+							<goals>
+								<goal>jar-no-fork</goal>
+							</goals>
+						</execution>
+					</executions>
+				</plugin>
+				<!--				文档插件-->
+				<plugin>
+					<groupId>org.apache.maven.plugins</groupId>
+					<artifactId>maven-javadoc-plugin</artifactId>
+					<version>3.2.0</version>
+					<configuration>
+						<show>private</show>
+						<nohelp>true</nohelp>
+						<charset>UTF-8</charset>
+						<encoding>UTF-8</encoding>
+						<docencoding>UTF-8</docencoding>
+						<additionalOptions>
+							<!-- 解决jdk1.8以后编译出错问题-->
+							<!--
+							<additionalOption>-Xdoclint:none</additionalOption>-->
+							<additionalparam>-Xdoclint:none</additionalparam>
+						</additionalOptions>
+					</configuration>
+					<executions>
+						<execution>
+							<phase>compile</phase>
+							<goals>
+								<goal>jar</goal>
+							</goals>
+						</execution>
+					</executions>
+				</plugin>
+				<!--				gpg插件 -->
+				<plugin>
+					<groupId>org.apache.maven.plugins</groupId>
+					<artifactId>maven-gpg-plugin</artifactId>
+					<version>1.6</version>
+					<executions>
+						<execution>
+							<id>sign-artifacts</id>
+							<phase>verify</phase>
+							<goals>
+								<goal>sign</goal>
+							</goals>
+						</execution>
+					</executions>
+				</plugin>
+			</plugins>
+		</pluginManagement>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-surefire-plugin</artifactId>
+				<version>3.0.0-M3</version>
+				<configuration>
+					<skipTests>true</skipTests>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-war-plugin</artifactId>
+				<version>3.2.3</version>
+				<configuration>
+					<archive>
+						<addMavenDescriptor>false</addMavenDescriptor>
+					</archive>
+					<packagingExcludes>persistence.xml,WEB-INF/lib/**,META-INF
+					</packagingExcludes>
+					<failOnMissingWebXml>false</failOnMissingWebXml>
+				</configuration>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<version>3.2.0</version>
+				<configuration>
+					<archive>
+						<addMavenDescriptor>false</addMavenDescriptor>
+					</archive>
+					<excludes>
+						<exclude>META-INF/**</exclude>
+					</excludes>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+	<dependencyManagement>
+		<dependencies>
+			<dependency>
+				<groupId>javax</groupId>
+				<artifactId>javaee-api</artifactId>
+				<version>7.0</version>
+			</dependency>
+			<dependency>
+				<groupId>com.google.code.gson</groupId>
+				<artifactId>gson</artifactId>
+				<version>2.8.9</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.openjpa</groupId>
+				<artifactId>openjpa</artifactId>
+				<version>3.2.2</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.commons</groupId>
+				<artifactId>commons-lang3</artifactId>
+				<version>3.12.0</version>
+			</dependency>
+			<dependency>
+				<groupId>commons-cli</groupId>
+				<artifactId>commons-cli</artifactId>
+				<version>1.4</version>
+			</dependency>
+			<dependency>
+				<groupId>commons-beanutils</groupId>
+				<artifactId>commons-beanutils</artifactId>
+				<version>1.9.4</version>
+			</dependency>
+			<dependency>
+				<groupId>commons-net</groupId>
+				<artifactId>commons-net</artifactId>
+				<version>3.6</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.commons</groupId>
+				<artifactId>commons-math3</artifactId>
+				<version>3.6.1</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.commons</groupId>
+				<artifactId>commons-collections4</artifactId>
+				<version>4.4</version>
+			</dependency>
+			<dependency>
+				<groupId>commons-codec</groupId>
+				<artifactId>commons-codec</artifactId>
+				<version>1.13</version>
+			</dependency>
+			<dependency>
+				<groupId>commons-io</groupId>
+				<artifactId>commons-io</artifactId>
+				<version>2.7</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.commons</groupId>
+				<artifactId>commons-vfs2-jackrabbit2</artifactId>
+				<version>2.9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.commons</groupId>
+				<artifactId>commons-pool2</artifactId>
+				<version>2.9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.commons</groupId>
+				<artifactId>commons-text</artifactId>
+				<version>1.10.0</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.maven</groupId>
+				<artifactId>maven-model</artifactId>
+				<version>3.6.3</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.commons</groupId>
+				<artifactId>commons-compress</artifactId>
+				<version>1.20</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.commons</groupId>
+				<artifactId>commons-configuration2</artifactId>
+				<version>2.9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>commons-fileupload</groupId>
+				<artifactId>commons-fileupload</artifactId>
+				<version>1.5</version>
+			</dependency>
+			<dependency>
+				<groupId>org.eclipse.jetty</groupId>
+				<artifactId>jetty-server</artifactId>
+				<version>9.4.33.v20201020</version>
+			</dependency>
+			<dependency>
+				<groupId>org.eclipse.jetty</groupId>
+				<artifactId>jetty-deploy</artifactId>
+				<version>9.4.33.v20201020</version>
+			</dependency>
+			<dependency>
+				<groupId>org.eclipse.jetty</groupId>
+				<artifactId>jetty-annotations</artifactId>
+				<version>9.4.33.v20201020</version>
+			</dependency>
+			<dependency>
+				<groupId>org.eclipse.jetty</groupId>
+				<artifactId>jetty-quickstart</artifactId>
+				<version>9.4.33.v20201020</version>
+			</dependency>
+			<dependency>
+				<groupId>org.eclipse.jetty</groupId>
+				<artifactId>jetty-proxy</artifactId>
+				<version>9.4.33.v20201020</version>
+			</dependency>
+			<dependency>
+				<groupId>com.google.zxing</groupId>
+				<artifactId>core</artifactId>
+				<version>3.4.0</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.ftpserver</groupId>
+				<artifactId>ftpserver-core</artifactId>
+				<version>1.2.0</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.ftpserver</groupId>
+				<artifactId>ftplet-api</artifactId>
+				<version>1.2.0</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.cxf</groupId>
+				<artifactId>cxf-core</artifactId>
+				<version>3.6.4</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.cxf</groupId>
+				<artifactId>cxf-rt-frontend-jaxws</artifactId>
+				<version>3.6.4</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.cxf</groupId>
+				<artifactId>cxf-rt-frontend-simple</artifactId>
+				<version>3.6.4</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.cxf</groupId>
+				<artifactId>cxf-rt-wsdl</artifactId>
+				<version>3.6.4</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.cxf</groupId>
+				<artifactId>cxf-rt-databinding-jaxb</artifactId>
+				<version>3.6.4</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.cxf</groupId>
+				<artifactId>cxf-rt-transports-http</artifactId>
+				<version>3.6.4</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.cxf</groupId>
+				<artifactId>cxf-rt-bindings-soap</artifactId>
+				<version>3.6.4</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.cxf</groupId>
+				<artifactId>cxf-rt-bindings-xml</artifactId>
+				<version>3.6.4</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.cxf</groupId>
+				<artifactId>cxf-rt-ws-addr</artifactId>
+				<version>3.6.4</version>
+			</dependency>
+			<dependency>
+				<groupId>com.fasterxml.woodstox</groupId>
+				<artifactId>woodstox-core</artifactId>
+				<version>6.2.1</version>
+			</dependency>
+			<dependency>
+				<groupId>org.codehaus.woodstox</groupId>
+				<artifactId>stax2-api</artifactId>
+				<version>4.2.1</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.neethi</groupId>
+				<artifactId>neethi</artifactId>
+				<version>3.1.1</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.poi</groupId>
+				<artifactId>poi</artifactId>
+				<version>5.2.3</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.poi</groupId>
+				<artifactId>poi-ooxml</artifactId>
+				<version>5.2.3</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.poi</groupId>
+				<artifactId>poi-scratchpad</artifactId>
+				<version>5.2.3</version>
+			</dependency>
+			<dependency>
+				<groupId>fr.opensagres.xdocreport</groupId>
+				<artifactId>fr.opensagres.poi.xwpf.converter.xhtml</artifactId>
+				<version>2.0.2</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.tika</groupId>
+				<artifactId>tika-core</artifactId>
+				<version>2.5.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.sourceforge.tess4j</groupId>
+				<artifactId>tess4j</artifactId>
+				<version>5.2.0</version>
+			</dependency>
+			<dependency>
+				<groupId>io.github.classgraph</groupId>
+				<artifactId>classgraph</artifactId>
+				<version>4.8.89</version>
+			</dependency>
+			<dependency>
+				<groupId>org.glassfish.jersey.core</groupId>
+				<artifactId>jersey-server</artifactId>
+				<version>2.35</version>
+			</dependency>
+			<dependency>
+				<groupId>fr.opensagres.xdocreport</groupId>
+				<artifactId>xdocreport</artifactId>
+				<version>2.0.2</version>
+			</dependency>
+			<dependency>
+				<groupId>org.glassfish.jersey.media</groupId>
+				<artifactId>jersey-media-multipart</artifactId>
+				<version>2.35</version>
+			</dependency>
+			<dependency>
+				<groupId>org.dom4j</groupId>
+				<artifactId>dom4j</artifactId>
+				<version>2.1.3</version>
+			</dependency>
+			<dependency>
+				<groupId>net.sf.ehcache</groupId>
+				<artifactId>ehcache</artifactId>
+				<version>2.10.5</version>
+			</dependency>
+			<dependency>
+				<groupId>org.quartz-scheduler</groupId>
+				<artifactId>quartz</artifactId>
+				<version>2.3.1</version>
+			</dependency>
+			<dependency>
+				<groupId>org.imgscalr</groupId>
+				<artifactId>imgscalr-lib</artifactId>
+				<version>4.2</version>
+			</dependency>
+			<dependency>
+				<groupId>com.github.stuxuhai</groupId>
+				<artifactId>jpinyin</artifactId>
+				<version>1.1.8</version>
+			</dependency>
+			<dependency>
+				<groupId>com.hankcs</groupId>
+				<artifactId>hanlp</artifactId>
+				<version>portable-1.8.3</version>
+			</dependency>
+			<dependency>
+				<groupId>com.hankcs.nlp</groupId>
+				<artifactId>hanlp-lucene-plugin</artifactId>
+				<version>1.1.7</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.lucene</groupId>
+				<artifactId>lucene-core</artifactId>
+				<version>9.4.1</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.lucene</groupId>
+				<artifactId>lucene-queryparser</artifactId>
+				<version>9.4.2</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.lucene</groupId>
+				<artifactId>lucene-highlighter</artifactId>
+				<version>9.4.2</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.lucene</groupId>
+				<artifactId>lucene-grouping</artifactId>
+				<version>9.4.2</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.lucene</groupId>
+				<artifactId>lucene-luke</artifactId>
+				<version>9.4.1</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.solr</groupId>
+				<artifactId>solr-hdfs</artifactId>
+				<version>9.2.1</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.solr</groupId>
+				<artifactId>solr-solrj</artifactId>
+				<version>9.2.1</version>
+			</dependency>
+			<dependency>
+				<groupId>de.vandermeer</groupId>
+				<artifactId>asciitable</artifactId>
+				<version>0.3.2</version>
+			</dependency>
+			<dependency>
+				<groupId>com.h2database</groupId>
+				<artifactId>h2</artifactId>
+				<version>1.4.200</version>
+			</dependency>
+			<dependency>
+				<groupId>org.mnode.ical4j</groupId>
+				<artifactId>ical4j</artifactId>
+				<version>3.0.9</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.xbean</groupId>
+				<artifactId>xbean-asm8-shaded</artifactId>
+				<version>4.17</version>
+			</dependency>
+			<dependency>
+				<groupId>com.squareup</groupId>
+				<artifactId>javapoet</artifactId>
+				<version>1.11.1</version>
+			</dependency>
+			<dependency>
+				<groupId>com.github.neuroph</groupId>
+				<artifactId>neuroph-core</artifactId>
+				<version>2.98</version>
+			</dependency>
+			<dependency>
+				<groupId>com.github.neuroph</groupId>
+				<artifactId>neuroph-imgrec</artifactId>
+				<version>2.98</version>
+			</dependency>
+			<dependency>
+				<groupId>com.github.neuroph</groupId>
+				<artifactId>neuroph-ocr</artifactId>
+				<version>2.98</version>
+			</dependency>
+			<dependency>
+				<groupId>com.github.neuroph</groupId>
+				<artifactId>neuroph-contrib</artifactId>
+				<version>2.98</version>
+			</dependency>
+			<dependency>
+				<groupId>com.alibaba</groupId>
+				<artifactId>druid</artifactId>
+				<version>1.2.8</version>
+				<exclusions>
+					<exclusion>
+						<groupId>com.sun</groupId>
+						<artifactId>tools</artifactId>
+					</exclusion>
+					<exclusion>
+						<groupId>com.sun</groupId>
+						<artifactId>jconsole</artifactId>
+					</exclusion>
+				</exclusions>
+			</dependency>
+			<dependency>
+				<groupId>com.itextpdf</groupId>
+				<artifactId>html2pdf</artifactId>
+				<version>2.1.6</version>
+			</dependency>
+			<dependency>
+				<groupId>com.itextpdf</groupId>
+				<artifactId>font-asian</artifactId>
+				<version>7.1.9</version>
+			</dependency>
+			<dependency>
+				<groupId>redis.clients</groupId>
+				<artifactId>jedis</artifactId>
+				<version>3.3.0</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.commons</groupId>
+				<artifactId>commons-email</artifactId>
+				<version>1.5</version>
+			</dependency>
+			<dependency>
+				<groupId>javax.activation</groupId>
+				<artifactId>activation</artifactId>
+				<version>1.1.1</version>
+			</dependency>
+			<dependency>
+				<groupId>com.github.xuwei-k</groupId>
+				<artifactId>html2image</artifactId>
+				<version>0.1.0</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.logging.log4j</groupId>
+				<artifactId>log4j-api</artifactId>
+				<version>2.17.2</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.logging.log4j</groupId>
+				<artifactId>log4j-core</artifactId>
+				<version>2.17.2</version>
+			</dependency>
+			<dependency>
+				<groupId>org.slf4j</groupId>
+				<artifactId>slf4j-api</artifactId>
+				<version>1.7.32</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.logging.log4j</groupId>
+				<artifactId>log4j-slf4j-impl</artifactId>
+				<version>2.18.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_base_core_project</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_attendance_core_entity</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_bbs_core_entity</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_calendar_core_entity</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_cms_core_entity</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_cms_core_express</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_component_core_entity</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_file_core_entity</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_general_core_entity</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_hotpic_core_entity</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_meeting_core_entity</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_message_core_entity</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_mind_core_entity</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_organization_core_entity</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_jpush_core_entity</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_organization_core_express</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_portal_core_entity</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_processplatform_core_entity</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_processplatform_core_express</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_query_core_entity</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_correlation_core_entity</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_correlation_core_express</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_query_core_express</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_program_center</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.o2oa</groupId>
+				<artifactId>x_program_center_core_entity</artifactId>
+				<version>9.0</version>
+			</dependency>
+			<dependency>
+				<groupId>net.lingala.zip4j</groupId>
+				<artifactId>zip4j</artifactId>
+				<version>2.3.2</version>
+			</dependency>
+			<dependency>
+				<groupId>com.microsoft.playwright</groupId>
+				<artifactId>playwright</artifactId>
+				<version>1.17.1</version>
+			</dependency>
+			<dependency>
+				<groupId>javax.cache</groupId>
+				<artifactId>cache-api</artifactId>
+				<version>1.1.1</version>
+			</dependency>
+			<dependency>
+				<groupId>org.jsr107.ri</groupId>
+				<artifactId>cache-ri-impl</artifactId>
+				<version>1.1.1</version>
+			</dependency>
+			<dependency>
+				<groupId>com.github.penggle</groupId>
+				<artifactId>kaptcha</artifactId>
+				<version>2.3.2</version>
+			</dependency>
+			<dependency>
+				<groupId>org.apache.hadoop</groupId>
+				<artifactId>hadoop-client</artifactId>
+				<version>3.3.5</version>
+			</dependency>
+			<dependency>
+				<groupId>com.sun.mail</groupId>
+				<artifactId>javax.mail</artifactId>
+				<version>1.6.2</version>
+			</dependency>
+			<dependency>
+				<groupId>io.swagger.core.v3</groupId>
+				<artifactId>swagger-annotations</artifactId>
+				<version>2.2.0</version>
+			</dependency>
+			<dependency>
+				<groupId>io.swagger.core.v3</groupId>
+				<artifactId>swagger-core</artifactId>
+				<version>2.2.0</version>
+			</dependency>
+			<dependency>
+				<groupId>io.swagger.core.v3</groupId>
+				<artifactId>swagger-jaxrs2</artifactId>
+				<version>2.2.0</version>
+			</dependency>
+			<dependency>
+				<groupId>org.eclipse.jgit</groupId>
+				<artifactId>org.eclipse.jgit</artifactId>
+				<version>6.2.0.202206071550-r</version>
+			</dependency>
+			<dependency>
+				<groupId>org.ldaptive</groupId>
+				<artifactId>ldaptive-jldap</artifactId>
+				<version>1.3.1</version>
+			</dependency>
+			<dependency>
+				<groupId>org.jsoup</groupId>
+				<artifactId>jsoup</artifactId>
+				<version>1.15.3</version>
+			</dependency>
+			<dependency>
+				<groupId>com.github.jsqlparser</groupId>
+				<artifactId>jsqlparser</artifactId>
+				<version>4.6</version>
+			</dependency>
+			<!-- tika needs -->
+			<dependency>
+				<groupId>com.zaxxer</groupId>
+				<artifactId>SparseBitSet</artifactId>
+				<version>1.2</version>
+			</dependency>
+			<dependency>
+				<groupId>org.junit.jupiter</groupId>
+				<artifactId>junit-jupiter-api</artifactId>
+				<version>5.10.0</version>
+			</dependency>
+			<dependency>
+				<groupId>org.junit.jupiter</groupId>
+				<artifactId>junit-jupiter-engine</artifactId>
+				<version>5.10.0</version>
+			</dependency>
+			<dependency>
+				<groupId>org.graalvm.sdk</groupId>
+				<artifactId>graal-sdk</artifactId>
+				<version>${graalvm.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.graalvm.js</groupId>
+				<artifactId>js</artifactId>
+				<version>${graalvm.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.graalvm.js</groupId>
+				<artifactId>js-scriptengine</artifactId>
+				<version>${graalvm.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.graalvm.tools</groupId>
+				<artifactId>profiler</artifactId>
+				<version>${graalvm.version}</version>
+			</dependency>
+			<dependency>
+				<groupId>org.graalvm.tools</groupId>
+				<artifactId>chromeinspector</artifactId>
+				<version>${graalvm.version}</version>
+			</dependency>
+		</dependencies>
+	</dependencyManagement>
+	<!--	中心仓库发布地址-->
+	<!--distributionManagement>
+		<snapshotRepository>
+			<id>ossrh</id>
+			<url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
+		</snapshotRepository>
+		<repository>
+			<id>ossrh</id>
+			<url>https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/</url>
+		</repository>
+	</distributionManagement-->
+	<!--repositories>
+		<repository>
+			<id>nexus-o2oa</id>
+			<name>nexus-o2oa</name>
+			<url>http://maven.o2oa.net/repository/maven-public/</url>
+			<releases>
+				<enabled>true</enabled>
+			</releases>
+			<snapshots>
+				<enabled>true</enabled>
+			</snapshots>
+		</repository>
+	</repositories-->
+	<distributionManagement>
+		<repository>
+			<id>o2oa-release</id>
+			<url>https://maven.o2oa.net/repository/o2oa-release/</url>
+		</repository>
+	</distributionManagement>
+</project>

+ 9 - 0
o2server/restart_arm.sh

@@ -0,0 +1,9 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+    cd "$(dirname "$0")"
+    pwd
+)"
+cd ${current_dir}
+${current_dir}/stop_arm.sh
+${current_dir}/start_arm.sh

+ 9 - 0
o2server/restart_linux.sh

@@ -0,0 +1,9 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+    cd "$(dirname "$0")"
+    pwd
+)"
+cd ${current_dir}
+${current_dir}/stop_linux.sh
+${current_dir}/start_linux.sh

+ 9 - 0
o2server/restart_macosarm.sh

@@ -0,0 +1,9 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+    cd "$(dirname "$0")"
+    pwd
+)"
+cd ${current_dir}
+${current_dir}/stop_macosarm.sh
+${current_dir}/start_macosarm.sh

+ 9 - 0
o2server/restart_macosx64.sh

@@ -0,0 +1,9 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+    cd "$(dirname "$0")"
+    pwd
+)"
+cd ${current_dir}
+${current_dir}/stop_macosx64.sh
+${current_dir}/start_macosx64.sh

+ 9 - 0
o2server/restart_mips.sh

@@ -0,0 +1,9 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+    cd "$(dirname "$0")"
+    pwd
+)"
+cd ${current_dir}
+${current_dir}/stop_mips.sh
+${current_dir}/start_mips.sh

+ 9 - 0
o2server/restart_raspi.sh

@@ -0,0 +1,9 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+    cd "$(dirname "$0")"
+    pwd
+)"
+cd ${current_dir}
+${current_dir}/stop_raspi.sh
+${current_dir}/start_raspi.sh

+ 9 - 0
o2server/restart_sw.sh

@@ -0,0 +1,9 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+    cd "$(dirname "$0")"
+    pwd
+)"
+cd ${current_dir}
+${current_dir}/stop_sw.sh
+${current_dir}/start_sw.sh

+ 36 - 0
o2server/service_linux.sh

@@ -0,0 +1,36 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+    cd "$(dirname "$0")"
+    pwd
+)"
+cd ${current_dir}
+if [ -z "$1" ]; then
+    echo "usage: ./service_linux.sh name [start.sh default is start_linux.sh]"
+    exit
+fi
+name="$1"
+scriptName="start_linux.sh"
+if [ -z "$2" ]; then
+    echo "use ${current_dir}/${scriptName} as start script."
+else
+    scriptName="$2"
+fi
+if [ ! -f ${current_dir}/${scriptName} ]; then
+    echo "start script ${current_dir}/${scriptName} not exist."
+    exit
+fi
+servicePath="/etc/systemd/system/${name}.service"
+echo "[Unit]" >${servicePath}
+echo "Description=o2server name:${name}" >>${servicePath}
+echo "Wants=network-online.target" >>${servicePath}
+echo "After=network.target" >>${servicePath}
+echo "[Service]" >>${servicePath}
+echo "Type=simple" >>${servicePath}
+echo "StandardOutput=null" >>${servicePath}
+echo "StandardError=null" >>${servicePath}
+echo "ExecStart=${current_dir}/${scriptName}" >>${servicePath}
+echo "ExecReload=${current_dir}/restart_linux.sh" >>${servicePath}
+echo "ExecStop=${current_dir}/stop_linux.sh" >>${servicePath}
+echo "[Install]" >>${servicePath}
+echo "WantedBy=multi-user.target" >>${servicePath}

+ 21 - 0
o2server/service_windows.bat

@@ -0,0 +1,21 @@
+@echo off
+cd "%~dp0"
+if not "%1" == "install" ( if not "%1" == "uninstall"  ( if not "%1" == "start" ( if not "%1" == "stop" (
+echo     service_windows.bat install ^| uninstall ^| start ^| stop
+echo     depends on Microsoft.NET Framework 4
+goto out
+))))
+if not exist "%~dp0local" (
+md "%~dp0local"
+)
+echo ^<?xml version="1.0" encoding="GBK" ?^> > "%~dp0local\service.xml"
+echo ^<configuration^> >> "%~dp0local\service.xml"
+echo ^<id^>O2SERVER^<^/id^> >> "%~dp0local\service.xml"
+echo ^<name^>O2SERVER SERVICE^<^/name^> >> "%~dp0local\service.xml"
+echo ^<description^>O2OA Platform Service.^<^/description^> >> "%~dp0local\service.xml"
+echo ^<executable^>"%~dp0start_windows.bat"^</executable^> >> "%~dp0local\service.xml"
+echo ^<log mode="none"^/^> >> "%~dp0local\service.xml"
+echo ^</configuration^> >> "%~dp0local\service.xml"
+copy "%~dp0commons\winsw.exe" "%~dp0local\service.exe"
+"%~dp0local\service.exe" %1
+:out

+ 44 - 0
o2server/start_arm.sh

@@ -0,0 +1,44 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+	cd "$(dirname "$0")"
+	pwd
+)"
+cd ${current_dir}
+if [ -d ${current_dir}/local/update ]; then
+	for D in commons configSample localSample jvm servers store config local; do
+		if [ ! -d ${current_dir}/$D ]; then
+			mkdir ${current_dir}/commons
+		fi
+	done
+	if [ -f ${current_dir}/local/update/o2server/version.o2 ]; then
+		echo 'update o2server.'
+		for D in commons configSample localSample jvm servers store; do
+			if [ -d ${current_dir}/local/update/o2server/$D ]; then
+				echo "update ${current_dir}/$D."
+				cp -Rf -p ${current_dir}/local/update/o2server/$D ${current_dir}/
+			fi
+		done
+		for F in console.jar index.html src.zip; do
+			if [ -f ${current_dir}/local/update/o2server/$F ]; then
+				echo "update ${current_dir}/$F."
+				cp -f -p ${current_dir}/local/update/o2server/$F ${current_dir}/
+			fi
+		done
+		for A in "start" "stop" "restart" "console" "service"; do
+			for B in "_windows.bat" "_windows_debug.bat" "_linux.sh" "_linux_debug.sh" "_linux_min.sh" "_macosx64.sh" "_macosx64_debug.sh" "_macosarm.sh" "_macosarm_debug.sh" "_arm.sh" "_arm_debug.sh" "_mips.sh" "_mips_debug.sh" "_raspi.sh" "_raspi_debug.sh" "_sw.sh" "_sw_debug.sh"; do
+				if [ -f ${current_dir}/local/update/o2server/$A$B ]; then
+					echo "update ${current_dir}/$A$B."
+					cp -f -p ${current_dir}/local/update/o2server/$A$B ${current_dir}/
+				fi
+			done
+		done
+		echo "update ${current_dir}/version.o2."
+		cp ${current_dir}/local/update/o2server/version.o2 ${current_dir}/
+		echo "clean local/update directory."
+		rm -Rf ${current_dir}/local/update
+		echo "the update is complete, please restart the server."
+		exit 1
+	fi
+fi
+setsid ${current_dir}/jvm/arm_java11/bin/java -javaagent:${current_dir}/console.jar -server -Djava.awt.headless=true -Xms2g -Xmx4g -Duser.timezone=GMT+08 -XX:+HeapDumpOnOutOfMemoryError -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --module-path=${current_dir}/commons/module_java11 --upgrade-module-path=${current_dir}/commons/module_java11/compiler.jar:${current_dir}/commons/module_java11/compiler-management.jar -jar ${current_dir}/console.jar

+ 44 - 0
o2server/start_arm_debug.sh

@@ -0,0 +1,44 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+	cd "$(dirname "$0")"
+	pwd
+)"
+cd ${current_dir}
+if [ -d ${current_dir}/local/update ]; then
+	for D in commons configSample localSample jvm servers store config local; do
+		if [ ! -d ${current_dir}/$D ]; then
+			mkdir ${current_dir}/commons
+		fi
+	done
+	if [ -f ${current_dir}/local/update/o2server/version.o2 ]; then
+		echo 'update o2server.'
+		for D in commons configSample localSample jvm servers store; do
+			if [ -d ${current_dir}/local/update/o2server/$D ]; then
+				echo "update ${current_dir}/$D."
+				cp -Rf -p ${current_dir}/local/update/o2server/$D ${current_dir}/
+			fi
+		done
+		for F in console.jar index.html src.zip; do
+			if [ -f ${current_dir}/local/update/o2server/$F ]; then
+				echo "update ${current_dir}/$F."
+				cp -f -p ${current_dir}/local/update/o2server/$F ${current_dir}/
+			fi
+		done
+		for A in "start" "stop" "restart" "console" "service"; do
+			for B in "_windows.bat" "_windows_debug.bat" "_linux.sh" "_linux_debug.sh" "_linux_min.sh" "_macosx64.sh" "_macosx64_debug.sh" "_macosarm.sh" "_macosarm_debug.sh" "_arm.sh" "_arm_debug.sh" "_mips.sh" "_mips_debug.sh" "_raspi.sh" "_raspi_debug.sh" "_sw.sh" "_sw_debug.sh"; do
+				if [ -f ${current_dir}/local/update/o2server/$A$B ]; then
+					echo "update ${current_dir}/$A$B."
+					cp -f -p ${current_dir}/local/update/o2server/$A$B ${current_dir}/
+				fi
+			done
+		done
+		echo "update ${current_dir}/version.o2."
+		cp ${current_dir}/local/update/o2server/version.o2 ${current_dir}/
+		echo "clean local/update directory."
+		rm -Rf ${current_dir}/local/update
+		echo "the update is complete, please restart the server."
+		exit 1
+	fi
+fi
+setsid ${current_dir}/jvm/arm_java11/bin/java -javaagent:${current_dir}/console.jar -server -Djava.awt.headless=true -Xms2g -Xms4g -Duser.timezone=GMT+08 -XX:+HeapDumpOnOutOfMemoryError -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.authenticate=false -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=0.0.0.0:20000 -Djava.rmi.server.hostname=127.0.0.1 -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --module-path=${current_dir}/commons/module_java11 --upgrade-module-path=${current_dir}/commons/module_java11/compiler.jar:${current_dir}/commons/module_java11/compiler-management.jar -jar ${current_dir}/console.jar

+ 44 - 0
o2server/start_linux.sh

@@ -0,0 +1,44 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+	cd "$(dirname "$0")"
+	pwd
+)"
+cd ${current_dir}
+if [ -d ${current_dir}/local/update ]; then
+	for D in commons configSample localSample jvm servers store config local; do
+		if [ ! -d ${current_dir}/$D ]; then
+			mkdir ${current_dir}/commons
+		fi
+	done
+	if [ -f ${current_dir}/local/update/o2server/version.o2 ]; then
+		echo 'update o2server.'
+		for D in commons configSample localSample jvm servers store; do
+			if [ -d ${current_dir}/local/update/o2server/$D ]; then
+				echo "update ${current_dir}/$D."
+				cp -Rf -p ${current_dir}/local/update/o2server/$D ${current_dir}/
+			fi
+		done
+		for F in console.jar index.html src.zip; do
+			if [ -f ${current_dir}/local/update/o2server/$F ]; then
+				echo "update ${current_dir}/$F."
+				cp -f -p ${current_dir}/local/update/o2server/$F ${current_dir}/
+			fi
+		done
+		for A in "start" "stop" "restart" "console" "service"; do
+			for B in "_windows.bat" "_windows_debug.bat" "_linux.sh" "_linux_debug.sh" "_linux_min.sh" "_macosx64.sh" "_macosx64_debug.sh" "_macosarm.sh" "_macosarm_debug.sh" "_arm.sh" "_arm_debug.sh" "_mips.sh" "_mips_debug.sh" "_raspi.sh" "_raspi_debug.sh" "_sw.sh" "_sw_debug.sh"; do
+				if [ -f ${current_dir}/local/update/o2server/$A$B ]; then
+					echo "update ${current_dir}/$A$B."
+					cp -f -p ${current_dir}/local/update/o2server/$A$B ${current_dir}/
+				fi
+			done
+		done
+		echo "update ${current_dir}/version.o2."
+		cp ${current_dir}/local/update/o2server/version.o2 ${current_dir}/
+		echo "clean local/update directory."
+		rm -Rf ${current_dir}/local/update
+		echo "the update is complete, please restart the server."
+		exit 1
+	fi
+fi
+setsid ${current_dir}/jvm/linux_java11/bin/java -javaagent:${current_dir}/console.jar -server -Djava.awt.headless=true -Xms2g -Xmx4g -Duser.timezone=GMT+08 -XX:+HeapDumpOnOutOfMemoryError -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --module-path=${current_dir}/commons/module_java11 --upgrade-module-path=${current_dir}/commons/module_java11/compiler.jar:${current_dir}/commons/module_java11/compiler-management.jar -jar ${current_dir}/console.jar

+ 44 - 0
o2server/start_linux_debug.sh

@@ -0,0 +1,44 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+	cd "$(dirname "$0")"
+	pwd
+)"
+cd ${current_dir}
+if [ -d ${current_dir}/local/update ]; then
+	for D in commons configSample localSample jvm servers store config local; do
+		if [ ! -d ${current_dir}/$D ]; then
+			mkdir ${current_dir}/commons
+		fi
+	done
+	if [ -f ${current_dir}/local/update/o2server/version.o2 ]; then
+		echo 'update o2server.'
+		for D in commons configSample localSample jvm servers store; do
+			if [ -d ${current_dir}/local/update/o2server/$D ]; then
+				echo "update ${current_dir}/$D."
+				cp -Rf -p ${current_dir}/local/update/o2server/$D ${current_dir}/
+			fi
+		done
+		for F in console.jar index.html src.zip; do
+			if [ -f ${current_dir}/local/update/o2server/$F ]; then
+				echo "update ${current_dir}/$F."
+				cp -f -p ${current_dir}/local/update/o2server/$F ${current_dir}/
+			fi
+		done
+		for A in "start" "stop" "restart" "console" "service"; do
+			for B in "_windows.bat" "_windows_debug.bat" "_linux.sh" "_linux_debug.sh" "_linux_min.sh" "_macosx64.sh" "_macosx64_debug.sh" "_macosarm.sh" "_macosarm_debug.sh" "_arm.sh" "_arm_debug.sh" "_mips.sh" "_mips_debug.sh" "_raspi.sh" "_raspi_debug.sh" "_sw.sh" "_sw_debug.sh"; do
+				if [ -f ${current_dir}/local/update/o2server/$A$B ]; then
+					echo "update ${current_dir}/$A$B."
+					cp -f -p ${current_dir}/local/update/o2server/$A$B ${current_dir}/
+				fi
+			done
+		done
+		echo "update ${current_dir}/version.o2."
+		cp ${current_dir}/local/update/o2server/version.o2 ${current_dir}/
+		echo "clean local/update directory."
+		rm -Rf ${current_dir}/local/update
+		echo "the update is complete, please restart the server."
+		exit 1
+	fi
+fi
+setsid ${current_dir}/jvm/linux_java11/bin/java -javaagent:${current_dir}/console.jar -server -Djava.awt.headless=true -Xms2g -Xmx4g -Duser.timezone=GMT+08 -XX:+HeapDumpOnOutOfMemoryError -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.authenticate=false -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=0.0.0.0:20000 -Djava.rmi.server.hostname=127.0.0.1 -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --module-path=${current_dir}/commons/module_java11 --upgrade-module-path=${current_dir}/commons/module_java11/compiler.jar:${current_dir}/commons/module_java11/compiler-management.jar -jar ${current_dir}/console.jar

+ 49 - 0
o2server/start_linux_min.sh

@@ -0,0 +1,49 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(cd "$(dirname "$0")"; pwd)"
+cd ${current_dir}
+if [ -d ${current_dir}/local/update ]
+then
+	for D in commons configSample localSample jvm servers store config  local
+	do
+		if [ ! -d ${current_dir}/$D ]
+		then
+			mkdir ${current_dir}/commons
+		fi
+	done
+	if [ -f ${current_dir}/local/update/o2server/version.o2 ]
+	then
+		echo 'update o2server.'
+		for D in commons configSample localSample jvm servers store
+		do
+			if [ -d ${current_dir}/local/update/o2server/$D ]
+			then
+				echo "update ${current_dir}/$D."
+				cp -Rf -p ${current_dir}/local/update/o2server/$D  ${current_dir}/
+			fi
+		done
+		for F in console.jar index.html src.zip
+		do
+			if [ -f ${current_dir}/local/update/o2server/$F ]
+			then
+				echo "update ${current_dir}/$F."
+				cp -f -p ${current_dir}/local/update/o2server/$F ${current_dir}/
+			fi
+		done
+		for A in "start" "stop" "restart" "console" "service"; do
+			for B in "_windows.bat" "_windows_debug.bat" "_linux.sh" "_linux_debug.sh" "_linux_min.sh" "_macosx64.sh" "_macosx64_debug.sh" "_macosarm.sh" "_macosarm_debug.sh" "_arm.sh" "_arm_debug.sh" "_mips.sh" "_mips_debug.sh" "_raspi.sh" "_raspi_debug.sh" "_sw.sh" "_sw_debug.sh"; do
+				if [ -f ${current_dir}/local/update/o2server/$A$B ]; then
+					echo "update ${current_dir}/$A$B."
+					cp -f -p ${current_dir}/local/update/o2server/$A$B ${current_dir}/
+				fi
+			done
+		done
+		echo "update ${current_dir}/version.o2."
+		cp ${current_dir}/local/update/o2server/version.o2 ${current_dir}/
+		echo "clean local/update directory."
+		rm -Rf ${current_dir}/local/update
+		echo "the update is complete, please restart the server."
+		exit 1
+	fi
+fi
+setsid ${current_dir}/jvm/linux_java11/bin/java -javaagent:${current_dir}/console.jar -server -Djava.awt.headless=true -Xms1g -Xmx3g -XX:MinHeapFreeRatio=15 -XX:MaxHeapFreeRatio=30 -Duser.timezone=GMT+08 -XX:+HeapDumpOnOutOfMemoryError -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --module-path=${current_dir}/commons/module_java11 --upgrade-module-path=${current_dir}/commons/module_java11/compiler.jar:${current_dir}/commons/module_java11/compiler-management.jar -jar ${current_dir}/console.jar

+ 45 - 0
o2server/start_macosarm.sh

@@ -0,0 +1,45 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+	cd "$(dirname "$0")"
+	pwd
+)"
+cd ${current_dir}
+sudo date
+if [ -d ${current_dir}/local/update ]; then
+	for D in commons configSample localSample jvm servers store config local; do
+		if [ ! -d ${current_dir}/$D ]; then
+			mkdir ${current_dir}/commons
+		fi
+	done
+	if [ -f ${current_dir}/local/update/o2server/version.o2 ]; then
+		echo 'update o2server.'
+		for D in commons configSample localSample jvm servers store; do
+			if [ -d ${current_dir}/local/update/o2server/$D ]; then
+				echo "update ${current_dir}/$D."
+				cp -Rf -p ${current_dir}/local/update/o2server/$D ${current_dir}/
+			fi
+		done
+		for F in console.jar index.html src.zip; do
+			if [ -f ${current_dir}/local/update/o2server/$F ]; then
+				echo "update ${current_dir}/$F."
+				cp -f -p ${current_dir}/local/update/o2server/$F ${current_dir}/
+			fi
+		done
+		for A in "start" "stop" "restart" "console" "service"; do
+			for B in "_windows.bat" "_windows_debug.bat" "_linux.sh" "_linux_debug.sh" "_linux_min.sh" "_macosx64.sh" "_macosx64_debug.sh" "_macosarm.sh" "_macosarm_debug.sh" "_arm.sh" "_arm_debug.sh" "_mips.sh" "_mips_debug.sh" "_raspi.sh" "_raspi_debug.sh" "_sw.sh" "_sw_debug.sh"; do
+				if [ -f ${current_dir}/local/update/o2server/$A$B ]; then
+					echo "update ${current_dir}/$A$B."
+					cp -f -p ${current_dir}/local/update/o2server/$A$B ${current_dir}/
+				fi
+			done
+		done
+		echo "update ${current_dir}/version.o2."
+		cp ${current_dir}/local/update/o2server/version.o2 ${current_dir}/
+		echo "clean local/update directory."
+		rm -Rf ${current_dir}/local/update
+		echo "the update is complete, please restart the server."
+		exit 1
+	fi
+fi
+sudo ${current_dir}/jvm/macosarm_java11/bin/java -javaagent:${current_dir}/console.jar -server -Djava.awt.headless=true -Xms2g -Xmx4g -Duser.timezone=GMT+08 -XX:+HeapDumpOnOutOfMemoryError -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --module-path=${current_dir}/commons/module_java11 --upgrade-module-path=${current_dir}/commons/module_java11/compiler.jar:${current_dir}/commons/module_java11/compiler-management.jar -jar ${current_dir}/console.jar

+ 45 - 0
o2server/start_macosarm_debug.sh

@@ -0,0 +1,45 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+	cd "$(dirname "$0")"
+	pwd
+)"
+cd ${current_dir}
+sudo date
+if [ -d ${current_dir}/local/update ]; then
+	for D in commons configSample localSample jvm servers store config local; do
+		if [ ! -d ${current_dir}/$D ]; then
+			mkdir ${current_dir}/commons
+		fi
+	done
+	if [ -f ${current_dir}/local/update/o2server/version.o2 ]; then
+		echo 'update o2server.'
+		for D in commons configSample localSample jvm servers store; do
+			if [ -d ${current_dir}/local/update/o2server/$D ]; then
+				echo "update ${current_dir}/$D."
+				cp -Rf -p ${current_dir}/local/update/o2server/$D ${current_dir}/
+			fi
+		done
+		for F in console.jar index.html src.zip; do
+			if [ -f ${current_dir}/local/update/o2server/$F ]; then
+				echo "update ${current_dir}/$F."
+				cp -f -p ${current_dir}/local/update/o2server/$F ${current_dir}/
+			fi
+		done
+		for A in "start" "stop" "restart" "console" "service"; do
+			for B in "_windows.bat" "_windows_debug.bat" "_linux.sh" "_linux_debug.sh" "_linux_min.sh" "_macosx64.sh" "_macosx64_debug.sh" "_macosarm.sh" "_macosarm_debug.sh" "_arm.sh" "_arm_debug.sh" "_mips.sh" "_mips_debug.sh" "_raspi.sh" "_raspi_debug.sh" "_sw.sh" "_sw_debug.sh"; do
+				if [ -f ${current_dir}/local/update/o2server/$A$B ]; then
+					echo "update ${current_dir}/$A$B."
+					cp -f -p ${current_dir}/local/update/o2server/$A$B ${current_dir}/
+				fi
+			done
+		done
+		echo "update ${current_dir}/version.o2."
+		cp ${current_dir}/local/update/o2server/version.o2 ${current_dir}/
+		echo "clean local/update directory."
+		rm -Rf ${current_dir}/local/update
+		echo "the update is complete, please restart the server."
+		exit 1
+	fi
+fi
+sudo ${current_dir}/jvm/macosarm_java11/bin/java -javaagent:${current_dir}/console.jar -server -Djava.awt.headless=true -Xms2g -Xmx4g -Duser.timezone=GMT+08 -XX:+HeapDumpOnOutOfMemoryError -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.authenticate=false -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=0.0.0.0:20000 -Djava.rmi.server.hostname=127.0.0.1 -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --module-path=${current_dir}/commons/module_java11 --upgrade-module-path=${current_dir}/commons/module_java11/compiler.jar:${current_dir}/commons/module_java11/compiler-management.jar -jar ${current_dir}/console.jar

+ 45 - 0
o2server/start_macosx64.sh

@@ -0,0 +1,45 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+	cd "$(dirname "$0")"
+	pwd
+)"
+cd ${current_dir}
+sudo date
+if [ -d ${current_dir}/local/update ]; then
+	for D in commons configSample localSample jvm servers store config local; do
+		if [ ! -d ${current_dir}/$D ]; then
+			mkdir ${current_dir}/commons
+		fi
+	done
+	if [ -f ${current_dir}/local/update/o2server/version.o2 ]; then
+		echo 'update o2server.'
+		for D in commons configSample localSample jvm servers store; do
+			if [ -d ${current_dir}/local/update/o2server/$D ]; then
+				echo "update ${current_dir}/$D."
+				cp -Rf -p ${current_dir}/local/update/o2server/$D ${current_dir}/
+			fi
+		done
+		for F in console.jar index.html src.zip; do
+			if [ -f ${current_dir}/local/update/o2server/$F ]; then
+				echo "update ${current_dir}/$F."
+				cp -f -p ${current_dir}/local/update/o2server/$F ${current_dir}/
+			fi
+		done
+		for A in "start" "stop" "restart" "console" "service"; do
+			for B in "_windows.bat" "_windows_debug.bat" "_linux.sh" "_linux_debug.sh" "_linux_min.sh" "_macosx64.sh" "_macosx64_debug.sh" "_macosarm.sh" "_macosarm_debug.sh" "_arm.sh" "_arm_debug.sh" "_mips.sh" "_mips_debug.sh" "_raspi.sh" "_raspi_debug.sh" "_sw.sh" "_sw_debug.sh"; do
+				if [ -f ${current_dir}/local/update/o2server/$A$B ]; then
+					echo "update ${current_dir}/$A$B."
+					cp -f -p ${current_dir}/local/update/o2server/$A$B ${current_dir}/
+				fi
+			done
+		done
+		echo "update ${current_dir}/version.o2."
+		cp ${current_dir}/local/update/o2server/version.o2 ${current_dir}/
+		echo "clean local/update directory."
+		rm -Rf ${current_dir}/local/update
+		echo "the update is complete, please restart the server."
+		exit 1
+	fi
+fi
+sudo ${current_dir}/jvm/macosx64_java11/bin/java -javaagent:${current_dir}/console.jar -server -Djava.awt.headless=true -Xms2g -Xmx4g -Duser.timezone=GMT+08 -XX:+HeapDumpOnOutOfMemoryError -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --module-path=${current_dir}/commons/module_java11 --upgrade-module-path=${current_dir}/commons/module_java11/compiler.jar:${current_dir}/commons/module_java11/compiler-management.jar -jar ${current_dir}/console.jar

+ 45 - 0
o2server/start_macosx64_debug.sh

@@ -0,0 +1,45 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+	cd "$(dirname "$0")"
+	pwd
+)"
+cd ${current_dir}
+sudo date
+if [ -d ${current_dir}/local/update ]; then
+	for D in commons configSample localSample jvm servers store config local; do
+		if [ ! -d ${current_dir}/$D ]; then
+			mkdir ${current_dir}/commons
+		fi
+	done
+	if [ -f ${current_dir}/local/update/o2server/version.o2 ]; then
+		echo 'update o2server.'
+		for D in commons configSample localSample jvm servers store; do
+			if [ -d ${current_dir}/local/update/o2server/$D ]; then
+				echo "update ${current_dir}/$D."
+				cp -Rf -p ${current_dir}/local/update/o2server/$D ${current_dir}/
+			fi
+		done
+		for F in console.jar index.html src.zip; do
+			if [ -f ${current_dir}/local/update/o2server/$F ]; then
+				echo "update ${current_dir}/$F."
+				cp -f -p ${current_dir}/local/update/o2server/$F ${current_dir}/
+			fi
+		done
+		for A in "start" "stop" "restart" "console" "service"; do
+			for B in "_windows.bat" "_windows_debug.bat" "_linux.sh" "_linux_debug.sh" "_linux_min.sh" "_macosx64.sh" "_macosx64_debug.sh" "_macosarm.sh" "_macosarm_debug.sh" "_arm.sh" "_arm_debug.sh" "_mips.sh" "_mips_debug.sh" "_raspi.sh" "_raspi_debug.sh" "_sw.sh" "_sw_debug.sh"; do
+				if [ -f ${current_dir}/local/update/o2server/$A$B ]; then
+					echo "update ${current_dir}/$A$B."
+					cp -f -p ${current_dir}/local/update/o2server/$A$B ${current_dir}/
+				fi
+			done
+		done
+		echo "update ${current_dir}/version.o2."
+		cp ${current_dir}/local/update/o2server/version.o2 ${current_dir}/
+		echo "clean local/update directory."
+		rm -Rf ${current_dir}/local/update
+		echo "the update is complete, please restart the server."
+		exit 1
+	fi
+fi
+sudo ${current_dir}/jvm/macosx64_java11/bin/java -javaagent:${current_dir}/console.jar -server -Djava.awt.headless=true -Xms2g -Xmx4g -Duser.timezone=GMT+08 -XX:+HeapDumpOnOutOfMemoryError -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.authenticate=false -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=0.0.0.0:20000 -Djava.rmi.server.hostname=127.0.0.1 -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --module-path=${current_dir}/commons/module_java11 --upgrade-module-path=${current_dir}/commons/module_java11/compiler.jar:${current_dir}/commons/module_java11/compiler-management.jar -jar ${current_dir}/console.jar

+ 44 - 0
o2server/start_mips.sh

@@ -0,0 +1,44 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+	cd "$(dirname "$0")"
+	pwd
+)"
+cd ${current_dir}
+if [ -d ${current_dir}/local/update ]; then
+	for D in commons configSample localSample jvm servers store config local; do
+		if [ ! -d ${current_dir}/$D ]; then
+			mkdir ${current_dir}/commons
+		fi
+	done
+	if [ -f ${current_dir}/local/update/o2server/version.o2 ]; then
+		echo 'update o2server.'
+		for D in commons configSample localSample jvm servers store; do
+			if [ -d ${current_dir}/local/update/o2server/$D ]; then
+				echo "update ${current_dir}/$D."
+				cp -Rf -p ${current_dir}/local/update/o2server/$D ${current_dir}/
+			fi
+		done
+		for F in console.jar index.html src.zip; do
+			if [ -f ${current_dir}/local/update/o2server/$F ]; then
+				echo "update ${current_dir}/$F."
+				cp -f -p ${current_dir}/local/update/o2server/$F ${current_dir}/
+			fi
+		done
+		for A in "start" "stop" "restart" "console" "service"; do
+			for B in "_windows.bat" "_windows_debug.bat" "_linux.sh" "_linux_debug.sh" "_linux_min.sh" "_macosx64.sh" "_macosx64_debug.sh" "_macosarm.sh" "_macosarm_debug.sh" "_arm.sh" "_arm_debug.sh" "_mips.sh" "_mips_debug.sh" "_raspi.sh" "_raspi_debug.sh" "_sw.sh" "_sw_debug.sh"; do
+				if [ -f ${current_dir}/local/update/o2server/$A$B ]; then
+					echo "update ${current_dir}/$A$B."
+					cp -f -p ${current_dir}/local/update/o2server/$A$B ${current_dir}/
+				fi
+			done
+		done
+		echo "update ${current_dir}/version.o2."
+		cp ${current_dir}/local/update/o2server/version.o2 ${current_dir}/
+		echo "clean local/update directory."
+		rm -Rf ${current_dir}/local/update
+		echo "the update is complete, please restart the server."
+		exit 1
+	fi
+fi
+setsid ${current_dir}/jvm/mips_java11/bin/java -javaagent:${current_dir}/console.jar -server -Djava.awt.headless=true -Xms2g -Xmx4g -Duser.timezone=GMT+08 -XX:+HeapDumpOnOutOfMemoryError -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --module-path=${current_dir}/commons/module_java11 --upgrade-module-path=${current_dir}/commons/module_java11/compiler.jar:${current_dir}/commons/module_java11/compiler-management.jar -jar ${current_dir}/console.jar

+ 44 - 0
o2server/start_mips_debug.sh

@@ -0,0 +1,44 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+	cd "$(dirname "$0")"
+	pwd
+)"
+cd ${current_dir}
+if [ -d ${current_dir}/local/update ]; then
+	for D in commons configSample localSample jvm servers store config local; do
+		if [ ! -d ${current_dir}/$D ]; then
+			mkdir ${current_dir}/commons
+		fi
+	done
+	if [ -f ${current_dir}/local/update/o2server/version.o2 ]; then
+		echo 'update o2server.'
+		for D in commons configSample localSample jvm servers store; do
+			if [ -d ${current_dir}/local/update/o2server/$D ]; then
+				echo "update ${current_dir}/$D."
+				cp -Rf -p ${current_dir}/local/update/o2server/$D ${current_dir}/
+			fi
+		done
+		for F in console.jar index.html src.zip; do
+			if [ -f ${current_dir}/local/update/o2server/$F ]; then
+				echo "update ${current_dir}/$F."
+				cp -f -p ${current_dir}/local/update/o2server/$F ${current_dir}/
+			fi
+		done
+		for A in "start" "stop" "restart" "console" "service"; do
+			for B in "_windows.bat" "_windows_debug.bat" "_linux.sh" "_linux_debug.sh" "_linux_min.sh" "_macosx64.sh" "_macosx64_debug.sh" "_macosarm.sh" "_macosarm_debug.sh" "_arm.sh" "_arm_debug.sh" "_mips.sh" "_mips_debug.sh" "_raspi.sh" "_raspi_debug.sh" "_sw.sh" "_sw_debug.sh"; do
+				if [ -f ${current_dir}/local/update/o2server/$A$B ]; then
+					echo "update ${current_dir}/$A$B."
+					cp -f -p ${current_dir}/local/update/o2server/$A$B ${current_dir}/
+				fi
+			done
+		done
+		echo "update ${current_dir}/version.o2."
+		cp ${current_dir}/local/update/o2server/version.o2 ${current_dir}/
+		echo "clean local/update directory."
+		rm -Rf ${current_dir}/local/update
+		echo "the update is complete, please restart the server."
+		exit 1
+	fi
+fi
+setsid ${current_dir}/jvm/mips_java11/bin/java -javaagent:${current_dir}/console.jar -server -Djava.awt.headless=true -Xms2g -Xmx4g -Duser.timezone=GMT+08 -XX:+HeapDumpOnOutOfMemoryError -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.authenticate=false -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=0.0.0.0:20000 -Djava.rmi.server.hostname=127.0.0.1 -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --module-path=${current_dir}/commons/module_java11 --upgrade-module-path=${current_dir}/commons/module_java11/compiler.jar:${current_dir}/commons/module_java11/compiler-management.jar -jar ${current_dir}/console.jar

+ 44 - 0
o2server/start_raspi.sh

@@ -0,0 +1,44 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+	cd "$(dirname "$0")"
+	pwd
+)"
+cd ${current_dir}
+if [ -d ${current_dir}/local/update ]; then
+	for D in commons configSample localSample jvm servers store config local; do
+		if [ ! -d ${current_dir}/$D ]; then
+			mkdir ${current_dir}/commons
+		fi
+	done
+	if [ -f ${current_dir}/local/update/o2server/version.o2 ]; then
+		echo 'update o2server.'
+		for D in commons configSample localSample jvm servers store; do
+			if [ -d ${current_dir}/local/update/o2server/$D ]; then
+				echo "update ${current_dir}/$D."
+				cp -Rf -p ${current_dir}/local/update/o2server/$D ${current_dir}/
+			fi
+		done
+		for F in console.jar index.html src.zip; do
+			if [ -f ${current_dir}/local/update/o2server/$F ]; then
+				echo "update ${current_dir}/$F."
+				cp -f -p ${current_dir}/local/update/o2server/$F ${current_dir}/
+			fi
+		done
+		for A in "start" "stop" "restart" "console" "service"; do
+			for B in "_windows.bat" "_windows_debug.bat" "_linux.sh" "_linux_debug.sh" "_linux_min.sh" "_macosx64.sh" "_macosx64_debug.sh" "_macosarm.sh" "_macosarm_debug.sh" "_arm.sh" "_arm_debug.sh" "_mips.sh" "_mips_debug.sh" "_raspi.sh" "_raspi_debug.sh" "_sw.sh" "_sw_debug.sh"; do
+				if [ -f ${current_dir}/local/update/o2server/$A$B ]; then
+					echo "update ${current_dir}/$A$B."
+					cp -f -p ${current_dir}/local/update/o2server/$A$B ${current_dir}/
+				fi
+			done
+		done
+		echo "update ${current_dir}/version.o2."
+		cp ${current_dir}/local/update/o2server/version.o2 ${current_dir}/
+		echo "clean local/update directory."
+		rm -Rf ${current_dir}/local/update
+		echo "the update is complete, please restart the server."
+		exit 1
+	fi
+fi
+setsid ${current_dir}/jvm/raspi_java11/bin/java -javaagent:${current_dir}/console.jar -server -Djava.awt.headless=true -Xms1g -Xmx2g -Duser.timezone=GMT+08 -XX:+HeapDumpOnOutOfMemoryError -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --module-path=${current_dir}/commons/module_java11 --upgrade-module-path=${current_dir}/commons/module_java11/compiler.jar:${current_dir}/commons/module_java11/compiler-management.jar -jar ${current_dir}/console.jar

+ 44 - 0
o2server/start_raspi_debug.sh

@@ -0,0 +1,44 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+	cd "$(dirname "$0")"
+	pwd
+)"
+cd ${current_dir}
+if [ -d ${current_dir}/local/update ]; then
+	for D in commons configSample localSample jvm servers store config local; do
+		if [ ! -d ${current_dir}/$D ]; then
+			mkdir ${current_dir}/commons
+		fi
+	done
+	if [ -f ${current_dir}/local/update/o2server/version.o2 ]; then
+		echo 'update o2server.'
+		for D in commons configSample localSample jvm servers store; do
+			if [ -d ${current_dir}/local/update/o2server/$D ]; then
+				echo "update ${current_dir}/$D."
+				cp -Rf -p ${current_dir}/local/update/o2server/$D ${current_dir}/
+			fi
+		done
+		for F in console.jar index.html src.zip; do
+			if [ -f ${current_dir}/local/update/o2server/$F ]; then
+				echo "update ${current_dir}/$F."
+				cp -f -p ${current_dir}/local/update/o2server/$F ${current_dir}/
+			fi
+		done
+		for A in "start" "stop" "restart" "console" "service"; do
+			for B in "_windows.bat" "_windows_debug.bat" "_linux.sh" "_linux_debug.sh" "_linux_min.sh" "_macosx64.sh" "_macosx64_debug.sh" "_macosarm.sh" "_macosarm_debug.sh" "_arm.sh" "_arm_debug.sh" "_mips.sh" "_mips_debug.sh" "_raspi.sh" "_raspi_debug.sh" "_sw.sh" "_sw_debug.sh"; do
+				if [ -f ${current_dir}/local/update/o2server/$A$B ]; then
+					echo "update ${current_dir}/$A$B."
+					cp -f -p ${current_dir}/local/update/o2server/$A$B ${current_dir}/
+				fi
+			done
+		done
+		echo "update ${current_dir}/version.o2."
+		cp ${current_dir}/local/update/o2server/version.o2 ${current_dir}/
+		echo "clean local/update directory."
+		rm -Rf ${current_dir}/local/update
+		echo "the update is complete, please restart the server."
+		exit 1
+	fi
+fi
+setsid ${current_dir}/jvm/raspi_java11/bin/java -javaagent:${current_dir}/console.jar -server -Djava.awt.headless=true -Xms1g -Xmx2g -Duser.timezone=GMT+08 -XX:+HeapDumpOnOutOfMemoryError -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.authenticate=false -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=0.0.0.0:20000 -Djava.rmi.server.hostname=127.0.0.1 -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --module-path=${current_dir}/commons/module_java11 --upgrade-module-path=${current_dir}/commons/module_java11/compiler.jar:${current_dir}/commons/module_java11/compiler-management.jar -jar ${current_dir}/console.jar

+ 44 - 0
o2server/start_sw.sh

@@ -0,0 +1,44 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+	cd "$(dirname "$0")"
+	pwd
+)"
+cd ${current_dir}
+if [ -d ${current_dir}/local/update ]; then
+	for D in commons configSample localSample jvm servers store config local; do
+		if [ ! -d ${current_dir}/$D ]; then
+			mkdir ${current_dir}/commons
+		fi
+	done
+	if [ -f ${current_dir}/local/update/o2server/version.o2 ]; then
+		echo 'update o2server.'
+		for D in commons configSample localSample jvm servers store; do
+			if [ -d ${current_dir}/local/update/o2server/$D ]; then
+				echo "update ${current_dir}/$D."
+				cp -Rf -p ${current_dir}/local/update/o2server/$D ${current_dir}/
+			fi
+		done
+		for F in console.jar index.html src.zip; do
+			if [ -f ${current_dir}/local/update/o2server/$F ]; then
+				echo "update ${current_dir}/$F."
+				cp -f -p ${current_dir}/local/update/o2server/$F ${current_dir}/
+			fi
+		done
+		for A in "start" "stop" "restart" "console" "service"; do
+			for B in "_windows.bat" "_windows_debug.bat" "_linux.sh" "_linux_debug.sh" "_linux_min.sh" "_macosx64.sh" "_macosx64_debug.sh" "_macosarm.sh" "_macosarm_debug.sh" "_arm.sh" "_arm_debug.sh" "_mips.sh" "_mips_debug.sh" "_raspi.sh" "_raspi_debug.sh" "_sw.sh" "_sw_debug.sh"; do
+				if [ -f ${current_dir}/local/update/o2server/$A$B ]; then
+					echo "update ${current_dir}/$A$B."
+					cp -f -p ${current_dir}/local/update/o2server/$A$B ${current_dir}/
+				fi
+			done
+		done
+		echo "update ${current_dir}/version.o2."
+		cp ${current_dir}/local/update/o2server/version.o2 ${current_dir}/
+		echo "clean local/update directory."
+		rm -Rf ${current_dir}/local/update
+		echo "the update is complete, please restart the server."
+		exit 1
+	fi
+fi
+setsid ${current_dir}/jvm/sw_java11/bin/java -javaagent:${current_dir}/console.jar -server -Djava.awt.headless=true -Xms2g -Xmx4g -Duser.timezone=GMT+08 -XX:+HeapDumpOnOutOfMemoryError -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --module-path=${current_dir}/commons/module_java11 -jar ${current_dir}/console.jar

+ 44 - 0
o2server/start_sw_debug.sh

@@ -0,0 +1,44 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+	cd "$(dirname "$0")"
+	pwd
+)"
+cd ${current_dir}
+if [ -d ${current_dir}/local/update ]; then
+	for D in commons configSample localSample jvm servers store config local; do
+		if [ ! -d ${current_dir}/$D ]; then
+			mkdir ${current_dir}/commons
+		fi
+	done
+	if [ -f ${current_dir}/local/update/o2server/version.o2 ]; then
+		echo 'update o2server.'
+		for D in commons configSample localSample jvm servers store; do
+			if [ -d ${current_dir}/local/update/o2server/$D ]; then
+				echo "update ${current_dir}/$D."
+				cp -Rf -p ${current_dir}/local/update/o2server/$D ${current_dir}/
+			fi
+		done
+		for F in console.jar index.html src.zip; do
+			if [ -f ${current_dir}/local/update/o2server/$F ]; then
+				echo "update ${current_dir}/$F."
+				cp -f -p ${current_dir}/local/update/o2server/$F ${current_dir}/
+			fi
+		done
+		for A in "start" "stop" "restart" "console" "service"; do
+			for B in "_windows.bat" "_windows_debug.bat" "_linux.sh" "_linux_debug.sh" "_linux_min.sh" "_macosx64.sh" "_macosx64_debug.sh" "_macosarm.sh" "_macosarm_debug.sh" "_arm.sh" "_arm_debug.sh" "_mips.sh" "_mips_debug.sh" "_raspi.sh" "_raspi_debug.sh" "_sw.sh" "_sw_debug.sh"; do
+				if [ -f ${current_dir}/local/update/o2server/$A$B ]; then
+					echo "update ${current_dir}/$A$B."
+					cp -f -p ${current_dir}/local/update/o2server/$A$B ${current_dir}/
+				fi
+			done
+		done
+		echo "update ${current_dir}/version.o2."
+		cp ${current_dir}/local/update/o2server/version.o2 ${current_dir}/
+		echo "clean local/update directory."
+		rm -Rf ${current_dir}/local/update
+		echo "the update is complete, please restart the server."
+		exit 1
+	fi
+fi
+setsid ${current_dir}/jvm/sw_java11/bin/java -javaagent:${current_dir}/console.jar -server -Djava.awt.headless=true -Xms2g -Xmx4g -Duser.timezone=GMT+08 -XX:+HeapDumpOnOutOfMemoryError -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.authenticate=false -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=0.0.0.0:20000 -Djava.rmi.server.hostname=127.0.0.1 -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --module-path=${current_dir}/commons/module_java11 --upgrade-module-path=${current_dir}/commons/module_java11/compiler.jar:${current_dir}/commons/module_java11/compiler-management.jar -jar ${current_dir}/console.jar

+ 40 - 0
o2server/start_windows.bat

@@ -0,0 +1,40 @@
+@echo off
+cd "%~dp0"
+if exist "%~dp0local\update" (
+	for  %%D in (commons,config,configSample,local,localSample,jvm,servers,store) do (
+		if not exist "%~dp0%%D" (
+			mkdir "%~dp0%%D"
+		)	
+	)
+	if exist "%~dp0local\update\o2server\version.o2" (
+		echo "update o2server."
+		for  %%D in (commons,config,configSample,local,localSample,jvm,servers,store) do (
+			if exist "%~dp0local\update\o2server\%%D" (
+				echo update %~dp0%%D
+				xcopy "%~dp0local\update\o2server\%%D" "%~dp0%%D" /S /Y /Q
+			)	
+		)
+		for  %%F in (console.jar,index.html,src.zip) do (
+			if exist "%~dp0local\update\o2server\%%F" (
+				echo update %~dp0%%F
+				copy "%~dp0local\update\o2server\%%F" "%~dp0"
+			)	
+		)
+		for  %%A in (start,stop,restart,console,service) do (
+			for  %%B in (_windows.bat,_windows_debug.bat,_linux.sh._linux_debug.sh,_linux_min.sh,_macosx64.sh,_macosx64_debug.sh,_macosarm.sh,_macosarm_debug.sh,_arm.sh,_arm_debug.sh,_mips.sh,_mips_debug.sh,_raspi.sh,_raspi_debug.sh,_sw.sh,_sw_debug.sh) do (
+				if exist "%~dp0local\update\o2server\%%A%%B" (
+					echo update %~dp0%%A%%B
+					copy "%~dp0local\update\o2server\%%A%%B" "%~dp0"
+				)	
+			)
+		)
+		echo update %~dp0version.o2.
+		copy "%~dp0local\update\o2server\version.o2" "%~dp0"
+		echo clean local\update directory.	
+		rmdir /S/Q "%~dp0local\update"
+		echo the update is complete, please restart the server.
+		exit
+	)
+)
+@echo on
+"%~dp0jvm\windows_java11\bin\java" -javaagent:"%~dp0console.jar" -server -Xms2g -Xmx4g -Duser.timezone=GMT+08 -XX:+HeapDumpOnOutOfMemoryError -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --module-path="%~dp0/commons/module_java11" --upgrade-module-path="%~dp0/commons/module_java11/compiler.jar;%~dp0/commons/module_java11/compiler-management.jar" -jar "%~dp0console.jar"

+ 40 - 0
o2server/start_windows_debug.bat

@@ -0,0 +1,40 @@
+@echo off
+cd "%~dp0"
+if exist "%~dp0local\update" (
+	for  %%D in (commons,config,configSample,local,localSample,jvm,servers,store) do (
+		if not exist "%~dp0%%D" (
+			mkdir "%~dp0%%D"
+		)	
+	)
+	if exist "%~dp0local\update\o2server\version.o2" (
+		echo "update o2server."
+		for  %%D in (commons,config,configSample,local,localSample,jvm,servers,store) do (
+			if exist "%~dp0local\update\o2server\%%D" (
+				echo update %~dp0%%D
+				xcopy "%~dp0local\update\o2server\%%D" "%~dp0%%D" /S /Y /Q
+			)	
+		)
+		for  %%F in (console.jar,index.html,src.zip) do (
+			if exist "%~dp0local\update\o2server\%%F" (
+				echo update %~dp0%%F
+				copy "%~dp0local\update\o2server\%%F" "%~dp0"
+			)	
+		)
+		for  %%A in (start,stop,restart,console,service) do (
+			for  %%B in (_windows.bat,_windows_debug.bat,_linux.sh._linux_debug.sh,_linux_min.sh,_macosx64.sh,_macosx64_debug.sh,_macosarm.sh,_macosarm_debug.sh,_arm.sh,_arm_debug.sh,_mips.sh,_mips_debug.sh,_raspi.sh,_raspi_debug,_sw.sh,_sw_debug.sh) do (
+				if exist "%~dp0local\update\o2server\%%A%%B" (
+					echo update %~dp0%%A%%B
+					copy "%~dp0local\update\o2server\%%A%%B" "%~dp0"
+				)	
+			)
+		)
+		echo update %~dp0version.o2.
+		copy "%~dp0local\update\o2server\version.o2" "%~dp0"
+		echo clean local\update directory.	
+		rmdir /S/Q "%~dp0local\update"
+		echo the update is complete, please restart the server.
+		exit
+	)
+)
+@echo on
+"%~dp0jvm\windows_java11\bin\java" -javaagent:"%~dp0console.jar" -server -Xms2g -Xmx4g -Duser.timezone=GMT+08 -XX:+HeapDumpOnOutOfMemoryError -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.authenticate=false -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=0.0.0.0:20000 -Djava.rmi.server.hostname=127.0.0.1 -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --module-path="%~dp0/commons/module_java11" --upgrade-module-path="%~dp0/commons/module_java11/compiler.jar;%~dp0/commons/module_java11/compiler-management.jar" -jar "%~dp0console.jar"

+ 18 - 0
o2server/stop_arm.sh

@@ -0,0 +1,18 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+    cd "$(dirname "$0")"
+    pwd
+)"
+cd ${current_dir}
+echo "ready to stop o2server path: ${current_dir}"
+${current_dir}/jvm/arm_java11/bin/java -cp ${current_dir}/console.jar com.x.server.console.swapcommand.Exit
+sleep 10
+PID=$(ps -ef | grep "${current_dir}/jvm/arm_java11/bin/java" | grep -v grep | awk '{print $2}')
+if [ "X$PID" != "X" ]; then
+    sleep 5
+    if [ "X$PID" != "X" ]; then
+        echo ready to kill server $PID
+        kill -9 $PID
+    fi
+fi

+ 18 - 0
o2server/stop_linux.sh

@@ -0,0 +1,18 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+    cd "$(dirname "$0")"
+    pwd
+)"
+cd ${current_dir}
+echo "ready to stop o2server path: ${current_dir}"
+${current_dir}/jvm/linux_java11/bin/java -cp ${current_dir}/console.jar com.x.server.console.swapcommand.Exit
+sleep 10
+PID=$(ps -ef | grep "${current_dir}/jvm/linux_java11/bin/java" | grep -v grep | awk '{print $2}')
+if [ "X$PID" != "X" ]; then
+    sleep 5
+    if [ "X$PID" != "X" ]; then
+        echo ready to kill server $PID
+        kill -9 $PID
+    fi
+fi

+ 18 - 0
o2server/stop_macosarm.sh

@@ -0,0 +1,18 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+    cd "$(dirname "$0")"
+    pwd
+)"
+cd ${current_dir}
+echo "ready to stop o2server path: ${current_dir}"
+${current_dir}/jvm/macos_java11/bin/java -cp ${current_dir}/console.jar com.x.server.console.swapcommand.Exit
+sleep 10
+PID=$(ps -ef | grep "${current_dir}/jvm/macosarm_java11/bin/java" | grep -v grep | awk '{print $2}')
+if [ "X$PID" != "X" ]; then
+    sleep 5
+    if [ "X$PID" != "X" ]; then
+        echo ready to kill server $PID
+        kill -9 $PID
+    fi
+fi

+ 18 - 0
o2server/stop_macosx64.sh

@@ -0,0 +1,18 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+    cd "$(dirname "$0")"
+    pwd
+)"
+cd ${current_dir}
+echo "ready to stop o2server path: ${current_dir}"
+${current_dir}/jvm/macos_java11/bin/java -cp ${current_dir}/console.jar com.x.server.console.swapcommand.Exit
+sleep 10
+PID=$(ps -ef | grep "${current_dir}/jvm/macosx64_java11/bin/java" | grep -v grep | awk '{print $2}')
+if [ "X$PID" != "X" ]; then
+    sleep 5
+    if [ "X$PID" != "X" ]; then
+        echo ready to kill server $PID
+        kill -9 $PID
+    fi
+fi

+ 18 - 0
o2server/stop_mips.sh

@@ -0,0 +1,18 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+    cd "$(dirname "$0")"
+    pwd
+)"
+cd ${current_dir}
+echo "ready to stop o2server path: ${current_dir}"
+${current_dir}/jvm/mips_java11/bin/java -cp ${current_dir}/console.jar com.x.server.console.swapcommand.Exit
+sleep 10
+PID=$(ps -ef | grep "${current_dir}/jvm/mips_java11/bin/java" | grep -v grep | awk '{print $2}')
+if [ "X$PID" != "X" ]; then
+    sleep 5
+    if [ "X$PID" != "X" ]; then
+        echo ready to kill server $PID
+        kill -9 $PID
+    fi
+fi

+ 18 - 0
o2server/stop_raspi.sh

@@ -0,0 +1,18 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+    cd "$(dirname "$0")"
+    pwd
+)"
+cd ${current_dir}
+echo "ready to stop o2server path: ${current_dir}"
+${current_dir}/jvm/raspi_java11/bin/java -cp ${current_dir}/console.jar com.x.server.console.swapcommand.Exit
+sleep 10
+PID=$(ps -ef | grep "${current_dir}/jvm/raspi_java11/bin/java" | grep -v grep | awk '{print $2}')
+if [ "X$PID" != "X" ]; then
+    sleep 5
+    if [ "X$PID" != "X" ]; then
+        echo ready to kill server $PID
+        kill -9 $PID
+    fi
+fi

+ 18 - 0
o2server/stop_sw.sh

@@ -0,0 +1,18 @@
+#!/bin/bash
+# Copyright (c) http://www.o2oa.net/
+current_dir="$(
+    cd "$(dirname "$0")"
+    pwd
+)"
+cd ${current_dir}
+echo "ready to stop o2server path: ${current_dir}"
+${current_dir}/jvm/sw_java11/bin/java -cp ${current_dir}/console.jar com.x.server.console.swapcommand.Exit
+sleep 10
+PID=$(ps -ef | grep "${current_dir}/jvm/sw_java11/bin/java" | grep -v grep | awk '{print $2}')
+if [ "X$PID" != "X" ]; then
+    sleep 5
+    if [ "X$PID" != "X" ]; then
+        echo ready to kill server $PID
+        kill -9 $PID
+    fi
+fi

+ 2 - 0
o2server/stop_windows.bat

@@ -0,0 +1,2 @@
+cd "%~dp0"
+"%~dp0jvm\windows_java11\bin\java" -cp "%~dp0console.jar" com.x.server.console.swapcommand.Exit

+ 24 - 0
o2server/store/jars/manifest.cfg

@@ -0,0 +1,24 @@
+x_attendance_core_entity.jar
+x_base_core_project.jar
+x_bbs_core_entity.jar
+x_calendar_core_entity.jar
+x_cms_core_entity.jar
+x_cms_core_express.jar
+x_component_core_entity.jar
+x_correlation_core_entity.jar
+x_correlation_core_express.jar
+x_file_core_entity.jar
+x_general_core_entity.jar
+x_hotpic_core_entity.jar
+x_meeting_core_entity.jar
+x_message_core_entity.jar
+x_mind_core_entity.jar
+x_organization_core_entity.jar
+x_organization_core_express.jar
+x_portal_core_entity.jar
+x_processplatform_core_entity.jar
+x_processplatform_core_express.jar
+x_program_center_core_entity.jar
+x_query_core_entity.jar
+x_query_core_express.jar
+x_jpush_core_entity.jar

+ 28 - 0
o2server/store/manifest.cfg

@@ -0,0 +1,28 @@
+x_attendance_assemble_control.war
+x_bbs_assemble_control.war
+x_calendar_assemble_control.war
+x_cms_assemble_control.war
+x_component_assemble_control.war
+x_correlation_service_processing.war
+x_file_assemble_control.war
+x_general_assemble_control.war
+x_hotpic_assemble_control.war
+x_meeting_assemble_control.war
+x_message_assemble_communicate.war
+x_mind_assemble_control.war
+x_organization_assemble_authentication.war
+x_organization_assemble_control.war
+x_organization_assemble_express.war
+x_organization_assemble_personal.war
+x_portal_assemble_designer.war
+x_portal_assemble_surface.war
+x_processplatform_assemble_bam.war
+x_processplatform_assemble_designer.war
+x_processplatform_assemble_surface.war
+x_processplatform_service_processing.war
+x_program_center.war
+x_program_init.war
+x_query_assemble_designer.war
+x_query_assemble_surface.war
+x_query_service_processing.war
+x_jpush_assemble_control.war

+ 138 - 0
o2server/x_attendance_assemble_control/pom.xml

@@ -0,0 +1,138 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>net.o2oa</groupId>
+		<artifactId>o2server</artifactId>
+		<version>9.0</version>
+	</parent>
+	<artifactId>x_attendance_assemble_control</artifactId>
+	<packaging>war</packaging>
+	<dependencies>
+		<dependency>
+			<groupId>net.o2oa</groupId>
+			<artifactId>x_base_core_project</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>net.o2oa</groupId>
+			<artifactId>x_attendance_core_entity</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>net.o2oa</groupId>
+			<artifactId>x_organization_core_express</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>net.o2oa</groupId>
+			<artifactId>x_processplatform_core_entity</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>net.o2oa</groupId>
+			<artifactId>x_general_core_entity</artifactId>
+		</dependency>
+	</dependencies>
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.codehaus.mojo</groupId>
+				<artifactId>exec-maven-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>describeBuilder</id>
+						<phase>prepare-package</phase>
+						<goals>
+							<goal>java</goal>
+						</goals>
+						<configuration>
+							<addOutputToClasspath>true</addOutputToClasspath>
+							<includePluginDependencies>true</includePluginDependencies>
+							<includeProjectDependencies>true</includeProjectDependencies>
+							<mainClass>com.x.base.core.project.annotation.DescribeBuilder</mainClass>
+							<arguments>
+								<argument>${basedir}</argument>
+								<argument>${project.build.sourceDirectory}</argument>
+							</arguments>
+						</configuration>
+					</execution>
+					<execution>
+						<id>apiBuilder</id>
+						<phase>prepare-package</phase>
+						<goals>
+							<goal>java</goal>
+						</goals>
+						<configuration>
+							<addOutputToClasspath>true</addOutputToClasspath>
+							<includePluginDependencies>true</includePluginDependencies>
+							<includeProjectDependencies>true</includeProjectDependencies>
+							<mainClass>com.x.base.core.project.annotation.ApiBuilder</mainClass>
+							<arguments>
+								<argument>${basedir}</argument>
+								<argument>${project.build.sourceDirectory}</argument>
+							</arguments>
+						</configuration>
+					</execution>
+					<execution>
+						<id>checkAssemble</id>
+						<phase>prepare-package</phase>
+						<goals>
+							<goal>java</goal>
+						</goals>
+						<configuration>
+							<addOutputToClasspath>true</addOutputToClasspath>
+							<includePluginDependencies>true</includePluginDependencies>
+							<includeProjectDependencies>true</includeProjectDependencies>
+							<mainClass>com.x.base.core.project.build.CheckAssemble</mainClass>
+						</configuration>
+					</execution>
+					<execution>
+						<id>createWebXml</id>
+						<phase>prepare-package</phase>
+						<goals>
+							<goal>java</goal>
+						</goals>
+						<configuration>
+							<addOutputToClasspath>true</addOutputToClasspath>
+							<includePluginDependencies>true</includePluginDependencies>
+							<includeProjectDependencies>true</includeProjectDependencies>
+							<mainClass>com.x.base.core.project.build.CreateWebXml</mainClass>
+							<arguments>
+								<argument>${basedir}</argument>
+								<argument>${project.artifactId}</argument>
+							</arguments>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<artifactId>maven-resources-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>copy-war</id>
+						<phase>verify</phase>
+						<goals>
+							<goal>copy-resources</goal>
+						</goals>
+						<configuration>
+							<outputDirectory>../store</outputDirectory>
+							<resources>
+								<resource>
+									<directory>target</directory>
+									<includes>
+										<include>${project.artifactId}.war</include>
+									</includes>
+								</resource>
+							</resources>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-deploy-plugin</artifactId>
+				<version>3.0.0-M2</version>
+				<configuration>
+					<skip>true</skip>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+</project>

+ 807 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/common/date/DateOperation.java

@@ -0,0 +1,807 @@
+package com.x.attendance.assemble.common.date;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+
+public class DateOperation {
+
+	private static Logger logger = LoggerFactory.getLogger(DateOperation.class);
+
+	/**
+	 * 根据样式得到格式化对象SimpleDateFormat
+	 * 
+	 * @param date
+	 * @param style
+	 * @return
+	 */
+	public String getDate(Date date, String style) {
+		SimpleDateFormat format = new SimpleDateFormat(style);
+		return format.format(date);
+	}
+
+	/**
+	 * 得到某日期的日期部分
+	 * 
+	 * @param date
+	 * @return yyyy-MM-dd
+	 */
+	public String getDate(Date date) {
+		return format1.format(date);
+	}
+
+	/**
+	 * 得到某日期的时间部分
+	 * 
+	 * @param date
+	 * @return HH:mm:ss
+	 */
+	public String getTime(Date date) {
+		return format2.format(date);
+	}
+
+	public Date getDateFromString(String dateString, String style) throws Exception {
+		Date date = null;
+		if (style == null || "".equals(style.trim())) {
+			date = format1.parse(dateString);
+		} else if ("yyyy-MM-dd HH:mm:ss".equalsIgnoreCase(style.trim())) {
+			date = format3.parse(dateString);
+		} else if ("yyyy-MM-dd HH:mm".equalsIgnoreCase(style.trim())) {
+			date = format3_2.parse(dateString);
+		} else if ("yyyy/MM/dd HH:mm:ss".equalsIgnoreCase(style.trim())) {
+			date = format3_1.parse(dateString);
+		} else if ("yyyy/MM/dd".equalsIgnoreCase(style.trim())) {
+			date = format4.parse(dateString);
+		} else if ("yyyy-MM-dd".equalsIgnoreCase(style.trim())) {
+			date = format1.parse(dateString);
+		} else if ("yyyyMMdd".equalsIgnoreCase(style.trim())) {
+			date = format5.parse(dateString);
+		} else if ("yyyyMMddHHmmss".equalsIgnoreCase(style.trim())) {
+			date = format7.parse(dateString);
+		} else if ("HH:mm:ss".equalsIgnoreCase(style.trim())) {
+			date = format2.parse(dateString);
+		} else if ("HH:mm".equalsIgnoreCase(style.trim())) {
+			date = format2_1.parse(dateString);
+		} else {
+			throw new Exception("对不起,您输入的日期style系统无法识别,请检查您的参数输入!");
+		}
+		return date;
+	}
+
+	/**
+	 * 将字符串转换为日期格式 会尝试多种格式转换,转换成功后返回结果
+	 * 
+	 * @param dateString
+	 * @return
+	 * @throws Exception
+	 */
+	public Date getDateFromString(String dateString) throws Exception {
+		Date date = null;
+		try {
+			date = getDateFromString(dateString, "yyyy-MM-dd HH:mm:ss");
+			return date;
+		} catch (Exception e) {
+			logger.debug(e.getMessage());
+		}
+
+		try {
+			date = getDateFromString(dateString, "yyyy-MM-dd HH:mm");
+			return date;
+		} catch (Exception e) {
+			logger.debug(e.getMessage());
+		}
+
+		try {
+			date = getDateFromString(dateString, "yyyy/MM/dd HH:mm:ss");
+			return date;
+		} catch (Exception e) {
+			logger.debug(e.getMessage());
+		}
+
+		try {
+			date = getDateFromString(dateString, "yyyy-MM-dd");
+			return date;
+		} catch (Exception e) {
+			logger.debug(e.getMessage());
+		}
+
+		try {
+			date = getDateFromString(dateString, "yyyy/MM/dd");
+			return date;
+		} catch (Exception e) {
+			logger.debug(e.getMessage());
+		}
+
+		try {
+			date = getDateFromString(dateString, "yyyyMMdd");
+			return date;
+		} catch (Exception e) {
+			logger.debug(e.getMessage());
+		}
+
+		try {
+			date = getDateFromString(dateString, "yyyyMMddHHmmss");
+			return date;
+		} catch (Exception e) {
+			logger.debug(e.getMessage());
+		}
+
+		try {
+			date = getDateFromString(dateString, "HH:mm:ss");
+			return date;
+		} catch (Exception e) {
+			logger.debug(e.getMessage());
+		}
+
+		try {
+			date = getDateFromString(dateString, "HH:mm");
+			return date;
+		} catch (Exception e) {
+			logger.debug(e.getMessage());
+		}
+
+		return date;
+	}
+
+	public String getDateStringFromDate(Date date, String style) throws Exception {
+		String dateString = null;
+		if (style == null || "".equals(style.trim())) {
+			dateString = format1.format(date);
+		} else if ("yyyy-MM-dd HH:mm:ss".equalsIgnoreCase(style.trim())) {
+			dateString = format3.format(date);
+		} else if ("yyyy/MM/dd HH:mm:ss".equalsIgnoreCase(style.trim())) {
+			dateString = format3_1.format(date);
+		} else if ("yyyy/MM/dd".equalsIgnoreCase(style.trim())) {
+			dateString = format4.format(date);
+		} else if ("yyyy-MM-dd".equalsIgnoreCase(style.trim())) {
+			dateString = format1.format(date);
+		} else if ("yyyyMMdd".equalsIgnoreCase(style.trim())) {
+			dateString = format5.format(date);
+		} else if ("yyyyMMddHHmmss".equalsIgnoreCase(style.trim())) {
+			dateString = format7.format(date);
+		} else if ("HH:mm:ss".equalsIgnoreCase(style.trim())) {
+			dateString = format2.format(date);
+		} else {
+			throw new Exception("对不起,您输入的日期style系统无法识别,请检查您的参数输入!style=" + style);
+		}
+		return dateString;
+	}
+
+	/**
+	 * 得到某日期加上或减去天数后的日期,day为负数时减去
+	 * 
+	 * @param dateString
+	 * @param day
+	 * @param style
+	 * @return
+	 * @throws Exception
+	 */
+	public String getDayAdd(String dateString, int day, String style) throws Exception {
+		Date date = getDateFromString(dateString, style);
+		return getDayAdd(date, day);
+	}
+
+	/**
+	 * 得到某日期加上或减去天数后的日期,day为负数时减去
+	 * 
+	 * @param date
+	 * @param day
+	 * @return
+	 */
+	public String getDayAdd(Date date, int day) {
+		Calendar calendar = Calendar.getInstance();
+		calendar.setTime(date);
+		calendar.add(Calendar.DATE, day);
+		return format1.format(calendar.getTime());
+	}
+
+	/**
+	 * 得到某日期加上或减去月份后的日期,month为负数时减去
+	 * 
+	 * @param date
+	 * @param month
+	 * @return "yyyy-MM-dd"
+	 */
+	public String getMonthAdd(Date date, int month) {
+		Calendar calendar = Calendar.getInstance();
+		calendar.setTime(date);
+		calendar.add(Calendar.MONTH, month);
+		return format1.format(calendar.getTime());
+	}
+
+	/**
+	 * 得到某日期加上或减去分钟后的日期,min为负数时减去
+	 * 
+	 * @param date
+	 * @param min
+	 * @return
+	 */
+	public String getMinutesAdd(Date date, int min) {
+		Calendar calendar = Calendar.getInstance();
+		calendar.setTime(date);
+		calendar.add(Calendar.MINUTE, min);
+		return format3.format(calendar.getTime());
+	}
+
+	/**
+	 * 得到某日期的日
+	 * 
+	 * @param date
+	 * @return
+	 */
+	public String getDay(Date date) {
+		try {
+			return format10.format(date);
+		} catch (Exception e) {
+			return "0";
+		}
+	}
+
+	public int getDayNumber(Date date) {
+		String result = null;
+		try {
+			result = format10.format(date);
+		} catch (Exception e) {
+			result = "0";
+		}
+		return Integer.parseInt(result);
+	}
+
+	/**
+	 * 得到某日期的月份
+	 * 
+	 * @param date
+	 * @return
+	 */
+	public String getMonth(Date date) {
+		try {
+			return format9.format(date);
+		} catch (Exception e) {
+			return "0";
+		}
+	}
+
+	public int getMonthNumber(Date date) {
+		String result = null;
+		try {
+			result = format9.format(date);
+		} catch (Exception e) {
+			result = "0";
+		}
+		return Integer.parseInt(result);
+	}
+
+	/**
+	 * 得到某日期的年份
+	 * 
+	 * @param date
+	 * @return
+	 */
+	public String getYear(Date date) {
+		try {
+			return format8.format(date);
+		} catch (Exception e) {
+			return "0";
+		}
+	}
+
+	public int getYearNumber(Date date) {
+		String result = null;
+		try {
+			result = format8.format(date);
+		} catch (Exception e) {
+			result = "0";
+		}
+		return Integer.parseInt(result);
+	}
+
+	/**
+	 * 得到某日期的小时
+	 * 
+	 * @param date
+	 * @return
+	 */
+	public String getHour(Date date) {
+		try {
+			return format11.format(date);
+		} catch (Exception e) {
+			return "0";
+		}
+	}
+
+	/**
+	 * 得到某日期的分钟
+	 * 
+	 * @param date
+	 * @return
+	 */
+	public String getMinites(Date date) {
+		return format12.format(date);
+	}
+
+	/**
+	 * 得到某日期的秒
+	 * 
+	 * @param date
+	 * @return
+	 */
+	public String getSeconds(Date date) {
+		return format13.format(date);
+	}
+
+	/**
+	 * 得到某年有多少天
+	 * 
+	 * @param date
+	 * @return
+	 * @throws ParseException
+	 */
+	@SuppressWarnings("static-access")
+	public int getDaysForYear(String date) throws ParseException {
+		Calendar calendar = Calendar.getInstance();
+		calendar.setTime(format1.parse(date));
+		return calendar.get(calendar.DAY_OF_YEAR);
+	}
+
+	/**
+	 * 得到某年有多少天
+	 * 
+	 * @param date
+	 * @return
+	 * @throws ParseException
+	 */
+	@SuppressWarnings("static-access")
+	public int getDaysForYear(Date date) throws ParseException {
+		Calendar calendar = Calendar.getInstance();
+		calendar.setTime(date);
+		return calendar.get(calendar.DAY_OF_YEAR);
+	}
+
+	/**
+	 * 得到某年有多少天
+	 * 
+	 * @param year
+	 * @return
+	 * @throws ParseException
+	 */
+	@SuppressWarnings("static-access")
+	public int getDaysForYear_YYYY(String year) throws ParseException {
+		Calendar calendar = Calendar.getInstance();
+		calendar.setTime(format8.parse(year));
+		return calendar.get(calendar.DAY_OF_YEAR);
+	}
+
+	/**
+	 * 得到某月有多少天
+	 * 
+	 * @param date
+	 * @return
+	 * @throws ParseException
+	 */
+	public int getDaysForMonth(String date) throws ParseException {
+		Calendar calendar = Calendar.getInstance();
+		calendar.setTime(format1.parse(date));
+		return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
+	}
+
+	/**
+	 * 得到某月有多少天
+	 * 
+	 * @param date
+	 * @return
+	 * @throws ParseException
+	 */
+	@SuppressWarnings("static-access")
+	public int getDaysForMonth_MM(String date) throws ParseException {
+		Calendar calendar = Calendar.getInstance();
+		calendar.setTime(format14.parse(date));
+		return calendar.get(calendar.DAY_OF_MONTH);
+	}
+
+	/**
+	 * 得到某月有多少天
+	 * 
+	 * @param date
+	 * @return
+	 * @throws ParseException
+	 */
+	public int getDaysForMonth(Date date) throws ParseException {
+		Calendar calendar = Calendar.getInstance();
+		calendar.setTime(date);
+		return calendar.get(Calendar.DAY_OF_MONTH);
+	}
+
+	/**
+	 * 得到当前的日期
+	 * 
+	 * @return
+	 */
+	public String getNowDate() {
+		return format1.format(new Date());
+	}
+
+	/**
+	 * 得到当前的时间
+	 * 
+	 * @return
+	 */
+	public String getNowTime() {
+		return format2.format(new Date());
+	}
+
+	/**
+	 * 得到当前的时间 yyyyMMddHHmmss
+	 * 
+	 * @return
+	 */
+	public String getNowTimeChar() {
+		return format7.format(new Date());
+	}
+
+	/**
+	 * 得到当前的时间 yyyy-MM-dd HH:mm:ss
+	 * 
+	 * @return
+	 */
+	public String getNowDateTime() {
+		return format3.format(new Date());
+	}
+
+	/**
+	 * 得到两个时间之前的分差
+	 * 
+	 * @param date1 yyyy-MM-dd HH:mm:ss
+	 * @param date2 yyyy-MM-dd HH:mm:ss
+	 * @return
+	 * @throws ParseException
+	 */
+	public long getDeff(String date1, String date2) throws ParseException {
+		long dayNumber = 0;
+		// 1小时=60分钟=3600秒=3600000
+		long mins = 60L * 1000L;
+		// long day= 24L * 60L * 60L * 1000L;计算天数之差
+		SimpleDateFormat df = null;
+		if (date1.length() == 19) {
+			df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+		} else if (date1.length() == 10) {
+			df = new SimpleDateFormat("yyyy-MM-dd");
+		} else if (date1.length() == 8) {
+			df = new SimpleDateFormat("HH:mm:ss");
+		} else {
+			df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+		}
+		java.util.Date d1 = df.parse(date1);
+		java.util.Date d2 = df.parse(date2);
+		dayNumber = (d2.getTime() - d1.getTime()) / mins;
+		return dayNumber;
+	}
+
+	/**
+	 * 得到两个时间之前的分差
+	 * 
+	 * @param date1 yyyy-MM-dd HH:mm:ss
+	 * @param date2 yyyy-MM-dd HH:mm:ss
+	 * @return
+	 */
+	public long getDeff(Date date1, Date date2) {
+		long dayNumber = 0;
+		long mins = 60L * 1000L;
+		dayNumber = (date2.getTime() - date1.getTime()) / mins;
+		return dayNumber;
+	}
+
+	/**
+	 * 日期格式转换 从YYYY-MM-DD转换到YYYYMMDD
+	 * 
+	 * @param dateString
+	 * @return
+	 * @throws ParseException
+	 */
+	public String changeDateFormat(String dateString) throws ParseException {
+		Date date;
+		String reslut = null;
+		try {
+			if ("".equals(dateString)) {
+				dateString = "0000-00-00";
+			}
+			date = format1.parse(dateString);
+			reslut = format5.format(date);
+		} catch (ParseException e) {
+			date = format1.parse("0000-00-00");
+		}
+		return reslut;
+	}
+
+	/** 格式 yyyy-MM-dd */
+	public final SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd");
+	/** 格式 HH:mm:ss */
+	public final SimpleDateFormat format2 = new SimpleDateFormat("HH:mm:ss");
+	public final SimpleDateFormat format2_1 = new SimpleDateFormat("HH:mm");
+	/** 格式 yyyy-MM-dd HH:mm:ss */
+	public final SimpleDateFormat format3 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+	public final SimpleDateFormat format3_2 = new SimpleDateFormat("yyyy-MM-dd HH:mm");
+	public final SimpleDateFormat format3_1 = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
+	/** 格式 yyyy/MM/dd */
+	public final SimpleDateFormat format4 = new SimpleDateFormat("yyyy/MM/dd");
+	/** 格式 yyyyMMdd */
+	public final SimpleDateFormat format5 = new SimpleDateFormat("yyyyMMdd");
+	/** 格式 HHmmss */
+	public final SimpleDateFormat format6 = new SimpleDateFormat("HHmmss");
+	/** 格式 yyyyMMddHHmmss */
+	public final SimpleDateFormat format7 = new SimpleDateFormat("yyyyMMddHHmmss");
+	/** 格式 yyyy */
+	public final SimpleDateFormat format8 = new SimpleDateFormat("yyyy");
+	/** 格式 MM */
+	public final SimpleDateFormat format9 = new SimpleDateFormat("MM");
+	/** 格式 dd */
+	public final SimpleDateFormat format10 = new SimpleDateFormat("dd");
+	/** 格式 HH */
+	public final SimpleDateFormat format11 = new SimpleDateFormat("HH");
+	/** 格式 mm */
+	public final SimpleDateFormat format12 = new SimpleDateFormat("mm");
+	/** 格式 ss */
+	public final SimpleDateFormat format13 = new SimpleDateFormat("ss");
+	/** 格式 ss */
+	public final SimpleDateFormat format14 = new SimpleDateFormat("yyyy-MM");
+
+	public String getStartOfWeek(String dateString) throws Exception {
+		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
+		Date date = format.parse(dateString);
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(date);
+		cal.setFirstDayOfWeek(Calendar.MONDAY);
+		int tmp = cal.get(Calendar.DAY_OF_WEEK) - 1;
+		if (0 == tmp) {
+			tmp = 7;
+		}
+		cal.add(Calendar.DATE, -(tmp - 1));
+		return getDateStringFromDate(cal.getTime(), "yyyy-MM-dd") + " 00:00:00";
+	}
+
+	public String getEndOfWeek(String dateString) throws Exception {
+		Date date = getDateFromString(getStartOfWeek(dateString), "yyyy-MM-dd HH:mm:ss");
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(date);
+		cal.setFirstDayOfWeek(Calendar.MONDAY);
+		cal.add(Calendar.DATE, 6);
+		return getDateStringFromDate(cal.getTime(), "yyyy-MM-dd") + " 23:59:59";
+	}
+
+	/**
+	 * 将时间格式转换为 **月**日**时**分的格式
+	 * 
+	 * @param dateString
+	 * @param style
+	 * @return
+	 * @throws Exception
+	 */
+	public String getDateCNString(String dateString, String style) throws Exception {
+		StringBuffer ch_date_string = new StringBuffer();
+		Date _date = null;
+		_date = getDateFromString(dateString, style);
+		if (_date == null) {
+			_date = new Date();
+		}
+		String year = getYear(_date);
+		String month = getMonth(_date);
+		String day = getDay(_date);
+		String hour = getHour(_date);
+		String min = getMinites(_date);
+		ch_date_string.append(year);
+		ch_date_string.append("年");
+		ch_date_string.append(month);
+		ch_date_string.append("月");
+		ch_date_string.append(day);
+		ch_date_string.append("日");
+		ch_date_string.append(hour);
+		ch_date_string.append("时");
+		ch_date_string.append(min);
+		ch_date_string.append("分");
+		return ch_date_string.toString();
+	}
+
+	/**
+	 * 将时间格式转换为 **月**日**时**分**秒 的格式
+	 * 
+	 * @param dateString
+	 * @param style
+	 * @return
+	 * @throws Exception
+	 */
+	public String getDateCNString2(String dateString, String style) throws Exception {
+		StringBuffer ch_date_string = new StringBuffer();
+		Date _date = null;
+		_date = getDateFromString(dateString, style);
+		if (_date == null) {
+			_date = new Date();
+		}
+		String year = getYear(_date);
+		String month = getMonth(_date);
+		String day = getDay(_date);
+		String hour = getHour(_date);
+		String min = getMinites(_date);
+		String sec = getSeconds(_date);
+		ch_date_string.append(year);
+		ch_date_string.append("年");
+		ch_date_string.append(month);
+		ch_date_string.append("月");
+		ch_date_string.append(day);
+		ch_date_string.append("日");
+		ch_date_string.append(hour);
+		ch_date_string.append("时");
+		ch_date_string.append(min);
+		ch_date_string.append("分");
+		ch_date_string.append(sec);
+		ch_date_string.append("秒");
+		return ch_date_string.toString();
+	}
+
+	/**
+	 * 获取日期在一年中的周数 结果从1开始
+	 * 
+	 * @param dateString yyyy-mm-dd
+	 * @return
+	 * @throws Exception
+	 */
+	public int getWeekNumOfYear(String dateString) throws Exception {
+		Date date = getDateFromString(dateString, "yyyy-MM-dd");
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(date);
+		cal.setFirstDayOfWeek(2); // 设置每周的第一天是星期一
+		return cal.get(Calendar.WEEK_OF_YEAR);
+	}
+
+	/**
+	 * 判断是否周末
+	 * 
+	 * @param recordDate
+	 * @return
+	 */
+	public boolean isWeekend(Date recordDate) {
+		Calendar cal = Calendar.getInstance();
+		cal.setTime(recordDate);
+		if (cal.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || cal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
+			return true;
+		}
+		return false;
+	}
+
+	public long getMinutes(Date date1, Date data2) {
+		long l = data2.getTime() - date1.getTime();
+		long min = ((l / (60 * 1000)));
+		return min;
+	}
+
+	/**
+	 * 根据提供的年份月份,获取当月所有的日期字符串:yyyy-mm-dd
+	 * 
+	 * @param s_year
+	 * @param _month
+	 * @return
+	 */
+	public List<String> getDateStringFormMonth(String s_year, String _month) {
+		List<String> result = new ArrayList<String>();
+		Calendar cal = Calendar.getInstance();
+		int year = 0;
+		int month = 0;
+		int days = 0;
+		try {
+			year = Integer.parseInt(s_year);
+		} catch (Exception e) {
+			logger.debug(e.getMessage());
+		}
+		try {
+			month = Integer.parseInt(_month);
+		} catch (Exception e) {
+			logger.debug(e.getMessage());
+		}
+		cal.set(year, month - 1, 1);
+		days = cal.getActualMaximum(Calendar.DATE);
+		for (int i = 1; i <= days; i++) {
+			result.add(s_year + "-" + (month < 10 ? "0" + month : month) + "-" + (i < 10 ? "0" + i : i));
+		}
+		return result;
+	}
+
+	/**
+	 * 根据提供的两个时间之间所有的日期字符串:yyyy-mm-dd
+	 * 
+	 * @param startDate
+	 * @param endDate
+	 * @return
+	 * @throws Exception
+	 */
+	public List<String> listDateStringBetweenDate(Date startDate, Date endDate) throws Exception {
+		List<String> result = new ArrayList<String>();
+		Calendar cal = Calendar.getInstance();
+		startDate = getDateFromString(getDateStringFromDate(startDate, "yyyy-MM-dd"));
+		endDate = getDateFromString(getDateStringFromDate(endDate, "yyyy-MM-dd") + " 23:59:59");
+		cal.setTime(startDate);
+		while (cal.getTime().before(endDate)) {
+			result.add(getDateStringFromDate(cal.getTime(), "yyyy-MM-dd"));
+			cal.add(Calendar.DATE, 1);
+		}
+		return result;
+	}
+
+	/**
+	 * 获取日期所在月份的第一天
+	 * 
+	 * @param recordDate
+	 * @return
+	 * @throws Exception
+	 */
+	public Date getFirstDateInMonth(Date recordDate) throws Exception {
+		String year = format8.format(recordDate);
+		String month = format9.format(recordDate);
+		return getDateFromString(year + "-" + month + "-01");
+	}
+
+	/**
+	 * 获取日期所在月份的第一天
+	 * 
+	 * @param recordDate yyyy-MM-dd
+	 * @return
+	 * @throws Exception
+	 */
+	public String getFirstDateStringInMonth(Date recordDate) throws Exception {
+		String year = format8.format(recordDate);
+		String month = format9.format(recordDate);
+		return year + "-" + month + "-01";
+	}
+
+	/**
+	 * 获取当月的最后一日
+	 * 
+	 * @param date
+	 * @return
+	 */
+	public static Date getLastDayOfMonth(Date date) {
+		Calendar calendar = convert(date);
+		// calendar.set(Calendar.DATE, calendar.getMaximum(Calendar.DATE));
+		calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
+		return calendar.getTime();
+	}
+
+	/**
+	 * 将日期转换为日历
+	 * 
+	 * @param date 日期
+	 * @return 日历
+	 */
+	private static Calendar convert(Date date) {
+		Calendar calendar = Calendar.getInstance();
+		calendar.setTime(date);
+		return calendar;
+	}
+
+	/**
+	 * 获取日期所在月份的第一天
+	 * 
+	 * @param recordDate
+	 * @return
+	 * @throws Exception
+	 */
+	public Date getLastDateInMonth(Date recordDate) throws Exception {
+		Date lastDate = getLastDayOfMonth(recordDate);
+		return getDateFromString(getDateStringFromDate(lastDate, "yyyy-MM-dd"));
+	}
+
+	/**
+	 * 获取日期所在月份的第一天
+	 * 
+	 * @param recordDate yyyy-MM-dd
+	 * @return
+	 * @throws Exception
+	 */
+	public String getLastDateStringInMonth(Date recordDate) throws Exception {
+		Date lastDate = getLastDayOfMonth(recordDate);
+		return getDateStringFromDate(lastDate, "yyyy-MM-dd");
+	}
+}

+ 275 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/common/excel/reader/Excel2003Reader.java

@@ -0,0 +1,275 @@
+package com.x.attendance.assemble.common.excel.reader;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.poi.hssf.eventusermodel.EventWorkbookBuilder.SheetRecordCollectingListener;
+import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener;
+import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
+import org.apache.poi.hssf.eventusermodel.HSSFListener;
+import org.apache.poi.hssf.eventusermodel.HSSFRequest;
+import org.apache.poi.hssf.eventusermodel.MissingRecordAwareHSSFListener;
+import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord;
+import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord;
+import org.apache.poi.hssf.model.HSSFFormulaParser;
+import org.apache.poi.hssf.record.BOFRecord;
+import org.apache.poi.hssf.record.BlankRecord;
+import org.apache.poi.hssf.record.BoolErrRecord;
+import org.apache.poi.hssf.record.BoundSheetRecord;
+import org.apache.poi.hssf.record.FormulaRecord;
+import org.apache.poi.hssf.record.LabelRecord;
+import org.apache.poi.hssf.record.LabelSSTRecord;
+import org.apache.poi.hssf.record.NumberRecord;
+import org.apache.poi.hssf.record.Record;
+import org.apache.poi.hssf.record.SSTRecord;
+import org.apache.poi.hssf.record.StringRecord;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+
+/**
+ * 抽象Excel2003读取器,通过实现HSSFListener监听器,采用事件驱动模式解析excel2003
+ * 中的内容,遇到特定事件才会触发,大大减少了内存的使用。
+ */
+public class Excel2003Reader implements HSSFListener {
+	private int minColumns = -1;
+	// private POIFSFileSystem fs;
+	private int lastRowNumber;
+	private int lastColumnNumber;
+
+	/** Should we output the formula, or the value it has? */
+	private boolean outputFormulaValues = true;
+
+	/** For parsing Formulas */
+	private SheetRecordCollectingListener workbookBuildingListener;
+	// excel2003工作薄
+	private HSSFWorkbook stubWorkbook;
+
+	// Records we pick up as we process
+	private SSTRecord sstRecord;
+	private FormatTrackingHSSFListener formatListener;
+
+	// 表索引
+	private int sheetIndex = -1;
+	private BoundSheetRecord[] orderedBSRs;
+	@SuppressWarnings("rawtypes")
+	private ArrayList boundSheetRecords = new ArrayList();
+
+	// For handling formulas with string results
+	private int nextRow;
+	private int nextColumn;
+	private boolean outputNextStringRecord;
+	// 当前行
+	private int curRow = 0;
+	// 存储行记录的容器
+	private List<String> rowlist = new ArrayList<String>();;
+	@SuppressWarnings("unused")
+	private String sheetName;
+
+	private String fileKey;
+	private int startRow;
+	private IRowReader rowReader;
+
+	public void setRowReader(IRowReader rowReader, String fileKey, int startRow) {
+		this.rowReader = rowReader;
+		this.fileKey = fileKey;
+		this.startRow = startRow;
+	}
+
+	/**
+	 * 遍历excel下所有的sheet
+	 * 
+	 * @param fileKey
+	 * @throws IOException
+	 */
+	public void process(String fileName) throws IOException {
+
+		try (FileInputStream fis = new FileInputStream(fileName); POIFSFileSystem fs = new POIFSFileSystem(fis)) {
+			MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this);
+			formatListener = new FormatTrackingHSSFListener(listener);
+			HSSFEventFactory factory = new HSSFEventFactory();
+			HSSFRequest request = new HSSFRequest();
+			if (outputFormulaValues) {
+				request.addListenerForAllRecords(formatListener);
+			} else {
+				workbookBuildingListener = new SheetRecordCollectingListener(formatListener);
+				request.addListenerForAllRecords(workbookBuildingListener);
+			}
+			factory.processWorkbookEvents(request, fs);
+		}
+		// 数据读取完成
+	}
+
+	/**
+	 * 遍历excel下所有的sheet
+	 * 
+	 * @param fileKey
+	 * @throws IOException
+	 */
+	public void process(InputStream is) throws IOException {
+		try (POIFSFileSystem fs = new POIFSFileSystem(is)) {
+			MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this);
+			formatListener = new FormatTrackingHSSFListener(listener);
+			HSSFEventFactory factory = new HSSFEventFactory();
+			HSSFRequest request = new HSSFRequest();
+			if (outputFormulaValues) {
+				request.addListenerForAllRecords(formatListener);
+			} else {
+				workbookBuildingListener = new SheetRecordCollectingListener(formatListener);
+				request.addListenerForAllRecords(workbookBuildingListener);
+			}
+			factory.processWorkbookEvents(request, fs);
+		}
+		// 数据读取完成
+	}
+
+	/**
+	 * HSSFListener 监听方法,处理 Record
+	 */
+	@SuppressWarnings("unchecked")
+	public void processRecord(Record record) {
+		int thisRow = -1;
+		int thisColumn = -1;
+		String thisStr = null;
+		String value = null;
+		switch (record.getSid()) {
+		case BoundSheetRecord.sid:
+			boundSheetRecords.add(record);
+			break;
+		case BOFRecord.sid:
+			BOFRecord br = (BOFRecord) record;
+			if (br.getType() == BOFRecord.TYPE_WORKSHEET) {
+				// 如果有需要,则建立子工作薄
+				if (workbookBuildingListener != null && stubWorkbook == null) {
+					stubWorkbook = workbookBuildingListener.getStubHSSFWorkbook();
+				}
+
+				sheetIndex++;
+				if (orderedBSRs == null) {
+					orderedBSRs = BoundSheetRecord.orderByBofPosition(boundSheetRecords);
+				}
+				sheetName = orderedBSRs[sheetIndex].getSheetname();
+			}
+			break;
+
+		case SSTRecord.sid:
+			sstRecord = (SSTRecord) record;
+			break;
+
+		case BlankRecord.sid:
+			BlankRecord brec = (BlankRecord) record;
+			thisRow = brec.getRow();
+			thisColumn = brec.getColumn();
+			thisStr = "";
+			rowlist.add(thisColumn, thisStr);
+			break;
+		case BoolErrRecord.sid: // 单元格为布尔类型
+			BoolErrRecord berec = (BoolErrRecord) record;
+			thisRow = berec.getRow();
+			thisColumn = berec.getColumn();
+			thisStr = berec.getBooleanValue() + "";
+			rowlist.add(thisColumn, thisStr);
+			break;
+
+		case FormulaRecord.sid: // 单元格为公式类型
+			FormulaRecord frec = (FormulaRecord) record;
+			thisRow = frec.getRow();
+			thisColumn = frec.getColumn();
+			if (outputFormulaValues) {
+				if (Double.isNaN(frec.getValue())) {
+					// Formula result is a string
+					// This is stored in the next record
+					outputNextStringRecord = true;
+					nextRow = frec.getRow();
+					nextColumn = frec.getColumn();
+				} else {
+					thisStr = formatListener.formatNumberDateCell(frec);
+				}
+			} else {
+				thisStr = '"' + HSSFFormulaParser.toFormulaString(stubWorkbook, frec.getParsedExpression()) + '"';
+			}
+			rowlist.add(thisColumn, thisStr);
+			break;
+		case StringRecord.sid:// 单元格中公式的字符串
+			if (outputNextStringRecord) {
+				// String for formula
+				StringRecord srec = (StringRecord) record;
+				thisStr = srec.getString();
+				thisRow = nextRow;
+				thisColumn = nextColumn;
+				outputNextStringRecord = false;
+			}
+			break;
+		case LabelRecord.sid:
+			LabelRecord lrec = (LabelRecord) record;
+			curRow = thisRow = lrec.getRow();
+			thisColumn = lrec.getColumn();
+			value = lrec.getValue().trim();
+			value = value.equals("") ? " " : value;
+			this.rowlist.add(thisColumn, value);
+			break;
+		case LabelSSTRecord.sid: // 单元格为字符串类型
+			LabelSSTRecord lsrec = (LabelSSTRecord) record;
+			curRow = thisRow = lsrec.getRow();
+			thisColumn = lsrec.getColumn();
+			if (sstRecord == null) {
+				rowlist.add(thisColumn, " ");
+			} else {
+				value = sstRecord.getString(lsrec.getSSTIndex()).toString().trim();
+				value = value.equals("") ? " " : value;
+				rowlist.add(thisColumn, value);
+			}
+			break;
+		case NumberRecord.sid: // 单元格为数字类型
+			NumberRecord numrec = (NumberRecord) record;
+			curRow = thisRow = numrec.getRow();
+			thisColumn = numrec.getColumn();
+			value = formatListener.formatNumberDateCell(numrec).trim();
+			value = value.equals("") ? " " : value;
+			// 向容器加入列值
+			rowlist.add(thisColumn, value);
+			break;
+		default:
+			break;
+		}
+
+		// 遇到新行的操作
+		if (thisRow != -1 && thisRow != lastRowNumber) {
+			lastColumnNumber = -1;
+		}
+
+		// 空值的操作
+		if (record instanceof MissingCellDummyRecord) {
+			MissingCellDummyRecord mc = (MissingCellDummyRecord) record;
+			curRow = thisRow = mc.getRow();
+			thisColumn = mc.getColumn();
+			rowlist.add(thisColumn, " ");
+		}
+
+		// 更新行和列的值
+		if (thisRow > -1)
+			lastRowNumber = thisRow;
+		if (thisColumn > -1)
+			lastColumnNumber = thisColumn;
+
+		// 行结束时的操作
+		if (record instanceof LastCellOfRowDummyRecord) {
+			if (minColumns > 0) {
+				// 列值重新置空
+				if (lastColumnNumber == -1) {
+					lastColumnNumber = 0;
+				}
+			}
+			lastColumnNumber = -1;
+			// 每行结束时, 调用getRows() 方法
+
+			rowReader.getRows(sheetIndex, curRow, rowlist, this.fileKey, this.startRow);
+
+			// 清空容器
+			rowlist.clear();
+		}
+	}
+
+}

+ 239 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/common/excel/reader/Excel2007Reader.java

@@ -0,0 +1,239 @@
+package com.x.attendance.assemble.common.excel.reader;
+
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.poi.openxml4j.opc.OPCPackage;
+import org.apache.poi.ss.usermodel.DateUtil;
+import org.apache.poi.xssf.eventusermodel.XSSFReader;
+import org.apache.poi.xssf.model.SharedStringsTable;
+import org.apache.poi.xssf.usermodel.XSSFRichTextString;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+
+/**
+ * 抽象Excel2007读取器,excel2007的底层数据结构是xml文件,采用SAX的事件驱动的方法解析
+ * xml,需要继承DefaultHandler,在遇到文件内容时,事件会触发,这种做法可以大大降低 内存的耗费,特别使用于大数据量的文件。
+ *
+ */
+
+public class Excel2007Reader extends DefaultHandler {
+
+    private static Logger logger = LoggerFactory.getLogger(Excel2007Reader.class);
+    // 共享字符串表
+    private SharedStringsTable sst;
+    // 上一次的内容
+    private String lastContents;
+
+    private boolean nextIsString;
+
+    private int sheetIndex = -1;
+
+    private List<String> rowlist = new ArrayList<String>();
+    // 当前行
+    private int curRow = 0;
+    // 当前列
+    private int curCol = 0;
+    // 日期标志
+    private boolean dateFlag;
+    // 数字标志
+    private boolean numberFlag;
+
+    private boolean isTElement;
+
+    private String fileKey;
+    private int startRow;
+    private IRowReader rowReader;
+
+    public void setRowReader(IRowReader rowReader, String fileKey, int startRow) {
+        this.rowReader = rowReader;
+        this.fileKey = fileKey;
+        this.startRow = startRow;
+    }
+
+    /**
+     * 只遍历一个电子表格,其中sheetId为要遍历的sheet索引,从1开始,1-3
+     * 
+     * @param filename
+     * @param sheetId
+     * @throws Exception
+     */
+    public void processOneSheet(String filename, int sheetId) throws Exception {
+        OPCPackage pkg = OPCPackage.open(filename);
+        XSSFReader r = new XSSFReader(pkg);
+        SharedStringsTable sst = (SharedStringsTable) r.getSharedStringsTable();
+        XMLReader parser = fetchSheetParser(sst);
+
+        // 根据 rId# 或 rSheet# 查找sheet
+        InputStream sheet2 = r.getSheet("rId" + sheetId);
+        sheetIndex++;
+        InputSource sheetSource = new InputSource(sheet2);
+        parser.parse(sheetSource);
+        sheet2.close();
+    }
+
+    /**
+     * 遍历工作簿中所有的电子表格
+     * 
+     * @param filename
+     * @param fileKey
+     * @throws Exception
+     */
+    public void process(String filename) throws Exception {
+        OPCPackage pkg = OPCPackage.open(filename);
+        XSSFReader r = new XSSFReader(pkg);
+        SharedStringsTable sst = (SharedStringsTable) r.getSharedStringsTable();
+        XMLReader parser = fetchSheetParser(sst);
+        Iterator<InputStream> sheets = r.getSheetsData();
+        while (sheets.hasNext()) {
+            curRow = 0;
+            sheetIndex++;
+            InputStream sheet = sheets.next();
+            InputSource sheetSource = new InputSource(sheet);
+            parser.parse(sheetSource);
+            sheet.close();
+        }
+        // 数据读取完成
+    }
+
+    /**
+     * 遍历工作簿中所有的电子表格
+     * 
+     * @param filename
+     * @param fileKey
+     * @throws Exception
+     */
+    public void process(InputStream is) throws Exception {
+        OPCPackage pkg = OPCPackage.open(is);
+        XSSFReader r = new XSSFReader(pkg);
+        SharedStringsTable sst = (SharedStringsTable) r.getSharedStringsTable();
+        XMLReader parser = fetchSheetParser(sst);
+        Iterator<InputStream> sheets = r.getSheetsData();
+        while (sheets.hasNext()) {
+            curRow = 0;
+            sheetIndex++;
+            InputStream sheet = sheets.next();
+            InputSource sheetSource = new InputSource(sheet);
+            parser.parse(sheetSource);
+            sheet.close();
+        }
+    }
+
+    public XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException {
+        XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
+        this.sst = sst;
+        parser.setContentHandler(this);
+        return parser;
+    }
+
+    public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
+
+        // c => 单元格
+        if ("c".equals(name)) {
+            // 如果下一个元素是 SST 的索引,则将nextIsString标记为true
+            String cellType = attributes.getValue("t");
+            if ("s".equals(cellType)) {
+                nextIsString = true;
+            } else {
+                nextIsString = false;
+            }
+            // 日期格式
+            String cellDateType = attributes.getValue("s");
+            if ("1".equals(cellDateType)) {
+                dateFlag = true;
+            } else {
+                dateFlag = false;
+            }
+            String cellNumberType = attributes.getValue("s");
+            if ("2".equals(cellNumberType)) {
+                numberFlag = true;
+            } else {
+                numberFlag = false;
+            }
+
+        }
+        // 当元素为t时
+        if ("t".equals(name)) {
+            isTElement = true;
+        } else {
+            isTElement = false;
+        }
+
+        // 置空
+        lastContents = "";
+    }
+
+    public void endElement(String uri, String localName, String name) throws SAXException {
+
+        // 根据SST的索引值的到单元格的真正要存储的字符串
+        // 这时characters()方法可能会被调用多次
+        if (nextIsString) {
+            try {
+                int idx = Integer.parseInt(lastContents);
+                lastContents = sst.getItemAt(idx).toString();
+            } catch (Exception e) {
+                logger.error(e);
+            }
+        }
+        // t元素也包含字符串
+        if (isTElement) {
+            String value = lastContents.trim();
+            rowlist.add(curCol, value);
+            curCol++;
+            isTElement = false;
+            // v => 单元格的值,如果单元格是字符串则v标签的值为该字符串在SST中的索引
+            // 将单元格内容加入rowlist中,在这之前先去掉字符串前后的空白符
+        } else if ("v".equals(name)) {
+            String value = lastContents.trim();
+            value = value.equals("") ? " " : value;
+            // 日期格式处理
+            if (dateFlag) {
+                try {
+                    Date date = DateUtil.getJavaDate(Double.valueOf(value));
+                    SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
+                    value = dateFormat.format(date);
+                } catch (Exception e) {
+                    logger.error(e);
+                }
+            }
+            // 数字类型处理
+            if (numberFlag) {
+                try {
+                    BigDecimal bd = new BigDecimal(value);
+                    value = bd.setScale(3, BigDecimal.ROUND_UP).toString();
+                } catch (Exception e) {
+                    logger.error(e);
+                }
+            }
+            rowlist.add(curCol, value);
+            curCol++;
+        } else {
+            // 如果标签名称为 row ,这说明已到行尾,调用 optRows() 方法
+            if (name.equals("row")) {
+
+                rowReader.getRows(sheetIndex, curRow, rowlist, this.fileKey, this.startRow);
+                rowlist.clear();
+                curRow++;
+                curCol = 0;
+            }
+        }
+
+    }
+
+    public void characters(char[] ch, int start, int length) throws SAXException {
+        // 得到单元格内容的值
+        lastContents += new String(ch, start, length);
+    }
+}

+ 58 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/common/excel/reader/ExcelReaderUtil.java

@@ -0,0 +1,58 @@
+package com.x.attendance.assemble.common.excel.reader;
+
+import java.io.InputStream;
+
+public class ExcelReaderUtil {
+	
+	//excel2003扩展名
+	public static final String EXCEL03_EXTENSION = ".xls";
+	//excel2007扩展名
+	public static final String EXCEL07_EXTENSION = ".xlsx";
+	
+	/**
+	 * 读取Excel文件,可能是03也可能是07版本
+	 * @param excel03
+	 * @param excel07
+	 * @param fileName
+	 * @throws Exception 
+	 */
+	public static void readExcel( IRowReader reader, String fileName, String fileKey, int startRow ) throws Exception{
+		// 处理excel2003文件
+		if (fileName.endsWith(EXCEL03_EXTENSION)){
+			Excel2003Reader excel03 = new Excel2003Reader();
+			excel03.setRowReader(reader, fileKey, startRow);
+			excel03.process(fileName);
+		// 处理excel2007文件
+		} else if (fileName.endsWith(EXCEL07_EXTENSION)){
+			Excel2007Reader excel07 = new Excel2007Reader();
+			excel07.setRowReader(reader, fileKey, startRow);
+			excel07.process(fileName);
+		} else {
+			throw new  Exception("文件格式错误,fileName的扩展名只能是xls或xlsx。");
+		}
+	}
+	
+	public static void readExcel( IRowReader reader, InputStream is, String fileName, String fileKey, int startRow ) throws Exception{
+		// 处理excel2003文件
+		if (fileName.endsWith(EXCEL03_EXTENSION)){
+			if ( is != null ){
+				Excel2003Reader excel03 = new Excel2003Reader();
+				excel03.setRowReader( reader, fileKey, startRow );
+				excel03.process( is );
+			} else {
+				throw new  Exception("there is no input stream.");
+			}
+		// 处理excel2007文件
+		} else if (fileName.endsWith(EXCEL07_EXTENSION)){
+			if ( is != null ){
+				Excel2007Reader excel07 = new Excel2007Reader();
+				excel07.setRowReader(reader, fileKey, startRow);
+				excel07.process( is );
+			} else {
+				throw new  Exception("there is no input stream.");
+			}
+		} else {
+			throw new  Exception("文件格式错误,fileName的扩展名只能是xls或xlsx。");
+		}
+	}
+}

+ 13 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/common/excel/reader/IRowReader.java

@@ -0,0 +1,13 @@
+package com.x.attendance.assemble.common.excel.reader;
+
+import java.util.List;
+
+public interface IRowReader {
+	
+	/**业务逻辑实现方法
+	 * @param sheetIndex
+	 * @param curRow
+	 * @param rowlist
+	 */
+	public  void getRows(int sheetIndex,int curRow, List<String> rowlist, String fileKey, int startRow);
+}

+ 23 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/common/excel/reader/ImportExcelReader.java

@@ -0,0 +1,23 @@
+package com.x.attendance.assemble.common.excel.reader;
+
+import java.util.List;
+
+import com.x.attendance.assemble.control.processor.sender.SenderForValidateData;
+
+public class ImportExcelReader implements IRowReader{
+	
+	/* 业务逻辑实现方法
+	 * @see com.eprosun.util.excel.IRowReader#getRows(int, int, java.util.List)
+	 */
+	public void getRows( int sheetIndex, int curRow, List<String> colmlist, String fileKey, int startRow ) {
+		if( curRow < startRow ){
+			return;
+		}
+
+		if( colmlist != null && !colmlist.isEmpty() ) {
+			if( !colmlist.get(0).isEmpty() && !colmlist.get(2).isEmpty()){
+				new SenderForValidateData().execute( sheetIndex, curRow, colmlist, fileKey );
+			}
+		}
+	}	
+}

+ 225 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/common/excel/reader/UserModelEventListener.java

@@ -0,0 +1,225 @@
+package com.x.attendance.assemble.common.excel.reader;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.poi.hssf.eventusermodel.EventWorkbookBuilder.SheetRecordCollectingListener;
+import org.apache.poi.hssf.eventusermodel.HSSFListener;
+import org.apache.poi.hssf.record.BOFRecord;
+import org.apache.poi.hssf.record.BlankRecord;
+import org.apache.poi.hssf.record.BoolErrRecord;
+import org.apache.poi.hssf.record.BoundSheetRecord;
+import org.apache.poi.hssf.record.FormulaRecord;
+import org.apache.poi.hssf.record.LabelSSTRecord;
+import org.apache.poi.hssf.record.NumberRecord;
+import org.apache.poi.hssf.record.Record;
+import org.apache.poi.hssf.record.RowRecord;
+import org.apache.poi.hssf.record.SSTRecord;
+
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+
+/**
+ * 基于POI HSSF的eventmodel 模型的时间解析方式
+ *   优点:解析数据相当快。
+ *   缺点:1.仅仅支持97~2003版本的excel,不支持2007版本的excel。
+ *         2.只能读Excel中一个Sheet页面。
+ * 
+ * @Title: 
+ * @Description: 实现TODO
+ * @Copyright:Copyright (c) 2011
+ * @Date:2012-6-14
+ * @author  O2LEE
+ * @version 1.0
+ */
+public class UserModelEventListener implements HSSFListener {
+	
+	private static Logger logger=LoggerFactory.getLogger( UserModelEventListener.class );
+	private SSTRecord sstrec;
+	/** Should we output the formula, or the value it has? */
+	private boolean outputFormulaValues = true;
+	/** For parsing Formulas */
+	private SheetRecordCollectingListener workbookBuildingListener;
+
+    //当前Sheet的内容
+    private List<Map<String,Object>> currentSheetDataMap=new ArrayList<Map<String,Object>>();
+    //列对应的字段
+	private static String[] trianListheadTitle=new String[]{"trainCode","firstStation","lastStation","startStation","arriveStation","startTime","arriveTime","fistLevelPrice","secondLevelPrice","km","useDate"};
+
+    //一行记录
+    private Map<String,Object> currentSheetRowDataMap=new HashMap<String,Object>();
+    private int curRowNum=0;
+    private int ignoreRowNum=1;
+    private int sheetNo=0;
+    
+    private Boolean debugger = false;
+
+	@Override
+	public void processRecord( Record record ) {
+		switch (record.getSid()) {
+		
+		case BOFRecord.sid:
+			BOFRecord bof = (BOFRecord) record;
+			//顺序进入新的Workbook  
+			if (bof.getType() == bof.TYPE_WORKBOOK) {
+				logger.debug( debugger, ">>>>>>>>>>开始解析excel 文档.....");
+			//顺序进入新的Worksheet,因为Event API不会把Excel文件里的所有数据结构都关联起来,
+			//所以这儿一定要记录现在进入第几个sheet了。
+			} else if (bof.getType() == bof.TYPE_WORKSHEET) {
+				//读取新的一个Sheet页
+				logger.debug( debugger, ">>>>>>>>>>开始解析sheet页面内容...");
+				sheetNo++;
+				currentSheetDataMap=new ArrayList<Map<String,Object>>();
+			}
+			break;
+	    //开始解析Sheet的信息,记录sheet,这儿会把所有的sheet都顺序打印出来,如果有多个sheet的话,可以顺序记入到一个List里   
+		case BoundSheetRecord.sid:
+			BoundSheetRecord bsr = (BoundSheetRecord) record;
+			logger.debug( debugger, ">>>>>>>>>>New sheet named: " + bsr.getSheetname());
+			break;
+		//执行行记录事件
+		case RowRecord.sid:
+			RowRecord rowrec = (RowRecord) record;
+			logger.debug( debugger, ">>>>>>>>>>记录开始, first column at "
+					+ rowrec.getFirstCol() + " last column at "
+					+ rowrec.getLastCol());
+			break;
+		// SSTRecords store a array of unique strings used in Excel.
+		case SSTRecord.sid:
+			sstrec = (SSTRecord) record;
+			for (int k = 0; k < sstrec.getNumUniqueStrings(); k++) {
+				logger.debug( debugger, ">>>>>>>>>>String table value " + k + " = "
+						+ sstrec.getString(k));
+			}
+			break;
+			
+		//发现数字类型的cell,因为数字和日期都是用这个格式,所以下面一定要判断是不是日期格式,另外默认的数字也会被视为日期格式,所以如果是数字的话,一定要明确指定格式!!!!!!!   
+		case NumberRecord.sid:
+				NumberRecord nr = (NumberRecord) record;
+				//HSSFDateUtil.isInternalDateFormat(nr.getXFIndex())  判断是否为时间列
+				int column=nr.getColumn();
+				if(column==5||column==6){
+					addDataAndrChangeRow(nr.getRow(),nr.getColumn(),getTime(nr.getValue()), debugger);
+				}else{
+					addDataAndrChangeRow(nr.getRow(),nr.getColumn(),(int)nr.getValue(), debugger);
+				}
+				break;
+		//发现字符串类型,这儿要取字符串的值的话,跟据其index去字符串表里读取   
+		case LabelSSTRecord.sid:
+			LabelSSTRecord lsr = (LabelSSTRecord)record; 
+			addDataAndrChangeRow(lsr.getRow(),lsr.getColumn(), sstrec.getString(lsr.getSSTIndex()), debugger);
+			logger.debug( debugger, ">>>>>>>>>>文字列:"+sstrec.getString(lsr.getSSTIndex())+", 行:"+lsr.getRow()+", 列:"+lsr.getColumn());   
+			break;
+	    case BoolErrRecord.sid: //解析boolean错误信息
+            BoolErrRecord ber = (BoolErrRecord)record;   
+            if(ber.isBoolean()){   
+            	addDataAndrChangeRow(ber.getRow(),ber.getColumn(), ber.getBooleanValue(), debugger);
+            	logger.debug( debugger, ">>>>>>>>>>Boolean:"+ber.getBooleanValue()+", 行:"+ber.getRow()+", 列:"+ber.getColumn());   
+            }   
+            if(ber.isError()){   
+            	logger.debug( debugger, ">>>>>>>>>>Error:"+ber.getErrorValue()+", 行:"+ber.getRow()+", 列:"+ber.getColumn());   
+            }   
+            break;   
+         //空白记录的信息
+        case BlankRecord.sid: 
+            BlankRecord br = (BlankRecord)record;   
+            addDataAndrChangeRow(br.getRow(),br.getColumn(), "", debugger);
+            logger.debug( debugger, ">>>>>>>>>>空。 行:"+br.getRow()+", 列:"+br.getColumn());   
+            break;   
+        case FormulaRecord.sid: //数式   
+            FormulaRecord fr = (FormulaRecord)record;  
+            addDataAndrChangeRow(fr.getRow(),fr.getColumn(), fr.getValue(), debugger);
+            logger.debug( debugger, ">>>>>>>>>>数字 。 行:"+fr.getRow()+", 列:"+fr.getColumn());  
+            break;  
+		}
+	}
+	/** 
+     * HH:MM格式时间的数字转换方法</li> 
+     * @param sNum 
+     * @return 
+     */ 
+    private static String getTime(double daynum) 
+    { 
+        double totalSeconds=daynum*86400.0D;
+        //总的分钟数
+        int seconds =(int)totalSeconds/60;
+        //实际小时数
+        int hours =seconds/60;
+        int minutes = seconds-hours*60;
+        //剩余的实际分钟数
+        StringBuffer sb=new StringBuffer();
+        if(String.valueOf(hours).length()==1){
+        	sb.append("0"+hours);
+        }else{
+        	sb.append(hours);
+        }
+        sb.append(":");
+        if(String.valueOf(minutes).length()==1){
+        	sb.append("0"+minutes);
+        }else{
+        	sb.append(minutes);
+        }
+        return sb.toString();
+    } 
+	/**
+	 *  添加数据记录并检查是否换行
+	 * @param row 实际当前行号
+	 * @param col 实际记录当前列
+	 * @param value  当前cell的值
+	 */
+	public void addDataAndrChangeRow( int row,int col,Object value, Boolean debugger ){
+		//当前行如果大于实际行表示改行忽略,不记录
+		if(curRowNum!=row){
+			if(CollectionUtils.isEmpty(currentSheetDataMap)){
+				 currentSheetDataMap=new ArrayList<Map<String,Object>>();
+			}
+			currentSheetDataMap.add(currentSheetRowDataMap);
+			logger.debug( debugger, ">>>>>>>>>>行号:"+curRowNum +" 行内容:"+currentSheetRowDataMap.toString());
+			logger.debug( debugger, ">>>>>>>>>>\n");
+			currentSheetRowDataMap=new HashMap<String,Object>();
+			currentSheetRowDataMap.put(trianListheadTitle[col], value);
+			logger.debug( debugger, ">>>>>>>>>>"+ row+":"+col+"  "+value+"\r");
+			curRowNum=row;
+		}else{
+			currentSheetRowDataMap.put(trianListheadTitle[col], value);
+			logger.debug(row+":"+col+"  "+value+"\r");
+		}
+	}
+	public List<Map<String, Object>> getCurrentSheetDataMap() {
+		return currentSheetDataMap;
+	}
+	public void setCurrentSheetDataMap(List<Map<String, Object>> currentSheetDataMap) {
+		this.currentSheetDataMap = currentSheetDataMap;
+	}
+	public Map<String, Object> getCurrentSheetRowDataMap() {
+		return currentSheetRowDataMap;
+	}
+	public void setCurrentSheetRowDataMap(Map<String, Object> currentSheetRowDataMap) {
+		this.currentSheetRowDataMap = currentSheetRowDataMap;
+	}
+	public int getCurRowNum() {
+		return curRowNum;
+	}
+	public void setCurRowNum(int curRowNum) {
+		this.curRowNum = curRowNum;
+	}
+	public int getIgnoreRowNum() {
+		return ignoreRowNum;
+	}
+	public void setIgnoreRowNum(int ignoreRowNum) {
+		this.ignoreRowNum = ignoreRowNum;
+	}
+	public Boolean getDebugger() {
+		return debugger;
+	}
+	/**
+	 * 是否开启调试日志
+	 * @param debugger
+	 */
+	public void setDebugger(Boolean debugger) {
+		this.debugger = debugger;
+	}
+}

+ 215 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/common/excel/writer/AbstractExcel2007Writer.java

@@ -0,0 +1,215 @@
+package com.x.attendance.assemble.common.excel.writer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.util.Calendar;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.poi.ss.usermodel.DateUtil;
+import org.apache.poi.ss.util.CellReference;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+/**
+ * 抽象excel2007读入器,先构建.xlsx一张模板,改写模板中的sheet.xml,使用这种方法 写入.xlsx文件,不需要太大的内存
+ *
+ */
+public abstract class AbstractExcel2007Writer {
+
+	private SpreadsheetWriter sw;
+
+	/**
+	 * 写入电子表格的主要流程
+	 * 
+	 * @param fileName
+	 * @throws Exception
+	 */
+	public void process(String fileName) throws Exception {
+		// 建立工作簿和电子表格对象
+		XSSFWorkbook wb = new XSSFWorkbook();
+		XSSFSheet sheet = wb.createSheet("sheet1");
+		// 持有电子表格数据的xml文件名 例如 /xl/worksheets/sheet1.xml
+		String sheetRef = sheet.getPackagePart().getPartName().getName();
+
+		// 保存模板
+		try (FileOutputStream os = new FileOutputStream("template.xlsx")) {
+			wb.write(os);
+		}
+
+		// 生成xml文件
+		File tmp = File.createTempFile("sheet", ".xml");
+		try (Writer fw = new FileWriter(tmp)) {
+			sw = new SpreadsheetWriter(fw);
+			generate();
+		}
+
+		// 使用产生的数据替换模板
+		File templateFile = new File("template.xlsx");
+		try (FileOutputStream out = new FileOutputStream(fileName)) {
+			substitute(templateFile, tmp, sheetRef.substring(1), out);
+		}
+		// 删除临时模板文件
+		if (templateFile.isFile() && templateFile.exists()) {
+			templateFile.delete();
+		}
+	}
+
+	/**
+	 * 类使用者应该使用此方法进行写操作
+	 * 
+	 * @throws Exception
+	 */
+	public abstract void generate() throws Exception;
+
+	public void beginSheet() throws IOException {
+		sw.beginSheet();
+	}
+
+	public void insertRow(int rowNum) throws IOException {
+		sw.insertRow(rowNum);
+	}
+
+	public void createCell(int columnIndex, String value) throws IOException {
+		sw.createCell(columnIndex, value, -1);
+	}
+
+	public void createCell(int columnIndex, double value) throws IOException {
+		sw.createCell(columnIndex, value, -1);
+	}
+
+	public void endRow() throws IOException {
+		sw.endRow();
+	}
+
+	public void endSheet() throws IOException {
+		sw.endSheet();
+	}
+
+	/**
+	 *
+	 * @param zipfile the template file
+	 * @param tmpfile the XML file with the sheet data
+	 * @param entry   the name of the sheet entry to substitute, e.g.
+	 *                xl/worksheets/sheet1.xml
+	 * @param out     the stream to write the result to
+	 */
+	private static void substitute(File zipfile, File tmpfile, String entry, OutputStream out) throws IOException {
+		try (ZipFile zip = new ZipFile(zipfile); ZipOutputStream zos = new ZipOutputStream(out)) {
+
+			@SuppressWarnings("unchecked")
+			Enumeration<ZipEntry> en = (Enumeration<ZipEntry>) zip.entries();
+			while (en.hasMoreElements()) {
+				ZipEntry ze = en.nextElement();
+				if (!ze.getName().equals(entry)) {
+					zos.putNextEntry(new ZipEntry(ze.getName()));
+					InputStream is = zip.getInputStream(ze);
+					copyStream(is, zos);
+					is.close();
+				}
+			}
+			zos.putNextEntry(new ZipEntry(entry));
+			try (InputStream is = new FileInputStream(tmpfile)) {
+				copyStream(is, zos);
+			}
+		}
+	}
+
+	private static void copyStream(InputStream in, OutputStream out) throws IOException {
+		byte[] chunk = new byte[1024];
+		int count;
+		while ((count = in.read(chunk)) >= 0) {
+			out.write(chunk, 0, count);
+		}
+	}
+
+	/**
+	 * 在写入器中写入电子表格
+	 * 
+	 */
+	public static class SpreadsheetWriter {
+		private final Writer _out;
+		private int _rownum;
+		private static String LINE_SEPARATOR = System.getProperty("line.separator");
+
+		public SpreadsheetWriter(Writer out) {
+			_out = out;
+		}
+
+		public void beginSheet() throws IOException {
+			_out.write("<?xml version=\"1.0\" encoding=\"GB2312\"?>"
+					+ "<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">");
+			_out.write("<sheetData>" + LINE_SEPARATOR);
+		}
+
+		public void endSheet() throws IOException {
+			_out.write("</sheetData>");
+			_out.write("</worksheet>");
+		}
+
+		/**
+		 * 插入新行
+		 *
+		 * @param rownum 以0开始
+		 */
+		public void insertRow(int rownum) throws IOException {
+			_out.write("<row r=\"" + (rownum + 1) + "\">" + LINE_SEPARATOR);
+			this._rownum = rownum;
+		}
+
+		/**
+		 * 插入行结束标志
+		 */
+		public void endRow() throws IOException {
+			_out.write("</row>" + LINE_SEPARATOR);
+		}
+
+		/**
+		 * 插入新列
+		 * 
+		 * @param columnIndex
+		 * @param value
+		 * @param styleIndex
+		 * @throws IOException
+		 */
+		public void createCell(int columnIndex, String value, int styleIndex) throws IOException {
+			String ref = new CellReference(_rownum, columnIndex).formatAsString();
+			_out.write("<c r=\"" + ref + "\" t=\"inlineStr\"");
+			if (styleIndex != -1)
+				_out.write(" s=\"" + styleIndex + "\"");
+			_out.write(">");
+			_out.write("<is><t>" + XMLEncoder.encode(value) + "</t></is>");
+			_out.write("</c>");
+		}
+
+		public void createCell(int columnIndex, String value) throws IOException {
+			createCell(columnIndex, value, -1);
+		}
+
+		public void createCell(int columnIndex, double value, int styleIndex) throws IOException {
+			String ref = new CellReference(_rownum, columnIndex).formatAsString();
+			_out.write("<c r=\"" + ref + "\" t=\"n\"");
+			if (styleIndex != -1)
+				_out.write(" s=\"" + styleIndex + "\"");
+			_out.write(">");
+			_out.write("<v>" + value + "</v>");
+			_out.write("</c>");
+		}
+
+		public void createCell(int columnIndex, double value) throws IOException {
+			createCell(columnIndex, value, -1);
+		}
+
+		public void createCell(int columnIndex, Calendar value, int styleIndex) throws IOException {
+			createCell(columnIndex, DateUtil.getExcelDate(value, false), styleIndex);
+		}
+	}
+}

+ 60 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/common/excel/writer/Excel2003Writer.java

@@ -0,0 +1,60 @@
+package com.x.attendance.assemble.common.excel.writer;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+
+public class Excel2003Writer {
+
+	/**
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		try{	
+			writeExcel("tes2003.xls");
+		} catch (IOException e) {
+		e.printStackTrace();
+		}
+	}
+	
+	
+	/**
+	 * 写入excel并填充内容,一个sheet只能写65536行以下,超出会报异常,写入时建议使用AbstractExcel2007Writer
+	 * @param fileName
+	 * @throws IOException
+	 */
+	public static void writeExcel(String fileName) throws IOException{
+			
+			// 创建excel2003对象
+			Workbook wb = new HSSFWorkbook();
+			
+			// 设置文件放置路径和文件名
+		    FileOutputStream fileOut = new FileOutputStream(fileName);
+		    // 创建新的表单
+		    Sheet sheet = wb.createSheet("newsheet");
+		    // 创建新行
+		    for(int i=0;i<20000;i++){
+			    Row row = sheet.createRow(i);
+			    // 创建单元格
+			    Cell cell = row.createCell(0);
+			    // 设置单元格值
+			    cell.setCellValue(1);
+			    row.createCell(1).setCellValue(1+i);
+			    row.createCell(2).setCellValue(true);
+			    row.createCell(3).setCellValue(0.43d);
+			    row.createCell(4).setCellValue('d');
+			    row.createCell(5).setCellValue("");
+			    row.createCell(6).setCellValue("第七列"+i);
+			    row.createCell(7).setCellValue("第八列"+i);
+		    }
+		    wb.write(fileOut);
+		    fileOut.close();
+	}
+
+
+}

+ 44 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/common/excel/writer/Excel2007WriterImpl.java

@@ -0,0 +1,44 @@
+package com.x.attendance.assemble.common.excel.writer;
+public class Excel2007WriterImpl extends AbstractExcel2007Writer{
+
+	
+	/**
+	 * @param args
+	 * @throws Exception
+	 */
+	public static void main(String[] args) throws Exception {
+		//构建excel2007写入器
+		AbstractExcel2007Writer excel07Writer = new Excel2007WriterImpl();
+		//调用处理方法
+		excel07Writer.process("F://test07.xlsx");
+	}
+
+	
+	/* 
+	 * 可根据需求重写此方法,对于单元格的小数或者日期格式,会出现精度问题或者日期格式转化问题,建议使用字符串插入方法
+	 * @see com.excel.ver2.AbstractExcel2007Writer#generate()
+	 */
+	@Override
+	public void generate()throws Exception {
+        //电子表格开始
+        beginSheet();
+        for (int rownum = 0; rownum < 100; rownum++) {
+        	//插入新行
+            insertRow(rownum);
+            //建立新单元格,索引值从0开始,表示第一列
+            createCell(0, "中国<" + rownum + "!");
+            createCell(1, 34343.123456789);
+            createCell(2, "23.67%");
+            createCell(3, "12:12:23");
+            createCell(4, "2010-10-11 12:12:23");
+            createCell(5, "true");
+            createCell(6, "false");
+          
+            //结束行
+            endRow();
+        }
+        //电子表格结束
+        endSheet();
+	}
+
+}

+ 48 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/common/excel/writer/XMLEncoder.java

@@ -0,0 +1,48 @@
+package com.x.attendance.assemble.common.excel.writer;
+
+public class XMLEncoder {
+
+    private static final String[] xmlCode = new String[256];
+
+    static {
+        // Special characters
+        xmlCode['\''] = "'";
+        xmlCode['\"'] = "\""; // double quote
+        xmlCode['&'] = "&"; // ampersand
+        xmlCode['<'] = "<"; // lower than
+        xmlCode['>'] = ">"; // greater than
+    }
+
+    /**
+     * <p>
+     * Encode the given text into xml.
+     * </p>
+     * 
+     * @param string the text to encode
+     * @return the encoded string
+     */
+    public static String encode(String string) {
+        if (string == null) return "";
+        int n = string.length();
+        char character;
+        String xmlchar;
+        StringBuffer buffer = new StringBuffer();
+        // loop over all the characters of the String.
+        for (int i = 0; i < n; i++) {
+            character = string.charAt(i);
+            // the xmlcode of these characters are added to a StringBuffer one by one
+            try {
+                xmlchar = xmlCode[character];
+                if (xmlchar == null) {
+                    buffer.append(character);
+                } else {
+                    buffer.append(xmlCode[character]);
+                }
+            } catch (ArrayIndexOutOfBoundsException aioobe) {
+                buffer.append(character);
+            }
+        }
+        return buffer.toString();
+    }
+
+}

+ 24 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/AbstractFactory.java

@@ -0,0 +1,24 @@
+package com.x.attendance.assemble.control;
+
+import com.x.base.core.container.EntityManagerContainer;
+
+public abstract class AbstractFactory {
+
+	private Business business;
+
+	public AbstractFactory( Business business ) throws Exception {
+		try {
+			if ( null == business ) {
+				throw new Exception("business can not be null.");
+			}
+			this.business = business;
+		} catch ( Exception e ) {
+			throw new Exception("can not instantiating factory.");
+		}
+	}
+
+	public EntityManagerContainer entityManagerContainer() throws Exception {
+		return this.business.entityManagerContainer();
+	}
+
+}

+ 33 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/ApplicationServletContextListener.java

@@ -0,0 +1,33 @@
+package com.x.attendance.assemble.control;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.annotation.WebListener;
+
+import com.x.base.core.project.Context;
+
+@WebListener
+public class ApplicationServletContextListener implements ServletContextListener {
+
+	@Override
+	public void contextInitialized(ServletContextEvent servletContextEvent) {
+		try {
+			ThisApplication.context = Context.concrete(servletContextEvent);
+			ThisApplication.init();
+			ThisApplication.context().regist();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	@Override
+	public void contextDestroyed(ServletContextEvent servletContextEvent) {
+		try {
+			ThisApplication.destroy();
+			ThisApplication.context.destrory(servletContextEvent);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+}

+ 279 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/Business.java

@@ -0,0 +1,279 @@
+package com.x.attendance.assemble.control;
+
+import java.util.List;
+
+
+import com.x.attendance.assemble.control.factory.v2.AttendanceV2ManagerFactory;
+import org.apache.commons.lang3.StringUtils;
+
+import com.x.attendance.assemble.control.factory.AttendanceAdminFactory;
+import com.x.attendance.assemble.control.factory.AttendanceAppealInfoFactory;
+import com.x.attendance.assemble.control.factory.AttendanceDetailFactory;
+import com.x.attendance.assemble.control.factory.AttendanceDetailMobileFactory;
+import com.x.attendance.assemble.control.factory.AttendanceDetailStatisticFactory;
+import com.x.attendance.assemble.control.factory.AttendanceEmployeeConfigFactory;
+import com.x.attendance.assemble.control.factory.AttendanceImportFileInfoFactory;
+import com.x.attendance.assemble.control.factory.AttendanceScheduleSettingFactory;
+import com.x.attendance.assemble.control.factory.AttendanceSelfHolidayFactory;
+import com.x.attendance.assemble.control.factory.AttendanceSettingFactory;
+import com.x.attendance.assemble.control.factory.AttendanceStatisticRequireLogFactory;
+import com.x.attendance.assemble.control.factory.AttendanceStatisticalCycleFactory;
+import com.x.attendance.assemble.control.factory.AttendanceWorkDayConfigFactory;
+import com.x.attendance.assemble.control.factory.AttendanceWorkPlaceFactory;
+import com.x.attendance.assemble.control.factory.DingdingAttendanceFactory;
+import com.x.attendance.assemble.control.factory.StatisticPersonForMonthFactory;
+import com.x.attendance.assemble.control.factory.StatisticTopUnitForDayFactory;
+import com.x.attendance.assemble.control.factory.StatisticTopUnitForMonthFactory;
+import com.x.attendance.assemble.control.factory.StatisticUnitForDayFactory;
+import com.x.attendance.assemble.control.factory.StatisticUnitForMonthFactory;
+import com.x.base.core.container.EntityManagerContainer;
+import com.x.base.core.project.http.EffectivePerson;
+import com.x.organization.core.express.Organization;
+
+public class Business {
+
+	private EntityManagerContainer emc;
+
+	public Business(EntityManagerContainer emc) throws Exception {
+		this.emc = emc;
+	}
+
+	public EntityManagerContainer entityManagerContainer() {
+		return this.emc;
+	}
+
+	//钉钉同步数据处理
+	private DingdingAttendanceFactory dingdingAttendanceFactory;
+	// 人员组织业务处理类
+	private Organization organization;
+	// 系统配置业务处理类
+	private AttendanceSettingFactory attendanceSettingFactory;
+	// 工作场所配置业务处理类
+	private AttendanceWorkPlaceFactory attendanceWorkPlaceFactory;
+	// 节假日工作日配置业务处理类
+	private AttendanceWorkDayConfigFactory attendanceWorkDayConfigFactory;
+	// 人员考勤数据导入文件操作业务处理类
+	private AttendanceImportFileInfoFactory attendanceImportFileInfoFactory;
+	// 人员考勤数据业务处理类
+	private AttendanceDetailFactory attendanceDetailFactory;
+	private AttendanceDetailMobileFactory attendanceDetailMobileFactory;
+	// 考勤管理员业务处理类
+	private AttendanceAdminFactory attendanceAdminFactory;
+	// 排班管理业务处理类
+	private AttendanceScheduleSettingFactory attendanceScheduleSettingFactory;
+	// 休假申请数据业务处理类
+	private AttendanceSelfHolidayFactory attendanceSelfHolidayFactory;
+
+	private StatisticTopUnitForDayFactory statisticTopUnitForDayFactory;
+
+	private StatisticTopUnitForMonthFactory statisticTopUnitForMonthFactory;
+
+	private StatisticUnitForDayFactory statisticUnitForDayFactory;
+
+	private StatisticUnitForMonthFactory statisticUnitForMonthFactory;
+
+	private StatisticPersonForMonthFactory statisticPersonForMonthFactory;
+
+	private AttendanceAppealInfoFactory attendanceAppealInfoFactory;
+
+	private AttendanceStatisticalCycleFactory attendanceStatisticalCycleFactory;
+
+	private AttendanceEmployeeConfigFactory attendanceEmployeeConfigFactory;
+
+	private AttendanceStatisticRequireLogFactory attendanceStatisticRequireLogFactory;
+
+	private AttendanceDetailStatisticFactory attendanceDetailStatisticFactory;
+
+
+	private AttendanceV2ManagerFactory attendanceV2ManagerFactory;
+
+	public DingdingAttendanceFactory dingdingAttendanceFactory() throws Exception {
+		if (null == this.dingdingAttendanceFactory) {
+			this.dingdingAttendanceFactory = new DingdingAttendanceFactory(this);
+		}
+		return this.dingdingAttendanceFactory;
+	}
+
+	public AttendanceWorkPlaceFactory attendanceWorkPlaceFactory() throws Exception {
+		if (null == this.attendanceWorkPlaceFactory) {
+			this.attendanceWorkPlaceFactory = new AttendanceWorkPlaceFactory(this);
+		}
+		return attendanceWorkPlaceFactory;
+	}
+
+	public AttendanceDetailMobileFactory getAttendanceDetailMobileFactory() throws Exception {
+		if (null == this.attendanceDetailMobileFactory) {
+			this.attendanceDetailMobileFactory = new AttendanceDetailMobileFactory(this);
+		}
+		return attendanceDetailMobileFactory;
+	}
+
+	public AttendanceDetailStatisticFactory getAttendanceDetailStatisticFactory() throws Exception {
+		if (null == this.attendanceDetailStatisticFactory) {
+			this.attendanceDetailStatisticFactory = new AttendanceDetailStatisticFactory(this);
+		}
+		return attendanceDetailStatisticFactory;
+	}
+
+	public AttendanceEmployeeConfigFactory getAttendanceEmployeeConfigFactory() throws Exception {
+		if (null == this.attendanceEmployeeConfigFactory) {
+			this.attendanceEmployeeConfigFactory = new AttendanceEmployeeConfigFactory(this);
+		}
+		return attendanceEmployeeConfigFactory;
+	}
+
+	public AttendanceStatisticRequireLogFactory getAttendanceStatisticRequireLogFactory() throws Exception {
+		if (null == this.attendanceStatisticRequireLogFactory) {
+			this.attendanceStatisticRequireLogFactory = new AttendanceStatisticRequireLogFactory(this);
+		}
+		return attendanceStatisticRequireLogFactory;
+	}
+
+	public AttendanceStatisticalCycleFactory getAttendanceStatisticalCycleFactory() throws Exception {
+		if (null == this.attendanceStatisticalCycleFactory) {
+			this.attendanceStatisticalCycleFactory = new AttendanceStatisticalCycleFactory(this);
+		}
+		return attendanceStatisticalCycleFactory;
+	}
+
+	public AttendanceAppealInfoFactory getAttendanceAppealInfoFactory() throws Exception {
+		if (null == this.attendanceAppealInfoFactory) {
+			this.attendanceAppealInfoFactory = new AttendanceAppealInfoFactory(this);
+		}
+		return attendanceAppealInfoFactory;
+	}
+
+	public StatisticTopUnitForDayFactory getStatisticTopUnitForDayFactory() throws Exception {
+		if (null == this.statisticTopUnitForDayFactory) {
+			this.statisticTopUnitForDayFactory = new StatisticTopUnitForDayFactory(this);
+		}
+		return statisticTopUnitForDayFactory;
+	}
+
+	public StatisticTopUnitForMonthFactory getStatisticTopUnitForMonthFactory() throws Exception {
+		if (null == this.statisticTopUnitForMonthFactory) {
+			this.statisticTopUnitForMonthFactory = new StatisticTopUnitForMonthFactory(this);
+		}
+		return statisticTopUnitForMonthFactory;
+	}
+
+	public StatisticUnitForDayFactory getStatisticUnitForDayFactory() throws Exception {
+		if (null == this.statisticUnitForDayFactory) {
+			this.statisticUnitForDayFactory = new StatisticUnitForDayFactory(this);
+		}
+		return statisticUnitForDayFactory;
+	}
+
+	public StatisticUnitForMonthFactory getStatisticUnitForMonthFactory() throws Exception {
+		if (null == this.statisticUnitForMonthFactory) {
+			this.statisticUnitForMonthFactory = new StatisticUnitForMonthFactory(this);
+		}
+		return statisticUnitForMonthFactory;
+	}
+
+	public StatisticPersonForMonthFactory getStatisticPersonForMonthFactory() throws Exception {
+		if (null == this.statisticPersonForMonthFactory) {
+			this.statisticPersonForMonthFactory = new StatisticPersonForMonthFactory(this);
+		}
+		return statisticPersonForMonthFactory;
+	}
+
+	public Organization organization() throws Exception {
+		if (null == this.organization) {
+			this.organization = new Organization(ThisApplication.context());
+		}
+		return organization;
+	}
+
+	public AttendanceSettingFactory getAttendanceSettingFactory() throws Exception {
+		if (null == this.attendanceSettingFactory) {
+			this.attendanceSettingFactory = new AttendanceSettingFactory(this);
+		}
+		return attendanceSettingFactory;
+	}
+
+	public AttendanceWorkDayConfigFactory getAttendanceWorkDayConfigFactory() throws Exception {
+		if (null == this.attendanceWorkDayConfigFactory) {
+			this.attendanceWorkDayConfigFactory = new AttendanceWorkDayConfigFactory(this);
+		}
+		return attendanceWorkDayConfigFactory;
+	}
+
+	public AttendanceImportFileInfoFactory getAttendanceImportFileInfoFactory() throws Exception {
+		if (null == this.attendanceImportFileInfoFactory) {
+			this.attendanceImportFileInfoFactory = new AttendanceImportFileInfoFactory(this);
+		}
+		return attendanceImportFileInfoFactory;
+	}
+
+	public AttendanceDetailFactory getAttendanceDetailFactory() throws Exception {
+		if (null == this.attendanceDetailFactory) {
+			this.attendanceDetailFactory = new AttendanceDetailFactory(this);
+		}
+		return attendanceDetailFactory;
+	}
+
+	public AttendanceAdminFactory getAttendanceAdminFactory() throws Exception {
+		if (null == this.attendanceAdminFactory) {
+			this.attendanceAdminFactory = new AttendanceAdminFactory(this);
+		}
+		return attendanceAdminFactory;
+	}
+
+	public AttendanceScheduleSettingFactory getAttendanceScheduleSettingFactory() throws Exception {
+		if (null == this.attendanceScheduleSettingFactory) {
+			this.attendanceScheduleSettingFactory = new AttendanceScheduleSettingFactory(this);
+		}
+		return attendanceScheduleSettingFactory;
+	}
+
+	public AttendanceSelfHolidayFactory getAttendanceSelfHolidayFactory() throws Exception {
+		if (null == this.attendanceSelfHolidayFactory) {
+			this.attendanceSelfHolidayFactory = new AttendanceSelfHolidayFactory(this);
+		}
+		return attendanceSelfHolidayFactory;
+	}
+
+	public AttendanceV2ManagerFactory getAttendanceV2ManagerFactory() throws Exception {
+		if (null == this.attendanceV2ManagerFactory) {
+			this.attendanceV2ManagerFactory = new AttendanceV2ManagerFactory(this);
+		}
+		return this.attendanceV2ManagerFactory;
+	}
+
+	/**
+	 * TODO 判断用户是否管理员权限 1、person.isManager() 2、xadmin 3、ROLE_AttendanceManager
+	 * @return
+	 * @throws Exception
+	 */
+
+	public boolean isManager(EffectivePerson person) throws Exception {
+		// 如果用户的身份是平台的超级管理员,那么就是超级管理员权限
+		if (person.isManager()) {
+			return true;
+		}
+		if (isHasPlatformRole(person.getDistinguishedName(), ThisApplication.ROLE_AttendanceManager)) {
+			return true;
+		}
+		return false;
+	}
+
+	public boolean isHasPlatformRole(String personName, String roleName) throws Exception {
+		if (StringUtils.isEmpty(personName)) {
+			throw new Exception("personName is null!");
+		}
+		if (StringUtils.isEmpty(roleName)) {
+			throw new Exception("roleName is null!");
+		}
+		List<String> roleList = null;
+		roleList = organization().role().listWithPerson(personName);
+		if (roleList != null && !roleList.isEmpty()) {
+			if (roleList.stream().filter(r -> roleName.equalsIgnoreCase(r)).count() > 0) {
+				return true;
+			}
+		} else {
+			return false;
+		}
+		return false;
+	}
+}

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

@@ -0,0 +1,16 @@
+package com.x.attendance.assemble.control;
+
+import com.x.base.core.entity.JpaObject;
+import com.x.base.core.project.cache.CacheManager;
+
+/**
+ * 缓存管理帮助类
+ *
+ */
+public class CacheUtil {
+	
+	public static <T extends JpaObject> void notify( Class<T> clz ) throws Exception {
+		CacheManager.notify( clz );
+	}
+	
+}

+ 58 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/CriteriaQueryTools.java

@@ -0,0 +1,58 @@
+package com.x.attendance.assemble.control;
+
+import java.lang.reflect.Field;
+
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.Order;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.apache.commons.lang3.StringUtils;
+
+public class CriteriaQueryTools {
+    public static Order setOrder(CriteriaBuilder cb, Root<?> root, Class<?> clazz_, String fieldName, String orderType) {
+
+        Boolean fieldExists = false;
+        Field[] fields = clazz_.getFields();
+        for (Field field : fields) {
+            if (StringUtils.equalsIgnoreCase(field.getName(), fieldName)) {
+                fieldName = field.getName(); // 忽略大小写之后,重置查询字段的名称
+                fieldExists = true;
+            }
+        }
+
+        if (!fieldExists) {
+            return null; // 如果查询字段根本和object 对不上,那么就返回null
+        }
+
+        if (StringUtils.equalsIgnoreCase(orderType, "asc")) {
+            return cb.asc(root.get(fieldName));
+        } else {
+            return cb.desc(root.get(fieldName));
+        }
+    }
+
+    public static Predicate predicate_or(CriteriaBuilder criteriaBuilder, Predicate predicate, Predicate predicate_target) {
+        if (predicate == null) {
+            return predicate_target;
+        } else {
+            if (predicate_target != null) {
+                return criteriaBuilder.or(predicate, predicate_target);
+            } else {
+                return predicate;
+            }
+        }
+    }
+
+    public static Predicate predicate_and(CriteriaBuilder criteriaBuilder, Predicate predicate, Predicate predicate_target) {
+        if (predicate == null) {
+            return predicate_target;
+        } else {
+            if (predicate_target != null) {
+                return criteriaBuilder.and(predicate, predicate_target);
+            } else {
+                return predicate;
+            }
+        }
+    }
+}

+ 12 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/ExceptionDingDingRequest.java

@@ -0,0 +1,12 @@
+package com.x.attendance.assemble.control;
+
+import com.x.base.core.project.exception.PromptException;
+
+class ExceptionDingDingRequest extends PromptException {
+
+    private static final long serialVersionUID = -2160589718239895222L;
+
+    public ExceptionDingDingRequest(String errorMsg) {
+        super(errorMsg);
+    }
+}

+ 9 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/ExceptionDingdingFindNoArgumentError.java

@@ -0,0 +1,9 @@
+package com.x.attendance.assemble.control;
+
+import com.x.base.core.project.exception.PromptException;
+
+class ExceptionDingdingFindNoArgumentError extends PromptException {
+    public ExceptionDingdingFindNoArgumentError() {
+        super("没有传入正确的参数");
+    }
+}

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

@@ -0,0 +1,16 @@
+package com.x.attendance.assemble.control;
+
+import com.x.base.core.project.exception.PromptException;
+
+class ExceptionPersonHasNoIdentity extends PromptException {
+
+	private static final long serialVersionUID = 1859164370743532895L;
+
+	public ExceptionPersonHasNoIdentity() {
+		super("用户未分配任何身份,请检查用户所在的组织信息。");
+	}
+	
+	public ExceptionPersonHasNoIdentity(String name ) {
+		super("用户'"+ name +"'未分配任何身份,请检查用户所在的组织信息。");
+	}
+}

+ 9 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/ExceptionQywxFindNoArgumentError.java

@@ -0,0 +1,9 @@
+package com.x.attendance.assemble.control;
+
+import com.x.base.core.project.exception.PromptException;
+
+class ExceptionQywxFindNoArgumentError extends PromptException {
+    public ExceptionQywxFindNoArgumentError() {
+        super("没有传入正确的参数");
+    }
+}

+ 13 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/ExceptionWrapInConvert.java

@@ -0,0 +1,13 @@
+package com.x.attendance.assemble.control;
+
+import com.google.gson.JsonElement;
+import com.x.base.core.project.exception.PromptException;
+
+public class ExceptionWrapInConvert extends PromptException {
+
+	private static final long serialVersionUID = 1859164370743532895L;
+
+	public ExceptionWrapInConvert( Throwable e, JsonElement jsonElement) {
+		super( "系统在将JSON信息转换为对象时发生异常。JSON:" + jsonElement.toString(), e);
+	}
+}

+ 7 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/MimeTypeDefinition.java

@@ -0,0 +1,7 @@
+package com.x.attendance.assemble.control;
+
+import javax.activation.MimetypesFileTypeMap;
+
+public class MimeTypeDefinition {
+	public static MimetypesFileTypeMap instance;
+}

+ 121 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QueueAttendanceDetailStatistic.java

@@ -0,0 +1,121 @@
+package com.x.attendance.assemble.control;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.x.attendance.assemble.control.service.AttendanceStatisticRequireLogServiceAdv;
+import com.x.attendance.assemble.control.service.AttendanceStatisticServiceAdv;
+import com.x.attendance.assemble.control.service.AttendanceStatisticalCycleServiceAdv;
+import com.x.attendance.assemble.control.service.AttendanceWorkDayConfigServiceAdv;
+import com.x.attendance.entity.AttendanceStatisticRequireLog;
+import com.x.attendance.entity.AttendanceStatisticalCycle;
+import com.x.attendance.entity.AttendanceWorkDayConfig;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+import com.x.base.core.project.queue.AbstractQueue;
+
+/**
+ * 对单个员工的打卡信息进行分析的队列
+ */
+public class QueueAttendanceDetailStatistic extends AbstractQueue<String> {
+
+    private static final Logger logger = LoggerFactory.getLogger(QueueAttendanceDetailStatistic.class);
+
+    @Override
+    protected void execute( String logId ) throws Exception {
+        AttendanceStatisticRequireLogServiceAdv statisticRequireLogServiceAdv = new AttendanceStatisticRequireLogServiceAdv();
+        AttendanceWorkDayConfigServiceAdv workDayConfigServiceAdv = new AttendanceWorkDayConfigServiceAdv();
+        AttendanceStatisticServiceAdv statisticServiceAdv = new AttendanceStatisticServiceAdv();
+        AttendanceStatisticalCycleServiceAdv statisticalCycleServiceAdv = new AttendanceStatisticalCycleServiceAdv();
+        AttendanceStatisticRequireLog log = statisticRequireLogServiceAdv.get(logId);
+
+        if( log != null ){
+            logger.debug("system try to statistic attendance detail, logId:" + logId );
+            AttendanceStatisticalCycle attendanceStatisticalCycle  = null;
+            List<AttendanceWorkDayConfig> workDayConfigList = null;
+            List<AttendanceStatisticRequireLog> attendanceStatisticRequireLogList = null;
+            Map<String, Map<String, List<AttendanceStatisticalCycle>>> statisticalCycleMap = null;
+
+            try {//先查询所有的法定节假日和工作日配置列表
+                workDayConfigList = workDayConfigServiceAdv.getAllWorkDayConfigWithCache(false );
+            } catch ( Exception e ) {
+                logger.warn("【统计】系统在查询当月有打卡记录的员工姓名列表时发生异常!" );
+                logger.error(e);
+            }
+
+            try{//查询所有的考勤统计周期信息,并且组织成MAP
+                statisticalCycleMap = statisticalCycleServiceAdv.getAllStatisticalCycleMapWithCache(false);
+            }catch(Exception e){
+                logger.warn( "【统计】系统在查询并且组织所有的考勤统计周期信息时发生异常。" );
+                logger.error(e);
+            }
+
+            //先处理所有的统计错误
+            try {
+                logger.debug( false, "准备处理恢复的统计错误信息, 所有错误统计将会重新计算......" );
+                statisticRequireLogServiceAdv.resetStatisticError();
+            } catch (Exception e) {
+                logger.warn("【统计】系统在重置统计错误信息时发生异常!" );
+                logger.error(e);
+            }
+            //统计类型:PERSON_PER_MONTH|UNIT_PER_MONTH|TOPUNIT_PER_MONTH|UNIT_PER_DAY|TOPUNIT_PER_DAY
+            //统计处理状态:WAITING|PROCESSING|COMPLETE|ERROR
+            if( StringUtils.equals( "PERSON_PER_MONTH", log.getStatisticType() )){
+                logger.debug( false, "系统准备统计[员工每月统计], 员工:" + log.getStatisticKey() + ", 统计月份:" + log.getStatisticYear() + "-" +log.getStatisticMonth() );
+                try {
+                    attendanceStatisticalCycle = statisticalCycleServiceAdv.getStatisticCycleByEmployee( log, statisticalCycleMap, false );
+                } catch (Exception e) {
+                    logger.warn("【统计】系统在根据统计需求记录信息查询统计周期信息时发生异常!" );
+                    logger.error(e);
+                }
+                if( attendanceStatisticalCycle != null ){
+                    try{
+                        statisticServiceAdv.statisticEmployeeAttendanceForMonth( log, attendanceStatisticalCycle, workDayConfigList, statisticalCycleMap);
+                    }catch(Exception e){
+                        logger.warn( "【统计】系统在根据需求进行员工月度打卡记录分析结果统计时发生异常。" );
+                        logger.error(e);
+                    }
+                }
+            } else if ( StringUtils.equals( "UNIT_PER_MONTH", log.getStatisticType() )){
+                logger.debug( false, "系统准备统计[组织每月统计], 组织:" + log.getStatisticKey() + ", 统计月份:" + log.getStatisticYear() + "-" +log.getStatisticMonth() );
+                try{
+                    statisticServiceAdv.statisticUnitAttendanceForMonth( log, workDayConfigList, statisticalCycleMap );
+                }catch(Exception e){
+                    logger.warn( "【统计】系统在根据需求进行员工月度打卡记录分析结果统计时发生异常。" );
+                    logger.error(e);
+                }
+            } else if ( StringUtils.equals( "TOPUNIT_PER_MONTH", log.getStatisticType() )){
+                logger.debug( false, "系统准备统计[顶层组织每月统计], 顶层组织:" + log.getStatisticKey() + ", 统计月份:" + log.getStatisticYear() + "-" +log.getStatisticMonth() );
+                try{
+                    statisticServiceAdv.statisticTopUnitAttendanceForMonth( log, workDayConfigList, statisticalCycleMap);
+                }catch(Exception e){
+                    logger.warn( "【统计】系统在根据需求进行员工月度打卡记录分析结果统计时发生异常。" );
+                    logger.error(e);
+                }
+            } else if ( StringUtils.equals( "UNIT_PER_DAY", log.getStatisticType() )){
+                logger.debug( false, "系统准备统计[组织每月统计], 组织:" + log.getStatisticKey() + ", 统计日期:" + log.getStatisticDay() );
+                try{
+                    statisticServiceAdv.statisticUnitAttendanceForDay( log, workDayConfigList, statisticalCycleMap, false );
+                }catch(Exception e){
+                    logger.warn( "【统计】系统在根据需求进行组织每日打卡记录分析结果统计时发生异常。" );
+                    logger.error(e);
+                }
+            } else if ( StringUtils.equals( "TOPUNIT_PER_DAY", log.getStatisticType() )){
+                logger.debug( false, "系统准备统计[顶层组织每月统计], 顶层组织:" + log.getStatisticKey() + ", 统计日期:" + log.getStatisticDay() );
+                try{
+                    statisticServiceAdv.statisticTopUnitAttendanceForDay( log, workDayConfigList, statisticalCycleMap );
+                }catch(Exception e){
+                    logger.warn( "【统计】系统在根据需求进行顶层组织每日打卡记录分析结果统计时发生异常。" );
+                    logger.error(e);
+                }
+            }else{
+                logger.warn( "statistic require log can not execute, type:" + log.getStatisticType() );
+            }
+            logger.debug("["+logId+"] attendance detail record statistic task execute completed。" );
+        }else{
+            logger.warn("attandence statistic require logId not exists, id:" + logId );
+        }
+    }
+}

+ 460 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QueueDingdingAttendance.java

@@ -0,0 +1,460 @@
+package com.x.attendance.assemble.control;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import javax.persistence.EntityManager;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.x.attendance.entity.AttendanceDingtalkDetail;
+import com.x.attendance.entity.AttendanceDingtalkDetail_;
+import com.x.attendance.entity.DingdingQywxSyncRecord;
+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.Application;
+import com.x.base.core.project.Applications;
+import com.x.base.core.project.x_organization_assemble_control;
+import com.x.base.core.project.bean.WrapCopier;
+import com.x.base.core.project.bean.WrapCopierFactory;
+import com.x.base.core.project.config.Config;
+import com.x.base.core.project.connection.HttpConnection;
+import com.x.base.core.project.gson.GsonPropertyObject;
+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;
+
+public class QueueDingdingAttendance extends AbstractQueue<DingdingQywxSyncRecord> {
+
+    private static final Logger logger = LoggerFactory.getLogger(QueueDingdingAttendance.class);
+
+    @Override
+    protected void execute(DingdingQywxSyncRecord record) throws Exception {
+        logger.info("开始执行钉钉打卡数据同步,from:" + record.getDateFrom() + ", to:"+record.getDateTo());
+        if (DingdingQywxSyncRecord.syncType_dingding.equals(record.getType())) {
+            try {
+                dingdingSync(record);
+            }catch (Exception e) {
+                logger.error(e);
+                updateSyncRecord(record, e.getMessage());
+            }
+        }
+    }
+
+    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 dingdingSync(DingdingQywxSyncRecord record) throws Exception {
+        Application app = ThisApplication.context().applications().randomWithWeight(x_organization_assemble_control.class.getName());
+        //开始分页查询人员
+        boolean hasNextPerson = true;
+        int personPageSize = 50;
+        //开始时间和结束时间
+        Date fromDate = new Date();
+        fromDate.setTime(record.getDateFrom());
+        Date toDate =new Date();
+        toDate.setTime(record.getDateTo());
+        //先删除
+        deleteDingdingAttendance(fromDate, toDate);
+        //人员查询地址
+        String uri = "person/list/(0)/next/50";
+        //钉钉考勤同步接口地址
+        String dingdingUrl = Config.dingding().getOapiAddress() + "/attendance/list?access_token=" + Config.dingding().corpAccessToken();
+        int saveNumber = 0;
+        while (hasNextPerson) {
+            logger.info("uri:"+ uri);
+            List<Person> list = ThisApplication.context().applications().getQuery(false, app, uri).getDataAsList(Person.class);
+            if (list != null && list.size() > 0) {
+                //钉钉用户id
+                List<String> ddUsers = list.stream().filter(person -> StringUtils.isNotEmpty(person.getDingdingId()))
+                        .map(Person::getDingdingId).collect(Collectors.toList());
+                if (ListTools.isNotEmpty(ddUsers)) {
+                    //分页查询
+                    int page = 0;
+                    boolean hasMoreResult = true;
+                    while (hasMoreResult) {
+                        DingdingAttendancePost post = new DingdingAttendancePost();
+                        //post传入 时间(时间间隔不能超过7天) 人员(人员数量不能超过50个)
+                        post.setLimit(50L);
+                        post.setOffset(page * 50L);//从0开始翻页
+                        //这里的开始时间和结束时间 查询的是钉钉返回结果中的workDate
+                        //查询考勤打卡记录的起始工作日
+                        post.setWorkDateFrom(DateTools.format(fromDate));
+                        //查询考勤打卡记录的结束工作日
+                        post.setWorkDateTo(DateTools.format(toDate));
+                        post.setUserIdList(ddUsers);
+                        DingdingAttendanceResult result = HttpConnection.postAsObject(dingdingUrl, null, post.toString(), DingdingAttendanceResult.class);
+                        if (result.errcode != null && result.errcode == 0) {
+                            List<DingdingAttendanceResultItem> resultList = result.getRecordresult();
+                            saveDingdingAttendance(resultList, list);
+                            saveNumber += resultList.size();
+                            if (result.hasMore) {
+                                page++;
+                            } else {
+                                logger.info("同步钉钉考勤结束!");
+                                hasMoreResult = false;
+                            }
+                        } else {
+                            //请求结果异常 结束
+                            throw new ExceptionDingDingRequest(result.errmsg);
+                        }
+                    }
+                }
+                //是否还有更多用户
+                if (list.size() < personPageSize) {
+                    logger.info("同步钉钉考勤 没有更多用户了,结束!");
+                    hasNextPerson = false;
+                    updateSyncRecord(record, null);
+                } else {
+                    //还有更多用户继续查询
+                    uri = Applications.joinQueryUri("person","list", list.get(list.size() - 1).getDistinguishedName(), "next", "50");
+//                    uri = "person/list/" + list.get(list.size() - 1).getDistinguishedName() + "/next/50";
+                }
+            } else {
+                //没有用户查询到结束
+                logger.info("同步钉钉考勤 查询不到用户了,结束!");
+                hasNextPerson = false;
+                updateSyncRecord(record, null);
+            }
+        }
+        logger.info("结束 插入:"+saveNumber+" 条");
+        //插入数据成功 开始统计程序
+
+        boolean hasNextDate = true;
+        Date statisticDate = fromDate;
+        while (hasNextDate) {
+            logger.info("发起钉钉考勤数据统计, date:"+ DateTools.format(statisticDate));
+            ThisApplication.personStatisticQueue.send(statisticDate);
+            ThisApplication.unitStatisticQueue.send(statisticDate);
+            if (!isSameDay(statisticDate, toDate)) {
+                statisticDate = DateTools.addDay(statisticDate, 1);
+            }else {
+                hasNextDate = false;
+            }
+        }
+        logger.info("发起数据统计程序 完成!");
+    }
+    private void deleteDingdingAttendance(Date fromDate, Date toDate) throws Exception{
+        try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+            //先删除 再同步
+            EntityManager em = emc.get(AttendanceDingtalkDetail.class);
+            CriteriaBuilder cb = em.getCriteriaBuilder();
+            CriteriaQuery<AttendanceDingtalkDetail> query = cb.createQuery(AttendanceDingtalkDetail.class);
+            Root<AttendanceDingtalkDetail> root = query.from(AttendanceDingtalkDetail.class);
+            long start = fromDate.getTime();
+            long end = toDate.getTime();
+            Predicate p = cb.between(root.get(AttendanceDingtalkDetail_.workDate), start, end);
+            query.select(root).where(p);
+            List<AttendanceDingtalkDetail> detailList = em.createQuery(query).getResultList();
+            //先删除
+            logger.info("删除");
+            if (detailList != null) {
+                logger.info("删除 list:"+detailList.size());
+                emc.beginTransaction(AttendanceDingtalkDetail.class);
+                for (int i = 0; i < detailList.size(); i++) {
+                    emc.remove(detailList.get(i));
+                }
+                emc.commit();
+            }
+            logger.info("删除结束");
+        }
+    }
+
+    private void saveDingdingAttendance(List<DingdingAttendanceResultItem> list, List<Person> personList) throws Exception {
+        if (list != null && !list.isEmpty()) {
+            try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+                Business business = new Business(emc);
+                emc.beginTransaction(AttendanceDingtalkDetail.class);
+                for (int i = 0; i < list.size(); i++) {
+                    DingdingAttendanceResultItem item = list.get(i);
+                    AttendanceDingtalkDetail detail = DingdingAttendanceResultItem.copier.copy(item);
+                    detail.setDdId(item.getId());
+                    if (detail.getUserCheckTime() > 0) {
+                        Date date = new Date(detail.getUserCheckTime());
+                        detail.setUserCheckTimeDate(date);
+                    }
+                    //添加o2组织和用户
+                    Optional<Person> first = personList.stream().filter(p -> item.userId.equals(p.getDingdingId())).findFirst();
+                    if (first.isPresent()) {
+                        Person person = first.get();
+                        String unit = getUnitWithPerson(person.getDistinguishedName(), business);
+                        detail.setO2Unit(unit);
+                        detail.setO2User(person.getDistinguishedName());
+                    }
+                    emc.persist(detail);
+                }
+                emc.commit();
+            }
+        }
+    }
+    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 updateSyncRecord(DingdingQywxSyncRecord record, String errMsg) throws Exception {
+        try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+            emc.beginTransaction(DingdingQywxSyncRecord.class);
+            DingdingQywxSyncRecord entity = emc.find(record.getId(), DingdingQywxSyncRecord.class);
+            entity.setEndTime(new Date());
+            if (errMsg == null || errMsg.isEmpty()) {
+                entity.setStatus(DingdingQywxSyncRecord.status_end);
+            } else {
+                entity.setStatus(DingdingQywxSyncRecord.status_error);
+                entity.setExceptionMessage(errMsg);
+            }
+            emc.commit();
+        }
+    }
+
+
+    public static class DingdingAttendancePost extends GsonPropertyObject {
+        //        {
+//            "workDateFrom": "yyyy-MM-dd HH:mm:ss",
+//                "workDateTo": "yyyy-MM-dd HH:mm:ss",
+//                "userIdList":["员工UserId列表"],    // 必填,与offset和limit配合使用
+//            "offset":0,    // 必填,第一次传0,如果还有多余数据,下次传之前的offset加上limit的值
+//                "limit":1,     // 必填,表示数据条数,最大不能超过50条
+//        }
+        private String workDateFrom;
+        private String workDateTo;
+        private List<String> userIdList;
+        private Long offset;
+        private Long limit;
+
+        public String getWorkDateFrom() {
+            return workDateFrom;
+        }
+
+        public void setWorkDateFrom(String workDateFrom) {
+            this.workDateFrom = workDateFrom;
+        }
+
+        public String getWorkDateTo() {
+            return workDateTo;
+        }
+
+        public void setWorkDateTo(String workDateTo) {
+            this.workDateTo = workDateTo;
+        }
+
+        public List<String> getUserIdList() {
+            return userIdList;
+        }
+
+        public void setUserIdList(List<String> userIdList) {
+            this.userIdList = userIdList;
+        }
+
+        public Long getOffset() {
+            return offset;
+        }
+
+        public void setOffset(Long offset) {
+            this.offset = offset;
+        }
+
+        public Long getLimit() {
+            return limit;
+        }
+
+        public void setLimit(Long limit) {
+            this.limit = limit;
+        }
+    }
+
+    public static class DingdingAttendanceResult extends GsonPropertyObject {
+        private Integer errcode;
+        private String errmsg;
+        private Boolean hasMore;
+        private List<DingdingAttendanceResultItem> recordresult;
+
+        public Integer getErrcode() {
+            return errcode;
+        }
+
+        public void setErrcode(Integer errcode) {
+            this.errcode = errcode;
+        }
+
+        public String getErrmsg() {
+            return errmsg;
+        }
+
+        public void setErrmsg(String errmsg) {
+            this.errmsg = errmsg;
+        }
+
+        public Boolean getHasMore() {
+            return hasMore;
+        }
+
+        public void setHasMore(Boolean hasMore) {
+            this.hasMore = hasMore;
+        }
+
+        public List<DingdingAttendanceResultItem> getRecordresult() {
+            return recordresult;
+        }
+
+        public void setRecordresult(List<DingdingAttendanceResultItem> recordresult) {
+            this.recordresult = recordresult;
+        }
+    }
+
+    public static class DingdingAttendanceResultItem extends GsonPropertyObject {
+
+        private static final long serialVersionUID = 1618421987561110713L;
+
+        static WrapCopier<DingdingAttendanceResultItem, AttendanceDingtalkDetail> copier = WrapCopierFactory.wi(DingdingAttendanceResultItem.class,
+                AttendanceDingtalkDetail.class, null, JpaObject.FieldsUnmodify);
+
+        private long id;
+        private long ddId;
+        private String userId;
+        private long baseCheckTime;
+        private long userCheckTime;
+        private long workDate;
+        private String timeResult;
+        private String checkType;
+        private String locationResult;
+        private String sourceType;
+        private long groupId;
+        private long planId;
+        private long recordId;
+
+        public long getId() {
+            return id;
+        }
+
+        public void setId(long id) {
+            this.id = id;
+        }
+
+        public long getDdId() {
+            return ddId;
+        }
+
+        public void setDdId(long ddId) {
+            this.ddId = ddId;
+        }
+
+        public String getUserId() {
+            return userId;
+        }
+
+        public void setUserId(String userId) {
+            this.userId = userId;
+        }
+
+        public long getBaseCheckTime() {
+            return baseCheckTime;
+        }
+
+        public void setBaseCheckTime(long baseCheckTime) {
+            this.baseCheckTime = baseCheckTime;
+        }
+
+        public long getUserCheckTime() {
+            return userCheckTime;
+        }
+
+        public void setUserCheckTime(long userCheckTime) {
+            this.userCheckTime = userCheckTime;
+        }
+
+        public long getWorkDate() {
+            return workDate;
+        }
+
+        public void setWorkDate(long workDate) {
+            this.workDate = workDate;
+        }
+
+        public String getTimeResult() {
+            return timeResult;
+        }
+
+        public void setTimeResult(String timeResult) {
+            this.timeResult = timeResult;
+        }
+
+        public String getCheckType() {
+            return checkType;
+        }
+
+        public void setCheckType(String checkType) {
+            this.checkType = checkType;
+        }
+
+        public String getLocationResult() {
+            return locationResult;
+        }
+
+        public void setLocationResult(String locationResult) {
+            this.locationResult = locationResult;
+        }
+
+        public String getSourceType() {
+            return sourceType;
+        }
+
+        public void setSourceType(String sourceType) {
+            this.sourceType = sourceType;
+        }
+
+        public long getGroupId() {
+            return groupId;
+        }
+
+        public void setGroupId(long groupId) {
+            this.groupId = groupId;
+        }
+
+        public long getPlanId() {
+            return planId;
+        }
+
+        public void setPlanId(long planId) {
+            this.planId = planId;
+        }
+
+        public long getRecordId() {
+            return recordId;
+        }
+
+        public void setRecordId(long recordId) {
+            this.recordId = recordId;
+        }
+    }
+}

+ 131 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QueueDingdingPersonStatistic.java

@@ -0,0 +1,131 @@
+package com.x.attendance.assemble.control;
+
+import java.util.Date;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.x.attendance.entity.AttendanceDingtalkDetail;
+import com.x.attendance.entity.StatisticDingdingPersonForMonth;
+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.Applications;
+import com.x.base.core.project.x_organization_assemble_control;
+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;
+
+/**
+ * Created by fancyLou on 2020-04-05.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class QueueDingdingPersonStatistic extends AbstractQueue<Date> {
+    private static final Logger logger = LoggerFactory.getLogger(QueueDingdingPersonStatistic.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()
+                            .getStatPersonForMonthIds(year, month, person.getDistinguishedName());
+                    emc.beginTransaction(StatisticDingdingPersonForMonth.class);
+                    if ( ListTools.isNotEmpty( ids ) ) {
+                        for (String item : ids) {
+                            StatisticDingdingPersonForMonth personForMonth_temp = emc.find(item, StatisticDingdingPersonForMonth.class);
+                            emc.remove(personForMonth_temp);
+                        }
+                    }
+                    StatisticDingdingPersonForMonth personForMonth = new StatisticDingdingPersonForMonth();
+                    personForMonth.setStatisticYear(year);
+                    personForMonth.setStatisticMonth(month);
+                    personForMonth.setO2Unit(getUnitWithPerson(person.getDistinguishedName(), business));
+                    personForMonth.setO2User(person.getDistinguishedName());
+                    Long onduty = business.dingdingAttendanceFactory().dingdingPersonForMonthDutyTimesCount(year, month,
+                            person.getDistinguishedName(), AttendanceDingtalkDetail.OnDuty);
+                    personForMonth.setWorkDayCount(onduty);
+                    personForMonth.setOnDutyTimes(onduty);
+                    personForMonth.setOffDutyTimes(business.dingdingAttendanceFactory().dingdingPersonForMonthDutyTimesCount(year, month,
+                            person.getDistinguishedName(), AttendanceDingtalkDetail.OffDuty));
+                    personForMonth.setResultNormal(business.dingdingAttendanceFactory().dingdingPersonForMonthTimeResultCount(year, month,
+                            person.getDistinguishedName(), AttendanceDingtalkDetail.TIMERESULT_NORMAL));
+                    personForMonth.setLateTimes(business.dingdingAttendanceFactory().dingdingPersonForMonthTimeResultCount(year, month,
+                            person.getDistinguishedName(), AttendanceDingtalkDetail.TIMERESULT_Late));
+                    personForMonth.setLeaveEarlyTimes(business.dingdingAttendanceFactory().dingdingPersonForMonthTimeResultCount(year, month,
+                            person.getDistinguishedName(), AttendanceDingtalkDetail.TIMERESULT_Early));
+                    personForMonth.setAbsenteeismTimes(business.dingdingAttendanceFactory().dingdingPersonForMonthTimeResultCount(year, month,
+                            person.getDistinguishedName(), AttendanceDingtalkDetail.TIMERESULT_Absenteeism));
+                    personForMonth.setNotSignedCount(business.dingdingAttendanceFactory().dingdingPersonForMonthTimeResultCount(year, month,
+                            person.getDistinguishedName(), AttendanceDingtalkDetail.TIMERESULT_NotSigned));
+                    personForMonth.setSeriousLateTimes(business.dingdingAttendanceFactory().dingdingPersonForMonthTimeResultCount(year, month,
+                            person.getDistinguishedName(), AttendanceDingtalkDetail.TIMERESULT_SeriousLate));
+                    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";
+                    uri = Applications.joinQueryUri("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;
+    }
+
+
+
+
+}

+ 147 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QueueDingdingUnitStatistic.java

@@ -0,0 +1,147 @@
+package com.x.attendance.assemble.control;
+
+import java.util.Date;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.x.attendance.entity.AttendanceDingtalkDetail;
+import com.x.attendance.entity.StatisticDingdingUnitForDay;
+import com.x.attendance.entity.StatisticDingdingUnitForMonth;
+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 com.x.base.core.project.tools.ListTools;
+
+/**
+ * Created by fancyLou on 2020-04-05.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class QueueDingdingUnitStatistic extends AbstractQueue<Date> {
+    private static final Logger logger = LoggerFactory.getLogger(QueueDingdingUnitStatistic.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().dingdingUnitDistinct(dateString);
+        if (units != null && !units.isEmpty()) {
+            for (String unit : units) {
+                if (StringUtils.isEmpty(unit)){
+                    continue;
+                }
+                List<String> ids = business.dingdingAttendanceFactory().getStatUnitForDayIds(year, month, day, unit);
+                emc.beginTransaction(StatisticDingdingUnitForDay.class);
+                if ( ListTools.isNotEmpty( ids ) ) {
+                    for (String item : ids) {
+                        StatisticDingdingUnitForDay statisticTopUnitForDay_tmp = emc.find(item, StatisticDingdingUnitForDay.class);
+                        emc.remove(statisticTopUnitForDay_tmp);
+                    }
+                }
+                //for day
+                StatisticDingdingUnitForDay unitForDay = new StatisticDingdingUnitForDay();
+                unitForDay.setO2Unit(unit);
+                unitForDay.setStatisticYear(year);
+                unitForDay.setStatisticMonth(month);
+                unitForDay.setStatisticDate(day);
+                Long on = business.dingdingAttendanceFactory().dingdingUnitForDayDutyTimesCount(dateString, unit, AttendanceDingtalkDetail.OnDuty);
+                unitForDay.setWorkDayCount(on);
+                unitForDay.setOnDutyTimes(on);
+                unitForDay.setOffDutyTimes(business.dingdingAttendanceFactory().
+                        dingdingUnitForDayDutyTimesCount(dateString, unit, AttendanceDingtalkDetail.OffDuty));
+                unitForDay.setResultNormal(business.dingdingAttendanceFactory().dingdingUnitForDayTimeResultCount(dateString, unit,
+                        AttendanceDingtalkDetail.TIMERESULT_NORMAL));
+                unitForDay.setLateTimes(business.dingdingAttendanceFactory().dingdingUnitForDayTimeResultCount(dateString, unit,
+                        AttendanceDingtalkDetail.TIMERESULT_Late));
+                unitForDay.setLeaveEarlyTimes(business.dingdingAttendanceFactory().dingdingUnitForDayTimeResultCount(dateString, unit,
+                        AttendanceDingtalkDetail.TIMERESULT_Early));
+                unitForDay.setNotSignedCount(business.dingdingAttendanceFactory().dingdingUnitForDayTimeResultCount(dateString, unit,
+                        AttendanceDingtalkDetail.TIMERESULT_NotSigned));
+                unitForDay.setAbsenteeismTimes(business.dingdingAttendanceFactory().dingdingUnitForDayTimeResultCount(dateString, unit,
+                        AttendanceDingtalkDetail.TIMERESULT_Absenteeism));
+                unitForDay.setSeriousLateTimes(business.dingdingAttendanceFactory().dingdingUnitForDayTimeResultCount(dateString, unit,
+                        AttendanceDingtalkDetail.TIMERESULT_SeriousLate));
+                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().sumWorkDayUnitForDayWithMonth(year, month, unit);
+            Long onduty = business.dingdingAttendanceFactory().sumOnDutyUnitForDayWithMonth(year, month, unit);
+            Long offDuty = business.dingdingAttendanceFactory().sumOffDutyUnitForDayWithMonth(year, month, unit);
+            Long normal = business.dingdingAttendanceFactory().sumNormalUnitForDayWithMonth(year, month, unit);
+            Long late = business.dingdingAttendanceFactory().sumLateTimesUnitForDayWithMonth(year, month, unit);
+            Long leaveearly = business.dingdingAttendanceFactory().sumLeaveEarlyUnitForDayWithMonth(year, month, unit);
+            Long notSign = business.dingdingAttendanceFactory().sumNotSignedUnitForDayWithMonth(year, month, unit);
+            Long absenteeism = business.dingdingAttendanceFactory().sumAbsenteeismUnitForDayWithMonth(year, month, unit);
+            Long serious = business.dingdingAttendanceFactory().sumSeriousLateUnitForDayWithMonth(year, month, unit);
+            List<String> list = business.dingdingAttendanceFactory().getStatUnitForMonthIds(year, month, unit);
+            emc.beginTransaction(StatisticDingdingUnitForMonth.class);
+            if (list != null && list.size() > 0) {
+                for (String item : list) {
+                    StatisticDingdingUnitForMonth statisticTopUnitForMonth_tmp = emc.find(item, StatisticDingdingUnitForMonth.class);
+                    emc.remove(statisticTopUnitForMonth_tmp);
+                }
+            }
+            StatisticDingdingUnitForMonth unitForMonth = new StatisticDingdingUnitForMonth();
+            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.setSeriousLateTimes(serious);
+            emc.persist(unitForMonth);
+            emc.commit();
+        }
+
+    }
+}

+ 53 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QueuePersonAttendanceDetailAnalyse.java

@@ -0,0 +1,53 @@
+package com.x.attendance.assemble.control;
+
+import java.util.List;
+import java.util.Map;
+
+import com.x.attendance.assemble.control.service.AttendanceDetailAnalyseServiceAdv;
+import com.x.attendance.assemble.control.service.AttendanceDetailServiceAdv;
+import com.x.attendance.assemble.control.service.AttendanceStatisticalCycleServiceAdv;
+import com.x.attendance.assemble.control.service.AttendanceWorkDayConfigServiceAdv;
+import com.x.attendance.entity.AttendanceDetail;
+import com.x.attendance.entity.AttendanceStatisticalCycle;
+import com.x.attendance.entity.AttendanceWorkDayConfig;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+import com.x.base.core.project.queue.AbstractQueue;
+
+/**
+ * 对单个员工的打卡信息进行分析的队列
+ */
+public class QueuePersonAttendanceDetailAnalyse extends AbstractQueue<String> {
+
+    private static final Logger logger = LoggerFactory.getLogger(QueuePersonAttendanceDetailAnalyse.class);
+
+    @Override
+    protected void execute( String detailId ) throws Exception {
+        AttendanceWorkDayConfigServiceAdv attendanceWorkDayConfigServiceAdv = new AttendanceWorkDayConfigServiceAdv();
+        AttendanceStatisticalCycleServiceAdv statisticalCycleServiceAdv = new AttendanceStatisticalCycleServiceAdv();
+        AttendanceDetailAnalyseServiceAdv detailAnalyseServiceAdv = new AttendanceDetailAnalyseServiceAdv();
+        AttendanceDetailServiceAdv detailServiceAdv = new AttendanceDetailServiceAdv();
+        AttendanceDetail record = detailServiceAdv.get( detailId );
+        if( record != null ){
+            logger.debug("system try to analyse attendance detail for person, Id:" + record.getId() );
+
+            try {
+                List<AttendanceWorkDayConfig> workDayConfigList = attendanceWorkDayConfigServiceAdv.getAllWorkDayConfigWithCache( false );
+                Map<String, Map<String, List<AttendanceStatisticalCycle>>> statisticalCycleMap = statisticalCycleServiceAdv.getAllStatisticalCycleMapWithCache( false );
+
+                detailAnalyseServiceAdv.analyseAttendanceDetail( record, workDayConfigList, statisticalCycleMap, false );
+                logger.debug( "attendance detail analyse completed.person:" + record.getEmpName() + ", date:" + record.getRecordDateString());
+
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+
+            logger.debug("["+record.getEmpName()+"]["+ record.getRecordDateString() +"] attendance detail record analyse task execute completed。" );
+        }else{
+            logger.warn("attandence detail not exists, id:" + detailId );
+        }
+
+    }
+
+
+}

+ 397 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QueueQywxAttendanceSync.java

@@ -0,0 +1,397 @@
+package com.x.attendance.assemble.control;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import javax.persistence.EntityManager;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.apache.commons.lang3.StringUtils;
+
+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.factory.EntityManagerContainerFactory;
+import com.x.base.core.entity.JpaObject;
+import com.x.base.core.project.Application;
+import com.x.base.core.project.x_organization_assemble_control;
+import com.x.base.core.project.bean.WrapCopier;
+import com.x.base.core.project.bean.WrapCopierFactory;
+import com.x.base.core.project.config.Config;
+import com.x.base.core.project.connection.HttpConnection;
+import com.x.base.core.project.gson.GsonPropertyObject;
+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;
+
+public class QueueQywxAttendanceSync extends AbstractQueue<DingdingQywxSyncRecord> {
+
+    private static final Logger logger = LoggerFactory.getLogger(QueueQywxAttendanceSync.class);
+
+
+    @Override
+    protected void execute(DingdingQywxSyncRecord record) throws Exception {
+        logger.info("开始执行企业微信打卡数据同步,from:" + record.getDateFrom() + ", to:"+record.getDateTo());
+        if (DingdingQywxSyncRecord.syncType_qywx.equals(record.getType())) {
+            try {
+                qywxSync(record);
+            }catch (Exception e) {
+                logger.error(e);
+                updateSyncRecord(record, e.getMessage());
+            }
+        }
+    }
+
+
+    private void qywxSync(DingdingQywxSyncRecord record) throws Exception{
+        Application app = ThisApplication.context().applications().randomWithWeight(x_organization_assemble_control.class.getName());
+        //开始分页查询人员
+        boolean hasNextPerson = true;
+        int personPageSize = 50;
+        //开始时间和结束时间
+        Date fromDate = new Date();
+        fromDate.setTime(record.getDateFrom());
+        Date toDate =new Date();
+        toDate.setTime(record.getDateTo());
+        //先删除
+        deleteQywxAttendance(fromDate, toDate);
+        //人员查询地址
+        String uri = "person/list/(0)/next/50";
+        String qywxuri = Config.qiyeweixin().getApiAddress() + "/cgi-bin/checkin/getcheckindata?access_token="+ Config.qiyeweixin().attendanceAccessToken();
+        int saveNumber = 0;
+        while (hasNextPerson) {
+            List<Person> list = ThisApplication.context().applications().getQuery(false, app, uri).getDataAsList(Person.class);
+            if (list != null && list.size() > 0) {
+                //钉钉用户id
+                List<String> qywxUsers = list.stream().filter(person -> StringUtils.isNotEmpty(person.getQiyeweixinId()))
+                        .map(Person::getQiyeweixinId).collect(Collectors.toList());
+
+                if(ListTools.isNotEmpty(qywxUsers)){
+                    QywxPost post = new QywxPost();
+                    post.setStarttime(DateTools.toUnixTimeStamp(fromDate.getTime()));
+                    post.setEndtime(DateTools.toUnixTimeStamp(toDate.getTime()));
+                    post.setUseridlist(qywxUsers);
+                    post.setOpencheckindatatype(3);//全部打卡信息
+                    logger.info("企业微信 post :" + post.toString());
+                    QywxResult result = HttpConnection.postAsObject(qywxuri, null, post.toString(), QywxResult.class);
+                    logger.info("返回结果:"+result.toString());
+                    if (result.errcode == 0) {
+                        List<QywxResultItem> resultList = result.getCheckindata();
+                        saveQywxAttendance(resultList, list);
+                        saveNumber += resultList.size();
+                    } else {
+                        //请求结果异常 结束
+                        throw new ExceptionDingDingRequest(result.errmsg);
+                    }
+
+                }
+
+                //是否还有更多用户
+                if (list.size() < personPageSize) {
+                    logger.info("同步钉钉考勤 没有更多用户了,结束!");
+                    hasNextPerson = false;
+                    updateSyncRecord(record, null);
+                } else {
+                    //还有更多用户继续查询
+                    uri = "person/list/" + list.get(list.size() - 1).getDistinguishedName() + "/next/50";
+                }
+            } else {
+                //没有用户查询到结束
+                logger.info("同步钉钉考勤 查询不到用户了,结束!");
+                hasNextPerson = false;
+                updateSyncRecord(record, null);
+            }
+        }
+        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, List<Person> personList) throws Exception {
+        if (resultList != null && !resultList.isEmpty()) {
+            try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+                Business business = new Business(emc);
+                emc.beginTransaction(AttendanceQywxDetail.class);
+                for (int i = 0; i < resultList.size(); i++) {
+                    QywxResultItem item = resultList.get(i);
+                    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.commit();
+            }
+        }
+    }
+
+    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 {
+        try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+            //先删除 再同步
+            EntityManager em = emc.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), fromDate, toDate);
+            query.select(root).where(p);
+            List<AttendanceQywxDetail> detailList = em.createQuery(query).getResultList();
+            //先删除
+            logger.info("删除");
+            if (detailList != null) {
+                logger.info("删除 list:"+detailList.size());
+                emc.beginTransaction(AttendanceQywxDetail.class);
+                for (int i = 0; i < detailList.size(); i++) {
+                    emc.remove(detailList.get(i));
+                }
+                emc.commit();
+            }
+            logger.info("删除结束");
+        }
+    }
+
+
+    private void updateSyncRecord(DingdingQywxSyncRecord record, String message) throws Exception {
+        try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+            emc.beginTransaction(DingdingQywxSyncRecord.class);
+            DingdingQywxSyncRecord entity = emc.find(record.getId(), DingdingQywxSyncRecord.class);
+            entity.setEndTime(new Date());
+            if (message == null || message.isEmpty()) {
+                entity.setStatus(DingdingQywxSyncRecord.status_end);
+            } else {
+                entity.setStatus(DingdingQywxSyncRecord.status_error);
+                entity.setExceptionMessage(message);
+            }
+            emc.commit();
+        }
+    }
+
+
+    public static class QywxPost extends GsonPropertyObject {
+        //{
+        //   "opencheckindatatype": 3,
+        //   "starttime": 1492617600,
+        //   "endtime": 1492790400,
+        //   "useridlist": ["james","paul"]
+        //}
+
+        //打卡类型。1:上下班打卡;2:外出打卡;3:全部打卡
+        private int opencheckindatatype;
+        private long starttime;
+        private long endtime;
+        private List<String> useridlist;
+
+        public int getOpencheckindatatype() {
+            return opencheckindatatype;
+        }
+
+        public void setOpencheckindatatype(int opencheckindatatype) {
+            this.opencheckindatatype = opencheckindatatype;
+        }
+
+        public long getStarttime() {
+            return starttime;
+        }
+
+        public void setStarttime(long starttime) {
+            this.starttime = starttime;
+        }
+
+        public long getEndtime() {
+            return endtime;
+        }
+
+        public void setEndtime(long endtime) {
+            this.endtime = endtime;
+        }
+
+        public List<String> getUseridlist() {
+            return useridlist;
+        }
+
+        public void setUseridlist(List<String> useridlist) {
+            this.useridlist = useridlist;
+        }
+    }
+
+    public static class QywxResult extends GsonPropertyObject {
+        private int errcode;
+        private String errmsg;
+        private List<QywxResultItem> checkindata;
+
+        public int getErrcode() {
+            return errcode;
+        }
+
+        public void setErrcode(int errcode) {
+            this.errcode = errcode;
+        }
+
+        public String getErrmsg() {
+            return errmsg;
+        }
+
+        public void setErrmsg(String errmsg) {
+            this.errmsg = errmsg;
+        }
+
+        public List<QywxResultItem> getCheckindata() {
+            return checkindata;
+        }
+
+        public void setCheckindata(List<QywxResultItem> checkindata) {
+            this.checkindata = checkindata;
+        }
+    }
+    public static class QywxResultItem extends GsonPropertyObject {
+
+
+        static WrapCopier<QywxResultItem, AttendanceQywxDetail> copier = WrapCopierFactory.wi(QywxResultItem.class,
+                AttendanceQywxDetail.class, null, JpaObject.FieldsUnmodify);
+
+        private String userid;
+        private String groupname;
+        private String checkin_type;
+        private long checkin_time;
+        private String exception_type;
+        private String location_title;
+        private String location_detail;
+        private String wifiname;
+        private String notes;
+
+        public String getUserid() {
+            return userid;
+        }
+
+        public void setUserid(String userid) {
+            this.userid = userid;
+        }
+
+        public String getGroupname() {
+            return groupname;
+        }
+
+        public void setGroupname(String groupname) {
+            this.groupname = groupname;
+        }
+
+        public String getCheckin_type() {
+            return checkin_type;
+        }
+
+        public void setCheckin_type(String checkin_type) {
+            this.checkin_type = checkin_type;
+        }
+
+        public long getCheckin_time() {
+            return checkin_time;
+        }
+
+        public void setCheckin_time(long checkin_time) {
+            this.checkin_time = checkin_time;
+        }
+
+        public String getException_type() {
+            return exception_type;
+        }
+
+        public void setException_type(String exception_type) {
+            this.exception_type = exception_type;
+        }
+
+        public String getLocation_title() {
+            return location_title;
+        }
+
+        public void setLocation_title(String location_title) {
+            this.location_title = location_title;
+        }
+
+        public String getLocation_detail() {
+            return location_detail;
+        }
+
+        public void setLocation_detail(String location_detail) {
+            this.location_detail = location_detail;
+        }
+
+        public String getWifiname() {
+            return wifiname;
+        }
+
+        public void setWifiname(String wifiname) {
+            this.wifiname = wifiname;
+        }
+
+        public String getNotes() {
+            return notes;
+        }
+
+        public void setNotes(String notes) {
+            this.notes = notes;
+        }
+    }
+}

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

@@ -0,0 +1,165 @@
+package com.x.attendance.assemble.control;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+
+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.x_organization_assemble_control;
+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;
+
+/**
+ * Created by fancyLou on 2020-04-05.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class QueueQywxPersonStatistic extends AbstractQueue<Date> {
+    private static final Logger logger = LoggerFactory.getLogger(QueueQywxPersonStatistic.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 ( ListTools.isNotEmpty( ids ) ) {
+                        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;
+    }
+
+
+
+
+}

+ 183 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/QueueQywxUnitStatistic.java

@@ -0,0 +1,183 @@
+package com.x.attendance.assemble.control;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.x.attendance.entity.AttendanceQywxDetail;
+import com.x.attendance.entity.StatisticQywxUnitForDay;
+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.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;
+
+/**
+ * Created by fancyLou on 2020-04-05.
+ * Copyright © 2020 O2. All rights reserved.
+ */
+public class QueueQywxUnitStatistic extends AbstractQueue<Date> {
+    private static final Logger logger = LoggerFactory.getLogger(QueueQywxUnitStatistic.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();
+        }
+
+    }
+}

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

@@ -0,0 +1,135 @@
+package com.x.attendance.assemble.control;
+
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadPoolExecutor;
+
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import com.x.attendance.assemble.control.processor.monitor.MonitorFileDataOpt;
+import com.x.attendance.assemble.control.processor.thread.DataProcessThreadFactory;
+import com.x.attendance.assemble.control.schedule.AttendanceStatisticTask;
+import com.x.attendance.assemble.control.schedule.DetailLastDayRecordAnalyseTask;
+import com.x.attendance.assemble.control.schedule.DingdingAttendanceSyncScheduleTask;
+import com.x.attendance.assemble.control.schedule.QywxAttendanceSyncScheduleTask;
+import com.x.attendance.assemble.control.schedule.v2.AttendanceV2DetailGenerateTask;
+import com.x.attendance.assemble.control.schedule.v2.AttendanceV2MessageSendTask;
+import com.x.attendance.assemble.control.schedule.v2.AttendanceV2TodayMessageDataGenerateTask;
+import com.x.attendance.assemble.control.schedule.v2.QueueAttendanceV2Detail;
+import com.x.attendance.assemble.control.service.AttendanceSettingService;
+import com.x.attendance.entity.v2.AttendanceV2Config;
+import com.x.base.core.container.EntityManagerContainer;
+import com.x.base.core.container.factory.EntityManagerContainerFactory;
+import com.x.base.core.project.Context;
+import com.x.base.core.project.cache.CacheManager;
+import com.x.base.core.project.config.Config;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+import com.x.base.core.project.message.MessageConnector;
+
+public class ThisApplication {
+
+
+  private static final Logger LOGGER = LoggerFactory.getLogger(ThisApplication.class);
+
+
+    private ThisApplication() {
+        // nothing
+    }
+
+    protected static Context context;
+
+    public static Context context() {
+        return context;
+    }
+
+    public static final QueueDingdingAttendance dingdingQueue = new QueueDingdingAttendance();
+    public static final QueueQywxAttendanceSync qywxQueue = new QueueQywxAttendanceSync();
+    public static final QueueQywxUnitStatistic unitQywxStatisticQueue = new QueueQywxUnitStatistic();
+    public static final QueueQywxPersonStatistic personQywxStatisticQueue = new QueueQywxPersonStatistic();
+    public static final QueueDingdingPersonStatistic personStatisticQueue = new QueueDingdingPersonStatistic();
+    public static final QueueDingdingUnitStatistic unitStatisticQueue = new QueueDingdingUnitStatistic();
+
+    public static final QueuePersonAttendanceDetailAnalyse detailAnalyseQueue = new QueuePersonAttendanceDetailAnalyse();
+    public static final QueueAttendanceDetailStatistic detailStatisticQueue = new QueueAttendanceDetailStatistic();
+    public static final String ROLE_AttendanceManager = "AttendanceManager@AttendanceManagerSystemRole@R";
+
+    // V2
+    public static final QueueAttendanceV2Detail queueV2Detail = new QueueAttendanceV2Detail();
+    
+    // 同步执行器  这里还有集群服务器的问题
+    public static final ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(1);
+
+    public static void init() throws Exception {
+        try {
+            CacheManager.init(context.clazz().getSimpleName());
+            new AttendanceSettingService().initAllSystemConfig();
+            context.startQueue(detailAnalyseQueue);
+            context.startQueue(detailStatisticQueue);
+            MessageConnector.start(context());
+            if (BooleanUtils.isTrue(Config.dingding().getAttendanceSyncEnable())) {
+                context.startQueue(dingdingQueue);
+                context.startQueue(personStatisticQueue);
+                context.startQueue(unitStatisticQueue);
+                context.schedule(DingdingAttendanceSyncScheduleTask.class, "0 0 1 * * ?");
+                // 已经将任务 放到了同步结束后执行 暂时不需要开定时任务了
+            }
+            if (BooleanUtils.isTrue(Config.qiyeweixin().getAttendanceSyncEnable())) {
+                context.startQueue(qywxQueue);
+                context.startQueue(unitQywxStatisticQueue);
+                context.startQueue(personQywxStatisticQueue);
+                context.schedule(QywxAttendanceSyncScheduleTask.class, "0 0 1 * * ?");
+            }
+            context.schedule(AttendanceStatisticTask.class, "0 0 0/4 * * ?");
+            // context.schedule(MobileRecordAnalyseTask.class, "0 0 * * * ?");
+            // 每天凌晨1点,计算前一天所有的未签退和未分析的打卡数据
+            context.schedule(DetailLastDayRecordAnalyseTask.class, "0 0 1 * * ?");
+
+            /////////////////// V2///
+            // 处理考勤统计相关的队列
+            context.startQueue(queueV2Detail);
+            // 配置对象 考勤统计定时器可配置
+            AttendanceV2Config config = null; 
+            String cronString = null;
+            try  {
+                EntityManagerContainer emc = EntityManagerContainerFactory.instance().create();
+                List<AttendanceV2Config> configs = emc.listAll(AttendanceV2Config.class);
+                if (configs != null && !configs.isEmpty()) {
+                    config = configs.get(0);
+                }
+                if (config != null) {
+                    cronString = config.getDetailStatisticCronString();
+                }
+            } catch (Exception e) {
+                LOGGER.error(e);
+            }
+            if (StringUtils.isEmpty(cronString)) {
+                cronString = "0 0 3 * * ?";
+            }
+            if (LOGGER.isDebugEnabled()) {
+                LOGGER.debug("定时表达式 {}", cronString);
+            }
+            // 每天凌晨3点,计算前一天的考勤数据
+            context.schedule(AttendanceV2DetailGenerateTask.class, cronString);
+            // 每天凌晨 3 点半,重新计算当天要发送消息的数据。
+            context.schedule(AttendanceV2TodayMessageDataGenerateTask.class, "0 30 3 * * ?");
+            // 4点钟开始 每 5 分钟检查 发送考勤相关消息的任务
+            context.schedule(AttendanceV2MessageSendTask.class, "0 0/5 4-23 * * ?");
+
+        } catch (Exception e) {
+            LOGGER.error(e);
+        }
+    }
+
+    public static void destroy() {
+        try {
+            CacheManager.shutdown();
+            DataProcessThreadFactory.getInstance().showdown();
+            MonitorFileDataOpt.stop();
+            executor.shutdown();
+        } catch (Exception e) {
+            LOGGER.error(e);
+        }
+    }
+}

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

@@ -0,0 +1,53 @@
+package com.x.attendance.assemble.control.factory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import com.x.attendance.assemble.control.AbstractFactory;
+import com.x.attendance.assemble.control.Business;
+import com.x.attendance.entity.AttendanceAdmin;
+import com.x.attendance.entity.AttendanceAdmin_;
+import com.x.base.core.project.exception.ExceptionWhen;
+/**
+ * 系统配置信息表基础功能服务类
+ */
+public class AttendanceAdminFactory extends AbstractFactory {
+	
+	public AttendanceAdminFactory(Business business) throws Exception {
+		super(business);
+	}
+
+	//@MethodDescribe("获取指定Id的AttendanceAdmin应用信息对象")
+	public AttendanceAdmin get( String id ) throws Exception {
+		return this.entityManagerContainer().find(id, AttendanceAdmin.class, ExceptionWhen.none);
+	}
+	
+	//@MethodDescribe("列示全部的AttendanceAdmin应用信息列表")
+	@SuppressWarnings("unused")
+	public List<AttendanceAdmin> listAll() throws Exception {
+		EntityManager em = this.entityManagerContainer().get(AttendanceAdmin.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceAdmin> cq = cb.createQuery(AttendanceAdmin.class);
+		Root<AttendanceAdmin> root = cq.from( AttendanceAdmin.class);
+		return em.createQuery(cq).getResultList();
+	}
+	
+	//@MethodDescribe("列示指定Id的AttendanceAdmin应用信息列表")
+	public List<AttendanceAdmin> list(List<String> ids) throws Exception {
+		if( ids == null || ids.size() == 0 ){
+			return new ArrayList<AttendanceAdmin>();
+		}
+		EntityManager em = this.entityManagerContainer().get(AttendanceAdmin.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceAdmin> cq = cb.createQuery(AttendanceAdmin.class);
+		Root<AttendanceAdmin> root = cq.from(AttendanceAdmin.class);
+		Predicate p = root.get(AttendanceAdmin_.id).in(ids);
+		return em.createQuery(cq.where(p)).getResultList();
+	}	
+}

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

@@ -0,0 +1,703 @@
+package com.x.attendance.assemble.control.factory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Order;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.x.attendance.assemble.control.AbstractFactory;
+import com.x.attendance.assemble.control.Business;
+import com.x.attendance.assemble.control.CriteriaQueryTools;
+import com.x.attendance.assemble.control.jaxrs.attendanceappealinfo.WrapInFilterAppeal;
+import com.x.attendance.entity.AttendanceAdmin;
+import com.x.attendance.entity.AttendanceAdmin_;
+import com.x.attendance.entity.AttendanceAppealInfo;
+import com.x.attendance.entity.AttendanceAppealInfo_;
+import com.x.base.core.project.exception.ExceptionWhen;
+import com.x.base.core.project.http.EffectivePerson;
+import com.x.base.core.project.tools.ListTools;
+/**
+ * 系统配置信息表基础功能服务类
+
+ */
+public class AttendanceAppealInfoFactory extends AbstractFactory {
+	
+	public AttendanceAppealInfoFactory(Business business) throws Exception {
+		super(business);
+	}
+
+	//@MethodDescribe("获取指定Id的AttendanceAppealInfo信息对象")
+	public AttendanceAppealInfo get( String id ) throws Exception {
+		return this.entityManagerContainer().find(id, AttendanceAppealInfo.class, ExceptionWhen.none);
+	}
+
+	public List<AttendanceAppealInfo> listWithDetailId(String id) throws Exception {
+		EntityManager em = this.entityManagerContainer().get(AttendanceAppealInfo.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceAppealInfo> cq = cb.createQuery(AttendanceAppealInfo.class);
+		Root<AttendanceAppealInfo> root = cq.from( AttendanceAppealInfo.class);
+		Predicate p = cb.equal( root.get(AttendanceAppealInfo_.detailId),  id );
+		return em.createQuery(cq.where(p)).getResultList();
+	}
+
+	//@MethodDescribe("列示全部的AttendanceAppealInfo信息列表")
+	public List<String> listAll() throws Exception {
+		EntityManager em = this.entityManagerContainer().get(AttendanceAppealInfo.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceAppealInfo> root = cq.from( AttendanceAppealInfo.class);
+		cq.select(root.get(AttendanceAppealInfo_.id));
+		return em.createQuery(cq).getResultList();
+	}
+	
+	//@MethodDescribe("列示全部的AttendanceAppealInfo信息列表")
+	public String getMaxRecordDate() throws Exception {
+		EntityManager em = this.entityManagerContainer().get(AttendanceAppealInfo.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceAppealInfo> cq = cb.createQuery(AttendanceAppealInfo.class);
+		Root<AttendanceAppealInfo> root = cq.from( AttendanceAppealInfo.class);
+		cq.orderBy( cb.desc( root.get( AttendanceAppealInfo_.recordDateString) ) );	
+		List<AttendanceAppealInfo> resultList = em.createQuery(cq).setMaxResults(1).getResultList();
+		if( resultList == null || resultList.size() == 0 ){
+			return null;
+		}else{
+			return resultList.get(0).getRecordDateString();
+		}
+	}
+	
+	//@MethodDescribe("根据员工姓名和打卡日期列示AttendanceAppealInfo信息列表")
+	public List<String> listByEmployeeNameAndAppealDate( String employeeName, String appealDateString ) throws Exception {
+		
+		if( employeeName == null || appealDateString == null ){
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get(AttendanceAppealInfo.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceAppealInfo> root = cq.from( AttendanceAppealInfo.class);
+		cq.select(root.get(AttendanceAppealInfo_.id));
+		Predicate p = cb.equal( root.get(AttendanceAppealInfo_.empName),  employeeName );
+		p = cb.and( p, cb.equal( root.get(AttendanceAppealInfo_.appealDateString ),  appealDateString ) );
+		return em.createQuery(cq.where( p )).getResultList();
+	}
+	
+	//@MethodDescribe("根据组织和打卡日期列示AttendanceAppealInfo信息列表")
+	public List<String> listByUnitNameAndAppealDate( String unitName, String appealDateString ) throws Exception {
+		
+		if( unitName == null || appealDateString == null ){
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get(AttendanceAppealInfo.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceAppealInfo> root = cq.from( AttendanceAppealInfo.class);
+		cq.select(root.get(AttendanceAppealInfo_.id));
+		Predicate p = cb.equal( root.get(AttendanceAppealInfo_.unitName),  unitName );
+		p = cb.and( p, cb.equal( root.get(AttendanceAppealInfo_.appealDateString ),  appealDateString ) );
+		return em.createQuery(cq.where( p )).getResultList();
+	}
+	
+	//@MethodDescribe("根据顶层组织和打卡日期列示AttendanceAppealInfo信息列表")
+	public List<String> listByTopUnitNameAndAppealDate( String topUnitName, String appealDateString ) throws Exception {
+		
+		if( topUnitName == null || appealDateString == null ){
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get(AttendanceAppealInfo.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceAppealInfo> root = cq.from( AttendanceAppealInfo.class);
+		cq.select(root.get(AttendanceAppealInfo_.id));
+		Predicate p = cb.equal( root.get(AttendanceAppealInfo_.topUnitName),  topUnitName );
+		p = cb.and( p, cb.equal( root.get(AttendanceAppealInfo_.appealDateString ),  appealDateString ) );
+		return em.createQuery(cq.where( p )).getResultList();
+	}
+	
+	//@MethodDescribe("列示指定Id的AttendanceAppealInfo信息列表")
+	public List<AttendanceAppealInfo> list(List<String> ids) throws Exception {
+		if( ids == null || ids.size() == 0 ){
+			return new ArrayList<AttendanceAppealInfo>();
+		}
+		EntityManager em = this.entityManagerContainer().get(AttendanceAppealInfo.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceAppealInfo> cq = cb.createQuery(AttendanceAppealInfo.class);
+		Root<AttendanceAppealInfo> root = cq.from(AttendanceAppealInfo.class);
+		Predicate p = root.get(AttendanceAppealInfo_.id).in(ids);
+		return em.createQuery(cq.where(p)).getResultList();
+	}
+
+	//@MethodDescribe("按年份月份查询某用户的申诉记录列表")
+	public List<String> listUserAttendanceAppealInfoByYearAndMonth(String user, String year, String month)  throws Exception {
+		if( user == null || user.isEmpty() ||year == null || month == null || year.isEmpty() || month.isEmpty()  ){
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceAppealInfo.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceAppealInfo> root = cq.from( AttendanceAppealInfo.class);
+		cq.select( root.get(AttendanceAppealInfo_.id ));
+		//一般始终为true, id is not null
+		Predicate p = cb.equal( root.get(AttendanceAppealInfo_.empName), user );
+		if( StringUtils.isNotEmpty( year  ) ){
+			p = cb.and(p, cb.equal( root.get(AttendanceAppealInfo_.yearString), year ));
+		}
+		if( StringUtils.isNotEmpty( month ) ){
+			p = cb.and(p, cb.equal( root.get(AttendanceAppealInfo_.monthString), month ));
+		}
+		
+		return em.createQuery(cq.where(p)).getResultList();
+	}
+	
+	//@MethodDescribe("按年份月份查询某组织的申诉记录列表")
+	public List<String> listUnitAttendanceAppealInfoByYearAndMonth(String unitName, String year, String month)  throws Exception {
+		EntityManager em = this.entityManagerContainer().get( AttendanceAppealInfo.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceAppealInfo> root = cq.from( AttendanceAppealInfo.class);
+		cq.select( root.get(AttendanceAppealInfo_.id ));
+		//一般始终为true, id is not null
+		Predicate p = cb.equal( root.get(AttendanceAppealInfo_.unitName), unitName );
+		if( StringUtils.isNotEmpty( year ) ){
+			p = cb.and(p, cb.equal( root.get(AttendanceAppealInfo_.yearString), year ));
+		}
+		if( StringUtils.isNotEmpty( month ) ){
+			p = cb.and(p, cb.equal( root.get(AttendanceAppealInfo_.monthString), month ));
+		}
+		
+		return em.createQuery(cq.where(p)).getResultList();
+	}
+	
+//	@MethodDescribe("按年份月份查询某顶层组织的申诉记录列表")
+	public List<String> listTopUnitAttendanceAppealInfoByYearAndMonth(String topUnitName, String year, String month)  throws Exception {
+		EntityManager em = this.entityManagerContainer().get( AttendanceAppealInfo.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceAppealInfo> root = cq.from( AttendanceAppealInfo.class);
+		cq.select( root.get(AttendanceAppealInfo_.id ));
+		//一般始终为true, id is not null
+		Predicate p = cb.equal( root.get(AttendanceAppealInfo_.topUnitName), topUnitName );
+		if( StringUtils.isNotEmpty( year ) ){
+			p = cb.and(p, cb.equal( root.get(AttendanceAppealInfo_.yearString), year ));
+		}
+		if( StringUtils.isNotEmpty( month ) ){
+			p = cb.and(p, cb.equal( root.get(AttendanceAppealInfo_.monthString), month ));
+		}
+		
+		return em.createQuery(cq.where(p)).getResultList();
+	}
+	
+	//@MethodDescribe("查询未归档的申诉记录列表,最大2000条")
+	public List<String> listNonArchiveAppealInfoIds()  throws Exception {
+		EntityManager em = this.entityManagerContainer().get( AttendanceAppealInfo.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceAppealInfo> root = cq.from( AttendanceAppealInfo.class);
+		cq.select( root.get(AttendanceAppealInfo_.id ));
+		//一般始终为true, id is not null
+		Predicate p = cb.isNotNull( root.get(AttendanceAppealInfo_.archiveTime) );
+		return em.createQuery(cq.where(p)).setMaxResults(2000).getResultList();
+	}
+	
+	/**
+	 * 查询下一页的信息数据
+	 * @param id
+	 * @param count
+	 * @param sequence
+	 * @param wrapIn
+	 * @return
+	 * @throws Exception
+	 */
+	@SuppressWarnings("unchecked")
+	public List<AttendanceAppealInfo> listIdsNextWithFilter( String id, Integer count, Object sequence, WrapInFilterAppeal wrapIn ) throws Exception {
+		//先获取上一页最后一条的sequence值,如果有值的话,以此sequence值作为依据取后续的count条数据
+		EntityManager em = this.entityManagerContainer().get( AttendanceAppealInfo.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceAppealInfo> cq = cb.createQuery(AttendanceAppealInfo.class);
+		Root<AttendanceAppealInfo> root = cq.from(AttendanceAppealInfo.class);
+
+		String order = wrapIn.getOrder();//排序方式
+		if( order == null || order.isEmpty() ){
+			order = "DESC";
+		}
+		String orderFieldName = "";
+		if(StringUtils.isNotEmpty( wrapIn.getKey())){
+			orderFieldName = wrapIn.getKey();
+		}else{
+			orderFieldName = "sequence";
+		}
+		Order _order = CriteriaQueryTools.setOrder(cb, root, AttendanceAppealInfo_.class, orderFieldName,order);
+		Predicate p = cb.isNotNull(root.get(AttendanceAppealInfo_.detailId));
+		if ((null != sequence) ) {
+			if(StringUtils.equalsIgnoreCase(order, "DESC")){
+				p = cb.and(p,cb.lessThan(root.get(AttendanceAppealInfo_.sequence),sequence.toString()));
+			}else{
+				p = cb.and(p,cb.greaterThan(root.get(AttendanceAppealInfo_.sequence),sequence.toString()));
+			}
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getDetailId())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.detailId),wrapIn.getDetailId()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getEmpName())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.empName),wrapIn.getEmpName()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getUnitName())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.unitName),wrapIn.getUnitName()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getTopUnitName())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.topUnitName),wrapIn.getTopUnitName()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getYearString())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.yearString),wrapIn.getYearString()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getMonthString())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.monthString),wrapIn.getMonthString()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getAppealReason())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.appealReason),wrapIn.getAppealReason()));
+		}
+		if (wrapIn.getStatus()!=999) {
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.status),wrapIn.getStatus()));
+		}
+		//添加OR条件
+		/*if (wrapIn.getOrAtrribute() != null && wrapIn.getOrAtrribute().size() > 0) {
+			sql_stringBuffer.append(" and (");
+			NameValueCountPair nameValueCountPair = null;
+			for (int p = 0; p < wrapIn.getOrAtrribute().size(); p++) {
+				nameValueCountPair = wrapIn.getOrAtrribute().get(p);
+				if (p == 0) {
+					sql_stringBuffer.append(" o." + nameValueCountPair.getName() + " = ?" + (index));
+
+				} else {
+					sql_stringBuffer.append(" or o." + nameValueCountPair.getName() + " = ?" + (index));
+				}
+				vs.add(nameValueCountPair.getValue());
+				index++;
+			}
+			sql_stringBuffer.append(" )");
+		}*/
+		Query query = em.createQuery(cq.select(root).where(p).orderBy(_order) );
+		return query.setMaxResults(count).getResultList();
+	}
+
+	/**
+	 * 查询下一页的信息数据--只查询当前人有权限审批的
+	 * @param id
+	 * @param count
+	 * @param sequence
+	 * @param wrapIn
+	 * @return
+	 * @throws Exception
+	 */
+	@SuppressWarnings("unchecked")
+	public List<AttendanceAppealInfo> listIdsNextWithFilterWithCurrentProcessor( String id, Integer count, Object sequence, WrapInFilterAppeal wrapIn ,Boolean isManager) throws Exception {
+		//先获取上一页最后一条的sequence值,如果有值的话,以此sequence值作为依据取后续的count条数据
+		EntityManager em = this.entityManagerContainer().get( AttendanceAppealInfo.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceAppealInfo> cq = cb.createQuery(AttendanceAppealInfo.class);
+		Root<AttendanceAppealInfo> root = cq.from(AttendanceAppealInfo.class);
+
+		String order = wrapIn.getOrder();//排序方式
+		if( order == null || order.isEmpty() ){
+			order = "DESC";
+		}
+		String orderFieldName = "";
+		if(StringUtils.isNotEmpty( wrapIn.getKey())){
+			orderFieldName = wrapIn.getKey();
+		}else{
+			orderFieldName = "sequence";
+		}
+		Order _order = CriteriaQueryTools.setOrder(cb, root, AttendanceAppealInfo_.class, orderFieldName,order);
+		Predicate p = cb.isNotNull(root.get(AttendanceAppealInfo_.detailId));
+		if ((null != sequence) ) {
+			if(StringUtils.equalsIgnoreCase(order, "DESC")){
+				p = cb.and(p,cb.lessThan(root.get(AttendanceAppealInfo_.sequence),sequence.toString()));
+			}else{
+				p = cb.and(p,cb.greaterThan(root.get(AttendanceAppealInfo_.sequence),sequence.toString()));
+			}
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getDetailId())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.detailId),wrapIn.getDetailId()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getEmpName())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.empName),wrapIn.getEmpName()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getUnitName())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.unitName),wrapIn.getUnitName()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getTopUnitName())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.topUnitName),wrapIn.getTopUnitName()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getYearString())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.yearString),wrapIn.getYearString()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getMonthString())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.monthString),wrapIn.getMonthString()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getAppealReason())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.appealReason),wrapIn.getAppealReason()));
+		}
+		if (wrapIn.getStatus()!=999) {
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.status),wrapIn.getStatus()));
+		}
+
+		if(!isManager){
+			if ((null != wrapIn.getProcessPerson1()) && (!wrapIn.getProcessPerson1().isEmpty())) {
+				p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.currentProcessor),wrapIn.getProcessPerson1()));
+			}
+		}
+
+		Query query = em.createQuery(cq.select(root).where(p).orderBy(_order) );
+		return query.setMaxResults(count).getResultList();
+	}
+
+	/**
+	 * 查询下一页的信息数据--按管理员组织
+	 * @param id
+	 * @param count
+	 * @param sequence
+	 * @param wrapIn
+	 * @return
+	 * @throws Exception
+	 */
+	@SuppressWarnings("unchecked")
+	public List<AttendanceAppealInfo> listIdsNextWithFilterWithManager( String id, Integer count, Object sequence, WrapInFilterAppeal wrapIn ,Map<String,List<String>> unitMap,Boolean isManager) throws Exception {
+		//先获取上一页最后一条的sequence值,如果有值的话,以此sequence值作为依据取后续的count条数据
+		Boolean unitFlag = false;
+		EntityManager em = this.entityManagerContainer().get( AttendanceAppealInfo.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceAppealInfo> cq = cb.createQuery(AttendanceAppealInfo.class);
+		Root<AttendanceAppealInfo> root = cq.from(AttendanceAppealInfo.class);
+
+		String order = wrapIn.getOrder();//排序方式
+		if( order == null || order.isEmpty() ){
+			order = "DESC";
+		}
+		String orderFieldName = "";
+		if(StringUtils.isNotEmpty( wrapIn.getKey())){
+			orderFieldName = wrapIn.getKey();
+		}else{
+			orderFieldName = "sequence";
+		}
+		Order _order = CriteriaQueryTools.setOrder(cb, root, AttendanceAppealInfo_.class, orderFieldName,order);
+		Predicate p = cb.isNotNull(root.get(AttendanceAppealInfo_.detailId));
+		if ((null != sequence) ) {
+			if(StringUtils.equalsIgnoreCase(order, "DESC")){
+				p = cb.and(p,cb.lessThan(root.get(AttendanceAppealInfo_.sequence),sequence.toString()));
+			}else{
+				p = cb.and(p,cb.greaterThan(root.get(AttendanceAppealInfo_.sequence),sequence.toString()));
+			}
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getProcessPerson1())) {
+			p = cb.and(p, cb.equal(root.get(AttendanceAppealInfo_.currentProcessor), wrapIn.getProcessPerson1()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getDetailId())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.detailId),wrapIn.getDetailId()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getEmpName())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.empName),wrapIn.getEmpName()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getUnitName())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.unitName),wrapIn.getUnitName()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getTopUnitName())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.topUnitName),wrapIn.getTopUnitName()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getYearString())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.yearString),wrapIn.getYearString()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getMonthString())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.monthString),wrapIn.getMonthString()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getAppealReason())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.appealReason),wrapIn.getAppealReason()));
+		}
+		if (wrapIn.getStatus()!=999) {
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.status),wrapIn.getStatus()));
+		}
+		// 这个管理员应该不需要 以前的错误逻辑 2022-1-11 @FancyLou
+//		if(!isManager){
+//			if(unitMap.isEmpty()){
+//				p = cb.and(p,cb.isNull(root.get(AttendanceAppealInfo_.detailId)));
+//			}else{
+//				if(unitMap.containsKey("COMPANY")){
+//					p = cb.and(p,root.get(AttendanceAppealInfo_.topUnitName).in(unitMap.get("COMPANY")));
+//				}
+//				if(unitMap.containsKey("DEPT")){
+//					p = cb.and(p,root.get(AttendanceAppealInfo_.unitName).in(unitMap.get("DEPT")));
+//				}
+//			}
+//		}
+		Query query = em.createQuery(cq.select(root).where(p).orderBy(_order) );
+		return query.setMaxResults(count).getResultList();
+	}
+
+	/**
+	 * 查询上一页的文档信息数据
+	 * @param id
+	 * @param count
+	 * @param sequence
+	 * @param wrapIn
+	 * @return
+	 * @throws Exception
+	 */
+	@SuppressWarnings("unchecked")
+	public List<AttendanceAppealInfo> listIdsPrevWithFilter( String id, Integer count, Object sequence, WrapInFilterAppeal wrapIn ) throws Exception {
+		//先获取上一页最后一条的sequence值,如果有值的话,以此sequence值作为依据取后续的count条数据
+		EntityManager em = this.entityManagerContainer().get( AttendanceAppealInfo.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceAppealInfo> cq = cb.createQuery(AttendanceAppealInfo.class);
+		Root<AttendanceAppealInfo> root = cq.from(AttendanceAppealInfo.class);
+
+		String order = wrapIn.getOrder();//排序方式
+		if( order == null || order.isEmpty() ){
+			order = "DESC";
+		}
+		String orderFieldName = "";
+		if(StringUtils.isNotEmpty( wrapIn.getKey())){
+			orderFieldName = wrapIn.getKey();
+		}else{
+			orderFieldName = "sequence";
+		}
+		Order _order = CriteriaQueryTools.setOrder(cb, root, AttendanceAppealInfo_.class, orderFieldName,order);
+		Predicate p = cb.isNotNull(root.get(AttendanceAppealInfo_.detailId));
+		if ((null != sequence) ) {
+			if(StringUtils.equalsIgnoreCase(order, "DESC")){
+				p = cb.and(p,cb.greaterThan(root.get(AttendanceAppealInfo_.sequence),sequence.toString()));
+			}else{
+				p = cb.and(p,cb.lessThan(root.get(AttendanceAppealInfo_.sequence),sequence.toString()));
+			}
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getDetailId())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.detailId),wrapIn.getDetailId()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getEmpName())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.empName),wrapIn.getEmpName()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getUnitName())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.unitName),wrapIn.getUnitName()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getTopUnitName())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.topUnitName),wrapIn.getTopUnitName()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getYearString())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.yearString),wrapIn.getYearString()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getMonthString())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.monthString),wrapIn.getMonthString()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getAppealReason())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.appealReason),wrapIn.getAppealReason()));
+		}
+		if (wrapIn.getStatus()!=999) {
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.status),wrapIn.getStatus()));
+		}
+		//添加OR条件
+		/*if (wrapIn.getOrAtrribute() != null && wrapIn.getOrAtrribute().size() > 0) {
+			sql_stringBuffer.append(" and (");
+			NameValueCountPair nameValueCountPair = null;
+			for (int p = 0; p < wrapIn.getOrAtrribute().size(); p++) {
+				nameValueCountPair = wrapIn.getOrAtrribute().get(p);
+				if (p == 0) {
+					sql_stringBuffer.append(" o." + nameValueCountPair.getName() + " = ?" + (index));
+
+				} else {
+					sql_stringBuffer.append(" or o." + nameValueCountPair.getName() + " = ?" + (index));
+				}
+				vs.add(nameValueCountPair.getValue());
+				index++;
+			}
+			sql_stringBuffer.append(" )");
+		}*/
+		Query query = em.createQuery(cq.select(root).where(p).orderBy(_order) );
+		return query.setMaxResults(20).getResultList();
+	}
+
+	/**
+	 * 查询符合的文档信息总数
+	 * @param wrapIn
+	 * @return
+	 * @throws Exception
+	 */
+	public long getCountWithFilter( WrapInFilterAppeal wrapIn ) throws Exception {
+		//先获取上一页最后一条的sequence值,如果有值的话,以此sequence值作为依据取后续的count条数据
+		EntityManager em = this.entityManagerContainer().get( AttendanceAppealInfo.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceAppealInfo> root = cq.from(AttendanceAppealInfo.class);
+		Predicate p = cb.isNotNull(root.get(AttendanceAppealInfo_.detailId));
+
+		if(StringUtils.isNotEmpty(wrapIn.getDetailId())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.detailId),wrapIn.getDetailId()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getEmpName())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.empName),wrapIn.getEmpName()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getUnitName())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.unitName),wrapIn.getUnitName()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getTopUnitName())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.topUnitName),wrapIn.getTopUnitName()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getYearString())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.yearString),wrapIn.getYearString()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getMonthString())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.monthString),wrapIn.getMonthString()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getAppealReason())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.appealReason),wrapIn.getAppealReason()));
+		}
+		if (wrapIn.getStatus()!=999) {
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.status),wrapIn.getStatus()));
+		}
+
+		cq.select(cb.count(root)).where(p);
+		return em.createQuery(cq).getSingleResult();
+	}
+
+	/**
+	 * 查询符合的文档信息总数
+	 * @param wrapIn
+	 * @return
+	 * @throws Exception
+	 */
+	public long getCountWithFilterWithCurrentProcessor(WrapInFilterAppeal wrapIn , boolean isManager) throws Exception {
+		//先获取上一页最后一条的sequence值,如果有值的话,以此sequence值作为依据取后续的count条数据
+		EntityManager em = this.entityManagerContainer().get( AttendanceAppealInfo.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceAppealInfo> root = cq.from(AttendanceAppealInfo.class);
+		Predicate p = cb.isNotNull(root.get(AttendanceAppealInfo_.detailId));
+
+		if(StringUtils.isNotEmpty(wrapIn.getDetailId())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.detailId),wrapIn.getDetailId()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getEmpName())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.empName),wrapIn.getEmpName()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getUnitName())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.unitName),wrapIn.getUnitName()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getTopUnitName())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.topUnitName),wrapIn.getTopUnitName()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getYearString())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.yearString),wrapIn.getYearString()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getMonthString())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.monthString),wrapIn.getMonthString()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getAppealReason())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.appealReason),wrapIn.getAppealReason()));
+		}
+		if (wrapIn.getStatus()!=999) {
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.status),wrapIn.getStatus()));
+		}
+		if(!isManager){
+			if ((null != wrapIn.getProcessPerson1()) && (!wrapIn.getProcessPerson1().isEmpty())) {
+				p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.currentProcessor),wrapIn.getProcessPerson1()));
+			}
+		}
+		cq.select(cb.count(root)).where(p);
+		return em.createQuery(cq).getSingleResult();
+	}
+
+	/**
+	 * 查询符合的文档信息总数
+	 * @param wrapIn
+	 * @return
+	 * @throws Exception
+	 */
+	public long getCountWithFilterWithManager(WrapInFilterAppeal wrapIn , Map<String,List<String>> unitMap, boolean isManager) throws Exception {
+		//先获取上一页最后一条的sequence值,如果有值的话,以此sequence值作为依据取后续的count条数据
+		EntityManager em = this.entityManagerContainer().get( AttendanceAppealInfo.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceAppealInfo> root = cq.from(AttendanceAppealInfo.class);
+		Predicate p = cb.isNotNull(root.get(AttendanceAppealInfo_.detailId));
+
+
+		if(StringUtils.isNotEmpty(wrapIn.getProcessPerson1())) {
+			p = cb.and(p, cb.equal(root.get(AttendanceAppealInfo_.currentProcessor), wrapIn.getProcessPerson1()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getDetailId())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.detailId),wrapIn.getDetailId()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getEmpName())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.empName),wrapIn.getEmpName()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getUnitName())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.unitName),wrapIn.getUnitName()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getTopUnitName())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.topUnitName),wrapIn.getTopUnitName()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getYearString())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.yearString),wrapIn.getYearString()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getMonthString())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.monthString),wrapIn.getMonthString()));
+		}
+		if(StringUtils.isNotEmpty(wrapIn.getAppealReason())){
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.appealReason),wrapIn.getAppealReason()));
+		}
+		if (wrapIn.getStatus()!=999) {
+			p = cb.and(p,cb.equal(root.get(AttendanceAppealInfo_.status),wrapIn.getStatus()));
+		}
+		// 这个管理员应该不需要 以前的错误逻辑 2022-1-11 @FancyLou
+//		if(!isManager){
+//			if(unitMap.isEmpty()){
+//				p = cb.and(p,cb.isNull(root.get(AttendanceAppealInfo_.detailId)));
+//			}else{
+//				if(unitMap.containsKey("COMPANY")){
+//					p = cb.and(p,root.get(AttendanceAppealInfo_.topUnitName).in(unitMap.get("COMPANY")));
+//				}
+//				if(unitMap.containsKey("DEPT")){
+//					p = cb.and(p,root.get(AttendanceAppealInfo_.unitName).in(unitMap.get("DEPT")));
+//				}
+//			}
+//		}
+		cq.select(cb.count(root)).where(p);
+		return em.createQuery(cq).getSingleResult();
+	}
+
+	public Map<String,List<String>> listUnits(EffectivePerson effectivePerson)  throws Exception {
+		Map<String,List<String>> unitMap=new HashMap<>();
+		List<AttendanceAdmin> unitlist = new ArrayList<>();
+		List<String> companyList = new ArrayList<String>();
+		List<String> deptList = new ArrayList<String>();
+		EntityManager em = this.entityManagerContainer().get( AttendanceAdmin.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceAdmin> cq = cb.createQuery(AttendanceAdmin.class);
+		Root<AttendanceAdmin> root = cq.from( AttendanceAdmin.class);
+		Predicate p = cb.equal(root.get(AttendanceAdmin_.admin),effectivePerson.getDistinguishedName());
+		unitlist =  em.createQuery(cq.where(p)).getResultList();
+		if(ListTools.isNotEmpty(unitlist)){
+			for(AttendanceAdmin attendanceAdmin :unitlist){
+				if(StringUtils.equals(attendanceAdmin.getAdminLevel(),"DEPT")){
+					deptList.add(attendanceAdmin.getUnitName());
+				}
+				if(StringUtils.equals(attendanceAdmin.getAdminLevel(),"COMPANY")){
+					companyList.add(attendanceAdmin.getUnitName());
+				}
+			}
+		}
+		if(ListTools.isNotEmpty(deptList)){
+			unitMap.put("DEPT",deptList);
+		}
+		if(ListTools.isNotEmpty(companyList)){
+			unitMap.put("COMPANY",companyList);
+		}
+		return  unitMap;
+	}
+}

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

@@ -0,0 +1,1313 @@
+package com.x.attendance.assemble.control.factory;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Order;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+import javax.persistence.criteria.Selection;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.x.attendance.assemble.common.date.DateOperation;
+import com.x.attendance.assemble.control.AbstractFactory;
+import com.x.attendance.assemble.control.Business;
+import com.x.attendance.assemble.control.CriteriaQueryTools;
+import com.x.attendance.assemble.control.jaxrs.attendancedetail.AttendanceCycles;
+import com.x.attendance.assemble.control.jaxrs.attendancedetail.WrapInFilter;
+import com.x.attendance.entity.AttendanceDetail;
+import com.x.attendance.entity.AttendanceDetail_;
+import com.x.base.core.project.exception.ExceptionWhen;
+import com.x.base.core.project.tools.ListTools;
+/**
+ * 系统配置信息表基础功能服务类
+ */
+public class AttendanceDetailFactory extends AbstractFactory {
+	
+	public AttendanceDetailFactory(Business business) throws Exception {
+		super(business);
+	}
+
+	//@MethodDescribe("获取指定Id的AttendanceDetail信息对象")
+	public AttendanceDetail get( String id ) throws Exception {
+		return this.entityManagerContainer().find(id, AttendanceDetail.class, ExceptionWhen.none);
+	}
+	
+	//@MethodDescribe("列示全部的AttendanceDetail信息列表")
+	public List<String> listAll() throws Exception {
+		EntityManager em = this.entityManagerContainer().get(AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		cq.select(root.get(AttendanceDetail_.id));
+		return em.createQuery(cq).getResultList();
+	}
+	
+	//@MethodDescribe("列示全部的AttendanceDetail信息列表")
+	public String getMaxRecordDate() throws Exception {
+		EntityManager em = this.entityManagerContainer().get(AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();		
+		CriteriaQuery<AttendanceDetail> cq = cb.createQuery( AttendanceDetail.class );
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);		
+		cq.orderBy( cb.desc( root.get( AttendanceDetail_.recordDateString) ) );	
+		List<AttendanceDetail> resultList = em.createQuery(cq).setMaxResults(1).getResultList();
+		if( resultList == null || resultList.size() == 0 ){
+			return null;
+		}else{
+			return resultList.get(0).getRecordDateString();
+		}
+	}
+	
+	//@MethodDescribe("根据员工姓名和打卡日期列示AttendanceDetail信息列表")
+	public List<String> listByEmployeeNameAndDate( String employeeName, String recordDateString ) throws Exception {
+		
+		if( employeeName == null || recordDateString == null ){
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get(AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		cq.select(root.get(AttendanceDetail_.id));
+		Predicate p = cb.equal( root.get(AttendanceDetail_.empName),  employeeName );
+		p = cb.and( p, cb.equal( root.get(AttendanceDetail_.recordDateString ),  recordDateString ) );
+		return em.createQuery(cq.where( p )).getResultList();
+	}
+	
+	//@MethodDescribe("根据员工姓名和打卡日期列示AttendanceDetail信息列表")
+	public List<AttendanceDetail> listDetailByEmployeeNameAndDate( String employeeName, String recordDateString ) throws Exception {
+		
+		if( employeeName == null || recordDateString == null ){
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get(AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceDetail> cq = cb.createQuery(AttendanceDetail.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = cb.equal( root.get(AttendanceDetail_.empName),  employeeName );
+		p = cb.and( p, cb.equal( root.get(AttendanceDetail_.recordDateString ),  recordDateString ) );
+		return em.createQuery(cq.where( p )).getResultList();
+	}
+	
+	//@MethodDescribe("列示指定Id的AttendanceDetail信息列表")
+	public List<AttendanceDetail> list(List<String> ids) throws Exception {
+		List<AttendanceDetail> resultList = null;
+		if( ids == null || ids.size() == 0 ){
+			return new ArrayList<AttendanceDetail>();
+		}
+		EntityManager em = this.entityManagerContainer().get(AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceDetail> cq = cb.createQuery(AttendanceDetail.class);
+		Root<AttendanceDetail> root = cq.from(AttendanceDetail.class);
+		Predicate p = root.get(AttendanceDetail_.id).in(ids);
+		resultList = em.createQuery( cq.where(p) ).getResultList();
+		if( resultList == null ){
+			resultList = new ArrayList<AttendanceDetail>();
+		}
+		return resultList;
+	}
+
+	/**
+	 * 分析时间范围内的所有打卡记录
+	 * 1、如果未传入时间,或者时间有错,那么分析所有未分析过的打卡记录
+	 * 2、只分析未归档的,已经归档的将不再分析了
+	 * @param startDateString
+	 * @param endDateString
+	 * @return
+	 * @throws Exception
+	 */
+	//@MethodDescribe("按指定的开始时间,结束时间列示未被分析的AttendanceDetail信息列表")
+	public List<String> getAllAnalysenessDetails(String startDateString, String endDateString, String personName ) throws Exception {
+		DateOperation dateOperation = new DateOperation();
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		
+		//一般始终为true, id is not null
+		Predicate p = root.get( AttendanceDetail_.id ).isNotNull();
+		p = cb.and( p, root.get( AttendanceDetail_.archiveTime ).isNull()); //要未归档的,才再次进行分析
+		if( StringUtils.isNotEmpty( personName ) ) {
+			p = cb.and( p, cb.equal( root.get(AttendanceDetail_.empName ), personName)); //匹配员工姓名
+		}
+		Date startDate = null;
+		Date endDate = null;
+		try{
+			startDate = dateOperation.getDateFromString( startDateString );
+		}catch(Exception e){
+			startDate = null;
+		}
+		try{
+			endDate = dateOperation.getDateFromString( endDateString );
+		}catch(Exception e){
+			endDate = null;
+		}
+		//如果开始时间和结束时间有值,那么分析一个时间区间内的所有打卡记录,已经分析过了的,也需要重新分析一次
+		if( startDate != null  && endDate != null ){
+			p = cb.and( p, cb.between( root.get( AttendanceDetail_.recordDate), startDate, endDate));
+		}else{
+			if( startDate != null ){
+				p = cb.and( p, cb.between( root.get( AttendanceDetail_.recordDate), startDate, new Date()));
+			}
+			if( endDate != null ){
+				p = cb.and( p, cb.between( root.get( AttendanceDetail_.recordDate), new Date(), endDate));
+			}
+			if( startDate == null && endDate == null ){
+				//startDateString和endDateString都为空,只分析所有未分析过的
+				List<Integer> statusArray = new ArrayList<Integer>();
+				statusArray.add( 0 ); //未分析的
+				statusArray.add( -1 ); //有错误的
+				p = cb.and( p, root.get( AttendanceDetail_.recordStatus).in( statusArray ));
+			}
+		}
+		
+		cq.select( root.get( AttendanceDetail_.id ) );
+		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList();
+	}
+	
+	/**
+	 * 分析时间范围内的所有打卡记录
+	 * 1、如果未传入时间,或者时间有错,那么分析所有未分析过的打卡记录
+	 * 2、只分析未归档的,已经归档的将不再分析了
+	 * 3、如果forceFlag为true则分析(未分析的,已分析的,错误的)
+	 * @param startDateString
+	 * @param endDateString
+	 * @param personName
+	 * @param forceFlag
+	 * @return
+	 * @throws Exception
+	 */
+	//@MethodDescribe("按指定的开始时间,结束时间列示未被分析的AttendanceDetail信息列表")
+	public List<String> getAllAnalysenessDetailsForce(String startDateString, String endDateString, String personName ,Boolean forceFlag) throws Exception {
+		DateOperation dateOperation = new DateOperation();
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		
+		//一般始终为true, id is not null
+		Predicate p = root.get( AttendanceDetail_.id ).isNotNull();
+		p = cb.and( p, root.get( AttendanceDetail_.archiveTime ).isNull()); //要未归档的,才再次进行分析
+		if( StringUtils.isNotEmpty( personName ) ) {
+			p = cb.and( p, cb.equal( root.get(AttendanceDetail_.empName ), personName)); //匹配员工姓名
+		}
+		Date startDate = null;
+		Date endDate = null;
+		try{
+			startDate = dateOperation.getDateFromString( startDateString );
+		}catch(Exception e){
+			startDate = null;
+		}
+		try{
+			endDate = dateOperation.getDateFromString( endDateString );
+		}catch(Exception e){
+			endDate = null;
+		}
+		//如果开始时间和结束时间有值,那么分析一个时间区间内的所有打卡记录,已经分析过了的,也需要重新分析一次
+		if( startDate != null  && endDate != null ){
+			p = cb.and( p, cb.between( root.get( AttendanceDetail_.recordDate), startDate, endDate));
+		}else{
+			if( startDate != null ){
+				p = cb.and( p, cb.between( root.get( AttendanceDetail_.recordDate), startDate, new Date()));
+			}
+			if( endDate != null ){
+				p = cb.and( p, cb.between( root.get( AttendanceDetail_.recordDate), new Date(), endDate));
+			}
+			if( startDate == null && endDate == null ){
+				//startDateString和endDateString都为空,只分析所有未分析过的
+				List<Integer> statusArray = new ArrayList<Integer>();
+				statusArray.add( 0 ); //未分析的
+				if(forceFlag){
+					statusArray.add( 1 ); //已分析的
+				}
+				statusArray.add( -1 ); //有错误的
+				p = cb.and( p, root.get( AttendanceDetail_.recordStatus).in( statusArray ));
+			}
+		}
+		
+		cq.select( root.get( AttendanceDetail_.id ) );
+		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList();
+	}
+	
+	/**
+	 * 分析时间范围内的所有打卡记录
+	 * 1、如果未传入时间,或者时间有错,那么分析所有未分析过的打卡记录
+	 * 2、只分析未归档的,已经归档的将不再分析了
+	 * @param startDateString
+	 * @param endDateString
+	 * @return
+	 * @throws Exception
+	 */
+	//@MethodDescribe("按指定的开始时间,结束时间列示未被分析的员工姓名列表")
+	public List<String> getAllAnalysenessPersonNames(String startDateString, String endDateString ) throws Exception {
+		DateOperation dateOperation = new DateOperation();
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		
+		//一般始终为true, id is not null
+		Predicate p = root.get( AttendanceDetail_.id ).isNotNull();
+		p = cb.and( p, root.get( AttendanceDetail_.archiveTime ).isNull()); //要未归档的,才再次进行分析
+		Date startDate = null;
+		Date endDate = null;
+		try{
+			startDate = dateOperation.getDateFromString( startDateString );
+		}catch(Exception e){
+			startDate = null;
+		}
+		try{
+			endDate = dateOperation.getDateFromString( endDateString );
+		}catch(Exception e){
+			endDate = null;
+		}
+		//如果开始时间和结束时间有值,那么分析一个时间区间内的所有打卡记录,已经分析过了的,也需要重新分析一次
+		if( startDate != null  && endDate != null ){
+			p = cb.and( p, cb.between( root.get( AttendanceDetail_.recordDate), startDate, endDate));
+		}else{
+			if( startDate != null ){
+				p = cb.and( p, cb.between( root.get( AttendanceDetail_.recordDate), startDate, new Date()));
+			}
+			if( endDate != null ){
+				p = cb.and( p, cb.between( root.get( AttendanceDetail_.recordDate), new Date(), endDate));
+			}
+			if( startDate == null && endDate == null ){
+				//startDateString和endDateString都为空,只分析所有未分析过的
+				List<Integer> statusArray = new ArrayList<Integer>();
+				statusArray.add( 0 ); //未分析的
+				statusArray.add( -1 ); //有错误的
+				p = cb.and( p, root.get( AttendanceDetail_.recordStatus).in( statusArray ));
+			}
+		}
+		
+		/*cq.distinct(true).select( root.get( AttendanceDetail_.empName ) );
+		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList();*/
+		cq.select( root.get( AttendanceDetail_.empName ) ).where(p);
+		return em.createQuery(cq).setMaxResults(20000).getResultList().stream().distinct().collect(Collectors.toList());
+	}
+	
+	/**
+	 * 分析时间范围内的所有打卡记录(可选择已分析/未分析)
+	 * 1、如果未传入时间,或者时间有错, 那么分析所有未分析过的打卡记录
+	 * 2、只分析未归档的,已经归档的将不再分析了
+	 * 3、如果forceFlag为true则分析(未分析的,已分析的,错误的)
+	 * @param startDateString
+	 * @param endDateString
+	 * @param forceFlag
+	 * @return
+	 * @throws Exception
+	 */
+	//@MethodDescribe("按指定的开始时间,结束时间列示员工姓名列表")
+	public List<String> getAllAnalysenessPersonNamesForce(String startDateString, String endDateString ,Boolean forceFlag) throws Exception {
+		DateOperation dateOperation = new DateOperation();
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		
+		//一般始终为true, id is not null
+		Predicate p = root.get( AttendanceDetail_.id ).isNotNull();
+		p = cb.and( p, root.get( AttendanceDetail_.archiveTime ).isNull()); //要未归档的,才再次进行分析
+		Date startDate = null;
+		Date endDate = null;
+		try{
+			startDate = dateOperation.getDateFromString( startDateString );
+		}catch(Exception e){
+			startDate = null;
+		}
+		try{
+			endDate = dateOperation.getDateFromString( endDateString );
+		}catch(Exception e){
+			endDate = null;
+		}
+		//如果开始时间和结束时间有值,那么分析一个时间区间内的所有打卡记录,已经分析过了的,也需要重新分析一次
+		if( startDate != null  && endDate != null ){
+			p = cb.and( p, cb.between( root.get( AttendanceDetail_.recordDate), startDate, endDate));
+		}else{
+			if( startDate != null ){
+				p = cb.and( p, cb.between( root.get( AttendanceDetail_.recordDate), startDate, new Date()));
+			}
+			if( endDate != null ){
+				p = cb.and( p, cb.between( root.get( AttendanceDetail_.recordDate), new Date(), endDate));
+			}
+			if( startDate == null && endDate == null ){
+				//startDateString和endDateString都为空,只分析所有未分析过的
+				List<Integer> statusArray = new ArrayList<Integer>();
+				statusArray.add( 0 ); //未分析的
+				if(forceFlag){
+					statusArray.add( 1 ); //已分析的
+				}
+				statusArray.add( -1 ); //有错误的
+				p = cb.and( p, root.get( AttendanceDetail_.recordStatus).in( statusArray ));
+			}
+		}
+		
+		cq.select( root.get( AttendanceDetail_.empName ) ).where(p);
+		return em.createQuery(cq).setMaxResults(20000).getResultList().stream().distinct().collect(Collectors.toList());
+	}
+	
+	//@MethodDescribe("按指定的开始时间,结束时间列示未被分析的AttendanceDetail信息列表")
+	public List<String> getUserAnalysenessDetails(String empName, String startDateString, String endDateString) throws Exception {
+		DateOperation dateOperation = new DateOperation();
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		cq.select(root.get(AttendanceDetail_.id));
+
+		Predicate p = cb.equal( root.get(AttendanceDetail_.empName),  empName );
+		
+		Date startDate = null;
+		Date endDate = null;
+		try{
+			startDate = dateOperation.getDateFromString( startDateString );
+		}catch(Exception e){
+			startDate = null;
+		}
+		try{
+			endDate = dateOperation.getDateFromString( endDateString );
+		}catch(Exception e){
+			endDate = null;
+		}
+		//如果开始时间和结束时间有值,那么分析一个时间区间内的所有打卡记录,已经分析过了的,也需要重新分析一次
+		if( startDate != null  && endDate != null ){
+			p = cb.and( p, cb.between( root.get( AttendanceDetail_.recordDate), startDate, endDate));
+		}else{
+			if( startDate != null ){
+				p = cb.and( p, cb.between( root.get( AttendanceDetail_.recordDate), startDate, new Date()));
+			}
+			if( endDate != null ){
+				p = cb.and( p, cb.between( root.get( AttendanceDetail_.recordDate), new Date(), endDate));
+			}
+			if( startDate == null && endDate == null ){
+				//startDateString和endDateString都为空,只分析所有未分析过的
+				List<Integer> statusArray = new ArrayList<Integer>();
+				statusArray.add( 0 ); //未分析的
+				statusArray.add( -1 ); //有错误的
+				p = cb.and( p, root.get( AttendanceDetail_.recordStatus).in( statusArray ));
+			}
+		}
+		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList();
+	}
+	
+	//@MethodDescribe("按指定的年份,月份列示AttendanceDetail信息列表")
+	public List<AttendanceDetail> getDetailsByYearAndMonth(String year, String month) throws Exception {
+		if( year == null || month == null ){
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceDetail> cq = cb.createQuery(AttendanceDetail.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		//一般始终为true, id is not null
+		Predicate p = cb.equal( root.get(AttendanceDetail_.recordStatus), 1 );
+		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.yearString), year ));
+		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.monthString), month ));
+		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList();
+	}
+	
+	//@MethodDescribe("按指定的统计周期年份,月份列示AttendanceDetail信息列表")
+	public List<AttendanceDetail> getDetailsByCycleYearAndMonth(String cycleYear, String cycleMonth) throws Exception {
+		if( cycleYear == null || cycleMonth == null ){
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceDetail> cq = cb.createQuery(AttendanceDetail.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		//一般始终为true, id is not null
+		Predicate p = cb.equal( root.get(AttendanceDetail_.recordStatus), 1 );
+		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleYear), cycleYear ));
+		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleMonth), cycleMonth ));
+		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList();
+	}
+	
+	//@MethodDescribe("按指定的年份,月份列示已分析的AttendanceDetail信息中涉及的组织列表")
+	public List<String> getDetailsUnitsByYearAndMonth(String year, String month) throws Exception {
+		if( year == null || month == null ){
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		cq.select( root.get(AttendanceDetail_.unitName ));
+		Predicate p = cb.equal( root.get(AttendanceDetail_.recordStatus), 1 );
+		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.yearString), year ));
+		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.monthString), month ));
+		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList().stream().distinct().collect(Collectors.toList());
+	}
+	
+	/**
+	 * 迟到、缺勤、早退、工时不足、异常打卡,但未申诉通过的
+	 * @param year
+	 * @param month
+	 * @return
+	 * @throws Exception
+	 */
+	//@MethodDescribe("获取所有需要导出所有异常数据(未申诉的、申诉未通过的)")
+	public List<String> getDetailsWithAllAbnormalCase( String year, String month ) throws Exception {
+		if( year == null || month == null ){
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		cq.select( root.get(AttendanceDetail_.id ));
+		
+		Predicate p = cb.lessThan( root.get(AttendanceDetail_.appealStatus), 9); //如果等于9就是申诉通过
+		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleYear), year ));
+		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleMonth), month ));
+		
+		Predicate orCase = cb.isTrue(root.get(AttendanceDetail_.isLate)); //迟到
+		orCase = cb.or( orCase, cb.isTrue( root.get(AttendanceDetail_.isLeaveEarlier)) ); //或者早退
+		orCase = cb.or( orCase, cb.isTrue( root.get(AttendanceDetail_.isAbnormalDuty) )); //或者异常打卡
+		orCase = cb.or( orCase, cb.isTrue( root.get(AttendanceDetail_.isAbsent) )); //或者缺勤
+		orCase = cb.or( orCase, cb.isTrue( root.get(AttendanceDetail_.isLackOfTime) )); //或者工时不足
+		
+		Predicate where = cb.and( p, orCase );
+		
+		return em.createQuery(cq.where(where)).setMaxResults(20000).getResultList();
+	}
+	
+	//@MethodDescribe("按指定的统计周期年份,月份列示AttendanceDetail信息中涉及的组织列表")
+	public List<String> getDetailsUnitsByCycleYearAndMonth(String cycleYear, String cycleMonth) throws Exception {
+		if( cycleYear == null || cycleMonth == null ){
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		cq.select( root.get(AttendanceDetail_.unitName ));
+		Predicate p = cb.equal( root.get(AttendanceDetail_.recordStatus), 1 );
+		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleYear), cycleYear ));
+		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleMonth), cycleMonth ));
+		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList().stream().distinct().collect(Collectors.toList());
+	}
+	
+	//@MethodDescribe("按指定的统计周期年份,月份列示AttendanceDetail信息中涉及的顶层组织名称列表")
+	public List<String> distinctDetailsTopUnitNamesByYearAndMonth(String year, String month) throws Exception {
+		if( year == null || month == null ){
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		cq.select( root.get(AttendanceDetail_.topUnitName ));
+		Predicate p = cb.equal( root.get(AttendanceDetail_.recordStatus), 1 );
+		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.yearString), year ));
+		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.monthString), month ));
+		return em.createQuery(cq.where(p)).setMaxResults(1000).getResultList().stream().distinct().collect(Collectors.toList());
+	}
+	
+	//@MethodDescribe("按指定的统计周期年份,月份列示AttendanceDetail信息中涉及的员工姓名列表")
+	public List<String> distinctDetailsEmployeeNamesByCycleYearAndMonth(String cycleYear, String cycleMonth) throws Exception {
+		if( cycleYear == null || cycleMonth == null ){
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		cq.select( root.get(AttendanceDetail_.empName ));
+		Predicate p = cb.equal( root.get(AttendanceDetail_.recordStatus), 1 );
+		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleYear), cycleYear ));
+		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleMonth), cycleMonth ));
+		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList().stream().distinct().collect(Collectors.toList());
+	}
+	
+	//@MethodDescribe("按指定的统计周期年份,月份列示AttendanceDetail信息中涉及的顶层组织名称列表")
+	public List<String> distinctDetailsTopUnitNamesByCycleYearAndMonth(String cycleYear, String cycleMonth) throws Exception {
+		if( cycleYear == null || cycleMonth == null ){
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		cq.select( root.get(AttendanceDetail_.topUnitName ));
+		Predicate p = cb.equal( root.get(AttendanceDetail_.recordStatus), 1 );
+		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleYear), cycleYear ));
+		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleMonth), cycleMonth ));
+		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList().stream().distinct().collect(Collectors.toList());
+	}
+	
+	//@MethodDescribe("按指定的统计周期年份,月份列示AttendanceDetail信息中涉及的组织名称列表")
+	public List<String> distinctDetailsUnitNamesByCycleYearAndMonth( String cycleYear, String cycleMonth ) throws Exception {
+		if( cycleYear == null || cycleMonth == null ){
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		cq.select( root.get(AttendanceDetail_.unitName ));
+		Predicate p = cb.equal( root.get(AttendanceDetail_.recordStatus), 1 );
+		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleYear), cycleYear ));
+		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleMonth), cycleMonth ));
+		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList().stream().distinct().collect(Collectors.toList());
+	}
+	
+	//@MethodDescribe("按指定的统计周期年份,月份列示AttendanceDetail信息中涉及的组织名称列表")
+	public List<String> distinctDetailsUnitNamesByCycleYearAndMonth( String cycleYear, String cycleMonth, String employeeName ) throws Exception {
+		if( cycleYear == null || cycleMonth == null || employeeName == null ){
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		cq.select( root.get(AttendanceDetail_.unitName ));
+		Predicate p = cb.equal( root.get(AttendanceDetail_.recordStatus), 1 );
+		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleYear), cycleYear ));
+		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleMonth), cycleMonth ));
+		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.empName), employeeName ));
+		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList().stream().distinct().collect(Collectors.toList());
+	}
+
+
+	//@MethodDescribe("按batchName查询一次导入的所有数据记录列表")
+	public List<String> listByBatchName( String file_id ) throws Exception{
+		if( file_id == null ){
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		cq.select( root.get(AttendanceDetail_.id ));
+		//一般始终为true, id is not null
+		Predicate p = cb.equal( root.get(AttendanceDetail_.batchName), file_id );
+		return em.createQuery(cq.where(p)).getResultList().stream().distinct().collect(Collectors.toList());
+	}
+
+	//@MethodDescribe("按年份月份查询某用户的打卡数据记录列表")
+	public List<String> listUserAttendanceDetailByYearAndMonth(String user, String year, String month)  throws Exception {
+		if( user == null || user.isEmpty() ||year == null || month == null || year.isEmpty() || month.isEmpty()  ){
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		cq.select( root.get(AttendanceDetail_.id ));
+		//一般始终为true, id is not null
+		Predicate p = cb.equal( root.get(AttendanceDetail_.recordStatus), 1 );
+		if( StringUtils.isNotEmpty( user ) ){
+			p = cb.and(p, cb.equal( root.get(AttendanceDetail_.empName), user ));
+		}
+		if( StringUtils.isNotEmpty( year ) ){
+			p = cb.and(p, cb.equal( root.get(AttendanceDetail_.yearString), year ));
+		}
+		if( StringUtils.isNotEmpty( month ) ){
+			p = cb.and(p, cb.equal( root.get(AttendanceDetail_.monthString), month ));
+		}
+		return em.createQuery(cq.where(p)).getResultList();
+	}
+	
+	//@MethodDescribe("按周期年份月份查询某用户的打卡数据记录列表")
+	public List<String> listUserAttendanceDetailByCycleYearAndMonth(String user, String year, String month)  throws Exception {
+		if( user == null || user.isEmpty() ||year == null || month == null || year.isEmpty() || month.isEmpty()  ){
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		cq.select( root.get(AttendanceDetail_.id ));
+		//一般始终为true, id is not null
+		Predicate p = cb.equal( root.get(AttendanceDetail_.recordStatus), 1 );
+		if( StringUtils.isNotEmpty( user ) ){
+			p = cb.and(p, cb.equal( root.get(AttendanceDetail_.empName), user ));
+		}
+		if( StringUtils.isNotEmpty( year ) ){
+			p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleYear), year ));
+		}
+		if( StringUtils.isNotEmpty( month ) ){
+			p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleMonth), month ));
+		}
+		
+		return em.createQuery(cq.where(p)).getResultList();
+	}
+
+	public List<String> listDetailByCycleYearAndMonthWithOutStatus( String user, String year, String month )  throws Exception {
+		if( user == null || user.isEmpty() ||year == null || month == null || year.isEmpty() || month.isEmpty()  ){
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		cq.select( root.get(AttendanceDetail_.id ));
+		//一般始终为true, id is not null
+		Predicate p = cb.isNotNull( root.get(AttendanceDetail_.id) );
+		if( StringUtils.isNotEmpty( user ) ){
+			p = cb.and(p, cb.equal( root.get(AttendanceDetail_.empName), user ));
+		}
+		if( StringUtils.isNotEmpty( year ) ){
+			p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleYear), year ));
+		}
+		if( StringUtils.isNotEmpty( month ) ){
+			p = cb.and(p, cb.equal( root.get(AttendanceDetail_.cycleMonth), month ));
+		}
+
+		return em.createQuery(cq.where(p)).getResultList();
+	}
+	
+	//根据人员和打卡日期查找打卡记录
+		public List<String> listDetailByNameAndDate( String user, String dateStr )  throws Exception {
+			if( user == null || user.isEmpty() ||dateStr == null ){
+				return null;
+			}
+			EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+			CriteriaBuilder cb = em.getCriteriaBuilder();
+			CriteriaQuery<String> cq = cb.createQuery(String.class);
+			Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+			cq.select( root.get(AttendanceDetail_.id ));
+			//一般始终为true, id is not null
+			Predicate p = cb.isNotNull( root.get(AttendanceDetail_.id) );
+			if( StringUtils.isNotEmpty( user ) ){
+				p = cb.and(p, cb.equal( root.get(AttendanceDetail_.empName), user ));
+			}
+			if( StringUtils.isNotEmpty( dateStr ) ){
+				p = cb.and(p, cb.equal( root.get(AttendanceDetail_.recordDateString), dateStr ));
+			}
+			
+	
+			return em.createQuery(cq.where(p)).getResultList();
+		}
+	
+	//@MethodDescribe("按年份月份查询某组织的打卡数据记录列表")
+	public List<String> listUnitAttendanceDetailByYearAndMonth( List<String> unitNames, String year, String month)  throws Exception {
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		cq.select( root.get(AttendanceDetail_.id ));
+		//一般始终为true, id is not null
+		Predicate p = cb.equal( root.get(AttendanceDetail_.recordStatus), 1 );
+		if( unitNames != null && unitNames.size() > 0 ){
+			p = cb.and(p, root.get(AttendanceDetail_.unitName).in(unitNames));
+		}
+		if( StringUtils.isNotEmpty( year ) ){
+			p = cb.and(p, cb.equal( root.get(AttendanceDetail_.yearString), year ));
+		}
+		if( StringUtils.isNotEmpty( month ) ){
+			p = cb.and(p, cb.equal( root.get(AttendanceDetail_.monthString), month ));
+		}
+		
+		return em.createQuery(cq.where(p)).getResultList();
+	}
+	
+	//@MethodDescribe("按年份月份查询某顶层组织的打卡数据记录列表")
+	public List<String> listTopUnitAttendanceDetailByYearAndMonth(List<String> topUnitNames, String year, String month)  throws Exception {
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		cq.select( root.get(AttendanceDetail_.id ));
+		//一般始终为true, id is not null
+		Predicate p = cb.equal( root.get(AttendanceDetail_.recordStatus), 1 );
+		if( ListTools.isNotEmpty(  topUnitNames ) ){
+			p = cb.and(p, root.get(AttendanceDetail_.topUnitName).in( topUnitNames ));
+		}
+		if( StringUtils.isNotEmpty( year ) ){
+			p = cb.and(p, cb.equal( root.get(AttendanceDetail_.yearString), year ));
+		}
+		if( StringUtils.isNotEmpty( month ) ){
+			p = cb.and(p, cb.equal( root.get(AttendanceDetail_.monthString), month ));
+		}
+		return em.createQuery(cq.where(p)).getResultList();
+	}
+	
+	
+	/**
+	 * 查询下一页的信息数据
+	 * @param id
+	 * @param count
+	 * @param sequence
+	 * @param wrapIn
+	 * @return
+	 * @throws Exception
+	 */
+	@SuppressWarnings("unchecked")
+	public List<AttendanceDetail> listIdsNextWithFilter( String id, Integer count, Object sequence, WrapInFilter wrapIn ) throws Exception {
+		//先获取上一页最后一条的sequence值,如果有值的话,以此sequence值作为依据取后续的count条数据
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceDetail> cq = cb.createQuery(AttendanceDetail.class);
+		Root<AttendanceDetail> root = cq.from(AttendanceDetail.class);
+
+		String order = wrapIn.getOrder();//排序方式
+		if( order == null || order.isEmpty() ){
+			order = "DESC";
+		}
+		String orderFieldName = "";
+		if(StringUtils.isNotEmpty( wrapIn.getKey())){
+			orderFieldName = wrapIn.getKey();
+		}else{
+			orderFieldName = "sequence";
+		}
+		Order _order = CriteriaQueryTools.setOrder(cb, root, AttendanceDetail_.class, orderFieldName,order);
+		Predicate p = cb.isNotNull(root.get(AttendanceDetail_.id));
+		if ((null != sequence) ) {
+			if(StringUtils.equalsIgnoreCase(order, "DESC")){
+				p = cb.and(p,cb.lessThan(root.get(AttendanceDetail_.sequence),sequence.toString()));
+			}else{
+				p = cb.and(p,cb.greaterThan(root.get(AttendanceDetail_.sequence),sequence.toString()));
+			}
+		}
+		if ((null != wrapIn.getQ_empName()) && (!wrapIn.getQ_empName().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.empName),wrapIn.getQ_empName()));
+		}
+		if ((null != wrapIn.getUnitNames()) && wrapIn.getUnitNames().size() > 0) {
+			p = cb.and(p,root.get(AttendanceDetail_.unitName).in(wrapIn.getUnitNames()));
+		}
+		if ((null != wrapIn.getTopUnitNames()) && wrapIn.getTopUnitNames().size() > 0) {
+			p = cb.and(p,root.get(AttendanceDetail_.topUnitName).in(wrapIn.getTopUnitNames()));
+		}
+		if ((null != wrapIn.getCycleYear()) && (!wrapIn.getCycleYear().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.cycleYear),wrapIn.getCycleYear()));
+		}
+		if ((null != wrapIn.getCycleMonth()) && (!wrapIn.getCycleMonth().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.cycleMonth),wrapIn.getCycleMonth()));
+		}
+		if ((null != wrapIn.getQ_year() ) && (!wrapIn.getQ_year().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.yearString),wrapIn.getQ_year()));
+		}
+		if ((null != wrapIn.getQ_month()) && (!wrapIn.getQ_month().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.monthString),wrapIn.getQ_month()));
+		}
+		if ((null != wrapIn.getQ_date()) && (!wrapIn.getQ_date().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.recordDateString),wrapIn.getQ_date()));
+		}
+		if ( wrapIn.getRecordStatus() != 999 ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.recordStatus),wrapIn.getRecordStatus()));
+		}
+		if (wrapIn.getIsAbsent() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isAbsent),wrapIn.getIsAbsent()));
+		}
+		if (wrapIn.getIsLate() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isLate),wrapIn.getIsLate()));
+		}
+		if (wrapIn.getIsLackOfTime() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isLackOfTime),wrapIn.getIsLackOfTime()));
+		}
+		if (wrapIn.getIsLeaveEarlier() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isLeaveEarlier),wrapIn.getIsLeaveEarlier()));
+		}
+
+		Query query = em.createQuery(cq.select(root).where(p).orderBy(_order) );
+		return query.setMaxResults(count).getResultList();
+	}	
+	
+	/**
+	 * 查询下一页的信息数据(排除不参加考勤的人员)
+	 * @param id
+	 * @param count
+	 * @param sequence
+	 * @param wrapIn
+	 * @return
+	 * @throws Exception
+	 */
+	@SuppressWarnings("unchecked")
+	public List<AttendanceDetail> listIdsNextWithFilterUn( String id, Integer count, Object sequence, WrapInFilter wrapIn ,List<String> unUnitNameList,List<String> personNameList) throws Exception {
+		//先获取上一页最后一条的sequence值,如果有值的话,以此sequence值作为依据取后续的count条数据
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceDetail> cq = cb.createQuery(AttendanceDetail.class);
+		Root<AttendanceDetail> root = cq.from(AttendanceDetail.class);
+
+		String order = wrapIn.getOrder();//排序方式
+		if( order == null || order.isEmpty() ){
+			order = "DESC";
+		}
+		String orderFieldName = "";
+		if(StringUtils.isNotEmpty( wrapIn.getKey())){
+			orderFieldName = wrapIn.getKey();
+		}else{
+			orderFieldName = "sequence";
+		}
+		Order _order = CriteriaQueryTools.setOrder(cb, root, AttendanceDetail_.class, orderFieldName,order);
+		Predicate p = cb.isNotNull(root.get(AttendanceDetail_.id));
+		if ((null != sequence) ) {
+			if(StringUtils.equalsIgnoreCase(order, "DESC")){
+				p = cb.and(p,cb.lessThan(root.get(AttendanceDetail_.sequence),sequence.toString()));
+			}else{
+				p = cb.and(p,cb.greaterThan(root.get(AttendanceDetail_.sequence),sequence.toString()));
+			}
+		}
+		if ((null != wrapIn.getQ_empName()) && (!wrapIn.getQ_empName().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.empName),wrapIn.getQ_empName()));
+		}
+		if ((null != wrapIn.getUnitNames()) && wrapIn.getUnitNames().size() > 0) {
+			p = cb.and(p,root.get(AttendanceDetail_.unitName).in(wrapIn.getUnitNames()));
+		}
+		if ((null != wrapIn.getTopUnitNames()) && wrapIn.getTopUnitNames().size() > 0) {
+			p = cb.and(p,root.get(AttendanceDetail_.topUnitName).in(wrapIn.getTopUnitNames()));
+		}
+		if ((null != wrapIn.getCycleYear()) && (!wrapIn.getCycleYear().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.cycleYear),wrapIn.getCycleYear()));
+		}
+		if ((null != wrapIn.getCycleMonth()) && (!wrapIn.getCycleMonth().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.cycleMonth),wrapIn.getCycleMonth()));
+		}
+		if ((null != wrapIn.getQ_year() ) && (!wrapIn.getQ_year().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.yearString),wrapIn.getQ_year()));
+		}
+		if ((null != wrapIn.getQ_month()) && (!wrapIn.getQ_month().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.monthString),wrapIn.getQ_month()));
+		}
+		if ((null != wrapIn.getQ_date()) && (!wrapIn.getQ_date().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.recordDateString),wrapIn.getQ_date()));
+		}
+		if ( wrapIn.getRecordStatus() != 999 ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.recordStatus),wrapIn.getRecordStatus()));
+		}
+		if (wrapIn.getIsAbsent() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isAbsent),wrapIn.getIsAbsent()));
+		}
+		if (wrapIn.getIsLate() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isLate),wrapIn.getIsLate()));
+		}
+		if (wrapIn.getIsLackOfTime() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isLackOfTime),wrapIn.getIsLackOfTime()));
+		}
+		if (wrapIn.getIsLeaveEarlier() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isLeaveEarlier),wrapIn.getIsLeaveEarlier()));
+		}
+		if (ListTools.isNotEmpty(unUnitNameList)) {
+			p = cb.and(p,cb.not(root.get(AttendanceDetail_.unitName).in(unUnitNameList)));
+		}
+		if (ListTools.isNotEmpty(personNameList)) {
+			p = cb.and(p,cb.not(root.get(AttendanceDetail_.empName).in(personNameList)));
+		}
+
+		Query query = em.createQuery(cq.select(root).where(p).orderBy(_order) );
+		return query.setMaxResults(count).getResultList();
+	}
+
+	/**
+	 * 根据条件查询考勤信息ids(排除不参加考勤的人员)
+	 * @param wrapIn
+	 * @return
+	 * @throws Exception
+	 */
+	//根据人员和打卡日期查找打卡记录
+	public List<AttendanceDetail> listIdsWithFilterUn( WrapInFilter wrapIn ,List<String> unUnitNameList,List<String> personNameList )  throws Exception {
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceDetail> cq = cb.createQuery(AttendanceDetail.class);
+		Root<AttendanceDetail> root = cq.from(AttendanceDetail.class);
+
+		String order = wrapIn.getOrder();//排序方式
+		if( order == null || order.isEmpty() ){
+			order = "DESC";
+		}
+		String orderFieldName = "";
+		if(StringUtils.isNotEmpty( wrapIn.getKey())){
+			orderFieldName = wrapIn.getKey();
+		}else{
+			orderFieldName = "sequence";
+		}
+		Order _order = CriteriaQueryTools.setOrder(cb, root, AttendanceDetail_.class, orderFieldName,order);
+		Predicate p = cb.isNotNull(root.get(AttendanceDetail_.id));
+		if ((null != wrapIn.getQ_empName()) && (!wrapIn.getQ_empName().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.empName),wrapIn.getQ_empName()));
+		}
+		if ((null != wrapIn.getUnitNames()) && wrapIn.getUnitNames().size() > 0) {
+			p = cb.and(p,root.get(AttendanceDetail_.unitName).in(wrapIn.getUnitNames()));
+		}
+		if ((null != wrapIn.getTopUnitNames()) && wrapIn.getTopUnitNames().size() > 0) {
+			p = cb.and(p,root.get(AttendanceDetail_.topUnitName).in(wrapIn.getTopUnitNames()));
+		}
+		if ((null != wrapIn.getCycleYear()) && (!wrapIn.getCycleYear().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.cycleYear),wrapIn.getCycleYear()));
+		}
+		if ((null != wrapIn.getCycleMonth()) && (!wrapIn.getCycleMonth().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.cycleMonth),wrapIn.getCycleMonth()));
+		}
+		if ((null != wrapIn.getQ_year() ) && (!wrapIn.getQ_year().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.yearString),wrapIn.getQ_year()));
+		}
+		if ((null != wrapIn.getQ_month()) && (!wrapIn.getQ_month().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.monthString),wrapIn.getQ_month()));
+		}
+		if ((null != wrapIn.getQ_date()) && (!wrapIn.getQ_date().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.recordDateString),wrapIn.getQ_date()));
+		}
+		if ( wrapIn.getRecordStatus() != 999 ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.recordStatus),wrapIn.getRecordStatus()));
+		}
+		if (wrapIn.getIsAbsent() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isAbsent),wrapIn.getIsAbsent()));
+		}
+		if (wrapIn.getIsLate() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isLate),wrapIn.getIsLate()));
+		}
+		if (wrapIn.getIsLackOfTime() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isLackOfTime),wrapIn.getIsLackOfTime()));
+		}
+		if (wrapIn.getIsLeaveEarlier() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isLeaveEarlier),wrapIn.getIsLeaveEarlier()));
+		}
+		if (ListTools.isNotEmpty(unUnitNameList)) {
+			p = cb.and(p,cb.not(root.get(AttendanceDetail_.unitName).in(unUnitNameList)));
+		}
+		if (ListTools.isNotEmpty(personNameList)) {
+			p = cb.and(p,cb.not(root.get(AttendanceDetail_.empName).in(personNameList)));
+		}
+
+		Query query = em.createQuery(cq.select(root).where(p).orderBy(_order) );
+		return query.getResultList();
+	}
+	
+	/**
+	 * 查询上一页的文档信息数据
+	 * @param id
+	 * @param count
+	 * @param sequence
+	 * @param wrapIn
+	 * @return
+	 * @throws Exception
+	 */
+	@SuppressWarnings("unchecked")
+	public List<AttendanceDetail> listIdsPrevWithFilter( String id, Integer count, Object sequence, WrapInFilter wrapIn ) throws Exception {
+		//先获取上一页最后一条的sequence值,如果有值的话,以此sequence值作为依据取后续的count条数据
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceDetail> cq = cb.createQuery(AttendanceDetail.class);
+		Root<AttendanceDetail> root = cq.from(AttendanceDetail.class);
+
+		String order = wrapIn.getOrder();//排序方式
+		if( order == null || order.isEmpty() ){
+			order = "DESC";
+		}
+		String orderFieldName = "";
+		if(StringUtils.isNotEmpty( wrapIn.getKey())){
+			orderFieldName = wrapIn.getKey();
+		}else{
+			orderFieldName = "sequence";
+		}
+		Order _order = CriteriaQueryTools.setOrder(cb, root, AttendanceDetail_.class, orderFieldName,order);
+		Predicate p = cb.isNotNull(root.get(AttendanceDetail_.id));
+		if ((null != sequence) ) {
+			if(StringUtils.equalsIgnoreCase(order, "DESC")){
+				p = cb.and(p,cb.greaterThan(root.get(AttendanceDetail_.sequence),sequence.toString()));
+			}else{
+				p = cb.and(p,cb.lessThan(root.get(AttendanceDetail_.sequence),sequence.toString()));
+			}
+		}
+		if ((null != wrapIn.getQ_empName()) && (!wrapIn.getQ_empName().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.empName),wrapIn.getQ_empName()));
+		}
+		if ((null != wrapIn.getUnitNames()) && wrapIn.getUnitNames().size() > 0) {
+			p = cb.and(p,root.get(AttendanceDetail_.unitName).in(wrapIn.getUnitNames()));
+		}
+		if ((null != wrapIn.getTopUnitNames()) && wrapIn.getTopUnitNames().size() > 0) {
+			p = cb.and(p,root.get(AttendanceDetail_.topUnitName).in(wrapIn.getTopUnitNames()));
+		}
+		if ((null != wrapIn.getCycleYear()) && (!wrapIn.getCycleYear().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.cycleYear),wrapIn.getCycleYear()));
+		}
+		if ((null != wrapIn.getCycleMonth()) && (!wrapIn.getCycleMonth().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.cycleMonth),wrapIn.getCycleMonth()));
+		}
+		if ((null != wrapIn.getQ_year() ) && (!wrapIn.getQ_year().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.yearString),wrapIn.getQ_year()));
+		}
+		if ((null != wrapIn.getQ_month()) && (!wrapIn.getQ_month().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.monthString),wrapIn.getQ_month()));
+		}
+		if ((null != wrapIn.getQ_date()) && (!wrapIn.getQ_date().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.recordDateString),wrapIn.getQ_date()));
+		}
+		if ( wrapIn.getRecordStatus() != 999 ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.recordStatus),wrapIn.getRecordStatus()));
+		}
+		if (wrapIn.getIsAbsent() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isAbsent),wrapIn.getIsAbsent()));
+		}
+		if (wrapIn.getIsLate() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isLate),wrapIn.getIsLate()));
+		}
+		if (wrapIn.getIsLackOfTime() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isLackOfTime),wrapIn.getIsLackOfTime()));
+		}
+		if (wrapIn.getIsLeaveEarlier() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isLeaveEarlier),wrapIn.getIsLeaveEarlier()));
+		}
+
+		Query query = em.createQuery(cq.select(root).where(p).orderBy(_order) );
+		return query.setMaxResults(count).getResultList();
+	}
+	
+	/**
+	 * 查询符合的文档信息总数
+	 * @param wrapIn
+	 * @return
+	 * @throws Exception
+	 */
+	public long getCountWithFilter( WrapInFilter wrapIn ) throws Exception {
+		//先获取上一页最后一条的sequence值,如果有值的话,以此sequence值作为依据取后续的count条数据
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from(AttendanceDetail.class);
+		Predicate p = cb.isNotNull(root.get(AttendanceDetail_.id));
+
+		if ((null != wrapIn.getQ_empName()) && (!wrapIn.getQ_empName().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.empName),wrapIn.getQ_empName()));
+		}
+		if ((null != wrapIn.getUnitNames()) && wrapIn.getUnitNames().size() > 0) {
+			p = cb.and(p,root.get(AttendanceDetail_.unitName).in(wrapIn.getUnitNames()));
+		}
+		if ((null != wrapIn.getTopUnitNames()) && wrapIn.getTopUnitNames().size() > 0) {
+			p = cb.and(p,root.get(AttendanceDetail_.topUnitName).in(wrapIn.getTopUnitNames()));
+		}
+		if ((null != wrapIn.getCycleYear()) && (!wrapIn.getCycleYear().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.cycleYear),wrapIn.getCycleYear()));
+		}
+		if ((null != wrapIn.getCycleMonth()) && (!wrapIn.getCycleMonth().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.cycleMonth),wrapIn.getCycleMonth()));
+		}
+		if ((null != wrapIn.getQ_year() ) && (!wrapIn.getQ_year().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.yearString),wrapIn.getQ_year()));
+		}
+		if ((null != wrapIn.getQ_month()) && (!wrapIn.getQ_month().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.monthString),wrapIn.getQ_month()));
+		}
+		if ((null != wrapIn.getQ_date()) && (!wrapIn.getQ_date().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.recordDateString),wrapIn.getQ_date()));
+		}
+		if ( wrapIn.getRecordStatus() != 999 ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.recordStatus),wrapIn.getRecordStatus()));
+		}
+		if (wrapIn.getIsAbsent() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isAbsent),wrapIn.getIsAbsent()));
+		}
+		if (wrapIn.getIsLate() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isLate),wrapIn.getIsLate()));
+		}
+		if (wrapIn.getIsLackOfTime() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isLackOfTime),wrapIn.getIsLackOfTime()));
+		}
+		if (wrapIn.getIsLeaveEarlier() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isLeaveEarlier),wrapIn.getIsLeaveEarlier()));
+		}
+
+		cq.select(cb.count(root)).where(p);
+		return em.createQuery(cq).getSingleResult();
+	}
+	
+	/**
+	 * 查询符合的文档信息总数(排除不参加考勤的人员)
+	 * @param wrapIn
+	 * @return
+	 * @throws Exception
+	 */
+	public long getCountWithFilterUn( WrapInFilter wrapIn ,List<String> unUnitNameList,List<String> personNameList) throws Exception {
+		//先获取上一页最后一条的sequence值,如果有值的话,以此sequence值作为依据取后续的count条数据
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from(AttendanceDetail.class);
+		Predicate p = cb.isNotNull(root.get(AttendanceDetail_.id));
+
+		if ((null != wrapIn.getQ_empName()) && (!wrapIn.getQ_empName().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.empName),wrapIn.getQ_empName()));
+		}
+		if ((null != wrapIn.getUnitNames()) && wrapIn.getUnitNames().size() > 0) {
+			p = cb.and(p,root.get(AttendanceDetail_.unitName).in(wrapIn.getUnitNames()));
+		}
+		if ((null != wrapIn.getTopUnitNames()) && wrapIn.getTopUnitNames().size() > 0) {
+			p = cb.and(p,root.get(AttendanceDetail_.topUnitName).in(wrapIn.getTopUnitNames()));
+		}
+		if ((null != wrapIn.getCycleYear()) && (!wrapIn.getCycleYear().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.cycleYear),wrapIn.getCycleYear()));
+		}
+		if ((null != wrapIn.getCycleMonth()) && (!wrapIn.getCycleMonth().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.cycleMonth),wrapIn.getCycleMonth()));
+		}
+		if ((null != wrapIn.getQ_year() ) && (!wrapIn.getQ_year().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.yearString),wrapIn.getQ_year()));
+		}
+		if ((null != wrapIn.getQ_month()) && (!wrapIn.getQ_month().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.monthString),wrapIn.getQ_month()));
+		}
+		if ((null != wrapIn.getQ_date()) && (!wrapIn.getQ_date().isEmpty())) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.recordDateString),wrapIn.getQ_date()));
+		}
+		if ( wrapIn.getRecordStatus() != 999 ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.recordStatus),wrapIn.getRecordStatus()));
+		}
+		if (wrapIn.getIsAbsent() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isAbsent),wrapIn.getIsAbsent()));
+		}
+		if (wrapIn.getIsLate() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isLate),wrapIn.getIsLate()));
+		}
+		if (wrapIn.getIsLackOfTime() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isLackOfTime),wrapIn.getIsLackOfTime()));
+		}
+		if (wrapIn.getIsLeaveEarlier() != null ) {
+			p = cb.and(p,cb.equal(root.get(AttendanceDetail_.isLeaveEarlier),wrapIn.getIsLeaveEarlier()));
+		}
+		if (ListTools.isNotEmpty(unUnitNameList)) {
+			p = cb.and(p,cb.not(root.get(AttendanceDetail_.unitName).in(unUnitNameList)));
+		}
+		if (ListTools.isNotEmpty(personNameList)) {
+			p = cb.and(p,cb.not(root.get(AttendanceDetail_.empName).in(personNameList)));
+		}
+
+		cq.select(cb.count(root)).where(p);
+		return em.createQuery(cq).getSingleResult();
+	}
+
+	public List<String> getByUserAndRecordDate(String employeeName, String recordDateStringFormated)  throws Exception{		
+		if( employeeName == null || employeeName.isEmpty() || recordDateStringFormated == null || recordDateStringFormated.isEmpty() ){
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = cb.equal( root.get(AttendanceDetail_.empName), employeeName );
+		p = cb.and(p, cb.equal( root.get(AttendanceDetail_.recordDateString), recordDateStringFormated ));	
+		cq.select( root.get(AttendanceDetail_.id ));
+		return em.createQuery(cq.where(p)).getResultList();
+	}
+	
+	
+	public List<AttendanceCycles> getCyclesFromDetailWithDateSplit( Date startDate, Date endDate )  throws Exception{
+		if( startDate == null || startDate == null ){
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceCycles> cq = cb.createQuery(AttendanceCycles.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = cb.between( root.get(AttendanceDetail_.recordDate), startDate, endDate);
+		
+		List<Selection<?>> selectionList = new ArrayList<Selection<?>>();
+		selectionList.add(root.get(AttendanceDetail_.cycleYear ));
+		selectionList.add(root.get(AttendanceDetail_.cycleMonth ));
+		cq.multiselect(selectionList);
+		
+		return em.createQuery(cq.where(p)).getResultList().stream().distinct().collect(Collectors.toList());
+	}
+	
+	public List<AttendanceCycles> getCyclesFromDetailWithDateSplit( String empName, Date startDate, Date endDate )  throws Exception{
+		if( startDate == null || startDate == null ){
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceCycles> cq = cb.createQuery(AttendanceCycles.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = cb.between( root.get(AttendanceDetail_.recordDate), startDate, endDate);
+		p = cb.and( p, cb.equal( root.get(AttendanceDetail_.empName), empName));
+		List<Selection<?>> selectionList = new ArrayList<Selection<?>>();
+		selectionList.add(root.get(AttendanceDetail_.cycleYear ));
+		selectionList.add(root.get(AttendanceDetail_.cycleMonth ));
+		cq.multiselect(selectionList);
+		
+		return em.createQuery(cq.where(p)).getResultList().stream().distinct().collect(Collectors.toList());
+	}
+	
+	//@MethodDescribe("查询未归档的条卡记录列表,最大2000条")
+	public List<String> listNonArchiveDetailInfoIds()  throws Exception {
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		cq.select( root.get(AttendanceDetail_.id ));
+		//一般始终为true, id is not null
+		Predicate p = cb.isNotNull( root.get(AttendanceDetail_.archiveTime) );
+		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList();
+	}
+
+	public List<String> listAnalysenessDetailsByEmployee( String empName ) throws Exception {
+		List<Integer> statusArray = new ArrayList<Integer>();
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		cq.select(root.get(AttendanceDetail_.id));
+		Predicate p = cb.equal( root.get( AttendanceDetail_.empName ), empName );
+		statusArray.add( 0 ); //未分析的
+		statusArray.add( -1 ); //有错误的
+		p = cb.and( p, root.get( AttendanceDetail_.recordStatus).in( statusArray ));
+		return em.createQuery(cq.where(p)).setMaxResults(20000).getResultList();
+	}
+
+	public List<String> listRecordWithDateAndNoOffDuty( String recordDate ) throws Exception {
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		cq.select(root.get(AttendanceDetail_.id));
+		Predicate p = cb.equal( root.get( AttendanceDetail_.recordDateString ), recordDate );
+		Predicate offDutyTime_1 = cb.isNull(root.get( AttendanceDetail_.offDutyTime ));
+		Predicate offDutyTime_2 = cb.equal( root.get( AttendanceDetail_.offDutyTime ), "" );
+		p = cb.and( p, cb.or( offDutyTime_1, offDutyTime_2 ));
+		return em.createQuery(cq.where(p)).setMaxResults(100000).getResultList();
+	}
+
+	/**
+	 * 查询在指定截止日期前已经打过卡的人员
+	 * @param deadline
+	 * @param type:all#onDuty#offDuty#morningOffDuty#afternoonOnDuty
+	 * @return
+	 * @throws Exception
+	 */
+    public List<String> listSignedPersonsWithDeadLine( String deadline, String type ) throws Exception {
+		DateOperation dateOperation = new DateOperation();
+		if( StringUtils.isEmpty( deadline )){
+			deadline = dateOperation.getNowDateTime();
+		}
+		Date deadlineDate = dateOperation.getDateFromString( deadline, "yyyy-MM-dd HH:mm:ss");
+		String recordDate = dateOperation.getDate( deadlineDate, "yyyy-MM-dd");
+		String deadlineTime = dateOperation.getDate( deadlineDate, "HH:mm:ss");
+
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		cq.select(root.get(AttendanceDetail_.id));
+		Predicate p = cb.equal( root.get( AttendanceDetail_.recordDateString ), recordDate );
+		if( StringUtils.equalsAnyIgnoreCase("onDuty", type)){
+			Predicate p_type = cb.lessThan( root.get( AttendanceDetail_.onDutyTime ), deadlineTime);
+			p = cb.and( p, p_type );
+		}else if(StringUtils.equalsAnyIgnoreCase("offDuty", type)){
+			Predicate p_type = cb.lessThan(root.get( AttendanceDetail_.offDutyTime ), deadlineTime);
+			p = cb.and( p, p_type );
+		}else if(StringUtils.equalsAnyIgnoreCase("morningOffDuty", type)){
+			Predicate p_type = cb.lessThan(root.get( AttendanceDetail_.morningOffDutyTime ), deadlineTime);
+			p = cb.and( p, p_type );
+		}else if(StringUtils.equalsAnyIgnoreCase("afternoonOnDuty", type)){
+			Predicate p_type = cb.lessThan(root.get( AttendanceDetail_.afternoonOnDutyTime ), deadlineTime);
+			p = cb.and( p, p_type );
+		}else{
+			Predicate p_or = cb.or(
+					cb.lessThan( root.get( AttendanceDetail_.onDutyTime ), deadlineTime),
+					cb.lessThan(root.get( AttendanceDetail_.morningOffDutyTime ), deadlineTime),
+					cb.lessThan(root.get( AttendanceDetail_.afternoonOnDutyTime ), deadlineTime),
+					cb.lessThan(root.get( AttendanceDetail_.offDutyTime ), deadlineTime)
+			);
+			p = cb.and( p, p_or );
+		}
+		cq.select(root.get(AttendanceDetail_.empName));
+		return em.createQuery(cq.where(p)).setMaxResults(100000).getResultList().stream().distinct().collect(Collectors.toList());
+	}
+}

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

@@ -0,0 +1,183 @@
+package com.x.attendance.assemble.control.factory;
+
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.x.attendance.assemble.control.AbstractFactory;
+import com.x.attendance.assemble.control.Business;
+import com.x.attendance.entity.AttendanceDetailMobile;
+import com.x.attendance.entity.AttendanceDetailMobile_;
+/**
+ * 系统配置信息表基础功能服务类
+ */
+public class AttendanceDetailMobileFactory extends AbstractFactory {
+	
+	public AttendanceDetailMobileFactory( Business business ) throws Exception {
+		super(business);
+	}
+
+	public List<String> listByEmployeeNameDateAndTime( String empName, String recordDateString, String signTime ) throws Exception {
+		if( StringUtils.isEmpty( empName ) ){
+			throw new Exception("empName is null!");
+		}
+		if( StringUtils.isEmpty( recordDateString ) ){
+			throw new Exception("recordDateString is null!");
+		}
+		if( StringUtils.isEmpty( signTime ) ){
+			throw new Exception("signTime is null!");
+		}
+		EntityManager em = this.entityManagerContainer().get(AttendanceDetailMobile.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetailMobile> root = cq.from( AttendanceDetailMobile.class);
+		cq.select(root.get(AttendanceDetailMobile_.id));
+		Predicate p = cb.equal( root.get(AttendanceDetailMobile_.empName),  empName );
+		p = cb.and( p, cb.equal( root.get(AttendanceDetailMobile_.recordDateString ),  recordDateString ) );
+		p = cb.and( p, cb.equal( root.get(AttendanceDetailMobile_.signTime ),  signTime ) );
+		return em.createQuery(cq.where( p )).getResultList();
+	}
+	
+	/**
+	 * 列示指定人员指定日期的所有移动打卡信息
+	 * @param empName
+	 * @param recordDateString
+	 * @return
+	 * @throws Exception
+	 */
+	public List<AttendanceDetailMobile> listAttendanceDetailMobileWithEmployee( String empName, String recordDateString) throws Exception {
+		if( StringUtils.isEmpty( empName ) ){
+			throw new Exception("empName is null!");
+		}
+		if( StringUtils.isEmpty( recordDateString ) ){
+			throw new Exception("recordDateString is null!");
+		}
+		EntityManager em = this.entityManagerContainer().get(AttendanceDetailMobile.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceDetailMobile> cq = cb.createQuery(AttendanceDetailMobile.class);
+		Root<AttendanceDetailMobile> root = cq.from( AttendanceDetailMobile.class);
+		Predicate p = cb.equal( root.get(AttendanceDetailMobile_.empName),  empName );
+		p = cb.and( p, cb.equal( root.get(AttendanceDetailMobile_.recordDateString ),  recordDateString ) );
+		return em.createQuery(cq.where( p ).orderBy(cb.asc(root.get(AttendanceDetailMobile_.recordDate)))).getResultList();
+	}
+	
+	//@MethodDescribe("列示指定Id的AttendanceDetailMobile信息列表")
+	public List<AttendanceDetailMobile> list(List<String> ids) throws Exception {
+		List<AttendanceDetailMobile> resultList = null;
+		if( ids == null || ids.size() == 0 ){
+			throw new Exception("ids is null!");
+		}
+		EntityManager em = this.entityManagerContainer().get(AttendanceDetailMobile.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceDetailMobile> cq = cb.createQuery(AttendanceDetailMobile.class);
+		Root<AttendanceDetailMobile> root = cq.from(AttendanceDetailMobile.class);
+		Predicate p = root.get( AttendanceDetailMobile_.id).in( ids );
+		resultList = em.createQuery( cq.where(p) ).getResultList();
+		return resultList;
+	}
+
+	public Long countAttendanceDetailMobileForPage( String empNo, String empName, String signDescription,
+			String startDate, String endDate) throws Exception {
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetailMobile.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetailMobile> root = cq.from(AttendanceDetailMobile.class);
+		Predicate p = cb.isNotNull( root.get( AttendanceDetailMobile_.id ) );
+		if( StringUtils.isNotEmpty( empNo ) ){
+			p = cb.and( p, cb.equal( root.get( AttendanceDetailMobile_.empNo ), empNo ) );
+		}
+		if( StringUtils.isNotEmpty( empName ) ){
+			p = cb.and( p, cb.equal( root.get( AttendanceDetailMobile_.empName ), empName ) );
+		}
+		if( StringUtils.isNotEmpty( signDescription ) ){
+			p = cb.and( p, cb.equal( root.get( AttendanceDetailMobile_.signDescription ), signDescription ) );
+		}
+		if( StringUtils.isNotEmpty( startDate ) ){
+			if( StringUtils.isNotEmpty( endDate ) && !endDate.equals( startDate ) ){//查询日期区间
+				p = cb.and( p, cb.between( root.get( AttendanceDetailMobile_.recordDateString ), startDate, endDate ));
+			}else{
+				//查询startDate当天
+				p = cb.and( p, cb.equal( root.get( AttendanceDetailMobile_.recordDateString ), startDate ) );
+			}
+		}
+		cq.select( cb.count( root ) );		
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	public List<AttendanceDetailMobile> listAttendanceDetailMobileForPage( String empNo, String empName, String signDescription,
+			String startDate, String endDate, Integer selectTotal ) throws Exception {
+		if( selectTotal == null ){
+			selectTotal = 100;
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetailMobile.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceDetailMobile> cq = cb.createQuery(AttendanceDetailMobile.class);
+		Root<AttendanceDetailMobile> root = cq.from(AttendanceDetailMobile.class);
+		Predicate p = cb.isNotNull( root.get( AttendanceDetailMobile_.id ) );
+		if( StringUtils.isNotEmpty( empNo ) ){
+			p = cb.and( p, cb.equal( root.get( AttendanceDetailMobile_.empNo ), empNo ) );
+		}
+		if( StringUtils.isNotEmpty( empName ) ){
+			p = cb.and( p, cb.equal( root.get( AttendanceDetailMobile_.empName ), empName ) );
+		}
+		if( StringUtils.isNotEmpty( signDescription ) ){
+			p = cb.and( p, cb.equal( root.get( AttendanceDetailMobile_.signDescription ), signDescription ) );
+		}
+		if( StringUtils.isNotEmpty( startDate ) ){
+			if( StringUtils.isNotEmpty( endDate ) && !endDate.equals( startDate ) ){//查询日期区间
+				p = cb.and( p, cb.between( root.get( AttendanceDetailMobile_.recordDateString ), startDate, endDate ));
+			}else{
+				//查询startDate当天
+				p = cb.and( p, cb.equal( root.get( AttendanceDetailMobile_.recordDateString ), startDate ) );
+			}
+		}
+		return em.createQuery(cq.where(p).orderBy(cb.asc(root.get(AttendanceDetailMobile_.recordDate)))).setMaxResults( selectTotal ).getResultList();
+	}
+
+	public List<AttendanceDetailMobile> listAttendanceDetailMobile( String distinguishedName, String signDate ) throws Exception {
+		if( StringUtils.isEmpty( distinguishedName ) ){
+			throw new Exception("distinguishedName is null!");
+		}
+		if( StringUtils.isEmpty( signDate ) ){
+			throw new Exception("signDate is null!");
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetailMobile.class );
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceDetailMobile> cq = cb.createQuery(AttendanceDetailMobile.class);
+		Root<AttendanceDetailMobile> root = cq.from(AttendanceDetailMobile.class);
+		Predicate p = cb.isNotNull( root.get( AttendanceDetailMobile_.id ) );
+		if( StringUtils.isNotEmpty( distinguishedName ) ){
+			p = cb.or(
+					cb.equal( root.get( AttendanceDetailMobile_.empNo ), distinguishedName ),
+					cb.equal( root.get( AttendanceDetailMobile_.empName ), distinguishedName )
+			);
+		}
+		if( StringUtils.isNotEmpty( signDate ) ){
+			p = cb.and( cb.equal( root.get( AttendanceDetailMobile_.recordDateString ), signDate ), p );
+		}
+		return em.createQuery(cq.where(p)).setMaxResults( 100 ).getResultList();
+	}
+
+	public AttendanceDetailMobile get(String id) throws Exception {
+		return this.entityManagerContainer().find(id, AttendanceDetailMobile.class );
+	}
+
+	public List<String> listAllAnalyseWithStatus(int status) throws Exception {
+		EntityManager em = this.entityManagerContainer().get(AttendanceDetailMobile.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceDetailMobile> root = cq.from( AttendanceDetailMobile.class);
+		cq.select(root.get(AttendanceDetailMobile_.id));
+		Predicate p = cb.equal( root.get(AttendanceDetailMobile_.recordStatus), status );
+		return em.createQuery(cq.where( p )).getResultList();
+	}
+
+	
+
+}

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

@@ -0,0 +1,960 @@
+package com.x.attendance.assemble.control.factory;
+
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import com.x.attendance.assemble.control.AbstractFactory;
+import com.x.attendance.assemble.control.Business;
+import com.x.attendance.entity.AttendanceDetail;
+import com.x.attendance.entity.AttendanceDetail_;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+
+/**
+ * 打卡信息统计服务类,以打卡信息表为基础进行统计
+ */
+public class AttendanceDetailStatisticFactory extends AbstractFactory {
+
+	private static  Logger logger = LoggerFactory.getLogger( AttendanceDetailStatisticFactory.class );
+	
+	public AttendanceDetailStatisticFactory(Business business) throws Exception {
+		super(business);
+	}
+
+	/**
+	 * 根据员工,年月,统计异常打卡次数
+	 * @param employeeNames
+	 * @param cycleYear
+	 * @param cycleMonth
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countAbNormalDutyByEmployeeCycleYearAndMonth(List<String> employeeNames, String cycleYear, String cycleMonth) throws Exception{
+		if( employeeNames == null || employeeNames.size() == 0 ){
+			logger.error( new EmployeeNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = root.get( AttendanceDetail_.empName).in( employeeNames );
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		p = cb.and( p, cb.isTrue( root.get( AttendanceDetail_.isAbnormalDuty) ));
+		if( cycleYear == null || cycleYear.isEmpty() ){
+			logger.error( new CycleYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.cycleYear), cycleYear));
+		}
+		if( cycleMonth == null || cycleMonth.isEmpty() ){
+			logger.error( new CycleMonthEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.cycleMonth), cycleMonth));
+		}
+		//查询总数
+		cq.select( cb.count( root ) );
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据员工,年月,统计工时不足次数
+	 * @param employeeNames
+	 * @param cycleYear
+	 * @param cycleMonth
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countLackOfTimeByEmployeeCycleYearAndMonth( List<String> employeeNames, String cycleYear, String cycleMonth) throws Exception{
+		if( employeeNames == null || employeeNames.size() == 0 ){
+			logger.error( new EmployeeNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = root.get( AttendanceDetail_.empName).in( employeeNames );
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		p = cb.and( p, cb.isTrue( root.get( AttendanceDetail_.isLackOfTime) ));
+		if( cycleYear == null || cycleYear.isEmpty() ){
+			logger.error( new CycleYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.cycleYear), cycleYear));
+		}
+		if( cycleMonth == null || cycleMonth.isEmpty() ){
+			logger.error( new CycleMonthEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.cycleMonth), cycleMonth));
+		}
+		//查询总数
+		cq.select( cb.count( root ) );
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据员工,年月,统计早退次数
+	 * @param employeeNames
+	 * @param cycleYear
+	 * @param cycleMonth
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countLeaveEarlierByEmployeeCycleYearAndMonth( List<String> employeeNames, String cycleYear, String cycleMonth) throws Exception{
+		if( employeeNames == null || employeeNames.size() == 0 ){
+			logger.error( new EmployeeNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = root.get( AttendanceDetail_.empName).in( employeeNames );
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		p = cb.and( p, cb.isTrue( root.get( AttendanceDetail_.isLeaveEarlier ) ));
+		if( cycleYear == null || cycleYear.isEmpty() ){
+			logger.error( new CycleYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.cycleYear), cycleYear));
+		}
+		if( cycleMonth == null || cycleMonth.isEmpty() ){
+			logger.error( new CycleMonthEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.cycleMonth), cycleMonth));
+		}
+		//查询总数
+		cq.select( cb.count( root ) );
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据员工,年月,统计迟到次数
+	 * @param employeeNames
+	 * @param cycleYear
+	 * @param cycleMonth
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countLateByEmployeeCycleYearAndMonth( List<String> employeeNames, String cycleYear, String cycleMonth) throws Exception{
+		if( employeeNames == null || employeeNames.size() == 0 ){
+			logger.error( new EmployeeNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = root.get( AttendanceDetail_.empName).in( employeeNames );
+		p = cb.and( p, cb.isTrue( root.get( AttendanceDetail_.isLate ) ));
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		if( cycleYear == null || cycleYear.isEmpty() ){
+			logger.error( new CycleYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.cycleYear), cycleYear));
+		}
+		if( cycleMonth == null || cycleMonth.isEmpty() ){
+			logger.error( new CycleMonthEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.cycleMonth), cycleMonth));
+		}
+		//查询总数
+		cq.select( cb.count( root ) );
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据员工,年月,统计签退次数
+	 * @param employeeNames
+	 * @param cycleYear
+	 * @param cycleMonth
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countOffDutyByEmployeeCycleYearAndMonth( List<String> employeeNames, String cycleYear, String cycleMonth) throws Exception{
+		if( employeeNames == null || employeeNames.size() == 0 ){
+			logger.error( new EmployeeNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = cb.isNotNull(root.get( AttendanceDetail_.offDutyTime ));
+		p = cb.and( p, cb.notEqual( root.get( AttendanceDetail_.offDutyTime), ""));
+		p = cb.and( p, root.get( AttendanceDetail_.empName).in( employeeNames ));
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		if( cycleYear == null || cycleYear.isEmpty() ){
+			logger.error( new CycleYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.cycleYear), cycleYear));
+		}
+		if( cycleMonth == null || cycleMonth.isEmpty() ){
+			logger.error( new CycleMonthEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.cycleMonth), cycleMonth));
+		}
+		//查询总数
+		cq.select( cb.count( root ) );
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+
+	/**
+	 * 根据员工,年月,统计次数 就是出勤天数
+	 * @param employeeNames
+	 * @param cycleYear
+	 * @param cycleMonth
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countDutyDaysByEmployeeCycleYearAndMonth( List<String> employeeNames, String cycleYear, String cycleMonth) throws Exception {
+		if( employeeNames == null || employeeNames.size() == 0 ){
+			logger.error( new EmployeeNamesEmptyException() );
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = cb.equal( root.get( AttendanceDetail_.recordStatus ), 1);
+		p = cb.and( p, root.get( AttendanceDetail_.empName).in( employeeNames ));
+		if( cycleYear == null || cycleYear.isEmpty() ){
+			logger.error( new CycleYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.cycleYear), cycleYear));
+		}
+		if( cycleMonth == null || cycleMonth.isEmpty() ){
+			logger.error( new CycleMonthEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.cycleMonth), cycleMonth));
+		}
+		//查询总数
+		cq.select( cb.count( root ) );
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据员工,年月,统计签退次数
+	 * @param employeeNames
+	 * @param cycleYear
+	 * @param cycleMonth
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countOnDutyByEmployeeCycleYearAndMonth( List<String> employeeNames, String cycleYear, String cycleMonth) throws Exception{
+		if( employeeNames == null || employeeNames.size() == 0 ){
+			logger.error( new EmployeeNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = cb.isNotNull(root.get( AttendanceDetail_.onDutyTime ));
+		p = cb.and( p, cb.notEqual( root.get( AttendanceDetail_.onDutyTime), ""));
+		p = cb.and( p, root.get( AttendanceDetail_.empName).in( employeeNames ));
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		if( cycleYear == null || cycleYear.isEmpty() ){
+			logger.error( new CycleYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.cycleYear), cycleYear));
+		}
+		if( cycleMonth == null || cycleMonth.isEmpty() ){
+			logger.error( new CycleMonthEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.cycleMonth), cycleMonth));
+		}
+		//查询总数
+		cq.select( cb.count( root ) );
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据员工,年月,统计请假天数
+	 * @param employeeNames
+	 * @param cycleYear
+	 * @param cycleMonth
+	 * @return
+	 * @throws Exception
+	 */
+	public Double sumOnSelfHolidayDaysByEmployeeYearAndMonth( List<String> employeeNames, String cycleYear, String cycleMonth) throws Exception{
+		if( employeeNames == null || employeeNames.size() == 0 ){
+			logger.error( new EmployeeNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Double> cq = cb.createQuery(Double.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		// 不是周末,或者是周末但是调工作日了
+		Predicate p1 = cb.isFalse(root.get( AttendanceDetail_.isWeekend ));
+		p1 = cb.or( p1, cb.and(cb.isTrue(root.get( AttendanceDetail_.isWeekend )), cb.isTrue(root.get( AttendanceDetail_.isWorkday )) ));
+								
+		Predicate p = root.get( AttendanceDetail_.empName).in( employeeNames );
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		p = cb.and( p, cb.isFalse(root.get( AttendanceDetail_.isHoliday ))); //不是节假日
+		p = cb.and( p, p1 ); //不是周末并且未调休工作晶
+		if( cycleYear == null || cycleYear.isEmpty() ){
+			logger.error( new CycleYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.cycleYear), cycleYear ));
+		}
+		if( cycleMonth == null || cycleMonth.isEmpty() ){
+			logger.error( new CycleMonthEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get(AttendanceDetail_.cycleMonth), cycleMonth ));
+		}
+		
+		//查询总数
+		cq.select( cb.sum( root.get( AttendanceDetail_.getSelfHolidayDays ) ) );	
+				
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	
+	
+	/**
+	 * 根据员工,年月,统计缺勤天数
+	 * @param employeeNames
+	 * @param cycleYear
+	 * @param cycleMonth
+	 * @return
+	 * @throws Exception
+	 */
+	public Double sumAbsenceDaysByEmployeeYearAndMonth( List<String> employeeNames, String cycleYear, String cycleMonth) throws Exception{
+		if( employeeNames == null || employeeNames.size() == 0 ){
+			logger.error( new EmployeeNamesEmptyException() );
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Double> cq = cb.createQuery(Double.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);	
+		Predicate p = root.get(AttendanceDetail_.empName).in( employeeNames );		
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		p = cb.and( p, cb.isTrue( root.get( AttendanceDetail_.isAbsent) ));
+		if( cycleYear == null || cycleYear.isEmpty() ){
+			logger.error( new CycleYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get(AttendanceDetail_.cycleYear), cycleYear));
+		}
+		
+		if( cycleMonth == null || cycleMonth.isEmpty() ){
+			logger.error( new CycleMonthEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get(AttendanceDetail_.cycleMonth), cycleMonth));
+		}
+		//查询总数
+		cq.select( cb.sum( root.get(AttendanceDetail_.absence ) ) );	
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据组织,年月,统计异常打卡次数
+	 * @param unitNames
+	 * @param cycleYear
+	 * @param cycleMonth
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countAbNormalDutyByUnitCycleYearAndMonth(List<String> unitNames, String cycleYear, String cycleMonth) throws Exception{
+		if( unitNames == null || unitNames.size() == 0 ){
+			logger.error( new UnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = root.get( AttendanceDetail_.unitName ).in( unitNames );
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		p = cb.and( p, cb.isTrue( root.get( AttendanceDetail_.isAbnormalDuty) ));
+		if( cycleYear == null || cycleYear.isEmpty() ){
+			logger.error( new CycleYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.cycleYear), cycleYear));
+		}
+		if( cycleMonth == null || cycleMonth.isEmpty() ){
+			logger.error( new CycleMonthEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.cycleMonth), cycleMonth));
+		}
+		//查询总数
+		cq.select( cb.count( root ) );
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据组织,年月,统计工时不足次数
+	 * @param unitNames
+	 * @param cycleYear
+	 * @param cycleMonth
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countLackOfTimeByUnitCycleYearAndMonth( List<String> unitNames, String cycleYear, String cycleMonth) throws Exception{
+		if( unitNames == null || unitNames.size() == 0 ){
+			logger.error( new UnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = root.get( AttendanceDetail_.unitName ).in( unitNames );
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		p = cb.and( p, cb.isTrue( root.get( AttendanceDetail_.isLackOfTime) ));
+		if( cycleYear == null || cycleYear.isEmpty() ){
+			logger.error( new CycleYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.cycleYear), cycleYear));
+		}
+		if( cycleMonth == null || cycleMonth.isEmpty() ){
+			logger.error( new CycleMonthEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.cycleMonth), cycleMonth));
+		}
+		//查询总数
+		cq.select( cb.count( root ) );
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据组织,年月,统计早退次数
+	 * @param unitNames
+	 * @param cycleYear
+	 * @param cycleMonth
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countLeaveEarlierByUnitCycleYearAndMonth( List<String> unitNames, String cycleYear, String cycleMonth) throws Exception{
+		if( unitNames == null || unitNames.size() == 0 ){
+			logger.error( new UnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = root.get( AttendanceDetail_.unitName ).in( unitNames );
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		p = cb.and( p, cb.isTrue( root.get( AttendanceDetail_.isLeaveEarlier ) ));
+		if( cycleYear == null || cycleYear.isEmpty() ){
+			logger.error( new CycleYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.cycleYear), cycleYear));
+		}
+		if( cycleMonth == null || cycleMonth.isEmpty() ){
+			logger.error( new CycleMonthEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.cycleMonth), cycleMonth));
+		}
+		//查询总数
+		cq.select( cb.count( root ) );
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据组织,年月,统计迟到次数
+	 * @param unitNames
+	 * @param cycleYear
+	 * @param cycleMonth
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countLateByUnitCycleYearAndMonth( List<String> unitNames, String cycleYear, String cycleMonth) throws Exception{
+		if( unitNames == null || unitNames.size() == 0 ){
+			logger.error( new UnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = root.get( AttendanceDetail_.unitName ).in( unitNames );
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		p = cb.and( p, cb.isTrue( root.get( AttendanceDetail_.isLate ) ));
+		if( cycleYear == null || cycleYear.isEmpty() ){
+			logger.error( new CycleYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.cycleYear), cycleYear));
+		}
+		if( cycleMonth == null || cycleMonth.isEmpty() ){
+			logger.error( new CycleMonthEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.cycleMonth), cycleMonth));
+		}
+		//查询总数
+		cq.select( cb.count( root ) );
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据组织,打卡日期,统计异常打卡次数
+	 * @param unitNames
+	 * @param recordDate
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countAbNormalDutyByUnitAndDate(List<String> unitNames, String recordDate ) throws Exception{
+		if( unitNames == null || unitNames.size() == 0 ){
+			logger.error( new UnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = root.get( AttendanceDetail_.unitName ).in( unitNames );
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		p = cb.and( p, cb.isTrue( root.get( AttendanceDetail_.isAbnormalDuty) ));
+		if( recordDate == null || recordDate.isEmpty() ){
+			logger.error( new RecordDateEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordDateString ), recordDate));
+		}
+		//查询总数
+		cq.select( cb.count( root ) );
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据组织,打卡日期,统计工时不足次数
+	 * @param unitNames
+	 * @param recordDate
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countLackOfTimeByUnitAndDate( List<String> unitNames, String recordDate ) throws Exception{
+		if( unitNames == null || unitNames.size() == 0 ){
+			logger.error( new UnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = root.get( AttendanceDetail_.unitName ).in( unitNames );
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		p = cb.and( p, cb.isTrue( root.get( AttendanceDetail_.isLackOfTime) ));
+		if( recordDate == null || recordDate.isEmpty() ){
+			logger.error( new RecordDateEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordDateString ), recordDate));
+		}
+		//查询总数
+		cq.select( cb.count( root ) );
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据组织,打卡日期,统计早退次数
+	 * @param unitNames
+	 * @param recordDate
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countLeaveEarlierByUnitAndDate( List<String> unitNames, String recordDate ) throws Exception{
+		if( unitNames == null || unitNames.size() == 0 ){
+			logger.error( new UnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = root.get( AttendanceDetail_.unitName ).in( unitNames );
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		p = cb.and( p, cb.isTrue( root.get( AttendanceDetail_.isLeaveEarlier ) ));
+		if( recordDate == null || recordDate.isEmpty() ){
+			logger.error( new RecordDateEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordDateString ), recordDate));
+		}
+		//查询总数
+		cq.select( cb.count( root ) );
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据组织,打卡日期,统计迟到次数
+	 * @param unitNames
+	 * @param recordDate
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countLateByUnitAndDate( List<String> unitNames, String recordDate ) throws Exception{
+		if( unitNames == null || unitNames.size() == 0 ){
+			logger.error( new UnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = root.get( AttendanceDetail_.unitName ).in( unitNames );
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		p = cb.and( p, cb.isTrue( root.get( AttendanceDetail_.isLate ) ));
+		if( recordDate == null || recordDate.isEmpty() ){
+			logger.error( new RecordDateEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordDateString ), recordDate));
+		}
+		//查询总数
+		cq.select( cb.count( root ) );
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据员工,打卡日期,统计缺勤天数
+	 * @param unitNames
+	 * @param recordDate
+	 * @return
+	 * @throws Exception
+	 */
+	public Double sumAbsenceDaysByUnitAndDate( List<String> unitNames, String recordDate ) throws Exception{
+		if( unitNames == null || unitNames.size() == 0 ){
+			logger.error( new UnitNamesEmptyException() );
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Double> cq = cb.createQuery(Double.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);	
+		Predicate p = root.get( AttendanceDetail_.unitName ).in( unitNames );
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		if( recordDate == null || recordDate.isEmpty() ){
+			logger.error( new RecordDateEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordDateString ), recordDate));
+		}
+		//查询总数
+		cq.select( cb.sum( root.get(AttendanceDetail_.absence ) ) );	
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据员工,打卡日期月,统计请假天数
+	 * @param unitNames
+	 * @param recordDate
+	 * @return
+	 * @throws Exception
+	 */
+	public Double sumOnSelfHolidayDaysByUnitAndDate( List<String> unitNames, String recordDate ) throws Exception{
+		if( unitNames == null || unitNames.size() == 0 ){
+			logger.error( new UnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Double> cq = cb.createQuery(Double.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);			
+		Predicate p = root.get( AttendanceDetail_.unitName ).in( unitNames );
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		if( recordDate == null || recordDate.isEmpty() ){
+			logger.error( new RecordDateEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordDateString ), recordDate));
+		}		
+		//查询总数
+		cq.select( cb.sum( root.get( AttendanceDetail_.getSelfHolidayDays ) ) );	
+				
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据组织,打卡日期,统计签退人数
+	 * @param unitNames
+	 * @param recordDate
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countOffDutyByUnitAndDate( List<String> unitNames, String recordDate ) throws Exception{
+		if( unitNames == null || unitNames.size() == 0 ){
+			logger.error( new UnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = cb.isNotNull(root.get( AttendanceDetail_.offDutyTime ));
+		p = cb.and( p, cb.notEqual( root.get( AttendanceDetail_.offDutyTime), ""));
+		p = cb.and( p, root.get( AttendanceDetail_.unitName).in( unitNames ));
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		if( recordDate == null || recordDate.isEmpty() ){
+			logger.error( new CycleYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.unitName), recordDate));
+		}
+		//查询总数
+		cq.select( cb.count( root ) );
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据组织,打卡日期,统计签到人数
+	 * @param unitNames
+	 * @param recordDate
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countOnDutyByUnitAndDate( List<String> unitNames, String recordDate) throws Exception{
+		if( unitNames == null || unitNames.size() == 0 ){
+			logger.error( new UnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = cb.isNotNull(root.get( AttendanceDetail_.onDutyTime ));
+		p = cb.and( p, cb.notEqual( root.get( AttendanceDetail_.onDutyTime), ""));
+		p = cb.and( p, root.get( AttendanceDetail_.unitName ).in( unitNames ));
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		if( recordDate == null || recordDate.isEmpty() ){
+			logger.error( new CycleYearEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.unitName), recordDate));
+		}
+		//查询总数
+		cq.select( cb.count( root ) );
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据顶层组织,打卡日期,统计异常打卡次数
+	 * @param topUnitNames
+	 * @param recordDate
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countAbNormalDutyByTopUnitAndDate(List<String> topUnitNames, String recordDate ) throws Exception{
+		if( topUnitNames == null || topUnitNames.size() == 0 ){
+			logger.error( new TopUnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = root.get( AttendanceDetail_.topUnitName ).in( topUnitNames );
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		p = cb.and( p, cb.isTrue( root.get( AttendanceDetail_.isAbnormalDuty) ));
+		if( recordDate == null || recordDate.isEmpty() ){
+			logger.error( new RecordDateEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordDateString ), recordDate));
+		}
+		//查询总数
+		cq.select( cb.count( root ) );
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据顶层组织,打卡日期,统计工时不足次数
+	 * @param topUnitNames
+	 * @param recordDate
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countLackOfTimeByTopUnitAndDate( List<String> topUnitNames, String recordDate ) throws Exception{
+		if( topUnitNames == null || topUnitNames.size() == 0 ){
+			logger.error( new TopUnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = root.get( AttendanceDetail_.topUnitName ).in( topUnitNames );
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		p = cb.and( p, cb.isTrue( root.get( AttendanceDetail_.isLackOfTime) ));
+		if( recordDate == null || recordDate.isEmpty() ){
+			logger.error( new RecordDateEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordDateString ), recordDate));
+		}
+		//查询总数
+		cq.select( cb.count( root ) );
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据顶层组织,打卡日期,统计早退次数
+	 * @param topUnitNames
+	 * @param recordDate
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countLeaveEarlierByTopUnitAndDate( List<String> topUnitNames, String recordDate ) throws Exception{
+		if( topUnitNames == null || topUnitNames.size() == 0 ){
+			logger.error( new TopUnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = root.get( AttendanceDetail_.topUnitName ).in( topUnitNames );
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		p = cb.and( p, cb.isTrue( root.get( AttendanceDetail_.isLeaveEarlier ) ));
+		if( recordDate == null || recordDate.isEmpty() ){
+			logger.error( new RecordDateEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordDateString ), recordDate));
+		}
+		//查询总数
+		cq.select( cb.count( root ) );
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据顶层组织,打卡日期,统计迟到次数
+	 * @param topUnitNames
+	 * @param recordDate
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countLateByTopUnitAndDate( List<String> topUnitNames, String recordDate ) throws Exception{
+		if( topUnitNames == null || topUnitNames.size() == 0 ){
+			logger.error( new TopUnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = root.get( AttendanceDetail_.topUnitName ).in( topUnitNames );
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		p = cb.and( p, cb.isTrue( root.get( AttendanceDetail_.isLate ) ));
+		if( recordDate == null || recordDate.isEmpty() ){
+			logger.error( new RecordDateEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordDateString ), recordDate));
+		}
+		//查询总数
+		cq.select( cb.count( root ) );
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据顶层组织,打卡日期,统计缺勤天数
+	 * @param topUnitNames
+	 * @param recordDate
+	 * @return
+	 * @throws Exception
+	 */
+	public Double sumAbsenceDaysByTopUnitAndDate( List<String> topUnitNames, String recordDate ) throws Exception{
+		if( topUnitNames == null || topUnitNames.size() == 0 ){
+			logger.error( new TopUnitNamesEmptyException() );
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Double> cq = cb.createQuery(Double.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);	
+		Predicate p = root.get( AttendanceDetail_.topUnitName ).in( topUnitNames );
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		if( recordDate == null || recordDate.isEmpty() ){
+			logger.error( new RecordDateEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordDateString ), recordDate));
+		}
+		//查询总数
+		cq.select( cb.sum( root.get(AttendanceDetail_.absence ) ) );	
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据顶层组织,打卡日期月,统计请假天数
+	 * @param topUnitNames
+	 * @param recordDate
+	 * @return
+	 * @throws Exception
+	 */
+	public Double sumOnSelfHolidayDaysByTopUnitAndDate( List<String> topUnitNames, String recordDate ) throws Exception{
+		if( topUnitNames == null || topUnitNames.size() == 0 ){
+			logger.error( new TopUnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Double> cq = cb.createQuery(Double.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);			
+		Predicate p = root.get( AttendanceDetail_.topUnitName ).in( topUnitNames );
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		if( recordDate == null || recordDate.isEmpty() ){
+			logger.error( new RecordDateEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordDateString ), recordDate));
+		}		
+		//查询总数
+		cq.select( cb.sum( root.get( AttendanceDetail_.getSelfHolidayDays ) ) );	
+				
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据顶层组织,打卡日期,统计签到人数
+	 * @param topUnitNames
+	 * @param recordDate
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countOffDutyByTopUnitAndDate( List<String> topUnitNames, String recordDate ) throws Exception{
+		if( topUnitNames == null || topUnitNames.size() == 0 ){
+			logger.error( new TopUnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = cb.isNotNull(root.get( AttendanceDetail_.offDutyTime ));
+		p = cb.and( p, cb.notEqual( root.get( AttendanceDetail_.offDutyTime), ""));
+		p = cb.and( p, root.get( AttendanceDetail_.topUnitName ).in( topUnitNames ));
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		if( recordDate == null || recordDate.isEmpty() ){
+			logger.error( new RecordDateEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordDateString), recordDate));
+		}
+		//查询总数
+		cq.select( cb.count( root ) );
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+	
+	/**
+	 * 根据顶层组织,打卡日期,统计签到人数
+	 * @param topUnitNames
+	 * @param recordDate
+	 * @return
+	 * @throws Exception
+	 */
+	public Long countOnDutyByTopUnitAndDate( List<String> topUnitNames, String recordDate ) throws Exception{
+		if( topUnitNames == null || topUnitNames.size() == 0 ){
+			logger.error( new TopUnitNamesEmptyException() );
+			return null;
+		}		
+		EntityManager em = this.entityManagerContainer().get( AttendanceDetail.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<Long> cq = cb.createQuery(Long.class);
+		Root<AttendanceDetail> root = cq.from( AttendanceDetail.class);
+		Predicate p = cb.isNotNull(root.get( AttendanceDetail_.onDutyTime ));
+		p = cb.and( p, cb.notEqual( root.get( AttendanceDetail_.onDutyTime), ""));
+		p = cb.and( p, root.get( AttendanceDetail_.topUnitName).in( topUnitNames ));
+		p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordStatus ), 1));
+		if( recordDate == null || recordDate.isEmpty() ){
+			logger.error( new RecordDateEmptyException() );
+		}else{
+			p = cb.and( p, cb.equal( root.get( AttendanceDetail_.recordDateString), recordDate));
+		}
+		//查询总数
+		cq.select( cb.count( root ) );
+		return em.createQuery(cq.where(p)).getSingleResult();
+	}
+}

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

@@ -0,0 +1,67 @@
+package com.x.attendance.assemble.control.factory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import com.x.attendance.assemble.control.AbstractFactory;
+import com.x.attendance.assemble.control.Business;
+import com.x.attendance.entity.AttendanceEmployeeConfig;
+import com.x.attendance.entity.AttendanceEmployeeConfig_;
+import com.x.base.core.project.exception.ExceptionWhen;
+/**
+ * 员工考勤需求配置服务器
+ */
+public class AttendanceEmployeeConfigFactory extends AbstractFactory {
+	
+	public AttendanceEmployeeConfigFactory(Business business) throws Exception {
+		super(business);
+	}
+
+//	@MethodDescribe("获取指定Id的AttendanceEmployeeConfig信息对象")
+	public AttendanceEmployeeConfig get( String id ) throws Exception {
+		return this.entityManagerContainer().find(id, AttendanceEmployeeConfig.class, ExceptionWhen.none);
+	}
+	
+	//@MethodDescribe("列示全部的AttendanceEmployeeConfig信息列表")
+	@SuppressWarnings("unused")
+	public List<AttendanceEmployeeConfig> listAll() throws Exception {
+		EntityManager em = this.entityManagerContainer().get(AttendanceEmployeeConfig.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceEmployeeConfig> cq = cb.createQuery(AttendanceEmployeeConfig.class);
+		Root<AttendanceEmployeeConfig> root = cq.from( AttendanceEmployeeConfig.class);
+		return em.createQuery(cq).getResultList();
+	}
+	
+	//@MethodDescribe("列示指定Id的AttendanceEmployeeConfig信息列表")
+	public List<AttendanceEmployeeConfig> list(List<String> ids) throws Exception {
+		if( ids == null || ids.size() == 0 ){
+			return new ArrayList<AttendanceEmployeeConfig>();
+		}
+		EntityManager em = this.entityManagerContainer().get(AttendanceEmployeeConfig.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceEmployeeConfig> cq = cb.createQuery(AttendanceEmployeeConfig.class);
+		Root<AttendanceEmployeeConfig> root = cq.from(AttendanceEmployeeConfig.class);
+		Predicate p = root.get(AttendanceEmployeeConfig_.id).in(ids);
+		return em.createQuery(cq.where(p)).getResultList();
+	}
+	
+	//@MethodDescribe("根据配置类别列示AttendanceEmployeeConfig信息列表")
+	public List<String> listByConfigType( String configType ) throws Exception {
+		if( configType == null || configType.isEmpty()){
+			return new ArrayList<String>();
+		}
+		EntityManager em = this.entityManagerContainer().get(AttendanceEmployeeConfig.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceEmployeeConfig> root = cq.from(AttendanceEmployeeConfig.class);
+		Predicate p = cb.equal(root.get(AttendanceEmployeeConfig_.configType), configType);	
+		cq.select(root.get(AttendanceEmployeeConfig_.id));
+		return em.createQuery(cq.where(p)).getResultList();
+	}	
+}

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

@@ -0,0 +1,53 @@
+package com.x.attendance.assemble.control.factory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import com.x.attendance.assemble.control.AbstractFactory;
+import com.x.attendance.assemble.control.Business;
+import com.x.attendance.entity.AttendanceImportFileInfo;
+import com.x.attendance.entity.AttendanceImportFileInfo_;
+import com.x.base.core.project.exception.ExceptionWhen;
+/**
+ * 系统配置信息表基础功能服务类
+ */
+public class AttendanceImportFileInfoFactory extends AbstractFactory {
+	
+	public AttendanceImportFileInfoFactory(Business business) throws Exception {
+		super(business);
+	}
+
+	//@MethodDescribe("获取指定Id的AttendanceImportFileInfo应用信息对象")
+	public AttendanceImportFileInfo get( String id ) throws Exception {
+		return this.entityManagerContainer().find(id, AttendanceImportFileInfo.class, ExceptionWhen.none);
+	}
+	
+	//@MethodDescribe("列示全部的AttendanceImportFileInfo应用信息列表")
+	@SuppressWarnings("unused")
+	public List<AttendanceImportFileInfo> listAll() throws Exception {
+		EntityManager em = this.entityManagerContainer().get(AttendanceImportFileInfo.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceImportFileInfo> cq = cb.createQuery(AttendanceImportFileInfo.class);
+		Root<AttendanceImportFileInfo> root = cq.from( AttendanceImportFileInfo.class);
+		return em.createQuery(cq).getResultList();
+	}
+	
+//	@MethodDescribe("列示指定Id的AttendanceImportFileInfo应用信息列表")
+	public List<AttendanceImportFileInfo> list(List<String> ids) throws Exception {
+		if( ids == null || ids.size() == 0 ){
+			return new ArrayList<AttendanceImportFileInfo>();
+		}
+		EntityManager em = this.entityManagerContainer().get(AttendanceImportFileInfo.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceImportFileInfo> cq = cb.createQuery(AttendanceImportFileInfo.class);
+		Root<AttendanceImportFileInfo> root = cq.from(AttendanceImportFileInfo.class);
+		Predicate p = root.get(AttendanceImportFileInfo_.id).in(ids);
+		return em.createQuery(cq.where(p)).getResultList();
+	}	
+}

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

@@ -0,0 +1,97 @@
+package com.x.attendance.assemble.control.factory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import com.x.attendance.assemble.control.AbstractFactory;
+import com.x.attendance.assemble.control.Business;
+import com.x.attendance.entity.AttendanceScheduleSetting;
+import com.x.attendance.entity.AttendanceScheduleSetting_;
+import com.x.base.core.project.exception.ExceptionWhen;
+/**
+ * 系统配置信息表基础功能服务类
+ */
+public class AttendanceScheduleSettingFactory extends AbstractFactory {
+	
+	public AttendanceScheduleSettingFactory(Business business) throws Exception {
+		super(business);
+	}
+
+	//@MethodDescribe("获取指定Id的AttendanceScheduleSetting应用信息对象")
+	public AttendanceScheduleSetting get( String id ) throws Exception {
+		return this.entityManagerContainer().find(id, AttendanceScheduleSetting.class, ExceptionWhen.none);
+	}
+	
+//	@MethodDescribe("列示全部的AttendanceScheduleSetting应用信息列表")
+	@SuppressWarnings("unused")
+	public List<AttendanceScheduleSetting> listAll() throws Exception {
+		EntityManager em = this.entityManagerContainer().get(AttendanceScheduleSetting.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceScheduleSetting> cq = cb.createQuery(AttendanceScheduleSetting.class);
+		Root<AttendanceScheduleSetting> root = cq.from( AttendanceScheduleSetting.class);
+		return em.createQuery(cq).getResultList();
+	}
+	
+	//@MethodDescribe("列示指定Id的AttendanceScheduleSetting应用信息列表")
+	public List<AttendanceScheduleSetting> list(List<String> ids) throws Exception {
+		if( ids == null || ids.size() == 0 ){
+			return new ArrayList<AttendanceScheduleSetting>();
+		}
+		EntityManager em = this.entityManagerContainer().get(AttendanceScheduleSetting.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceScheduleSetting> cq = cb.createQuery(AttendanceScheduleSetting.class);
+		Root<AttendanceScheduleSetting> root = cq.from(AttendanceScheduleSetting.class);
+		Predicate p = root.get(AttendanceScheduleSetting_.id).in(ids);
+		return em.createQuery(cq.where(p)).getResultList();
+	}
+	
+	//@MethodDescribe("根据组织名称,查询组织排班信息对象")
+	public List<String> listByUnitName( String unitName ) throws Exception{		
+		EntityManager em = this.entityManagerContainer().get(AttendanceScheduleSetting.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceScheduleSetting> root = cq.from(AttendanceScheduleSetting.class);
+		Predicate p = cb.equal(root.get(AttendanceScheduleSetting_.unitName), unitName );
+		cq.select(root.get(AttendanceScheduleSetting_.id));
+		return em.createQuery(cq.where(p)).getResultList();
+	}
+	
+	//@MethodDescribe("根据组织名称,查询组织排班信息对象")
+	public List<String> listByUnitNames( List<String> unitNameList ) throws Exception{		
+		EntityManager em = this.entityManagerContainer().get(AttendanceScheduleSetting.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceScheduleSetting> root = cq.from(AttendanceScheduleSetting.class);
+		Predicate p = root.get(AttendanceScheduleSetting_.unitName).in(unitNameList);
+		cq.select(root.get(AttendanceScheduleSetting_.id));
+		return em.createQuery(cq.where(p)).getResultList();
+	}
+	
+	//@MethodDescribe("根据顶层组织名称,查询顶层组织排班信息对象")
+	public List<String> listByTopUnitName( String topUnitName ) throws Exception{		
+		EntityManager em = this.entityManagerContainer().get(AttendanceScheduleSetting.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceScheduleSetting> root = cq.from(AttendanceScheduleSetting.class);
+		Predicate p = cb.equal(root.get(AttendanceScheduleSetting_.topUnitName), topUnitName );
+		cq.select(root.get(AttendanceScheduleSetting_.id));
+		return em.createQuery(cq.where(p)).getResultList();
+	}
+
+	public List<String> listByUnitName( String unitName, String topUnitName ) throws Exception {
+		EntityManager em = this.entityManagerContainer().get(AttendanceScheduleSetting.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceScheduleSetting> root = cq.from(AttendanceScheduleSetting.class);
+		Predicate p = cb.equal(root.get(AttendanceScheduleSetting_.topUnitName), topUnitName );
+		p = cb.and( p, cb.equal(root.get(AttendanceScheduleSetting_.unitName), unitName ) );
+		cq.select(root.get(AttendanceScheduleSetting_.id));
+		return em.createQuery(cq.where(p)).getResultList();
+	}
+}

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

@@ -0,0 +1,310 @@
+package com.x.attendance.assemble.control.factory;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.x.attendance.assemble.control.AbstractFactory;
+import com.x.attendance.assemble.control.Business;
+import com.x.attendance.assemble.control.jaxrs.selfholiday.ActionListNextWithFilter;
+import com.x.attendance.assemble.control.jaxrs.selfholiday.WrapInFilter;
+import com.x.attendance.entity.AttendanceSelfHoliday;
+import com.x.attendance.entity.AttendanceSelfHoliday_;
+import com.x.base.core.project.exception.ExceptionWhen;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+
+/**
+ * 系统配置信息表基础功能服务类
+ */
+public class AttendanceSelfHolidayFactory extends AbstractFactory {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(AttendanceSelfHolidayFactory.class);
+
+    public AttendanceSelfHolidayFactory(Business business) throws Exception {
+        super(business);
+    }
+
+    // @MethodDescribe("获取指定Id的AttendanceSelfHoliday信息对象")
+    public AttendanceSelfHoliday get(String id) throws Exception {
+        return this.entityManagerContainer().find(id, AttendanceSelfHoliday.class, ExceptionWhen.none);
+    }
+
+//@MethodDescribe("列示全部的AttendanceSelfHoliday信息列表")
+    public List<String> listAll() throws Exception {
+        EntityManager em = this.entityManagerContainer().get(AttendanceSelfHoliday.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<String> cq = cb.createQuery(String.class);
+        Root<AttendanceSelfHoliday> root = cq.from(AttendanceSelfHoliday.class);
+        cq.select(root.get(AttendanceSelfHoliday_.id));
+        return em.createQuery(cq).getResultList();
+    }
+
+//	@MethodDescribe("列示指定Id的AttendanceSelfHoliday信息列表")
+    public List<AttendanceSelfHoliday> list(List<String> ids) throws Exception {
+        if (ids == null || ids.size() == 0) {
+            return new ArrayList<AttendanceSelfHoliday>();
+        }
+        EntityManager em = this.entityManagerContainer().get(AttendanceSelfHoliday.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<AttendanceSelfHoliday> cq = cb.createQuery(AttendanceSelfHoliday.class);
+        Root<AttendanceSelfHoliday> root = cq.from(AttendanceSelfHoliday.class);
+        Predicate p = root.get(AttendanceSelfHoliday_.id).in(ids);
+        return em.createQuery(cq.where(p)).getResultList();
+    }
+
+    public List<AttendanceSelfHoliday> listWithBatchFlag(String batchFlag) throws Exception {
+        if (StringUtils.isEmpty(batchFlag)) {
+            return null;
+        }
+        EntityManager em = this.entityManagerContainer().get(AttendanceSelfHoliday.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<AttendanceSelfHoliday> cq = cb.createQuery(AttendanceSelfHoliday.class);
+        Root<AttendanceSelfHoliday> root = cq.from(AttendanceSelfHoliday.class);
+        Predicate p = cb.equal(root.get(AttendanceSelfHoliday_.batchFlag), batchFlag);
+        return em.createQuery(cq.where(p)).getResultList();
+    }
+
+    public List<String> listIdsWithBatchFlag(String batchFlag) throws Exception {
+        if (StringUtils.isEmpty(batchFlag)) {
+            return null;
+        }
+        EntityManager em = this.entityManagerContainer().get(AttendanceSelfHoliday.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<String> cq = cb.createQuery(String.class);
+        Root<AttendanceSelfHoliday> root = cq.from(AttendanceSelfHoliday.class);
+        Predicate p = cb.equal(root.get(AttendanceSelfHoliday_.batchFlag), batchFlag);
+        cq.select(root.get(AttendanceSelfHoliday_.id));
+        return em.createQuery(cq.where(p)).getResultList();
+    }
+
+    // @MethodDescribe("列示单个员工的AttendanceSelfHoliday信息列表")
+    public List<String> getByEmployeeName(String empName) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(AttendanceSelfHoliday.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<String> cq = cb.createQuery(String.class);
+        Root<AttendanceSelfHoliday> root = cq.from(AttendanceSelfHoliday.class);
+        Predicate p = root.get(AttendanceSelfHoliday_.employeeName).in(empName);
+        cq.select(root.get(AttendanceSelfHoliday_.id));
+        return em.createQuery(cq.where(p)).getResultList();
+    }
+
+    // @MethodDescribe("根据流程的文档ID列示员工的AttendanceSelfHoliday信息列表")
+//	public List<String> getByWorkFlowDocId(String docId) throws Exception {
+//		EntityManager em = this.entityManagerContainer().get(AttendanceSelfHoliday.class);
+//		CriteriaBuilder cb = em.getCriteriaBuilder();
+//		CriteriaQuery<String> cq = cb.createQuery(String.class);
+//		Root<AttendanceSelfHoliday> root = cq.from( AttendanceSelfHoliday.class);
+//		Predicate p = cb.equal(root.get(AttendanceSelfHoliday_.docId), docId);
+//		cq.select(root.get(AttendanceSelfHoliday_.id));
+//		return em.createQuery(cq.where(p)).getResultList();
+//	}
+
+    public List<String> listByStartDateAndEndDate(Date startDate, Date endDate) throws Exception {
+        EntityManager em = this.entityManagerContainer().get(AttendanceSelfHoliday.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<String> cq = cb.createQuery(String.class);
+        Root<AttendanceSelfHoliday> root = cq.from(AttendanceSelfHoliday.class);
+        Predicate p = cb.between(root.get(AttendanceSelfHoliday_.startTime), startDate, endDate);
+        p = cb.and(p, cb.between(root.get(AttendanceSelfHoliday_.endTime), startDate, endDate));
+        cq.select(root.get(AttendanceSelfHoliday_.id));
+        return em.createQuery(cq.where(p)).getResultList();
+    }
+
+    /**
+     * 查询下一页的信息数据
+     * 
+     * @param id
+     * @param count
+     * @param sequence
+     * @param wrapIn
+     * @return
+     * @throws Exception
+     */
+    @SuppressWarnings("unchecked")
+    public List<AttendanceSelfHoliday> listIdsNextWithFilter(String id, Integer count, Object sequence,
+            ActionListNextWithFilter.WrapIn wrapIn) throws Exception {
+        // 先获取上一页最后一条的sequence值,如果有值的话,以此sequence值作为依据取后续的count条数据
+        EntityManager em = this.entityManagerContainer().get(AttendanceSelfHoliday.class);
+        String order = wrapIn.getOrder();// 排序方式
+        List<Object> vs = new ArrayList<>();
+        StringBuffer sql_stringBuffer = new StringBuffer();
+
+        if (order == null || order.isEmpty()) {
+            order = "DESC";
+        }
+
+        Integer index = 1;
+        sql_stringBuffer.append("SELECT o FROM " + AttendanceSelfHoliday.class.getCanonicalName() + " o where 1=1");
+
+        if ((null != sequence)) {
+            sql_stringBuffer.append(
+                    " and o.sequence " + (StringUtils.equalsIgnoreCase(order, "DESC") ? "<" : ">") + (" ?" + (index)));
+            vs.add(sequence);
+            index++;
+        }
+        if ((null != wrapIn.getQ_empName()) && (!wrapIn.getQ_empName().isEmpty())) {
+            sql_stringBuffer.append(" and o.employeeName = ?" + (index));
+            vs.add(wrapIn.getQ_empName());
+            index++;
+        }
+        if (null != wrapIn.getUnitNames() && wrapIn.getUnitNames().size() > 0) {
+            sql_stringBuffer.append(" and o.unitOu in ( ?" + (index) + ")");
+            vs.add(wrapIn.getUnitNames());
+            index++;
+        }
+        if (null != wrapIn.getTopUnitNames() && wrapIn.getTopUnitNames().size() > 0) {
+            sql_stringBuffer.append(" and o.topUnitOu in ( ?" + (index) + ")");
+            vs.add(wrapIn.getTopUnitNames());
+            index++;
+        }
+        if (null != wrapIn.getStartdate() && null != wrapIn.getEnddate()) {
+            sql_stringBuffer.append(" and o.startTime >=  ?" + (index));
+            vs.add(wrapIn.getStartdate());
+            index++;
+
+            sql_stringBuffer.append(" and o.endTime <= ?" + (index));
+            vs.add(wrapIn.getEnddate());
+            index++;
+        }
+
+        if (StringUtils.isNotEmpty(wrapIn.getKey())) {
+            sql_stringBuffer.append(" order by o." + wrapIn.getKey() + " " + order);
+        } else {
+            sql_stringBuffer.append(" order by o.sequence " + order);
+        }
+
+        Query query = em.createQuery(sql_stringBuffer.toString(), AttendanceSelfHoliday.class);
+        // System.out.println("query=" +query.toString());
+        // 为查询设置所有的参数值
+        for (int i = 0; i < vs.size(); i++) {
+            query.setParameter(i + 1, vs.get(i));
+        }
+        return query.setMaxResults(count).getResultList();
+    }
+
+    /**
+     * 查询上一页的文档信息数据
+     * 
+     * @param id
+     * @param count
+     * @param sequence
+     * @param wrapIn
+     * @return
+     * @throws Exception
+     */
+    @SuppressWarnings("unchecked")
+    public List<AttendanceSelfHoliday> listIdsPrevWithFilter(String id, Integer count, Object sequence,
+            WrapInFilter wrapIn) throws Exception {
+        // 先获取上一页最后一条的sequence值,如果有值的话,以此sequence值作为依据取后续的count条数据
+        EntityManager em = this.entityManagerContainer().get(AttendanceSelfHoliday.class);
+        String order = wrapIn.getOrder();// 排序方式
+        List<Object> vs = new ArrayList<>();
+        StringBuffer sql_stringBuffer = new StringBuffer();
+        Integer index = 1;
+
+        if (order == null || order.isEmpty()) {
+            order = "DESC";
+        }
+
+        sql_stringBuffer.append("SELECT o FROM " + AttendanceSelfHoliday.class.getCanonicalName() + " o where 1=1");
+        if ((null != sequence)) {
+            sql_stringBuffer.append(
+                    " and o.sequence " + (StringUtils.equalsIgnoreCase(order, "DESC") ? ">" : "<") + (" ?" + (index)));
+            vs.add(sequence);
+            index++;
+        }
+        if ((null != wrapIn.getQ_empName()) && (!wrapIn.getQ_empName().isEmpty())) {
+            sql_stringBuffer.append(" and o.employeeName = ?" + (index));
+            vs.add(wrapIn.getQ_empName());
+            index++;
+        }
+        if (null != wrapIn.getUnitNames() && wrapIn.getUnitNames().size() > 0) {
+            sql_stringBuffer.append(" and o.unitName in ( ?" + (index) + ")");
+            vs.add(wrapIn.getUnitNames());
+            index++;
+        }
+        if (null != wrapIn.getTopUnitNames() && wrapIn.getTopUnitNames().size() > 0) {
+            sql_stringBuffer.append(" and o.topUnitName in ( ?" + (index) + ")");
+            vs.add(wrapIn.getTopUnitNames());
+            index++;
+        }
+
+        if (StringUtils.isNotEmpty(wrapIn.getKey())) {
+            sql_stringBuffer.append(" order by o." + wrapIn.getKey() + " " + order);
+        } else {
+            sql_stringBuffer.append(" order by o.sequence " + order);
+        }
+
+        Query query = em.createQuery(sql_stringBuffer.toString(), AttendanceSelfHoliday.class);
+        // 为查询设置所有的参数值
+        for (int i = 0; i < vs.size(); i++) {
+            query.setParameter(i + 1, vs.get(i));
+        }
+        LOGGER.debug("query :{}, parameter:{}.", query::toString, () -> vs);
+        return query.setMaxResults(20).getResultList();
+    }
+
+    /**
+     * 查询符合的文档信息总数
+     * 
+     * @param wrapIn
+     * @return
+     * @throws Exception
+     */
+    public long getCountWithFilter(WrapInFilter wrapIn) throws Exception {
+        // 先获取上一页最后一条的sequence值,如果有值的话,以此sequence值作为依据取后续的count条数据
+        EntityManager em = this.entityManagerContainer().get(AttendanceSelfHoliday.class);
+        List<Object> vs = new ArrayList<>();
+        StringBuffer sql_stringBuffer = new StringBuffer();
+        Integer index = 1;
+
+        sql_stringBuffer
+                .append("SELECT count(o.id) FROM " + AttendanceSelfHoliday.class.getCanonicalName() + " o where 1=1");
+
+        if ((null != wrapIn.getQ_empName()) && (!wrapIn.getQ_empName().isEmpty())) {
+            sql_stringBuffer.append(" and o.employeeName = ?" + (index));
+            vs.add(wrapIn.getQ_empName());
+            index++;
+        }
+        if (null != wrapIn.getUnitNames() && wrapIn.getUnitNames().size() > 0) {
+            sql_stringBuffer.append(" and o.unitName in ( ?" + (index) + ")");
+            vs.add(wrapIn.getUnitNames());
+            index++;
+        }
+        if (null != wrapIn.getTopUnitNames() && wrapIn.getTopUnitNames().size() > 0) {
+            sql_stringBuffer.append(" and o.topUnitName in ( ?" + (index) + ")");
+            vs.add(wrapIn.getTopUnitNames());
+            index++;
+        }
+
+        Query query = em.createQuery(sql_stringBuffer.toString(), AttendanceSelfHoliday.class);
+        // 为查询设置所有的参数值
+        for (int i = 0; i < vs.size(); i++) {
+            query.setParameter(i + 1, vs.get(i));
+        }
+        return (Long) query.getSingleResult();
+    }
+
+    public List<String> getByPersonName(String personName) throws Exception {
+        if (personName == null || personName.isEmpty()) {
+            throw new Exception("personName is null.");
+        }
+        EntityManager em = this.entityManagerContainer().get(AttendanceSelfHoliday.class);
+        CriteriaBuilder cb = em.getCriteriaBuilder();
+        CriteriaQuery<String> cq = cb.createQuery(String.class);
+        Root<AttendanceSelfHoliday> root = cq.from(AttendanceSelfHoliday.class);
+        Predicate p = cb.equal(root.get(AttendanceSelfHoliday_.employeeName), personName);
+        cq.select(root.get(AttendanceSelfHoliday_.id));
+        return em.createQuery(cq.where(p)).getResultList();
+    }
+}

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

@@ -0,0 +1,85 @@
+package com.x.attendance.assemble.control.factory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import com.x.attendance.assemble.control.AbstractFactory;
+import com.x.attendance.assemble.control.Business;
+import com.x.attendance.entity.AttendanceSetting;
+import com.x.attendance.entity.AttendanceSetting_;
+import com.x.base.core.project.exception.ExceptionWhen;
+/**
+ * 系统配置信息表基础功能服务类
+ */
+public class AttendanceSettingFactory extends AbstractFactory {
+	
+	public AttendanceSettingFactory(Business business) throws Exception {
+		super(business);
+	}
+
+	//@MethodDescribe("获取指定Id的AttendanceSetting应用信息对象")
+	public AttendanceSetting get( String id ) throws Exception {
+		return this.entityManagerContainer().find(id, AttendanceSetting.class, ExceptionWhen.none);
+	}
+	
+	//@MethodDescribe("列示全部的AttendanceSetting应用信息列表")
+	@SuppressWarnings("unused")
+	public List<AttendanceSetting> listAll() throws Exception {
+		EntityManager em = this.entityManagerContainer().get(AttendanceSetting.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceSetting> cq = cb.createQuery(AttendanceSetting.class);
+		Root<AttendanceSetting> root = cq.from( AttendanceSetting.class);
+		return em.createQuery(cq).getResultList();
+	}
+	
+	//@MethodDescribe("列示指定Id的AttendanceSetting应用信息列表")
+	public List<AttendanceSetting> list(List<String> ids) throws Exception {
+		if( ids == null || ids.size() == 0 ){
+			return new ArrayList<AttendanceSetting>();
+		}
+		EntityManager em = this.entityManagerContainer().get(AttendanceSetting.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceSetting> cq = cb.createQuery(AttendanceSetting.class);
+		Root<AttendanceSetting> root = cq.from(AttendanceSetting.class);
+		Predicate p = root.get(AttendanceSetting_.id).in(ids);
+		return em.createQuery(cq.where(p)).getResultList();
+	}
+	
+	//@MethodDescribe("根据CODE列示指定Id的AttendanceSetting应用信息列表")
+	public List<String> listIdsByCode(String code) throws Exception {
+		if( code == null || code.isEmpty() ){
+			return null;
+		}
+		EntityManager em = this.entityManagerContainer().get(AttendanceSetting.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceSetting> root = cq.from(AttendanceSetting.class);
+		Predicate p = cb.equal(root.get(AttendanceSetting_.configCode),  code);
+		cq.select(root.get(AttendanceSetting_.id));
+		return em.createQuery(cq.where(p)).getResultList();
+	}
+
+	public AttendanceSetting getWithConfigCode(String configCode) throws Exception {
+		if( configCode == null || configCode.isEmpty() ){
+			return null;
+		}
+		AttendanceSetting attendanceSetting = null;
+		List<AttendanceSetting> settingList = null;
+		EntityManager em = this.entityManagerContainer().get(AttendanceSetting.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceSetting> cq = cb.createQuery(AttendanceSetting.class);
+		Root<AttendanceSetting> root = cq.from(AttendanceSetting.class);
+		Predicate p = cb.equal(root.get( AttendanceSetting_.configCode ),  configCode );
+		settingList = em.createQuery(cq.where(p)).getResultList();
+		if( settingList != null && settingList.size() > 0 ){
+			attendanceSetting = settingList.get(0);
+		}
+		return attendanceSetting;
+	}
+}

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

@@ -0,0 +1,124 @@
+package com.x.attendance.assemble.control.factory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.apache.commons.lang3.StringUtils;
+
+import com.x.attendance.assemble.control.AbstractFactory;
+import com.x.attendance.assemble.control.Business;
+import com.x.attendance.entity.AttendanceStatisticRequireLog;
+import com.x.attendance.entity.AttendanceStatisticRequireLog_;
+import com.x.base.core.project.exception.ExceptionWhen;
+/**
+ * 员工考勤需求配置服务器
+ */
+public class AttendanceStatisticRequireLogFactory extends AbstractFactory {
+	
+	public AttendanceStatisticRequireLogFactory(Business business) throws Exception {
+		super(business);
+	}
+
+	//@MethodDescribe("获取指定Id的AttendanceStatisticRequireLog信息对象")
+	public AttendanceStatisticRequireLog get( String id ) throws Exception {
+		return this.entityManagerContainer().find(id, AttendanceStatisticRequireLog.class, ExceptionWhen.none);
+	}
+	
+//	@MethodDescribe("列示全部的AttendanceStatisticRequireLog信息列表")
+	@SuppressWarnings("unused")
+	public List<AttendanceStatisticRequireLog> listAll() throws Exception {
+		EntityManager em = this.entityManagerContainer().get(AttendanceStatisticRequireLog.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceStatisticRequireLog> cq = cb.createQuery(AttendanceStatisticRequireLog.class);
+		Root<AttendanceStatisticRequireLog> root = cq.from( AttendanceStatisticRequireLog.class);
+		return em.createQuery(cq).getResultList();
+	}
+	
+	//@MethodDescribe("列示指定Id的AttendanceStatisticRequireLog信息列表")
+	public List<AttendanceStatisticRequireLog> list(List<String> ids) throws Exception {
+		if( ids == null || ids.size() == 0 ){
+			return new ArrayList<AttendanceStatisticRequireLog>();
+		}
+		EntityManager em = this.entityManagerContainer().get(AttendanceStatisticRequireLog.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceStatisticRequireLog> cq = cb.createQuery(AttendanceStatisticRequireLog.class);
+		Root<AttendanceStatisticRequireLog> root = cq.from(AttendanceStatisticRequireLog.class);
+		Predicate p = root.get(AttendanceStatisticRequireLog_.id).in(ids);
+		return em.createQuery(cq.where(p)).getResultList();
+	}
+	
+	//@MethodDescribe("根据统计名称,统计Key,处理状态,列示AttendanceStatisticRequireLog信息列表")
+	public List<AttendanceStatisticRequireLog> getByNameKeyAndStatus( String name, String key, String stauts ) throws Exception {
+		EntityManager em = this.entityManagerContainer().get(AttendanceStatisticRequireLog.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceStatisticRequireLog> cq = cb.createQuery(AttendanceStatisticRequireLog.class);
+		Root<AttendanceStatisticRequireLog> root = cq.from(AttendanceStatisticRequireLog.class);
+		Predicate p = root.get(AttendanceStatisticRequireLog_.id).isNotNull();
+		if( StringUtils.isNotEmpty( name ) ){
+			p = cb.and(p, cb.equal(root.get(AttendanceStatisticRequireLog_.statisticName), name));
+		}
+		if( StringUtils.isNotEmpty( key ) ){
+			p = cb.and(p,  cb.equal(root.get(AttendanceStatisticRequireLog_.statisticKey), key));
+		}
+		if( StringUtils.isNotEmpty( stauts ) ){
+			p = cb.and(p, cb.equal(root.get(AttendanceStatisticRequireLog_.processStatus), stauts));
+		}
+		return em.createQuery( cq.where(p) ).getResultList();
+	}
+
+	public List<AttendanceStatisticRequireLog> getByNameKeyAndStatus(String statisticType, String key, String statisticYear, String statisticMonth, String statisticDate, String status) throws Exception {
+		EntityManager em = this.entityManagerContainer().get(AttendanceStatisticRequireLog.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceStatisticRequireLog> cq = cb.createQuery(AttendanceStatisticRequireLog.class);
+		Root<AttendanceStatisticRequireLog> root = cq.from(AttendanceStatisticRequireLog.class);
+		Predicate p = root.get(AttendanceStatisticRequireLog_.id).isNotNull();
+		if( StringUtils.isNotEmpty( statisticType ) ){
+			p = cb.and(p, cb.equal(root.get(AttendanceStatisticRequireLog_.statisticType), statisticType));
+		}
+		if( StringUtils.isNotEmpty( key ) ){
+			p = cb.and(p, cb.equal(root.get(AttendanceStatisticRequireLog_.statisticKey), key));
+		}
+		if( StringUtils.isNotEmpty( statisticYear ) ){
+			p = cb.and(p, cb.equal(root.get(AttendanceStatisticRequireLog_.statisticYear), statisticYear));
+		}
+		if( StringUtils.isNotEmpty( statisticMonth ) ){
+			p = cb.and(p, cb.equal(root.get(AttendanceStatisticRequireLog_.statisticMonth), statisticMonth));
+		}
+		if( StringUtils.isNotEmpty( statisticDate ) ){
+			p = cb.and(p, cb.equal(root.get(AttendanceStatisticRequireLog_.statisticDay), statisticDate));
+		}
+		if( StringUtils.isNotEmpty( status ) ){
+			p = cb.and(p, cb.equal(root.get(AttendanceStatisticRequireLog_.processStatus), status));
+		}
+		return em.createQuery( cq.where(p) ).getResultList();
+	}
+
+	/**
+	 * 根据统计的类型以及处理状态获取所有符合条件的统计需求对象列表
+	 * 
+	 * @param statisticType:PERSON_PER_MONTH|UNIT_PER_MONTH|TOPUNIT_PER_MONTH|UNIT_PER_DAY|TOPUNIT_PER_DAY
+	 * @param processStatus:WAITING|PROCESSING|COMPLETE|ERROR
+	 * @return
+	 * @throws Exception
+	 */
+	public List<AttendanceStatisticRequireLog> listByStatisticTypeAndStatus( String statisticType, String processStatus) throws Exception {
+		EntityManager em = this.entityManagerContainer().get(AttendanceStatisticRequireLog.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceStatisticRequireLog> cq = cb.createQuery(AttendanceStatisticRequireLog.class);
+		Root<AttendanceStatisticRequireLog> root = cq.from(AttendanceStatisticRequireLog.class);
+		Predicate p = root.get(AttendanceStatisticRequireLog_.id).isNotNull();
+		if( StringUtils.isNotEmpty( statisticType ) ){
+			p = cb.and(p, cb.equal(root.get(AttendanceStatisticRequireLog_.statisticType), statisticType));
+		}
+		if( StringUtils.isNotEmpty( processStatus ) ){
+			p = cb.and(p, cb.equal(root.get(AttendanceStatisticRequireLog_.processStatus), processStatus));
+		}
+		return em.createQuery( cq.where(p) ).getResultList();
+	}
+}

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

@@ -0,0 +1,73 @@
+package com.x.attendance.assemble.control.factory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import com.x.attendance.assemble.control.AbstractFactory;
+import com.x.attendance.assemble.control.Business;
+import com.x.attendance.entity.AttendanceStatisticalCycle;
+import com.x.attendance.entity.AttendanceStatisticalCycle_;
+import com.x.base.core.project.exception.ExceptionWhen;
+/**
+ * 系统配置信息表基础功能服务类
+ */
+public class AttendanceStatisticalCycleFactory extends AbstractFactory {
+	
+	public AttendanceStatisticalCycleFactory(Business business) throws Exception {
+		super(business);
+	}
+
+	//@MethodDescribe("获取指定Id的AttendanceStatisticalCycle信息对象")
+	public AttendanceStatisticalCycle get( String id ) throws Exception {
+		return this.entityManagerContainer().find(id, AttendanceStatisticalCycle.class, ExceptionWhen.none);
+	}
+	
+	//@MethodDescribe("列示全部的AttendanceStatisticalCycle信息列表")
+	@SuppressWarnings("unused")
+	public List<AttendanceStatisticalCycle> listAll() throws Exception {
+		EntityManager em = this.entityManagerContainer().get(AttendanceStatisticalCycle.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceStatisticalCycle> cq = cb.createQuery( AttendanceStatisticalCycle.class );
+		Root<AttendanceStatisticalCycle> root = cq.from( AttendanceStatisticalCycle.class);
+		return em.createQuery(cq).getResultList();
+	}
+	
+	//@MethodDescribe("列示指定Id的AttendanceStatisticalCycle信息列表")
+	public List<AttendanceStatisticalCycle> list(List<String> ids) throws Exception {
+		if( ids == null || ids.size() == 0 ){
+			return new ArrayList<AttendanceStatisticalCycle>();
+		}
+		EntityManager em = this.entityManagerContainer().get(AttendanceStatisticalCycle.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<AttendanceStatisticalCycle> cq = cb.createQuery(AttendanceStatisticalCycle.class);
+		Root<AttendanceStatisticalCycle> root = cq.from(AttendanceStatisticalCycle.class);
+		Predicate p = root.get(AttendanceStatisticalCycle_.id).in(ids);
+		return em.createQuery(cq.where(p)).getResultList();
+	}
+	
+	//@MethodDescribe("根据参数列示AttendanceStatisticalCycle信息列表")
+	public List<String> listByParameter( String topUnitName, String organizatinName, String cycleYear, String cycleMonth ) throws Exception{
+		EntityManager em = this.entityManagerContainer().get(AttendanceStatisticalCycle.class);
+		CriteriaBuilder cb = em.getCriteriaBuilder();
+		CriteriaQuery<String> cq = cb.createQuery(String.class);
+		Root<AttendanceStatisticalCycle> root = cq.from(AttendanceStatisticalCycle.class);
+		Predicate p = cb.equal( root.get( AttendanceStatisticalCycle_.cycleYear), cycleYear);		
+		if( topUnitName != null ){ 
+			 p = cb.and( p, cb.equal( root.get( AttendanceStatisticalCycle_.topUnitName), topUnitName));
+		}
+		if( organizatinName != null ){ 
+			 p = cb.and( p, cb.equal( root.get( AttendanceStatisticalCycle_.unitName), organizatinName));
+		}
+		if( cycleMonth != null ){ 
+			 p = cb.and( p, cb.equal( root.get( AttendanceStatisticalCycle_.cycleMonth), cycleMonth));
+		}
+		cq.select(root.get(AttendanceStatisticalCycle_.id));
+		return em.createQuery(cq.where(p)).getResultList();
+	}
+}

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