소스 검색

initial: timertask

BiYuChen 2 년 전
커밋
779b81e4ee
45개의 변경된 파일1520개의 추가작업 그리고 0개의 파일을 삭제
  1. 31 0
      .gitignore
  2. 13 0
      Dockerfile
  3. 143 0
      pom.xml
  4. 3 0
      src/main/java/META-INF/MANIFEST.MF
  5. 17 0
      src/main/java/cn/edu/nju/timer_task_service/TimerTaskServiceApplication.java
  6. 17 0
      src/main/java/cn/edu/nju/timer_task_service/config/RabbitMQConfig.java
  7. 62 0
      src/main/java/cn/edu/nju/timer_task_service/controller/TimerTaskController.java
  8. 24 0
      src/main/java/cn/edu/nju/timer_task_service/data/dao/MessageDAO.java
  9. 14 0
      src/main/java/cn/edu/nju/timer_task_service/data/dao/TaskDAO.java
  10. 40 0
      src/main/java/cn/edu/nju/timer_task_service/data/entity/Message.java
  11. 59 0
      src/main/java/cn/edu/nju/timer_task_service/data/entity/Task.java
  12. 55 0
      src/main/java/cn/edu/nju/timer_task_service/dto/TimerTaskDTO.java
  13. 52 0
      src/main/java/cn/edu/nju/timer_task_service/listener/TimerTaskListener.java
  14. 22 0
      src/main/java/cn/edu/nju/timer_task_service/response/EmptyResponse.java
  15. 21 0
      src/main/java/cn/edu/nju/timer_task_service/response/ErrorResponse.java
  16. 16 0
      src/main/java/cn/edu/nju/timer_task_service/response/ResourceResponse.java
  17. 12 0
      src/main/java/cn/edu/nju/timer_task_service/response/Response.java
  18. 20 0
      src/main/java/cn/edu/nju/timer_task_service/response/TimerTaskResponse.java
  19. 23 0
      src/main/java/cn/edu/nju/timer_task_service/service/ChannelService.java
  20. 19 0
      src/main/java/cn/edu/nju/timer_task_service/service/TaskService.java
  21. 125 0
      src/main/java/cn/edu/nju/timer_task_service/service/impl/TaskServiceImpl.java
  22. 37 0
      src/main/java/cn/edu/nju/timer_task_service/util/ToolKit.java
  23. 28 0
      src/main/java/cn/edu/nju/timer_task_service/util/enums/MessageStatus.java
  24. 31 0
      src/main/java/cn/edu/nju/timer_task_service/util/enums/TaskStatus.java
  25. 21 0
      src/main/java/cn/edu/nju/timer_task_service/util/exceptions/Asserts.java
  26. 15 0
      src/main/java/cn/edu/nju/timer_task_service/util/exceptions/ConflictException.java
  27. 7 0
      src/main/java/cn/edu/nju/timer_task_service/util/exceptions/EntityConflictException.java
  28. 11 0
      src/main/java/cn/edu/nju/timer_task_service/util/exceptions/EntityGoneException.java
  29. 11 0
      src/main/java/cn/edu/nju/timer_task_service/util/exceptions/EntityNotAvailableException.java
  30. 15 0
      src/main/java/cn/edu/nju/timer_task_service/util/exceptions/EntityNotFoundException.java
  31. 19 0
      src/main/java/cn/edu/nju/timer_task_service/util/exceptions/GoneException.java
  32. 15 0
      src/main/java/cn/edu/nju/timer_task_service/util/exceptions/InvalidRequestException.java
  33. 36 0
      src/main/java/cn/edu/nju/timer_task_service/util/exceptions/ServiceException.java
  34. 111 0
      src/main/java/cn/edu/nju/timer_task_service/util/httpHepler/HttpHelper.java
  35. 28 0
      src/main/java/cn/edu/nju/timer_task_service/util/serializer/ContentDeserializer.java
  36. 41 0
      src/main/java/cn/edu/nju/timer_task_service/util/serializer/TimestampLocalDateDeserializer.java
  37. 29 0
      src/main/java/cn/edu/nju/timer_task_service/util/serializer/TimestampLocalDateTimeSerializer.java
  38. 41 0
      src/main/java/cn/edu/nju/timer_task_service/util/validate/FieldCompare.java
  39. 40 0
      src/main/java/cn/edu/nju/timer_task_service/util/validate/FieldEquals.java
  40. 38 0
      src/main/java/cn/edu/nju/timer_task_service/util/validate/validator/FieldCompareValidator.java
  41. 30 0
      src/main/java/cn/edu/nju/timer_task_service/util/validate/validator/FieldEqualsValidator.java
  42. 36 0
      src/main/java/cn/edu/nju/timer_task_service/vo/TaskVO.java
  43. 53 0
      src/main/resources/application.yml
  44. 13 0
      src/test/java/cn/edu/nju/timer_task_service/TimerTaskServiceApplicationTests.java
  45. 26 0
      src/test/java/cn/edu/nju/timer_task_service/test.java

+ 31 - 0
.gitignore

