Pārlūkot izejas kodu

Merge branch 'master' into dev

thinking_fioa 7 gadi atpakaļ
vecāks
revīzija
94eeade64c
86 mainītis faili ar 2098 papildinājumiem un 899 dzēšanām
  1. 13 7
      README.md
  2. 20 1
      doc/XXL-JOB-English-Documentation.md
  3. 91 31
      doc/XXL-JOB官方文档.md
  4. BIN
      doc/XXL-JOB架构图.pptx
  5. 14 6
      pom.xml
  6. 1 1
      xxl-job-admin/pom.xml
  7. 7 6
      xxl-job-admin/src/main/java/com/xxl/job/admin/controller/IndexController.java
  8. 6 5
      xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobCodeController.java
  9. 16 15
      xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java
  10. 5 5
      xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobInfoController.java
  11. 8 5
      xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java
  12. 9 1
      xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/CookieInterceptor.java
  13. 3 3
      xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/PermissionInterceptor.java
  14. 23 7
      xxl-job-admin/src/main/java/com/xxl/job/admin/controller/resolver/WebExceptionResolver.java
  15. 81 0
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/conf/XxlJobAdminConfig.java
  16. 4 2
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/enums/ExecutorFailStrategyEnum.java
  17. 11 10
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/ExecutorRouteStrategyEnum.java
  18. 2 1
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteBusyover.java
  19. 2 1
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteFailover.java
  20. 8 7
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobFailMonitorHelper.java
  21. 24 21
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/trigger/XxlJobTrigger.java
  22. 25 0
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/FtlUtil.java
  23. 82 0
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/I18nUtil.java
  24. 134 0
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/LocalCacheUtil.java
  25. 5 17
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/MailUtil.java
  26. 0 38
      xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/PropertiesUtil.java
  27. 10 2
      xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobInfoDao.java
  28. 1 2
      xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobLogDao.java
  29. 69 10
      xxl-job-admin/src/main/java/com/xxl/job/admin/service/XxlJobService.java
  30. 15 8
      xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/AdminBizImpl.java
  31. 65 48
      xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/XxlJobServiceImpl.java
  32. 226 0
      xxl-job-admin/src/main/resources/i18n/message.properties
  33. 226 0
      xxl-job-admin/src/main/resources/i18n/message_en.properties
  34. 6 0
      xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobInfoMapper.xml
  35. 8 7
      xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogMapper.xml
  36. 1 1
      xxl-job-admin/src/main/resources/quartz.properties
  37. 1 1
      xxl-job-admin/src/main/resources/spring/applicationcontext-base.xml
  38. 4 1
      xxl-job-admin/src/main/resources/xxl-job-admin.properties
  39. 4 3
      xxl-job-admin/src/main/webapp/500.html
  40. 3 3
      xxl-job-admin/src/main/webapp/WEB-INF/template/common/common.exception.ftl
  41. 16 9
      xxl-job-admin/src/main/webapp/WEB-INF/template/common/common.macro.ftl
  42. 0 1
      xxl-job-admin/src/main/webapp/WEB-INF/template/common/common.result.ftl
  43. 5 11
      xxl-job-admin/src/main/webapp/WEB-INF/template/help.ftl
  44. 10 9
      xxl-job-admin/src/main/webapp/WEB-INF/template/index.ftl
  45. 13 9
      xxl-job-admin/src/main/webapp/WEB-INF/template/jobcode/jobcode.index.ftl
  46. 38 38
      xxl-job-admin/src/main/webapp/WEB-INF/template/jobgroup/jobgroup.index.ftl
  47. 81 82
      xxl-job-admin/src/main/webapp/WEB-INF/template/jobinfo/jobinfo.index.ftl
  48. 6 6
      xxl-job-admin/src/main/webapp/WEB-INF/template/joblog/joblog.detail.ftl
  49. 45 45
      xxl-job-admin/src/main/webapp/WEB-INF/template/joblog/joblog.index.ftl
  50. 6 6
      xxl-job-admin/src/main/webapp/WEB-INF/template/login.ftl
  51. 15 17
      xxl-job-admin/src/main/webapp/static/js/common.1.js
  52. 54 46
      xxl-job-admin/src/main/webapp/static/js/index.js
  53. 12 14
      xxl-job-admin/src/main/webapp/static/js/jobcode.index.1.js
  54. 48 38
      xxl-job-admin/src/main/webapp/static/js/jobgroup.index.1.js
  55. 105 85
      xxl-job-admin/src/main/webapp/static/js/jobinfo.index.1.js
  56. 2 2
      xxl-job-admin/src/main/webapp/static/js/joblog.detail.1.js
  57. 102 91
      xxl-job-admin/src/main/webapp/static/js/joblog.index.1.js
  58. 13 20
      xxl-job-admin/src/main/webapp/static/js/login.1.js
  59. 0 1
      xxl-job-admin/src/main/webapp/static/plugins/layer/mobile/layer.js
  60. 0 0
      xxl-job-admin/src/main/webapp/static/plugins/layer/mobile/need/layer.css
  61. 3 3
      xxl-job-admin/src/test/java/com/xxl/job/admin/controller/JobInfoControllerTest.java
  62. 2 2
      xxl-job-admin/src/test/java/com/xxl/job/admin/dao/XxlJobInfoDaoTest.java
  63. 1 1
      xxl-job-admin/src/test/java/com/xxl/job/admin/dao/XxlJobLogDaoTest.java
  64. 25 0
      xxl-job-admin/src/test/java/com/xxl/job/admin/util/I18nUtilTest.java
  65. 5 0
      xxl-job-admin/src/test/java/com/xxl/job/admin/util/MailUtilTest.java
  66. 0 18
      xxl-job-admin/src/test/java/com/xxl/job/admin/util/PropertiesUtilTest.java
  67. 1 1
      xxl-job-core/pom.xml
  68. 19 9
      xxl-job-core/src/main/java/com/xxl/job/core/executor/XxlJobExecutor.java
  69. 2 2
      xxl-job-core/src/main/java/com/xxl/job/core/handler/impl/ScriptJobHandler.java
  70. 18 2
      xxl-job-core/src/main/java/com/xxl/job/core/log/XxlJobFileAppender.java
  71. 118 0
      xxl-job-core/src/main/java/com/xxl/job/core/thread/JobLogFileCleanThread.java
  72. 27 0
      xxl-job-core/src/main/java/com/xxl/job/core/util/FileUtil.java
  73. 15 14
      xxl-job-core/src/main/java/com/xxl/job/core/util/ScriptUtil.java
  74. 1 1
      xxl-job-executor-samples/pom.xml
  75. 1 1
      xxl-job-executor-samples/xxl-job-executor-sample-jfinal/pom.xml
  76. 4 3
      xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/java/com/xuxueli/executor/sample/jfinal/config/JFinalCoreConfig.java
  77. 5 3
      xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/resources/xxl-job-executor.properties
  78. 1 1
      xxl-job-executor-samples/xxl-job-executor-sample-nutz/pom.xml
  79. 4 3
      xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/java/com/xuxueli/executor/sample/nutz/config/NutzSetup.java
  80. 5 3
      xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/resources/xxl-job-executor.properties
  81. 1 1
      xxl-job-executor-samples/xxl-job-executor-sample-spring/pom.xml
  82. 8 6
      xxl-job-executor-samples/xxl-job-executor-sample-spring/src/main/resources/applicationcontext-xxl-job.xml
  83. 6 4
      xxl-job-executor-samples/xxl-job-executor-sample-spring/src/main/resources/xxl-job-executor.properties
  84. 1 1
      xxl-job-executor-samples/xxl-job-executor-sample-springboot/pom.xml
  85. 14 9
      xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/java/com/xxl/job/executor/core/config/XxlJobConfig.java
  86. 7 5
      xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/resources/application.properties

+ 13 - 7
README.md

@@ -1,12 +1,10 @@
 <p align="center">
-    <a href="http://www.xuxueli.com/xxl-job/">
-        <img src="https://raw.githubusercontent.com/xuxueli/xxl-job/master/doc/images/xxl-logo.jpg" width="150">
-    </a>
+    <img src="https://raw.githubusercontent.com/xuxueli/xxl-job/master/doc/images/xxl-logo.jpg" width="150">
     <h3 align="center">XXL-JOB</h3>
     <p align="center">
         XXL-JOB, a lightweight distributed task scheduling framework.
         <br>
-        <a href="http://www.xuxueli.com/xxl-job/"><strong>-- Browse website. --</strong></a>
+        <a href="http://www.xuxueli.com/xxl-job/"><strong>-- Home Page --</strong></a>
         <br>
         <br>
         <a href="https://travis-ci.org/xuxueli/xxl-job">
@@ -71,7 +69,8 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
 - 24、邮件报警:任务失败时支持邮件报警,支持配置多邮件地址群发报警邮件;
 - 25、推送maven中央仓库: 将会把最新稳定版推送到maven中央仓库, 方便用户接入和使用;
 - 26、运行报表:支持实时查看运行数据,如任务数量、调度次数、执行器数量等;以及调度报表,如调度日期分布图,调度成功分布图等;
-
+- 27、全异步:系统底层实现全部异步化,针对密集调度进行流量削峰,理论上支持任意时长任务的运行;
+- 28、国际化:调度中心支持国际化设置,提供中文、英文两种可选语言,默认为中文;
 
 ## Development
 于2015年中,我在github上创建XXL-JOB项目仓库并提交第一个commit,随之进行系统结构设计,UI选型,交互设计……
@@ -150,6 +149,13 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
     - 57、华夏票联(北京)科技有限公司
     - 58、拍拍贷
     - 59、北京尚德机构在线教育有限公司
