Skip to content

Commit d900d3e

Browse files
authored
Merge pull request #85 from kookmin-sw/backend
[Backend] merge: SSE API 연동
2 parents 20ff052 + c7700fc commit d900d3e

File tree

11 files changed

+180
-16
lines changed

11 files changed

+180
-16
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.example.backend.dashboard.controller;
2+
3+
import com.example.backend.dashboard.dto.CaseDetectRequest;
4+
import com.example.backend.dashboard.dto.CaseDetectResponse;
5+
import com.example.backend.dashboard.service.CaseDetectService;
6+
import com.example.backend.dashboard.service.SseEmitterService;
7+
import lombok.RequiredArgsConstructor;
8+
import org.springframework.http.ResponseEntity;
9+
import org.springframework.web.bind.annotation.*;
10+
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
11+
12+
@RestController
13+
@RequestMapping("/api/v1/case")
14+
@RequiredArgsConstructor
15+
public class CaseDetectController {
16+
17+
private final CaseDetectService caseDetectService;
18+
private final SseEmitterService sseEmitterService;
19+
20+
@PostMapping("/detect")
21+
public ResponseEntity<?> detectAlarm(@RequestBody CaseDetectRequest request) {
22+
CaseDetectResponse response = caseDetectService.saveCase(request);
23+
sseEmitterService.broadcast(response);
24+
return ResponseEntity.ok(response);
25+
}
26+
27+
@GetMapping("/subscribe")
28+
public SseEmitter subscribe() {
29+
return sseEmitterService.createEmitter();
30+
}
31+
32+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.example.backend.dashboard.dto;
2+
import lombok.Data;
3+
4+
@Data
5+
public class CaseDetectRequest {
6+
private Integer officeId; // 사무소 ID
7+
private Integer cctvId; // CCTV ID
8+
private Integer level; // 위험 레벨
9+
private String category; // "fire", "assault" 등
10+
private String video; // 스토리지에 저장된 영상 URL
11+
private String memo; // 기타 메모
12+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.example.backend.dashboard.dto;
2+
3+
import com.example.backend.common.domain.CaseEntity;
4+
import java.time.format.DateTimeFormatter;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Builder;
7+
import lombok.Data;
8+
import lombok.NoArgsConstructor;
9+
10+
@Data
11+
@Builder
12+
@NoArgsConstructor
13+
@AllArgsConstructor
14+
public class CaseDetectResponse {
15+
private Integer caseId; // 사건 ID
16+
private String category; // 사건 카테고리 (예: fire, assault 등)
17+
private String cctvAddress; // CCTV 주소
18+
private String dateTime; // 사건 감지 시각 (문자열 형태, yyyy-MM-dd HH:mm:ss)
19+
private String video; // 영상 URL
20+
21+
public static CaseDetectResponse fromEntity(CaseEntity entity) {
22+
String formattedDateTime = entity.getDate()
23+
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
24+
return CaseDetectResponse.builder()
25+
.caseId(entity.getId())
26+
.category(entity.getCategory().name())
27+
.cctvAddress(entity.getCctv().getAddress())
28+
.dateTime(formattedDateTime)
29+
.video(entity.getVideo())
30+
.build();
31+
}
32+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.example.backend.dashboard.service;
2+
3+
import com.example.backend.common.domain.CaseEntity;
4+
import com.example.backend.common.domain.CctvEntity;
5+
import com.example.backend.common.domain.OfficeEntity;
6+
import com.example.backend.dashboard.dto.CaseDetectRequest;
7+
import com.example.backend.dashboard.dto.CaseDetectResponse;
8+
import com.example.backend.dashboard.repository.ProgressRepository;
9+
import com.example.backend.search.repository.CctvRepository;
10+
import jakarta.persistence.EntityNotFoundException;
11+
import lombok.RequiredArgsConstructor;
12+
import org.springframework.stereotype.Service;
13+
import org.springframework.transaction.annotation.Transactional;
14+
15+
import java.time.LocalDateTime;
16+
17+
@Service
18+
@Transactional
19+
@RequiredArgsConstructor
20+
public class CaseDetectService {
21+
22+
private final ProgressRepository caseRepository;
23+
private final CctvRepository cctvRepository; // CCTV 정보(주소) 조회용
24+
25+
public CaseDetectResponse saveCase(CaseDetectRequest request) {
26+
// 1) CCTV 정보 조회 (주소 포함)
27+
CctvEntity cctvEntity = cctvRepository.findById(request.getCctvId())
28+
.orElseThrow(() -> new EntityNotFoundException("CCTV 정보를 찾을 수 없습니다."));
29+
30+
// 2) 사건 저장
31+
CaseEntity caseEntity = CaseEntity.builder()
32+
.office(OfficeEntity.builder().id(request.getOfficeId()).build())
33+
.cctv(cctvEntity)
34+
.date(LocalDateTime.now())
35+
.category(CaseEntity.CaseCategory.valueOf(request.getCategory()))
36+
.video(request.getVideo())
37+
.state(CaseEntity.CaseState.미확인)
38+
.accuracy(true)
39+
.memo(request.getMemo())
40+
.level(request.getLevel())
41+
.build();
42+
43+
CaseEntity saved = caseRepository.save(caseEntity);
44+
45+
// 3) DTO 변환: 기존에 정의한 fromEntity 메서드를 사용하여 응답 객체 생성
46+
return CaseDetectResponse.fromEntity(saved);
47+
}
48+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.example.backend.dashboard.service;
2+
import com.example.backend.dashboard.dto.CaseDetectResponse;
3+
import org.springframework.http.MediaType;
4+
import org.springframework.stereotype.Service;
5+
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
6+
7+
import java.io.IOException;
8+
import java.util.ArrayList;
9+
import java.util.List;
10+
import java.util.concurrent.CopyOnWriteArrayList;
11+
12+
@Service
13+
public class SseEmitterService {
14+
15+
private final List<SseEmitter> emitters = new CopyOnWriteArrayList<>();
16+
17+
public SseEmitter createEmitter() {
18+
SseEmitter emitter = new SseEmitter(Long.MAX_VALUE);
19+
emitters.add(emitter);
20+
21+
emitter.onCompletion(() -> emitters.remove(emitter));
22+
emitter.onTimeout(() -> emitters.remove(emitter));
23+
return emitter;
24+
}
25+
26+
public void broadcast(CaseDetectResponse response) {
27+
List<SseEmitter> deadEmitters = new ArrayList<>();
28+
for (SseEmitter emitter : emitters) {
29+
try {
30+
emitter.send(SseEmitter.event()
31+
.name("alarm-detected")
32+
.data(response, MediaType.APPLICATION_JSON));
33+
} catch (IOException e) {
34+
deadEmitters.add(emitter);
35+
}
36+
}
37+
emitters.removeAll(deadEmitters);
38+
}
39+
40+
}

backend/src/main/java/com/example/backend/search/controller/SearchController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public ResponseEntity<?> getCheckLog(@RequestBody SearchRequest request, HttpSes
3131
request.getCategory(),
3232
request.getStartDateTime(),
3333
request.getEndDateTime(),
34-
request.getLocation(),
34+
request.getAddress(),
3535
request.getPolice(),
3636
request.getOrder(),
3737
request.getPage(),

backend/src/main/java/com/example/backend/search/dto/DetailResponse.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class DetailResponse {
1313
private int policeId;
1414
private String policeName;
1515
private int cctvId;
16-
private String location;
16+
private String address;
1717
private Double longitude;
1818
private Double latitude;
1919

@@ -25,13 +25,13 @@ public class DetailResponse {
2525
private String memo;
2626

2727
@Builder
28-
public DetailResponse(Integer id, Integer policeId, String policeName, Integer cctvId, String location, Double latitude, Double longitude,LocalDateTime date,
28+
public DetailResponse(Integer id, Integer policeId, String policeName, Integer cctvId, String address, Double latitude, Double longitude, LocalDateTime date,
2929
String category, String video, String memo) {
3030
this.id = id;
3131
this.policeId = policeId;
3232
this.policeName = policeName;
3333
this.cctvId = cctvId;
34-
this.location = location;
34+
this.address = address;
3535
this.latitude = latitude;
3636
this.longitude = longitude;
3737
this.date = date;
@@ -46,7 +46,7 @@ public static DetailResponse fromEntity(CaseEntity entity) {
4646
.policeId(entity.getPolice() != null ? entity.getPolice().getId() : null)
4747
.policeName(entity.getPolice() != null ? entity.getPolice().getName() : null)
4848
.cctvId(entity.getCctv() != null ? entity.getCctv().getId() : null)
49-
.location(entity.getCctv().getAddress())
49+
.address(entity.getCctv().getAddress())
5050
.latitude(entity.getCctv().getLatitude())
5151
.longitude(entity.getCctv().getLongitude())
5252
.date(entity.getDate())

backend/src/main/java/com/example/backend/search/dto/SearchRequest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public class SearchRequest {
1010
private String category;
1111
private String startDate;
1212
private String endDate;
13-
private String location;
13+
private String address;
1414
private String police;
1515
private String order;
1616
private Integer page;

backend/src/main/java/com/example/backend/search/dto/SearchResponse.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public class SearchResponse {
1717
private int policeId;
1818
private String policeName;
1919
private int cctvId;
20-
private String location;
20+
private String address;
2121

2222
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
2323
private LocalDateTime date;
@@ -26,13 +26,13 @@ public class SearchResponse {
2626
private String memo;
2727

2828
@Builder
29-
public SearchResponse(Integer id, Integer policeId, String policeName, Integer cctvId, String location, LocalDateTime date,
29+
public SearchResponse(Integer id, Integer policeId, String policeName, Integer cctvId, String address, LocalDateTime date,
3030
String category, String memo) {
3131
this.id = id;
3232
this.policeId = policeId;
3333
this.policeName = policeName;
3434
this.cctvId = cctvId;
35-
this.location = location;
35+
this.address = address;
3636
this.date = date;
3737
this.category = category;
3838
this.memo = memo;
@@ -44,7 +44,7 @@ public static SearchResponse fromEntity(CaseEntity entity) {
4444
.policeId(entity.getPolice() != null ? entity.getPolice().getId() : null)
4545
.policeName(entity.getPolice() != null ? entity.getPolice().getName() : null)
4646
.cctvId(entity.getCctv() != null ? entity.getCctv().getId() : null)
47-
.location(entity.getCctv().getAddress())
47+
.address(entity.getCctv().getAddress())
4848
.date(entity.getDate())
4949
.category(entity.getCategory().name())
5050
.memo(entity.getMemo())

backend/src/main/java/com/example/backend/search/repository/CctvRepository.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@
1111
@Repository
1212
public interface CctvRepository extends JpaRepository<CctvEntity, Integer> {
1313

14-
@Query("SELECT c.id FROM CctvEntity c WHERE c.address LIKE %:location%")
15-
List<Integer> findCctvIdsByAddress(@Param("location") String location);
14+
@Query("SELECT c.id FROM CctvEntity c WHERE c.address LIKE %:address%")
15+
List<Integer> findCctvIdsByAddress(@Param("location") String address);
1616
}

0 commit comments

Comments
 (0)