Browse Source

[Feature][api] schedule add timezone support #5259 (#5465)

* [Feature][api] schedule add timezone support #5259

* import moment-timezone

* fix code smell
pull/3/MERGE
ruanwenjun 3 years ago committed by GitHub
parent
commit
a925f64571
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/SchedulerController.java
  2. 112
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/ScheduleParam.java
  3. 17
      dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/SchedulerServiceImpl.java
  4. 30
      dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/DateUtils.java
  5. 13
      dolphinscheduler-common/src/test/java/org/apache/dolphinscheduler/common/utils/DateUtilsTest.java
  6. 567
      dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/Schedule.java
  7. 4
      dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/ScheduleMapper.xml
  8. 1
      dolphinscheduler-dist/release-docs/LICENSE
  9. 20
      dolphinscheduler-dist/release-docs/licenses/ui-licenses/LICENSE-moment-timezone
  10. 45
      dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/quartz/QuartzExecutors.java
  11. 1
      dolphinscheduler-ui/package.json
  12. 22
      dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/timing.vue
  13. 1
      dolphinscheduler-ui/src/js/module/i18n/locale/en_US.js
  14. 1
      dolphinscheduler-ui/src/js/module/i18n/locale/zh_CN.js
  15. 1
      sql/dolphinscheduler_mysql.sql
  16. 1
      sql/dolphinscheduler_postgre.sql
  17. 20
      sql/upgrade/1.4.0_schema/mysql/dolphinscheduler_ddl.sql
  18. 17
      sql/upgrade/1.4.0_schema/postgresql/dolphinscheduler_ddl.sql

3
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/SchedulerController.java

