From 832ce4d9d7ceaf8651aaeeaa205c24a51f3e427e Mon Sep 17 00:00:00 2001 From: clainyun Date: Tue, 20 May 2025 20:06:09 +0900 Subject: [PATCH 1/7] =?UTF-8?q?feat=20|=20sprint2=20|=20FRB-156=20|=20?= =?UTF-8?q?=EC=9E=91=EC=97=85=EC=9E=90=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5(Worker,=20WorkerZone=20=EC=97=94=ED=8B=B0=ED=8B=B0=20?= =?UTF-8?q?=EB=A7=A4=ED=95=91,=20Zone=EB=B3=84=20=EC=9E=91=EC=97=85?= =?UTF-8?q?=EC=9E=90=20=EB=B0=8F=20=EC=A0=84=EC=B2=B4=20=EC=9E=91=EC=97=85?= =?UTF-8?q?=EC=9E=90=20=EC=A1=B0=ED=9A=8C,=20Swagger=20API)=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EB=B0=8F=20JUnit5=20Mockito=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1=20|=20?= =?UTF-8?q?=EC=9C=A4=EB=8B=A4=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/controller/WorkerController.java | 39 ++++++++ .../com/factoreal/backend/dto/WorkerDto.java | 30 ++++++ .../repository/WorkerZoneRepository.java | 13 +++ .../backend/service/WorkerService.java | 49 ++++++++++ .../backend/service/WorkerServiceTest.java | 91 +++++++++++++++++++ 5 files changed, 222 insertions(+) create mode 100644 src/main/java/com/factoreal/backend/controller/WorkerController.java create mode 100644 src/main/java/com/factoreal/backend/dto/WorkerDto.java create mode 100644 src/main/java/com/factoreal/backend/repository/WorkerZoneRepository.java create mode 100644 src/main/java/com/factoreal/backend/service/WorkerService.java create mode 100644 src/test/java/com/factoreal/backend/service/WorkerServiceTest.java diff --git a/src/main/java/com/factoreal/backend/controller/WorkerController.java b/src/main/java/com/factoreal/backend/controller/WorkerController.java new file mode 100644 index 00000000..b943abb7 --- /dev/null +++ b/src/main/java/com/factoreal/backend/controller/WorkerController.java @@ -0,0 +1,39 @@ +package com.factoreal.backend.controller; + +import com.factoreal.backend.dto.WorkerDto; +import com.factoreal.backend.service.WorkerService; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/workers") +@RequiredArgsConstructor +@Slf4j +@Tag(name = "작업자 API", description = "작업자 조회 API") +public class WorkerController { + + private final WorkerService workerService; + + @Operation(summary = "전체 작업자 목록 조회", description = "전체 작업자 목록을 조회합니다.") + @GetMapping + public ResponseEntity> getAllWorkers() { + log.info("전체 작업자 목록 조회 요청"); + List workers = workerService.getAllWorkers(); + return ResponseEntity.ok(workers); + } + + @Operation(summary = "공간별 작업자 목록 조회", description = "공간 ID를 기반으로 공간별 작업자 목록을 조회합니다.") + @GetMapping("/zone/{zoneId}") + public ResponseEntity> getWorkersByZoneId(@PathVariable String zoneId) { + log.info("공간 ID: {}의 작업자 목록 조회 요청", zoneId); + List zoneWorkers = workerService.getWorkersByZoneId(zoneId); + return ResponseEntity.ok(zoneWorkers); + } +} \ No newline at end of file diff --git a/src/main/java/com/factoreal/backend/dto/WorkerDto.java b/src/main/java/com/factoreal/backend/dto/WorkerDto.java new file mode 100644 index 00000000..b79b04b7 --- /dev/null +++ b/src/main/java/com/factoreal/backend/dto/WorkerDto.java @@ -0,0 +1,30 @@ +package com.factoreal.backend.dto; + +import com.factoreal.backend.entity.Worker; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class WorkerDto { + private String workerId; + private String name; + private String phoneNumber; + private String email; + private Boolean isManager; // 관리자 여부 + + // Entity -> DTO 변환 + public static WorkerDto fromEntity(Worker worker, Boolean isManager) { + return WorkerDto.builder() + .workerId(worker.getWorkerId()) + .name(worker.getName()) + .phoneNumber(worker.getPhoneNumber()) + .email(worker.getEmail()) + .isManager(isManager) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/factoreal/backend/repository/WorkerZoneRepository.java b/src/main/java/com/factoreal/backend/repository/WorkerZoneRepository.java new file mode 100644 index 00000000..5733d7e8 --- /dev/null +++ b/src/main/java/com/factoreal/backend/repository/WorkerZoneRepository.java @@ -0,0 +1,13 @@ +package com.factoreal.backend.repository; + +import com.factoreal.backend.entity.WorkerZone; +import com.factoreal.backend.entity.WorkerZoneId; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface WorkerZoneRepository extends JpaRepository { + + // 특정 zone_id에 속한 작업자 목록 조회 + List findByZoneZoneId(String zoneId); +} \ No newline at end of file diff --git a/src/main/java/com/factoreal/backend/service/WorkerService.java b/src/main/java/com/factoreal/backend/service/WorkerService.java new file mode 100644 index 00000000..e547c821 --- /dev/null +++ b/src/main/java/com/factoreal/backend/service/WorkerService.java @@ -0,0 +1,49 @@ +package com.factoreal.backend.service; + +import com.factoreal.backend.dto.WorkerDto; +import com.factoreal.backend.entity.Worker; +import com.factoreal.backend.entity.WorkerZone; +import com.factoreal.backend.repository.WorkerRepository; +import com.factoreal.backend.repository.WorkerZoneRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +@Slf4j +public class WorkerService { + + private final WorkerRepository workerRepository; + private final WorkerZoneRepository workerZoneRepository; + + public List getAllWorkers() { + log.info("전체 작업자 목록 조회"); + List workers = workerRepository.findAll(); + + return workers.stream() + .map(worker -> { + // 기본값으로는 관리자임 (zoneId랑 매핑되어 있으니까) + return WorkerDto.fromEntity(worker, true); + }) + .collect(Collectors.toList()); + } + + public List getWorkersByZoneId(String zoneId) { + log.info("공간 ID: {}의 작업자 목록 조회", zoneId); + List workerZones = workerZoneRepository.findByZoneZoneId(zoneId); + + return workerZones.stream() + .map(workerZone -> { + Worker worker = workerZone.getWorker(); + Boolean isManager = workerZone.getManageYn(); + return WorkerDto.fromEntity(worker, isManager); + }) + .collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/src/test/java/com/factoreal/backend/service/WorkerServiceTest.java b/src/test/java/com/factoreal/backend/service/WorkerServiceTest.java new file mode 100644 index 00000000..3777a5f8 --- /dev/null +++ b/src/test/java/com/factoreal/backend/service/WorkerServiceTest.java @@ -0,0 +1,91 @@ +package com.factoreal.backend.service; + +import com.factoreal.backend.dto.WorkerDto; +import com.factoreal.backend.entity.Worker; +import com.factoreal.backend.repository.WorkerRepository; +import com.factoreal.backend.repository.WorkerZoneRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.Mockito.when; + +public class WorkerServiceTest { + + @Mock + private WorkerRepository workerRepository; + + @Mock + private WorkerZoneRepository workerZoneRepository; + + @InjectMocks + private WorkerService workerService; + + private Worker worker1; + private Worker worker2; + + @BeforeEach + public void setup() { + MockitoAnnotations.openMocks(this); + + // 테스트용 작업자 데이터 생성 + worker1 = Worker.builder() + .workerId("20240101-1234") + .name("홍길동") + .phoneNumber("01012345678") + .email("hong@example.com") + .build(); + + worker2 = Worker.builder() + .workerId("20240102-5678") + .name("김철수") + .phoneNumber("01087654321") + .email("kim@example.com") + .build(); + } + + @Test + public void testGetAllWorkers() { + // Mock 설정 + when(workerRepository.findAll()).thenReturn(Arrays.asList(worker1, worker2)); + + // 서비스 메소드 호출 + List result = workerService.getAllWorkers(); + + // 결과 검증 + assertNotNull(result); + assertEquals(2, result.size()); + + // 첫 번째 작업자 정보 확인 + assertEquals("20240101-1234", result.get(0).getWorkerId()); + assertEquals("홍길동", result.get(0).getName()); + assertEquals("01012345678", result.get(0).getPhoneNumber()); + assertEquals("hong@example.com", result.get(0).getEmail()); + assertEquals(true, result.get(0).getIsManager()); + + // 두 번째 작업자 정보 확인 + assertEquals("20240102-5678", result.get(1).getWorkerId()); + assertEquals("김철수", result.get(1).getName()); + assertEquals("01087654321", result.get(1).getPhoneNumber()); + assertEquals("kim@example.com", result.get(1).getEmail()); + assertEquals(false, result.get(1).getIsManager()); + } + + @Test + void testGetWorkersByZoneId() { + // 테스트 실행 + List workers = workerService.getWorkersByZoneId("zone1"); + + // 검증 + assertEquals(1, workers.size()); + assertEquals("worker1", workers.get(0).getWorkerId()); + assertEquals(true, workers.get(0).getIsManager()); // 관리자 여부 검증 + } +} From e9b1a7724c9d54078eb7f59c788b61ea2cf94609 Mon Sep 17 00:00:00 2001 From: clainyun Date: Tue, 20 May 2025 20:12:34 +0900 Subject: [PATCH 2/7] =?UTF-8?q?feat=20|=20sprint2=20|=20FRB-156=20|=20?= =?UTF-8?q?=EC=9E=91=EC=97=85=EC=9E=90=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5(Worker,=20WorkerZone=20=EC=97=94=ED=8B=B0=ED=8B=B0=20?= =?UTF-8?q?=EB=A7=A4=ED=95=91,=20Zone=EB=B3=84=20=EC=9E=91=EC=97=85?= =?UTF-8?q?=EC=9E=90=20=EB=B0=8F=20=EC=A0=84=EC=B2=B4=20=EC=9E=91=EC=97=85?= =?UTF-8?q?=EC=9E=90=20=EC=A1=B0=ED=9A=8C,=20Swagger=20API)=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EB=B0=8F=20JUnit5=20Mockito=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1=20|=20?= =?UTF-8?q?=EC=9C=A4=EB=8B=A4=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/service/WorkerServiceTest.java | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/factoreal/backend/service/WorkerServiceTest.java b/src/test/java/com/factoreal/backend/service/WorkerServiceTest.java index 3777a5f8..d506f35b 100644 --- a/src/test/java/com/factoreal/backend/service/WorkerServiceTest.java +++ b/src/test/java/com/factoreal/backend/service/WorkerServiceTest.java @@ -2,6 +2,9 @@ import com.factoreal.backend.dto.WorkerDto; import com.factoreal.backend.entity.Worker; +import com.factoreal.backend.entity.WorkerZone; +import com.factoreal.backend.entity.WorkerZoneId; +import com.factoreal.backend.entity.Zone; import com.factoreal.backend.repository.WorkerRepository; import com.factoreal.backend.repository.WorkerZoneRepository; import org.junit.jupiter.api.BeforeEach; @@ -30,6 +33,8 @@ public class WorkerServiceTest { private Worker worker1; private Worker worker2; + private Zone zone1; + private WorkerZone workerZone1; @BeforeEach public void setup() { @@ -49,6 +54,24 @@ public void setup() { .phoneNumber("01087654321") .email("kim@example.com") .build(); + + // 테스트용 공간 데이터 생성 + zone1 = Zone.builder() + .zoneId("zone1") + .zoneName("테스트 공간") + .build(); + + // 테스트용 WorkerZone 데이터 생성 + WorkerZoneId id1 = new WorkerZoneId(); + id1.setWorkerId("20240101-1234"); + id1.setZoneId("zone1"); + + workerZone1 = WorkerZone.builder() + .id(id1) + .worker(worker1) + .zone(zone1) + .manageYn(true) // 관리자 여부 설정 + .build(); } @Test @@ -68,24 +91,30 @@ public void testGetAllWorkers() { assertEquals("홍길동", result.get(0).getName()); assertEquals("01012345678", result.get(0).getPhoneNumber()); assertEquals("hong@example.com", result.get(0).getEmail()); - assertEquals(true, result.get(0).getIsManager()); + assertEquals(true, result.get(0).getIsManager()); // 서비스 로직에 따라 true로 설정 // 두 번째 작업자 정보 확인 assertEquals("20240102-5678", result.get(1).getWorkerId()); assertEquals("김철수", result.get(1).getName()); assertEquals("01087654321", result.get(1).getPhoneNumber()); assertEquals("kim@example.com", result.get(1).getEmail()); - assertEquals(false, result.get(1).getIsManager()); + assertEquals(true, result.get(1).getIsManager()); // 서비스 로직에 따라 true로 설정 } @Test void testGetWorkersByZoneId() { + // Mock 설정 + when(workerZoneRepository.findByZoneZoneId("zone1")).thenReturn(Arrays.asList(workerZone1)); + // 테스트 실행 List workers = workerService.getWorkersByZoneId("zone1"); // 검증 assertEquals(1, workers.size()); - assertEquals("worker1", workers.get(0).getWorkerId()); + assertEquals("20240101-1234", workers.get(0).getWorkerId()); + assertEquals("홍길동", workers.get(0).getName()); + assertEquals("01012345678", workers.get(0).getPhoneNumber()); + assertEquals("hong@example.com", workers.get(0).getEmail()); assertEquals(true, workers.get(0).getIsManager()); // 관리자 여부 검증 } } From 835b3841c05366dc61a84a14a52f86ac5717334a Mon Sep 17 00:00:00 2001 From: clainyun Date: Wed, 21 May 2025 09:36:57 +0900 Subject: [PATCH 3/7] =?UTF-8?q?feat=20|=20sprint2=20|=20FRB-166=20|=20?= =?UTF-8?q?=EC=9B=B9=EC=86=8C=EC=BC=93=20=EA=B8=B0=EB=B0=98=20=EC=8B=9C?= =?UTF-8?q?=EC=8A=A4=ED=85=9C=20=EB=A1=9C=EA=B7=B8=20=EC=A0=84=EC=86=A1?= =?UTF-8?q?=EC=9D=84=20REST=20API=20=EA=B8=B0=EB=B0=98=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EB=B0=A9=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EB=B0=8F=20=EA=B5=AC=ED=98=84=20|=20=EC=9C=A4?= =?UTF-8?q?=EB=8B=A4=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/consumer/kafka/KafkaConsumer.java | 3 +- .../controller/SystemLogController.java | 31 +++++++++ .../dto/abnormalLog/AbnormalPagingDto.java | 5 ++ .../dto/abnormalLog/SystemLogResponseDto.java | 67 +++++++++++++++++++ .../backend/repository/AbnLogRepository.java | 3 + .../backend/service/AbnormalLogService.java | 13 ++++ 6 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/factoreal/backend/controller/SystemLogController.java create mode 100644 src/main/java/com/factoreal/backend/dto/abnormalLog/SystemLogResponseDto.java diff --git a/src/main/java/com/factoreal/backend/consumer/kafka/KafkaConsumer.java b/src/main/java/com/factoreal/backend/consumer/kafka/KafkaConsumer.java index fb0dc44f..67fcb450 100644 --- a/src/main/java/com/factoreal/backend/consumer/kafka/KafkaConsumer.java +++ b/src/main/java/com/factoreal/backend/consumer/kafka/KafkaConsumer.java @@ -78,7 +78,7 @@ public void consume(String message) { SensorKafkaDto dto = objectMapper.readValue(message, SensorKafkaDto.class); // 시스템 로그 (위험도 변화 감지 -> 비동기 전송) - sendSystemLog(dto); + // sendSystemLog(dto); // 공간 센서일 때만 히트맵용 웹소켓 전송 if (dto.getEquipId() != null && dto.getZoneId() != null && dto.getEquipId().equals(dto.getZoneId())) { @@ -170,6 +170,7 @@ public void startAlarm(SensorKafkaDto sensorData, AbnormalLog abnormalLog, RiskL // 공간(zone)별 위험도 변경 시 시스템 로그 전송 @Async + @Deprecated public void sendSystemLog(SensorKafkaDto dto) { String zoneId = dto.getZoneId(); int newLevel = getDangerLevel(dto.getSensorType(), dto.getVal()); diff --git a/src/main/java/com/factoreal/backend/controller/SystemLogController.java b/src/main/java/com/factoreal/backend/controller/SystemLogController.java new file mode 100644 index 00000000..dfeb3839 --- /dev/null +++ b/src/main/java/com/factoreal/backend/controller/SystemLogController.java @@ -0,0 +1,31 @@ +package com.factoreal.backend.controller; + +import com.factoreal.backend.dto.abnormalLog.AbnormalPagingDto; +import com.factoreal.backend.dto.abnormalLog.SystemLogResponseDto; +import com.factoreal.backend.service.AbnormalLogService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@Tag(name = "시스템 로그 API", description = "공간별 시스템 로그 조회 API") +@RestController +@RequestMapping("/api/system-logs") +@RequiredArgsConstructor +public class SystemLogController { + private final AbnormalLogService abnormalLogService; + + @Operation(summary = "공간별 시스템 로그 조회", description = "특정 공간(zone)의 시스템 로그를 페이징 처리하여 조회합니다.") + @GetMapping("/zone/{zoneId}") + public ResponseEntity> getSystemLogsByZone( + @Parameter(description = "조회할 공간 ID", required = true) + @PathVariable String zoneId, + @Parameter(description = "페이징 정보 (page: 페이지 번호, size: 페이지 크기)") + @ModelAttribute AbnormalPagingDto pagingDto) { + Page logs = abnormalLogService.findSystemLogsByZoneId(zoneId, pagingDto); + return ResponseEntity.ok(logs); + } +} \ No newline at end of file diff --git a/src/main/java/com/factoreal/backend/dto/abnormalLog/AbnormalPagingDto.java b/src/main/java/com/factoreal/backend/dto/abnormalLog/AbnormalPagingDto.java index 79d459a5..7c90ea2a 100644 --- a/src/main/java/com/factoreal/backend/dto/abnormalLog/AbnormalPagingDto.java +++ b/src/main/java/com/factoreal/backend/dto/abnormalLog/AbnormalPagingDto.java @@ -1,9 +1,14 @@ package com.factoreal.backend.dto.abnormalLog; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @Data +@Schema(description = "페이징 정보 DTO") public class AbnormalPagingDto { + @Schema(description = "페이지 번호 (0부터 시작)", example = "0") private int page; + + @Schema(description = "페이지 크기", example = "10") private int size; } diff --git a/src/main/java/com/factoreal/backend/dto/abnormalLog/SystemLogResponseDto.java b/src/main/java/com/factoreal/backend/dto/abnormalLog/SystemLogResponseDto.java new file mode 100644 index 00000000..5a5f5f24 --- /dev/null +++ b/src/main/java/com/factoreal/backend/dto/abnormalLog/SystemLogResponseDto.java @@ -0,0 +1,67 @@ +package com.factoreal.backend.dto.abnormalLog; + +import com.factoreal.backend.entity.AbnormalLog; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SystemLogResponseDto { + private String zoneId; + private String zoneName; + private String sensorType; + private int dangerLevel; + private double value; + private LocalDateTime timestamp; + private String abnormalType; + private String targetId; + + public static SystemLogResponseDto fromEntity(AbnormalLog abnormalLog) { + return SystemLogResponseDto.builder() + .zoneId(abnormalLog.getZone().getZoneId()) + .zoneName(abnormalLog.getZone().getZoneName()) + .sensorType(abnormalLog.getTargetType().toString()) + .dangerLevel(calculateDangerLevel(abnormalLog.getAbnormalType())) + .value(abnormalLog.getAbnVal()) + .timestamp(abnormalLog.getDetectedAt()) + .abnormalType(abnormalLog.getAbnormalType()) + .targetId(abnormalLog.getTargetId()) + .build(); + } + + private static int calculateDangerLevel(String abnormalType) { + if (abnormalType.contains("위험")) return 2; + if (abnormalType.contains("주의")) return 1; + return 0; + } +} + +/** + * { + "content": [ + { + "zoneId": "zone123", + "zoneName": "작업장 A", + "sensorType": "TEMPERATURE", + "dangerLevel": 2, + "value": 35.5, + "timestamp": "2024-03-20T14:30:00", + "abnormalType": "온도 위험", + "targetId": "sensor456" + } + // ... 더 많은 로그 + ], + "pageable": { + "pageNumber": 0, + "pageSize": 10 + }, + "totalElements": 50, + "totalPages": 5 +} + */ \ No newline at end of file diff --git a/src/main/java/com/factoreal/backend/repository/AbnLogRepository.java b/src/main/java/com/factoreal/backend/repository/AbnLogRepository.java index cfc7afbd..b066b882 100644 --- a/src/main/java/com/factoreal/backend/repository/AbnLogRepository.java +++ b/src/main/java/com/factoreal/backend/repository/AbnLogRepository.java @@ -12,4 +12,7 @@ public interface AbnLogRepository extends JpaRepository { long countByIsReadFalse(); // 읽지 않은 로그의 개수 반환 Page findAllByIsReadIsFalse(Pageable pageable); + + // zoneId로 페이징 처리된 로그 조회 + Page findByZone_ZoneIdOrderByDetectedAtDesc(String zoneId, Pageable pageable); } \ No newline at end of file diff --git a/src/main/java/com/factoreal/backend/service/AbnormalLogService.java b/src/main/java/com/factoreal/backend/service/AbnormalLogService.java index 7e49f5ce..911ef1c9 100644 --- a/src/main/java/com/factoreal/backend/service/AbnormalLogService.java +++ b/src/main/java/com/factoreal/backend/service/AbnormalLogService.java @@ -3,6 +3,7 @@ import com.factoreal.backend.dto.abnormalLog.AbnormalLogDto; import com.factoreal.backend.dto.abnormalLog.AbnormalPagingDto; import com.factoreal.backend.dto.abnormalLog.LogType; +import com.factoreal.backend.dto.abnormalLog.SystemLogResponseDto; import com.factoreal.backend.dto.SensorKafkaDto; import com.factoreal.backend.entity.AbnormalLog; import com.factoreal.backend.entity.Zone; @@ -21,6 +22,8 @@ import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; @Service @Slf4j @@ -150,6 +153,16 @@ public Long readRequired(){ return count; } + @Transactional(readOnly = true) + public Page findSystemLogsByZoneId(String zoneId, AbnormalPagingDto pagingDto) { + log.info("공간 ID: {}의 시스템 로그 조회", zoneId); + Pageable pageable = getPageable(pagingDto); + + // zoneId로 직접 필터링된 로그 조회 + Page logs = abnLogRepository.findByZone_ZoneIdOrderByDetectedAtDesc(zoneId, pageable); + return logs.map(SystemLogResponseDto::fromEntity); + } + private Pageable getPageable(AbnormalPagingDto abnormalPagingDto){ return PageRequest.of( abnormalPagingDto.getPage(), From 94f781007301568a88c0d9076c24564e0405c904 Mon Sep 17 00:00:00 2001 From: clainyun Date: Wed, 21 May 2025 11:00:02 +0900 Subject: [PATCH 4/7] =?UTF-8?q?fix=20|=20sprint2=20|=20FRB-166=20|=20?= =?UTF-8?q?=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EB=A1=9C=EA=B7=B8=20API=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=20=ED=98=95=EC=8B=9D=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?(zoneName=20=EC=82=AD=EC=A0=9C,=20targetType=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(=ED=95=9C=EA=B8=80=20=EB=A7=A4=ED=95=91=20?= =?UTF-8?q?=ED=99=98=EA=B2=BD,=20=EC=9E=91=EC=97=85=EC=9E=90,=20=EC=84=A4?= =?UTF-8?q?=EB=B9=84)=20|=20=EC=9C=A4=EB=8B=A4=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/abnormalLog/SystemLogResponseDto.java | 26 ++++++++++++------- .../factoreal/backend/entity/AbnormalLog.java | 3 ++- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/factoreal/backend/dto/abnormalLog/SystemLogResponseDto.java b/src/main/java/com/factoreal/backend/dto/abnormalLog/SystemLogResponseDto.java index 5a5f5f24..c6fe9ea6 100644 --- a/src/main/java/com/factoreal/backend/dto/abnormalLog/SystemLogResponseDto.java +++ b/src/main/java/com/factoreal/backend/dto/abnormalLog/SystemLogResponseDto.java @@ -14,7 +14,7 @@ @AllArgsConstructor public class SystemLogResponseDto { private String zoneId; - private String zoneName; + private String targetType; private String sensorType; private int dangerLevel; private double value; @@ -25,7 +25,7 @@ public class SystemLogResponseDto { public static SystemLogResponseDto fromEntity(AbnormalLog abnormalLog) { return SystemLogResponseDto.builder() .zoneId(abnormalLog.getZone().getZoneId()) - .zoneName(abnormalLog.getZone().getZoneName()) + .targetType(convertLogTypeToKorean(abnormalLog.getTargetType())) .sensorType(abnormalLog.getTargetType().toString()) .dangerLevel(calculateDangerLevel(abnormalLog.getAbnormalType())) .value(abnormalLog.getAbnVal()) @@ -35,6 +35,14 @@ public static SystemLogResponseDto fromEntity(AbnormalLog abnormalLog) { .build(); } + private static String convertLogTypeToKorean(LogType logType) { + return switch (logType) { + case Sensor -> "환경"; + case Worker -> "작업자"; + case Equip -> "설비"; + }; + } + private static int calculateDangerLevel(String abnormalType) { if (abnormalType.contains("위험")) return 2; if (abnormalType.contains("주의")) return 1; @@ -47,13 +55,13 @@ private static int calculateDangerLevel(String abnormalType) { "content": [ { "zoneId": "zone123", - "zoneName": "작업장 A", - "sensorType": "TEMPERATURE", - "dangerLevel": 2, - "value": 35.5, - "timestamp": "2024-03-20T14:30:00", - "abnormalType": "온도 위험", - "targetId": "sensor456" + "targetType": "환경", // 또는 "작업자", "설비" + "sensorType": "TEMPERATURE", + "dangerLevel": 2, + "value": 35.5, + "timestamp": "2024-03-20T14:30:00", + "abnormalType": "온도 위험", + "targetId": "sensor456" } // ... 더 많은 로그 ], diff --git a/src/main/java/com/factoreal/backend/entity/AbnormalLog.java b/src/main/java/com/factoreal/backend/entity/AbnormalLog.java index 8681652e..d3f53ae9 100644 --- a/src/main/java/com/factoreal/backend/entity/AbnormalLog.java +++ b/src/main/java/com/factoreal/backend/entity/AbnormalLog.java @@ -30,7 +30,8 @@ public class AbnormalLog { private String targetId; // 고유 ID : 센서ID, WorkerID, EquipID @Column(name = "abnormal_type", length = 100) - private String abnormalType; // 이상 유형 : (예: 심박수 이상, 온도 초과, 진동 이상 등) + private String abnormalType; // 이상 유형 : (예: 심박수 위험, 온도 초과 위험, 진동 주의 등) + // 이상은 위험과 주의로 구분이 애매하므로 명확한 표현 필요 @Column(name = "abn_val") private Double abnVal; // 이상치 값 From 9e259286d85e742acba6f1e9a0a9847d1e86e349 Mon Sep 17 00:00:00 2001 From: clainyun Date: Wed, 21 May 2025 15:01:24 +0900 Subject: [PATCH 5/7] =?UTF-8?q?fix=20|=20sprint2=20|=20FRB-156=20|=20manag?= =?UTF-8?q?eYn=EC=9D=98=20default=EB=A5=BC=20false=EB=A1=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20FRB-156=20=EB=B3=91=ED=95=A9=20?= =?UTF-8?q?=EC=A4=80=EB=B9=84(=EC=9E=91=EC=97=85=EC=9E=90=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EC=88=98=EC=A0=95=20=EC=98=88?= =?UTF-8?q?=EC=A0=95)=20|=20=EC=9C=A4=EB=8B=A4=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/service/WorkerService.java | 29 ++++++++++--------- .../backend/service/WorkerServiceTest.java | 18 ++++++------ 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/factoreal/backend/service/WorkerService.java b/src/main/java/com/factoreal/backend/service/WorkerService.java index e547c821..13c6d41d 100644 --- a/src/main/java/com/factoreal/backend/service/WorkerService.java +++ b/src/main/java/com/factoreal/backend/service/WorkerService.java @@ -28,22 +28,25 @@ public List getAllWorkers() { return workers.stream() .map(worker -> { - // 기본값으로는 관리자임 (zoneId랑 매핑되어 있으니까) - return WorkerDto.fromEntity(worker, true); + // 기본값으로는 관리자가 아님 + return WorkerDto.fromEntity(worker, false); }) .collect(Collectors.toList()); } - public List getWorkersByZoneId(String zoneId) { - log.info("공간 ID: {}의 작업자 목록 조회", zoneId); - List workerZones = workerZoneRepository.findByZoneZoneId(zoneId); + + // TODO. 수정되어야 할 로직. 현재는 WorkerZone 테이블에서 공간id로 필터링 되는 모든 작업자를 끌고왔는데, + // 사실 현재 그 공간에서 실제로 작업하고 있는, 즉 들어 + // public List getWorkersByZoneId(String zoneId) { + // log.info("공간 ID: {}의 작업자 목록 조회", zoneId); + // List workerZones = workerZoneRepository.findByZoneZoneId(zoneId); - return workerZones.stream() - .map(workerZone -> { - Worker worker = workerZone.getWorker(); - Boolean isManager = workerZone.getManageYn(); - return WorkerDto.fromEntity(worker, isManager); - }) - .collect(Collectors.toList()); - } + // return workerZones.stream() + // .map(workerZone -> { + // Worker worker = workerZone.getWorker(); + // Boolean isManager = workerZone.getManageYn(); + // return WorkerDto.fromEntity(worker, isManager); + // }) + // .collect(Collectors.toList()); + // } } \ No newline at end of file diff --git a/src/test/java/com/factoreal/backend/service/WorkerServiceTest.java b/src/test/java/com/factoreal/backend/service/WorkerServiceTest.java index d506f35b..5b40f2ec 100644 --- a/src/test/java/com/factoreal/backend/service/WorkerServiceTest.java +++ b/src/test/java/com/factoreal/backend/service/WorkerServiceTest.java @@ -43,10 +43,10 @@ public void setup() { // 테스트용 작업자 데이터 생성 worker1 = Worker.builder() .workerId("20240101-1234") - .name("홍길동") - .phoneNumber("01012345678") - .email("hong@example.com") - .build(); + .name("홍길동") + .phoneNumber("01012345678") + .email("hong@example.com") + .build(); worker2 = Worker.builder() .workerId("20240102-5678") @@ -68,10 +68,10 @@ public void setup() { workerZone1 = WorkerZone.builder() .id(id1) - .worker(worker1) - .zone(zone1) + .worker(worker1) + .zone(zone1) .manageYn(true) // 관리자 여부 설정 - .build(); + .build(); } @Test @@ -91,14 +91,14 @@ public void testGetAllWorkers() { assertEquals("홍길동", result.get(0).getName()); assertEquals("01012345678", result.get(0).getPhoneNumber()); assertEquals("hong@example.com", result.get(0).getEmail()); - assertEquals(true, result.get(0).getIsManager()); // 서비스 로직에 따라 true로 설정 + assertEquals(false, result.get(0).getIsManager()); // 기본값은 false // 두 번째 작업자 정보 확인 assertEquals("20240102-5678", result.get(1).getWorkerId()); assertEquals("김철수", result.get(1).getName()); assertEquals("01087654321", result.get(1).getPhoneNumber()); assertEquals("kim@example.com", result.get(1).getEmail()); - assertEquals(true, result.get(1).getIsManager()); // 서비스 로직에 따라 true로 설정 + assertEquals(false, result.get(1).getIsManager()); // 기본값은 false } @Test From e006163d79d21c05c1caa02123597942490b1da2 Mon Sep 17 00:00:00 2001 From: clainyun Date: Wed, 21 May 2025 16:26:54 +0900 Subject: [PATCH 6/7] =?UTF-8?q?fix=20|=20sprint2=20|=20FRB-156=20|=20Zone?= =?UTF-8?q?=EB=B3=84=20=EC=9E=91=EC=97=85=EC=9E=90=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EB=A5=BC=20=ED=98=84=EC=9E=AC=20=EA=B7=B8=20=EA=B3=B5?= =?UTF-8?q?=EA=B0=84=EC=97=90=EC=84=9C=20=EC=8B=A4=EC=A0=9C=EB=A1=9C=20?= =?UTF-8?q?=EC=9E=91=EC=97=85=ED=95=98=EA=B3=A0=20=EC=9E=88=EB=8A=94=20?= =?UTF-8?q?=EC=9E=91=EC=97=85=EC=9E=90=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=EB=A1=9C=20=EC=88=98=EC=A0=95=20=20|=20?= =?UTF-8?q?=EC=9C=A4=EB=8B=A4=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/controller/WorkerController.java | 3 +- .../controller/WorkerLocationController.java | 36 ++++++++++ .../backend/dto/WorkerLocationRequest.java | 17 +++++ .../repository/ZoneHistRepository.java | 14 ++++ .../service/WorkerLocationService.java | 72 +++++++++++++++++++ .../backend/service/WorkerService.java | 61 +++++++++------- .../backend/service/WorkerServiceTest.java | 41 +++++------ 7 files changed, 194 insertions(+), 50 deletions(-) create mode 100644 src/main/java/com/factoreal/backend/controller/WorkerLocationController.java create mode 100644 src/main/java/com/factoreal/backend/dto/WorkerLocationRequest.java create mode 100644 src/main/java/com/factoreal/backend/repository/ZoneHistRepository.java create mode 100644 src/main/java/com/factoreal/backend/service/WorkerLocationService.java diff --git a/src/main/java/com/factoreal/backend/controller/WorkerController.java b/src/main/java/com/factoreal/backend/controller/WorkerController.java index b943abb7..05ce247e 100644 --- a/src/main/java/com/factoreal/backend/controller/WorkerController.java +++ b/src/main/java/com/factoreal/backend/controller/WorkerController.java @@ -18,7 +18,6 @@ @Slf4j @Tag(name = "작업자 API", description = "작업자 조회 API") public class WorkerController { - private final WorkerService workerService; @Operation(summary = "전체 작업자 목록 조회", description = "전체 작업자 목록을 조회합니다.") @@ -29,7 +28,7 @@ public ResponseEntity> getAllWorkers() { return ResponseEntity.ok(workers); } - @Operation(summary = "공간별 작업자 목록 조회", description = "공간 ID를 기반으로 공간별 작업자 목록을 조회합니다.") + @Operation(summary = "공간별 작업자 목록 조회", description = "공간 ID를 기반으로 현재 해당 공간에 들어가있는 작업자 리스트를 조회합니다.") @GetMapping("/zone/{zoneId}") public ResponseEntity> getWorkersByZoneId(@PathVariable String zoneId) { log.info("공간 ID: {}의 작업자 목록 조회 요청", zoneId); diff --git a/src/main/java/com/factoreal/backend/controller/WorkerLocationController.java b/src/main/java/com/factoreal/backend/controller/WorkerLocationController.java new file mode 100644 index 00000000..74bb5a8b --- /dev/null +++ b/src/main/java/com/factoreal/backend/controller/WorkerLocationController.java @@ -0,0 +1,36 @@ +package com.factoreal.backend.controller; + +import com.factoreal.backend.dto.WorkerLocationRequest; +import com.factoreal.backend.service.WorkerLocationService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.time.LocalDateTime; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@Slf4j +@Tag(name = "작업자 위치 API", description = "작업자의 실시간 위치 정보를 관리하는 API") +@RestController +@RequestMapping("/api/worker-locations") +@RequiredArgsConstructor +// 웨어러블 디바이스에서 받아오는 데이터를 업데이트하는 컨트롤러 +public class WorkerLocationController { + + private final WorkerLocationService workerLocationService; + + @Operation(summary = "작업자 위치 업데이트", description = "웨어러블 디바이스로부터 받은 작업자의 위치 정보를 업데이트합니다.") + @PostMapping("/update") + public ResponseEntity updateWorkerLocation(@RequestBody WorkerLocationRequest request) { + log.info("작업자 위치 업데이트 요청: {}", request); + workerLocationService.updateWorkerLocation( + request.getWorkerId(), + request.getZoneId(), + request.getTimestamp() != null ? request.getTimestamp() : LocalDateTime.now() + ); + return ResponseEntity.ok().build(); // 200 OK 응답 + } +} \ No newline at end of file diff --git a/src/main/java/com/factoreal/backend/dto/WorkerLocationRequest.java b/src/main/java/com/factoreal/backend/dto/WorkerLocationRequest.java new file mode 100644 index 00000000..7530a47e --- /dev/null +++ b/src/main/java/com/factoreal/backend/dto/WorkerLocationRequest.java @@ -0,0 +1,17 @@ +package com.factoreal.backend.dto; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.time.LocalDateTime; + +@Getter +@Setter +@ToString +// Wearable 장치에서 받아오는 데이터 by 우영. 추후 논의 예정 +public class WorkerLocationRequest { + private String workerId; + private String zoneId; + private LocalDateTime timestamp; // 장치에서 받아오는 데이터 +} \ No newline at end of file diff --git a/src/main/java/com/factoreal/backend/repository/ZoneHistRepository.java b/src/main/java/com/factoreal/backend/repository/ZoneHistRepository.java new file mode 100644 index 00000000..ba438d4b --- /dev/null +++ b/src/main/java/com/factoreal/backend/repository/ZoneHistRepository.java @@ -0,0 +1,14 @@ +package com.factoreal.backend.repository; + +import com.factoreal.backend.entity.ZoneHist; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface ZoneHistRepository extends JpaRepository { + // 특정 공간에 현재 있는 작업자들 조회 (existFlag = 1) + List findByZone_ZoneIdAndExistFlag(String zoneId, Integer existFlag); + + // 특정 작업자의 현재 위치 조회 (existFlag = 1) + ZoneHist findByWorker_WorkerIdAndExistFlag(String workerId, Integer existFlag); +} \ No newline at end of file diff --git a/src/main/java/com/factoreal/backend/service/WorkerLocationService.java b/src/main/java/com/factoreal/backend/service/WorkerLocationService.java new file mode 100644 index 00000000..351dcb2f --- /dev/null +++ b/src/main/java/com/factoreal/backend/service/WorkerLocationService.java @@ -0,0 +1,72 @@ +package com.factoreal.backend.service; + +import com.factoreal.backend.entity.Worker; +import com.factoreal.backend.entity.Zone; +import com.factoreal.backend.entity.ZoneHist; +import com.factoreal.backend.repository.WorkerRepository; +import com.factoreal.backend.repository.ZoneHistRepository; +import com.factoreal.backend.repository.ZoneRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor +public class WorkerLocationService { + + private final ZoneHistRepository zoneHistRepository; + private final WorkerRepository workerRepository; + private final ZoneRepository zoneRepository; + + /** + * 작업자의 위치 변경을 처리 + */ + @Transactional + public void updateWorkerLocation(String workerId, String zoneId, LocalDateTime timestamp) { + Worker worker = workerRepository.findById(workerId) + .orElseThrow(() -> new IllegalArgumentException("작업자를 찾을 수 없습니다: " + workerId)); + + Zone zone = zoneRepository.findById(zoneId) + .orElseThrow(() -> new IllegalArgumentException("공간을 찾을 수 없습니다: " + zoneId)); + + // 1. workerId 기반 작업자의 이전 위치가 있으면, 새로운 기록 생성 전 해당 작업자 위치 기록에 endTime 찍어주기 + ZoneHist currentLocation = zoneHistRepository.findByWorker_WorkerIdAndExistFlag(workerId, 1); + if (currentLocation != null) { + currentLocation.setEndTime(timestamp); // 다음 공간의 입장 시간으로 update + currentLocation.setExistFlag(0); + zoneHistRepository.save(currentLocation); + } + + // 2. 새로운 위치 기록 생성 + ZoneHist newLocation = ZoneHist.builder() + .worker(worker) + .zone(zone) + .startTime(timestamp) + .endTime(null) + .existFlag(1) + .build(); + + zoneHistRepository.save(newLocation); + + /** + * currentLocation이 있으면 (이전 위치가 있으면) -> 그 공간의 ID를 출력 + * currentLocation이 없으면 (최초 입장이면) -> "없음" 출력 + */ + log.info("작업자 {} 위치 변경: {} -> {}", workerId, + currentLocation != null ? currentLocation.getZone().getZoneId() : "없음", + zoneId); + } + + /** + * 특정 공간에 현재 들어가있는 작업자 리스트 조회 + */ + @Transactional(readOnly = true) + public List getCurrentWorkersByZoneId(String zoneId) { + return zoneHistRepository.findByZone_ZoneIdAndExistFlag(zoneId, 1); // 해당 공간의 existFlag가 1인 모든 작업자 리스트 + } +} \ No newline at end of file diff --git a/src/main/java/com/factoreal/backend/service/WorkerService.java b/src/main/java/com/factoreal/backend/service/WorkerService.java index 13c6d41d..ac571296 100644 --- a/src/main/java/com/factoreal/backend/service/WorkerService.java +++ b/src/main/java/com/factoreal/backend/service/WorkerService.java @@ -2,9 +2,8 @@ import com.factoreal.backend.dto.WorkerDto; import com.factoreal.backend.entity.Worker; -import com.factoreal.backend.entity.WorkerZone; +import com.factoreal.backend.entity.ZoneHist; import com.factoreal.backend.repository.WorkerRepository; -import com.factoreal.backend.repository.WorkerZoneRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -13,40 +12,50 @@ import java.util.List; import java.util.stream.Collectors; +@Slf4j @Service -@Transactional(readOnly = true) @RequiredArgsConstructor -@Slf4j public class WorkerService { - private final WorkerRepository workerRepository; - private final WorkerZoneRepository workerZoneRepository; + private final WorkerLocationService workerLocationService; + @Transactional(readOnly = true) public List getAllWorkers() { log.info("전체 작업자 목록 조회"); List workers = workerRepository.findAll(); - return workers.stream() - .map(worker -> { - // 기본값으로는 관리자가 아님 - return WorkerDto.fromEntity(worker, false); - }) + .map(worker -> WorkerDto.fromEntity(worker, false)) .collect(Collectors.toList()); } - - // TODO. 수정되어야 할 로직. 현재는 WorkerZone 테이블에서 공간id로 필터링 되는 모든 작업자를 끌고왔는데, - // 사실 현재 그 공간에서 실제로 작업하고 있는, 즉 들어 - // public List getWorkersByZoneId(String zoneId) { - // log.info("공간 ID: {}의 작업자 목록 조회", zoneId); - // List workerZones = workerZoneRepository.findByZoneZoneId(zoneId); + /** + * 특정 공간에 현재 들어가있는 작업자 목록 조회 + */ + @Transactional(readOnly = true) + public List getWorkersByZoneId(String zoneId) { + log.info("공간 ID: {}의 현재 작업자 목록 조회", zoneId); - // return workerZones.stream() - // .map(workerZone -> { - // Worker worker = workerZone.getWorker(); - // Boolean isManager = workerZone.getManageYn(); - // return WorkerDto.fromEntity(worker, isManager); - // }) - // .collect(Collectors.toList()); - // } -} \ No newline at end of file + // 현재 해당 공간에 있는 작업자 이력 조회 (existFlag = 1) + List currentWorkers = workerLocationService.getCurrentWorkersByZoneId(zoneId); + + // ZoneHist에서 Worker 정보만 추출하여 DTO로 변환 + return currentWorkers.stream() + .map(zoneHist -> WorkerDto.fromEntity(zoneHist.getWorker(), false)) + .collect(Collectors.toList()); + } +} + +// TODO. 수정되어야 할 로직. 현재는 WorkerZone 테이블에서 공간id로 필터링 되는 모든 작업자를 끌고왔는데, +// 사실 현재 그 공간에서 실제로 작업하고 있는, 즉 들어 +// public List getWorkersByZoneId(String zoneId) { +// log.info("공간 ID: {}의 작업자 목록 조회", zoneId); +// List workerZones = workerZoneRepository.findByZoneZoneId(zoneId); + +// return workerZones.stream() +// .map(workerZone -> { +// Worker worker = workerZone.getWorker(); +// Boolean isManager = workerZone.getManageYn(); +// return WorkerDto.fromEntity(worker, isManager); +// }) +// .collect(Collectors.toList()); +// } \ No newline at end of file diff --git a/src/test/java/com/factoreal/backend/service/WorkerServiceTest.java b/src/test/java/com/factoreal/backend/service/WorkerServiceTest.java index 5b40f2ec..5e527e98 100644 --- a/src/test/java/com/factoreal/backend/service/WorkerServiceTest.java +++ b/src/test/java/com/factoreal/backend/service/WorkerServiceTest.java @@ -2,17 +2,16 @@ import com.factoreal.backend.dto.WorkerDto; import com.factoreal.backend.entity.Worker; -import com.factoreal.backend.entity.WorkerZone; -import com.factoreal.backend.entity.WorkerZoneId; import com.factoreal.backend.entity.Zone; +import com.factoreal.backend.entity.ZoneHist; import com.factoreal.backend.repository.WorkerRepository; -import com.factoreal.backend.repository.WorkerZoneRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.time.LocalDateTime; import java.util.Arrays; import java.util.List; @@ -26,7 +25,7 @@ public class WorkerServiceTest { private WorkerRepository workerRepository; @Mock - private WorkerZoneRepository workerZoneRepository; + private WorkerLocationService workerLocationService; @InjectMocks private WorkerService workerService; @@ -34,7 +33,7 @@ public class WorkerServiceTest { private Worker worker1; private Worker worker2; private Zone zone1; - private WorkerZone workerZone1; + private ZoneHist zoneHist1; @BeforeEach public void setup() { @@ -43,10 +42,10 @@ public void setup() { // 테스트용 작업자 데이터 생성 worker1 = Worker.builder() .workerId("20240101-1234") - .name("홍길동") - .phoneNumber("01012345678") - .email("hong@example.com") - .build(); + .name("홍길동") + .phoneNumber("01012345678") + .email("hong@example.com") + .build(); worker2 = Worker.builder() .workerId("20240102-5678") @@ -61,17 +60,15 @@ public void setup() { .zoneName("테스트 공간") .build(); - // 테스트용 WorkerZone 데이터 생성 - WorkerZoneId id1 = new WorkerZoneId(); - id1.setWorkerId("20240101-1234"); - id1.setZoneId("zone1"); - - workerZone1 = WorkerZone.builder() - .id(id1) - .worker(worker1) - .zone(zone1) - .manageYn(true) // 관리자 여부 설정 - .build(); + // 테스트용 ZoneHist 데이터 생성 + zoneHist1 = ZoneHist.builder() + .id(1L) + .worker(worker1) + .zone(zone1) + .startTime(LocalDateTime.now()) + .endTime(null) + .existFlag(1) + .build(); } @Test @@ -104,7 +101,7 @@ public void testGetAllWorkers() { @Test void testGetWorkersByZoneId() { // Mock 설정 - when(workerZoneRepository.findByZoneZoneId("zone1")).thenReturn(Arrays.asList(workerZone1)); + when(workerLocationService.getCurrentWorkersByZoneId("zone1")).thenReturn(Arrays.asList(zoneHist1)); // 테스트 실행 List workers = workerService.getWorkersByZoneId("zone1"); @@ -115,6 +112,6 @@ void testGetWorkersByZoneId() { assertEquals("홍길동", workers.get(0).getName()); assertEquals("01012345678", workers.get(0).getPhoneNumber()); assertEquals("hong@example.com", workers.get(0).getEmail()); - assertEquals(true, workers.get(0).getIsManager()); // 관리자 여부 검증 + assertEquals(false, workers.get(0).getIsManager()); // 관리자 여부는 더 이상 ZoneHist에서 확인할 수 없음 } } From e30b4379aa4de8fac37bbdbc9a3bf55caac97aa5 Mon Sep 17 00:00:00 2001 From: clainyun Date: Wed, 21 May 2025 17:07:28 +0900 Subject: [PATCH 7/7] =?UTF-8?q?fix=20|=20sprint2=20|=20FRB-156=20|=20?= =?UTF-8?q?=EA=B3=B5=EA=B0=84=EB=B3=84=20=EB=8B=B4=EB=8B=B9=EC=9E=90=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84=20(=EB=8B=B4?= =?UTF-8?q?=EB=8B=B9=EC=9E=90=20=EC=97=B0=EB=9D=BD=EC=B2=98=20=EB=B0=8F=20?= =?UTF-8?q?=ED=98=84=EC=9E=AC=20=EC=9C=84=EC=B9=98=20=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=ED=8F=AC=ED=95=A8)=20=20|=20=EC=9C=A4=EB=8B=A4=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/controller/WorkerController.java | 10 ++++++ .../backend/dto/ZoneManagerResponseDto.java | 33 +++++++++++++++++++ .../repository/WorkerZoneRepository.java | 4 +++ .../service/WorkerLocationService.java | 8 +++++ .../backend/service/WorkerService.java | 28 ++++++++++++++++ 5 files changed, 83 insertions(+) create mode 100644 src/main/java/com/factoreal/backend/dto/ZoneManagerResponseDto.java diff --git a/src/main/java/com/factoreal/backend/controller/WorkerController.java b/src/main/java/com/factoreal/backend/controller/WorkerController.java index 05ce247e..e4efd46f 100644 --- a/src/main/java/com/factoreal/backend/controller/WorkerController.java +++ b/src/main/java/com/factoreal/backend/controller/WorkerController.java @@ -1,6 +1,7 @@ package com.factoreal.backend.controller; import com.factoreal.backend.dto.WorkerDto; +import com.factoreal.backend.dto.ZoneManagerResponseDto; import com.factoreal.backend.service.WorkerService; import io.swagger.v3.oas.annotations.Operation; @@ -35,4 +36,13 @@ public ResponseEntity> getWorkersByZoneId(@PathVariable String z List zoneWorkers = workerService.getWorkersByZoneId(zoneId); return ResponseEntity.ok(zoneWorkers); } + + @Operation(summary = "공간 담당자 정보 조회", + description = "공간 ID를 기반으로 해당 공간의 담당자와 현재 위치 정보를 조회합니다.") + @GetMapping("/zone/{zoneId}/manager") + public ResponseEntity getZoneManager(@PathVariable String zoneId) { + log.info("공간 ID: {}의 담당자 정보 조회 요청", zoneId); + ZoneManagerResponseDto manager = workerService.getZoneManagerWithLocation(zoneId); + return ResponseEntity.ok(manager); + } } \ No newline at end of file diff --git a/src/main/java/com/factoreal/backend/dto/ZoneManagerResponseDto.java b/src/main/java/com/factoreal/backend/dto/ZoneManagerResponseDto.java new file mode 100644 index 00000000..476c5883 --- /dev/null +++ b/src/main/java/com/factoreal/backend/dto/ZoneManagerResponseDto.java @@ -0,0 +1,33 @@ +package com.factoreal.backend.dto; + +import com.factoreal.backend.entity.Worker; +import com.factoreal.backend.entity.Zone; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +// 공간 담당자 정보 조회 시 사용되는 DTO (BE -> FE) +public class ZoneManagerResponseDto { + private String workerId; // 작업자 ID + private String name; // 작업자 이름 + private String phoneNumber; // 연락처 + private String email; // 이메일 + private String currentZoneId; // 현재 위치한 공간 ID + private String currentZoneName; // 현재 위치한 공간 이름 + + public static ZoneManagerResponseDto fromEntity(Worker worker, Zone currentZone) { + return ZoneManagerResponseDto.builder() + .workerId(worker.getWorkerId()) + .name(worker.getName()) + .phoneNumber(worker.getPhoneNumber()) + .email(worker.getEmail()) + .currentZoneId(currentZone != null ? currentZone.getZoneId() : null) + .currentZoneName(currentZone != null ? currentZone.getZoneName() : null) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/factoreal/backend/repository/WorkerZoneRepository.java b/src/main/java/com/factoreal/backend/repository/WorkerZoneRepository.java index 5733d7e8..ab3dd503 100644 --- a/src/main/java/com/factoreal/backend/repository/WorkerZoneRepository.java +++ b/src/main/java/com/factoreal/backend/repository/WorkerZoneRepository.java @@ -5,9 +5,13 @@ import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; +import java.util.Optional; public interface WorkerZoneRepository extends JpaRepository { // 특정 zone_id에 속한 작업자 목록 조회 List findByZoneZoneId(String zoneId); + + // 특정 zone_id의 담당자 조회 (manageYn = true) + Optional findByZoneZoneIdAndManageYnIsTrue(String zoneId); } \ No newline at end of file diff --git a/src/main/java/com/factoreal/backend/service/WorkerLocationService.java b/src/main/java/com/factoreal/backend/service/WorkerLocationService.java index 351dcb2f..b647e731 100644 --- a/src/main/java/com/factoreal/backend/service/WorkerLocationService.java +++ b/src/main/java/com/factoreal/backend/service/WorkerLocationService.java @@ -69,4 +69,12 @@ public void updateWorkerLocation(String workerId, String zoneId, LocalDateTime t public List getCurrentWorkersByZoneId(String zoneId) { return zoneHistRepository.findByZone_ZoneIdAndExistFlag(zoneId, 1); // 해당 공간의 existFlag가 1인 모든 작업자 리스트 } + + /** + * 특정 작업자의 현재 위치 조회 + */ + @Transactional(readOnly = true) + public ZoneHist getCurrentWorkerLocation(String workerId) { + return zoneHistRepository.findByWorker_WorkerIdAndExistFlag(workerId, 1); + } } \ No newline at end of file diff --git a/src/main/java/com/factoreal/backend/service/WorkerService.java b/src/main/java/com/factoreal/backend/service/WorkerService.java index ac571296..27b49682 100644 --- a/src/main/java/com/factoreal/backend/service/WorkerService.java +++ b/src/main/java/com/factoreal/backend/service/WorkerService.java @@ -1,9 +1,13 @@ package com.factoreal.backend.service; import com.factoreal.backend.dto.WorkerDto; +import com.factoreal.backend.dto.ZoneManagerResponseDto; import com.factoreal.backend.entity.Worker; +import com.factoreal.backend.entity.WorkerZone; +import com.factoreal.backend.entity.Zone; import com.factoreal.backend.entity.ZoneHist; import com.factoreal.backend.repository.WorkerRepository; +import com.factoreal.backend.repository.WorkerZoneRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -18,6 +22,7 @@ public class WorkerService { private final WorkerRepository workerRepository; private final WorkerLocationService workerLocationService; + private final WorkerZoneRepository workerZoneRepository; @Transactional(readOnly = true) public List getAllWorkers() { @@ -43,6 +48,29 @@ public List getWorkersByZoneId(String zoneId) { .map(zoneHist -> WorkerDto.fromEntity(zoneHist.getWorker(), false)) .collect(Collectors.toList()); } + + /** + * 특정 공간의 담당자와 현재 위치 정보 조회 + */ + @Transactional(readOnly = true) + public ZoneManagerResponseDto getZoneManagerWithLocation(String zoneId) { + log.info("공간 ID: {}의 담당자 정보 조회", zoneId); + + // 1. 해당 공간의 담당자 조회 (manageYn = true) + WorkerZone zoneManager = workerZoneRepository.findByZoneZoneIdAndManageYnIsTrue(zoneId) + .orElseThrow(() -> new IllegalArgumentException("해당 공간의 담당자를 찾을 수 없습니다: " + zoneId)); + + Worker manager = zoneManager.getWorker(); + + // 2. 담당자의 현재 위치 조회 (existFlag = 1) + ZoneHist currentLocation = workerLocationService.getCurrentWorkerLocation(manager.getWorkerId()); + + // 3. 현재 위치한 공간 정보 (없을 수 있음) + Zone currentZone = currentLocation != null ? currentLocation.getZone() : null; + + // 4. DTO 변환 및 반환 + return ZoneManagerResponseDto.fromEntity(manager, currentZone); + } } // TODO. 수정되어야 할 로직. 현재는 WorkerZone 테이블에서 공간id로 필터링 되는 모든 작업자를 끌고왔는데,