Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/home", "/register", "/login", "/css/**", "/js/**").permitAll()
.requestMatchers("/", "/reports/**", "/register", "/login", "/css/**", "/js/**").permitAll()
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**").hasRole("ADMIN")
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/user/**").hasRole("USER")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package ru.dmitriev.NauJavaSpring.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import ru.dmitriev.NauJavaSpring.entity.Report;
import ru.dmitriev.NauJavaSpring.services.ReportService;

import java.util.concurrent.CompletableFuture;

@RestController
@RequestMapping("/reports")
public class ReportController {

private final ReportService reportService;

@Autowired
public ReportController(ReportService reportService) {
this.reportService = reportService;
}

@PostMapping
public ResponseEntity<Long> createReport() {
// Создаем отчет и запускаем асинхронное формирование
Long reportId = reportService.createReport();
reportService.generateReport(reportId);
return ResponseEntity.ok(reportId); // Возвращаем ID созданного отчета
}

@GetMapping("/{id}")
public ResponseEntity<String> getReportContent(@PathVariable Long id) {
Report report = reportService.getReportById(id);

if (report == null) {
return ResponseEntity.notFound().build(); // Возвращаем 404, если отчет не найден
}

return switch (report.getStatus()) {
case CREATED -> ResponseEntity.ok("Отчет еще формируется. Пожалуйста, попробуйте позже.");
case ERROR -> ResponseEntity.ok("Произошла ошибка при формировании отчета.");
case COMPLETED -> ResponseEntity.ok(report.getContent()); // Возвращаем содержимое готового отчета
default -> ResponseEntity.status(500).body("Неизвестный статус отчета.");
};
}
}
35 changes: 35 additions & 0 deletions src/main/java/ru/dmitriev/NauJavaSpring/entity/Report.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package ru.dmitriev.NauJavaSpring.entity;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.List;

@Getter
@Setter
@Entity
@Table(name = "reports")
public class Report {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Enumerated(EnumType.STRING)
@Column(name = "status", nullable = false)
private ReportStatus status;

@Column(name = "content", columnDefinition = "TEXT")
private String content;

public Report() {
}

public Report(ReportStatus status, String content) {
this.status = status;
this.content = content;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ru.dmitriev.NauJavaSpring.entity;

public enum ReportStatus {
CREATED,
COMPLETED,
ERROR
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ru.dmitriev.NauJavaSpring.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import ru.dmitriev.NauJavaSpring.entity.Report;

@Repository
public interface ReportRepository extends JpaRepository<Report, Long> {

}
115 changes: 115 additions & 0 deletions src/main/java/ru/dmitriev/NauJavaSpring/services/ReportService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package ru.dmitriev.NauJavaSpring.services;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.servlet.ModelAndView;
import ru.dmitriev.NauJavaSpring.entity.Event;
import ru.dmitriev.NauJavaSpring.entity.Report;
import ru.dmitriev.NauJavaSpring.entity.ReportStatus;
import ru.dmitriev.NauJavaSpring.repository.EventRepository;
import ru.dmitriev.NauJavaSpring.repository.ReportRepository;
import ru.dmitriev.NauJavaSpring.repository.UserRepository;

import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.StreamSupport;
import java.util.stream.Collectors;

@Service
public class ReportService {

private final UserRepository userRepository;
private final EventRepository eventRepository;

private final ReportRepository reportRepository;

@Autowired
public ReportService(UserRepository userRepository, EventRepository eventRepository, ReportRepository reportRepository) {
this.userRepository = userRepository;
this.eventRepository = eventRepository;
this.reportRepository = reportRepository;
}

public Report getReportById(Long id) {
Optional<Report> report = reportRepository.findById(id);
return report.orElse(null); // Вернуть отчет, если он найден, иначе вернуть null
}

public String getReportContent(Long reportId) {
return reportRepository.findById(reportId)
.map(Report::getContent)
.orElse("Отчет не найден");
}

public Long createReport() {
Report report = new Report();
report.setStatus(ReportStatus.CREATED);
report.setContent("Отчет находится в процессе формирования...");
Report savedReport = reportRepository.save(report);
return savedReport.getId();
}

@Transactional
public CompletableFuture<Void> generateReport(Long reportId) {
return CompletableFuture.runAsync(() -> {
Report report = reportRepository.findById(reportId)
.orElseThrow(() -> new IllegalArgumentException("Отчет не найден"));

try {
long startTime = System.currentTimeMillis();

// Задача для подсчета пользователей
CompletableFuture<Long> userCountFuture = CompletableFuture.supplyAsync(() -> {
long start = System.currentTimeMillis();
long count = userRepository.count();
System.out.println("Время для подсчета пользователей: " + (System.currentTimeMillis() - start) + " ms");
return count;
});

// Задача для получения списка объектов Event
CompletableFuture<List<String>> eventsFuture = CompletableFuture.supplyAsync(() -> {
long start = System.currentTimeMillis();
List<String> eventNames = StreamSupport.stream(eventRepository.findAll().spliterator(), false)
.map(Event::getTitle)
.collect(Collectors.toList());
System.out.println("Время для получения объектов Event: " + (System.currentTimeMillis() - start) + " ms");
return eventNames;
});

// Ожидание завершения задач
Long userCount = userCountFuture.join();
List<String> events = eventsFuture.join();

long totalElapsedTime = System.currentTimeMillis() - startTime;

// Формирование содержимого отчета
StringBuilder reportContent = new StringBuilder();
reportContent.append("<html><body>");
reportContent.append("<h1>Статистика приложения</h1>");
reportContent.append("<p>Количество пользователей: ").append(userCount).append("</p>");
reportContent.append("<p>Список объектов Event:</p><ul>");
events.forEach(eventName -> reportContent.append("<li>").append(eventName).append("</li>"));
reportContent.append("</ul>");
reportContent.append("<p>Общее время формирования отчета: ").append(totalElapsedTime).append(" ms</p>");
reportContent.append("</body></html>");

// Сохранение содержимого и обновление статуса отчета
report.setContent(reportContent.toString());
report.setStatus(ReportStatus.COMPLETED);
} catch (Exception e) {
report.setStatus(ReportStatus.ERROR);
report.setContent("Произошла ошибка при формировании отчета: " + e.getMessage());
e.printStackTrace();
} finally {
reportRepository.save(report);
}
});
}

public Report saveReport(Report report) {
return reportRepository.save(report);
}
}

33 changes: 33 additions & 0 deletions src/main/resources/templates/report.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Отчет</title>
</head>
<body>
<h1>Отчет о статистике</h1>
<p>Количество зарегистрированных пользователей: <span th:text="${userCount}"></span></p>

<h2>Список событий</h2>
<table>
<thead>
<tr>
<th>Название события</th>
<th>Дата</th>
<th>Описание</th>
</tr>
</thead>
<tbody>
<tr th:each="event : ${events}">
<td th:text="${event.title}"></td>
<td th:text="${event.eventTime}"></td>
<td th:text="${event.description}"></td>
</tr>
</tbody>
</table>

<p>Время на получение количества пользователей: <span th:text="${usersTime}"></span> мс</p>
<p>Время на получение списка событий: <span th:text="${eventsTime}"></span> мс</p>
<p>Общее время формирования отчета: <span th:text="${totalTime}"></span> мс</p>
</body>
</html>