@ -94,7 +94,8 @@ public class SchedulerController extends BaseController {
@ApiOperation(value = "createSchedule", notes = "CREATE_SCHEDULE_NOTES") @ApiOperation(value = "createSchedule", notes = "CREATE_SCHEDULE_NOTES")
@ApiImplicitParams({ @ApiImplicitParams({
@ApiImplicitParam(name = "processDefinitionId", value = "PROCESS_DEFINITION_ID", required = true, dataType = "Int", example = "100"), @ApiImplicitParam(name = "processDefinitionId", value = "PROCESS_DEFINITION_ID", required = true, dataType = "Int", example = "100"),
@ApiImplicitParam(name = "schedule", value = "SCHEDULE", dataType = "String", example = "{'startTime':'2019-06-10 00:00:00','endTime':'2019-06-13 00:00:00','crontab':'0 0 3/6 * * ? *'}"), @ApiImplicitParam(name = "schedule", value = "SCHEDULE", dataType = "String",
example = "{'startTime':'2019-06-10 00:00:00','endTime':'2019-06-13 00:00:00','timezoneId':'America/Phoenix','crontab':'0 0 3/6 * * ? *'}"),
@ApiImplicitParam(name = "warningType", value = "WARNING_TYPE", type = "WarningType"), @ApiImplicitParam(name = "warningType", value = "WARNING_TYPE", type = "WarningType"),
@ApiImplicitParam(name = "warningGroupId", value = "WARNING_GROUP_ID", dataType = "Int", example = "100"), @ApiImplicitParam(name = "warningGroupId", value = "WARNING_GROUP_ID", dataType = "Int", example = "100"),
@ApiImplicitParam(name = "failureStrategy", value = "FAILURE_STRATEGY", type = "FailureStrategy"), @ApiImplicitParam(name = "failureStrategy", value = "FAILURE_STRATEGY", type = "FailureStrategy"),

112
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/ScheduleParam.java

@ -14,62 +14,74 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.dolphinscheduler.api.dto;
import com.fasterxml.jackson.annotation.JsonFormat; package org.apache.dolphinscheduler.api.dto;
import java.util.Date; import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
/** /**
* schedule parameters * schedule parameters
*/ */
public class ScheduleParam { public class ScheduleParam {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date startTime; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8") private Date startTime;
private Date endTime; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private String crontab; private Date endTime;
private String crontab;
public ScheduleParam() { private String timezoneId;
}
public ScheduleParam() {
public ScheduleParam(Date startTime, Date endTime, String crontab) { }
this.startTime = startTime;
this.endTime = endTime; public ScheduleParam(Date startTime, Date endTime, String timezoneId, String crontab) {
this.crontab = crontab; this.startTime = startTime;
} this.endTime = endTime;
this.timezoneId = timezoneId;
public Date getStartTime() { this.crontab = crontab;
return startTime; }
}
public Date getStartTime() {
public void setStartTime(Date startTime) { return startTime;
this.startTime = startTime; }
}
public void setStartTime(Date startTime) {
public Date getEndTime() { this.startTime = startTime;
return endTime; }
}
public Date getEndTime() {
public void setEndTime(Date endTime) { return endTime;
this.endTime = endTime; }
}
public void setEndTime(Date endTime) {
public String getCrontab() { this.endTime = endTime;
return crontab; }
}
public String getCrontab() {
public void setCrontab(String crontab) { return crontab;
this.crontab = crontab; }
}
public void setCrontab(String crontab) {
this.crontab = crontab;
@Override }
public String toString() {
return "ScheduleParam{" + public String getTimezoneId() {
"startTime=" + startTime + return timezoneId;
", endTime=" + endTime + }
", crontab='" + crontab + '\'' +
'}'; public void setTimezoneId(String timezoneId) {
} this.timezoneId = timezoneId;
}
@Override
public String toString() {
return "ScheduleParam{"
+ "startTime=" + startTime
+ ", endTime=" + endTime
+ ", crontab='" + crontab + '\''
+ ", timezoneId='" + timezoneId + '\''
+ '}';
}
} }

17
dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/SchedulerServiceImpl.java

@ -157,6 +157,7 @@ public class SchedulerServiceImpl extends BaseServiceImpl implements SchedulerSe
return result; return result;
} }
scheduleObj.setCrontab(scheduleParam.getCrontab()); scheduleObj.setCrontab(scheduleParam.getCrontab());
scheduleObj.setTimezoneId(scheduleParam.getTimezoneId());
scheduleObj.setWarningType(warningType); scheduleObj.setWarningType(warningType);
scheduleObj.setWarningGroupId(warningGroupId); scheduleObj.setWarningGroupId(warningGroupId);
scheduleObj.setFailureStrategy(failureStrategy); scheduleObj.setFailureStrategy(failureStrategy);
@ -258,6 +259,7 @@ public class SchedulerServiceImpl extends BaseServiceImpl implements SchedulerSe
return result; return result;
} }
schedule.setCrontab(scheduleParam.getCrontab()); schedule.setCrontab(scheduleParam.getCrontab());
schedule.setTimezoneId(scheduleParam.getTimezoneId());
} }
if (warningType != null) { if (warningType != null) {
@ -471,20 +473,9 @@ public class SchedulerServiceImpl extends BaseServiceImpl implements SchedulerSe
} }
public void setSchedule(int projectId, Schedule schedule) { public void setSchedule(int projectId, Schedule schedule) {
int scheduleId = schedule.getId(); logger.info("set schedule, project id: {}, scheduleId: {}", projectId, schedule.getId());
logger.info("set schedule, project id: {}, scheduleId: {}", projectId, scheduleId);
Date startDate = schedule.getStartTime();
Date endDate = schedule.getEndTime();
String jobName = QuartzExecutors.buildJobName(scheduleId);
String jobGroupName = QuartzExecutors.buildJobGroupName(projectId);
Map<String, Object> dataMap = QuartzExecutors.buildDataMap(projectId, scheduleId, schedule);
QuartzExecutors.getInstance().addJob(ProcessScheduleJob.class, jobName, jobGroupName, startDate, endDate,
schedule.getCrontab(), dataMap);
QuartzExecutors.getInstance().addJob(ProcessScheduleJob.class, projectId, schedule);
} }
/** /**

30
dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/DateUtils.java

@ -22,9 +22,11 @@ import org.apache.dolphinscheduler.common.Constants;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.TimeZone;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -492,6 +494,34 @@ public class DateUtils {
return getCurrentTime(Constants.YYYYMMDDHHMMSSSSS); return getCurrentTime(Constants.YYYYMMDDHHMMSSSSS);
} }
/**
* transform date to target timezone date
* <p>e.g.
* <p> if input date is 2020-01-01 00:00:00 current timezone is CST
* <p>targetTimezoneId is MST
* <p>this method will return 2020-01-01 15:00:00
*/
public static Date getTimezoneDate(Date date, String targetTimezoneId) {
if (StringUtils.isEmpty(targetTimezoneId)) {
return date;
}
String dateToString = dateToString(date);
LocalDateTime localDateTime = LocalDateTime.parse(dateToString, DateTimeFormatter.ofPattern(Constants.YYYY_MM_DD_HH_MM_SS));
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, TimeZone.getTimeZone(targetTimezoneId).toZoneId());
return Date.from(zonedDateTime.toInstant());
}
/**
* get timezone by timezoneId
*/
public static TimeZone getTimezone(String timezoneId) {
if (StringUtils.isEmpty(timezoneId)) {
return null;
}
return TimeZone.getTimeZone(timezoneId);
}
static final long C0 = 1L; static final long C0 = 1L;
static final long C1 = C0 * 1000L; static final long C1 = C0 * 1000L;
static final long C2 = C1 * 1000L; static final long C2 = C1 * 1000L;

