-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit c93193a
Showing
14 changed files
with
526 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
.gradle | ||
/build/ | ||
!gradle/wrapper/gradle-wrapper.jar | ||
|
||
/out/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
**Тестовое задание Java** | ||
|
||
Требуется реализовать REST-сервис по сбору статистики посещаемости WEB-сайта. | ||
|
||
**Сборка** | ||
|
||
Команда **gradle bootJar** соберет приложение Spring Boot в jar. | ||
|
||
Собранный файл будет располагаться в build/libs | ||
|
||
**Запуск** | ||
|
||
Для запуска нужна СУБД Mongo. Для настройки подключения нужно отредактировать файл application.yml, указать хост, порт, | ||
имя базы данных в соотвествующих полях. | ||
|
||
Запустить можно командой **gradle bootRun**. | ||
|
||
Или можно запустить собранный jar командой java -jar digitalzone-0.0.1-SNAPSHOT.jar. Отредактированный файл application.yml расположить рядом с jar. | ||
|
||
**Тестирование** | ||
|
||
Приложение имеет API c двумя endpoint: | ||
|
||
**POST /event :** | ||
|
||
Добавляет новое посещение страницы пользователем. | ||
|
||
В теле сообщения нужно передать json создаваемоего объекта: | ||
|
||
```$javascript | ||
{ | ||
"userUUID": "d8898714-37ad-4327-a34b-faf7b3f87461", // ид пользователя | ||
"pageUUID": "5218de4f-3f96-49d6-9ba7-d6f47c49d3e0" // ид страницы | ||
} | ||
``` | ||
|
||
Ответом будет статистика за день: | ||
|
||
```$javascript | ||
{ | ||
"amountAllVisits": 1, // количество визитов за день | ||
"amountUniqueUser": 1 // количество уникальных пользователей за день | ||
} | ||
``` | ||
|
||
**GET /event :** | ||
|
||
Возвращает отчет за период | ||
|
||
В параметрах запроса нужно передать | ||
|
||
`begin` - дата в формате dd.MM.yyyy (02.10.2018), начало периода включительно | ||
|
||
`end` - дата в формате dd.MM.yyyy (02.10.2018), окончание периода не включительно | ||
|
||
Ответом будет статистика за день: | ||
|
||
```$javascript | ||
{ | ||
"amountAllVisits": 1, // количество визитов | ||
"amountUniqueUser": 1 // количество уникальных пользователей | ||
"amountRegularUser": 1 // количество постоянных пользователей | ||
} | ||
``` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
buildscript { | ||
ext { | ||
springBootVersion = '2.0.4.RELEASE' | ||
} | ||
repositories { | ||
mavenCentral() | ||
} | ||
dependencies { | ||
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") | ||
} | ||
} | ||
|
||
apply plugin: 'java' | ||
apply plugin: 'eclipse' | ||
apply plugin: 'org.springframework.boot' | ||
apply plugin: 'io.spring.dependency-management' | ||
|
||
group = 'space.zhdanov.testwork' | ||
version = '0.0.1-SNAPSHOT' | ||
sourceCompatibility = 1.8 | ||
|
||
repositories { | ||
mavenCentral() | ||
} | ||
|
||
|
||
dependencies { | ||
implementation('org.springframework.boot:spring-boot-starter-data-mongodb-reactive') | ||
implementation('org.springframework.boot:spring-boot-starter-webflux') | ||
testImplementation('org.springframework.boot:spring-boot-starter-test') | ||
testImplementation('io.projectreactor:reactor-test') | ||
compileOnly 'org.projectlombok:lombok:1.16.8' | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
rootProject.name = 'digitalzone' |
16 changes: 16 additions & 0 deletions
16
src/main/java/space/zhdanov/testwork/digitalzone/DigitalzoneApplication.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package space.zhdanov.testwork.digitalzone; | ||
|
||
import org.springframework.boot.SpringApplication; | ||
import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
import org.springframework.data.mongodb.config.EnableMongoAuditing; | ||
import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories; | ||
|
||
@EnableMongoAuditing | ||
@EnableReactiveMongoRepositories | ||
@SpringBootApplication | ||
public class DigitalzoneApplication { | ||
|
||
public static void main(String[] args) { | ||
SpringApplication.run(DigitalzoneApplication.class, args); | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
src/main/java/space/zhdanov/testwork/digitalzone/Test.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package space.zhdanov.testwork.digitalzone; | ||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import space.zhdanov.testwork.digitalzone.entity.Event; | ||
|
||
import java.util.Date; | ||
import java.util.UUID; | ||
|
||
public class Test { | ||
|
||
public static void main(String[] args) { | ||
Event event = Event.builder().PageUUID(UUID.randomUUID()).UserUUID(UUID.randomUUID()).timestamp(new Date()).build(); | ||
|
||
ObjectMapper ob = new ObjectMapper(); | ||
|
||
try { | ||
System.out.println(ob.writeValueAsString(event)); | ||
} catch (JsonProcessingException e) { | ||
e.printStackTrace(); | ||
} | ||
} | ||
} |
40 changes: 40 additions & 0 deletions
40
src/main/java/space/zhdanov/testwork/digitalzone/controller/EventController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package space.zhdanov.testwork.digitalzone.controller; | ||
|
||
import org.springframework.format.annotation.DateTimeFormat; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.web.bind.annotation.*; | ||
import reactor.core.publisher.Mono; | ||
import space.zhdanov.testwork.digitalzone.entity.Event; | ||
import space.zhdanov.testwork.digitalzone.entity.ResponseAggregationEvents; | ||
import space.zhdanov.testwork.digitalzone.service.EventService; | ||
|
||
import java.util.Date; | ||
import java.util.UUID; | ||
|
||
@RestController | ||
@RequestMapping("/event") | ||
public class EventController { | ||
|
||
private final EventService eventService; | ||
|
||
public EventController(EventService eventService) { | ||
this.eventService = eventService; | ||
} | ||
|
||
@PostMapping | ||
@ResponseStatus(HttpStatus.CREATED) | ||
public Mono<ResponseAggregationEvents> addEvent(@RequestBody Mono<Event> event) { | ||
return eventService.addEvent( | ||
event.map(it-> { | ||
it.setTimestamp(new Date()); | ||
return it; | ||
})) | ||
.then(eventService.getAggregationDataForDay()); | ||
} | ||
|
||
@GetMapping() | ||
public Mono<ResponseAggregationEvents> getStatisticForPeriod(@RequestParam @DateTimeFormat(pattern="dd.MM.yyyy") Date begin, | ||
@RequestParam @DateTimeFormat(pattern="dd.MM.yyyy") Date end) { | ||
return eventService.getAggregationDataForPeriod(begin, end); | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
src/main/java/space/zhdanov/testwork/digitalzone/entity/Event.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package space.zhdanov.testwork.digitalzone.entity; | ||
|
||
import com.fasterxml.jackson.annotation.JsonFormat; | ||
import com.fasterxml.jackson.annotation.JsonInclude; | ||
import lombok.*; | ||
import org.springframework.data.annotation.Id; | ||
import org.springframework.data.mongodb.core.mapping.Document; | ||
|
||
import java.io.Serializable; | ||
import java.util.Date; | ||
import java.util.UUID; | ||
|
||
@Data | ||
@Builder | ||
@Document | ||
@NoArgsConstructor | ||
@AllArgsConstructor | ||
public class Event implements Serializable { | ||
|
||
@Id | ||
private String _id; | ||
|
||
private UUID UserUUID; | ||
private UUID PageUUID; | ||
@JsonFormat(shape = JsonFormat.Shape.STRING, timezone = "GMT+3", pattern = "dd.MM.yyyy HH:mm:ss") | ||
private Date timestamp; | ||
} |
15 changes: 15 additions & 0 deletions
15
src/main/java/space/zhdanov/testwork/digitalzone/entity/ResponseAggregationEvents.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package space.zhdanov.testwork.digitalzone.entity; | ||
|
||
import com.fasterxml.jackson.annotation.JsonInclude; | ||
import lombok.Builder; | ||
import lombok.Data; | ||
|
||
@JsonInclude(value = JsonInclude.Include.NON_NULL) | ||
@Data | ||
@Builder | ||
public class ResponseAggregationEvents { | ||
|
||
private Integer amountAllVisits; | ||
private Integer amountUniqueUser; | ||
private Integer amountRegularUser; | ||
} |
8 changes: 8 additions & 0 deletions
8
src/main/java/space/zhdanov/testwork/digitalzone/repository/EventRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package space.zhdanov.testwork.digitalzone.repository; | ||
|
||
import org.springframework.data.mongodb.repository.ReactiveMongoRepository; | ||
import org.springframework.data.repository.reactive.ReactiveCrudRepository; | ||
import space.zhdanov.testwork.digitalzone.entity.Event; | ||
|
||
public interface EventRepository extends ReactiveMongoRepository<Event, String> { | ||
} |
102 changes: 102 additions & 0 deletions
102
src/main/java/space/zhdanov/testwork/digitalzone/service/EventService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package space.zhdanov.testwork.digitalzone.service; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Data; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.data.mongodb.core.ReactiveMongoTemplate; | ||
import org.springframework.data.mongodb.core.aggregation.Aggregation; | ||
import org.springframework.data.mongodb.core.aggregation.GroupOperation; | ||
import org.springframework.data.mongodb.core.aggregation.MatchOperation; | ||
import org.springframework.data.mongodb.core.query.Criteria; | ||
import org.springframework.stereotype.Service; | ||
import reactor.core.publisher.Flux; | ||
import reactor.core.publisher.Mono; | ||
import space.zhdanov.testwork.digitalzone.entity.Event; | ||
import space.zhdanov.testwork.digitalzone.entity.ResponseAggregationEvents; | ||
import space.zhdanov.testwork.digitalzone.repository.EventRepository; | ||
|
||
import java.util.Calendar; | ||
import java.util.Date; | ||
import java.util.UUID; | ||
|
||
import static org.springframework.data.mongodb.core.aggregation.Aggregation.group; | ||
import static org.springframework.data.mongodb.core.aggregation.Aggregation.match; | ||
import static org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation; | ||
|
||
@Service | ||
public class EventService { | ||
|
||
private EventRepository eventRepository; | ||
private ReactiveMongoTemplate mongoTemplate; | ||
|
||
@Autowired | ||
public EventService(EventRepository eventRepository, ReactiveMongoTemplate mongoTemplate) { | ||
this.eventRepository = eventRepository; | ||
this.mongoTemplate = mongoTemplate; | ||
} | ||
|
||
public Mono<Event> addEvent(Mono<Event> event) { | ||
return mongoTemplate.insert(event); | ||
} | ||
|
||
public Mono<ResponseAggregationEvents> getAggregationDataForDay() { | ||
Calendar calendar = Calendar.getInstance(); | ||
calendar.setTime(new Date()); | ||
calendar.set(Calendar.HOUR_OF_DAY, 0); | ||
calendar.set(Calendar.MINUTE, 0); | ||
calendar.set(Calendar.SECOND, 0); | ||
calendar.set(Calendar.MILLISECOND, 0); | ||
|
||
Date today = calendar.getTime(); | ||
calendar.add(Calendar.DAY_OF_MONTH, 1); | ||
Date tomorrow = calendar.getTime(); | ||
|
||
GroupOperation group = group("UserUUID").count().as("countUsers"); | ||
MatchOperation match = match(Criteria.where("timestamp").gte(today).lt(tomorrow)); | ||
Aggregation aggregation = newAggregation(match, group); | ||
Flux<GroupEvent> eventFlux = mongoTemplate.aggregate(aggregation, Event.class, GroupEvent.class); | ||
return eventFlux.reduce(ResponseAggregationEvents.builder().amountAllVisits(0).amountUniqueUser(0).build(), | ||
(a, b) -> { | ||
a.setAmountAllVisits(a.getAmountAllVisits() + b.countUsers); | ||
a.setAmountUniqueUser(a.getAmountUniqueUser() + 1); | ||
return a; | ||
}); | ||
} | ||
|
||
public Mono<ResponseAggregationEvents> getAggregationDataForPeriod(Date begin, Date end) { | ||
MatchOperation match = match(Criteria.where("timestamp").gte(begin).lt(end)); | ||
GroupOperation group = group("UserUUID", "PageUUID").count().as("amountVisits"); | ||
GroupOperation group2 = group("UserUUID").count().as("amountUniqueVisits") | ||
.sum("amountVisits").as("amountAllVisits"); | ||
Aggregation aggregation = newAggregation(match, group, group2); | ||
Flux<GroupVisit> eventFlux = mongoTemplate.aggregate(aggregation, Event.class, GroupVisit.class); | ||
|
||
return eventFlux | ||
.reduce(ResponseAggregationEvents.builder().amountAllVisits(0).amountUniqueUser(0).amountRegularUser(0).build(), | ||
(a, b) -> { | ||
a.setAmountAllVisits(a.getAmountAllVisits() + b.getAmountAllVisits()); | ||
a.setAmountUniqueUser(a.getAmountUniqueUser() + 1); | ||
if (b.getAmountUniqueVisits() >= 10) | ||
a.setAmountRegularUser(a.getAmountRegularUser() + 1); | ||
return a; | ||
}); | ||
} | ||
|
||
@Data | ||
@AllArgsConstructor | ||
private class GroupEvent { | ||
|
||
private UUID userUUID; | ||
private Integer countUsers; | ||
} | ||
|
||
@Data | ||
@AllArgsConstructor | ||
private class GroupVisit { | ||
|
||
private UUID userUUID; | ||
private Integer amountUniqueVisits; | ||
private Integer amountAllVisits; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
server: | ||
port: 8081 | ||
|
||
spring: | ||
data: | ||
mongodb: | ||
host: localhost | ||
port: 27017 | ||
database: digital |
Oops, something went wrong.