Преглед изворни кода

Merge branch 'wrdp' into 'feature/search_in_application'

# Conflicts:
#   package.json
胡起 пре 5 година
родитељ
комит
a4e3d2baf5
100 измењених фајлова са 2418 додато и 1801 уклоњено
  1. 25 0
      .gitignore
  2. 78 232
      README.md
  3. 7 0
      gulpfile.js
  4. 12 1
      o2android/README.md
  5. 4 4
      o2android/app/assets/html/huawei.html
  6. 4 4
      o2android/app/assets/html/xiaomi.html
  7. 3 1
      o2android/app/build.gradle
  8. 2 2
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/meeting/apply/MeetingApplyActivity.kt
  9. 2 2
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/meeting/edit/MeetingEditActivity.kt
  10. 1 1
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/meeting/invited/MeetingDetailInfoActivity.kt
  11. 12 11
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/meeting/invited/MeetingInvitedFragment.kt
  12. 3 3
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/o2/security/AccountSecurityActivity.kt
  13. 32 39
      o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/service/DownloadAPKService.kt
  14. 1 1
      o2android/app/src/main/res/layout/fragment_meeting_invited.xml
  15. 2 2
      o2android/gradle.properties
  16. 1 1
      o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/component/api/APIAddressHelper.kt
  17. 1 1
      o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/api/meeting/MeetingJSON.kt
  18. 134 1
      o2ios/O2Platform.xcodeproj/project.pbxproj
  19. 10 0
      o2ios/O2Platform.xcodeproj/xcshareddata/xcschemes/O2Platform.xcscheme
  20. BIN
      o2ios/O2Platform/Framework/O2OA_Auth_SDK.framework/Info.plist
  21. BIN
      o2ios/O2Platform/Framework/O2OA_Auth_SDK.framework/Modules/O2OA_Auth_SDK.swiftmodule/Project/arm.swiftsourceinfo
  22. BIN
      o2ios/O2Platform/Framework/O2OA_Auth_SDK.framework/Modules/O2OA_Auth_SDK.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo
  23. BIN
      o2ios/O2Platform/Framework/O2OA_Auth_SDK.framework/Modules/O2OA_Auth_SDK.swiftmodule/Project/arm64.swiftsourceinfo
  24. BIN
      o2ios/O2Platform/Framework/O2OA_Auth_SDK.framework/Modules/O2OA_Auth_SDK.swiftmodule/Project/armv7-apple-ios.swiftsourceinfo
  25. BIN
      o2ios/O2Platform/Framework/O2OA_Auth_SDK.framework/Modules/O2OA_Auth_SDK.swiftmodule/Project/armv7.swiftsourceinfo
  26. BIN
      o2ios/O2Platform/Framework/O2OA_Auth_SDK.framework/O2OA_Auth_SDK
  27. 2 2
      o2ios/O2Platform/Info.plist
  28. 22 0
      o2ios/O2PlatformTests1/Info.plist
  29. 34 0
      o2ios/O2PlatformTests1/O2PlatformTests1.swift
  30. 14 0
      o2ios/README.md
  31. 0 10
      o2server/configSample/appStyle.json
  32. 0 28
      o2server/configSample/cache.json
  33. 0 33
      o2server/configSample/centerServer.json
  34. 0 21
      o2server/configSample/clientInit.json
  35. 0 22
      o2server/configSample/collect.json
  36. 0 24
      o2server/configSample/communicate.json
  37. 0 255
      o2server/configSample/components.json
  38. 0 32
      o2server/configSample/dingding.json
  39. 0 19
      o2server/configSample/dumpRestoreData.json
  40. 0 13
      o2server/configSample/dumpRestoreStorage.json
  41. 0 20
      o2server/configSample/exmail.json
  42. 2 2
      o2server/configSample/externalDataSources.json
  43. 0 24
      o2server/configSample/externalDataSources_oracle.json
  44. 0 8
      o2server/configSample/jpushConfig.json
  45. 0 70
      o2server/configSample/logLevel.json
  46. 4 4
      o2server/configSample/manifest.cfg
  47. 0 17
      o2server/configSample/meeting.json
  48. 0 198
      o2server/configSample/messageSendRule.js
  49. 2 2
      o2server/configSample/mq.json
  50. 0 185
      o2server/configSample/node_127.0.0.1.json
  51. 0 40
      o2server/configSample/person.json
  52. 0 21
      o2server/configSample/portal.json
  53. 0 109
      o2server/configSample/processPlatform.json
  54. 0 36
      o2server/configSample/qiyeweixin.json
  55. 0 55
      o2server/configSample/query.json
  56. 0 85
      o2server/configSample/token.json
  57. 0 11
      o2server/configSample/vfs.json
  58. 0 20
      o2server/configSample/web.json
  59. 0 19
      o2server/configSample/workTime.json
  60. 0 33
      o2server/configSample/zhengwuDingding.json
  61. 11 2
      o2server/pom.xml
  62. 3 2
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/common/date/DateOperation.java
  63. 65 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/Business.java
  64. 2 1
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/ThisApplication.java
  65. 212 2
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/AttendanceAppealInfoFactory.java
  66. 118 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/AttendanceDetailFactory.java
  67. 1 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/AttendanceDetailStatisticFactory.java
  68. 14 3
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/AttendanceSelfHolidayFactory.java
  69. 23 19
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/AttendanceWorkDayConfigFactory.java
  70. 16 3
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attachment/ActionExportAbnormalDetail.java
  71. 333 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attachment/ActionExportDetailWithFilter.java
  72. 6 1
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attachment/ActionExportHolidayDetail.java
  73. 163 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attachment/ActionExportPersonStatistic.java
  74. 172 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attachment/ActionExportTopUnitStatistic.java
  75. 190 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attachment/ActionExportUnitSubNestedStatistic.java
  76. 157 1
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attachment/BaseAction.java
  77. 16 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attachment/ExceptionAttendanceDetailProcess.java
  78. 12 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attachment/ExceptionAttendanceStatisticProcess.java
  79. 12 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attachment/ExceptionPersonNameEmpty.java
  80. 12 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attachment/ExceptionQueryStatisticUnitNameEmpty.java
  81. 12 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attachment/ExceptionTopUnitNameEmpty.java
  82. 93 6
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attachment/FileImportExportAction.java
  83. 7 4
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attendanceappealinfo/ActionListNextWithFilter.java
  84. 13 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attendancedetail/ActionCheckWithPersonByCycle.java
  85. 26 1
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attendancedetail/ActionReciveAttendanceMobile.java
  86. 15 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attendanceschedulesetting/ActionSave.java
  87. 60 3
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/selfholiday/ActionListNextWithFilter.java
  88. 1 1
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/selfholiday/ActionSave.java
  89. 1 1
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/service/AttendanceDetailAnalyseCoreService.java
  90. 7 2
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/service/AttendanceDetailAnalyseService.java
  91. 51 7
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/service/AttendanceDetailAnalyseSignProxy2.java
  92. 12 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/service/AttendanceDetailService.java
  93. 17 1
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/service/AttendanceDetailServiceAdv.java
  94. 12 0
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/service/AttendanceSettingService.java
  95. 29 1
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/service/AttendanceSettingServiceAdv.java
  96. 45 7
      o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/service/AttendanceStatisticalCycleServiceAdv.java
  97. 8 8
      o2server/x_attendance_assemble_control/src/main/webapp/jest/describe.js
  98. 28 0
      o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/AttendanceDetail.java
  99. 28 0
      o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/AttendanceDetailMobile.java
  100. 1 1
      o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/AttendanceSelfHoliday.java

+ 25 - 0
.gitignore

@@ -13,6 +13,31 @@
 /o2server/commons/
 /o2server/jvm/
 /o2server/local/
+/o2server/configSample/appStyle.json
+/o2server/configSample/cache.json
+/o2server/configSample/centerServer.json
+/o2server/configSample/clientInit.json
+/o2server/configSample/collect.json
+/o2server/configSample/communicate.json
+/o2server/configSample/components.json
+/o2server/configSample/dingding.json
+/o2server/configSample/dumpRestoreData.json
+/o2server/configSample/exmail.json
+/o2server/configSample/logLevel.json
+/o2server/configSample/meeting.json
+/o2server/configSample/messages.json
+/o2server/configSample/node_127.0.0.1.json
+/o2server/configSample/person.json
+/o2server/configSample/portal.json
+/o2server/configSample/processPlatform.json
+/o2server/configSample/jpushConfig.json
+/o2server/configSample/qiyeweixin.json
+/o2server/configSample/query.json
+/o2server/configSample/token.json
+/o2server/configSample/vfs.json
+/o2server/configSample/weLink.json
+/o2server/configSample/workTime.json
+/o2server/configSample/zhengwuDingding.json
 /o2server/*/src/main/webapp/describe/
 /o2server/*/src/main/webapp/WEB-INF/
 **/.settings/

+ 78 - 232
README.md

@@ -78,25 +78,25 @@ O2OA自带的H2数据库是一个内嵌式的内存数据库,适合用于开
 另外,O2OA提供数据定期备份和恢复的能力,建议您开启正式环境的数据定期备份的功能,以确保数据库异常时可以进行数据恢复。
 
 
-# 最新版本服务器安装包下载[o2server_V5.2.1]\:
+# 最新版本服务器安装包下载[o2server_V5.3.0]\:
 
-windows 64Bit : http://download.o2oa.net/download/o2server-5.2.1-windows.zip
+windows 64Bit : http://download.o2oa.net/download/o2server-5.3.0-windows-x64.zip
 
-Linux 64Bit : http://download.o2oa.net/download/o2server-5.2.1-linux-x86.zip
+Linux 64Bit : http://download.o2oa.net/download/o2server-5.3.0-linux-x64.zip
 
-MacOS : http://download.o2oa.net/download/o2server-5.2.1-macos.zip
+MacOS : http://download.o2oa.net/download/o2server-5.3.0-macos.zip
 
-AIX : http://download.o2oa.net/download/o2server-5.2.1-aix.zip
+AIX : http://download.o2oa.net/download/o2server-5.3.0-aix.zip
 
-raspberrypi(树莓派):http://download.o2oa.net/download/o2server-5.2.1-raspi.zip
+raspberrypi(树莓派):http://download.o2oa.net/download/o2server-5.3.0-raspi.zip
 
 ARM[深度Linux(deepin),优麒麟(Ubuntu),中标麒麟(NeoKylin),威科乐恩Linux(WiOS)]:
 
-http://download.o2oa.net/download/o2server-5.2.1-linux-arm.zip
+http://download.o2oa.net/download/o2server-5.3.0-linux-arm.zip
 
 RISC-V[Debian GNU/Linux,银河麒麟飞腾]:
 
-http://download.o2oa.net/download/o2server-5.2.1-linux-mips.zip
+http://download.o2oa.net/download/o2server-5.3.0-linux-mips.zip
 
 
 
@@ -104,306 +104,152 @@ http://download.o2oa.net/download/o2server-5.2.1-linux-mips.zip
 
 百度云盘:https://pan.baidu.com/s/1oBQ1atXGyXdLaYE5uAqF1w   提取码: pnk9
 
-腾讯微云:https://share.weiyun.com/5krUMjj
 
-
-# 最新版本 v5.2\:
+# 最新版本 v5.3\:
 
 功能新增
 
-[系统架构]系统安全加固,使用加密信息传输配置文件密码
-
-[系统架构]功能增强,增加通过URL传入参数,直接打开指定应用界面的功能
-
-[系统架构]功能增强,Dump命令支持全量数据导出和部分数据导出
-
-[系统架构]功能增强,Dump命令支持对数据和文件信息的合并操作
-
-[系统架构]系统增强,改用Instrumentation实现jar包加载
-
-[系统架构]系统增强,webServer增加proxy功能,可以替代nginx实现映射
-
-[系统架构]新增集成消息中间件管理消息队列,比如kafka,activeMQ
-
-[平台配置]系统增强,增加在线编辑部分配置文件的能力,并在集群时同步配置信息
-
-[用户认证]新增用户登录ip限制(黑名单)功能
-
-[流程管理]新增了数据字典编辑器
+[移动办公]新增了企业微信通讯录同步回调功能
 
-[流程管理]新增了管理员 (增加、删除、修改)授权接口
+[移动办公]新增了钉钉通讯录同步回调功能
 
-[流程平台]新增了待办已办待阅已阅稽核查询接口
+[数据库连接]新增了人大金仓数据库V8R6适配器
 
-[流程平台]新增了流程附件拷贝到已完成工作接口
+[流程平台]新增了表单组件的默认值等脚本支持异步返回
 
-[流程平台]新增了数据字典、脚本分页查询接口
+[流程平台]优化表单展现速度,增加预加载脚本
 
-[流程平台]新增了流程草稿模式
+[流程平台]新增了数据网格支持插入附件功能
 
-[流程平台]新增了管理员转交记录功能
+[流程平台]新增了V2版本的work接口
 
-[流程平台]新增了拆分值记录功能
+[流程平台]新增了V2版本的form接口
 
-[流程平台]新增了Message消息中流程上一处理人信息
+[文件存储]新增了对sftp协议的支持
 
-[流程平台]新增了流程环节可以异步返回功能,针对大量拆分时可以异步返回
+[系统服务]新增了工作时间计算的前端接口
 
-[表单设计]新增了表单版本恢复功能
+[人员组织]新增了组织关联用户、身份是否存在的校验接口
 
-[表单设计]新增了表单中直接选择需要引入的脚本的功能
+[人员组织]新增了根据职务名称和组织查询职务信息接口
 
-[表单设计]新增了表单中直接选择引入的数据字典的功能
+[人员组织]新增了组织成员按身份排序的按钮
 
-[表单设计]新增了版式文件缩放比例调整功能
+[中心服务]新增了手动发起同步区域数据接口
 
-[内容管理]新增了数据字典、脚本分页查询接口
+[内容管理]新增了点赞信息查询接口
 
-[内容管理]新增了查询文档阅读权限接口
+[内容管理]新增了根据id列表查询doc数据接口
 
-[门户管理]新增了脚本分页查询接口
+[数据中心]新增了查询语句的视图配置
 
-[数据中心]新增了数据视图中默认选中行的配置
+[数据中心]新增了查询语句的API
 
-[数据中心]新增了清空自建表数据功能
+[数据中心]新增了获取实体对象的属性信息接口
 
-[考勤管理]新增了指定人员分析打卡数据接口考勤
+[数据中心]新增了视图数组类型in语句查询条件
 
-[考勤管理]新增了指定人员分析打卡数据接口
+[数据中心]新增了查询语句查询数据与查询总数同时执行的接口
 
-[个人通讯录]新增通讯录权限设置
+[数据中心]新增了视图中的全选功能
 
-[会议管理]新增了会议室分页功能
+[数据中心]新增了视图中按脚本设置默认选中条目的功能
 
-[会议管理]新增了对会议室预定时间修改功能
+[数据中心]新增了视图中按脚本设置选中条目权限的功能
 
-[会议管理]新增了根据会议室ID查询,某一时间段一个会议的使用情况的功能
+[平台架构]打包应用jar包上传到maven仓库
 
-[即时通讯]新增了IM聊天Web端创建群聊功能和修改群属性等功能
+[平台架构]新增了自定义应用模板创建
 
-[移动办公]新增了移动端通知消息可打开对应应用的功能
+[平台架构]引入Promise对象,所有前端Action的请求全部返回Promise对象
 
-[移动办公]新增了移动端工作消息可以打开对应工作的功能
+[平台架构]新增了web.json配置,可配置configMapping减少获取服务地址请求
 
-[移动办公]新增了移动端添加地图位置展现功能,js api支持
-
-[移动办公]新增了移动端支持第三方地图打开位置信息,js api打开
-
-[移动办公]新增了移动端添加jsapi支持定位功能
-
-[移动办公]新增了华为WeLink集成能力
-
-[移动办公]新增了移动端启动流程的列表配置
-
-[移动办公]新增了Android添加修改个人密码功能
-
-[平台API]新增了数据库表结构显示功能
-
-[平台工具]新增了Html转PDF工具接口
+[考勤管理]新增了工作地点的百度开发者账号设置
 
 功能优化
 
-[应用市场]应用市场整体升级,提供应用下载安装相关说明,提供对应用评论打分的功能
-
-[系统架构]服务器身份可以调用前端服务
-
-[系统架构]options响应头更新
-
-[系统架构]后台优先连接到本地服务器
-
-[系统架构]页面缓存更新
-
-[系统架构]默认mysql驱动由 com.mysql.jdbc.Driver 改为 com.mysql.cj.jdbc.Driver
-
-[通用功能]ACE编辑器从1.2.8升级到1.4.12
-
-[平台命令]创建encryptKey时增加please restart sever提示
-
-[平台API]ResetAction里增加了JaxrsDescribe注解
-
-[人员组织]新增人员组织属性导入
-
-[人员组织]人员相关选择组件增加保存精简数据的配置
-
-[人员组织]人员选择增加选项:当候选只有一个选项时,是否默认选中
-
-[人员组织]组织管理和个人设置增加字段[登录IP]
-
-[人员组织]批量查询用户、组织接口返回内容修改
-
-[人员组织]人员组织导入添加校验手机号有效性
-
-[流程平台]并行节点接拆分分支执行错误
-
-[流程平台]并行活动路由时,如果选择的路由数相同,那么优先执行数量相同的最晚路由
-
-[流程管理]内容管理、流程管理人员相关选择组件增加保存精简数据的配置
-
-[流程管理]流程重置处理人选择范围增加脚本
-
-[流程平台]简化待阅发送内容
-
-[流程平台]流程活动增加重置处理人脚本
-
-[流程平台]增加work中的活动到达时间数据存入item数据中,可用于视图查询
-
-[流程平台]流程审计日志补充
+[缓存]简化ETag计算,减少ETag计算开销
 
-[流程平台]优化已办查询,增加已办title字段搜索
+[服务器]ctl -hs 命令增加对center,application,web服务器的dump
 
-[流程平台]打开流程工作性能优化
+[流程平台]优化优先路由功能,增加立即执行配置项
 
-[表单设计]增加子表单延迟加载的功能
+[流程平台]增加在拆分合并状态下的拆分值的回滚
 
-[表单设计]表单样式允许使用脚本在前台自定义
+[流程平台]增加提交时人员选择的隐藏条件
 
-[表单设计]流程表单的组件校验脚本中增加了当前决策
+[流程平台]修改服务调用活动颜色,以免和流程监控的已流转过活动颜色过于接近
 
-[表单设计]增加表单中数据字典的缓存,重复使用不用每次都发起后台请求
+[流程平台]多人处理时,优先路由是立即执行还是所有人处理完成后执行,现在可以在流程配置时选择
 
-[表单设计]Fck编辑器图片上传增加保存为base64选项
+[流程平台]默认隐藏流程配置的高级属性
 
-[表单设计]流程记录增加加载每行记录的事件
+[流程平台]流程配置事件增加了有代码的提示标志
 
-[表单设计]数据网格增加允许添加允许删除选项
+[系统服务]接口脚本改由编译后缓存执行
 
-[内容管理]缓存系统升级,使用新的缓存写法,随平台一起支持Redis
+[数据库连接]增加数据库连接池借入借出的testConnection配置项
 
-[内容管理]新建内容管理应用,不自动添加当前处理人为管理员
+[服务器]ctl -td命令改由调用jcmd执行
 
-[服务管理]修改webserver增加几个类包升级到3.3.7
+[数据中心]优化自建表查询,允许自建表间关联查询、子查询、join查询
 
-[数据中心]优化视图查询,取消视图查询2000条限制
+[数据中心]自建表编译优化,支持集群同时编译
 
-[会议管理]允许会议申请发布后修改
+[数据中心]取消视图查询时默认时间范围1年限制
 
-[会议管理]优化会议签到二维码,修改二维码大小
+[数据中心]限制了自检表的表名为文本加日期
 
-[个人通讯录]base64编码解码优化个人通讯录,base64编码解码优化
+[数据中心、流程引擎、内容管理]操作条的系统操作和自定义操作可以混合排序
 
-[移动办公]更新语音助手功能
+[内容管理]缓存及权限刷新修改
 
-[移动办公]ios已办日志展现形式和UI改进优化
+[内容管理]增加了评论的编辑器设置
 
-[移动办公]更新改进扫码功能
+[人员组织]修改根据组织和职务名称查询身份信息接口(优化查询速度)
 
-[移动办公]会议签到二维码大小调整
+[人员组织]增加了组织中按照人员拼音排序的功能
 
-[移动办公]考勤打卡界面更新,使用钉钉类似的界面
+[门户管理]优化门户展现速度
 
-[移动办公]移动端聊天窗口上拉加载更多消息内容
-
-[移动办公]移动端聊天窗口下拉刷新消息内容
-
-[移动办公]移动端聊天语音消息上滑取消发送功能优化
-
-[移动办公]群发消息增加华为WeLink的类型
-
-[移动办公]Welink 消息提示功能完成
-
-[移动办公]im消息用户不在线发送app推送消息
-
-[移动办公]移动端页面支持xtoken query传入
+[云文件]支持管理员设置用户最大使用容量、可上传的文件类型限制
 
 问题修复
 
-[系统架构]修复了ScriptObjectMirror映射过程中嵌套循环的问题
-
-[平台API]修复了api接口界面附件上传报错的问题
-
-[平台API]修复了x_program_center load模块api脚本出错的问题
-
-[流程平台]修复了可调度人员权限判断错误的问题
-
-[流程平台]修复了在特定情况下编号脚本执行错误的问题
-
-[人员组织]修复了在某些情况下人员搜索不正确的问题
-
-[人员组织]修复了部分群组查询接口缺少身份信息的问题
-
-[人员组织]修复了钉钉同步员工编码的问题
-
-[系统架构]修复了在某此情况下Distinct 导致的排序的问题
-
-[流程平台]修复了一键下载标题带斜杠的问题
-
-[流程平台]修复了流程多版本的查询的问题
-
-[流程平台]修复了附件查询权限的问题
-
-[流程平台]修复了旧版本流程意见展现不全的问题
-
-[流程平台]修复了流程流转到取消环节报错的问题
-
-[流程平台]修复了删除流程未删除草稿的问题
-
-[流程平台]修复了意见过长,流程记录(record)显示的问题
-
-[流程平台]修复了脚本拷贝粘贴时,和设计元素的拷贝粘贴冲突的问题
-
-[表单设计]修复了日期选择组件在表单中跟随滚动条滚动的问题
-
-[表单设计]修复了某些情况下日期选择界面异常的问题
-
-[表单设计]修复了页面设置树组件无效的问题
-
-[表单设计]修复了textarea只读状态下内容有尖括号的时候内容被截断的问题
-
-[表单设计]修复了表单可选值插入报错的问题
-
-[内容管理]修复了在没有设置主身份时,内容管理和考勤管理中根据用户名查询主身份报错的问题
-
-[内容管理]修复了在某些情况下,分页数据排序不正确引起,分页功能不正常的问题
-
-[内容管理]修复了在某些情况下,发布文档时会通知所有人员的问题
-
-[内容管理]修复了在某些情况下排序索引列过长,无法正确处理权限的问题
-
-[人员选择]修复了人员选择双击分类可能重复加载的问题
-
-[内容管理]修复了模块中分类过长时不显示的问题
-
-[数据中心]修复了数据统计图表类型无法保存的问题
-
-[考勤管理]修复了考勤部门人员明细排除不参加考勤人员考勤部门人员明细排除不参加考勤人员的问题
-
-[考勤管理]修复了考勤统计结果-排除不参加考勤的人员及组织的问题
-
-[考勤管理]修复了部门考勤月报排除不参加考勤的组织和人员的问题
-
-[考勤管理]修复了移动打卡类型错误的问题
+[移动办公]修复了Android app 下载更新的bug
 
-[论坛管理]修复了论坛停用分区后,在导航中还是会显示的问题
+[移动办公]修复了Android app 指纹认证登录不成功的bug
 
-[论坛管理]修复了论坛回复数量不会实时更新的问题
+[移动办公]修复了Android app 会议处理页面的bug
 
-[会议管理]修复了serviceAPI枚举类型提交错误的问题
+[移动办公]修复了钉钉工作页面无法关闭的bug
 
-[移动办公]修复了IOS任务列表图标随机变化的问题
+[论坛管理]修复了内外网域名不同图片不能正确显示的bug
 
-[移动办公]修复了ios考勤统计列表排序的问题
+[数据库连接]修复了神州通用数据库Boolean映射错误的bug
 
-[移动办公]修复了了文档附件无法打开的问题
+[自定义表]修复了驼峰式命名单个小写字母转换的错误
 
-[移动办公]修复了IOS通讯录搜索框按钮看不到的问题
+[流程平台]修复了一键下载文件标题包含特殊字符、标题过长的问题
 
-[移动办公]修复了android sdk环境升级,developtools、Android support等官方库升级的问题
+[流程平台]修复了提交时选人可能重复出现人员的问题
 
-[移动办公]修复了Android流程启动身份选择样式的问题
+[流程平台]修复了关闭WORK页面时,有时beforeunload事件会执行两次的问题
 
-[移动办公]修复了IOS流程启动身份选择样式的问题
+[流程平台]修复了版式文件粘贴大段内容后容器高度异常问题
 
-[移动办公]修复了ios已办查询功能,修复多次下拉闪退的问题
+[流程平台]修复了数据网格中多行文本的显示问题
 
-[移动办公]修复了IOS打开结束工作错误的问题
+[应用中心]修复了导入应用覆盖时应用本身信息没有覆盖的问题
 
-[移动办公]修复了Android 8 下面的闪退的问题
+[内容管理]修复了附件update权限判断错误的问题
 
-[移动办公]修复了移动端消息没有消费的问题
+[数据中心]修复了数据中心应用删除时关联的子表数据未删除完全的问题
 
-[移动办公]修复了Android 10 图片选择不了的问题
+[系统服务]修复了代理模式代理url特殊字符比如|的问题
 
-[移动办公]修复了jpush配置文件名字错误的问题
+[平台首页]修复了首页日程安排标题显示重叠的问题
 
 
 # 配置编译环境\:

+ 7 - 0
gulpfile.js