13
dolphinscheduler-common/src/test/java/org/apache/dolphinscheduler/common/utils/DateUtilsTest.java

@ -20,6 +20,7 @@ package org.apache.dolphinscheduler.common.utils;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.TimeZone;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -204,4 +205,16 @@ public class DateUtilsTest {
Assert.assertNull(DateUtils.format2Duration(d1, d2)); Assert.assertNull(DateUtils.format2Duration(d1, d2));
} }
@Test
public void testTransformToTimezone() {
Date date = new Date();
Date mst = DateUtils.getTimezoneDate(date, TimeZone.getDefault().getID());
Assert.assertEquals(DateUtils.dateToString(date), DateUtils.dateToString(mst));
}
@Test
public void testGetTimezone() {
Assert.assertNull(DateUtils.getTimezone(null));
Assert.assertEquals(TimeZone.getTimeZone("MST"), DateUtils.getTimezone("MST"));
}
} }

567
dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/Schedule.java

@ -14,13 +14,9 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.apache.dolphinscheduler.dao.entity; package org.apache.dolphinscheduler.dao.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.apache.dolphinscheduler.common.enums.FailureStrategy; import org.apache.dolphinscheduler.common.enums.FailureStrategy;
import org.apache.dolphinscheduler.common.enums.Priority; import org.apache.dolphinscheduler.common.enums.Priority;
import org.apache.dolphinscheduler.common.enums.ReleaseState; import org.apache.dolphinscheduler.common.enums.ReleaseState;
@ -28,6 +24,12 @@ import org.apache.dolphinscheduler.common.enums.WarningType;
import java.util.Date; import java.util.Date;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
/** /**
* schedule * schedule
* *
@ -35,278 +37,285 @@ import java.util.Date;
@TableName("t_ds_schedules") @TableName("t_ds_schedules")
public class Schedule { public class Schedule {
@TableId(value="id", type=IdType.AUTO) @TableId(value = "id", type = IdType.AUTO)
private int id; private int id;
/** /**
* process definition id * process definition id
*/ */
private int processDefinitionId; private int processDefinitionId;
/** /**
* process definition name * process definition name
*/ */
@TableField(exist = false) @TableField(exist = false)
private String processDefinitionName; private String processDefinitionName;
/** /**
* project name * project name
*/ */
@TableField(exist = false) @TableField(exist = false)
private String projectName; private String projectName;
/** /**
* schedule description * schedule description
*/ */
@TableField(exist = false) @TableField(exist = false)
private String definitionDescription; private String definitionDescription;
/** /**
* schedule start time * schedule start time
*/ */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date startTime; private Date startTime;
/** /**
* schedule end time * schedule end time
*/ */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date endTime; private Date endTime;
/** /**
* crontab expression * timezoneId
*/ * <p>see {@link java.util.TimeZone#getTimeZone(String)}
private String crontab; */
private String timezoneId;
/**
* failure strategy /**
*/ * crontab expression
private FailureStrategy failureStrategy; */
private String crontab;
/**
* warning type /**
*/ * failure strategy
private WarningType warningType; */
private FailureStrategy failureStrategy;
/**
* create time /**
*/ * warning type
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8") */
private Date createTime; private WarningType warningType;
/** /**
* update time * create time
*/ */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date updateTime; private Date createTime;
/** /**
* created user id * update time
*/ */
private int userId; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date updateTime;
/**
* created user name /**
*/ * created user id
@TableField(exist = false) */
private String userName; private int userId;
/** /**
* release state * created user name
*/ */
private ReleaseState releaseState; @TableField(exist = false)
private String userName;
/**
* warning group id /**
*/ * release state
private int warningGroupId; */
private ReleaseState releaseState;
/** /**
* process instance priority * warning group id
*/ */
private Priority processInstancePriority; private int warningGroupId;
/**
* worker group /**
*/ * process instance priority
private String workerGroup; */
private Priority processInstancePriority;
public int getWarningGroupId() {
return warningGroupId; /**
} * worker group
*/
public void setWarningGroupId(int warningGroupId) { private String workerGroup;
this.warningGroupId = warningGroupId;
} public int getWarningGroupId() {
return warningGroupId;
}
public Schedule() { public void setWarningGroupId(int warningGroupId) {
} this.warningGroupId = warningGroupId;
}
public String getProjectName() {
return projectName; public Schedule() {
} }
public void setProjectName(String projectName) { public String getProjectName() {
this.projectName = projectName; return projectName;
} }
public void setProjectName(String projectName) {
this.projectName = projectName;
public Date getStartTime() { }
return startTime; public Date getStartTime() {
} return startTime;
}
public void setStartTime(Date startTime) {
this.startTime = startTime; public void setStartTime(Date startTime) {
} this.startTime = startTime;
}
public Date getEndTime() {
return endTime; public Date getEndTime() {
} return endTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime; public void setEndTime(Date endTime) {
} this.endTime = endTime;
}
public String getCrontab() {
return crontab; public String getTimezoneId() {
} return timezoneId;
}
public void setCrontab(String crontab) {
this.crontab = crontab; public void setTimezoneId(String timezoneId) {
} this.timezoneId = timezoneId;
}
public FailureStrategy getFailureStrategy() {
return failureStrategy; public String getCrontab() {
} return crontab;
}
public void setFailureStrategy(FailureStrategy failureStrategy) {
this.failureStrategy = failureStrategy; public void setCrontab(String crontab) {
} this.crontab = crontab;
}
public WarningType getWarningType() {
return warningType; public FailureStrategy getFailureStrategy() {
} return failureStrategy;
}
public void setWarningType(WarningType warningType) {
this.warningType = warningType; public void setFailureStrategy(FailureStrategy failureStrategy) {
} this.failureStrategy = failureStrategy;
}
public Date getCreateTime() {
return createTime; public WarningType getWarningType() {
} return warningType;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime; public void setWarningType(WarningType warningType) {
} this.warningType = warningType;
}
public ReleaseState getReleaseState() { public Date getCreateTime() {
return releaseState; return createTime;
} }
public void setReleaseState(ReleaseState releaseState) { public void setCreateTime(Date createTime) {
this.releaseState = releaseState; this.createTime = createTime;
} }
public ReleaseState getReleaseState() {
return releaseState;
public int getProcessDefinitionId() { }
return processDefinitionId;
} public void setReleaseState(ReleaseState releaseState) {
this.releaseState = releaseState;
public void setProcessDefinitionId(int processDefinitionId) { }
this.processDefinitionId = processDefinitionId;
} public int getProcessDefinitionId() {
return processDefinitionId;
public String getProcessDefinitionName() { }
return processDefinitionName;
} public void setProcessDefinitionId(int processDefinitionId) {
this.processDefinitionId = processDefinitionId;
public void setProcessDefinitionName(String processDefinitionName) { }
this.processDefinitionName = processDefinitionName;
} public String getProcessDefinitionName() {
return processDefinitionName;
public Date getUpdateTime() { }
return updateTime;
} public void setProcessDefinitionName(String processDefinitionName) {
this.processDefinitionName = processDefinitionName;
public void setUpdateTime(Date updateTime) { }
this.updateTime = updateTime;
} public Date getUpdateTime() {
return updateTime;
public int getUserId() { }
return userId;
} public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
public void setUserId(int userId) { }
this.userId = userId;
} public int getUserId() {
return userId;
public String getUserName() { }
return userName;
} public void setUserId(int userId) {
this.userId = userId;
public void setUserName(String userName) { }
this.userName = userName;
} public String getUserName() {
return userName;
public int getId() { }
return id;
} public void setUserName(String userName) {
this.userName = userName;
public void setId(int id) { }
this.id = id;
} public int getId() {
return id;
public Priority getProcessInstancePriority() { }
return processInstancePriority;
} public void setId(int id) {
this.id = id;
public void setProcessInstancePriority(Priority processInstancePriority) { }
this.processInstancePriority = processInstancePriority;
} public Priority getProcessInstancePriority() {
return processInstancePriority;
public String getWorkerGroup() { }
return workerGroup;
} public void setProcessInstancePriority(Priority processInstancePriority) {
this.processInstancePriority = processInstancePriority;
public void setWorkerGroup(String workerGroup) { }
this.workerGroup = workerGroup;
} public String getWorkerGroup() {
return workerGroup;
@Override }
public String toString() {
return "Schedule{" + public void setWorkerGroup(String workerGroup) {
"id=" + id + this.workerGroup = workerGroup;
", processDefinitionId=" + processDefinitionId + }
", processDefinitionName='" + processDefinitionName + '\'' +
", projectName='" + projectName + '\'' + @Override
", description='" + definitionDescription + '\'' + public String toString() {
", startTime=" + startTime + return "Schedule{"
", endTime=" + endTime + + "id=" + id
", crontab='" + crontab + '\'' + + ", processDefinitionId=" + processDefinitionId
", failureStrategy=" + failureStrategy + + ", processDefinitionName='" + processDefinitionName + '\''
", warningType=" + warningType + + ", projectName='" + projectName + '\''
", createTime=" + createTime + + ", description='" + definitionDescription + '\''
", updateTime=" + updateTime + + ", startTime=" + startTime
", userId=" + userId + + ", endTime=" + endTime
", userName='" + userName + '\'' + + ", timezoneId='" + timezoneId + +'\''
", releaseState=" + releaseState + + ", crontab='" + crontab + '\''
", warningGroupId=" + warningGroupId + + ", failureStrategy=" + failureStrategy
", processInstancePriority=" + processInstancePriority + + ", warningType=" + warningType
", workerGroup='" + workerGroup + '\'' + + ", createTime=" + createTime
'}'; + ", updateTime=" + updateTime
} + ", userId=" + userId
+ ", userName='" + userName + '\''
public String getDefinitionDescription() { + ", releaseState=" + releaseState
return definitionDescription; + ", warningGroupId=" + warningGroupId
} + ", processInstancePriority=" + processInstancePriority
+ ", workerGroup='" + workerGroup + '\''
public void setDefinitionDescription(String definitionDescription) { + '}';
this.definitionDescription = definitionDescription; }
}
public String getDefinitionDescription() {
return definitionDescription;
}
public void setDefinitionDescription(String definitionDescription) {
this.definitionDescription = definitionDescription;
}
} }

