diff --git a/src/main/java/capstone/checkIT/DTO/LocationDTO/LocationRequestDTO.java b/src/main/java/capstone/checkIT/DTO/LocationDTO/LocationRequestDTO.java new file mode 100644 index 0000000..dfb0a37 --- /dev/null +++ b/src/main/java/capstone/checkIT/DTO/LocationDTO/LocationRequestDTO.java @@ -0,0 +1,19 @@ +package capstone.checkIT.DTO.LocationDTO; + + +import lombok.Getter; +import lombok.Setter; + +import java.math.BigDecimal; +import java.sql.Timestamp; + +@Getter +@Setter +public class LocationRequestDTO { + private Long id; //디바이스아이디 + private BigDecimal latitude; // 위도 + private BigDecimal longitude; // 경도 + private BigDecimal velocity; // 속도 + private Timestamp time; // 기록 시간 + private Timestamp startTime; // 시작 시간 +} \ No newline at end of file diff --git a/src/main/java/capstone/checkIT/DTO/LocationDTO/LocationResponseDTO.java b/src/main/java/capstone/checkIT/DTO/LocationDTO/LocationResponseDTO.java new file mode 100644 index 0000000..a03c6a3 --- /dev/null +++ b/src/main/java/capstone/checkIT/DTO/LocationDTO/LocationResponseDTO.java @@ -0,0 +1,22 @@ +package capstone.checkIT.DTO.LocationDTO; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.math.BigDecimal; +import java.sql.Timestamp; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class LocationResponseDTO { + private Long id; // Location ID + private BigDecimal latitude; // 위도 + private BigDecimal longitude; // 경도 + private BigDecimal velocity; // 속도 + private Timestamp time; // 기록 시간 + private Timestamp startTime; // 시작 시간 +} diff --git a/src/main/java/capstone/checkIT/apipayLoad/code/status/ErrorStatus.java b/src/main/java/capstone/checkIT/apipayLoad/code/status/ErrorStatus.java index c8157cf..2f79662 100644 --- a/src/main/java/capstone/checkIT/apipayLoad/code/status/ErrorStatus.java +++ b/src/main/java/capstone/checkIT/apipayLoad/code/status/ErrorStatus.java @@ -13,7 +13,7 @@ public enum ErrorStatus implements BaseCode { example(HttpStatus.BAD_REQUEST, "example4001", "예시 에러코드입니다."), - + // Member LOGIN_ERROR_EMAIL(HttpStatus.BAD_REQUEST, "LOGIN4001", "해당 사용자가 존재하지 않습니다."), MEMBER_DUPLICATE(HttpStatus.BAD_REQUEST, "LOGIN4002", "이미 로그인 하셨습니다."), LOGIN_ERROR_PW(HttpStatus.BAD_REQUEST, "LOGIN4003", "올바르지 않은 비밀번호입니다"), @@ -26,8 +26,13 @@ public enum ErrorStatus implements BaseCode { // DEVICE DEVICE_NOT_FOUND(HttpStatus.UNAUTHORIZED, "DEVICE4001", "해당 디바이스가 존재하지 않습니다."), - DEVICE_UNVALID(HttpStatus.UNAUTHORIZED, "DEVICE4002", "현재 접속중인 디바이스가 아닙니다."); + DEVICE_UNVALID(HttpStatus.UNAUTHORIZED, "DEVICE4002", "현재 접속중인 디바이스가 아닙니다."), + + + // Location + START_TIME_NOT_FOUND(HttpStatus.BAD_REQUEST, "STARTTIME4001", "해당 시작시간이 존재하지 않습니다."), + LOCATION_NOT_FOUND(HttpStatus.BAD_REQUEST, "LOCATION4001", "위치정보를 찾을 수 없습니다."); private final HttpStatus httpStatus; private final String code; diff --git a/src/main/java/capstone/checkIT/controller/DeviceLocationController.java b/src/main/java/capstone/checkIT/controller/DeviceLocationController.java index ef7b6b3..0c0bd3c 100644 --- a/src/main/java/capstone/checkIT/controller/DeviceLocationController.java +++ b/src/main/java/capstone/checkIT/controller/DeviceLocationController.java @@ -1,4 +1,32 @@ package capstone.checkIT.controller; +import capstone.checkIT.apipayLoad.ApiResponse; +import capstone.checkIT.config.JwtManager; +import capstone.checkIT.service.deviceLocationService.DeviceLocationService; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.sql.Timestamp; + +@RestController +@RequestMapping("/device-location") +@RequiredArgsConstructor public class DeviceLocationController { + + private final DeviceLocationService deviceLocationService; + private final JwtManager jwtManager; + + @PostMapping("/stop") + public ApiResponse stopLocation( + HttpServletRequest token, + @RequestParam Timestamp startTime) { + + String accessToken = jwtManager.getToken(token); + deviceLocationService.stopLocation(accessToken, startTime); + return ApiResponse.onSuccess("경로저장이 종료되었습니다."); + } } diff --git a/src/main/java/capstone/checkIT/controller/LocationController.java b/src/main/java/capstone/checkIT/controller/LocationController.java index 48b45ff..a94a2ee 100644 --- a/src/main/java/capstone/checkIT/controller/LocationController.java +++ b/src/main/java/capstone/checkIT/controller/LocationController.java @@ -1,4 +1,48 @@ package capstone.checkIT.controller; +import capstone.checkIT.DTO.LocationDTO.LocationRequestDTO; +import capstone.checkIT.DTO.LocationDTO.LocationResponseDTO; +import capstone.checkIT.apipayLoad.ApiResponse; +import capstone.checkIT.config.JwtManager; +import capstone.checkIT.service.deviceLocationService.DeviceLocationService; +import capstone.checkIT.service.locationService.LocationService; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/location") public class LocationController { -} + + private final LocationService locationService; + private final DeviceLocationService deviceLocationService; + private final JwtManager jwtManager; + + // 위치 데이터 저장 + @PostMapping + public ApiResponse saveLocation(HttpServletRequest token, @RequestBody LocationRequestDTO request) { + String accessToken = jwtManager.getToken(token); + log.info("Location save request received: {}", request); + locationService.saveLocation(accessToken, request); + log.info("Location data saved successfully."); + return ApiResponse.onSuccess("위치 저장 완료"); + } + + // 가장 최신 경로 반환 + @GetMapping("/latest/{deviceId}") + public ApiResponse> getLatestRoute( + HttpServletRequest token, + @PathVariable("deviceId") Long deviceId) { + String accessToken = jwtManager.getToken(token); + List response = locationService.getLatestRoute(accessToken, deviceId); + return ApiResponse.onSuccess(response); + } + + + +} \ No newline at end of file diff --git a/src/main/java/capstone/checkIT/entity/Location.java b/src/main/java/capstone/checkIT/entity/Location.java index 4a4cc50..fb8787d 100644 --- a/src/main/java/capstone/checkIT/entity/Location.java +++ b/src/main/java/capstone/checkIT/entity/Location.java @@ -7,7 +7,7 @@ import org.hibernate.annotations.DynamicUpdate; import java.math.BigDecimal; -import java.security.Timestamp; +import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; @@ -30,14 +30,14 @@ public class Location extends BaseEntity { @Column(nullable=false, precision=10, scale=5) private BigDecimal longitude; - @Column(nullable = false) - private Boolean isFinish; + /*@Column(nullable = false) + private Boolean isFinish;*/ @Column(nullable = false, precision = 5, scale = 2) private BigDecimal velocity; @Column - private Timestamp timestamp; + private Timestamp time; @Column private Timestamp startTime; diff --git a/src/main/java/capstone/checkIT/repository/DeviceLocationRepository.java b/src/main/java/capstone/checkIT/repository/DeviceLocationRepository.java index 4a5f432..0f5a39a 100644 --- a/src/main/java/capstone/checkIT/repository/DeviceLocationRepository.java +++ b/src/main/java/capstone/checkIT/repository/DeviceLocationRepository.java @@ -1,4 +1,17 @@ package capstone.checkIT.repository; -public interface DeviceLocationRepository { +import capstone.checkIT.entity.DeviceLocation; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.sql.Timestamp; + +public interface DeviceLocationRepository extends JpaRepository { + @Modifying + @Query("UPDATE DeviceLocation dl SET dl.isFinished = true " + + "WHERE dl.location.startTime = :startTime " + + "AND dl.device.member.id = :memberId") + int updateIsFinishedByStartTimeAndMemberId(@Param("startTime") Timestamp startTime, @Param("memberId") Long memberId); } diff --git a/src/main/java/capstone/checkIT/repository/LocationRepository.java b/src/main/java/capstone/checkIT/repository/LocationRepository.java index 88b8cdc..92a5751 100644 --- a/src/main/java/capstone/checkIT/repository/LocationRepository.java +++ b/src/main/java/capstone/checkIT/repository/LocationRepository.java @@ -1,4 +1,20 @@ package capstone.checkIT.repository; -public interface LocationRepository { +import capstone.checkIT.entity.Location; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.sql.Timestamp; +import java.util.List; +import java.util.Optional; + +public interface LocationRepository extends JpaRepository { + List findByStartTime(Timestamp startTime); + + // 최신 startTime 찾기 + @Query("SELECT MAX(l.startTime) FROM Location l WHERE l.id IN " + + "(SELECT dl.location.id FROM DeviceLocation dl WHERE dl.device.id = :deviceId)") + Optional findLatestStartTimeByDeviceId(@Param("deviceId") Long deviceId); + } diff --git a/src/main/java/capstone/checkIT/service/deviceLocationService/DeviceLocationService.java b/src/main/java/capstone/checkIT/service/deviceLocationService/DeviceLocationService.java index 1efb061..396abdf 100644 --- a/src/main/java/capstone/checkIT/service/deviceLocationService/DeviceLocationService.java +++ b/src/main/java/capstone/checkIT/service/deviceLocationService/DeviceLocationService.java @@ -1,4 +1,7 @@ package capstone.checkIT.service.deviceLocationService; +import java.sql.Timestamp; + public interface DeviceLocationService { + void stopLocation(String accessToken, Timestamp startTime); } diff --git a/src/main/java/capstone/checkIT/service/deviceLocationService/DeviceLocationServiceImpl.java b/src/main/java/capstone/checkIT/service/deviceLocationService/DeviceLocationServiceImpl.java index c5d06b7..ab267a5 100644 --- a/src/main/java/capstone/checkIT/service/deviceLocationService/DeviceLocationServiceImpl.java +++ b/src/main/java/capstone/checkIT/service/deviceLocationService/DeviceLocationServiceImpl.java @@ -1,9 +1,42 @@ package capstone.checkIT.service.deviceLocationService; +import capstone.checkIT.apipayLoad.code.status.ErrorStatus; +import capstone.checkIT.config.JwtManager; +import capstone.checkIT.entity.Location; +import capstone.checkIT.exception.GeneralException; +import capstone.checkIT.repository.DeviceLocationRepository; +import capstone.checkIT.repository.LocationRepository; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.sql.Timestamp; +import java.util.List; + +@Slf4j @Service @RequiredArgsConstructor -public class DeviceLocationServiceImpl implements DeviceLocationService { +public class DeviceLocationServiceImpl implements DeviceLocationService{ + + private final DeviceLocationRepository deviceLocationRepository; + private final LocationRepository locationRepository; + private final JwtManager jwtManager; + + public void stopLocation(String accessToken, Timestamp startTime) { + log.info("Stopping location tracking for startTime: {}", startTime); + + // 1. JWT 토큰에서 memberId 추출 + Long memberId = jwtManager.validateJwt(accessToken); + + // 2. 해당 memberId의 Location과 DeviceLocation 조회 + List locations = locationRepository.findByStartTime(startTime); + if (locations.isEmpty()) { + throw new GeneralException(ErrorStatus.START_TIME_NOT_FOUND); // 시작 시간에 해당하는 경로가 없는 경우 + } + + // 3. Location과 연결된 DeviceLocation의 isFinished 업데이트 + int updatedRows = deviceLocationRepository.updateIsFinishedByStartTimeAndMemberId(startTime, memberId); + + log.info("Updated {} rows for startTime: {}", updatedRows, startTime); + } } diff --git a/src/main/java/capstone/checkIT/service/deviceService/DeviceServiceImpl.java b/src/main/java/capstone/checkIT/service/deviceService/DeviceServiceImpl.java index 6f6b7af..c08aba4 100644 --- a/src/main/java/capstone/checkIT/service/deviceService/DeviceServiceImpl.java +++ b/src/main/java/capstone/checkIT/service/deviceService/DeviceServiceImpl.java @@ -11,7 +11,6 @@ import capstone.checkIT.repository.MemberRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; diff --git a/src/main/java/capstone/checkIT/service/locationService/LocationService.java b/src/main/java/capstone/checkIT/service/locationService/LocationService.java index 2c329d8..2fa3728 100644 --- a/src/main/java/capstone/checkIT/service/locationService/LocationService.java +++ b/src/main/java/capstone/checkIT/service/locationService/LocationService.java @@ -1,4 +1,12 @@ package capstone.checkIT.service.locationService; +import capstone.checkIT.DTO.LocationDTO.LocationRequestDTO; +import capstone.checkIT.DTO.LocationDTO.LocationResponseDTO; + +import java.util.List; + public interface LocationService { + void saveLocation(String accessToken, LocationRequestDTO request); + + List getLatestRoute(String accessToken, Long deviceId); } diff --git a/src/main/java/capstone/checkIT/service/locationService/LocationServiceImpl.java b/src/main/java/capstone/checkIT/service/locationService/LocationServiceImpl.java index 17e8280..13cd1f6 100644 --- a/src/main/java/capstone/checkIT/service/locationService/LocationServiceImpl.java +++ b/src/main/java/capstone/checkIT/service/locationService/LocationServiceImpl.java @@ -1,9 +1,118 @@ package capstone.checkIT.service.locationService; +import capstone.checkIT.apipayLoad.code.status.ErrorStatus; +import capstone.checkIT.config.JwtManager; +import capstone.checkIT.entity.Device; +import capstone.checkIT.entity.DeviceLocation; +import capstone.checkIT.entity.Location; +import capstone.checkIT.entity.Member; +import capstone.checkIT.exception.GeneralException; +import capstone.checkIT.repository.DeviceLocationRepository; +import capstone.checkIT.repository.DeviceRepository; +import capstone.checkIT.repository.LocationRepository; +import capstone.checkIT.DTO.LocationDTO.*; +import capstone.checkIT.repository.MemberRepository; +import capstone.checkIT.service.deviceService.DeviceService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import lombok.extern.slf4j.Slf4j; +import java.sql.Timestamp; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j @Service @RequiredArgsConstructor public class LocationServiceImpl implements LocationService { + private final LocationRepository locationRepository; + private final JwtManager jwtManager; + private final MemberRepository memberRepository; + private final DeviceService deviceService; + private final DeviceRepository deviceRepository; + private final DeviceLocationRepository deviceLocationRepository; + + // 위치 데이터 저장 로직 + @Override + public void saveLocation(String accessToken, LocationRequestDTO request) { + log.info("Saving location: {}", request); + + // 토큰에서 memberId 추출 + Long memberId = jwtManager.validateJwt(accessToken); + + // 멤버 검증 + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new GeneralException(ErrorStatus.LOGIN_ERROR_EMAIL)); + + // 디바이스 검증 및 조회 + Device device = deviceRepository.findById(request.getId()) + .orElseThrow(() -> new GeneralException(ErrorStatus.DEVICE_NOT_FOUND)); + + // 디바이스 멤버 검증 + if (!device.getMember().getId().equals(memberId)) { + throw new GeneralException(ErrorStatus.DEVICE_UNVALID); // 디바이스가 멤버 소유가 아닌 경우 + } + + // DTO → Entity 변환 + Location location = Location.builder() + .latitude(request.getLatitude()) + .longitude(request.getLongitude()) + .velocity(request.getVelocity()) + .time(request.getTime()) + .startTime(request.getStartTime()) + .build(); + + // Device와 Location의 연관 관계 설정 + DeviceLocation deviceLocation = DeviceLocation.builder() + .device(device) + .location(location) + .isFinished(false) // 초기값은 false로 설정 stop누르면 true 변경 + .build(); + + // 데이터 저장 + locationRepository.save(location); + deviceLocationRepository.save(deviceLocation); + + log.info("Location data saved successfully for deviceId: {}", request.getId()); + } + + + // 최신경로 반환 서비스 + public List getLatestRoute(String accessToken, Long deviceId) { + log.info("Fetching latest route for deviceId: {}", deviceId); + + // 1. JWT 토큰에서 memberId 추출 및 검증 + Long memberId = jwtManager.validateJwt(accessToken); + + // 2. 디바이스 검증 + Device device = deviceRepository.findById(deviceId) + .orElseThrow(() -> new GeneralException(ErrorStatus.DEVICE_NOT_FOUND)); + + // 3. 디바이스 소유자 검증 + if(!deviceService.isOwner(accessToken, memberId)){ + throw new GeneralException(ErrorStatus.DEVICE_UNVALID); + } + + // 4. 가장 최신 startTime 조회 + Timestamp latestStartTime = locationRepository.findLatestStartTimeByDeviceId(deviceId) + .orElseThrow(() -> new GeneralException(ErrorStatus.LOCATION_NOT_FOUND)); + + log.info("Latest startTime found: {}", latestStartTime); + + // 5. 최신 startTime과 관련된 Location 리스트 조회 + List locations = locationRepository.findByStartTime(latestStartTime); + + // 6. Location → LocationResponseDTO 변환 및 반환 + return locations.stream() + .map(location -> new LocationResponseDTO( + location.getId(), + location.getLatitude(), + location.getLongitude(), + location.getVelocity(), + location.getTime(), + location.getStartTime() + )) + .collect(Collectors.toList()); + } } +