diff --git a/💼 Работа/Собеседования/Лето 2025/Задачи/💼 Прикладная задача для Middle разработчика.md b/💼 Работа/Собеседования/Лето 2025/Задачи/💼 Прикладная задача для Middle разработчика.md
index 24b56a1..e636226 100644
--- a/💼 Работа/Собеседования/Лето 2025/Задачи/💼 Прикладная задача для Middle разработчика.md
+++ b/💼 Работа/Собеседования/Лето 2025/Задачи/💼 Прикладная задача для Middle разработчика.md
@@ -24,29 +24,16 @@
### **Техническое задание:**
-Реализуйте следующие компоненты:
+**Опишите подход к решению задачи:**
-1. **Entity классы** (Event, Participant, Registration)
-
-2. **Repository интерфейсы**
-
-3. **Service класс EventRegistrationService** с методами:
-
- - `registerParticipant(Long eventId, Long participantId)`
- - `cancelRegistration(Long eventId, Long participantId)`
- - `cancelEvent(Long eventId)`
- - `getEventParticipants(Long eventId)`
-4. **REST Controller** с endpoint'ами для регистрации
-
-5. **Обработка исключений** для всех бизнес-правил
-
-
-### **Дополнительные требования:**
-
-- Использовать Spring Boot, JPA/Hibernate
-- Добавить валидацию данных
-- Написать unit-тесты для сервиса
-- Подумать о транзакциях и concurrency
+1. **Архитектуру приложения** - какие слои нужны
+2. **Entity классы** - основные поля и связи
+3. **Ключевые методы EventRegistrationService**
+4. **Обработку бизнес-правил** - где и как валидировать
+5. **REST API endpoints** - какие нужны
+6. **Обработку ошибок** - типы исключений
+7. **Транзакции** - где нужны и почему
+8. **Тестирование** - что важно покрыть тестами
**Время на выполнение:** 25 минут
@@ -69,7 +56,7 @@
```
Ввод: meetings = [[0,30],[5,10],[15,20]]
Вывод: 2
-Объяснение: Можно выбрать встречи [0,30] и [15,20] ИЛИ [5,10] и [15,20]
+Объяснение: Можно выбрать встречи [5,10] и [15,20]
```
**Пример 2:**
@@ -104,397 +91,7 @@
---
-# ✅ Решения задач
-
-## 💼 Решение прикладной задачи
-
- Показать решение
-
-### **1. Entity классы:**
-
-```java
-@Entity
-@Table(name = "events")
-public class Event {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
-
- @Column(nullable = false)
- private String name;
-
- private String description;
-
- @Column(name = "start_date", nullable = false)
- private LocalDateTime startDate;
-
- @Column(name = "end_date", nullable = false)
- private LocalDateTime endDate;
-
- @Column(name = "max_participants", nullable = false)
- private Integer maxParticipants;
-
- @Enumerated(EnumType.STRING)
- private EventStatus status;
-
- @OneToMany(mappedBy = "event", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
- private List registrations = new ArrayList<>();
-
- // конструкторы, геттеры, сеттеры
-}
-
-@Entity
-@Table(name = "participants")
-public class Participant {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
-
- @Column(nullable = false)
- private String name;
-
- @Column(nullable = false, unique = true)
- private String email;
-
- // конструкторы, геттеры, сеттеры
-}
-
-@Entity
-@Table(name = "registrations")
-public class Registration {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
-
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "event_id", nullable = false)
- private Event event;
-
- @ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "participant_id", nullable = false)
- private Participant participant;
-
- @Column(name = "registration_date", nullable = false)
- private LocalDateTime registrationDate;
-
- // конструкторы, геттеры, сеттеры
-}
-
-public enum EventStatus {
- DRAFT, PUBLISHED, CANCELLED
-}
-```
-
-### **2. Repository интерфейсы:**
-
-```java
-@Repository
-public interface EventRepository extends JpaRepository {
- List findByStatus(EventStatus status);
-}
-
-@Repository
-public interface ParticipantRepository extends JpaRepository {
- Optional findByEmail(String email);
-}
-
-@Repository
-public interface RegistrationRepository extends JpaRepository {
- boolean existsByEventIdAndParticipantId(Long eventId, Long participantId);
-
- Optional findByEventIdAndParticipantId(Long eventId, Long participantId);
-
- @Query("SELECT r.participant FROM Registration r WHERE r.event.id = :eventId")
- List findParticipantsByEventId(@Param("eventId") Long eventId);
-
- long countByEventId(Long eventId);
-
- void deleteByEventId(Long eventId);
-}
-```
-
-### **3. Service класс:**
-
-```java
-@Service
-@Transactional
-public class EventRegistrationService {
-
- private final EventRepository eventRepository;
- private final ParticipantRepository participantRepository;
- private final RegistrationRepository registrationRepository;
- private final NotificationService notificationService;
-
- public EventRegistrationService(EventRepository eventRepository,
- ParticipantRepository participantRepository,
- RegistrationRepository registrationRepository,
- NotificationService notificationService) {
- this.eventRepository = eventRepository;
- this.participantRepository = participantRepository;
- this.registrationRepository = registrationRepository;
- this.notificationService = notificationService;
- }
-
- public void registerParticipant(Long eventId, Long participantId) {
- Event event = eventRepository.findById(eventId)
- .orElseThrow(() -> new EventNotFoundException("Event not found: " + eventId));
-
- Participant participant = participantRepository.findById(participantId)
- .orElseThrow(() -> new ParticipantNotFoundException("Participant not found: " + participantId));
-
- validateRegistration(event, participant);
-
- Registration registration = new Registration();
- registration.setEvent(event);
- registration.setParticipant(participant);
- registration.setRegistrationDate(LocalDateTime.now());
-
- registrationRepository.save(registration);
- }
-
- private void validateRegistration(Event event, Participant participant) {
- // Проверка статуса события
- if (event.getStatus() != EventStatus.PUBLISHED) {
- throw new RegistrationException("Event is not published");
- }
-
- // Проверка даты начала
- if (event.getStartDate().isBefore(LocalDateTime.now())) {
- throw new RegistrationException("Event has already started");
- }
-
- // Проверка лимита участников
- long currentParticipants = registrationRepository.countByEventId(event.getId());
- if (currentParticipants >= event.getMaxParticipants()) {
- throw new RegistrationException("Event is full");
- }
-
- // Проверка дублирования регистрации
- if (registrationRepository.existsByEventIdAndParticipantId(event.getId(), participant.getId())) {
- throw new RegistrationException("Participant already registered for this event");
- }
- }
-
- public void cancelRegistration(Long eventId, Long participantId) {
- Registration registration = registrationRepository.findByEventIdAndParticipantId(eventId, participantId)
- .orElseThrow(() -> new RegistrationNotFoundException("Registration not found"));
-
- registrationRepository.delete(registration);
- }
-
- public void cancelEvent(Long eventId) {
- Event event = eventRepository.findById(eventId)
- .orElseThrow(() -> new EventNotFoundException("Event not found: " + eventId));
-
- List participants = registrationRepository.findParticipantsByEventId(eventId);
-
- // Отмена события
- event.setStatus(EventStatus.CANCELLED);
- eventRepository.save(event);
-
- // Уведомление участников
- participants.forEach(participant ->
- notificationService.notifyEventCancellation(participant, event));
- }
-
- @Transactional(readOnly = true)
- public List getEventParticipants(Long eventId) {
- if (!eventRepository.existsById(eventId)) {
- throw new EventNotFoundException("Event not found: " + eventId);
- }
-
- return registrationRepository.findParticipantsByEventId(eventId);
- }
-}
-```
-
-### **4. REST Controller:**
-
-```java
-@RestController
-@RequestMapping("/api/events")
-@Validated
-public class EventRegistrationController {
-
- private final EventRegistrationService registrationService;
-
- public EventRegistrationController(EventRegistrationService registrationService) {
- this.registrationService = registrationService;
- }
-
- @PostMapping("/{eventId}/participants/{participantId}")
- public ResponseEntity registerParticipant(
- @PathVariable @Positive Long eventId,
- @PathVariable @Positive Long participantId) {
-
- registrationService.registerParticipant(eventId, participantId);
- return ResponseEntity.ok().build();
- }
-
- @DeleteMapping("/{eventId}/participants/{participantId}")
- public ResponseEntity cancelRegistration(
- @PathVariable @Positive Long eventId,
- @PathVariable @Positive Long participantId) {
-
- registrationService.cancelRegistration(eventId, participantId);
- return ResponseEntity.ok().build();
- }
-
- @DeleteMapping("/{eventId}")
- public ResponseEntity cancelEvent(@PathVariable @Positive Long eventId) {
- registrationService.cancelEvent(eventId);
- return ResponseEntity.ok().build();
- }
-
- @GetMapping("/{eventId}/participants")
- public ResponseEntity> getEventParticipants(
- @PathVariable @Positive Long eventId) {
-
- List participants = registrationService.getEventParticipants(eventId);
- List dtos = participants.stream()
- .map(this::toDto)
- .collect(Collectors.toList());
-
- return ResponseEntity.ok(dtos);
- }
-
- private ParticipantDto toDto(Participant participant) {
- return new ParticipantDto(participant.getId(), participant.getName(), participant.getEmail());
- }
-}
-```
-
-### **5. Exception Handling:**
-
-```java
-@ControllerAdvice
-public class GlobalExceptionHandler {
-
- @ExceptionHandler(EventNotFoundException.class)
- public ResponseEntity handleEventNotFound(EventNotFoundException ex) {
- ErrorResponse error = new ErrorResponse("EVENT_NOT_FOUND", ex.getMessage());
- return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
- }
-
- @ExceptionHandler(RegistrationException.class)
- public ResponseEntity handleRegistrationError(RegistrationException ex) {
- ErrorResponse error = new ErrorResponse("REGISTRATION_ERROR", ex.getMessage());
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
- }
-
- @ExceptionHandler(ConstraintViolationException.class)
- public ResponseEntity handleValidation(ConstraintViolationException ex) {
- ErrorResponse error = new ErrorResponse("VALIDATION_ERROR", "Invalid input parameters");
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
- }
-}
-```
-
-### **6. Unit тесты:**
-
-```java
-@ExtendWith(MockitoExtension.class)
-class EventRegistrationServiceTest {
-
- @Mock
- private EventRepository eventRepository;
-
- @Mock
- private ParticipantRepository participantRepository;
-
- @Mock
- private RegistrationRepository registrationRepository;
-
- @Mock
- private NotificationService notificationService;
-
- @InjectMocks
- private EventRegistrationService service;
-
- @Test
- void registerParticipant_Success() {
- // Given
- Long eventId = 1L;
- Long participantId = 1L;
-
- Event event = createPublishedEvent();
- Participant participant = createParticipant();
-
- when(eventRepository.findById(eventId)).thenReturn(Optional.of(event));
- when(participantRepository.findById(participantId)).thenReturn(Optional.of(participant));
- when(registrationRepository.countByEventId(eventId)).thenReturn(5L);
- when(registrationRepository.existsByEventIdAndParticipantId(eventId, participantId)).thenReturn(false);
-
- // When
- service.registerParticipant(eventId, participantId);
-
- // Then
- verify(registrationRepository).save(any(Registration.class));
- }
-
- @Test
- void registerParticipant_EventNotPublished_ThrowsException() {
- // Given
- Event event = createDraftEvent();
- Participant participant = createParticipant();
-
- when(eventRepository.findById(1L)).thenReturn(Optional.of(event));
- when(participantRepository.findById(1L)).thenReturn(Optional.of(participant));
-
- // When & Then
- assertThrows(RegistrationException.class,
- () -> service.registerParticipant(1L, 1L));
- }
-
- @Test
- void registerParticipant_EventFull_ThrowsException() {
- // Given
- Event event = createPublishedEvent();
- event.setMaxParticipants(10);
- Participant participant = createParticipant();
-
- when(eventRepository.findById(1L)).thenReturn(Optional.of(event));
- when(participantRepository.findById(1L)).thenReturn(Optional.of(participant));
- when(registrationRepository.countByEventId(1L)).thenReturn(10L);
-
- // When & Then
- assertThrows(RegistrationException.class,
- () -> service.registerParticipant(1L, 1L));
- }
-
- private Event createPublishedEvent() {
- Event event = new Event();
- event.setId(1L);
- event.setStatus(EventStatus.PUBLISHED);
- event.setStartDate(LocalDateTime.now().plusDays(1));
- event.setMaxParticipants(10);
- return event;
- }
-
- private Event createDraftEvent() {
- Event event = new Event();
- event.setId(1L);
- event.setStatus(EventStatus.DRAFT);
- event.setStartDate(LocalDateTime.now().plusDays(1));
- event.setMaxParticipants(10);
- return event;
- }
-
- private Participant createParticipant() {
- Participant participant = new Participant();
- participant.setId(1L);
- participant.setName("John Doe");
- participant.setEmail("john@example.com");
- return participant;
- }
-}
-```
-
-
-
----
-
-## 🧮 Решение алгоритмической задачи
+# ✅ Решение алгоритмической задачи
Показать решение
@@ -556,6 +153,14 @@ public class MeetingScheduler {
### **Пошаговый пример:**
+Для `meetings = [[0,30],[5,10],[15,20]]`:
+
+1. После сортировки по времени окончания: `[[5,10], [15,20], [0,30]]`
+2. Выбираем `[5,10]`, `lastEndTime = 10`
+3. Проверяем `[15,20]`: `15 >= 10` ✓, выбираем, `lastEndTime = 20`
+4. Проверяем `[0,30]`: `0 < 20` ✗, пропускаем
+5. Результат: 2 встречи
+
Для `meetings = [[1,5],[8,9],[8,9],[5,9],[9,15]]`:
1. После сортировки по времени окончания: `[[1,5], [8,9], [8,9], [5,9], [9,15]]`
@@ -631,14 +236,10 @@ class MeetingSchedulerTest {
}
```
-### **Альтернативные подходы:**
-
-1. **Dynamic Programming:** Сложность O(n²), не оптимально для этой задачи
-2. **Recursive with memoization:** Также O(n²), избыточно
-3. **Priority Queue:** Можно использовать, но жадный алгоритм проще и эффективнее
-
### **Почему жадный алгоритм работает:**
Выбирая встречу с самым ранним окончанием, мы всегда оставляем максимально возможное время для размещения остальных встреч. Это оптимальная стратегия для данной задачи.
+**Доказательство корректности:** Пусть OPT - оптимальное решение, а GREEDY - решение жадного алгоритма. Можно показать, что GREEDY не хуже OPT путем замены встреч в OPT на встречи из GREEDY без ухудшения результата.
+
\ No newline at end of file