4
dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/ScheduleMapper.xml

@ -19,11 +19,11 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="org.apache.dolphinscheduler.dao.mapper.ScheduleMapper"> <mapper namespace="org.apache.dolphinscheduler.dao.mapper.ScheduleMapper">
<sql id="baseSql"> <sql id="baseSql">
id, process_definition_id, start_time, end_time, crontab, failure_strategy, user_id, release_state, id, process_definition_id, start_time, end_time, timezone_id, crontab, failure_strategy, user_id, release_state,
warning_type, warning_group_id, process_instance_priority, worker_group, create_time, update_time warning_type, warning_group_id, process_instance_priority, worker_group, create_time, update_time
</sql> </sql>
<sql id="baseSqlV2"> <sql id="baseSqlV2">
${alias}.id, ${alias}.process_definition_id, ${alias}.start_time, ${alias}.end_time, ${alias}.crontab, ${alias}.failure_strategy, ${alias}.user_id, ${alias}.release_state, ${alias}.id, ${alias}.process_definition_id, ${alias}.start_time, ${alias}.end_time, ${alias}.timezone_id, ${alias}.crontab, ${alias}.failure_strategy, ${alias}.user_id, ${alias}.release_state,
${alias}.warning_type, ${alias}.warning_group_id, ${alias}.process_instance_priority, ${alias}.worker_group, ${alias}.create_time, ${alias}.update_time ${alias}.warning_type, ${alias}.warning_group_id, ${alias}.process_instance_priority, ${alias}.worker_group, ${alias}.create_time, ${alias}.update_time
</sql> </sql>
<select id="queryByProcessDefineIdPaging" resultType="org.apache.dolphinscheduler.dao.entity.Schedule"> <select id="queryByProcessDefineIdPaging" resultType="org.apache.dolphinscheduler.dao.entity.Schedule">