@@ -0,0 +1,31 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**
+!**/src/test/**
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+
+### VS Code ###
+.vscode/

+ 13 - 0
Dockerfile

@@ -0,0 +1,13 @@
+FROM maven:3.6.1 AS compile_stage
+ENV PROJECT_NAME app
+ENV WORK_PATH /opt/$PROJECT_NAME
+COPY settings.xml /root/.m2/settings.xml
+COPY . $WORK_PATH
+RUN cd $WORK_PATH && mvn clean install -DskipTests -DfinalName=$PROJECT_NAME
+
+FROM openjdk:8-jre-alpine
+ENV PROJECT_NAME app
+ENV WORK_PATH /opt/$PROJECT_NAME
+WORKDIR /app
+COPY --from=compile_stage $WORK_PATH/target/${PROJECT_NAME}.jar .
+CMD ["sh", "-c", "java -Xdebug -Xrunjdwp:transport=dt_socket,address=8888,server=y,suspend=n -jar -server /app/${PROJECT_NAME}.jar"]

+ 143 - 0
pom.xml

@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.2.2.RELEASE</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+    <groupId>cn.edu.nju</groupId>
+    <artifactId>timer_task_service</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <name>timer_task_service</name>
+    <description>timer task service for moocoder</description>
+
+    <properties>
+        <java.version>1.8</java.version>
+        <spring-cloud.version>Hoxton.SR1</spring-cloud.version>
+    </properties>
+
+    <dependencies>
+        <!-- Spring Boot Auto-Configurable Dependencies -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-jpa</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-jdbc</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+        </dependency>
+
+        <!-- Hibernate Dependencies -->
+        <dependency>
+            <groupId>org.hibernate</groupId>
+            <artifactId>hibernate-java8</artifactId>
+        </dependency>
+
+         <!--Database Driver Dependencies-->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+
+
+        <!--Spring Cloud Dependencies-->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-eureka</artifactId>
+            <version>1.4.6.RELEASE</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-feign</artifactId>
+            <version>1.4.6.RELEASE</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-hystrix</artifactId>
+            <version>1.4.6.RELEASE</version>
+        </dependency>
+
+        <!--Swagger2 Dependencies-->
+        <dependency>
+            <groupId>com.spring4all</groupId>
+            <artifactId>swagger-spring-boot-starter</artifactId>
+            <version>1.8.0.RELEASE</version>
+        </dependency>
+
+        <!--Lombok Dependencies-->
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.18.4</version>
+        </dependency>
+
+        <!--RabbitMq and Spring Cloud Stream Dependencies-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-amqp</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
+            <version>2.0.1.RELEASE</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.json</groupId>
+            <artifactId>json</artifactId>
+            <version>20180130</version>
+        </dependency>
+
+        <!-- Test Dependencies -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.junit.vintage</groupId>
+                    <artifactId>junit-vintage-engine</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+    </dependencies>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.springframework.cloud</groupId>
+                <artifactId>spring-cloud-dependencies</artifactId>
+                <version>${spring-cloud.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>2.2.6.RELEASE</version>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 3 - 0
src/main/java/META-INF/MANIFEST.MF

@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: cn.edu.nju.timer_task_service.TimerTaskServiceApplication
+

+ 17 - 0
src/main/java/cn/edu/nju/timer_task_service/TimerTaskServiceApplication.java

@@ -0,0 +1,17 @@
+package cn.edu.nju.timer_task_service;
+
+import com.spring4all.swagger.EnableSwagger2Doc;
+import org.springframework.boot.SpringApplication;
+import org.springframework.cloud.client.SpringCloudApplication;
+/**
+ * @author fjj
+ */
+@EnableSwagger2Doc
+@SpringCloudApplication
+public class TimerTaskServiceApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(TimerTaskServiceApplication.class, args);
+    }
+
+}

+ 17 - 0
src/main/java/cn/edu/nju/timer_task_service/config/RabbitMQConfig.java

@@ -0,0 +1,17 @@
+package cn.edu.nju.timer_task_service.config;
+
+import org.springframework.amqp.core.Queue;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author fjj
+ * @date 2019/12/25 5:40 PM
+ */
+@Configuration
+public class RabbitMQConfig {
+    @Bean
+    public Queue TimerTaskQueue() {
+        return new Queue("timer_task");
+    }
+}

+ 62 - 0
src/main/java/cn/edu/nju/timer_task_service/controller/TimerTaskController.java

@@ -0,0 +1,62 @@
+package cn.edu.nju.timer_task_service.controller;
+
+import cn.edu.nju.timer_task_service.dto.TimerTaskDTO;
+import cn.edu.nju.timer_task_service.service.TaskService;
+import cn.edu.nju.timer_task_service.util.exceptions.InvalidRequestException;
+import cn.edu.nju.timer_task_service.util.exceptions.ServiceException;
+import cn.edu.nju.timer_task_service.vo.TaskVO;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.Errors;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+/**
+ * @author fjj
+ * @date 2019/12/28 10:52 PM
+ */
+@Slf4j
+@RestController
+@RequestMapping("/api")
+public class TimerTaskController {
+    @Autowired
+    private TaskService taskService;
+
+    @PostMapping("/timerTask")
+    @ApiOperation(value = "创建定时任务,uuid不用传")
+    public TaskVO create(@RequestBody @Validated(PostMapping.class) TimerTaskDTO timerTaskDTO, Errors errors) throws ServiceException {
+        checkErrors(errors);
+        return taskService.create(timerTaskDTO);
+    }
+
+    @PutMapping("/timerTask")
+    @ApiOperation(value = "修改定时任务")
+    public TaskVO update(@RequestBody @Validated(PutMapping.class) TimerTaskDTO timerTaskDTO, Errors errors) throws ServiceException {
+        checkErrors(errors);
+        return taskService.update(timerTaskDTO);
+    }
+
+    @DeleteMapping("/timerTask/{uuid}")
+    @ApiOperation(value = "删除定时任务")
+    public void delete(@PathVariable String uuid) throws ServiceException {
+        taskService.delete(uuid);
+    }
+
+    @GetMapping("/timerTask/{uuid}")
+    @ApiOperation(value = "查找taskId对应的定时任务信息")
+    public TaskVO get(@PathVariable String uuid) throws ServiceException {
+        return taskService.get(uuid);
+    }
+
+
+    protected void checkErrors(Errors errors) throws ServiceException {
+        if (errors.hasGlobalErrors()) {
+            throw new InvalidRequestException(errors.getGlobalError().getDefaultMessage());
+        }
+        if (errors.hasFieldErrors()) {
+            throw new InvalidRequestException(errors.getFieldError().getDefaultMessage());
+        }
+    }
+
+
+}

+ 24 - 0
src/main/java/cn/edu/nju/timer_task_service/data/dao/MessageDAO.java

@@ -0,0 +1,24 @@
+package cn.edu.nju.timer_task_service.data.dao;
+
+import cn.edu.nju.timer_task_service.data.entity.Message;
+import cn.edu.nju.timer_task_service.data.entity.Task;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ * @author fjj
+ * @date 2019/12/30 11:55 PM
+ */
+@Repository
+public interface MessageDAO extends JpaRepository<Message, String> {
+//    List<Message> findAllByTask(Task task);
+
+    Message findByUuid(String uuid);
+
+//    Message findFirstByTaskOrderByCreatedAtDesc(Task task);
+
+    Message findByTask(Task task);
+
+}

+ 14 - 0
src/main/java/cn/edu/nju/timer_task_service/data/dao/TaskDAO.java

@@ -0,0 +1,14 @@
+package cn.edu.nju.timer_task_service.data.dao;
+
+import cn.edu.nju.timer_task_service.data.entity.Task;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+/**
+ * @author fjj
+ * @date 2019/12/30 11:53 PM
+ */
+@Repository
+public interface TaskDAO extends JpaRepository<Task,String> {
+    Task findByUuid(String uuid);
+}

+ 40 - 0
src/main/java/cn/edu/nju/timer_task_service/data/entity/Message.java

@@ -0,0 +1,40 @@
+package cn.edu.nju.timer_task_service.data.entity;
+
+import cn.edu.nju.timer_task_service.util.enums.MessageStatus;
+import lombok.Data;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+import javax.persistence.*;
+import java.time.LocalDateTime;
+
+/**
+ * @author fjj
+ * @date 2019/12/30 9:53 PM
+ */
+@Data
+@Entity
+@Table(name = "ENTITY_MESSAGE")
+@EntityListeners(AuditingEntityListener.class)
+public class Message {
+    @Id
+    @Column(name = "id", nullable = false)
+    private String uuid;
+
+//    @Enumerated(value = EnumType.STRING)
+//    @Column(name = "status", columnDefinition = "VARCHAR(255) NOT NULL DEFAULT 'NORMAL'")
+//    private MessageStatus status = MessageStatus.NORMAL;
+
+//    @ManyToOne(targetEntity = Task.class, fetch = FetchType.LAZY)
+//    @JoinColumn(name = "task", referencedColumnName = "id")
+//    private Task task;
+
+    @OneToOne(targetEntity = Task.class, fetch = FetchType.LAZY)
+    @JoinColumn(name = "task", referencedColumnName = "id")
+    private Task task;
+
+    @Column(name = "created_at", nullable = false)
+    @CreatedDate
+    private LocalDateTime createdAt;
+
+}

+ 59 - 0
src/main/java/cn/edu/nju/timer_task_service/data/entity/Task.java

@@ -0,0 +1,59 @@
+package cn.edu.nju.timer_task_service.data.entity;
+
+import cn.edu.nju.timer_task_service.util.enums.TaskStatus;
+import lombok.Data;
+import org.hibernate.annotations.LazyCollection;
+import org.hibernate.annotations.LazyCollectionOption;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+import javax.persistence.*;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author fjj
+ * @date 2019/12/30 9:31 PM
+ */
+@Data
+@Entity
+@Table(name = "ENTITY_TASK")
+@EntityListeners(AuditingEntityListener.class)
+public class Task {
+    @Id
+    @Column(name = "id", nullable = false)
+    private String uuid;
+
+    @Column(name = "callback_url", nullable = false)
+    private String callbackUrl;
+
+    @Basic(fetch = FetchType.LAZY)
+    @Column(name = "content", columnDefinition = "TEXT NOT NULL")
+    private String content;
+
+    @Enumerated(value = EnumType.STRING)
+    @Column(name = "status", columnDefinition = "VARCHAR(255) NOT NULL DEFAULT 'NORMAL'")
+    private TaskStatus status = TaskStatus.NORMAL;
+
+    /**
+     * 触发定时任务的时间
+     */
+    @Column(name = "time", nullable = false)
+    private LocalDateTime time;
+
+    @Column(name = "created_at", nullable = false)
+    @CreatedDate
+    private LocalDateTime createdAt;
+
+//    @OneToMany(targetEntity = Message.class, mappedBy = "task", cascade = CascadeType.ALL)
+//    @LazyCollection(LazyCollectionOption.EXTRA)
+//    @OrderBy("created_at DESC")
+//    private List<Message> messages = new ArrayList<>();
+
+    @OneToOne(targetEntity = Message.class, mappedBy = "task", cascade = CascadeType.ALL)
+    @LazyCollection(LazyCollectionOption.EXTRA)
+    private Message message;
+}

+ 55 - 0
src/main/java/cn/edu/nju/timer_task_service/dto/TimerTaskDTO.java

@@ -0,0 +1,55 @@
+package cn.edu.nju.timer_task_service.dto;
+
+import cn.edu.nju.timer_task_service.util.serializer.ContentDeserializer;
+import cn.edu.nju.timer_task_service.util.serializer.TimestampLocalDateDeserializer;
+import cn.edu.nju.timer_task_service.util.validate.FieldCompare;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import lombok.Data;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+
+/**
+ * @author fjj
+ * @date 2019/12/30 6:20 PM
+ */
+@Data
+@FieldCompare.List({
+        @FieldCompare(lessField = "now", greaterField = "time", message = "定时任务触发时间必须晚于此刻时间", groups = {PostMapping.class, PutMapping.class})
+})
+public class TimerTaskDTO {
+    /**
+     * 回调地址
+     */
+    @NotBlank(message = "请填写回调地址!", groups = {PostMapping.class, PutMapping.class})
+    private String callbackUrl;
+    /**
+     * 在这个时间点触发回调
+     */
+    @JsonDeserialize(using = TimestampLocalDateDeserializer.class)
+    @NotNull(message = "请填写触发回调的时间点", groups = {PostMapping.class, PutMapping.class})
+    private LocalDateTime time;
+    /**
+     * 发生回调时,timer_task_service将会用get方式请求callbackUrl,并传递此message
+     */
+    @JsonDeserialize(using = ContentDeserializer.class)
+    @NotBlank(message = "请填写需要回传的json格式的内容!", groups = {PostMapping.class, PutMapping.class})
+    private String content;
+
+    /**
+     * 定时任务创建成功后会返回一个uuid给调用方
+     * 修改或者移除定时任务的时候调用方需要提供uuid
+     */
+    @NotBlank(message = "请填写此定时任务的uuid!", groups = {PutMapping.class})
+    private String uuid;
+
+    /**
+     * 用于比较定时任务时间
+     */
+    @JsonIgnore
+    private final LocalDateTime now = LocalDateTime.now();
+}

+ 52 - 0
src/main/java/cn/edu/nju/timer_task_service/listener/TimerTaskListener.java

@@ -0,0 +1,52 @@
+package cn.edu.nju.timer_task_service.listener;
+
+import cn.edu.nju.timer_task_service.data.dao.MessageDAO;
+import cn.edu.nju.timer_task_service.data.dao.TaskDAO;
+import cn.edu.nju.timer_task_service.data.entity.Message;
+import cn.edu.nju.timer_task_service.data.entity.Task;
+import cn.edu.nju.timer_task_service.service.ChannelService;
+import cn.edu.nju.timer_task_service.util.enums.TaskStatus;
+import cn.edu.nju.timer_task_service.util.httpHepler.HttpHelper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.stream.annotation.EnableBinding;
+import org.springframework.cloud.stream.annotation.StreamListener;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * @author fjj
+ * @date 2019/12/28 10:53 PM
+ */
+@Slf4j
+@Component
+@EnableBinding({ChannelService.class})
+public class TimerTaskListener {
+
+    @Autowired
+    private MessageDAO messageDAO;
+    @Autowired
+    private TaskDAO taskDAO;
+
+    @StreamListener(ChannelService.INPUT)
+    @Transactional
+    public void receive(String messageUuid) {
+        try {
+            Message message = messageDAO.findByUuid(messageUuid);
+            if (message != null) {
+                log.info("Received: " + messageUuid);
+                Task task = taskDAO.findByUuid(message.getTask().getUuid());
+                boolean isSuccess = HttpHelper.sendPost(task.getCallbackUrl(), task.getContent());
+                task.setStatus(isSuccess ? TaskStatus.SUCCESS : TaskStatus.FAILED);
+                taskDAO.save(task);
+//                message.setStatus(MessageStatus.COMPLETED);
+//                messageDAO.save(message);
+            } else {
+                log.info("this message is deleted:" + messageUuid);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+    }
+}

+ 22 - 0
src/main/java/cn/edu/nju/timer_task_service/response/EmptyResponse.java

@@ -0,0 +1,22 @@
+package cn.edu.nju.timer_task_service.response;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * @author fjj
+ * @date 2019/12/28 11:11 PM
+ */
+@Data
+public class EmptyResponse implements Response {
+    @JsonProperty("err")
+    private int error;
+
+    public EmptyResponse() {
+        this.error = 0;
+    }
+
+    protected EmptyResponse(int error) {
+        this.error = error;
+    }
+}

+ 21 - 0
src/main/java/cn/edu/nju/timer_task_service/response/ErrorResponse.java

@@ -0,0 +1,21 @@
+package cn.edu.nju.timer_task_service.response;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * @author fjj
+ * @date 2019/12/28 11:14 PM
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ErrorResponse extends EmptyResponse {
+    @JsonProperty("msg")
+    private String message;
+
+    public ErrorResponse(int err, String message) {
+        super(err);
+        this.message = message;
+    }
+}

+ 16 - 0
src/main/java/cn/edu/nju/timer_task_service/response/ResourceResponse.java

@@ -0,0 +1,16 @@
+package cn.edu.nju.timer_task_service.response;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ResourceResponse extends EmptyResponse {
+	@JsonProperty("res")
+	private Object resource;
+
+	public ResourceResponse(Object resource) {
+		this.resource = resource;
+	}
+}

+ 12 - 0
src/main/java/cn/edu/nju/timer_task_service/response/Response.java

@@ -0,0 +1,12 @@
+package cn.edu.nju.timer_task_service.response;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+/**
+ * @author fjj
+ * @date 2019/12/28 11:08 PM
+ */
+@FunctionalInterface
+public interface Response {
+    int getError();
+}

+ 20 - 0
src/main/java/cn/edu/nju/timer_task_service/response/TimerTaskResponse.java

@@ -0,0 +1,20 @@
+package cn.edu.nju.timer_task_service.response;
+
+
+import cn.edu.nju.timer_task_service.vo.TaskVO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * @author fjj
+ * @date 2020/3/6 3:56 PM
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class TimerTaskResponse extends EmptyResponse {
+    private Object task;
+
+    public TimerTaskResponse(Object task) {
+        this.task = task;
+    }
+}

+ 23 - 0
src/main/java/cn/edu/nju/timer_task_service/service/ChannelService.java

@@ -0,0 +1,23 @@
+package cn.edu.nju.timer_task_service.service;
+
+import org.springframework.cloud.stream.annotation.Input;
+import org.springframework.cloud.stream.annotation.Output;
+import org.springframework.messaging.MessageChannel;
+import org.springframework.messaging.SubscribableChannel;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author fjj
+ * @date 2019/12/28 10:50 PM
+ */
+@Service
+public interface ChannelService {
+    String OUTPUT = "timer-task-output";
+    String INPUT = "timer-task-input";
+
+    @Output(OUTPUT)
+    MessageChannel output();
+
+    @Input(INPUT)
+    SubscribableChannel input();
+}

+ 19 - 0
src/main/java/cn/edu/nju/timer_task_service/service/TaskService.java

@@ -0,0 +1,19 @@
+package cn.edu.nju.timer_task_service.service;
+
+import cn.edu.nju.timer_task_service.dto.TimerTaskDTO;
+import cn.edu.nju.timer_task_service.util.exceptions.ServiceException;
+import cn.edu.nju.timer_task_service.vo.TaskVO;
+
+/**
+ * @author fjj
+ * @date 2019/12/30 10:14 PM
+ */
+public interface TaskService {
+    TaskVO create(TimerTaskDTO timerTaskDTO) throws ServiceException;
+
+    TaskVO update(TimerTaskDTO timerTaskDTO) throws ServiceException;
+
+    void delete(String uuid) throws ServiceException;
+
+    TaskVO get(String uuid) throws ServiceException;
+}

+ 125 - 0
src/main/java/cn/edu/nju/timer_task_service/service/impl/TaskServiceImpl.java

@@ -0,0 +1,125 @@
+package cn.edu.nju.timer_task_service.service.impl;
+
+
+import cn.edu.nju.timer_task_service.data.dao.MessageDAO;
+import cn.edu.nju.timer_task_service.data.dao.TaskDAO;
+import cn.edu.nju.timer_task_service.data.entity.Message;
+import cn.edu.nju.timer_task_service.data.entity.Task;
+import cn.edu.nju.timer_task_service.service.ChannelService;
+
+import cn.edu.nju.timer_task_service.dto.TimerTaskDTO;
+import cn.edu.nju.timer_task_service.service.TaskService;
+import cn.edu.nju.timer_task_service.util.ToolKit;
+import cn.edu.nju.timer_task_service.util.enums.TaskStatus;
+import cn.edu.nju.timer_task_service.util.exceptions.Asserts;
+import cn.edu.nju.timer_task_service.util.exceptions.EntityNotAvailableException;
+import cn.edu.nju.timer_task_service.util.exceptions.ServiceException;
+import cn.edu.nju.timer_task_service.vo.TaskVO;
+import lombok.extern.apachecommons.CommonsLog;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.messaging.support.MessageBuilder;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.List;
+
+/**
+ * @author fjj
+ * @date 2019/12/30 10:15 PM
+ */
+@Service
+@CommonsLog
+public class TaskServiceImpl implements TaskService {
+    @Autowired
+    private ChannelService channelService;
+    @Autowired
+    private TaskDAO taskDAO;
+    @Autowired
+    private MessageDAO messageDAO;
+
+    //    @Override
+//    @Transactional
+//    public TaskVO create(TimerTaskDTO timerTaskDTO) throws ServiceException {
+//        LocalDateTime now = LocalDateTime.now();
+//        System.out.println(now);
+//        Task task = new Task();
+//        BeanUtils.copyProperties(timerTaskDTO, task);
+//        task.setUuid(ToolKit.randomUUID());
+//        task.setCreatedAt(now);
+//        List<Message> messages = messageDAO.findAllByTask(task);
+//        task = taskDAO.save(task);
+//        Message message = createNewMessage(task);
+//        messages.add(message);
+//        //将消息加入到rabbitmq
+//        long delay = task.getTime().toInstant(ZoneOffset.of("+8")).toEpochMilli() - System.currentTimeMillis();
+//        channelService.output().send(MessageBuilder.withPayload(message.getUuid()).setHeader("x-delay", delay).build());
+//        task.setMessages(messages);
+//        return new TaskVO(taskDAO.save(task));
+//    }
+    @Override
+    @Transactional
+    public TaskVO create(TimerTaskDTO timerTaskDTO) throws ServiceException {
+        LocalDateTime now = LocalDateTime.now();
+        System.out.println(now);
+        Task task = new Task();
+        BeanUtils.copyProperties(timerTaskDTO, task);
+        task.setUuid(ToolKit.randomUUID());
+        task.setCreatedAt(now);
+        Message message = createNewMessage(task);
+        //将消息加入到rabbitmq
+        long delay = task.getTime().toInstant(ZoneOffset.of("+8")).toEpochMilli() - System.currentTimeMillis();
+        channelService.output().send(MessageBuilder.withPayload(message.getUuid()).setHeader("x-delay", delay).build());
+        task.setMessage(message);
+        return new TaskVO(taskDAO.save(task));
+    }
+
+    @Override
+    @Transactional
+    public TaskVO update(TimerTaskDTO timerTaskDTO) throws ServiceException {
+        Task task = taskDAO.findByUuid(timerTaskDTO.getUuid());
+        Asserts.notNull(task, "该定时任务不存在");
+        if (task.getStatus() != TaskStatus.NORMAL) {
+            throw new EntityNotAvailableException("该定时任务已经执行完毕");
+        }
+        task.setCallbackUrl(timerTaskDTO.getCallbackUrl());
+        task.setContent(timerTaskDTO.getContent());
+        if (!task.getTime().equals(timerTaskDTO.getTime())) {
+            task.setTime(timerTaskDTO.getTime());
+            Message oldMessage = messageDAO.findByTask(task);
+            messageDAO.delete(oldMessage);
+            // 创建新的message(数据库 and Rabbitmq)
+            Message newMessage = createNewMessage(task);
+            //将消息加入到rabbitmq
+            long delay = task.getTime().toInstant(ZoneOffset.of("+8")).toEpochMilli() - System.currentTimeMillis();
+            channelService.output().send(MessageBuilder.withPayload(newMessage.getUuid()).setHeader("x-delay", delay).build());
+            task.setMessage(newMessage);
+        }
+        return new TaskVO(taskDAO.save(task));
+    }
+
+    @Override
+    public void delete(String uuid) throws ServiceException {
+        Task task = taskDAO.findByUuid(uuid);
+        Asserts.notNull(task, "该定时任务不存在");
+        taskDAO.delete(task);
+    }
+
+    @Override
+    public TaskVO get(String uuid) throws ServiceException {
+        Task task = taskDAO.findByUuid(uuid);
+        Asserts.notNull(task, "该定时任务不存在");
+        return new TaskVO(task);
+    }
+
+    private Message createNewMessage(Task task) {
+        Message message = new Message();
+        message.setTask(task);
+        message.setCreatedAt(LocalDateTime.now());
+        message.setUuid(ToolKit.randomUUID());
+        return message;
+    }
+}

+ 37 - 0
src/main/java/cn/edu/nju/timer_task_service/util/ToolKit.java

@@ -0,0 +1,37 @@
+package cn.edu.nju.timer_task_service.util;
+
+import org.apache.commons.lang.RandomStringUtils;
+import org.springframework.http.HttpStatus;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.UUID;
+
+/**
+ * @author fjj
+ */
+public abstract class ToolKit {
+
+	public static String format(Exception exception) {
+		return "[" + exception.getClass().getName() + "]:" + exception.getMessage();
+	}
+
+	public static HttpStatus httpStatus(HttpServletRequest request) {
+		Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
+		if (statusCode == null) {
+			return HttpStatus.INTERNAL_SERVER_ERROR;
+		}
+		try {
+			return HttpStatus.valueOf(statusCode);
+		} catch (Exception e) {
+			return HttpStatus.INTERNAL_SERVER_ERROR;
+		}
+	}
+
+	public static String randomUUID() {
+		return UUID.randomUUID().toString().replace("-", "");
+	}
+
+	public static String randomPassword() {
+		return RandomStringUtils.randomAlphanumeric(12);
+	}
+}

+ 28 - 0
src/main/java/cn/edu/nju/timer_task_service/util/enums/MessageStatus.java

@@ -0,0 +1,28 @@
+package cn.edu.nju.timer_task_service.util.enums;
+
+/**
+ * @author fjj
+ * @date 2019/12/30 9:34 PM
+ */
+public enum MessageStatus {
+    /**
+     * 正常状态(创建或者修改定时任务)
+     */
+    NORMAL("正常"),
+    /**
+     * 消息被消费后的状态(可能成功或失败)
+     */
+    COMPLETED("已完成");
+
+    private final String desc;
+
+    MessageStatus(String desc) {
+        this.desc = desc;
+    }
+
+    @Override
+    public String toString() {
+        return desc;
+    }
+
+    }

+ 31 - 0
src/main/java/cn/edu/nju/timer_task_service/util/enums/TaskStatus.java

@@ -0,0 +1,31 @@
+package cn.edu.nju.timer_task_service.util.enums;
+
+/**
+ * @author fjj
+ * @date 2019/12/31 2:27 PM
+ */
+public enum TaskStatus {
+    /**
+     * 创建定时任务时,表示该定时任务尚未开始
+     */
+    NORMAL("正常"),
+    /**
+     * 到预定时间发起post请求的响应状态为成功
+     */
+    SUCCESS("回调成功"),
+    /**
+     * 到预定时间发起post请求的响应状态为失败
+     */
+    FAILED("回调失败");
+
+    private String desc;
+
+    TaskStatus(String desc) {
+        this.desc = desc;
+    }
+
+    @Override
+    public String toString() {
+        return desc;
+    }
+}

+ 21 - 0
src/main/java/cn/edu/nju/timer_task_service/util/exceptions/Asserts.java

@@ -0,0 +1,21 @@
+package cn.edu.nju.timer_task_service.util.exceptions;
+
+public abstract class Asserts {
+	public static void notNull(Object object) throws EntityNotFoundException {
+		if (object == null) {
+			throw new EntityNotFoundException();
+		}
+	}
+
+	public static void notNull(Object object, String message) throws EntityNotFoundException {
+		if (object == null) {
+			throw new EntityNotFoundException(message);
+		}
+	}
+
+	public static void isNull(Object object, String message) throws EntityConflictException {
+		if (object != null) {
+			throw new EntityConflictException(message);
+		}
+	}
+}

+ 15 - 0
src/main/java/cn/edu/nju/timer_task_service/util/exceptions/ConflictException.java

@@ -0,0 +1,15 @@
+package cn.edu.nju.timer_task_service.util.exceptions;
+
+import org.springframework.http.HttpStatus;
+
+public class ConflictException extends ServiceException {
+	private static final HttpStatus HTTP_STATUS = HttpStatus.CONFLICT;
+
+	public ConflictException() {
+		this(HTTP_STATUS.getReasonPhrase());
+	}
+
+	public ConflictException(String message) {
+		super(HTTP_STATUS.value(), message);
+	}
+}

+ 7 - 0
src/main/java/cn/edu/nju/timer_task_service/util/exceptions/EntityConflictException.java

@@ -0,0 +1,7 @@
+package cn.edu.nju.timer_task_service.util.exceptions;
+
+public class EntityConflictException extends ConflictException {
+	public EntityConflictException(String message) {
+		super(message);
+	}
+}

+ 11 - 0
src/main/java/cn/edu/nju/timer_task_service/util/exceptions/EntityGoneException.java

@@ -0,0 +1,11 @@
+package cn.edu.nju.timer_task_service.util.exceptions;
+
+/**
+ * @author fjj
+ * @date 2019/12/31 2:56 PM
+ */
+public class EntityGoneException extends GoneException {
+    public EntityGoneException(String message) {
+        super(message);
+    }
+}

+ 11 - 0
src/main/java/cn/edu/nju/timer_task_service/util/exceptions/EntityNotAvailableException.java

@@ -0,0 +1,11 @@
+package cn.edu.nju.timer_task_service.util.exceptions;
+
+import org.springframework.http.HttpStatus;
+
+public class EntityNotAvailableException extends ServiceException {
+	private static final HttpStatus HTTP_STATUS = HttpStatus.UNPROCESSABLE_ENTITY;
+
+	public EntityNotAvailableException(String message) {
+		super(HTTP_STATUS.value(), message);
+	}
+}

+ 15 - 0
src/main/java/cn/edu/nju/timer_task_service/util/exceptions/EntityNotFoundException.java

@@ -0,0 +1,15 @@
+package cn.edu.nju.timer_task_service.util.exceptions;
+
+import org.springframework.http.HttpStatus;
+
+public class EntityNotFoundException extends ServiceException {
+	private static final HttpStatus HTTP_STATUS = HttpStatus.NOT_FOUND;
+
+	public EntityNotFoundException() {
+		this(HTTP_STATUS.getReasonPhrase());
+	}
+
+	public EntityNotFoundException(String message) {
+		super(HTTP_STATUS.value(), message);
+	}
+}

+ 19 - 0
src/main/java/cn/edu/nju/timer_task_service/util/exceptions/GoneException.java

@@ -0,0 +1,19 @@
+package cn.edu.nju.timer_task_service.util.exceptions;
+
+import org.springframework.http.HttpStatus;
+
+/**
+ * @author fjj
+ * @date 2019/12/31 2:53 PM
+ */
+public class GoneException extends ServiceException {
+    private static final HttpStatus HTTP_STATUS = HttpStatus.GONE;
+
+    public GoneException() {
+        this(HTTP_STATUS.getReasonPhrase());
+    }
+
+    public GoneException(String message) {
+        super(HTTP_STATUS.value(), message);
+    }
+}

+ 15 - 0
src/main/java/cn/edu/nju/timer_task_service/util/exceptions/InvalidRequestException.java

@@ -0,0 +1,15 @@
+package cn.edu.nju.timer_task_service.util.exceptions;
+
+import org.springframework.http.HttpStatus;
+
+public class InvalidRequestException extends ServiceException {
+	private static final HttpStatus HTTP_STATUS = HttpStatus.BAD_REQUEST;
+
+	public InvalidRequestException() {
+		this(HTTP_STATUS.getReasonPhrase());
+	}
+
+	public InvalidRequestException(String message) {
+		super(HTTP_STATUS.value(), message);
+	}
+}

+ 36 - 0
src/main/java/cn/edu/nju/timer_task_service/util/exceptions/ServiceException.java

@@ -0,0 +1,36 @@
+package cn.edu.nju.timer_task_service.util.exceptions;
+
+import lombok.Getter;
+import lombok.ToString;
+import org.springframework.http.HttpStatus;
+
+@Getter
+@ToString
+public class ServiceException extends Exception {
+	private int error;
+
+	public ServiceException() {
+		this(HttpStatus.INTERNAL_SERVER_ERROR.value(), HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());
+	}
+
+	public ServiceException(int error) {
+		this(error, message(error));
+	}
+
+	public ServiceException(String message) {
+		this(HttpStatus.INTERNAL_SERVER_ERROR.value(), message);
+	}
+
+	public ServiceException(int error, String message) {
+		super(message);
+		this.error = error;
+	}
+
+	private static String message(int error) {
+		try {
+			return HttpStatus.valueOf(error).getReasonPhrase();
+		} catch (IllegalArgumentException e) {
+			return HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase();
+		}
+	}
+}

+ 111 - 0
src/main/java/cn/edu/nju/timer_task_service/util/httpHepler/HttpHelper.java

@@ -0,0 +1,111 @@
+package cn.edu.nju.timer_task_service.util.httpHepler;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.http.*;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.config.ConnectionConfig;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.util.EntityUtils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.CodingErrorAction;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author fjj
+ * @date 2019/12/31 12:35 PM
+ */
+@Slf4j
+public class HttpHelper {
+    private static final String CHARSET = "utf-8";
+    private static final String CONTENT_TYPE = "application/json";
+    private static final String X_MOOCODER_SECRET = "1qaz2wsx";
+
+    private static final String USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36";
+
+    // 超时设置
+    private static final RequestConfig requestConfig = RequestConfig.custom()
+            .setConnectTimeout(5000)
+            .setConnectionRequestTimeout(5000)
+            .setSocketTimeout(10000)
+            .build();
+
+    // 编码设置
+    private static final ConnectionConfig connectionConfig = ConnectionConfig.custom()
+            .setMalformedInputAction(CodingErrorAction.IGNORE)
+            .setUnmappableInputAction(CodingErrorAction.IGNORE)
+            .setCharset(Consts.UTF_8)
+            .build();
+
+
+    public static boolean post(String callbackUrl, String json) {
+
+        HttpClient client = new DefaultHttpClient();
+        HttpPost post = new HttpPost(callbackUrl);
+        post.setHeader("Content-Type", CONTENT_TYPE);
+//        post.addHeader("Authorization", "123456");
+        try {
+            StringEntity stringEntity = new StringEntity(json, CHARSET);
+            stringEntity.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, CONTENT_TYPE));
+            post.setEntity(stringEntity);
+            HttpResponse httpResponse = client.execute(post);
+            if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
+                return true;
+            } else {
+
+                return false;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    private static HttpClientBuilder getBuilder() {
+        List<Header> headers = new ArrayList<>();
+        Header header = new BasicHeader("User-Agent", USER_AGENT);
+        Header secretHeader = new BasicHeader("X-Moocoder-Secret", X_MOOCODER_SECRET);
+        headers.add(header);
+        headers.add(secretHeader);
+        return HttpClients.custom().setDefaultConnectionConfig(connectionConfig).setDefaultHeaders(headers).setDefaultRequestConfig(requestConfig);
+    }
+
+    public static boolean sendPost(String url, String jsonStr) {
+        String result = "";
+
+        // 设置entity
+        StringEntity stringEntity = new StringEntity(jsonStr, Consts.UTF_8);
+        stringEntity.setContentType("application/json");
+
+        HttpPost httpPost = new HttpPost(url);
+        httpPost.setEntity(stringEntity);
+
+        try (CloseableHttpClient httpclient = getBuilder().build(); CloseableHttpResponse httpResponse = httpclient.execute(httpPost)) {
+            if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { //TODO 如果不是200,log错误信息
+                return true;
+            } else {
+                log.info("HttpPost请求失败,状态码为"+httpResponse.getStatusLine().getStatusCode()+",原因为"+httpResponse.getStatusLine().getReasonPhrase());
+                return false;
+            }
+        } catch (ClientProtocolException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return false;
+    }
+}

+ 28 - 0
src/main/java/cn/edu/nju/timer_task_service/util/serializer/ContentDeserializer.java

@@ -0,0 +1,28 @@
+package cn.edu.nju.timer_task_service.util.serializer;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.io.IOException;
+
+/**
+ * @author fjj
+ * @date 2019/12/31 12:16 PM
+ */
+public class ContentDeserializer extends JsonDeserializer<String> {
+    @Override
+    public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
+        try {
+            String jsonInString = jsonParser.getText().trim();
+            final ObjectMapper mapper = new ObjectMapper();
+            mapper.readTree(jsonInString);
+            return jsonInString;
+        } catch (IOException e) {
+            return null;
+        }
+
+    }
+}

+ 41 - 0
src/main/java/cn/edu/nju/timer_task_service/util/serializer/TimestampLocalDateDeserializer.java

@@ -0,0 +1,41 @@
+package cn.edu.nju.timer_task_service.util.serializer;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeParseException;
+
+/**
+ * @author fjj
+ * @date 2019/12/30 6:36 PM
+ */
+public class TimestampLocalDateDeserializer extends JsonDeserializer<LocalDateTime> {
+    @Override
+    public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
+        Long epochSecond = null;
+        if (jsonParser.hasToken(JsonToken.VALUE_NUMBER_INT)) {
+            epochSecond = jsonParser.getLongValue();
+        } else if (jsonParser.hasToken(JsonToken.VALUE_STRING)) {
+            String value = jsonParser.getText().trim();
+            try{
+                return LocalDateTime.parse(value);
+            }catch (DateTimeParseException e){
+            }
+            try {
+                epochSecond = Long.valueOf(value);
+            } catch (NumberFormatException e) {
+                epochSecond = null;
+            }
+        }
+        if (epochSecond != null) {
+            return LocalDateTime.ofInstant(Instant.ofEpochSecond(epochSecond), ZoneId.systemDefault());
+        }
+        return null;
+    }
+}

+ 29 - 0
src/main/java/cn/edu/nju/timer_task_service/util/serializer/TimestampLocalDateTimeSerializer.java

@@ -0,0 +1,29 @@
+package cn.edu.nju.timer_task_service.util.serializer;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
+
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+
+public class TimestampLocalDateTimeSerializer extends LocalDateTimeSerializer {
+	@Override
+	protected LocalDateTimeSerializer withFormat(Boolean useTimestamp, DateTimeFormatter f, JsonFormat.Shape shape) {
+		return this;
+	}
+
+	@Override
+	public void serialize(LocalDateTime value, JsonGenerator g, SerializerProvider provider) throws IOException {
+		g.writeNumber(value.atZone(ZoneId.systemDefault()).toEpochSecond());
+	}
+
+	@Override
+	public void serializeWithType(LocalDateTime value, JsonGenerator g, SerializerProvider provider, TypeSerializer typeSer) throws IOException {
+		serialize(value, g, provider);
+	}
+}

+ 41 - 0
src/main/java/cn/edu/nju/timer_task_service/util/validate/FieldCompare.java

@@ -0,0 +1,41 @@
+package cn.edu.nju.timer_task_service.util.validate;
+
+import cn.edu.nju.timer_task_service.util.validate.FieldCompare.List;
+import cn.edu.nju.timer_task_service.util.validate.validator.FieldCompareValidator;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Target({TYPE, ANNOTATION_TYPE})
+@Retention(RUNTIME)
+@Repeatable(List.class)
+@Constraint(validatedBy = FieldCompareValidator.class)
+@Documented
+public @interface FieldCompare {
+	String message() default "{cn.edu.nju.timer_task_service_util.validate.FieldCompare}";
+
+	Class<?>[] groups() default {};
+
+	Class<? extends Payload>[] payload() default {};
+
+	String lessField();
+
+	String greaterField();
+
+	boolean allowEqual() default false;
+
+	@Target({TYPE, ANNOTATION_TYPE})
+	@Retention(RUNTIME)
+	@Documented
+	@interface List {
+		FieldCompare[] value();
+	}
+}

+ 40 - 0
src/main/java/cn/edu/nju/timer_task_service/util/validate/FieldEquals.java

@@ -0,0 +1,40 @@
+package cn.edu.nju.timer_task_service.util.validate;
+
+import cn.edu.nju.timer_task_service.util.validate.FieldEquals.List;
+import cn.edu.nju.timer_task_service.util.validate.validator.FieldEqualsValidator;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Target({TYPE, ANNOTATION_TYPE})
+@Retention(RUNTIME)
+@Repeatable(List.class)
+@Constraint(validatedBy = FieldEqualsValidator.class)
+@Documented
+public @interface FieldEquals {
+	String message() default "{cn.edu.nju.timer_task_service_util.validate.FieldEquals}";
+
+	Class<?>[] groups() default {};
+
+	Class<? extends Payload>[] payload() default {};
+
+	String firstField();
+
+	String secondField();
+
+	@Target({TYPE, ANNOTATION_TYPE})
+	@Retention(RUNTIME)
+	@Documented
+	@interface List {
+		FieldEquals[] value();
+	}
+
+}

+ 38 - 0
src/main/java/cn/edu/nju/timer_task_service/util/validate/validator/FieldCompareValidator.java

@@ -0,0 +1,38 @@
+package cn.edu.nju.timer_task_service.util.validate.validator;
+
+import cn.edu.nju.timer_task_service.util.validate.FieldCompare;
+import org.springframework.beans.BeanUtils;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+public class FieldCompareValidator implements ConstraintValidator<FieldCompare, Object> {
+	private String lessField;
+	private String greaterField;
+	private boolean allowEqual;
+
+
+	@Override
+	public void initialize(FieldCompare constraintAnnotation) {
+		this.lessField = constraintAnnotation.lessField();
+		this.greaterField = constraintAnnotation.greaterField();
+		this.allowEqual = constraintAnnotation.allowEqual();
+	}
+
+	@Override
+	@SuppressWarnings("unchecked")
+	public boolean isValid(Object value, ConstraintValidatorContext context) {
+		try {
+			Comparable lessObject = (Comparable) BeanUtils.getPropertyDescriptor(value.getClass(), lessField).getReadMethod().invoke(value);
+			Comparable greaterObject = (Comparable) BeanUtils.getPropertyDescriptor(value.getClass(), greaterField).getReadMethod().invoke(value);
+			int result = lessObject.compareTo(greaterObject);
+			if (allowEqual && result == 0) {
+				return true;
+			} else {
+				return result < 0;
+			}
+		} catch (Exception e) {
+			return false;
+		}
+	}
+}

+ 30 - 0
src/main/java/cn/edu/nju/timer_task_service/util/validate/validator/FieldEqualsValidator.java

@@ -0,0 +1,30 @@
+package cn.edu.nju.timer_task_service.util.validate.validator;
+
+import cn.edu.nju.timer_task_service.util.validate.FieldEquals;
+import org.springframework.beans.BeanUtils;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import java.util.Objects;
+
+public class FieldEqualsValidator implements ConstraintValidator<FieldEquals, Object> {
+	private String firstField;
+	private String secondField;
+
+	@Override
+	public void initialize(FieldEquals constraintAnnotation) {
+		this.firstField = constraintAnnotation.firstField();
+		this.secondField = constraintAnnotation.secondField();
+	}
+
+	@Override
+	public boolean isValid(Object value, ConstraintValidatorContext context) {
+		try {
+			Object firstObject = BeanUtils.getPropertyDescriptor(value.getClass(), firstField).getReadMethod().invoke(value);
+			Object secondObject = BeanUtils.getPropertyDescriptor(value.getClass(), secondField).getReadMethod().invoke(value);
+			return Objects.equals(firstObject, secondObject);
+		} catch (Exception e) {
+			return false;
+		}
+	}
+}

+ 36 - 0
src/main/java/cn/edu/nju/timer_task_service/vo/TaskVO.java

@@ -0,0 +1,36 @@
+package cn.edu.nju.timer_task_service.vo;
+
+import cn.edu.nju.timer_task_service.data.entity.Task;
+import cn.edu.nju.timer_task_service.util.enums.TaskStatus;
+import cn.edu.nju.timer_task_service.util.serializer.TimestampLocalDateTimeSerializer;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
+import lombok.Data;
+import org.springframework.beans.BeanUtils;
+
+import java.time.LocalDateTime;
+
+
+/**
+ * @author fjj
+ * @date 2019/12/30 10:19 PM
+ */
+@Data
+public class TaskVO {
+    private String uuid;
+    private String callbackUrl;
+    private String content;
+//    @JsonSerialize(using = TimestampLocalDateTimeSerializer.class)
+//    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
+    private LocalDateTime time;
+//    @JsonSerialize(using = TimestampLocalDateTimeSerializer.class)
+//    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
+    private LocalDateTime createdAt;
+    private TaskStatus status;
+
+    public TaskVO(Task task) {
+        BeanUtils.copyProperties(task, this);
+    }
+}

+ 53 - 0
src/main/resources/application.yml

@@ -0,0 +1,53 @@
+server:
+  port: 3001
+spring:
+  application:
+    name: timer-task-service
+  datasource:
+    #    url: jdbc:mysql://192.168.68.78:3306/seecoder?useUnicode=true&characterEncoding=UTF-8&useSSL=false
+    url: jdbc:mysql://localhost:3306/timer_task?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
+    username: root
+    password: NJU67mysql
+#    password: root
+  jpa:
+    hibernate:
+      ddl-auto: update
+      naming:
+        implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
+        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
+    database-platform: org.hibernate.dialect.MySQL57InnoDBDialect
+  rabbitmq:
+#    host: localhost
+#    port: 5672
+#    username: root
+#    password: 123456
+    host: localhost
+    port: 5672
+    username: root
+    password: NJU67rabbitmq
+  cloud:
+    stream:
+      bindings:
+        timer-task-output:
+          destination: timer-task
+        timer-task-input:
+          destination: timer-task
+          group: timer-task
+      rabbit:
+        bindings:
+          timer-task-output:
+            producer:
+              delayed-exchange: true
+          timer-task-input:
+            consumer:
+              delayed-exchange: true
+
+eureka:
+  client:
+    serviceUrl:
+      defaultZone: http://localhost:1001/eureka/
+  instance:
+    prefer-ip-address: true
+
+swagger:
+  base-package: cn.edu.nju

+ 13 - 0
src/test/java/cn/edu/nju/timer_task_service/TimerTaskServiceApplicationTests.java

@@ -0,0 +1,13 @@
+package cn.edu.nju.timer_task_service;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class TimerTaskServiceApplicationTests {
+
+    @Test
+    void contextLoads() {
+    }
+
+}

+ 26 - 0
src/test/java/cn/edu/nju/timer_task_service/test.java

@@ -0,0 +1,26 @@
+package cn.edu.nju.timer_task_service;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author fjj
+ * @date 2020/3/5 5:40 PM
+ */
+public class test {
+    public static void main(String[] args){
+        LocalDateTime now = LocalDateTime.now();
+        System.out.println(now);
+        int  k = 5;
+        int x = 4;
+        flag: for(int i = 1;i<6;i++){
+            System.out.println("i:" + i);
+            for(int j = 1;j<x;j++){
+                if(j==k){
+                    break flag;
+                }
+                System.out.println("j:" + j);
+            }
+            x++;
+        }
+    }
+}