diff --git a/src/main/java/com/olive/pribee/module/feed/controller/FbPostController.java b/src/main/java/com/olive/pribee/module/feed/controller/FbPostController.java index 98a0e54..af5238e 100644 --- a/src/main/java/com/olive/pribee/module/feed/controller/FbPostController.java +++ b/src/main/java/com/olive/pribee/module/feed/controller/FbPostController.java @@ -3,6 +3,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @@ -12,6 +13,7 @@ import com.olive.pribee.global.common.ResponseDto; import com.olive.pribee.global.enums.DetectKeyword; import com.olive.pribee.module.auth.domain.entity.Member; +import com.olive.pribee.module.feed.dto.res.FbPostDetailRes; import com.olive.pribee.module.feed.dto.res.FbPostTotalRes; import com.olive.pribee.module.feed.service.FbPostService; @@ -25,17 +27,24 @@ public class FbPostController implements FbPostControllerDocs { private final FbPostService fbPostService; // 개별 게시물 조회 + @GetMapping("/{id}") + public ResponseEntity getDetailPost(@AuthenticationPrincipal Member member, + @PathVariable(name = "id") Long postId) { + FbPostDetailRes res = fbPostService.getDetailPost(member.getId(), postId); + return ResponseEntity.status(200).body(DataResponseDto.of(res, 200)); + + } // 전체 게시물 조회 @GetMapping - public ResponseEntity getExhibitions( + public ResponseEntity getTotalPost( @AuthenticationPrincipal Member member, @Schema(description = "필터를 의미합니다.") @RequestParam(name = "detectType") @Nullable DetectKeyword detectType, @Schema(description = "검색어를 의미합니다.") @RequestParam(name = "keyword") @Nullable String keyword, @Schema(description = "0번부터 시작합니다. 조회할 페이지 번호를 의미합니다.") @RequestParam(name = "page") int page, @Schema(description = "조회할 페이지 크기를 의미합니다.") @RequestParam(name = "size") int size) { - FbPostTotalRes resPage = fbPostService.getTotalPost(member.getId(), detectType, keyword, page, size); - return ResponseEntity.status(200).body(DataResponseDto.of(resPage, 200)); + FbPostTotalRes resDto = fbPostService.getTotalPost(member.getId(), detectType, keyword, page, size); + return ResponseEntity.status(200).body(DataResponseDto.of(resDto, 200)); } // 게시물 첨부 조회 diff --git a/src/main/java/com/olive/pribee/module/feed/controller/FbPostControllerDocs.java b/src/main/java/com/olive/pribee/module/feed/controller/FbPostControllerDocs.java index f803476..96d1c54 100644 --- a/src/main/java/com/olive/pribee/module/feed/controller/FbPostControllerDocs.java +++ b/src/main/java/com/olive/pribee/module/feed/controller/FbPostControllerDocs.java @@ -16,7 +16,150 @@ @Tag(name = "Feed", description = "게시물 관련 API") public interface FbPostControllerDocs { - @Operation(summary = "게시물 조회", description = "전체 게시물을 조회합니다.") + + @Operation(summary = "게시물 상세 조회", description = "게시물 ID 기반 상세 게시물을 조회합니다.") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "Ok", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ResponseDto.class), + examples = + @ExampleObject(value = "{\n" + + " \"code\": 200,\n" + + " \"message\": \"OK\",\n" + + " \"data\": {\n" + + " \"createdTime\": \"2025-03-06T14:35:45\",\n" + + " \"safeMessage\": null,\n" + + " \"message\": \"2025년 2월 27일 ~ 3월 2일 대만 여행✈\\n\\n오랜만에 해외여행! 겨울이 끝나갈 즈음, 따뜻한 나라로 떠나고 싶어서 친구..." + + " \"permalinkUrl\": \"https://www.facebook.com/122100024608769823/posts/122110815812769823\",\n" + + " \"messageDetectRes\": [\n" + + " {\n" + + " \"detectWord\": \"오랜만\",\n" + + " \"keyword\": \"LOCATION\",\n" + + " \"likelihood\": \"LIKELY\",\n" + + " \"startAt\": 29,\n" + + " \"endAt\": 32\n" + + " },\n" + + " {\n" + + " \"detectWord\": \"M12345678\",\n" + + " \"keyword\": \"KOREA_PASSPORT\",\n" + + " \"likelihood\": \"POSSIBLE\",\n" + + " \"startAt\": 160,\n" + + " \"endAt\": 169\n" + + " },\n" + + " {\n" + + " \"detectWord\": \"타이베이\",\n" + + " \"keyword\": \"LOCATION\",\n" + + " \"likelihood\": \"LIKELY\",\n" + + " \"startAt\": 256,\n" + + " \"endAt\": 260\n" + + " },\n" + + " {\n" + + " \"detectWord\": \"금광 마을\",\n" + + " \"keyword\": \"LOCATION\",\n" + + " \"likelihood\": \"LIKELY\",\n" + + " \"startAt\": 626,\n" + + " \"endAt\": 631\n" + + " },\n" + + " {\n" + + " \"detectWord\": \"센\",\n" + + " \"keyword\": \"PERSON_NAME\",\n" + + " \"likelihood\": \"POSSIBLE\",\n" + + " \"startAt\": 674,\n" + + " \"endAt\": 675\n" + + " },\n" + + " {\n" + + " \"detectWord\": \"치히로\",\n" + + " \"keyword\": \"PERSON_NAME\",\n" + + " \"likelihood\": \"POSSIBLE\",\n" + + " \"startAt\": 677,\n" + + " \"endAt\": 680\n" + + " },\n" + + " {\n" + + " \"detectWord\": \"한국\",\n" + + " \"keyword\": \"LOCATION\",\n" + + " \"likelihood\": \"LIKELY\",\n" + + " \"startAt\": 1110,\n" + + " \"endAt\": 1112\n" + + " }\n" + + " ],\n" + + " \"photoUrlRes\": [\n" + + " {\n" + + " \"pictureUrl\": \"https://scontent-gmp1-1.xx.fbcdn.net/v/t39.30808-6/481898235_122110815350769823_7830570790572853712_n.jpg?stp=dst-jpg_s960x960_tt6&_nc_cat=103&ccb=1-7&_nc_sid=127cfc&_nc_ohc=pqTxmMiaRmAQ7kNvgF_M2fo&_nc_oc=AdinZRm68WKJbnn4k8rmen-R1dYh-fVdY5Ln9wy9FUZzoYlqG6p3dTwjVynw9mzo0uM&_nc_zt=23&_nc_ht=scontent-gmp1-1.xx&edm=AP4hL3IEAAAA&_nc_gid=APgMhPiomivhYaHlQ0N7ogZ&oh=00_AYEzEWPriAvCi_kxV_i4eY7DSTzniaPtlf7qcPcVrlettQ&oe=67D07B53\",\n" + + " \"photoDetectRes\": [\n" + + " {\n" + + " \"detectWord\": \"TAIPEI\",\n" + + " \"keyword\": \"LOCATION\",\n" + + " \"likelihood\": \"LIKELY\",\n" + + " \"boundingBoxRes\": [\n" + + " {\n" + + " \"x\": 0,\n" + + " \"y\": 0,\n" + + " \"width\": 69,\n" + + " \"height\": 130\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"detectWord\": \"INCHEON\",\n" + + " \"keyword\": \"LOCATION\",\n" + + " \"likelihood\": \"LIKELY\",\n" + + " \"boundingBoxRes\": [\n" + + " {\n" + + " \"x\": 0,\n" + + " \"y\": 0,\n" + + " \"width\": 74,\n" + + " \"height\": 142\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"detectWord\": \"SEOUL\",\n" + + " \"keyword\": \"LOCATION\",\n" + + " \"likelihood\": \"LIKELY\",\n" + + " \"boundingBoxRes\": [\n" + + " {\n" + + " \"x\": 0,\n" + + " \"y\": 0,\n" + + " \"width\": 61,\n" + + " \"height\": 112\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"pictureUrl\": \"https://scontent-gmp1-1.xx.fbcdn.net/v/t39.30808-6/481660333_122110815518769823_8695251980723261352_n.jpg?stp=dst-jpg_s960x960_tt6&_nc_cat=107&ccb=1-7&_nc_sid=127cfc&_nc_ohc=tNw5uyzpP4gQ7kNvgFaUSzy&_nc_oc=AdgNS_NCXQvdYi0SgeISTbhNFApOfOkD1Sl_JdC6fvn7fpMSPJBJkz6ouL23GQCQfuI&_nc_zt=23&_nc_ht=scontent-gmp1-1.xx&edm=AP4hL3IEAAAA&_nc_gid=APgMhPiomivhYaHlQ0N7ogZ&oh=00_AYFp1fMpIXr7D6TfKutspe9qE-b1q_3xwJ0RWlOOerkOvA&oe=67D06CCD\",\n" + + " \"photoDetectRes\": []\n" + + " },\n" + + " {\n" + + " \"pictureUrl\": \"https://scontent-gmp1-1.xx.fbcdn.net/v/t39.30808-6/482017104_122110815512769823_160186683350267742_n.jpg?stp=dst-jpg_s960x960_tt6&_nc_cat=101&ccb=1-7&_nc_sid=127cfc&_nc_ohc=tm6myDkjx3MQ7kNvgF5sYwJ&_nc_oc=AdhOyAWpZwVmm2tD2EEkbkhPotc9zV-WEOKIHDIL0TdDQZynqgcQAd0Rg_9wnqXUa-Y&_nc_zt=23&_nc_ht=scontent-gmp1-1.xx&edm=AP4hL3IEAAAA&_nc_gid=APgMhPiomivhYaHlQ0N7ogZ&oh=00_AYHz8uyuq3j_H8Bpk_L4b2eMtQQF03OajuYazQouyt-SzQ&oe=67D0555D\",\n" + + " \"photoDetectRes\": []\n" + + " },\n" + + " {\n" + + " \"pictureUrl\": \"https://scontent-gmp1-1.xx.fbcdn.net/v/t39.30808-6/481771934_122110815506769823_5550061559215650391_n.jpg?stp=dst-jpg_p720x720_tt6&_nc_cat=107&ccb=1-7&_nc_sid=127cfc&_nc_ohc=OL0Fw7PcimsQ7kNvgHOKkl5&_nc_oc=AdhoVSqUqxSa0f67GtTVnVZAyLWXH4ZZQ0RS5n9rZaSbj1bDkuuA79MDFkGcEQfVW2E&_nc_zt=23&_nc_ht=scontent-gmp1-1.xx&edm=AP4hL3IEAAAA&_nc_gid=APgMhPiomivhYaHlQ0N7ogZ&oh=00_AYFHmdkt3hxwMgV-JVz6U6HFM8g5VICr0lHYYPSipDHZgQ&oe=67D04EA9\",\n" + + " \"photoDetectRes\": []\n" + + " },\n" + + " {\n" + + " \"pictureUrl\": \"https://scontent-gmp1-1.xx.fbcdn.net/v/t39.30808-6/482010082_122110815596769823_3206669003289189272_n.jpg?stp=dst-jpg_s960x960_tt6&_nc_cat=101&ccb=1-7&_nc_sid=127cfc&_nc_ohc=0NL_Dl9eJPIQ7kNvgH_LG7T&_nc_oc=AdiJ1S12kB4r37eXclp5Sn7TsGKWQvkaMhb7Zci2YfSDRtUqFz3qe0A9TmkVCn8OE5g&_nc_zt=23&_nc_ht=scontent-gmp1-1.xx&edm=AP4hL3IEAAAA&_nc_gid=APgMhPiomivhYaHlQ0N7ogZ&oh=00_AYFDtbA0NTz8R74OOg1eBFY5YrdvuMZ_OiT5KI4-xFI39Q&oe=67D048A2\",\n" + + " \"photoDetectRes\": []\n" + + " }\n" + + " ]\n" + + " }\n" + + "}") + ) + ), + @ApiResponse(responseCode = "404", description = "해당 자원을 찾을 수 없습니다.", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ResponseDto.class), + examples = @ExampleObject(value = "{ \"code\": 404, \"message\": \"권한이 없거나 존재하지 않는 게시물입니다.\" }") + ) + ) + }) + ResponseEntity getDetailPost(Member member, Long postId); + + @Operation(summary = "게시물 전체 조회", description = "전체 게시물을 조회합니다.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "Ok", content = @Content( @@ -26,101 +169,52 @@ public interface FbPostControllerDocs { @ExampleObject(value = "{\n" + " \"code\": 200,\n" + " \"message\": \"OK\",\n" - + " \"data\": [\n" - + " {\n" - + " \"id\": 151,\n" - + " \"createdTime\": \"2025-03-06T20:41:12\",\n" - + " \"firstPhotoUrl\": null,\n" - + " \"detectedKeywords\": [\n" - + " \"PERSON_NAME\",\n" - + " \"PHONE_NUMBER\",\n" - + " \"EMAIL_ADDRESS\"\n" - + " ]\n" - + " },\n" - + " {\n" - + " \"id\": 152,\n" - + " \"createdTime\": \"2025-03-06T15:01:36\",\n" - + " \"firstPhotoUrl\": \"https://scontent-gmp1-1.xx.fbcdn.net/v/t39.30808-6/482005688_122110821902769823_5883437262206830682_n.jpg?stp=cp1_dst-jpegr_s960x960_tt6&_nc_cat=109&ccb=1-7&_nc_sid=127cfc&_nc_ohc=_89orQPXAU4Q7kNvgHHDDfF&_nc_oc=Adis2DKgtqlPMSInKT1rq0EdMJk6HmxfVGCXD0dV3Tjoo34wIBMFr4wRhjgTpLtCcaA&_nc_zt=23&se=-1&_nc_ht=scontent-gmp1-1.xx&edm=AP4hL3IEAAAA&_nc_gid=APgMhPiomivhYaHlQ0N7ogZ&oh=00_AYEhMlsoqOMBiXKi-Zaa3UBaSD3oSBvmqZspg37f3cG94A&oe=67D04B41\",\n" - + " \"detectedKeywords\": [\n" - + " \"LOCATION\"\n" - + " ]\n" - + " },\n" - + " {\n" - + " \"id\": 153,\n" - + " \"createdTime\": \"2025-03-06T14:57:00\",\n" - + " \"firstPhotoUrl\": \"https://scontent-gmp1-1.xx.fbcdn.net/v/t39.30808-6/481820902_122110819862769823_6805450053096602031_n.jpg?stp=dst-jpg_p720x720_tt6&_nc_cat=106&ccb=1-7&_nc_sid=127cfc&_nc_ohc=4QVBrscw0c8Q7kNvgELjzdr&_nc_oc=AdhCgQD2zlsa4Hg825zj-DKr4ap131fSquivJ46rCh7kRs5ggPSEZ52ooD_TfcsR7zU&_nc_zt=23&_nc_ht=scontent-gmp1-1.xx&edm=AP4hL3IEAAAA&_nc_gid=APgMhPiomivhYaHlQ0N7ogZ&oh=00_AYEjnM7gBNxhuTBrvgLmhy3CfgTTnCLhHN1H3EAZDqqBiw&oe=67D0502C\",\n" - + " \"detectedKeywords\": [\n" - + " \"PERSON_NAME\",\n" - + " \"PHONE_NUMBER\",\n" - + " \"LOCATION\"\n" - + " ]\n" - + " },\n" - + " {\n" - + " \"id\": 154,\n" - + " \"createdTime\": \"2025-03-06T14:40:15\",\n" - + " \"firstPhotoUrl\": \"https://scontent-gmp1-1.xx.fbcdn.net/v/t39.30808-6/482006157_122110816010769823_4822934692025284850_n.jpg?stp=dst-jpg_p720x720_tt6&_nc_cat=103&ccb=1-7&_nc_sid=127cfc&_nc_ohc=KlCInOtXz28Q7kNvgEvqHN8&_nc_oc=AdjDKcRdysEHQpSEO879poNXbnmxaC9b6Q76zyGCFaZjI7ZSPOFWRjPTBMK68iTIM7U&_nc_zt=23&_nc_ht=scontent-gmp1-1.xx&edm=AP4hL3IEAAAA&_nc_gid=APgMhPiomivhYaHlQ0N7ogZ&oh=00_AYEX_qhEoL8SnhCEj5rLYYEwsELQyz5jYm3FC_Frf9D9Hw&oe=67D07164\",\n" - + " \"detectedKeywords\": [\n" - + " \"PERSON_NAME\",\n" - + " \"LOCATION\"\n" - + " ]\n" - + " },\n" - + " {\n" - + " \"id\": 155,\n" - + " \"createdTime\": \"2025-03-06T14:35:45\",\n" - + " \"firstPhotoUrl\": \"https://scontent-gmp1-1.xx.fbcdn.net/v/t39.30808-6/481898235_122110815350769823_7830570790572853712_n.jpg?stp=dst-jpg_s960x960_tt6&_nc_cat=103&ccb=1-7&_nc_sid=127cfc&_nc_ohc=pqTxmMiaRmAQ7kNvgF_M2fo&_nc_oc=AdinZRm68WKJbnn4k8rmen-R1dYh-fVdY5Ln9wy9FUZzoYlqG6p3dTwjVynw9mzo0uM&_nc_zt=23&_nc_ht=scontent-gmp1-1.xx&edm=AP4hL3IEAAAA&_nc_gid=APgMhPiomivhYaHlQ0N7ogZ&oh=00_AYEzEWPriAvCi_kxV_i4eY7DSTzniaPtlf7qcPcVrlettQ&oe=67D07B53\",\n" - + " \"detectedKeywords\": [\n" - + " \"LOCATION\",\n" - + " \"KOREA_PASSPORT\",\n" - + " \"PERSON_NAME\"\n" - + " ]\n" - + " },\n" - + " {\n" - + " \"id\": 156,\n" - + " \"createdTime\": \"2025-03-06T14:30:10\",\n" - + " \"firstPhotoUrl\": \"https://scontent-gmp1-1.xx.fbcdn.net/v/t39.30808-6/481784709_122110814570769823_1075445356216340862_n.jpg?stp=dst-jpg_s720x720_tt6&_nc_cat=105&ccb=1-7&_nc_sid=127cfc&_nc_ohc=akejy1pFTa0Q7kNvgE9XOoT&_nc_oc=Adg34RMotqKCf1aJJAMqtuREdnJ9ylGTvIzVdGfHbXZftniJzyMuimRrC_Wfo7HdGl4&_nc_zt=23&_nc_ht=scontent-gmp1-1.xx&edm=AP4hL3IEAAAA&_nc_gid=APgMhPiomivhYaHlQ0N7ogZ&oh=00_AYEZ135NtAgXdENzv8jaUqSTc9b9K4SIOGgrvQQqQdwZyA&oe=67D06263\",\n" - + " \"detectedKeywords\": [\n" - + " \"PHONE_NUMBER\"\n" - + " ]\n" - + " },\n" - + " {\n" - + " \"id\": 157,\n" - + " \"createdTime\": \"2025-03-06T12:04:58\",\n" - + " \"firstPhotoUrl\": \"https://scontent-gmp1-1.xx.fbcdn.net/v/t39.30808-6/482000767_122110765268769823_7669289345773660085_n.jpg?stp=dst-jpg_p720x720_tt6&_nc_cat=102&ccb=1-7&_nc_sid=127cfc&_nc_ohc=uJUeAzILfEAQ7kNvgE7TfMb&_nc_oc=AdgepuB4YVwf55XUeu9-8y-cr_rmliSInY3FAYhpT63pKsjb9eL_N2p-jigE9ZCJpRo&_nc_zt=23&_nc_ht=scontent-gmp1-1.xx&edm=AP4hL3IEAAAA&_nc_gid=APgMhPiomivhYaHlQ0N7ogZ&oh=00_AYEcpbr8H07mfjd6pB9WGwWx66r6ij4Flr19Wdv9dNWR4w&oe=67D06C87\",\n" - + " \"detectedKeywords\": []\n" - + " },\n" - + " {\n" - + " \"id\": 158,\n" - + " \"createdTime\": \"2025-03-06T11:57:26\",\n" - + " \"firstPhotoUrl\": \"https://scontent-gmp1-1.xx.fbcdn.net/v/t39.30808-6/482023063_122110760102769823_4351467489794574342_n.jpg?stp=dst-jpg_p720x720_tt6&_nc_cat=104&ccb=1-7&_nc_sid=127cfc&_nc_ohc=OGF2WaeTx20Q7kNvgEAI-yv&_nc_oc=AdjU9rcdQLeDH_HZcwdgFlhNo5JGFKTFmQi5MKgTzBIDSw5_99ll42yHGYQiwQU3SM4&_nc_zt=23&_nc_ht=scontent-gmp1-1.xx&edm=AP4hL3IEAAAA&_nc_gid=APgMhPiomivhYaHlQ0N7ogZ&oh=00_AYFYX1e3_iuFWGzn-GeWuYgWdDWa4IBU7fMCGRNvhSq4xA&oe=67D05993\",\n" - + " \"detectedKeywords\": [\n" - + " \"PERSON_NAME\",\n" - + " \"LOCATION\"\n" - + " ]\n" - + " },\n" - + " {\n" - + " \"id\": 159,\n" - + " \"createdTime\": \"2025-02-26T06:44:10\",\n" - + " \"firstPhotoUrl\": \"https://scontent-gmp1-1.xx.fbcdn.net/v/t51.75761-15/481183925_17845332873425342_9111973536618245990_n.jpg?stp=dst-jpg_p720x720_tt6&_nc_cat=106&ccb=1-7&_nc_sid=127cfc&_nc_ohc=tGJeHDNelXUQ7kNvgFkn75g&_nc_oc=AdgigxklAKeh_KvLXY-OITdWuG5Skm4ZgxrNyllvej-XbBcqrhZ894tG8M-yrrIGcJM&_nc_zt=23&_nc_ht=scontent-gmp1-1.xx&edm=AP4hL3IEAAAA&_nc_gid=APgMhPiomivhYaHlQ0N7ogZ&oh=00_AYEoZ1MUAsbf-JCOLxGRTH5qKKIGf3ANu4dq1qaP7giNMQ&oe=67D0597B\",\n" - + " \"detectedKeywords\": []\n" - + " },\n" - + " {\n" - + " \"id\": 160,\n" - + " \"createdTime\": \"2025-02-26T05:53:39\",\n" - + " \"firstPhotoUrl\": \"https://scontent-gmp1-1.xx.fbcdn.net/v/t51.75761-15/480981728_17845326564425342_6913190954196226453_n.jpg?stp=dst-jpg_s720x720_tt6&_nc_cat=103&ccb=1-7&_nc_sid=127cfc&_nc_ohc=bOuIQ3JTMHcQ7kNvgEYt7zo&_nc_oc=Adg5hRwzuNW2Xjx6lJBkl_7JXv3pfkTAMTydiGSX7Zvc8e4TWYfBXcx_ugva3lscHZA&_nc_zt=23&_nc_ht=scontent-gmp1-1.xx&edm=AP4hL3IEAAAA&_nc_gid=APgMhPiomivhYaHlQ0N7ogZ&oh=00_AYG0asG6FVLmE_eW2OA6dYFXakMs5tJQx5mMCMbYzo8JxA&oe=67D0711C\",\n" - + " \"detectedKeywords\": [\n" - + " \"LOCATION\"\n" - + " ]\n" + + " \"data\": {\n" + + " \"totalPostCount\": 10,\n" + + " \"detectPostCount\": 8,\n" + + " \"averageDangerScore\": 46.25,\n" + + " \"fbPostResPage\": {\n" + + " \"data\": [\n" + + " {\n" + + " \"id\": 151,\n" + + " \"createdTime\": \"2025-03-06T20:41:12\",\n" + + " \"firstPhotoUrl\": null,\n" + + " \"detectedKeywords\": [\n" + + " \"PERSON_NAME\",\n" + + " \"PHONE_NUMBER\",\n" + + " \"EMAIL_ADDRESS\"\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"id\": 152,\n" + + " \"createdTime\": \"2025-03-06T15:01:36\",\n" + + " \"firstPhotoUrl\": \"https://scontent-gmp1-1.xx.fbcdn.net/v/t39.30808-6/482005688_122110821902769823_5883437262206830682_n.jpg?stp=cp1_dst-jpegr_s960x960_tt6&_nc_cat=109&ccb=1-7&_nc_sid=127cfc&_nc_ohc=_89orQPXAU4Q7kNvgHHDDfF&_nc_oc=Adis2DKgtqlPMSInKT1rq0EdMJk6HmxfVGCXD0dV3Tjoo34wIBMFr4wRhjgTpLtCcaA&_nc_zt=23&se=-1&_nc_ht=scontent-gmp1-1.xx&edm=AP4hL3IEAAAA&_nc_gid=APgMhPiomivhYaHlQ0N7ogZ&oh=00_AYEhMlsoqOMBiXKi-Zaa3UBaSD3oSBvmqZspg37f3cG94A&oe=67D04B41\",\n" + + " \"detectedKeywords\": [\n" + + " \"LOCATION\"\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"id\": 153,\n" + + " \"createdTime\": \"2025-03-06T14:57:00\",\n" + + " \"firstPhotoUrl\": \"https://scontent-gmp1-1.xx.fbcdn.net/v/t39.30808-6/481820902_122110819862769823_6805450053096602031_n.jpg?stp=dst-jpg_p720x720_tt6&_nc_cat=106&ccb=1-7&_nc_sid=127cfc&_nc_ohc=4QVBrscw0c8Q7kNvgELjzdr&_nc_oc=AdhCgQD2zlsa4Hg825zj-DKr4ap131fSquivJ46rCh7kRs5ggPSEZ52ooD_TfcsR7zU&_nc_zt=23&_nc_ht=scontent-gmp1-1.xx&edm=AP4hL3IEAAAA&_nc_gid=APgMhPiomivhYaHlQ0N7ogZ&oh=00_AYEjnM7gBNxhuTBrvgLmhy3CfgTTnCLhHN1H3EAZDqqBiw&oe=67D0502C\",\n" + + " \"detectedKeywords\": [\n" + + " \"PERSON_NAME\",\n" + + " \"PHONE_NUMBER\",\n" + + " \"LOCATION\"\n" + + " ]\n" + + " }\n" + + " ],\n" + + " \"totalElements\": 10,\n" + + " \"totalPages\": 4,\n" + + " \"size\": 3,\n" + + " \"numberOfElements\": 3\n" + " }\n" - + " ],\n" - + " \"totalElements\": 10,\n" - + " \"totalPages\": 1,\n" - + " \"size\": 100,\n" - + " \"numberOfElements\": 10\n" + + " }\n" + "}") ) ), }) - ResponseEntity getExhibitions(Member member, DetectKeyword detectType, String keyword, int page, + ResponseEntity getTotalPost(Member member, DetectKeyword detectType, String keyword, int page, int size); } diff --git a/src/main/java/com/olive/pribee/module/feed/domain/entity/DetectKeywordInPhotoBoundingBox.java b/src/main/java/com/olive/pribee/module/feed/domain/entity/DetectKeywordInPhotoBoundingBox.java index 8ed98b9..709a87e 100644 --- a/src/main/java/com/olive/pribee/module/feed/domain/entity/DetectKeywordInPhotoBoundingBox.java +++ b/src/main/java/com/olive/pribee/module/feed/domain/entity/DetectKeywordInPhotoBoundingBox.java @@ -32,10 +32,10 @@ public class DetectKeywordInPhotoBoundingBox extends BaseTime { private DetectKeywordInPhoto detectKeywordInPhoto; @NotNull - private int y_position; + private int y; @NotNull - private int x_position; + private int x; @NotNull private int width; @@ -43,12 +43,12 @@ public class DetectKeywordInPhotoBoundingBox extends BaseTime { @NotNull private int height; - public static DetectKeywordInPhotoBoundingBox of(@NotNull DetectKeywordInPhoto detectKeywordInPhoto, int y_position, - int x_position, int width, int height) { + public static DetectKeywordInPhotoBoundingBox of(@NotNull DetectKeywordInPhoto detectKeywordInPhoto, int y, + int x, int width, int height) { return DetectKeywordInPhotoBoundingBox.builder() .detectKeywordInPhoto(detectKeywordInPhoto) - .y_position(y_position) - .x_position(x_position) + .y(y) + .x(x) .width(width) .height(height) .build(); diff --git a/src/main/java/com/olive/pribee/module/feed/domain/repository/custom/FbPostRepositoryCustom.java b/src/main/java/com/olive/pribee/module/feed/domain/repository/custom/FbPostRepositoryCustom.java index 7688dfc..8af27ee 100644 --- a/src/main/java/com/olive/pribee/module/feed/domain/repository/custom/FbPostRepositoryCustom.java +++ b/src/main/java/com/olive/pribee/module/feed/domain/repository/custom/FbPostRepositoryCustom.java @@ -5,6 +5,7 @@ import org.springframework.stereotype.Repository; import com.olive.pribee.global.enums.DetectKeyword; +import com.olive.pribee.module.feed.dto.res.FbPostDetailRes; import com.olive.pribee.module.feed.dto.res.FbPostRes; import com.olive.pribee.module.feed.dto.res.FbPostTotalRes; @@ -15,4 +16,6 @@ Page getFbPostByKeywordAndPaging(Long userId, DetectKeyword detectTyp FbPostTotalRes getFbPostTotal(Long memberId, DetectKeyword detectType, String keyword, Pageable pageable); + + FbPostDetailRes getFbPostDetail(Long memberId, Long fbPostId); } diff --git a/src/main/java/com/olive/pribee/module/feed/domain/repository/custom/FbPostRepositoryImpl.java b/src/main/java/com/olive/pribee/module/feed/domain/repository/custom/FbPostRepositoryImpl.java index f290a99..8e949f3 100644 --- a/src/main/java/com/olive/pribee/module/feed/domain/repository/custom/FbPostRepositoryImpl.java +++ b/src/main/java/com/olive/pribee/module/feed/domain/repository/custom/FbPostRepositoryImpl.java @@ -1,5 +1,7 @@ package com.olive.pribee.module.feed.domain.repository.custom; +import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -11,16 +13,26 @@ import org.springframework.util.StringUtils; import com.olive.pribee.global.enums.DetectKeyword; +import com.olive.pribee.global.enums.DetectLikelihood; +import com.olive.pribee.global.error.exception.AppException; import com.olive.pribee.module.feed.domain.entity.DetectKeywordInMessage; import com.olive.pribee.module.feed.domain.entity.FbPost; import com.olive.pribee.module.feed.domain.entity.FbPostPictureUrl; import com.olive.pribee.module.feed.domain.entity.QDetectKeywordInMessage; import com.olive.pribee.module.feed.domain.entity.QDetectKeywordInPhoto; +import com.olive.pribee.module.feed.domain.entity.QDetectKeywordInPhotoBoundingBox; import com.olive.pribee.module.feed.domain.entity.QFbPost; import com.olive.pribee.module.feed.domain.entity.QFbPostPictureUrl; +import com.olive.pribee.module.feed.dto.res.BoundingBoxRes; +import com.olive.pribee.module.feed.dto.res.FbPostDetailRes; import com.olive.pribee.module.feed.dto.res.FbPostRes; import com.olive.pribee.module.feed.dto.res.FbPostTotalRes; +import com.olive.pribee.module.feed.dto.res.MessageDetectRes; +import com.olive.pribee.module.feed.dto.res.PhotoDetectRes; +import com.olive.pribee.module.feed.dto.res.PhotoUrlRes; +import com.olive.pribee.module.feed.error.FbPostErrorCode; import com.querydsl.core.BooleanBuilder; +import com.querydsl.core.Tuple; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; @@ -31,82 +43,82 @@ public class FbPostRepositoryImpl implements FbPostRepositoryCustom { private final JPAQueryFactory jpaQueryFactory; - @Override - public Page getFbPostByKeywordAndPaging(Long memberId, DetectKeyword detectType, String keyword, - Pageable pageable) { - - QFbPost fbPost = QFbPost.fbPost; - QDetectKeywordInMessage detectKeywordInMessage = QDetectKeywordInMessage.detectKeywordInMessage; - QDetectKeywordInPhoto detectKeywordInPhoto = QDetectKeywordInPhoto.detectKeywordInPhoto; - - BooleanBuilder builder = new BooleanBuilder(); - builder.and(fbPost.member.id.eq(memberId)); - - if (detectType != null) { - builder.and(detectKeywordInMessage.keyword.eq(detectType) - .or(detectKeywordInPhoto.keyword.eq(detectType))); - } - - if (StringUtils.hasText(keyword)) { - builder.and(fbPost.message.containsIgnoreCase(keyword)); - } - - // 1. 페이징 적용하여 FbPost ID 조회 - List postIds = jpaQueryFactory.select(fbPost.id) - .from(fbPost) - .where(builder) - .orderBy(fbPost.createdTime.desc()) - .offset(pageable.getOffset()) - .limit(pageable.getPageSize()) - .fetch(); - - if (postIds.isEmpty()) { - return new PageImpl<>(List.of(), pageable, 0); - } - - // 2. FbPost 엔티티 조회 (Batch Fetch 적용) - List posts = jpaQueryFactory.selectFrom(fbPost) - .where(fbPost.id.in(postIds)) - .fetch(); - - // 3. detectKeywordInMessages 조회 (Batch Fetch) - Map> detectKeywordMap = jpaQueryFactory - .selectFrom(detectKeywordInMessage) - .where(detectKeywordInMessage.fbPost.id.in(postIds)) - .fetch() - .stream() - .collect(Collectors.groupingBy(d -> d.getFbPost().getId())); - - // 4. fbPostPictureUrls 조회 (Batch Fetch) - Map> pictureUrlMap = jpaQueryFactory - .selectFrom(QFbPostPictureUrl.fbPostPictureUrl) - .where(QFbPostPictureUrl.fbPostPictureUrl.fbPost.id.in(postIds)) - .fetch() - .stream() - .collect(Collectors.groupingBy(p -> p.getFbPost().getId())); - - // 5. DTO 변환 - List resDtos = posts.stream().map(post -> FbPostRes.of( - post.getId(), - post.getCreatedTime(), - pictureUrlMap.getOrDefault(post.getId(), List.of()).stream() - .findFirst().map(FbPostPictureUrl::getPhotoUrl).orElse(null), - detectKeywordMap.getOrDefault(post.getId(), List.of()).stream() - .map(DetectKeywordInMessage::getKeyword) - .distinct() - .collect(Collectors.toList()) - )).collect(Collectors.toList()); - - long total = jpaQueryFactory.select(fbPost.count()) - .from(fbPost) - .where(builder) - .fetchOne(); - - return new PageImpl<>(resDtos, pageable, total); - } - - - @Override + @Override + public Page getFbPostByKeywordAndPaging(Long memberId, DetectKeyword detectType, String keyword, + Pageable pageable) { + + QFbPost fbPost = QFbPost.fbPost; + QDetectKeywordInMessage detectKeywordInMessage = QDetectKeywordInMessage.detectKeywordInMessage; + QDetectKeywordInPhoto detectKeywordInPhoto = QDetectKeywordInPhoto.detectKeywordInPhoto; + + BooleanBuilder builder = new BooleanBuilder(); + builder.and(fbPost.member.id.eq(memberId)); + + if (detectType != null) { + builder.and(detectKeywordInMessage.keyword.eq(detectType) + .or(detectKeywordInPhoto.keyword.eq(detectType))); + } + + if (StringUtils.hasText(keyword)) { + builder.and(fbPost.message.containsIgnoreCase(keyword)); + } + + // 1. 페이징 적용하여 FbPost ID 조회 + List postIds = jpaQueryFactory.select(fbPost.id) + .from(fbPost) + .where(builder) + .orderBy(fbPost.createdTime.desc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + if (postIds.isEmpty()) { + return new PageImpl<>(List.of(), pageable, 0); + } + + // 2. FbPost 엔티티 조회 (Batch Fetch 적용) + List posts = jpaQueryFactory.selectFrom(fbPost) + .where(fbPost.id.in(postIds)) + .fetch(); + + // 3. detectKeywordInMessages 조회 (Batch Fetch) + Map> detectKeywordMap = jpaQueryFactory + .selectFrom(detectKeywordInMessage) + .where(detectKeywordInMessage.fbPost.id.in(postIds)) + .fetch() + .stream() + .collect(Collectors.groupingBy(d -> d.getFbPost().getId())); + + // 4. fbPostPictureUrls 조회 (Batch Fetch) - 오래된 순으로 정렬 + Map> pictureUrlMap = jpaQueryFactory + .selectFrom(QFbPostPictureUrl.fbPostPictureUrl) + .where(QFbPostPictureUrl.fbPostPictureUrl.fbPost.id.in(postIds)) + .orderBy(QFbPostPictureUrl.fbPostPictureUrl.createdAt.asc()) + .fetch() + .stream() + .collect(Collectors.groupingBy(p -> p.getFbPost().getId())); + + // 5. DTO 변환 + List resDtos = posts.stream().map(post -> FbPostRes.of( + post.getId(), + post.getCreatedTime(), + pictureUrlMap.getOrDefault(post.getId(), List.of()).stream() + .findFirst().map(FbPostPictureUrl::getPhotoUrl).orElse(null), + detectKeywordMap.getOrDefault(post.getId(), List.of()).stream() + .map(DetectKeywordInMessage::getKeyword) + .distinct() + .collect(Collectors.toList()) + )).collect(Collectors.toList()); + + long total = jpaQueryFactory.select(fbPost.count()) + .from(fbPost) + .where(builder) + .fetchOne(); + + return new PageImpl<>(resDtos, pageable, total); + } + + @Override public FbPostTotalRes getFbPostTotal(Long memberId, DetectKeyword detectType, String keyword, Pageable pageable) { @@ -140,4 +152,112 @@ public FbPostTotalRes getFbPostTotal(Long memberId, DetectKeyword detectType, St fbPostResPage ); } + + @Override + public FbPostDetailRes getFbPostDetail(Long memberId, Long fbPostId) { + QFbPost fbPost = QFbPost.fbPost; + QDetectKeywordInMessage detectKeywordInMessage = QDetectKeywordInMessage.detectKeywordInMessage; + QDetectKeywordInPhoto detectKeywordInPhoto = QDetectKeywordInPhoto.detectKeywordInPhoto; + QFbPostPictureUrl fbPostPictureUrl = QFbPostPictureUrl.fbPostPictureUrl; + QDetectKeywordInPhotoBoundingBox boundingBox = QDetectKeywordInPhotoBoundingBox.detectKeywordInPhotoBoundingBox; + + // 1. 게시물 기본 정보 조회 + FbPost post = jpaQueryFactory + .selectFrom(fbPost) + .where(fbPost.id.eq(fbPostId).and(fbPost.member.id.eq(memberId))) + .fetchOne(); + + if (post == null) { + throw new AppException(FbPostErrorCode.INVALID_QUIZ_ID); + } + + // 2. 메시지 내 감지된 개인정보 조회 + List messageDetectResList = jpaQueryFactory + .selectFrom(detectKeywordInMessage) + .where(detectKeywordInMessage.fbPost.id.eq(fbPostId)) + .fetch() + .stream() + .map(msg -> MessageDetectRes.of( + msg.getDetectWord(), + msg.getKeyword(), + msg.getLikelihood(), + msg.getStartAt(), + msg.getEndAt() + )) + .collect(Collectors.toList()); + + // 3. 사진 URL 가져오기 (오래된 순 정렬) + List allPhotoUrls = jpaQueryFactory + .select(fbPostPictureUrl.photoUrl) + .from(fbPostPictureUrl) + .where(fbPostPictureUrl.fbPost.id.eq(fbPostId)) + .orderBy(fbPostPictureUrl.createdAt.asc()) + .fetch(); + + // 4. 사진 내 감지된 개인정보 조회 + List photoData = jpaQueryFactory + .select( + fbPostPictureUrl.photoUrl, + detectKeywordInPhoto.detectWord, + detectKeywordInPhoto.keyword, + detectKeywordInPhoto.likelihood, + boundingBox.x, + boundingBox.y, + boundingBox.width, + boundingBox.height + ) + .from(fbPostPictureUrl) + .leftJoin(detectKeywordInPhoto).on(detectKeywordInPhoto.fbPostPictureUrl.id.eq(fbPostPictureUrl.id)) + .leftJoin(boundingBox).on(detectKeywordInPhoto.id.eq(boundingBox.detectKeywordInPhoto.id)) + .where(fbPostPictureUrl.fbPost.id.eq(fbPostId)) + .orderBy(fbPostPictureUrl.createdAt.asc()) + .fetch(); + + // 5. 데이터를 `PhotoUrlRes`로 변환 (LinkedHashMap 사용) + Map> photoDetectMap = new LinkedHashMap<>(); + + // 5-1. 모든 사진 URL을 우선 빈 리스트로 저장 + for (String pictureUrl : allPhotoUrls) { + photoDetectMap.put(pictureUrl, new ArrayList<>()); + } + + // 5-2. 감지된 개인정보 정보 추가 + for (Tuple tuple : photoData) { + String pictureUrl = tuple.get(fbPostPictureUrl.photoUrl); + String detectWord = tuple.get(detectKeywordInPhoto.detectWord); + DetectKeyword keyword = tuple.get(detectKeywordInPhoto.keyword); + DetectLikelihood likelihood = tuple.get(detectKeywordInPhoto.likelihood); + + Integer x = tuple.get(boundingBox.x); + Integer y = tuple.get(boundingBox.y); + Integer width = tuple.get(boundingBox.width); + Integer height = tuple.get(boundingBox.height); + + List boundingBoxes = new ArrayList<>(); + if (x != null && y != null && width != null && height != null) { + boundingBoxes.add(BoundingBoxRes.of(x, y, width, height)); + } + + if (detectWord != null || keyword != null || likelihood != null) { + PhotoDetectRes photoDetectRes = PhotoDetectRes.of(detectWord, keyword, likelihood, boundingBoxes); + photoDetectMap.get(pictureUrl).add(photoDetectRes); // 기존 리스트에 추가 + } + } + + // 5-3. `PhotoUrlRes` 리스트 생성 + List photoUrlResList = photoDetectMap.entrySet().stream() + .map(entry -> PhotoUrlRes.of(entry.getKey(), entry.getValue())) // 빈 리스트 유지 + .collect(Collectors.toList()); + + // 6. DTO 변환 후 반환 + return FbPostDetailRes.of( + post.getCreatedTime(), + post.getMessage(), + post.getPermalinkUrl(), + post.getSafeMessage(), + messageDetectResList, + photoUrlResList + ); + } + } \ No newline at end of file diff --git a/src/main/java/com/olive/pribee/module/feed/dto/res/BoundingBoxRes.java b/src/main/java/com/olive/pribee/module/feed/dto/res/BoundingBoxRes.java new file mode 100644 index 0000000..0d844c1 --- /dev/null +++ b/src/main/java/com/olive/pribee/module/feed/dto/res/BoundingBoxRes.java @@ -0,0 +1,39 @@ + +package com.olive.pribee.module.feed.dto.res; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; + +@Schema(description = "Facebook 게시물 상세 사진 탐지 위치 응답 DTO") +@Getter +@Builder(access = AccessLevel.PRIVATE) +public class BoundingBoxRes { + + @Schema(description = "x 좌표") + private int x; + + @Schema(name = "y 좌표") + private int y; + + @Schema(description = "너비") + private int width; + + @Schema(description = "높이") + private int height; + + public static BoundingBoxRes of( + int x, + int y, + int width, + int height + ) { + return BoundingBoxRes.builder() + .x(x) + .y(y) + .width(width) + .height(height) + .build(); + } +} diff --git a/src/main/java/com/olive/pribee/module/feed/dto/res/FbPostDetailRes.java b/src/main/java/com/olive/pribee/module/feed/dto/res/FbPostDetailRes.java new file mode 100644 index 0000000..3562dfc --- /dev/null +++ b/src/main/java/com/olive/pribee/module/feed/dto/res/FbPostDetailRes.java @@ -0,0 +1,50 @@ +package com.olive.pribee.module.feed.dto.res; + +import java.time.LocalDateTime; +import java.util.List; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; + +@Schema(description = "Facebook 게시물 상세 응답 DTO") +@Getter +@Builder(access = AccessLevel.PRIVATE) +public class FbPostDetailRes { + @Schema(description = "게시물 생성 시간", example = "2024-03-07T12:34:56") + private LocalDateTime createdTime; + + @Schema(description = "chat gpt 에서 제안한 수정본") + private String safeMessage; + + @Schema(description = "원본 메세지") + private String message; + + @Schema(description = "페이지 이동 링크") + private String permalinkUrl; + + @Schema(description = "메세지 분석 정보") + private List messageDetectRes; + + @Schema(description = "사진 분석 정보") + private List photoUrlRes; + + public static FbPostDetailRes of( + LocalDateTime createdTime, + String message, + String permalinkUrl, + String safeMessage, + List messageDetectRes, + List photoUrlRes + ) { + return FbPostDetailRes.builder() + .message(message) + .permalinkUrl(permalinkUrl) + .createdTime(createdTime) + .safeMessage(safeMessage) + .messageDetectRes(messageDetectRes) + .photoUrlRes(photoUrlRes) + .build(); + } +} diff --git a/src/main/java/com/olive/pribee/module/feed/dto/res/MessageDetectRes.java b/src/main/java/com/olive/pribee/module/feed/dto/res/MessageDetectRes.java new file mode 100644 index 0000000..0fa35e6 --- /dev/null +++ b/src/main/java/com/olive/pribee/module/feed/dto/res/MessageDetectRes.java @@ -0,0 +1,46 @@ +package com.olive.pribee.module.feed.dto.res; + +import com.olive.pribee.global.enums.DetectKeyword; +import com.olive.pribee.global.enums.DetectLikelihood; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; + +@Schema(description = "Facebook 게시물 상세 메세지 분석 응답 DTO") +@Getter +@Builder(access = AccessLevel.PRIVATE) +public class MessageDetectRes { + + @Schema(description = "유출 위험 글자") + private String detectWord; + + @Schema(description = "탐지된 항목", example = "PERSON_NAME") + private DetectKeyword keyword; + + @Schema(description = "신뢰도", example = "POSSIBLE") + private DetectLikelihood likelihood; + + @Schema(description = "유출 위험 글자 시작 지점") + private Integer startAt; + + @Schema(description = "유출 위험 글자 끝 지점") + private Integer endAt; + + public static MessageDetectRes of( + String detectWord, + DetectKeyword keyword, + DetectLikelihood likelihood, + Integer startAt, + Integer endAt + ) { + return MessageDetectRes.builder() + .detectWord(detectWord) + .keyword(keyword) + .likelihood(likelihood) + .startAt(startAt) + .endAt(endAt) + .build(); + } +} diff --git a/src/main/java/com/olive/pribee/module/feed/dto/res/PhotoDetectRes.java b/src/main/java/com/olive/pribee/module/feed/dto/res/PhotoDetectRes.java new file mode 100644 index 0000000..2a04f1d --- /dev/null +++ b/src/main/java/com/olive/pribee/module/feed/dto/res/PhotoDetectRes.java @@ -0,0 +1,43 @@ +package com.olive.pribee.module.feed.dto.res; + +import java.util.List; + +import com.olive.pribee.global.enums.DetectKeyword; +import com.olive.pribee.global.enums.DetectLikelihood; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; + +@Schema(description = "Facebook 게시물 상세 사진 분석 응답 DTO") +@Getter +@Builder(access = AccessLevel.PRIVATE) +public class PhotoDetectRes { + + @Schema(description = "유출 위험 글자") + private String detectWord; + + @Schema(description = "탐지된 항목", example = "PERSON_NAME") + private DetectKeyword keyword; + + @Schema(description = "신뢰도", example = "POSSIBLE") + private DetectLikelihood likelihood; + + @Schema(description = "사진 탐지 영역") + private List boundingBoxRes; + + public static PhotoDetectRes of( + String detectWord, + DetectKeyword keyword, + DetectLikelihood likelihood, + List boundingBoxRes + ) { + return PhotoDetectRes.builder() + .detectWord(detectWord) + .keyword(keyword) + .likelihood(likelihood) + .boundingBoxRes(boundingBoxRes) + .build(); + } +} diff --git a/src/main/java/com/olive/pribee/module/feed/dto/res/PhotoUrlRes.java b/src/main/java/com/olive/pribee/module/feed/dto/res/PhotoUrlRes.java new file mode 100644 index 0000000..e20ebf4 --- /dev/null +++ b/src/main/java/com/olive/pribee/module/feed/dto/res/PhotoUrlRes.java @@ -0,0 +1,29 @@ +package com.olive.pribee.module.feed.dto.res; + +import java.util.List; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; + +@Schema(description = "Facebook 게시물 상세 사진 url 응답 DTO") +@Getter +@Builder(access = AccessLevel.PRIVATE) +public class PhotoUrlRes { + + @Schema(name = "사진 url") + private String pictureUrl; + @Schema(description = "사진 탐지 태그 리스트") + private List photoDetectRes; + + public static PhotoUrlRes of( + String pictureUrl, + List photoDetectRes + ) { + return PhotoUrlRes.builder() + .pictureUrl(pictureUrl) + .photoDetectRes(photoDetectRes) + .build(); + } +} diff --git a/src/main/java/com/olive/pribee/module/feed/error/FbPostErrorCode.java b/src/main/java/com/olive/pribee/module/feed/error/FbPostErrorCode.java new file mode 100644 index 0000000..784bff3 --- /dev/null +++ b/src/main/java/com/olive/pribee/module/feed/error/FbPostErrorCode.java @@ -0,0 +1,20 @@ +package com.olive.pribee.module.feed.error; + +import org.springframework.http.HttpStatus; + +import com.olive.pribee.global.error.ErrorCode; + +import lombok.Getter; + +@Getter +public enum FbPostErrorCode implements ErrorCode { + INVALID_QUIZ_ID(HttpStatus.NOT_FOUND, "권한이 없거나 존재하지 않는 게시물입니다."); + + private final HttpStatus httpStatus; + private final String message; + + FbPostErrorCode(HttpStatus httpStatus, String message) { + this.httpStatus = httpStatus; + this.message = message; + } +} diff --git a/src/main/java/com/olive/pribee/module/feed/service/FbPostService.java b/src/main/java/com/olive/pribee/module/feed/service/FbPostService.java index 08015f4..6d762d7 100644 --- a/src/main/java/com/olive/pribee/module/feed/service/FbPostService.java +++ b/src/main/java/com/olive/pribee/module/feed/service/FbPostService.java @@ -31,6 +31,7 @@ import com.olive.pribee.module.feed.domain.repository.DetectKeywordInPhotoRepository; import com.olive.pribee.module.feed.domain.repository.FbPostRepository; import com.olive.pribee.module.feed.domain.repository.custom.FbPostRepositoryImpl; +import com.olive.pribee.module.feed.dto.res.FbPostDetailRes; import com.olive.pribee.module.feed.dto.res.FbPostTotalRes; import lombok.RequiredArgsConstructor; @@ -228,8 +229,14 @@ public LocalDateTime getRecentPostsCreateTime() { return fbPost.map(FbPost::getCreatedTime).orElse(null); } + // 전체 게시물 가져오기 public FbPostTotalRes getTotalPost(Long memberId, DetectKeyword detectType, String keyword, int page, int size) { Pageable pageable = PageRequest.of(page, size); return fbPostQueryRepository.getFbPostTotal(memberId, detectType, keyword, pageable); } + + // 게시물 상세보기 + public FbPostDetailRes getDetailPost(Long memberId, Long postId) { + return fbPostQueryRepository.getFbPostDetail(memberId, postId); + } }