diff --git a/hsweb-task/hsweb-task-cluster/pom.xml b/hsweb-task/hsweb-task-cluster/pom.xml
new file mode 100644
index 000000000..b27d557b2
--- /dev/null
+++ b/hsweb-task/hsweb-task-cluster/pom.xml
@@ -0,0 +1,15 @@
+
+
+
+ hsweb-task
+ org.hswebframework.web
+ 3.0.0-RC-SNAPSHOT
+
+ 4.0.0
+
+ hsweb-task-cluster
+
+
+
\ No newline at end of file
diff --git a/hsweb-task/hsweb-task-core/pom.xml b/hsweb-task/hsweb-task-core/pom.xml
new file mode 100644
index 000000000..ffe8866b6
--- /dev/null
+++ b/hsweb-task/hsweb-task-core/pom.xml
@@ -0,0 +1,15 @@
+
+
+
+ hsweb-task
+ org.hswebframework.web
+ 3.0.0-RC-SNAPSHOT
+
+ 4.0.0
+
+ hsweb-task-core
+
+
+
\ No newline at end of file
diff --git a/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/JobDetail.java b/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/JobDetail.java
new file mode 100644
index 000000000..c0a6aa51c
--- /dev/null
+++ b/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/JobDetail.java
@@ -0,0 +1,28 @@
+package org.hswebframework.web.task;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class JobDetail {
+
+ private String id;
+
+ private String name;
+
+ private String description;
+
+ private String type;
+
+ private long executeTimeOut;
+
+ private long retryTimes;
+
+ private long retryInterval;
+
+ private boolean parallel;
+
+ private String content;
+
+}
diff --git a/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/JobRepository.java b/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/JobRepository.java
new file mode 100644
index 000000000..4a583a623
--- /dev/null
+++ b/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/JobRepository.java
@@ -0,0 +1,18 @@
+package org.hswebframework.web.task;
+
+public interface JobRepository {
+
+
+ JobDetail findById(String id);
+
+ JobDetail save(JobDetail detail);
+
+ JobDetail update(JobDetail detail);
+
+ JobDetail delete(JobDetail detail);
+
+ void enable(String id);
+
+ void disable(String id);
+
+}
diff --git a/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/Task.java b/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/Task.java
new file mode 100644
index 000000000..b130cf82f
--- /dev/null
+++ b/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/Task.java
@@ -0,0 +1,54 @@
+package org.hswebframework.web.task;
+
+import lombok.SneakyThrows;
+import org.hswebframework.web.task.enums.TaskExecuteStatus;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+
+public interface Task {
+
+ String getId();
+
+ String getJobId();
+
+ JobDetail getJob();
+
+ long getLastExecuteTime();
+
+ long getCreateTime();
+
+ TaskExecuteStatus getStatus();
+
+ String getCreator();
+
+ String getSubmitor();
+
+ long getTimeout();
+
+ @SneakyThrows
+ default TaskOperationResult execute() {
+ CountDownLatch latch = new CountDownLatch(1);
+ AtomicReference reference = new AtomicReference<>();
+ execute(result -> {
+ reference.set(result);
+ latch.countDown();
+ });
+ latch.await(getTimeout(), TimeUnit.MILLISECONDS);
+ return reference.get();
+
+ }
+
+ void execute(Consumer onExecute);
+
+ void cancel(Consumer onExecute);
+
+ void suspend(Consumer onExecute);
+
+ void interrupt(Consumer onExecute);
+
+ void start(Consumer onExecute);
+
+}
diff --git a/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/TaskExecutor.java b/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/TaskExecutor.java
new file mode 100644
index 000000000..f54199f11
--- /dev/null
+++ b/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/TaskExecutor.java
@@ -0,0 +1,16 @@
+package org.hswebframework.web.task;
+
+import java.util.List;
+
+public interface TaskExecutor {
+
+ List findAll();
+
+ long total();
+
+ Task createTask(String jobId);
+
+ Task createTask(JobDetail jobDetail);
+
+
+}
diff --git a/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/TaskOperationResult.java b/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/TaskOperationResult.java
new file mode 100644
index 000000000..fceae38f0
--- /dev/null
+++ b/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/TaskOperationResult.java
@@ -0,0 +1,21 @@
+package org.hswebframework.web.task;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.hswebframework.web.task.enums.TaskExecuteStatus;
+
+@Getter
+@Setter
+public class TaskOperationResult {
+ private String taskId;
+
+ private String jobId;
+
+ private TaskExecuteStatus status;
+
+ private boolean success;
+
+ private Throwable cause;
+
+
+}
diff --git a/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/enums/TaskExecuteStatus.java b/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/enums/TaskExecuteStatus.java
new file mode 100644
index 000000000..169454be3
--- /dev/null
+++ b/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/enums/TaskExecuteStatus.java
@@ -0,0 +1,10 @@
+package org.hswebframework.web.task.enums;
+
+public enum TaskExecuteStatus {
+ success,
+ failed,
+ running,
+ suspend,
+ interrupt,
+ starting;
+}
diff --git a/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/events/TaskCreatedEvent.java b/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/events/TaskCreatedEvent.java
new file mode 100644
index 000000000..ee7f1bfe6
--- /dev/null
+++ b/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/events/TaskCreatedEvent.java
@@ -0,0 +1,12 @@
+package org.hswebframework.web.task.events;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.hswebframework.web.task.Task;
+
+@AllArgsConstructor
+@Getter
+public class TaskCreatedEvent {
+ private Task task;
+
+}
diff --git a/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/events/TaskExecuteAfterEvent.java b/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/events/TaskExecuteAfterEvent.java
new file mode 100644
index 000000000..07bb9fc37
--- /dev/null
+++ b/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/events/TaskExecuteAfterEvent.java
@@ -0,0 +1,14 @@
+package org.hswebframework.web.task.events;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.hswebframework.web.task.Task;
+
+@AllArgsConstructor
+@Getter
+public class TaskExecuteAfterEvent {
+ private Task task;
+
+ private String executionId;
+
+}
diff --git a/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/events/TaskExecuteBeforeEvent.java b/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/events/TaskExecuteBeforeEvent.java
new file mode 100644
index 000000000..30c6ce215
--- /dev/null
+++ b/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/events/TaskExecuteBeforeEvent.java
@@ -0,0 +1,14 @@
+package org.hswebframework.web.task.events;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.hswebframework.web.task.Task;
+
+@AllArgsConstructor
+@Getter
+public class TaskExecuteBeforeEvent {
+ private Task task;
+
+ private String executionId;
+
+}
diff --git a/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/events/TaskFailedEvent.java b/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/events/TaskFailedEvent.java
new file mode 100644
index 000000000..47f3d33e5
--- /dev/null
+++ b/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/events/TaskFailedEvent.java
@@ -0,0 +1,14 @@
+package org.hswebframework.web.task.events;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.hswebframework.web.task.Task;
+
+@AllArgsConstructor
+@Getter
+public class TaskFailedEvent {
+ private Task task;
+
+ protected Throwable cause;
+
+}
diff --git a/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/events/TaskStatusChangedEvent.java b/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/events/TaskStatusChangedEvent.java
new file mode 100644
index 000000000..567effa6e
--- /dev/null
+++ b/hsweb-task/hsweb-task-core/src/main/java/org/hswebframework/web/task/events/TaskStatusChangedEvent.java
@@ -0,0 +1,17 @@
+package org.hswebframework.web.task.events;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.hswebframework.web.task.Task;
+import org.hswebframework.web.task.enums.TaskExecuteStatus;
+
+@AllArgsConstructor
+@Getter
+public class TaskStatusChangedEvent {
+
+ private TaskExecuteStatus before;
+
+ private TaskExecuteStatus after;
+
+ private Task task;
+}
diff --git a/hsweb-task/hsweb-task-local/pom.xml b/hsweb-task/hsweb-task-local/pom.xml
new file mode 100644
index 000000000..e248f8c78
--- /dev/null
+++ b/hsweb-task/hsweb-task-local/pom.xml
@@ -0,0 +1,22 @@
+
+
+
+ hsweb-task
+ org.hswebframework.web
+ 3.0.0-RC-SNAPSHOT
+
+ 4.0.0
+
+ hsweb-task-local
+
+
+
+ org.hswebframework.web
+ hsweb-task-core
+ ${project.version}
+
+
+
+
\ No newline at end of file
diff --git a/hsweb-task/pom.xml b/hsweb-task/pom.xml
new file mode 100644
index 000000000..c178c9cdc
--- /dev/null
+++ b/hsweb-task/pom.xml
@@ -0,0 +1,21 @@
+
+
+
+ hsweb-framework
+ org.hswebframework.web
+ 3.0.0-RC-SNAPSHOT
+
+ 4.0.0
+
+ hsweb-task
+ pom
+
+ hsweb-task-core
+ hsweb-task-cluster
+ hsweb-task-local
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 6853d46e5..b0c323332 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,6 +39,7 @@
hsweb-examples
hsweb-boost
hsweb-thirdparty
+ hsweb-task
pom