diff --git a/src/main/java/org/ioteatime/meonghanyangserver/cctv/repository/CctvRepository.java b/src/main/java/org/ioteatime/meonghanyangserver/cctv/repository/CctvRepository.java index dfe59d65..feb78377 100644 --- a/src/main/java/org/ioteatime/meonghanyangserver/cctv/repository/CctvRepository.java +++ b/src/main/java/org/ioteatime/meonghanyangserver/cctv/repository/CctvRepository.java @@ -19,7 +19,9 @@ public interface CctvRepository { void deleteByGroupId(Long groupId); - Optional findByCctvId(Long thingId); + Optional findByCctvId(Long cctvId); List findByGroupId(Long groupId); + + Optional findByThingId(String thingId); } diff --git a/src/main/java/org/ioteatime/meonghanyangserver/cctv/repository/CctvRepositoryImpl.java b/src/main/java/org/ioteatime/meonghanyangserver/cctv/repository/CctvRepositoryImpl.java index 6ed29fc3..18935521 100644 --- a/src/main/java/org/ioteatime/meonghanyangserver/cctv/repository/CctvRepositoryImpl.java +++ b/src/main/java/org/ioteatime/meonghanyangserver/cctv/repository/CctvRepositoryImpl.java @@ -1,6 +1,7 @@ package org.ioteatime.meonghanyangserver.cctv.repository; import static org.ioteatime.meonghanyangserver.cctv.domain.QCctvEntity.cctvEntity; +import static org.ioteatime.meonghanyangserver.group.domain.QGroupEntity.groupEntity; import com.querydsl.core.types.Projections; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -83,4 +84,15 @@ public List findByGroupId(Long groupId) { return result; } + + @Override + public Optional findByThingId(String thingId) { + return Optional.ofNullable( + jpaQueryFactory + .selectFrom(cctvEntity) + .join(cctvEntity.group, groupEntity) + .fetchJoin() + .where(cctvEntity.thingId.eq(thingId)) + .fetchOne()); + } } diff --git a/src/main/java/org/ioteatime/meonghanyangserver/clients/iot/AIDetectClient.java b/src/main/java/org/ioteatime/meonghanyangserver/clients/iot/AIDetectClient.java new file mode 100644 index 00000000..6292167c --- /dev/null +++ b/src/main/java/org/ioteatime/meonghanyangserver/clients/iot/AIDetectClient.java @@ -0,0 +1,57 @@ +package org.ioteatime.meonghanyangserver.clients.iot; + +import java.util.Arrays; +import java.util.Optional; +import lombok.extern.slf4j.Slf4j; +import org.ioteatime.meonghanyangserver.cctv.domain.CctvEntity; +import org.ioteatime.meonghanyangserver.cctv.repository.CctvRepository; +import org.ioteatime.meonghanyangserver.clients.google.FcmClient; +import org.ioteatime.meonghanyangserver.common.exception.BadRequestException; +import org.ioteatime.meonghanyangserver.common.type.IoTErrorType; +import org.springframework.stereotype.Component; +import software.amazon.awssdk.crt.mqtt.MqttMessage; + +// 객체 탐지 클라이언트 +@Slf4j +@Component +public class AIDetectClient { + private final FcmClient fcmClient; + private final IotMqttClient iotMqttClient; + private final CctvRepository cctvRepository; + + public AIDetectClient( + FcmClient fcmClient, IotMqttClient iotMqttClient, CctvRepository cctvRepository) { + this.fcmClient = fcmClient; + this.iotMqttClient = iotMqttClient; + this.cctvRepository = cctvRepository; + + iotMqttClient.subscribe("/mhn/event/detect/things/#", this::handleDetectEvent); + iotMqttClient.subscribe("/mhn/event/detect/things/+", this::handleDetectEvent); + log.info("[객체 탐지] {}", "탐지 Topic을 구독하였습니다."); + } + + private void handleDetectEvent(MqttMessage mqttMessage) { + log.info("[객체 탐지] {}", "탐지 Topic이 발행되었습니다."); + // thingId 기준 GroupId 검색 + if (mqttMessage.getTopic() == null || mqttMessage.getTopic().isEmpty()) { + throw new BadRequestException(IoTErrorType.TOPIC_NULL); + } + Optional cctvEntity = + cctvRepository.findByThingId(getThingIdFromTopic(mqttMessage)); + if (cctvEntity.isPresent()) { + CctvEntity cctv = cctvEntity.get(); + fcmClient.sendPush( + "뽀삐가 나타났어요!", + Arrays.toString(mqttMessage.getPayload()), + cctv.getGroup().getFcmTopic()); + log.info( + "[객체 탐지 알림] {}", + cctv.getGroup().getGroupName() + " 그룹 객체 탐지 푸시 알림 전송에 성공하였습니다."); + } + } + + private static String getThingIdFromTopic(MqttMessage mqttMessage) { + String topic = mqttMessage.getTopic(); + return topic.substring(topic.lastIndexOf('/') + 1); + } +} diff --git a/src/main/java/org/ioteatime/meonghanyangserver/common/type/IoTErrorType.java b/src/main/java/org/ioteatime/meonghanyangserver/common/type/IoTErrorType.java index 3771888a..bee7708c 100644 --- a/src/main/java/org/ioteatime/meonghanyangserver/common/type/IoTErrorType.java +++ b/src/main/java/org/ioteatime/meonghanyangserver/common/type/IoTErrorType.java @@ -1,7 +1,8 @@ package org.ioteatime.meonghanyangserver.common.type; public enum IoTErrorType implements ErrorTypeCode { - UPDATE_SHADOW("INTERNAL_SERVER", "Shadow 갱신에 실패하였습니다."); + UPDATE_SHADOW("INTERNAL_SERVER", "Shadow 갱신에 실패하였습니다."), + TOPIC_NULL("BAD_REQUEST", "Topic이 Null입니다."); private final String message; private final String description;