diff --git a/src/main/java/ru/ulstu/core/util/StreamApiUtils.java b/src/main/java/ru/ulstu/core/util/StreamApiUtils.java index 282601c..e1fb55a 100644 --- a/src/main/java/ru/ulstu/core/util/StreamApiUtils.java +++ b/src/main/java/ru/ulstu/core/util/StreamApiUtils.java @@ -1,12 +1,15 @@ package ru.ulstu.core.util; +import java.util.Collections; import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; public class StreamApiUtils { - public static List convert(List entitites, Function converter) { - return entitites.stream().map(e -> converter.apply(e)).collect(Collectors.toList()); + public static List convert(List entities, Function converter) { + return entities == null + ? Collections.EMPTY_LIST + : entities.stream().map(e -> converter.apply(e)).collect(Collectors.toList()); } } diff --git a/src/main/java/ru/ulstu/timeline/controller/EventController.java b/src/main/java/ru/ulstu/timeline/controller/EventController.java new file mode 100644 index 0000000..6f79b31 --- /dev/null +++ b/src/main/java/ru/ulstu/timeline/controller/EventController.java @@ -0,0 +1,51 @@ +package ru.ulstu.timeline.controller; + +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import ru.ulstu.configuration.Constants; +import ru.ulstu.core.model.response.Response; +import ru.ulstu.timeline.model.EventDto; +import ru.ulstu.timeline.service.EventService; + +import java.util.List; + +import static ru.ulstu.timeline.controller.EventController.URL; + +@RestController +@RequestMapping(URL) +public class EventController { + public static final String URL = Constants.API_1_0 + "events"; + + private final EventService eventService; + + public EventController(EventService eventService) { + this.eventService = eventService; + } + + @GetMapping + public Response> getEvents() { + return new Response<>(eventService.findAll()); + } + + @PostMapping + public Response createEvent(@RequestBody EventDto timelineDto) { + return new Response(eventService.create(timelineDto)); + } + + @PutMapping + public Response updateEvent(@RequestBody EventDto eventDto) { + return new Response(eventService.update(eventDto)); + } + + @DeleteMapping("/{event-id}") + public Response delete(@PathVariable("event-id") Integer eventId) { + eventService.delete(eventId); + return new Response(true); + } +} diff --git a/src/main/java/ru/ulstu/timeline/model/Event.java b/src/main/java/ru/ulstu/timeline/model/Event.java new file mode 100644 index 0000000..404025a --- /dev/null +++ b/src/main/java/ru/ulstu/timeline/model/Event.java @@ -0,0 +1,85 @@ +package ru.ulstu.timeline.model; + +import org.hibernate.validator.constraints.NotBlank; +import ru.ulstu.core.model.BaseEntity; +import ru.ulstu.user.model.User; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.ManyToMany; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import java.util.Date; +import java.util.List; + +@Entity +public class Event extends BaseEntity { + @NotBlank + private String title; + + @Column(name = "execute_date") + @Temporal(TemporalType.TIMESTAMP) + private Date executeDate; + + @Column(name = "create_date") + @Temporal(TemporalType.TIMESTAMP) + private Date createDate; + + @Column(name = "update_date") + @Temporal(TemporalType.TIMESTAMP) + private Date updateDate; + + private String description; + + @ManyToMany(fetch = FetchType.EAGER) + private List recipients; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public Date getCreateDate() { + return createDate; + } + + public void setCreateDate(Date createDate) { + this.createDate = createDate; + } + + public Date getUpdateDate() { + return updateDate; + } + + public void setUpdateDate(Date updateDate) { + this.updateDate = updateDate; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public List getRecipients() { + return recipients; + } + + public void setRecipients(List recipients) { + this.recipients = recipients; + } + + public Date getExecuteDate() { + return executeDate; + } + + public void setExecuteDate(Date executeDate) { + this.executeDate = executeDate; + } +} diff --git a/src/main/java/ru/ulstu/timeline/model/EventDto.java b/src/main/java/ru/ulstu/timeline/model/EventDto.java new file mode 100644 index 0000000..59cef34 --- /dev/null +++ b/src/main/java/ru/ulstu/timeline/model/EventDto.java @@ -0,0 +1,75 @@ +package ru.ulstu.timeline.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import ru.ulstu.user.model.UserDto; + +import java.util.Date; +import java.util.List; + +import static ru.ulstu.core.util.StreamApiUtils.convert; + +public class EventDto { + private final Integer id; + private final String title; + private final Date executeDate; + private final Date createDate; + private final Date updateDate; + private final String description; + private final List recipients; + + @JsonCreator + public EventDto(@JsonProperty("id") Integer id, + @JsonProperty("title") String title, + @JsonProperty("executeDate") Date executeDate, + @JsonProperty("createDate") Date createDate, + @JsonProperty("updateDate") Date updateDate, + @JsonProperty("description") String description, + @JsonProperty("recipients") List recipients) { + this.id = id; + this.title = title; + this.executeDate = executeDate; + this.createDate = createDate; + this.updateDate = updateDate; + this.description = description; + this.recipients = recipients; + } + + public EventDto(Event event) { + this.id = event.getId(); + this.title = event.getTitle(); + this.executeDate = event.getExecuteDate(); + this.createDate = event.getCreateDate(); + this.updateDate = event.getUpdateDate(); + this.description = event.getDescription(); + this.recipients = convert(event.getRecipients(), UserDto::new); + } + + public Integer getId() { + return id; + } + + public String getTitle() { + return title; + } + + public Date getCreateDate() { + return createDate; + } + + public Date getUpdateDate() { + return updateDate; + } + + public String getDescription() { + return description; + } + + public List getRecipients() { + return recipients; + } + + public Date getExecuteDate() { + return executeDate; + } +} diff --git a/src/main/java/ru/ulstu/timeline/model/Timeline.java b/src/main/java/ru/ulstu/timeline/model/Timeline.java new file mode 100644 index 0000000..8e588cb --- /dev/null +++ b/src/main/java/ru/ulstu/timeline/model/Timeline.java @@ -0,0 +1,25 @@ +package ru.ulstu.timeline.model; + +import ru.ulstu.core.model.BaseEntity; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; +import java.util.List; + +@Entity +public class Timeline extends BaseEntity { + + @OneToMany(cascade = CascadeType.ALL) + @JoinColumn(name = "timeline_id") + private List events; + + public List getEvents() { + return events; + } + + public void setEvents(List events) { + this.events = events; + } +} diff --git a/src/main/java/ru/ulstu/timeline/model/TimelineDto.java b/src/main/java/ru/ulstu/timeline/model/TimelineDto.java new file mode 100644 index 0000000..1c651a7 --- /dev/null +++ b/src/main/java/ru/ulstu/timeline/model/TimelineDto.java @@ -0,0 +1,33 @@ +package ru.ulstu.timeline.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +import static ru.ulstu.core.util.StreamApiUtils.convert; + +public class TimelineDto { + private final Integer id; + private final List events; + + @JsonCreator + public TimelineDto(@JsonProperty("id") Integer id, + @JsonProperty("events") List events) { + this.id = id; + this.events = events; + } + + public TimelineDto(Timeline timeline) { + this.id = timeline.getId(); + this.events = convert(timeline.getEvents(), EventDto::new); + } + + public Integer getId() { + return id; + } + + public List getEvents() { + return events; + } +} diff --git a/src/main/java/ru/ulstu/timeline/repository/EventRepository.java b/src/main/java/ru/ulstu/timeline/repository/EventRepository.java new file mode 100644 index 0000000..6267d51 --- /dev/null +++ b/src/main/java/ru/ulstu/timeline/repository/EventRepository.java @@ -0,0 +1,12 @@ +package ru.ulstu.timeline.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import ru.ulstu.timeline.model.Event; + +import java.util.List; + +public interface EventRepository extends JpaRepository { + @Query("SELECT e FROM Event e WHERE e.executeDate = CURRENT_DATE") + List findByCurrentDate(); +} diff --git a/src/main/java/ru/ulstu/timeline/repository/TimelineRepository.java b/src/main/java/ru/ulstu/timeline/repository/TimelineRepository.java new file mode 100644 index 0000000..6b1be39 --- /dev/null +++ b/src/main/java/ru/ulstu/timeline/repository/TimelineRepository.java @@ -0,0 +1,7 @@ +package ru.ulstu.timeline.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.ulstu.timeline.model.Timeline; + +public interface TimelineRepository extends JpaRepository { +} diff --git a/src/main/java/ru/ulstu/timeline/service/EventService.java b/src/main/java/ru/ulstu/timeline/service/EventService.java new file mode 100644 index 0000000..0c1cf6c --- /dev/null +++ b/src/main/java/ru/ulstu/timeline/service/EventService.java @@ -0,0 +1,78 @@ +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.List; +import java.util.Map; + +import static ru.ulstu.core.util.StreamApiUtils.convert; + +@Service +public class EventService { + + private final EventRepository eventRepository; + private final UserService userService; + private final MailService mailService; + + public EventService(EventRepository eventRepository, + UserService userService, + MailService mailService) { + this.eventRepository = eventRepository; + this.userService = userService; + this.mailService = mailService; + } + + public List findAll() { + return convert(eventRepository.findAll(), EventDto::new); + } + + @Transactional + public int create(EventDto eventDto) { + return eventRepository.save(copyFromDto(new Event(), eventDto)).getId(); + } + + private Event copyFromDto(Event event, EventDto eventDto) { + event.setExecuteDate(eventDto.getExecuteDate()); + event.setCreateDate(eventDto.getCreateDate()); + event.setDescription(eventDto.getDescription()); + event.setRecipients(userService.findByIds(convert(eventDto.getRecipients(), UserDto::getId))); + event.setTitle(eventDto.getTitle()); + event.setUpdateDate(eventDto.getUpdateDate()); + return event; + } + + @Transactional + public Integer update(EventDto eventDto) { + Event event = eventRepository.findOne(eventDto.getId()); + return eventRepository.save(copyFromDto(event, eventDto)).getId(); + } + + @Transactional + public void delete(Integer timelineId) { + Event event = eventRepository.findOne(timelineId); + eventRepository.delete(event); + } + + public List findByIds(List ids) { + 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())); + }); + } +} diff --git a/src/main/java/ru/ulstu/timeline/service/TimelineService.java b/src/main/java/ru/ulstu/timeline/service/TimelineService.java new file mode 100644 index 0000000..94388ee --- /dev/null +++ b/src/main/java/ru/ulstu/timeline/service/TimelineService.java @@ -0,0 +1,51 @@ +package ru.ulstu.timeline.service; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.ulstu.timeline.model.EventDto; +import ru.ulstu.timeline.model.Timeline; +import ru.ulstu.timeline.model.TimelineDto; +import ru.ulstu.timeline.repository.TimelineRepository; + +import java.util.List; + +import static ru.ulstu.core.util.StreamApiUtils.convert; + +@Service +public class TimelineService { + + private final TimelineRepository timelineRepository; + private final EventService eventService; + + public TimelineService(TimelineRepository timelineRepository, + EventService eventService) { + this.timelineRepository = timelineRepository; + this.eventService = eventService; + } + + public List findAll() { + return convert(timelineRepository.findAll(), TimelineDto::new); + } + + @Transactional + public int create(TimelineDto timelineDto) { + return timelineRepository.save(copyFromDto(new Timeline(), timelineDto)).getId(); + } + + private Timeline copyFromDto(Timeline timeline, TimelineDto timelineDto) { + timeline.setEvents(eventService.findByIds(convert(timelineDto.getEvents(), EventDto::getId))); + return timeline; + } + + @Transactional + public Integer update(TimelineDto timelineDto) { + Timeline timeline = timelineRepository.findOne(timelineDto.getId()); + return timelineRepository.save(copyFromDto(timeline, timelineDto)).getId(); + } + + @Transactional + public void delete(Integer timelineId) { + Timeline timeline = timelineRepository.findOne(timelineId); + timelineRepository.delete(timeline); + } +} diff --git a/src/main/java/ru/ulstu/user/service/MailService.java b/src/main/java/ru/ulstu/user/service/MailService.java index 0708450..da1da6d 100644 --- a/src/main/java/ru/ulstu/user/service/MailService.java +++ b/src/main/java/ru/ulstu/user/service/MailService.java @@ -15,6 +15,7 @@ import ru.ulstu.user.model.User; import javax.mail.internet.MimeMessage; import java.nio.charset.StandardCharsets; +import java.util.Map; @Service public class MailService { @@ -66,6 +67,17 @@ public class MailService { sendEmail(user.getEmail(), subject, content); } + //Todo: выделить сервис нотификаций + @Async + public void sendEmailFromTemplate(Map variables, User user, String templateName, String subject) { + Context context = new Context(); + variables.entrySet().forEach(entry -> context.setVariable(entry.getKey(), entry.getValue())); + context.setVariable(USER, user); + context.setVariable(BASE_URL, applicationProperties.getBaseUrl()); + String content = templateEngine.process(templateName, context); + sendEmail(user.getEmail(), subject, content); + } + @Async public void sendActivationEmail(User user) { sendEmailFromTemplate(user, "activationEmail", Constants.MAIL_ACTIVATE); diff --git a/src/main/java/ru/ulstu/user/service/UserService.java b/src/main/java/ru/ulstu/user/service/UserService.java index 0c51e5a..0603620 100644 --- a/src/main/java/ru/ulstu/user/service/UserService.java +++ b/src/main/java/ru/ulstu/user/service/UserService.java @@ -282,4 +282,8 @@ public class UserService implements UserDetailsService { .map(role -> new SimpleGrantedAuthority(role.getName())) .collect(Collectors.toList())); } + + public List findByIds(List ids) { + return userRepository.findAll(ids); + } } diff --git a/src/main/resources/db/changelog-20181030_000000-schema.xml b/src/main/resources/db/changelog-20181030_000000-schema.xml new file mode 100644 index 0000000..0d65c2a --- /dev/null +++ b/src/main/resources/db/changelog-20181030_000000-schema.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/db/changelog-master.xml b/src/main/resources/db/changelog-master.xml index 776b2bb..bd52f71 100644 --- a/src/main/resources/db/changelog-master.xml +++ b/src/main/resources/db/changelog-master.xml @@ -12,4 +12,5 @@ + \ No newline at end of file diff --git a/src/main/resources/mail_templates/eventNotification.html b/src/main/resources/mail_templates/eventNotification.html new file mode 100644 index 0000000..0500e26 --- /dev/null +++ b/src/main/resources/mail_templates/eventNotification.html @@ -0,0 +1,21 @@ + + + + Уведомление о событии + + + + +

+ Уважаемый Ivan Ivanov +

+

+ some text +

+

+ Regards, +
+ Balance Team. +

+ + diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html index fe62bbc..155ec6a 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/templates/index.html @@ -102,7 +102,7 @@