diff --git a/src/main/java/devkor/com/teamcback/domain/ble/controller/AdminBLEController.java b/src/main/java/devkor/com/teamcback/domain/ble/controller/AdminBLEController.java new file mode 100644 index 00000000..8475a12c --- /dev/null +++ b/src/main/java/devkor/com/teamcback/domain/ble/controller/AdminBLEController.java @@ -0,0 +1,41 @@ +package devkor.com.teamcback.domain.ble.controller; + +import devkor.com.teamcback.domain.ble.dto.request.CreateBLEReq; +import devkor.com.teamcback.domain.ble.dto.response.CreateBLERes; +import devkor.com.teamcback.domain.ble.entity.BLEDevice; +import devkor.com.teamcback.domain.ble.service.AdminBLEService; +import devkor.com.teamcback.global.response.CommonResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/admin/ble") +public class AdminBLEController { + private final AdminBLEService adminBLEService; + + @PostMapping + @Operation(summary = "BLE장비 생성", + description = "BLE장비 생성") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."), + @ApiResponse(responseCode = "404", description = "장비를 찾을 수 없습니다.", + content = @Content(schema = @Schema(implementation = CommonResponse.class))), + @ApiResponse(responseCode = "401", description = "권한이 없습니다.", + content = @Content(schema = @Schema(implementation = CommonResponse.class))), + }) + public CommonResponse createBLE( + @Parameter(description = "BLE장비 생성 요청 dto") @Valid @RequestBody CreateBLEReq createBLEReq) { + return CommonResponse.success(adminBLEService.CreateBLEDevice(createBLEReq)); + } +} diff --git a/src/main/java/devkor/com/teamcback/domain/ble/controller/BLEController.java b/src/main/java/devkor/com/teamcback/domain/ble/controller/BLEController.java new file mode 100644 index 00000000..db1d3dbb --- /dev/null +++ b/src/main/java/devkor/com/teamcback/domain/ble/controller/BLEController.java @@ -0,0 +1,48 @@ +package devkor.com.teamcback.domain.ble.controller; + +import devkor.com.teamcback.domain.ble.dto.request.UpdateBLEReq; +import devkor.com.teamcback.domain.ble.dto.response.GetBLERes; +import devkor.com.teamcback.domain.ble.dto.response.UpdateBLERes; +import devkor.com.teamcback.domain.ble.service.BLEService; +import devkor.com.teamcback.global.response.CommonResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/ble") + +public class BLEController { + private final BLEService bleService; + + /*** + * + * @param placeId BLE정보를 얻고자 하는 place Id + */ + @GetMapping + @Operation(summary = "placeId를 통한 BLE device 상태 요청", + description = "placeId를 통한 BLE device 상태 요청") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."), + @ApiResponse(responseCode = "404", description = "Not Found", + content = @Content(schema = @Schema(implementation = CommonResponse.class))), + }) + public CommonResponse getBLE( + @Parameter(name="placeId", description = "BLE 정보를 얻고자 하는 placeId") + @RequestParam Long placeId) { + return CommonResponse.success(bleService.getBLE(placeId)); + } + + @PutMapping + public CommonResponse updateBLE( + @Valid @RequestBody UpdateBLEReq updateBLEReq){ + return CommonResponse.success(bleService.updateBLEDevice(updateBLEReq)); + } +} diff --git a/src/main/java/devkor/com/teamcback/domain/ble/dto/request/CreateBLEReq.java b/src/main/java/devkor/com/teamcback/domain/ble/dto/request/CreateBLEReq.java new file mode 100644 index 00000000..72266bb5 --- /dev/null +++ b/src/main/java/devkor/com/teamcback/domain/ble/dto/request/CreateBLEReq.java @@ -0,0 +1,21 @@ +package devkor.com.teamcback.domain.ble.dto.request; + +import devkor.com.teamcback.domain.ble.entity.BLEstatus; +import devkor.com.teamcback.domain.place.entity.Place; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalTime; + +@Schema(description = "BLEdevice 생성 정보") +@Getter +@Setter +public class CreateBLEReq { + @Schema(description = "라운지 설치된 기기명", example = "woodang_1f_lounge") + private String deviceName; + @Schema(description ="라운지 place") + private Long placeId; + @Schema(description = "라운지별 최대정원", example = "20") + private int capacity; +} diff --git a/src/main/java/devkor/com/teamcback/domain/ble/dto/request/UpdateBLEReq.java b/src/main/java/devkor/com/teamcback/domain/ble/dto/request/UpdateBLEReq.java new file mode 100644 index 00000000..4af66dc7 --- /dev/null +++ b/src/main/java/devkor/com/teamcback/domain/ble/dto/request/UpdateBLEReq.java @@ -0,0 +1,22 @@ +package devkor.com.teamcback.domain.ble.dto.request; + +import com.fasterxml.jackson.annotation.JsonFormat; +import devkor.com.teamcback.domain.ble.entity.BLEstatus; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Schema(description = "BLEdevice 수정 정보") +@Getter +@Setter +public class UpdateBLEReq { + @Schema(description = "라운지 설치된 기기명", example = "woodang_1f_lounge") + private String deviceName; + @Schema(description = "최근 감지 인원", example = "10") + private int lastCount; + @Schema(description = "최근 신호 전송 시간") + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") + private LocalDateTime lastTime; +} diff --git a/src/main/java/devkor/com/teamcback/domain/ble/dto/response/CreateBLERes.java b/src/main/java/devkor/com/teamcback/domain/ble/dto/response/CreateBLERes.java new file mode 100644 index 00000000..68577888 --- /dev/null +++ b/src/main/java/devkor/com/teamcback/domain/ble/dto/response/CreateBLERes.java @@ -0,0 +1,15 @@ +package devkor.com.teamcback.domain.ble.dto.response; + +import devkor.com.teamcback.domain.ble.entity.BLEDevice; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; + +@Schema(description = "BLEdevice 생성 정보") +@Getter +public class CreateBLERes { + private Long id; + + public CreateBLERes(BLEDevice bleDevice) { + this.id = bleDevice.getId(); + } +} diff --git a/src/main/java/devkor/com/teamcback/domain/ble/dto/response/GetBLERes.java b/src/main/java/devkor/com/teamcback/domain/ble/dto/response/GetBLERes.java new file mode 100644 index 00000000..716ea1b0 --- /dev/null +++ b/src/main/java/devkor/com/teamcback/domain/ble/dto/response/GetBLERes.java @@ -0,0 +1,29 @@ +package devkor.com.teamcback.domain.ble.dto.response; + +import devkor.com.teamcback.domain.ble.entity.BLEData; +import devkor.com.teamcback.domain.ble.entity.BLEDevice; +import devkor.com.teamcback.domain.ble.entity.BLEstatus; +import lombok.Getter; + +import java.time.LocalDateTime; + +@Getter +public class GetBLERes { + private Long id; + private String deviceName; + private Long placeId; + private int capacity; + private int lastCount; + private int lastStatus; + private LocalDateTime lastTime; + + public GetBLERes(BLEDevice device, BLEData data, BLEstatus status) { + this.id = device.getId(); + this.deviceName = device.getDeviceName(); + this.placeId = device.getPlace().getId(); + this.capacity = device.getCapacity(); + this.lastCount = data.getLastCount(); + this.lastStatus = status.getCode(); + this.lastTime = data.getLastTime(); + } +} diff --git a/src/main/java/devkor/com/teamcback/domain/ble/dto/response/UpdateBLERes.java b/src/main/java/devkor/com/teamcback/domain/ble/dto/response/UpdateBLERes.java new file mode 100644 index 00000000..f6d515b3 --- /dev/null +++ b/src/main/java/devkor/com/teamcback/domain/ble/dto/response/UpdateBLERes.java @@ -0,0 +1,27 @@ +package devkor.com.teamcback.domain.ble.dto.response; + +import devkor.com.teamcback.domain.ble.entity.BLEData; +import devkor.com.teamcback.domain.ble.entity.BLEstatus; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; + +import java.time.LocalDateTime; + +@Getter +public class UpdateBLERes { + @Schema(description = "라운지 설치된 기기명", example = "woodang_1f_lounge") + private Long deviceId; + @Schema(description = "최근 감지 인원", example = "10") + private int lastCount; + @Schema(description = "최근 Status", example = "AVAILABLE") + private BLEstatus lastStatus; + @Schema(description = "최근 신호 전송 시간") + private LocalDateTime lastTime; + + public UpdateBLERes(BLEData data) { + this.deviceId = data.getDevice().getId(); + this.lastCount = data.getLastCount(); + this.lastStatus = data.getLastStatus(); + this.lastTime = data.getLastTime(); + } +} diff --git a/src/main/java/devkor/com/teamcback/domain/ble/entity/BLEData.java b/src/main/java/devkor/com/teamcback/domain/ble/entity/BLEData.java new file mode 100644 index 00000000..89ec79da --- /dev/null +++ b/src/main/java/devkor/com/teamcback/domain/ble/entity/BLEData.java @@ -0,0 +1,36 @@ +package devkor.com.teamcback.domain.ble.entity; + + +import devkor.com.teamcback.domain.ble.dto.request.UpdateBLEReq; +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Entity +@Table(name="tb_ble_data") +@Getter +@Setter +@NoArgsConstructor +public class BLEData { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(optional = false, fetch = FetchType.LAZY) + @JoinColumn(name = "device_id", nullable = false) + private BLEDevice device; + + @Column + private Integer lastCount; + + @Enumerated(EnumType.ORDINAL) + @Column + private BLEstatus lastStatus; + + @Column(nullable = false) + private LocalDateTime lastTime; + +} diff --git a/src/main/java/devkor/com/teamcback/domain/ble/entity/BLEDevice.java b/src/main/java/devkor/com/teamcback/domain/ble/entity/BLEDevice.java new file mode 100644 index 00000000..e5d7dca6 --- /dev/null +++ b/src/main/java/devkor/com/teamcback/domain/ble/entity/BLEDevice.java @@ -0,0 +1,40 @@ +package devkor.com.teamcback.domain.ble.entity; + + +import devkor.com.teamcback.domain.ble.dto.request.UpdateBLEReq; +import devkor.com.teamcback.domain.common.entity.BaseEntity; +import devkor.com.teamcback.domain.place.entity.Place; +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Entity +@Table(name="tb_ble_device") +@Getter +@Setter +@NoArgsConstructor +public class BLEDevice extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, unique = true) + private String deviceName; + + @OneToOne + @JoinColumn(name = "placeId") + private Place place; + + @Column + private int capacity; + + public BLEDevice(String deviceName, Place place, int capacity) { + this.deviceName = deviceName; + this.place = place; + this.capacity = capacity; + } + +} diff --git a/src/main/java/devkor/com/teamcback/domain/ble/entity/BLEstatus.java b/src/main/java/devkor/com/teamcback/domain/ble/entity/BLEstatus.java new file mode 100644 index 00000000..c628993f --- /dev/null +++ b/src/main/java/devkor/com/teamcback/domain/ble/entity/BLEstatus.java @@ -0,0 +1,17 @@ +package devkor.com.teamcback.domain.ble.entity; + + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum BLEstatus { + VACANT(0, "여유"), + AVAILABLE(1, "보통"), + CROWDED(2, "포화"), + FAILURE(3, "신호없음"); + + private final int code; + private final String label; +} diff --git a/src/main/java/devkor/com/teamcback/domain/ble/repository/BLEDataRepository.java b/src/main/java/devkor/com/teamcback/domain/ble/repository/BLEDataRepository.java new file mode 100644 index 00000000..d03d497f --- /dev/null +++ b/src/main/java/devkor/com/teamcback/domain/ble/repository/BLEDataRepository.java @@ -0,0 +1,12 @@ +package devkor.com.teamcback.domain.ble.repository; + +import devkor.com.teamcback.domain.ble.entity.BLEData; +import devkor.com.teamcback.domain.ble.entity.BLEDevice; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.CrudRepository; + +import java.util.Optional; + +public interface BLEDataRepository extends JpaRepository { + Optional findTopByDeviceOrderByLastTimeDesc(BLEDevice device); +} diff --git a/src/main/java/devkor/com/teamcback/domain/ble/repository/BLEDeviceRepository.java b/src/main/java/devkor/com/teamcback/domain/ble/repository/BLEDeviceRepository.java new file mode 100644 index 00000000..64f254e8 --- /dev/null +++ b/src/main/java/devkor/com/teamcback/domain/ble/repository/BLEDeviceRepository.java @@ -0,0 +1,13 @@ +package devkor.com.teamcback.domain.ble.repository; + +import devkor.com.teamcback.domain.ble.entity.BLEDevice; +import devkor.com.teamcback.domain.place.entity.Place; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface BLEDeviceRepository extends JpaRepository { + List findByDeviceName(String deviceName); + List findByPlace(Place place); + boolean existsByDeviceName(String deviceName); +} diff --git a/src/main/java/devkor/com/teamcback/domain/ble/service/AdminBLEService.java b/src/main/java/devkor/com/teamcback/domain/ble/service/AdminBLEService.java new file mode 100644 index 00000000..4ecc8885 --- /dev/null +++ b/src/main/java/devkor/com/teamcback/domain/ble/service/AdminBLEService.java @@ -0,0 +1,30 @@ +package devkor.com.teamcback.domain.ble.service; + +import devkor.com.teamcback.domain.ble.dto.request.CreateBLEReq; +import devkor.com.teamcback.domain.ble.dto.response.CreateBLERes; +import devkor.com.teamcback.domain.ble.entity.BLEDevice; +import devkor.com.teamcback.domain.ble.repository.BLEDeviceRepository; +import devkor.com.teamcback.domain.place.entity.Place; +import devkor.com.teamcback.domain.place.repository.PlaceRepository; +import devkor.com.teamcback.global.exception.exception.GlobalException; +import devkor.com.teamcback.global.response.ResultCode; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import static devkor.com.teamcback.global.response.ResultCode.NOT_FOUND_PLACE; + +@Service +@RequiredArgsConstructor +public class AdminBLEService { + private final BLEDeviceRepository bleDeviceRepository; + private final PlaceRepository placeRepository; + + @Transactional + public CreateBLERes CreateBLEDevice(CreateBLEReq createBLEReq) { + Place place = placeRepository.findById(createBLEReq.getPlaceId()).orElseThrow(() -> new GlobalException(NOT_FOUND_PLACE)); + if (bleDeviceRepository.existsByDeviceName(createBLEReq.getDeviceName())) throw new GlobalException(ResultCode.EXISTING_DEVICE_NAME); + BLEDevice bleDevice = bleDeviceRepository.save(new BLEDevice(createBLEReq.getDeviceName(), place, createBLEReq.getCapacity())); + return new CreateBLERes(bleDevice); + } +} diff --git a/src/main/java/devkor/com/teamcback/domain/ble/service/BLEService.java b/src/main/java/devkor/com/teamcback/domain/ble/service/BLEService.java new file mode 100644 index 00000000..a1bbc85c --- /dev/null +++ b/src/main/java/devkor/com/teamcback/domain/ble/service/BLEService.java @@ -0,0 +1,73 @@ +package devkor.com.teamcback.domain.ble.service; + + +import devkor.com.teamcback.domain.ble.dto.request.UpdateBLEReq; +import devkor.com.teamcback.domain.ble.dto.response.GetBLERes; +import devkor.com.teamcback.domain.ble.dto.response.UpdateBLERes; +import devkor.com.teamcback.domain.ble.entity.BLEData; +import devkor.com.teamcback.domain.ble.entity.BLEDevice; +import devkor.com.teamcback.domain.ble.entity.BLEstatus; +import devkor.com.teamcback.domain.ble.repository.BLEDataRepository; +import devkor.com.teamcback.domain.ble.repository.BLEDeviceRepository; +import devkor.com.teamcback.domain.place.entity.Place; +import devkor.com.teamcback.domain.place.repository.PlaceRepository; +import devkor.com.teamcback.global.exception.exception.GlobalException; +import devkor.com.teamcback.global.response.ResultCode; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.List; + +import static devkor.com.teamcback.global.response.ResultCode.NOT_FOUND_PLACE; + +@Slf4j +@Service +@RequiredArgsConstructor +public class BLEService { + private final BLEDeviceRepository bledeviceRepository; + private final BLEDataRepository bleDataRepository; + private final PlaceRepository placeRepository; + + @Transactional + public UpdateBLERes updateBLEDevice(UpdateBLEReq updateBLEReq) { + List deviceList = bledeviceRepository.findByDeviceName(updateBLEReq.getDeviceName()); + if (deviceList.isEmpty()) throw new GlobalException(ResultCode.NOT_FOUND_DEVICE_NAME); + BLEDevice bleDevice = deviceList.get(0); + double ratio = (double) updateBLEReq.getLastCount() /bleDevice.getCapacity(); + BLEstatus status; + if (ratio < 0.3) status = BLEstatus.VACANT; + else if (ratio < 0.7) status = BLEstatus.AVAILABLE; + else status = BLEstatus.CROWDED; + + BLEData bleData = new BLEData(); + bleData.setDevice(bleDevice); + bleData.setLastCount(updateBLEReq.getLastCount()); + bleData.setLastStatus(status); + bleData.setLastTime(updateBLEReq.getLastTime()); + bleDataRepository.save(bleData); + + return new UpdateBLERes(bleData); + } + + @Transactional(readOnly = true) + public GetBLERes getBLE(Long placeId) { + LocalDateTime now = LocalDateTime.now(); + Place place = placeRepository.findById(placeId).orElseThrow(() -> new GlobalException(NOT_FOUND_PLACE)); + List devices = bledeviceRepository.findByPlace(place); + if (devices.isEmpty()) throw new GlobalException(ResultCode.NOT_FOUND_DEVICE); + BLEDevice device = devices.get(0); + BLEData latest = bleDataRepository.findTopByDeviceOrderByLastTimeDesc(device).orElseThrow(() -> new GlobalException(ResultCode.NO_DATA_FOR_DEVICE)); + BLEstatus status; + if (latest.getLastTime() == null || + Duration.between(latest.getLastTime(), LocalDateTime.now()).toMinutes() >= 30) { + status = BLEstatus.FAILURE; + } + else status = latest.getLastStatus(); + return new GetBLERes(device, latest, status); + } + +} diff --git a/src/main/java/devkor/com/teamcback/global/response/ResultCode.java b/src/main/java/devkor/com/teamcback/global/response/ResultCode.java index bfe4899f..90c588d7 100644 --- a/src/main/java/devkor/com/teamcback/global/response/ResultCode.java +++ b/src/main/java/devkor/com/teamcback/global/response/ResultCode.java @@ -81,10 +81,16 @@ public enum ResultCode { NOT_FOUND_VOTE(HttpStatus.NOT_FOUND, 12000, "투표를 찾을 수 없습니다."), NOT_FOUND_VOTE_TOPIC(HttpStatus.NOT_FOUND, 12001, "투표 주제를 찾을 수 없습니다."), NOT_FOUND_VOTE_OPTION(HttpStatus.NOT_FOUND, 12002, "투표 항목을 찾을 수 없습니다."), - CLOSED_VOTE(HttpStatus.BAD_REQUEST, 12003, "종료된 투표입니다.") + CLOSED_VOTE(HttpStatus.BAD_REQUEST, 12003, "종료된 투표입니다."), + + //ble 13000번대 + NOT_FOUND_DEVICE(HttpStatus.NOT_FOUND, 13000, "해당 placeId에 해당하는 device가 없습니다."), + NOT_FOUND_DEVICE_NAME(HttpStatus.NOT_FOUND, 13001, "해당 device 이름이 없습니다."), + EXISTING_DEVICE_NAME(HttpStatus.CONFLICT, 13002, "중복되는 device 이름입니다."), + NO_DATA_FOR_DEVICE(HttpStatus.NOT_FOUND, 13003, "device에 해당하는 정보가 없습니다.") ; private final HttpStatus status; private final int code; private final String message; -} +} \ No newline at end of file