+    - 60、任子行股份有限公司
+    - 61、北京时态电子商务有限公司
+    - 62、深圳卷皮网络科技有限公司
+    - 63、北京安博通科技股份有限公司
+    - 64、未来无线网
+    - 65、厦门瓷禧网络有限公司
+    - 66、北京递蓝科软件股份有限公司
 	- ……
 
 > 更多接入的公司,欢迎在 [登记地址](https://github.com/xuxueli/xxl-job/issues/1 ) 登记,登记仅仅为了产品推广。
@@ -179,6 +185,6 @@ This product is open source and free, and will continue to provide free communit
 
 
 ## Donate
-No matter how much the amount is enough to express your thought, thank you very much :)     [To donate](http://www.xuxueli.com/page/donate.html )
+No matter how much the donation amount is enough to express your thought, thank you very much :)     [To donate](http://www.xuxueli.com/page/donate.html )
 
-无论金额多少都足够表达您这份心意,非常感谢 :)      [前往捐赠](http://www.xuxueli.com/page/donate.html )
+无论捐赠金额多少都足够表达您这份心意,非常感谢 :)      [前往捐赠](http://www.xuxueli.com/page/donate.html )

+ 20 - 1
doc/XXL-JOB-English-Documentation.md

@@ -12,6 +12,9 @@
 
 ### 1.1 Overview
 XXL-JOB is a lightweight distributed task scheduling framework, the core design goal is to develop quickly, learning simple, lightweight, easy to expand. Is now open source and access to a number of companies online product line, download and use it now.
+
+> English document update slightly delayed, Please check the Chinese version for the latest document.
+
 ### 1.2 Features
 - 1.Simple: support through the Web page on the task CRUD operation, simple operation, a minute to get started;
 - 2.Dynamic: support dynamic modification of task status, pause / resume tasks, and termination of running tasks,immediate effect;
@@ -105,6 +108,19 @@ So far, XXL-JOB has access to a number of companies online product line, access
     - 52、聚金资本
     - 53、北京父母邦网络科技有限公司
     - 54、中山元赫软件科技有限公司
+    - 55、中商惠民(北京)电子商务有限公司
+    - 56、凯京集团
+    - 57、华夏票联(北京)科技有限公司
+    - 58、拍拍贷
+    - 59、北京尚德机构在线教育有限公司
+    - 60、任子行股份有限公司
+    - 61、北京时态电子商务有限公司
+    - 62、深圳卷皮网络科技有限公司
+    - 63、北京安博通科技股份有限公司
+    - 64、未来无线网
+    - 65、厦门瓷禧网络有限公司
+    - 66、北京递蓝科软件股份有限公司
+    - 67、郑州创海软件科技公司
 	- ……
 
 > The company that access and use this product is welcome to register at the [address](https://github.com/xuxueli/xxl-job/issues/1 ), only for product promotion. 
@@ -122,7 +138,7 @@ Welcome everyone's attention and use, XXL-JOB will also embrace changes, sustain
 Source repository address | Release Download
 --- | ---
 [https://github.com/xuxueli/xxl-job](https://github.com/xuxueli/xxl-job) | [Download](https://github.com/xuxueli/xxl-job/releases)  
-[http://git.oschina.net/xuxueli0323/xxl-job](http://git.oschina.net/xuxueli0323/xxl-job) | [Download](http://git.oschina.net/xuxueli0323/xxl-job/releases)
+[http://gitee.com/xuxueli0323/xxl-job](http://gitee.com/xuxueli0323/xxl-job) | [Download](http://gitee.com/xuxueli0323/xxl-job/releases)
 
 #### Center repository address (The latest Release version:1.8.1)
 ```
@@ -203,6 +219,9 @@ The concrete contet describe as follows:
     
     ### TOKEN used for communication between the executor and schedule center, enabled if it’s not null
     xxl.job.accessToken=
+    
+    ### Internationalized Settings, the default is Chinese version,Switch to English when the value is "en".
+    xxl.job.i18n=en
 
 #### Step 2:Deploy:
 If you has finished step 1,then you can compile the project in maven and deploy the war package to tomcat.

+ 91 - 31
doc/XXL-JOB官方文档.md

@@ -40,6 +40,8 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
 - 24、邮件报警:任务失败时支持邮件报警,支持配置多邮件地址群发报警邮件;
 - 25、推送maven中央仓库: 将会把最新稳定版推送到maven中央仓库, 方便用户接入和使用;
 - 26、运行报表:支持实时查看运行数据,如任务数量、调度次数、执行器数量等;以及调度报表,如调度日期分布图,调度成功分布图等;
+- 27、全异步:系统底层实现全部异步化,针对密集调度进行流量削峰,理论上支持任意时长任务的运行;
+- 28、国际化:调度中心支持国际化设置,提供中文、英文两种可选语言,默认为中文;
 
 ### 1.3 发展
 于2015年中,我在github上创建XXL-JOB项目仓库并提交第一个commit,随之进行系统结构设计,UI选型,交互设计……
@@ -52,12 +54,18 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
 
 于2017-05-13,在上海举办的 "[第62期开源中国源创会](https://www.oschina.net/event/2236961)" 的 "放码过来" 环节,我登台对XXL-JOB做了演讲,台下五百位在场观众反响热烈([图文回顾](https://www.oschina.net/question/2686220_2242120) )。
 
+于2017-10-22,又拍云 Open Talk 联合 Spring Cloud 中国社区举办的 "[进击的微服务实战派上海站](https://opentalk.upyun.com/303.html)",我登台对XXL-JOB做了演讲,现场观众反响热烈并在会后与XXL-JOB用户热烈讨论交流
+
 于2017-12-11,XXL-JOB有幸参会《[InfoQ ArchSummit全球架构师峰会](http://bj2017.archsummit.com/)》,并被拍拍贷架构总监"杨波老师"在专题 "[微服务原理、基础架构和开源实践](http://bj2017.archsummit.com/training/2)" 中现场介绍。
 
+于2017-12-18,XXL-JOB参与"[2017年度最受欢迎中国开源软件](http://www.oschina.net/project/top_cn_2017?sort=1)"评比,在当时已录入的约九千个国产开源项目中角逐,最终进入了前30强。
+
+于2018-01-15,XXL-JOB参与"[2017码云最火开源项目](https://www.oschina.net/news/92438/2017-mayun-top-50)"评比,在当时已录入的约六千五百个码云项目中角逐,最终进去了前20强。
+
 > 我司大众点评目前已接入XXL-JOB,内部别名《Ferrari》(Ferrari基于XXL-JOB的V1.1版本定制而成,新接入应用推荐升级最新版本)。
 据最新统计, 自2016-01-21接入至2017-12-01期间,该系统已调度约100万次,表现优异。新接入应用推荐使用最新版本,因为经过数个大版本的更新,系统的任务模型、UI交互模型以及底层调度通讯模型都有了较大的优化和提升,核心功能更加稳定高效。
 
-至今,XXL-JOB已接入多家公司的线上产品线,接入场景如电商业务,O2O业务和大数据作业等,截止2016-07-19为止,XXL-JOB已接入的公司包括不限于:
+至今,XXL-JOB已接入多家公司的线上产品线,接入场景如电商业务,O2O业务和大数据作业等,截止最新统计时间为止,XXL-JOB已接入的公司包括不限于:
     
 	- 1、大众点评;
 	- 2、山东学而网络科技有限公司;
@@ -118,6 +126,14 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
     - 57、华夏票联(北京)科技有限公司
     - 58、拍拍贷
     - 59、北京尚德机构在线教育有限公司
+    - 60、任子行股份有限公司
+    - 61、北京时态电子商务有限公司
+    - 62、深圳卷皮网络科技有限公司
+    - 63、北京安博通科技股份有限公司
+    - 64、未来无线网
+    - 65、厦门瓷禧网络有限公司
+    - 66、北京递蓝科软件股份有限公司
+    - 67、郑州创海软件科技公司
 	- ……
 
 > 更多接入的公司,欢迎在 [登记地址](https://github.com/xuxueli/xxl-job/issues/1 ) 登记,登记仅仅为了产品推广。
@@ -137,7 +153,7 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
 源码仓库地址 | Release Download
 --- | ---
 [https://github.com/xuxueli/xxl-job](https://github.com/xuxueli/xxl-job) | [Download](https://github.com/xuxueli/xxl-job/releases)  
-[http://git.oschina.net/xuxueli0323/xxl-job](http://git.oschina.net/xuxueli0323/xxl-job) | [Download](http://git.oschina.net/xuxueli0323/xxl-job/releases)
+[http://gitee.com/xuxueli0323/xxl-job](http://gitee.com/xuxueli0323/xxl-job) | [Download](http://gitee.com/xuxueli0323/xxl-job/releases)
 
 
 #### 中央仓库地址
@@ -147,7 +163,7 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
 <dependency>
     <groupId>com.xuxueli</groupId>
     <artifactId>xxl-job-core</artifactId>
-    <version>1.8.2</version>
+    <version>${最新稳定版本}</version>
 </dependency>
 ```
 
@@ -222,6 +238,9 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
     
     ### 调度中心通讯TOKEN,非空时启用
     xxl.job.accessToken=
+    
+    ### 调度中心国际化设置,默认为中文版本,值设置为“en”时切换为英文版本
+    xxl.job.i18n=
 
 #### 步骤二:部署项目:
 如果已经正确进行上述配置,可将项目编译打war包并部署到tomcat中。
@@ -263,12 +282,15 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
     xxl.job.executor.ip=
     xxl.job.executor.port=9999
     
+    ### xxl-job, access token:执行器通讯TOKEN,非空时启用
+    xxl.job.accessToken=
+        
     ### xxl-job log path:执行器运行日志文件存储的磁盘位置,需要对该路径拥有读写权限
     xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler/
     
-    ### xxl-job, access token:执行器通讯TOKEN,非空时启用
-    xxl.job.accessToken=
-
+    ### xxl-job log retention days:执行器Log文件定期清理功能,指定日志保存天数,日志文件过期自动删除。限制至少保持3天,否则功能不生效;
+    xxl.job.executor.logretentiondays=-1
+    
 
 #### 步骤三:执行器组件配置
 
@@ -282,35 +304,42 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
 <!-- 配置01、JobHandler 扫描路径:自动扫描容器中JobHandler -->
 <context:component-scan base-package="com.xxl.job.executor.service.jobhandler" />
 
-<!-- 配置02、执行器Excutor配置:执行器核心配置 -->
+<!-- 配置02、执行器 -->
 <bean id="xxlJobExecutor" class="com.xxl.job.core.executor.XxlJobExecutor" init-method="start" destroy-method="destroy" >
+    <!-- 执行器注册中心地址[选填],为空则关闭自动注册 -->
+    <property name="adminAddresses" value="${xxl.job.admin.addresses}" />
+    <!-- 执行器AppName[选填],为空则关闭自动注册 -->
+    <property name="appName" value="${xxl.job.executor.appname}" />
     <!-- 执行器IP[选填],为空则自动获取 -->
     <property name="ip" value="${xxl.job.executor.ip}" />
     <!-- 执行器端口号[选填],为空则自动获取 -->
     <property name="port" value="${xxl.job.executor.port}" />
-    <!-- 执行器AppName[选填],为空则关闭自动注册 -->
-    <property name="appName" value="${xxl.job.executor.appname}" />
-    <!-- 执行器注册中心地址[选填],为空则关闭自动注册 -->
-    <property name="adminAddresses" value="${xxl.job.admin.addresses}" />
-    <!-- 执行器日志路径[选填],为空则使用默认路径 -->
-    <property name="logPath" value="${xxl.job.executor.logpath}" />
     <!-- 访问令牌[选填],非空则进行匹配校验 -->
     <property name="accessToken" value="${xxl.job.accessToken}" />
+    <!-- 执行器日志路径[选填],为空则使用默认路径 -->
+    <property name="logPath" value="${xxl.job.executor.logpath}" />
+    <!-- 日志保存天数[选填],值大于3时生效 -->
+    <property name="logRetentionDays" value="${xxl.job.executor.logretentiondays}" />
 </bean>
 ```
 
 #### 步骤四:部署执行器项目:
-如果已经正确进行上述配置,可将执行器项目编译打部署,系统提供三个执行器Sample示例项目,选择其中一个即可,各自的部署方式如下。
+如果已经正确进行上述配置,可将执行器项目编译打部署,系统提供多种执行器Sample示例项目,选择其中一个即可,各自的部署方式如下。
 
-    xxl-job-executor-sample-spring:项目编译打包成WAR包,并部署到tomcat中。
     xxl-job-executor-sample-springboot:项目编译打包成springboot类型的可执行JAR包,命令启动即可;
+    xxl-job-executor-sample-spring:项目编译打包成WAR包,并部署到tomcat中。
+    xxl-job-executor-sample-jfinal:同上
+    xxl-job-executor-sample-nutz:同上
+    
 
 至此“执行器”项目已经部署结束。
 
 #### 步骤五:执行器集群(可选):
 执行器支持集群部署,提升调度系统可用性,同时提升任务处理能力。
 
-集群部署唯一要求为:保证集群中每个执行器的配置项 "xxl.job.admin.addresses/调度中心地址" 保持一致,执行器根据该配置进行执行器自动注册等操作。 
+执行器集群部署时,几点要求和建议:
+- 执行器回调地址(xxl.job.admin.addresses)需要保持一致;执行器根据该配置进行执行器自动注册等操作。 
+- 同一个执行器集群内AppName(xxl.job.executor.appname)需要保持一致;调度中心根据该配置动态发现不同集群的在线执行器列表。
 
 
 ### 2.5 开发第一个任务“Hello World”       
@@ -602,7 +631,7 @@ XXL-JOB首先定制了Quartz原生表结构前缀(XXL_JOB_QRTZ_)。
 
 #### 5.3.3 架构图
 
-![输入图片说明](https://static.oschina.net/uploads/img/201707/17190028_aEE2.png "在这里输入图片标题")
+![输入图片说明](https://static.oschina.net/uploads/img/201801/03103007_Qohm.png "在这里输入图片标题")
 
 ### 5.4 调度模块剖析
 #### 5.4.1 quartz的不足
@@ -639,15 +668,22 @@ org.quartz.threadPool.threadPriority: 5
 org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
 ```
 
-XXL-JOB系统中业务逻辑在远程执行器执行,调度中心每次触发调度时仅发送一次调度请求,执行器会将请求存入执行队列并且立即响应调度中心;相比直接在quartz的QuartzJobBean中执行业务逻辑,极大的降低了调度线程占用;
+XXL-JOB系统中业务逻辑在远程执行器执行,全异步化设计,调度中心每次触发调度时仅发送一次调度请求,执行器会将请求存入执行队列并且立即响应调度中心,异步运行;相比直接在quartz的QuartzJobBean中执行业务逻辑,极大的降低了调度线程占用时间
 
 XXL-JOB调度中心中每个JOB逻辑非常 “轻”,单个JOB一次运行平均耗时基本在 "10ms" 之内(基本为一次请求的网络开销);因此,可以保证使用有限的线程支撑大量的JOB并发运行;
 
-理论上采用推荐机器配置 "4核4G内存"情况下,单线程可以承担 100(quartz最小时间粒度1000ms/触发一次任务耗时10ms)个密集任务(每秒执行一次)的正常调度触发。因此,默认配置的15个线程理论上可以承担起1500个密集任务的正常运行。
+理论支撑任务量公式如下:
+
+    理论支撑任务量 = 线程数配置 / 平均调度频率(每秒) * 平均触发耗时(单位s)
+
+理论上采用推荐机器配置 "4核4G内存" + "配置1s运行1次密集任务" + "调度中心与执行器ping延迟10ms(0.01s)" 的情况下,
+
+    - 单线程支撑任务量  :1 / 1 * 0.01 = 100个任务
+    - 15个线程支撑任务量:15 / 1 * 0.01 = 1500个任务
 
-实际场景中,调度请求网络耗时不同、DB读写耗时不同、任务密集或稀疏调度情况不同,会导致任务量上限会上下波动。
+实际场景中,由于调度中心与执行器ping延迟不同、DB读写耗时不同、任务调度密集程度不同,会导致任务量上限会上下波动。
 
-如若需要支撑更多的任务量,可以通过 "调大调度线程数" 和 "提升机器配置" 两种方式实现。
+如若需要支撑更多的任务量,可以通过 "调大调度线程数" 、"降低调度中心与执行器ping延迟" 和 "提升机器配置" 几种方式实现。
 
 #### 5.4.5 @DisallowConcurrentExecution
 XXL-JOB调度模块的“调度中心”默认不使用该注解,即默认开启并行机制,因为RemoteHttpJobBean为公共QuartzJobBean,这样在多线程调度的情况下,调度模块被阻塞的几率很低,大大提高了调度系统的承载量。
@@ -1074,7 +1110,7 @@ Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段
 - 9、调度中心任务监控线程销毁时,批量对失败任务告警,防止告警信息丢失;
 - 10、任务日志文件路径时间戳格式化时SimpleDateFormat并发问题解决;
 
-### 6.20 版本 V1.9.0 特性[迭代中]
+### 6.20 版本 V1.9.0 特性[2017-12-29]
 - 1、新增Nutz执行器Sample示例项目;
 - 2、新增任务运行模式 "GLUE模式(NodeJS) ",支持NodeJS脚本任务;
 - 3、脚本任务Shell、Python和Nodejs等支持获取分片参数;
@@ -1084,13 +1120,13 @@ Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段
 - 7、调度报表优化,支持时间区间筛选;
 - 8、Log组件支持输出异常栈信息,底层实现优化;
 - 9、告警邮件样式优化,调整为表格形式,邮件组件调整为commons-email简化邮件操作;
-- 10、项目依赖升级,如spring、jackson等;
+- 10、项目依赖全量升级至较新稳定版本,如spring、jackson等;
 - 11、任务日志,记录发起调度的机器信息;
 - 12、交互优化,如登陆注销;
 - 13、任务Cron长度扩展支持至128位,支持负责类型Cron设置;
 - 14、执行器地址录入交互优化,地址长度扩展支持至512位,支持大规模执行器集群配置;
 - 15、任务参数“IJobHandler.execute”入参改为“String params”,增强入参通用性。
-- 16、JobHandler提供init/destroy方法,支持在JobHandler初始化和销毁时进行附加操作;
+- 16、IJobHandler提供init/destroy方法,支持在相应任务线程初始化和销毁时进行附加操作;
 - 17、任务注解调整为 “@JobHandler”,与任务抽象接口统一;
 - 18、修复任务监控线程被耗时任务阻塞的问题;
 - 19、修复任务监控线程无法监控任务触发和执行状态均未0的问题;
@@ -1104,6 +1140,24 @@ Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段
 - 27、系统安全性优化,登陆Token写Cookie时进行MD5加密,同时Cookie启用HttpOnly;
 - 28、新增"任务ID"属性,移除"JobKey"属性,前者承担所有功能,方便后续增强任务依赖功能。
 - 29、任务循环依赖问题修复,避免子任务与父任务重复导致的调度死循环;
+- 30、任务列表新增筛选条件 "任务描述",快速检索任务;
+- 31、执行器Log文件定期清理功能:执行器新增配置项("xxl.job.executor.logretentiondays")日志保存天数,日志文件过期自动删除。
+
+### 6.21 版本 V1.9.1 特性[2018-02-22]
+- 1、国际化:调度中心实现国际化,支持中文、英文两种语言,默认为中文。
+- 2、调度报表新增"运行中"中状态项;
+- 3、调度报表优化,报表SQL调优并且新增LocalCache缓存(缓存时间60s),提高大数据量下报表加载速度;
+- 4、修复打包部署时资源文件乱码问题;
+- 5、修复新版本chrome滚动到顶部失效问题;
+- 6、调度中心配置加载优化,取消对配置文件名的强依赖,支持加载磁盘配置;
+- 7、修复脚本任务Log文件未正常close的问题;
+- 8、项目依赖全量升级至较新稳定版本,如spring、jackson等等;
+
+### 6.22 版本 V1.9.2 特性[迭代中]
+- 1、[迭代中]支持通过API服务操作任务信息;
+- 2、[迭代中]任务告警逻辑调整:任务调度,以及任务回调失败时,均推送监控队列。后期考虑通过任务Log字段控制告警状态;
+- 3、[迭代中]任务超时设置,超时任务主动终止;
+
 
 ### TODO LIST
 - 1、任务权限管理:执行器为粒度分配权限,核心操作校验权限;
@@ -1114,12 +1168,18 @@ Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段
 - 6、调度任务优先级;
 - 7、移除quartz依赖,重写调度模块:新增或恢复任务时将下次执行记录插入delayqueue,调度中心集群竞争分布式锁,成功节点批量加载到期delayqueue数据,批量执行。
 - 8、springboot 和 docker镜像,并且推送docker镜像到中央仓库,更进一步实现产品开箱即用;
-- 9、国际化:调度中心界面。
-- 10、任务告警逻辑调整:任务调度,以及任务回调失败时,均推送监控队列。后期考虑通过任务Log字段控制告警状态;
-- 11、执行器Log清理功能:调度中心Log删除时同步删除执行器中的Log文件;
-- 12、Bean模式任务,JobHandler自动从执行器中查询展示为下拉框,选择后自动填充任务名称等属性;
-- 13、API事件触发类型任务(更类似MQ消息)支持"动态传参、延时消费";该类型任务不走Quartz,单独建立MQ消息表,调度中心竞争触发;
-- 14、任务依赖增强,新增任务类型 "流程任务",流程节点可挂载普通类型任务,承担任务依赖功能。现有子任务模型取消;需要考虑任务依赖死循环问题;
+- 9、多数据库支持;
+- 10、执行器Log清理功能:调度中心Log删除时同步删除执行器中的Log文件;
+- 11、Bean模式任务,JobHandler自动从执行器中查询展示为下拉框,选择后自动填充任务名称等属性;
+- 12、API事件触发类型任务(更类似MQ消息)支持"动态传参、延时消费";该类型任务不走Quartz,单独建立MQ消息表,调度中心竞争触发;
+- 13、任务依赖增强,新增任务类型 "流程任务",流程节点可挂载普通类型任务,承担任务依赖功能。现有子任务模型取消;需要考虑任务依赖死循环问题;
+- 14、分片任务某一分片失败,支持分片转移;
+- 15、调度中心触发任务后,先推送触发队列,异步触发,然后立即返回。降低quartz线程占用时长。
+- 16、新增API服务 "XxlJobService" ,支持通过API服务来维护管理任务信息;
+- 17、新增任务默认运行状态,任务更新时运行状态保持不变;
+- 18、告警邮件中展示失败告警信息;
+- 19、提供多版本执行器:不依赖容器版本、不内嵌Jetty版本(通过配置executoraddress替换jetty通讯)等;
+
 
 
 ## 七、其他
@@ -1138,4 +1198,4 @@ Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段
 
 ---
 ### 捐赠
-无论金额多少都足够表达您这份心意,非常感谢 :)      [前往捐赠](http://www.xuxueli.com/page/donate.html )
+无论捐赠金额多少都足够表达您这份心意,非常感谢 :)      [前往捐赠](http://www.xuxueli.com/page/donate.html )

BIN
doc/XXL-JOB架构图.pptx


+ 14 - 6
pom.xml

@@ -3,7 +3,7 @@
 	<modelVersion>4.0.0</modelVersion>
 	<groupId>com.xuxueli</groupId>
 	<artifactId>xxl-job</artifactId>
-	<version>1.9.0-SNAPSHOT</version>
+	<version>1.9.2-SNAPSHOT</version>
 	<packaging>pom</packaging>
 
 	<name>${project.artifactId}</name>
@@ -20,16 +20,16 @@
 		<javax.servlet-api.version>3.0.1</javax.servlet-api.version>
 		<jsp-api.version>2.2</jsp-api.version>
 
-		<spring.version>4.3.13.RELEASE</spring.version>
-		<jackson.version>2.9.3</jackson.version>
+		<spring.version>4.3.14.RELEASE</spring.version>
+		<jackson.version>2.9.4</jackson.version>
 		<aspectjweaver.version>1.8.13</aspectjweaver.version>
 		<slf4j-api.version>1.7.25</slf4j-api.version>
 		<freemarker.version>2.3.23</freemarker.version>
 		<junit.version>4.12</junit.version>
 
-		<jetty-server.version>9.2.22.v20170606</jetty-server.version>
+		<jetty-server.version>9.2.24.v20180105</jetty-server.version>
 		<hessian.version>4.0.51</hessian.version>
-		<httpclient.version>4.5.4</httpclient.version>
+		<httpclient.version>4.5.5</httpclient.version>
 		
 		<commons-exec.version>1.3</commons-exec.version>
 		<commons-collections4.version>4.1</commons-collections4.version>
@@ -44,7 +44,7 @@
 		<groovy-all.version>2.4.13</groovy-all.version>
 		<quartz.version>2.3.0</quartz.version>
 
-		<spring-boot.version>1.5.9.RELEASE</spring-boot.version>
+		<spring-boot.version>1.5.10.RELEASE</spring-boot.version>
 	</properties>
 
 	<build>
@@ -59,6 +59,14 @@
 					<encoding>UTF8</encoding>
 				</configuration>
 			</plugin>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-resources-plugin</artifactId>
+				<version>2.6</version>
+				<configuration>
+					<encoding>UTF-8</encoding>
+				</configuration>
+			</plugin>
 			<plugin>
 				<groupId>org.apache.maven.plugins</groupId>
 				<artifactId>maven-war-plugin</artifactId>

+ 1 - 1
xxl-job-admin/pom.xml

@@ -4,7 +4,7 @@
 	<parent>
 		<groupId>com.xuxueli</groupId>
 		<artifactId>xxl-job</artifactId>
-		<version>1.9.0-SNAPSHOT</version>
+		<version>1.9.2-SNAPSHOT</version>
 	</parent>
 	<artifactId>xxl-job-admin</artifactId>
 	<packaging>war</packaging>

+ 7 - 6
xxl-job-admin/src/main/java/com/xxl/job/admin/controller/IndexController.java

@@ -2,6 +2,7 @@ package com.xxl.job.admin.controller;
 
 import com.xxl.job.admin.controller.annotation.PermessionLimit;
 import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
+import com.xxl.job.admin.core.util.I18nUtil;
 import com.xxl.job.admin.service.XxlJobService;
 import com.xxl.job.core.biz.model.ReturnT;
 import org.apache.commons.lang3.StringUtils;
@@ -40,11 +41,11 @@ public class IndexController {
 		return "index";
 	}
 
-    @RequestMapping("/triggerChartDate")
+    @RequestMapping("/chartInfo")
 	@ResponseBody
-	public ReturnT<Map<String, Object>> triggerChartDate(Date startDate, Date endDate) {
-        ReturnT<Map<String, Object>> triggerChartDate = xxlJobService.triggerChartDate(startDate, endDate);
-        return triggerChartDate;
+	public ReturnT<Map<String, Object>> chartInfo(Date startDate, Date endDate) {
+        ReturnT<Map<String, Object>> chartInfo = xxlJobService.chartInfo(startDate, endDate);
+        return chartInfo;
     }
 	
 	@RequestMapping("/toLogin")
@@ -67,14 +68,14 @@ public class IndexController {
 
 		// param
 		if (StringUtils.isBlank(userName) || StringUtils.isBlank(password)){
-			return new ReturnT<String>(500, "账号或密码为空");
+			return new ReturnT<String>(500, I18nUtil.getString("login_param_empty"));
 		}
 		boolean ifRem = (StringUtils.isNotBlank(ifRemember) && "on".equals(ifRemember))?true:false;
 
 		// do login
 		boolean loginRet = PermissionInterceptor.login(response, userName, password, ifRem);
 		if (!loginRet) {
-			return new ReturnT<String>(500, "账号或密码错误");
+			return new ReturnT<String>(500, I18nUtil.getString("login_param_unvalid"));
 		}
 		return ReturnT.SUCCESS;
 	}

+ 6 - 5
xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobCodeController.java

@@ -2,6 +2,7 @@ package com.xxl.job.admin.controller;
 
 import com.xxl.job.admin.core.model.XxlJobInfo;
 import com.xxl.job.admin.core.model.XxlJobLogGlue;
+import com.xxl.job.admin.core.util.I18nUtil;
 import com.xxl.job.admin.dao.XxlJobInfoDao;
 import com.xxl.job.admin.dao.XxlJobLogGlueDao;
 import com.xxl.job.core.biz.model.ReturnT;
@@ -34,10 +35,10 @@ public class JobCodeController {
 		List<XxlJobLogGlue> jobLogGlues = xxlJobLogGlueDao.findByJobId(jobId);
 
 		if (jobInfo == null) {
-			throw new RuntimeException("抱歉,任务不存在.");
+			throw new RuntimeException(I18nUtil.getString("jobinfo_glue_jobid_unvalid"));
 		}
 		if (GlueTypeEnum.BEAN == GlueTypeEnum.match(jobInfo.getGlueType())) {
-			throw new RuntimeException("该任务非GLUE模式.");
+			throw new RuntimeException(I18nUtil.getString("jobinfo_glue_gluetype_unvalid"));
 		}
 
 		// Glue类型-字典
@@ -53,14 +54,14 @@ public class JobCodeController {
 	public ReturnT<String> save(Model model, int id, String glueSource, String glueRemark) {
 		// valid
 		if (glueRemark==null) {
-			return new ReturnT<String>(500, "请输入备注");
+			return new ReturnT<String>(500, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_glue_remark")) );
 		}
 		if (glueRemark.length()<4 || glueRemark.length()>100) {
-			return new ReturnT<String>(500, "备注长度应该在4至100之间");
+			return new ReturnT<String>(500, I18nUtil.getString("jobinfo_glue_remark_limit"));
 		}
 		XxlJobInfo exists_jobInfo = xxlJobInfoDao.loadById(id);
 		if (exists_jobInfo == null) {
-			return new ReturnT<String>(500, "参数异常");
+			return new ReturnT<String>(500, I18nUtil.getString("jobinfo_glue_jobid_unvalid"));
 		}
 		
 		// update new code

+ 16 - 15
xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobGroupController.java

@@ -1,6 +1,7 @@
 package com.xxl.job.admin.controller;
 
 import com.xxl.job.admin.core.model.XxlJobGroup;
+import com.xxl.job.admin.core.util.I18nUtil;
 import com.xxl.job.admin.dao.XxlJobGroupDao;
 import com.xxl.job.admin.dao.XxlJobInfoDao;
 import com.xxl.job.core.biz.model.ReturnT;
@@ -42,22 +43,22 @@ public class JobGroupController {
 
 		// valid
 		if (xxlJobGroup.getAppName()==null || StringUtils.isBlank(xxlJobGroup.getAppName())) {
-			return new ReturnT<String>(500, "请输入AppName");
+			return new ReturnT<String>(500, (I18nUtil.getString("system_please_input")+"AppName") );
 		}
-		if (xxlJobGroup.getAppName().length()>64) {
-			return new ReturnT<String>(500, "AppName长度限制为4~64");
+		if (xxlJobGroup.getAppName().length()<4 || xxlJobGroup.getAppName().length()>64) {
+			return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_appName_length") );
 		}
 		if (xxlJobGroup.getTitle()==null || StringUtils.isBlank(xxlJobGroup.getTitle())) {
-			return new ReturnT<String>(500, "请输入名称");
+			return new ReturnT<String>(500, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title")) );
 		}
 		if (xxlJobGroup.getAddressType()!=0) {
 			if (StringUtils.isBlank(xxlJobGroup.getAddressList())) {
-				return new ReturnT<String>(500, "手动录入注册方式,机器地址不可为空");
+				return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_addressType_limit") );
 			}
 			String[] addresss = xxlJobGroup.getAddressList().split(",");
 			for (String item: addresss) {
 				if (StringUtils.isBlank(item)) {
-					return new ReturnT<String>(500, "机器地址非法");
+					return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_registryList_unvalid") );
 				}
 			}
 		}
@@ -71,22 +72,22 @@ public class JobGroupController {
 	public ReturnT<String> update(XxlJobGroup xxlJobGroup){
 		// valid
 		if (xxlJobGroup.getAppName()==null || StringUtils.isBlank(xxlJobGroup.getAppName())) {
-			return new ReturnT<String>(500, "请输入AppName");
+			return new ReturnT<String>(500, (I18nUtil.getString("system_please_input")+"AppName") );
 		}
-		if (xxlJobGroup.getAppName().length()>64) {
-			return new ReturnT<String>(500, "AppName长度限制为4~64");
+		if (xxlJobGroup.getAppName().length()<4 || xxlJobGroup.getAppName().length()>64) {
+			return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_appName_length") );
 		}
 		if (xxlJobGroup.getTitle()==null || StringUtils.isBlank(xxlJobGroup.getTitle())) {
-			return new ReturnT<String>(500, "请输入名称");
+			return new ReturnT<String>(500, (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobgroup_field_title")) );
 		}
 		if (xxlJobGroup.getAddressType()!=0) {
 			if (StringUtils.isBlank(xxlJobGroup.getAddressList())) {
-				return new ReturnT<String>(500, "手动录入注册方式,机器地址不可为空");
+				return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_addressType_limit") );
 			}
 			String[] addresss = xxlJobGroup.getAddressList().split(",");
 			for (String item: addresss) {
 				if (StringUtils.isBlank(item)) {
-					return new ReturnT<String>(500, "机器地址非法");
+					return new ReturnT<String>(500, I18nUtil.getString("jobgroup_field_registryList_unvalid") );
 				}
 			}
 		}
@@ -100,14 +101,14 @@ public class JobGroupController {
 	public ReturnT<String> remove(int id){
 
 		// valid
-		int count = xxlJobInfoDao.pageListCount(0, 10, id, null);
+		int count = xxlJobInfoDao.pageListCount(0, 10, id, null, null);
 		if (count > 0) {
-			return new ReturnT<String>(500, "该分组使用中, 不可删除");
+			return new ReturnT<String>(500, I18nUtil.getString("jobgroup_del_limit_0") );
 		}
 
 		List<XxlJobGroup> allList = xxlJobGroupDao.findAll();
 		if (allList.size() == 1) {
-			return new ReturnT<String>(500, "删除失败, 系统需要至少预留一个默认分组");
+			return new ReturnT<String>(500, I18nUtil.getString("jobgroup_del_limit_1") );
 		}
 
 		int ret = xxlJobGroupDao.remove(id);

+ 5 - 5
xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobInfoController.java

@@ -53,9 +53,9 @@ public class JobInfoController {
 	@ResponseBody
 	public Map<String, Object> pageList(@RequestParam(required = false, defaultValue = "0") int start,  
 			@RequestParam(required = false, defaultValue = "10") int length,
-			int jobGroup, String executorHandler, String filterTime) {
+			int jobGroup, String jobDesc, String executorHandler, String filterTime) {
 		
-		return xxlJobService.pageList(start, length, jobGroup, executorHandler, filterTime);
+		return xxlJobService.pageList(start, length, jobGroup, jobDesc, executorHandler, filterTime);
 	}
 	
 	@RequestMapping("/add")
@@ -64,10 +64,10 @@ public class JobInfoController {
 		return xxlJobService.add(jobInfo);
 	}
 	
-	@RequestMapping("/reschedule")
+	@RequestMapping("/update")
 	@ResponseBody
-	public ReturnT<String> reschedule(XxlJobInfo jobInfo) {
-		return xxlJobService.reschedule(jobInfo);
+	public ReturnT<String> update(XxlJobInfo jobInfo) {
+		return xxlJobService.update(jobInfo);
 	}
 	
 	@RequestMapping("/remove")

+ 8 - 5
xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java

@@ -4,12 +4,14 @@ import com.xxl.job.admin.core.model.XxlJobGroup;
 import com.xxl.job.admin.core.model.XxlJobInfo;
 import com.xxl.job.admin.core.model.XxlJobLog;
 import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
+import com.xxl.job.admin.core.util.I18nUtil;
 import com.xxl.job.admin.dao.XxlJobGroupDao;
 import com.xxl.job.admin.dao.XxlJobInfoDao;
 import com.xxl.job.admin.dao.XxlJobLogDao;
 import com.xxl.job.core.biz.ExecutorBiz;
 import com.xxl.job.core.biz.model.LogResult;
 import com.xxl.job.core.biz.model.ReturnT;
+import com.xxl.job.core.glue.GlueTypeEnum;
 import com.xxl.job.core.rpc.netcom.NetComClientProxy;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.time.DateUtils;
@@ -50,6 +52,7 @@ public class JobLogController {
 		// 执行器列表
 		List<XxlJobGroup> jobGroupList =  xxlJobGroupDao.findAll();
 		model.addAttribute("JobGroupList", jobGroupList);
+		model.addAttribute("GlueTypeEnum", GlueTypeEnum.values());
 
 		// 任务
 		if (jobId > 0) {
@@ -105,7 +108,7 @@ public class JobLogController {
 		ReturnT<String> logStatue = ReturnT.SUCCESS;
 		XxlJobLog jobLog = xxlJobLogDao.load(id);
 		if (jobLog == null) {
-            throw new RuntimeException("抱歉,日志ID非法.");
+            throw new RuntimeException(I18nUtil.getString("joblog_logid_unvalid"));
 		}
 
         model.addAttribute("triggerCode", jobLog.getTriggerCode());
@@ -145,10 +148,10 @@ public class JobLogController {
 		XxlJobLog log = xxlJobLogDao.load(id);
 		XxlJobInfo jobInfo = xxlJobInfoDao.loadById(log.getJobId());
 		if (jobInfo==null) {
-			return new ReturnT<String>(500, "参数异常");
+			return new ReturnT<String>(500, I18nUtil.getString("jobinfo_glue_jobid_unvalid"));
 		}
 		if (ReturnT.SUCCESS_CODE != log.getTriggerCode()) {
-			return new ReturnT<String>(500, "调度失败,无法终止日志");
+			return new ReturnT<String>(500, I18nUtil.getString("joblog_kill_log_limit"));
 		}
 
 		// request of kill
@@ -163,7 +166,7 @@ public class JobLogController {
 
 		if (ReturnT.SUCCESS_CODE == runResult.getCode()) {
 			log.setHandleCode(ReturnT.FAIL_CODE);
-			log.setHandleMsg("人为操作主动终止:" + (runResult.getMsg()!=null?runResult.getMsg():""));
+			log.setHandleMsg( I18nUtil.getString("joblog_kill_log_byman")+":" + (runResult.getMsg()!=null?runResult.getMsg():""));
 			log.setHandleTime(new Date());
 			xxlJobLogDao.updateHandleInfo(log);
 			return new ReturnT<String>(runResult.getMsg());
@@ -197,7 +200,7 @@ public class JobLogController {
 		} else if (type == 9) {
 			clearBeforeNum = 0;			// 清理所有日志数据
 		} else {
-			return new ReturnT<String>(ReturnT.FAIL_CODE, "清理类型参数异常");
+			return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("joblog_clean_type_unvalid"));
 		}
 
 		xxlJobLogDao.clearLog(jobGroup, jobId, clearBeforeTime, clearBeforeNum);

+ 9 - 1
xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/CookieInterceptor.java

@@ -1,5 +1,7 @@
 package com.xxl.job.admin.controller.interceptor;
 
+import com.xxl.job.admin.core.util.FtlUtil;
+import com.xxl.job.admin.core.util.I18nUtil;
 import org.apache.commons.lang3.ArrayUtils;
 import org.springframework.web.servlet.ModelAndView;
 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
@@ -19,7 +21,8 @@ public class CookieInterceptor extends HandlerInterceptorAdapter {
 	@Override
 	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
 			ModelAndView modelAndView) throws Exception {
-		
+
+		// cookie
 		if (modelAndView!=null && ArrayUtils.isNotEmpty(request.getCookies())) {
 			HashMap<String, Cookie> cookieMap = new HashMap<String, Cookie>();
 			for (Cookie ck : request.getCookies()) {
@@ -27,6 +30,11 @@ public class CookieInterceptor extends HandlerInterceptorAdapter {
 			}
 			modelAndView.addObject("cookieMap", cookieMap);
 		}
+
+		// static method
+		if (modelAndView != null) {
+			modelAndView.addObject("I18nUtil", FtlUtil.generateStaticModel(I18nUtil.class.getName()));
+		}
 		
 		super.postHandle(request, response, handler, modelAndView);
 	}

+ 3 - 3
xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/PermissionInterceptor.java

@@ -1,8 +1,8 @@
 package com.xxl.job.admin.controller.interceptor;
 
 import com.xxl.job.admin.controller.annotation.PermessionLimit;
+import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
 import com.xxl.job.admin.core.util.CookieUtil;
-import com.xxl.job.admin.core.util.PropertiesUtil;
 import org.apache.commons.codec.digest.DigestUtils;
 import org.springframework.web.method.HandlerMethod;
 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
@@ -22,8 +22,8 @@ public class PermissionInterceptor extends HandlerInterceptorAdapter {
 	public static final String LOGIN_IDENTITY_KEY = "XXL_JOB_LOGIN_IDENTITY";
 	public static final String LOGIN_IDENTITY_TOKEN;
     static {
-        String username = PropertiesUtil.getString("xxl.job.login.username");
-        String password = PropertiesUtil.getString("xxl.job.login.password");
+        String username = XxlJobAdminConfig.getAdminConfig().getLoginUsername();
+        String password = XxlJobAdminConfig.getAdminConfig().getLoginPassword();
 
         // login token
         String tokenTmp = DigestUtils.md5Hex(username + "_" + password);

+ 23 - 7
xxl-job-admin/src/main/java/com/xxl/job/admin/controller/resolver/WebExceptionResolver.java

@@ -11,6 +11,7 @@ import org.springframework.web.servlet.ModelAndView;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
 
 /**
  * common exception resolver
@@ -23,19 +24,34 @@ public class WebExceptionResolver implements HandlerExceptionResolver {
 	public ModelAndView resolveException(HttpServletRequest request,
 			HttpServletResponse response, Object handler, Exception ex) {
 		logger.error("WebExceptionResolver:{}", ex);
-		
-		ModelAndView mv = new ModelAndView();
+
+		// if json
+		boolean isJson = false;
 		HandlerMethod method = (HandlerMethod)handler;
 		ResponseBody responseBody = method.getMethodAnnotation(ResponseBody.class);
 		if (responseBody != null) {
-			response.setContentType("application/json;charset=UTF-8");
-			mv.addObject("result", JacksonUtil.writeValueAsString(new ReturnT<String>(500, ex.toString().replaceAll("\n", "<br/>"))));
-			mv.setViewName("/common/common.result");
+			isJson = true;
+		}
+
+		// error result
+		ReturnT<String> errorResult = new ReturnT<String>(ReturnT.FAIL_CODE, ex.toString().replaceAll("\n", "<br/>"));
+
+		// response
+		ModelAndView mv = new ModelAndView();
+		if (isJson) {
+			try {
+				response.setContentType("application/json;charset=utf-8");
+				response.getWriter().print(JacksonUtil.writeValueAsString(errorResult));
+			} catch (IOException e) {
+				logger.error(e.getMessage(), e);
+			}
+			return mv;
 		} else {
-			mv.addObject("exceptionMsg", ex.toString().replaceAll("\n", "<br/>"));	
+
+			mv.addObject("exceptionMsg", errorResult.getMsg());
 			mv.setViewName("/common/common.exception");
+			return mv;
 		}
-		return mv;
 	}
 	
 }

+ 81 - 0
xxl-job-admin/src/main/java/com/xxl/job/admin/core/conf/XxlJobAdminConfig.java

@@ -0,0 +1,81 @@
+package com.xxl.job.admin.core.conf;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * xxl-job config
+ *
+ * @author xuxueli 2017-04-28
+ */
+@Configuration
+public class XxlJobAdminConfig implements InitializingBean{
+    private static XxlJobAdminConfig adminConfig = null;
+    public static XxlJobAdminConfig getAdminConfig() {
+        return adminConfig;
+    }
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        adminConfig = this;
+    }
+
+    @Value("${xxl.job.mail.host}")
+    private String mailHost;
+
+    @Value("${xxl.job.mail.port}")
+    private String mailPort;
+
+    @Value("${xxl.job.mail.username}")
+    private String mailUsername;
+
+    @Value("${xxl.job.mail.password}")
+    private String mailPassword;
+
+    @Value("${xxl.job.mail.sendNick}")
+    private String mailSendNick;
+
+    @Value("${xxl.job.login.username}")
+    private String loginUsername;
+
+    @Value("${xxl.job.login.password}")
+    private String loginPassword;
+
+    @Value("${xxl.job.i18n}")
+    private String i18n;
+
+
+    public String getMailHost() {
+        return mailHost;
+    }
+
+    public String getMailPort() {
+        return mailPort;
+    }
+
+    public String getMailUsername() {
+        return mailUsername;
+    }
+
+    public String getMailPassword() {
+        return mailPassword;
+    }
+
+    public String getMailSendNick() {
+        return mailSendNick;
+    }
+
+    public String getLoginUsername() {
+        return loginUsername;
+    }
+
+    public String getLoginPassword() {
+        return loginPassword;
+    }
+
+    public String getI18n() {
+        return i18n;
+    }
+
+}

+ 4 - 2
xxl-job-admin/src/main/java/com/xxl/job/admin/core/enums/ExecutorFailStrategyEnum.java

@@ -1,13 +1,15 @@
 package com.xxl.job.admin.core.enums;
 
+import com.xxl.job.admin.core.util.I18nUtil;
+
 /**
  * Created by xuxueli on 17/5/9.
  */
 public enum ExecutorFailStrategyEnum {
 
-    FAIL_ALARM("失败告警"),
+    FAIL_ALARM(I18nUtil.getString("jobconf_fail_alarm")),
 
-    FAIL_RETRY("失败重试");
+    FAIL_RETRY(I18nUtil.getString("jobconf_fail_retry"));
 
     private final String title;
     private ExecutorFailStrategyEnum(String title) {

+ 11 - 10
xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/ExecutorRouteStrategyEnum.java

@@ -1,22 +1,23 @@
 package com.xxl.job.admin.core.route;
 
 import com.xxl.job.admin.core.route.strategy.*;
+import com.xxl.job.admin.core.util.I18nUtil;
 
 /**
  * Created by xuxueli on 17/3/10.
  */
 public enum ExecutorRouteStrategyEnum {
 
-    FIRST("第一个", new ExecutorRouteFirst()),
-    LAST("最后一个", new ExecutorRouteLast()),
-    ROUND("轮询", new ExecutorRouteRound()),
-    RANDOM("随机", new ExecutorRouteRandom()),
-    CONSISTENT_HASH("一致性HASH", new ExecutorRouteConsistentHash()),
-    LEAST_FREQUENTLY_USED("最不经常使用", new ExecutorRouteLFU()),
-    LEAST_RECENTLY_USED("最近最久未使用", new ExecutorRouteLRU()),
-    FAILOVER("故障转移", new ExecutorRouteFailover()),
-    BUSYOVER("忙碌转移", new ExecutorRouteBusyover()),
-    SHARDING_BROADCAST("分片广播", null);
+    FIRST(I18nUtil.getString("jobconf_route_first"), new ExecutorRouteFirst()),
+    LAST(I18nUtil.getString("jobconf_route_last"), new ExecutorRouteLast()),
+    ROUND(I18nUtil.getString("jobconf_route_round"), new ExecutorRouteRound()),
+    RANDOM(I18nUtil.getString("jobconf_route_random"), new ExecutorRouteRandom()),
+    CONSISTENT_HASH(I18nUtil.getString("jobconf_route_consistenthash"), new ExecutorRouteConsistentHash()),
+    LEAST_FREQUENTLY_USED(I18nUtil.getString("jobconf_route_lfu"), new ExecutorRouteLFU()),
+    LEAST_RECENTLY_USED(I18nUtil.getString("jobconf_route_lru"), new ExecutorRouteLRU()),
+    FAILOVER(I18nUtil.getString("jobconf_route_failover"), new ExecutorRouteFailover()),
+    BUSYOVER(I18nUtil.getString("jobconf_route_busyover"), new ExecutorRouteBusyover()),
+    SHARDING_BROADCAST(I18nUtil.getString("jobconf_route_shard"), null);
 
     ExecutorRouteStrategyEnum(String title, ExecutorRouter router) {
         this.title = title;

+ 2 - 1
xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteBusyover.java

@@ -3,6 +3,7 @@ package com.xxl.job.admin.core.route.strategy;
 import com.xxl.job.admin.core.route.ExecutorRouter;
 import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
 import com.xxl.job.admin.core.trigger.XxlJobTrigger;
+import com.xxl.job.admin.core.util.I18nUtil;
 import com.xxl.job.core.biz.ExecutorBiz;
 import com.xxl.job.core.biz.model.ReturnT;
 import com.xxl.job.core.biz.model.TriggerParam;
@@ -33,7 +34,7 @@ public class ExecutorRouteBusyover extends ExecutorRouter {
                 idleBeatResult = new ReturnT<String>(ReturnT.FAIL_CODE, ""+e );
             }
             idleBeatResultSB.append( (idleBeatResultSB.length()>0)?"<br><br>":"")
-                    .append("空闲检测:")
+                    .append(I18nUtil.getString("jobconf_idleBeat") + ":")
                     .append("<br>address:").append(address)
                     .append("<br>code:").append(idleBeatResult.getCode())
                     .append("<br>msg:").append(idleBeatResult.getMsg());

+ 2 - 1
xxl-job-admin/src/main/java/com/xxl/job/admin/core/route/strategy/ExecutorRouteFailover.java

@@ -3,6 +3,7 @@ package com.xxl.job.admin.core.route.strategy;
 import com.xxl.job.admin.core.route.ExecutorRouter;
 import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
 import com.xxl.job.admin.core.trigger.XxlJobTrigger;
+import com.xxl.job.admin.core.util.I18nUtil;
 import com.xxl.job.core.biz.ExecutorBiz;
 import com.xxl.job.core.biz.model.ReturnT;
 import com.xxl.job.core.biz.model.TriggerParam;
@@ -33,7 +34,7 @@ public class ExecutorRouteFailover extends ExecutorRouter {
                 beatResult = new ReturnT<String>(ReturnT.FAIL_CODE, ""+e );
             }
             beatResultSB.append( (beatResultSB.length()>0)?"<br><br>":"")
-                    .append("心跳检测:")
+                    .append(I18nUtil.getString("jobconf_beat") + ":")
                     .append("<br>address:").append(address)
                     .append("<br>code:").append(beatResult.getCode())
                     .append("<br>msg:").append(beatResult.getMsg());

+ 8 - 7
xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobFailMonitorHelper.java

@@ -4,6 +4,7 @@ import com.xxl.job.admin.core.model.XxlJobGroup;
 import com.xxl.job.admin.core.model.XxlJobInfo;
 import com.xxl.job.admin.core.model.XxlJobLog;
 import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
+import com.xxl.job.admin.core.util.I18nUtil;
 import com.xxl.job.admin.core.util.MailUtil;
 import com.xxl.job.core.biz.model.ReturnT;
 import com.xxl.job.core.handler.IJobHandler;
@@ -119,14 +120,14 @@ public class JobFailMonitorHelper {
 	// ---------------------- alarm ----------------------
 
 	// email alarm template
-	private static final String mailBodyTemplate = "<h5>监控告警明细:</span>" +
+	private static final String mailBodyTemplate = "<h5>" + I18nUtil.getString("jobconf_monitor_detail") + ":</span>" +
 			"<table border=\"1\" cellpadding=\"3\" style=\"border-collapse:collapse; width:80%;\" >\n" +
 			"   <thead style=\"font-weight: bold;color: #ffffff;background-color: #ff8c00;\" >" +
 			"      <tr>\n" +
-			"         <td>执行器</td>\n" +
-			"         <td>任务ID</td>\n" +
-			"         <td>任务描述</td>\n" +
-			"         <td>告警类型</td>\n" +
+			"         <td>"+ I18nUtil.getString("jobinfo_field_jobgroup") +"</td>\n" +
+			"         <td>"+ I18nUtil.getString("jobinfo_field_id") +"</td>\n" +
+			"         <td>"+ I18nUtil.getString("jobinfo_field_jobdesc") +"</td>\n" +
+			"         <td>"+ I18nUtil.getString("jobconf_monitor_alarm_title") +"</td>\n" +
 			"      </tr>\n" +
 			"   <thead/>\n" +
 			"   <tbody>\n" +
@@ -134,7 +135,7 @@ public class JobFailMonitorHelper {
 			"         <td>{0}</td>\n" +
 			"         <td>{1}</td>\n" +
 			"         <td>{2}</td>\n" +
-			"         <td>调度失败</td>\n" +
+			"         <td>"+ I18nUtil.getString("jobconf_monitor_alarm_type") +"</td>\n" +
 			"      </tr>\n" +
 			"   <tbody>\n" +
 			"</table>";
@@ -154,7 +155,7 @@ public class JobFailMonitorHelper {
 			for (String email: emailSet) {
 				XxlJobGroup group = XxlJobDynamicScheduler.xxlJobGroupDao.load(Integer.valueOf(info.getJobGroup()));
 
-				String title = "调度中心监控报警";
+				String title = I18nUtil.getString("jobconf_monitor");
 				String content = MessageFormat.format(mailBodyTemplate, group!=null?group.getTitle():"null", info.getId(), info.getJobDesc());
 
 				MailUtil.sendMail(email, title, content);

+ 24 - 21
xxl-job-admin/src/main/java/com/xxl/job/admin/core/trigger/XxlJobTrigger.java

@@ -7,6 +7,7 @@ import com.xxl.job.admin.core.model.XxlJobLog;
 import com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum;
 import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
 import com.xxl.job.admin.core.thread.JobFailMonitorHelper;
+import com.xxl.job.admin.core.util.I18nUtil;
 import com.xxl.job.core.biz.ExecutorBiz;
 import com.xxl.job.core.biz.model.ReturnT;
 import com.xxl.job.core.biz.model.TriggerParam;
@@ -67,17 +68,18 @@ public class XxlJobTrigger {
 
                 ReturnT<String> triggerResult = new ReturnT<String>(null);
                 StringBuffer triggerMsgSb = new StringBuffer();
-                triggerMsgSb.append("调度机器:").append(IpUtil.getIp());
-                triggerMsgSb.append("<br>执行器-注册方式:").append( (group.getAddressType() == 0)?"自动注册":"手动录入" );
-                triggerMsgSb.append("<br>执行器-地址列表:").append(group.getRegistryList());
-                triggerMsgSb.append("<br>路由策略:").append(executorRouteStrategyEnum.getTitle()).append("("+i+"/"+addressList.size()+")"); // update01
-                triggerMsgSb.append("<br>阻塞处理策略:").append(blockStrategy.getTitle());
-                triggerMsgSb.append("<br>失败处理策略:").append(failStrategy.getTitle());
+                triggerMsgSb.append(I18nUtil.getString("jobconf_trigger_admin_adress")).append(":").append(IpUtil.getIp());
+                triggerMsgSb.append("<br>").append(I18nUtil.getString("jobconf_trigger_exe_regtype")).append(":")
+                        .append( (group.getAddressType() == 0)?I18nUtil.getString("jobgroup_field_addressType_0"):I18nUtil.getString("jobgroup_field_addressType_1") );
+                triggerMsgSb.append("<br>").append(I18nUtil.getString("jobconf_trigger_exe_regaddress")).append(":").append(group.getRegistryList());
+                triggerMsgSb.append("<br>").append(I18nUtil.getString("jobinfo_field_executorRouteStrategy")).append(":").append(executorRouteStrategyEnum.getTitle()).append("("+i+"/"+addressList.size()+")"); // update01
+                triggerMsgSb.append("<br>").append(I18nUtil.getString("jobinfo_field_executorBlockStrategy")).append(":").append(blockStrategy.getTitle());
+                triggerMsgSb.append("<br>").append(I18nUtil.getString("jobinfo_field_executorFailStrategy")).append(":").append(failStrategy.getTitle());
 
                 // 3、trigger-valid
                 if (triggerResult.getCode()==ReturnT.SUCCESS_CODE && CollectionUtils.isEmpty(addressList)) {
                     triggerResult.setCode(ReturnT.FAIL_CODE);
-                    triggerMsgSb.append("<br>----------------------<br>").append("调度失败:").append("执行器地址为空");
+                    triggerMsgSb.append("<br>----------------------<br>").append(I18nUtil.getString("jobconf_trigger_address_empty"));
                 }
 
                 if (triggerResult.getCode() == ReturnT.SUCCESS_CODE) {
@@ -97,12 +99,12 @@ public class XxlJobTrigger {
 
                     // 4.2、trigger-run (route run / trigger remote executor)
                     triggerResult = runExecutor(triggerParam, address);     // update03
-                    triggerMsgSb.append("<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>").append(triggerResult.getMsg());
+                    triggerMsgSb.append("<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_run") +"<<<<<<<<<<< </span><br>").append(triggerResult.getMsg());
 
                     // 4.3、trigger (fail retry)
                     if (triggerResult.getCode()!=ReturnT.SUCCESS_CODE && failStrategy == ExecutorFailStrategyEnum.FAIL_RETRY) {
                         triggerResult = runExecutor(triggerParam, address);  // update04
-                        triggerMsgSb.append("<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>失败重试<<<<<<<<<<< </span><br>").append(triggerResult.getMsg());
+                        triggerMsgSb.append("<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_fail_retry") +"<<<<<<<<<<< </span><br>").append(triggerResult.getMsg());
                     }
                 }
 
@@ -112,7 +114,7 @@ public class XxlJobTrigger {
                 jobLog.setTriggerMsg(triggerMsgSb.toString());
                 XxlJobDynamicScheduler.xxlJobLogDao.updateTriggerInfo(jobLog);
 
-                // 6、monitor triger
+                // 6、monitor trigger
                 JobFailMonitorHelper.monitor(jobLog.getId());
                 logger.debug(">>>>>>>>>>> xxl-job trigger end, jobId:{}", jobLog.getId());
 
@@ -134,17 +136,18 @@ public class XxlJobTrigger {
 
             ReturnT<String> triggerResult = new ReturnT<String>(null);
             StringBuffer triggerMsgSb = new StringBuffer();
-            triggerMsgSb.append("调度机器:").append(IpUtil.getIp());
-            triggerMsgSb.append("<br>执行器-注册方式:").append( (group.getAddressType() == 0)?"自动注册":"手动录入" );
-            triggerMsgSb.append("<br>执行器-地址列表:").append(group.getRegistryList());
-            triggerMsgSb.append("<br>路由策略:").append(executorRouteStrategyEnum.getTitle());
-            triggerMsgSb.append("<br>阻塞处理策略:").append(blockStrategy.getTitle());
-            triggerMsgSb.append("<br>失败处理策略:").append(failStrategy.getTitle());
+            triggerMsgSb.append(I18nUtil.getString("jobconf_trigger_admin_adress")).append(":").append(IpUtil.getIp());
+            triggerMsgSb.append("<br>").append(I18nUtil.getString("jobconf_trigger_exe_regtype")).append(":")
+                    .append( (group.getAddressType() == 0)?I18nUtil.getString("jobgroup_field_addressType_0"):I18nUtil.getString("jobgroup_field_addressType_1") );
+            triggerMsgSb.append("<br>").append(I18nUtil.getString("jobconf_trigger_exe_regaddress")).append(":").append(group.getRegistryList());
+            triggerMsgSb.append("<br>").append(I18nUtil.getString("jobinfo_field_executorRouteStrategy")).append(":").append(executorRouteStrategyEnum.getTitle());
+            triggerMsgSb.append("<br>").append(I18nUtil.getString("jobinfo_field_executorBlockStrategy")).append(":").append(blockStrategy.getTitle());
+            triggerMsgSb.append("<br>").append(I18nUtil.getString("jobinfo_field_executorFailStrategy")).append(":").append(failStrategy.getTitle());
 
             // 3、trigger-valid
             if (triggerResult.getCode()==ReturnT.SUCCESS_CODE && CollectionUtils.isEmpty(addressList)) {
                 triggerResult.setCode(ReturnT.FAIL_CODE);
-                triggerMsgSb.append("<br>----------------------<br>").append("调度失败:").append("执行器地址为空");
+                triggerMsgSb.append("<br>----------------------<br>").append(I18nUtil.getString("jobconf_trigger_address_empty"));
             }
 
             if (triggerResult.getCode() == ReturnT.SUCCESS_CODE) {
@@ -164,12 +167,12 @@ public class XxlJobTrigger {
 
                 // 4.2、trigger-run (route run / trigger remote executor)
                 triggerResult = executorRouteStrategyEnum.getRouter().routeRun(triggerParam, addressList);
-                triggerMsgSb.append("<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>").append(triggerResult.getMsg());
+                triggerMsgSb.append("<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_run") +"<<<<<<<<<<< </span><br>").append(triggerResult.getMsg());
 
                 // 4.3、trigger (fail retry)
                 if (triggerResult.getCode()!=ReturnT.SUCCESS_CODE && failStrategy == ExecutorFailStrategyEnum.FAIL_RETRY) {
                     triggerResult = executorRouteStrategyEnum.getRouter().routeRun(triggerParam, addressList);
-                    triggerMsgSb.append("<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>调度失败重试<<<<<<<<<<< </span><br>").append(triggerResult.getMsg());
+                    triggerMsgSb.append("<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_fail_retry") +"<<<<<<<<<<< </span><br>").append(triggerResult.getMsg());
                 }
             }
 
@@ -179,7 +182,7 @@ public class XxlJobTrigger {
             jobLog.setTriggerMsg(triggerMsgSb.toString());
             XxlJobDynamicScheduler.xxlJobLogDao.updateTriggerInfo(jobLog);
 
-            // 6、monitor triger
+            // 6、monitor trigger
             JobFailMonitorHelper.monitor(jobLog.getId());
             logger.debug(">>>>>>>>>>> xxl-job trigger end, jobId:{}", jobLog.getId());
         }
@@ -202,7 +205,7 @@ public class XxlJobTrigger {
             runResult = new ReturnT<String>(ReturnT.FAIL_CODE, ""+e );
         }
 
-        StringBuffer runResultSB = new StringBuffer("触发调度:");
+        StringBuffer runResultSB = new StringBuffer(I18nUtil.getString("jobconf_trigger_run") + ":");
         runResultSB.append("<br>address:").append(address);
         runResultSB.append("<br>code:").append(runResult.getCode());
         runResultSB.append("<br>msg:").append(runResult.getMsg());

+ 25 - 0
xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/FtlUtil.java

@@ -0,0 +1,25 @@
+package com.xxl.job.admin.core.util;
+
+import freemarker.ext.beans.BeansWrapper;
+import freemarker.template.TemplateHashModel;
+
+/**
+ * ftl util
+ *
+ * @author xuxueli 2018-01-17 20:37:48
+ */
+public class FtlUtil {
+
+    public static TemplateHashModel generateStaticModel(String packageName) {
+        try {
+            BeansWrapper wrapper = BeansWrapper.getDefaultInstance();
+            TemplateHashModel staticModels = wrapper.getStaticModels();
+            TemplateHashModel fileStatics = (TemplateHashModel) staticModels.get(packageName);
+            return fileStatics;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+}

+ 82 - 0
xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/I18nUtil.java

@@ -0,0 +1,82 @@
+package com.xxl.job.admin.core.util;
+
+import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
+import com.xxl.job.core.util.JacksonUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.EncodedResource;
+import org.springframework.core.io.support.PropertiesLoaderUtils;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * i18n util
+ *
+ * @author xuxueli 2018-01-17 20:39:06
+ */
+public class I18nUtil {
+    private static Logger logger = LoggerFactory.getLogger(I18nUtil.class);
+
+    private static Properties prop = null;
+    public static Properties loadI18nProp(){
+        if (prop != null) {
+            return prop;
+        }
+        try {
+            // bild i18n prop
+            String i18n = XxlJobAdminConfig.getAdminConfig().getI18n();
+            i18n = StringUtils.isNotBlank(i18n)?("_"+i18n):i18n;
+            String i18nFile = MessageFormat.format("i18n/message{0}.properties", i18n);
+
+            // load prop
+            Resource resource = new ClassPathResource(i18nFile);
+            EncodedResource encodedResource = new EncodedResource(resource,"UTF-8");
+            prop = PropertiesLoaderUtils.loadProperties(encodedResource);
+        } catch (IOException e) {
+            logger.error(e.getMessage(), e);
+        }
+        return prop;
+    }
+
+    /**
+     * get val of i18n key
+     *
+     * @param key
+     * @return
+     */
+    public static String getString(String key) {
+        return loadI18nProp().getProperty(key);
+    }
+
+    /**
+     * get mult val of i18n mult key, as json
+     *
+     * @param keys
+     * @return
+     */
+    public static String getMultString(String... keys) {
+        Map<String, String> map = new HashMap<>();
+
+        Properties prop = loadI18nProp();
+        if (keys!=null && keys.length>0) {
+            for (String key: keys) {
+                map.put(key, prop.getProperty(key));
+            }
+        } else {
+            for (String key: prop.stringPropertyNames()) {
+                map.put(key, prop.getProperty(key));
+            }
+        }
+
+        String json = JacksonUtil.writeValueAsString(map);
+        return json;
+    }
+
+}

+ 134 - 0
xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/LocalCacheUtil.java

@@ -0,0 +1,134 @@
+package com.xxl.job.admin.core.util;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * local cache tool
+ *
+ * @author xuxueli 2018-01-22 21:37:34
+ */
+public class LocalCacheUtil {
+
+    private static ConcurrentHashMap<String, LocalCacheData> cacheRepository = new ConcurrentHashMap<>();
+    private static class LocalCacheData{
+        private String key;
+        private Object val;
+        private long timeoutTime;
+
+        public LocalCacheData() {
+        }
+
+        public LocalCacheData(String key, Object val, long timeoutTime) {
+            this.key = key;
+            this.val = val;
+            this.timeoutTime = timeoutTime;
+        }
+
+        public String getKey() {
+            return key;
+        }
+
+        public void setKey(String key) {
+            this.key = key;
+        }
+
+        public Object getVal() {
+            return val;
+        }
+
+        public void setVal(Object val) {
+            this.val = val;
+        }
+
+        public long getTimeoutTime() {
+            return timeoutTime;
+        }
+
+        public void setTimeoutTime(long timeoutTime) {
+            this.timeoutTime = timeoutTime;
+        }
+    }
+
+
+    /**
+     * set cache
+     *
+     * @param key
+     * @param val
+     * @param cacheTime
+     * @return
+     */
+    public static boolean set(String key, Object val, long cacheTime){
+
+        // clean timeout cache, before set new cache (avoid cache too much)
+        cleanTimeutCache();
+
+        // set new cache
+        if (StringUtils.isBlank(key)) {
+            return false;
+        }
+        if (val == null) {
+            remove(key);
+        }
+        if (cacheTime <= 0) {
+            remove(key);
+        }
+        long timeoutTime = System.currentTimeMillis() + cacheTime;
+        LocalCacheData localCacheData = new LocalCacheData(key, val, timeoutTime);
+        cacheRepository.put(localCacheData.getKey(), localCacheData);
+        return true;
+    }
+
+    /**
+     * remove cache
+     *
+     * @param key
+     * @return
+     */
+    public static boolean remove(String key){
+        if (StringUtils.isBlank(key)) {
+            return false;
+        }
+        cacheRepository.remove(key);
+        return true;
+    }
+
+    /**
+     * get cache
+     *
+     * @param key
+     * @return
+     */
+    public static Object get(String key){
+        if (StringUtils.isBlank(key)) {
+            return null;
+        }
+        LocalCacheData localCacheData = cacheRepository.get(key);
+        if (localCacheData!=null && System.currentTimeMillis()<localCacheData.getTimeoutTime()) {
+            return localCacheData.getVal();
+        } else {
+            remove(key);
+            return null;
+        }
+    }
+
+    /**
+     * clean timeout cache
+     *
+     * @return
+     */
+    public static boolean cleanTimeutCache(){
+        if (!cacheRepository.keySet().isEmpty()) {
+            for (String key: cacheRepository.keySet()) {
+                LocalCacheData localCacheData = cacheRepository.get(key);
+                if (localCacheData!=null && System.currentTimeMillis()>=localCacheData.getTimeoutTime()) {
+                    cacheRepository.remove(key);
+                }
+            }
+        }
+        return true;
+    }
+
+}

+ 5 - 17
xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/MailUtil.java

@@ -1,5 +1,6 @@
 package com.xxl.job.admin.core.util;
 
+import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
 import org.apache.commons.mail.DefaultAuthenticator;
 import org.apache.commons.mail.EmailException;
 import org.apache.commons.mail.HtmlEmail;
@@ -16,19 +17,6 @@ import java.nio.charset.Charset;
 public class MailUtil {
 	private static Logger logger = LoggerFactory.getLogger(MailUtil.class);
 	
-	private static String host;
-	private static String port;
-	private static String username;
-	private static String password;
-	private static String sendNick;
-	static{
-		host = PropertiesUtil.getString("xxl.job.mail.host");
-		port = PropertiesUtil.getString("xxl.job.mail.port");
-		username = PropertiesUtil.getString("xxl.job.mail.username");
-		password = PropertiesUtil.getString("xxl.job.mail.password");
-		sendNick = PropertiesUtil.getString("xxl.job.mail.sendNick");
-	}
-
 	/**
 	 *
 	 * @param toAddress		收件人邮箱
@@ -46,13 +34,13 @@ public class MailUtil {
 			//email.setTLS(true);		// 是否TLS校验,,某些邮箱需要TLS安全校验,同理有SSL校验
 			//email.setSSL(true);
 
-			email.setHostName(host);
-			email.setSmtpPort(Integer.valueOf(port));
+			email.setHostName(XxlJobAdminConfig.getAdminConfig().getMailHost());
+			email.setSmtpPort(Integer.valueOf(XxlJobAdminConfig.getAdminConfig().getMailPort()));
 			//email.setSslSmtpPort(port);
-			email.setAuthenticator(new DefaultAuthenticator(username, password));
+			email.setAuthenticator(new DefaultAuthenticator(XxlJobAdminConfig.getAdminConfig().getMailUsername(), XxlJobAdminConfig.getAdminConfig().getMailPassword()));
 			email.setCharset(Charset.defaultCharset().name());
 
-			email.setFrom(username, sendNick);
+			email.setFrom(XxlJobAdminConfig.getAdminConfig().getMailUsername(), XxlJobAdminConfig.getAdminConfig().getMailSendNick());
 			email.addTo(toAddress);
 			email.setSubject(mailSubject);
 			email.setMsg(mailBody);

+ 0 - 38
xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/PropertiesUtil.java

@@ -1,38 +0,0 @@
-package com.xxl.job.admin.core.util;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.core.io.ClassPathResource;
-import org.springframework.core.io.Resource;
-import org.springframework.core.io.support.EncodedResource;
-import org.springframework.core.io.support.PropertiesLoaderUtils;
-
-import java.io.IOException;
-import java.util.Properties;
-
-/**
- * properties util
- *
- * @author xuxueli 2015-8-28 10:35:53
- */
-public class PropertiesUtil {
-	private static Logger logger = LoggerFactory.getLogger(PropertiesUtil.class);
-	private static final String file_name = "xxl-job-admin.properties";
-
-
-	public static String getString(String key) {
-		Properties prop = null;
-		try {
-			Resource resource = new ClassPathResource(file_name);
-			EncodedResource encodedResource = new EncodedResource(resource,"UTF-8");
-			prop = PropertiesLoaderUtils.loadProperties(encodedResource);
-		} catch (IOException e) {
-			logger.error(e.getMessage(), e);
-		}
-		if (prop!=null) {
-			return prop.getProperty(key);
-		}
-		return null;
-	}
-
-}

+ 10 - 2
xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobInfoDao.java

@@ -12,8 +12,16 @@ import java.util.List;
  */
 public interface XxlJobInfoDao {
 
-	public List<XxlJobInfo> pageList(@Param("offset") int offset, @Param("pagesize") int pagesize, @Param("jobGroup") int jobGroup, @Param("executorHandler") String executorHandler);
-	public int pageListCount(@Param("offset") int offset, @Param("pagesize") int pagesize, @Param("jobGroup") int jobGroup, @Param("executorHandler") String executorHandler);
+	public List<XxlJobInfo> pageList(@Param("offset") int offset,
+									 @Param("pagesize") int pagesize,
+									 @Param("jobGroup") int jobGroup,
+									 @Param("jobDesc") String jobDesc,
+									 @Param("executorHandler") String executorHandler);
+	public int pageListCount(@Param("offset") int offset,
+							 @Param("pagesize") int pagesize,
+							 @Param("jobGroup") int jobGroup,
+							 @Param("jobDesc") String jobDesc,
+							 @Param("executorHandler") String executorHandler);
 	
 	public int save(XxlJobInfo info);
 

+ 1 - 2
xxl-job-admin/src/main/java/com/xxl/job/admin/dao/XxlJobLogDao.java

@@ -41,8 +41,7 @@ public interface XxlJobLogDao {
 	public int triggerCountByHandleCode(@Param("handleCode") int handleCode);
 
 	public List<Map<String, Object>> triggerCountByDay(@Param("from") Date from,
-													   @Param("to") Date to,
-													   @Param("handleCode") int handleCode);
+													   @Param("to") Date to);
 
 	public int clearLog(@Param("jobGroup") int jobGroup,
 						@Param("jobId") int jobId,

+ 69 - 10
xxl-job-admin/src/main/java/com/xxl/job/admin/service/XxlJobService.java

@@ -13,23 +13,82 @@ import java.util.Map;
  * @author xuxueli 2016-5-28 15:30:33
  */
 public interface XxlJobService {
-	
-	public Map<String, Object> pageList(int start, int length, int jobGroup, String executorHandler, String filterTime);
-	
+
+	/**
+	 * page list
+	 *
+	 * @param start
+	 * @param length
+	 * @param jobGroup
+	 * @param jobDesc
+	 * @param executorHandler
+	 * @param filterTime
+	 * @return
+	 */
+	public Map<String, Object> pageList(int start, int length, int jobGroup, String jobDesc, String executorHandler, String filterTime);
+
+	/**
+	 * add job
+	 *
+	 * @param jobInfo
+	 * @return
+	 */
 	public ReturnT<String> add(XxlJobInfo jobInfo);
-	
-	public ReturnT<String> reschedule(XxlJobInfo jobInfo);
-	
+
+	/**
+	 * update job
+	 *
+	 * @param jobInfo
+	 * @return
+	 */
+	public ReturnT<String> update(XxlJobInfo jobInfo);
+
+	/**
+	 * remove job
+	 *
+	 * @param id
+	 * @return
+	 */
 	public ReturnT<String> remove(int id);
-	
+
+	/**
+	 * pause job
+	 *
+	 * @param id
+	 * @return
+	 */
 	public ReturnT<String> pause(int id);
-	
+
+	/**
+	 * resume job
+	 *
+	 * @param id
+	 * @return
+	 */
 	public ReturnT<String> resume(int id);
-	
+
+	/**
+	 * trigger job
+	 *
+	 * @param id
+	 * @return
+	 */
 	public ReturnT<String> triggerJob(int id);
 
+	/**
+	 * dashboard info
+	 *
+	 * @return
+	 */
 	public Map<String,Object> dashboardInfo();
 
-	public ReturnT<Map<String,Object>> triggerChartDate(Date startDate, Date endDate);
+	/**
+	 * chart info
+	 *
+	 * @param startDate
+	 * @param endDate
+	 * @return
+	 */
+	public ReturnT<Map<String,Object>> chartInfo(Date startDate, Date endDate);
 
 }

+ 15 - 8
xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/AdminBizImpl.java

@@ -2,6 +2,7 @@ package com.xxl.job.admin.service.impl;
 
 import com.xxl.job.admin.core.model.XxlJobInfo;
 import com.xxl.job.admin.core.model.XxlJobLog;
+import com.xxl.job.admin.core.util.I18nUtil;
 import com.xxl.job.admin.dao.XxlJobInfoDao;
 import com.xxl.job.admin.dao.XxlJobLogDao;
 import com.xxl.job.admin.dao.XxlJobRegistryDao;
@@ -64,7 +65,7 @@ public class AdminBizImpl implements AdminBiz {
         if (IJobHandler.SUCCESS.getCode() == handleCallbackParam.getExecuteResult().getCode()) {
             XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(log.getJobId());
             if (xxlJobInfo!=null && StringUtils.isNotBlank(xxlJobInfo.getChildJobId())) {
-                callbackMsg = "<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发子任务<<<<<<<<<<< </span><br>";
+                callbackMsg = "<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_trigger_child_run") +"<<<<<<<<<<< </span><br>";
 
                 String[] childJobIds = xxlJobInfo.getChildJobId().split(",");
                 for (int i = 0; i < childJobIds.length; i++) {
@@ -73,21 +74,27 @@ public class AdminBizImpl implements AdminBiz {
                         ReturnT<String> triggerChildResult = xxlJobService.triggerJob(childJobId);
 
                         // add msg
-                        callbackMsg += MessageFormat.format("{0}/{1} [任务ID={2}], 触发{3}, 触发备注: {4} <br>",
-                                (i+1), childJobIds.length, childJobIds[i], (triggerChildResult.getCode()==ReturnT.SUCCESS_CODE?"成功":"失败"), triggerChildResult.getMsg());
+                        callbackMsg += MessageFormat.format(I18nUtil.getString("jobconf_callback_child_msg1"),
+                                (i+1),
+                                childJobIds.length,
+                                childJobIds[i],
+                                (triggerChildResult.getCode()==ReturnT.SUCCESS_CODE?I18nUtil.getString("system_success"):I18nUtil.getString("system_fail")),
+                                triggerChildResult.getMsg());
                     } else {
-                        callbackMsg += MessageFormat.format(" {0}/{1} [任务ID={2}], 触发失败, 触发备注: 任务ID格式错误 <br>",
-                                (i+1), childJobIds.length, childJobIds[i]);
+                        callbackMsg += MessageFormat.format(I18nUtil.getString("jobconf_callback_child_msg2"),
+                                (i+1),
+                                childJobIds.length,
+                                childJobIds[i]);
                     }
                 }
 
             }
         } else if (IJobHandler.FAIL_RETRY.getCode() == handleCallbackParam.getExecuteResult().getCode()){
             ReturnT<String> retryTriggerResult = xxlJobService.triggerJob(log.getJobId());
-            callbackMsg = "<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>执行失败重试<<<<<<<<<<< </span><br>";
+            callbackMsg = "<br><br><span style=\"color:#F39C12;\" > >>>>>>>>>>>"+ I18nUtil.getString("jobconf_exe_fail_retry") +"<<<<<<<<<<< </span><br>";
 
-            callbackMsg += MessageFormat.format("触发{0}, 触发备注: {1}",
-                   (retryTriggerResult.getCode()==ReturnT.SUCCESS_CODE?"成功":"失败"), retryTriggerResult.getMsg());
+            callbackMsg += MessageFormat.format(I18nUtil.getString("jobconf_callback_msg1"),
+                   (retryTriggerResult.getCode()==ReturnT.SUCCESS_CODE?I18nUtil.getString("system_success"):I18nUtil.getString("system_fail")), retryTriggerResult.getMsg());
         }
 
         // handle msg

+ 65 - 48
xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/XxlJobServiceImpl.java

@@ -5,6 +5,8 @@ import com.xxl.job.admin.core.model.XxlJobGroup;
 import com.xxl.job.admin.core.model.XxlJobInfo;
 import com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum;
 import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
+import com.xxl.job.admin.core.util.I18nUtil;
+import com.xxl.job.admin.core.util.LocalCacheUtil;
 import com.xxl.job.admin.dao.XxlJobGroupDao;
 import com.xxl.job.admin.dao.XxlJobInfoDao;
 import com.xxl.job.admin.dao.XxlJobLogDao;
@@ -45,11 +47,11 @@ public class XxlJobServiceImpl implements XxlJobService {
 	private XxlJobLogGlueDao xxlJobLogGlueDao;
 	
 	@Override
-	public Map<String, Object> pageList(int start, int length, int jobGroup, String executorHandler, String filterTime) {
+	public Map<String, Object> pageList(int start, int length, int jobGroup, String jobDesc, String executorHandler, String filterTime) {
 
 		// page list
-		List<XxlJobInfo> list = xxlJobInfoDao.pageList(start, length, jobGroup, executorHandler);
-		int list_count = xxlJobInfoDao.pageListCount(start, length, jobGroup, executorHandler);
+		List<XxlJobInfo> list = xxlJobInfoDao.pageList(start, length, jobGroup, jobDesc, executorHandler);
+		int list_count = xxlJobInfoDao.pageListCount(start, length, jobGroup, jobDesc, executorHandler);
 		
 		// fill job info
 		if (list!=null && list.size()>0) {
@@ -71,31 +73,31 @@ public class XxlJobServiceImpl implements XxlJobService {
 		// valid
 		XxlJobGroup group = xxlJobGroupDao.load(jobInfo.getJobGroup());
 		if (group == null) {
-			return new ReturnT<String>(ReturnT.FAIL_CODE, "请选择“执行器”");
+			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_choose")+I18nUtil.getString("jobinfo_field_jobgroup")) );
 		}
 		if (!CronExpression.isValidExpression(jobInfo.getJobCron())) {
-			return new ReturnT<String>(ReturnT.FAIL_CODE, "请输入格式正确的“Cron”");
+			return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("jobinfo_field_cron_unvalid") );
 		}
 		if (StringUtils.isBlank(jobInfo.getJobDesc())) {
-			return new ReturnT<String>(ReturnT.FAIL_CODE, "请输入“任务描述”");
+			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_jobdesc")) );
 		}
 		if (StringUtils.isBlank(jobInfo.getAuthor())) {
-			return new ReturnT<String>(ReturnT.FAIL_CODE, "请输入“负责人”");
+			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_author")) );
 		}
 		if (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null) == null) {
-			return new ReturnT<String>(ReturnT.FAIL_CODE, "路由策略非法");
+			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorRouteStrategy")+I18nUtil.getString("system_unvalid")) );
 		}
 		if (ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(), null) == null) {
-			return new ReturnT<String>(ReturnT.FAIL_CODE, "阻塞处理策略非法");
+			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorBlockStrategy")+I18nUtil.getString("system_unvalid")) );
 		}
 		if (ExecutorFailStrategyEnum.match(jobInfo.getExecutorFailStrategy(), null) == null) {
-			return new ReturnT<String>(ReturnT.FAIL_CODE, "失败处理策略非法");
+			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorFailStrategy")+I18nUtil.getString("system_unvalid")) );
 		}
 		if (GlueTypeEnum.match(jobInfo.getGlueType()) == null) {
-			return new ReturnT<String>(ReturnT.FAIL_CODE, "运行模式非法非法");
+			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_gluetype")+I18nUtil.getString("system_unvalid")) );
 		}
 		if (GlueTypeEnum.BEAN==GlueTypeEnum.match(jobInfo.getGlueType()) && StringUtils.isBlank(jobInfo.getExecutorHandler())) {
-			return new ReturnT<String>(ReturnT.FAIL_CODE, "请输入“JobHandler”");
+			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+"JobHandler") );
 		}
 
 		// fix "\r" in shell
@@ -110,10 +112,12 @@ public class XxlJobServiceImpl implements XxlJobService {
 				if (StringUtils.isNotBlank(childJobIdItem) && StringUtils.isNumeric(childJobIdItem)) {
 					XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer.valueOf(childJobIdItem));
 					if (childJobInfo==null) {
-						return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})无效", childJobIdItem));
+						return new ReturnT<String>(ReturnT.FAIL_CODE,
+								MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_not_found")), childJobIdItem));
 					}
 				} else {
-					return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})格式错误", childJobIdItem));
+					return new ReturnT<String>(ReturnT.FAIL_CODE,
+							MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_unvalid")), childJobIdItem));
 				}
 			}
 			jobInfo.setChildJobId(StringUtils.join(childJobIds, ","));
@@ -122,7 +126,7 @@ public class XxlJobServiceImpl implements XxlJobService {
 		// add in db
 		xxlJobInfoDao.save(jobInfo);
 		if (jobInfo.getId() < 1) {
-			return new ReturnT<String>(ReturnT.FAIL_CODE, "新增任务失败");
+			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_add")+I18nUtil.getString("system_fail")) );
 		}
 
 		// add in quartz
@@ -140,31 +144,31 @@ public class XxlJobServiceImpl implements XxlJobService {
             } catch (SchedulerException e1) {
                 logger.error(e.getMessage(), e1);
             }
-            return new ReturnT<String>(ReturnT.FAIL_CODE, "新增任务失败:" + e.getMessage());
+            return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_add")+I18nUtil.getString("system_fail"))+":" + e.getMessage());
         }
 	}
 
 	@Override
-	public ReturnT<String> reschedule(XxlJobInfo jobInfo) {
+	public ReturnT<String> update(XxlJobInfo jobInfo) {
 
 		// valid
 		if (!CronExpression.isValidExpression(jobInfo.getJobCron())) {
-			return new ReturnT<String>(ReturnT.FAIL_CODE, "请输入格式正确的“Cron”");
+			return new ReturnT<String>(ReturnT.FAIL_CODE, I18nUtil.getString("jobinfo_field_cron_unvalid") );
 		}
 		if (StringUtils.isBlank(jobInfo.getJobDesc())) {
-			return new ReturnT<String>(ReturnT.FAIL_CODE, "请输入“任务描述”");
+			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_jobdesc")) );
 		}
 		if (StringUtils.isBlank(jobInfo.getAuthor())) {
-			return new ReturnT<String>(ReturnT.FAIL_CODE, "请输入“负责人”");
+			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_author")) );
 		}
 		if (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null) == null) {
-			return new ReturnT<String>(ReturnT.FAIL_CODE, "路由策略非法");
+			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorRouteStrategy")+I18nUtil.getString("system_unvalid")) );
 		}
 		if (ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(), null) == null) {
-			return new ReturnT<String>(ReturnT.FAIL_CODE, "阻塞处理策略非法");
+			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorBlockStrategy")+I18nUtil.getString("system_unvalid")) );
 		}
 		if (ExecutorFailStrategyEnum.match(jobInfo.getExecutorFailStrategy(), null) == null) {
-			return new ReturnT<String>(ReturnT.FAIL_CODE, "失败处理策略非法");
+			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorFailStrategy")+I18nUtil.getString("system_unvalid")));
 		}
 
 		// ChildJobId valid
@@ -174,14 +178,16 @@ public class XxlJobServiceImpl implements XxlJobService {
 				if (StringUtils.isNotBlank(childJobIdItem) && StringUtils.isNumeric(childJobIdItem)) {
 					XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer.valueOf(childJobIdItem));
 					if (childJobInfo==null) {
-						return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})无效", childJobIdItem));
+						return new ReturnT<String>(ReturnT.FAIL_CODE,
+								MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_not_found")), childJobIdItem));
 					}
 					// avoid cycle relate
 					if (childJobInfo.getId() == jobInfo.getId()) {
-						return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})不可与父任务重复", childJobIdItem));
+						return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format(I18nUtil.getString("jobinfo_field_childJobId_limit"), childJobIdItem));
 					}
 				} else {
-					return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})格式错误", childJobIdItem));
+					return new ReturnT<String>(ReturnT.FAIL_CODE,
+							MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_unvalid")), childJobIdItem));
 				}
 			}
 			jobInfo.setChildJobId(StringUtils.join(childJobIds, ","));
@@ -190,7 +196,7 @@ public class XxlJobServiceImpl implements XxlJobService {
 		// stage job info
 		XxlJobInfo exists_jobInfo = xxlJobInfoDao.loadById(jobInfo.getId());
 		if (exists_jobInfo == null) {
-			return new ReturnT<String>(ReturnT.FAIL_CODE, "参数异常");
+			return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_id")+I18nUtil.getString("system_not_found")) );
 		}
 		//String old_cron = exists_jobInfo.getJobCron();
 
@@ -271,7 +277,7 @@ public class XxlJobServiceImpl implements XxlJobService {
 	public ReturnT<String> triggerJob(int id) {
         XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(id);
         if (xxlJobInfo == null) {
-        	return new ReturnT<String>(ReturnT.FAIL_CODE, "任务ID非法");
+        	return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_id")+I18nUtil.getString("system_unvalid")) );
 		}
 
         String group = String.valueOf(xxlJobInfo.getJobGroup());
@@ -315,38 +321,42 @@ public class XxlJobServiceImpl implements XxlJobService {
 		return dashboardMap;
 	}
 
+	private static final String TRIGGER_CHART_DATA_CACHE = "trigger_chart_data_cache";
 	@Override
-	public ReturnT<Map<String, Object>> triggerChartDate(Date startDate, Date endDate) {
+	public ReturnT<Map<String, Object>> chartInfo(Date startDate, Date endDate) {
+		// get cache
+		String cacheKey = TRIGGER_CHART_DATA_CACHE + "_" + startDate.getTime() + "_" + endDate.getTime();
+		Map<String, Object> chartInfo = (Map<String, Object>) LocalCacheUtil.get(cacheKey);
+		if (chartInfo != null) {
+			return new ReturnT<Map<String, Object>>(chartInfo);
+		}
+
+		// process
 		List<String> triggerDayList = new ArrayList<String>();
+		List<Integer> triggerDayCountRunningList = new ArrayList<Integer>();
 		List<Integer> triggerDayCountSucList = new ArrayList<Integer>();
 		List<Integer> triggerDayCountFailList = new ArrayList<Integer>();
+		int triggerCountRunningTotal = 0;
 		int triggerCountSucTotal = 0;
 		int triggerCountFailTotal = 0;
 
-		List<Map<String, Object>> triggerCountMapAll = xxlJobLogDao.triggerCountByDay(startDate, endDate, -1);
-		List<Map<String, Object>> triggerCountMapSuc = xxlJobLogDao.triggerCountByDay(startDate, endDate, ReturnT.SUCCESS_CODE);
+		List<Map<String, Object>> triggerCountMapAll = xxlJobLogDao.triggerCountByDay(startDate, endDate);
 		if (CollectionUtils.isNotEmpty(triggerCountMapAll)) {
 			for (Map<String, Object> item: triggerCountMapAll) {
 				String day = String.valueOf(item.get("triggerDay"));
-				int dayAllCount = Integer.valueOf(String.valueOf(item.get("triggerCount")));
-				int daySucCount = 0;
-				int dayFailCount = dayAllCount - daySucCount;
-
-				if (CollectionUtils.isNotEmpty(triggerCountMapSuc)) {
-					for (Map<String, Object> sucItem: triggerCountMapSuc) {
-						String daySuc = String.valueOf(sucItem.get("triggerDay"));
-						if (day.equals(daySuc)) {
-							daySucCount = Integer.valueOf(String.valueOf(sucItem.get("triggerCount")));
-							dayFailCount = dayAllCount - daySucCount;
-						}
-					}
-				}
+				int triggerDayCount = Integer.valueOf(String.valueOf(item.get("triggerDayCount")));
+				int triggerDayCountRunning = Integer.valueOf(String.valueOf(item.get("triggerDayCountRunning")));
+				int triggerDayCountSuc = Integer.valueOf(String.valueOf(item.get("triggerDayCountSuc")));
+				int triggerDayCountFail = triggerDayCount - triggerDayCountRunning - triggerDayCountSuc;
 
 				triggerDayList.add(day);
-				triggerDayCountSucList.add(daySucCount);
-				triggerDayCountFailList.add(dayFailCount);
-				triggerCountSucTotal += daySucCount;
-				triggerCountFailTotal += dayFailCount;
+				triggerDayCountRunningList.add(triggerDayCountRunning);
+				triggerDayCountSucList.add(triggerDayCountSuc);
+				triggerDayCountFailList.add(triggerDayCountFail);
+
+				triggerCountRunningTotal += triggerDayCountRunning;
+				triggerCountSucTotal += triggerDayCountSuc;
+				triggerCountFailTotal += triggerDayCountFail;
 			}
 		} else {
             for (int i = 4; i > -1; i--) {
@@ -358,10 +368,17 @@ public class XxlJobServiceImpl implements XxlJobService {
 
 		Map<String, Object> result = new HashMap<String, Object>();
 		result.put("triggerDayList", triggerDayList);
+		result.put("triggerDayCountRunningList", triggerDayCountRunningList);
 		result.put("triggerDayCountSucList", triggerDayCountSucList);
 		result.put("triggerDayCountFailList", triggerDayCountFailList);
+
+		result.put("triggerCountRunningTotal", triggerCountRunningTotal);
 		result.put("triggerCountSucTotal", triggerCountSucTotal);
 		result.put("triggerCountFailTotal", triggerCountFailTotal);
+
+		// set cache
+		LocalCacheUtil.set(cacheKey, result, 60*1000);     // cache 60s
+
 		return new ReturnT<Map<String, Object>>(result);
 	}
 

+ 226 - 0
xxl-job-admin/src/main/resources/i18n/message.properties

@@ -0,0 +1,226 @@
+admin_name=任务调度中心
+admin_name_full=分布式任务调度平台XXL-JOB
+admin_version=1.9.2 (快照版)
+
+## system
+system_tips=系统提示
+system_ok=确定
+system_close=关闭
+system_save=保存
+system_cancel=取消
+system_search=搜索
+system_status=状态
+system_opt=操作
+system_please_input=请输入
+system_please_choose=请选择
+system_success=成功
+system_fail=失败
+system_add_suc=新增成功
+system_add_fail=新增失败
+system_update_suc=更新成功
+system_update_fail=更新失败
+system_all=全部
+system_api_error=接口异常
+system_show=查看
+system_empty=无
+system_opt_suc=操作成功
+system_opt_fail=操作失败
+system_opt_edit=编辑
+system_opt_del=删除
+system_unvalid=非法
+system_not_found=不存在
+system_nav=导航
+
+## daterangepicker
+daterangepicker_ranges_recent_hour=最近一小时
+daterangepicker_ranges_today=今日
+daterangepicker_ranges_yesterday=昨日
+daterangepicker_ranges_this_month=本月
+daterangepicker_ranges_last_month=上个月
+daterangepicker_ranges_recent_week=最近一周
+daterangepicker_ranges_recent_month=最近一月
+daterangepicker_custom_name=自定义
+daterangepicker_custom_starttime=起始时间
+daterangepicker_custom_endtime=结束时间
+daterangepicker_custom_daysofweek=日,一,二,三,四,五,六
+daterangepicker_custom_monthnames=一月,二月,三月,四月,五月,六月,七月,八月,九月,十月,十一月,十二月
+
+## dataTable
+dataTable_sProcessing=处理中...
+dataTable_sLengthMenu=每页 _MENU_ 条记录
+dataTable_sZeroRecords=没有匹配结果
+dataTable_sInfo=第 _PAGE_ 页 ( 总共 _PAGES_ 页,_TOTAL_ 条记录 )
+dataTable_sInfoEmpty=无记录
+dataTable_sInfoFiltered=(由 _MAX_ 项结果过滤)
+dataTable_sSearch=搜索
+dataTable_sEmptyTable=表中数据为空
+dataTable_sLoadingRecords=载入中...
+dataTable_sFirst=首页
+dataTable_sPrevious=上页
+dataTable_sNext=下页
+dataTable_sLast=末页
+dataTable_sSortAscending=: 以升序排列此列
+dataTable_sSortDescending=: 以降序排列此列
+
+## login
+login_btn=登录
+login_remember_me=记住密码
+login_username_placeholder=请输入登录账号
+login_password_placeholder=请输入登录密码
+login_username_empty=请输入登录账号
+login_username_lt_5=登录账号不应低于5位
+login_password_empty=请输入登录密码
+login_password_lt_5=登录密码不应低于5位
+login_success=登录成功
+login_fail=登录失败
+login_param_empty=账号或密码为空
+login_param_unvalid=账号或密码错误
+
+## logout
+logout_btn=注销
+logout_confirm=确认注销登录?
+logout_success=注销成功
+logout_fail=注销失败
+
+## dashboard
+job_dashboard_name=运行报表
+job_dashboard_job_num=任务数量
+job_dashboard_job_num_tip=调度中心运行的任务数量
+job_dashboard_trigger_num=调度次数
+job_dashboard_trigger_num_tip=调度中心触发的调度次数
+job_dashboard_jobgroup_num=执行器数量
+job_dashboard_jobgroup_num_tip=调度中心在线的执行器机器数量
+job_dashboard_report=调度报表
+job_dashboard_report_loaddata_fail=调度报表数据加载异常
+job_dashboard_date_report=日期分布图
+job_dashboard_rate_report=成功比例图
+
+## job info
+jobinfo_name=任务管理
+jobinfo_job=任务
+jobinfo_field_add=新增任务
+jobinfo_field_update=更新任务
+jobinfo_field_id=任务ID
+jobinfo_field_jobgroup=执行器
+jobinfo_field_jobdesc=任务描述
+jobinfo_field_gluetype=运行模式
+jobinfo_field_executorparam=任务参数
+jobinfo_field_cron_unvalid=Cron格式非法
+jobinfo_field_author=负责人
+jobinfo_field_alarmemail=报警邮件
+jobinfo_field_alarmemail_placeholder=请输入报警邮件,多个邮件地址则逗号分隔
+jobinfo_field_executorRouteStrategy=路由策略
+jobinfo_field_childJobId=子任务ID
+jobinfo_field_childJobId_limit=子任务ID({0})不可与父任务重复
+jobinfo_field_childJobId_placeholder=请输入子任务的任务ID,如存在多个则逗号分隔
+jobinfo_field_executorBlockStrategy=阻塞处理策略
+jobinfo_field_executorFailStrategy=失败处理策略
+jobinfo_script_location=脚本位置
+jobinfo_shard_index=分片序号
+jobinfo_shard_total=分片总数
+jobinfo_opt_pause=暂停
+jobinfo_opt_resume=恢复
+jobinfo_opt_log=日志
+jobinfo_opt_run=执行
+jobinfo_glue_remark=源码备注
+jobinfo_glue_remark_limit=源码备注长度限制为4~100
+jobinfo_glue_rollback=版本回溯
+jobinfo_glue_jobid_unvalid=任务ID非法
+jobinfo_glue_gluetype_unvalid=该任务非GLUE模式
+
+## job log
+joblog_name=调度日志
+joblog_status=状态
+joblog_status_all=全部
+joblog_status_suc=成功
+joblog_status_fail=失败
+joblog_status_running=进行中
+joblog_field_triggerTime=调度时间
+joblog_field_triggerCode=调度结果
+joblog_field_triggerMsg=调度备注
+joblog_field_handleTime=执行时间
+joblog_field_handleCode=执行结果
+joblog_field_handleMsg=执行备注
+joblog_field_executorAddress=执行器地址
+joblog_clean=清理
+joblog_clean_log=日志清理
+joblog_clean_type=清理方式
+joblog_clean_type_1=清理一个月之前日志数据
+joblog_clean_type_2=清理三个月之前日志数据
+joblog_clean_type_3=清理六个月之前日志数据
+joblog_clean_type_4=清理一年之前日志数据
+joblog_clean_type_5=清理一千条以前日志数据
+joblog_clean_type_6=清理一万条以前日志数据
+joblog_clean_type_7=清理三万条以前日志数据
+joblog_clean_type_8=清理十万条以前日志数据
+joblog_clean_type_9=清理所有日志数据
+joblog_clean_type_unvalid=清理类型参数异常
+joblog_handleCode_200=成功
+joblog_handleCode_500=失败
+joblog_handleCode_501=失败重试
+joblog_kill_log=终止任务
+joblog_kill_log_limit=调度失败,无法终止日志
+joblog_kill_log_byman=人为操作主动终止
+joblog_rolling_log=执行日志
+joblog_rolling_log_refresh=刷新
+joblog_rolling_log_triggerfail=任务发起调度失败,无法查看执行日志
+joblog_rolling_log_failoften=终止请求Rolling日志,请求失败次数超上限,可刷新页面重新加载日志
+joblog_logid_unvalid=日志ID非法
+
+## job group
+jobgroup_name=执行器管理
+jobgroup_list=执行器列表
+jobgroup_add=新增执行器
+jobgroup_edit=编辑执行器
+jobgroup_del=删除执行器
+jobgroup_field_order=排序
+jobgroup_field_title=名称
+jobgroup_field_addressType=注册方式
+jobgroup_field_addressType_0=自动注册
+jobgroup_field_addressType_1=手动录入
+jobgroup_field_addressType_limit=手动录入注册方式,机器地址不可为空
+jobgroup_field_registryList=机器地址
+jobgroup_field_registryList_unvalid=机器地址格式非法
+jobgroup_field_registryList_placeholder=请输入执行器地址列表,多地址逗号分隔
+jobgroup_field_appName_limit=限制以小写字母开头,由小写字母、数字和中划线组成
+jobgroup_field_appName_length=AppName长度限制为4~64
+jobgroup_field_title_length=名称长度限制为4~12
+jobgroup_field_order_digits=请输入整数
+jobgroup_field_orderrange=取值范围为1~1000
+jobgroup_del_limit_0=拒绝删除,该执行器使用中
+jobgroup_del_limit_1=拒绝删除, 系统至少保留一个执行器
+
+## job conf
+jobconf_fail_alarm=失败告警
+jobconf_fail_retry=失败重试
+jobconf_route_first=第一个
+jobconf_route_last=最后一个
+jobconf_route_round=轮询
+jobconf_route_random=随机
+jobconf_route_consistenthash=一致性HASH
+jobconf_route_lfu=最不经常使用
+jobconf_route_lru=最近最久未使用
+jobconf_route_failover=故障转移
+jobconf_route_busyover=忙碌转移
+jobconf_route_shard=分片广播
+jobconf_idleBeat=空闲检测
+jobconf_beat=心跳检测
+jobconf_monitor=调度中心监控报警
+jobconf_monitor_detail=监控告警明细
+jobconf_monitor_alarm_title=告警类型
+jobconf_monitor_alarm_type=调度失败
+jobconf_trigger_admin_adress=调度机器
+jobconf_trigger_exe_regtype=执行器-注册方式
+jobconf_trigger_exe_regaddress=执行器-地址列表
+jobconf_trigger_address_empty=调度失败:执行器地址为空
+jobconf_trigger_run=触发调度
+jobconf_trigger_child_run=触发子任务
+jobconf_trigger_fail_retry=调度失败重试
+jobconf_exe_fail_retry=执行失败重试
+jobconf_callback_child_msg1={0}/{1} [任务ID={2}], 触发{3}, 触发备注: {4} <br>
+jobconf_callback_child_msg2={0}/{1} [任务ID={2}], 触发失败, 触发备注: 任务ID格式错误 <br>
+jobconf_callback_msg1=触发{0}, 触发备注: {1} <br>
+
+## help
+job_help=使用教程
+job_help_document=官方文档

+ 226 - 0
xxl-job-admin/src/main/resources/i18n/message_en.properties

@@ -0,0 +1,226 @@
+admin_name=Scheduling Center
+admin_name_full=Distributed Task Scheduling Platform XXL-JOB
+admin_version=1.9.2 (SNAPSHOT)
+
+## system
+system_tips=System message
+system_ok=Confirm 
+system_close=Close
+system_save=Save 
+system_cancel=Cancel
+system_search=Search
+system_status=Status
+system_opt=Operate
+system_please_input=please input 
+system_please_choose=please choose 
+system_success=success
+system_fail=fail
+system_add_suc=add success
+system_add_fail=add fail
+system_update_suc=update success
+system_update_fail=update fail
+system_all=All
+system_api_error=net error
+system_show=Show
+system_empty=Empty
+system_opt_suc=operate success
+system_opt_fail=operate fail
+system_opt_edit=Edit 
+system_opt_del=Delete 
+system_unvalid=illegal
+system_not_found=not exist
+system_nav=Navigation
+
+## daterangepicker
+daterangepicker_ranges_recent_hour=recent one hour
+daterangepicker_ranges_today=today
+daterangepicker_ranges_yesterday=yesterday
+daterangepicker_ranges_this_month=this month
+daterangepicker_ranges_last_month=last month
+daterangepicker_ranges_recent_week=recent one week
+daterangepicker_ranges_recent_month=recent one month
+daterangepicker_custom_name=custom
+daterangepicker_custom_starttime=start time
+daterangepicker_custom_endtime=end time
+daterangepicker_custom_daysofweek=Sun,Mon,Tue,Wed,Thu,Fri,Sat
+daterangepicker_custom_monthnames=Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec
+
+## dataTable
+dataTable_sProcessing=processing...
+dataTable_sLengthMenu= _MENU_ records per page
+dataTable_sZeroRecords=No matching results
+dataTable_sInfo=page _PAGE_  ( Total _PAGES_ pages,_TOTAL_ records )
+dataTable_sInfoEmpty=No Record
+dataTable_sInfoFiltered=(Filtered by _MAX_ results)
+dataTable_sSearch=Search
+dataTable_sEmptyTable=Table data is empty
+dataTable_sLoadingRecords=Loading...
+dataTable_sFirst=FIRST PAGE
+dataTable_sPrevious=Previous Page
+dataTable_sNext=Next Page
+dataTable_sLast=LAST PAGE
+dataTable_sSortAscending=: Rank this column in ascending order
+dataTable_sSortDescending=: Rank this column in descending order
+
+## login
+login_btn=Login
+login_remember_me=Remember Me
+login_username_placeholder=Please enter username
+login_password_placeholder=Please enter password
+login_username_empty=Please enter username
+login_username_lt_5=Username length should not be less than 5
+login_password_empty=Please enter password
+login_password_lt_5=Password length should not be less than 5
+login_success=Login success
+login_fail=Login fail
+login_param_empty=Username or password is empty
+login_param_unvalid=Username or password error
+
+## logout
+logout_btn=Logout
+logout_confirm=Confirm logout?
+logout_success=Logout success
+logout_fail=Logout fail
+
+## dashboard
+job_dashboard_name=Run report
+job_dashboard_job_num=Job number
+job_dashboard_job_num_tip=The number of tasks running in the scheduling center
+job_dashboard_trigger_num=trigger number
+job_dashboard_trigger_num_tip=The number of trigger record scheduled by the scheduling center
+job_dashboard_jobgroup_num=Executor number
+job_dashboard_jobgroup_num_tip=The number of online executor machines perceived by the scheduling center
+job_dashboard_report=Scheduling report
+job_dashboard_report_loaddata_fail=Scheduling report load data error
+job_dashboard_date_report=Date distribution
+job_dashboard_rate_report=Percentage distribution
+
+## job info
+jobinfo_name=Job Manage
+jobinfo_job=Job
+jobinfo_field_add=Add Job
+jobinfo_field_update=Edit Job
+jobinfo_field_id=Job ID
+jobinfo_field_jobgroup=Executor
+jobinfo_field_jobdesc=Job description
+jobinfo_field_gluetype=GLUE Type
+jobinfo_field_executorparam=Param
+jobinfo_field_cron_unvalid=The Cron is illegal
+jobinfo_field_author=Author
+jobinfo_field_alarmemail=Alarm email
+jobinfo_field_alarmemail_placeholder=Please enter alarm mail, if there are more than one comma separated
+jobinfo_field_executorRouteStrategy=Route Strategy
+jobinfo_field_childJobId=Child Job ID
+jobinfo_field_childJobId_limit=Child job ID({0}) cannot be duplicated with the parent job.
+jobinfo_field_childJobId_placeholder=Please enter the Child job ID, if there are more than one comma separated
+jobinfo_field_executorBlockStrategy=Block Strategy
+jobinfo_field_executorFailStrategy=Fail Strategy
+jobinfo_script_location=Script location
+jobinfo_shard_index=Shard index
+jobinfo_shard_total=Shard total
+jobinfo_opt_pause=Pause
+jobinfo_opt_resume=Resume
+jobinfo_opt_log=Log
+jobinfo_opt_run=Run
+jobinfo_glue_remark=Resource Remark
+jobinfo_glue_remark_limit=Resource Remark length is limited to 4~100
+jobinfo_glue_rollback=Version Backtrack
+jobinfo_glue_jobid_unvalid=Job ID is illegal
+jobinfo_glue_gluetype_unvalid=The job is not GLUE Type
+
+## job log
+joblog_name=Trigger Log
+joblog_status=Status
+joblog_status_all=All
+joblog_status_suc=Success
+joblog_status_fail=Fail
+joblog_status_running=Running
+joblog_field_triggerTime=Trigger Time
+joblog_field_triggerCode=Trigger Result
+joblog_field_triggerMsg=Trigger Msg
+joblog_field_handleTime=Handle Time
+joblog_field_handleCode=Handle Result
+joblog_field_handleMsg=Trigger Msg
+joblog_field_executorAddress=Executor Address
+joblog_clean=Clean
+joblog_clean_log=Clean Log
+joblog_clean_type=Clean Type
+joblog_clean_type_1=Clean up log data a month ago
+joblog_clean_type_2=Clean up log data three month ago
+joblog_clean_type_3=Clean up log data six month ago
+joblog_clean_type_4=Clean up log data a year ago
+joblog_clean_type_5=Clean up log data a thousand record ago
+joblog_clean_type_6=Clean up log data ten thousand record ago
+joblog_clean_type_7=Clean up log data thirty thousand record ago
+joblog_clean_type_8=Clean up log data hundred thousand record ago
+joblog_clean_type_9=Clean up all log data
+joblog_clean_type_unvalid=Clean type is illegal
+joblog_handleCode_200=Success
+joblog_handleCode_500=Fail
+joblog_handleCode_501=Fail retry
+joblog_kill_log=Kill Job
+joblog_kill_log_limit=Trigger Fail, can not kill job
+joblog_kill_log_byman=Manual operation to active kill job
+joblog_rolling_log=Rolling log
+joblog_rolling_log_refresh=Refresh 
+joblog_rolling_log_triggerfail=The job trigger fail, can not view the rolling log
+joblog_rolling_log_failoften=The request for the Rolling log is terminated, the number of failed requests exceeds the limit, Reload the log on the refresh page
+joblog_logid_unvalid=Log ID is illegal
+
+## job group
+jobgroup_name=Executor Manage
+jobgroup_list=Executor List
+jobgroup_add=Add Executor
+jobgroup_edit=Edit Executor
+jobgroup_del=Delete Executor
+jobgroup_field_order=Order
+jobgroup_field_title=Title
+jobgroup_field_addressType=Registry Type
+jobgroup_field_addressType_0=Automatic registration
+jobgroup_field_addressType_1=Manual registration
+jobgroup_field_addressType_limit=Manually registration type, the machine address must not be empty
+jobgroup_field_registryList=machine address
+jobgroup_field_registryList_unvalid=registry machine address is illegal
+jobgroup_field_registryList_placeholder=Please enter the machine address, if there are more than one comma separated
+jobgroup_field_appName_limit=Limit the beginning of a lowercase letter, consists of lowercase letters、number and underscores.
+jobgroup_field_appName_length=AppName length is limited to 4~64
+jobgroup_field_title_length=Title length is limited to 4~12
+jobgroup_field_order_digits=Please enter a positive integer
+jobgroup_field_orderrange=Order is limited to 1~1000
+jobgroup_del_limit_0=Refuse to delete, the executor is being used
+jobgroup_del_limit_1=Refuses to delete, the system retains at least one executor
+
+## job conf
+jobconf_fail_alarm=Fail Alarm
+jobconf_fail_retry=Fail Retry
+jobconf_route_first=First
+jobconf_route_last=Last
+jobconf_route_round=Round
+jobconf_route_random=Random
+jobconf_route_consistenthash=Consistent Hash
+jobconf_route_lfu=Least Frequently Used
+jobconf_route_lru=Least Recently Used
+jobconf_route_failover=Failover
+jobconf_route_busyover=Busyover
+jobconf_route_shard=Sharding Broadcast
+jobconf_idleBeat=Idle check
+jobconf_beat=Heartbeats
+jobconf_monitor=Scheduling Center monitor alarm
+jobconf_monitor_detail=monitor alarm details
+jobconf_monitor_alarm_title=Alarm Type
+jobconf_monitor_alarm_type=Trigger Fail
+jobconf_trigger_admin_adress=Trigger machine address
+jobconf_trigger_exe_regtype=Execotor-Registry Type
+jobconf_trigger_exe_regaddress=Execotor-Registry Address
+jobconf_trigger_address_empty=Trigger Fail:registry address is empty
+jobconf_trigger_run=Trigger Job
+jobconf_trigger_child_run=Trigger child job
+jobconf_trigger_fail_retry=Trigger fail retry
+jobconf_exe_fail_retry=Handle fail retry
+jobconf_callback_child_msg1={0}/{1} [Job ID={2}], Trigger {3}, Trigger msg: {4} <br>
+jobconf_callback_child_msg2={0}/{1} [Job ID={2}], Trigger Fail, Trigger msg: Job ID is illegal <br>
+jobconf_callback_msg1=Trigger {0}, Trigger msg: {1} <br>
+
+## help
+job_help=Tutorial
+job_help_document=Official Document

+ 6 - 0
xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobInfoMapper.xml

@@ -58,6 +58,9 @@
 			<if test="jobGroup gt 0">
 				AND t.job_group = #{jobGroup}
 			</if>
+			<if test="jobDesc != null and jobDesc != ''">
+				AND t.job_desc like CONCAT(CONCAT('%', #{jobDesc}), '%')
+			</if>
 			<if test="executorHandler != null and executorHandler != ''">
 				AND t.executor_handler like CONCAT(CONCAT('%', #{executorHandler}), '%')
 			</if>
@@ -73,6 +76,9 @@
 			<if test="jobGroup gt 0">
 				AND t.job_group = #{jobGroup}
 			</if>
+			<if test="jobDesc != null and jobDesc != ''">
+				AND t.job_desc like CONCAT(CONCAT('%', #{jobDesc}), '%')
+			</if>
 			<if test="executorHandler != null and executorHandler != ''">
 				AND t.executor_handler like CONCAT(CONCAT('%', #{executorHandler}), '%')
 			</if>

+ 8 - 7
xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobLogMapper.xml

@@ -163,13 +163,14 @@
 	</select>
 
     <select id="triggerCountByDay" resultType="java.util.Map" >
-        SELECT DATE_FORMAT(trigger_time,'%Y-%m-%d') triggerDay, COUNT(id) triggerCount
-        FROM XXL_JOB_QRTZ_TRIGGER_LOG
-        WHERE trigger_time BETWEEN #{from} and #{to}
-		<if test="handleCode gt 0">
-			AND handle_code = #{handleCode}
-		</if>
-        GROUP BY triggerDay;
+		SELECT
+			DATE_FORMAT(trigger_time,'%Y-%m-%d') triggerDay,
+			COUNT(handle_code) triggerDayCount,
+			SUM(CASE WHEN handle_code = 0 then 1 else 0 end) as triggerDayCountRunning,
+			SUM(CASE WHEN handle_code = 200 then 1 else 0 end) as triggerDayCountSuc
+		FROM XXL_JOB_QRTZ_TRIGGER_LOG
+		WHERE trigger_time BETWEEN #{from} and #{to}
+		GROUP BY triggerDay;
     </select>
 
 	<delete id="clearLog" >

+ 1 - 1
xxl-job-admin/src/main/resources/quartz.properties

@@ -20,7 +20,7 @@ org.quartz.jobStore.maxMisfiresToHandleAtATime: 1
 #org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
 
 # for cluster
-org.quartz.jobStore.tablePrefix = XXL_JOB_QRTZ_
+org.quartz.jobStore.tablePrefix: XXL_JOB_QRTZ_
 org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX
 org.quartz.jobStore.isClustered: true
 org.quartz.jobStore.clusterCheckinInterval: 5000

+ 1 - 1
xxl-job-admin/src/main/resources/spring/applicationcontext-base.xml

@@ -7,7 +7,7 @@
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
 
-	<context:component-scan base-package="com.xxl.job.admin.service, com.xxl.job.admin.dao" />
+	<context:component-scan base-package="com.xxl.job.admin.core.conf, com.xxl.job.admin.service, com.xxl.job.admin.dao" />
 
 	<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
 		<property name="templateLoaderPath" value="/WEB-INF/template/" />

+ 4 - 1
xxl-job-admin/src/main/resources/xxl-job-admin.properties

@@ -16,4 +16,7 @@ xxl.job.login.username=admin
 xxl.job.login.password=123456
 
 ### xxl-job, access token
-xxl.job.accessToken=
+xxl.job.accessToken=
+
+### xxl-job, i18n (default empty as chinese, "en" as english)
+xxl.job.i18n=

+ 4 - 3
xxl-job-admin/src/main/webapp/500.html

@@ -2,7 +2,7 @@
 <html>
 <head>
 	<meta charset="UTF-8">
-	<title>应用程序异常 (500)</title> 
+	<title>Error</title>
     <style type="text/css"> 
         body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
         div.dialog {
@@ -21,8 +21,9 @@
 <body> 
 
 	<div class="dialog"> 
-	    <h1>应用程序异常</h1> 
-	    <p>抱歉!您访问的页面出现异常,请稍后重试或联系管理员。</p> 
+	    <h1>System Error</h1>
+	    <p>Oops! Page not found.</p>
+		<a href="javascript:window.location.href='${request.contextPath}/'">Back</a>
 	</div>
 
 </body>

+ 3 - 3
xxl-job-admin/src/main/webapp/WEB-INF/template/common/common.exception.ftl

@@ -2,7 +2,7 @@
 <html>
 <head>
 	<meta charset="UTF-8">
-	<title>应用程序异常 (error)</title> 
+	<title>Error</title>
     <style type="text/css"> 
         body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
         div.dialog {
@@ -21,9 +21,9 @@
 <body> 
 
 	<div class="dialog"> 
-	    <h1>应用程序异常</h1> 
+	    <h1>System Error</h1>
 	    <p>${exceptionMsg}</p>
-		<a href="javascript:window.location.href='${request.contextPath}/'">返 回</a>
+		<a href="javascript:window.location.href='${request.contextPath}/'">Back</a>
 	    </p> 
 	</div>
 

+ 16 - 9
xxl-job-admin/src/main/webapp/WEB-INF/template/common/common.macro.ftl

@@ -29,6 +29,10 @@
 
 	<!-- pace -->
 	<link rel="stylesheet" href="${request.contextPath}/static/plugins/pace/themes/pace-theme-flash.css">
+
+	<#-- i18n -->
+	<#global I18n = I18nUtil.getMultString()?eval />
+
 </#macro>
 
 <#macro commonScript>
@@ -53,7 +57,10 @@
 
 	<#-- common -->
     <script src="${request.contextPath}/static/js/common.1.js"></script>
-    <script>var base_url = '${request.contextPath}';</script>
+    <script>
+		var base_url = '${request.contextPath}';
+        var I18n = ${I18nUtil.getMultString()};
+	</script>
 
 </#macro>
 
@@ -61,7 +68,7 @@
 	<header class="main-header">
 		<a href="${request.contextPath}/" class="logo">
 			<span class="logo-mini"><b>XXL</b></span>
-			<span class="logo-lg"><b>任务调度中心</b></span>
+			<span class="logo-lg"><b>${I18n.admin_name}</b></span>
 		</a>
 		<nav class="navbar navbar-static-top" role="navigation">
 			<a href="#" class="sidebar-toggle" data-toggle="offcanvas" role="button"><span class="sr-only">切换导航</span></a>
@@ -69,7 +76,7 @@
 				<ul class="nav navbar-nav">
 					<li class="dropdown user user-menu">
 	                    <a href=";" id="logoutBtn" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
-                      		<span class="hidden-xs">注销</span>
+                      		<span class="hidden-xs">${I18n.logout_btn}</span>
 	                    </a>
 					</li>
 				</ul>
@@ -85,11 +92,11 @@
 		<section class="sidebar">
 			<!-- sidebar menu: : style can be found in sidebar.less -->
 			<ul class="sidebar-menu">
-				<li class="header">常用模块</li>
-				<li class="nav-click <#if pageName == "jobinfo">active</#if>" ><a href="${request.contextPath}/jobinfo"><i class="fa fa-circle-o text-aqua"></i><span>任务管理</span></a></li>
-				<li class="nav-click <#if pageName == "joblog">active</#if>" ><a href="${request.contextPath}/joblog"><i class="fa fa-circle-o text-yellow"></i><span>调度日志</span></a></li>
-                <li class="nav-click <#if pageName == "jobgroup">active</#if>" ><a href="${request.contextPath}/jobgroup"><i class="fa fa-circle-o text-green"></i><span>执行器管理</span></a></li>
-				<li class="nav-click <#if pageName == "help">active</#if>" ><a href="${request.contextPath}/help"><i class="fa fa-circle-o text-gray"></i><span>使用教程</span></a></li>
+                <li class="header">${I18n.system_nav}</li>
+				<li class="nav-click <#if pageName == "jobinfo">active</#if>" ><a href="${request.contextPath}/jobinfo"><i class="fa fa-circle-o text-aqua"></i><span>${I18n.jobinfo_name}</span></a></li>
+				<li class="nav-click <#if pageName == "joblog">active</#if>" ><a href="${request.contextPath}/joblog"><i class="fa fa-circle-o text-yellow"></i><span>${I18n.joblog_name}</span></a></li>
+                <li class="nav-click <#if pageName == "jobgroup">active</#if>" ><a href="${request.contextPath}/jobgroup"><i class="fa fa-circle-o text-green"></i><span>${I18n.jobgroup_name}</span></a></li>
+				<li class="nav-click <#if pageName == "help">active</#if>" ><a href="${request.contextPath}/help"><i class="fa fa-circle-o text-gray"></i><span>${I18n.job_help}</span></a></li>
 			</ul>
 		</section>
 		<!-- /.sidebar -->
@@ -175,7 +182,7 @@
 
 <#macro commonFooter >
 	<footer class="main-footer">
-        Powered by <b>XXL-JOB</b> 1.9.0(快照版本)
+        Powered by <b>XXL-JOB</b> ${I18n.admin_version}
 		<div class="pull-right hidden-xs">
             <strong>Copyright &copy; 2015-${.now?string('yyyy')} &nbsp;
                 <a href="http://www.xuxueli.com/" target="_blank" >xuxueli</a>

+ 0 - 1
xxl-job-admin/src/main/webapp/WEB-INF/template/common/common.result.ftl

@@ -1 +0,0 @@
-${result?if_exists}

+ 5 - 11
xxl-job-admin/src/main/webapp/WEB-INF/template/help.ftl

@@ -1,9 +1,9 @@
 <!DOCTYPE html>
 <html>
 <head>
-  	<title>任务调度中心</title>
   	<#import "/common/common.macro.ftl" as netCommon>
 	<@netCommon.commonStyle />
+	<title>${I18n.admin_name}</title>
 </head>
 <body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if> ">
 <div class="wrapper">
@@ -16,25 +16,19 @@
 	<div class="content-wrapper">
 		<!-- Content Header (Page header) -->
 		<section class="content-header">
-			<h1>使用教程<small>任务调度中心</small></h1>
-			<!--
-			<ol class="breadcrumb">
-				<li><a><i class="fa fa-dashboard"></i>调度中心</a></li>
-				<li class="active">使用教程</li>
-			</ol>
-			-->
+			<h1>${I18n.job_help}</h1>
 		</section>
 
 		<!-- Main content -->
 		<section class="content">
 			<div class="callout callout-info">
-				<h4>分布式任务调度平台XXL-JOB</h4>
+				<h4>${I18n.admin_name_full}</h4>
 				<br>
 				<p>
-					<a target="_blank" href="https://github.com/xuxueli/xxl-job">github</a>&nbsp;&nbsp;&nbsp;&nbsp;
+					<a target="_blank" href="https://github.com/xuxueli/xxl-job">Github</a>&nbsp;&nbsp;&nbsp;&nbsp;
 					<iframe src="https://ghbtns.com/github-btn.html?user=xuxueli&repo=xxl-job&type=star&count=true" frameborder="0" scrolling="0" width="170px" height="20px" style="margin-bottom:-5px;"></iframe> 
 					<br><br>
-                    <a target="_blank" href="http://www.xuxueli.com/xxl-job/">文档地址</a>
+                    <a target="_blank" href="http://www.xuxueli.com/xxl-job/">${I18n.job_help_document}</a>
                     <br><br>
 
 				</p>

+ 10 - 9
xxl-job-admin/src/main/webapp/WEB-INF/template/index.ftl

@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html>
 <head>
-  	<title>任务调度中心</title>
   	<#import "/common/common.macro.ftl" as netCommon>
 	<@netCommon.commonStyle />
     <!-- daterangepicker -->
     <link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/daterangepicker/daterangepicker.css">
+    <title>${I18n.admin_name}</title>
 </head>
 <body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if> ">
 <div class="wrapper">
@@ -18,8 +18,9 @@
 	<div class="content-wrapper">
 		<!-- Content Header (Page header) -->
 		<section class="content-header">
-			<h1>运行报表<small>任务调度中心</small></h1>
+			<h1>${I18n.job_dashboard_name}</h1>
 			<!--
+			<h1>运行报表<small>任务调度中心</small></h1>
 			<ol class="breadcrumb">
 				<li><a><i class="fa fa-dashboard"></i>调度中心</a></li>
 				<li class="active">使用教程</li>
@@ -39,13 +40,13 @@
                         <span class="info-box-icon"><i class="fa fa-flag-o"></i></span>
 
                         <div class="info-box-content">
-                            <span class="info-box-text">任务数量</span>
+                            <span class="info-box-text">${I18n.job_dashboard_job_num}</span>
                             <span class="info-box-number">${jobInfoCount}</span>
 
                             <div class="progress">
                                 <div class="progress-bar" style="width: 100%"></div>
                             </div>
-                            <span class="progress-description">调度中心运行的任务数量</span>
+                            <span class="progress-description">${I18n.job_dashboard_job_num_tip}</span>
                         </div>
                     </div>
                 </div>
@@ -56,14 +57,14 @@
                         <span class="info-box-icon"><i class="fa fa-calendar"></i></span>
 
                         <div class="info-box-content">
-                            <span class="info-box-text">调度次数</span>
+                            <span class="info-box-text">${I18n.job_dashboard_trigger_num}</span>
                             <span class="info-box-number">${jobLogCount}</span>
 
                             <div class="progress">
                                 <div class="progress-bar" style="width: 100%" ></div>
                             </div>
                             <span class="progress-description">
-                                调度中心触发的调度次数
+                                ${I18n.job_dashboard_trigger_num_tip}
                                 <#--<#if jobLogCount gt 0>
                                     调度成功率:${(jobLogSuccessCount*100/jobLogCount)?string("0.00")}<small>%</small>
                                 </#if>-->
@@ -78,13 +79,13 @@
                         <span class="info-box-icon"><i class="fa ion-ios-settings-strong"></i></span>
 
                         <div class="info-box-content">
-                            <span class="info-box-text">执行器数量</span>
+                            <span class="info-box-text">${I18n.job_dashboard_jobgroup_num}</span>
                             <span class="info-box-number">${executorCount}</span>
 
                             <div class="progress">
                                 <div class="progress-bar" style="width: 100%"></div>
                             </div>
-                            <span class="progress-description">调度中心在线的执行器机器数量</span>
+                            <span class="progress-description">${I18n.job_dashboard_jobgroup_num_tip}</span>
                         </div>
                     </div>
                 </div>
@@ -96,7 +97,7 @@
                 <div class="col-md-12">
                     <div class="box">
                         <div class="box-header with-border">
-                            <h3 class="box-title">调度报表</h3>
+                            <h3 class="box-title">${I18n.job_dashboard_report}</h3>
                             <#--<input type="text" class="form-control" id="filterTime" readonly >-->
 
                             <!-- tools box -->

+ 13 - 9
xxl-job-admin/src/main/webapp/WEB-INF/template/jobcode/jobcode.index.ftl

@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html>
 <head>
-  	<title>任务调度中心</title>
   	<#import "/common/common.macro.ftl" as netCommon>
 	<@netCommon.commonStyle />
 	<link rel="stylesheet" href="${request.contextPath}/static/plugins/codemirror/lib/codemirror.css">
 	<link rel="stylesheet" href="${request.contextPath}/static/plugins/codemirror/addon/hint/show-hint.css">
+    <title>${I18n.admin_name}</title>
 	<style type="text/css">
 		.CodeMirror {
       		font-size:16px;
@@ -35,7 +35,11 @@
                     <#-- left nav -->
                     <div class="collapse navbar-collapse pull-left" id="navbar-collapse">
                         <ul class="nav navbar-nav">
-                            <li class="active" ><a href="javascript:;"><#list GlueTypeEnum as item><#if item == jobInfo.glueType>${item.desc}</#if></#list> 任务:${jobInfo.jobDesc}<span class="sr-only">(current)</span></a></li>
+                            <li class="active" ><a href="javascript:;">
+                                <span class="sr-only">(current)</span>
+                                【<#list GlueTypeEnum as item><#if item == jobInfo.glueType>${item.desc}</#if></#list>】
+                                ${jobInfo.jobDesc}
+                            </a></li>
                         </ul>
                     </div>
 
@@ -43,7 +47,7 @@
                     <div class="navbar-custom-menu">
                         <ul class="nav navbar-nav">
                             <li class="dropdown">
-                                <a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">版本回溯 <span class="caret"></span></a>
+                                <a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">${I18n.jobinfo_glue_rollback} <span class="caret"></span></a>
                                 <ul class="dropdown-menu" role="menu">
                                     <li <#if jobLogGlues?exists && jobLogGlues?size gt 0 >style="display: none;"</#if> >
                                         <a href="javascript:;" class="source_version" version="version_now" glueType="${jobInfo.glueType}" >
@@ -66,7 +70,7 @@
                             <li id="save" >
 								<a href="javascript:;" >
 									<i class="fa fa-fw fa-save" ></i>
-                                    保存
+                                    ${I18n.system_save}
 								</a>
 							</li>
                         </ul>
@@ -87,19 +91,19 @@
         <div class="modal-dialog ">
             <div class="modal-content">
                 <div class="modal-header">
-                    <h4 class="modal-title" ><i class="fa fa-fw fa-save"></i>保存</h4>
+                    <h4 class="modal-title" ><i class="fa fa-fw fa-save"></i>${I18n.system_save}</h4>
                 </div>
                 <div class="modal-body">
                     <form class="form-horizontal form" role="form" >
                         <div class="form-group">
-                            <label for="lastname" class="col-sm-2 control-label">源码备注<font color="red">*</font></label>
-                            <div class="col-sm-10"><input type="text" class="form-control" id="glueRemark" placeholder="请输入备注信息" maxlength="64" ></div>
+                            <label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_glue_remark}<font color="red">*</font></label>
+                            <div class="col-sm-10"><input type="text" class="form-control" id="glueRemark" placeholder="${I18n.system_please_input}${I18n.jobinfo_glue_remark}" maxlength="64" ></div>
                         </div>
                         <hr>
                         <div class="form-group">
                             <div class="col-sm-offset-3 col-sm-6">
-                                <button type="button" class="btn btn-primary ok" >保存</button>
-                                <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
+                                <button type="button" class="btn btn-primary ok" >${I18n.system_save}</button>
+                                <button type="button" class="btn btn-default" data-dismiss="modal">${I18n.system_cancel}</button>
                             </div>
                         </div>
                     </form>

+ 38 - 38
xxl-job-admin/src/main/webapp/WEB-INF/template/jobgroup/jobgroup.index.ftl

@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html>
 <head>
-  	<title>任务调度中心</title>
   	<#import "/common/common.macro.ftl" as netCommon>
 	<@netCommon.commonStyle />
 	<!-- DataTables -->
   	<link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/datatables/dataTables.bootstrap.css">
+    <title>${I18n.admin_name}</title>
 </head>
 <body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if> ">
 <div class="wrapper">
@@ -18,7 +18,7 @@
 	<div class="content-wrapper">
 		<!-- Content Header (Page header) -->
 		<section class="content-header">
-			<h1>执行器管理<small>任务调度中心</small></h1>
+			<h1>${I18n.jobgroup_name}</h1>
 		</section>
 
 		<!-- Main content -->
@@ -28,20 +28,20 @@
 				<div class="col-xs-12">
 					<div class="box">
 			            <div class="box-header">
-							<h3 class="box-title">执行器列表</h3>&nbsp;&nbsp;
-                            <button class="btn btn-info btn-xs pull-left2 add" >+新增执行器</button>
+							<h3 class="box-title">${I18n.jobgroup_list}</h3>&nbsp;&nbsp;
+                            <button class="btn btn-info btn-xs pull-left2 add" >${I18n.jobgroup_add}</button>
 						</div>
 			            <div class="box-body">
 			              	<table id="joblog_list" class="table table-bordered table-striped display" width="100%" >
 				                <thead>
 					            	<tr>
                                         <#--<th name="id" >ID</th>-->
-                                        <th name="order" >排序</th>
+                                        <th name="order" >${I18n.jobgroup_field_order}</th>
                                         <th name="appName" >AppName</th>
-                                        <th name="title" >名称</th>
-                                        <th name="addressType" >注册方式</th>
-                                        <th name="registryList" >OnLine 机器</th>
-                                        <th name="operate" >操作</th>
+                                        <th name="title" >${I18n.jobgroup_field_title}</th>
+                                        <th name="addressType" >${I18n.jobgroup_field_addressType}</th>
+                                        <th name="registryList" >OnLine ${I18n.jobgroup_field_registryList}</th>
+                                        <th name="operate" >${I18n.system_opt}</th>
 					                </tr>
 				                </thead>
                                 <tbody>
@@ -52,7 +52,7 @@
                                         <td>${group.order}</td>
                                         <td>${group.appName}</td>
                                         <td>${group.title}</td>
-                                        <td><#if group.addressType==0>自动注册<#else>手动录入</#if></td>
+                                        <td><#if group.addressType==0>${I18n.jobgroup_field_addressType_0}<#else>${I18n.jobgroup_field_addressType_1}</#if></td>
                                         <td>
                                             <#if group.registryList?exists>
                                                 <#list group.registryList as item>
@@ -74,8 +74,8 @@
                                                     title="${group.title}"
                                                     order="${group.order}"
                                                     addressType="${group.addressType}"
-                                                    addressList="${group.addressList}" >编辑</button>
-                                            <button class="btn btn-danger btn-xs remove" id="${group.id}" >删除</button>
+                                                    addressList="${group.addressList}" >${I18n.system_opt_edit}</button>
+                                            <button class="btn btn-danger btn-xs remove" id="${group.id}" >${I18n.system_opt_del}</button>
 										</td>
 									</tr>
 								</#list>
@@ -94,41 +94,41 @@
         <div class="modal-dialog ">
             <div class="modal-content">
                 <div class="modal-header">
-                    <h4 class="modal-title" >新增执行器</h4>
+                    <h4 class="modal-title" >${I18n.jobgroup_add}</h4>
                 </div>
                 <div class="modal-body">
                     <form class="form-horizontal form" role="form" >
                         <div class="form-group">
                             <label for="lastname" class="col-sm-2 control-label">AppName<font color="red">*</font></label>
-                            <div class="col-sm-10"><input type="text" class="form-control" name="appName" placeholder="请输入“AppName”" maxlength="64" ></div>
+                            <div class="col-sm-10"><input type="text" class="form-control" name="appName" placeholder="${I18n.system_please_input}AppName" maxlength="64" ></div>
                         </div>
                         <div class="form-group">
-                            <label for="lastname" class="col-sm-2 control-label">名称<font color="red">*</font></label>
-                            <div class="col-sm-10"><input type="text" class="form-control" name="title" placeholder="请输入“名称”" maxlength="12" ></div>
+                            <label for="lastname" class="col-sm-2 control-label">${I18n.jobgroup_field_title}<font color="red">*</font></label>
+                            <div class="col-sm-10"><input type="text" class="form-control" name="title" placeholder="${I18n.system_please_input}${I18n.jobgroup_field_title}" maxlength="12" ></div>
                         </div>
                         <div class="form-group">
-                            <label for="lastname" class="col-sm-2 control-label">排序<font color="red">*</font></label>
-                            <div class="col-sm-10"><input type="text" class="form-control" name="order" placeholder="请输入“排序”" maxlength="50" ></div>
+                            <label for="lastname" class="col-sm-2 control-label">${I18n.jobgroup_field_order}<font color="red">*</font></label>
+                            <div class="col-sm-10"><input type="text" class="form-control" name="order" placeholder="${I18n.system_please_input}${I18n.jobgroup_field_order}" maxlength="50" ></div>
                         </div>
                         <div class="form-group">
-                            <label for="lastname" class="col-sm-2 control-label">注册方式<font color="red">*</font></label>
+                            <label for="lastname" class="col-sm-2 control-label">${I18n.jobgroup_field_addressType}<font color="red">*</font></label>
                             <div class="col-sm-10">
-                                <input type="radio" name="addressType" value="0" checked />自动注册
+                                <input type="radio" name="addressType" value="0" checked />${I18n.jobgroup_field_addressType_0}
                                 &nbsp;&nbsp;&nbsp;&nbsp;
-                                <input type="radio" name="addressType" value="1" />手动录入
+                                <input type="radio" name="addressType" value="1" />${I18n.jobgroup_field_addressType_1}
                             </div>
                         </div>
                         <div class="form-group">
-                            <label for="lastname" class="col-sm-2 control-label">机器地址<font color="red">*</font></label>
+                            <label for="lastname" class="col-sm-2 control-label">${I18n.jobgroup_field_registryList}<font color="red">*</font></label>
                             <div class="col-sm-10">
-                                <textarea class="textarea" name="addressList" maxlength="512" placeholder="请输入执行器地址列表,多地址逗号分隔" readonly="readonly" style="background-color:#eee; width: 100%; height: 100px; font-size: 14px; line-height: 10px; border: 1px solid #dddddd; padding: 10px;"></textarea>
+                                <textarea class="textarea" name="addressList" maxlength="512" placeholder="${I18n.jobgroup_field_registryList_placeholder}" readonly="readonly" style="background-color:#eee; width: 100%; height: 100px; font-size: 14px; line-height: 10px; border: 1px solid #dddddd; padding: 10px;"></textarea>
                             </div>
                         </div>
                         <hr>
                         <div class="form-group">
                             <div class="col-sm-offset-3 col-sm-6">
-                                <button type="submit" class="btn btn-primary"  >保存</button>
-                                <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
+                                <button type="submit" class="btn btn-primary"  >${I18n.system_save}</button>
+                                <button type="button" class="btn btn-default" data-dismiss="modal">${I18n.system_cancel}</button>
                             </div>
                         </div>
                     </form>
@@ -142,41 +142,41 @@
         <div class="modal-dialog ">
             <div class="modal-content">
                 <div class="modal-header">
-                    <h4 class="modal-title" >编辑执行器</h4>
+                    <h4 class="modal-title" >${I18n.jobgroup_edit}</h4>
                 </div>
                 <div class="modal-body">
                     <form class="form-horizontal form" role="form" >
                         <div class="form-group">
                             <label for="lastname" class="col-sm-2 control-label">AppName<font color="red">*</font></label>
-                            <div class="col-sm-10"><input type="text" class="form-control" name="appName" placeholder="请输入“AppName”" maxlength="64" ></div>
+                            <div class="col-sm-10"><input type="text" class="form-control" name="appName" placeholder="${I18n.system_please_input}AppName" maxlength="64" ></div>
                         </div>
                         <div class="form-group">
-                            <label for="lastname" class="col-sm-2 control-label">名称<font color="red">*</font></label>
-                            <div class="col-sm-10"><input type="text" class="form-control" name="title" placeholder="请输入“名称”" maxlength="12" ></div>
+                            <label for="lastname" class="col-sm-2 control-label">${I18n.jobgroup_field_title}<font color="red">*</font></label>
+                            <div class="col-sm-10"><input type="text" class="form-control" name="title" placeholder="${I18n.system_please_input}${I18n.jobgroup_field_title}" maxlength="12" ></div>
                         </div>
                         <div class="form-group">
-                            <label for="lastname" class="col-sm-2 control-label">排序<font color="red">*</font></label>
-                            <div class="col-sm-10"><input type="text" class="form-control" name="order" placeholder="请输入“排序”" maxlength="50" ></div>
+                            <label for="lastname" class="col-sm-2 control-label">${I18n.jobgroup_field_order}<font color="red">*</font></label>
+                            <div class="col-sm-10"><input type="text" class="form-control" name="order" placeholder="${I18n.system_please_input}${I18n.jobgroup_field_order}" maxlength="50" ></div>
                         </div>
                         <div class="form-group">
-                            <label for="lastname" class="col-sm-2 control-label">注册方式<font color="red">*</font></label>
+                            <label for="lastname" class="col-sm-2 control-label">${I18n.jobgroup_field_addressType}<font color="red">*</font></label>
                             <div class="col-sm-10">
-                                <input type="radio" name="addressType" value="0" />自动注册
+                                <input type="radio" name="addressType" value="0" />${I18n.jobgroup_field_addressType_0}
                                 &nbsp;&nbsp;&nbsp;&nbsp;
-                                <input type="radio" name="addressType" value="1" />手动录入
+                                <input type="radio" name="addressType" value="1" />${I18n.jobgroup_field_addressType_1}
                             </div>
                         </div>
                         <div class="form-group">
-                            <label for="lastname" class="col-sm-2 control-label">机器地址<font color="red">*</font></label>
+                            <label for="lastname" class="col-sm-2 control-label">${I18n.jobgroup_field_registryList}<font color="red">*</font></label>
                             <div class="col-sm-10">
-                                <textarea class="textarea" name="addressList" maxlength="512" placeholder="请输入执行器地址列表,多地址逗号分隔" readonly="readonly" style="background-color:#eee; width: 100%; height: 100px; font-size: 14px; line-height: 10px; border: 1px solid #dddddd; padding: 10px;"></textarea>
+                                <textarea class="textarea" name="addressList" maxlength="512" placeholder="${I18n.jobgroup_field_registryList_placeholder}" readonly="readonly" style="background-color:#eee; width: 100%; height: 100px; font-size: 14px; line-height: 10px; border: 1px solid #dddddd; padding: 10px;"></textarea>
                             </div>
                         </div>
                         <hr>
                         <div class="form-group">
                             <div class="col-sm-offset-3 col-sm-6">
-                                <button type="submit" class="btn btn-primary"  >保存</button>
-                                <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
+                                <button type="submit" class="btn btn-primary"  >${I18n.system_save}</button>
+                                <button type="button" class="btn btn-default" data-dismiss="modal">${I18n.system_cancel}</button>
                                 <input type="hidden" name="id" >
                             </div>
                         </div>

+ 81 - 82
xxl-job-admin/src/main/webapp/WEB-INF/template/jobinfo/jobinfo.index.ftl

@@ -1,12 +1,11 @@
 <!DOCTYPE html>
 <html>
 <head>
-  	<title>任务调度中心</title>
   	<#import "/common/common.macro.ftl" as netCommon>
 	<@netCommon.commonStyle />
 	<!-- DataTables -->
   	<link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/datatables/dataTables.bootstrap.css">
-
+    <title>${I18n.admin_name}</title>
 </head>
 <body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if>">
 <div class="wrapper">
@@ -19,22 +18,16 @@
 	<div class="content-wrapper">
 		<!-- Content Header (Page header) -->
 		<section class="content-header">
-			<h1>任务管理<small>任务调度中心</small></h1>
-			<!--
-			<ol class="breadcrumb">
-				<li><a><i class="fa fa-dashboard"></i>调度管理</a></li>
-				<li class="active">调度中心</li>
-			</ol>
-			-->
+			<h1>${I18n.jobinfo_name}</h1>
 		</section>
 		
 		<!-- Main content -->
 	    <section class="content">
 	    
 	    	<div class="row">
-	    		<div class="col-xs-4">
+	    		<div class="col-xs-3">
 	              	<div class="input-group">
-	                	<span class="input-group-addon">执行器</span>
+	                	<span class="input-group-addon">${I18n.jobinfo_field_jobgroup}</span>
                 		<select class="form-control" id="jobGroup" >
                 			<#list JobGroupList as group>
                 				<option value="${group.id}" <#if jobGroup==group.id>selected</#if> >${group.title}</option>
@@ -42,43 +35,48 @@
 	                  	</select>
 	              	</div>
 	            </div>
-                <div class="col-xs-4">
+                <div class="col-xs-3">
+                    <div class="input-group">
+                        <span class="input-group-addon">${I18n.jobinfo_field_jobdesc}</span>
+                        <input type="text" class="form-control" id="jobDesc" autocomplete="on" >
+                    </div>
+                </div>
+                <div class="col-xs-3">
                     <div class="input-group">
                         <span class="input-group-addon">JobHandler</span>
                         <input type="text" class="form-control" id="executorHandler" autocomplete="on" >
                     </div>
                 </div>
-	            <div class="col-xs-2">
-	            	<button class="btn btn-block btn-info" id="searchBtn">搜索</button>
+	            <div class="col-xs-1">
+	            	<button class="btn btn-block btn-info" id="searchBtn">${I18n.system_search}</button>
 	            </div>
 	            <div class="col-xs-2">
-	            	<button class="btn btn-block btn-success add" type="button">+新增任务</button>
+	            	<button class="btn btn-block btn-success add" type="button">${I18n.jobinfo_field_add}</button>
 	            </div>
           	</div>
 	    	
 			<div class="row">
 				<div class="col-xs-12">
 					<div class="box">
-			            <div class="box-header hide">
+			            <#--<div class="box-header hide">
 			            	<h3 class="box-title">调度列表</h3>
-			            </div>
+			            </div>-->
 			            <div class="box-body" >
 			              	<table id="job_list" class="table table-bordered table-striped" width="100%" >
 				                <thead>
 					            	<tr>
-					            		<th name="id" >任务ID</th>
-					                	<th name="jobGroup" >jobGroup</th>
-					                  	<th name="jobDesc" >描述</th>
-                                        <th name="glueType" >运行模式</th>
-					                  	<th name="executorParam" >任务参数</th>
+					            		<th name="id" >${I18n.jobinfo_field_id}</th>
+					                	<th name="jobGroup" >${I18n.jobinfo_field_jobgroup}</th>
+					                  	<th name="jobDesc" >${I18n.jobinfo_field_jobdesc}</th>
+                                        <th name="glueType" >${I18n.jobinfo_field_gluetype}</th>
+					                  	<th name="executorParam" >${I18n.jobinfo_field_executorparam}</th>
                                         <th name="jobCron" >Cron</th>
-					                  	<th name="addTime" >新增时间</th>
-					                  	<th name="updateTime" >更新时间</th>
-					                  	<th name="author" >负责人</th>
-					                  	<th name="alarmEmail" >报警邮件</th>
-					                  	<th name="glueType" >运行模式</th>
-					                  	<th name="jobStatus" >状态</th>
-					                  	<th>操作</th>
+					                  	<th name="addTime" >addTime</th>
+					                  	<th name="updateTime" >updateTime</th>
+					                  	<th name="author" >${I18n.jobinfo_field_author}</th>
+					                  	<th name="alarmEmail" >${I18n.jobinfo_field_alarmemail}</th>
+					                  	<th name="jobStatus" >${I18n.system_status}</th>
+					                  	<th>${I18n.system_opt}</th>
 					                </tr>
 				                </thead>
 				                <tbody></tbody>
@@ -100,12 +98,12 @@
 	<div class="modal-dialog modal-lg">
 		<div class="modal-content">
 			<div class="modal-header">
-            	<h4 class="modal-title" >新增任务</h4>
+            	<h4 class="modal-title" >${I18n.jobinfo_field_add}</h4>
          	</div>
          	<div class="modal-body">
 				<form class="form-horizontal form" role="form" >
 					<div class="form-group">
-						<label for="firstname" class="col-sm-2 control-label">执行器<font color="red">*</font></label>
+						<label for="firstname" class="col-sm-2 control-label">${I18n.jobinfo_field_jobgroup}<font color="red">*</font></label>
 						<div class="col-sm-4">
 							<select class="form-control" name="jobGroup" >
 		            			<#list JobGroupList as group>
@@ -113,11 +111,11 @@
 		            			</#list>
 		                  	</select>
 						</div>
-                        <label for="lastname" class="col-sm-2 control-label">任务描述<font color="red">*</font></label>
-                        <div class="col-sm-4"><input type="text" class="form-control" name="jobDesc" placeholder="请输入“描述”" maxlength="50" ></div>
+                        <label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_field_jobdesc}<font color="red">*</font></label>
+                        <div class="col-sm-4"><input type="text" class="form-control" name="jobDesc" placeholder="${I18n.system_please_input}${I18n.jobinfo_field_jobdesc}" maxlength="50" ></div>
 					</div>
                     <div class="form-group">
-                        <label for="firstname" class="col-sm-2 control-label">路由策略<font color="red">*</font></label>
+                        <label for="firstname" class="col-sm-2 control-label">${I18n.jobinfo_field_executorRouteStrategy}<font color="red">*</font></label>
                         <div class="col-sm-4">
                             <select class="form-control" name="executorRouteStrategy" >
 							<#list ExecutorRouteStrategyEnum as item>
@@ -126,10 +124,10 @@
                             </select>
                         </div>
                         <label for="lastname" class="col-sm-2 control-label">Cron<font color="red">*</font></label>
-                        <div class="col-sm-4"><input type="text" class="form-control" name="jobCron" placeholder="请输入“Cron”" maxlength="128" ></div>
+                        <div class="col-sm-4"><input type="text" class="form-control" name="jobCron" placeholder="${I18n.system_please_input}Cron" maxlength="128" ></div>
                     </div>
                     <div class="form-group">
-                        <label for="firstname" class="col-sm-2 control-label">运行模式<font color="red">*</font></label>
+                        <label for="firstname" class="col-sm-2 control-label">${I18n.jobinfo_field_gluetype}<font color="red">*</font></label>
                         <div class="col-sm-4">
                             <select class="form-control glueType" name="glueType" >
 								<#list GlueTypeEnum as item>
@@ -138,16 +136,16 @@
                             </select>
                         </div>
                         <label for="firstname" class="col-sm-2 control-label">JobHandler<font color="black">*</font></label>
-                        <div class="col-sm-4"><input type="text" class="form-control" name="executorHandler" placeholder="请输入“JobHandler”" maxlength="100" ></div>
+                        <div class="col-sm-4"><input type="text" class="form-control" name="executorHandler" placeholder="${I18n.system_please_input}JobHandler" maxlength="100" ></div>
                     </div>
                     <div class="form-group">
-                        <label for="firstname" class="col-sm-2 control-label">执行参数<font color="black">*</font></label>
-                        <div class="col-sm-4"><input type="text" class="form-control" name="executorParam" placeholder="请输入“执行参数”" maxlength="512" ></div>
-                        <label for="lastname" class="col-sm-2 control-label">子任务ID<font color="black">*</font></label>
-                        <div class="col-sm-4"><input type="text" class="form-control" name="childJobId" placeholder="请输入子任务的任务ID,如存在多个逗号分隔" maxlength="100" ></div>
+                        <label for="firstname" class="col-sm-2 control-label">${I18n.jobinfo_field_executorparam}<font color="black">*</font></label>
+                        <div class="col-sm-4"><input type="text" class="form-control" name="executorParam" placeholder="${I18n.system_please_input}${I18n.jobinfo_field_executorparam}" maxlength="512" ></div>
+                        <label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_field_childJobId}<font color="black">*</font></label>
+                        <div class="col-sm-4"><input type="text" class="form-control" name="childJobId" placeholder="${I18n.jobinfo_field_childJobId_placeholder}" maxlength="100" ></div>
                     </div>
                     <div class="form-group">
-                        <label for="firstname" class="col-sm-2 control-label">阻塞处理策略<font color="red">*</font></label>
+                        <label for="firstname" class="col-sm-2 control-label">${I18n.jobinfo_field_executorBlockStrategy}<font color="red">*</font></label>
                         <div class="col-sm-4">
                             <select class="form-control" name="executorBlockStrategy" >
 								<#list ExecutorBlockStrategyEnum as item>
@@ -155,7 +153,7 @@
 								</#list>
                             </select>
 						</div>
-                        <label for="lastname" class="col-sm-2 control-label">失败处理策略<font color="red">*</font></label>
+                        <label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_field_executorFailStrategy}<font color="red">*</font></label>
                         <div class="col-sm-4">
                             <select class="form-control" name="executorFailStrategy" >
 								<#list ExecutorFailStrategyEnum as item>
@@ -165,17 +163,17 @@
 						</div>
                     </div>
 					<div class="form-group">
-                        <label for="lastname" class="col-sm-2 control-label">负责人<font color="red">*</font></label>
-                        <div class="col-sm-4"><input type="text" class="form-control" name="author" placeholder="请输入“负责人”" maxlength="50" ></div>
-						<label for="lastname" class="col-sm-2 control-label">报警邮件<font color="black">*</font></label>
-						<div class="col-sm-4"><input type="text" class="form-control" name="alarmEmail" placeholder="请输入“报警邮件”,多个邮件地址逗号分隔" maxlength="100" ></div>
+                        <label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_field_author}<font color="red">*</font></label>
+                        <div class="col-sm-4"><input type="text" class="form-control" name="author" placeholder="${I18n.system_please_input}${I18n.jobinfo_field_author}" maxlength="50" ></div>
+						<label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_field_alarmemail}<font color="black">*</font></label>
+						<div class="col-sm-4"><input type="text" class="form-control" name="alarmEmail" placeholder="${I18n.jobinfo_field_alarmemail_placeholder}" maxlength="100" ></div>
 					</div>
 
                     <hr>
 					<div class="form-group">
 						<div class="col-sm-offset-3 col-sm-6">
-							<button type="submit" class="btn btn-primary"  >保存</button>
-							<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
+							<button type="submit" class="btn btn-primary"  >${I18n.system_save}</button>
+							<button type="button" class="btn btn-default" data-dismiss="modal">${I18n.system_cancel}</button>
 						</div>
 					</div>
 
@@ -202,10 +200,10 @@ public class DemoGlueJobHandler extends IJobHandler {
 #!/bin/bash
 echo "xxl-job: hello shell"
 
-echo "脚本位置:$0"
-echo "任务参数:$1"
-echo "分片序号 = $2"
-echo "分片总数 = $3"
+echo "${I18n.jobinfo_script_location}:$0"
+echo "${I18n.jobinfo_field_executorparam}:$1"
+echo "${I18n.jobinfo_shard_index} = $2"
+echo "${I18n.jobinfo_shard_total} = $3"
 <#--echo "参数数量:$#"
 for param in $*
 do
@@ -224,10 +222,10 @@ import sys
 
 print "xxl-job: hello python"
 
-print "脚本文件:", sys.argv[0]
-print "任务参数:", sys.argv[1]
-print "分片序号:", sys.argv[2]
-print "分片总数:", sys.argv[3]
+print "${I18n.jobinfo_script_location}:", sys.argv[0]
+print "${I18n.jobinfo_field_executorparam}:", sys.argv[1]
+print "${I18n.jobinfo_shard_index}:", sys.argv[2]
+print "${I18n.jobinfo_shard_total}:", sys.argv[3]
 <#--for i in range(1, len(sys.argv)):
 	time.sleep(1)
 	print "参数", i, sys.argv[i]-->
@@ -246,10 +244,10 @@ console.log("xxl-job: hello nodejs")
 
 var arguments = process.argv
 
-console.log("脚本文件: " + arguments[1])
-console.log("任务参数: " + arguments[2])
-console.log("分片序号: " + arguments[3])
-console.log("分片总数: " + arguments[4])
+console.log("${I18n.jobinfo_script_location}: " + arguments[1])
+console.log("${I18n.jobinfo_field_executorparam}: " + arguments[2])
+console.log("${I18n.jobinfo_shard_index}: " + arguments[3])
+console.log("${I18n.jobinfo_shard_total}: " + arguments[4])
 <#--for (var i = 2; i < arguments.length; i++){
 	console.log("参数 %s = %s", (i-1), arguments[i]);
 }-->
@@ -268,12 +266,12 @@ process.exit(0)
 	<div class="modal-dialog modal-lg">
 		<div class="modal-content">
 			<div class="modal-header">
-            	<h4 class="modal-title" >更新任务</h4>
+            	<h4 class="modal-title" >${I18n.jobinfo_field_update}</h4>
          	</div>
          	<div class="modal-body">
 				<form class="form-horizontal form" role="form" >
-                    <div class="form-group">
-                        <label for="firstname" class="col-sm-2 control-label">执行器<font color="red">*</font></label>
+					<div class="form-group">
+                        <label for="firstname" class="col-sm-2 control-label">${I18n.jobinfo_field_jobgroup}<font color="red">*</font></label>
                         <div class="col-sm-4">
                             <select class="form-control" name="jobGroup" disabled >
 							<#list JobGroupList as group>
@@ -281,11 +279,11 @@ process.exit(0)
 							</#list>
                             </select>
                         </div>
-                        <label for="lastname" class="col-sm-2 control-label">任务描述<font color="red">*</font></label>
-                        <div class="col-sm-4"><input type="text" class="form-control" name="jobDesc" placeholder="请输入“描述”" maxlength="50" ></div>
+                        <label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_field_jobdesc}<font color="red">*</font></label>
+                        <div class="col-sm-4"><input type="text" class="form-control" name="jobDesc" placeholder="${I18n.system_please_input}${I18n.jobinfo_field_jobdesc}" maxlength="50" ></div>
                     </div>
                     <div class="form-group">
-                        <label for="firstname" class="col-sm-2 control-label">路由策略<font color="red">*</font></label>
+                        <label for="firstname" class="col-sm-2 control-label">${I18n.jobinfo_field_executorRouteStrategy}<font color="red">*</font></label>
                         <div class="col-sm-4">
                             <select class="form-control" name="executorRouteStrategy" >
 							<#list ExecutorRouteStrategyEnum as item>
@@ -294,10 +292,10 @@ process.exit(0)
                             </select>
                         </div>
                         <label for="lastname" class="col-sm-2 control-label">Cron<font color="red">*</font></label>
-                        <div class="col-sm-4"><input type="text" class="form-control" name="jobCron" placeholder="请输入“Cron”" maxlength="128" ></div>
+                        <div class="col-sm-4"><input type="text" class="form-control" name="jobCron" placeholder="${I18n.system_please_input}Cron" maxlength="128" ></div>
                     </div>
                     <div class="form-group">
-                        <label for="firstname" class="col-sm-2 control-label">运行模式<font color="red">*</font></label>
+                        <label for="firstname" class="col-sm-2 control-label">${I18n.jobinfo_field_gluetype}<font color="red">*</font></label>
                         <div class="col-sm-4">
                             <select class="form-control glueType" name="glueType" disabled >
 							<#list GlueTypeEnum as item>
@@ -306,16 +304,16 @@ process.exit(0)
                             </select>
                         </div>
                         <label for="firstname" class="col-sm-2 control-label">JobHandler<font color="black">*</font></label>
-                        <div class="col-sm-4"><input type="text" class="form-control" name="executorHandler" placeholder="请输入“JobHandler”" maxlength="100" ></div>
+                        <div class="col-sm-4"><input type="text" class="form-control" name="executorHandler" placeholder="${I18n.system_please_input}JobHandler" maxlength="100" ></div>
                     </div>
                     <div class="form-group">
-                        <label for="firstname" class="col-sm-2 control-label">执行参数<font color="black">*</font></label>
-                        <div class="col-sm-4"><input type="text" class="form-control" name="executorParam" placeholder="请输入“执行参数”" maxlength="512" ></div>
-                        <label for="lastname" class="col-sm-2 control-label">子任务ID<font color="black">*</font></label>
-                        <div class="col-sm-4"><input type="text" class="form-control" name="childJobId" placeholder="请输入子任务的任务ID,如存在多个逗号分隔" maxlength="100" ></div>
+                        <label for="firstname" class="col-sm-2 control-label">${I18n.jobinfo_field_executorparam}<font color="black">*</font></label>
+                        <div class="col-sm-4"><input type="text" class="form-control" name="executorParam" placeholder="${I18n.system_please_input}${I18n.jobinfo_field_executorparam}" maxlength="512" ></div>
+                        <label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_field_childJobId}<font color="black">*</font></label>
+                        <div class="col-sm-4"><input type="text" class="form-control" name="childJobId" placeholder="${I18n.jobinfo_field_childJobId_placeholder}" maxlength="100" ></div>
                     </div>
                     <div class="form-group">
-                        <label for="firstname" class="col-sm-2 control-label">阻塞处理策略<font color="red">*</font></label>
+                        <label for="firstname" class="col-sm-2 control-label">${I18n.jobinfo_field_executorBlockStrategy}<font color="red">*</font></label>
                         <div class="col-sm-4">
                             <select class="form-control" name="executorBlockStrategy" >
 							<#list ExecutorBlockStrategyEnum as item>
@@ -323,7 +321,7 @@ process.exit(0)
 							</#list>
                             </select>
                         </div>
-                        <label for="lastname" class="col-sm-2 control-label">失败处理策略<font color="red">*</font></label>
+                        <label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_field_executorFailStrategy}<font color="red">*</font></label>
                         <div class="col-sm-4">
                             <select class="form-control" name="executorFailStrategy" >
 							<#list ExecutorFailStrategyEnum as item>
@@ -333,20 +331,21 @@ process.exit(0)
                         </div>
                     </div>
                     <div class="form-group">
-                        <label for="lastname" class="col-sm-2 control-label">负责人<font color="red">*</font></label>
-                        <div class="col-sm-4"><input type="text" class="form-control" name="author" placeholder="请输入“负责人”" maxlength="50" ></div>
-                        <label for="lastname" class="col-sm-2 control-label">报警邮件<font color="black">*</font></label>
-                        <div class="col-sm-4"><input type="text" class="form-control" name="alarmEmail" placeholder="请输入“报警邮件”,多个邮件地址逗号分隔" maxlength="100" ></div>
+                        <label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_field_author}<font color="red">*</font></label>
+                        <div class="col-sm-4"><input type="text" class="form-control" name="author" placeholder="${I18n.system_please_input}${I18n.jobinfo_field_author}" maxlength="50" ></div>
+                        <label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_field_alarmemail}<font color="black">*</font></label>
+                        <div class="col-sm-4"><input type="text" class="form-control" name="alarmEmail" placeholder="${I18n.jobinfo_field_alarmemail_placeholder}" maxlength="100" ></div>
                     </div>
 
 					<hr>
 					<div class="form-group">
                         <div class="col-sm-offset-3 col-sm-6">
-							<button type="submit" class="btn btn-primary"  >保存</button>
-							<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
+							<button type="submit" class="btn btn-primary"  >${I18n.system_save}</button>
+							<button type="button" class="btn btn-default" data-dismiss="modal">${I18n.system_cancel}</button>
                             <input type="hidden" name="id" >
 						</div>
 					</div>
+
 				</form>
          	</div>
 		</div>

+ 6 - 6
xxl-job-admin/src/main/webapp/WEB-INF/template/joblog/joblog.detail.ftl

@@ -1,9 +1,9 @@
 <!DOCTYPE html>
 <html>
 <head>
-    <title>任务调度中心</title>
-<#import "/common/common.macro.ftl" as netCommon>
-<@netCommon.commonStyle />
+    <#import "/common/common.macro.ftl" as netCommon>
+    <@netCommon.commonStyle />
+    <title>${I18n.admin_name}</title>
 </head>
 <body class="hold-transition skin-blue layout-top-nav">
 
@@ -12,9 +12,9 @@
     <header class="main-header">
         <nav class="navbar navbar-static-top">
             <div class="container">
-            <#-- icon -->
+                <#-- icon -->
                 <div class="navbar-header">
-                    <a class="navbar-brand"><b>执行日志</b>Console</a>
+                    <a class="navbar-brand"><b>${I18n.joblog_rolling_log}</b> Console</a>
                     <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse">
                         <i class="fa fa-bars"></i>
                     </button>
@@ -33,7 +33,7 @@
                         <li>
                             <a href="javascript:window.location.reload();" >
                                 <i class="fa fa-fw fa-refresh" ></i>
-                                刷新
+                                ${I18n.joblog_rolling_log_refresh}
                             </a>
                         </li>
                     </ul>

+ 45 - 45
xxl-job-admin/src/main/webapp/WEB-INF/template/joblog/joblog.index.ftl

@@ -1,13 +1,13 @@
 <!DOCTYPE html>
 <html>
 <head>
-  	<title>任务调度中心</title>
   	<#import "/common/common.macro.ftl" as netCommon>
 	<@netCommon.commonStyle />
 	<!-- DataTables -->
   	<link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/datatables/dataTables.bootstrap.css">
   	<!-- daterangepicker -->
   	<link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/daterangepicker/daterangepicker.css">
+    <title>${I18n.admin_name}</title>
 </head>
 <body class="hold-transition skin-blue sidebar-mini <#if cookieMap?exists && "off" == cookieMap["xxljob_adminlte_settings"].value >sidebar-collapse</#if> ">
 <div class="wrapper">
@@ -20,13 +20,7 @@
 	<div class="content-wrapper">
 		<!-- Content Header (Page header) -->
 		<section class="content-header">
-			<h1>调度日志<small>任务调度中心</small></h1>
-			<!--
-			<ol class="breadcrumb">
-				<li><a><i class="fa fa-dashboard"></i>调度日志</a></li>
-				<li class="active">调度管理</li>
-			</ol>
-			-->
+			<h1>${I18n.joblog_name}</h1>
 		</section>
 		
 		<!-- Main content -->
@@ -34,9 +28,9 @@
 	    	<div class="row">
 	    		<div class="col-xs-2">
  					<div class="input-group">
-	                	<span class="input-group-addon">执行器</span>
+	                	<span class="input-group-addon">${I18n.jobinfo_field_jobgroup}</span>
                 		<select class="form-control" id="jobGroup"  paramVal="<#if jobInfo?exists>${jobInfo.jobGroup}</#if>" >
-                            <option value="0" >全部</option>
+                            <option value="0" >${I18n.system_all}</option>
                 			<#list JobGroupList as group>
                 				<option value="${group.id}" >${group.title}</option>
                 			</#list>
@@ -45,21 +39,21 @@
 	            </div>
 	            <div class="col-xs-2">
 	              	<div class="input-group">
-	                	<span class="input-group-addon">任务</span>
+	                	<span class="input-group-addon">${I18n.jobinfo_job}</span>
                         <select class="form-control" id="jobId" paramVal="<#if jobInfo?exists>${jobInfo.id}</#if>" >
-                            <option value="0" >全部</option>
+                            <option value="0" >${I18n.system_all}</option>
 						</select>
 	              	</div>
 	            </div>
 
                 <div class="col-xs-2">
                     <div class="input-group">
-                        <span class="input-group-addon">状态</span>
+                        <span class="input-group-addon">${I18n.joblog_status}</span>
                         <select class="form-control" id="logStatus" >
-                            <option value="-1" >全部</option>
-                            <option value="1" >成功</option>
-                            <option value="2" >失败</option>
-                            <option value="3" >进行中</option>
+                            <option value="-1" >${I18n.joblog_status_all}</option>
+                            <option value="1" >${I18n.joblog_status_suc}</option>
+                            <option value="2" >${I18n.joblog_status_fail}</option>
+                            <option value="3" >${I18n.joblog_status_running}</option>
                         </select>
                     </div>
                 </div>
@@ -67,41 +61,41 @@
 	            <div class="col-xs-4">
               		<div class="input-group">
                 		<span class="input-group-addon">
-	                  		调度时间
+	                  		${I18n.joblog_field_triggerTime}
 	                	</span>
 	                	<input type="text" class="form-control" id="filterTime" readonly >
 	              	</div>
 	            </div>
 
                 <div class="col-xs-1">
-                    <button class="btn btn-block btn-info" id="searchBtn">搜索</button>
+                    <button class="btn btn-block btn-info" id="searchBtn">${I18n.system_search}</button>
                 </div>
 
 	            <div class="col-xs-1">
-                    <button class="btn btn-block btn-nomal" id="clearLog">清理</button>
+                    <button class="btn btn-block btn-nomal" id="clearLog">${I18n.joblog_clean}</button>
 	            </div>
           	</div>
 			
 			<div class="row">
 				<div class="col-xs-12">
 					<div class="box">
-			            <div class="box-header hide"><h3 class="box-title">调度日志</h3></div>
+			            <#--<div class="box-header hide"><h3 class="box-title">调度日志</h3></div>-->
 			            <div class="box-body">
 			              	<table id="joblog_list" class="table table-bordered table-striped display" width="100%" >
 				                <thead>
 					            	<tr>
-                                        <th name="jobId" >任务ID</th>
-                                        <th name="jobGroup" >执行器ID</th>
+                                        <th name="jobId" >${I18n.jobinfo_field_id}</th>
+                                        <th name="jobGroup" >jobGroup</th>
 										<#--<th name="executorAddress" >执行器地址</th>
 										<th name="glueType" >运行模式</th>
                                       	<th name="executorParam" >任务参数</th>-->
-                                        <th name="triggerTime" >调度时间</th>
-                                        <th name="triggerCode" >调度结果</th>
-                                        <th name="triggerMsg" >调度备注</th>
-					                  	<th name="handleTime" >执行时间</th>
-					                  	<th name="handleCode" >执行结果</th>
-					                  	<th name="handleMsg" >执行备注</th>
-					                  	<th name="handleMsg" >操作</th>
+                                        <th name="triggerTime" >${I18n.joblog_field_triggerTime}</th>
+                                        <th name="triggerCode" >${I18n.joblog_field_triggerCode}</th>
+                                        <th name="triggerMsg" >${I18n.joblog_field_triggerMsg}</th>
+					                  	<th name="handleTime" >${I18n.joblog_field_handleTime}</th>
+					                  	<th name="handleCode" >${I18n.joblog_field_handleCode}</th>
+					                  	<th name="handleMsg" >${I18n.joblog_field_handleMsg}</th>
+					                  	<th name="handleMsg" >${I18n.system_opt}</th>
 					                </tr>
 				                </thead>
 				                <tbody></tbody>
@@ -122,12 +116,12 @@
     <div class="modal-dialog">
         <div class="modal-content">
             <div class="modal-header">
-                <h4 class="modal-title" >日志清理</h4>
+                <h4 class="modal-title" >${I18n.joblog_clean_log}</h4>
             </div>
             <div class="modal-body">
                 <form class="form-horizontal form" role="form" >
                     <div class="form-group">
-                        <label class="col-sm-3 control-label"">执行器:</label>
+                        <label class="col-sm-3 control-label"">${I18n.jobinfo_field_jobgroup}:</label>
                         <div class="col-sm-9">
                             <input type="text" class="form-control jobGroupText" readonly >
 							<input type="hidden" name="jobGroup" >
@@ -135,7 +129,7 @@
                     </div>
 
                     <div class="form-group">
-                        <label class="col-sm-3 control-label"">任务:</label>
+                        <label class="col-sm-3 control-label"">${I18n.jobinfo_job}:</label>
                         <div class="col-sm-9">
                             <input type="text" class="form-control jobIdText" readonly >
                             <input type="hidden" name="jobId" >
@@ -143,18 +137,18 @@
                     </div>
 
                     <div class="form-group">
-                        <label class="col-sm-3 control-label"">清理类型:</label>
+                        <label class="col-sm-3 control-label"">${I18n.joblog_clean_type}:</label>
                         <div class="col-sm-9">
                             <select class="form-control" name="type" >
-                                <option value="1" >清理一个月之前日志数据</option>
-                                <option value="2" >清理三个月之前日志数据</option>
-                                <option value="3" >清理六个月之前日志数据</option>
-                                <option value="4" >清理一年之前日志数据</option>
-                                <option value="5" >清理一千条以前日志数据</option>
-                                <option value="6" >清理一万条以前日志数据</option>
-                                <option value="7" >清理三万条以前日志数据</option>
-                                <option value="8" >清理十万条以前日志数据</option>
-                                <option value="9" >清理所有日志数据</option>
+                                <option value="1" >${I18n.joblog_clean_type_1}</option>
+                                <option value="2" >${I18n.joblog_clean_type_2}</option>
+                                <option value="3" >${I18n.joblog_clean_type_3}</option>
+                                <option value="4" >${I18n.joblog_clean_type_4}</option>
+                                <option value="5" >${I18n.joblog_clean_type_5}</option>
+                                <option value="6" >${I18n.joblog_clean_type_6}</option>
+                                <option value="7" >${I18n.joblog_clean_type_7}</option>
+                                <option value="8" >${I18n.joblog_clean_type_8}</option>
+                                <option value="9" >${I18n.joblog_clean_type_9}</option>
                             </select>
                         </div>
                     </div>
@@ -162,8 +156,8 @@
                     <hr>
                     <div class="form-group">
                         <div class="col-sm-offset-3 col-sm-6">
-                            <button type="button" class="btn btn-primary ok" >确定</button>
-                            <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
+                            <button type="button" class="btn btn-primary ok" >${I18n.system_ok}</button>
+                            <button type="button" class="btn btn-default" data-dismiss="modal">${I18n.system_cancel}</button>
                         </div>
                     </div>
                 </form>
@@ -173,6 +167,12 @@
 </div>
 
 <@netCommon.commonScript />
+<script>
+    var GlueTypeEnum = {};
+    <#list GlueTypeEnum as item>
+    GlueTypeEnum['${item}'] = '${item.desc}';
+    </#list>
+</script>
 <!-- DataTables -->
 <script src="${request.contextPath}/static/adminlte/plugins/datatables/jquery.dataTables.min.js"></script>
 <script src="${request.contextPath}/static/adminlte/plugins/datatables/dataTables.bootstrap.min.js"></script>

+ 6 - 6
xxl-job-admin/src/main/webapp/WEB-INF/template/login.ftl

@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <html>
 <head>
-  	<title>调度中心</title>
   	<#import "/common/common.macro.ftl" as netCommon>
 	<@netCommon.commonStyle />
     <link rel="stylesheet" href="${request.contextPath}/static/adminlte/plugins/iCheck/square/blue.css">
+	<title>${I18n.admin_name}</title>
 </head>
 <body class="hold-transition login-page">
 	<div class="login-box">
@@ -13,25 +13,25 @@
 		</div>
 		<form id="loginForm" method="post" >
 			<div class="login-box-body">
-				<p class="login-box-msg">任务调度中心</p>
+				<p class="login-box-msg">${I18n.admin_name}</p>
 				<div class="form-group has-feedback">
-	            	<input type="text" name="userName" class="form-control" placeholder="请输入登录账号" value="admin" >
+	            	<input type="text" name="userName" class="form-control" placeholder="${I18n.login_username_placeholder}" value="admin" maxlength="18" >
 	            	<span class="glyphicon glyphicon-envelope form-control-feedback"></span>
 				</div>
 	          	<div class="form-group has-feedback">
-	            	<input type="password" name="password" class="form-control" placeholder="请输入登录密码" value="123456" >
+	            	<input type="password" name="password" class="form-control" placeholder="${I18n.login_password_placeholder}" value="123456" maxlength="18" >
 	            	<span class="glyphicon glyphicon-lock form-control-feedback"></span>
 	          	</div>
 				<div class="row">
 					<div class="col-xs-8">
 		              	<div class="checkbox icheck">
 		                	<label>
-		                  		<input type="checkbox" name="ifRemember" > Remember Me
+		                  		<input type="checkbox" name="ifRemember" >${I18n.login_remember_me}
 		                	</label>
 						</div>
 		            </div><!-- /.col -->
 		            <div class="col-xs-4">
-						<button type="submit" class="btn btn-primary btn-block btn-flat">登录</button>
+						<button type="submit" class="btn btn-primary btn-block btn-flat">${I18n.login_btn}</button>
 					</div>
 				</div>
 			</div>

+ 15 - 17
xxl-job-admin/src/main/webapp/static/js/common.1.js

@@ -2,27 +2,24 @@ $(function(){
 
 	// logout
 	$("#logoutBtn").click(function(){
-		layer.confirm('确认注销登录?', {icon: 3, title:'系统提示'}, function(index){
+		layer.confirm( I18n.logout_confirm , {
+			icon: 3,
+			title: I18n.system_tips ,
+            btn: [ I18n.system_ok, I18n.system_cancel ]
+		}, function(index){
 			layer.close(index);
 
 			$.post(base_url + "/logout", function(data, status) {
 				if (data.code == "200") {
-                    layer.msg('注销成功');
+                    layer.msg( I18n.logout_success );
                     setTimeout(function(){
                         window.location.href = base_url + "/";
                     }, 500);
-					/*layer.open({
-						title: '系统提示',
-						content: '注销成功',
-						icon: '1',
-						end: function(layero, index){
-							window.location.href = base_url + "/";
-						}
-					});*/
 				} else {
 					layer.open({
-						title: '系统提示',
-						content: (data.msg || "操作失败"),
+						title: I18n.system_tips ,
+                        btn: [ I18n.system_ok ],
+						content: (data.msg || I18n.logout_fail),
 						icon: '2'
 					});
 				}
@@ -68,14 +65,14 @@ $(function(){
 		}
 	});
 	$(slideToTop).click(function () {
-		$("body").animate({
+		$("html,body").animate({		// firefox ie not support body, chrome support body. but found that new version chrome not support body too.
 			scrollTop: 0
 		}, 100);
 	});
 
-	// 左侧菜单状态,js + 后端 + cookie方式(新)
+	// left menu status v: js + server + cookie
 	$('.sidebar-toggle').click(function(){
-		var xxljob_adminlte_settings = $.cookie('xxljob_adminlte_settings');	// 左侧菜单展开状态[xxljob_adminlte_settings]:on=展开,off=折叠
+		var xxljob_adminlte_settings = $.cookie('xxljob_adminlte_settings');	// on=open,off=close
 		if ('off' == xxljob_adminlte_settings) {
             xxljob_adminlte_settings = 'on';
 		} else {
@@ -83,11 +80,12 @@ $(function(){
 		}
 		$.cookie('xxljob_adminlte_settings', xxljob_adminlte_settings, { expires: 7 });	//$.cookie('the_cookie', '', { expires: -1 });
 	});
-	// 左侧菜单状态,js + cookie方式(遗弃)
+
+	// left menu status v1: js + cookie
 	/*
 	 var xxljob_adminlte_settings = $.cookie('xxljob_adminlte_settings');
 	 if (xxljob_adminlte_settings == 'off') {
-	 $('body').addClass('sidebar-collapse');
+	 	$('body').addClass('sidebar-collapse');
 	 }
 	 */
 	

+ 54 - 46
xxl-job-admin/src/main/webapp/static/js/index.js

@@ -1,13 +1,17 @@
 /**
  * Created by xuxueli on 17/4/24.
  */
-
-
 $(function () {
 
-    // 过滤时间
-    var _startDate = moment().subtract(1, 'months');    // 默认,最近一月
-    var _endDate = moment();
+    // filter Time
+    var rangesConf = {};
+    rangesConf[I18n.daterangepicker_ranges_today] = [moment().startOf('day'), moment().endOf('day')];
+    rangesConf[I18n.daterangepicker_ranges_yesterday] = [moment().subtract(1, 'days').startOf('day'), moment().subtract(1, 'days').endOf('day')];
+    rangesConf[I18n.daterangepicker_ranges_this_month] = [moment().startOf('month'), moment().endOf('month')];
+    rangesConf[I18n.daterangepicker_ranges_last_month] = [moment().subtract(1, 'months').startOf('month'), moment().subtract(1, 'months').endOf('month')];
+    rangesConf[I18n.daterangepicker_ranges_recent_week] = [moment().subtract(1, 'weeks').startOf('day'), moment().endOf('day')];
+    rangesConf[I18n.daterangepicker_ranges_recent_month] = [moment().subtract(1, 'months').startOf('day'), moment().endOf('day')];
+
     $('#filterTime').daterangepicker({
         autoApply:false,
         singleDatePicker:false,
@@ -16,36 +20,28 @@ $(function () {
         timePickerIncrement: 10, 	// 时间的增量,单位为分钟
         timePicker24Hour : true,
         opens : 'left', //日期选择框的弹出位置
-        ranges: {
-            //'最近1小时': [moment().subtract(1, 'hours'), moment()],
-            '今日': [moment().startOf('day'), moment().endOf('day')],
-            '昨日': [moment().subtract(1, 'days').startOf('day'), moment().subtract(1, 'days').endOf('day')],
-            '本月': [moment().startOf('month'), moment().endOf('month')],
-            '上个月': [moment().subtract(1, 'months').startOf('month'), moment().subtract(1, 'months').endOf('month')],
-            '最近1周': [moment().subtract(1, 'weeks'), moment()],
-            '最近1月': [_startDate, _endDate]
-        },
+        ranges: rangesConf,
         locale : {
             format: 'YYYY-MM-DD HH:mm:ss',
             separator : ' - ',
-            customRangeLabel : '自定义',
-            applyLabel : '确定',
-            cancelLabel : '取消',
-            fromLabel : '起始时间',
-            toLabel : '结束时间',
-            daysOfWeek : [ '日', '一', '二', '三', '四', '五', '六' ],
-            monthNames : [ '一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月' ],
+            customRangeLabel : I18n.daterangepicker_custom_name ,
+            applyLabel : I18n.system_ok ,
+            cancelLabel : I18n.system_cancel ,
+            fromLabel : I18n.daterangepicker_custom_starttime ,
+            toLabel : I18n.daterangepicker_custom_endtime ,
+            daysOfWeek : I18n.daterangepicker_custom_daysofweek.split(',') ,        // '日', '一', '二', '三', '四', '五', '六'
+            monthNames : I18n.daterangepicker_custom_monthnames.split(',') ,        // '一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'
             firstDay : 1
         },
-        startDate:_startDate,
-        endDate: _endDate
+        startDate: rangesConf[I18n.daterangepicker_ranges_recent_month][0] ,
+        endDate: rangesConf[I18n.daterangepicker_ranges_recent_month][1]
     }, function (start, end, label) {
         freshChartDate(start, end);
     });
-    freshChartDate(_startDate, _endDate);
+    freshChartDate(rangesConf[I18n.daterangepicker_ranges_recent_month][0], rangesConf[I18n.daterangepicker_ranges_recent_month][1]);
 
     /**
-     * 刷新报表
+     * fresh Chart Date
      *
      * @param startDate
      * @param endDate
@@ -53,7 +49,7 @@ $(function () {
     function freshChartDate(startDate, endDate) {
         $.ajax({
             type : 'POST',
-            url : base_url + '/triggerChartDate',
+            url : base_url + '/chartInfo',
             data : {
                 'startDate':startDate.format('YYYY-MM-DD HH:mm:ss'),
                 'endDate':endDate.format('YYYY-MM-DD HH:mm:ss')
@@ -65,8 +61,9 @@ $(function () {
                     pieChartInit(data);
                 } else {
                     layer.open({
-                        title: '系统提示',
-                        content: (data.msg || '调度报表数据加载异常'),
+                        title: I18n.system_tips ,
+                        btn: [ I18n.system_ok ],
+                        content: (data.msg || I18n.job_dashboard_report_loaddata_fail ),
                         icon: '2'
                     });
                 }
@@ -75,12 +72,12 @@ $(function () {
     }
 
     /**
-     * 折线图
+     * line Chart Init
      */
     function lineChartInit(data) {
         var option = {
                title: {
-                   text: '日期分布图'
+                   text: I18n.job_dashboard_date_report
                },
                tooltip : {
                    trigger: 'axis',
@@ -92,7 +89,7 @@ $(function () {
                    }
                },
                legend: {
-                   data:['成功调度次数','失败调度次数']
+                   data:[I18n.joblog_status_suc, I18n.joblog_status_fail, I18n.joblog_status_running]
                },
                toolbox: {
                    feature: {
@@ -119,16 +116,16 @@ $(function () {
                ],
                series : [
                    {
-                       name:'成功调度次数',
+                       name:I18n.joblog_status_suc,
                        type:'line',
-                       stack: '总量',
+                       stack: 'Total',
                        areaStyle: {normal: {}},
                        data: data.content.triggerDayCountSucList
                    },
                    {
-                       name:'失败调度次数',
+                       name:I18n.joblog_status_fail,
                        type:'line',
-                       stack: '总量',
+                       stack: 'Total',
                        label: {
                            normal: {
                                show: true,
@@ -137,9 +134,16 @@ $(function () {
                        },
                        areaStyle: {normal: {}},
                        data: data.content.triggerDayCountFailList
+                   },
+                   {
+                       name:I18n.joblog_status_running,
+                       type:'line',
+                       stack: 'Total',
+                       areaStyle: {normal: {}},
+                       data: data.content.triggerDayCountRunningList
                    }
                ],
-                color:['#00A65A', '#F39C12']
+                color:['#00A65A', '#c23632', '#F39C12']
         };
 
         var lineChart = echarts.init(document.getElementById('lineChart'));
@@ -147,38 +151,42 @@ $(function () {
     }
 
     /**
-     * 饼图
+     * pie Chart Init
      */
     function pieChartInit(data) {
         var option = {
             title : {
-                text: '成功比例图',
+                text: I18n.job_dashboard_rate_report ,
                 /*subtext: 'subtext',*/
                 x:'center'
             },
             tooltip : {
                 trigger: 'item',
-                formatter: "{a} <br/>{b} : {c} ({d}%)"
+                formatter: "{b} : {c} ({d}%)"
             },
             legend: {
                 orient: 'vertical',
                 left: 'left',
-                data: ['成功调度次数','失败调度次数']
+                data: [I18n.joblog_status_suc, I18n.joblog_status_fail, I18n.joblog_status_running ]
             },
             series : [
                 {
-                    name: '分布比例',
+                    //name: '分布比例',
                     type: 'pie',
                     radius : '55%',
                     center: ['50%', '60%'],
                     data:[
                         {
-                            value:data.content.triggerCountSucTotal,
-                            name:'成功调度次数'
+                            name:I18n.joblog_status_suc,
+                            value:data.content.triggerCountSucTotal
+                        },
+                        {
+                            name:I18n.joblog_status_fail,
+                            value:data.content.triggerCountFailTotal
                         },
                         {
-                            value:data.content.triggerCountFailTotal,
-                            name:'失败调度次数'
+                            name:I18n.joblog_status_running,
+                            value:data.content.triggerCountRunningTotal
                         }
                     ],
                     itemStyle: {
@@ -190,7 +198,7 @@ $(function () {
                     }
                 }
             ],
-            color:['#00A65A', '#F39C12']
+            color:['#00A65A', '#c23632', '#F39C12']
         };
         var pieChart = echarts.init(document.getElementById('pieChart'));
         pieChart.setOption(option);

+ 12 - 14
xxl-job-admin/src/main/webapp/static/js/jobcode.index.1.js

@@ -1,12 +1,6 @@
 $(function() {
 
 	// init code editor
-	/*var codeEditor = CodeMirror.fromTextArea(document.getElementById("glueSource"), {
-		mode : "text/x-java",
-		lineNumbers : true,
-		matchBrackets : true
-	});*/
-
 	var codeEditor;
 	function initIde(glueSource) {
 		if (codeEditor == null) {
@@ -44,16 +38,18 @@ $(function() {
 		
 		if (!glueRemark) {
 			layer.open({
-				title: '系统提示',
-				content: '请输入备注',
+				title: I18n.system_tips,
+                btn: [ I18n.system_ok],
+				content: I18n.system_please_input + I18n.jobinfo_glue_remark ,
 				icon: '2'
 			});
 			return;
 		}
 		if (glueRemark.length <4 || glueRemark.length > 100) {
 			layer.open({
-				title: '系统提示',
-				content: '备注长度应该在4至100之间',
+				title: I18n.system_tips ,
+                btn: [ I18n.system_ok ],
+				content: I18n.jobinfo_glue_remark_limit ,
 				icon: '2'
 			});
 			return;
@@ -71,8 +67,9 @@ $(function() {
 			success : function(data){
 				if (data.code == 200) {
 					layer.open({
-						title: '系统提示',
-						content: '保存成功',
+						title: I18n.system_tips,
+                        btn: [ I18n.system_ok ],
+						content: (I18n.system_save + I18n.system_success) ,
 						icon: '1',
 						end: function(layero, index){
 							//$(window).unbind('beforeunload');
@@ -81,8 +78,9 @@ $(function() {
 					});
 				} else {
 					layer.open({
-						title: '系统提示',
-						content: (data.msg || "保存失败"),
+						title: I18n.system_tips,
+                        btn: [ I18n.system_ok ],
+						content: (data.msg || (I18n.system_save + I18n.system_fail) ),
 						icon: '2'
 					});
 				}

+ 48 - 38
xxl-job-admin/src/main/webapp/static/js/jobgroup.index.1.js

@@ -4,7 +4,11 @@ $(function() {
 	$('.remove').on('click', function(){
 		var id = $(this).attr('id');
 
-		layer.confirm('确认删除分组?', {icon: 3, title:'系统提示'}, function(index){
+		layer.confirm( (I18n.system_ok + I18n.jobgroup_del + '?') , {
+			icon: 3,
+			title: I18n.system_tips ,
+            btn: [ I18n.system_ok, I18n.system_cancel ]
+		}, function(index){
 			layer.close(index);
 
 			$.ajax({
@@ -15,8 +19,9 @@ $(function() {
 				success : function(data){
 					if (data.code == 200) {
 						layer.open({
-							title: '系统提示',
-							content: '删除成功',
+							title: I18n.system_tips ,
+                            btn: [ I18n.system_ok ],
+							content: (I18n.jobgroup_del + I18n.system_success),
 							icon: '1',
 							end: function(layero, index){
 								window.location.reload();
@@ -24,8 +29,9 @@ $(function() {
 						});
 					} else {
 						layer.open({
-							title: '系统提示',
-							content: (data.msg || "删除失败"),
+							title: I18n.system_tips,
+                            btn: [ I18n.system_ok ],
+							content: (data.msg || (I18n.jobgroup_del + I18n.system_fail)),
 							icon: '2'
 						});
 					}
@@ -35,12 +41,12 @@ $(function() {
 
 	});
 
-	// jquery.validate 自定义校验 “英文字母开头,只含有英文字母、数字和下划线
+	// jquery.validate “low letters start, limit contants、 letters、numbers and line-through.
 	jQuery.validator.addMethod("myValid01", function(value, element) {
 		var length = value.length;
 		var valid = /^[a-z][a-zA-Z0-9-]*$/;
 		return this.optional(element) || valid.test(value);
-	}, "限制以小写字母开头,由小写字母、数字和下划线组成");
+	}, I18n.jobgroup_field_appName_limit );
 
 	$('.add').on('click', function(){
 		$('#addModal').modal({backdrop: false, keyboard: false}).modal('show');
@@ -67,18 +73,18 @@ $(function() {
 		},
 		messages : {
 			appName : {
-				required :"请输入“AppName",
-				rangelength:"AppName长度限制为4~64",
-				myValid01: "限制以小写字母开头,由小写字母、数字和中划线组成"
+				required : I18n.system_please_input+"AppName",
+				rangelength: I18n.jobgroup_field_appName_length ,
+				myValid01: I18n.jobgroup_field_appName_limit
 			},
 			title : {
-				required :"请输入“执行器名称”",
-				rangelength:"长度限制为4~12"
+				required : I18n.system_please_input + I18n.jobgroup_field_title ,
+				rangelength: I18n.jobgroup_field_title_length
 			},
 			order : {
-				required :"请输入“排序”",
-				digits: "请输入整数",
-				range: "取值范围为1~1000"
+				required : I18n.system_please_input + I18n.jobgroup_field_order ,
+				digits: I18n.jobgroup_field_order_digits ,
+				range: I18n.jobgroup_field_orderrange
 			}
 		},
 		highlight : function(element) {
@@ -96,8 +102,9 @@ $(function() {
 				if (data.code == "200") {
 					$('#addModal').modal('hide');
 					layer.open({
-						title: '系统提示',
-						content: '新增成功',
+						title: I18n.system_tips ,
+                        btn: [ I18n.system_ok ],
+						content: I18n.system_add_suc ,
 						icon: '1',
 						end: function(layero, index){
 							window.location.reload();
@@ -105,8 +112,9 @@ $(function() {
 					});
 				} else {
 					layer.open({
-						title: '系统提示',
-						content: (data.msg || "新增失败"),
+						title: I18n.system_tips,
+                        btn: [ I18n.system_ok ],
+						content: (data.msg || I18n.system_add_fail  ),
 						icon: '2'
 					});
 				}
@@ -119,7 +127,7 @@ $(function() {
 		$("#addModal .form .form-group").removeClass("has-error");
 	});
 
-	// 注册方式,切换
+	// addressType change
 	$("#addModal input[name=addressType], #updateModal input[name=addressType]").click(function(){
 		var addressType = $(this).val();
 		var $addressList = $(this).parents("form").find("textarea[name=addressList]");
@@ -171,20 +179,20 @@ $(function() {
 			}
 		},
 		messages : {
-			appName : {
-				required :"请输入“AppName”",
-				rangelength:"AppName长度限制为4~64",
-				myValid01: "限制以小写字母开头,由小写字母、数字和中划线组成"
-			},
-			title : {
-				required :"请输入“执行器名称”",
-				rangelength:"长度限制为4~12"
-			},
-			order : {
-				required :"请输入“排序”",
-				digits: "请输入整数",
-				range: "取值范围为1~1000"
-			}
+            appName : {
+                required : I18n.system_please_input+"AppName",
+                rangelength: I18n.jobgroup_field_appName_length ,
+                myValid01: I18n.jobgroup_field_appName_limit
+            },
+            title : {
+                required : I18n.system_please_input + I18n.jobgroup_field_title ,
+                rangelength: I18n.jobgroup_field_title_length
+            },
+            order : {
+                required : I18n.system_please_input + I18n.jobgroup_field_order ,
+                digits: I18n.jobgroup_field_order_digits ,
+                range: I18n.jobgroup_field_orderrange
+            }
 		},
 		highlight : function(element) {
 			$(element).closest('.form-group').addClass('has-error');
@@ -202,8 +210,9 @@ $(function() {
 					$('#addModal').modal('hide');
 
 					layer.open({
-						title: '系统提示',
-						content: '更新成功',
+						title: I18n.system_tips ,
+                        btn: [ I18n.system_ok ],
+						content: I18n.system_update_suc ,
 						icon: '1',
 						end: function(layero, index){
 							window.location.reload();
@@ -211,8 +220,9 @@ $(function() {
 					});
 				} else {
 					layer.open({
-						title: '系统提示',
-						content: (data.msg || "更新失败"),
+						title: I18n.system_tips,
+                        btn: [ I18n.system_ok ],
+						content: (data.msg || I18n.system_update_fail  ),
 						icon: '2'
 					});
 				}

+ 105 - 85
xxl-job-admin/src/main/webapp/static/js/jobinfo.index.1.js

@@ -1,4 +1,5 @@
 $(function() {
+
 	// init date tables
 	var jobTable = $("#job_list").dataTable({
 		"deferRender": true,
@@ -10,6 +11,7 @@ $(function() {
 	        data : function ( d ) {
 	        	var obj = {};
 	        	obj.jobGroup = $('#jobGroup').val();
+                obj.jobDesc = $('#jobDesc').val();
 	        	obj.executorHandler = $('#executorHandler').val();
 	        	obj.start = d.start;
 	        	obj.length = d.length;
@@ -18,12 +20,18 @@ $(function() {
 	    },
 	    "searching": false,
 	    "ordering": false,
-	    //"scrollX": true,	// X轴滚动条,取消自适应
+	    //"scrollX": true,	// scroll x,close self-adaption
 	    "columns": [
-	                { "data": 'id', "bSortable": false, "visible" : true},
+	                {
+	                	"data": 'id',
+						"bSortable": false,
+						"visible" : true,
+						"width":'10%'
+					},
 	                { 
 	                	"data": 'jobGroup', 
 	                	"visible" : false,
+						"width":'20%',
 	                	"render": function ( data, type, row ) {
 	            			var groupMenu = $("#jobGroup").find("option");
 	            			for ( var index in $("#jobGroup").find("option")) {
@@ -34,28 +42,30 @@ $(function() {
 	            			return data;
 	            		}
             		},
-	                { "data": 'jobDesc', "visible" : true,"width":'20%'},
+	                {
+	                	"data": 'jobDesc',
+						"visible" : true,
+						"width":'20%'
+					},
 					{
 						"data": 'glueType',
 						"width":'20%',
 						"visible" : true,
 						"render": function ( data, type, row ) {
-							if ('GLUE_GROOVY'==row.glueType) {
-								return "GLUE模式(Java)";
-							} else if ('GLUE_SHELL'==row.glueType) {
-								return "GLUE模式(Shell)";
-							} else if ('GLUE_PYTHON'==row.glueType) {
-								return "GLUE模式(Python)";
-							}else if  ('GLUE_NODEJS'==row.glueType){
-								return "GLUE模式(Nodejs)";
-							} else if ('BEAN'==row.glueType) {
-								return "BEAN模式:" + row.executorHandler;
-							}
-							return row.executorHandler;
+							var glueTypeTitle = findGlueTypeTitle(row.glueType);
+                            if (row.executorHandler) {
+                                return glueTypeTitle +":" + row.executorHandler;
+                            } else {
+                                return glueTypeTitle;
+                            }
 						}
 					},
 	                { "data": 'executorParam', "visible" : false},
-					{ "data": 'jobCron', "visible" : true,"width":'10%'},
+					{
+						"data": 'jobCron',
+						"visible" : true,
+						"width":'10%'
+					},
 	                { 
 	                	"data": 'addTime', 
 	                	"visible" : false, 
@@ -72,7 +82,6 @@ $(function() {
 	                },
 	                { "data": 'author', "visible" : true, "width":'10%'},
 	                { "data": 'alarmEmail', "visible" : false},
-	                { "data": 'glueType', "visible" : false},
 	                { 
 	                	"data": 'jobStatus',
 						"width":'10%',
@@ -81,24 +90,24 @@ $(function() {
 	                		if ('NORMAL' == data) {
 	                			return '<small class="label label-success" ><i class="fa fa-clock-o"></i>'+ data +'</small>'; 
 							} else if ('PAUSED' == data){
-								return '<small class="label label-default" title="暂停" ><i class="fa fa-clock-o"></i>'+ data +'</small>'; 
+								return '<small class="label label-default" ><i class="fa fa-clock-o"></i>'+ data +'</small>';
 							} else if ('BLOCKED' == data){
-								return '<small class="label label-default" title="阻塞[串行]" ><i class="fa fa-clock-o"></i>'+ data +'</small>'; 
+								return '<small class="label label-default" ><i class="fa fa-clock-o"></i>'+ data +'</small>';
 							}
 	                		return data;
 	                	}
 	                },
 	                {
-						"data": '操作' ,
+						"data": I18n.system_opt ,
 						"width":'15%',
 	                	"render": function ( data, type, row ) {
 	                		return function(){
 	                			// status
 	                			var pause_resume = "";
 	                			if ('NORMAL' == row.jobStatus) {
-	                				pause_resume = '<button class="btn btn-primary btn-xs job_operate" _type="job_pause" type="button">暂停</button>  ';
+	                				pause_resume = '<button class="btn btn-primary btn-xs job_operate" _type="job_pause" type="button">'+ I18n.jobinfo_opt_pause +'</button>  ';
 								} else if ('PAUSED' == row.jobStatus){
-									pause_resume = '<button class="btn btn-primary btn-xs job_operate" _type="job_resume" type="button">恢复</button>  ';
+									pause_resume = '<button class="btn btn-primary btn-xs job_operate" _type="job_resume" type="button">'+ I18n.jobinfo_opt_resume +'</button>  ';
 								}
 	                			// log url
 	                			var logUrl = base_url +'/joblog?jobId='+ row.id;
@@ -113,12 +122,12 @@ $(function() {
 								// html
                                 tableData['key'+row.id] = row;
 								var html = '<p id="'+ row.id +'" >'+
-									'<button class="btn btn-primary btn-xs job_operate" _type="job_trigger" type="button">执行</button>  '+
+									'<button class="btn btn-primary btn-xs job_operate" _type="job_trigger" type="button">'+ I18n.jobinfo_opt_run +'</button>  '+
 									pause_resume +
-									'<button class="btn btn-primary btn-xs" type="job_del" type="button" onclick="javascript:window.open(\'' + logUrl + '\')" >日志</button><br>  '+
-									'<button class="btn btn-warning btn-xs update" type="button">编辑</button>  '+
+									'<button class="btn btn-primary btn-xs" type="job_del" type="button" onclick="javascript:window.open(\'' + logUrl + '\')" >'+ I18n.jobinfo_opt_log +'</button><br>  '+
+									'<button class="btn btn-warning btn-xs update" type="button">'+ I18n.system_opt_edit +'</button>  '+
 									codeBtn +
-									'<button class="btn btn-danger btn-xs job_operate" _type="job_del" type="button">删除</button>  '+
+									'<button class="btn btn-danger btn-xs job_operate" _type="job_del" type="button">'+ I18n.system_opt_del +'</button>  '+
 									'</p>';
 
 	                			return html;
@@ -127,27 +136,27 @@ $(function() {
 	                }
 	            ],
 		"language" : {
-			"sProcessing" : "处理中...",
-			"sLengthMenu" : "每页 _MENU_ 条记录",
-			"sZeroRecords" : "没有匹配结果",
-			"sInfo" : "第 _PAGE_ 页 ( 总共 _PAGES_ 页,_TOTAL_ 条记录 )",
-			"sInfoEmpty" : "无记录",
-			"sInfoFiltered" : "(由 _MAX_ 项结果过滤)",
+			"sProcessing" : I18n.dataTable_sProcessing ,
+			"sLengthMenu" : I18n.dataTable_sLengthMenu ,
+			"sZeroRecords" : I18n.dataTable_sZeroRecords ,
+			"sInfo" : I18n.dataTable_sInfo ,
+			"sInfoEmpty" : I18n.dataTable_sInfoEmpty ,
+			"sInfoFiltered" : I18n.dataTable_sInfoFiltered ,
 			"sInfoPostFix" : "",
-			"sSearch" : "搜索:",
+			"sSearch" : I18n.dataTable_sSearch ,
 			"sUrl" : "",
-			"sEmptyTable" : "表中数据为空",
-			"sLoadingRecords" : "载入中...",
+			"sEmptyTable" : I18n.dataTable_sEmptyTable ,
+			"sLoadingRecords" : I18n.dataTable_sLoadingRecords ,
 			"sInfoThousands" : ",",
 			"oPaginate" : {
-				"sFirst" : "首页",
-				"sPrevious" : "上页",
-				"sNext" : "下页",
-				"sLast" : "末页"
+				"sFirst" : I18n.dataTable_sFirst ,
+				"sPrevious" : I18n.dataTable_sPrevious ,
+				"sNext" : I18n.dataTable_sNext ,
+				"sLast" : I18n.dataTable_sLast
 			},
 			"oAria" : {
-				"sSortAscending" : ": 以升序排列此列",
-				"sSortDescending" : ": 以降序排列此列"
+				"sSortAscending" : I18n.dataTable_sSortAscending ,
+				"sSortDescending" : I18n.dataTable_sSortDescending
 			}
 		}
 	});
@@ -155,7 +164,7 @@ $(function() {
     // table data
     var tableData = {};
 
-	// 搜索按钮
+	// search btn
 	$('#searchBtn').on('click', function(){
 		jobTable.fnDraw();
 	});
@@ -175,19 +184,19 @@ $(function() {
 
 		var type = $(this).attr("_type");
 		if ("job_pause" == type) {
-			typeName = "暂停";
+			typeName = I18n.jobinfo_opt_pause ;
 			url = base_url + "/jobinfo/pause";
 			needFresh = true;
 		} else if ("job_resume" == type) {
-			typeName = "恢复";
+			typeName = I18n.jobinfo_opt_resume ;
 			url = base_url + "/jobinfo/resume";
 			needFresh = true;
 		} else if ("job_del" == type) {
-			typeName = "删除";
+			typeName = I18n.system_opt_del ;
 			url = base_url + "/jobinfo/remove";
 			needFresh = true;
 		} else if ("job_trigger" == type) {
-			typeName = "执行";
+			typeName = I18n.jobinfo_opt_run ;
 			url = base_url + "/jobinfo/trigger";
 		} else {
 			return;
@@ -195,7 +204,11 @@ $(function() {
 		
 		var id = $(this).parent('p').attr("id");
 
-		layer.confirm('确认' + typeName + '?', {icon: 3, title:'系统提示'}, function(index){
+		layer.confirm( I18n.system_ok + typeName + '?', {
+			icon: 3,
+			title: I18n.system_tips ,
+            btn: [ I18n.system_ok, I18n.system_cancel ]
+		}, function(index){
 			layer.close(index);
 
 			$.ajax({
@@ -209,8 +222,9 @@ $(function() {
 					if (data.code == 200) {
 
 						layer.open({
-							title: '系统提示',
-							content: typeName + "成功",
+							title: I18n.system_tips,
+                            btn: [ I18n.system_ok ],
+							content: typeName + I18n.system_success ,
 							icon: '1',
 							end: function(layero, index){
 								if (needFresh) {
@@ -221,8 +235,9 @@ $(function() {
 						});
 					} else {
 						layer.open({
-							title: '系统提示',
-							content: (data.msg || typeName + "失败"),
+							title: I18n.system_tips,
+                            btn: [ I18n.system_ok ],
+							content: (data.msg || typeName + I18n.system_fail ),
 							icon: '2'
 						});
 					}
@@ -230,15 +245,8 @@ $(function() {
 			});
 		});
 	});
-	
-	// jquery.validate 自定义校验 “英文字母开头,只含有英文字母、数字和下划线”
-	jQuery.validator.addMethod("myValid01", function(value, element) {
-		var length = value.length;
-		var valid = /^[a-zA-Z][a-zA-Z0-9_]*$/;
-		return this.optional(element) || valid.test(value);
-	}, "只支持英文字母开头,只含有英文字母、数字和下划线");
-	
-	// 新增
+
+	// add
 	$(".add").click(function(){
 		$('#addModal').modal({backdrop: false, keyboard: false}).modal('show');
 	});
@@ -260,13 +268,13 @@ $(function() {
         }, 
         messages : {  
             jobDesc : {
-            	required :"请输入“描述”."
+            	required : I18n.system_please_input + I18n.jobinfo_field_jobdesc
             },
             jobCron : {
-            	required :"请输入“Cron”."
+            	required : I18n.system_please_input + "Cron"
             },
             author : {
-            	required : "请输入“负责人”."
+            	required : I18n.system_please_input + I18n.jobinfo_field_author
             }
         },
 		highlight : function(element) {  
@@ -284,8 +292,9 @@ $(function() {
     			if (data.code == "200") {
 					$('#addModal').modal('hide');
 					layer.open({
-						title: '系统提示',
-						content: '新增任务成功',
+						title: I18n.system_tips ,
+                        btn: [ I18n.system_ok ],
+						content: I18n.system_add_suc ,
 						icon: '1',
 						end: function(layero, index){
 							jobTable.fnDraw();
@@ -294,8 +303,9 @@ $(function() {
 					});
     			} else {
 					layer.open({
-						title: '系统提示',
-						content: (data.msg || "新增失败"),
+						title: I18n.system_tips ,
+                        btn: [ I18n.system_ok ],
+						content: (data.msg || I18n.system_add_fail),
 						icon: '2'
 					});
     			}
@@ -312,7 +322,7 @@ $(function() {
 	});
 
 
-    // 运行模式
+    // glueType change
     $(".glueType").change(function(){
 		// executorHandler
         var $executorHandler = $(this).parents("form").find("input[name='executorHandler']");
@@ -339,19 +349,11 @@ $(function() {
 		}
 	});
 
-	// 更新
+	// update
 	$("#job_list").on('click', '.update',function() {
 
         var id = $(this).parent('p').attr("id");
         var row = tableData['key'+id];
-        if (!row) {
-            layer.open({
-                title: '系统提示',
-                content: ("任务信息加载失败,请刷新页面"),
-                icon: '2'
-            });
-            return;
-        }
 
 		// base data
 		$("#updateModal .form input[name='id']").val( row.id );
@@ -392,13 +394,13 @@ $(function() {
 		},
 		messages : {
 			jobDesc : {
-				required :"请输入“描述”."
+                required : I18n.system_please_input + I18n.jobinfo_field_jobdesc
 			},
 			jobCron : {
-				required :"请输入“Cron”."
+				required : I18n.system_please_input + "Cron"
 			},
 			author : {
-				required : "请输入“负责人”."
+				required : I18n.system_please_input + I18n.jobinfo_field_author
 			}
 		},
 		highlight : function(element) {
@@ -413,12 +415,13 @@ $(function() {
         },
         submitHandler : function(form) {
 			// post
-    		$.post(base_url + "/jobinfo/reschedule", $("#updateModal .form").serialize(), function(data, status) {
+    		$.post(base_url + "/jobinfo/update", $("#updateModal .form").serialize(), function(data, status) {
     			if (data.code == "200") {
 					$('#updateModal').modal('hide');
 					layer.open({
-						title: '系统提示',
-						content: '更新成功',
+						title: I18n.system_tips ,
+                        btn: [ I18n.system_ok ],
+						content: I18n.system_update_suc ,
 						icon: '1',
 						end: function(layero, index){
 							//window.location.reload();
@@ -427,8 +430,9 @@ $(function() {
 					});
     			} else {
 					layer.open({
-						title: '系统提示',
-						content: (data.msg || "更新失败"),
+						title: I18n.system_tips ,
+                        btn: [ I18n.system_ok ],
+						content: (data.msg || I18n.system_update_fail ),
 						icon: '2'
 					});
     			}
@@ -439,4 +443,20 @@ $(function() {
 		$("#updateModal .form")[0].reset()
 	});
 
+    /**
+	 * find title by name, GlueType
+     */
+	function findGlueTypeTitle(glueType) {
+		var glueTypeTitle;
+        $("#addModal .form select[name=glueType] option").each(function () {
+            var name = $(this).val();
+            var title = $(this).text();
+            if (glueType == name) {
+                glueTypeTitle = title;
+                return false
+            }
+        });
+        return glueTypeTitle;
+    }
+
 });

+ 2 - 2
xxl-job-admin/src/main/webapp/static/js/joblog.detail.1.js

@@ -3,7 +3,7 @@ $(function() {
     // trigger fail, end
     if (triggerCode != 200) {
         $('#logConsoleRunning').hide();
-        $('#logConsole').append('<span style="color: red;">任务发起调度失败,无法查看执行日志</span>');
+        $('#logConsole').append('<span style="color: red;">'+ I18n.joblog_rolling_log_triggerfail +'</span>');
         return;
     }
 
@@ -13,7 +13,7 @@ $(function() {
     function pullLog() {
         // pullFailCount, max=20
         if (pullFailCount++ > 20) {
-            logRunStop('<span style="color: red;">终止请求Rolling日志,请求失败次数超上限,可刷新页面重新加载日志</span>');
+            logRunStop('<span style="color: red;">'+ I18n.joblog_rolling_log_failoften +'</span>');
             return;
         }
 

+ 102 - 91
xxl-job-admin/src/main/webapp/static/js/joblog.index.1.js

@@ -1,6 +1,6 @@
 $(function() {
 
-	// 任务组列表选中, 任务列表初始化和选中
+	// jobGroup change, job list init and select
 	$("#jobGroup").on("change", function () {
 		var jobGroup = $(this).children('option:selected').val();
 		$.ajax({
@@ -11,7 +11,7 @@ $(function() {
 			dataType : "json",
 			success : function(data){
 				if (data.code == 200) {
-					$("#jobId").html('<option value="0" >全部</option>');
+					$("#jobId").html( '<option value="0" >'+ I18n.system_all +'</option>' );
 					$.each(data.content, function (n, value) {
                         $("#jobId").append('<option value="' + value.id + '" >' + value.jobDesc + '</option>');
                     });
@@ -20,8 +20,9 @@ $(function() {
                     }
 				} else {
 					layer.open({
-						title: '系统提示',
-						content: (data.msg || "接口异常"),
+						title: I18n.system_tips ,
+                        btn: [ I18n.system_ok ],
+						content: (data.msg || I18n.system_api_error ),
 						icon: '2'
 					});
 				}
@@ -33,7 +34,16 @@ $(function() {
         $("#jobGroup").change();
 	}
 
-	// 过滤时间
+	// filter Time
+    var rangesConf = {};
+    rangesConf[I18n.daterangepicker_ranges_recent_hour] = [moment().subtract(1, 'hours'), moment()];
+    rangesConf[I18n.daterangepicker_ranges_today] = [moment().startOf('day'), moment().endOf('day')];
+    rangesConf[I18n.daterangepicker_ranges_yesterday] = [moment().subtract(1, 'days').startOf('day'), moment().subtract(1, 'days').endOf('day')];
+    rangesConf[I18n.daterangepicker_ranges_this_month] = [moment().startOf('month'), moment().endOf('month')];
+    rangesConf[I18n.daterangepicker_ranges_last_month] = [moment().subtract(1, 'months').startOf('month'), moment().subtract(1, 'months').endOf('month')];
+    rangesConf[I18n.daterangepicker_ranges_recent_week] = [moment().subtract(1, 'weeks').startOf('day'), moment().endOf('day')];
+    rangesConf[I18n.daterangepicker_ranges_recent_month] = [moment().subtract(1, 'months').startOf('day'), moment().endOf('day')];
+
 	$('#filterTime').daterangepicker({
         autoApply:false,
         singleDatePicker:false,
@@ -42,29 +52,21 @@ $(function() {
 		timePickerIncrement: 10, 	// 时间的增量,单位为分钟
         timePicker24Hour : true,
         opens : 'left', //日期选择框的弹出位置
-		ranges: {
-			'最近1小时': [moment().subtract(1, 'hours'), moment()],
-			'今日': [moment().startOf('day'), moment().endOf('day')],
-			'昨日': [moment().subtract(1, 'days').startOf('day'), moment().subtract(1, 'days').endOf('day')],
-			'最近7日': [moment().subtract(6, 'days'), moment()],
-			'最近30日': [moment().subtract(29, 'days'), moment()],
-			'本月': [moment().startOf('month'), moment().endOf('month')],
-			'上个月': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
-		},
+		ranges: rangesConf,
         locale : {
             format: 'YYYY-MM-DD HH:mm:ss',
             separator : ' - ',
-        	customRangeLabel : '自定义',
-            applyLabel : '确定',
-            cancelLabel : '取消',
-            fromLabel : '起始时间',
-            toLabel : '结束时间',
-            daysOfWeek : [ '日', '一', '二', '三', '四', '五', '六' ],
-            monthNames : [ '一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月' ],
-            firstDay : 1,
-            startDate: moment().startOf('day'),
-            endDate: moment().endOf('day')
-        }
+            customRangeLabel : I18n.daterangepicker_custom_name ,
+            applyLabel : I18n.system_ok ,
+            cancelLabel : I18n.system_cancel ,
+            fromLabel : I18n.daterangepicker_custom_starttime ,
+            toLabel : I18n.daterangepicker_custom_endtime ,
+            daysOfWeek : I18n.daterangepicker_custom_daysofweek.split(',') ,        // '日', '一', '二', '三', '四', '五', '六'
+            monthNames : I18n.daterangepicker_custom_monthnames.split(',') ,        // '一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'
+            firstDay : 1
+        },
+        startDate: rangesConf[I18n.daterangepicker_ranges_today][0],
+        endDate: rangesConf[I18n.daterangepicker_ranges_today][1]
 	});
 
 	// init date tables
@@ -74,6 +76,7 @@ $(function() {
 	    "serverSide": true,
 		"ajax": {
 	        url: base_url + "/joblog/pageList" ,
+            type:"post",
 	        data : function ( d ) {
 	        	var obj = {};
 	        	obj.jobGroup = $('#jobGroup').val();
@@ -92,24 +95,18 @@ $(function() {
 					{
 						"data": 'jobId',
 						"visible" : true,
+                        "width":'10%',
 						"render": function ( data, type, row ) {
-							var glueTypeTitle = row.glueType;
-							if ('GLUE_GROOVY'==row.glueType) {
-								glueTypeTitle = "GLUE模式(Java)";
-							} else if ('GLUE_SHELL'==row.glueType) {
-								glueTypeTitle = "GLUE模式(Shell)";
-							} else if ('GLUE_PYTHON'==row.glueType) {
-								glueTypeTitle = "GLUE模式(Python)";
-							}else if ('GLUE_NODEJS'==row.glueType) {
-								glueTypeTitle = "GLUE模式(Nodejs)";
-							} else if ('BEAN'==row.glueType) {
-								glueTypeTitle = "BEAN模式:" + row.executorHandler;
-							}
+
+                            var glueTypeTitle = GlueTypeEnum[row.glueType];
+                            if (row.executorHandler) {
+                                glueTypeTitle = glueTypeTitle +":" + row.executorHandler;
+                            }
 
 							var temp = '';
-							temp += '执行器地址:' + (row.executorAddress?row.executorAddress:'');
-							temp += '<br>运行模式:' + glueTypeTitle;
-							temp += '<br>任务参数:' + row.executorParam;
+							temp += I18n.joblog_field_executorAddress + ':' + (row.executorAddress?row.executorAddress:'');
+							temp += '<br>'+ I18n.jobinfo_field_gluetype +':' + glueTypeTitle;
+							temp += '<br>'+ I18n.jobinfo_field_executorparam +':' + row.executorParam;
 
 							return '<a class="logTips" href="javascript:;" >'+ row.jobId +'<span style="display:none;">'+ temp +'</span></a>';
 						}
@@ -117,18 +114,20 @@ $(function() {
 					{ "data": 'jobGroup', "visible" : false},
 					{
 						"data": 'triggerTime',
+                        "width":'16%',
 						"render": function ( data, type, row ) {
 							return data?moment(new Date(data)).format("YYYY-MM-DD HH:mm:ss"):"";
 						}
 					},
 					{
 						"data": 'triggerCode',
+                        "width":'12%',
 						"render": function ( data, type, row ) {
 							var html = data;
 							if (data == 200) {
-								html = '<span style="color: green">成功</span>';
+								html = '<span style="color: green">'+ I18n.system_success +'</span>';
 							} else if (data == 500) {
-								html = '<span style="color: red">失败</span>';
+								html = '<span style="color: red">'+ I18n.system_fail +'</span>';
 							} else if (data == 0) {
                                 html = '';
 							}
@@ -137,26 +136,29 @@ $(function() {
 					},
 					{
 						"data": 'triggerMsg',
+                        "width":'12%',
 						"render": function ( data, type, row ) {
-							return data?'<a class="logTips" href="javascript:;" >查看<span style="display:none;">'+ data +'</span></a>':"无";
+							return data?'<a class="logTips" href="javascript:;" >'+ I18n.system_show +'<span style="display:none;">'+ data +'</span></a>':I18n.system_empty;
 						}
 					},
 	                { 
 	                	"data": 'handleTime',
+                        "width":'16%',
 	                	"render": function ( data, type, row ) {
 	                		return data?moment(new Date(data)).format("YYYY-MM-DD HH:mm:ss"):"";
 	                	}
 	                },
 	                {
 						"data": 'handleCode',
+                        "width":'12%',
 						"render": function ( data, type, row ) {
                             var html = data;
                             if (data == 200) {
-                                html = '<span style="color: green">成功</span>';
+                                html = '<span style="color: green">'+ I18n.joblog_handleCode_200 +'</span>';
                             } else if (data == 500) {
-                                html = '<span style="color: red">失败</span>';
+                                html = '<span style="color: red">'+ I18n.joblog_handleCode_500 +'</span>';
                             } else if (data == 501) {
-                                html = '<span style="color: red">失败重试</span>';
+                                html = '<span style="color: red">'+ I18n.joblog_handleCode_501 +'</span>';
                             } else if (data == 0) {
                                 html = '';
                             }
@@ -165,21 +167,22 @@ $(function() {
 	                },
 	                { 
 	                	"data": 'handleMsg',
+                        "width":'12%',
 	                	"render": function ( data, type, row ) {
-	                		return data?'<a class="logTips" href="javascript:;" >查看<span style="display:none;">'+ data +'</span></a>':"无";
+	                		return data?'<a class="logTips" href="javascript:;" >'+ I18n.system_show +'<span style="display:none;">'+ data +'</span></a>':I18n.system_empty;
 	                	}
 	                },
 	                {
 						"data": 'handleMsg' ,
 						"bSortable": false,
-						"width": "8%" ,
+                        "width":'10%',
 	                	"render": function ( data, type, row ) {
 	                		// better support expression or string, not function
 	                		return function () {
 		                		if (row.triggerCode == 200){
-		                			var temp = '<a href="javascript:;" class="logDetail" _id="'+ row.id +'">执行日志</a>';
+		                			var temp = '<a href="javascript:;" class="logDetail" _id="'+ row.id +'">'+ I18n.joblog_rolling_log +'</a>';
 		                			if(row.handleCode == 0){
-		                				temp += '<br><a href="javascript:;" class="logKill" _id="'+ row.id +'" style="color: red;" >终止任务</a>';
+		                				temp += '<br><a href="javascript:;" class="logKill" _id="'+ row.id +'" style="color: red;" >'+ I18n.joblog_kill_log +'</a>';
 		                			}
 		                			return temp;
 		                		}
@@ -188,44 +191,44 @@ $(function() {
 	                	}
 	                }
 	            ],
-		"language" : {
-			"sProcessing" : "处理中...",
-			"sLengthMenu" : "每页 _MENU_ 条记录",
-			"sZeroRecords" : "没有匹配结果",
-			"sInfo" : "第 _PAGE_ 页 ( 总共 _PAGES_ 页,_TOTAL_ 条记录 )",
-			"sInfoEmpty" : "无记录",
-			"sInfoFiltered" : "(由 _MAX_ 项结果过滤)",
-			"sInfoPostFix" : "",
-			"sSearch" : "搜索:",
-			"sUrl" : "",
-			"sEmptyTable" : "表中数据为空",
-			"sLoadingRecords" : "载入中...",
-			"sInfoThousands" : ",",
-			"oPaginate" : {
-				"sFirst" : "首页",
-				"sPrevious" : "上页",
-				"sNext" : "下页",
-				"sLast" : "末页"
-			},
-			"oAria" : {
-				"sSortAscending" : ": 以升序排列此列",
-				"sSortDescending" : ": 以降序排列此列"
-			}
-		}
+        "language" : {
+            "sProcessing" : I18n.dataTable_sProcessing ,
+            "sLengthMenu" : I18n.dataTable_sLengthMenu ,
+            "sZeroRecords" : I18n.dataTable_sZeroRecords ,
+            "sInfo" : I18n.dataTable_sInfo ,
+            "sInfoEmpty" : I18n.dataTable_sInfoEmpty ,
+            "sInfoFiltered" : I18n.dataTable_sInfoFiltered ,
+            "sInfoPostFix" : "",
+            "sSearch" : I18n.dataTable_sSearch ,
+            "sUrl" : "",
+            "sEmptyTable" : I18n.dataTable_sEmptyTable ,
+            "sLoadingRecords" : I18n.dataTable_sLoadingRecords ,
+            "sInfoThousands" : ",",
+            "oPaginate" : {
+                "sFirst" : I18n.dataTable_sFirst ,
+                "sPrevious" : I18n.dataTable_sPrevious ,
+                "sNext" : I18n.dataTable_sNext ,
+                "sLast" : I18n.dataTable_sLast
+            },
+            "oAria" : {
+                "sSortAscending" : I18n.dataTable_sSortAscending ,
+                "sSortDescending" : I18n.dataTable_sSortDescending
+            }
+        }
 	});
 	
-	// 日志弹框提示
+	// logTips alert
 	$('#joblog_list').on('click', '.logTips', function(){
 		var msg = $(this).find('span').html();
 		ComAlertTec.show(msg);
 	});
 	
-	// 搜索按钮
+	// search Btn
 	$('#searchBtn').on('click', function(){
 		logTable.fnDraw();
 	});
 	
-	// 查看执行器详细执行日志
+	// logDetail look
 	$('#joblog_list').on('click', '.logDetail', function(){
 		var _id = $(this).attr('_id');
 		
@@ -234,12 +237,16 @@ $(function() {
 	});
 
 	/**
-	 * 终止任务
+	 * log Kill
 	 */
 	$('#joblog_list').on('click', '.logKill', function(){
 		var _id = $(this).attr('_id');
 
-        layer.confirm('确认主动终止任务?', {icon: 3, title:'系统提示'}, function(index){
+        layer.confirm( (I18n.system_ok + I18n.joblog_kill_log + '?'), {
+        	icon: 3,
+			title: I18n.system_tips ,
+            btn: [ I18n.system_ok, I18n.system_cancel ]
+		}, function(index){
             layer.close(index);
 
             $.ajax({
@@ -250,8 +257,9 @@ $(function() {
                 success : function(data){
                     if (data.code == 200) {
                         layer.open({
-                            title: '系统提示',
-                            content: '操作成功',
+                            title: I18n.system_tips,
+                            btn: [ I18n.system_ok ],
+                            content: I18n.system_opt_suc ,
                             icon: '1',
                             end: function(layero, index){
                                 logTable.fnDraw();
@@ -259,8 +267,9 @@ $(function() {
                         });
                     } else {
                         layer.open({
-                            title: '系统提示',
-                            content: (data.msg || "操作失败"),
+                            title: I18n.system_tips,
+                            btn: [ I18n.system_ok ],
+                            content: (data.msg || I18n.system_opt_fail ),
                             icon: '2'
                         });
                     }
@@ -271,7 +280,7 @@ $(function() {
 	});
 
 	/**
-	 * 清理任务Log
+	 * clear Log
 	 */
 	$('#clearLog').on('click', function(){
 
@@ -295,8 +304,9 @@ $(function() {
 			if (data.code == "200") {
 				$('#clearLogModal').modal('hide');
 				layer.open({
-					title: '系统提示',
-					content: '日志清理成功',
+					title: I18n.system_tips ,
+                    btn: [ I18n.system_ok ],
+					content: (I18n.joblog_clean_log + I18n.system_success) ,
 					icon: '1',
 					end: function(layero, index){
 						logTable.fnDraw();
@@ -304,8 +314,9 @@ $(function() {
 				});
 			} else {
 				layer.open({
-					title: '系统提示',
-					content: (data.msg || "日志清理失败"),
+					title: I18n.system_tips ,
+                    btn: [ I18n.system_ok ],
+					content: (data.msg || (I18n.joblog_clean_log + I18n.system_fail) ),
 					icon: '2'
 				});
 			}
@@ -318,7 +329,7 @@ $(function() {
 });
 
 
-// 提示-科技主题
+// Com Alert by Tec theme
 var ComAlertTec = {
 	html:function(){
 		var html =
@@ -328,7 +339,7 @@ var ComAlertTec = {
 			'<div class="modal-body"><div class="alert" style="color:#fff;"></div></div>' +
 			'<div class="modal-footer">' +
 			'<div class="text-center" >' +
-			'<button type="button" class="btn btn-info ok" data-dismiss="modal" >确认</button>' +
+			'<button type="button" class="btn btn-info ok" data-dismiss="modal" >'+ I18n.system_ok +'</button>' +
 			'</div>' +
 			'</div>' +
 			'</div>' +
@@ -342,7 +353,7 @@ var ComAlertTec = {
 			$('body').append(ComAlertTec.html());
 		}
 
-		// 弹框初始
+		// init com alert
 		$('#ComAlertTec .alert').html(msg);
 		$('#ComAlertTec').modal('show');
 

+ 13 - 20
xxl-job-admin/src/main/webapp/static/js/login.1.js

@@ -1,12 +1,13 @@
 $(function(){
-	// 复选框
+
+	// input iCheck
     $('input').iCheck({
       checkboxClass: 'icheckbox_square-blue',
       radioClass: 'iradio_square-blue',
       increaseArea: '20%' // optional
     });
     
-	// 登录.规则校验
+	// login Form Valid
 	var loginFormValid = $("#loginForm").validate({
 		errorElement : 'span',  
         errorClass : 'help-block',
@@ -25,14 +26,13 @@ $(function(){
         }, 
         messages : {  
         	userName : {  
-                required :"请输入登录账号."  ,
-                minlength:"登录账号不应低于5位",
-                maxlength:"登录账号不应超过18位"
-            },  
+                required  : I18n.login_username_empty,
+                minlength : I18n.login_username_lt_5
+            },
             password : {
-            	required :"请输入登录密码."  ,
-                minlength:"登录密码不应低于5位",
-                maxlength:"登录密码不应超过18位"
+            	required  : I18n.login_password_empty  ,
+                minlength : I18n.login_password_lt_5
+                /*,maxlength:"登录密码不应超过18位"*/
             }
         }, 
 		highlight : function(element) {  
@@ -48,22 +48,15 @@ $(function(){
         submitHandler : function(form) {
 			$.post(base_url + "/login", $("#loginForm").serialize(), function(data, status) {
 				if (data.code == "200") {
-                    layer.msg('登录成功');
+                    layer.msg( I18n.login_success );
                     setTimeout(function(){
                         window.location.href = base_url;
                     }, 500);
-                    /*layer.open({
-                        title: '系统提示',
-                        content: '登录成功',
-                        icon: '1',
-                        end: function(layero, index){
-                            window.location.href = base_url;
-                        }
-                    });*/
 				} else {
                     layer.open({
-                        title: '系统提示',
-                        content: (data.msg || "登录失败"),
+                        title: I18n.system_tips,
+                        btn: [ I18n.system_ok ],
+                        content: (data.msg || I18n.login_fail ),
                         icon: '2'
                     });
 				}

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 1
xxl-job-admin/src/main/webapp/static/plugins/layer/mobile/layer.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
xxl-job-admin/src/main/webapp/static/plugins/layer/mobile/need/layer.css


+ 3 - 3
xxl-job-admin/src/test/java/com/xxl/job/admin/controller/JobInfoControllerTest.java

@@ -1,7 +1,7 @@
 package com.xxl.job.admin.controller;
 
 import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
-import com.xxl.job.admin.core.util.PropertiesUtil;
+import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
 import org.junit.Before;
 import org.junit.Test;
 import org.springframework.http.MediaType;
@@ -22,8 +22,8 @@ public class JobInfoControllerTest extends AbstractSpringMvcTest {
     MvcResult ret = mockMvc.perform(
         post("/login")
             .contentType(MediaType.APPLICATION_FORM_URLENCODED)
-            .param("userName", PropertiesUtil.getString("xxl.job.login.username"))
-            .param("password", PropertiesUtil.getString("xxl.job.login.password"))
+            .param("userName", XxlJobAdminConfig.getAdminConfig().getLoginUsername())
+            .param("password", XxlJobAdminConfig.getAdminConfig().getLoginPassword())
     ).andReturn();
     cookie = ret.getResponse().getCookie(PermissionInterceptor.LOGIN_IDENTITY_KEY);
   }

+ 2 - 2
xxl-job-admin/src/test/java/com/xxl/job/admin/dao/XxlJobInfoDaoTest.java

@@ -20,8 +20,8 @@ public class XxlJobInfoDaoTest {
 	
 	@Test
 	public void pageList(){
-		List<XxlJobInfo> list = xxlJobInfoDao.pageList(0, 20, 0, null);
-		int list_count = xxlJobInfoDao.pageListCount(0, 20, 0, null);
+		List<XxlJobInfo> list = xxlJobInfoDao.pageList(0, 20, 0, null, null);
+		int list_count = xxlJobInfoDao.pageListCount(0, 20, 0, null, null);
 		
 		System.out.println(list);
 		System.out.println(list_count);

+ 1 - 1
xxl-job-admin/src/test/java/com/xxl/job/admin/dao/XxlJobLogDaoTest.java

@@ -50,7 +50,7 @@ public class XxlJobLogDaoTest {
         dto = xxlJobLogDao.load(log.getId());
 
 
-        List<Map<String, Object>> list2 = xxlJobLogDao.triggerCountByDay(DateUtils.addDays(new Date(), 30), new Date(), 200);
+        List<Map<String, Object>> list2 = xxlJobLogDao.triggerCountByDay(DateUtils.addDays(new Date(), 30), new Date());
 
         int ret4 = xxlJobLogDao.clearLog(1, 1, new Date(), 100);
 

+ 25 - 0
xxl-job-admin/src/test/java/com/xxl/job/admin/util/I18nUtilTest.java

@@ -0,0 +1,25 @@
+package com.xxl.job.admin.util;
+
+import com.xxl.job.admin.core.util.I18nUtil;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+/**
+ * email util test
+ *
+ * @author xuxueli 2017-12-22 17:16:23
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = "classpath*:spring/applicationcontext-*.xml")
+public class I18nUtilTest {
+
+    @Test
+    public void test(){
+        System.out.println(I18nUtil.getString("admin_name"));
+        System.out.println(I18nUtil.getMultString("admin_name", "admin_name_full"));
+        System.out.println(I18nUtil.getMultString());
+    }
+
+}

+ 5 - 0
xxl-job-admin/src/test/java/com/xxl/job/admin/util/MailUtilTest.java

@@ -2,6 +2,9 @@ package com.xxl.job.admin.util;
 
 import com.xxl.job.admin.core.util.MailUtil;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 
 import java.text.MessageFormat;
 
@@ -10,6 +13,8 @@ import java.text.MessageFormat;
  *
  * @author xuxueli 2017-12-22 17:16:23
  */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = "classpath*:spring/applicationcontext-*.xml")
 public class MailUtilTest {
 
     @Test

+ 0 - 18
xxl-job-admin/src/test/java/com/xxl/job/admin/util/PropertiesUtilTest.java

@@ -1,18 +0,0 @@
-package com.xxl.job.admin.util;
-
-import com.xxl.job.admin.core.util.PropertiesUtil;
-import org.junit.Test;
-
-/**
- * prop util test
- *
- * @author xuxueli 2017-12-25 15:17:36
- */
-public class PropertiesUtilTest {
-
-    @Test
-    public void registryTest() throws Exception {
-        System.out.println(PropertiesUtil.getString("xxl.job.login.username"));
-    }
-
-}

+ 1 - 1
xxl-job-core/pom.xml

@@ -4,7 +4,7 @@
 	<parent>
 		<groupId>com.xuxueli</groupId>
 		<artifactId>xxl-job</artifactId>
-		<version>1.9.0-SNAPSHOT</version>
+		<version>1.9.2-SNAPSHOT</version>
 	</parent>
 	<artifactId>xxl-job-core</artifactId>
 	<packaging>jar</packaging>

+ 19 - 9
xxl-job-core/src/main/java/com/xxl/job/core/executor/XxlJobExecutor.java

@@ -8,6 +8,7 @@ import com.xxl.job.core.handler.annotation.JobHandler;
 import com.xxl.job.core.log.XxlJobFileAppender;
 import com.xxl.job.core.rpc.netcom.NetComClientProxy;
 import com.xxl.job.core.rpc.netcom.NetComServerFactory;
+import com.xxl.job.core.thread.JobLogFileCleanThread;
 import com.xxl.job.core.thread.JobThread;
 import com.xxl.job.core.util.NetUtil;
 import org.slf4j.Logger;
@@ -28,32 +29,35 @@ public class XxlJobExecutor implements ApplicationContextAware {
     private static final Logger logger = LoggerFactory.getLogger(XxlJobExecutor.class);
 
     // ---------------------- param ----------------------
+    private String adminAddresses;
+    private String appName;
     private String ip;
     private int port;
-    private String appName;
-    private String adminAddresses;
     private String accessToken;
     private String logPath;
+    private int logRetentionDays;
 
+    public void setAdminAddresses(String adminAddresses) {
+        this.adminAddresses = adminAddresses;
+    }
+    public void setAppName(String appName) {
+        this.appName = appName;
+    }
     public void setIp(String ip) {
         this.ip = ip;
     }
     public void setPort(int port) {
         this.port = port;
     }
-    public void setAppName(String appName) {
-        this.appName = appName;
-    }
-    public void setAdminAddresses(String adminAddresses) {
-        this.adminAddresses = adminAddresses;
-    }
     public void setAccessToken(String accessToken) {
         this.accessToken = accessToken;
     }
     public void setLogPath(String logPath) {
         this.logPath = logPath;
     }
-
+    public void setLogRetentionDays(int logRetentionDays) {
+        this.logRetentionDays = logRetentionDays;
+    }
 
     // ---------------------- applicationContext ----------------------
     private static ApplicationContext applicationContext;
@@ -79,6 +83,9 @@ public class XxlJobExecutor implements ApplicationContextAware {
 
         // init executor-server
         initExecutorServer(port, ip, appName, accessToken);
+
+        // init JobLogFileCleanThread
+        JobLogFileCleanThread.getInstance().start(logRetentionDays);
     }
     public void destroy(){
         // destory JobThreadRepository
@@ -91,6 +98,9 @@ public class XxlJobExecutor implements ApplicationContextAware {
 
         // destory executor-server
         stopExecutorServer();
+
+        // destory JobLogFileCleanThread
+        JobLogFileCleanThread.getInstance().toStop();
     }
 
 

+ 2 - 2
xxl-job-core/src/main/java/com/xxl/job/core/handler/impl/ScriptJobHandler.java

@@ -40,8 +40,8 @@ public class ScriptJobHandler extends IJobHandler {
         String cmd = glueType.getCmd();
 
         // make script file
-        String scriptFileName = XxlJobFileAppender.getLogPath()
-                .concat("/gluesource/")
+        String scriptFileName = XxlJobFileAppender.getGlueSrcPath()
+                .concat("/")
                 .concat(String.valueOf(jobId))
                 .concat("_")
                 .concat(String.valueOf(glueUpdatetime))

+ 18 - 2
xxl-job-core/src/main/java/com/xxl/job/core/log/XxlJobFileAppender.java

@@ -20,8 +20,21 @@ public class XxlJobFileAppender {
 	public static final InheritableThreadLocal<String> contextHolder = new InheritableThreadLocal<String>();
 
 
-	// log base path
+	/**
+	 * log base path
+	 *
+	 * strut like:
+	 * 	---/
+	 * 	---/gluesource/
+	 * 	---/gluesource/10_1514171108000.js
+	 * 	---/gluesource/10_1514171108000.js
+	 * 	---/2017-12-25/
+	 * 	---/2017-12-25/639.log
+	 * 	---/2017-12-25/821.log
+	 *
+	 */
 	private static String logBasePath = "/data/applogs/xxl-job/jobhandler";
+	private static String glueSrcPath = logBasePath.concat("/gluesource");
 	public static void initLogPath(String logPath){
 		// init
 		if (logPath!=null && logPath.trim().length()>0) {
@@ -39,11 +52,14 @@ public class XxlJobFileAppender {
 		if (!glueBaseDir.exists()) {
 			glueBaseDir.mkdirs();
 		}
+		glueSrcPath = glueBaseDir.getPath();
 	}
 	public static String getLogPath() {
 		return logBasePath;
 	}
-
+	public static String getGlueSrcPath() {
+		return glueSrcPath;
+	}
 
 	/**
 	 * log filename, like "logPath/yyyy-MM-dd/9999.log"

+ 118 - 0
xxl-job-core/src/main/java/com/xxl/job/core/thread/JobLogFileCleanThread.java

@@ -0,0 +1,118 @@
+package com.xxl.job.core.thread;
+
+import com.xxl.job.core.log.XxlJobFileAppender;
+import com.xxl.job.core.util.FileUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * job file clean thread
+ *
+ * @author xuxueli 2017-12-29 16:23:43
+ */
+public class JobLogFileCleanThread extends Thread {
+    private static Logger logger = LoggerFactory.getLogger(JobLogFileCleanThread.class);
+
+    private static JobLogFileCleanThread instance = new JobLogFileCleanThread();
+    public static JobLogFileCleanThread getInstance(){
+        return instance;
+    }
+
+    private Thread localThread;
+    private volatile boolean toStop = false;
+    public void start(final long logRetentionDays){
+
+        // limit min value
+        if (logRetentionDays < 3 ) {
+            return;
+        }
+
+        localThread = new Thread(new Runnable() {
+            @Override
+            public void run() {
+                while (!toStop) {
+                    try {
+                        // clean log dir, over logRetentionDays
+                        File[] childDirs = new File(XxlJobFileAppender.getLogPath()).listFiles();
+                        if (childDirs!=null && childDirs.length>0) {
+
+                            // today
+                            Calendar todayCal = Calendar.getInstance();
+                            todayCal.set(Calendar.HOUR_OF_DAY,0);
+                            todayCal.set(Calendar.MINUTE,0);
+                            todayCal.set(Calendar.SECOND,0);
+                            todayCal.set(Calendar.MILLISECOND,0);
+
+                            Date todayDate = todayCal.getTime();
+
+                            for (File childFile: childDirs) {
+
+                                // valid
+                                if (!childFile.isDirectory()) {
+                                    continue;
+                                }
+                                if (childFile.getName().indexOf("-") == -1) {
+                                    continue;
+                                }
+
+                                // file create date
+                                Date logFileCreateDate = null;
+                                try {
+                                    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
+                                    logFileCreateDate = simpleDateFormat.parse(childFile.getName());
+                                } catch (ParseException e) {
+                                    logger.error(e.getMessage(), e);
+                                }
+                                if (logFileCreateDate == null) {
+                                    continue;
+                                }
+
+                                if ((todayDate.getTime()-logFileCreateDate.getTime()) >= logRetentionDays * (24 * 60 * 60 * 1000) ) {
+                                    FileUtil.deleteRecursively(childFile);
+                                }
+
+                            }
+                        }
+
+                    } catch (Exception e) {
+                        logger.error(e.getMessage(), e);
+                    }
+
+                    try {
+                        TimeUnit.DAYS.sleep(1);
+                    } catch (InterruptedException e) {
+                        logger.error(e.getMessage(), e);
+                    }
+                }
+                logger.info(">>>>>>>>>>> xxl-job, executor JobLogFileCleanThread thread destory.");
+
+            }
+        });
+        localThread.setDaemon(true);
+        localThread.start();
+    }
+
+    public void toStop() {
+        toStop = true;
+
+        if (localThread == null) {
+            return;
+        }
+
+        // interrupt and wait
+        localThread.interrupt();
+        try {
+            localThread.join();
+        } catch (InterruptedException e) {
+            logger.error(e.getMessage(), e);
+        }
+    }
+
+}

+ 27 - 0
xxl-job-core/src/main/java/com/xxl/job/core/util/FileUtil.java

@@ -0,0 +1,27 @@
+package com.xxl.job.core.util;
+
+import java.io.File;
+
+/**
+ * file tool
+ *
+ * @author xuxueli 2017-12-29 17:56:48
+ */
+public class FileUtil {
+
+    public static boolean deleteRecursively(File root) {
+        if (root != null && root.exists()) {
+            if (root.isDirectory()) {
+                File[] children = root.listFiles();
+                if (children != null) {
+                    for (File child : children) {
+                        deleteRecursively(child);
+                    }
+                }
+            }
+            return root.delete();
+        }
+        return false;
+    }
+
+}

+ 15 - 14
xxl-job-core/src/main/java/com/xxl/job/core/util/ScriptUtil.java

@@ -59,22 +59,23 @@ public class ScriptUtil {
         // 标准输出:print (null if watchdog timeout)
         // 错误输出:logging + 异常 (still exists if watchdog timeout)
         // 标准输入
-        FileOutputStream fileOutputStream = new FileOutputStream(logFile, true);
-        PumpStreamHandler streamHandler = new PumpStreamHandler(fileOutputStream, fileOutputStream, null);
+        try (FileOutputStream fileOutputStream = new FileOutputStream(logFile, true)) {
+            PumpStreamHandler streamHandler = new PumpStreamHandler(fileOutputStream, fileOutputStream, null);
 
-        // command
-        CommandLine commandline = new CommandLine(command);
-        commandline.addArgument(scriptFile);
-        if (params!=null && params.length>0) {
-            commandline.addArguments(params);
-        }
+            // command
+            CommandLine commandline = new CommandLine(command);
+            commandline.addArgument(scriptFile);
+            if (params!=null && params.length>0) {
+                commandline.addArguments(params);
+            }
 
-        // exec
-        DefaultExecutor exec = new DefaultExecutor();
-        exec.setExitValues(null);
-        exec.setStreamHandler(streamHandler);
-        int exitValue = exec.execute(commandline);  // exit code: 0=success, 1=error
-        return exitValue;
+            // exec
+            DefaultExecutor exec = new DefaultExecutor();
+            exec.setExitValues(null);
+            exec.setStreamHandler(streamHandler);
+            int exitValue = exec.execute(commandline);  // exit code: 0=success, 1=error
+            return exitValue;
+        }
     }
 
 }

+ 1 - 1
xxl-job-executor-samples/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <groupId>com.xuxueli</groupId>
         <artifactId>xxl-job</artifactId>
-        <version>1.9.0-SNAPSHOT</version>
+        <version>1.9.2-SNAPSHOT</version>
     </parent>
     <artifactId>xxl-job-executor-samples</artifactId>
     <packaging>pom</packaging>

+ 1 - 1
xxl-job-executor-samples/xxl-job-executor-sample-jfinal/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>xxl-job-executor-samples</artifactId>
         <groupId>com.xuxueli</groupId>
-        <version>1.9.0-SNAPSHOT</version>
+        <version>1.9.2-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>xxl-job-executor-sample-jfinal</artifactId>

+ 4 - 3
xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/java/com/xuxueli/executor/sample/jfinal/config/JFinalCoreConfig.java

@@ -29,12 +29,13 @@ public class JFinalCoreConfig extends JFinalConfig {
 
 		// init executor
 		xxlJobExecutor = new XxlJobExecutor();
+		xxlJobExecutor.setAdminAddresses(xxlJobProp.get("xxl.job.admin.addresses"));
+		xxlJobExecutor.setAppName(xxlJobProp.get("xxl.job.executor.appname"));
 		xxlJobExecutor.setIp(xxlJobProp.get("xxl.job.executor.ip"));
 		xxlJobExecutor.setPort(xxlJobProp.getInt("xxl.job.executor.port"));
-		xxlJobExecutor.setAppName(xxlJobProp.get("xxl.job.executor.appname"));
-		xxlJobExecutor.setAdminAddresses(xxlJobProp.get("xxl.job.admin.addresses"));
-		xxlJobExecutor.setLogPath(xxlJobProp.get("xxl.job.executor.logpath"));
 		xxlJobExecutor.setAccessToken(xxlJobProp.get("xxl.job.accessToken"));
+		xxlJobExecutor.setLogPath(xxlJobProp.get("xxl.job.executor.logpath"));
+		xxlJobExecutor.setLogRetentionDays(xxlJobProp.getInt("xxl.job.executor.logretentiondays"));
 
 		// start executor
 		try {

+ 5 - 3
xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/resources/xxl-job-executor.properties

@@ -6,8 +6,10 @@ xxl.job.executor.appname=xxl-job-executor-sample
 xxl.job.executor.ip=
 xxl.job.executor.port=9996
 
+### xxl-job, access token
+xxl.job.accessToken=
+
 ### xxl-job log path
 xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
-
-### xxl-job, access token
-xxl.job.accessToken=
+### xxl-job log retention days
+xxl.job.executor.logretentiondays=-1

+ 1 - 1
xxl-job-executor-samples/xxl-job-executor-sample-nutz/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <groupId>com.xuxueli</groupId>
         <artifactId>xxl-job-executor-samples</artifactId>
-        <version>1.9.0-SNAPSHOT</version>
+        <version>1.9.2-SNAPSHOT</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>xxl-job-executor-sample-nutz</artifactId>

+ 4 - 3
xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/java/com/xuxueli/executor/sample/nutz/config/NutzSetup.java

@@ -39,12 +39,13 @@ public class NutzSetup implements Setup {
 
 		// init executor
 		xxlJobExecutor = new XxlJobExecutor();
+		xxlJobExecutor.setAdminAddresses(xxlJobProp.get("xxl.job.admin.addresses"));
+		xxlJobExecutor.setAppName(xxlJobProp.get("xxl.job.executor.appname"));
 		xxlJobExecutor.setIp(xxlJobProp.get("xxl.job.executor.ip"));
 		xxlJobExecutor.setPort(xxlJobProp.getInt("xxl.job.executor.port"));
-		xxlJobExecutor.setAppName(xxlJobProp.get("xxl.job.executor.appname"));
-		xxlJobExecutor.setAdminAddresses(xxlJobProp.get("xxl.job.admin.addresses"));
-		xxlJobExecutor.setLogPath(xxlJobProp.get("xxl.job.executor.logpath"));
 		xxlJobExecutor.setAccessToken(xxlJobProp.get("xxl.job.accessToken"));
+		xxlJobExecutor.setLogPath(xxlJobProp.get("xxl.job.executor.logpath"));
+		xxlJobExecutor.setLogRetentionDays(xxlJobProp.getInt("xxl.job.executor.logretentiondays"));
 
 		// start executor
 		try {

+ 5 - 3
xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/resources/xxl-job-executor.properties

@@ -6,8 +6,10 @@ xxl.job.executor.appname=xxl-job-executor-sample
 xxl.job.executor.ip=
 xxl.job.executor.port=9997
 
+### xxl-job, access token
+xxl.job.accessToken=
+
 ### xxl-job log path
 xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
-
-### xxl-job, access token
-xxl.job.accessToken=
+### xxl-job log retention days
+xxl.job.executor.logretentiondays=-1

+ 1 - 1
xxl-job-executor-samples/xxl-job-executor-sample-spring/pom.xml

@@ -4,7 +4,7 @@
 	<parent>
 		<groupId>com.xuxueli</groupId>
 		<artifactId>xxl-job-executor-samples</artifactId>
-		<version>1.9.0-SNAPSHOT</version>
+		<version>1.9.2-SNAPSHOT</version>
 	</parent>
 	<artifactId>xxl-job-executor-sample-spring</artifactId>
 	<packaging>war</packaging>

+ 8 - 6
xxl-job-executor-samples/xxl-job-executor-sample-spring/src/main/resources/applicationcontext-xxl-job.xml

@@ -23,18 +23,20 @@
 
 	<!-- 配置02、执行器 -->
 	<bean id="xxlJobExecutor" class="com.xxl.job.core.executor.XxlJobExecutor" init-method="start" destroy-method="destroy" >
+		<!-- 执行器注册中心地址[选填],为空则关闭自动注册 -->
+		<property name="adminAddresses" value="${xxl.job.admin.addresses}" />
+		<!-- 执行器AppName[选填],为空则关闭自动注册 -->
+		<property name="appName" value="${xxl.job.executor.appname}" />
 		<!-- 执行器IP[选填],为空则自动获取 -->
 		<property name="ip" value="${xxl.job.executor.ip}" />
 		<!-- 执行器端口号[选填],为空则自动获取 -->
 		<property name="port" value="${xxl.job.executor.port}" />
-		<!-- 执行器AppName[选填],为空则关闭自动注册 -->
-        <property name="appName" value="${xxl.job.executor.appname}" />
-        <!-- 执行器注册中心地址[选填],为空则关闭自动注册 -->
-		<property name="adminAddresses" value="${xxl.job.admin.addresses}" />
-		<!-- 执行器日志路径[选填],为空则使用默认路径 -->
-		<property name="logPath" value="${xxl.job.executor.logpath}" />
 		<!-- 访问令牌[选填],非空则进行匹配校验 -->
 		<property name="accessToken" value="${xxl.job.accessToken}" />
+		<!-- 执行器日志路径[选填],为空则使用默认路径 -->
+		<property name="logPath" value="${xxl.job.executor.logpath}" />
+		<!-- 日志保存天数[选填],值大于3时生效 -->
+		<property name="logRetentionDays" value="${xxl.job.executor.logretentiondays}" />
 	</bean>
 
 

+ 6 - 4
xxl-job-executor-samples/xxl-job-executor-sample-spring/src/main/resources/xxl-job-executor.properties

@@ -4,10 +4,12 @@ xxl.job.admin.addresses=http://127.0.0.1:18080/xxl-job-admin
 ### xxl-job executor address
 xxl.job.executor.appname=xxl-job-executor-sample
 xxl.job.executor.ip=
-xxl.job.executor.port=9999
+xxl.job.executor.port=9998
+
+### xxl-job, access token
+xxl.job.accessToken=
 
 ### xxl-job log path
 xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
-
-### xxl-job, access token
-xxl.job.accessToken=
+### xxl-job log retention days
+xxl.job.executor.logretentiondays=-1

+ 1 - 1
xxl-job-executor-samples/xxl-job-executor-sample-springboot/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <groupId>com.xuxueli</groupId>
         <artifactId>xxl-job-executor-samples</artifactId>
-        <version>1.9.0-SNAPSHOT</version>
+        <version>1.9.2-SNAPSHOT</version>
     </parent>
     <artifactId>xxl-job-executor-sample-springboot</artifactId>
     <packaging>jar</packaging>

+ 14 - 9
xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/java/com/xxl/job/executor/core/config/XxlJobConfig.java

@@ -18,12 +18,11 @@ import org.springframework.context.annotation.Configuration;
 public class XxlJobConfig {
     private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
 
-
     @Value("${xxl.job.admin.addresses}")
-    private String addresses;
+    private String adminAddresses;
 
     @Value("${xxl.job.executor.appname}")
-    private String appname;
+    private String appName;
 
     @Value("${xxl.job.executor.ip}")
     private String ip;
@@ -31,22 +30,28 @@ public class XxlJobConfig {
     @Value("${xxl.job.executor.port}")
     private int port;
 
-    @Value("${xxl.job.executor.logpath}")
-    private String logpath;
-
     @Value("${xxl.job.accessToken}")
     private String accessToken;
 
+    @Value("${xxl.job.executor.logpath}")
+    private String logPath;
+
+    @Value("${xxl.job.executor.logretentiondays}")
+    private int logRetentionDays;
+
+
     @Bean(initMethod = "start", destroyMethod = "destroy")
     public XxlJobExecutor xxlJobExecutor() {
         logger.info(">>>>>>>>>>> xxl-job config init.");
         XxlJobExecutor xxlJobExecutor = new XxlJobExecutor();
+        xxlJobExecutor.setAdminAddresses(adminAddresses);
+        xxlJobExecutor.setAppName(appName);
         xxlJobExecutor.setIp(ip);
         xxlJobExecutor.setPort(port);
-        xxlJobExecutor.setAppName(appname);
-        xxlJobExecutor.setAdminAddresses(addresses);
-        xxlJobExecutor.setLogPath(logpath);
         xxlJobExecutor.setAccessToken(accessToken);
+        xxlJobExecutor.setLogPath(logPath);
+        xxlJobExecutor.setLogRetentionDays(logRetentionDays);
+
         return xxlJobExecutor;
     }
 

+ 7 - 5
xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/resources/application.properties

@@ -4,17 +4,19 @@ server.port=8081
 # log config
 logging.config=classpath:logback.xml
 
-# xxl-job
+
 ### xxl-job admin address list, such as "http://address" or "http://address01,http://address02"
 xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
 
 ### xxl-job executor address
 xxl.job.executor.appname=xxl-job-executor-sample
 xxl.job.executor.ip=
-xxl.job.executor.port=9998
-
-### xxl-job log path
-xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
+xxl.job.executor.port=9999
 
 ### xxl-job, access token
 xxl.job.accessToken=
+
+### xxl-job log path
+xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
+### xxl-job log retention days
+xxl.job.executor.logretentiondays=-1

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels