diff --git a/src/main/java/ru/ulstu/timeline/controller/EventController.java b/src/main/java/ru/ulstu/timeline/controller/EventController.java index 6f79b31..b2e4a23 100644 --- a/src/main/java/ru/ulstu/timeline/controller/EventController.java +++ b/src/main/java/ru/ulstu/timeline/controller/EventController.java @@ -13,6 +13,7 @@ import ru.ulstu.core.model.response.Response; import ru.ulstu.timeline.model.EventDto; import ru.ulstu.timeline.service.EventService; +import javax.validation.Valid; import java.util.List; import static ru.ulstu.timeline.controller.EventController.URL; @@ -30,16 +31,16 @@ public class EventController { @GetMapping public Response> getEvents() { - return new Response<>(eventService.findAll()); + return new Response<>(eventService.findAllDto()); } @PostMapping - public Response createEvent(@RequestBody EventDto timelineDto) { + public Response createEvent(@RequestBody @Valid EventDto timelineDto) { return new Response(eventService.create(timelineDto)); } @PutMapping - public Response updateEvent(@RequestBody EventDto eventDto) { + public Response updateEvent(@RequestBody @Valid EventDto eventDto) { return new Response(eventService.update(eventDto)); } diff --git a/src/main/java/ru/ulstu/timeline/model/Event.java b/src/main/java/ru/ulstu/timeline/model/Event.java index 89ff4cd..7ae715d 100644 --- a/src/main/java/ru/ulstu/timeline/model/Event.java +++ b/src/main/java/ru/ulstu/timeline/model/Event.java @@ -9,9 +9,13 @@ import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.FetchType; +import javax.persistence.JoinColumn; import javax.persistence.ManyToMany; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; import javax.persistence.Temporal; import javax.persistence.TemporalType; +import javax.validation.constraints.NotNull; import java.util.Date; import java.util.List; @@ -34,11 +38,15 @@ public class Event extends BaseEntity { @NotBlank private String title; + @Enumerated(value = EnumType.STRING) + private PeriodEvent period; + @Enumerated(value = EnumType.STRING) private EventStatus status; @Column(name = "execute_date") @Temporal(TemporalType.TIMESTAMP) + @NotNull private Date executeDate; @Column(name = "create_date") @@ -54,6 +62,10 @@ public class Event extends BaseEntity { @ManyToMany(fetch = FetchType.EAGER) private List recipients; + @ManyToOne + @JoinColumn(name = "child_id") + private Event child; + public String getTitle() { return title; } @@ -62,7 +74,7 @@ public class Event extends BaseEntity { this.title = title; } - public EventStatus getStatus() { + public Event.EventStatus getStatus() { return status; } @@ -70,6 +82,14 @@ public class Event extends BaseEntity { this.status = status; } + public PeriodEvent getPeriod() { + return period; + } + + public void setPeriod(PeriodEvent period) { + this.period = period; + } + public Date getCreateDate() { return createDate; } @@ -109,4 +129,12 @@ public class Event extends BaseEntity { public void setExecuteDate(Date executeDate) { this.executeDate = executeDate; } + + public Event getChild() { + return child; + } + + public void setChild(Event child) { + this.child = child; + } } diff --git a/src/main/java/ru/ulstu/timeline/model/EventDto.java b/src/main/java/ru/ulstu/timeline/model/EventDto.java index b409ce8..0ec07ea 100644 --- a/src/main/java/ru/ulstu/timeline/model/EventDto.java +++ b/src/main/java/ru/ulstu/timeline/model/EventDto.java @@ -2,8 +2,10 @@ package ru.ulstu.timeline.model; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import org.hibernate.validator.constraints.NotBlank; import ru.ulstu.user.model.UserDto; +import javax.validation.constraints.NotNull; import java.util.Date; import java.util.List; @@ -11,8 +13,11 @@ import static ru.ulstu.core.util.StreamApiUtils.convert; public class EventDto { private final Integer id; + @NotBlank private final String title; + private final PeriodEvent period; private final Event.EventStatus status; + @NotNull private final Date executeDate; private final Date createDate; private final Date updateDate; @@ -23,6 +28,7 @@ public class EventDto { public EventDto(@JsonProperty("id") Integer id, @JsonProperty("title") String title, @JsonProperty("status") Event.EventStatus status, + @JsonProperty("period") PeriodEvent period, @JsonProperty("executeDate") Date executeDate, @JsonProperty("createDate") Date createDate, @JsonProperty("updateDate") Date updateDate, @@ -30,6 +36,7 @@ public class EventDto { @JsonProperty("recipients") List recipients) { this.id = id; this.title = title; + this.period = period; this.status = status; this.executeDate = executeDate; this.createDate = createDate; @@ -42,6 +49,7 @@ public class EventDto { this.id = event.getId(); this.title = event.getTitle(); this.status = event.getStatus(); + this.period = event.getPeriod(); this.executeDate = event.getExecuteDate(); this.createDate = event.getCreateDate(); this.updateDate = event.getUpdateDate(); @@ -57,6 +65,8 @@ public class EventDto { return title; } + public PeriodEvent getPeriod() { return period; } + public Event.EventStatus getStatus() { return status; } public Date getCreateDate() { diff --git a/src/main/java/ru/ulstu/timeline/model/PeriodEvent.java b/src/main/java/ru/ulstu/timeline/model/PeriodEvent.java new file mode 100644 index 0000000..b5e73b8 --- /dev/null +++ b/src/main/java/ru/ulstu/timeline/model/PeriodEvent.java @@ -0,0 +1,24 @@ +package ru.ulstu.timeline.model; + +import java.time.Period; + +public enum PeriodEvent { + EVERY_YEAR(Period.ofYears(1), "Каждый год"), + EVERY_MONTH(Period.ofMonths(1), "Каждый месяц"), + EVERY_WEEK(Period.ofWeeks(1), "Каждую неделю"), + EVERY_DAY(Period.ofDays(1), "Каждый день"); + + private Period period; + private String message; + + PeriodEvent(Period period, String message) { + this.period = period; + this.message = message; + } + + public Period getPeriod() { return period; } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/ru/ulstu/timeline/repository/EventRepository.java b/src/main/java/ru/ulstu/timeline/repository/EventRepository.java index 6267d51..2829261 100644 --- a/src/main/java/ru/ulstu/timeline/repository/EventRepository.java +++ b/src/main/java/ru/ulstu/timeline/repository/EventRepository.java @@ -9,4 +9,7 @@ import java.util.List; public interface EventRepository extends JpaRepository { @Query("SELECT e FROM Event e WHERE e.executeDate = CURRENT_DATE") List findByCurrentDate(); + + @Query("SELECT e FROM Event e WHERE e.executeDate > CURRENT_DATE") + List findAllFuture(); } diff --git a/src/main/java/ru/ulstu/timeline/service/EventScheduler.java b/src/main/java/ru/ulstu/timeline/service/EventScheduler.java new file mode 100644 index 0000000..7a0bc66 --- /dev/null +++ b/src/main/java/ru/ulstu/timeline/service/EventScheduler.java @@ -0,0 +1,72 @@ +package ru.ulstu.timeline.service; + +import com.google.common.collect.ImmutableMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import ru.ulstu.core.util.DateUtils; +import ru.ulstu.timeline.model.Event; +import ru.ulstu.timeline.model.PeriodEvent; +import ru.ulstu.user.service.MailService; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +@Service +public class EventScheduler { + + private final Logger log = LoggerFactory.getLogger(EventScheduler.class); + private final EventService eventService; + private final MailService mailService; + + public EventScheduler(EventService eventService, + MailService mailService) { + this.eventService = eventService; + this.mailService = mailService; + } + + @Scheduled(cron = "0 0 8 * * ?") + public void sendNotifications() { + List events = eventService.findByCurrentDate(); + events.forEach(event -> { + Map variables = ImmutableMap.of("description", event.getDescription()); + event.getRecipients() + .forEach(recipient -> mailService.sendEmailFromTemplate(variables, recipient, "eventNotification", event.getTitle())); + }); + } + + @Scheduled(cron = "0 0 * * * ?") + public void checkPeriodEvents() { + log.debug("EventScheduler.checkPeriodEvents started"); + for (Event event : eventService.findAllFuture()) { + if (halfOfThePeriodHasPassed(event)) { + eventService.createBasedOn(event, DateUtils.addDays(event.getExecuteDate(), getShiftInDays(event.getPeriod()))); + } + } + + log.debug("EventScheduler.checkPeriodEvents finished"); + } + + private int getShiftInDays(PeriodEvent periodEvent) { + switch (periodEvent) { + case EVERY_DAY: + return periodEvent.getPeriod().getDays(); + case EVERY_WEEK: + return periodEvent.getPeriod().getDays(); + case EVERY_MONTH: + return periodEvent.getPeriod().getMonths() * 30; + case EVERY_YEAR: + return periodEvent.getPeriod().getYears() * 365; + default: + throw new RuntimeException("period event not exists"); + } + } + + private boolean halfOfThePeriodHasPassed(Event event) { + return event.getPeriod() != null && event.getChild() == null + && new Date().after( + DateUtils.addDays(event.getExecuteDate(), (int) -Math.round((double) getShiftInDays(event.getPeriod()) / 2))); + } +} diff --git a/src/main/java/ru/ulstu/timeline/service/EventService.java b/src/main/java/ru/ulstu/timeline/service/EventService.java index 8d8189d..602e7aa 100644 --- a/src/main/java/ru/ulstu/timeline/service/EventService.java +++ b/src/main/java/ru/ulstu/timeline/service/EventService.java @@ -1,18 +1,15 @@ package ru.ulstu.timeline.service; -import com.google.common.collect.ImmutableMap; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import ru.ulstu.timeline.model.Event; import ru.ulstu.timeline.model.EventDto; import ru.ulstu.timeline.repository.EventRepository; import ru.ulstu.user.model.UserDto; -import ru.ulstu.user.service.MailService; import ru.ulstu.user.service.UserService; +import java.util.Date; import java.util.List; -import java.util.Map; import static ru.ulstu.core.util.StreamApiUtils.convert; @@ -21,18 +18,19 @@ public class EventService { private final EventRepository eventRepository; private final UserService userService; - private final MailService mailService; public EventService(EventRepository eventRepository, - UserService userService, - MailService mailService) { + UserService userService) { this.eventRepository = eventRepository; this.userService = userService; - this.mailService = mailService; } - public List findAll() { - return convert(eventRepository.findAll(), EventDto::new); + public List findAllDto() { + return convert(findAll(), EventDto::new); + } + + public List findAll() { + return eventRepository.findAll(); } @Transactional @@ -42,12 +40,13 @@ public class EventService { private Event copyFromDto(Event event, EventDto eventDto) { event.setExecuteDate(eventDto.getExecuteDate()); - event.setCreateDate(eventDto.getCreateDate()); + event.setCreateDate(eventDto.getId() == null ? new Date() : eventDto.getCreateDate()); event.setDescription(eventDto.getDescription()); event.setRecipients(userService.findByIds(convert(eventDto.getRecipients(), UserDto::getId))); event.setTitle(eventDto.getTitle()); + event.setPeriod(eventDto.getPeriod()); event.setStatus(eventDto.getStatus()); - event.setUpdateDate(eventDto.getUpdateDate()); + event.setUpdateDate(new Date()); return event; } @@ -67,13 +66,28 @@ public class EventService { return eventRepository.findAll(ids); } - @Scheduled(cron = "0 0 8 * * ?") - public void sendNotifications() { - List events = eventRepository.findByCurrentDate(); - events.forEach(event -> { - Map variables = ImmutableMap.of("description", event.getDescription()); - event.getRecipients() - .forEach(recipient -> mailService.sendEmailFromTemplate(variables, recipient, "eventNotification", event.getTitle())); - }); + public void createBasedOn(Event event, Date executeDate) { + //backup event id + Integer parentEventId = event.getId(); + + event.setId(null); + event.setStatus(Event.EventStatus.POSSIBLE); + event.setExecuteDate(executeDate); + event.setCreateDate(new Date()); + event.setUpdateDate(new Date()); + event = eventRepository.save(event); + + //set child to parent + Event parentEvent = eventRepository.findOne(parentEventId); + parentEvent.setChild(event); + eventRepository.save(parentEvent); + } + + public List findByCurrentDate() { + return eventRepository.findByCurrentDate(); + } + + public List findAllFuture() { + return eventRepository.findAllFuture(); } } diff --git a/src/main/resources/db/changelog-20181111_000000-schema.xml b/src/main/resources/db/changelog-20181111_000000-schema.xml new file mode 100644 index 0000000..1aa0c64 --- /dev/null +++ b/src/main/resources/db/changelog-20181111_000000-schema.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/db/changelog-master.xml b/src/main/resources/db/changelog-master.xml index b5d2999..d0199a1 100644 --- a/src/main/resources/db/changelog-master.xml +++ b/src/main/resources/db/changelog-master.xml @@ -15,4 +15,5 @@ + \ No newline at end of file