@@ -803,6 +803,12 @@ function build_web_v_html() {
         .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_web_v_o2() {
     var src = 'target/o2server/servers/webServer/o2_core//o2.js';
     var dest = 'target/o2server/servers/webServer/o2_core/';
@@ -897,6 +903,7 @@ exports.build_web = gulp.series(
         build_bundle
     ),
     build_web_v_html,
+    build_web_api,
     build_web_v_o2);
 
 if (os.platform().indexOf("win")==-1){

+ 12 - 1
o2android/README.md

@@ -1,3 +1,14 @@
-#O2办公平台(O2OA)
+# 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)
+

+ 4 - 4
o2android/app/assets/html/huawei.html

@@ -45,7 +45,7 @@
             <span class="s2">操作步骤:</span>
             <span class="s3">设置-电池-锁屏清理应用,找到O2,关闭锁屏清理开关,参见下面的GIF动画:</span>
             <br>
-            <img alt="" src="http://muliba.u.qiniudn.com/o2/20170920/%E9%94%81%E5%B1%8F%E6%B8%85%E7%90%86.gif"
+            <img alt="" src="https://app.o2oa.net/o2/20170920/%E9%94%81%E5%B1%8F%E6%B8%85%E7%90%86.gif"
                  style="width: 100%;">
         </p>
         <p class="p2">&nbsp;</p>
@@ -56,7 +56,7 @@
             <span class="s2">操作步骤:</span>
             <span class="s3">找到手机管家-自启管理,找到O2并允许自启动,参见下面的GIF动画:</span>
             <br>
-            <img alt="" src="http://muliba.u.qiniudn.com/o2/20170920/%E5%BC%80%E5%90%AF%E8%87%AA%E5%90%AF%E5%8A%A8.gif"
+            <img alt="" src="https://app.o2oa.net/o2/20170920/%E5%BC%80%E5%90%AF%E8%87%AA%E5%90%AF%E5%8A%A8.gif"
                  style="width: 100%;">
         </p>
         <p class="p2">&nbsp;</p>
@@ -64,7 +64,7 @@
         <p class="p1">
             <span class="s2">操作步骤:</span>
             <span class="s3">设置-通知和状态栏-通知管理,找到O2-开启允许通知及其他开关。参见下面的GIF动画:</span>
-            <br><img alt="" src="http://muliba.u.qiniudn.com/o2/20170920/%E9%80%9A%E7%9F%A5%E8%AE%BE%E7%BD%AE.gif"
+            <br><img alt="" src="https://app.o2oa.net/o2/20170920/%E9%80%9A%E7%9F%A5%E8%AE%BE%E7%BD%AE.gif"
                      style="width: 100%;">
         </p>
         <p class="p2">&nbsp;</p>
@@ -72,7 +72,7 @@
         <p class="p1">
             <span class="s2">操作步骤:</span>
             <span class="s3">点击手机右下角的功能键-进入多任务页面-找到O2,点击O2右上角锁图标锁定。参见下面的GIF动画:</span>
-            <br><img alt="" src="http://muliba.u.qiniudn.com/o2/20170920/%E5%A4%9A%E4%BB%BB%E5%8A%A1.gif"
+            <br><img alt="" src="https://app.o2oa.net/o2/20170920/%E5%A4%9A%E4%BB%BB%E5%8A%A1.gif"
                      style="width: 100%;">
         </p>
         <p class="p2">&nbsp;</p>

+ 4 - 4
o2android/app/assets/html/xiaomi.html

@@ -43,7 +43,7 @@
             <span class="s2">操作步骤:</span>
             <span class="s3">手机找到并点击 安全中心-授权管理-自启动管理-找到O2开启开关。参见下面的GIF动画:</span>
             <br>
-            <img alt="" src="http://muliba.u.qiniudn.com/o2/20170921/%E5%B0%8F%E7%B1%B3%E8%87%AA%E5%90%AF%E5%8A%A8.gif"
+            <img alt="" src="https://app.o2oa.net/o2/20170921/%E5%B0%8F%E7%B1%B3%E8%87%AA%E5%90%AF%E5%8A%A8.gif"
                  style="width: 100%;">
         </p>
         <p class="p2">&nbsp;</p>
@@ -51,7 +51,7 @@
         <p class="p1">
             <span class="s2">操作步骤:</span>
             <span class="s3">手机设置-电量和性能-神隐模式-应用配置-找到O2-点击无限制并允许定位。参见下面的GIF动画:</span>
-            <br><img alt="" src="http://muliba.u.qiniudn.com/o2/20170921/%E5%B0%8F%E7%B1%B3%E7%A5%9E%E9%9A%90%E6%A8%A1%E5%BC%8F.gif"
+            <br><img alt="" src="https://app.o2oa.net/o2/20170921/%E5%B0%8F%E7%B1%B3%E7%A5%9E%E9%9A%90%E6%A8%A1%E5%BC%8F.gif"
                      style="width: 100%;">
         </p>
         <p class="p2">&nbsp;</p>
@@ -59,7 +59,7 @@
         <p class="p1">
             <span class="s2">操作步骤:</span>
             <span class="s3">手机设置-通知和状态栏-通知管理-找到O2-开启允许通知和其他选项。参见下面的GIF动画:</span>
-            <br><img alt="" src="http://muliba.u.qiniudn.com/o2/20170921/%E5%B0%8F%E7%B1%B3%E9%80%9A%E7%9F%A5%E8%AE%BE%E7%BD%AE.gif"
+            <br><img alt="" src="https://app.o2oa.net/o2/20170921/%E5%B0%8F%E7%B1%B3%E9%80%9A%E7%9F%A5%E8%AE%BE%E7%BD%AE.gif"
                      style="width: 100%;">
         </p>
         <p class="p2">&nbsp;</p>
@@ -67,7 +67,7 @@
         <p class="p1">
             <span class="s2">操作步骤:</span>
             <span class="s3">点击手机左下角的功能键-进入多任务页面-找到O2-拖曳O2下滑-点击锁定任务。参见下面的GIF动画:</span>
-            <br><img alt="" src="http://muliba.u.qiniudn.com/o2/20170921/%E5%B0%8F%E7%B1%B3%E5%A4%9A%E4%BB%BB%E5%8A%A1.gif"
+            <br><img alt="" src="https://app.o2oa.net/o2/20170921/%E5%B0%8F%E7%B1%B3%E5%A4%9A%E4%BB%BB%E5%8A%A1.gif"
                      style="width: 100%;">
         </p>
         <p class="p2">&nbsp;</p>

+ 3 - 1
o2android/app/build.gradle

@@ -238,7 +238,9 @@ dependencies {
     implementation 'com.readystatesoftware.systembartint:systembartint:1.0.3'
     implementation 'com.facebook.shimmer:shimmer:0.1.0@aar'
     implementation 'com.borax12.materialdaterangepicker:library:1.9'
-    implementation 'com.yanzhenjie:recyclerview-swipe:1.1.4'
+//    implementation 'com.yanzhenjie:recyclerview-swipe:1.1.4'
+    //d
+    implementation 'com.yanzhenjie.recyclerview:x:1.3.2'
     implementation 'com.race604.waveloading:library:1.1.1'
 
     implementation 'com.squareup.retrofit2:retrofit:2.4.0'

+ 2 - 2
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/meeting/apply/MeetingApplyActivity.kt

@@ -200,7 +200,7 @@ class MeetingApplyActivity : BaseMVPActivity<MeetingApplyContract.View, MeetingA
                 info.subject = subject
                 info.startTime = "$startDay $startTime:00"
                 info.completedTime = "$startDay $endTime:00"
-                info.description = edit_meeting_create_form_desc.text.toString()
+                info.summary = edit_meeting_create_form_desc.text.toString()
                 val savePersonList = invitePersonList
                 savePersonList.remove(invitePersonAdd)
                 info.invitePersonList = savePersonList
@@ -344,7 +344,7 @@ class MeetingApplyActivity : BaseMVPActivity<MeetingApplyContract.View, MeetingA
             info.subject = subject
             info.startTime = "$startDay $startTime:00"
             info.completedTime = "$startDay $endTime:00"
-            info.description = edit_meeting_create_form_desc.text.toString()
+            info.summary = edit_meeting_create_form_desc.text.toString()
             val savePersonList = invitePersonList
             savePersonList.remove(invitePersonAdd)
             info.invitePersonList = savePersonList

+ 2 - 2
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/meeting/edit/MeetingEditActivity.kt

@@ -81,7 +81,7 @@ class MeetingEditActivity : BaseMVPActivity<MeetingEditContract.View, MeetingEdi
         edit_meeting_edit_form_start_day.text = startDay
         edit_meeting_edit_form_start_time.text = startTime
         edit_meeting_edit_form_end_time.text = completeTime
-        edit_meeting_edit_form_desc.setText(meeting.description)
+        edit_meeting_edit_form_desc.setText(meeting.summary)
 
         meetingFileList.addAll(meeting.attachmentList)
         recycler_meeting_edit_form_file_list.layoutManager = LinearLayoutManager(this)
@@ -219,7 +219,7 @@ class MeetingEditActivity : BaseMVPActivity<MeetingEditContract.View, MeetingEdi
             return
         }
         meeting.subject = subject
-        meeting.description = edit_meeting_edit_form_desc.text.toString()
+        meeting.summary = edit_meeting_edit_form_desc.text.toString()
         val savePersonList = invitePersonList
         savePersonList.remove(invitePersonAdd)
         meeting.invitePersonList = savePersonList

+ 1 - 1
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/meeting/invited/MeetingDetailInfoActivity.kt

@@ -53,7 +53,7 @@ class MeetingDetailInfoActivity : BaseMVPActivity<MeetingDetailInfoContract.View
         edit_meeting_invited_start_day.text = meetingDetailInfo.startTime.substring(0,10)
         edit_meeting_time.text = meetingDetailInfo.startTime.substring(11,16)+"-"+meetingDetailInfo.completedTime.substring(11,16)
         meeting_people_sum.text = Integer.toString(meetingDetailInfo.invitePersonList.size+1)+"人"
-        edit_meeting_create_form_desc.text = meetingDetailInfo.description
+        edit_meeting_create_form_desc.text = meetingDetailInfo.summary
         mPresenter.asyncLoadRoomName(edit_meeting_invited_room,meetingDetailInfo.room)
 
         meetingFileList.addAll(meetingDetailInfo.attachmentList)

+ 12 - 11
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/app/meeting/invited/MeetingInvitedFragment.kt

@@ -2,14 +2,14 @@ package net.zoneland.x.bpm.mobile.v1.zoneXBPM.app.meeting.invited
 
 import android.graphics.Color
 import android.graphics.drawable.ColorDrawable
-import androidx.core.content.ContextCompat
-import androidx.recyclerview.widget.LinearLayoutManager
 import android.view.View
 import android.view.ViewGroup
 import android.widget.TextView
-import com.yanzhenjie.recyclerview.swipe.SwipeMenuCreator
-import com.yanzhenjie.recyclerview.swipe.SwipeMenuItem
-import com.yanzhenjie.recyclerview.swipe.widget.DefaultItemDecoration
+import androidx.core.content.ContextCompat
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.yanzhenjie.recyclerview.SwipeMenuCreator
+import com.yanzhenjie.recyclerview.SwipeMenuItem
+import com.yanzhenjie.recyclerview.widget.DefaultItemDecoration
 import kotlinx.android.synthetic.main.fragment_meeting_invited.*
 import net.muliba.changeskin.FancySkinManager
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
@@ -70,16 +70,17 @@ class MeetingInvitedFragment : BaseMVPViewPagerFragment<MeetingInvitedFragmentCo
         receive_invite_list.layoutManager = LinearLayoutManager(activity)
         receive_invite_list.addItemDecoration(DefaultItemDecoration(ContextCompat.getColor(activity!!, R.color.z_color_split_line_ddd)))
         receive_invite_list.setSwipeMenuCreator(menuCreator)
-        receive_invite_list.setSwipeMenuItemClickListener { menuBridge ->
+        receive_invite_list.setOnItemMenuClickListener { menuBridge, adapterPosition ->
             menuBridge.closeMenu()// 任何操作必须先关闭菜单,否则可能出现Item菜单打开状态错乱。
-            val menuPosition = menuBridge.position
-            val itemPosition = menuBridge.adapterPosition
-            when (menuPosition) {
+            // 左侧还是右侧菜单:
+//            val direction = menuBridge.direction
+            // 菜单在Item中的Position:
+            when (menuBridge.position) {
                 0 -> {
-                    mPresenter.acceptMeetingInvited(receiveInviteList[itemPosition].id)
+                    mPresenter.acceptMeetingInvited(receiveInviteList[adapterPosition].id)
                 }
                 1 -> {
-                    mPresenter.rejectMeetingInvited(receiveInviteList[itemPosition].id)
+                    mPresenter.rejectMeetingInvited(receiveInviteList[adapterPosition].id)
                 }
             }
         }

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

@@ -172,15 +172,15 @@ class AccountSecurityActivity : BaseMVPActivity<AccountSecurityContract.View, Ac
                 }
             }
         }
-        val userId = if(isAuthed)  "" else unitId+"^^"+O2SDKManager.instance().cId
+        val userId = if(isAuthed)  "" else unitId+"^^"+O2SDKManager.instance().distinguishedName
 
         O2SDKManager.instance().prefs().edit{
             putString(BioConstants.O2_bio_auth_user_id_prefs_key, userId)
         }
         if (isAuthed) {
-            image_btn_account_security_biometry_enable.setImageResource(R.mipmap.icon_toggle_on_29dp)
-        }else {
             image_btn_account_security_biometry_enable.setImageResource(R.mipmap.icon_toggle_off_29dp)
+        }else {
+            image_btn_account_security_biometry_enable.setImageResource(R.mipmap.icon_toggle_on_29dp)
         }
 
     }

+ 32 - 39
o2android/app/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/core/service/DownloadAPKService.kt

@@ -1,27 +1,18 @@
 package net.zoneland.x.bpm.mobile.v1.zoneXBPM.core.service
 
 import android.app.IntentService
-import android.app.Notification
-import android.app.NotificationManager
-import android.app.PendingIntent
-import android.content.Context
 import android.content.Intent
 import android.os.Handler
 import android.os.Looper
 import android.os.Message
-import androidx.core.app.NotificationCompat
-import android.widget.RemoteViews
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.R
+import androidx.localbroadcastmanager.content.LocalBroadcastManager
+import net.zoneland.x.bpm.mobile.v1.zoneXBPM.BuildConfig
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.FileUtil
 import net.zoneland.x.bpm.mobile.v1.zoneXBPM.utils.XLog
 import java.io.File
 import java.io.FileOutputStream
 import java.net.HttpURLConnection
 import java.net.URL
-import android.app.NotificationChannel
-import android.os.Build
-import androidx.localbroadcastmanager.content.LocalBroadcastManager
-import net.zoneland.x.bpm.mobile.v1.zoneXBPM.BuildConfig
 
 
 /**
@@ -89,41 +80,43 @@ class DownloadAPKService : IntentService("DownloadAPKService") {
         val downloadUrl = intent.getStringExtra(DOWNLOAD_URL_EXTRA_NAME)
         val file = File(FileUtil.appExternalCacheDir(applicationContext)?.absolutePath + File.separator + versionName + ".apk")
         return try {
-            if (!file.exists()) {
-                val url = URL(downloadUrl)
-                val conn = url.openConnection() as HttpURLConnection
-                conn.setRequestProperty("Accept-Encoding", "identity")
-                conn.connect()
-                val length = conn.contentLength
-                val inputStream = conn.inputStream
-                val fos = FileOutputStream(file, true)
-                var oldProgress = 0
-                val buf = ByteArray(1024 * 8)
-                var currentLength = 0
-                while (true) {
-                    val num = inputStream.read(buf)
-                    currentLength += num
-                    // 计算进度条位置
-                    val progress = (currentLength / length.toFloat() * 100).toInt()
-                    if (progress > oldProgress) {
-                        updateMessage(progress)
-                        oldProgress = progress
-                    }
-                    if (num <= 0) {
-                        break
-                    }
-                    fos.write(buf, 0, num)
-                    fos.flush()
+            if (file.exists()) {
+                file.delete()
+            }
+            val url = URL(downloadUrl)
+            val conn = url.openConnection() as HttpURLConnection
+            conn.setRequestProperty("Accept-Encoding", "identity")
+            conn.connect()
+            val length = conn.contentLength
+            val inputStream = conn.inputStream
+            val fos = FileOutputStream(file, true)
+            var oldProgress = 0
+            val buf = ByteArray(1024 * 8)
+            var currentLength = 0
+            while (true) {
+                val num = inputStream.read(buf)
+                currentLength += num
+                // 计算进度条位置
+                val progress = (currentLength / length.toFloat() * 100).toInt()
+                if (progress > oldProgress) {
+                    updateMessage(progress)
+                    oldProgress = progress
+                }
+                if (num <= 0) {
+                    break
                 }
+                fos.write(buf, 0, num)
                 fos.flush()
-                fos.close()
-                inputStream.close()
             }
+            fos.flush()
+            fos.close()
+            inputStream.close()
+
             file
         } catch (e: Exception) {
             XLog.error("下载应用安装包失败", e)
             try {
-                file.deleteOnExit()
+                file.delete()
             } catch (e: Exception) {
             }
             null

+ 1 - 1
o2android/app/src/main/res/layout/fragment_meeting_invited.xml

@@ -95,7 +95,7 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"/>
 
-            <com.yanzhenjie.recyclerview.swipe.SwipeMenuRecyclerView
+            <com.yanzhenjie.recyclerview.SwipeRecyclerView
                 android:id="@+id/receive_invite_list"
                 android:background="@android:color/white"
                 android:layout_width="match_parent"

+ 2 - 2
o2android/gradle.properties

@@ -23,6 +23,6 @@ android.enableJetifier=true
 
 
 # o2
-o2.versionName=5.2.0
-o2.versionCode=120
+o2.versionName=5.2.1
+o2.versionCode=121
 

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

@@ -219,7 +219,7 @@ class APIAddressHelper private constructor() {
      */
     fun getPortalWebViewUrl(portalId:String): String {
         return webServerData?.let {
-            "$httpHead${webServerData?.host}:${webServerData?.port}/x_desktop/appMobile.html?app=portal.Portal&status={\"portalId\":\"$portalId\"}"
+            "$httpHead${webServerData?.host}:${webServerData?.port}/x_desktop/portalmobile.html?id=$portalId"
         } ?: ""
     }
 

+ 1 - 1
o2android/o2_auth_sdk/src/main/java/net/zoneland/x/bpm/mobile/v1/zoneXBPM/model/bo/api/meeting/MeetingJSON.kt

@@ -19,7 +19,7 @@ data class MeetingInfoJson(
         var pinyinInitial: String = "",
         var id: String = "",
         var subject: String = "",//名称
-        var description: String = "",//说明
+        var summary: String = "",//说明
         var room: String = "",//会议室
         var date: String = "",//会议日期
         var startTime: String = "",//开始日期

+ 134 - 1
o2ios/O2Platform.xcodeproj/project.pbxproj

@@ -205,6 +205,7 @@
 		B168BF4724F798790070329F /* libz.1.2.5.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = B168BF4624F7986C0070329F /* libz.1.2.5.tbd */; };
 		B169E3D421095EC9007156B3 /* Date+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B169E3D321095EC9007156B3 /* Date+Extension.swift */; };
 		B17126C121084A2F00369F15 /* calendar.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B17126C021084A2F00369F15 /* calendar.storyboard */; };
+		B1737EF525402E0100B487D6 /* O2PlatformTests1.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1737EF425402E0100B487D6 /* O2PlatformTests1.swift */; };
 		B1750078233C6908003DA7B9 /* BlockPan.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1750072233C6907003DA7B9 /* BlockPan.swift */; };
 		B1750079233C6908003DA7B9 /* BlockSwipe.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1750073233C6907003DA7B9 /* BlockSwipe.swift */; };
 		B175007A233C6908003DA7B9 /* BlockLongPress.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1750074233C6908003DA7B9 /* BlockLongPress.swift */; };
@@ -1086,6 +1087,13 @@
 			remoteGlobalIDString = AEBB0C4F19DFB746403D40BC540276A3;
 			remoteInfo = JTCalendar;
 		};
+		B1737EF725402E0100B487D6 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = E4B8870B1D9D477A002E1A46 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = E4B887121D9D477A002E1A46;
+			remoteInfo = O2Platform;
+		};
 		B1AE4E6321A3F53A00183FCD /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			containerPortal = E4E7755421017DB4006ED7FC /* Pods.xcodeproj */;
@@ -1379,6 +1387,9 @@
 		B168BF4624F7986C0070329F /* libz.1.2.5.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.1.2.5.tbd; path = usr/lib/libz.1.2.5.tbd; sourceTree = SDKROOT; };
 		B169E3D321095EC9007156B3 /* Date+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extension.swift"; sourceTree = "<group>"; };
 		B17126C021084A2F00369F15 /* calendar.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = calendar.storyboard; sourceTree = "<group>"; };
+		B1737EF225402E0100B487D6 /* O2PlatformTests1.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = O2PlatformTests1.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		B1737EF425402E0100B487D6 /* O2PlatformTests1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = O2PlatformTests1.swift; sourceTree = "<group>"; };
+		B1737EF625402E0100B487D6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		B1750072233C6907003DA7B9 /* BlockPan.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockPan.swift; sourceTree = "<group>"; };
 		B1750073233C6907003DA7B9 /* BlockSwipe.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockSwipe.swift; sourceTree = "<group>"; };
 		B1750074233C6908003DA7B9 /* BlockLongPress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockLongPress.swift; sourceTree = "<group>"; };
@@ -2059,6 +2070,13 @@
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
+		B1737EEF25402E0100B487D6 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		E4B887101D9D477A002E1A46 /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
@@ -2597,6 +2615,15 @@
 			path = BDSClientLib;
 			sourceTree = "<group>";
 		};
+		B1737EF325402E0100B487D6 /* O2PlatformTests1 */ = {
+			isa = PBXGroup;
+			children = (
+				B1737EF425402E0100B487D6 /* O2PlatformTests1.swift */,
+				B1737EF625402E0100B487D6 /* Info.plist */,
+			);
+			path = O2PlatformTests1;
+			sourceTree = "<group>";
+		};
 		B18FFF3323756B24001B2887 /* JpushAPI */ = {
 			isa = PBXGroup;
 			children = (
@@ -3585,6 +3612,7 @@
 				E4E7755421017DB4006ED7FC /* Pods.xcodeproj */,
 				E4B889061D9D4C7E002E1A46 /* podfile */,
 				E4B887151D9D477A002E1A46 /* O2Platform */,
+				B1737EF325402E0100B487D6 /* O2PlatformTests1 */,
 				E4B887141D9D477A002E1A46 /* Products */,
 				6FDEB623AC8AB47096221625 /* Pods */,
 				E034A62CD4B3425CCABD0B19 /* Frameworks */,
@@ -3597,6 +3625,7 @@
 				E4B887131D9D477A002E1A46 /* O2Platform.app */,
 				E4B887271D9D477A002E1A46 /* O2PlatformTests.xctest */,
 				E4B887321D9D477A002E1A46 /* O2PlatformUITests.xctest */,
+				B1737EF225402E0100B487D6 /* O2PlatformTests1.xctest */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -4326,6 +4355,24 @@
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
+		B1737EF125402E0100B487D6 /* O2PlatformTests1 */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = B1737F2925402E0100B487D6 /* Build configuration list for PBXNativeTarget "O2PlatformTests1" */;
+			buildPhases = (
+				B1737EEE25402E0100B487D6 /* Sources */,
+				B1737EEF25402E0100B487D6 /* Frameworks */,
+				B1737EF025402E0100B487D6 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				B1737EF825402E0100B487D6 /* PBXTargetDependency */,
+			);
+			name = O2PlatformTests1;
+			productName = O2PlatformTests1;
+			productReference = B1737EF225402E0100B487D6 /* O2PlatformTests1.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
 		E4B887121D9D477A002E1A46 /* O2Platform */ = {
 			isa = PBXNativeTarget;
 			buildConfigurationList = E4B8873B1D9D477A002E1A46 /* Build configuration list for PBXNativeTarget "O2Platform" */;
@@ -4391,10 +4438,17 @@
 		E4B8870B1D9D477A002E1A46 /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastSwiftUpdateCheck = 0930;
+				DefaultBuildSystemTypeForWorkspace = Original;
+				LastSwiftUpdateCheck = 1200;
 				LastUpgradeCheck = 0930;
 				ORGANIZATIONNAME = zoneland;
 				TargetAttributes = {
+					B1737EF125402E0100B487D6 = {
+						CreatedOnToolsVersion = 12.0;
+						DevelopmentTeam = NYCN9K3RXF;
+						ProvisioningStyle = Automatic;
+						TestTargetID = E4B887121D9D477A002E1A46;
+					};
 					E4B887121D9D477A002E1A46 = {
 						CreatedOnToolsVersion = 8.0;
 						DevelopmentTeam = NTDRU2P6T4;
@@ -4451,6 +4505,7 @@
 				E4B887121D9D477A002E1A46 /* O2Platform */,
 				E4B887261D9D477A002E1A46 /* O2PlatformTests */,
 				E4B887311D9D477A002E1A46 /* O2PlatformUITests */,
+				B1737EF125402E0100B487D6 /* O2PlatformTests1 */,
 			);
 		};
 /* End PBXProject section */
@@ -4739,6 +4794,13 @@
 /* End PBXReferenceProxy section */
 
 /* Begin PBXResourcesBuildPhase section */
+		B1737EF025402E0100B487D6 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		E4B887111D9D477A002E1A46 /* Resources */ = {
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -5118,6 +5180,14 @@
 /* End PBXShellScriptBuildPhase section */
 
 /* Begin PBXSourcesBuildPhase section */
+		B1737EEE25402E0100B487D6 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				B1737EF525402E0100B487D6 /* O2PlatformTests1.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		E4B8870F1D9D477A002E1A46 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -5708,6 +5778,11 @@
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXTargetDependency section */
+		B1737EF825402E0100B487D6 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = E4B887121D9D477A002E1A46 /* O2Platform */;
+			targetProxy = B1737EF725402E0100B487D6 /* PBXContainerItemProxy */;
+		};
 		E4B887291D9D477A002E1A46 /* PBXTargetDependency */ = {
 			isa = PBXTargetDependency;
 			target = E4B887121D9D477A002E1A46 /* O2Platform */;
@@ -5747,6 +5822,55 @@
 /* End PBXVariantGroup section */
 
 /* Begin XCBuildConfiguration section */
+		B1737EF925402E0100B487D6 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_TEAM = NYCN9K3RXF;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				INFOPLIST_FILE = O2PlatformTests1/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+				MTL_FAST_MATH = YES;
+				PRODUCT_BUNDLE_IDENTIFIER = net.muliba.O2PlatformTests1;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/O2Platform.app/O2Platform";
+			};
+			name = Debug;
+		};
+		B1737EFA25402E0100B487D6 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_TEAM = NYCN9K3RXF;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				INFOPLIST_FILE = O2PlatformTests1/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				MTL_FAST_MATH = YES;
+				PRODUCT_BUNDLE_IDENTIFIER = net.muliba.O2PlatformTests1;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/O2Platform.app/O2Platform";
+			};
+			name = Release;
+		};
 		E4B887391D9D477A002E1A46 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
