diff --git a/backend/src/main/java/com/example/backend/analysis/dto/CaseStatsOverviewResponse.java b/backend/src/main/java/com/example/backend/analysis/dto/CaseStatsOverviewResponse.java index 48c56d9d..c8e38534 100644 --- a/backend/src/main/java/com/example/backend/analysis/dto/CaseStatsOverviewResponse.java +++ b/backend/src/main/java/com/example/backend/analysis/dto/CaseStatsOverviewResponse.java @@ -8,4 +8,5 @@ public class CaseStatsOverviewResponse { private int recentCase; private int todayCase; private String mostCase; + private String patrolRegion; } diff --git a/backend/src/main/java/com/example/backend/analysis/repository/CaseStatsOverviewRepository.java b/backend/src/main/java/com/example/backend/analysis/repository/CaseStatsOverviewRepository.java index 30c0935e..fabe5888 100644 --- a/backend/src/main/java/com/example/backend/analysis/repository/CaseStatsOverviewRepository.java +++ b/backend/src/main/java/com/example/backend/analysis/repository/CaseStatsOverviewRepository.java @@ -2,6 +2,8 @@ import com.example.backend.common.domain.CaseStatsOverviewEntity; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import java.util.Optional; @@ -9,4 +11,16 @@ @Repository public interface CaseStatsOverviewRepository extends JpaRepository { Optional findByOfficeId(int officeId); + + @Query(value = """ + SELECT ci.address + FROM case_stats_category csc + JOIN cctv_info ci ON csc.cctv_id = ci.id + WHERE csc.office_id = :officeId + AND csc.date >= NOW() - INTERVAL '1 month' + GROUP BY ci.address + ORDER BY SUM(csc.fire_count + csc.assault_count + csc.crowd_congestion_count + csc.weapon_count + csc.swoon_count) DESC + LIMIT 1 + """, nativeQuery = true) + Optional findAddressWithMostIncidentsLastMonth(@Param("officeId") int officeId); } diff --git a/backend/src/main/java/com/example/backend/analysis/service/CaseStatsService.java b/backend/src/main/java/com/example/backend/analysis/service/CaseStatsService.java index 961aaa43..938050a5 100644 --- a/backend/src/main/java/com/example/backend/analysis/service/CaseStatsService.java +++ b/backend/src/main/java/com/example/backend/analysis/service/CaseStatsService.java @@ -58,10 +58,16 @@ public CaseStatsOverviewResponse getOverview(HttpSession session) { CaseStatsOverviewEntity stats = statsOverviewRepository.findByOfficeId(officeId) .orElseThrow(() -> new NoSuchElementException("해당 office_id에 대한 통계 데이터가 없습니다.")); + // 최근 한 달간 데이터를 기준으로 사건 건수가 가장 많은 CCTV 주소 조회 + String patrolRegionAddress = statsOverviewRepository.findAddressWithMostIncidentsLastMonth(officeId) + .orElse("정보 없음"); + + // 응답 생성 (순찰 강화 지역은 주소만 포함) return new CaseStatsOverviewResponse( stats.getRecentCaseCount(), // 지난 7일간 사건 수 stats.getTodayCaseCount(), // 오늘 사건 수 - stats.getMostCase().name() // 이번 달 가장 많이 발생 한 사건 유형 + stats.getMostCase().name(), // 이번 달 가장 많이 발생한 사건 유형 + patrolRegionAddress // 순찰 강화 지역 (주소) ); } diff --git a/backend/src/main/java/com/example/backend/dashboard/controller/AlarmListController.java b/backend/src/main/java/com/example/backend/dashboard/controller/AlarmListController.java deleted file mode 100644 index 9bc495b6..00000000 --- a/backend/src/main/java/com/example/backend/dashboard/controller/AlarmListController.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.example.backend.dashboard.controller; - -import com.example.backend.dashboard.dto.AlarmRequest; -import com.example.backend.dashboard.dto.AlarmResponse; -import com.example.backend.dashboard.service.AlarmListService; -import jakarta.servlet.http.HttpSession; -import lombok.RequiredArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.util.Collections; -import java.util.List; - -@RestController -@RequestMapping("/api/v1/case") -@RequiredArgsConstructor -public class AlarmListController { - private final AlarmListService alarmListService; - - @GetMapping("/ready") - public ResponseEntity getReadyAlarmList(HttpSession session) { - List alarms = alarmListService.getReadyCases(session); - return ResponseEntity.ok(alarms); - } - - // case_info id 값으로 상세 조회 (officeId가 동일한 데이터만) - @GetMapping("/ready/{id}") - public ResponseEntity getCaseById(@PathVariable("id") int id, HttpSession session) { - AlarmResponse alarm = alarmListService.getCaseById(id, session); - return ResponseEntity.ok(alarm); - } - - // 출동 | 미출동 클릭 시 => 1. 이미 출동인 상태 or 2. state를 업데이트 - @PutMapping("/ready/{id}") - public ResponseEntity updateCaseState(@PathVariable("id") int id, @RequestBody AlarmRequest request, HttpSession session) { - String message = alarmListService.updateCaseState(id, request.getState(), session); - return ResponseEntity.ok(Collections.singletonMap("message", message)); - } - -} diff --git a/backend/src/main/java/com/example/backend/dashboard/controller/DashboardController.java b/backend/src/main/java/com/example/backend/dashboard/controller/DashboardController.java new file mode 100644 index 00000000..b85a519e --- /dev/null +++ b/backend/src/main/java/com/example/backend/dashboard/controller/DashboardController.java @@ -0,0 +1,57 @@ +package com.example.backend.dashboard.controller; + +import com.example.backend.dashboard.dto.*; +import com.example.backend.dashboard.service.DashboardService; +import jakarta.servlet.http.HttpSession; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/api/v1/case") +@RequiredArgsConstructor +public class DashboardController { + + private final DashboardService dashboardService; + + // 사건 정보 조회 + @GetMapping("") + public ResponseEntity getCases(HttpSession session) { + List cases = dashboardService.getCases(session); + if (cases.isEmpty()) { + return ResponseEntity.status(404) + .body(Collections.singletonMap("message", "사건이 없습니다.")); + } + return ResponseEntity.ok(cases); + } + + // 사건 영상 확인 + @GetMapping("/{id}") + public ResponseEntity getCaseVideo(@PathVariable("id") int id, HttpSession session) { + Map videoResponse = dashboardService.getCaseVideo(id, session); + return ResponseEntity.ok(videoResponse); + } + + // 출동 | 미출동 클릭 시 => 1. 이미 출동인 상태 or 2. state를 업데이트 + @PutMapping("/ready/{id}") + public ResponseEntity updateCaseState(@PathVariable("id") int id, + @RequestBody StateRequest request, + HttpSession session) { + Map message = dashboardService.updateCaseState(id, request, session); + return ResponseEntity.ok(message); + } + + // 출동 중인 사건 해결 처리 + @PutMapping("/complete/{id}") + public ResponseEntity completeCase(@PathVariable("id") int id, + @RequestBody SurveyRequest surveyRequest, + HttpSession session) { + Map completedCase = dashboardService.completeCase(id, surveyRequest, session); + return ResponseEntity.ok(completedCase); + } + +} diff --git a/backend/src/main/java/com/example/backend/dashboard/controller/ProgressController.java b/backend/src/main/java/com/example/backend/dashboard/controller/ProgressController.java deleted file mode 100644 index 017684a0..00000000 --- a/backend/src/main/java/com/example/backend/dashboard/controller/ProgressController.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.example.backend.dashboard.controller; - -import com.example.backend.dashboard.dto.ProgressResponse; -import com.example.backend.dashboard.dto.SurveyRequest; -import com.example.backend.dashboard.dto.SurveyResponse; -import com.example.backend.dashboard.service.ProgressService; -import jakarta.servlet.http.HttpSession; -import lombok.RequiredArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.util.Collections; -import java.util.List; -import java.util.Map; - -@RestController -@RequestMapping("/api/v1") -@RequiredArgsConstructor -public class ProgressController { - - private final ProgressService progressService; - - // 출동 중인 사건 조회 - @GetMapping("/case/move") - public ResponseEntity getActiveCases(HttpSession session) { - List cases = progressService.getActiveCases(session); - if (cases.isEmpty()) { - return ResponseEntity.status(404) - .body(Collections.singletonMap("message", "출동 중인 사건이 없습니다.")); - } - return ResponseEntity.ok(cases); - } - - // 출동 중인 사건 영상 확인 - @GetMapping("/case/move/{id}") - public ResponseEntity getCaseVideo(@PathVariable("id") int id, HttpSession session) { - Map videoResponse = progressService.getCaseVideo(id, session); - return ResponseEntity.ok(videoResponse); - } - - // 출동 중인 사건 해결 처리 - @PutMapping("/case/complete/{id}") - public ResponseEntity completeCase(@PathVariable("id") int id, HttpSession session) { - Map completedCase = progressService.completeCase(id, session); - return ResponseEntity.ok(completedCase); - } - - // AI 설문조사 결과 저장 - @PutMapping("/survey/{id}") - public ResponseEntity saveSurveyResult(@PathVariable("id") int id, - @RequestBody SurveyRequest surveyRequest, - HttpSession session) { - SurveyResponse surveyResult = progressService.saveSurveyResult(id, surveyRequest, session); - return ResponseEntity.ok(surveyResult); - } -} diff --git a/backend/src/main/java/com/example/backend/dashboard/dto/AlarmResponse.java b/backend/src/main/java/com/example/backend/dashboard/dto/AlarmResponse.java deleted file mode 100644 index 26221fe8..00000000 --- a/backend/src/main/java/com/example/backend/dashboard/dto/AlarmResponse.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.example.backend.dashboard.dto; - -import com.example.backend.common.domain.CaseEntity; -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonInclude; -import lombok.Builder; -import lombok.Data; - -import java.time.LocalDateTime; - -@Data -@Builder -@JsonInclude(JsonInclude.Include.NON_NULL) -public class AlarmResponse { - private Integer id; - private Integer officeId; - private Integer cctvId; - private String address; - - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") - private LocalDateTime date; - - private Integer level; - private String category; - private String video; - private String state; - - public static AlarmResponse fromEntity(CaseEntity entity) { - return AlarmResponse.builder() - .id(entity.getId()) - .officeId(entity.getOffice() != null ? entity.getOffice().getId() : null) - .cctvId(entity.getCctv() != null ? entity.getCctv().getId() : null) - .address(entity.getCctv() != null ? entity.getCctv().getAddress() : null) - .date(entity.getDate()) - .level(entity.getLevel()) - .category(entity.getCategory().name()) - .state(entity.getState().name()) - .build(); - } - - public static AlarmResponse fromEntityWithVideo(CaseEntity entity) { - return AlarmResponse.builder() - .id(entity.getId()) - .officeId(entity.getOffice() != null ? entity.getOffice().getId() : null) - .cctvId(entity.getCctv() != null ? entity.getCctv().getId() : null) - .address(entity.getCctv() != null ? entity.getCctv().getAddress() : null) - .date(entity.getDate()) - .level(entity.getLevel()) - .category(entity.getCategory().name()) - .video(entity.getVideo()) - .state(entity.getState().name()) - .build(); - } - -} diff --git a/backend/src/main/java/com/example/backend/dashboard/dto/ProgressResponse.java b/backend/src/main/java/com/example/backend/dashboard/dto/DashboardResponse.java similarity index 70% rename from backend/src/main/java/com/example/backend/dashboard/dto/ProgressResponse.java rename to backend/src/main/java/com/example/backend/dashboard/dto/DashboardResponse.java index 25993a71..a09094cc 100644 --- a/backend/src/main/java/com/example/backend/dashboard/dto/ProgressResponse.java +++ b/backend/src/main/java/com/example/backend/dashboard/dto/DashboardResponse.java @@ -10,7 +10,7 @@ @Data @Builder -public class ProgressResponse { +public class DashboardResponse { private Integer id; private String police_name; private String police_rank; @@ -21,22 +21,18 @@ public class ProgressResponse { private Integer level; private CaseCategory category; - private String video; private CaseState state; - private String memo; - public static ProgressResponse fromEntity(CaseEntity entity) { - return ProgressResponse.builder() + public static DashboardResponse fromEntity(CaseEntity entity) { + return DashboardResponse.builder() .id(entity.getId()) - .police_name(entity.getPolice().getName()) - .police_rank(String.valueOf(entity.getPolice().getRank())) + .police_name(entity.getPolice() != null ? entity.getPolice().getName() : null) + .police_rank(entity.getPolice() != null ? String.valueOf(entity.getPolice().getRank()) : null) .address(entity.getCctv().getAddress()) .date(entity.getDate()) .level(entity.getLevel()) .category(entity.getCategory()) - .video(entity.getVideo()) .state(entity.getState()) - .memo(entity.getMemo()) .build(); } diff --git a/backend/src/main/java/com/example/backend/dashboard/dto/AlarmRequest.java b/backend/src/main/java/com/example/backend/dashboard/dto/StateRequest.java similarity index 64% rename from backend/src/main/java/com/example/backend/dashboard/dto/AlarmRequest.java rename to backend/src/main/java/com/example/backend/dashboard/dto/StateRequest.java index 62b8751d..494386d3 100644 --- a/backend/src/main/java/com/example/backend/dashboard/dto/AlarmRequest.java +++ b/backend/src/main/java/com/example/backend/dashboard/dto/StateRequest.java @@ -1,11 +1,13 @@ package com.example.backend.dashboard.dto; +import com.example.backend.common.domain.CaseEntity; import com.example.backend.common.domain.CaseEntity.CaseState; import lombok.Getter; import lombok.Setter; @Getter @Setter -public class AlarmRequest { +public class StateRequest { private CaseState state; // "출동" or "미출동" + private CaseEntity.CaseCategory category; } diff --git a/backend/src/main/java/com/example/backend/dashboard/dto/SurveyResponse.java b/backend/src/main/java/com/example/backend/dashboard/dto/SurveyResponse.java deleted file mode 100644 index 27307ddc..00000000 --- a/backend/src/main/java/com/example/backend/dashboard/dto/SurveyResponse.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.example.backend.dashboard.dto; -import com.example.backend.common.domain.CaseEntity.CaseCategory; - -import lombok.*; - -@Getter -@AllArgsConstructor -public class SurveyResponse { - private int id; - private CaseCategory category; - private String resultMessage; - -} diff --git a/backend/src/main/java/com/example/backend/dashboard/repository/AlarmListRepository.java b/backend/src/main/java/com/example/backend/dashboard/repository/AlarmListRepository.java deleted file mode 100644 index 4cbe564e..00000000 --- a/backend/src/main/java/com/example/backend/dashboard/repository/AlarmListRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.example.backend.dashboard.repository; - -import com.example.backend.common.domain.CaseEntity; -import com.example.backend.common.domain.CaseEntity.CaseState; -import org.springframework.data.jpa.repository.JpaRepository; - -import java.util.List; - -public interface AlarmListRepository extends JpaRepository { - List findByOfficeIdAndStateIn(Integer officeId, List states); -} diff --git a/backend/src/main/java/com/example/backend/dashboard/repository/DashboardRepository.java b/backend/src/main/java/com/example/backend/dashboard/repository/DashboardRepository.java new file mode 100644 index 00000000..5c9bce44 --- /dev/null +++ b/backend/src/main/java/com/example/backend/dashboard/repository/DashboardRepository.java @@ -0,0 +1,11 @@ +package com.example.backend.dashboard.repository; + +import com.example.backend.common.domain.CaseEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface DashboardRepository extends JpaRepository { + List findAllByOfficeIdAndStateInOrderById(int officeId, List states); + +} diff --git a/backend/src/main/java/com/example/backend/dashboard/repository/ProgressRepository.java b/backend/src/main/java/com/example/backend/dashboard/repository/ProgressRepository.java deleted file mode 100644 index 6565de50..00000000 --- a/backend/src/main/java/com/example/backend/dashboard/repository/ProgressRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.example.backend.dashboard.repository; - -import com.example.backend.common.domain.CaseEntity; -import com.example.backend.common.domain.CaseEntity.CaseState; -import org.springframework.data.jpa.repository.JpaRepository; - -import java.util.List; - -public interface ProgressRepository extends JpaRepository { - List findAllByOfficeIdAndStateOrderById(int officeId, CaseState state); -} diff --git a/backend/src/main/java/com/example/backend/dashboard/service/AlarmListService.java b/backend/src/main/java/com/example/backend/dashboard/service/AlarmListService.java deleted file mode 100644 index 1fabc3af..00000000 --- a/backend/src/main/java/com/example/backend/dashboard/service/AlarmListService.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.example.backend.dashboard.service; - -import com.example.backend.common.domain.CaseEntity; -import com.example.backend.common.domain.PoliceEntity; -import com.example.backend.dashboard.dto.AlarmResponse; -import com.example.backend.dashboard.repository.AlarmListRepository; -import com.example.backend.user.dto.UserResponseDto; -import jakarta.persistence.EntityNotFoundException; -import jakarta.servlet.http.HttpSession; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -import java.util.Arrays; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.stream.Collectors; - -import static com.example.backend.common.domain.CaseEntity.CaseState; - -@Service -@RequiredArgsConstructor -public class AlarmListService { - private final AlarmListRepository alarmListRepository; - - // 세션에서 officeId 추출 - private int getAuthenticatedOfficeId(HttpSession session) { - UserResponseDto user = (UserResponseDto) session.getAttribute("user"); - if (user == null) { - throw new IllegalStateException("세션이 만료되었거나 로그인되지 않았습니다."); - } - return user.getOfficeId(); - } - - private int getAuthenticatedPoliceId(HttpSession session) { - UserResponseDto user = (UserResponseDto) session.getAttribute("user"); - if (user == null) { - throw new IllegalStateException("세션이 만료되었거나 로그인되지 않았습니다."); - } - return user.getId(); - } - - // 사건 조회 및 권한 검증 (중복 제거) - private CaseEntity getAuthorizedCase(int caseId, HttpSession session) { - int officeId = getAuthenticatedOfficeId(session); - - CaseEntity caseEntity = alarmListRepository.findById(caseId) - .orElseThrow(() -> new EntityNotFoundException("해당 사건을 찾을 수 없습니다.")); - if (caseEntity.getOffice().getId() != officeId) { - throw new NoSuchElementException("해당 사건에 대한 권한이 없습니다."); - } - return caseEntity; - } - - public List getReadyCases(HttpSession session) { - int officeId = getAuthenticatedOfficeId(session); - List cases = alarmListRepository.findByOfficeIdAndStateIn( - officeId, - Arrays.asList(CaseState.확인, CaseState.미확인) - ); - - return cases.stream() - .map(AlarmResponse::fromEntity) - .collect(Collectors.toList()); - } - - public AlarmResponse getCaseById(Integer id, HttpSession session) { - CaseEntity caseEntity = getAuthorizedCase(id, session); - - if (caseEntity.getState() == CaseState.출동 || caseEntity.getState() == CaseState.미출동 || caseEntity.getState() == CaseState.완료) { - throw new IllegalStateException("해당 사건은 이미 출동 처리되었거나 완료된 사건입니다."); - } - - if(caseEntity.getState() == CaseState.미확인){ - caseEntity.setState(CaseState.확인); - alarmListRepository.save(caseEntity); - } - - return AlarmResponse.fromEntityWithVideo(caseEntity); - } - - public String updateCaseState(Integer id, CaseState state, HttpSession session) { - CaseEntity caseEntity = getAuthorizedCase(id, session); - int policeId = getAuthenticatedPoliceId(session); - - if (caseEntity.getState() == CaseState.출동 || caseEntity.getState() == CaseState.미출동 || caseEntity.getState() == CaseState.완료) { - throw new IllegalStateException("해당 사건은 이미 출동 처리되었거나 완료된 사건입니다."); - } - - if (state == CaseState.출동) { - // state를 "출동"으로 변경 후 저장 - caseEntity.setState(CaseState.출동); - - // 경찰관 배정: 현재 로그인한 경찰관의 id를 PoliceEntity에 할당 - PoliceEntity assignedPolice = PoliceEntity.builder().id(policeId).build(); - caseEntity.setPolice(assignedPolice); - - alarmListRepository.save(caseEntity); - return "지금 출동합니다."; - } else { - // state를 "미출동"으로 변경 후 저장 - caseEntity.setState(CaseState.미출동); - - alarmListRepository.save(caseEntity); - return "미출동 사건으로 변경합니다."; - } - } - -} diff --git a/backend/src/main/java/com/example/backend/dashboard/service/CaseDetectService.java b/backend/src/main/java/com/example/backend/dashboard/service/CaseDetectService.java index 10f0c70f..d50e7254 100644 --- a/backend/src/main/java/com/example/backend/dashboard/service/CaseDetectService.java +++ b/backend/src/main/java/com/example/backend/dashboard/service/CaseDetectService.java @@ -5,7 +5,7 @@ import com.example.backend.common.domain.OfficeEntity; import com.example.backend.dashboard.dto.CaseDetectRequest; import com.example.backend.dashboard.dto.CaseDetectResponse; -import com.example.backend.dashboard.repository.ProgressRepository; +import com.example.backend.dashboard.repository.DashboardRepository; import com.example.backend.search.repository.CctvRepository; import jakarta.persistence.EntityNotFoundException; import lombok.RequiredArgsConstructor; @@ -19,7 +19,7 @@ @RequiredArgsConstructor public class CaseDetectService { - private final ProgressRepository caseRepository; + private final DashboardRepository caseRepository; private final CctvRepository cctvRepository; // CCTV 정보(주소) 조회용 public CaseDetectResponse saveCase(CaseDetectRequest request) { diff --git a/backend/src/main/java/com/example/backend/dashboard/service/DashboardService.java b/backend/src/main/java/com/example/backend/dashboard/service/DashboardService.java new file mode 100644 index 00000000..dc33a3ba --- /dev/null +++ b/backend/src/main/java/com/example/backend/dashboard/service/DashboardService.java @@ -0,0 +1,142 @@ +package com.example.backend.dashboard.service; + +import com.example.backend.common.domain.CaseEntity; +import com.example.backend.common.domain.PoliceEntity; +import com.example.backend.dashboard.dto.DashboardResponse; +import com.example.backend.dashboard.dto.StateRequest; +import com.example.backend.dashboard.dto.SurveyRequest; +import com.example.backend.dashboard.repository.DashboardRepository; +import com.example.backend.user.dto.UserResponseDto; +import jakarta.persistence.EntityNotFoundException; +import jakarta.servlet.http.HttpSession; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.stream.Collectors; + +@Service +@Transactional +@RequiredArgsConstructor +public class DashboardService { + + private final DashboardRepository dashboardRepository; + + // 세션에서 officeId 추출 + private int getAuthenticatedOfficeId(HttpSession session) { + UserResponseDto user = (UserResponseDto) session.getAttribute("user"); + if (user == null) { + throw new IllegalStateException("세션이 만료되었거나 로그인되지 않았습니다."); + } + return user.getOfficeId(); + } + + private int getAuthenticatedPoliceId(HttpSession session) { + UserResponseDto user = (UserResponseDto) session.getAttribute("user"); + if (user == null) { + throw new IllegalStateException("세션이 만료되었거나 로그인되지 않았습니다."); + } + return user.getId(); + } + + // 사건 조회 및 권한 검증 (중복 제거) + private CaseEntity getAuthorizedCase(int caseId, HttpSession session) { + int officeId = getAuthenticatedOfficeId(session); + + CaseEntity caseEntity = dashboardRepository.findById(caseId) + .orElseThrow(() -> new EntityNotFoundException("해당 사건을 찾을 수 없습니다.")); + if (caseEntity.getOffice().getId() != officeId) { + throw new NoSuchElementException("해당 사건에 대한 권한이 없습니다."); + } + return caseEntity; + } + + // 사건 조회 + public List getCases(HttpSession session) { + int officeId = getAuthenticatedOfficeId(session); + + List targetStates = List.of( + CaseEntity.CaseState.미확인, + CaseEntity.CaseState.확인, + CaseEntity.CaseState.출동 + ); + List cases = dashboardRepository.findAllByOfficeIdAndStateInOrderById(officeId, targetStates); + + return cases.stream() + .map(DashboardResponse::fromEntity) + .collect(Collectors.toList()); + } + + // id별 사건 영상 확인 + public Map getCaseVideo(int id, HttpSession session) { + CaseEntity caseEntity = getAuthorizedCase(id, session); + + String videoUrl = caseEntity.getVideo(); + if (videoUrl == null || videoUrl.trim().isEmpty()) { + throw new EntityNotFoundException("해당 사건에 대한 영상이 없습니다."); + } + + return Collections.singletonMap("video", videoUrl); + } + + // 출동, 미출동 상태 변경 + public Map updateCaseState(int id, StateRequest request, HttpSession session) { + CaseEntity caseEntity = getAuthorizedCase(id, session); + int policeId = getAuthenticatedPoliceId(session); + + if (caseEntity.getState() == CaseEntity.CaseState.출동 || + caseEntity.getState() == CaseEntity.CaseState.미출동 || + caseEntity.getState() == CaseEntity.CaseState.완료) { + throw new IllegalStateException("해당 사건은 이미 출동 처리되었거나 완료된 사건입니다."); + } + + if (request.getState() == CaseEntity.CaseState.출동) { + // 출동 처리 + caseEntity.setState(CaseEntity.CaseState.출동); + + // 경찰관 배정 + PoliceEntity assignedPolice = PoliceEntity.builder().id(policeId).build(); + caseEntity.setPolice(assignedPolice); + + dashboardRepository.save(caseEntity); + + return Collections.singletonMap(id, "지금 출동합니다."); + } else { + // 미출동 처리 + caseEntity.setState(CaseEntity.CaseState.미출동); + + if (request.getCategory() != null) { + caseEntity.setAccuracy(false); + caseEntity.setCategory(request.getCategory()); + } + + dashboardRepository.save(caseEntity); + + return Collections.singletonMap(id, "미출동 사건으로 변경합니다."); + } + } + + // 출동 중인 사건 해결 처리 + public Map completeCase(int id, SurveyRequest surveyRequest, HttpSession session) { + CaseEntity caseEntity = getAuthorizedCase(id, session); + + if (caseEntity.getState() != CaseEntity.CaseState.출동) { + throw new IllegalStateException("해당 사건은 출동 상태가 아닙니다."); + } + + caseEntity.setState(CaseEntity.CaseState.완료); + + if (surveyRequest.getCategory() != null) { + caseEntity.setAccuracy(false); + caseEntity.setCategory(surveyRequest.getCategory()); + } + + dashboardRepository.save(caseEntity); + + return Collections.singletonMap(id, "해당 사건이 해결 처리되었습니다."); + } +} diff --git a/backend/src/main/java/com/example/backend/dashboard/service/ProgressService.java b/backend/src/main/java/com/example/backend/dashboard/service/ProgressService.java deleted file mode 100644 index e1a9081a..00000000 --- a/backend/src/main/java/com/example/backend/dashboard/service/ProgressService.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.example.backend.dashboard.service; - -import com.example.backend.common.domain.CaseEntity; -import com.example.backend.common.domain.CaseEntity.CaseState; -import com.example.backend.dashboard.dto.ProgressResponse; -import com.example.backend.dashboard.dto.SurveyRequest; -import com.example.backend.dashboard.dto.SurveyResponse; -import com.example.backend.dashboard.repository.ProgressRepository; -import com.example.backend.user.dto.UserResponseDto; -import jakarta.persistence.EntityNotFoundException; -import jakarta.servlet.http.HttpSession; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.stream.Collectors; - -@Service -@Transactional -@RequiredArgsConstructor -public class ProgressService { - - private final ProgressRepository progressRepository; - - // 세션에서 officeId 추출 - private int getAuthenticatedOfficeId(HttpSession session) { - UserResponseDto user = (UserResponseDto) session.getAttribute("user"); - if (user == null) { - throw new IllegalStateException("세션이 만료되었거나 로그인되지 않았습니다."); - } - return user.getOfficeId(); - } - - // 사건 조회 및 권한 검증 (중복 제거) - private CaseEntity getAuthorizedCase(int caseId, HttpSession session) { - int officeId = getAuthenticatedOfficeId(session); - CaseEntity caseEntity = progressRepository.findById(caseId) - .orElseThrow(() -> new EntityNotFoundException("해당 사건을 찾을 수 없습니다.")); - if (caseEntity.getOffice().getId() != officeId) { - throw new NoSuchElementException("해당 사건에 대한 권한이 없습니다."); - } - return caseEntity; - } - - // (전체) 출동 중인 사건 조회 - public List getActiveCases(HttpSession session) { - int officeId = getAuthenticatedOfficeId(session); - - List cases = progressRepository.findAllByOfficeIdAndStateOrderById(officeId, CaseState.출동); - - return cases.stream().map(ProgressResponse::fromEntity).collect(Collectors.toList()); - } - - // 출동 중인 사건 영상 확인 - public Map getCaseVideo(int id, HttpSession session) { - CaseEntity caseEntity = getAuthorizedCase(id, session); - - if (caseEntity.getState() != CaseState.출동) { - throw new IllegalStateException("해당 사건은 출동 상태가 아닙니다."); - } - - String videoUrl = caseEntity.getVideo(); - if (videoUrl == null || videoUrl.trim().isEmpty()) { - throw new EntityNotFoundException("해당 사건에 대한 영상이 없습니다."); - } - - return Collections.singletonMap("video", videoUrl); - } - - // 출동 중인 사건 해결 처리 - public Map completeCase(int id, HttpSession session) { - CaseEntity caseEntity = getAuthorizedCase(id, session); - - if (caseEntity.getState() != CaseState.출동) { - throw new IllegalStateException("해당 사건은 출동 상태가 아닙니다."); - } - - caseEntity.setState(CaseState.완료); - progressRepository.save(caseEntity); - - return Collections.singletonMap(id, "해당 사건이 해결 처리되었습니다."); - } - - // AI 설문조사 결과 저장 - public SurveyResponse saveSurveyResult(int id, SurveyRequest surveyRequest, HttpSession session) { - CaseEntity caseEntity = getAuthorizedCase(id, session); - - if (!caseEntity.getAccuracy()) { - throw new IllegalStateException("이미 설문조사가 완료된 사건입니다."); - } - - caseEntity.setAccuracy(false); - if (surveyRequest.getCategory() != null) { - caseEntity.setCategory(surveyRequest.getCategory()); - } - progressRepository.save(caseEntity); - - return new SurveyResponse(id, caseEntity.getCategory(), "설문조사가 정상적으로 저장되었습니다."); - } -}