1
dolphinscheduler-dist/release-docs/LICENSE vendored

@ -539,6 +539,7 @@ MIT licenses
js-cookie 2.2.1: https://github.com/js-cookie/js-cookie MIT js-cookie 2.2.1: https://github.com/js-cookie/js-cookie MIT
jsplumb 2.8.6: https://github.com/jsplumb/jsplumb MIT and GPLv2 jsplumb 2.8.6: https://github.com/jsplumb/jsplumb MIT and GPLv2
lodash 4.17.11: https://github.com/lodash/lodash MIT lodash 4.17.11: https://github.com/lodash/lodash MIT
moment-timezone 0.5.33: https://github.com/moment/moment-timezone MIT
vue-treeselect 0.4.0: https://github.com/riophae/vue-treeselect MIT vue-treeselect 0.4.0: https://github.com/riophae/vue-treeselect MIT
vue 2.5.17: https://github.com/vuejs/vue MIT vue 2.5.17: https://github.com/vuejs/vue MIT
vue-router 2.7.0: https://github.com/vuejs/vue-router MIT vue-router 2.7.0: https://github.com/vuejs/vue-router MIT

20
dolphinscheduler-dist/release-docs/licenses/ui-licenses/LICENSE-moment-timezone vendored

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) JS Foundation and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

45
dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/quartz/QuartzExecutors.java

