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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
name: CD for main

on:
workflow_run:
workflows: ["CI for main"] # CI 워크플로우 이름과 정확히 일치해야 함
push:
branches: [main]
types:
- completed
workflow_dispatch:
inputs:
branch:
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ name: CI for main
on:
pull_request:
branches: [ main ]
push:
branches: [ main ]

env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,14 @@ public ResponseEntity<Page<SensorDetailDto>> getAllSensors(
Page<SensorDetailDto> sensors = adminDeviceService.getAllSensorsDetailForAdmin(pageable);
return ResponseEntity.ok(sensors);
}

// 센서 활성 상태 지정
@Override
@PatchMapping("/sensors/{serialNumber}/active")
public ResponseEntity<String> setSensorActiveStatus(
@PathVariable String serialNumber,
@RequestParam boolean active) {
adminDeviceService.setSensorActiveStatus(serialNumber, active);
return ResponseEntity.ok("센서 활성 상태가 성공적으로 변경되었습니다.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
@Tag(name = "Admin API", description = "관리자 기능 관련 API 명세")
public interface AdminControllerDocs {

@Operation(summary = "전체 방 현황 조회 (관리자)", description = "시스템의 모든 방 목록을 페이징하여 조회합니다. 관리자 권한이 필요합니다.")
@Operation(summary = "전체 방 현황 조회 (관리자)", description = "시스템의 모든 방 목록을 페이징하여 조회합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "전체 방 목록 조회 성공",
content = @Content(schema = @Schema(implementation = Page.class))), // 실제론 Page<RoomDetailResponseDto> 형태
Expand All @@ -35,7 +35,7 @@ ResponseEntity<Page<RoomDetailResponseDto>> getAllRooms(
" \"sort\": \"id,desc\")",
schema = @Schema(implementation = Pageable.class)) Pageable pageable);

@Operation(summary = "특정 방 상세 조회 (관리자)", description = "특정 방의 상세 정보를 조회합니다. 관리자 권한이 필요합니다.")
@Operation(summary = "특정 방 상세 조회 (관리자)", description = "특정 방의 상세 정보를 조회합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "방 상세 정보 조회 성공",
content = @Content(schema = @Schema(implementation = RoomDetailResponseDto.class))),
Expand All @@ -47,7 +47,7 @@ ResponseEntity<RoomDetailResponseDto> getRoomDetailForAdmin(
@Parameter(name = "roomId", description = "조회할 방의 ID", required = true, in = ParameterIn.PATH)
@PathVariable Long roomId);

@Operation(summary = "방 정보 수정 (관리자)", description = "특정 방의 정보를 수정합니다. 관리자 권한이 필요합니다.",
@Operation(summary = "방 정보 수정 (관리자)", description = "특정 방의 정보를 수정합니다.",
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "방 수정 요청 정보", required = true,
content = @Content(schema = @Schema(implementation = CreateRoomRequestDto.class))))
@ApiResponses(value = {
Expand All @@ -63,7 +63,7 @@ ResponseEntity<RoomDetailResponseDto> updateRoomForAdmin(
@PathVariable Long roomId,
@RequestBody CreateRoomRequestDto requestDto);

@Operation(summary = "방 삭제 (관리자)", description = "특정 방을 삭제합니다. 관리자 권한이 필요합니다.")
@Operation(summary = "방 삭제 (관리자)", description = "특정 방을 삭제합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "204", description = "방 삭제 성공 (No Content)"),
@ApiResponse(responseCode = "401", description = "인증되지 않은 사용자"),
Expand All @@ -74,7 +74,7 @@ ResponseEntity<Void> deleteRoomForAdmin(
@Parameter(name = "roomId", description = "삭제할 방의 ID", required = true, in = ParameterIn.PATH)
@PathVariable Long roomId);

@Operation(summary = "전체 사용자 현황 조회 (관리자)", description = "시스템의 모든 사용자 목록을 페이징하여 조회합니다. 관리자 권한이 필요합니다.")
@Operation(summary = "전체 사용자 현황 조회 (관리자)", description = "시스템의 모든 사용자 목록을 페이징하여 조회합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "전체 사용자 목록 조회 성공",
content = @Content(schema = @Schema(implementation = Page.class))), // 실제론 Page<UserDetailResponseDto> 형태
Expand All @@ -85,7 +85,7 @@ ResponseEntity<Page<UserDetailResponseDto>> getAllUsers(
@Parameter(description = "페이지네이션 정보 (예 : \"page\": 0,\n" + " \"size\": 10,\n" +
" \"sort\": \"id,desc\")", schema = @Schema(implementation = Pageable.class)) Pageable pageable);

@Operation(summary = "전체 기기 현황 조회 (관리자)", description = "시스템의 모든 기기 목록을 페이징하여 조회합니다. 관리자 권한이 필요합니다.")
@Operation(summary = "전체 기기 현황 조회 (관리자)", description = "시스템의 모든 기기 목록을 페이징하여 조회합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "전체 기기 목록 조회 성공",
content = @Content(schema = @Schema(implementation = Page.class))), // 실제론 Page<DeviceDetailDto> 형태
Expand All @@ -97,7 +97,7 @@ ResponseEntity<Page<DeviceDetailDto>> getAllDevices(
" \"size\": 10,\n" +
" \"sort\": \"id,desc\")", schema = @Schema(implementation = Pageable.class)) Pageable pageable);

@Operation(summary = "전체 센서 현황 조회 (관리자)", description = "시스템의 모든 센서 목록을 페이징하여 조회합니다. 관리자 권한이 필요합니다.")
@Operation(summary = "전체 센서 현황 조회 (관리자)", description = "시스템의 모든 센서 목록을 페이징하여 조회합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "전체 센서 목록 조회 성공",
content = @Content(schema = @Schema(implementation = Page.class))), // 실제론 Page<SensorDetailDto> 형태
Expand All @@ -108,4 +108,18 @@ ResponseEntity<Page<SensorDetailDto>> getAllSensors(
@Parameter(description = "페이지네이션 정보 (예 : \"page\": 0,\n" +
" \"size\": 10,\n" +
" \"sort\": \"id,desc\")", schema = @Schema(implementation = Pageable.class)) Pageable pageable);

@Operation(summary = "센서 활성 상태 지정 (관리자)", description = "특정 센서의 활성 상태를 지정합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "센서 활성 상태 지정 성공",
content = @Content(schema = @Schema(implementation = SensorDetailDto.class))),
@ApiResponse(responseCode = "400", description = "잘못된 요청"),
@ApiResponse(responseCode = "401", description = "인증되지 않은 사용자"),
@ApiResponse(responseCode = "403", description = "권한 없음"),
@ApiResponse(responseCode = "404", description = "센서를 찾을 수 없음")
})
ResponseEntity<String> setSensorActiveStatus(
@Parameter(name = "serialNumber", description = "활성 상태를 지정할 센서의 일련번호", required = true, in = ParameterIn.PATH)
@PathVariable String serialNumber,
@RequestBody boolean isActive);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
@Tag(name = "Room API", description = "방 관련 API 명세")
public interface RoomControllerDocs {

@Operation(summary = "방 생성", description = "새로운 방을 생성합니다. 시스템 관리자(ADMIN) 또는 매니저(MANAGER) 권한의 사용자만 생성 가능하며, 생성한 사용자가 방의 소유자(MANAGER)가 됩니다.",
@Operation(summary = "방 생성", description = "새로운 방을 생성합니다. 생성한 사용자가 방의 소유자(MANAGER)가 됩니다.",
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "방 생성 요청 정보", required = true,
content = @Content(schema = @Schema(implementation = CreateRoomRequestDto.class))))
@ApiResponses(value = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.example.smartair.exception.CustomException;
import com.example.smartair.exception.ErrorCode;
import com.example.smartair.service.sensorService.SensorService;
import io.swagger.v3.oas.annotations.Parameter;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
Expand Down Expand Up @@ -57,15 +58,15 @@ public ResponseEntity<RoomSensorResponseDto> addSensorToRoom(@AuthenticationPrin
@Override
@DeleteMapping("/sensor")
public ResponseEntity<?> deleteSensor(@AuthenticationPrincipal CustomUserDetails userDetails,
@RequestBody SensorRequestDto.deleteSensorDto deviceDto) throws Exception {
@RequestParam String serialNumber) throws Exception {
if(userDetails == null){
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
User user = userDetails.getUser();

sensorService.deleteSensor(user, deviceDto);
sensorService.deleteSensor(user, serialNumber);

return ResponseEntity.noContent().build();
return ResponseEntity.ok("센서가 성공적으로 삭제되었습니다.");
}

@Override
Expand All @@ -79,21 +80,22 @@ public ResponseEntity<?> getSensorStatus(@AuthenticationPrincipal CustomUserDeta

Boolean status = sensorService.getSensorStatus(deviceSerialNumber);

return ResponseEntity.ok("device"+ deviceSerialNumber + " running state: " + status);
return ResponseEntity.ok("sensor"+ deviceSerialNumber + " running state: " + status);
}

@Override
@DeleteMapping("/sensor/room")
public ResponseEntity<?> unregisterSensorFromRoom(
@AuthenticationPrincipal CustomUserDetails userDetails,
@RequestBody SensorRequestDto.unregisterSensorFromRoomDto request) throws Exception {
@RequestParam String serialNumber,
@RequestParam Long roomId) throws Exception {
if(userDetails == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
User user = userDetails.getUser();

sensorService.unregisterSensorFromRoom(user, request);
return ResponseEntity.noContent().build();
sensorService.unregisterSensorFromRoom(user, serialNumber, roomId);
return ResponseEntity.ok("센서가 방에서 성공적으로 등록 해제되었습니다.");
}

@Override
Expand Down Expand Up @@ -131,5 +133,20 @@ public ResponseEntity<List<SensorResponseDto>> getUserSensors(
return ResponseEntity.ok(sensors);
}

@Override
@GetMapping("/sensor/find/{serialNumber}")
public ResponseEntity<SensorResponseDto> getSensorBySerialNumber(
@AuthenticationPrincipal CustomUserDetails userDetails,
@PathVariable String serialNumber) {
if (userDetails == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
User user = userDetails.getUser();

SensorResponseDto sensorDto = sensorService.getSensorBySerialNumber(serialNumber);

return ResponseEntity.ok(sensorDto);
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,13 @@ ResponseEntity<RoomSensorResponseDto> addSensorToRoom(@AuthenticationPrincipal C
## 센서 삭제

사용자가 등록한 센서를 삭제합니다.
관련된 SensorAirQualityData, PredictedAirQualityData, Fineparticlesdata, DailyReport, WeeklyReport, HourlySnapshot도 삭제됩니다.

---

**요청 정보**
- 인증 정보는 `@AuthenticationPrincipal` 을 통해 자동 주입됩니다.

**요청 본문 (`RequestBody`)**
- `deviceDto` (Object): 삭제할 센서 정보
- `serialNumber` (Long): 센서의 일련번호
- `roomId` (Long): 삭제할 센서가 등록된 방의 ID
- 'serialNumber' (String): 삭제할 센서의 일련번호

---

Expand All @@ -103,7 +100,7 @@ ResponseEntity<RoomSensorResponseDto> addSensorToRoom(@AuthenticationPrincipal C
"""
)
ResponseEntity<?> deleteSensor(@AuthenticationPrincipal CustomUserDetails userDetails,
@RequestBody SensorRequestDto.deleteSensorDto deviceDto) throws Exception;
@RequestParam String serialNumber) throws Exception;

@Operation(
summary = "센서 상태 조회",
Expand Down Expand Up @@ -140,7 +137,8 @@ ResponseEntity<?> getSensorStatus(@AuthenticationPrincipal CustomUserDetails use
})
ResponseEntity<?> unregisterSensorFromRoom(
@Parameter(hidden = true) @AuthenticationPrincipal CustomUserDetails userDetails,
@RequestBody SensorRequestDto.unregisterSensorFromRoomDto request) throws Exception;
@RequestParam String serialNumber,
@RequestParam Long roomId) throws Exception;

@GetMapping("/sensor/{sensorId}")
@Operation(summary = "센서 ID로 센서 정보 조회", description = "센서 ID를 기반으로 센서 정보를 조회합니다.")
Expand All @@ -161,4 +159,15 @@ ResponseEntity<SensorResponseDto> getSensorById(
@ApiResponse(responseCode = "401", description = "인증 실패")
})
ResponseEntity<List<SensorResponseDto>> getUserSensors(@AuthenticationPrincipal CustomUserDetails userDetails);

@GetMapping("/sensor/find/{serialNumber}")
@Operation(summary = "일련번호로 센서 정보 조회", description = "센서의 일련번호를 기반으로 센서 정보를 조회합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "센서 정보 조회 성공"),
@ApiResponse(responseCode = "401", description = "인증 실패"),
@ApiResponse(responseCode = "404", description = "해당 일련번호의 센서를 찾을 수 없음")
})
ResponseEntity<SensorResponseDto> getSensorBySerialNumber(
@AuthenticationPrincipal CustomUserDetails userDetails,
@Parameter(description = "센서 일련번호", required = true) String serialNumber);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,9 @@ public record setSensorDto(
String name
){}

public record deleteSensorDto(
String serialNumber,
Long roomId
){}

public record addSensorToRoomDto(
String serialNumber,
Long roomId
){}

public record unregisterSensorFromRoomDto(
String serialNumber,
Long roomId
){}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,24 @@
import com.example.smartair.entity.airData.airQualityData.SensorAirQualityData;
import com.example.smartair.entity.sensor.Sensor;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

@Repository
public interface AirQualityDataRepository extends JpaRepository<SensorAirQualityData, Long> {
List<SensorAirQualityData> findBySensorAndCreatedAtBetweenOrderByCreatedAtAsc(Sensor sensor, LocalDateTime snapshotHour, LocalDateTime nextHour);
public interface SensorAirQualityDataRepository extends JpaRepository<SensorAirQualityData, Long> {
Optional<SensorAirQualityData> findTopBySensorIdOrderByCreatedAtDesc(Long sensorId);

Optional<SensorAirQualityData> findBySensor_Id(Long sensor_id);
List <SensorAirQualityData> findAllBySensor_Id(Long sensorId);

Optional<SensorAirQualityData> findTopBySensor_SerialNumberOrderByCreatedAtDesc(String serialNumber);
List<SensorAirQualityData> findBySensorAndCreatedAtBetweenOrderByCreatedAtAsc(
Sensor sensor, LocalDateTime startTime, LocalDateTime endTime);

List<SensorAirQualityData> findByCreatedAtBetween(LocalDateTime startTime, LocalDateTime endTime);

int deleteByCreatedAtBefore(LocalDateTime dateTime);

Optional<SensorAirQualityData> findFirstBySensorAndCreatedAtAfterOrderByCreatedAtDesc(Sensor sensor, LocalDateTime createdAt);

Optional<SensorAirQualityData> findTopBySensorIdOrderByCreatedAtDesc(Long sensorId);


Optional<SensorAirQualityData> findTopBySensor_SerialNumberOrderByCreatedAtDesc(String serialNumber);

int deleteByCreatedAtBefore(LocalDateTime createdAt);
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,6 @@ List<SensorAirQualityScore> findScoresBySensorSerialNumberAndTimeRange(
@Param("startTime") LocalDateTime startTime,
@Param("endTime") LocalDateTime endTime);

List<SensorAirQualityScore> findAllBySensorAirQualityDataId(Long sensorAirQualityDataId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import java.util.Set;

@Repository
public interface HourlyDeviceAirQualitySnapshotRepository extends JpaRepository<HourlySensorAirQualitySnapshot, Long> {
public interface HourlySensorAirQualitySnapshotRepository extends JpaRepository<HourlySensorAirQualitySnapshot, Long> {
Optional<HourlySensorAirQualitySnapshot> findBySensorAndSnapshotHour(Sensor sensor, LocalDateTime snapshotHour);

@Query("SELECT DISTINCT h.sensor FROM HourlySensorAirQualitySnapshot h " +
Expand All @@ -30,4 +30,6 @@ Optional<HourlySensorAirQualitySnapshot> findBySensorAndHourRange(
@Param("sensor") Sensor sensor,
@Param("startHour") LocalDateTime startHour,
@Param("endHour") LocalDateTime endHour);

List<HourlySensorAirQualitySnapshot> findAllBySensor_Id(Long sensorId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ public interface PredictedAirQualityRepository extends JpaRepository<PredictedAi

List<PredictedAirQualityData> findBySensorSerialNumberOrderByTimestamp(String sensorSerialNumber);

List<PredictedAirQualityData> findBySensorSerialNumber(String sensorSerialNumber);
List<PredictedAirQualityData> findAllBySensorSerialNumber(String sensorSerialNumber);
Optional<PredictedAirQualityData> findBySensorSerialNumberAndTimestamp(String sensorSerialNumber, LocalDateTime timestamp);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public interface RoomSensorRepository extends JpaRepository<RoomSensor, Long> {

Optional<RoomSensor> findBySensor_Id(Long sensorId);

boolean existsBySensorId(Long sensor_id);
List<RoomSensor> findAllBySensor_Id(Long sensorId);

boolean existsBySensor_SerialNumberAndRoom_Id(String serialNumber, Long roomId);
}
Loading