diff --git a/build.gradle b/build.gradle index 512da2d..fee5a8a 100644 --- a/build.gradle +++ b/build.gradle @@ -65,6 +65,8 @@ dependencies { implementation 'org.springframework.cloud:spring-cloud-starter-config' //애플 인증 서버에 JSON Web Key(JWK)를 가져올 때 사용 implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' + + implementation 'org.hibernate:hibernate-spatial:6.4.4.Final' } tasks.named('test') { diff --git a/src/main/java/com/example/nzgeneration/domain/auth/AuthController.java b/src/main/java/com/example/nzgeneration/domain/auth/AuthController.java index 0d47481..5b5a545 100644 --- a/src/main/java/com/example/nzgeneration/domain/auth/AuthController.java +++ b/src/main/java/com/example/nzgeneration/domain/auth/AuthController.java @@ -8,6 +8,8 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -44,4 +46,10 @@ public ApiResponse refreshToken(@RequestParam String ref return ApiResponse.onSuccess(authService.updateUserToken(refreshToken)); } + @GetMapping("/check-nickname/{nickname}") + @Operation(summary = "닉네임 중복 확인", description = "true : 사용가능한 닉네임, false : 사용 불가능한 닉네임") + public ApiResponse checkNickName(@PathVariable String nickName){ + boolean status = authService.checkNickNameDuplicate(nickName); + return ApiResponse.onSuccess(status); + } } diff --git a/src/main/java/com/example/nzgeneration/domain/auth/AuthService.java b/src/main/java/com/example/nzgeneration/domain/auth/AuthService.java index 66cab41..830848b 100644 --- a/src/main/java/com/example/nzgeneration/domain/auth/AuthService.java +++ b/src/main/java/com/example/nzgeneration/domain/auth/AuthService.java @@ -65,5 +65,11 @@ public TokenRefreshSimpleInfo updateUserToken(String refreshToken) { } - -} + @Transactional + public boolean checkNickNameDuplicate(String name){ + if(userRepository.findByNickname(name).isPresent()){ + return false; + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/nzgeneration/domain/errorreport/ErrorReport.java b/src/main/java/com/example/nzgeneration/domain/errorreport/ErrorReport.java deleted file mode 100644 index b93898b..0000000 --- a/src/main/java/com/example/nzgeneration/domain/errorreport/ErrorReport.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.example.nzgeneration.domain.errorreport; - -import com.example.nzgeneration.global.utils.BaseTimeEntity; -import com.example.nzgeneration.domain.trashcan.Trashcan; -import com.example.nzgeneration.domain.user.User; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.ManyToOne; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.NoArgsConstructor; - -@Entity -@AllArgsConstructor -@NoArgsConstructor -@Builder -public class ErrorReport extends BaseTimeEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne - private User errorReportUser; - - private String content; - - @ManyToOne - private Trashcan trashcan; -} \ No newline at end of file diff --git a/src/main/java/com/example/nzgeneration/domain/trashcan/TrashCategory.java b/src/main/java/com/example/nzgeneration/domain/trashcan/TrashCategory.java index d368c99..959d86b 100644 --- a/src/main/java/com/example/nzgeneration/domain/trashcan/TrashCategory.java +++ b/src/main/java/com/example/nzgeneration/domain/trashcan/TrashCategory.java @@ -1,5 +1,5 @@ package com.example.nzgeneration.domain.trashcan; public enum TrashCategory { - PLASTIC, PAPER, GENERAL + PLASTIC, PAPER, GENERAL, ALL } \ No newline at end of file diff --git a/src/main/java/com/example/nzgeneration/domain/trashcan/Trashcan.java b/src/main/java/com/example/nzgeneration/domain/trashcan/Trashcan.java index 7c59ee6..a335ecf 100644 --- a/src/main/java/com/example/nzgeneration/domain/trashcan/Trashcan.java +++ b/src/main/java/com/example/nzgeneration/domain/trashcan/Trashcan.java @@ -1,6 +1,7 @@ package com.example.nzgeneration.domain.trashcan; import com.example.nzgeneration.global.utils.BaseTimeEntity; +import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; @@ -12,6 +13,7 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import org.locationtech.jts.geom.Point; @Entity @Getter @@ -24,8 +26,13 @@ public class Trashcan extends BaseTimeEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column(name = "trash_category") @Enumerated(EnumType.STRING) private TrashCategory trashCategory; + @Column(name = "representative_image_url") private String representativeImageUrl; + + @Column(name = "trashcan_point") + private Point trashcanPoint; } \ No newline at end of file diff --git a/src/main/java/com/example/nzgeneration/domain/trashcan/TrashcanController.java b/src/main/java/com/example/nzgeneration/domain/trashcan/TrashcanController.java index ca85203..8e242da 100644 --- a/src/main/java/com/example/nzgeneration/domain/trashcan/TrashcanController.java +++ b/src/main/java/com/example/nzgeneration/domain/trashcan/TrashcanController.java @@ -1,21 +1,80 @@ package com.example.nzgeneration.domain.trashcan; +import com.example.nzgeneration.domain.trashcan.dto.TrashcanRequestDto.GetTrashcansRequest; import com.example.nzgeneration.domain.trashcan.dto.TrashcanResponseDto.GetTrashcanResponse; +import com.example.nzgeneration.domain.trashcan.dto.TrashcanResponseDto.GetTrashcanResponses; +import com.example.nzgeneration.domain.user.User; +import com.example.nzgeneration.domain.user.UserService; import com.example.nzgeneration.global.common.response.ApiResponse; +import com.example.nzgeneration.global.common.response.code.status.ErrorStatus; +import com.example.nzgeneration.global.common.response.exception.GeneralException; +import com.example.nzgeneration.global.security.CurrentUser; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; import lombok.RequiredArgsConstructor; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Polygon; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequiredArgsConstructor +@Tag(name = "쓰레기통 조회", description = "쓰레기통 조회 API") public class TrashcanController { private final TrashcanService trashcanService; + private final UserService userService; - @GetMapping + @GetMapping("api/trashcan/{id}") + @Operation( + summary = "쓰레기통 상세 조회" + ) public ApiResponse getTrashcan(@PathVariable Long id) { return ApiResponse.onSuccess(trashcanService.getTrashcan(id)); } + @PostMapping("api/trashcans/") + @Operation( + summary = "영역 내 쓰레기통 조회", + description = "영역의 네 좌표값으로 영역 내 거점을 조회.
영역의 좌표값은 위도와 경도
카테고리는 all할 경우 전체 조회" + ) + public ApiResponse getTrashcans( + @RequestBody GetTrashcansRequest request, @RequestParam TrashCategory trashCategory) { + + //다각형 객체 초기화 + Polygon polygon = null; + + try { + GeometryFactory geometryFactory = new GeometryFactory(); + //request 객체에서 좌표를 가져와 Coordinate객체 리스트를 생성 + List coordinatesPolygon = List.of( + new Coordinate(request.getTopLeftPoint().getX(), request.getTopLeftPoint().getY()), + new Coordinate(request.getBottomLeftPoint().getX(), request.getBottomLeftPoint().getY()), + new Coordinate(request.getBottomRightPoint().getX(), request.getBottomRightPoint().getY()), + new Coordinate(request.getTopRightPoint().getX(), request.getTopRightPoint().getY()), + new Coordinate(request.getTopLeftPoint().getX(), request.getTopLeftPoint().getY()) + ); + polygon = geometryFactory.createPolygon(coordinatesPolygon.toArray(new Coordinate[0])); + } catch (NullPointerException e) { + throw new GeneralException(ErrorStatus._TRASHCAN_COORDINATE_INVALID); + } catch (IllegalArgumentException e) { + throw new GeneralException(ErrorStatus._TRASHCAN_POLYGON_INVALID); + } + return ApiResponse.onSuccess(trashcanService.getTrashcans(polygon, trashCategory)); + } + + @Operation( + summary = "쓰레기통 스탬프 찍기" + ) + @PostMapping("api/user/trashcan-stamp") + public ApiResponse addStamp(@CurrentUser User user) { + userService.addStamp(user); + return ApiResponse.onSuccess("스탬프 찍기 성공"); + } } \ No newline at end of file diff --git a/src/main/java/com/example/nzgeneration/domain/trashcan/TrashcanRepository.java b/src/main/java/com/example/nzgeneration/domain/trashcan/TrashcanRepository.java index 0f84d3a..55a769e 100644 --- a/src/main/java/com/example/nzgeneration/domain/trashcan/TrashcanRepository.java +++ b/src/main/java/com/example/nzgeneration/domain/trashcan/TrashcanRepository.java @@ -1,7 +1,17 @@ package com.example.nzgeneration.domain.trashcan; +import java.util.List; +import org.locationtech.jts.geom.Polygon; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; public interface TrashcanRepository extends JpaRepository { -} + //polygon안에 trashcanPoint가 포함되는지 확인 + @Query(value = "SELECT tc FROM Trashcan tc WHERE ST_CONTAINS(:polygon, tc.trashcanPoint) = true AND tc.trashCategory = :trashCategory") + List findTrashcansByPolygonAndTrashCategory(@Param("polygon") Polygon polygon, @Param("trashCategory") TrashCategory trashCategory); + + @Query(value = "SELECT tc FROM Trashcan tc WHERE ST_CONTAINS(:polygon, tc.trashcanPoint)") + List findTrashcansByPolygon(@Param("polygon") Polygon polygon); +} \ No newline at end of file diff --git a/src/main/java/com/example/nzgeneration/domain/trashcan/TrashcanService.java b/src/main/java/com/example/nzgeneration/domain/trashcan/TrashcanService.java index 68292b9..11bfe84 100644 --- a/src/main/java/com/example/nzgeneration/domain/trashcan/TrashcanService.java +++ b/src/main/java/com/example/nzgeneration/domain/trashcan/TrashcanService.java @@ -1,9 +1,13 @@ package com.example.nzgeneration.domain.trashcan; import com.example.nzgeneration.domain.trashcan.dto.TrashcanResponseDto.GetTrashcanResponse; +import com.example.nzgeneration.domain.trashcan.dto.TrashcanResponseDto.GetTrashcanResponses; import com.example.nzgeneration.global.common.response.code.status.ErrorStatus; import com.example.nzgeneration.global.common.response.exception.GeneralException; +import java.util.List; import lombok.RequiredArgsConstructor; +import org.locationtech.jts.geom.Polygon; +import org.springframework.data.geo.Point; import org.springframework.stereotype.Service; @Service @@ -21,4 +25,28 @@ public GetTrashcanResponse getTrashcan(Long id) { .imageUrl(trashcan.getRepresentativeImageUrl()) .build(); } + + /** + * 원형 거점 내 쓰레기통 카테고리 별 조회 + */ + public GetTrashcanResponses getTrashcans(Polygon polygon, TrashCategory trashCategory) { + List trashcans = null; + + //전체 조회 + if(trashCategory.equals(TrashCategory.ALL)) { + trashcans = trashcanRepository.findTrashcansByPolygon(polygon); + } + //카테고리별 조회 + else { + trashcans = trashcanRepository.findTrashcansByPolygonAndTrashCategory(polygon, trashCategory); + } + List trashcanResponses = trashcans.stream().map(trashcan -> + GetTrashcanResponse.builder() + .id(trashcan.getId()) + .trashcanPoint(new Point(trashcan.getTrashcanPoint().getX(), trashcan.getTrashcanPoint().getY())) + .imageUrl(trashcan.getRepresentativeImageUrl()) + .build() + ).toList(); + return new GetTrashcanResponses(trashcanResponses); + } } \ No newline at end of file diff --git a/src/main/java/com/example/nzgeneration/domain/trashcan/dto/TrashcanRequestDto.java b/src/main/java/com/example/nzgeneration/domain/trashcan/dto/TrashcanRequestDto.java index ce83da0..5d56df4 100644 --- a/src/main/java/com/example/nzgeneration/domain/trashcan/dto/TrashcanRequestDto.java +++ b/src/main/java/com/example/nzgeneration/domain/trashcan/dto/TrashcanRequestDto.java @@ -1,6 +1,22 @@ package com.example.nzgeneration.domain.trashcan.dto; -public class TrashcanRequestDto { +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.data.geo.Point; +public class TrashcanRequestDto { + @Getter + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class GetTrashcansRequest { + private Point currentPoint; + private Point topLeftPoint; + private Point topRightPoint; + private Point bottomRightPoint; + private Point bottomLeftPoint; + } } \ No newline at end of file diff --git a/src/main/java/com/example/nzgeneration/domain/trashcan/dto/TrashcanResponseDto.java b/src/main/java/com/example/nzgeneration/domain/trashcan/dto/TrashcanResponseDto.java index 207b5ce..a131f5b 100644 --- a/src/main/java/com/example/nzgeneration/domain/trashcan/dto/TrashcanResponseDto.java +++ b/src/main/java/com/example/nzgeneration/domain/trashcan/dto/TrashcanResponseDto.java @@ -1,10 +1,11 @@ package com.example.nzgeneration.domain.trashcan.dto; -import com.example.nzgeneration.domain.trashcan.TrashCategory; +import java.util.List; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import org.springframework.data.geo.Point; public class TrashcanResponseDto { @@ -13,7 +14,17 @@ public class TrashcanResponseDto { @NoArgsConstructor @AllArgsConstructor public static class GetTrashcanResponse { - String trashCategory; - String imageUrl; + private Long id; + private String trashCategory; + private String imageUrl; + private Point trashcanPoint; + } + + @Getter + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class GetTrashcanResponses { + List trashcans; } } \ No newline at end of file diff --git a/src/main/java/com/example/nzgeneration/domain/trashcanerrorreport/TrashcanErrorReportController.java b/src/main/java/com/example/nzgeneration/domain/trashcanerrorreport/TrashcanErrorReportController.java index a91fef0..1f409ab 100644 --- a/src/main/java/com/example/nzgeneration/domain/trashcanerrorreport/TrashcanErrorReportController.java +++ b/src/main/java/com/example/nzgeneration/domain/trashcanerrorreport/TrashcanErrorReportController.java @@ -1,19 +1,24 @@ package com.example.nzgeneration.domain.trashcanerrorreport; import com.example.nzgeneration.global.common.response.ApiResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController @RequiredArgsConstructor +@Tag(name = "쓰레기통 오류 신고", description = "쓰레기통 오류 신고 API") public class TrashcanErrorReportController { private final TrashcanErrorReportService trashcanErrorReportService; - @PostMapping("trashcan-error/{trashcan-id}") + @PostMapping("/api/trashcan-error/{trashcan-id}") + @Operation( + summary = "쓰레기통 오류 신고" + ) public ApiResponse addError(@PathVariable(value = "trashcan-id") Long id) { return ApiResponse.onSuccess(trashcanErrorReportService.addError(id)); } diff --git a/src/main/java/com/example/nzgeneration/domain/trashcanreport/TrashcanReportController.java b/src/main/java/com/example/nzgeneration/domain/trashcanreport/TrashcanReportController.java index 2ca1e86..30b8c7d 100644 --- a/src/main/java/com/example/nzgeneration/domain/trashcanreport/TrashcanReportController.java +++ b/src/main/java/com/example/nzgeneration/domain/trashcanreport/TrashcanReportController.java @@ -3,7 +3,6 @@ import com.example.nzgeneration.domain.trashcanreport.dto.TrashcanReportRequestDto.AddTrashcanReportRequest; import com.example.nzgeneration.domain.user.User; import com.example.nzgeneration.global.common.response.ApiResponse; -import com.example.nzgeneration.global.security.CurrentUser; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -15,7 +14,7 @@ public class TrashcanReportController { private final TrashcanReportService trashcanReportService; - @PostMapping("trashcan") + @PostMapping("/api/trashcan") public ApiResponse addTrashcanReport( @RequestBody AddTrashcanReportRequest addTrashcanReportRequest) { trashcanReportService.addTrashcanReport(addTrashcanReportRequest.getMapX(), diff --git a/src/main/java/com/example/nzgeneration/domain/user/UserController.java b/src/main/java/com/example/nzgeneration/domain/user/UserController.java index 1212204..a63afc0 100644 --- a/src/main/java/com/example/nzgeneration/domain/user/UserController.java +++ b/src/main/java/com/example/nzgeneration/domain/user/UserController.java @@ -7,6 +7,7 @@ import com.example.nzgeneration.global.common.response.ApiResponse; import com.example.nzgeneration.global.security.CurrentUser; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; @@ -20,20 +21,10 @@ @RestController @RequiredArgsConstructor @RequestMapping("/api/user") +@Tag(name = "유저 관련", description = "유저 관련 API") public class UserController { private final UserService userService; - @PostMapping("/stamp") - public ApiResponse addStamp(@CurrentUser User user) { - userService.addStamp(user); - return ApiResponse.onSuccess("스탬프 찍기 성공"); - } - @GetMapping("/check-nickname/{nickName}") - @Operation(summary = "닉네임 중복 확인", description = "true : 사용가능한 닉네임, false : 사용 불가능한 닉네임") - public ApiResponse checkNickName(@PathVariable String nickName){ - boolean status = userService.checkNickNameDuplicate(nickName); - return ApiResponse.onSuccess(status); - } @PatchMapping("/nickname/{nickName}") @Operation(summary = "닉네임 수정") public ApiResponse updateNickName(@CurrentUser User user, @PathVariable String nickName){ diff --git a/src/main/java/com/example/nzgeneration/domain/user/UserService.java b/src/main/java/com/example/nzgeneration/domain/user/UserService.java index 4505749..5c98481 100644 --- a/src/main/java/com/example/nzgeneration/domain/user/UserService.java +++ b/src/main/java/com/example/nzgeneration/domain/user/UserService.java @@ -1,5 +1,6 @@ package com.example.nzgeneration.domain.user; +import com.example.nzgeneration.domain.auth.AuthService; import com.example.nzgeneration.domain.user.dto.UserResponseDto.UserEditingPageDetailInfo; import com.example.nzgeneration.domain.user.dto.UserResponseDto.UserMyPageDetailInfo; import com.example.nzgeneration.domain.user.dto.UserResponseDto.UserSigningSimpleInfo; @@ -19,6 +20,7 @@ public class UserService { private final UserRepository userRepository; private final S3Service s3Service; + private final AuthService authService; @Transactional public void addStamp(User user) { @@ -30,17 +32,9 @@ public void addStamp(User user) { user.stamp(20); } - @Transactional - public boolean checkNickNameDuplicate(String name){ - if(userRepository.findByNickname(name).isPresent()){ - return false; - } - return true; - } - @Transactional public void updateNickName(User user, String name){ - if(!checkNickNameDuplicate(name)){ + if(!authService.checkNickNameDuplicate(name)){ throw new GeneralException(ErrorStatus._DUPLICATE_NICKNAME); } user.updateNickName(name); diff --git a/src/main/java/com/example/nzgeneration/global/common/response/code/status/ErrorStatus.java b/src/main/java/com/example/nzgeneration/global/common/response/code/status/ErrorStatus.java index f02771a..6f86427 100644 --- a/src/main/java/com/example/nzgeneration/global/common/response/code/status/ErrorStatus.java +++ b/src/main/java/com/example/nzgeneration/global/common/response/code/status/ErrorStatus.java @@ -34,7 +34,10 @@ public enum ErrorStatus implements BaseErrorCode { _FILE_UPLOAD_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, 4031, "파일 업로드에 실패했습니다."), //쓰레기통 관련 - _EMPTY_TRASHCAN(HttpStatus.CONFLICT, 4041, "존재하지 않는 쓰레기통입니다.") + _EMPTY_TRASHCAN(HttpStatus.CONFLICT, 4041, "존재하지 않는 쓰레기통입니다."), + _TRASHCAN_COORDINATE_INVALID(HttpStatus.CONFLICT, 4042, "쓰레기통 좌표가 유효하지 않습니다."), + _TRASHCAN_POLYGON_INVALID(HttpStatus.CONFLICT, 4043, "영역이 잘못되었습니다."), + ;