@ -60,6 +60,7 @@ import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.JobBuilder.newJob; import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger; import static org.quartz.TriggerBuilder.newTrigger;
import org.apache.dolphinscheduler.common.utils.DateUtils;
import org.apache.dolphinscheduler.common.utils.JSONUtils; import org.apache.dolphinscheduler.common.utils.JSONUtils;
import org.apache.dolphinscheduler.common.utils.PropertyUtils; import org.apache.dolphinscheduler.common.utils.PropertyUtils;
import org.apache.dolphinscheduler.common.utils.StringUtils; import org.apache.dolphinscheduler.common.utils.StringUtils;
@ -220,16 +221,18 @@ public class QuartzExecutors {
* add task trigger , if this task already exists, return this task with updated trigger * add task trigger , if this task already exists, return this task with updated trigger
* *
* @param clazz job class name * @param clazz job class name
* @param jobName job name * @param projectId projectId
* @param jobGroupName job group name * @param schedule schedule
* @param startDate job start date
* @param endDate job end date
* @param cronExpression cron expression
* @param jobDataMap job parameters data map
*/ */
public void addJob(Class<? extends Job> clazz, String jobName, String jobGroupName, Date startDate, Date endDate, public void addJob(Class<? extends Job> clazz, int projectId, final Schedule schedule) {
String cronExpression, String jobName = QuartzExecutors.buildJobName(schedule.getId());
Map<String, Object> jobDataMap) { String jobGroupName = QuartzExecutors.buildJobGroupName(projectId);
Date startDate = schedule.getStartTime();
Date endDate = schedule.getEndTime();
Map<String, Object> jobDataMap = QuartzExecutors.buildDataMap(projectId, schedule);
String cronExpression = schedule.getCrontab();
String timezoneId = schedule.getTimezoneId();
lock.writeLock().lock(); lock.writeLock().lock();
try { try {
@ -239,15 +242,11 @@ public class QuartzExecutors {
if (scheduler.checkExists(jobKey)) { if (scheduler.checkExists(jobKey)) {
jobDetail = scheduler.getJobDetail(jobKey); jobDetail = scheduler.getJobDetail(jobKey);
if (jobDataMap != null) { jobDetail.getJobDataMap().putAll(jobDataMap);
jobDetail.getJobDataMap().putAll(jobDataMap);
}
} else { } else {
jobDetail = newJob(clazz).withIdentity(jobKey).build(); jobDetail = newJob(clazz).withIdentity(jobKey).build();
if (jobDataMap != null) { jobDetail.getJobDataMap().putAll(jobDataMap);
jobDetail.getJobDataMap().putAll(jobDataMap);
}
scheduler.addJob(jobDetail, false, true); scheduler.addJob(jobDetail, false, true);
@ -263,8 +262,15 @@ public class QuartzExecutors {
* current time (taking into account any associated Calendar), * current time (taking into account any associated Calendar),
* but it does not want to be fired now. * but it does not want to be fired now.
*/ */
CronTrigger cronTrigger = newTrigger().withIdentity(triggerKey).startAt(startDate).endAt(endDate) CronTrigger cronTrigger = newTrigger()
.withSchedule(cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing()) .withIdentity(triggerKey)
.startAt(DateUtils.getTimezoneDate(startDate, timezoneId))
.endAt(DateUtils.getTimezoneDate(endDate, timezoneId))
.withSchedule(
cronSchedule(cronExpression)
.withMisfireHandlingInstructionDoNothing()
.inTimeZone(DateUtils.getTimezone(timezoneId))
)
.forJob(jobDetail).build(); .forJob(jobDetail).build();
if (scheduler.checkExists(triggerKey)) { if (scheduler.checkExists(triggerKey)) {
@ -368,14 +374,13 @@ public class QuartzExecutors {
* add params to map * add params to map
* *
* @param projectId project id * @param projectId project id
* @param scheduleId schedule id
* @param schedule schedule * @param schedule schedule
* @return data map * @return data map
*/ */
public static Map<String, Object> buildDataMap(int projectId, int scheduleId, Schedule schedule) { public static Map<String, Object> buildDataMap(int projectId, Schedule schedule) {
Map<String, Object> dataMap = new HashMap<>(8); Map<String, Object> dataMap = new HashMap<>(8);
dataMap.put(PROJECT_ID, projectId); dataMap.put(PROJECT_ID, projectId);
dataMap.put(SCHEDULE_ID, scheduleId); dataMap.put(SCHEDULE_ID, schedule.getId());
dataMap.put(SCHEDULE, JSONUtils.toJsonString(schedule)); dataMap.put(SCHEDULE, JSONUtils.toJsonString(schedule));
return dataMap; return dataMap;

1
dolphinscheduler-ui/package.json

@ -33,6 +33,7 @@
"js-cookie": "^2.2.1", "js-cookie": "^2.2.1",
"jsplumb": "^2.8.6", "jsplumb": "^2.8.6",
"lodash": "^4.17.11", "lodash": "^4.17.11",
"moment-timezone": "^0.5.33",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",
"remixicon": "^2.5.0", "remixicon": "^2.5.0",
"vue": "^2.5.17", "vue": "^2.5.17",

22
dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/timing.vue

@ -61,6 +61,21 @@
</template> </template>
</div> </div>
</div> </div>
<div class="clearfix list">
<div class="text">
{{$t('Timezone')}}
</div>
<div class="cont">
<el-select v-model=timezoneId filterable placeholder="Timezone">
<el-option
v-for="item in availableTimezoneIDList"
:key="item"
:label="item"
:value="item">
</el-option>
</el-select>
</div>
</div>
<div class="clearfix list"> <div class="clearfix list">
<div style = "padding-left: 150px;">{{$t('Next five execution times')}}</div> <div style = "padding-left: 150px;">{{$t('Next five execution times')}}</div>
<ul style = "padding-left: 150px;"> <ul style = "padding-left: 150px;">
@ -144,6 +159,7 @@
</div> </div>
</template> </template>
<script> <script>
import moment from 'moment-timezone'
import i18n from '@/module/i18n' import i18n from '@/module/i18n'
import store from '@/conf/home/store' import store from '@/conf/home/store'
import { warningTypeList } from './util' import { warningTypeList } from './util'
@ -160,12 +176,14 @@
processDefinitionId: 0, processDefinitionId: 0,
failureStrategy: 'CONTINUE', failureStrategy: 'CONTINUE',
warningTypeList: warningTypeList, warningTypeList: warningTypeList,
availableTimezoneIDList: moment.tz.names(),
warningType: 'NONE', warningType: 'NONE',
notifyGroupList: [], notifyGroupList: [],
warningGroupId: '', warningGroupId: '',
spinnerLoading: false, spinnerLoading: false,
scheduleTime: '', scheduleTime: '',
crontab: '0 0 * * * ? *', crontab: '0 0 * * * ? *',
timezoneId: moment.tz.guess(),
cronPopover: false, cronPopover: false,
i18n: i18n.globalScope.LOCALE, i18n: i18n.globalScope.LOCALE,
processInstancePriority: 'MEDIUM', processInstancePriority: 'MEDIUM',
@ -204,7 +222,8 @@
schedule: JSON.stringify({ schedule: JSON.stringify({
startTime: this.scheduleTime[0], startTime: this.scheduleTime[0],
endTime: this.scheduleTime[1], endTime: this.scheduleTime[1],
crontab: this.crontab crontab: this.crontab,
timezoneId: this.timezoneId
}), }),
failureStrategy: this.failureStrategy, failureStrategy: this.failureStrategy,
warningType: this.warningType, warningType: this.warningType,
@ -323,6 +342,7 @@
if (this.timingData.item.crontab) { if (this.timingData.item.crontab) {
this.crontab = item.crontab this.crontab = item.crontab
this.scheduleTime = [formatDate(item.startTime), formatDate(item.endTime)] this.scheduleTime = [formatDate(item.startTime), formatDate(item.endTime)]
this.timezoneId = item.timezoneId === null ? moment.tz.guess() : item.timezoneId
this.failureStrategy = item.failureStrategy this.failureStrategy = item.failureStrategy
this.warningType = item.warningType this.warningType = item.warningType
this.processInstancePriority = item.processInstancePriority this.processInstancePriority = item.processInstancePriority

1
dolphinscheduler-ui/src/js/module/i18n/locale/en_US.js

@ -399,6 +399,7 @@ export default {
'Import process': 'Import process', 'Import process': 'Import process',
'Timing state': 'Timing state', 'Timing state': 'Timing state',
Timing: 'Timing', Timing: 'Timing',
Timezone: 'Timezone',
TreeView: 'TreeView', TreeView: 'TreeView',
'Mailbox already exists! Recipients and copyers cannot repeat': 'Mailbox already exists! Recipients and copyers cannot repeat', 'Mailbox already exists! Recipients and copyers cannot repeat': 'Mailbox already exists! Recipients and copyers cannot repeat',
'Mailbox input is illegal': 'Mailbox input is illegal', 'Mailbox input is illegal': 'Mailbox input is illegal',

1
dolphinscheduler-ui/src/js/module/i18n/locale/zh_CN.js

@ -399,6 +399,7 @@ export default {
'Import process': '导入工作流', 'Import process': '导入工作流',
'Timing state': '定时状态', 'Timing state': '定时状态',
Timing: '定时', Timing: '定时',
Timezone: '时区',
TreeView: '树形图', TreeView: '树形图',
'Mailbox already exists! Recipients and copyers cannot repeat': '邮箱已存在收件人和抄送人不能重复', 'Mailbox already exists! Recipients and copyers cannot repeat': '邮箱已存在收件人和抄送人不能重复',
'Mailbox input is illegal': '邮箱输入不合法', 'Mailbox input is illegal': '邮箱输入不合法',

1
sql/dolphinscheduler_mysql.sql

@ -750,6 +750,7 @@ CREATE TABLE `t_ds_schedules` (
`process_definition_id` int(11) NOT NULL COMMENT 'process definition id', `process_definition_id` int(11) NOT NULL COMMENT 'process definition id',
`start_time` datetime NOT NULL COMMENT 'start time', `start_time` datetime NOT NULL COMMENT 'start time',
`end_time` datetime NOT NULL COMMENT 'end time', `end_time` datetime NOT NULL COMMENT 'end time',
`timezone_id` varchar(40) DEFAULT NULL COMMENT 'timezoneId',
`crontab` varchar(256) NOT NULL COMMENT 'crontab description', `crontab` varchar(256) NOT NULL COMMENT 'crontab description',
`failure_strategy` tinyint(4) NOT NULL COMMENT 'failure strategy. 0:end,1:continue', `failure_strategy` tinyint(4) NOT NULL COMMENT 'failure strategy. 0:end,1:continue',
`user_id` int(11) NOT NULL COMMENT 'user id', `user_id` int(11) NOT NULL COMMENT 'user id',

1
sql/dolphinscheduler_postgre.sql

@ -614,6 +614,7 @@ CREATE TABLE t_ds_schedules (
process_definition_id int NOT NULL , process_definition_id int NOT NULL ,
start_time timestamp NOT NULL , start_time timestamp NOT NULL ,
end_time timestamp NOT NULL , end_time timestamp NOT NULL ,
timezone_id varchar(40) default NULL ,
crontab varchar(256) NOT NULL , crontab varchar(256) NOT NULL ,
failure_strategy int NOT NULL , failure_strategy int NOT NULL ,
user_id int NOT NULL , user_id int NOT NULL ,

20
sql/upgrade/1.4.0_schema/mysql/dolphinscheduler_ddl.sql

@ -316,6 +316,26 @@ d//
delimiter ; delimiter ;
CALL uc_dolphin_T_t_ds_datasource_A_add_UN_datasourceName(); CALL uc_dolphin_T_t_ds_datasource_A_add_UN_datasourceName();
DROP PROCEDURE uc_dolphin_T_t_ds_datasource_A_add_UN_datasourceName; DROP PROCEDURE uc_dolphin_T_t_ds_datasource_A_add_UN_datasourceName;
-- uc_dolphin_T_t_ds_schedules_A_add_timezone
drop PROCEDURE if EXISTS uc_dolphin_T_t_ds_schedules_A_add_timezone;
delimiter d//
CREATE PROCEDURE uc_dolphin_T_t_ds_schedules_A_add_timezone()
BEGIN
IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS
WHERE TABLE_NAME='t_ds_schedules'
AND TABLE_SCHEMA=(SELECT DATABASE())
AND COLUMN_NAME ='timezone_id')
THEN
ALTER TABLE t_ds_schedules ADD COLUMN `timezone_id` varchar(40) default NULL COMMENT 'schedule timezone id' AFTER `end_time`;
END IF;
END;
d//
delimiter ;
CALL uc_dolphin_T_t_ds_schedules_A_add_timezone();
DROP PROCEDURE uc_dolphin_T_t_ds_schedules_A_add_timezone;
-- ---------------------------- -- ----------------------------
-- These columns will not be used in the new version,if you determine that the historical data is useless, you can delete it using the sql below -- These columns will not be used in the new version,if you determine that the historical data is useless, you can delete it using the sql below
-- ---------------------------- -- ----------------------------

17
sql/upgrade/1.4.0_schema/postgresql/dolphinscheduler_ddl.sql

@ -304,6 +304,23 @@ delimiter ;
SELECT uc_dolphin_T_t_ds_datasource_A_add_UN_datasourceName(); SELECT uc_dolphin_T_t_ds_datasource_A_add_UN_datasourceName();
DROP FUNCTION IF EXISTS uc_dolphin_T_t_ds_datasource_A_add_UN_datasourceName(); DROP FUNCTION IF EXISTS uc_dolphin_T_t_ds_datasource_A_add_UN_datasourceName();
-- uc_dolphin_T_t_ds_schedules_A_add_timezone
delimiter d//
CREATE OR REPLACE FUNCTION uc_dolphin_T_t_ds_schedules_A_add_timezone() RETURNS void AS $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS
WHERE TABLE_NAME='t_ds_schedules'
AND COLUMN_NAME ='timezone_id')
THEN
ALTER TABLE t_ds_schedules ADD COLUMN timezone_id varchar(40) DEFAULT NULL;
END IF;
END;
$$ LANGUAGE plpgsql;
d//
delimiter ;
SELECT uc_dolphin_T_t_ds_schedules_A_add_timezone();
DROP FUNCTION IF EXISTS uc_dolphin_T_t_ds_schedules_A_add_timezone();
-- ---------------------------- -- ----------------------------
-- These columns will not be used in the new version,if you determine that the historical data is useless, you can delete it using the sql below -- These columns will not be used in the new version,if you determine that the historical data is useless, you can delete it using the sql below
-- ---------------------------- -- ----------------------------

Loading…
Cancel
Save