@@ -6162,6 +6286,15 @@
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
+		B1737F2925402E0100B487D6 /* Build configuration list for PBXNativeTarget "O2PlatformTests1" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				B1737EF925402E0100B487D6 /* Debug */,
+				B1737EFA25402E0100B487D6 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 		E4B8870E1D9D477A002E1A46 /* Build configuration list for PBXProject "O2Platform" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (

+ 10 - 0
o2ios/O2Platform.xcodeproj/xcshareddata/xcschemes/O2Platform.xcscheme

@@ -48,6 +48,16 @@
                ReferencedContainer = "container:O2Platform.xcodeproj">
             </BuildableReference>
          </TestableReference>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "B1737EF125402E0100B487D6"
+               BuildableName = "O2PlatformTests1.xctest"
+               BlueprintName = "O2PlatformTests1"
+               ReferencedContainer = "container:O2Platform.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
       </Testables>
    </TestAction>
    <LaunchAction

BIN
o2ios/O2Platform/Framework/O2OA_Auth_SDK.framework/Info.plist


BIN
o2ios/O2Platform/Framework/O2OA_Auth_SDK.framework/Modules/O2OA_Auth_SDK.swiftmodule/Project/arm.swiftsourceinfo


BIN
o2ios/O2Platform/Framework/O2OA_Auth_SDK.framework/Modules/O2OA_Auth_SDK.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo


BIN
o2ios/O2Platform/Framework/O2OA_Auth_SDK.framework/Modules/O2OA_Auth_SDK.swiftmodule/Project/arm64.swiftsourceinfo


BIN
o2ios/O2Platform/Framework/O2OA_Auth_SDK.framework/Modules/O2OA_Auth_SDK.swiftmodule/Project/armv7-apple-ios.swiftsourceinfo


BIN
o2ios/O2Platform/Framework/O2OA_Auth_SDK.framework/Modules/O2OA_Auth_SDK.swiftmodule/Project/armv7.swiftsourceinfo


BIN
o2ios/O2Platform/Framework/O2OA_Auth_SDK.framework/O2OA_Auth_SDK


+ 2 - 2
o2ios/O2Platform/Info.plist

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

+ 22 - 0
o2ios/O2PlatformTests1/Info.plist

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>

+ 34 - 0
o2ios/O2PlatformTests1/O2PlatformTests1.swift

@@ -0,0 +1,34 @@
+//
+//  O2PlatformTests1.swift
+//  O2PlatformTests1
+//
+//  Created by FancyLou on 2020/10/21.
+//  Copyright © 2020 zoneland. All rights reserved.
+//
+
+import XCTest
+
+
+class O2PlatformTests1: XCTestCase {
+
+    override func setUpWithError() throws {
+        // Put setup code here. This method is called before the invocation of each test method in the class.
+    }
+
+    override func tearDownWithError() throws {
+        // Put teardown code here. This method is called after the invocation of each test method in the class.
+    }
+
+    func testExample() throws {
+        // This is an example of a functional test case.
+        // Use XCTAssert and related functions to verify your tests produce the correct results.
+    }
+
+    func testPerformanceExample() throws {
+        // This is an example of a performance test case.
+        measure {
+            // Put the code you want to measure the time of here.
+        }
+    }
+
+}

+ 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)
+

+ 0 - 10
o2server/configSample/appStyle.json

@@ -1,10 +0,0 @@
-{
-  "indexType": "default",
-  "indexPortal": "",
-  "nativeAppList": [],
-  "images": [],
-  "###indexType": "首页展现类型,default是移动端原来的首页,portal是门户.###",
-  "###indexPortal": "门户首页.###",
-  "###nativeAppList": "导航设置###",
-  "###images": "图片设置.###"
-}

+ 0 - 28
o2server/configSample/cache.json

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

+ 0 - 33
o2server/configSample/centerServer.json

@@ -1,33 +0,0 @@
-{
-  "enable": true,
-  "order": 0.0,
-  "sslEnable": false,
-  "redeploy": true,
-  "port": 20030.0,
-  "httpProtocol": "",
-  "proxyHost": "",
-  "proxyPort": 20030.0,
-  "scanInterval": 0.0,
-  "configApiEnable": true,
-  "statEnable": true,
-  "statExclusions": "*.js,*.gif,*.jpg,*.png,*.css,*.ico",
-  "maxFormContent": 20.0,
-  "exposeJest": true,
-  "persistentConnectionsEnable": true,
-  "###enable": "是否启用###",
-  "###order": "center节点顺序,顺序排列0,1,2...###",
-  "###sslEnable": "是否启用ssl传输加密,如果启用将使用config/keystore文件作为密钥文件.使用config/token.json文件中的sslKeyStorePassword字段为密钥密码,sslKeyManagerPassword为管理密码.###",
-  "###redeploy": "每次启动是否重新部署所有应用.###",
-  "###port": "端口,center服务器端口,默认20030###",
-  "###httpProtocol": "对外http访问协议,http/https###",
-  "###proxyHost": "代理主机,当服务器是通过apache/nginx等代理服务器映射到公网或者通过路由器做端口映射,在这样的情况下需要设置此地址以标明公网访问地址.###",
-  "###proxyPort": "代理端口,当服务器是通过apache/nginx等代理服务器映射到公网或者通过路由器做端口映射,在这样的情况下需要设置此地址以标明公网访问端口.###",
-  "###scanInterval": "重新扫描war包时间间隔(秒)###",
-  "###config": "其他参数###",
-  "###configApiEnable": "允许通过Api修改config###",
-  "###statEnable": "启用统计,默认启用统计.###",
-  "###statExclusions": "统计忽略路径,默认忽略*.js,*.gif,*.jpg,*.png,*.css,*.ico###",
-  "###maxFormContent": "最大提交数据限制(M),限制有所上传的内容大小,包括附件.###",
-  "###exposeJest": "暴露jest接口.###",
-  "###persistentConnectionsEnable": "是否启用长连接,默认false.###"
-}

+ 0 - 21
o2server/configSample/clientInit.json

@@ -1,21 +0,0 @@
-{
-  "enable": false,
-  "center": [
-    {}
-  ],
-  "footer": "",
-  "title": "",
-  "app_protocol": "auto",
-  "loginPage": {
-    "###enable": "是否启用定制的登录页面.###",
-    "###portal": "登录的门户.###",
-    "###page": "登录页面.###"
-  },
-  "###enable": "是否启用.###",
-  "###center": "center节点信息.###",
-  "###footer": "网页底部说明.###",
-  "###title": "网页头部说明.###",
-  "###app_protocol": "APP使用协议,auto,http,https.###",
-  "###loginPage": "登录页面配置.###",
-  "###webSocketEnable": "是否启用webSocket###"
-}

+ 0 - 22
o2server/configSample/collect.json

@@ -1,22 +0,0 @@
-{
-  "enable": false,
-  "name": "",
-  "password": "",
-  "title": "",
-  "footer": "",
-  "appUrl": "",
-  "server": "",
-  "port": 20080.0,
-  "sslEnable": false,
-  "###enable": "是否启用连接到云平台###",
-  "###name": "云平台账户名称,同时显示在登录页面底部.###",
-  "###password": "云平台密码###",
-  "###title": "系统标题,同时显示在登录页面上部.###",
-  "###footer": "底部申明###",
-  "###appUrl": "app下载地址###",
-  "###server": "云平台服务器地址###",
-  "###port": "云平台端口###",
-  "###sslEnable": "云平台连接是否启用ssl###",
-  "###secret": "推送消息secret###",
-  "###key": "推送消息key###"
-}

+ 0 - 24
o2server/configSample/communicate.json

@@ -1,24 +0,0 @@
-{
-  "wsEnable": true,
-  "pmsEnable": true,
-  "calendarEnable": true,
-  "###wsEnable": "是否启用ws消息.###",
-  "###pmsEnable": "是否启用pms消息.###",
-  "###calendarEnable": "是否启用calendar消息.###",
-  "###cronMq": "定时触发发送到消息队列MQ.###",
-  "cronMq": {
-    "enable": false,
-    "cron": "0 0 * * * ? *",
-    "###enable": "是否启用###",
-    "###cron": "定时cron表达式###"
-  },
-  "###clean": "清理设置.###",
-  "clean": {
-    "enable": true,
-    "cron": "30 30 6 * * ?",
-    "keep": 7.0,
-    "###enable": "是否启用###",
-    "###cron": "定时cron表达式###",
-    "###keep": "消息保留天数###"
-  }
-}

+ 0 - 255
o2server/configSample/components.json

@@ -1,255 +0,0 @@
-{
-  "systems": [
-    {
-      "name": "Setting",
-      "path": "Setting",
-      "title": "系统设置",
-      "iconPath": "appicon.png",
-      "orderNumber": 1.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "Org",
-      "path": "Org",
-      "title": "组织管理",
-      "iconPath": "appicon.png",
-      "orderNumber": 2.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "cmsManager",
-      "path": "cms.Column",
-      "title": "内容管理平台",
-      "iconPath": "appicon.png",
-      "orderNumber": 3.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "cms",
-      "path": "cms.Index",
-      "title": "信息平台",
-      "iconPath": "appicon.png",
-      "orderNumber": 12.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "ApplicationExplorer",
-      "path": "process.ApplicationExplorer",
-      "title": "流程管理平台",
-      "iconPath": "appicon.png",
-      "orderNumber": 4.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "PortalExplorer",
-      "path": "portal.PortalExplorer",
-      "title": "门户管理平台",
-      "iconPath": "appicon.png",
-      "orderNumber": 5.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "DataExplorer",
-      "path": "query.QueryExplorer",
-      "title": "数据中心平台",
-      "iconPath": "appicon.png",
-      "orderNumber": 6.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "service.ServiceManager",
-      "path": "service.ServiceManager",
-      "title": "服务管理平台",
-      "iconPath": "appicon.png",
-      "orderNumber": 7.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "AppMarketV2",
-      "path": "AppMarketV2",
-      "title": "应用市场",
-      "iconPath": "appicon.png",
-      "orderNumber": 8.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "AppCenter",
-      "path": "AppCenter",
-      "title": "应用管理",
-      "iconPath": "appicon.png",
-      "orderNumber": 9.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "LogViewer",
-      "path": "LogViewer",
-      "title": "日志",
-      "iconPath": "appicon.png",
-      "orderNumber": 10.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "Profile",
-      "path": "Profile",
-      "title": "个人设置",
-      "iconPath": "appicon.png",
-      "orderNumber": 11.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "BAM",
-      "path": "BAM",
-      "title": "流程监控",
-      "iconPath": "appicon.png",
-      "orderNumber": 12.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "TaskCenter",
-      "path": "process.TaskCenter",
-      "title": "办公中心",
-      "iconPath": "appicon.png",
-      "orderNumber": 13.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "Homepage",
-      "path": "Homepage",
-      "title": "首页",
-      "iconPath": "appicon.png",
-      "orderNumber": 14.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "HotArticle",
-      "path": "HotArticle",
-      "title": "热点",
-      "iconPath": "appicon.png",
-      "orderNumber": 15.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "Note",
-      "path": "Note",
-      "title": "便签",
-      "iconPath": "appicon.png",
-      "orderNumber": 17.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "Meeting",
-      "path": "Meeting",
-      "title": "会议管理",
-      "iconPath": "appicon.png",
-      "orderNumber": 18.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "Attendance",
-      "path": "Attendance",
-      "title": "考勤管理",
-      "iconPath": "appicon.png",
-      "orderNumber": 20.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "Forum",
-      "path": "Forum",
-      "title": "论坛",
-      "iconPath": "appicon.png",
-      "orderNumber": 21.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "Minder",
-      "path": "Minder",
-      "title": "脑图编辑器",
-      "iconPath": "appicon.png",
-      "orderNumber": 22.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "Calendar",
-      "path": "Calendar",
-      "title": "日程安排",
-      "iconPath": "appicon.png",
-      "orderNumber": 23.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "ANN",
-      "path": "ANN",
-      "title": "神经网络",
-      "iconPath": "appicon.png",
-      "orderNumber": 24.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "Search",
-      "path": "Search",
-      "title": "搜索",
-      "iconPath": "appicon.png",
-      "orderNumber": 25.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    },
-    {
-      "name": "IMV2",
-      "path": "IMV2",
-      "title": "聊天",
-      "iconPath": "appicon.png",
-      "orderNumber": 26.0,
-      "type": "system",
-      "allowList": [],
-      "dentyList": []
-    }
-  ],
-  "###systems": "默认模块###"
-}

+ 0 - 32
o2server/configSample/dingding.json

@@ -1,32 +0,0 @@
-{
-  "enable": false,
-  "corpId": "",
-  "agentId": "",
-  "appKey": "",
-  "appSecret": "",
-  "syncCron": "10 0/10 * * * ?",
-  "forceSyncCron": "10 45 8,12 * * ?",
-  "oapiAddress": "https://oapi.dingtalk.com",
-  "workUrl": "",
-  "messageRedirectPortal": "",
-  "messageEnable": true,
-  "scanLoginEnable": false,
-  "scanLoginAppId": "",
-  "scanLoginAppSecret": "",
-  "attendanceSyncEnable": false,
-  "###enable": "是否启用###",
-  "###corpId": "钉钉corpId###",
-  "###agentId": "agentId###",
-  "###appKey": "应用的key,唯一标识###",
-  "###appSecret": "应用的密钥###",
-  "###syncCron": "回调信号触发同步检查,默认每10分钟运行一次,如果期间内有钉钉回调信号接收到,那么触发同步任务进行人员同步.###",
-  "###forceSyncCron": "强制拉入同步cron,默认在每天的8点和12点强制进行同步.###",
-  "###oapiAddress": "oapi服务器地址###",
-  "###workUrl": "钉钉消息打开工作的url地址,如:http://dev.o2oa.net/x_desktop/###",
-  "###messageRedirectPortal": "钉钉消息处理完成后跳转到特定的门户页面的Id###",
-  "###messageEnable": "是否启用消息推送###",
-  "###scanLoginEnable": "是否开启钉钉扫码登录###",
-  "###scanLoginAppId": "钉钉扫码登录的AppId###",
-  "###scanLoginAppSecret": "钉钉扫码登录的appSecret###",
-  "###attendanceSyncEnable": "是否启用考勤信息###"
-}

+ 0 - 19
o2server/configSample/dumpRestoreData.json

@@ -1,19 +0,0 @@
-{
-  "enable": false,
-  "includes": [],
-  "excludes": [],
-  "mode": "lite",
-  "parallel": true,
-  "redistribute": true,
-  "exceptionInvalidStorage": true,
-  "itemCategory": "",
-  "###enable": "是否启用.###",
-  "###includes": "导出导入包含对象,可以使用通配符*.###",
-  "###excludes": "导出导入排除对象,可以使用通配符*.###",
-  "###mode": "导出数据模式,lite|full,默认使用lite###",
-  "###parallel": "使用并行导出,默认true###",
-  "###redistribute": "是否进行重新分布.###",
-  "###exceptionInvalidStorage": "无法获取storage是否升起错误.###",
-  "###restoreOverride": "数据导入方式,clean:清空重新导入,skipExisted:如果有相同id的数据跳过.默认方式为clean.###",
-  "###itemCategory": "对于com.x.query.core.entity.Item的itemCategory进行单独过滤,可选值pp, cms, bbs, pp_dict.###"
-}

+ 0 - 13
o2server/configSample/dumpRestoreStorage.json

@@ -1,13 +0,0 @@
-{
-  "includes": [],
-  "excludes": [],
-  "batchSize": 100.0,
-  "redistribute": true,
-  "exceptionInvalidStorage": true,
-  "###enable": "是否启用.###",
-  "###includes": "导出导入包含对象,可以使用通配符*.###",
-  "###excludes": "导出导入排除对象,可以使用通配符*.###",
-  "###batchSize": "批量对象数量.###",
-  "###redistribute": "是否进行重新分布.###",
-  "###exceptionInvalidStorage": "无法获取storage是否升起错误.###"
-}

+ 0 - 20
o2server/configSample/exmail.json

@@ -1,20 +0,0 @@
-{
-  "enable": false,
-  "corpId": "",
-  "corpAccessTokenAddress": "https://api.exmail.qq.com/cgi-bin/gettoken",
-  "newCountAddress": "https://api.exmail.qq.com/cgi-bin/mail/newcount",
-  "ssoAddress": "https://api.exmail.qq.com/cgi-bin/service/get_login_url",
-  "personAttributeNewCountName": "exmailNewCount",
-  "personAttributeTitleName": "exmailTitle",
-  "###enable": "是否启用.###",
-  "###corpId": "腾讯企业邮corpId###",
-  "###newRemindSecret": "新邮件提醒secret###",
-  "###ssoSecret": "单点登录secret###",
-  "###corpAccessTokenAddress": "corpAccessToken获取地址###",
-  "###newCountAddress": "新邮件数量获取地址###",
-  "###ssoAddress": "单点登录获取地址###",
-  "###token": "回调token###",
-  "###encodingAesKey": "回调encodingAesKey###",
-  "###personAttributeNewCountName": "存储邮件数量个人属性值.###",
-  "###personAttributeTitleName": "存储邮件标题个人属性值.###"
-}

+ 2 - 2
o2server/configSample/externalDataSources.json

@@ -1,10 +1,10 @@
 [
 	{
-		"url":"jdbc:mysql://127.0.0.1:3306/X?autoReconnect=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8",
+		"url":"jdbc:mysql://127.0.0.1:3306/X?autoReconnect=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8",      
 		"username" : "root",
 		"password" :"password",
 		"includes": [],
 		"excludes": [],
 		"enable" : true
 	}
-]
+]

+ 0 - 24
o2server/configSample/externalDataSources_oracle.json

@@ -286,29 +286,5 @@
       "###name": "存储节点名,对应存储名称,谨慎修改.###",
       "###deepPath": "是否使用更深的路径.###"
     }
-  ],
-  "general": [
-    {
-      "protocol": "webdav",
-      "username": "admin",
-      "password": "admin",
-      "host": "127.0.0.1",
-      "port": 8080.0,
-      "prefix": "",
-      "enable": true,
-      "weight": 100.0,
-      "name": "251",
-      "deepPath": false,
-      "###protocol": "协议,可选值ftp,webdav###",
-      "###username": "登录用户名.###",
-      "###password": "登录密码.###",
-      "###host": "主机地址.###",
-      "###port": "端口.###",
-      "###prefix": "前缀路径.###",
-      "###enable": "是否启用###",
-      "###weight": "设置权重.###",
-      "###name": "存储节点名,对应存储名称,谨慎修改.###",
-      "###deepPath": "是否使用更深的路径.###"
-    }
   ]
 }

+ 0 - 8
o2server/configSample/jpushConfig.json

@@ -1,8 +0,0 @@
-{
-  "enable": false,
-  "appKey": "9aca7cc20fe0cc987cd913ca",
-  "masterSecret": "96ee7e2e0daffd51bac57815",
-  "###enable": "是否启用.###",
-  "###appKey": "极光推送应用的AppKey###",
-  "###masterSecret": "极光推送应用的Master Secret###"
-}

+ 0 - 70
o2server/configSample/logLevel.json

@@ -1,70 +0,0 @@
-{
-  "x_program_center": "",
-  "x_processplatform_service_processing": "",
-  "x_processplatform_assemble_surface": "",
-  "x_processplatform_assemble_designer": "",
-  "x_query_assemble_designer": "",
-  "x_query_assemble_surface": "",
-  "x_query_service_processing": "",
-  "x_meeting_assemble_control": "",
-  "x_organization_assemble_authentication": "",
-  "x_organization_assemble_control": "",
-  "x_general_assemble_control": "",
-  "x_file_assemble_control": "",
-  "x_attendance_assemble_control": "",
-  "x_collaboration_core_message": "",
-  "x_organization_core_express": "",
-  "x_query_core_express": "",
-  "x_bbs_assemble_control": "",
-  "x_calendar_assemble_control": "",
-  "x_cms_assemble_control": "",
-  "x_component_assemble_control": "",
-  "x_hotpic_assemble_control": "",
-  "x_message_assemble_communicate": "",
-  "x_mind_assemble_control": "",
-  "x_okr_assemble_control": "",
-  "x_organization_assemble_express": "",
-  "x_organization_assemble_personal": "",
-  "x_portal_assemble_designer": "",
-  "x_portal_assemble_surface": "",
-  "x_processplatform_assemble_bam": "",
-  "x_jpush_assemble_control": "",
-  "audit": {
-    "###enable": "是否启用审计日志###",
-    "###logSize": "审计日志保留天数###",
-    "###system": "审计日志归属系统code###",
-    "###systemName": "审计日志归属系统名称###",
-    "###extend1": "扩展字段1###"
-  },
-  "###x_program_center": "是否启用调试###",
-  "###x_processplatform_service_processing": "是否启用调试###",
-  "###x_processplatform_assemble_surface": "是否启用调试###",
-  "###x_processplatform_assemble_designer": "是否启用调试###",
-  "###x_query_assemble_designer": "是否启用调试###",
-  "###x_query_assemble_surface": "是否启用调试###",
-  "###x_query_service_processing": "是否启用调试###",
-  "###x_meeting_assemble_control": "是否启用调试###",
-  "###x_organization_assemble_authentication": "是否启用调试###",
-  "###x_organization_assemble_control": "是否启用调试###",
-  "###x_general_assemble_control": "是否启用调试###",
-  "###x_file_assemble_control": "是否启用调试###",
-  "###x_attendance_assemble_control": "是否启用调试###",
-  "###x_collaboration_core_message": "是否启用调试###",
-  "###x_organization_core_express": "是否启用调试###",
-  "###x_query_core_express": "是否启用调试###",
-  "###x_bbs_assemble_control": "是否启用调试###",
-  "###x_calendar_assemble_control": "是否启用调试###",
-  "###x_cms_assemble_control": "是否启用调试###",
-  "###x_component_assemble_control": "是否启用调试###",
-  "###x_hotpic_assemble_control": "是否启用调试###",
-  "###x_message_assemble_communicate": "是否启用调试###",
-  "###x_mind_assemble_control": "是否启用调试###",
-  "###x_okr_assemble_control": "是否启用调试###",
-  "###x_organization_assemble_express": "是否启用调试###",
-  "###x_organization_assemble_personal": "是否启用调试###",
-  "###x_portal_assemble_designer": "是否启用调试###",
-  "###x_portal_assemble_surface": "是否启用调试###",
-  "###x_processplatform_assemble_bam": "是否启用调试###",
-  "###x_jpush_assemble_control": "是否启用调试###",
-  "###audit": "审计日志配置###"
-}

+ 4 - 4
o2server/configSample/manifest.cfg

@@ -31,8 +31,8 @@
  "query.json":"数据中心配置",
  "token.json":"令牌,密钥配置",
  "vfs.json":"虚拟文件存储配置",
- "welink.json":"华为WeLink配置",
+ "web.json":"web端参数配置",
+ "weLink.json":"华为WeLink配置",
  "workTime.json":"工作时间配置",
- "zhengwuDingding.json":"政务钉钉配置",
- "web.json":"前端相关配置"
-}
+ "zhengwuDingding.json":"政务钉钉配置"
+}

+ 0 - 17
o2server/configSample/meeting.json

@@ -1,17 +0,0 @@
-{
-  "enable": false,
-  "oauth2Id": "5",
-  "port": 5080.0,
-  "host": "127.0.0.1",
-  "user": "xadmin",
-  "pass": "",
-  "anonymousAccessAttachment": false,
-  "###enable": "是否启用###",
-  "###oauth2Id": "openMeeting单点序号###",
-  "###port": "openMeeting端口###",
-  "###host": "openMeeting服务器###",
-  "###user": "openMeeting管理员账户###",
-  "###pass": "openMeeting管理员密码###",
-  "###httpProtocol": "openMeeting协议###",
-  "###anonymousAccessAttachment": "匿名用户是否可以访问附件###"
-}

+ 0 - 198
o2server/configSample/messageSendRule.js

@@ -1,198 +0,0 @@
-{
-  "##sample##": {
-    "consumers": [],
-    "consumersV2": {
-      "qiyeweixin": "excute",
-      "describe": "excute表示脚本messageSendRule.js中的方法名称,该js文件需放在与messages.json同目录下,更改脚本需重启服务"
-    }
-  },
-  "attachment_editor": {
-    "consumers": [],
-    "consumersV2": {
-      "mq": "",
-      "pms": "",
-      "zhengwuDingding": "",
-      "qiyeweixin": "",
-      "welink": "",
-      "ws": "",
-      "dingding": ""
-    }
-  },
-  "attachment_editorCancel": {
-    "consumers": [],
-    "consumersV2": {
-      "mq": "",
-      "pms": "",
-      "zhengwuDingding": "",
-      "qiyeweixin": "",
-      "welink": "",
-      "ws": "",
-      "dingding": ""
-    }
-  },
-  "attachment_editorModify": {
-    "consumers": [],
-    "consumersV2": {
-      "mq": "",
-      "pms": "",
-      "zhengwuDingding": "",
-      "qiyeweixin": "",
-      "welink": "",
-      "ws": "",
-      "dingding": ""
-    }
-  },
-  "attachment_share": {
-    "consumers": [],
-    "consumersV2": {
-      "mq": "",
-      "pms": "",
-      "zhengwuDingding": "",
-      "qiyeweixin": "",
-      "welink": "",
-      "ws": "",
-      "dingding": ""
-    }
-  },
-  "attachment_shareCancel": {
-    "consumers": [],
-    "consumersV2": {
-      "mq": "",
-      "pms": "",
-      "zhengwuDingding": "",
-      "qiyeweixin": "",
-      "welink": "",
-      "ws": "",
-      "dingding": ""
-    }
-  },
-  "bbs_replyCreate": {
-    "consumers": [],
-    "consumersV2": {
-      "pms": "",
-      "ws": ""
-    }
-  },
-  "bbs_subjectCreate": {
-    "consumers": [],
-    "consumersV2": {
-      "pms": "",
-      "ws": ""
-    }
-  },
-  "calendar_alarm": {
-    "consumers": [],
-    "consumersV2": {
-      "pms": "",
-      "zhengwuDingding": "",
-      "qiyeweixin": "",
-      "welink": "",
-      "ws": "",
-      "dingding": ""
-    }
-  },
-  "im_create": {
-    "consumers": [],
-    "consumersV2": {
-      "ws": ""
-    }
-  },
-  "meeting_delete": {
-    "consumers": [],
-    "consumersV2": {
-      "mq": "",
-      "pms": "",
-      "zhengwuDingding": "",
-      "qiyeweixin": "",
-      "welink": "",
-      "ws": "",
-      "dingding": ""
-    }
-  },
-  "meeting_invite": {
-    "consumers": [],
-    "consumersV2": {
-      "mq": "",
-      "pms": "",
-      "zhengwuDingding": "",
-      "qiyeweixin": "",
-      "welink": "",
-      "ws": "",
-      "dingding": ""
-    }
-  },
-  "mind_fileSend": {
-    "consumers": [],
-    "consumersV2": {
-      "pms": "",
-      "ws": ""
-    }
-  },
-  "mind_fileShare": {
-    "consumers": [],
-    "consumersV2": {
-      "pms": "",
-      "ws": ""
-    }
-  },
-  "readCompleted_create": {
-    "consumers": [],
-    "consumersV2": {}
-  },
-  "readCompleted_delete": {
-    "consumers": [],
-    "consumersV2": {}
-  },
-  "read_create": {
-    "consumers": [],
-    "consumersV2": {
-      "mq": "",
-      "pms": "",
-      "zhengwuDingding": "",
-      "qiyeweixin": "",
-      "welink": "",
-      "ws": "",
-      "dingding": ""
-    }
-  },
-  "read_delete": {
-    "consumers": [],
-    "consumersV2": {}
-  },
-  "taskCompleted_create": {
-    "consumers": [],
-    "consumersV2": {}
-  },
-  "taskCompleted_delete": {
-    "consumers": [],
-    "consumersV2": {}
-  },
-  "task_create": {
-    "consumers": [],
-    "consumersV2": {
-      "mq": "",
-      "pms": "",
-      "zhengwuDingding": "",
-      "qiyeweixin": "",
-      "welink": "",
-      "ws": "",
-      "dingding": ""
-    }
-  },
-  "task_delete": {
-    "consumers": [],
-    "consumersV2": {}
-  },
-  "task_press": {
-    "consumers": [],
-    "consumersV2": {
-      "mq": "",
-      "pms": "",
-      "zhengwuDingding": "",
-      "qiyeweixin": "",
-      "welink": "",
-      "ws": "",
-      "dingding": ""
-    }
-  }
-}

+ 2 - 2
o2server/configSample/mq.json

@@ -1,5 +1,5 @@
 {
-  "enable": true,
+  "enable": false,
   "mq":"kafka",
   "kafka":{
 	  "bootstrap_servers": "localhost:9092",
@@ -28,4 +28,4 @@
   },
   "###enable": "是否启用.###",
   "###mq": "消息服务类型.###"
-}
+}

+ 0 - 185
o2server/configSample/node_127.0.0.1.json

@@ -1,185 +0,0 @@
-{
-  "enable": true,
-  "isPrimaryCenter": true,
-  "center": {
-    "enable": true,
-    "order": 0.0,
-    "sslEnable": false,
-    "redeploy": true,
-    "port": 20030.0,
-    "httpProtocol": "",
-    "proxyHost": "",
-    "proxyPort": 20030.0,
-    "scanInterval": 0.0,
-    "configApiEnable": true,
-    "statEnable": true,
-    "statExclusions": "*.js,*.gif,*.jpg,*.png,*.css,*.ico",
-    "maxFormContent": 20.0,
-    "exposeJest": true,
-    "persistentConnectionsEnable": true,
-    "###enable": "是否启用###",
-    "###order": "center节点顺序,顺序排列0,1,2...###",
-    "###sslEnable": "是否启用ssl传输加密,如果启用将使用config/keystore文件作为密钥文件.使用config/token.json文件中的sslKeyStorePassword字段为密钥密码,sslKeyManagerPassword为管理密码.###",
-    "###redeploy": "每次启动是否重新部署所有应用.###",
-    "###port": "端口,center服务器端口,默认20030###",
-    "###httpProtocol": "对外http访问协议,http/https###",
-    "###proxyHost": "代理主机,当服务器是通过apache/nginx等代理服务器映射到公网或者通过路由器做端口映射,在这样的情况下需要设置此地址以标明公网访问地址.###",
-    "###proxyPort": "代理端口,当服务器是通过apache/nginx等代理服务器映射到公网或者通过路由器做端口映射,在这样的情况下需要设置此地址以标明公网访问端口.###",
-    "###scanInterval": "重新扫描war包时间间隔(秒)###",
-    "###config": "其他参数###",
-    "###configApiEnable": "允许通过Api修改config###",
-    "###statEnable": "启用统计,默认启用统计.###",
-    "###statExclusions": "统计忽略路径,默认忽略*.js,*.gif,*.jpg,*.png,*.css,*.ico###",
-    "###maxFormContent": "最大提交数据限制(M),限制有所上传的内容大小,包括附件.###",
-    "###exposeJest": "暴露jest接口.###",
-    "###persistentConnectionsEnable": "是否启用长连接,默认false.###"
-  },
-  "application": {
-    "enable": true,
-    "port": 20020.0,
-    "sslEnable": false,
-    "proxyHost": "",
-    "proxyPort": 20020.0,
-    "redeploy": true,
-    "scanInterval": 0.0,
-    "includes": [],
-    "excludes": [],
-    "weights": [],
-    "scheduleWeights": [],
-    "statEnable": true,
-    "statExclusions": "*.js,*.gif,*.jpg,*.png,*.css,*.ico",
-    "maxFormContent": 20.0,
-    "exposeJest": true,
-    "persistentConnectionsEnable": true,
-    "###enable": "是否启用###",
-    "###port": "http/https端口,负责向前端提供数据访问接口.默认为20020端口.###",
-    "###sslEnable": "是否启用ssl传输加密,如果启用将使用config/keystore文件作为密钥文件.使用config/token.json文件中的sslKeyStorePassword字段为密钥密码,sslKeyManagerPassword为管理密码.###",
-    "###proxyHost": "代理主机,当服务器是通过apache/nginx等代理服务器映射到公网或者通过路由器做端口映射,在这样的情况下需要设置此地址以标明公网访问地址.###",
-    "###proxyPort": "代理端口,当服务器是通过apache/nginx等代理服务器映射到公网或者通过路由器做端口映射,在这样的情况下需要设置此地址以标明公网访问端口.###",
-    "###redeploy": "每次启动是否重载全部应用.###",
-    "###scanInterval": "应用reload扫描间隔,\u003c0 表示不会reload应用,扫描到应用文件发生了变化.###",
-    "###includes": "承载的应用,在集群环境下可以选择仅承载部分应用以降低服务器负载,可以使用*作为通配符.###",
-    "###excludes": "选择不承载的应用,和includes的值配合使用可以选择或者排除承载的应用,可以使用*作为通配符.###",
-    "###weights": "设置应用的Web访问权重,在集群环境中,一个应用可以部署多个实例提供负载均衡.通过合计占比来分配应用占比.###",
-    "###scheduleWeights": "设置应用的定时任务权重,在集群环境中,一个应用可以部署多个实例提供负载均衡.通过合计占比来分配应用占比.###",
-    "###statEnable": "启用统计,默认启用统计.###",
-    "###statExclusions": "统计忽略路径,默认忽略*.js,*.gif,*.jpg,*.png,*.css,*.ico###",
-    "###maxFormContent": "最大提交数据限制(M),限制有所上传的内容大小,包括附件.###",
-    "###exposeJest": "暴露jest接口.###",
-    "###persistentConnectionsEnable": "是否启用长连接,默认false.###"
-  },
-  "web": {
-    "enable": true,
-    "sslEnable": false,
-    "proxyHost": "",
-    "weight": 100.0,
-    "dirAllowed": false,
-    "statEnable": false,
-    "statExclusions": "*.gif,*.jpg,*.png,*.ico",
-    "cacheControlMaxAge": 0.0,
-    "persistentConnectionsEnable": true,
-    "###enable": "是否启用###",
-    "###port": "http/https端口,用户输入网址后实际访问的第一个端口.http协议默认为80端口,https默认为443端口.###",
-    "###sslEnable": "是否启用ssl传输加密,如果启用将使用config/keystore文件作为密钥文件.使用config/token.json文件中的sslKeyStorePassword字段为密钥密码,sslKeyManagerPassword为管理密码.###",
-    "###proxyHost": "代理主机,当服务器是通过apache/nginx等代理服务器映射到公网或者通过路由器做端口映射,在这样的情况下需要设置此地址以标明公网访问地址.###",
-    "###proxyPort": "代理端口,当服务器是通过apache/nginx等代理服务器映射到公网或者通过路由器做端口映射,在这样的情况下需要设置此地址以标明公网访问端口.###",
-    "###weight": "设置权重.当前没有作用,###",
-    "###dirAllowed": "允许浏览目录,###",
-    "###statEnable": "启用统计,默认启用统计.###",
-    "###statExclusions": "统计忽略路径,默认忽略*.gif,*.jpg,*.png,*.ico###",
-    "###cacheControlMaxAge": "服务器max-age缓存时间(秒)###",
-    "###proxyCenterEnable": "是否启用center服务器代理.###",
-    "###proxyApplicationEnable": "是否启用application服务器代理###",
-    "###persistentConnectionsEnable": "是否启用长连接,默认true.###"
-  },
-  "data": {
-    "enable": true,
-    "tcpPort": 20050.0,
-    "webPort": 20051.0,
-    "includes": [],
-    "excludes": [],
-    "jmxEnable": false,
-    "cacheSize": 512.0,
-    "logLevel": "WARN",
-    "maxTotal": 50.0,
-    "maxIdle": 0.0,
-    "statEnable": true,
-    "statFilter": "mergeStat",
-    "slowSqlMillis": 2000.0,
-    "lockTimeout": 120000.0,
-    "###enable": "是否启用,如果没有可用的externalDataSources.json文件,那么默认会在节点中启用本地的H2数据库作为默认的数据库.###",
-    "###tcpPort": "H2数据库jdbc连接端口,登录的用户名:sa,密码为xadmin的密码.数据库创建在/o2server/local/repository/data/X.mv.db,一旦数据库文件被创建,那么该数据库的密码被创建.###",
-    "###webPort": "H2数据库web端口,H2提供一个web端的client,此端口为web端client的访问端口.用户名sa,密码为xadmin数据库初始创建的密码.###",
-    "###includes": "设置此数据库存储的类,默认情况下存储所有类型,如果需要对每个类进行单独的控制以达到高性能,可以将不同的类存储到不同的节点上提高性能.可以使用通配符*###",
-    "###excludes": "在此节点上不存储的类,和includes一起设置实际存储的类,可以使用通配符*###",
-    "###jmxEnable": "是否启动jmx,如果启用,可以通过本地的jmx客户端进行访问,不支持远程jmx客户端.###",
-    "###cacheSize": "H2数据库缓存大小,设置H2用于作为缓存的内存大小,以M作为单位,这里默认为512M.###",
-    "###logLevel": "默认日志级别,FATAL, ERROR, WARN, INFO, TRACE. 完整的配置为DefaultLevel\u003dWARN, Tool\u003dTRACE, Enhance\u003dTRACE, METADATA\u003dTRACE, Runtime\u003dTRACE, Query\u003dTRACE, DataCache\u003dTRACE, JDBC\u003dTRACE, SQL\u003dTRACE###",
-    "###maxTotal": "最大使用连接数###",
-    "###maxIdle": "最大空闲连接数###",
-    "###statEnable": "启用统计,默认启用###",
-    "###statFilter": "统计方式配置,默认mergeStat###",
-    "###slowSqlMillis": "执行缓慢sql毫秒数,默认2000毫秒,执行缓慢的sql将被单独记录.###",
-    "###lockTimeout": "默认锁超时时间()毫秒).###"
-  },
-  "storage": {
-    "enable": true,
-    "port": 20040.0,
-    "sslEnable": false,
-    "name": "251",
-    "accounts": [],
-    "prefix": "",
-    "deepPath": false,
-    "###enable": "是否启用,对于二进制流文件,比如附件,图片等存储在单独的文件服务器中,可以支持多种文件服务器,默认情况下使用ftp服务器作为文件服务器,每个节点可以启动一个文件服务器以提供高性能.###",
-    "###port": "ftp服务器端口,此端口可以不对外开放,仅有ApplicationServer进行访问,并不直接对用户提供服务.###",
-    "###sslEnable": "是否启用ssl传输加密,如果启用将使用config/keystore文件作为密钥文件.使用config/token.json文件中的sslKeyStorePassword字段为密钥密码,sslKeyManagerPassword为管理密码.###",
-    "###name": "名称,多个节点中不能重名,默认为251.###",
-    "###accounts": "二进制流文件是分多个账号分段存储的,可以单独设置每个分类的存储配置,一般不需要设置.###",
-    "###passivePorts": "ftp传输有主动和被动之分,如果使用了被动传输,设置被动端口范围,默认为29000-30000.###",
-    "###prefix": "路径前缀.###",
-    "###deepPath": "使用更深的路径###"
-  },
-  "logLevel": "warn",
-  "dumpData": {
-    "enable": false,
-    "cron": "",
-    "size": 7.0,
-    "path": "",
-    "###enable": "是否启用,默认禁用.###",
-    "###cron": "定时任务cron表达式,默认每天凌晨2点进行备份.###",
-    "###size": "最大保留份数,超过将自动删除最久的数据.###",
-    "###path": "备份路径###"
-  },
-  "restoreData": {
-    "enable": false,
-    "cron": "",
-    "path": "",
-    "###enable": "是否启用.###",
-    "###cron": "定时任务cron表达式###",
-    "###path": "恢复路径###"
-  },
-  "nodeAgentEnable": true,
-  "nodeAgentPort": 20010.0,
-  "nodeAgentEncrypt": true,
-  "quickStartWebApp": false,
-  "autoStart": true,
-  "###enable": "是否启用###",
-  "###isPrimaryCenter": "是否是center节点,仅允许存在一个center节点###",
-  "###center": "Center服务器配置###",
-  "###application": "Application服务器配置###",
-  "###web": "Web服务器配置###",
-  "###data": "Data服务器配置###",
-  "###storage": "Storage服务器配置###",
-  "###logLevel": "日志级别,默认当前节点的slf4j日志级别,通过系统变量\"org.slf4j.simpleLogger.defaultLogLevel\"设置到当前jvm中.###",
-  "###dumpData": "定时数据导出配置###",
-  "###restoreData": "定时数据导入配置###",
-  "###logSize": "日志文件保留天数.###",
-  "###auditLogSize": "审计日志文件保留天数.###",
-  "###nodeAgentEnable": "是否启用节点代理###",
-  "###nodeAgentPort": "是否启用节点端口###",
-  "###nodeAgentEncrypt": "是否启用节点代理加密###",
-  "###quickStartWebApp": "是否使用快速应用部署###",
-  "###banner": "服务器控制台启动标识###",
-  "###autoStart": "是否自动启动###",
-  "###eraseContentEnable": "是否允许使用擦除数据功能###"
-}

+ 0 - 40
o2server/configSample/person.json

@@ -1,40 +0,0 @@
-{
-  "MAX_PASSWORDPERIOD": 3650.0,
-  "captchaLogin": true,
-  "codeLogin": true,
-  "bindLogin": true,
-  "faceLogin": true,
-  "password": "(var v \\u003d person.getMobile();\\u000a return v.substring(v.length - 6))",
-  "passwordPeriod": 0.0,
-  "passwordRegex": "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,}$",
-  "passwordRegexHint": "6位以上,包含数字和字母.",
-  "register": "disable",
-  "superPermission": true,
-  "tokenCookieHttpOnly": false,
-  "personUnitOrderByAsc": true,
-  "###captchaLogin": "是否启用验证码登录,默认值:true###",
-  "###codeLogin": "是否启用验证码登录,默认值:true###",
-  "###bindLogin": "是否启用扫描二维码登录,默认值:false###",
-  "###faceLogin": "是否启用刷脸登录,默认值:false###",
-  "###password": "注册初始密码,使用()调用脚本生成初始密码,默认为:(var v \\u003d person.getMobile();\\u000a return v.substring(v.length - 6))###",
-  "###passwordPeriod": "密码过期时间(天),0表示不过期,默认值:0.###",
-  "###passwordRegex": "密码校验正则表达式,默认6位以上,包含数字和字母.###",
-  "###passwordRegexHint": "密码校验不通过的提示信息.###",
-  "###register": "是否允许用户自注册,disable:不允许,captcha通过验证码注册,code:通过短信注册,默认值:disable###",
-  "###superPermission": "是否启用超级管理员权限,默认值:true###",
-  "###mobileRegex": "手机号码校验正则表达式,()表示脚本内容,默认值:(^(\\+)?0{0,2}852\\d{8}$)|(^(\\+)?0{0,2}853\\d{8}$)|(^(\\+)?0{0,2}886\\d{9}$)|(^1(3|4|5|7|8|9)\\d{9}$)###",
-  "###loginPage": "定制登录页面设置.###",
-  "loginPage": {
-    "enable": false,
-    "portal": "",
-    "page": "",
-    "###enable": "是否启用定制登录页面.###",
-    "###portal": "指定登录页面所属的portal,可以用id,name,alias.###",
-    "###page": "指定的登录页面,可以使用name,alias,id###"
-  },
-  "###failureInterval": "登录限制时间(分钟)###",
-  "###failureCount": "尝试登录次数###",
-  "###tokenExpiredMinutes": "token时长,分钟###",
-  "###tokenCookieHttpOnly": "保存token的cookie是否启用httpOnly###",
-  "###personUnitOrderByAsc": "人员组织排序是否为升序,true为升序(默认),false为降序###"
-}

+ 0 - 21
o2server/configSample/portal.json

@@ -1,21 +0,0 @@
-{
-  "indexPage": {
-    "enable": false,
-    "portal": "",
-    "page": "",
-    "###enable": "是否启用定制的首页面.###",
-    "###portal": "指定首页面所属的portal,可以用id,name,alias.###",
-    "###page": "指定的首页面,可以使用name,alias,id###"
-  },
-  "loginPage": {
-    "enable": false,
-    "portal": "",
-    "page": "",
-    "###enable": "是否启用定制登录页面.###",
-    "###portal": "指定登录页面所属的portal,可以用id,name,alias.###",
-    "###page": "指定的登录页面,可以使用name,alias,id###"
-  },
-  "###urlMapping": "url转换配置.###",
-  "###indexPage": "定制首页面设置.###",
-  "###loginPage": "定制登录页面设置.###"
-}

+ 0 - 109
o2server/configSample/processPlatform.json

@@ -1,109 +0,0 @@
-{
-  "maintenanceIdentity": "",
-  "formVersionCount": 30.0,
-  "processVersionCount": 30.0,
-  "scriptVersionCount": 30.0,
-  "docToWordType": "local",
-  "docToWordDefaultFileName": "正文.docx",
-  "docToWordDefaultSite": "$doc",
-  "executorCount": 32.0,
-  "updateDataProjectionEnable": false,
-  "urge": {
-    "enable": false,
-    "cron": "30 0/10 8-18 * * ?",
-    "###enable": "是否启用###",
-    "###cron": "定时cron表达式.###"
-  },
-  "expire": {
-    "enable": true,
-    "cron": "45 0/15 8-18 * * ?",
-    "###enable": "是否启用###",
-    "###cron": "定时cron表达式###"
-  },
-  "touchDelay": {
-    "enable": true,
-    "cron": "5 0/5 * * * ?",
-    "###enable": "是否启用###",
-    "###cron": "定时cron表达式###"
-  },
-  "merge": {
-    "enable": false,
-    "cron": "30 30 6 * * ?",
-    "thresholdDays": 730.0,
-    "batchSize": 100.0,
-    "###enable": "是否启用###",
-    "###cron": "定时cron表达式###",
-    "###thresholdDays": "期限,已完成工作结束间隔指定时间进行merge,默认两年后进行merge###",
-    "###batchSize": "批量大小.###"
-  },
-  "deleteDraft": {
-    "enable": false,
-    "cron": "0 0 20 * * ?",
-    "thresholdMinutes": 86400.0,
-    "###enable": "是否启用###",
-    "###cron": "定时cron表达式###",
-    "###thresholdMinutes": "设定阈值,如果超过这个时间认为是可以删除的草稿,默认为10天.###"
-  },
-  "passExpired": {
-    "enable": true,
-    "cron": "5 5 8-18 * * ?",
-    "###enable": "是否启用###",
-    "###cron": "定时cron表达式###"
-  },
-  "touchDetained": {
-    "enable": true,
-    "cron": "30 30 12 * * ?",
-    "thresholdMinutes": 1440.0,
-    "###enable": "是否启用###",
-    "###cron": "定时cron表达式###",
-    "###thresholdMinutes": "当工作滞留设定时间后,将尝试触发工作流转,可以自动处理由于人员变动的引起的工作滞留,默认24*60分钟.###"
-  },
-  "processingSignalPersistEnable": false,
-  "attachmentConfig": {
-    "fileSize": 0.0,
-    "fileTypeIncludes": [],
-    "fileTypeExcludes": [],
-    "###fileSize": "附件大小限制(单位M,默认不限制).###",
-    "###fileTypeIncludes": "只允许上传的文件后缀###",
-    "###fileTypeExcludes": "不允许上传的文件后缀###"
-  },
-  "###maintenanceIdentity": "维护身份,当工作发生意外错误,无法找到对应的处理人情况下,先尝试将工作分配给创建身份,如果创建身份也不可获取,那么分配给指定人员,默认情况下这个值为空.###",
-  "###formVersionCount": "表单历史版本保留数量,0为不保留.###",
-  "###processVersionCount": "流程历史版本保留数量,0为不保留.###",
-  "###scriptVersionCount": "脚本历史版本保留数量,0为不保留.###",
-  "###docToWordType": "HTML版式公文转换成Word文件方式,local,cloud.###",
-  "###docToWordDefaultFileName": "HTML版式公文转换成Word文件缺省文件名.###",
-  "###docToWordDefaultSite": "HTML版式公文转换成Word文件缺省site.###",
-  "###executorCount": "执行器数量###",
-  "###updateDataProjectionEnable": "更新data数据是否执行映射.###",
-  "###urge": "催办任务设置,发现即将过期时发送提醒消息.###",
-  "###expire": "将已经过了截至时间的待办标记过期.###",
-  "###touchDelay": "延时任务设置,定时触发延时任务,当超过延时时间后继续流转.###",
-  "###merge": "合并任务设置,定时触发合并任务,将已完成工作的Data从Item表中提取合并到WorkCompleted的Data字段中,默认工作完成后2年开始进行合并.###",
-  "###deleteDraft": "清除草稿状态的工作.###",
-  "###passExpired": "超时工作路由设置.###",
-  "###touchDetained": "触发长时间未处理的工作.###",
-  "###logLongDetained": "记录长期滞留工作,待办,待阅设置.###",
-  "logLongDetained": {
-    "enable": true,
-    "cron": "0 0 4 * * ?",
-    "taskThresholdMinutes": 14400.0,
-    "readThresholdMinutes": 14400.0,
-    "workThresholdMinutes": 14400.0,
-    "###enable": "是否启用###",
-    "###cron": "定时cron表达式###",
-    "###taskThresholdMinutes": "设定待办滞留阈值,.###",
-    "###readThresholdMinutes": "设定待阅滞留阈值,.###",
-    "###workThresholdMinutes": "设定工作滞留阈值,.###"
-  },
-  "###press": "提醒设置,设置提醒间隔.###",
-  "press": {
-    "intervalMinutes": 10.0,
-    "count": 3.0,
-    "###intervalMinutes": "提醒间隔(分钟)###",
-    "###count": "提醒数量限制.###"
-  },
-  "###extensionEvents": "事件扩充.###",
-  "###processingSignalPersistEnable": "是否保存工作处理信号内容,默认false.###",
-  "###attachmentConfig": "流程附件上传限制大小或者类型.###"
-}

+ 0 - 36
o2server/configSample/qiyeweixin.json

@@ -1,36 +0,0 @@
-{
-  "enable": false,
-  "syncCron": "10 0/10 * * * ?",
-  "forceSyncCron": "10 45 8,12 * * ?",
-  "apiAddress": "https://qyapi.weixin.qq.com",
-  "corpId": "",
-  "syncSecret": "",
-  "corpSecret": "",
-  "agentId": "",
-  "token": "",
-  "encodingAesKey": "",
-  "workUrl": "",
-  "messageRedirectPortal": "",
-  "messageEnable": false,
-  "scanLoginEnable": false,
-  "attendanceSyncEnable": false,
-  "attendanceSyncAgentId": "",
-  "attendanceSyncSecret": "",
-  "###enable": "是否启用.###",
-  "###syncCron": "回调信号触发同步检查,默认每10分钟运行一次,如果期间内有企业微信回调信号接收到,那么触发同步任务进行人员同步.###",
-  "###forceSyncCron": "强制拉入同步cron,默认在每天的8点和12点强制进行同步.###",
-  "###apiAddress": "api服务器地址###",
-  "###corpId": "企业微信corpId###",
-  "###syncSecret": "企业微信同步通讯录Secret###",
-  "###corpSecret": "企业微信corpSecret###",
-  "###agentId": "企业微信agentId###",
-  "###token": "回调token###",
-  "###encodingAesKey": "回调encodingAesKey###",
-  "###workUrl": "企业微信消息打开工作的url地址,如:http://dev.o2oa.net/x_desktop/###",
-  "###messageRedirectPortal": "企业微信消息处理完成后跳转到特定的门户页面的Id###",
-  "###messageEnable": "推送消息到企业微信###",
-  "###scanLoginEnable": "企业微信扫码登录###",
-  "###attendanceSyncEnable": "是否启用考勤信息###",
-  "###attendanceSyncAgentId": "企业微信考勤打卡应用id###",
-  "###attendanceSyncSecret": "企业微信考勤打卡应用secret###"
-}

+ 0 - 55
o2server/configSample/query.json

@@ -1,55 +0,0 @@
-{
-  "crawlWorkCompleted": {
-    "enable": true,
-    "cron": "50 50 21 * * ?",
-    "count": 500.0,
-    "excludeAttachment": [],
-    "excludeSite": [],
-    "maxAttachmentSize": 5242880.0,
-    "###enable": "是否启用###",
-    "###cron": "定时cron表达式.###",
-    "###count": "每次处理的数量,默认每小时处理所以默认为500,同时每次将重爬最旧的25%,按时间轮询25%.###",
-    "###excludeAttachment": "忽略附件名称.###",
-    "###excludeSite": "忽略附件位置.###",
-    "###maxAttachmentSize": "最大附件大小.###"
-  },
-  "crawlWork": {
-    "enable": true,
-    "cron": "40 40 10,12,14,16 * * ?",
-    "count": 50.0,
-    "excludeAttachment": [],
-    "excludeSite": [],
-    "maxAttachmentSize": 5242880.0,
-    "###enable": "是否启用###",
-    "###cron": "定时cron表达式.###",
-    "###count": "每次处理的数量,默认每小时处理所以默认为50,同时每次将重爬最旧的50%,按时间轮询50%.###",
-    "###excludeAttachment": "忽略附件名称.###",
-    "###excludeSite": "忽略附件位置.###",
-    "###maxAttachmentSize": "最大附件大小.###"
-  },
-  "crawlCms": {
-    "enable": true,
-    "cron": "30 30 9,12,15,18 * * ?",
-    "count": 30.0,
-    "excludeAttachment": [],
-    "maxAttachmentSize": 5242880.0,
-    "###enable": "是否启用###",
-    "###cron": "定时cron表达式.###",
-    "###count": "每次处理的数量,默认每小时处理所以默认为30,同时每次将重爬最旧的50%,按时间轮询50%.###",
-    "###excludeAttachment": "忽略附件名称.###",
-    "###maxAttachmentSize": "最大附件大小.###"
-  },
-  "extractOffice": true,
-  "extractPdf": true,
-  "extractText": true,
-  "extractImage": false,
-  "tessLanguage": "chi_sim",
-  "###crawlWorkCompleted": "已完成工作收集器设置.###",
-  "###crawlWork": "工作收集器设置.###",
-  "###crawlCms": "内容管理收集器设置.###",
-  "###extractOffice": "抽取office中的文本.###",
-  "###extractPdf": "抽取pdf中的文本.###",
-  "###extractText": "抽取文本中的文本.###",
-  "###extractImage": "抽取图像中的文本.###",
-  "###tessLanguage": "tess使用语言.###"
-}

+ 0 - 85
o2server/configSample/token.json

@@ -1,85 +0,0 @@
-{
-  "key": "",
-  "password": "",
-  "sslKeyStorePassword": "123456",
-  "sslKeyManagerPassword": "123456",
-  "initialManager": "xadmin",
-  "initialManagerDistinguishedName": "xadmin@o2oa@P",
-  "ssos": [
-    {
-      "enable": false,
-      "client": "",
-      "key": "",
-      "###enable": "是否启用###",
-      "###client": "名称###",
-      "###key": "密钥###"
-    }
-  ],
-  "oauths": [
-    {
-      "enable": false,
-      "clientId": "",
-      "mapping": {},
-      "###enable": "是否启用###",
-      "###clientId": "客户端名称###",
-      "###clientSecret": "密钥###",
-      "###loginUrl": "登录地址###",
-      "###mapping": "返回值###"
-    }
-  ],
-  "oauthClients": [
-    {
-      "enable": false,
-      "name": "",
-      "displayName": "",
-      "icon": "",
-      "clientId": "",
-      "clientSecret": "",
-      "authAddress": "",
-      "authParameter": "client_id\u003d{$client_id}\u0026redirect_uri\u003d{$redirect_uri}",
-      "authMethod": "GET",
-      "tokenAddress": "",
-      "tokenParameter": "client_id\u003d{$client_id}\u0026client_secret\u003d{$client_secret}\u0026redirect_uri\u003d{$redirect_uri}\u0026grant_type\u003dauthorization_code\u0026code\u003d{$code}",
-      "tokenMethod": "POST",
-      "tokenType": "json",
-      "infoAddress": "",
-      "infoParameter": "access_token\u003d{$access_token}",
-      "infoMethod": "GET",
-      "infoType": "json",
-      "infoCredentialField": "openId",
-      "infoScriptText": "",
-      "bindingEnable": false,
-      "bindingField": "",
-      "###enable": "是否启用.###",
-      "###name": "名称.###",
-      "###displayName": "显示名称.###",
-      "###icon": "图标.###",
-      "###clientId": "用户oauth2认证的client_id.###",
-      "###clientSecret": "用户oauth2认证的client_secret.###",
-      "###authAddress": "认证后的跳转地址.###",
-      "###authParameter": "请求密钥方法参数.###",
-      "###authMethod": "请求密钥方法.一般为GET###",
-      "###tokenAddress": "请求令牌网址.###",
-      "###tokenParameter": "请求令牌方法参数.###",
-      "###tokenMethod": "请求令牌方法.一般为POST###",
-      "###tokenType": "token信息格式.json格式或者form格式###",
-      "###infoAddress": "请求信息网址.###",
-      "###infoParameter": "请求信息方法参数.###",
-      "###infoMethod": "请求信息方法.一般为GET###",
-      "###infoType": "info信息格式.json格式或者form格式或者script格式###",
-      "###infoCredentialField": "info信息中用于标识个人的字段.###",
-      "###infoScriptText": "info信息中用于标识个人的字段.###",
-      "###bindingEnable": "是否允许绑定到用户,如果允许,用户可以自行绑定.###",
-      "###bindingField": "绑定字段,对端的用户标识,一般为openId绑定到个人字段,可选值为open1Id,open2Id,open3Id,open4Id,open5Id###"
-    }
-  ],
-  "###key": "加密用口令的密钥,修改后会导致用户口令验证失败.###",
-  "###password": "初始管理员密码,用于内部数据库和FTP文件服务器,以及http的token加密.###",
-  "###sslKeyStorePassword": "ssl密码###",
-  "###sslKeyManagerPassword": "ssl管理密码###",
-  "###initialManager": "初始管理员名称,目前不可更改.###",
-  "###initialManagerDistinguishedName": "初始管理员DistinguishedName,不可更改.###",
-  "###ssos": "sso登录配置###",
-  "###oauths": "oauth单点登录配置###",
-  "###oauthClients": "作为客户端单点登录配置###"
-}

+ 0 - 11
o2server/configSample/vfs.json

@@ -1,11 +0,0 @@
-{
-  "ftp": {
-    "passive": true
-  },
-  "ftps": {
-    "passive": true
-  },
-  "sftp": {
-    "passive": true
-  }
-}

+ 0 - 20
o2server/configSample/web.json

@@ -1,20 +0,0 @@
-{
-  "enable": false,
-  "clientId": "",
-  "clientSecret": "",
-  "syncCron": "10 0/10 * * * ?",
-  "forceSyncCron": "10 45 8,12 * * ?",
-  "oapiAddress": "https://open.welink.huaweicloud.com/api",
-  "messageEnable": false,
-  "workUrl": "",
-  "messageRedirectPortal": "",
-  "###enable": "是否启用.###",
-  "###syncCron": "拉入同步cron,默认每10分钟同步一次.###",
-  "###forceSyncCron": "强制拉入同步cron,默认在每天的8点和12点强制进行同步.###",
-  "###oapiAddress": "api服务器地址###",
-  "###clientId": "华为WeLink轻应用的client_id###",
-  "###clientSecret": "华为WeLink轻应用的client_secret###",
-  "###messageEnable": "推送消息到华为WeLink###",
-  "###workUrl": "华为WeLink消息打开工作的url地址,如:http://dev.o2oa.net/x_desktop/###",
-  "###messageRedirectPortal": "华为WeLink消息处理完成后跳转到特定的门户页面的Id###"
-}

+ 0 - 19
o2server/configSample/workTime.json

@@ -1,19 +0,0 @@
-{
-  "amStart": "09:00:00",
-  "amEnd": "11:30:00",
-  "pmStart": "13:00:00",
-  "pmEnd": "17:30:00",
-  "holidays": [],
-  "workdays": [],
-  "weekends": [
-    1.0,
-    7.0
-  ],
-  "###amStart": "工作时间上午开始时间###",
-  "###amEnd": "工作时间上午结束时间###",
-  "###pmStart": "工作时间下午开始时间###",
-  "###pmEnd": "工作时间下午结束时间###",
-  "###holidays": "固定节假日,格式为[\"2019-01-01\",\"2019-05-01\"]###",
-  "###workdays": "固定工作时间,格式为[\"2019-01-01\",\"2019-05-01\"]###",
-  "###weekends": "周末设定,默认格式为[1,7]其中1代表周日,7代表周六.###"
-}

+ 0 - 33
o2server/configSample/zhengwuDingding.json

@@ -1,33 +0,0 @@
-{
-  "enable": false,
-  "appSecret": "",
-  "agentId": "",
-  "syncCron": "10 0/10 * * * ?",
-  "forceSyncCron": "10 45 8,12 * * ?",
-  "oapiAddress": "https://oapi.dingtalk.com",
-  "corpId": "",
-  "corpSecret": "",
-  "titleSplit": [
-    ",",
-    "、",
-    ",",
-    " ",
-    " "
-  ],
-  "personAttributeTitleName": "职务",
-  "nonce": "o2oa",
-  "messageEnable": true,
-  "###enable": "是否启用.###",
-  "###appId": "政务钉钉appId###",
-  "###appSecret": "政务钉钉appSecret###",
-  "###agentId": "政务钉钉agentId###",
-  "###syncCron": "回调信号触发同步检查,默认每10分钟运行一次,如果期间内有政务钉钉回调信号接收到,那么触发同步任务进行人员同步.###",
-  "###forceSyncCron": "强制拉入同步cron,默认在每天的8点和12点强制进行同步.###",
-  "###oapiAddress": "oapi服务器地址###",
-  "###corpId": "政务钉钉corpId###",
-  "###corpSecret": "政务钉钉corpSecret###",
-  "###titleSplit": "title分隔符###",
-  "###personAttributeTitleName": "title对应个人属性名称###",
-  "###nonce": "政务钉钉nonce###",
-  "###messageEnable": "推送消息到政务钉钉消息###"
-}

+ 11 - 2
o2server/pom.xml

@@ -294,7 +294,7 @@
 			<groupId>com.squareup</groupId>
 			<artifactId>javapoet</artifactId>
 		</dependency>
-		<!-- dependency> <groupId>javax.visrec</groupId> <artifactId>visrec-api</artifactId> 
+		<!-- dependency> <groupId>javax.visrec</groupId> <artifactId>visrec-api</artifactId>
 			</dependency -->
 		<dependency>
 			<groupId>com.github.neuroph</groupId>
@@ -348,6 +348,10 @@
 			<groupId>javax.activation</groupId>
 			<artifactId>activation</artifactId>
 		</dependency>
+		<dependency>
+			<groupId>net.lingala.zip4j</groupId>
+			<artifactId>zip4j</artifactId>
+		</dependency>
 	</dependencies>
 
 	<build>
@@ -785,7 +789,7 @@
 				<artifactId>javapoet</artifactId>
 				<version>1.11.1</version>
 			</dependency>
-			<!--dependency> <groupId>javax.visrec</groupId> <artifactId>visrec-api</artifactId> 
+			<!--dependency> <groupId>javax.visrec</groupId> <artifactId>visrec-api</artifactId>
 				<version>20200316</version> </dependency -->
 			<dependency>
 				<groupId>com.github.neuroph</groupId>
@@ -1002,6 +1006,11 @@
 				<artifactId>x_workschedu_core_entity</artifactId>
 				<version>5.3</version>
 			</dependency>
+			<dependency>
+				<groupId>net.lingala.zip4j</groupId>
+				<artifactId>zip4j</artifactId>
+				<version>2.3.2</version>
+			</dependency>
 		</dependencies>
 	</dependencyManagement>
 	<repositories>

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

@@ -711,8 +711,9 @@ public class DateOperation {
 	 */
 	public static Date getLastDayOfMonth(Date date) {  
         Calendar calendar = convert(date);  
-        calendar.set(Calendar.DATE, calendar.getMaximum(Calendar.DATE));  
-        return calendar.getTime();  
+        //calendar.set(Calendar.DATE, calendar.getMaximum(Calendar.DATE));
+		calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
+		return calendar.getTime();
     }  
 	/** 
      * 将日期转换为日历 

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

@@ -2,7 +2,12 @@ package com.x.attendance.assemble.control;
 
 import com.x.attendance.assemble.control.factory.*;
 import com.x.base.core.container.EntityManagerContainer;
+import com.x.base.core.project.http.EffectivePerson;
 import com.x.organization.core.express.Organization;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
 
 public class Business {
 
@@ -204,4 +209,64 @@ public class Business {
 		}
 		return attendanceSelfHolidayFactory;
 	}
+
+	/**
+	 * TODO 判断用户是否管理员权限 1、person.isManager() 2、xadmin 3、CRMManager
+	 *
+	 * @param request
+	 * @return
+	 * @throws Exception
+	 */
+	public boolean isManager(HttpServletRequest request, EffectivePerson person) throws Exception {
+		// 如果用户的身份是平台的超级管理员,那么就是超级管理员权限
+		if (person.isManager()) {
+			return true;
+		}
+		if ("xadmin".equalsIgnoreCase(person.getDistinguishedName())) {
+			return true;
+		}
+		if (isHasPlatformRole(person.getDistinguishedName(), ThisApplication.ROLE_AttendanceManager)) {
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * TODO 判断用户是否管理员权限 1、person.isManager() 2、xadmin 3、CRMManager
+	 * @return
+	 * @throws Exception
+	 */
+
+	public boolean isManager(EffectivePerson person) throws Exception {
+		// 如果用户的身份是平台的超级管理员,那么就是超级管理员权限
+		if (person.isManager()) {
+			return true;
+		}
+		if ("xadmin".equalsIgnoreCase(person.getDistinguishedName())) {
+			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;
+	}
 }

+ 2 - 1
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/ThisApplication.java

@@ -36,6 +36,7 @@ public class ThisApplication {
 
 	public static final QueuePersonAttendanceDetailAnalyse detailAnalyseQueue = new QueuePersonAttendanceDetailAnalyse();
 	public static final QueueAttendanceDetailStatistic detailStatisticQueue = new QueueAttendanceDetailStatistic();
+	public static final String ROLE_AttendanceManager = "AttendanceManager@AttendanceManagerSystemRole@R";
 
 	public static void init() throws Exception {
 		try {
@@ -58,7 +59,7 @@ public class ThisApplication {
 				context.schedule(QywxAttendanceSyncScheduleTask.class, "0 0 1 * * ?");
 			}
 			context.schedule(AttendanceStatisticTask.class, "0 0 0/4 * * ?");
-			context.schedule(MobileRecordAnalyseTask.class, "0 0 * * * ?");
+			//context.schedule(MobileRecordAnalyseTask.class, "0 0 * * * ?");
 			// 每天凌晨1点,计算前一天所有的未签退和未分析的打卡数据
 			context.schedule(DetailLastDayRecordAnalyseTask.class, "0 0 1 * * ?");
 

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

@@ -10,6 +10,7 @@ import javax.persistence.criteria.CriteriaQuery;
 import javax.persistence.criteria.Predicate;
 import javax.persistence.criteria.Root;
 
+import com.x.attendance.assemble.control.service.UserManagerService;
 import org.apache.commons.lang3.StringUtils;
 
 import com.x.attendance.assemble.control.AbstractFactory;
@@ -311,8 +312,124 @@ public class AttendanceAppealInfoFactory extends AbstractFactory {
 			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<AttendanceAppealInfo> listIdsNextWithFilterWithCurrentProcessor( String id, Integer count, Object sequence, WrapInFilterAppeal wrapIn ,Boolean isManager) throws Exception {
+		//先获取上一页最后一条的sequence值,如果有值的话,以此sequence值作为依据取后续的count条数据
+		EntityManager em = this.entityManagerContainer().get( AttendanceAppealInfo.class );
+		String order = wrapIn.getOrder();//排序方式
+		List<Object> vs = new ArrayList<>();
+		StringBuffer sql_stringBuffer = new StringBuffer();
+		UserManagerService userManagerService = new UserManagerService();
+		if( order == null || order.isEmpty() ){
+			order = "DESC";
+		}
+
+		Integer index = 1;
+		sql_stringBuffer.append( "SELECT o FROM "+AttendanceAppealInfo.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.getDetailId()) && (!wrapIn.getDetailId().isEmpty())) {
+			sql_stringBuffer.append(" and o.detailId = ?" + (index));
+			vs.add( wrapIn.getDetailId() );
+			index++;
+		}
+		if ((null != wrapIn.getEmpName()) && (!wrapIn.getEmpName().isEmpty())) {
+			sql_stringBuffer.append(" and o.empName = ?" + (index));
+			vs.add( wrapIn.getEmpName() );
+			index++;
+		}
+		if ((null != wrapIn.getUnitName()) && (!wrapIn.getUnitName().isEmpty())) {
+			sql_stringBuffer.append(" and o.unitName = ?" + (index));
+			vs.add( wrapIn.getUnitName() );
+			index++;
+		}
+		if ((null != wrapIn.getTopUnitName()) && (!wrapIn.getTopUnitName().isEmpty())) {
+			sql_stringBuffer.append(" and o.topUnitName = ?" + (index));
+			vs.add( wrapIn.getTopUnitName() );
+			index++;
+		}
+		if ((null != wrapIn.getYearString() ) && (!wrapIn.getYearString().isEmpty())) {
+			sql_stringBuffer.append(" and o.yearString = ?" + (index));
+			vs.add( wrapIn.getYearString() );
+			index++;
+		}
+		if ((null != wrapIn.getMonthString()) && (!wrapIn.getMonthString().isEmpty())) {
+			sql_stringBuffer.append(" and o.monthString = ?" + (index));
+			vs.add( wrapIn.getMonthString() );
+			index++;
+		}
+		if (wrapIn.getStatus()!=999) {
+			sql_stringBuffer.append(" and o.status = ?" + (index));
+			vs.add( wrapIn.getStatus() );
+			index++;
+		}
+		if ((null != wrapIn.getAppealReason()) && (!wrapIn.getAppealReason().isEmpty())) {
+			sql_stringBuffer.append(" and o.appealReason = ?" + (index));
+			vs.add( wrapIn.getAppealReason() );
+			index++;
+		}
+		if(!isManager){
+			if ((null != wrapIn.getProcessPerson1()) && (!wrapIn.getProcessPerson1().isEmpty())) {
+				sql_stringBuffer.append(" and o.currentProcessor = ?" + (index));
+				vs.add( wrapIn.getProcessPerson1() );
+				index++;
+			}
+		}
+
+//		if ((null != wrapIn.getProcessPerson2()) && (!wrapIn.getProcessPerson2().isEmpty())) {
+//			sql_stringBuffer.append(" and o.processPerson2 = ?" + (index));
+//			vs.add( wrapIn.getProcessPerson2() );
+//			index++;
+//		}
+
+		//添加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(" )");
+		}
+
+		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(), AttendanceAppealInfo.class );
+		//为查询设置所有的参数值
+		for (int i = 0; i < vs.size(); i++) {
+			query.setParameter(i + 1, vs.get(i));
+		}
+		System.out.println("listIdsNextWithFilterWithCurrentProcessor="+query);
+		return query.setMaxResults(count).getResultList();
+	}
+
 	/**
 	 * 查询上一页的文档信息数据
 	 * @param id
@@ -514,4 +631,97 @@ public class AttendanceAppealInfoFactory extends AbstractFactory {
 		}		
 		return (Long) query.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 );
+		List<Object> vs = new ArrayList<>();
+		StringBuffer sql_stringBuffer = new StringBuffer();
+		Integer index = 1;
+
+		sql_stringBuffer.append( "SELECT count(o.id) FROM "+AttendanceAppealInfo.class.getCanonicalName()+" o where 1=1" );
+
+		if ((null != wrapIn.getDetailId()) && (!wrapIn.getDetailId().isEmpty())) {
+			sql_stringBuffer.append(" and o.detailId = ?" + (index));
+			vs.add( wrapIn.getDetailId() );
+			index++;
+		}
+		if ((null != wrapIn.getEmpName()) && (!wrapIn.getEmpName().isEmpty())) {
+			sql_stringBuffer.append(" and o.empName = ?" + (index));
+			vs.add( wrapIn.getEmpName() );
+			index++;
+		}
+		if ((null != wrapIn.getUnitName()) && (!wrapIn.getUnitName().isEmpty())) {
+			sql_stringBuffer.append(" and o.unitName = ?" + (index));
+			vs.add( wrapIn.getUnitName() );
+			index++;
+		}
+		if ((null != wrapIn.getTopUnitName()) && (!wrapIn.getTopUnitName().isEmpty())) {
+			sql_stringBuffer.append(" and o.topUnitName = ?" + (index));
+			vs.add( wrapIn.getTopUnitName() );
+			index++;
+		}
+		if ((null != wrapIn.getYearString() ) && (!wrapIn.getYearString().isEmpty())) {
+			sql_stringBuffer.append(" and o.yearString = ?" + (index));
+			vs.add( wrapIn.getYearString() );
+			index++;
+		}
+		if ((null != wrapIn.getMonthString()) && (!wrapIn.getMonthString().isEmpty())) {
+			sql_stringBuffer.append(" and o.monthString = ?" + (index));
+			vs.add( wrapIn.getMonthString() );
+			index++;
+		}
+		if (wrapIn.getStatus()!=999) {
+			sql_stringBuffer.append(" and o.status = ?" + (index));
+			vs.add( wrapIn.getStatus() );
+			index++;
+		}
+		if ((null != wrapIn.getAppealReason()) && (!wrapIn.getAppealReason().isEmpty())) {
+			sql_stringBuffer.append(" and o.appealReason = ?" + (index));
+			vs.add( wrapIn.getAppealReason() );
+			index++;
+		}
+		if(!isManager){
+			if ((null != wrapIn.getProcessPerson1()) && (!wrapIn.getProcessPerson1().isEmpty())) {
+				sql_stringBuffer.append(" and o.currentProcessor = ?" + (index));
+				vs.add( wrapIn.getProcessPerson1() );
+				index++;
+			}
+		}
+//		if ((null != wrapIn.getProcessPerson2()) && (!wrapIn.getProcessPerson2().isEmpty())) {
+//			sql_stringBuffer.append(" and o.processPerson2 = ?" + (index));
+//			vs.add( wrapIn.getProcessPerson2() );
+//			index++;
+//		}
+		//添加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( sql_stringBuffer.toString(), AttendanceAppealInfo.class );
+		//为查询设置所有的参数值
+		for (int i = 0; i < vs.size(); i++) {
+			query.setParameter(i + 1, vs.get(i));
+		}
+		return (Long) query.getSingleResult();
+	}
 }

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

@@ -965,8 +965,126 @@ public class AttendanceDetailFactory extends AbstractFactory {
 		for (int i = 0; i < vs.size(); i++) {
 			query.setParameter(i + 1, vs.get(i));
 		}
+		System.out.println("query=" +query.toString());
 		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 );
+		String order = wrapIn.getOrder();//排序方式
+		List<Object> vs = new ArrayList<>();
+		StringBuffer sql_stringBuffer = new StringBuffer();
+
+		if( order == null || order.isEmpty() ){
+			order = "DESC";
+		}
+
+		Integer index = 1;
+		sql_stringBuffer.append( "SELECT o FROM "+AttendanceDetail.class.getCanonicalName()+" o where 1=1" );
+
+		if ((null != wrapIn.getQ_empName()) && (!wrapIn.getQ_empName().isEmpty())) {
+			sql_stringBuffer.append(" and o.empName = ?" + (index));
+			vs.add( wrapIn.getQ_empName() );
+			index++;
+		}
+		if (null != wrapIn.getUnitNames() && wrapIn.getUnitNames().size()>0) {
+			sql_stringBuffer.append(" and o.unitName in ( ?" + (index) + ")");
+			vs.add( wrapIn.getUnitNames() );
+			index++;
+		}
+		if (null != wrapIn.getTopUnitNames() && wrapIn.getTopUnitNames().size() > 0 ) {
+			sql_stringBuffer.append(" and o.topUnitName in ( ?" + (index) + ")");
+			vs.add( wrapIn.getTopUnitNames() );
+			index++;
+		}
+		if ((null != wrapIn.getCycleYear() ) && (!wrapIn.getCycleYear().isEmpty())) {
+			sql_stringBuffer.append(" and o.cycleYear = ?" + (index));
+			vs.add( wrapIn.getCycleYear() );
+			index++;
+		}
+		if ((null != wrapIn.getCycleMonth()) && (!wrapIn.getCycleMonth().isEmpty())) {
+			sql_stringBuffer.append(" and o.cycleMonth = ?" + (index));
+			vs.add( wrapIn.getCycleMonth() );
+			index++;
+		}
+		if ((null != wrapIn.getQ_year() ) && (!wrapIn.getQ_year().isEmpty())) {
+			sql_stringBuffer.append(" and o.yearString = ?" + (index));
+			vs.add( wrapIn.getQ_year() );
+			index++;
+		}
+		if ((null != wrapIn.getQ_month()) && (!wrapIn.getQ_month().isEmpty())) {
+			sql_stringBuffer.append(" and o.monthString = ?" + (index));
+			vs.add( wrapIn.getQ_month() );
+			index++;
+		}
+		if ((null != wrapIn.getQ_date()) && (!wrapIn.getQ_date().isEmpty())) {
+			sql_stringBuffer.append(" and o.recordDateString = ?" + (index));
+			vs.add( wrapIn.getQ_date() );
+			index++;
+		}
+
+		if ( wrapIn.getRecordStatus() != 999 ) {
+			sql_stringBuffer.append(" and o.recordStatus = ?" + (index));
+			vs.add( wrapIn.getRecordStatus() );
+			index++;
+		}
+
+		if (wrapIn.getIsAbsent() != null ) {
+			sql_stringBuffer.append(" and o.isAbsent = ?" + (index));
+			vs.add( wrapIn.getIsAbsent() );
+			index++;
+		}
+
+		if (wrapIn.getIsLate() != null ) {
+			sql_stringBuffer.append(" and o.isLate = ?" + (index));
+			vs.add( wrapIn.getIsLate() );
+			index++;
+		}
+
+		if (wrapIn.getIsLackOfTime() != null ) {
+			sql_stringBuffer.append(" and o.isLackOfTime = ?" + (index));
+			vs.add( wrapIn.getIsLackOfTime() );
+			index++;
+		}
+
+		if (wrapIn.getIsLeaveEarlier() != null ) {
+			sql_stringBuffer.append(" and o.isLeaveEarlier = ?" + (index));
+			vs.add( wrapIn.getIsLeaveEarlier() );
+			index++;
+		}
+
+		if (ListTools.isNotEmpty(unUnitNameList)) {
+			sql_stringBuffer.append(" and o.unitName not in ( ?" + (index) + ")");
+			vs.add( unUnitNameList );
+			index++;
+		}
+		if (ListTools.isNotEmpty(personNameList)) {
+			sql_stringBuffer.append(" and o.empName not in ( ?" + (index) + ")");
+			vs.add( personNameList );
+			index++;
+		}
+
+		if( StringUtils.isNotEmpty( wrapIn.getKey() )){
+			sql_stringBuffer.append(" order by o."+wrapIn.getKey()+" " + order );
+		}else{
+			sql_stringBuffer.append(" order by o.sequence " + order );
+		}
+		Query query = em.createQuery( sql_stringBuffer.toString(), AttendanceDetail.class );
+		for (int i = 0; i < vs.size(); i++) {
+			query.setParameter(i + 1, vs.get(i));
+		}
+		System.out.println("query=" +query.toString());
+		return query.getResultList();
+		/*cq.select( root.get(AttendanceDetail_.id ));
+		return em.createQuery(cq.where(p)).getResultList();*/
+	}
 	
 	/**
 	 * 查询上一页的文档信息数据

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

@@ -301,6 +301,7 @@ public class AttendanceDetailStatisticFactory extends AbstractFactory {
 		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{

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

@@ -2,6 +2,7 @@ package com.x.attendance.assemble.control.factory;
 
 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_;
@@ -122,7 +123,7 @@ public class AttendanceSelfHolidayFactory extends AbstractFactory {
 	 * @throws Exception
 	 */
 	@SuppressWarnings("unchecked")
-	public List<AttendanceSelfHoliday> listIdsNextWithFilter( String id, Integer count, Object sequence, WrapInFilter wrapIn ) throws Exception {
+	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();//排序方式
@@ -147,15 +148,24 @@ public class AttendanceSelfHolidayFactory extends AbstractFactory {
 			index++;
 		}
 		if (null != wrapIn.getUnitNames() && wrapIn.getUnitNames().size()>0) {
-			sql_stringBuffer.append(" and o.unitName in ( ?" + (index) + ")");
+			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.topUnitName in ( ?" + (index) + ")");
+			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 );
@@ -164,6 +174,7 @@ public class AttendanceSelfHolidayFactory extends AbstractFactory {
 		}
 		
 		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));

+ 23 - 19
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/factory/AttendanceWorkDayConfigFactory.java

@@ -1,8 +1,6 @@
 package com.x.attendance.assemble.control.factory;
 
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
 
 import javax.persistence.EntityManager;
 import javax.persistence.criteria.CriteriaBuilder;
@@ -13,6 +11,7 @@ import javax.persistence.criteria.Root;
 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.service.AttendanceSettingServiceAdv;
 import com.x.attendance.entity.AttendanceWorkDayConfig;
 import com.x.attendance.entity.AttendanceWorkDayConfig_;
 import com.x.base.core.project.exception.ExceptionWhen;
@@ -23,7 +22,8 @@ import com.x.base.core.project.exception.ExceptionWhen;
 public class AttendanceWorkDayConfigFactory extends AbstractFactory {
 
 	private DateOperation dateOperation = new DateOperation();
-	
+	private AttendanceSettingServiceAdv attendanceSettingServiceAdv = new AttendanceSettingServiceAdv();
+
 	public AttendanceWorkDayConfigFactory(Business business) throws Exception {
 		super(business);
 	}
@@ -32,8 +32,8 @@ public class AttendanceWorkDayConfigFactory extends AbstractFactory {
 	public AttendanceWorkDayConfig get( String id ) throws Exception {
 		return this.entityManagerContainer().find(id, AttendanceWorkDayConfig.class, ExceptionWhen.none);
 	}
-	
-//	@MethodDescribe("列示全部的AttendanceWorkDayConfig信息列表")
+
+	//	@MethodDescribe("列示全部的AttendanceWorkDayConfig信息列表")
 	@SuppressWarnings("unused")
 	public List<AttendanceWorkDayConfig> listAll() throws Exception {
 		EntityManager em = this.entityManagerContainer().get(AttendanceWorkDayConfig.class);
@@ -42,7 +42,7 @@ public class AttendanceWorkDayConfigFactory extends AbstractFactory {
 		Root<AttendanceWorkDayConfig> root = cq.from( AttendanceWorkDayConfig.class);
 		return em.createQuery(cq).getResultList();
 	}
-	
+
 	//@MethodDescribe("列示指定Id的AttendanceWorkDayConfig信息列表")
 	public List<AttendanceWorkDayConfig> list(List<String> ids) throws Exception {
 		if( ids == null || ids.size() == 0 ){
@@ -55,7 +55,7 @@ public class AttendanceWorkDayConfigFactory extends AbstractFactory {
 		Predicate p = root.get(AttendanceWorkDayConfig_.id).in(ids);
 		return em.createQuery(cq.where(p)).getResultList();
 	}
-	
+
 	//@MethodDescribe("根据年份月份列示全部的AttendanceWorkDayConfig信息列表")
 	public List<String> listByYearAndMonth( String year, String month ) throws Exception {
 		if( year == null ){
@@ -64,20 +64,20 @@ public class AttendanceWorkDayConfigFactory extends AbstractFactory {
 		if( "0".equals(month) || "00".equals(month) || "(0)".equals(month)){
 			month = null;
 		}
-		
+
 		EntityManager em = this.entityManagerContainer().get(AttendanceWorkDayConfig.class);
 		CriteriaBuilder cb = em.getCriteriaBuilder();
 		CriteriaQuery<String> cq = cb.createQuery(String.class);
 		Root<AttendanceWorkDayConfig> root = cq.from( AttendanceWorkDayConfig.class);
 		cq.select(root.get(AttendanceWorkDayConfig_.id));
-		
+
 		Predicate p = cb.equal( root.get(AttendanceWorkDayConfig_.configYear), year);
 		if( month != null ){
 			p = cb.and( p, cb.equal( root.get(AttendanceWorkDayConfig_.configMonth), month));
 		}
 		return em.createQuery(cq.where(p)).getResultList();
 	}
-	
+
 	//@MethodDescribe("根据年份和节假日名称列示全部的AttendanceWorkDayConfig信息列表")
 	public List<String> listByYearAndName( String year, String configName ) throws Exception {
 		if( year == null ){
@@ -88,14 +88,14 @@ public class AttendanceWorkDayConfigFactory extends AbstractFactory {
 		CriteriaQuery<String> cq = cb.createQuery(String.class);
 		Root<AttendanceWorkDayConfig> root = cq.from( AttendanceWorkDayConfig.class);
 		cq.select(root.get(AttendanceWorkDayConfig_.id));
-		
+
 		Predicate p = cb.equal( root.get(AttendanceWorkDayConfig_.configYear), year);
 		if( configName != null ){
 			p = cb.and( p, cb.equal( root.get(AttendanceWorkDayConfig_.configName), configName));
 		}
 		return em.createQuery(cq.where(p)).getResultList();
 	}
-	
+
 	//@MethodDescribe("根据节假日名称列示全部的AttendanceWorkDayConfig信息列表")
 	public List<String> listByName( String configName ) throws Exception {
 		EntityManager em = this.entityManagerContainer().get(AttendanceWorkDayConfig.class);
@@ -103,9 +103,9 @@ public class AttendanceWorkDayConfigFactory extends AbstractFactory {
 		CriteriaQuery<String> cq = cb.createQuery(String.class);
 		Root<AttendanceWorkDayConfig> root = cq.from( AttendanceWorkDayConfig.class);
 		cq.select(root.get(AttendanceWorkDayConfig_.id));
-		
+
 		Predicate p = cb.equal( root.get(AttendanceWorkDayConfig_.configName), configName);
-		
+
 		return em.createQuery(cq.where(p)).getResultList();
 	}
 
@@ -129,7 +129,8 @@ public class AttendanceWorkDayConfigFactory extends AbstractFactory {
 			isHoliday = true;
 			dateString = s_year + "-" + _month + "-" + (i<10?"0"+i:i);
 			//判断当天是否周末
-			if( !dateOperation.isWeekend( dateOperation.getDateFromString( dateString )) ){
+			//if( !dateOperation.isWeekend( dateOperation.getDateFromString( dateString )) ){
+			if( !attendanceSettingServiceAdv.isWeekend( dateOperation.getDateFromString( dateString )) ){
 				//如果不是周末
 				if( workDayConfigList != null && workDayConfigList.size() > 0 ){
 					//遍历所有的节假日配置进行判断,是否法定节假日
@@ -161,7 +162,7 @@ public class AttendanceWorkDayConfigFactory extends AbstractFactory {
 		}
 		return workDaysCountForMonth;
 	}
-	
+
 	//@MethodDescribe("根据节假日配置计算一个周期内的应出勤天数")
 	public Integer getWorkDaysCountForMonth( Date startDate, Date endDate, List<AttendanceWorkDayConfig> workDayConfigList ) throws Exception {
 		/**
@@ -174,14 +175,17 @@ public class AttendanceWorkDayConfigFactory extends AbstractFactory {
 		boolean isHoliday = true;
 		int workDaysCountForMonth = 0;
 		if( endDate.getTime() > new Date().getTime()){
-			endDate = new Date();
+			Calendar calendar = new GregorianCalendar();
+			calendar.setTime(new Date());
+			calendar.add(calendar.DATE,-1);
+			endDate = calendar.getTime();
 		}
 		List<String> dateStringList = dateOperation.listDateStringBetweenDate(startDate, endDate);
 		if( dateStringList != null && dateStringList.size() > 0 ){
 			workDaysCountForMonth = dateStringList.size();
 			for( String dateString : dateStringList){
 				//判断当天是否周末
-				if( !dateOperation.isWeekend( dateOperation.getDateFromString( dateString )) ){
+				if(!attendanceSettingServiceAdv.isWeekend( dateOperation.getDateFromString( dateString ))  ){
 					//如果不是周末
 					if( workDayConfigList != null && workDayConfigList.size() > 0 ){
 						//遍历所有的节假日配置进行判断,是否法定节假日

+ 16 - 3
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attachment/ActionExportAbnormalDetail.java

@@ -7,6 +7,7 @@ import java.util.List;
 import javax.servlet.http.HttpServletRequest;
 
 import com.x.base.core.project.tools.ListTools;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Sheet;
@@ -110,9 +111,21 @@ public class ActionExportAbnormalDetail extends BaseAction {
 			for (int i = 0; i < detailList.size(); i++) {
 				attendanceDetail = detailList.get(i);
 				row = sheet.createRow(i + 1);
-				row.createCell(0).setCellValue(attendanceDetail.getTopUnitName());
-				row.createCell(1).setCellValue(attendanceDetail.getUnitName());
-				row.createCell(2).setCellValue(attendanceDetail.getEmpName());
+				String topUnitName = attendanceDetail.getTopUnitName();
+				String unitName = attendanceDetail.getUnitName();
+				String empName = attendanceDetail.getEmpName();
+				if(StringUtils.isNotEmpty(topUnitName) && StringUtils.contains(topUnitName,"@")){
+					topUnitName = topUnitName.split("@")[0];
+				}
+				if(StringUtils.isNotEmpty(unitName) && StringUtils.contains(unitName,"@")){
+					unitName = unitName.split("@")[0];
+				}
+				if(StringUtils.isNotEmpty(empName) && StringUtils.contains(empName,"@")){
+					empName = empName.split("@")[0];
+				}
+				row.createCell(0).setCellValue(topUnitName);
+				row.createCell(1).setCellValue(unitName);
+				row.createCell(2).setCellValue(empName);
 				row.createCell(3).setCellValue(attendanceDetail.getRecordDateString());
 				if (attendanceDetail.getIsAbsent()) {
 					row.createCell(4).setCellValue("缺勤");

+ 333 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attachment/ActionExportDetailWithFilter.java

@@ -0,0 +1,333 @@
+package com.x.attendance.assemble.control.jaxrs.attachment;
+
+import com.google.gson.JsonElement;
+import com.x.attendance.assemble.control.Business;
+import com.x.attendance.assemble.control.ExceptionWrapInConvert;
+import com.x.attendance.assemble.control.jaxrs.attendancedetail.WrapInFilter;
+import com.x.attendance.assemble.control.service.AttendanceEmployeeConfigServiceAdv;
+import com.x.attendance.assemble.control.service.UserManagerService;
+import com.x.attendance.entity.*;
+import com.x.base.core.container.EntityManagerContainer;
+import com.x.base.core.container.factory.EntityManagerContainerFactory;
+import com.x.base.core.project.http.ActionResult;
+import com.x.base.core.project.http.EffectivePerson;
+import com.x.base.core.project.jaxrs.WoFile;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+import com.x.base.core.project.tools.DateTools;
+import com.x.base.core.project.tools.ListTools;
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 导出统计明细
+ */
+public class ActionExportDetailWithFilter extends BaseAction {
+	
+	private static  Logger logger = LoggerFactory.getLogger(ActionExportDetailWithFilter.class);
+	private UserManagerService userManagerService = new UserManagerService();
+	protected AttendanceEmployeeConfigServiceAdv attendanceEmployeeConfigServiceAdv = new AttendanceEmployeeConfigServiceAdv();
+	
+	protected ActionResult<Wo> execute( HttpServletRequest request, EffectivePerson effectivePerson,
+										String q_topUnitName,
+										String q_unitName,
+										String q_empName,
+										String cycleYear,
+										String cycleMonth,
+										String q_date,
+										String isAbsent,
+										String isLackOfTime,
+										String isLate,
+										Boolean stream) throws Exception {
+		ActionResult<Wo> result = new ActionResult<>();
+		EffectivePerson currentPerson = this.effectivePerson(request);
+		List<String> ids = null;
+		List<AttendanceDetail> detailList = null;
+		Workbook wb = null;
+		Wo wo = null;
+		String fileName = null;
+		String sheetName = null;
+		Boolean check = true;
+		WrapInFilter wrapIn = new WrapInFilter();
+
+		long total = 0;
+		List<String> topUnitNames = new ArrayList<String>();
+		List<String> unitNames = new ArrayList<String>();
+		List<String> topUnitNames_tmp = null;
+		List<String> unitNames_tmp = null;
+		AttendanceScheduleSetting scheduleSetting_top = null;
+		AttendanceScheduleSetting scheduleSetting = null;
+
+
+		List<String> unUnitNameList = new ArrayList<String>();
+		List<String> personNameList = new ArrayList<String>();
+
+		try {
+			if(StringUtils.isNotEmpty(q_topUnitName) && !StringUtils.equals(q_topUnitName,"0")){
+				wrapIn.setQ_topUnitName(q_topUnitName);
+			}
+			if(StringUtils.isNotEmpty(q_unitName)&& !StringUtils.equals(q_unitName,"0")){
+				wrapIn.setQ_unitName(q_unitName);
+			}
+			if(StringUtils.isNotEmpty(q_empName)&& !StringUtils.equals(q_empName,"0")){
+				wrapIn.setQ_empName(q_empName);
+				wrapIn.setKey("recordDateString");
+			}
+			if(StringUtils.isNotEmpty(cycleYear)&& !StringUtils.equals(cycleYear,"0")){
+				wrapIn.setCycleYear(cycleYear);
+			}
+			if(StringUtils.isNotEmpty(cycleMonth)&& !StringUtils.equals(cycleMonth,"0")){
+				wrapIn.setCycleMonth(cycleMonth);
+			}
+			if(StringUtils.isNotEmpty(q_date)&& !StringUtils.equals(q_date,"0")){
+				wrapIn.setQ_date(q_date);
+			}
+			if(!StringUtils.equals(isAbsent,"0")){
+				wrapIn.setIsAbsent(BooleanUtils.toBoolean(isAbsent));
+			}
+			if(!StringUtils.equals(isLackOfTime,"0")){
+				wrapIn.setIsLackOfTime(BooleanUtils.toBoolean(isLackOfTime));
+			}
+			if(!StringUtils.equals(isLate,"0")){
+				wrapIn.setIsLate(BooleanUtils.toBoolean(isLate));
+			}
+		} catch (Exception e) {
+			check = false;
+			logger.error(e, currentPerson, request, null);
+		}
+		if(check){
+				// 处理一下顶层组织,查询下级顶层组织
+				if ( StringUtils.isNotEmpty( wrapIn.getQ_topUnitName() )) {
+					topUnitNames.add(wrapIn.getQ_topUnitName());
+					scheduleSetting_top = attendanceScheduleSettingServiceAdv.getAttendanceScheduleSettingWithUnit(wrapIn.getQ_topUnitName(), effectivePerson.getDebugger() );
+					try {
+						topUnitNames_tmp = userManagerService.listSubUnitNameWithParent(wrapIn.getQ_topUnitName());
+					} catch (Exception e) {
+						Exception exception = new ExceptionAttendanceDetailProcess(e,
+								"根据顶层组织顶层组织列示所有下级组织名称发生异常!TopUnit:" + wrapIn.getQ_topUnitName());
+						result.error(exception);
+						logger.error(e, currentPerson, request, null);
+					}
+					if (topUnitNames_tmp != null && topUnitNames_tmp.size() > 0) {
+						for (String topUnitName : topUnitNames_tmp) {
+							topUnitNames.add(topUnitName);
+						}
+					}
+					wrapIn.setTopUnitNames(topUnitNames);
+				}
+
+				// 处理一下组织,查询下级组织
+				if ( StringUtils.isNotEmpty( wrapIn.getQ_unitName() )) {
+					unitNames.add(wrapIn.getQ_unitName());
+					scheduleSetting = attendanceScheduleSettingServiceAdv.getAttendanceScheduleSettingWithUnit(wrapIn.getQ_unitName(), effectivePerson.getDebugger() );
+					try {
+						unitNames_tmp = userManagerService.listSubUnitNameWithParent(wrapIn.getQ_unitName());
+					} catch (Exception e) {
+						Exception exception = new ExceptionAttendanceDetailProcess(e,
+								"根据组织名称列示所有下级组织名称发生异常!Unit:" + wrapIn.getQ_unitName());
+						result.error(exception);
+						logger.error(e, currentPerson, request, null);
+					}
+					if (unitNames_tmp != null && unitNames_tmp.size() > 0) {
+						for (String unitName : unitNames_tmp) {
+							unitNames.add(unitName);
+						}
+					}
+					wrapIn.setUnitNames(unitNames);
+				}
+
+		}
+		if (check ) {
+			unUnitNameList = getUnUnitNameList();
+			personNameList = getUnPersonNameList();
+			try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+				// 从数据库中查询符合条件的一页数据对象
+				Business business = new Business(emc);
+				/*ids = business.getAttendanceDetailFactory().listIdsWithFilterUn(wrapIn,unUnitNameList,personNameList);
+				detailList = business.getAttendanceDetailFactory().list(ids);*/
+				detailList = business.getAttendanceDetailFactory().listIdsWithFilterUn(wrapIn,unUnitNameList,personNameList);
+				logger.info("detailList======"+detailList.size() );
+			}catch (Exception e) {
+				logger.info("系统在查询符合条件的打卡记录时发生异常。" );
+				e.printStackTrace();
+			}
+
+		}
+		// 将结果组织成EXCEL		
+		if( check ) {
+			fileName = "统计打卡记录明细_"+ DateTools.formatDate(new Date())+".xls";
+			sheetName = "Sheet1";
+			wb = composeDetail( fileName, sheetName, detailList );
+		}
+		
+		if( check ) {
+			ByteArrayOutputStream bos = new ByteArrayOutputStream();
+			try {
+			    wb.write(bos);
+			    wo = new Wo(bos.toByteArray(), 
+						this.contentType(stream, fileName), 
+						this.contentDisposition(stream, fileName));
+			} finally {
+			    bos.close();
+			}
+		}		
+		result.setData(wo);
+		return result;
+	}
+
+	private Workbook composeDetail(String fileName, String sheetName, List<AttendanceDetail> detailList) {
+		AttendanceDetail attendanceDetail = null;
+		AttendanceAppealInfo attendanceAppealInfo = null;
+		
+		Workbook wb = new HSSFWorkbook();
+		Row row = null;
+		if (ListTools.isNotEmpty(detailList) ) {
+			// 创建新的表格
+			Sheet sheet = wb.createSheet(sheetName);
+			
+			// 先创建表头
+			row = sheet.createRow(0);
+			row.createCell(0).setCellValue("顶层组织名称");
+			row.createCell(1).setCellValue("组织名称");
+			row.createCell(2).setCellValue("姓名");
+			row.createCell(3).setCellValue("日期");
+			row.createCell(4).setCellValue("说明");
+			row.createCell(5).setCellValue("上午上班打卡时间");
+			row.createCell(6).setCellValue("上午下班打卡时间");
+			row.createCell(7).setCellValue("下午上班打卡时间");
+			row.createCell(8).setCellValue("下午下班打开时间");
+			row.createCell(9).setCellValue("考勤状态");
+			row.createCell(10).setCellValue("申诉状态");
+
+			for (int i = 0; i < detailList.size(); i++) {
+				attendanceDetail = detailList.get(i);
+				row = sheet.createRow(i + 1);
+				String topUnitName = attendanceDetail.getTopUnitName();
+				String unitName = attendanceDetail.getUnitName();
+				String empName = attendanceDetail.getEmpName();
+				if(StringUtils.isNotEmpty(topUnitName) && StringUtils.contains(topUnitName,"@")){
+					topUnitName = topUnitName.split("@")[0];
+				}
+				if(StringUtils.isNotEmpty(unitName) && StringUtils.contains(unitName,"@")){
+					unitName = unitName.split("@")[0];
+				}
+				if(StringUtils.isNotEmpty(empName) && StringUtils.contains(empName,"@")){
+					empName = empName.split("@")[0];
+				}
+				row.createCell(0).setCellValue(topUnitName);
+				row.createCell(1).setCellValue(unitName);
+				row.createCell(2).setCellValue(empName);
+				row.createCell(3).setCellValue(attendanceDetail.getRecordDateString());
+				row.createCell(4).setCellValue(attendanceDetail.getDescription());
+				row.createCell(5).setCellValue(attendanceDetail.getOnDutyTime());
+				row.createCell(6).setCellValue(attendanceDetail.getMorningOffDutyTime());
+				row.createCell(7).setCellValue(attendanceDetail.getAfternoonOnDutyTime());
+				row.createCell(8).setCellValue(attendanceDetail.getOffDutyTime());
+
+				if(attendanceDetail.getIsGetSelfHolidays()){
+					if(StringUtils.isNotEmpty(attendanceDetail.getLeaveType())){
+						row.createCell(9).setCellValue(attendanceDetail.getLeaveType());
+					}else{
+						row.createCell(9).setCellValue("请假或外出报备");
+					}
+				}else if (attendanceDetail.getIsAbsent()) {
+					row.createCell(9).setCellValue("缺勤");
+				} else if (attendanceDetail.getIsLackOfTime()) {
+					row.createCell(9).setCellValue("工时不足");
+				} else if (attendanceDetail.getIsAbnormalDuty()) {
+					row.createCell(9).setCellValue("异常打卡");
+				}else if(attendanceDetail.getIsLeaveEarlier()){
+					row.createCell(9).setCellValue("早退");
+				} else if (attendanceDetail.getIsLate()) {
+					row.createCell(9).setCellValue("迟到");
+				} else {
+					row.createCell(9).setCellValue("正常");
+				}
+
+				switch(attendanceDetail.getAppealStatus()){
+					case 1 :
+						row.createCell(10).setCellValue("申诉中");
+						break;
+					case -1 :
+						row.createCell(10).setCellValue("申诉未通过");
+						break;
+					case 9 :
+						row.createCell(10).setCellValue("申诉通过");
+						break;
+					default :
+						row.createCell(10).setCellValue("未申诉");
+				}
+			}
+		}
+		return wb;
+	}
+
+	/**
+	 * 获取不需要考勤的组织
+	 * @return
+	 * @throws Exception
+	 */
+	protected  List<String> getUnUnitNameList() throws Exception {
+		List<String> unUnitNameList = new ArrayList<String>();
+
+		List<AttendanceEmployeeConfig> attendanceEmployeeConfigs = attendanceEmployeeConfigServiceAdv.listByConfigType("NOTREQUIRED");
+
+		if(ListTools.isNotEmpty(attendanceEmployeeConfigs)){
+			for (AttendanceEmployeeConfig attendanceEmployeeConfig : attendanceEmployeeConfigs) {
+				String unitName = attendanceEmployeeConfig.getUnitName();
+				String employeeName = attendanceEmployeeConfig.getEmployeeName();
+
+				if(StringUtils.isEmpty(employeeName) && StringUtils.isNotEmpty(unitName)){
+					unUnitNameList.add(unitName);
+					List<String> tempUnitNameList = userManagerService.listSubUnitNameWithParent(unitName);
+					if(ListTools.isNotEmpty(tempUnitNameList)){
+						for(String tempUnit:tempUnitNameList){
+							if(!ListTools.contains(unUnitNameList, tempUnit)){
+								unUnitNameList.add(tempUnit);
+							}
+						}
+					}
+				}
+			}
+		}
+		return unUnitNameList;
+	}
+
+	/**
+	 * 获取不需要考勤的人员
+	 * @return
+	 * @throws Exception
+	 */
+	protected  List<String> getUnPersonNameList() throws Exception {
+		List<String> personNameList = new ArrayList<String>();
+		List<AttendanceEmployeeConfig> attendanceEmployeeConfigs = attendanceEmployeeConfigServiceAdv.listByConfigType("NOTREQUIRED");
+
+		if(ListTools.isNotEmpty(attendanceEmployeeConfigs)){
+			for (AttendanceEmployeeConfig attendanceEmployeeConfig : attendanceEmployeeConfigs) {
+				String employeeName = attendanceEmployeeConfig.getEmployeeName();
+
+				if(StringUtils.isNotEmpty(employeeName) && !ListTools.contains(personNameList, employeeName)){
+					personNameList.add(employeeName);
+				}
+			}
+		}
+		return personNameList;
+	}
+
+	public static class Wo extends WoFile {
+		public Wo(byte[] bytes, String contentType, String contentDisposition) {
+			super(bytes, contentType, contentDisposition);
+		}
+	}
+
+}

+ 6 - 1
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attachment/ActionExportHolidayDetail.java

@@ -6,6 +6,7 @@ import java.util.List;
 
 import javax.servlet.http.HttpServletRequest;
 
+import org.apache.commons.lang3.StringUtils;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Sheet;
@@ -126,9 +127,13 @@ public class ActionExportHolidayDetail extends BaseAction {
 						attendanceSelfHoliday = holidayList.get(i);
 						if( attendanceSelfHoliday != null ){
 							row = sheet.createRow(i + 1);
+							String empName = attendanceSelfHoliday.getEmployeeName();
+							if(StringUtils.isNotEmpty(empName) && StringUtils.contains(empName,"@")){
+								empName = empName.split("@")[0];
+							}
 							row.createCell(0).setCellValue(attendanceSelfHoliday.getTopUnitName());
 							row.createCell(1).setCellValue(attendanceSelfHoliday.getUnitName());
-							row.createCell(2).setCellValue(attendanceSelfHoliday.getEmployeeName());
+							row.createCell(2).setCellValue(empName);
 							row.createCell(3).setCellValue(attendanceSelfHoliday.getLeaveType());
 							
 							if( attendanceSelfHoliday.getStartTime() != null ){

+ 163 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attachment/ActionExportPersonStatistic.java

@@ -0,0 +1,163 @@
+package com.x.attendance.assemble.control.jaxrs.attachment;
+
+import com.x.attendance.entity.StatisticPersonForMonth;
+import com.x.base.core.project.http.ActionResult;
+import com.x.base.core.project.http.EffectivePerson;
+import com.x.base.core.project.jaxrs.WoFile;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+import com.x.base.core.project.tools.ListTools;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.ByteArrayOutputStream;
+import java.util.List;
+
+public class ActionExportPersonStatistic extends BaseAction {
+	
+	private static  Logger logger = LoggerFactory.getLogger(ActionExportPersonStatistic.class);
+	
+	protected ActionResult<Wo> execute( HttpServletRequest request, EffectivePerson effectivePerson, String name, String year, String month ,Boolean stream ) throws Exception {
+			ActionResult<Wo> result = new ActionResult<>();
+			List<String> ids = null;
+			List<StatisticPersonForMonth> statisticPersonForMonth_list = null;
+			Workbook wb = null;
+			Wo wo = null;
+			String fileName = null;
+			String sheetName = null;
+			Boolean check = true;
+
+			if ("(0)".equals(year)) {
+				year = null;
+			}
+			if ("(0)".equals(month)) {
+				month = null;
+			}
+			if( check ){
+				if( name == null || name.isEmpty() ){
+					check = false;
+					Exception exception = new ExceptionPersonNameEmpty();
+					result.error( exception );
+				}
+			}
+
+			if( check ){
+				try {
+					ids = attendanceStatisticServiceAdv.listPersonForMonthByUserYearAndMonth(name, year, month);
+				} catch (Exception e) {
+					check = false;
+					Exception exception = new ExceptionAttendanceStatisticProcess( e,
+							"系统根据人员姓名,年份和月份查询统计数据信息ID列表时发生异常.Name:"+name+", Year:"+year+", Month:" + month
+					);
+					result.error( exception );
+					logger.error( e, effectivePerson, request, null);
+				}
+			}
+			if( check ){
+				if( ids != null && !ids.isEmpty() ){
+					try {
+						statisticPersonForMonth_list = attendanceStatisticServiceAdv.listPersonForMonth(ids);
+					} catch (Exception e) {
+						check = false;
+						Exception exception = new ExceptionAttendanceStatisticProcess( e, "系统根据ID列表查询个人每月统计数据信息列表时发生异常." );
+						result.error( exception );
+						logger.error( e, effectivePerson, request, null);
+					}
+				}
+			}
+			
+			// 将结果组织成EXCEL		
+			if( check ) {
+				if(StringUtils.isNotEmpty(name) && StringUtils.contains(name,"@")){
+					fileName = "" + name.split("@")[0] + "的个人出勤率统计记录_"+year+"年"+month+"月.xls";
+				}else{
+					fileName = "" + name + "的个人出勤率统计记录_"+year+"年"+month+"月.xls";
+				}
+				sheetName = "个人出勤率统计记录";
+				wb = composeDetail( fileName, sheetName, statisticPersonForMonth_list );
+			}
+			
+			//输出数据信息
+			if( check ) {
+				ByteArrayOutputStream bos = new ByteArrayOutputStream();
+				try {
+				    wb.write(bos);
+				    wo = new Wo(bos.toByteArray(), 
+							this.contentType(stream, fileName), 
+							this.contentDisposition(stream, fileName));
+				} finally {
+				    bos.close();
+				}
+			}		
+			result.setData(wo);
+			return result;
+		}
+
+		private Workbook composeDetail(String fileName, String sheetName, List<StatisticPersonForMonth> statisticPersonForMonth_list) throws Exception {
+			Workbook wb = new HSSFWorkbook();
+			Row row = null;
+			if (ListTools.isNotEmpty(statisticPersonForMonth_list)) {
+				// 创建新的表格
+				Sheet sheet = wb.createSheet(sheetName);
+				// 先创建表头
+				row = sheet.createRow(0);
+				row.createCell(0).setCellValue("顶层组织名称");
+				row.createCell(1).setCellValue("组织名称");
+				row.createCell(2).setCellValue("姓名");
+				row.createCell(3).setCellValue("月份");
+				row.createCell(4).setCellValue("上班打卡次数");
+				row.createCell(5).setCellValue("下班打卡次数");
+				row.createCell(6).setCellValue("出勤人天数");
+				row.createCell(7).setCellValue("请假或外出报备人天数");
+				row.createCell(8).setCellValue("缺勤人天数");
+				row.createCell(9).setCellValue("迟到次数");
+				row.createCell(10).setCellValue("工时不足人次");
+				row.createCell(11).setCellValue("异常打卡人次");
+
+				logger.info("一共有"+statisticPersonForMonth_list.size()+"条请求记录可以输出。");
+				for (int i = 0; i < statisticPersonForMonth_list.size(); i++) {
+					StatisticPersonForMonth statisticPersonForMonth = null;
+					statisticPersonForMonth = statisticPersonForMonth_list.get(i);
+					if( statisticPersonForMonth != null ){
+						row = sheet.createRow(i + 1);
+						String topUnitName = statisticPersonForMonth.getTopUnitName();
+						String unitName = statisticPersonForMonth.getUnitName();
+						String empName = statisticPersonForMonth.getEmployeeName();
+						if(StringUtils.isNotEmpty(topUnitName) && StringUtils.contains(topUnitName,"@")){
+							topUnitName = topUnitName.split("@")[0];
+						}
+						if(StringUtils.isNotEmpty(unitName) && StringUtils.contains(unitName,"@")){
+							unitName = unitName.split("@")[0];
+						}
+						if(StringUtils.isNotEmpty(empName) && StringUtils.contains(empName,"@")){
+							empName = empName.split("@")[0];
+						}
+						row.createCell(0).setCellValue(topUnitName);
+						row.createCell(1).setCellValue(unitName);
+						row.createCell(2).setCellValue(empName);
+						row.createCell(3).setCellValue(statisticPersonForMonth.getStatisticYear()+"-"+statisticPersonForMonth.getStatisticMonth());
+						row.createCell(4).setCellValue(statisticPersonForMonth.getOnDutyTimes());
+						row.createCell(5).setCellValue(statisticPersonForMonth.getOnDutyTimes());
+						row.createCell(6).setCellValue(statisticPersonForMonth.getOnDutyDayCount());
+						row.createCell(7).setCellValue(statisticPersonForMonth.getOnSelfHolidayCount());
+						row.createCell(8).setCellValue(statisticPersonForMonth.getAbsenceDayCount());
+						row.createCell(9).setCellValue(statisticPersonForMonth.getLateTimes());
+						row.createCell(10).setCellValue(statisticPersonForMonth.getLackOfTimeCount());
+						row.createCell(11).setCellValue(statisticPersonForMonth.getAbNormalDutyCount());
+					}
+				}
+
+			}
+			return wb;
+		}
+
+		public static class Wo extends WoFile {
+			public Wo(byte[] bytes, String contentType, String contentDisposition) {
+				super(bytes, contentType, contentDisposition);
+			}
+		}
+	}

+ 172 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attachment/ActionExportTopUnitStatistic.java

@@ -0,0 +1,172 @@
+package com.x.attendance.assemble.control.jaxrs.attachment;
+
+import com.x.attendance.entity.StatisticUnitForMonth;
+import com.x.base.core.project.http.ActionResult;
+import com.x.base.core.project.http.EffectivePerson;
+import com.x.base.core.project.jaxrs.WoFile;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+import com.x.base.core.project.tools.ListTools;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ActionExportTopUnitStatistic extends BaseAction {
+	
+	private static  Logger logger = LoggerFactory.getLogger(ActionExportTopUnitStatistic.class);
+	
+	protected ActionResult<Wo> execute( HttpServletRequest request, EffectivePerson effectivePerson, String name, String year, String month ,Boolean stream ) throws Exception {
+			ActionResult<Wo> result = new ActionResult<>();
+			List<String> ids = null;
+			List<String> unitNames = new ArrayList<String>();
+			List<StatisticUnitForMonth> statisticUnitForMonth_list = null;
+			Workbook wb = null;
+			Wo wo = null;
+			String fileName = null;
+			String sheetName = null;
+			Boolean check = true;
+
+			if ("(0)".equals(year)) {
+				year = null;
+			}
+			if ("(0)".equals(month)) {
+				month = null;
+			}
+			if( check ){
+				if( name == null || name.isEmpty() ){
+					check = false;
+					Exception exception = new ExceptionTopUnitNameEmpty();
+					result.error( exception );
+				}
+			}
+			if( check ){
+				try {
+					//根据顶层组织递归查询下级顶层组织经及顶层组织的顶级组织
+					unitNames = getTopUnitNameList( name, unitNames, effectivePerson.getDebugger() );
+				} catch (Exception e) {
+					check = false;
+					Exception exception = new ExceptionAttendanceStatisticProcess( e,
+							"根据顶层组织递归查询下级顶层组织经及顶层组织的顶级组织时发生异常!"
+									+ "Name:" + name
+									+ ", Units:" + unitNames );
+					result.error( exception );
+					logger.error( e, effectivePerson, request, null);
+				}
+			}
+			if( check ){
+				if( unitNames == null ){
+					unitNames = new ArrayList<>();
+				}
+				unitNames.add( name );
+			}
+			if( check ){
+				try {
+					ids = attendanceStatisticServiceAdv.listUnitForMonthByUnitYearAndMonth( unitNames, year, month);
+				} catch (Exception e) {
+					check = false;
+					Exception exception = new ExceptionAttendanceStatisticProcess(e,
+							"系统根据组织名称列表,年份和月份查询组织统计数据信息ID列表时发生异常.Name:"+unitNames+", Year:"+year+", Month:" + month
+					);
+					result.error( exception );
+					logger.error( e, effectivePerson, request, null);
+				}
+			}
+			if( check ){
+				if( ids != null && !ids.isEmpty() ){
+					try {
+						statisticUnitForMonth_list = attendanceStatisticServiceAdv.listUnitForMonth( ids );
+					} catch (Exception e) {
+						check = false;
+						Exception exception = new ExceptionAttendanceStatisticProcess( e, "系统根据ID列表查询组织每月统计数据信息列表时发生异常." );
+						result.error( exception );
+						logger.error( e, effectivePerson, request, null);
+					}
+				}
+			}
+			
+			// 将结果组织成EXCEL		
+			if( check ) {
+				if(StringUtils.isNotEmpty(name) && StringUtils.contains(name,"@")){
+					fileName = "" + name.split("@")[0] + "的出勤率统计记录_"+year+"年"+month+"月.xls";
+				}else{
+					fileName = "" + name + "的出勤率统计记录_"+year+"年"+month+"月.xls";
+				}
+				sheetName = "公司出勤率统计记录";
+				wb = composeDetail( fileName, sheetName, statisticUnitForMonth_list );
+			}
+			
+			//输出数据信息
+			if( check ) {
+				ByteArrayOutputStream bos = new ByteArrayOutputStream();
+				try {
+				    wb.write(bos);
+				    wo = new Wo(bos.toByteArray(), 
+							this.contentType(stream, fileName), 
+							this.contentDisposition(stream, fileName));
+				} finally {
+				    bos.close();
+				}
+			}		
+			result.setData(wo);
+			return result;
+		}
+
+		private Workbook composeDetail(String fileName, String sheetName, List<StatisticUnitForMonth> statisticUnitForMonth_list) throws Exception {
+			Workbook wb = new HSSFWorkbook();
+			Row row = null;
+			if (ListTools.isNotEmpty(statisticUnitForMonth_list)) {
+				// 创建新的表格
+				Sheet sheet = wb.createSheet(sheetName);
+				// 先创建表头
+				row = sheet.createRow(0);
+				row.createCell(0).setCellValue("公司");
+				row.createCell(1).setCellValue("月份");
+				row.createCell(2).setCellValue("上班打卡次数");
+				row.createCell(3).setCellValue("下班打卡次数");
+				row.createCell(4).setCellValue("出勤人天数");
+				row.createCell(5).setCellValue("请假或外出报备人天数");
+				row.createCell(6).setCellValue("缺勤人天数");
+				row.createCell(7).setCellValue("迟到次数");
+				row.createCell(8).setCellValue("工时不足人次");
+				row.createCell(9).setCellValue("异常打卡人次");
+
+				logger.info("一共有"+statisticUnitForMonth_list.size()+"条请求记录可以输出。");
+				for (int i = 0; i < statisticUnitForMonth_list.size(); i++) {
+					StatisticUnitForMonth statisticUnitForMonth = null;
+					statisticUnitForMonth = statisticUnitForMonth_list.get(i);
+					if( statisticUnitForMonth != null ){
+						row = sheet.createRow(i + 1);
+						String topUnitName = statisticUnitForMonth.getTopUnitName();
+						if(StringUtils.isNotEmpty(topUnitName) && StringUtils.contains(topUnitName,"@")){
+							topUnitName = topUnitName.split("@")[0];
+						}
+						row.createCell(0).setCellValue(topUnitName);
+						row.createCell(1).setCellValue(statisticUnitForMonth.getStatisticYear()+"-"+statisticUnitForMonth.getStatisticMonth());
+						row.createCell(2).setCellValue(statisticUnitForMonth.getOnDutyCount());
+						row.createCell(3).setCellValue(statisticUnitForMonth.getOffDutyCount());
+						row.createCell(4).setCellValue(statisticUnitForMonth.getOnDutyEmployeeCount());
+						row.createCell(5).setCellValue(statisticUnitForMonth.getOnSelfHolidayCount());
+						row.createCell(6).setCellValue(statisticUnitForMonth.getAbsenceDayCount());
+						row.createCell(7).setCellValue(statisticUnitForMonth.getLateCount());
+						row.createCell(8).setCellValue(statisticUnitForMonth.getLackOfTimeCount());
+						row.createCell(9).setCellValue(statisticUnitForMonth.getAbNormalDutyCount());
+					}
+				}
+			}
+			return wb;
+		}
+
+
+		public static class Wo extends WoFile {
+			public Wo(byte[] bytes, String contentType, String contentDisposition) {
+				super(bytes, contentType, contentDisposition);
+			}
+		}
+	}

+ 190 - 0
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attachment/ActionExportUnitSubNestedStatistic.java

@@ -0,0 +1,190 @@
+package com.x.attendance.assemble.control.jaxrs.attachment;
+
+import com.x.attendance.entity.StatisticPersonForMonth;
+import com.x.base.core.project.http.ActionResult;
+import com.x.base.core.project.http.EffectivePerson;
+import com.x.base.core.project.jaxrs.WoFile;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+import com.x.base.core.project.tools.ListTools;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ActionExportUnitSubNestedStatistic extends BaseAction {
+	
+	private static  Logger logger = LoggerFactory.getLogger(ActionExportUnitSubNestedStatistic.class);
+	
+	protected ActionResult<Wo> execute( HttpServletRequest request, EffectivePerson effectivePerson, String name, String year, String month ,Boolean stream ) throws Exception {
+			ActionResult<Wo> result = new ActionResult<>();
+			List<String> ids = null;
+			List<String> unitNameList = new ArrayList<String>();
+			List<String> unUnitNameList = new ArrayList<String>();
+			List<String> personNameList = new ArrayList<String>();
+			List<StatisticPersonForMonth> statisticPersonForMonth_list = null;
+			Workbook wb = null;
+			Wo wo = null;
+			String fileName = null;
+			String sheetName = null;
+			Boolean check = true;
+
+			if ("(0)".equals(year)) {
+				year = null;
+			}
+			if ("(0)".equals(month)) {
+				month = null;
+			}
+			if( check ){
+				if( name == null || name.isEmpty() ){
+					check = false;
+					Exception exception = new ExceptionQueryStatisticUnitNameEmpty();
+					result.error( exception );
+				}
+			}
+			if( check ){
+				try {
+					unitNameList = userManagerService.listSubUnitNameWithParent( name );
+				} catch (Exception e) {
+					check = false;
+					Exception exception = new ExceptionAttendanceStatisticProcess( e, "根据组织名称列示所有下级组织名称发生异常!Unit:" + name );
+					result.error( exception );
+					logger.error( e, effectivePerson, request, null);
+				}
+			}
+			if( check ){
+				if( unitNameList == null ){
+					unitNameList = new ArrayList<>();
+				}
+				unitNameList.add( name );
+				unUnitNameList = getUnUnitNameList();
+				personNameList = getUnPersonNameList();
+				logger.info("ActionShowStForPersonInUnitSubNested____unitNameList="+unitNameList);
+				logger.info("ActionShowStForPersonInUnitSubNested____unUnitNameList="+unUnitNameList);
+				logger.info("ActionShowStForPersonInUnitSubNested____personNameList="+personNameList);
+			}
+			if( check ){
+				try {
+					//ids = attendanceStatisticServiceAdv.listPersonForMonthByUnitYearAndMonth( unitNameList, year, month);
+					ids = attendanceStatisticServiceAdv.listPersonForMonthByUnitYearMonthAndUn( unitNameList, unUnitNameList,personNameList,year, month);
+					logger.info("ActionShowStForPersonInUnitSubNested____ids="+ids.size());
+				} catch (Exception e) {
+					check = false;
+					Exception exception = new ExceptionAttendanceStatisticProcess(e,
+							"系统根据组织名称列表,年份和月份查询个人统计数据信息ID列表时发生异常.Name:"+unitNameList+", Year:"+year+", Month:" + month
+					);
+					result.error( exception );
+					logger.error( e, effectivePerson, request, null);
+				}
+			}
+			if( check ){
+				if( ids != null && !ids.isEmpty() ){
+					try {
+						statisticPersonForMonth_list = attendanceStatisticServiceAdv.listPersonForMonth( ids );
+					} catch (Exception e) {
+						check = false;
+						Exception exception = new ExceptionAttendanceStatisticProcess( e, "系统根据ID列表查询个人每月统计数据信息列表时发生异常." );
+						result.error( exception );
+						logger.error( e, effectivePerson, request, null);
+					}
+				}
+			}
+			
+			// 将结果组织成EXCEL		
+			if( check ) {
+				if(StringUtils.isNotEmpty(name) && StringUtils.contains(name,"@")){
+					fileName = "" + name.split("@")[0] + "的部门出勤率统计记录_"+year+"年"+month+"月.xls";
+				}else{
+					fileName = "" + name + "的部门出勤率统计记录_"+year+"年"+month+"月.xls";
+				}
+				sheetName = "部门出勤率统计记录";
+				wb = composeDetail( fileName, sheetName, statisticPersonForMonth_list );
+			}
+			
+			//输出数据信息
+			if( check ) {
+				ByteArrayOutputStream bos = new ByteArrayOutputStream();
+				try {
+				    wb.write(bos);
+				    wo = new Wo(bos.toByteArray(), 
+							this.contentType(stream, fileName), 
+							this.contentDisposition(stream, fileName));
+				} finally {
+				    bos.close();
+				}
+			}		
+			result.setData(wo);
+			return result;
+		}
+
+		private Workbook composeDetail(String fileName, String sheetName, List<StatisticPersonForMonth> statisticPersonForMonth_list) throws Exception {
+			Workbook wb = new HSSFWorkbook();
+			Row row = null;
+			if (ListTools.isNotEmpty(statisticPersonForMonth_list)) {
+				// 创建新的表格
+				Sheet sheet = wb.createSheet(sheetName);
+				// 先创建表头
+				row = sheet.createRow(0);
+				row.createCell(0).setCellValue("顶层组织名称");
+				row.createCell(1).setCellValue("组织名称");
+				row.createCell(2).setCellValue("姓名");
+				row.createCell(3).setCellValue("月份");
+				row.createCell(4).setCellValue("上班打卡次数");
+				row.createCell(5).setCellValue("下班打卡次数");
+				row.createCell(6).setCellValue("出勤人天数");
+				row.createCell(7).setCellValue("请假或外出报备人天数");
+				row.createCell(8).setCellValue("缺勤人天数");
+				row.createCell(9).setCellValue("迟到次数");
+				row.createCell(10).setCellValue("工时不足人次");
+				row.createCell(11).setCellValue("异常打卡人次");
+
+				logger.info("一共有"+statisticPersonForMonth_list.size()+"条请求记录可以输出。");
+				for (int i = 0; i < statisticPersonForMonth_list.size(); i++) {
+					StatisticPersonForMonth statisticPersonForMonth = null;
+					statisticPersonForMonth = statisticPersonForMonth_list.get(i);
+					if( statisticPersonForMonth != null ){
+						row = sheet.createRow(i + 1);
+						String topUnitName = statisticPersonForMonth.getTopUnitName();
+						String unitName = statisticPersonForMonth.getUnitName();
+						String empName = statisticPersonForMonth.getEmployeeName();
+						if(StringUtils.isNotEmpty(topUnitName) && StringUtils.contains(topUnitName,"@")){
+							topUnitName = topUnitName.split("@")[0];
+						}
+						if(StringUtils.isNotEmpty(unitName) && StringUtils.contains(unitName,"@")){
+							unitName = unitName.split("@")[0];
+						}
+						if(StringUtils.isNotEmpty(empName) && StringUtils.contains(empName,"@")){
+							empName = empName.split("@")[0];
+						}
+						row.createCell(0).setCellValue(topUnitName);
+						row.createCell(1).setCellValue(unitName);
+						row.createCell(2).setCellValue(empName);
+						row.createCell(3).setCellValue(statisticPersonForMonth.getStatisticYear()+"-"+statisticPersonForMonth.getStatisticMonth());
+						row.createCell(4).setCellValue(statisticPersonForMonth.getOnDutyTimes());
+						row.createCell(5).setCellValue(statisticPersonForMonth.getOnDutyTimes());
+						row.createCell(6).setCellValue(statisticPersonForMonth.getOnDutyDayCount());
+						row.createCell(7).setCellValue(statisticPersonForMonth.getOnSelfHolidayCount());
+						row.createCell(8).setCellValue(statisticPersonForMonth.getAbsenceDayCount());
+						row.createCell(9).setCellValue(statisticPersonForMonth.getLateTimes());
+						row.createCell(10).setCellValue(statisticPersonForMonth.getLackOfTimeCount());
+						row.createCell(11).setCellValue(statisticPersonForMonth.getAbNormalDutyCount());
+					}
+				}
+
+			}
+			return wb;
+		}
+
+
+		public static class Wo extends WoFile {
+			public Wo(byte[] bytes, String contentType, String contentDisposition) {
+				super(bytes, contentType, contentDisposition);
+			}
+		}
+	}

+ 157 - 1
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attachment/BaseAction.java

@@ -1,11 +1,167 @@
 package com.x.attendance.assemble.control.jaxrs.attachment;
 
 import com.x.attendance.assemble.common.date.DateOperation;
-import com.x.attendance.assemble.control.service.AttendanceImportFileInfoServiceAdv;
+import com.x.attendance.assemble.control.service.*;
+import com.x.attendance.entity.AttendanceEmployeeConfig;
+import com.x.base.core.container.EntityManagerContainer;
+import com.x.base.core.container.factory.EntityManagerContainerFactory;
 import com.x.base.core.project.jaxrs.StandardJaxrsAction;
+import com.x.base.core.project.logger.Logger;
+import com.x.base.core.project.logger.LoggerFactory;
+import com.x.base.core.project.tools.ListTools;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
 
 public class BaseAction extends StandardJaxrsAction {
+	protected Logger logger = LoggerFactory.getLogger(BaseAction.class );
 	protected AttendanceImportFileInfoServiceAdv importFileInfoServiceAdv = new AttendanceImportFileInfoServiceAdv();
+	protected AttendanceStatisticServiceAdv attendanceStatisticServiceAdv = new AttendanceStatisticServiceAdv();
+	protected UserManagerService userManagerService = new UserManagerService();
+	protected AttendanceScheduleSettingServiceAdv attendanceScheduleSettingServiceAdv = new AttendanceScheduleSettingServiceAdv();
+	protected AttendanceEmployeeConfigServiceAdv attendanceEmployeeConfigServiceAdv = new AttendanceEmployeeConfigServiceAdv();
 	protected DateOperation dateOperation = new DateOperation();
+
+	// 根据组织递归查询下级组织
+	protected List<String> getUnitNameList(String unitName, List<String> unitNameList ) throws Exception {
+		if (unitNameList == null) {
+			unitNameList = new ArrayList<String>();
+		}
+		if ( StringUtils.isNotEmpty( unitName ) && !unitNameList.contains(unitName.trim())) {
+			unitNameList.add(unitName.trim());
+
+			// 查询该组织的下级组织
+			List<String> unitList = null;
+			try {
+				// 对查询的unit进行解析,如果有下级组织的,全部解析出来
+				unitList = userManagerService.listSubUnitNameWithParent( unitName );
+			} catch (Exception e) {
+				throw e;
+			}
+			if (unitList != null && unitList.size() > 0) {
+				for (String unit : unitList) {
+					getUnitNameList( unit, unitNameList);
+				}
+			}
+		}
+		return unitNameList;
+	}
+
+	// 根据顶层组织递归查询下级顶层组织经及顶层组织的顶级组织
+	protected List<String> getTopUnitNameList( String topUnitName, List<String> unitNameList, Boolean debugger )
+			throws Exception {
+		if (unitNameList == null) {
+			unitNameList = new ArrayList<String>();
+		}
+		if ( StringUtils.isNotEmpty( topUnitName ) && !unitNameList.contains(topUnitName.trim())) {
+			unitNameList.add( topUnitName.trim() );
+
+			// 查询该组织的下级组织
+			List<String> unitList = null;
+			try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create();) {
+				// 查询所有的Top组织
+				unitList = userManagerService.listSubUnitNameWithParent(topUnitName);
+				if ( unitList != null && unitList.size() > 0 ) {
+					logger.debug( debugger, ">>>>>>>>>>根据顶层组织名称["+topUnitName+"]查询到"+unitList.size()+"个顶级组织。");
+					for ( String unit : unitList) {
+						if (unitNameList.contains( unit )) {
+							unitNameList.add( unit );
+						}
+						getUnitNameList( unit, unitNameList );
+					}
+				} else {
+					logger.debug( debugger, ">>>>>>>>>>根据顶层组织名称["+topUnitName+"]未查询到任何顶级组织。");
+				}
+			} catch (Exception e) {
+				throw e;
+			}
+		}
+		return unitNameList;
+	}
+
+	/**
+	 * 根据List<String> topUnitNameList, List<String> unitNameList
+	 * 查询所有符合查询范围所组织以及下级组织名称列表
+	 *
+	 * @param topUnitNameList
+	 * @param unitNameList
+	 * @return
+	 * @throws Exception
+	 */
+	protected List<String> getUnitNameList(List<String> topUnitNameList, List<String> unitNameList, Boolean debugger )
+			throws Exception {
+
+		if (unitNameList == null) {
+			unitNameList = new ArrayList<String>();
+		}
+		// 先查询顶层组织所有的下属组织,全部加入List中
+		// 对查询的unit进行解析,如果有下级组织的,全部解析出来
+		if (topUnitNameList != null && topUnitNameList.size() > 0) {
+			for (String topUnitName : topUnitNameList) {
+				// 再递归查询所有的下级顶层组织以及所有组织
+				getTopUnitNameList(topUnitName, unitNameList, debugger );
+			}
+		}
+		// 对查询的unit进行解析,如果有下级组织的,全部解析出来
+		if (unitNameList != null && unitNameList.size() > 0) {
+			for (String unitName : unitNameList) {
+				getUnitNameList(unitName, unitNameList);
+			}
+		}
+		return unitNameList;
+	}
+
+	/**
+	 * 获取不需要考勤的组织
+	 * @return
+	 * @throws Exception
+	 */
+	protected  List<String> getUnUnitNameList() throws Exception {
+		List<String> unUnitNameList = new ArrayList<String>();
+
+		List<AttendanceEmployeeConfig> attendanceEmployeeConfigs = attendanceEmployeeConfigServiceAdv.listByConfigType("NOTREQUIRED");
+
+		if(ListTools.isNotEmpty(attendanceEmployeeConfigs)){
+			for (AttendanceEmployeeConfig attendanceEmployeeConfig : attendanceEmployeeConfigs) {
+				String unitName = attendanceEmployeeConfig.getUnitName();
+				String employeeName = attendanceEmployeeConfig.getEmployeeName();
+
+				if(StringUtils.isEmpty(employeeName) && StringUtils.isNotEmpty(unitName)){
+					unUnitNameList.add(unitName);
+					List<String> tempUnitNameList = userManagerService.listSubUnitNameWithParent(unitName);
+					if(ListTools.isNotEmpty(tempUnitNameList)){
+						for(String tempUnit:tempUnitNameList){
+							if(!ListTools.contains(unUnitNameList, tempUnit)){
+								unUnitNameList.add(tempUnit);
+							}
+						}
+					}
+				}
+			}
+		}
+		return unUnitNameList;
+	}
+
+	/**
+	 * 获取不需要考勤的人员
+	 * @return
+	 * @throws Exception
+	 */
+	protected  List<String> getUnPersonNameList() throws Exception {
+		List<String> personNameList = new ArrayList<String>();
+		List<AttendanceEmployeeConfig> attendanceEmployeeConfigs = attendanceEmployeeConfigServiceAdv.listByConfigType("NOTREQUIRED");
+
+		if(ListTools.isNotEmpty(attendanceEmployeeConfigs)){
+			for (AttendanceEmployeeConfig attendanceEmployeeConfig : attendanceEmployeeConfigs) {
+				String employeeName = attendanceEmployeeConfig.getEmployeeName();
+
+				if(StringUtils.isNotEmpty(employeeName) && !ListTools.contains(personNameList, employeeName)){
+					personNameList.add(employeeName);
+				}
+			}
+		}
+		return personNameList;
+	}
 }
 

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

@@ -0,0 +1,16 @@
+package com.x.attendance.assemble.control.jaxrs.attachment;
+
+import com.x.base.core.project.exception.PromptException;
+
+class ExceptionAttendanceDetailProcess extends PromptException {
+
+	private static final long serialVersionUID = 1859164370743532895L;
+
+	public ExceptionAttendanceDetailProcess(Throwable e, String message ) {
+		super("用户在进行考勤打卡数据信息处理时发生异常!message:" + message, e );
+	}
+	
+	public ExceptionAttendanceDetailProcess(String message ) {
+		super("用户在进行考勤打卡数据信息处理时发生异常!message:" + message );
+	}
+}

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

@@ -0,0 +1,12 @@
+package com.x.attendance.assemble.control.jaxrs.attachment;
+
+import com.x.base.core.project.exception.PromptException;
+
+class ExceptionAttendanceStatisticProcess extends PromptException {
+
+	private static final long serialVersionUID = 1859164370743532895L;
+
+	public ExceptionAttendanceStatisticProcess(Throwable e, String message ) {
+		super("用户在进行考勤统计处理时发生异常!message:" + message, e );
+	}
+}

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

@@ -0,0 +1,12 @@
+package com.x.attendance.assemble.control.jaxrs.attachment;
+
+import com.x.base.core.project.exception.PromptException;
+
+class ExceptionPersonNameEmpty extends PromptException {
+
+	private static final long serialVersionUID = 1859164370743532895L;
+
+	public ExceptionPersonNameEmpty() {
+		super("系统未获取到查询参数员工姓名name,无法进行数据查询");
+	}
+}

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

@@ -0,0 +1,12 @@
+package com.x.attendance.assemble.control.jaxrs.attachment;
+
+import com.x.base.core.project.exception.PromptException;
+
+class ExceptionQueryStatisticUnitNameEmpty extends PromptException {
+
+	private static final long serialVersionUID = 1859164370743532895L;
+
+	public ExceptionQueryStatisticUnitNameEmpty() {
+		super("系统未获取到查询参数组织名称name,无法进行数据查询");
+	}
+}

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

@@ -0,0 +1,12 @@
+package com.x.attendance.assemble.control.jaxrs.attachment;
+
+import com.x.base.core.project.exception.PromptException;
+
+class ExceptionTopUnitNameEmpty extends PromptException {
+
+	private static final long serialVersionUID = 1859164370743532895L;
+
+	public ExceptionTopUnitNameEmpty() {
+		super("系统未获取到查询参数顶层组织名称name,无法进行数据查询");
+	}
+}

+ 93 - 6
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attachment/FileImportExportAction.java

@@ -1,17 +1,13 @@
 package com.x.attendance.assemble.control.jaxrs.attachment;
 
 import javax.servlet.http.HttpServletRequest;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
+import javax.ws.rs.*;
 import javax.ws.rs.container.AsyncResponse;
 import javax.ws.rs.container.Suspended;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 
+import com.google.gson.JsonElement;
 import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
 import org.glassfish.jersey.media.multipart.FormDataParam;
 
@@ -27,6 +23,8 @@ import com.x.base.core.project.jaxrs.StandardJaxrsAction;
 import com.x.base.core.project.logger.Logger;
 import com.x.base.core.project.logger.LoggerFactory;
 
+import java.util.List;
+
 @Path("file")
 @JaxrsDescribe("附件操作")
 public class FileImportExportAction extends StandardJaxrsAction {
@@ -111,4 +109,93 @@ public class FileImportExportAction extends StandardJaxrsAction {
 		}
 		asyncResponse.resume(ResponseFactory.getEntityTagActionResultResponse(request, result));
 	}
+
+	@JaxrsMethodDescribe(value = "导出符合过滤条件的打卡记录明细", action = ActionExportDetailWithFilter.class)
+	@GET
+	@Path("export/filter/{q_topUnitName}/{q_unitName}/{q_empName}/{cycleYear}/{cycleMonth}/{q_date}/{isAbsent}/{isLackOfTime}/{isLate}/stream/{stream}")
+	@Consumes(MediaType.APPLICATION_JSON)
+	public void detailsExportStream(@Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request,
+									@JaxrsParameterDescribe("公司,为空时输入0") @PathParam("q_topUnitName") String q_topUnitName,
+									@JaxrsParameterDescribe("部门,为空时输入0") @PathParam("q_unitName") String q_unitName,
+									@JaxrsParameterDescribe("员工,为空时输入0") @PathParam("q_empName") String q_empName,
+									@JaxrsParameterDescribe("统计周期年份,为空时输入0") @PathParam("cycleYear") String cycleYear,
+									@JaxrsParameterDescribe("统计周期月份,为空时输入0") @PathParam("cycleMonth") String cycleMonth,
+									@JaxrsParameterDescribe("统计具体日期,为空时输入0") @PathParam("q_date") String q_date,
+									@JaxrsParameterDescribe("是否缺勤,为空时输入0") @PathParam("isAbsent") String isAbsent,
+									@JaxrsParameterDescribe("是否工时不足,为空时输入0") @PathParam("isLackOfTime") String isLackOfTime,
+									@JaxrsParameterDescribe("是否迟到,为空时输入0") @PathParam("isLate") String isLate,
+								   @JaxrsParameterDescribe("用.APPLICATION_OCTET_STREAM头输出") @PathParam("stream") Boolean stream) {
+		ActionResult<ActionExportDetailWithFilter.Wo> result = new ActionResult<>();
+		EffectivePerson effectivePerson = this.effectivePerson(request);
+		try {
+			result = new ActionExportDetailWithFilter().execute(request, effectivePerson, q_topUnitName, q_unitName,q_empName,cycleYear,cycleMonth,q_date,isAbsent,isLackOfTime,isLate, stream);
+		} catch (Exception e) {
+			logger.error(e, effectivePerson, request, null);
+			result.error(e);
+		}
+		asyncResponse.resume(ResponseFactory.getEntityTagActionResultResponse(request, result));
+	}
+
+	@JaxrsMethodDescribe(value = "导出个人出勤率统计记录,设定是否使用stream输出", action = ActionExportPersonStatistic.class)
+	@GET
+	@Path("export/person/{name}/{year}/{month}/stream/{stream}")
+	@Consumes(MediaType.APPLICATION_JSON)
+	public void personStatisticExportStream(@Suspended final AsyncResponse asyncResponse,
+											@Context HttpServletRequest request,
+											@JaxrsParameterDescribe("统计员工姓名") @PathParam("name") String name,
+											@JaxrsParameterDescribe("统计周期年份") @PathParam("year") String year,
+											@JaxrsParameterDescribe("统计周期月份") @PathParam("month") String month,
+											@JaxrsParameterDescribe("用.APPLICATION_OCTET_STREAM头输出") @PathParam("stream") Boolean stream) {
+		ActionResult<ActionExportPersonStatistic.Wo> result = new ActionResult<>();
+		EffectivePerson effectivePerson = this.effectivePerson(request);
+		try {
+			result = new ActionExportPersonStatistic().execute(request, effectivePerson, name, year, month, stream);
+		} catch (Exception e) {
+			logger.error(e, effectivePerson, request, null);
+			result.error(e);
+		}
+		asyncResponse.resume(ResponseFactory.getEntityTagActionResultResponse(request, result));
+	}
+
+	@JaxrsMethodDescribe(value = "导出部门出勤率统计记录,设定是否使用stream输出", action = ActionExportUnitSubNestedStatistic.class)
+	@GET
+	@Path("export/unit/{name}/{year}/{month}/stream/{stream}")
+	@Consumes(MediaType.APPLICATION_JSON)
+	public void unitStatisticExportStream(@Suspended final AsyncResponse asyncResponse,
+										  @Context HttpServletRequest request,
+										  @JaxrsParameterDescribe("统计部门名称") @PathParam("name") String name,
+										  @JaxrsParameterDescribe("统计周期年份") @PathParam("year") String year,
+										  @JaxrsParameterDescribe("统计周期月份") @PathParam("month") String month,
+										  @JaxrsParameterDescribe("用.APPLICATION_OCTET_STREAM头输出") @PathParam("stream") Boolean stream) {
+		ActionResult<ActionExportUnitSubNestedStatistic.Wo> result = new ActionResult<>();
+		EffectivePerson effectivePerson = this.effectivePerson(request);
+		try {
+			result = new ActionExportUnitSubNestedStatistic().execute(request, effectivePerson, name, year, month, stream);
+		} catch (Exception e) {
+			logger.error(e, effectivePerson, request, null);
+			result.error(e);
+		}
+		asyncResponse.resume(ResponseFactory.getEntityTagActionResultResponse(request, result));
+	}
+
+	@JaxrsMethodDescribe(value = "导出公司出勤率统计记录,设定是否使用stream输出", action = ActionExportTopUnitStatistic.class)
+	@GET
+	@Path("export/topunit/{name}/{year}/{month}/stream/{stream}")
+	@Consumes(MediaType.APPLICATION_JSON)
+	public void topunitStatisticExportStream(@Suspended final AsyncResponse asyncResponse,
+										  @Context HttpServletRequest request,
+										  @JaxrsParameterDescribe("统计公司名称") @PathParam("name") String name,
+										  @JaxrsParameterDescribe("统计周期年份") @PathParam("year") String year,
+										  @JaxrsParameterDescribe("统计周期月份") @PathParam("month") String month,
+										  @JaxrsParameterDescribe("用.APPLICATION_OCTET_STREAM头输出") @PathParam("stream") Boolean stream) {
+		ActionResult<ActionExportTopUnitStatistic.Wo> result = new ActionResult<>();
+		EffectivePerson effectivePerson = this.effectivePerson(request);
+		try {
+			result = new ActionExportTopUnitStatistic().execute(request, effectivePerson, name, year, month, stream);
+		} catch (Exception e) {
+			logger.error(e, effectivePerson, request, null);
+			result.error(e);
+		}
+		asyncResponse.resume(ResponseFactory.getEntityTagActionResultResponse(request, result));
+	}
 }

+ 7 - 4
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attendanceappealinfo/ActionListNextWithFilter.java

@@ -37,6 +37,7 @@ public class ActionListNextWithFilter extends BaseAction {
 		List<AttendanceAppealInfo> detailList = null;
 		WrapInFilterAppeal wrapIn = null;
 		Boolean check = true;
+		Boolean isManager = false;
 
 		try {
 			wrapIn = this.convertToWrapIn(jsonElement, WrapInFilterAppeal.class);
@@ -60,11 +61,13 @@ public class ActionListNextWithFilter extends BaseAction {
 						sequence = PropertyUtils.getProperty(emc.find(id, AttendanceAppealInfo.class),  JpaObject.sequence_FIELDNAME);
 					}
 				}
-				// 从数据库中查询符合条件的一页数据对象
-				detailList = business.getAttendanceAppealInfoFactory().listIdsNextWithFilter(id, count, sequence,
-						wrapIn);
+
+				isManager = business.isManager(effectivePerson);
+				// 从数据库中查询符合条件的一页数据对象(根据当前审批人查询)
+				detailList = business.getAttendanceAppealInfoFactory().listIdsNextWithFilterWithCurrentProcessor(id, count, sequence,
+						wrapIn,isManager);
 				// 从数据库中查询符合条件的对象总数
-				total = business.getAttendanceAppealInfoFactory().getCountWithFilter(wrapIn);
+				total = business.getAttendanceAppealInfoFactory().getCountWithFilterWithCurrentProcessor(wrapIn,isManager);
 				// 将所有查询出来的有状态的对象转换为可以输出的过滤过属性的对象
 				wraps = Wo.copier.copy(detailList);
 

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

@@ -123,6 +123,19 @@ public class ActionCheckWithPersonByCycle extends BaseAction {
 			}
 		}
 		if (check) {
+			//由于检查统计周期是用的多线程,造成多份重复无法控制,改为提前检查统计周期
+			for(AttendanceEmployeeConfig attendanceEmployeeConfig : attendanceEmployeeConfigList){
+				AttendanceStatisticalCycle attendanceStatisticalCycle = attendanceStatisticCycleServiceAdv.getAttendanceDetailStatisticCycle(
+						attendanceEmployeeConfig.getTopUnitName(),
+						attendanceEmployeeConfig.getUnitName(),
+						cycleYear,
+						cycleMonth,
+						topUnitAttendanceStatisticalCycleMap,
+						false
+				);
+				attendanceStatisticCycleServiceAdv.checkAttendanceStatisticCycle(attendanceStatisticalCycle);
+			}
+
 			new SenderForSupplementData().execute( attendanceEmployeeConfigList, topUnitAttendanceStatisticalCycleMap, cycleYear, cycleMonth, effectivePerson.getDebugger() );
 		}
 		

+ 26 - 1
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/attendancedetail/ActionReciveAttendanceMobile.java

@@ -86,7 +86,14 @@ public class ActionReciveAttendanceMobile extends BaseAction {
 				attendanceDetailMobile.setLongitude( wrapIn.getLongitude() );
 			}
 		}
-
+		if( check ){
+			//是否在范围外打卡,默认否
+			if( wrapIn.getIsExternal() ){
+				attendanceDetailMobile.setIsExternal(true);
+			}else{
+				attendanceDetailMobile.setIsExternal(false);
+			}
+		}
 		if( check ){
 			String distinguishedName = wrapIn.getEmpName();
 			if( StringUtils.isEmpty( distinguishedName )){
@@ -240,6 +247,12 @@ public class ActionReciveAttendanceMobile extends BaseAction {
 		@FieldDescribe( "操作设备类别:Mac|Windows|IOS|Android|其他, 可以为空." )
 		private String optSystemName = "其他";
 
+		@FieldDescribe( "工作地点描述, 可以为空." )
+		private String workAddress = "未知";
+
+		@FieldDescribe("是否范围外打卡")
+		private Boolean isExternal = false;
+
 		public String getRecordDateString() {
 			return recordDateString;
 		}
@@ -316,6 +329,18 @@ public class ActionReciveAttendanceMobile extends BaseAction {
 		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 getWorkAddress() {
+			return workAddress;
+		}
+		public void setWorkAddress(String workAddress) {
+			this.workAddress = workAddress;
+		}
+		public Boolean getIsExternal() {
+			return isExternal;
+		}
+		public void setIsExternal(Boolean isExternal) {
+			this.isExternal = isExternal;
+		}
 	}
 	
 	public static class Wo extends WoId {

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

@@ -96,6 +96,21 @@ public class ActionSave extends BaseAction {
 			}
 		}
 		if (check) {
+			//计算由于打卡次数发生变化引起的打开时间值变化
+			Integer signProxy = attendanceScheduleSetting.getSignProxy();
+			switch(signProxy){
+				case 1 :
+					attendanceScheduleSetting.setMiddayRestStartTime("");
+					attendanceScheduleSetting.setMiddayRestEndTime("");
+					attendanceScheduleSetting.setLateStartTimeAfternoon("");
+					attendanceScheduleSetting.setLeaveEarlyStartTimeMorning("");
+					break;
+				case 2 :
+					attendanceScheduleSetting.setLateStartTimeAfternoon("");
+					break;
+			}
+
+
 			try {
 				attendanceScheduleSetting = attendanceScheduleSettingServiceAdv.save(attendanceScheduleSetting);
 				result.setData(new Wo(attendanceScheduleSetting.getId()));

+ 60 - 3
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/selfholiday/ActionListNextWithFilter.java

@@ -1,10 +1,12 @@
 package com.x.attendance.assemble.control.jaxrs.selfholiday;
 
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 
 import javax.servlet.http.HttpServletRequest;
 
+import com.x.attendance.assemble.common.date.DateOperation;
 import org.apache.commons.beanutils.PropertyUtils;
 import org.apache.commons.lang3.StringUtils;
 
@@ -26,7 +28,7 @@ import com.x.base.core.project.logger.LoggerFactory;
 public class ActionListNextWithFilter extends BaseAction {
 	
 	private static  Logger logger = LoggerFactory.getLogger( ActionListNextWithFilter.class );
-	
+	protected DateOperation dateOperation = new DateOperation();
 	protected ActionResult<List<Wo>> execute( HttpServletRequest request, EffectivePerson effectivePerson, String id, Integer count, JsonElement jsonElement ) throws Exception {
 		ActionResult<List<Wo>> result = new ActionResult<>();
 		List<Wo> wraps = new ArrayList<>();
@@ -36,11 +38,29 @@ public class ActionListNextWithFilter extends BaseAction {
 		List<String> topUnitNames = new ArrayList<String>();
 		List<String> unitNames = new ArrayList<String>();
 		List<String> unitNameList = null;
-		WrapInFilter wrapIn = null;
+		WrapIn wrapIn = null;
 		Boolean check = true;
+		Date startDate = null;
+		Date endDate = null;
 		
 		try {
-			wrapIn = this.convertToWrapIn( jsonElement, WrapInFilter.class );
+			wrapIn = this.convertToWrapIn( jsonElement, WrapIn.class );
+			startDate = dateOperation.getDateFromString( wrapIn.getStartdateString() + " 00:00:00");
+			endDate = dateOperation.getDateFromString( wrapIn.getEnddateString() + " 23:59:59");
+
+			if( endDate == null ){
+				endDate = dateOperation.getLastDateInMonth( new Date() );
+			}
+
+			if( startDate == null ){
+				startDate = dateOperation.getFirstDateInMonth( new Date() );
+			}
+
+			if( startDate.after( endDate ) ){
+				startDate = dateOperation.getFirstDateInMonth( new Date() );
+			}
+			wrapIn.setStartdate(startDate);
+			wrapIn.setEnddate(endDate);
 		} catch (Exception e ) {
 			check = false;
 			Exception exception = new ExceptionWrapInConvert( e, jsonElement );
@@ -116,7 +136,44 @@ public class ActionListNextWithFilter extends BaseAction {
 		result.setData(wraps);
 		return result;
 	}
+	public static class WrapIn extends WrapInFilter{
+		String startdateString;
+		String enddateString;
+		Date startdate = null;
+		Date enddate = null;
+
+		public String getStartdateString() {
+			return startdateString;
+		}
 
+		public void setStartdateString(String startdateString) {
+			this.startdateString = startdateString;
+		}
+
+		public String getEnddateString() {
+			return enddateString;
+		}
+
+		public void setEnddateString(String enddateString) {
+			this.enddateString = enddateString;
+		}
+
+		public Date getStartdate() {
+			return startdate;
+		}
+
+		public void setStartdate(Date startdate) {
+			this.startdate = startdate;
+		}
+
+		public Date getEnddate() {
+			return enddate;
+		}
+
+		public void setEnddate(Date enddate) {
+			this.enddate = enddate;
+		}
+	}
 	public static class Wo extends AttendanceSelfHoliday  {
 		
 		private static final long serialVersionUID = -5076990764713538973L;

+ 1 - 1
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/jaxrs/selfholiday/ActionSave.java

@@ -204,7 +204,7 @@ public class ActionSave extends BaseAction {
 
 		private String employeeNumber;
 
-		@FieldDescribe("请假类型:带薪年休假|带薪病假|带薪福利假|扣薪事假|其他")
+		@FieldDescribe("请假类型:带薪年休假|带薪病假|带薪福利假|扣薪事假|出差|培训|其他,<font color='red'>必填</font>")
 		private String leaveType;
 
 		@FieldDescribe("开始时间,<font color='red'>必填</font>: yyyy-mm-dd hh24:mi:ss")

+ 1 - 1
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/service/AttendanceDetailAnalyseCoreService.java

@@ -63,7 +63,7 @@ class AttendanceDetailAnalyseCoreService {
 	}
 
 	static Date getAfternoonOndutyTimeFromDetail(AttendanceDetail detail, Boolean debugger) {
-		if( StringUtils.isNotEmpty( detail.getMorningOffDutyTime()) ){
+		if( StringUtils.isNotEmpty( detail.getAfternoonOnDutyTime()) ){
 			try {
 				logger.debug( debugger, "格式化[下午上班签到时间]afternoonOndutyTime=" +  detail.getRecordDateString() + " " + detail.getAfternoonOnDutyTime() );
 				return dateOperation.getDateFromString( detail.getRecordDateString() + " " + detail.getAfternoonOnDutyTime() );

+ 7 - 2
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/service/AttendanceDetailAnalyseService.java

@@ -40,6 +40,7 @@ public class AttendanceDetailAnalyseService {
 	private AttendanceDetailAnalyseCoreService attendanceDetailAnalyseCoreService = new AttendanceDetailAnalyseCoreService();
 	private DateOperation dateOperation = new DateOperation();
 	private UserManagerService userManagerService = new UserManagerService();
+	private  AttendanceSettingServiceAdv attendanceSettingServiceAdv = new AttendanceSettingServiceAdv();
 
 	/**
 	 * 根据员工姓名\开始日期\结束日期查询日期范围内所有的打卡记录信息ID列表<br/>
@@ -336,7 +337,8 @@ public class AttendanceDetailAnalyseService {
 
 			if( check ){
 				try{
-					detail.setIsWeekend( dateOperation.isWeekend( detail.getRecordDate() ));
+					System.out.println("isWeekend="+attendanceSettingServiceAdv.isWeekend( detail.getRecordDate()));
+					detail.setIsWeekend( attendanceSettingServiceAdv.isWeekend( detail.getRecordDate() ));
 				}catch( Exception e ){
 					check = false;
 					logger.warn( "system analyse record date may be weekend got an exception." + detail.getRecordDateString() );
@@ -474,6 +476,7 @@ public class AttendanceDetailAnalyseService {
 							logger.debug( debugger, detail.getEmpName()+"全天请假了");
 							//全天休假
 							detail.setIsGetSelfHolidays(true);
+							detail.setLeaveType(selfHoliday.getLeaveType());
 							detail.setSelfHolidayDayTime("全天");
 							detail.setGetSelfHolidayDays(1.0);
 						}else if( selfHoliday.getEndTime().getTime() <= dayMiddle.getTime() && selfHoliday.getEndTime().getTime() > dayWorkStart.getTime()
@@ -481,6 +484,7 @@ public class AttendanceDetailAnalyseService {
 							//上午休假
 							logger.debug( debugger, detail.getEmpName()+"上午休假了");
 							detail.setIsGetSelfHolidays(true);
+							detail.setLeaveType(selfHoliday.getLeaveType());
 							detail.setSelfHolidayDayTime("上午");
 							detail.setGetSelfHolidayDays(0.5);
 						}else if( selfHoliday.getStartTime().getTime() >= dayMiddle.getTime() && selfHoliday.getStartTime().getTime() <= dayWorkEnd.getTime()
@@ -488,6 +492,7 @@ public class AttendanceDetailAnalyseService {
 							//上午休假
 							logger.debug( debugger, detail.getEmpName()+"下午休假了");
 							detail.setIsGetSelfHolidays( true );
+							detail.setLeaveType(selfHoliday.getLeaveType());
 							detail.setSelfHolidayDayTime("下午");
 							detail.setGetSelfHolidayDays(0.5);
 						}
@@ -590,7 +595,7 @@ public class AttendanceDetailAnalyseService {
 	 * @param debugger
 	 * @throws Exception
 	 */
-	private void recordStatisticRequireLog( AttendanceDetail detail, Boolean debugger ) throws Exception{
+	public void recordStatisticRequireLog( AttendanceDetail detail, Boolean debugger ) throws Exception{
 		//数据分析完成,那么需要记录一下需要统计的信息数据
 		AttendanceStatisticRequireLog log = null;
 		AttendanceStatisticRequireLogFactory attendanceStatisticRequireLogFactory = null;

+ 51 - 7
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/service/AttendanceDetailAnalyseSignProxy2.java

@@ -43,23 +43,25 @@ class AttendanceDetailAnalyseSignProxy2 {
 		//先初始化当前打卡信息中的上下班时间要求,该要求是是根据员工所在组织排班信息获取到的
 		onWorkTime = AttendanceDetailAnalyseCoreService.getOnWorkTimeFromDetail( detail, debugger );
 		offWorkTime = AttendanceDetailAnalyseCoreService.getOffWorkTimeFromDetail( detail, debugger );
-		middleDutyTime = AttendanceDetailAnalyseCoreService.getAfternoonOndutyTimeFromDetail( detail, debugger );
 
 		lateStartTime = AttendanceDetailAnalyseCoreService.getLateStartTimeFromDetail( detail, scheduleSetting, debugger );
 		leaveEarlyStartTime = AttendanceDetailAnalyseCoreService.getLeaveEarlyStartTimeFromDetail( detail, scheduleSetting, debugger );
 		absenceStartTime = AttendanceDetailAnalyseCoreService.getAbsenceStartTimeFromDetail( detail, scheduleSetting, debugger );
-		morningEndTime = AttendanceDetailAnalyseCoreService.getMiddleRestStartTimeFromDetail( detail, scheduleSetting, debugger );
-		afternoonStartTime = AttendanceDetailAnalyseCoreService.getMiddleRestEndTimeFromDetail( detail, scheduleSetting, debugger );
+		morningEndTime = AttendanceDetailAnalyseCoreService.getMiddleRestStartTimeFromDetail( detail, scheduleSetting, debugger );//上午结束时间
+		afternoonStartTime = AttendanceDetailAnalyseCoreService.getMiddleRestEndTimeFromDetail( detail, scheduleSetting, debugger );//下午开始时间
+		logger.debug( debugger, "三次打卡时,获排版设置时间:onWorkTime=" +  onWorkTime +  ", offWorkTime="+offWorkTime +  ", morningEndTime="+morningEndTime+  ", afternoonStartTime="+afternoonStartTime);
+		logger.debug( debugger, "三次打卡时,获排版设置时间:lateStartTime=" +  lateStartTime +  ", leaveEarlyStartTime="+leaveEarlyStartTime +  ", absenceStartTime="+absenceStartTime);
 
 		Date now = new Date();
 
 		if ( onWorkTime != null && offWorkTime != null && morningEndTime != null && afternoonStartTime != null ) {
-			logger.debug( debugger, "上下班排班信息获取正常:onWorkTime=" +  onWorkTime + ", morningEndTime="+morningEndTime + ", afternoonStartTime="+afternoonStartTime + ", offWorkTime="+offWorkTime );
-			logger.debug( debugger, "上下班签到信息获取正常:onDutyTime=" +  onDutyTime + ", middleDutyTime="+middleDutyTime + ", offDutyTime="+offDutyTime );
+			/*logger.debug( debugger, "上下班排班信息获取正常:onWorkTime=" +  onWorkTime + ", morningEndTime="+morningEndTime + ", afternoonStartTime="+afternoonStartTime + ", offWorkTime="+offWorkTime );
+			logger.debug( debugger, "上下班签到信息获取正常:onDutyTime=" +  onDutyTime + ", middleDutyTime="+middleDutyTime + ", offDutyTime="+offDutyTime );*/
 
 			onDutyTime = AttendanceDetailAnalyseCoreService.getOnDutyTimeFromDetail( detail, debugger );
 			offDutyTime = AttendanceDetailAnalyseCoreService.getOffDutyTimeFromDetail( detail, debugger );
-
+			middleDutyTime = AttendanceDetailAnalyseCoreService.getAfternoonOndutyTimeFromDetail( detail, debugger );
+			logger.debug( debugger, "三次打卡时,实际打卡时间:onDutyTime=" +  onDutyTime +  ", offDutyTime="+offDutyTime +  ", middleDutyTime="+middleDutyTime);
 			//规则:如果员工没有签到并且没有签退,一条打卡时间都没有,那么可能会是算缺勤的
 			if ( onDutyTime == null && offDutyTime == null ) {
 				//如果员工已经全天请假了,则不算缺勤,考勤结果正常
@@ -149,7 +151,7 @@ class AttendanceDetailAnalyseSignProxy2 {
 				//=========================================================================================================
 				//=====中午  如果员工已经签到, 中午签到只是一个记录 ,如果未签到,则记录中缺卡 ================================================================================
 				//=========================================================================================================
-				if( middleDutyTime == null && morningEndTime.before( now ) ){
+				/*if( middleDutyTime == null && morningEndTime.before( now ) ){
 					if ( isSelfHoliday_Afternoon || isSelfHoliday_Afternoon || isNotWorkDay ) {
 						logger.debug(debugger, "请假不计考勤,不算出勤");
 						detail.setIsAbsent(false);
@@ -163,6 +165,48 @@ class AttendanceDetailAnalyseSignProxy2 {
 						}
 						detail.setIsAbnormalDuty(true);
 					}
+				}*/
+				if( middleDutyTime == null){
+					if ( isSelfHoliday_Afternoon || isSelfHoliday_Afternoon || isNotWorkDay ) {
+						logger.debug(debugger, "请假不计考勤,不算出勤");
+						/*detail.setIsAbsent(false);
+						detail.setAbsence(0.0);*/
+					} else {
+						logger.debug(debugger, "没请假,中午缺卡");
+						if( StringUtils.equals( "上午", detail.getAbnormalDutyDayTime()) ){
+							detail.setAbnormalDutyDayTime("上午|中午");
+						}else {
+							detail.setAbnormalDutyDayTime("中午");
+						}
+						detail.setIsAbnormalDuty(true);
+					}
+				}else{
+					long minutes = 0L;
+					if(middleDutyTime.before( morningEndTime )){
+						if(isSelfHoliday_Morning || isSelfHoliday_Afternoon || isNotWorkDay){
+							logger.debug( debugger, "请假、休息天不计考勤,不算出勤,不算早退" );
+							/*detail.setLeaveEarlierTimeDuration( 0L );
+							detail.setIsLeaveEarlier( false );*/
+						}else{
+							minutes = dateOperation.getMinutes( middleDutyTime, morningEndTime );//计算早退时长
+							detail.setLeaveEarlierTimeDuration(minutes); //早退时长
+							detail.setIsLeaveEarlier( true );
+						}
+
+					}
+					if(middleDutyTime.after( afternoonStartTime )){
+						if( isSelfHoliday_Morning || isSelfHoliday_Afternoon || isNotWorkDay ){
+							logger.debug( debugger, "请过假了不算迟到" );
+							/*detail.setLateTimeDuration( 0L ); //请假了不算迟到
+							detail.setIsLate( false );//请假了不算迟到*/
+						}else{
+							//迟到计算从上班时间开始计算,不是迟到起算时间
+							minutes = dateOperation.getMinutes( afternoonStartTime, middleDutyTime );
+							detail.setLateTimeDuration( minutes );//没请假算迟到时长
+							detail.setIsLate( true );//没请假算迟到
+							logger.debug( debugger, "计迟到一次,迟到时长:minutes=" + minutes );
+						}
+					}
 				}
 
 				//=========================================================================================================

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

@@ -23,6 +23,7 @@ import javax.persistence.criteria.Root;
 public class AttendanceDetailService {
 	
 	private static  Logger logger = LoggerFactory.getLogger( AttendanceDetailService.class );
+	private AttendanceDetailAnalyseService attendanceDetailAnalyseService = new AttendanceDetailAnalyseService();
 
 	public AttendanceDetail get( EntityManagerContainer emc, String id ) throws Exception {
 		return emc.find(id, AttendanceDetail.class);
@@ -165,7 +166,18 @@ public class AttendanceDetailService {
 				attendanceDetail.setAppealProcessor( null );
 			}
 			attendanceDetail.setAppealStatus( status );
+			if(status == 9){
+				//若申述通过则更新Detail状态,使得Detail为正常打卡
+				attendanceDetail.setIsGetSelfHolidays(false);
+				attendanceDetail.setIsLate(false);
+				attendanceDetail.setIsAbsent(false);
+				attendanceDetail.setIsAbnormalDuty(false);
+				attendanceDetail.setIsLackOfTime(false);
+				//并对该条考勤数据发起统计请求
+				attendanceDetailAnalyseService.recordStatisticRequireLog(attendanceDetail,true);
+			}
 			emc.check( attendanceDetail, CheckPersistType.all );
+
 		}
 		if( autoCommit ){
 			emc.commit();

+ 17 - 1
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/service/AttendanceDetailServiceAdv.java

@@ -24,6 +24,7 @@ public class AttendanceDetailServiceAdv {
 	private AttendanceDetailService attendanceDetailService = new AttendanceDetailService();
 	private AttendanceDetailMobileService attendanceDetailMobileService = new AttendanceDetailMobileService();
 	private AttendanceScheduleSettingService attendanceScheduleSettingService = new AttendanceScheduleSettingService();
+	private AttendanceSettingService attendanceSettingService = new AttendanceSettingService();
 //	protected AttendanceDetailAnalyseServiceAdv attendanceDetailAnalyseServiceAdv = new AttendanceDetailAnalyseServiceAdv();
 //	protected AttendanceWorkDayConfigServiceAdv attendanceWorkDayConfigServiceAdv = new AttendanceWorkDayConfigServiceAdv();
 //	protected AttendanceStatisticalCycleServiceAdv attendanceStatisticCycleServiceAdv = new AttendanceStatisticalCycleServiceAdv();
@@ -282,6 +283,16 @@ public class AttendanceDetailServiceAdv {
 	 * @throws Exception
 	 */
 	public void pushToDetail(String distinguishedName, String recordDateString, Boolean debugger ) throws Exception {
+		//查询外勤打卡配置
+		Boolean ATTENDANCE_FIELD = false;
+		try (EntityManagerContainer em = EntityManagerContainerFactory.instance().create()) {
+			AttendanceSetting attendanceSettingField = attendanceSettingService.getByCode(em,"ATTENDANCE_FIELD");
+			if(attendanceSettingField !=null &&  StringUtils.equalsIgnoreCase(attendanceSettingField.getConfigValue(),"true")){
+				ATTENDANCE_FIELD = true;
+			}
+		}catch ( Exception e0 ) {
+			throw e0;
+		}
 		try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
 			List<AttendanceDetailMobile> mobileDetails = attendanceDetailMobileService.listAttendanceDetailMobileWithEmployee( emc, distinguishedName, recordDateString );
 			if( ListTools.isNotEmpty( mobileDetails )) {
@@ -292,7 +303,11 @@ public class AttendanceDetailServiceAdv {
 				if( scheduleSetting == null ){
 					throw new Exception("scheduleSetting is null, empName:" + distinguishedName );
 				}
-
+				for( AttendanceDetailMobile detailMobile : mobileDetails ) {
+					if(detailMobile.getIsExternal()){
+						ATTENDANCE_FIELD = true;
+					}
+				}
 				//获取打卡策略:两次,三次还是四次
 				//根据考勤打卡规则来判断启用何种规则来进行考勤结果分析
 				if( 2 == scheduleSetting.getSignProxy() ){
@@ -305,6 +320,7 @@ public class AttendanceDetailServiceAdv {
 					//1、一天只打上下班两次卡
 					detail = new ComposeDetailWithMobileInSignProxy1().compose( mobileDetails, scheduleSetting, debugger);
 				}
+				detail.setIsExternal(ATTENDANCE_FIELD);
 				if( detail_old == null ) {
 					detail.setBatchName( "FromMobile_" + dateOperation.getNowTimeChar() );
 					detail.setRecordStatus(1);

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

@@ -197,6 +197,18 @@ public class AttendanceSettingService {
 			logger.warn( "system init system config 'APPEAL_CHECKER_VALUE' got an exception." );
 			logger.error(e);
 		}
+
+		value = "无";
+		type = "select";
+		selectContent = "无|周六|周日";
+		isMultiple = false;
+		description = "周末设置描述:选择周六或周日。选中的为周末,未选中的按工作日计算";
+		try {
+			checkAndInitSystemConfig("ATTENDANCE_WEEKEND", "周末计算日期设置", value, description, type, selectContent, isMultiple, ++ordernumber );
+		} catch (Exception e) {
+			logger.warn( "system init system config 'APPEAL_CHECKER_VALUE' got an exception." );
+			logger.error(e);
+		}
 	}
 	
 	/**

+ 29 - 1
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/service/AttendanceSettingServiceAdv.java

@@ -8,11 +8,13 @@ import com.x.attendance.entity.AttendanceDetail;
 import com.x.attendance.entity.AttendanceSetting;
 import com.x.base.core.container.EntityManagerContainer;
 import com.x.base.core.container.factory.EntityManagerContainerFactory;
-
+import java.util.Calendar;
+import java.util.Date;
 
 public class AttendanceSettingServiceAdv {
 
 	private AttendanceSettingService attendanceSettingService = new AttendanceSettingService();
+	private DateOperation dateOperation = new DateOperation();
 
 	public List<AttendanceSetting> listAll() throws Exception {
 		try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
@@ -109,4 +111,30 @@ public class AttendanceSettingServiceAdv {
 		attendanceAppealInfo.setEndTime( endTime );
 		return attendanceAppealInfo;
 	}
+	/**
+	 * 判断是否周末
+	 * @param recordDate
+	 * @return
+	 */
+	public boolean isWeekend( Date recordDate ) throws Exception {
+		boolean iflag = false;
+		Calendar cal = Calendar.getInstance();
+		cal.setTime( recordDate );
+
+
+		if(cal.get(Calendar.DAY_OF_WEEK)==Calendar.SATURDAY || cal.get(Calendar.DAY_OF_WEEK)==Calendar.SUNDAY){
+			AttendanceSetting attendanceSetting = this.getByCode("ATTENDANCE_WEEKEND");
+			String configValue =  attendanceSetting.getConfigValue();
+			if(attendanceSetting != null){
+				if( (configValue.indexOf("周六")>-1 && cal.get(Calendar.DAY_OF_WEEK)==Calendar.SATURDAY) || (configValue.indexOf("周日")>-1 && cal.get(Calendar.DAY_OF_WEEK)==Calendar.SUNDAY)){
+					iflag = true;
+				}
+				if(configValue.indexOf("无")>-1 && (cal.get(Calendar.DAY_OF_WEEK)==Calendar.SATURDAY || cal.get(Calendar.DAY_OF_WEEK)==Calendar.SUNDAY) ){
+					iflag = true;
+				}
+			}
+
+		}
+		return iflag;
+	}
 }

+ 45 - 7
o2server/x_attendance_assemble_control/src/main/java/com/x/attendance/assemble/control/service/AttendanceStatisticalCycleServiceAdv.java

@@ -105,7 +105,7 @@ public class AttendanceStatisticalCycleServiceAdv {
 				}
 			}
 		}else{
-			logger.info("根据顶层组织["+topUnitName+"]和组织["+unitName+"]未获取到任何周期数据,需要创建新的统计周期数据......");
+			logger.info("根据顶层组织["+topUnitName+"]和组织["+unitName+"]未获取到任何周期数据,需要创建新的统计周期数据......1");
 		}
 		logger.info( "没有查询到适用的统计周期配置信息......" );
 		//如果程序到此仍未返回结果,说明未找到合适的配置记录,那么新建一条
@@ -170,7 +170,6 @@ public class AttendanceStatisticalCycleServiceAdv {
 		Map<String, List<AttendanceStatisticalCycle>> unitAttendanceStatisticalCycleMap = null;
 		Boolean hasConfig = false;
 		String topUnitName = null, unitName = null;
-		
 		if( Integer.parseInt( cycleMonth ) < 10 ){
 			cycleMonth = "0"+Integer.parseInt(cycleMonth);
 		}
@@ -231,7 +230,7 @@ public class AttendanceStatisticalCycleServiceAdv {
 				}
 			}
 		}else{
-			logger.info("根据顶层组织["+topUnitName+"]和组织["+unitName+"]未获取到任何周期数据,需要创建新的统计周期数据......");
+			logger.info("根据顶层组织["+topUnitName+"]和组织["+unitName+"]未获取到任何周期数据,需要创建新的统计周期数据......2");
 		}
 		
 		if( !hasConfig ){
@@ -265,16 +264,16 @@ public class AttendanceStatisticalCycleServiceAdv {
 				unitCycles = new ArrayList<AttendanceStatisticalCycle>();
 				unitAttendanceStatisticalCycleMap.put( unitName, unitCycles);
 			}
-			putDistinctCycleInList( attendanceStatisticalCycle, unitCycles, debugger);			
-			logger.debug( debugger, ">>>>>>>>>>准备保存新创建的统计周期信息......");
+			putDistinctCycleInList( attendanceStatisticalCycle, unitCycles, debugger);
+			/*logger.info(  ">>>>>>>>>>准备保存新创建的统计周期信息......");
 			try ( EntityManagerContainer emc = EntityManagerContainerFactory.instance().create() ) {
 				emc.beginTransaction(AttendanceStatisticalCycle.class);
 				emc.persist( attendanceStatisticalCycle, CheckPersistType.all);
 				emc.commit();
 			}catch(Exception e){
-				logger.debug( debugger, ">>>>>>>>>>系统在保存新的统计周期信息时发生异常!");
+				logger.info(  ">>>>>>>>>>系统在保存新的统计周期信息时发生异常!");
 				throw e;
-			}
+			}*/
 			return attendanceStatisticalCycle;
 		}
 		return null;
@@ -463,5 +462,44 @@ public class AttendanceStatisticalCycleServiceAdv {
 	public AttendanceStatisticalCycle getStatisticCycleByEmployee( AttendanceStatisticRequireLog attendanceStatisticRequireLog, Map<String, Map<String, List<AttendanceStatisticalCycle>>> topUnitAttendanceStatisticalCycleMap, Boolean debugger ) throws Exception{
 		return attendanceStatisticCycleService.getStatisticCycleByEmployee( attendanceStatisticRequireLog, topUnitAttendanceStatisticalCycleMap, debugger);
 	}
+
+	public void checkAttendanceStatisticCycle(AttendanceStatisticalCycle cycle){
+			if(cycle!=null){
+				List<AttendanceStatisticalCycle> cycles = null;
+				try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+					cycles = attendanceStatisticCycleService.listAll( emc );
+					if(cycles.size()>0){
+						for( AttendanceStatisticalCycle attendanceStatisticalCycle : cycles ){
+							System.out.println( ">>>>>>>>>>遍历:cycles="+attendanceStatisticalCycle.getTopUnitName()+", unitName="+attendanceStatisticalCycle.getUnitName()+", cycleYear="+attendanceStatisticalCycle.getCycleYear()+", cycleMonth="+ attendanceStatisticalCycle.getCycleMonth() );
+							if( attendanceStatisticalCycle.getTopUnitName().equals(cycle.getTopUnitName())
+									&& attendanceStatisticalCycle.getUnitName().equals(cycle.getUnitName())
+									&& attendanceStatisticalCycle.getCycleYear().equals(cycle.getCycleYear())
+									&& attendanceStatisticalCycle.getCycleMonth().equals(cycle.getCycleMonth())
+
+							){
+								//对象已经存在,不再保存
+								logger.info( ">>>>>>>>>>查询成功,对象已经存在!");
+							}else{
+								this.saveAttendanceStatisticCycle(cycle);
+							}
+						}
+					}else{
+						this.saveAttendanceStatisticCycle(cycle);
+					}
+				} catch ( Exception e ) {
+					logger.info( "未查询到考勤统计周期信息.......");
+				}
+			}
+	}
+	public void saveAttendanceStatisticCycle(AttendanceStatisticalCycle cycle){
+		try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
+			logger.info( ">>>>>>>>>>准备保存新创建的统计周期信息......");
+			emc.beginTransaction(AttendanceStatisticalCycle.class);
+			emc.persist( cycle, CheckPersistType.all);
+			emc.commit();
+		}catch ( Exception e ) {
+			logger.info( "保存新创建的统计周期信息出错.......");
+		}
+	}
 }
 

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

@@ -431,29 +431,29 @@ Describe.createSampleO2= function(m) {
 			
 			if(m.type=="POST"){
 			   strSample += " \n var string = JSON.stringify(data);" + "\n";
-               strSample += " var applications = this.Action.applications;"+ "\n";
+               strSample += " var apps = this.applications;"+ "\n";
                strSample += " var serviceRoot = \"" + uri + "\";"+ "\n";
                strSample += " var path = \"" + address + "\";"+ "\n"; ;
-               strSample += " var resp = applications.postQuery( serviceRoot, path , string);"+ "\n";
+               strSample += " var resp = apps.postQuery( serviceRoot, path , string);"+ "\n";
 			}
 			if(m.type=="GET"){
-               strSample += " \n var applications = this.Action.applications;"+ "\n";
+               strSample += " \n var apps = this.applications;"+ "\n";
                strSample += " var serviceRoot = \"" + uri + "\";"+ "\n";
                 strSample += " var path = \"" + address + "\";"+ "\n"; ;
-               strSample += " var resp = applications.getQuery( serviceRoot, path );"+ "\n";
+               strSample += " var resp = apps.getQuery( serviceRoot, path );"+ "\n";
 			}
 			if(m.type=="PUT"){
 			   strSample += " \n var string = JSON.stringify(data)"+ "\n";
-               strSample += " var applications = this.Action.applications"+ "\n";
+               strSample += " var apps = this.applications"+ "\n";
                strSample += " var serviceRoot = \"" + uri + "\";"+ "\n";
                strSample += " var path = \"" + address+ "\";"+ "\n"; ;
-               strSample += " var resp = applications.putQuery( serviceRoot, path , string);"+ "\n";
+               strSample += " var resp = apps.putQuery( serviceRoot, path , string);"+ "\n";
 			}
 			if(m.type=="DELETE"){
-			   strSample += " \n var applications = this.Action.applications;"+ "\n";
+			   strSample += " \n var apps = this.applications;"+ "\n";
                strSample += " var serviceRoot = \" "+ uri + "\";"+ "\n";
                  strSample += " var path = \"" + address + "\";"+ "\n"; ;
-               strSample += " var resp = applications.deleteQuery( serviceRoot, path);"+ "\n";
+               strSample += " var resp = apps.deleteQuery( serviceRoot, path);"+ "\n";
 			}
 			
                strSample += " var json = JSON.parse( resp.toString() );"+ "\n";

+ 28 - 0
o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/AttendanceDetail.java

@@ -294,6 +294,12 @@ public class AttendanceDetail extends SliceJpaObject {
 	@CheckPersist(allowEmpty = false)
 	private Boolean isGetSelfHolidays = false;
 
+	public static final String leaveType_FIELDNAME = "leaveType";
+	@FieldDescribe("休假类型")
+	@Column(length = JpaObject.length_255B, name = ColumnNamePrefix + leaveType_FIELDNAME)
+	@CheckPersist(allowEmpty = true)
+	private String leaveType;
+
 	public static final String isAbsent_FIELDNAME = "isAbsent";
 	@FieldDescribe("是否缺勤")
 	@Column(name = ColumnNamePrefix + isAbsent_FIELDNAME)
@@ -336,6 +342,12 @@ public class AttendanceDetail extends SliceJpaObject {
 	@CheckPersist(allowEmpty = true)
 	private String archiveTime;
 
+	public static final String isExternal_FIELDNAME = "isExternal";
+	@FieldDescribe("是否范围外打卡")
+	@Column(name = ColumnNamePrefix + isExternal_FIELDNAME)
+	@CheckPersist(allowEmpty = true)
+	private Boolean isExternal = false;
+
 	public String getMorningOffDutyTime() {
 		return morningOffDutyTime;
 	}
@@ -880,6 +892,14 @@ public class AttendanceDetail extends SliceJpaObject {
 		this.isGetSelfHolidays = isGetSelfHolidays;
 	}
 
+	public String getLeaveType() {
+		return leaveType;
+	}
+
+	public void setLeaveType(String leaveType) {
+		this.leaveType = leaveType;
+	}
+
 	/**
 	 * 获取休假时段:无,上午,下午,全天(String)
 	 * 
@@ -1170,6 +1190,14 @@ public class AttendanceDetail extends SliceJpaObject {
 		isWeekend = weekend;
 	}
 
+	public Boolean getIsExternal() {
+		return isExternal;
+	}
+
+	public void setIsExternal(Boolean isExternal) {
+		this.isExternal = isExternal;
+	}
+
 	/**
 	 * 清除对该条数据信息的分析结果
 	 */

+ 28 - 0
o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/AttendanceDetailMobile.java

@@ -159,6 +159,18 @@ public class AttendanceDetailMobile extends SliceJpaObject {
 	@Column( name = ColumnNamePrefix + recordStatus_FIELDNAME )
 	private Integer recordStatus = 0;
 
+	public static final String workAddress_FIELDNAME = "workAddress";
+	@FieldDescribe("打卡地点描述")
+	@Column( length = JpaObject.length_255B, name = ColumnNamePrefix + workAddress_FIELDNAME )
+	@CheckPersist(allowEmpty = true)
+	private String workAddress;
+
+	public static final String isExternal_FIELDNAME = "isExternal";
+	@FieldDescribe("是否范围外打卡")
+	@Column(name = ColumnNamePrefix + isExternal_FIELDNAME)
+	@CheckPersist(allowEmpty = true)
+	private Boolean isExternal = false;
+
 	public String getEmpNo() {
 		return empNo;
 	}
@@ -270,4 +282,20 @@ public class AttendanceDetailMobile extends SliceJpaObject {
 	public long getCheckin_time() { return checkin_time; }
 
 	public void setCheckin_time(long checkin_time) { this.checkin_time = checkin_time; }
+
+	public String getWorkAddress() {
+		return workAddress;
+	}
+
+	public void setWorkAddress(String workAddress) {
+		this.workAddress = workAddress;
+	}
+
+	public Boolean getIsExternal() {
+		return isExternal;
+	}
+
+	public void setIsExternal(Boolean isExternal) {
+		this.isExternal = isExternal;
+	}
 }

+ 1 - 1
o2server/x_attendance_core_entity/src/main/java/com/x/attendance/entity/AttendanceSelfHoliday.java

@@ -92,7 +92,7 @@ public class AttendanceSelfHoliday extends SliceJpaObject {
 	private String employeeNumber;
 
 	public static final String leaveType_FIELDNAME = "leaveType";
-	@FieldDescribe("请假类型:带薪年休假|带薪病假|带薪福利假|扣薪事假|其他")
+	@FieldDescribe("请假类型:带薪年休假|带薪病假|带薪福利假|扣薪事假|出差|培训|其他")
 	@Column( length = JpaObject.length_32B, name = ColumnNamePrefix + leaveType_FIELDNAME )
 	@CheckPersist(allowEmpty = false)
 	private String leaveType;

Неке датотеке нису приказане због велике количине промена