From 0cc97fb0f12836637f8a74e42d7814a645868098 Mon Sep 17 00:00:00 2001 From: Leeyoujin00 Date: Sun, 19 Jan 2025 16:09:11 +0900 Subject: [PATCH 01/12] =?UTF-8?q?[TEST]=20=EC=B1=84=ED=8C=85=20Test=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/app/treen/TreenApplication.class | Bin 1017 -> 0 bytes .../user/controller/UserController.class | Bin 327 -> 0 bytes .../com/app/treen/QBaseTimeEntity.java | 4 - .../treen/products/entity/QTradeProduct.java | 2 +- .../treen/products/entity/QTransProduct.java | 4 - .../app/treen/user/entity/QRefreshToken.java | 2 +- .../com/app/treen/user/entity/QUser.java | 14 -- .../controller/ChatRoomController.java | 169 +++++++----------- .../chat_room/service/ChatRoomService.java | 2 +- .../controller/ProductController.java | 2 +- .../treen/user/service/CustomUserDetails.java | 2 +- 11 files changed, 69 insertions(+), 132 deletions(-) delete mode 100644 out/production/classes/com/app/treen/TreenApplication.class delete mode 100644 out/production/classes/com/app/treen/user/controller/UserController.class diff --git a/out/production/classes/com/app/treen/TreenApplication.class b/out/production/classes/com/app/treen/TreenApplication.class deleted file mode 100644 index d56827e2cfd9a85e7b720518ceba8aa10cc77a5e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1017 zcmbVLO;Zy=5PcH@2{B4Y1Vs^$Q^+B6;}WF`QgX1?k0Mk&aGKo=>xAsg*6b|m&+??@ z!5`p{vg}#VL@Ic258XT6Gp}F2>i+Tb+jjuZv0Fn43l)^>Si}-TV=OKOp9mfEH{n=D zj$!GU(#jn$EVTEA6;v6vB9m~DrrcR6H9z(CFij^a5>6RiLk)K-sMm29_ZT|H#ym@{ z(($CqQ65rW*qD%7bJ7g8fyr$okCi8~c2n7|SA}8YB-c(Qa;P%pyVKgZLV1Ecnk&jk zIKjuM;8vz4Q_k2=oP;8Q_zUg3e3^>Fe59P$mErimK~8?-E13~cJrtRIE20Y#OCq;P z;&o|oy2aphiBNY+h#|PWY+`h5M&YgeU-|rv;;p}{6%Y&ubBh%7C~{}6RLQNpp3Wm0 z)S2<=;VCM#GX5)p`K2MdLMKk1kzee50sCB}8)SVNvsJ@hM2k+#xQ{i8_{aMN z7*K6iI$yE;Zu;KuC(xjxt1}o?tm6U2OL$n=4Ll;hi!vUQ-z3y0XJ8q&u>I$Mhp6Kh Dcg`ZH diff --git a/out/production/classes/com/app/treen/user/controller/UserController.class b/out/production/classes/com/app/treen/user/controller/UserController.class deleted file mode 100644 index bd798acb71702ab1f18c3ba10ce52dd759a8d3bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 327 zcmb7<%}PTt5QWd&pI*Jzw&)YMt0K(<__Havy4Xdv``k2WN}7~3*Y|QIxbOjdDCwk# z;Iavv%y$MdbH0B*zX04}R3k%Pq3EMTk1$%vHz}-iFJjiNw2Fk@t#Kya6Y}xpqQVj3 zTKTn*n~jJ;YbUy`4nlbsgSVE;XXfc25{ju`YQnHFPCs_*R);6qT6WGFucTebU=shD zMSL||!lXH5$PJ;M`z|Q`VA9X$|96PJ{pe diff --git a/src/main/generated/com/app/treen/QBaseTimeEntity.java b/src/main/generated/com/app/treen/QBaseTimeEntity.java index 2563df0..7674606 100644 --- a/src/main/generated/com/app/treen/QBaseTimeEntity.java +++ b/src/main/generated/com/app/treen/QBaseTimeEntity.java @@ -21,11 +21,7 @@ public class QBaseTimeEntity extends EntityPathBase { public final DateTimePath createdDate = createDateTime("createdDate", java.time.LocalDateTime.class); -<<<<<<< HEAD - public final DateTimePath modifiedAt = createDateTime("modifiedAt", java.time.LocalDateTime.class); -======= public final DateTimePath modifiedDate = createDateTime("modifiedDate", java.time.LocalDateTime.class); ->>>>>>> chatting public QBaseTimeEntity(String variable) { super(BaseTimeEntity.class, forVariable(variable)); diff --git a/src/main/generated/com/app/treen/products/entity/QTradeProduct.java b/src/main/generated/com/app/treen/products/entity/QTradeProduct.java index b83f5ec..f19ad2f 100644 --- a/src/main/generated/com/app/treen/products/entity/QTradeProduct.java +++ b/src/main/generated/com/app/treen/products/entity/QTradeProduct.java @@ -42,7 +42,7 @@ public class QTradeProduct extends EntityPathBase { public final EnumPath method = createEnum("method", com.app.treen.products.entity.enumeration.Method.class); //inherited - public final DateTimePath modifiedAt = _super.modifiedAt; + public final DateTimePath modifiedDate = _super.modifiedDate; public final StringPath name = createString("name"); diff --git a/src/main/generated/com/app/treen/products/entity/QTransProduct.java b/src/main/generated/com/app/treen/products/entity/QTransProduct.java index a95bc3f..c64cfa6 100644 --- a/src/main/generated/com/app/treen/products/entity/QTransProduct.java +++ b/src/main/generated/com/app/treen/products/entity/QTransProduct.java @@ -42,11 +42,7 @@ public class QTransProduct extends EntityPathBase { public final EnumPath method = createEnum("method", com.app.treen.products.entity.enumeration.Method.class); //inherited -<<<<<<< HEAD - public final DateTimePath modifiedAt = _super.modifiedAt; -======= public final DateTimePath modifiedDate = _super.modifiedDate; ->>>>>>> chatting public final StringPath name = createString("name"); diff --git a/src/main/generated/com/app/treen/user/entity/QRefreshToken.java b/src/main/generated/com/app/treen/user/entity/QRefreshToken.java index 6c282ee..d6454c2 100644 --- a/src/main/generated/com/app/treen/user/entity/QRefreshToken.java +++ b/src/main/generated/com/app/treen/user/entity/QRefreshToken.java @@ -29,7 +29,7 @@ public class QRefreshToken extends EntityPathBase { public final StringPath keyUserId = createString("keyUserId"); //inherited - public final DateTimePath modifiedAt = _super.modifiedAt; + public final DateTimePath modifiedDate = _super.modifiedDate; public final StringPath refreshToken = createString("refreshToken"); diff --git a/src/main/generated/com/app/treen/user/entity/QUser.java b/src/main/generated/com/app/treen/user/entity/QUser.java index 8a1b254..b653605 100644 --- a/src/main/generated/com/app/treen/user/entity/QUser.java +++ b/src/main/generated/com/app/treen/user/entity/QUser.java @@ -20,24 +20,10 @@ public class QUser extends EntityPathBase { public static final QUser user = new QUser("user"); -<<<<<<< HEAD -======= - public final DateTimePath birthDate = createDateTime("birthDate", java.time.LocalDateTime.class); - - public final NumberPath gender = createNumber("gender", Integer.class); - - public final NumberPath height = createNumber("height", Integer.class); - ->>>>>>> chatting public final NumberPath id = createNumber("id", Long.class); public final StringPath loginId = createString("loginId"); -<<<<<<< HEAD -======= - public final StringPath nickname = createString("nickname"); - ->>>>>>> chatting public final StringPath password = createString("password"); public final StringPath phoneNum = createString("phoneNum"); diff --git a/src/main/java/com/app/treen/chat_room/controller/ChatRoomController.java b/src/main/java/com/app/treen/chat_room/controller/ChatRoomController.java index 8f78c6a..eb0e6d8 100644 --- a/src/main/java/com/app/treen/chat_room/controller/ChatRoomController.java +++ b/src/main/java/com/app/treen/chat_room/controller/ChatRoomController.java @@ -1,136 +1,95 @@ -//package com.app.treen.chat_room.controller; -// -//import com.app.treen.chat_room.dto.request.ChatRoomRequestDto; -//import com.app.treen.chat_room.dto.request.MessageRequestDto; -//import com.app.treen.chat_room.dto.response.*; -//import com.app.treen.chat_room.service.ChatRoomService; -//import com.app.treen.common.jwt.service.dto.CustomUserDetails; -//import com.app.treen.common.response.ApiResponse; -//import jakarta.validation.Valid; -//import lombok.RequiredArgsConstructor; -//import org.springframework.http.ResponseEntity; -//import org.springframework.messaging.handler.annotation.MessageMapping; -//import org.springframework.messaging.simp.SimpMessageSendingOperations; -//import org.springframework.security.core.annotation.AuthenticationPrincipal; -//import org.springframework.web.bind.annotation.*; -//import reactor.core.publisher.Flux; -//import reactor.core.publisher.Mono; -// -//import java.util.List; -// -//@RestController -//@RequiredArgsConstructor -//public class ChatRoomController { -// -// private final SimpMessageSendingOperations template; -// private final ChatRoomService chatRoomService; -// -// // 채팅리스트 반환 -//// @GetMapping("/chat/{id}") -//// public ResponseEntity> getChatMessages(@PathVariable Long id) { -//// // 임시로 리스트 형식으로 구현, 실제로는 DB 접근 필요 -//// ChatMessageResponseDto test = new ChatMessageResponseDto(1L, "test", "test"); -//// return ResponseEntity.ok().body(List.of(test)); -//// } -// -// // 메시지 송신 및 수신, /pub가 생략된 모습. 클라이언트 단에서는 /pub/message 로 요청 -//// @MessageMapping("/message") -//// public ResponseEntity receiveMessage(@RequestBody MessageResponseDto chat) { -//// // 메시지를 해당 채팅방 구독자들에게 전송 -//// template.convertAndSend("/sub/chatroom/1", chat); -//// return ResponseEntity.ok().build(); -//// } -// -// // 채팅방 목록 조회 -// @GetMapping("/auth/chat-list") -// public ApiResponse getChatRooms(@AuthenticationPrincipal CustomUserDetails userDetails) { -// -// ChatRoomsResponse chatRoomsResponse = ChatRoomsResponse.builder() -// .chatRooms(chatRoomService.getChatRooms(userDetails.getMember().getId())) -// .build(); -// -// return ApiResponse.onSuccess(chatRoomsResponse); -// } -// -// // 채팅방 조회 -// @GetMapping("auth/{chatRoomId}") -// public Mono> getChatRoomDetail(@AuthenticationPrincipal CustomUserDetails userDetails, -// @PathVariable Long chatRoomId) { -// -// return chatRoomService.getChatRoomDetail(userDetails.getMember().getId(), chatRoomId) -// .map(ApiResponse::onSuccess); -// } -// -// // 채팅방 생성 -// @PostMapping("auth/create") -// public ApiResponse createChatRoom(@AuthenticationPrincipal CustomUserDetails userDetails, -// @Valid @RequestBody ChatRoomRequestDto dto) { -// Long chatRoomId = chatRoomService.saveChatRoom(userDetails.getMember().getId(), -// dto.getSellerId(), dto.getProductId()); -// return ApiResponse.onSuccess(ChatRoomCreateResponse.builder() -// .chatRoomId(chatRoomId) -// .build()); -// } -// -// // 이전 채팅 메시지들 조회 -// @GetMapping("/find/chat/list/{id}") -// public Mono>> findMessages(@PathVariable("id") Long id) { -// Flux response = chatRoomService.findChatMessages(id); -// return response.collectList().map(ApiResponse::onSuccess); -// } -// -// // 메시지 송신 및 수신 -// @MessageMapping("/message") -// public Mono> receiveMessage(@RequestBody MessageRequestDto chat) { -// return chatRoomService.saveChatMessage(chat).doOnNext(message -> { -// // 메시지를 해당 채팅방 구독자들에게 전송 -// template.convertAndSend("/sub/chatroom/" + chat.getRoomId(), -// MessageResponseDto.of(message)); -// -// }).thenReturn(ResponseEntity.ok().build()); -// } -// -//} +package com.app.treen.chat_room.controller; + +import com.app.treen.chat_room.dto.request.ChatRoomRequestDto; +import com.app.treen.chat_room.dto.request.MessageRequestDto; +import com.app.treen.chat_room.dto.response.*; +import com.app.treen.chat_room.service.ChatRoomService; +import com.app.treen.user.service.CustomUserDetails; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.messaging.handler.annotation.MessageMapping; +import org.springframework.messaging.simp.SimpMessageSendingOperations; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +public class ChatRoomController { + + private final SimpMessageSendingOperations template; + private final ChatRoomService chatRoomService; // 채팅방 목록 조회 @GetMapping("/auth/chat-list") - public ApiResponse getChatRooms(@AuthenticationPrincipal CustomUserDetails userDetails) { + public ResponseEntity getChatRooms(@AuthenticationPrincipal CustomUserDetails userDetails) { ChatRoomsResponse chatRoomsResponse = ChatRoomsResponse.builder() - .chatRooms(chatRoomService.getChatRooms(userDetails.getMember().getId())) + .chatRooms(chatRoomService.getChatRooms(userDetails.getUser().getId())) .build(); - return ApiResponse.onSuccess(chatRoomsResponse); + return ResponseEntity.ok(chatRoomsResponse); } // 채팅방 조회 @GetMapping("auth/{chatRoomId}") - public Mono> getChatRoomDetail(@AuthenticationPrincipal CustomUserDetails userDetails, + public Mono> getChatRoomDetail(@AuthenticationPrincipal CustomUserDetails userDetails, @PathVariable Long chatRoomId) { - return chatRoomService.getChatRoomDetail(userDetails.getMember().getId(), chatRoomId) - .map(ApiResponse::onSuccess); + return chatRoomService.getChatRoomDetail(userDetails.getUser().getId(), chatRoomId) + .map(ResponseEntity::ok); } // 채팅방 생성 @PostMapping("auth/create") - public ApiResponse createChatRoom(@AuthenticationPrincipal CustomUserDetails userDetails, + public ResponseEntity createChatRoom(@AuthenticationPrincipal CustomUserDetails userDetails, @Valid @RequestBody ChatRoomRequestDto dto) { - Long chatRoomId = chatRoomService.saveChatRoom(userDetails.getMember().getId(), + Long chatRoomId = chatRoomService.saveChatRoom(userDetails.getUser().getId(), dto.getSellerId(), dto.getProductId()); - return ApiResponse.onSuccess(ChatRoomCreateResponse.builder() + return ResponseEntity.ok(ChatRoomCreateResponse.builder() .chatRoomId(chatRoomId) .build()); } // 이전 채팅 메시지들 조회 @GetMapping("/find/chat/list/{id}") - public Mono>> findMessages(@PathVariable("id") Long id) { + public Mono>> findMessages(@PathVariable("id") Long id) { + Flux response = chatRoomService.findChatMessages(id); + return response.collectList().map(ResponseEntity::ok); + } + + // ======== 테스트 API 작성 ========== + + // 채팅방 조회 + @GetMapping("test/{chatRoomId}") + public Mono> getChatRoomDetailTest( + @PathVariable Long chatRoomId) { + + return chatRoomService.getChatRoomDetail(1L, chatRoomId) + .map(ResponseEntity::ok); + } + + @GetMapping("test/create") + public ResponseEntity createChatRoomTest( + @Valid @RequestBody ChatRoomRequestDto dto) { + Long chatRoomId = chatRoomService.saveChatRoom(1L, + dto.getSellerId(), dto.getProductId()); + return ResponseEntity.ok(ChatRoomCreateResponse.builder() + .chatRoomId(chatRoomId) + .build()); + } + + @GetMapping("test/find/chat/list/{id}") + public Mono>> findMessagesTest(@PathVariable("id") Long id) { Flux response = chatRoomService.findChatMessages(id); - return response.collectList().map(ApiResponse::onSuccess); + return response.collectList().map(ResponseEntity::ok); } -// // 메시지 송신 및 수신 + // 메시지 송신 및 수신 // @MessageMapping("/message") // public Mono> receiveMessage(@RequestBody MessageRequestDto chat) { // return chatRoomService.saveChatMessage(chat).doOnNext(message -> { diff --git a/src/main/java/com/app/treen/chat_room/service/ChatRoomService.java b/src/main/java/com/app/treen/chat_room/service/ChatRoomService.java index ea5d048..96ffde4 100644 --- a/src/main/java/com/app/treen/chat_room/service/ChatRoomService.java +++ b/src/main/java/com/app/treen/chat_room/service/ChatRoomService.java @@ -66,7 +66,7 @@ public List getChatRooms(Long loginMemberId) { .unreadCount(getUnreadCount(chatRoom, member)) //.otherMember(getOtherMemberDto(chatRoom.getOtherMember(loginMemberId))) .lastMessage(getLastMessageDto(chatRoom)) - .isReserved().build()) + .isReserved(false).build()) .collect(Collectors.toList()); } diff --git a/src/main/java/com/app/treen/products/controller/ProductController.java b/src/main/java/com/app/treen/products/controller/ProductController.java index 50ba911..c06452c 100644 --- a/src/main/java/com/app/treen/products/controller/ProductController.java +++ b/src/main/java/com/app/treen/products/controller/ProductController.java @@ -1,6 +1,6 @@ package com.app.treen.products.controller; -import com.app.treen.common.response.ApiResponse; + import com.app.treen.common.response.code.status.SuccessStatus; import com.app.treen.products.dto.ProductQueryHelper; import com.app.treen.products.dto.TradeQueryHelper; diff --git a/src/main/java/com/app/treen/user/service/CustomUserDetails.java b/src/main/java/com/app/treen/user/service/CustomUserDetails.java index e3ece90..41e2101 100644 --- a/src/main/java/com/app/treen/user/service/CustomUserDetails.java +++ b/src/main/java/com/app/treen/user/service/CustomUserDetails.java @@ -18,7 +18,7 @@ public CustomUserDetails(User user) { this.user = user; } - public User getuser() { + public User getUser() { System.out.println("getuser ... function call"); return user; } From 66d820178069d0d58ef433af65d1ec6e3d1b3dcd Mon Sep 17 00:00:00 2001 From: Leeyoujin00 Date: Sun, 19 Jan 2025 17:13:19 +0900 Subject: [PATCH 02/12] =?UTF-8?q?[REFACTOR]=20User=20=ED=95=84=EB=93=9C?= =?UTF-8?q?=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/app/treen/products/dto/request/ImgRequestDto.java | 2 -- .../com/app/treen/products/dto/request/TradeImgRequestDto.java | 3 +++ .../com/app/treen/user/service/CustomUserDetailService.java | 2 +- src/main/java/com/app/treen/user/service/UserService.java | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) delete mode 100644 src/main/java/com/app/treen/products/dto/request/ImgRequestDto.java create mode 100644 src/main/java/com/app/treen/products/dto/request/TradeImgRequestDto.java diff --git a/src/main/java/com/app/treen/products/dto/request/ImgRequestDto.java b/src/main/java/com/app/treen/products/dto/request/ImgRequestDto.java deleted file mode 100644 index bd68fdb..0000000 --- a/src/main/java/com/app/treen/products/dto/request/ImgRequestDto.java +++ /dev/null @@ -1,2 +0,0 @@ -package com.app.treen.products.dto.request;public class TradeImgRequestDto { -} diff --git a/src/main/java/com/app/treen/products/dto/request/TradeImgRequestDto.java b/src/main/java/com/app/treen/products/dto/request/TradeImgRequestDto.java new file mode 100644 index 0000000..0c85d73 --- /dev/null +++ b/src/main/java/com/app/treen/products/dto/request/TradeImgRequestDto.java @@ -0,0 +1,3 @@ +package com.app.treen.products.dto.request; +public class TradeImgRequestDto { +} diff --git a/src/main/java/com/app/treen/user/service/CustomUserDetailService.java b/src/main/java/com/app/treen/user/service/CustomUserDetailService.java index 3042348..42570fb 100644 --- a/src/main/java/com/app/treen/user/service/CustomUserDetailService.java +++ b/src/main/java/com/app/treen/user/service/CustomUserDetailService.java @@ -1,8 +1,8 @@ package com.app.treen.user.service; import com.app.treen.common.response.code.status.ErrorStatus; +import com.app.treen.jpa.repository.user.UserRepository; import com.app.treen.user.entity.User; -import com.app.treen.user.repository.UserRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.userdetails.UserDetails; diff --git a/src/main/java/com/app/treen/user/service/UserService.java b/src/main/java/com/app/treen/user/service/UserService.java index a9d6e30..c3e4cdd 100644 --- a/src/main/java/com/app/treen/user/service/UserService.java +++ b/src/main/java/com/app/treen/user/service/UserService.java @@ -2,6 +2,7 @@ import com.app.treen.common.response.code.status.ErrorStatus; import com.app.treen.common.response.exception.CustomException; +import com.app.treen.jpa.repository.user.UserRepository; import com.app.treen.user.dto.request.CustomUserInfoDto; import com.app.treen.user.dto.request.JoinRequestDto; import com.app.treen.user.dto.request.LoginRequestDto; @@ -12,7 +13,6 @@ import com.app.treen.user.entity.RoleType; import com.app.treen.user.entity.User; import com.app.treen.user.entity.repository.RefreshTokenRepository; -import com.app.treen.user.repository.UserRepository; import jakarta.transaction.Transactional; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; From fb9dc686ae7828e2e3d373f73abead45ccfc4322 Mon Sep 17 00:00:00 2001 From: Leeyoujin00 Date: Tue, 21 Jan 2025 17:57:56 +0900 Subject: [PATCH 03/12] =?UTF-8?q?[REFACTOR]=20=EC=B1=84=ED=8C=85=20Redis?= =?UTF-8?q?=20=EA=B5=AC=EC=84=B1=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 16 +++++++++ .gitignore | 2 ++ .gradle/8.11.1/fileHashes/fileHashes.bin | Bin 37747 -> 37747 bytes .gradle/8.11.1/fileHashes/fileHashes.lock | Bin 17 -> 17 bytes build/reports/problems/problems-report.html | 2 +- .../controller/ChatRoomController.java | 13 ++++--- .../chat_room/service/ChatRoomService.java | 11 +++--- .../treen/common/config/ChattingConfig.java | 3 +- .../app/treen/common/config/RedisConfig.java | 32 +++++++++++++++--- .../common/config/WebSecurityConfig.java | 9 ++--- .../service/RedisMessageStringSubscriber.java | 12 +++++++ .../redis/service/RedisTopicManager.java | 4 +-- .../message/controller/MessageController.java | 6 ++-- 13 files changed, 85 insertions(+), 25 deletions(-) diff --git a/.env b/.env index e69de29..222453a 100644 --- a/.env +++ b/.env @@ -0,0 +1,16 @@ +# AWS 설정 +AWS_ACCESS_KEY_ID=jdtDjTprqDxccFNKAlEprGcx71qRb3JehuyJR9UL +AWS_SECRET_KEY=jdtDjTprqDxccFNKAlEprGcx71qRb3JehuyJR9UL +AWS_REGION=ap-northeast-2 +AWS_S3_BUCKET=treen-bucket +JWT_SECRET_KEY=780f7e25c9e36b276459459a13b081bbe121af603c2d039b4228739a690d399c75386d3d89f63a099ba817e3764fbf1c7bf744c060c24d2e812e10d511cc3b64 +JWT_REFRESH_KEY=4dedc73032e5574ca6133ddfd6c767b4d7567012482d21f3a268248209997fc52af16f46a3624da10116d9e3e6cacd2ae0750b468738d15e12ba9952d710a7af + +# Mongo DB 설정 +MONGO_DB_URI=mongodb+srv//cymn6032:treen2025dev@cluster0.q68ku.mongodb.net/treen_dev +MONGO_DB_DATABASE=treen + +# CoolSMS API 정보 +COOLSMS_KEY=NCSWESNWGBKDDJTL +COOLSMS_SECRET_KEY=0I2YYHU66XWK2QXNGB76UM0VMUFVMWP0 +COOLSMS_SEND_NUMBER=01048466314 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 39b0984..6052fe2 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ build/ !**/src/test/**/build/ h2/ src/main/resources/application.yaml +src/main/resources/application.yml ### STS ### .apt_generated @@ -29,6 +30,7 @@ out/ !**/src/test/**/out/ ### env ### +.env src/main/java/com/app/treen/.env ### NetBeans ### diff --git a/.gradle/8.11.1/fileHashes/fileHashes.bin b/.gradle/8.11.1/fileHashes/fileHashes.bin index 7c51d503b4c442d8e7287c2ac0595c9c55ad49f7..97c729b676269fb1502d18ba7adff1bfc5c101fe 100644 GIT binary patch delta 225 zcmeyojOp_-rVWiA3Y{-?XXMWN`TVc^{>b%*)AuqkF!pP zQ1maF9Ox@N*~`-~DCkUg^3g?IPniE@H|>2};)SA6SZ*Vbk7jh@T(6R<_m>t0E&3?) T!NXJ+MF+zJhs|d_mw5sJj{-se diff --git a/.gradle/8.11.1/fileHashes/fileHashes.lock b/.gradle/8.11.1/fileHashes/fileHashes.lock index df1dfca12827e9ee28165afcb87c4e547acaa2ae..262e4c9cc08021e108bea3d9d75664fcc7349a58 100644 GIT binary patch literal 17 VcmZRc{Ql{ZH8H$x3}C?h697J%1+D-9 literal 17 VcmZRc{Ql{ZH8H$x3}C?h2>?Em1*ZT2 diff --git a/build/reports/problems/problems-report.html b/build/reports/problems/problems-report.html index deae76d..c7cc2a7 100644 --- a/build/reports/problems/problems-report.html +++ b/build/reports/problems/problems-report.html @@ -650,7 +650,7 @@ diff --git a/src/main/java/com/app/treen/chat_room/controller/ChatRoomController.java b/src/main/java/com/app/treen/chat_room/controller/ChatRoomController.java index c6f000e..6af93d8 100644 --- a/src/main/java/com/app/treen/chat_room/controller/ChatRoomController.java +++ b/src/main/java/com/app/treen/chat_room/controller/ChatRoomController.java @@ -19,6 +19,7 @@ @RestController @RequiredArgsConstructor +@RequestMapping("/api/chatroom") public class ChatRoomController { private final SimpMessageSendingOperations template; @@ -38,7 +39,7 @@ public ResponseEntity getChatRooms(@AuthenticationPrincipal C // 채팅방 조회 @GetMapping("auth/{chatRoomId}") public Mono> getChatRoomDetail(@AuthenticationPrincipal CustomUserDetails userDetails, - @PathVariable Long chatRoomId) { + @PathVariable Long chatRoomId) { return chatRoomService.getChatRoomDetail(userDetails.getUser().getId(), chatRoomId) .map(ResponseEntity::ok); @@ -47,7 +48,7 @@ public Mono> getChatRoomDetail(@Authentic // 채팅방 생성 @PostMapping("auth/create") public ResponseEntity createChatRoom(@AuthenticationPrincipal CustomUserDetails userDetails, - @Valid @RequestBody ChatRoomRequestDto dto) { + @Valid @RequestBody ChatRoomRequestDto dto) { Long chatRoomId = chatRoomService.saveChatRoom(userDetails.getUser().getId(), dto.getSellerId(), dto.getProductId()); return ResponseEntity.ok(ChatRoomCreateResponse.builder() @@ -67,15 +68,15 @@ public Mono>> findMessages(@PathVariable // 채팅방 조회 @GetMapping("test/{chatRoomId}") public Mono> getChatRoomDetailTest( - @PathVariable Long chatRoomId) { + @PathVariable("chatRoomId") Long chatRoomId) { return chatRoomService.getChatRoomDetail(1L, chatRoomId) .map(ResponseEntity::ok); } - @GetMapping("test/create") + @PostMapping("test/create") public ResponseEntity createChatRoomTest( - @Valid @RequestBody ChatRoomRequestDto dto) { + @Valid @RequestBody ChatRoomRequestDto dto) { Long chatRoomId = chatRoomService.saveChatRoom(1L, dto.getSellerId(), dto.getProductId()); return ResponseEntity.ok(ChatRoomCreateResponse.builder() @@ -89,6 +90,8 @@ public Mono>> findMessagesTest(@PathVari return response.collectList().map(ResponseEntity::ok); } +} + // 메시지 송신 및 수신 // @MessageMapping("/message") // public Mono> receiveMessage(@RequestBody MessageRequestDto chat) { diff --git a/src/main/java/com/app/treen/chat_room/service/ChatRoomService.java b/src/main/java/com/app/treen/chat_room/service/ChatRoomService.java index af76596..67ac20c 100644 --- a/src/main/java/com/app/treen/chat_room/service/ChatRoomService.java +++ b/src/main/java/com/app/treen/chat_room/service/ChatRoomService.java @@ -1,4 +1,3 @@ -/* package com.app.treen.chat_room.service; import com.app.treen.common.response.code.status.ErrorStatus; @@ -49,8 +48,8 @@ public long saveChatRoom(Long loginMemberId, Long sellerId, Long productId) { .orElseThrow()) .seller(userRepository.findById(sellerId) .orElseThrow()) - .transProduct(transProductRepository.findById(productId) - .orElseThrow()) + //.transProduct(transProductRepository.findById(productId) + //.orElseThrow()) //.isReserved() .build(); @@ -125,6 +124,10 @@ public void leaveChatRoom(Long chatRoomId) { } } +// private ChatRoomResponse.MemberDto getOtherMemberDto(Member otherMember) { +// return ChatRoomResponse.MemberDto.of(otherMember, fileManager.getFullPath(otherMember.getImage())); +// } + // 채팅방 마지막 메시지 조회 // private ChatRoomResponse.MessageDto getLastMessageDto(ChatRoom chatRoom) { // return messageRepository.findFirstByChatRoomOrderByIdDesc(chatRoom) @@ -133,4 +136,4 @@ public void leaveChatRoom(Long chatRoomId) { // // } } -*/ + diff --git a/src/main/java/com/app/treen/common/config/ChattingConfig.java b/src/main/java/com/app/treen/common/config/ChattingConfig.java index 955c152..87fc1c1 100644 --- a/src/main/java/com/app/treen/common/config/ChattingConfig.java +++ b/src/main/java/com/app/treen/common/config/ChattingConfig.java @@ -15,6 +15,7 @@ public class ChattingConfig implements WebSocketMessageBrokerConfigurer { private final StompHandler stompHandler; + /** * 클라이언트에서 WebSocket에 접속할 수 있는 endpoint를 지정 * @param registry @@ -23,7 +24,7 @@ public class ChattingConfig implements WebSocketMessageBrokerConfigurer { public void registerStompEndpoints(StompEndpointRegistry registry) { // stomp 접속 주소 url = ws://localhost:8080/ws, 프로토콜이 http가 아니다! registry.addEndpoint("/ws") // 연결될 엔드포인트 - .setAllowedOrigins("*").withSockJS(); + .setAllowedOrigins("http://localhost:3000").withSockJS(); } /** diff --git a/src/main/java/com/app/treen/common/config/RedisConfig.java b/src/main/java/com/app/treen/common/config/RedisConfig.java index ad65c9b..dc0bb2f 100644 --- a/src/main/java/com/app/treen/common/config/RedisConfig.java +++ b/src/main/java/com/app/treen/common/config/RedisConfig.java @@ -1,14 +1,18 @@ package com.app.treen.common.config; import com.app.treen.common.redis.service.RedisMessageStringSubscriber; +import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.listener.PatternTopic; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; import org.springframework.data.redis.serializer.StringRedisSerializer; +import org.springframework.messaging.simp.SimpMessagingTemplate; @Configuration public class RedisConfig { @@ -36,18 +40,36 @@ public RedisTemplate stringValueRedisTemplate() { * Redis의 channel 로부터 메시지를 수신받아 해당 MessageListenerAdapter 에게 디스패치 */ @Bean - public RedisMessageListenerContainer redisContainer() { + public RedisMessageListenerContainer redisContainer( + RedisConnectionFactory redisConnectionFactory, + MessageListenerAdapter messageStringListener) { + RedisMessageListenerContainer container = new RedisMessageListenerContainer(); - container.setConnectionFactory(redisConnectionFactory()); - return container; // 초기에는 토픽이 비어있음 + container.setConnectionFactory(redisConnectionFactory); + + // PatternTopic을 사용하여 chatroom/* 형식의 모든 채널을 구독 + container.addMessageListener(messageStringListener, new PatternTopic("chatroom/*")); + + return container; } /** * 메시지 수신 리스너 어댑터 (메시지 핸들러를 설정) */ @Bean - public MessageListenerAdapter messageStringListener() { - return new MessageListenerAdapter(new RedisMessageStringSubscriber(), "onMessage"); + public MessageListenerAdapter messageStringListener(SimpMessagingTemplate simpMessagingTemplate, ObjectMapper objectMapper) { + return new MessageListenerAdapter(new RedisMessageStringSubscriber(simpMessagingTemplate, objectMapper), "onMessage"); + } + + + @Primary + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory factory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new StringRedisSerializer()); // 이 부분을 확인 + redisTemplate.setConnectionFactory(redisConnectionFactory()); + return redisTemplate; } // /** diff --git a/src/main/java/com/app/treen/common/config/WebSecurityConfig.java b/src/main/java/com/app/treen/common/config/WebSecurityConfig.java index ea8f861..47d1a48 100644 --- a/src/main/java/com/app/treen/common/config/WebSecurityConfig.java +++ b/src/main/java/com/app/treen/common/config/WebSecurityConfig.java @@ -61,7 +61,9 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti "/swagger-ui/**", // Swagger UI "/v3/api-docs/**", "/swagger-resources/**", - "/webjars/**" + "/webjars/**", + "/ws/**", + "/**" ).permitAll() .requestMatchers("/api/auth/**").permitAll() // 인증이 필요한 경로 (JWT 필요) @@ -78,13 +80,11 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti // CORS 설정 @Bean public CorsConfigurationSource corsConfigurationSource() { - CorsConfiguration configuration = new CorsConfiguration(); - configuration.addAllowedOrigin("*"); + configuration.addAllowedOrigin("http://localhost:3000"); // 특정 도메인 허용 configuration.addAllowedHeader("*"); // 모든 헤더 허용 configuration.addAllowedMethod("*"); // 모든 HTTP 메서드 허용 configuration.setAllowCredentials(true); // 인증 정보 포함 요청 허용 - configuration.addAllowedHeader("Authorization"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); // 모든 경로에 대해 CORS 정책 적용 @@ -92,4 +92,5 @@ public CorsConfigurationSource corsConfigurationSource() { } + } \ No newline at end of file diff --git a/src/main/java/com/app/treen/common/redis/service/RedisMessageStringSubscriber.java b/src/main/java/com/app/treen/common/redis/service/RedisMessageStringSubscriber.java index dc2fdcc..2dddf8a 100644 --- a/src/main/java/com/app/treen/common/redis/service/RedisMessageStringSubscriber.java +++ b/src/main/java/com/app/treen/common/redis/service/RedisMessageStringSubscriber.java @@ -1,7 +1,10 @@ package com.app.treen.common.redis.service; +import com.app.treen.message.dto.MessageRequest; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Service; /** @@ -11,11 +14,20 @@ @RequiredArgsConstructor @Service public class RedisMessageStringSubscriber { + private final SimpMessagingTemplate simpMessagingTemplate; + private final ObjectMapper objectMapper; // Redis 메시지를 수신했을 때 호출 public void onMessage(String message, String channel) { log.info("Received message: {} from channel: {}", message, channel); + try { + MessageRequest receivedMessage = objectMapper.readValue(message, MessageRequest.class); + String destination = "/sub/chatroom/" + receivedMessage.getRoomId(); + simpMessagingTemplate.convertAndSend(destination, receivedMessage); + } catch (Exception e) { + log.error("Failed to process received message", e); + } // 실시간 거래 데이터 처리 // =========== diff --git a/src/main/java/com/app/treen/common/redis/service/RedisTopicManager.java b/src/main/java/com/app/treen/common/redis/service/RedisTopicManager.java index 97fe61f..f7dcf18 100644 --- a/src/main/java/com/app/treen/common/redis/service/RedisTopicManager.java +++ b/src/main/java/com/app/treen/common/redis/service/RedisTopicManager.java @@ -15,13 +15,13 @@ public class RedisTopicManager { // 채널 추가 및 구독 public void addTopic(Long roomId) { - ChannelTopic channelTopic = new ChannelTopic("ch" + roomId); + ChannelTopic channelTopic = new ChannelTopic("chatroom/" + roomId); redisContainer.addMessageListener(messageListener, channelTopic); } // 채널 제거 및 구독 취소 public void removeTopic(Long roomId) { - ChannelTopic channelTopic = new ChannelTopic("ch" + roomId); + ChannelTopic channelTopic = new ChannelTopic("chatroom/" + roomId); redisContainer.removeMessageListener(messageListener, channelTopic); } diff --git a/src/main/java/com/app/treen/message/controller/MessageController.java b/src/main/java/com/app/treen/message/controller/MessageController.java index e5189f4..803079e 100644 --- a/src/main/java/com/app/treen/message/controller/MessageController.java +++ b/src/main/java/com/app/treen/message/controller/MessageController.java @@ -23,7 +23,7 @@ public class MessageController { private final MessageService messageService; //private final SimpMessagingTemplate template; - private RedisTemplate redisTemplate; + private final RedisTemplate redisTemplate; private final ObjectMapper objectMapper; // 메시지 송신 및 수신 @@ -35,9 +35,9 @@ public Mono> receiveMessage(@Valid MessageRequest messageRe try { // 메시지를 Redis 채널로 퍼블리시 String publishMessage = new ObjectMapper().writeValueAsString(messageRequest); - redisTemplate.convertAndSend("chatroom:" + messageRequest.getRoomId(), publishMessage); + redisTemplate.convertAndSend("chatroom/" + messageRequest.getRoomId(), publishMessage); - log.info("Message published to Redis channel: {}", "chatroom:" + messageRequest.getRoomId()); + log.info("Message published to Redis channel: {}", "chatroom/" + messageRequest.getRoomId()); // 응답 반환 return Mono.just(ResponseEntity.ok().build()); From cb83d54c8cad82defc67f408d2c0485f8efb2c93 Mon Sep 17 00:00:00 2001 From: Leeyoujin00 Date: Tue, 21 Jan 2025 18:14:56 +0900 Subject: [PATCH 04/12] =?UTF-8?q?[CHORE]=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 16 ---------------- .gitignore | 2 +- 2 files changed, 1 insertion(+), 17 deletions(-) delete mode 100644 .env diff --git a/.env b/.env deleted file mode 100644 index 222453a..0000000 --- a/.env +++ /dev/null @@ -1,16 +0,0 @@ -# AWS 설정 -AWS_ACCESS_KEY_ID=jdtDjTprqDxccFNKAlEprGcx71qRb3JehuyJR9UL -AWS_SECRET_KEY=jdtDjTprqDxccFNKAlEprGcx71qRb3JehuyJR9UL -AWS_REGION=ap-northeast-2 -AWS_S3_BUCKET=treen-bucket -JWT_SECRET_KEY=780f7e25c9e36b276459459a13b081bbe121af603c2d039b4228739a690d399c75386d3d89f63a099ba817e3764fbf1c7bf744c060c24d2e812e10d511cc3b64 -JWT_REFRESH_KEY=4dedc73032e5574ca6133ddfd6c767b4d7567012482d21f3a268248209997fc52af16f46a3624da10116d9e3e6cacd2ae0750b468738d15e12ba9952d710a7af - -# Mongo DB 설정 -MONGO_DB_URI=mongodb+srv//cymn6032:treen2025dev@cluster0.q68ku.mongodb.net/treen_dev -MONGO_DB_DATABASE=treen - -# CoolSMS API 정보 -COOLSMS_KEY=NCSWESNWGBKDDJTL -COOLSMS_SECRET_KEY=0I2YYHU66XWK2QXNGB76UM0VMUFVMWP0 -COOLSMS_SEND_NUMBER=01048466314 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 6052fe2..6b049c1 100644 --- a/.gitignore +++ b/.gitignore @@ -30,7 +30,7 @@ out/ !**/src/test/**/out/ ### env ### -.env +./.env src/main/java/com/app/treen/.env ### NetBeans ### From 937df4fdb88ce6176b0f29d6886adae1a43ec11b Mon Sep 17 00:00:00 2001 From: jiwonLee42 Date: Tue, 21 Jan 2025 21:26:41 +0900 Subject: [PATCH 05/12] =?UTF-8?q?[FEAT]=20application.yml=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build/resources/main/application.yml | 20 +++++++++++++------ .../controller/ProductController.java | 14 +++++++------ .../treen/user/controller/UserController.java | 8 ++++++++ src/main/resources/application.yml | 6 +++--- 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/build/resources/main/application.yml b/build/resources/main/application.yml index 0639e58..2d467aa 100644 --- a/build/resources/main/application.yml +++ b/build/resources/main/application.yml @@ -13,16 +13,15 @@ spring: username: sa # 기본 사용자명 password: jpa: - hibernate.ddl-auto: create + hibernate.ddl-auto: update show-sql: true properties: hibernate: format_sql: true - database-platform: org.hibernate.dialect.H2Dialect # H2Dialect 설정 data: mongodb: - uri: mongodb+srv://cymn6032:leeujin0309%40@cluster0.q68ku.mongodb.net/ - database: treen_dev + uri: ${MONGO_DB_URI} + database: ${MONGO_DB_DATABASE} # database-platform: org.hibernate.dialect.MySQL8Dialect cloud: aws: @@ -36,5 +35,14 @@ cloud: auto: false jwt: secret: - key: 780f7e25c9e36b276459459a13b081bbe121af603c2d039b4228739a690d399c75386d3d89f63a099ba817e3764fbf1c7bf744c060c24d2e812e10d511cc3b64 - refresh: 4dedc73032e5574ca6133ddfd6c767b4d7567012482d21f3a268248209997fc52af16f46a3624da10116d9e3e6cacd2ae0750b468738d15e12ba9952d710a7af \ No newline at end of file + key: ${JWT_SECRET_KEY} + refresh: ${JWT_REFRESH_KEY} + redis: + host: localhost + port: 6379 + repositories: + enabled: false +coolsms: + apiKey: ${COOL_SMS_KEY} + apiSecret: ${COOL_SMS_SECRET_KEY} + senderNumber: ${COOL_SMS_SEND_NUMBER} \ No newline at end of file diff --git a/src/main/java/com/app/treen/products/controller/ProductController.java b/src/main/java/com/app/treen/products/controller/ProductController.java index 7ee2ac8..f9ed7c9 100644 --- a/src/main/java/com/app/treen/products/controller/ProductController.java +++ b/src/main/java/com/app/treen/products/controller/ProductController.java @@ -39,10 +39,10 @@ public class ProductController { public ResponseEntity saveTransProduct( @RequestPart("images") List images, // 이미지 리스트로 변경 @RequestPart("product") TransProductSaveDto requestDto, - User user + @AuthenticationPrincipal CustomUserDetails userDetails ) throws IOException { // User user = new User(); // 현재 사용자의 정보를 가져오는 로직 추가 필요 - TransProductResponseDto responseDto = productService.saveTransProduct(requestDto,images, user); + TransProductResponseDto responseDto = productService.saveTransProduct(requestDto,images, userDetails.getUser()); return ResponseEntity.status(HttpStatus.OK).body(responseDto); } @@ -141,17 +141,19 @@ public ResponseEntity> getFilteredTradeResults( @Operation(summary = "거래상품 좋아요 및 취소", description = "좋아요 되어있을 경우 취소, 안되어있을 경우 좋아요 등록됩니다.") @GetMapping("/transaction/like/{productId}") - public ResponseEntity registerLikesTransProduct(@PathVariable Long productId, User user){ - boolean isLike = productService.increaseLikeTransaction(productId, user); + public ResponseEntity registerLikesTransProduct(@PathVariable Long productId, @AuthenticationPrincipal CustomUserDetails userDetails){ + boolean isLike = productService.increaseLikeTransaction(productId, userDetails.getUser()); return isLike ? ResponseEntity.status(HttpStatus.OK).body(SuccessStatus.PIN_LIKE) : ResponseEntity.status(HttpStatus.OK).body(SuccessStatus.PIN_UNLIKE); } @Operation(summary = "교환상품 좋아요 및 취소", description = "좋아요 되어있을 경우 취소, 안되어있을 경우 좋아요 등록됩니다.") @GetMapping("/trade/like/{productId}") - public ResponseEntity registerLikesTradeProduct(@PathVariable Long productId, User user){ - boolean isLike = productService.increaseLikeTrade(productId, user); + public ResponseEntity registerLikesTradeProduct(@PathVariable Long productId, @AuthenticationPrincipal CustomUserDetails userDetails){ + boolean isLike = productService.increaseLikeTrade(productId, userDetails.getUser()); return isLike ? ResponseEntity.status(HttpStatus.OK).body(SuccessStatus.PIN_LIKE) : ResponseEntity.status(HttpStatus.OK).body(SuccessStatus.PIN_UNLIKE); } + + } diff --git a/src/main/java/com/app/treen/user/controller/UserController.java b/src/main/java/com/app/treen/user/controller/UserController.java index e821d1f..67036d0 100644 --- a/src/main/java/com/app/treen/user/controller/UserController.java +++ b/src/main/java/com/app/treen/user/controller/UserController.java @@ -42,4 +42,12 @@ public ResponseEntity getMember(@AuthenticationPrincipal CustomUserDetails us // 회원탈퇴 + + // 핸드폰 인증 + + // 카카오 로그인 + + // 비밀번호 재설정 + + } \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 344d8cc..2d467aa 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -13,15 +13,15 @@ spring: username: sa # 기본 사용자명 password: jpa: - hibernate.ddl-auto: create + hibernate.ddl-auto: update show-sql: true properties: hibernate: format_sql: true data: mongodb: - uri: ${MONGO_DB_URL} - database: treen_dev + uri: ${MONGO_DB_URI} + database: ${MONGO_DB_DATABASE} # database-platform: org.hibernate.dialect.MySQL8Dialect cloud: aws: From 7f1979252c2150c06004c187bde8ae4f2ee7a928 Mon Sep 17 00:00:00 2001 From: jiwonLee42 Date: Wed, 22 Jan 2025 17:09:38 +0900 Subject: [PATCH 06/12] =?UTF-8?q?[FEAT]=20=EC=83=81=ED=92=88=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/TradeImgRequestDto.java | 3 --- .../dto/request/TransRegionRequestDto.java | 2 ++ .../dto/request/TransactionRequestDto.java | 19 +++++++++++++++++++ .../products/entity/CategoryInitializer.java | 2 ++ 4 files changed, 23 insertions(+), 3 deletions(-) delete mode 100644 src/main/java/com/app/treen/products/dto/request/TradeImgRequestDto.java create mode 100644 src/main/java/com/app/treen/products/dto/request/TransRegionRequestDto.java create mode 100644 src/main/java/com/app/treen/products/dto/request/TransactionRequestDto.java create mode 100644 src/main/java/com/app/treen/products/entity/CategoryInitializer.java diff --git a/src/main/java/com/app/treen/products/dto/request/TradeImgRequestDto.java b/src/main/java/com/app/treen/products/dto/request/TradeImgRequestDto.java deleted file mode 100644 index 0c85d73..0000000 --- a/src/main/java/com/app/treen/products/dto/request/TradeImgRequestDto.java +++ /dev/null @@ -1,3 +0,0 @@ -package com.app.treen.products.dto.request; -public class TradeImgRequestDto { -} diff --git a/src/main/java/com/app/treen/products/dto/request/TransRegionRequestDto.java b/src/main/java/com/app/treen/products/dto/request/TransRegionRequestDto.java new file mode 100644 index 0000000..e71090c --- /dev/null +++ b/src/main/java/com/app/treen/products/dto/request/TransRegionRequestDto.java @@ -0,0 +1,2 @@ +package com.app.treen.products.dto.request;public class TransRegionRequestDto { +} diff --git a/src/main/java/com/app/treen/products/dto/request/TransactionRequestDto.java b/src/main/java/com/app/treen/products/dto/request/TransactionRequestDto.java new file mode 100644 index 0000000..977e5e1 --- /dev/null +++ b/src/main/java/com/app/treen/products/dto/request/TransactionRequestDto.java @@ -0,0 +1,19 @@ +package com.app.treen.products.dto.request; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class TransactionRequestDto { + + private TransProductSaveDto productRequest; // ✅ 상품 요청 DTO 포함 + private RegionRequestDto regionRequest; // ✅ 지역 요청 DTO 포함 + private TransImgRequestDto imageRequest; // ✅ 이미지 요청 DTO 포함 + + public TransactionRequestDto(TransProductSaveDto productRequest, RegionRequestDto regionRequest, TransImgRequestDto imageRequest) { + this.productRequest = productRequest; + this.regionRequest = regionRequest; + this.imageRequest = imageRequest; + } +} \ No newline at end of file diff --git a/src/main/java/com/app/treen/products/entity/CategoryInitializer.java b/src/main/java/com/app/treen/products/entity/CategoryInitializer.java new file mode 100644 index 0000000..e2f2bbe --- /dev/null +++ b/src/main/java/com/app/treen/products/entity/CategoryInitializer.java @@ -0,0 +1,2 @@ +package com.app.treen.products.entity;public class CategoryInitializer { +} From a05ad7df0ca318a652d62c1cb7ca8c4f6f369b73 Mon Sep 17 00:00:00 2001 From: jiwonLee42 Date: Wed, 22 Jan 2025 17:10:21 +0900 Subject: [PATCH 07/12] =?UTF-8?q?[FEAT]=20=EC=83=81=ED=92=88=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 0 .../executionHistory/executionHistory.bin | Bin 765385 -> 765385 bytes .../executionHistory/executionHistory.lock | Bin 17 -> 17 bytes .gradle/8.11.1/fileHashes/fileHashes.bin | Bin 37747 -> 38247 bytes .gradle/8.11.1/fileHashes/fileHashes.lock | Bin 17 -> 17 bytes .../8.11.1/fileHashes/resourceHashesCache.bin | Bin 27031 -> 28527 bytes .../buildOutputCleanup.lock | Bin 17 -> 17 bytes .gradle/file-system.probe | Bin 8 -> 8 bytes .../main/com/app/treen/TreenApplication.class | Bin 1047 -> 1058 bytes .../app/treen/common/config/S3Uploader.class | Bin 6632 -> 6007 bytes .../response/code/status/ErrorStatus.class | Bin 14412 -> 14705 bytes .../controller/ProductController.class | Bin 12112 -> 12155 bytes .../dto/request/TransProductSaveDto.class | Bin 7300 -> 4907 bytes .../response/TransProductResponseDto.class | Bin 6860 -> 7379 bytes .../dto/response/TransResponseListDto.class | Bin 4963 -> 4255 bytes .../treen/products/entity/QTransProduct.class | Bin 5878 -> 5404 bytes .../app/treen/products/entity/TransPImg.class | Bin 2592 -> 2608 bytes .../TransProduct$TransProductBuilder.class | Bin 7261 -> 6711 bytes .../treen/products/entity/TransProduct.class | Bin 9536 -> 7385 bytes .../products/service/ProductService.class | Bin 22631 -> 25018 bytes build/reports/problems/problems-report.html | 2 +- build/resources/main/application.yml | 8 +- .../treen/products/entity/QTransProduct.java | 2 - .../java/com/app/treen/TreenApplication.java | 2 +- .../app/treen/common/config/S3Uploader.java | 31 ++++-- .../response/code/status/ErrorStatus.java | 3 + .../products/TransPImgRepository.java | 7 ++ .../controller/ProductController.java | 9 +- .../dto/request/TransImgRequestDto.java | 25 +++++ .../dto/request/TransProductSaveDto.java | 47 ++++----- .../dto/request/TransRegionRequestDto.java | 33 +++++- .../dto/request/TransactionRequestDto.java | 10 +- .../dto/response/TransProductResponseDto.java | 22 ++-- .../dto/response/TransResponseListDto.java | 8 +- .../products/entity/CategoryInitializer.java | 24 ++++- .../app/treen/products/entity/TransPImg.java | 4 +- .../treen/products/entity/TransProduct.java | 77 +++++--------- .../products/service/ProductService.java | 99 ++++++++++-------- .../user/service/CustomUserDetailService.java | 10 +- .../app/treen/user/service/JwtProvider.java | 33 ++++++ .../app/treen/user/service/UserService.java | 40 ++----- .../user/service/filter/JwtAuthFilter.java | 7 +- src/main/resources/application.yml | 8 +- 43 files changed, 295 insertions(+), 216 deletions(-) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 0000000..e69de29 diff --git a/.gradle/8.11.1/executionHistory/executionHistory.bin b/.gradle/8.11.1/executionHistory/executionHistory.bin index 7e08eae23db7b71a241f281a7ba9e574372dc379..fd4a227fb43ec50c9a5c5b3f97c2a76d25fe99e8 100644 GIT binary patch delta 5086 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

oo|iQIDj)m~OR%aQqU;snRPL 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 diff --git a/.gradle/8.11.1/fileHashes/fileHashes.lock b/.gradle/8.11.1/fileHashes/fileHashes.lock index 262e4c9cc08021e108bea3d9d75664fcc7349a58..60fa3f0c78420436c5cacfb0a574a9e33fb072c1 100644 GIT binary patch literal 17 VcmZRc{Ql{ZH8H$x3}C>=2LL^_1jYaW literal 17 VcmZRc{Ql{ZH8H$x3}C?h697J%1+D-9 diff --git a/.gradle/8.11.1/fileHashes/resourceHashesCache.bin b/.gradle/8.11.1/fileHashes/resourceHashesCache.bin index 1983f422818a75b39b498048c942a9d0afdf4c3a..12eb67a9a6f0ab776c971232661ec31e39592be2 100644 GIT binary patch delta 2409 zcmZvddpJ~S9LJ4IOSU-&Sq5Wfh=w8BA!D?}QgT_BNUL;3gP9=|=`zNphtjiK?@&l` zEyNBpQ8X%-MK>zNx{MO-hE}y%Nwn-a9{V_Zp6C4Yc|O1I@4cP(oO^;Bm*RwUO}48W0#(BP!RS;$TTu5#^!WRysmMlfeTbnOZM#Fg#n!)kM`53xmD&pa@ju znY>3h2Y_Ls5N>#1)!5CE-bLX7a9Y)T5?K?n=!1v%swkd?q~wNl9d!nn=gAf*zzL@RvsfQLs}0U+vm!f!Q?aS)tJt9OA3JqC4J(VtXYF)Tq~xYEKT zr{5;*pljqoXzDd!pdSy*PF=7mXed2{Kz0kr(LWC>8^w`Zlgj==V}BQ!u1dv0#80EG zw*)l^3>^jK211yme9_M+t?m>8!={0+VF*-Mve89eV;9Q2MAr9bz^&S62d}e0Fg?DE%9eBcQ%u$2;_bzvZOx3L3Qw_g2Q|L(CR&+8Kb#O z@3B(<;KZDAwEmpPoi=w!{M9tL_A3H)QMU;Ans`Ctsw-Qc4x?9LX5V3~=+Heg`JfR9P_^)g5d^we|tz}GBNR{KUJ zW!p-Tz4KNIC~|>#wHZ}eQ#H%Vb>TSNa*YRb0lM=CnlSgO%)juN4cuG12=8`9M_Slv zK343&8WtJ>{%a#e)U=?%6Z@LGtgr?tVB97zG1-mE2ymP{{p_bj!+N1j$@lhUP_PF> z&NZe_p4;+zQCTp?padq=J)0qwSfmJYoxmlRmCzOoe^IqxeV}Gx$MxQTNw9~d##=Q# z{Z6H%FON3!WvuByE;cA|@>YaT%C^B$o1cT4Zz{)QjXUMTIm8@t{ij5 z)Q*~7X@r=4Av*shHl!wfw4#x2?P~k!?MP9lprDH|wQBUtLH zK(M`L!wbB7!~|ch(uh^m>zt2h*B-Wwp!e(~oA14hHEv${LC)PzdVOF;^{m+AYcbYf zgZr&axdE;BpPpC7_w-m`3?AJ7qg6Ed{@E`i=rJ!hCI16PfhQc6m96Ek?YcIrYOwnJ zmic>R$(r$LsrZu4tkj*iYngoxf3L$Dda>@xnMs>EH=A9g3XQ4$3kGlT(-~kqGPk@w zl#-iVI6dT3?YW@*$H?gAv zo)Vm}Nj>3mVrz);uloxhVGS+%?Rq?oS18kA>=42_GdTaZS~458kjLWkBBy0xbai8v z$I(Bq221FlbMP#+>BtoU>GRblCf4|6e--6Jq|$n$XD-GuC)X{V3~%nvQfxcSiLvhM z8Pt=ij5%rrU_&bJ*5a2%4WBz?80Fg-9bAnyjN$7(g9+pLE5ww<_JY*^nM{63C$Tl~ z!+^}>&BT~3v8G7I+z()Je0kAU|0s#AyH5g!%tr) zF_R`GFO!8Oc#{<-kh8refdP|ND5sMZDPgmYDNg~D^(q;YCMy@SRV&E>voq diff --git a/.gradle/file-system.probe b/.gradle/file-system.probe index e6f14f51fe6351a29ba8a0d09d2aa7e25a68f2e3..6be594ce20cffc1d061033daa438b3a0874b885a 100644 GIT binary patch literal 8 PcmZQzV4TwPgWm!G2w(zX literal 8 PcmZQzV4PChvv@NA2!jIk diff --git a/build/classes/java/main/com/app/treen/TreenApplication.class b/build/classes/java/main/com/app/treen/TreenApplication.class index f2d9d487d1613ea141672ae1a9ea156e8ada018e..ef3dba5b3d61eb2fd869736888b1a6e6017e8175 100644 GIT binary patch delta 44 zcmbQvv4~^C4<>oJ?o#qRP!Y%-oCsat068 delta 39 vcmZ3)F`Z+>4<-rW4$#3V)y!qbu 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 literal 6632 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 diff --git a/build/classes/java/main/com/app/treen/common/response/code/status/ErrorStatus.class b/build/classes/java/main/com/app/treen/common/response/code/status/ErrorStatus.class index 8ccf3ebafd09c73f23a110f961fab993443978d7..be817ba6f90a9c93aed4443ae13454963b694ed6 100644 GIT binary patch delta 5304 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 delta 4954 zcmZwL2Y6J)769Nmld?C-W_Pla-m^&vgutef&`dVT27*a8EQu5;q9|ApY$&L2v7>+j z10vEbSRW;kfT9R?QN#)s6vc*u_;}AQ;yd?D*xmTS_mR#1=ggTob7$|~FDI^ibEepH z>Y1I4v7!99g>hgNYJEZO;(HuhJg1^k<63>*`u4iENiE*CNe$limij0L>}M#m>V^NeiwjA}EWIxw!> z9OqWY?9L(=)LX%0CR^KE-0f|{y}rhAo_Yg@g=Ci)&V~+wCG+Ck9923>x z_0=}k*LzwF@P-U1Ghn3h3#Yven9)AU=y*B+<*(!M6_u^t1#dawIZ%M-v!gHa5*g}aJ>zf8O$7Dz?JGR9DPmDDh;?+RXNJ+ zbAnc7zzu4Sqt)RL+CT$t0_IgWI-Kg1qtt$DAXI{Tc)O~WHTFA$hE2{_`(&zmR3_W+ z4mviv2))#)t_vQp!h>d`md55uxUG1Z z9uDSVn~y9>xH!)Rk6YmhGtF?<)p=Um%)EaG^RV?zYGJ}$(ScXYUEkc;qOZ-N>_IS;BMNqxkj5X33I zP&vtE;;RtENxo52l1s&RA&Ao)RU4A~h~puM6a7aWN_L6=g&^+Y&nh#eNcFtM{U@-{fd&s(`w=Zc#G}HaRAOqGu!dlX!EI`6>L3{xf~5wR zzy_%CsmW?*YOY8OiIo{VS)E9A+0t;Y8r&mkTX=@*pH^ne4iHvwsu^ijHuIdcc3)gx zpthzJ*$M-MSw-r{v_hLJKv=#+6{J_$@CMU8u)^R2)O4hQ0UBWN!KyR8D(Vy%-MXJO3DoOu)$mLc4gkxJT3Y>ai2Pz z;c~S1U|jWRm7Ccwd0Y?1m5&Ejc67m9RhV9yu+iX?dc?7f$!b&PV8@gmjICUxP9k62 zgRz}URb$o=$K^d3o0+MWXVn&570Tx7pELL@{M0nK&%v8#n@3-k>y~8K%IkYFuJcCK zo?YsgizPf`+|8{`W;S+pvs#f|+Dt_geW|e4jd+*BJf|uwr~7DDIq=--Tc34EWouy)(b} zJnm->^E($G9USJ@HNE0DI%6W$d@8{A(JYF;%lF}!_wbz)pB&78(H-p9F81dxc5*Ym z1O7h#jHFl;i-K_e0ppZk&Nykb0}8R{T@1{89?17IEWaPgs?+!Zb8`M6N(Ar&bIsZx zK~DGVqr0o6Y=_9gr@-0;0$&okAUUiHQoA5CSUeS%!`LEbVT+lKEnyC(SOQzhlG&n= z`BMD+V@d@6Nnn=qyJyKh<19F#vkQ9lm@E^gVQeM-T!oXZ#>v*O6tLMN-)S{0+R5*jS2oW z75^r5!$h?CvCs#Kd!uO{#w*3h&fWdWx!>%y=W#apxBQ6CH-4rI%`7W+Fc>SLUswn9 zmry35VjEO;vM|kq!aHEFgtH{n5L-0YMsz@(ga!%2iK8`-i0pu~CA3Ir-3ETF606;3 zp(>*!v{Sm!JlY^0BVin|)I45b8Ju*2gh}Ly+9wNL=6nfL$WydmBy^pr5-uiBC-=jp zBB}!}lQ2WV6%@$Q1FjO$G{MypW|2F!UngS7XG^%AJYV~b!b<)x33JJNYrk2E!QA*iSx2`-dWfcH$!mpOBxc{ZocC-M7KPCLmlwexAPnJ*fldi-8olK=KP|z$Wce#US!& zl3zmJq5U#3n0&hAGsw4Uzd{V52Ch8az;^9di=nj8Yb3vx@^_&7ae1@FSyX6_GE-G|f4|qX%$zPQGW%3i+Ulpz7uSvd#{HN{w z%}(~K`EWts<}iZ!I{&V$hQ9MGbJ(4hB`k`Cbw3__$G@k?groczzD6^^KT%ou4=n2c DuE12i diff --git a/build/classes/java/main/com/app/treen/products/controller/ProductController.class b/build/classes/java/main/com/app/treen/products/controller/ProductController.class index cd0ee2cfe4feb576fc675c23dbb3a4724da77d19..36318497c0c309ddfb998175bd82959367a90a18 100644 GIT binary patch delta 2629 zcmcIlYfzL`7=F&je*3YwEW66Oa@hrT<&G4iWkccUNKGZ6qs>_2FgXpQs6e=DKWuU< z!)itpe8sZ6S($bdF)t)`x6&-^VwRcF{jRCikILyi=aXgG7_=Y#IPdqK_dDl(-sd^D zo|->v684?x+5?~rf3zjEiz4dH7(uPh6q*rNGR%P_15P_!7*5VWD(%i1*>=D!Hc@Y? zceO}#&AA~|*Vr(`+1OhZEErpJW1nL$S zI8u>jAl(ixd;+=rE0?mg21h2c4EXH`V6?zk6~0xoQ;&9)a8PsHUg7Ahu#6JURE)FZ zA~{HFT#x_s3YaC|V72nKuqOC$~pcY$c;s6SC z!d*=n3F|~@+niC<<8sCPUXpYHvP z%uH5t`s{SZIimCLq%zUD%r(WK)#Xudx|B<2!6#FGW~R0& zjPY>>e&v;zAWqZNK&j<-fkJW_X;kHN($0XV+=aQYVjiz&Gc3^g>n+#@QB*i)JB;l} zEVXRKh@D7DvFt*IhAliG4s~o3ps~$I5(+3eJK(-i^}T4qA|8~jE)x~3x9LjTgojQL>q1k;&v?K z-W}BtL9C{Y*@ZT;sNAMrO@@i;7+!@4OxSJ;jER6Obb?2m=t?D5>%vD{e zPvMFxhN$R~^QvflY(Eox(tug(mW5H$@S>d5sw+vk>{y2by~s{bO9-c zxdcxrz;q^jb_q60z$dYZX_`y0S@AZ6hCYc!9Y|Z#N$ec_4h~#vZl(e0UKr@kJtIFSDm}9tU3GgJb1@Lwc@tfS>pr%I^Lny z!W>hN_(An(MlzNwJ!bRL0RD&`ey;vqP9kG{diW!H_|d}}Nj>~Hi1*Y5>sK0CRZMDR zaBitlxPkY3pGaoteU8W-=QRds)We~J+y-j(L9A9EO05n>Y4wrPs>&E#pzz?~e@FWz z>{IYCK4xn26=~sZAnBIKSER5=JtEgk@UBC$dEU0-S|#vCxcUOsXB7_*d|FoUnQKv! z7nm6%l9%xHV&2Im>bcQ)u{$#W(zo!?$cTe5XVl&ffRj|AGIS*ZGh5iTkNME|cxgRvUi737njmf>ZcS Ge*Xb6UId^3 delta 2650 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 TlY9;T{>fDFFZ>7p!=`TlhqfHN literal 7300 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 K22dUig6|)bTjTct delta 1471 zcmZ`(Yiv_>6#xC(^>ur@_O@Ny+m3BQVY9Sj6A4V&WD`x$jS-m=zBszkZElRsuFeO_ z9Ge2sXw1=Qe8eUD;s-xKx-JZiQR5fM5C*981>y^31$=>ufS$GsX&3x(&;LAs=XcKk z-g~TSa+!4S>i%~DR15E3>0(}i1S2-fh@eft)EUYQ(!Z7XB7hCNR*lI#PcFNd=;rL;FomSgiR!{HS{1h@9Xp?NE`=!&g);#30HAG?^ zJ)75sqv7sIEE4S#cs3`shBiqH=&~Hix2%taTe}*&uBRu~ z7mJ2_Ggy6AJ${q$JN_`>f{cs!Gd^I|3O)W7SX4%5l_FPTOJ}4tcB@r~ePKbiy9U^9U5tDSHtG9Q_VnA204BzCH~O z^24j6xTDH61TFH|dx$PLItCVxqjdu90{hVoOyEHc_Tk|PJfXpR=ty8N5S$G^b-nP@ zfzUX1C-7YAd?A4sQ|HSGyb=g7@@wOGGl9{)+)ax^EL-4VxsdxWf{ao)2T+DW+z3Ac z2x2jAX1Nj}tVTKNu>>1Xfo4>q9ZS*A(QR0U-B^y7umZ253U6`t9#k{u8s=MzNz~zE zZV||gh{HI-77vRl9OZSSv}3#&OOO=w9Z3A7^Y}Z6gi3)a73=ypgRh@fE{cxZ-P^ z;MPv8!AaJ1j8cnlSTitI6;82c!nZW#nkS~|ylbhjQKipe`i`0C8S8tx%^eg<%BNiO zaZF$yTy%riL8ESk=DVF)6;tjYb>(=xo*<36opTT>aZ?&$yGNlbIr(pM{+%ADHMe6e z6?+TFrYiKjC%a#b8l-8@T=d2aKhZ>r>d#_#gtI%Vuk8B^ imw3puCNx6Fza1guw9HRSTUy$MlWQc76$rObq5cOI3{I~A diff --git a/build/classes/java/main/com/app/treen/products/dto/response/TransResponseListDto.class b/build/classes/java/main/com/app/treen/products/dto/response/TransResponseListDto.class index 0e923636d00c0b60cbabe8a48186f0578eeec2a9..a959ce10f97405a8c8f970ecd6c09430ed7c2ada 100644 GIT binary patch literal 4255 zcmb7IX?GMw6up&X=w#9fF@!)sF(^wCkU>Nc22e;uf(gbfxFBk0rc64S?jE~)fVeNX z@B4=PzMZ4zIMI{i@dxQ}e!d-dwQn*9Fv%x?hpbL=4LWnkKAMRw84$V~LwaCuAxVShml0?eKs=bw}s%IBHR6Aa0@_ra)K9a}riI zn+Sa=-9*;+(z#R^B%~YK;Z$PSx7=VLniPnQ$*?yq(A?44cVU}8&mBwFp%KdsG?};v z%>vgf0-FxKgfD}v=LRwYA1sXe>>%t3J%K8lJ2aKz$I*h723k$5!fJudi{Q_r-Rq1A z)G5x+*odDIXqm@*DD>?D^ED>c;$ndnzRZ#yQLNBDC3_ufOw!yUGKEFTi|b5Ws<;^3 z;h5z}<%0DlE>l1?E+>@I1``{xNuZue-Bu{aJb#K_Cz1cQU{*I7M+ep!=oHxW|5hj( zTTEh@n5q(kc z4B7WF%62TxPh6u39W!wg1{iUHM%lg;A1Mq)X3)eChNB=I31oWEawi3LEs4esuIN~FZEL%J4K3vA{qv!B9F`&$lv(;X)@_#D3w&V!zB{ z(+ndJY{Wlm3a9%tNldug8M@;=*@$8yf7 z^oOm{3}>2kL>#w#Ta62oHQ~5T+IttN{sZtS$ zyrL{+MU0ZifaSBYgwm%KZAC#aKm2*~(sMD@QlvH)4GgtlHUPv?jvU*~g_5YTaWALP zYtq&fMFTT(YO>$T7Cg9ikVS)6sMkGZ2R1!>&~-gkE*TWc=^4)%^(GTHNLQtFnI`pC zM%!Sy!m4UB;dPma4{_U+JZ!78YtxpOJPo#~3qwa7^1Lvh7qgK^g5}HbZXEC7oPqaM zzLp$qd<{OxEHf9-%n@)6?zeKRR2BPt@qI>9f)3 zGbSN%{<(91{!p0sLMy-jtNP6N>~-n;?u-=#GBEJ9z{UmcC}*XCZw1!WayJhm%&RUW&P+>^(C zB@gCty^_6o^ecID9wU0eZPPfJhxMao@d(>pXkdFOYS0c7m-7+2fwpYKW^CePYBP58 zb#^T}`6lRM8Q#LGw~bXIfk(NjJ>rYU@Hj_gjp6FF%P&a(0=u7SpjDB{!(XBW!ojI?$S0KJFT@hj#9zok?1|pD)P+xQbTp<7xHa zAo_6#BkU;~kHBDia?Zw+wEh{|SWAs(@f?k5;MqQp7r0LgdhsHAF(MztOY9lM--DOg ztHUeAS&iy{xRqg`(SS5CQNur=sY!b(CI}9K^M_01B|2JYhsL^oLW8oVN?Wr`Pp;4m z%}AlZ@Kx%ludivK*STX9UBy6cr9dqef%rS13{ZP1(8`KH8_NQ9l>)U^1UgX`$SMU| zRS}3k8OoTpy%cD5MIio^CM`zg{QW^6lhIFAU?p$0QHvwt*r>epBH6- zt}g|;xFQf=;bnkEN`Wq^2*iH{EC6EOypY(UE7DW}s2XqTOZ67srd-3E^bRl2hr0b( Vx1Z|vb9~9~aen&>-{3p6{R@cE@qz#V literal 4963 zcmcIo`+F2u8Ga|pGRfv7tRVzaXn{6uL%0kEEXYz?l8_P_AWZ_)RU+-2CY#N|k{`@I&z$>vzWeu` zy!M~DR{-3Nf5cIPS{-!)F|5##IBiawM#gj}jALV`tyG|4#Xj4$gZ&z6J2#(*qaF=9 z;sT8j8oE-RW0={j5#%h(HL^J`olgb6VYz`F%ow9N)AfhKNe!_HD;P*?XzAQMxNyy& z=T0OW(2SKj5&~~Qi-tYdfK3OUk+b})=lWI%J{*k(Z9nJ>JPkEAZ%CBmOR`%9R-;Wr zgCxT-CoI2&lzd=k3<;8K#9FlLxJh6g)@x{1l+G_x96ot99S!S>Y^cR>))-NX$rcLt z7J&`eNVX}T%#4N&i}sJ_-Be(EuF)TLc=4^cO-F~o?YKk3nuUuOVN(x|nWc<6LnhVj zOVJ_`SD;Pa(D!NjVG9YkRI#m-%8MLRYbg!3p1G#-r z;E42#cxHZD9ylg2BoEZ{z>sH$dxr%^m@-i3FEj(NliCOvxa@`wsgDWXh) zF{DV86Co{!s0c?AWeJR9BDARy`%&6d_wwY#Gm0Qv;54RaalS&?u^bsm3`vY5;DW(T z8tHN0N)MYZ5${?SLB(^5sP_ouK;75p=h1d8gRprlNnBoF8fO?ONn9^OV_>EG_$djH zf$dnvpqDZ;a$%Gsa@C9(fk*K^diA)MbIf4a$|zr#>l(I2k)*=e(tuLp+V2;5T>4aF zYJrG2p2P=qd{E#+lEg5kE`?-Fb-gojoWqB8d_>?We6$$p)aJf?X#F&YF)KL3RHtF6 zl)%7pvR=;27^6`q8Ad8~dg)@s!6IymtJjWi&Zy2b1jif$0W3ySnpm1#z`7>7Q>wt^CyCyauDQiuOx0 zd2;g!nscw0rkYj`+OBmp?~GZwQFAQAnHHt!6K2kq<7l!jn6ydF-fP`bHL)i(h+t7Q zvGrP56>1NjPK3TaWab$711m>r+Dmk_*gEML>3Nm6mTW5;0s(cus2}YxM~?0016fkU zCcV5wuS=UVY?>G_Q&UIGY$Sl~jaU7RW z&@n6U5-w}FRV8_qJ8gH`o1*uc&RE*y33Hs;L*>Xu%5h=zdTHj54uuySGTn5>@;e4S zZz`XS9HTTZmn8K?2S;bJR_XQ|UktCj{@GHuh>&~DOlHImtYiW^R>ts?I9|q2b-W_* zGg)5TdHwtzy2yaTq!E^w6UTFz1b(jJ?i=$i9$i!{MX`0sTb{J^%mLNkrqrB|401yz9xg9>1rHX;&{jwejkUXY26?Ir`V^*>Lnn#>G z(-;1EF`D?Z+Wf-Ha+z^pz@=~WW=!9=d>wz$aQhN*RI)rBf75Ww^|!Ka2+b6mn4oGH za2xsMn}od@S(&Pq6{Koe!l{;NN42ccRLlBHwXBX*%lxle7AC4?>Sf!^03O3^AC?|Y)_C1_n+W}z3*B->S2utviiZU(MkQw=|NYS3n}67w^OcBD95U;RSqw zBQl6D;zhFlCH7+)2U4=E1421yO}wy?bEmdk#uJxu_Q=**e5_|vD?aJ$j{A|K~6t&0r5?9_%v`)Epy!17MsI!azgG?6Z#xJBPSZ>@H|^yPz%XrH+ch+EMxYe ziBE%8?8kcCLtYP%rylaq%auO<4zdLA$8j7|TpfZgiJa%^6#4x!xni}Aui&d>rir!r z*YI`T(~1FngS{A$58#{Z@s-CH%eUBTz_*FB7PbH7Rl1I59Z%{wSI0jfktj;(ac-hl zkl4^#430F;#kww`Npe%8xLKhlS1AfbxFMqOJKQ5zzpH@0hrJbmc9sLRRt4g-s{&B6 z9B6e_pnEF<9V`cGs|qw-5$LgUpfy#2_!O(a+WqA~YpVkBiB^ diff --git a/build/classes/java/main/com/app/treen/products/entity/QTransProduct.class b/build/classes/java/main/com/app/treen/products/entity/QTransProduct.class index 34bf2cbf610948c8d1139597e8013278a2e483d1..ea6fbe51ab5eca523cdc79e093b65e77ed95a5d4 100644 GIT binary patch delta 1697 zcmaJ=+gDUo9RBt&aApn%kTYg0my#Bx9^zujDp#wpz*>c1;Y#_WtNp?Sy}x7J@r^T^_YEo4;}EOr}O)Mm+!atx4-juP% z*?&%)1aK36)8G-XoISp>v8v{RWqMDzI~1&s^+l6@=x8-?H#YmQRiGdeN^Xw@HQXam z!+y(I=+oQoGq4qneyo6P-~rSM7?D^o+z}218|-9Aa@anE1iVo@qF|?i5bAsg3*`64 z!nr-&2EuYrgpG;tu8@Y9v)fmD<%0$yX!Ic=P@0U}(S+Te49B7ylXkK%p`kB#l!_$^ z%Vj$ZB;>$=fUhqR3U08Y-5MTpmikvWJz`)$)>1RIO`&*1&g?Oek|)|L;O`HIcDT3S zXJD^9)d3E)^EREa_<)WwJYnETJmnnsTi&MymPzp{TI$^f9fu4&3!gw?M=ahPkM)M) zNe&KT*gz>9XNg{;KX2dwUJzKZPL4Y}b))bwFV8c_HN4~;(#wlRFf5OE)WBi9;+)dU zYy4dPnt|6*ETA>qv0-ifrfYpTM$wfxTovqVPwE_cOKy65xNwpFj)7x1E>Ni!4gFb^ zZYf+U($hv)-h%H1O1kWRySm4Yc2?K-*a?byF5NqCqe#uiLcELjAn-n&;=u>RMJQ%` znIz=56g60>zPyS0u0k#QKV>^A{rQAfC4Rn@^ zB{3bOh-n%(FNtUt7f%q=3ogzO^Zi|%CFZ*;9ty0!eD0Zgi3j5-AfSxA&7||YdHI@; z*`yNzH&cr`e8Omf!o@hDGyxTw!l#`3jJ14007X)XJd)G}s@i5rZp9+h&mmdtk}Sqa ze9pO3oHqCcWaf)mTKV_pbpBG&JL-&Q0%fUL`r9~q`yBcrm%a$6@zpHZX_Co{UL{Ew z-w7{I3-~QlR9rKOk}(1gW|6z7Nx3{Q^i$v8sFdypNL8+d8GrVmArX#EF!zUW!HD= zK;8>D2d@TQ!&wbKXgrx9y1?SaS&!-&+?M*E>WF&9qC9SZ`;s~T{YWqTgrB)rTBln1 q0SJua)inH5&|A$nCh=w(qf#?5$3&O#_qvq+$kCjK_6r+4`1N0lwMs<* delta 1906 zcmZuxX>(Ln5IuJ?nYZN4UFx+ut;EZ*SV^31O`T+#ljK<1ho2@ z%?&f=jyGXqsf7^k6);B^_eXR z3+^fFjYsVK;gt7gquV0 zb`4tu#>+t$&Fgo`)F&-$lBrw!Q|CsKG3oEJuvL!b2pFrQku@#_&slKfXxn+Al}~C* zBs)!RV~2$o@S?Lsx4kdPrJXBxY7M=GE` zH2F%Lih$p_qWjD@ENsV{^fu;e6b1H>1wJFG;Vpr3r_(4OWMVIN$~oV)um}5`j8R#t zV?XImCqwZRyM!~XY1#asY|`Vz^qtMJ**pFU;8dk6FDKT#pVx8Q(SMJ?uDXQ$M z^%YWJlNqlnpyB*kG@q;W^*BYrg5f_23|bLd9jfaH#oOv;bc9kA>CXa{PKCd;=VD-j z$PPgX`fwNm?^Dqp93l22Kzq1s&9fL|F;1O)s5&QLBJ)3>?ZJnP--fBmKOM7N|1tU- zG0(-viK$x`pCG0e4Ah?tUev1~-?sYXzDT^FBhLArkq^-F)=mc;#0)b zimI~*<@=ODjkpQN;iJth@O7U!Hx!po`;<8*5ipJ1Ovh)mhAJGu=ZYhcu`h6%_r7GT zP!K>BsX+mYtSzq14iu?JDH^URQsx#Z!x?iH!!2U;7m>biNr*?0VR+~U9r=<$NeIoq$+DNHa4rNJvUdj;9>n5a+>=AA zZ3mYHXZPaH9G25w5$xzieGYNkK7`ssSeZkL?sP9E<*=I8ef`O6Y2P2*ppqY;{ZQ~> zWj{*$@!)1#W(wdg#k8HL>MGMh-~ztmhDe{{oYJHvIWHK{j>`I;SwC>7 zJK(*7-{93?YWPvZMUB%5qAPOx?0?)Ig}N7Cjs8z?uUf6YfOEiIZ(f+cP#?dtl~BU0 tQyKvVbG23Je6J4&dT>bcCi3zuRpK2&NerWvuk$-DF~NgB{sm*Yfi(aC diff --git a/build/classes/java/main/com/app/treen/products/entity/TransPImg.class b/build/classes/java/main/com/app/treen/products/entity/TransPImg.class index fc77ac3f34046c33db35e87094fecc993a68cce4..823f0ec4964028985cffecbd6b70745c5b9eda6d 100644 GIT binary patch delta 790 zcmah{$x6de6g^3kG>O*6q1Do&B0?q3+SZ|3=XrLg8*$-65C`hM3qK)0;LeQ;i;AKs zy47#+SH$xYX(fmT?z_XwIXNfqo!4E}nQxyD&j3cTSv2ouLq9q`-9I|qJ-B|6UuF3) z@0_XcZE?uxVs!7J2fgUqMko3qA(=-C1`8EwZfBemEiDZ=7g{nrNYWJNKr4nY?0jhL zg)F;j1r^9gkGVT4=N@u`S-cUp z?;zQMTLi@tDsm*d1XURX*cea{;o1NpvC_i`iR}E`<3wbfr(iZI6x0w2m?eA_Yr&DD z2EcZ0HtA{CW<*6JELyc_)uL64RxRHLCNTLezMC?Hc-6i^LU{Hhd!B25C{=$|I)&*n zQK7^Xy!0_e|78kerp#0#3i*j*e-MSdlbOYwU~+bhXrX+5i9diIZXOH2D2VufO7lNN l3Km`SOT1DY;=ajP=3s@McxzX&#ztWi!#XR|t#oX__yW=TL&N|8 delta 781 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 literal 7261 zcmeHM>2@1M7XHeyE!(v5mIV{Y0?yziL=Yg5C~<;KLd?h}iJfG^GDBPHC>=}Pin?V8 zYcj(&12gOl1Q^x_fOFu$fpd5Ocm@8we#@?cS=Z zAO834_W<_cvmUfzO#tsTK&b_YZhLZwoM#oNcqYK>$ z^jNqGmcV4jFC^_^F&UN|$4eGVezu$mgQVkyZg@VqP_n(?h!T!OU#7}#KI@bO+T1LW zrmL)J-^-<#TQcE_DVi3}G$lcy_@N7>wY~hb31dueY!P z*9dfF?9j>irSk%#b?EgSnbs{O%e59ZGD{cPkX>*}1vdA33!7NjnRPCo)^(t^5r(WIC1j;;VNQ_YnV;I*qAG5tB zf!mwFp%Koqmc-g_;bxxvtBStsg(J&$zU**+lNN5ltpeRj9yqjk!~^{0HVe1o4gss+ zglGJ$QW0ooJZ7b)E(83~xD>FtHx}I2ue9*!}Z0}$NvNB!#`Og;q z!k>pKpR3ct-)#$v$Ov3p`ECqhv&QLpVRVN*||3E;pW|(N4~y9;W$;dtoE+5)RniLDdbX< zgw&y(Z0Cye9EkB*?bCjinEKML=ggH0i%x05COg?UKV#=l*d>?WwXi)r{;3Tk$Cy*J<9r8hjqTV;cFbAs@&AJFx!f6Dj6fI?DHE%czT6L0t6-*_j1clqFSNnoVfUDsRomz<=cQfH|{ zbTyJ!?02I6S2t9khixyLcY=|$?=O{$I-bfDgtj>nwQCd?X#K=Ae=yQsB{5Rb8;pjS zw)6RUH*`{6cwgYZHRBL{(W#12H@i=4-@=iJe>uUxzbh#O{Y|C=xURAQT+7LeJlFX8 zzS52_(_D{Sq4|{}V!?-ie*E|n~*x`wKZCMfO>%FWdsai18Y+xLt|N0{ln9Rz%9 z9E#~>0v`!%GbS`{o)Y*(VAGYG(j8SzDkVa?DA5E;ytHxV2m(v=)=9s4`fcM>k0GN} zjv=GOjUl6Cj3J|3j3J|Bj3J}MiXo#!i6NuJh9RRQh#{lIgdwBUhasbEgdwBsg(0K# zgCV1Qg(0Jqfgz*(gdwBE0gZM+j=5Ji2Yw80F~;_D1}@*DD1AuCC8{NfRj_3IYltr- zRX|UkY+w)}J-Gv{QpPg&#BsmdZ_iPiH4%adVM{{ z1dP^Shu??uarnIrRjUUc!3xxHi%vfP#^$a8vha#X?pCqgv2*E!quI#^w=fePjGej96k2R_XoJTe@{C- zr!JxE8$2xQ2lsT4WJXFJ+0*eIj-I0T1eIPY160;g8K!a_mCaOcq;k{5_M3;>hgmiK zHL@I5%-&BbGxdt^!1Mf{2laIwMJx{JZS zIxeF>fxllyUji@5C)Ed}gr5Jb8>UzAsv0Ib6!j4oXn{4*9cnW{Wha&0RQ6LjNG1I( zazK30RP#3#6MsAK8u7N_^%~pWkg}tAlYYC%vbU(bhY#p&klJ=pdi+1=#)tSApJMp` DBDy@O diff --git a/build/classes/java/main/com/app/treen/products/entity/TransProduct.class b/build/classes/java/main/com/app/treen/products/entity/TransProduct.class index 13fc984d39275a1a12fb0fae845039e0152d08d1..4783428595d34531ccbe0a0f7728df8ef37fd342 100644 GIT binary patch literal 7385 zcmds6`F9i775>JSJzB;F45SDMOJagi00nSq!HH8ahS>2ITPPu2+_5y6iRF)vTpxv$=}tROh@w4Y69)s%SVcwVp^u8w8ix9+5rz z4Q#6TXowd~*IF)DDD6;)dsw6~f2E?o z&%nE6mpEl8=B&!9Y@If+TLycJmTTH28QN>$e(dAWvgH)z3@6*{VmY-{t75u#*~wl~ zG-|&C1`gr@o~ml!epAs*xmya70JOz{5DI zq5rOer^?QoPujRm1k7$s+;V2&t0tq=8cy*3h$7 zwjEc)*49O*@g5!THE;%JsfzWO2wN?v&uPoORxVPgb5m2DS;C`K@;z$M5x8&fs^XTw z<9MHra|X_1tRra_T+^*p$>{v}#o77s&IIy^l|bVLF3Q&1T=5yRAkguqbv&VAbYuMl z9c?h!IbX9&L~Ih5&@D)Q(!iA1rOPf#cgDc1bQ9NY>-n*A&2eS)DFgE|nk?BXR#6Qn zIh-?a8F@lwuxMR1YbAHkH}1et3kGEUGX}2USq)p7>pf)?uq4>{K&=KRVB`3Jfe(rZ z15K7q!DV{Uz=vgedvkjI36=~LK`2oPw2|D2`|tjGSW3knmv@l>>5Fyh=$K;~3#Kq~LzJ8*7$O zOk5bB$xY<0Fu+IdF;*_sRvoU~s+6$k``n^U1?`Wdm^Pgox$>+d><>h)n54Hi`RWqc z6}x?ri^-_LE!7C%YI#D!@uJIk%wssUQpu!3yP3N*3R_TOU$Cx9R^o9PO>^VV5jSyrM5P{(M(|xl%4Vl`k9}o+Uy>rP)VtS^_LsTCm^{>86EKOgLnrN zzKb4VV2F*Ci_B+RrfkQWsjV(qm7HKp3XF2WEalCLEq#A5=3cYuy+_;Zm6M3=S2@I2 zANMP)Oq*+pi;m9|YU_ht7y>%JNMMIer{hZ+9$HTs&WjBw==e%US<5rEj<0DrzVQxW zbL;p9bKK@>8YbrfJk%1fV5(GJT`I3gHR@#+B2{1Vssjz@+a}~7$fGI@39QSOJHh0k^G&m} zOn#t+Z5a9@1((mLDu2hiI>NU;=X3(tEw1$hgpAW&+1#&PyMta)t@3!P?s zA3>hr5&`KCZDk6(Q9w4S#xMI4F=o!Mo~vkV|S`q&1u-d zzA|aB($uuKf({rJnOp1^;hq~iW5bdo5tQK&IMOEa2DSRX<=A&m>S;OL-&lvia|g0p za-m$S6s(K3WVCHTk3BB@?^OtT7kxr$4^7fqrm8f?*?w2@T%$~6^iIuKRpL#uD0{|) z!^kpLGOJaK-R;5c{aXkQvPSP>oz~A^UPD-~S?ujbH?H!%%>FeCHlD)@n_H?VaUH)i z`0KW?x5{;nH(6$v-v~I+C0_voi`MQg`Uod^#rG%Wy&_~)R8V~uF5A58hh}S4T$kDB zIm>ImKikLmNBP{aa1$Tn6mRnPn&JiBpSq6=162OGKF(2IkNq_%zGcGSlJ`=7qGe0o z)j~8p$tTE+*R|G(PjZ5HN`E3+k{7Ux^F8I(BB#J$3BLq9he zPT(mXYaUNy0oRbjXGnF^6B?hz=h&n8K5V2V7!lCf3MFz+uoWa?swL*{Q7tibuWE^9 zb=49>##Kwql~66QPg1o+xhcHF_CL`34nH9nI^sHB*6{@$uc%)(zO3V`I=-%BNXKcN zt2C~9heVrO-Nn_hIYrXd0SWh-{x66g$2Te2u@uh*Y7W#Hs4a0)&>29~gO&i{E1wcU zOUSkBTllsIApDDO(ax}dr%%AMQ@}&O5Ih77!9&0hJOm8ELxd1Kghhe?6D)KoEMyc9 zla^Uxt3XML*vVkAhk@cC1I1xl{uu3eim^iMf7RQwY5!qmbxq_@wif#jD_0l$4=cA8 z`wuHG7W)s&S2bn-Vda`)|6%z?r0hSeu#-^AIh0U}*+?m(w4{72@s)CjZ~5yHw|I>W zO`Woz$R`ZgJ4w_V_zru$?0uK-q2EK;JK zVMYhrGWvNWqg`P}gKZi80>6wn+MY0@!)+P;3crqEv_H%!)0WXK{3e3YgJDL;+cNrX zBp^?O8J%p)=yy%;4+3%|%;8bNM^Z%rzN%A!Tal&=IcXs%<*OVRYx#DBm;X5MFxl-{E5-} zMTTyJdr4-@H%g>Q$#S}6xjvQUSST^eu~1@`W8PzyW8PzyW8PzyW8O=uyq8jW&k)(t zeIi=AKSHvUlI&G)$7X)Z`{gsGP?e5E&AiHal2P(1TSW7hY4h0;%?;Ab6dIWtIC^yunV4olB^* Ll~k=b-bCU*-R#vw literal 9536 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 Xlle3L!jAwy4&$$Eg;c8ve?#y;6-0~) diff --git a/build/classes/java/main/com/app/treen/products/service/ProductService.class b/build/classes/java/main/com/app/treen/products/service/ProductService.class index cd3ff57d450beb7c25a6871963b26360f35a3688..addd7d784d1be3a3cceda7c40bdbceed2a0c499d 100644 GIT binary patch literal 25018 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 literal 22631 zcmdU1d3;pW^*`rjc}aLc2!ak)1&InFaf&QTA_xS~r0ii4QE|w;gn`LSn3(`U#oba7 zwbp&DD{a*lDG3DY(nV{nwF|A?)L$37DO%fV7k@6l@44^In>TM}!VLK7=kxm`dFwg% z-m`qqx#!+DFMPD`DI%JxJzP#273oxLPzjYX)wM=D{DIC+ei*ygu?#0p?IRfx(){- z=t3eEinKS3^}bR;l{7@BDuag75loZQTXlpGOho-LbA6W?PxzNe=abS1Y%pgfqD)5I zOf<q8M{p6J=~xp=_sbE))h9ZjEN8rW_olCLxc(bgDy)JhO@qY%RYI+l*pX{14;=y;~n(npJPGm;1;y8TJ9iYh;C(0Dq9X}B_G+aBENoR+6L-S(8CS1O%o&?K4+DJP<{RS%dp zcwMav4(hl2w-o0Mo@R560lG<7yZ1$RzhE(^(G;EP4Qim%nNCk9&1Ere7O9~z702N> zkalj1aT}3N)0k?~TkNm1;tbObnn5#}1}CD*EtU8}CR}rxSAhMoN;6=EDWdvW2F<29 z@N!G_O&#s-!Fd(Xzi`rsA?CBFNvCFmK0|Yvrlb$L08UK0$aqFL>=AQQ!i)u0ht2FG zn{UtpTF6uxGuz>T?qQwgF_&DOS?r-yhP>FI7Fq(Bgu7cWGzFmx2bxltJ%Es1%|K#b9M%kyL*gqv0@c6QYN72F4Mx!e$~GvAS8E z+g;r9TPGF-wKo)CJl-yf3q=fyQYUO75w#wtd}w|_KIDzL89`Vlq!6C&I$mjo-gqZO zVy4C3#in%=@wbB3Q^KGw+JJoE^iU*}0Aj2Nh&VP zFkjE0oO~i^F0`{NE1#&K%`~Eln5xAXFEQv+y3C3+&YN!6EFjWkp@%tcrYj`UT=`!P zFHUEGdsj<%`D`JP#i34#LZ{E=6Db@@8TmGe6xZe#7aYRu=y{z%*V7GFT)?Yo&<+Fl zm{cw-17>)MA)gz?tG`f4NboX|!QO1pErP>Uz~SYg_?l!w12mSLaJjs5yFqu*onmfL z_bY{Ye@apZ^?ot&yA8UB?nRp$U7bql723&Dc==318n}X{Z zu~?v6z`24Rpoay3cBmM#Oysy&{(VJ2^iCP#qXs=jU$sI?(DVXsK_MlR90S}b1AIIK zNBgxhZ!dKEdIkdzW~G4GQE?g&@1h=&LvMcg>+rDWYNI>{fBy#%LygL6K!F ztYIHbmw(?e=vjIW2?yM7exL&>@Da(Gk9(FXuRd?k_vF<9cok`VaETe~ko(^^=ta3- zj{CFBL?9HFyDu5^L;8_*_e?XQ=HwH>Iz0t >bt%t%)UvOzW1vZxRX_R&ua+D|{V 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 function configurationCacheProblems() { return ( // begin-report-data -{"diagnostics":[{"problem":[{"text":"The CopyProcessingSpec.getFileMode() method has been deprecated."}],"severity":"WARNING","problemDetails":[{"text":"This is scheduled to be removed in Gradle 9.0."}],"contextualLabel":"The CopyProcessingSpec.getFileMode() method has been deprecated.","documentationLink":"https://docs.gradle.org/8.11.1/userguide/upgrading_version_8.html#unix_file_permissions_deprecated","problemId":[{"name":"deprecation","displayName":"Deprecation"},{"name":"the-copyprocessingspec-getfilemode-method-has-been-deprecated","displayName":"The CopyProcessingSpec.getFileMode() method has been deprecated."}],"solutions":[[{"text":"Please use the getFilePermissions() method instead."}]]},{"problem":[{"text":"The CopyProcessingSpec.getDirMode() method has been deprecated."}],"severity":"WARNING","problemDetails":[{"text":"This is scheduled to be removed in Gradle 9.0."}],"contextualLabel":"The CopyProcessingSpec.getDirMode() method has been deprecated.","documentationLink":"https://docs.gradle.org/8.11.1/userguide/upgrading_version_8.html#unix_file_permissions_deprecated","problemId":[{"name":"deprecation","displayName":"Deprecation"},{"name":"the-copyprocessingspec-getdirmode-method-has-been-deprecated","displayName":"The CopyProcessingSpec.getDirMode() method has been deprecated."}],"solutions":[[{"text":"Please use the getDirPermissions() method instead."}]]},{"problem":[{"text":"The CopyProcessingSpec.getFileMode() method has been deprecated."}],"severity":"WARNING","problemDetails":[{"text":"This is scheduled to be removed in Gradle 9.0."}],"contextualLabel":"The CopyProcessingSpec.getFileMode() method has been deprecated.","documentationLink":"https://docs.gradle.org/8.11.1/userguide/upgrading_version_8.html#unix_file_permissions_deprecated","problemId":[{"name":"deprecation","displayName":"Deprecation"},{"name":"the-copyprocessingspec-getfilemode-method-has-been-deprecated","displayName":"The CopyProcessingSpec.getFileMode() method has been deprecated."}],"solutions":[[{"text":"Please use the getFilePermissions() method instead."}]]},{"problem":[{"text":"The CopyProcessingSpec.getDirMode() method has been deprecated."}],"severity":"WARNING","problemDetails":[{"text":"This is scheduled to be removed in Gradle 9.0."}],"contextualLabel":"The CopyProcessingSpec.getDirMode() method has been deprecated.","documentationLink":"https://docs.gradle.org/8.11.1/userguide/upgrading_version_8.html#unix_file_permissions_deprecated","problemId":[{"name":"deprecation","displayName":"Deprecation"},{"name":"the-copyprocessingspec-getdirmode-method-has-been-deprecated","displayName":"The CopyProcessingSpec.getDirMode() method has been deprecated."}],"solutions":[[{"text":"Please use the getDirPermissions() method instead."}]]}],"problemsReport":{"totalProblemCount":4,"buildName":"treen","requestedTasks":"","documentationLink":"https://docs.gradle.org/8.11.1/userguide/problems-report.html","documentationLinkCaption":"Problem report"}} +{"diagnostics":[{"locations":[{"path":"initialization script '/private/var/folders/vq/1gk4jby16fx_rp9g6r5y_ghc0000gn/T/TreenApplication_main__.gradle'","line":19},{"path":"initialization script '/private/var/folders/vq/1gk4jby16fx_rp9g6r5y_ghc0000gn/T/TreenApplication_main__.gradle'","line":19}],"problem":[{"text":"The org.gradle.api.plugins.Convention type has been deprecated."}],"severity":"WARNING","problemDetails":[{"text":"This is scheduled to be removed in Gradle 9.0."}],"contextualLabel":"The org.gradle.api.plugins.Convention type has been deprecated.","documentationLink":"https://docs.gradle.org/8.11.1/userguide/upgrading_version_8.html#deprecated_access_to_conventions","problemId":[{"name":"deprecation","displayName":"Deprecation"},{"name":"the-org-gradle-api-plugins-convention-type-has-been-deprecated","displayName":"The org.gradle.api.plugins.Convention type has been deprecated."}]},{"locations":[{"path":"initialization script '/private/var/folders/vq/1gk4jby16fx_rp9g6r5y_ghc0000gn/T/TreenApplication_main__.gradle'","line":23},{"path":"initialization script '/private/var/folders/vq/1gk4jby16fx_rp9g6r5y_ghc0000gn/T/TreenApplication_main__.gradle'","line":23}],"problem":[{"text":"The JavaExecSpec.main property has been deprecated."}],"severity":"WARNING","problemDetails":[{"text":"This is scheduled to be removed in Gradle 9.0."}],"contextualLabel":"The JavaExecSpec.main property has been deprecated.","documentationLink":"https://docs.gradle.org/8.11.1/dsl/org.gradle.process.JavaExecSpec.html#org.gradle.process.JavaExecSpec:main","problemId":[{"name":"deprecation","displayName":"Deprecation"},{"name":"the-javaexecspec-main-property-has-been-deprecated","displayName":"The JavaExecSpec.main property has been deprecated."}],"solutions":[[{"text":"Please use the mainClass property instead."}]]}],"problemsReport":{"totalProblemCount":2,"buildName":"treen","requestedTasks":":TreenApplication.main()","documentationLink":"https://docs.gradle.org/8.11.1/userguide/problems-report.html","documentationLinkCaption":"Problem report"}} // end-report-data );} diff --git a/build/resources/main/application.yml b/build/resources/main/application.yml index 2d467aa..fdfc689 100644 --- a/build/resources/main/application.yml +++ b/build/resources/main/application.yml @@ -5,6 +5,9 @@ # username: ${DB_USERNAME:root} # password: ${DB_PASSWORD:password} spring: + redis: + host: localhost + port: 6379 config: import: optional:file:.env[.properties] # .env 파일을 직접 로드 datasource: @@ -37,11 +40,6 @@ jwt: secret: key: ${JWT_SECRET_KEY} refresh: ${JWT_REFRESH_KEY} - redis: - host: localhost - port: 6379 - repositories: - enabled: false coolsms: apiKey: ${COOL_SMS_KEY} apiSecret: ${COOL_SMS_SECRET_KEY} diff --git a/src/main/generated/com/app/treen/products/entity/QTransProduct.java b/src/main/generated/com/app/treen/products/entity/QTransProduct.java index c64cfa6..2c46a25 100644 --- a/src/main/generated/com/app/treen/products/entity/QTransProduct.java +++ b/src/main/generated/com/app/treen/products/entity/QTransProduct.java @@ -35,8 +35,6 @@ public class QTransProduct extends EntityPathBase { public final NumberPath id = createNumber("id", Long.class); - public final ListPath images = this.createList("images", TransPImg.class, QTransPImg.class, PathInits.DIRECT2); - public final NumberPath likedCount = createNumber("likedCount", Long.class); public final EnumPath method = createEnum("method", com.app.treen.products.entity.enumeration.Method.class); diff --git a/src/main/java/com/app/treen/TreenApplication.java b/src/main/java/com/app/treen/TreenApplication.java index 0fe7e89..a4f5adc 100644 --- a/src/main/java/com/app/treen/TreenApplication.java +++ b/src/main/java/com/app/treen/TreenApplication.java @@ -8,7 +8,7 @@ @EnableJpaAuditing @EnableJpaRepositories(basePackages = "com.app.treen.jpa.repository") -@EnableMongoRepositories(basePackages = "com.app.treen.mongo") +@EnableMongoRepositories(basePackages = "com.app.treen.mongo.repository") @SpringBootApplication public class TreenApplication { diff --git a/src/main/java/com/app/treen/common/config/S3Uploader.java b/src/main/java/com/app/treen/common/config/S3Uploader.java index d45bcc9..294c3ad 100644 --- a/src/main/java/com/app/treen/common/config/S3Uploader.java +++ b/src/main/java/com/app/treen/common/config/S3Uploader.java @@ -32,18 +32,25 @@ public class S3Uploader { @Value("${cloud.aws.region.static}") private String region; // S3 리전 값 + private String defaultImageUrl = "default.url"; + // 단일 파일 업로드 public String upload(MultipartFile multipartFile, String dirName) throws IOException { - File uploadFile = convert(multipartFile) - .orElseThrow(() -> new IOException("Failed to create a file from MultipartFile")); + if (multipartFile == null || multipartFile.isEmpty()) { + log.warn("파일이 제공되지 않음. 기본 URL 반환"); + return defaultImageUrl; // 파일이 없으면 기본 이미지 반환 + } + + File uploadFile = convert(multipartFile); return upload(uploadFile, dirName); } + // 다중 파일 업로드 public List upload(List multipartFiles, String dirName) throws IOException { List fileUrls = new ArrayList<>(); for (MultipartFile file : multipartFiles) { - fileUrls.add(upload(file, dirName)); + fileUrls.add(upload(file, dirName)); // 개별적으로 업로드 수행 } return fileUrls; } @@ -73,14 +80,16 @@ private void removeNewFile(File targetFile) { } } - private Optional convert(MultipartFile multipartFile) throws IOException { - File convertFile = new File(System.getProperty("java.io.tmpdir") + "/" + multipartFile.getOriginalFilename()); - if (convertFile.createNewFile()) { - try (FileOutputStream fos = new FileOutputStream(convertFile)) { - fos.write(multipartFile.getBytes()); - } - return Optional.of(convertFile); + private File convert(MultipartFile multipartFile) throws IOException { + if (multipartFile == null || multipartFile.isEmpty()) { + throw new IOException("파일이 제공되지 않았습니다."); } - return Optional.empty(); + + File convertFile = File.createTempFile(UUID.randomUUID().toString(), "_" + multipartFile.getOriginalFilename()); + try (FileOutputStream fos = new FileOutputStream(convertFile)) { + fos.write(multipartFile.getBytes()); + } + return convertFile; } + } \ No newline at end of file diff --git a/src/main/java/com/app/treen/common/response/code/status/ErrorStatus.java b/src/main/java/com/app/treen/common/response/code/status/ErrorStatus.java index 0afdc3d..24651ce 100644 --- a/src/main/java/com/app/treen/common/response/code/status/ErrorStatus.java +++ b/src/main/java/com/app/treen/common/response/code/status/ErrorStatus.java @@ -130,6 +130,9 @@ public enum ErrorStatus implements BaseErrorCode { PINCOMPLAINT_NOT_FOUND(HttpStatus.NOT_FOUND, "PIN4001", "신고내역을 찾지 못했습니다."), NOT_FOUND_CHAT_ROOM(HttpStatus.NOT_FOUND, "CHATROOM4001", "채팅방을 찾지 못했습니다."), + // 이미지 에러 + IMAGE_MUST_BE_UPLOADED(HttpStatus.BAD_REQUEST, "IMAGE4001", "이미지는 1장 이상 업로드해야합니다."), + IMAGE_OVER_UPLOADED(HttpStatus.BAD_REQUEST, "IMAGE4001", "이미지는 5장까지만 업로드할 수 있습니다."), // 상품 에러 diff --git a/src/main/java/com/app/treen/jpa/repository/products/TransPImgRepository.java b/src/main/java/com/app/treen/jpa/repository/products/TransPImgRepository.java index cb3b2c2..9455512 100644 --- a/src/main/java/com/app/treen/jpa/repository/products/TransPImgRepository.java +++ b/src/main/java/com/app/treen/jpa/repository/products/TransPImgRepository.java @@ -4,6 +4,13 @@ import com.app.treen.products.entity.TransProduct; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; +import java.util.Optional; + public interface TransPImgRepository extends JpaRepository { void deleteByTransProduct(TransProduct existingProduct); + + Optional findFirstByTransProductAndIsMainTrue(TransProduct product); + + List findByTransProduct(TransProduct selectedProduct); } diff --git a/src/main/java/com/app/treen/products/controller/ProductController.java b/src/main/java/com/app/treen/products/controller/ProductController.java index f9ed7c9..7c1d155 100644 --- a/src/main/java/com/app/treen/products/controller/ProductController.java +++ b/src/main/java/com/app/treen/products/controller/ProductController.java @@ -3,10 +3,7 @@ import com.app.treen.common.response.code.status.SuccessStatus; import com.app.treen.products.dto.ProductQueryHelper; import com.app.treen.products.dto.TradeQueryHelper; -import com.app.treen.products.dto.request.TradeProductSaveDto; -import com.app.treen.products.dto.request.TradeProductUpdateDto; -import com.app.treen.products.dto.request.TransProductSaveDto; -import com.app.treen.products.dto.request.TransProductUpdateDto; +import com.app.treen.products.dto.request.*; import com.app.treen.products.dto.response.TradeProductResponseDto; import com.app.treen.products.dto.response.TradeResponseListDto; import com.app.treen.products.dto.response.TransProductResponseDto; @@ -38,12 +35,10 @@ public class ProductController { @PostMapping(value = "/transaction/save", consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE}) public ResponseEntity saveTransProduct( @RequestPart("images") List images, // 이미지 리스트로 변경 - @RequestPart("product") TransProductSaveDto requestDto, + @RequestPart("product") TransactionRequestDto requestDto, @AuthenticationPrincipal CustomUserDetails userDetails ) throws IOException { - // User user = new User(); // 현재 사용자의 정보를 가져오는 로직 추가 필요 TransProductResponseDto responseDto = productService.saveTransProduct(requestDto,images, userDetails.getUser()); - return ResponseEntity.status(HttpStatus.OK).body(responseDto); } diff --git a/src/main/java/com/app/treen/products/dto/request/TransImgRequestDto.java b/src/main/java/com/app/treen/products/dto/request/TransImgRequestDto.java index 181042c..913da59 100644 --- a/src/main/java/com/app/treen/products/dto/request/TransImgRequestDto.java +++ b/src/main/java/com/app/treen/products/dto/request/TransImgRequestDto.java @@ -1,4 +1,29 @@ package com.app.treen.products.dto.request; + +import com.app.treen.products.entity.TransPImg; +import com.app.treen.products.entity.TransProduct; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +@Getter +@NoArgsConstructor public class TransImgRequestDto { + + public List toImageEntities(TransProduct transProduct, List imageUrls) { + List images = new ArrayList<>(); + for (int i = 0; i imageUrls = new ArrayList<>(); - private List regionIds = new ArrayList<>(); - public void setImageUrls(List imageUrls) { - this.imageUrls = imageUrls; - } public TransProduct toEntity(User user, Category category) { return TransProduct.builder() @@ -47,28 +44,22 @@ public TransProduct toEntity(User user, Category category) { .build(); } - public List toImageEntities(TransProduct transProduct) { - List images = new ArrayList<>(); - for (int i = 0; i < imageUrls.size(); i++) { - images.add(TransPImg.builder() - .transProduct(transProduct) - .imgUrl(imageUrls.get(i)) - .sortOrder(i) - .isMain(i == 0) // 첫 번째 이미지를 대표 이미지로 설정 - .build()); - } - return images; - } - public List toRegionEntities(TransProduct transProduct, List regions) { - List transRegions = new ArrayList<>(); - for (Region region : regions) { - transRegions.add(TransRegion.builder() - .transProduct(transProduct) - .region(region) - .build()); + public static String formatRelativeTime(LocalDateTime createdDate) { + Duration duration = Duration.between(createdDate, LocalDateTime.now()); + long seconds = duration.getSeconds(); + long minutes = seconds / 60; + long hours = minutes / 60; + long days = hours / 24; + + if (seconds < 60) { + return seconds + "초 전"; + } else if (minutes < 60) { + return minutes + "분 전"; + } else if (hours < 24) { + return hours + "시간 전"; + } else { + return days + "일 전"; } - return transRegions; } - } diff --git a/src/main/java/com/app/treen/products/dto/request/TransRegionRequestDto.java b/src/main/java/com/app/treen/products/dto/request/TransRegionRequestDto.java index e71090c..079fff8 100644 --- a/src/main/java/com/app/treen/products/dto/request/TransRegionRequestDto.java +++ b/src/main/java/com/app/treen/products/dto/request/TransRegionRequestDto.java @@ -1,2 +1,33 @@ -package com.app.treen.products.dto.request;public class TransRegionRequestDto { +package com.app.treen.products.dto.request; + +import com.app.treen.products.entity.*; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +@Getter +@NoArgsConstructor +public class TransRegionRequestDto { + private List regionIds; + + public TransRegionRequestDto(List regionIds) { + this.regionIds = regionIds; + } + + public List toRegionEntities(TransProduct transProduct, List regions) { + if (transProduct == null) { + throw new IllegalArgumentException("TradeProduct cannot be null"); + } + + List transRegions = new ArrayList<>(); + for (Region region : regions) { + transRegions.add(TransRegion.builder() + .transProduct(transProduct) + .region(region) + .build()); + } + return transRegions; + } } diff --git a/src/main/java/com/app/treen/products/dto/request/TransactionRequestDto.java b/src/main/java/com/app/treen/products/dto/request/TransactionRequestDto.java index 977e5e1..fe2e27c 100644 --- a/src/main/java/com/app/treen/products/dto/request/TransactionRequestDto.java +++ b/src/main/java/com/app/treen/products/dto/request/TransactionRequestDto.java @@ -2,16 +2,18 @@ import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; @Getter +@Setter @NoArgsConstructor public class TransactionRequestDto { - private TransProductSaveDto productRequest; // ✅ 상품 요청 DTO 포함 - private RegionRequestDto regionRequest; // ✅ 지역 요청 DTO 포함 - private TransImgRequestDto imageRequest; // ✅ 이미지 요청 DTO 포함 + private TransProductSaveDto productRequest; + private TransRegionRequestDto regionRequest; + private TransImgRequestDto imageRequest; - public TransactionRequestDto(TransProductSaveDto productRequest, RegionRequestDto regionRequest, TransImgRequestDto imageRequest) { + public TransactionRequestDto(TransProductSaveDto productRequest, TransRegionRequestDto regionRequest, TransImgRequestDto imageRequest) { this.productRequest = productRequest; this.regionRequest = regionRequest; this.imageRequest = imageRequest; diff --git a/src/main/java/com/app/treen/products/dto/response/TransProductResponseDto.java b/src/main/java/com/app/treen/products/dto/response/TransProductResponseDto.java index 959c5fc..37b5335 100644 --- a/src/main/java/com/app/treen/products/dto/response/TransProductResponseDto.java +++ b/src/main/java/com/app/treen/products/dto/response/TransProductResponseDto.java @@ -1,5 +1,6 @@ package com.app.treen.products.dto.response; +import com.fasterxml.jackson.annotation.JsonInclude; import com.app.treen.products.entity.*; import com.app.treen.products.entity.enumeration.*; import com.app.treen.user.entity.User; @@ -11,6 +12,7 @@ import java.util.stream.Collectors; @Getter +@JsonInclude(JsonInclude.Include.NON_NULL) // ✅ NULL인 필드는 JSON에서 제외 public class TransProductResponseDto { private Long id; @@ -34,7 +36,7 @@ public class TransProductResponseDto { private Long writerId; - public TransProductResponseDto(TransProduct transProduct, List transRegions, User user) { + public TransProductResponseDto(TransProduct transProduct, List transRegions, List images, User user) { this.id = transProduct.getId(); this.name = transProduct.getName(); this.usedTerm = transProduct.getUsedTerm(); @@ -48,20 +50,24 @@ public TransProductResponseDto(TransProduct transProduct, List tran this.viewCount = transProduct.getViewCount(); this.likedCount = transProduct.getLikedCount(); - this.imageUrls = transProduct.getImages().stream() + // ✅ 이미지 URL 리스트 설정 + this.imageUrls = images.stream() .map(TransPImg::getImgUrl) .collect(Collectors.toList()); - this.regions = transRegions.stream() - .map(transRegion -> transRegion.getRegion().getName()) - .collect(Collectors.toList()); + // ✅ 지역 리스트 설정 (null이면 설정 안 함) + if (transRegions != null && !transRegions.isEmpty()) { + this.regions = transRegions.stream() + .map(transRegion -> transRegion.getRegion().getName()) + .collect(Collectors.toList()); + } - // 유저 정보 반환 + // ✅ 유저 정보 설정 this.userName = user.getUserName(); this.userImage = user.getProfileImgUrl(); this.writerId = user.getId(); - // createdDate 포맷팅 + // ✅ 생성 시간 포맷팅 this.createdDate = formatRelativeTime(transProduct.getCreatedDate()); } @@ -69,8 +75,6 @@ public TransProductResponseDto(TransProduct transProduct, List tran * 상대적인 시간 표현으로 변환 * @param createdDate 생성 시간 * @return 상대적 시간 - * / - */ private String formatRelativeTime(LocalDateTime createdDate) { Duration duration = Duration.between(createdDate, LocalDateTime.now()); diff --git a/src/main/java/com/app/treen/products/dto/response/TransResponseListDto.java b/src/main/java/com/app/treen/products/dto/response/TransResponseListDto.java index d95cc3b..f12da61 100644 --- a/src/main/java/com/app/treen/products/dto/response/TransResponseListDto.java +++ b/src/main/java/com/app/treen/products/dto/response/TransResponseListDto.java @@ -28,15 +28,11 @@ public class TransResponseListDto { private Method method; // 거래 방법 private UsedRank usedRank; // 사용 - public TransResponseListDto(TransProduct transProduct) { + public TransResponseListDto(TransProduct transProduct, TransPImg transPImg) { this.id = transProduct.getId(); // 상품 ID // 대표 사진 (첫 번째 이미지를 대표 사진으로 설정) - this.representativeImage = transProduct.getImages().stream() - .map(TransPImg::getImgUrl) - .findFirst() - .orElse(null); - + this.representativeImage = transPImg.getImgUrl(); this.name = transProduct.getName(); // 상품명 this.category = transProduct.getCategory().getName(); // 카테고리 this.likedCount = transProduct.getLikedCount(); // 좋아요 수 diff --git a/src/main/java/com/app/treen/products/entity/CategoryInitializer.java b/src/main/java/com/app/treen/products/entity/CategoryInitializer.java index e2f2bbe..fb413a1 100644 --- a/src/main/java/com/app/treen/products/entity/CategoryInitializer.java +++ b/src/main/java/com/app/treen/products/entity/CategoryInitializer.java @@ -1,2 +1,24 @@ -package com.app.treen.products.entity;public class CategoryInitializer { +package com.app.treen.products.entity; + +import com.app.treen.jpa.repository.products.CategoryRepository; +import org.springframework.boot.ApplicationRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import java.util.List; + +@Configuration +public class CategoryInitializer { + + @Bean + public ApplicationRunner initCategories(CategoryRepository categoryRepository) { + return args -> { + if (categoryRepository.count() == 0) { // 중복 방지 + List categories = List.of( + "아우터", "상의", "하의", "원피스", + "신발", "잡화", "키즈" + ); + categories.forEach(name -> categoryRepository.save(new Category(null, name))); + } + }; + } } diff --git a/src/main/java/com/app/treen/products/entity/TransPImg.java b/src/main/java/com/app/treen/products/entity/TransPImg.java index 974b7eb..a2980d5 100644 --- a/src/main/java/com/app/treen/products/entity/TransPImg.java +++ b/src/main/java/com/app/treen/products/entity/TransPImg.java @@ -10,7 +10,7 @@ @AllArgsConstructor @Entity @Getter -@Table(name = "trade_product_img") +@Table(name = "trans_product_img") public class TransPImg { @Id @@ -27,7 +27,7 @@ public class TransPImg { @Column(name = "img_url", nullable = false) private String imgUrl; - @ManyToOne(fetch = FetchType.LAZY) + @ManyToOne(fetch = FetchType.LAZY, optional = false) @JoinColumn(name = "trans_product_id") private TransProduct transProduct; diff --git a/src/main/java/com/app/treen/products/entity/TransProduct.java b/src/main/java/com/app/treen/products/entity/TransProduct.java index a1f4080..f3f67e2 100644 --- a/src/main/java/com/app/treen/products/entity/TransProduct.java +++ b/src/main/java/com/app/treen/products/entity/TransProduct.java @@ -1,17 +1,11 @@ package com.app.treen.products.entity; import com.app.treen.BaseTimeEntity; -import com.app.treen.products.dto.request.TransProductUpdateDto; import com.app.treen.products.entity.enumeration.*; import com.app.treen.user.entity.User; import jakarta.persistence.*; import lombok.*; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - @Getter @NoArgsConstructor @AllArgsConstructor @@ -20,16 +14,19 @@ @Table(name="trans_products") public class TransProduct extends BaseTimeEntity { - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "trans_product_id") private Long id; - @ManyToOne @JoinColumn(name = "user_id") + @ManyToOne + @JoinColumn(name = "user_id") private User user; private String name; - @OneToOne @JoinColumn(name = "category_id") + @OneToOne + @JoinColumn(name = "category_id") private Category category; @Column(name = "used_term", nullable = true) @@ -56,7 +53,6 @@ public class TransProduct extends BaseTimeEntity { @Enumerated(EnumType.STRING) private Method method = Method.ALL; - @Builder.Default @Column(name = "view_count") private Long viewCount = 0L; @@ -68,13 +64,24 @@ public class TransProduct extends BaseTimeEntity { @Builder.Default @Enumerated(EnumType.STRING) @Column(name = "transaction_status") - Status transactionStatus = Status.BEFORE; - - - @OneToMany(mappedBy = "transProduct", cascade = CascadeType.ALL, orphanRemoval = true) - private List images; - + private Status transactionStatus = Status.BEFORE; + @Builder + public TransProduct(User user, String name, Category category, String usedTerm, + String detail, Gender gender, Size size, UsedRank usedRank, + Long point, Method method, Status transactionStatus) { + this.user = user; + this.name = name; + this.category = category; + this.usedTerm = usedTerm; + this.detail = detail; + this.gender = gender; + this.size = size; + this.usedRank = usedRank; + this.point = point != null ? point : 0L; + this.method = method != null ? method : Method.ALL; + this.transactionStatus = transactionStatus != null ? transactionStatus : Status.BEFORE; + } public void updateDetails( String name, @@ -85,8 +92,7 @@ public void updateDetails( UsedRank usedRank, Long point, Method method, - Category category, - List newImageUrls) { + Category category) { this.name = name; this.usedTerm = usedTerm; @@ -97,38 +103,5 @@ public void updateDetails( this.point = point; this.method = method; this.category = category; - - // 이미지 업데이트 - updateImages(newImageUrls); - } - @Builder - public TransProduct(User user, String name, Category category, String usedTerm, - String detail, Gender gender, Size size, UsedRank usedRank, - Long point, Method method, Status transactionStatus) { - this.user = user; - this.name = name; - this.category = category; - this.usedTerm = usedTerm; - this.detail = detail; - this.gender = gender; - this.size = size; - this.usedRank = usedRank; - this.point = point != null ? point : 0L; - this.method = method != null ? method : Method.ALL; - this.transactionStatus = transactionStatus != null ? transactionStatus : Status.BEFORE; - } - - /** - * 기존 이미지 삭제 후 새로운 이미지 리스트로 업데이트 - */ - - private void updateImages(List imageUrls) { - this.images.clear(); - - for (int i = 0; i < imageUrls.size(); i++) { - this.images.add(new TransPImg(i, i == 0, imageUrls.get(i), this)); - } - } - -} +} \ No newline at end of file diff --git a/src/main/java/com/app/treen/products/service/ProductService.java b/src/main/java/com/app/treen/products/service/ProductService.java index 1d4ac5c..d930143 100644 --- a/src/main/java/com/app/treen/products/service/ProductService.java +++ b/src/main/java/com/app/treen/products/service/ProductService.java @@ -2,13 +2,11 @@ import com.app.treen.common.config.S3Uploader; import com.app.treen.common.response.code.status.ErrorStatus; +import com.app.treen.common.response.exception.CustomException; import com.app.treen.jpa.repository.products.*; import com.app.treen.products.dto.ProductQueryHelper; import com.app.treen.products.dto.TradeQueryHelper; -import com.app.treen.products.dto.request.TradeProductSaveDto; -import com.app.treen.products.dto.request.TradeProductUpdateDto; -import com.app.treen.products.dto.request.TransProductSaveDto; -import com.app.treen.products.dto.request.TransProductUpdateDto; +import com.app.treen.products.dto.request.*; import com.app.treen.products.dto.response.TradeProductResponseDto; import com.app.treen.products.dto.response.TradeResponseListDto; import com.app.treen.products.dto.response.TransProductResponseDto; @@ -29,6 +27,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; @RequiredArgsConstructor @Service @@ -58,56 +57,70 @@ public class ProductService { // 1 . 상품 거래 등록 @Transactional - public TransProductResponseDto saveTransProduct(TransProductSaveDto dto, List files, User user) throws IOException { - // S3에 파일 업로드 - List uploadedUrls = s3Uploader.upload(files, "trans-product-images"); - dto.setImageUrls(uploadedUrls); + public TransProductResponseDto saveTransProduct(TransactionRequestDto dto, List files, User user) throws IOException { + TransProductSaveDto transProductDto = dto.getProductRequest(); + TransImgRequestDto transImgRequestDto = dto.getImageRequest(); + TransRegionRequestDto regionRequestDto = dto.getRegionRequest(); // Category 조회 - Category category = categoryRepository.findById(dto.getCategoryId()) + Category category = categoryRepository.findById(transProductDto.getCategoryId()) .orElseThrow(() -> new IllegalArgumentException("Invalid category ID")); // TransProduct 저장 - TransProduct transProduct = transProductRepository.save(dto.toEntity(user, category)); + TransProduct transProduct = transProductRepository.save(transProductDto.toEntity(user, category)); + tradeProductRepository.flush(); + if (transProduct.getId() == null) { + throw new IllegalStateException("TradeProduct 저장 후 ID가 생성되지 않았습니다."); + } + + // S3에 파일 업로드 개수 검증 + if (files == null || files.isEmpty()) { + throw new CustomException(ErrorStatus.IMAGE_MUST_BE_UPLOADED); + } else if (files.size() > 5) { + throw new CustomException(ErrorStatus.IMAGE_OVER_UPLOADED); + } + + List uploadedUrls = s3Uploader.upload(files, "trans-product-images"); // 이미지 저장 - List images = dto.toImageEntities(transProduct); + List images = transImgRequestDto.toImageEntities(transProduct,uploadedUrls); transPImgRepository.saveAll(images); // Region 리스트 조회 - List regions = regionRepository.findAllById(dto.getRegionIds()); - - // regions가 비어있으면 null로 처리 - List transRegions = null; - if (regions.isEmpty()) { - // null을 저장 - transRegionRepository.saveAll(Collections.singletonList(null)); // null 저장 예시 - } else { - // TransRegion 객체 생성 - transRegions = dto.toRegionEntities(transProduct, regions); + List regions = regionRepository.findAllById(regionRequestDto.getRegionIds()); - // TransRegion 저장 + // TransRegion 저장 (regions가 비어있지 않은 경우에만 저장) + List transRegions = (regions.isEmpty()) ? null : regionRequestDto.toRegionEntities(transProduct, regions); + if (transRegions != null) { transRegionRepository.saveAll(transRegions); } - return new TransProductResponseDto(transProduct, transRegions, user); + return new TransProductResponseDto(transProduct, transRegions, images,user); } // 2. 상품 교환 등록 @Transactional public TradeProductResponseDto saveTradeProduct(TradeProductSaveDto dto, List files, User user) throws IOException { - // 1. S3에 파일 업로드 및 URL 변환 - List storedFiles = s3Uploader.upload(files, "trade-product-images"); - dto.setImageUrls(storedFiles); - // 2. Category 조회 + // Category 조회 Category category = categoryRepository.findById(dto.getCategoryId()) .orElseThrow(() -> new IllegalArgumentException("Invalid category ID")); - // 3. TradeProduct 저장 + // TradeProduct 저장 TradeProduct tradeProduct = tradeProductRepository.save(dto.toEntity(user, category)); + // S3에 파일 업로드 및 URL 변환 + List storedFiles = s3Uploader.upload(files, "trade-product-images"); + dto.setImageUrls(storedFiles); + + // S3에 파일 업로드 개수 검증 + if (files == null || files.isEmpty()) { + throw new CustomException(ErrorStatus.IMAGE_MUST_BE_UPLOADED); + } else if (files.size() > 5) { + throw new CustomException(ErrorStatus.IMAGE_OVER_UPLOADED); + } + // 4. 이미지 저장 List images = dto.toImageEntities(tradeProduct); tradePImgRepository.saveAll(images); @@ -170,8 +183,7 @@ public void updateTransProduct(Long productId, TransProductUpdateDto dto, List new RuntimeException(ErrorStatus.PRODUCT_NOT_FOUND.getMessage())); + .orElseThrow(() -> new CustomException(ErrorStatus.PRODUCT_NOT_FOUND)); List transRegions = transRegionRepository.findByTransProduct(selectedProduct); + // 이미지 조회 + List transPImgs = transPImgRepository.findByTransProduct(selectedProduct); // 상품에 연결된 유저 정보 조회 User user = transProductRepository.findUserById(id); // DTO 생성 및 반환 - return new TransProductResponseDto(selectedProduct, transRegions, user); + return new TransProductResponseDto(selectedProduct, transRegions,transPImgs,user); } // 8. 교환상품 상세 조회 @@ -277,20 +291,23 @@ public List getFilteredResults( // 정렬 조건 생성 OrderSpecifier orderSpecifier = ProductQueryHelper.getOrderSpecifier(condition, trans); - // 쿼리 실행 및 결과 반환 - return queryFactory.select( - Projections.constructor( - TransResponseListDto.class, - trans.id, - trans.name, - trans.point - )) - .from(trans) + // ✅ TransProduct 리스트 조회 + List products = queryFactory + .selectFrom(trans) .where(filterBuilder) .orderBy(orderSpecifier) .offset((long) page * size) .limit(size) .fetch(); + + // ✅ 각 상품에 대해 대표 이미지 조회 + return products.stream().map(product -> { + TransPImg mainImage = (TransPImg) transPImgRepository + .findFirstByTransProductAndIsMainTrue(product) + .orElse(null); // 대표 이미지가 없으면 null + + return new TransResponseListDto(product, mainImage); + }).collect(Collectors.toList()); } diff --git a/src/main/java/com/app/treen/user/service/CustomUserDetailService.java b/src/main/java/com/app/treen/user/service/CustomUserDetailService.java index 734e209..364b972 100644 --- a/src/main/java/com/app/treen/user/service/CustomUserDetailService.java +++ b/src/main/java/com/app/treen/user/service/CustomUserDetailService.java @@ -17,14 +17,14 @@ public class CustomUserDetailService implements UserDetailsService { private final UserRepository userRepository; @Override - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - log.info("user Name = {}", username); + public UserDetails loadUserByUsername(String loginId) throws UsernameNotFoundException { + log.info("user loginId = {}", loginId); // 유저가 존재하지 않을 경우 예외 발생 - User user = userRepository.findByUserName(username) + User user = userRepository.findByLoginId(loginId) .orElseThrow(() -> { - log.error("사용자를 찾을 수 없습니다: {}", username); - return new UsernameNotFoundException("사용자를 찾을 수 없습니다: " + username); + log.error("사용자를 찾을 수 없습니다: {}", loginId); + return new UsernameNotFoundException("사용자를 찾을 수 없습니다: " + loginId); }); return new CustomUserDetails(user); diff --git a/src/main/java/com/app/treen/user/service/JwtProvider.java b/src/main/java/com/app/treen/user/service/JwtProvider.java index 185732b..40e20ad 100644 --- a/src/main/java/com/app/treen/user/service/JwtProvider.java +++ b/src/main/java/com/app/treen/user/service/JwtProvider.java @@ -1,5 +1,8 @@ package com.app.treen.user.service; +import com.app.treen.common.response.code.status.ErrorStatus; +import com.app.treen.common.response.exception.CustomException; +import com.app.treen.jpa.repository.RefreshTokenRepository; import com.app.treen.user.dto.request.CustomUserInfoDto; import com.app.treen.user.dto.response.TokenResponseDto; import com.app.treen.user.entity.RefreshToken; @@ -8,6 +11,7 @@ import io.jsonwebtoken.security.Keys; import jakarta.annotation.PostConstruct; import jakarta.servlet.http.HttpServletRequest; +import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -38,6 +42,8 @@ public class JwtProvider { private static final Set blacklistedTokens = new HashSet<>(); + private final RefreshTokenRepository refreshTokenRepository; + @Value("${jwt.secret.key}") private String secretKey; @@ -231,5 +237,32 @@ public String validateRefreshToken(RefreshToken refreshTokenObj){ } + @Transactional + public void saveRefreshToken(TokenResponseDto tokenDto) { + RefreshToken refreshToken = RefreshToken.builder().keyUserId(tokenDto.getKey()).refreshToken(tokenDto.getRefreshToken()).build(); + String userId = refreshToken.getKeyUserId(); + + if (refreshTokenRepository.existsByKeyUserId(userId)) { + refreshTokenRepository.deleteByKeyUserId(userId); + } + refreshTokenRepository.save(refreshToken); + } + + public RefreshToken getRefreshToken(String refreshToken) { + return refreshTokenRepository.findByRefreshToken(refreshToken) + .orElseThrow(() -> new CustomException(ErrorStatus.JWT_INVALID)); + } + + public String validateRefreshToken(String refreshToken) { + RefreshToken getRefreshToken = getRefreshToken(refreshToken); + String createdAccessToken = validateRefreshToken(getRefreshToken); + + if (createdAccessToken == null) { + throw new CustomException(ErrorStatus.JWT_EXPIRED); + } + + return createdAccessToken; + } + } \ No newline at end of file diff --git a/src/main/java/com/app/treen/user/service/UserService.java b/src/main/java/com/app/treen/user/service/UserService.java index 7694292..31e45bf 100644 --- a/src/main/java/com/app/treen/user/service/UserService.java +++ b/src/main/java/com/app/treen/user/service/UserService.java @@ -2,7 +2,6 @@ import com.app.treen.common.response.code.status.ErrorStatus; import com.app.treen.common.response.exception.CustomException; -import com.app.treen.jpa.repository.RefreshTokenRepository; import com.app.treen.jpa.repository.user.UserRepository; import com.app.treen.user.dto.request.CustomUserInfoDto; import com.app.treen.user.dto.request.JoinRequestDto; @@ -10,7 +9,6 @@ import com.app.treen.user.dto.response.LoginResponseDto; import com.app.treen.user.dto.response.MemberResponseDto; import com.app.treen.user.dto.response.TokenResponseDto; -import com.app.treen.user.entity.RefreshToken; import com.app.treen.user.entity.RoleType; import com.app.treen.user.entity.User; import jakarta.transaction.Transactional; @@ -27,34 +25,6 @@ public class UserService { private final PasswordEncoder passwordEncoder; private final JwtProvider jwtProvider; - private final RefreshTokenRepository refreshTokenRepository; - - @Transactional - public void saveRefreshToken(TokenResponseDto tokenDto) { - RefreshToken refreshToken = RefreshToken.builder().keyUserId(tokenDto.getKey()).refreshToken(tokenDto.getRefreshToken()).build(); - String userId = refreshToken.getKeyUserId(); - - if (refreshTokenRepository.existsByKeyUserId(userId)) { - refreshTokenRepository.deleteByKeyUserId(userId); - } - refreshTokenRepository.save(refreshToken); - } - - public RefreshToken getRefreshToken(String refreshToken) { - return refreshTokenRepository.findByRefreshToken(refreshToken) - .orElseThrow(() -> new CustomException(ErrorStatus.JWT_INVALID)); - } - - public String validateRefreshToken(String refreshToken) { - RefreshToken getRefreshToken = getRefreshToken(refreshToken); - String createdAccessToken = jwtProvider.validateRefreshToken(getRefreshToken); - - if (createdAccessToken == null) { - throw new CustomException(ErrorStatus.JWT_EXPIRED); - } - - return createdAccessToken; - } // 회원가입 public MemberResponseDto signUp(@Valid JoinRequestDto joinRequest) throws CustomException { @@ -78,6 +48,10 @@ public MemberResponseDto signUp(@Valid JoinRequestDto joinRequest) throws Custom throw new CustomException(ErrorStatus.USER_PHONE_IS_USED); } + if (password.equals(joinRequest.getPassword2())) { + throw new CustomException(ErrorStatus.PASSWORD_NOT_MATCHED); + } + RoleType role = RoleType.USER; User member = joinRequest.toEntity(role, password); @@ -104,7 +78,7 @@ public LoginResponseDto login(LoginRequestDto loginRequest) { CustomUserInfoDto userInfoDto = new CustomUserInfoDto(member.getId(), member.getLoginId(), member.getRoles()); TokenResponseDto tokenDto = jwtProvider.createTokenByLogin(userInfoDto); - saveRefreshToken(tokenDto); + //saveRefreshToken(tokenDto); return new LoginResponseDto(member,tokenDto); } @@ -114,4 +88,8 @@ public MemberResponseDto getMember(User user) { MemberResponseDto memberResponse = new MemberResponseDto(user); return memberResponse; } + + // 비밀번호 재설정 + // 회원 탈퇴 + // 전화번호 인증 } diff --git a/src/main/java/com/app/treen/user/service/filter/JwtAuthFilter.java b/src/main/java/com/app/treen/user/service/filter/JwtAuthFilter.java index 7f7ede2..af4868b 100644 --- a/src/main/java/com/app/treen/user/service/filter/JwtAuthFilter.java +++ b/src/main/java/com/app/treen/user/service/filter/JwtAuthFilter.java @@ -32,12 +32,11 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha String token = jwtProvider.resolveToken(httpRequest); if (token != null && jwtProvider.validateToken(token)) { - // JWT 토큰에서 이메일 추출 Claims claims = jwtProvider.getUserInfoFromToken(token); - String email = claims.get("email", String.class); + String loginId = claims.get("loginId", String.class); - if (email != null) { - Authentication authentication = jwtProvider.createUserAuthentication(email); + if (loginId != null) { + Authentication authentication = jwtProvider.createUserAuthentication(loginId); SecurityContextHolder.getContext().setAuthentication(authentication); } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 2d467aa..fdfc689 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -5,6 +5,9 @@ # username: ${DB_USERNAME:root} # password: ${DB_PASSWORD:password} spring: + redis: + host: localhost + port: 6379 config: import: optional:file:.env[.properties] # .env 파일을 직접 로드 datasource: @@ -37,11 +40,6 @@ jwt: secret: key: ${JWT_SECRET_KEY} refresh: ${JWT_REFRESH_KEY} - redis: - host: localhost - port: 6379 - repositories: - enabled: false coolsms: apiKey: ${COOL_SMS_KEY} apiSecret: ${COOL_SMS_SECRET_KEY} From dcb6b5d1e0a97748db4203cf138ec5cc662f5c6b Mon Sep 17 00:00:00 2001 From: jiwonLee42 Date: Wed, 22 Jan 2025 18:37:32 +0900 Subject: [PATCH 08/12] =?UTF-8?q?[FEAT]=20=EA=B5=90=ED=99=98=20=EC=83=81?= =?UTF-8?q?=ED=92=88=20=EB=93=B1=EB=A1=9D=20=EC=97=90=EB=9F=AC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/app/treen/products/dto/request/TradeRequestDto.java | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/main/java/com/app/treen/products/dto/request/TradeRequestDto.java diff --git a/src/main/java/com/app/treen/products/dto/request/TradeRequestDto.java b/src/main/java/com/app/treen/products/dto/request/TradeRequestDto.java new file mode 100644 index 0000000..76f4cc7 --- /dev/null +++ b/src/main/java/com/app/treen/products/dto/request/TradeRequestDto.java @@ -0,0 +1,2 @@ +package com.app.treen.products.dto.request;public class TradeRequestDto { +} From 6308823ecfccfb83020f7b94bfd41b56ecf66709 Mon Sep 17 00:00:00 2001 From: jiwonLee42 Date: Wed, 22 Jan 2025 18:37:36 +0900 Subject: [PATCH 09/12] =?UTF-8?q?[FEAT]=20=EA=B5=90=ED=99=98=20=EC=83=81?= =?UTF-8?q?=ED=92=88=20=EB=93=B1=EB=A1=9D=20=EC=97=90=EB=9F=AC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../products/TradePImgRepository.java | 7 ++ .../products/WishCategoryRepository.java | 4 + .../products/dto/request/ImgRequestDto.java | 9 +- .../dto/request/RegionRequestDto.java | 4 - .../dto/request/TradeProductSaveDto.java | 55 ----------- .../products/dto/request/TradeRequestDto.java | 22 ++++- .../dto/response/TradeProductResponseDto.java | 26 +++-- .../dto/response/TradeResponseListDto.java | 9 +- .../treen/products/entity/TradeProduct.java | 23 ----- .../products/service/ProductService.java | 94 +++++++++++-------- 10 files changed, 111 insertions(+), 142 deletions(-) diff --git a/src/main/java/com/app/treen/jpa/repository/products/TradePImgRepository.java b/src/main/java/com/app/treen/jpa/repository/products/TradePImgRepository.java index 4ee8879..3791a55 100644 --- a/src/main/java/com/app/treen/jpa/repository/products/TradePImgRepository.java +++ b/src/main/java/com/app/treen/jpa/repository/products/TradePImgRepository.java @@ -4,6 +4,13 @@ import com.app.treen.products.entity.TradeProduct; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; +import java.util.Optional; + public interface TradePImgRepository extends JpaRepository { void deleteByTradeProduct(TradeProduct existingProduct); + + Optional findByTradeProductAndIsMainTrue(TradeProduct selectedProduct); + + List findAllByTradeProduct(TradeProduct selectedProduct); } diff --git a/src/main/java/com/app/treen/jpa/repository/products/WishCategoryRepository.java b/src/main/java/com/app/treen/jpa/repository/products/WishCategoryRepository.java index 6d1e7cd..393ac3d 100644 --- a/src/main/java/com/app/treen/jpa/repository/products/WishCategoryRepository.java +++ b/src/main/java/com/app/treen/jpa/repository/products/WishCategoryRepository.java @@ -4,6 +4,10 @@ import com.app.treen.products.entity.WishCategory; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; + public interface WishCategoryRepository extends JpaRepository { void deleteByTradeProduct(TradeProduct existingProduct); + + List findAllByTradeProduct(TradeProduct selectedProduct); } diff --git a/src/main/java/com/app/treen/products/dto/request/ImgRequestDto.java b/src/main/java/com/app/treen/products/dto/request/ImgRequestDto.java index 7ff7eff..ce0a60a 100644 --- a/src/main/java/com/app/treen/products/dto/request/ImgRequestDto.java +++ b/src/main/java/com/app/treen/products/dto/request/ImgRequestDto.java @@ -5,20 +5,17 @@ import com.app.treen.products.entity.TradeProduct; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; import java.util.ArrayList; import java.util.List; @Getter +@Setter @NoArgsConstructor public class ImgRequestDto { - private List imageUrls; - public ImgRequestDto(List imageUrls) { - this.imageUrls = imageUrls; - } - - public List toImageEntities(TradeProduct tradeProduct) { + public List toImageEntities(TradeProduct tradeProduct , List imageUrls) { List images = new ArrayList<>(); for (int i = 0; i < imageUrls.size(); i++) { images.add(TradePImg.builder() diff --git a/src/main/java/com/app/treen/products/dto/request/RegionRequestDto.java b/src/main/java/com/app/treen/products/dto/request/RegionRequestDto.java index 7bdb9c0..a25a7ba 100644 --- a/src/main/java/com/app/treen/products/dto/request/RegionRequestDto.java +++ b/src/main/java/com/app/treen/products/dto/request/RegionRequestDto.java @@ -22,10 +22,6 @@ public List toRegionEntities(TradeProduct tradeProduct, List tradeRegions = new ArrayList<>(); for (Region region : regions) { tradeRegions.add(TradeRegion.builder() diff --git a/src/main/java/com/app/treen/products/dto/request/TradeProductSaveDto.java b/src/main/java/com/app/treen/products/dto/request/TradeProductSaveDto.java index 4b3e8ed..48cab2c 100644 --- a/src/main/java/com/app/treen/products/dto/request/TradeProductSaveDto.java +++ b/src/main/java/com/app/treen/products/dto/request/TradeProductSaveDto.java @@ -25,21 +25,7 @@ public class TradeProductSaveDto { private TradeType tradeType; private Long categoryId; - private List imageUrls = new ArrayList<>(); // 이미지 리스트 - private List wishCategoryIds = new ArrayList<>(); // 희망 카테고리 리스트 - private List regionIds = new ArrayList<>(); // 거래 가능 지역 리스트 - public void setImageUrls(List imageUrls) { - this.imageUrls = (imageUrls != null) ? imageUrls : new ArrayList<>(); - } - - public void setWishCategoryIds(List wishCategoryIds) { - this.wishCategoryIds = (wishCategoryIds != null) ? wishCategoryIds : new ArrayList<>(); - } - - public void setRegionIds(List regionIds) { - this.regionIds = (regionIds != null) ? regionIds : new ArrayList<>(); - } public TradeProduct toEntity(User user, Category category) { return TradeProduct.builder() @@ -58,46 +44,5 @@ public TradeProduct toEntity(User user, Category category) { .build(); } - public List toImageEntities(TradeProduct tradeProduct) { - List images = new ArrayList<>(); - for (int i = 0; i < imageUrls.size(); i++) { - images.add(TradePImg.builder() - .tradeProduct(tradeProduct) - .imgUrl(imageUrls.get(i)) - .sortOrder(i) - .isMain(i == 0) // 첫 번째 이미지를 대표 이미지로 설정 - .build()); - } - return images; - } - - public List toWishCategoryEntities(TradeProduct tradeProduct, List wishCategories) { - List wishCategoryEntities = new ArrayList<>(); - for (Category category : wishCategories) { - wishCategoryEntities.add(WishCategory.builder() - .tradeProduct(tradeProduct) - .category(category) - .build()); - } - return wishCategoryEntities; - } - - public List toRegionEntities(TradeProduct tradeProduct, List regions) { - if (tradeProduct == null) { - throw new IllegalArgumentException("TradeProduct cannot be null"); - } - if (regions == null || regions.isEmpty()) { - throw new IllegalArgumentException("Regions cannot be null or empty"); - } - - List tradeRegions = new ArrayList<>(); - for (Region region : regions) { - tradeRegions.add(TradeRegion.builder() - .tradeProduct(tradeProduct) - .region(region) - .build()); - } - return tradeRegions; - } } diff --git a/src/main/java/com/app/treen/products/dto/request/TradeRequestDto.java b/src/main/java/com/app/treen/products/dto/request/TradeRequestDto.java index 76f4cc7..60c5478 100644 --- a/src/main/java/com/app/treen/products/dto/request/TradeRequestDto.java +++ b/src/main/java/com/app/treen/products/dto/request/TradeRequestDto.java @@ -1,2 +1,22 @@ -package com.app.treen.products.dto.request;public class TradeRequestDto { +package com.app.treen.products.dto.request; + + +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class TradeRequestDto { + + private RegionRequestDto regionRequest; + private ImgRequestDto imgRequest; + private TradeProductSaveDto tradeProduct; + private WishCategoryRequestDto wishCategoryRequest; + + public TradeRequestDto(TradeProductSaveDto tradeProduct, ImgRequestDto imgRequest, RegionRequestDto regionRequest, WishCategoryRequestDto wishCategoryRequest){ + this.regionRequest = regionRequest; + this.tradeProduct = tradeProduct; + this.imgRequest = imgRequest; + this.wishCategoryRequest = wishCategoryRequest; + } } diff --git a/src/main/java/com/app/treen/products/dto/response/TradeProductResponseDto.java b/src/main/java/com/app/treen/products/dto/response/TradeProductResponseDto.java index 1af5996..f3de168 100644 --- a/src/main/java/com/app/treen/products/dto/response/TradeProductResponseDto.java +++ b/src/main/java/com/app/treen/products/dto/response/TradeProductResponseDto.java @@ -8,6 +8,7 @@ import java.time.Duration; import java.time.LocalDateTime; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -42,7 +43,7 @@ public class TradeProductResponseDto { private Long writerId; - public TradeProductResponseDto(TradeProduct tradeProduct, List regions, User user) { + public TradeProductResponseDto(TradeProduct tradeProduct, List regions, List images,List wishCategories,User user) { this.id = tradeProduct.getId(); this.name = tradeProduct.getName(); this.usedTerm = tradeProduct.getUsedTerm(); @@ -56,15 +57,24 @@ public TradeProductResponseDto(TradeProduct tradeProduct, List regi this.likedCount = tradeProduct.getLikedCount(); this.wishSize = tradeProduct.getWishSize(); this.wishColor = tradeProduct.getWishColor(); - this.imageUrls = tradeProduct.getImages().stream() + this.imageUrls = images.stream() .map(TradePImg::getImgUrl) .collect(Collectors.toList()); - this.wishCategories = tradeProduct.getWishCategories().stream() - .map(wishCategory -> wishCategory.getCategory().getName()) - .collect(Collectors.toList()); - this.regions = regions.stream() - .map(tradeRegion -> tradeRegion.getRegion().getName()) - .collect(Collectors.toList()); + if (wishCategories != null) { + this.wishCategories = wishCategories.stream() + .map(wishCategory -> wishCategory.getCategory().getName()) + .collect(Collectors.toList()); + } else { + this.wishCategories = Collections.emptyList(); // 빈 리스트로 초기화 + } + + if (regions != null) { + this.regions = regions.stream() + .map(tradeRegion -> tradeRegion.getRegion().getName()) + .collect(Collectors.toList()); + } else { + this.regions = Collections.emptyList(); // 빈 리스트로 초기화 + } this.status = tradeProduct.getTransactionStatus(); this.tradeType = tradeProduct.getTradeType(); diff --git a/src/main/java/com/app/treen/products/dto/response/TradeResponseListDto.java b/src/main/java/com/app/treen/products/dto/response/TradeResponseListDto.java index d54cec7..8364aba 100644 --- a/src/main/java/com/app/treen/products/dto/response/TradeResponseListDto.java +++ b/src/main/java/com/app/treen/products/dto/response/TradeResponseListDto.java @@ -27,14 +27,9 @@ public class TradeResponseListDto { private Method method; // 거래 방법 private UsedRank usedRank; // 사용 - public TradeResponseListDto(TradeProduct tradeProduct) { + public TradeResponseListDto(TradeProduct tradeProduct, TradePImg tradePImg) { this.id = tradeProduct.getId(); - // 상품 대표 사진 (첫 번째 이미지를 대표 사진으로 설정) - this.representativeImage = tradeProduct.getImages().stream() - .map(TradePImg::getImgUrl) - .findFirst() - .orElse(null); - + this.representativeImage = tradePImg.getImgUrl(); this.name = tradeProduct.getName(); // 상품명 this.category = tradeProduct.getCategory().getName(); // 카테고리 this.likedCount = tradeProduct.getLikedCount(); // 좋아요 수 diff --git a/src/main/java/com/app/treen/products/entity/TradeProduct.java b/src/main/java/com/app/treen/products/entity/TradeProduct.java index 82139c2..a0b065c 100644 --- a/src/main/java/com/app/treen/products/entity/TradeProduct.java +++ b/src/main/java/com/app/treen/products/entity/TradeProduct.java @@ -67,12 +67,6 @@ public class TradeProduct extends BaseTimeEntity { @Column(name = "liked_count", nullable = false) private Long likedCount = 0L; - @OneToMany(mappedBy = "tradeProduct", cascade = CascadeType.ALL) - private List images; - - @Builder.Default - @OneToMany(mappedBy = "tradeProduct", cascade = CascadeType.ALL, orphanRemoval = true) - private List wishCategories = new ArrayList<>(); @Enumerated(EnumType.STRING) @Column(name = "trade_type", nullable = false) @@ -96,25 +90,8 @@ public void updateDetail(TradeProductUpdateDto dto, Category category) { this.wishSize = dto.getWishSize(); this.tradeType = dto.getTradeType(); this.category = category; - // 이미지 업데이트 - updateImages(dto.getImageUrls()); } - private void updateImages(List imageUrls) { - this.images.clear(); - - // 새로운 이미지 목록 추가 - for (int i = 0; i < imageUrls.size(); i++) { - this.images.add(TradePImg.builder() - .tradeProduct(this) - .imgUrl(imageUrls.get(i)) - .sortOrder(i) - .isMain(i == 0) // 첫 번째 이미지를 대표 이미지로 설정 - .build()); - } - } - - } diff --git a/src/main/java/com/app/treen/products/service/ProductService.java b/src/main/java/com/app/treen/products/service/ProductService.java index d930143..3a7fe46 100644 --- a/src/main/java/com/app/treen/products/service/ProductService.java +++ b/src/main/java/com/app/treen/products/service/ProductService.java @@ -99,60 +99,71 @@ public TransProductResponseDto saveTransProduct(TransactionRequestDto dto, List< } + // 2. 상품 교환 등록 @Transactional - public TradeProductResponseDto saveTradeProduct(TradeProductSaveDto dto, List files, User user) throws IOException { + public TradeProductResponseDto saveTradeProduct(TradeRequestDto dto, List files, User user) throws IOException { + TradeProductSaveDto tradeProductDto = dto.getTradeProduct(); + ImgRequestDto tradeImgRequestDto = dto.getImgRequest(); + RegionRequestDto regionRequestDto = dto.getRegionRequest(); + WishCategoryRequestDto wishCategoryRequestDto = dto.getWishCategoryRequest(); // Category 조회 - Category category = categoryRepository.findById(dto.getCategoryId()) + Category category = categoryRepository.findById(tradeProductDto.getCategoryId()) .orElseThrow(() -> new IllegalArgumentException("Invalid category ID")); // TradeProduct 저장 - TradeProduct tradeProduct = tradeProductRepository.save(dto.toEntity(user, category)); + TradeProduct tradeProduct = tradeProductRepository.save(tradeProductDto.toEntity(user, category)); + tradeProductRepository.flush(); - // S3에 파일 업로드 및 URL 변환 - List storedFiles = s3Uploader.upload(files, "trade-product-images"); - dto.setImageUrls(storedFiles); + if (tradeProduct.getId() == null) { + throw new IllegalStateException("TradeProduct 저장 후 ID가 생성되지 않았습니다."); + } - // S3에 파일 업로드 개수 검증 + // S3 파일 업로드 개수 검증 (검증 후 업로드 진행) if (files == null || files.isEmpty()) { throw new CustomException(ErrorStatus.IMAGE_MUST_BE_UPLOADED); } else if (files.size() > 5) { throw new CustomException(ErrorStatus.IMAGE_OVER_UPLOADED); } - // 4. 이미지 저장 - List images = dto.toImageEntities(tradeProduct); + // S3에 파일 업로드 및 URL 변환 + List uploadedUrls = s3Uploader.upload(files, "trade-product-images"); + tradeImgRequestDto.toImageEntities(tradeProduct,uploadedUrls); + + // 이미지 저장 + List images = tradeImgRequestDto.toImageEntities(tradeProduct, uploadedUrls); tradePImgRepository.saveAll(images); + List wishCategoryIds = wishCategoryRequestDto.getWishCategoryIds(); + + List wishCategoryEntities = new ArrayList<>(); + // 교환 타입에 따라 설정 + if (tradeProductDto.getTradeType() == TradeType.ANYTHING){ + if (wishCategoryIds != null && !wishCategoryIds.isEmpty()) { + List wishCategories = categoryRepository.findAllById(wishCategoryIds); + wishCategoryEntities = wishCategoryRequestDto.toWishCategoryEntities(tradeProduct, wishCategories); + wishCategoryRepository.saveAll(wishCategoryEntities); + } - // 5. 희망 카테고리 저장 - if (dto.getWishCategoryIds() != null && !dto.getWishCategoryIds().isEmpty()) { - List wishCategories = categoryRepository.findAllById(dto.getWishCategoryIds()); - List wishCategoryEntities = dto.toWishCategoryEntities(tradeProduct, wishCategories); - wishCategoryRepository.saveAll(wishCategoryEntities); } - // 6. 지역 정보 저장 + // 지역 정보 저장 List tradeRegions = new ArrayList<>(); - List regions = new ArrayList<>(); - if (dto.getRegionIds() != null && !dto.getRegionIds().isEmpty()) { - regions = regionRepository.findAllById(dto.getRegionIds()); + if (regionRequestDto.getRegionIds() != null && !regionRequestDto.getRegionIds().isEmpty()) { + List regions = regionRepository.findAllById(regionRequestDto.getRegionIds()); - // regions가 비어있지 않으면 TransRegion 생성 + // regions가 비어있지 않으면 TradeRegion 생성 후 저장 if (!regions.isEmpty()) { - tradeRegions = dto.toRegionEntities(tradeProduct, regions); + tradeRegions = regionRequestDto.toRegionEntities(tradeProduct, regions); tradeRegionRepository.saveAll(tradeRegions); } - } else { - // 만약 dto.getRegionIds()가 null이거나 비어 있으면, tradeRegions를 null로 설정하거나 적절한 값으로 처리 - tradeRegions = null; // 또는 빈 리스트를 설정: tradeRegions = new ArrayList<>(); } - // 7. 응답 DTO 생성 및 반환 - return new TradeProductResponseDto(tradeProduct, tradeRegions, user); - + // 응답 DTO 생성 및 반환 + return new TradeProductResponseDto(tradeProduct, tradeRegions, images,wishCategoryEntities, user); } + // 3. 거래 상품 수정 @Transactional public void updateTransProduct(Long productId, TransProductUpdateDto dto, List files) throws IOException @@ -261,16 +272,22 @@ public TransProductResponseDto findTransProductById(Long id) { public TradeProductResponseDto findTradeProductById(Long id) { // 교환 상품 조회 TradeProduct selectedProduct = tradeProductRepository.findById(id) - .orElseThrow(() -> new RuntimeException(ErrorStatus.PRODUCT_NOT_FOUND.getMessage())); + .orElseThrow(() -> new CustomException(ErrorStatus.PRODUCT_NOT_FOUND)); // 연결된 지역 정보 조회 List regions = tradeRegionRepository.findByTradeProduct(selectedProduct); + // 희망 카테고리 조회 + List wishCategories = wishCategoryRepository.findAllByTradeProduct(selectedProduct); + + // 이미지 조회 + List tradePImgs = tradePImgRepository.findAllByTradeProduct(selectedProduct); + // 상품에 연결된 유저 정보 조회 User user = selectedProduct.getUser(); // DTO 생성 및 반환 - return new TradeProductResponseDto(selectedProduct, regions, user); + return new TradeProductResponseDto(selectedProduct, regions, tradePImgs, wishCategories,user); } @@ -330,22 +347,23 @@ public List getFilteredTradeResults( // 정렬 조건 생성 OrderSpecifier orderSpecifier = TradeQueryHelper.getOrderSpecifier(trade); - // 쿼리 실행 및 결과 반환 - return queryFactory.select( - Projections.constructor( - TradeResponseListDto.class, - trade.id, - trade.name - )) - .from(trade) + // ✅ TradeProduct 리스트 조회 + List products = queryFactory + .selectFrom(trade) .where(filterBuilder) .orderBy(orderSpecifier) .offset((long) page * size) .limit(size) .fetch(); - } - + // ✅ 각 상품에 대해 대표 이미지 조회 + return products.stream().map(product -> { + TradePImg mainImage = tradePImgRepository + .findByTradeProductAndIsMainTrue(product) + .orElse(null); + return new TradeResponseListDto(product, mainImage); + }).collect(Collectors.toList()); + } // 11. 거래 상품 좋아요 취소 @Transactional public boolean increaseLikeTransaction(Long productId, User user) { From f4303334fd03fdc974523553203a41af3a67d12a Mon Sep 17 00:00:00 2001 From: Leeyoujin00 Date: Thu, 23 Jan 2025 18:54:33 +0900 Subject: [PATCH 10/12] =?UTF-8?q?[FEAT]=20=EC=83=81=ED=92=88=EA=B1=B0?= =?UTF-8?q?=EB=9E=98=20=EC=98=88=EC=95=BD=20=EC=83=9D=EC=84=B1,=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20#31?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- .../dto/response/ChatRoomDetailResponse.java | 60 +++++++++++ .../app/treen/chat_room/entity/ChatRoom.java | 1 + .../response/code/status/ErrorStatus.java | 8 +- .../transactions/TransactionsRepository.java | 10 ++ .../treen/products/entity/TransProduct.java | 11 ++ .../controller/TransactionsController.java | 42 ++++++++ .../request/TransactionsSaveRequestDto.java | 25 +++++ .../dto/response/TransactionsResponseDto.java | 23 ++++ .../transactions/entity/Transactions.java | 48 +++++++++ .../service/TransactionsService.java | 102 ++++++++++++++++++ 11 files changed, 329 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/app/treen/jpa/repository/transactions/TransactionsRepository.java create mode 100644 src/main/java/com/app/treen/transactions/controller/TransactionsController.java create mode 100644 src/main/java/com/app/treen/transactions/dto/request/TransactionsSaveRequestDto.java create mode 100644 src/main/java/com/app/treen/transactions/dto/response/TransactionsResponseDto.java create mode 100644 src/main/java/com/app/treen/transactions/entity/Transactions.java create mode 100644 src/main/java/com/app/treen/transactions/service/TransactionsService.java diff --git a/.gitignore b/.gitignore index 6b049c1..cf64bb1 100644 --- a/.gitignore +++ b/.gitignore @@ -30,8 +30,7 @@ out/ !**/src/test/**/out/ ### env ### -./.env -src/main/java/com/app/treen/.env +.env ### NetBeans ### /nbproject/private/ diff --git a/src/main/java/com/app/treen/chat_room/dto/response/ChatRoomDetailResponse.java b/src/main/java/com/app/treen/chat_room/dto/response/ChatRoomDetailResponse.java index 2e4ea76..450f523 100644 --- a/src/main/java/com/app/treen/chat_room/dto/response/ChatRoomDetailResponse.java +++ b/src/main/java/com/app/treen/chat_room/dto/response/ChatRoomDetailResponse.java @@ -1,6 +1,12 @@ package com.app.treen.chat_room.dto.response; import com.app.treen.message.document.Message; +import com.app.treen.products.entity.TransProduct; +import com.app.treen.products.entity.enumeration.Gender; +import com.app.treen.products.entity.enumeration.Method; +import com.app.treen.products.entity.enumeration.Size; +import com.app.treen.products.entity.enumeration.UsedRank; +import com.app.treen.transactions.entity.Transactions; import com.app.treen.user.entity.User; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Builder; @@ -17,6 +23,8 @@ public class ChatRoomDetailResponse { private List messages; private MemberDto buyer; private MemberDto seller; + private TransProductDto transProduct; + private TransactionsDto transaction; @Data @Builder @@ -52,4 +60,56 @@ public static MessageDto from(Message message) { } } + @Data + @Builder + public static class TransProductDto { + private Long id; + private String name; + private String usedTerm; + private Size size; + private UsedRank usedRank; + private Long point; + private Method method; + private String category; + private String imageUrls; + + public static TransProductDto from(TransProduct transProduct) { + return TransProductDto.builder() + .id(transProduct.getId()) + .name(transProduct.getName()) + .usedTerm(transProduct.getUsedTerm()) + .size(transProduct.getSize()) + .point(transProduct.getPoint()) + .method(transProduct.getMethod()) + .category(transProduct.getCategory().toString()) + //.imageUrls(transProduct.) + .build(); + } + } + + @Data + @Builder + public static class TransactionsDto { + + private Long id; + private LocalDateTime transDate; + private boolean isDirect; + private String place; + private String deliveryAddress; + private String deliveryRequest; + private String deliveryFeeAccount; + + public static TransactionsDto from(Transactions transaction) { + return TransactionsDto.builder() + .id(transaction.getId()) + .transDate(transaction.getTransDate()) + .isDirect(transaction.isDirect()) + .place(transaction.getPlace()) + .deliveryAddress(transaction.getDeliveryAddress()) + .deliveryRequest(transaction.getDeliveryRequest()) + .deliveryFeeAccount(transaction.getDeliveryFeeAccount()) + .build(); + } + } + } diff --git a/src/main/java/com/app/treen/chat_room/entity/ChatRoom.java b/src/main/java/com/app/treen/chat_room/entity/ChatRoom.java index 72fd0f6..d05da89 100644 --- a/src/main/java/com/app/treen/chat_room/entity/ChatRoom.java +++ b/src/main/java/com/app/treen/chat_room/entity/ChatRoom.java @@ -18,6 +18,7 @@ public class ChatRoom extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "chat_room_id") private Long id; private String title; private boolean isReserved; // 거래 예약 여부 diff --git a/src/main/java/com/app/treen/common/response/code/status/ErrorStatus.java b/src/main/java/com/app/treen/common/response/code/status/ErrorStatus.java index 0afdc3d..12b272b 100644 --- a/src/main/java/com/app/treen/common/response/code/status/ErrorStatus.java +++ b/src/main/java/com/app/treen/common/response/code/status/ErrorStatus.java @@ -36,7 +36,6 @@ public enum ErrorStatus implements BaseErrorCode { USER_IS_ALREADY_REGISTERED_KAKAO(HttpStatus.IM_USED, "USER4016", "이미 카카오계정으로 가입된 전화번호입니다. 카카오로 로그인해주세요."), USER_IS_INTEGRATED_KAKAO(HttpStatus.ACCEPTED, "USER4017", "사용자의 계정이 카카오계정과 통합되었습니다. YESOL 계정 혹은 카카오로 로그인해주세요."), - // 관리자 관련 에러 NOT_ADMIN(HttpStatus.UNAUTHORIZED, "ADMIN4001", "관리자의 권한이 없습니다."), @@ -128,9 +127,14 @@ public enum ErrorStatus implements BaseErrorCode { BOARDCOMPLAINT_NOT_FOUND(HttpStatus.NOT_FOUND, "COMPLAINT4001", "신고내역을 찾지 못했습니다."), COMMENTCOMPLAINT_NOT_FOUND(HttpStatus.NOT_FOUND, "COMMENT4001", "신고내역을 찾지 못했습니다."), PINCOMPLAINT_NOT_FOUND(HttpStatus.NOT_FOUND, "PIN4001", "신고내역을 찾지 못했습니다."), - NOT_FOUND_CHAT_ROOM(HttpStatus.NOT_FOUND, "CHATROOM4001", "채팅방을 찾지 못했습니다."), + // 채팅방 에러 + NOT_FOUND_CHAT_ROOM(HttpStatus.NOT_FOUND, "CHATROOM4001", "채팅방을 찾지 못했습니다."), + // 상품거래 예약 에러 + NOT_FOUND_TRANSACTIONS(HttpStatus.NOT_FOUND, "TRANSACTIONS4001", "상품거래 예약을 찾지 못했습니다."), + PERMISSION_DENIED_TRANSACTIONS(HttpStatus.UNAUTHORIZED, "USER4001", "타인의 예약 내역은 수정 및 삭제할 수 없습니다."), + SHOULD_BE_BOOKED(HttpStatus.BAD_REQUEST, "TRANSACTIONS400", "판매완료 및 예약 전 상품은 예약 취소할 수 없습니다."), // 상품 에러 PRODUCT_NOT_FOUND(HttpStatus.NOT_FOUND, "PRODUCT4001", "상품을 찾을 수 없습니다."), diff --git a/src/main/java/com/app/treen/jpa/repository/transactions/TransactionsRepository.java b/src/main/java/com/app/treen/jpa/repository/transactions/TransactionsRepository.java new file mode 100644 index 0000000..6d27984 --- /dev/null +++ b/src/main/java/com/app/treen/jpa/repository/transactions/TransactionsRepository.java @@ -0,0 +1,10 @@ +package com.app.treen.jpa.repository.transactions; + +import com.app.treen.transactions.entity.Transactions; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface TransactionsRepository extends JpaRepository { + + + +} diff --git a/src/main/java/com/app/treen/products/entity/TransProduct.java b/src/main/java/com/app/treen/products/entity/TransProduct.java index a1f4080..26c1e3e 100644 --- a/src/main/java/com/app/treen/products/entity/TransProduct.java +++ b/src/main/java/com/app/treen/products/entity/TransProduct.java @@ -131,4 +131,15 @@ private void updateImages(List imageUrls) { } } + public void bookTransaction() { + this.transactionStatus = Status.BOOKED; + } + + public void cancelTransaction() { + this.transactionStatus = Status.BEFORE; + } + + public void completeTransaction() { + this.transactionStatus = Status.COMPLETED; + } } diff --git a/src/main/java/com/app/treen/transactions/controller/TransactionsController.java b/src/main/java/com/app/treen/transactions/controller/TransactionsController.java new file mode 100644 index 0000000..28845a1 --- /dev/null +++ b/src/main/java/com/app/treen/transactions/controller/TransactionsController.java @@ -0,0 +1,42 @@ +package com.app.treen.transactions.controller; + +import com.app.treen.transactions.dto.request.TransactionsSaveRequestDto; +import com.app.treen.transactions.dto.response.TransactionsResponseDto; +import com.app.treen.transactions.service.TransactionsService; +import com.app.treen.user.service.CustomUserDetails; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/transactions") +public class TransactionsController { + + private final TransactionsService transactionsService; + + // 상품거래 예약 생성 + @PostMapping("/auth/save") + public ResponseEntity saveTransactions(@AuthenticationPrincipal CustomUserDetails userDetails, + TransactionsSaveRequestDto transactionsSaveRequestDto) { + + Long transactionsId = transactionsService.createTransactions(userDetails.getUser(), transactionsSaveRequestDto); + TransactionsResponseDto responseDto = transactionsService.getTransactions(transactionsId); + + return ResponseEntity.status(HttpStatus.OK).body(responseDto); + } + + // 상품거래 예약 취소 + @DeleteMapping("/auth/delete/{transactionsId}") + public ResponseEntity deleteTransactions(@AuthenticationPrincipal CustomUserDetails userDetails, + @PathVariable("transactionsId") Long transactionsId) { + + transactionsService.cancelTransactions(userDetails.getUser(), transactionsId); + TransactionsResponseDto responseDto = transactionsService.getTransactions(transactionsId); + + return ResponseEntity.ok().build(); + } + +} diff --git a/src/main/java/com/app/treen/transactions/dto/request/TransactionsSaveRequestDto.java b/src/main/java/com/app/treen/transactions/dto/request/TransactionsSaveRequestDto.java new file mode 100644 index 0000000..d142317 --- /dev/null +++ b/src/main/java/com/app/treen/transactions/dto/request/TransactionsSaveRequestDto.java @@ -0,0 +1,25 @@ +package com.app.treen.transactions.dto.request; + +import com.app.treen.chat_room.entity.ChatRoom; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import lombok.*; + +import java.time.LocalDateTime; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class TransactionsSaveRequestDto { + + private Long transChatRoomId; + private LocalDateTime transDate; + private boolean isDirect; + private String place; + private String deliveryAddress; + private String deliveryRequest; + private String deliveryFeeAccount; + +} diff --git a/src/main/java/com/app/treen/transactions/dto/response/TransactionsResponseDto.java b/src/main/java/com/app/treen/transactions/dto/response/TransactionsResponseDto.java new file mode 100644 index 0000000..97c0402 --- /dev/null +++ b/src/main/java/com/app/treen/transactions/dto/response/TransactionsResponseDto.java @@ -0,0 +1,23 @@ +package com.app.treen.transactions.dto.response; + +import lombok.*; + +import java.time.LocalDateTime; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class TransactionsResponseDto { + + private Long id; + private Long transChatRoomId; + private LocalDateTime transDate; + private boolean isDirect; + private String place; + private String deliveryAddress; + private String deliveryRequest; + private String deliveryFeeAccount; + +} diff --git a/src/main/java/com/app/treen/transactions/entity/Transactions.java b/src/main/java/com/app/treen/transactions/entity/Transactions.java new file mode 100644 index 0000000..2627fa4 --- /dev/null +++ b/src/main/java/com/app/treen/transactions/entity/Transactions.java @@ -0,0 +1,48 @@ +package com.app.treen.transactions.entity; + +import com.app.treen.BaseTimeEntity; +import com.app.treen.chat_room.entity.ChatRoom; +import com.app.treen.products.entity.TransProduct; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; + +import java.time.LocalDateTime; + +@Entity +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class Transactions extends BaseTimeEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "transactions_id") + private Long id; + + // 대상테이블에 외래키 방식 + @OneToOne + @JoinColumn(name = "chat_room_id") + private ChatRoom transChatRoom; + + private LocalDateTime transDate; // 거래일자 + private boolean isDirect; // 직거래 여부 + + private String place; // 거래 장소(직거래시) + + private String deliveryAddress; // 택배 배송지 + private String deliveryRequest; // 택배 요청사항 + private String deliveryFeeAccount; // 택배비 송금계좌 + + + public void setDeliveryInfo(String deliveryAddress, String deliveryRequest, String deliveryFeeAccount) { + this.deliveryAddress = deliveryAddress; + this.deliveryRequest = deliveryRequest; + this.deliveryFeeAccount = deliveryFeeAccount; + } +} diff --git a/src/main/java/com/app/treen/transactions/service/TransactionsService.java b/src/main/java/com/app/treen/transactions/service/TransactionsService.java new file mode 100644 index 0000000..61791d4 --- /dev/null +++ b/src/main/java/com/app/treen/transactions/service/TransactionsService.java @@ -0,0 +1,102 @@ +package com.app.treen.transactions.service; + +import com.app.treen.chat_room.entity.ChatRoom; +import com.app.treen.common.response.code.status.ErrorStatus; +import com.app.treen.common.response.exception.CustomException; +import com.app.treen.jpa.repository.ChatRoomRepository; +import com.app.treen.jpa.repository.transactions.TransactionsRepository; +import com.app.treen.products.entity.enumeration.Status; +import com.app.treen.transactions.dto.request.TransactionsSaveRequestDto; +import com.app.treen.transactions.dto.response.TransactionsResponseDto; +import com.app.treen.transactions.entity.Transactions; +import com.app.treen.user.entity.User; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class TransactionsService { + + private final TransactionsRepository transactionsRepository; + private final ChatRoomRepository chatRoomRepository; + + // 상품거래 예약 생성 + @Transactional + public long createTransactions(User user, TransactionsSaveRequestDto dto) { + + ChatRoom findChatRoom = chatRoomRepository.findById(dto.getTransChatRoomId()) + .orElseThrow(() -> new CustomException(ErrorStatus.NOT_FOUND_CHAT_ROOM)); + + Transactions transactions = Transactions.builder() + .transChatRoom(findChatRoom) + .transDate(dto.getTransDate()) + .isDirect(dto.isDirect()) + .place(dto.getPlace()) + .build(); + + if (!dto.isDirect()) { + transactions.setDeliveryInfo(dto.getDeliveryAddress(), dto.getDeliveryRequest(), dto.getDeliveryFeeAccount()); + } + + // 상품 거래 상태 변경 + transactions.getTransChatRoom().getTransProduct().bookTransaction(); + + return transactionsRepository.save(transactions).getId(); + } + + // 상품거래 예약 조회 + public TransactionsResponseDto getTransactions(Long transactionsId) { + Transactions transactions = transactionsRepository.findById(transactionsId) + .orElseThrow(() -> new CustomException(ErrorStatus.NOT_FOUND_TRANSACTIONS)); + + return TransactionsResponseDto.builder() + .id(transactions.getId()) + .transDate(transactions.getTransDate()) + .isDirect(transactions.isDirect()) + .place(transactions.getPlace()) + .deliveryAddress(transactions.getDeliveryAddress()) + .deliveryRequest(transactions.getDeliveryRequest()) + .deliveryFeeAccount(transactions.getDeliveryFeeAccount()) + .build(); + } + + // 상품거래 예약 취소 + @Transactional + public void cancelTransactions(User user, Long transactionsId) { + + Transactions transactions = transactionsRepository.findById(transactionsId) + .orElseThrow(() -> new CustomException(ErrorStatus.NOT_FOUND_TRANSACTIONS)); + + checkEditTransactions(user, transactions); + + // 상품 상태 변경 + transactions.getTransChatRoom().getTransProduct().cancelTransaction(); + + } + + // 거래 완료 + @Transactional + public void completeTransactions(Long transactionsId) { + + Transactions transactions = transactionsRepository.findById(transactionsId) + .orElseThrow(() -> new CustomException(ErrorStatus.NOT_FOUND_TRANSACTIONS)); + + // 상품 상태 변경 + transactions.getTransChatRoom().getTransProduct().completeTransaction(); + } + + private void checkEditTransactions(User user, Transactions transactions) { + + // 예약한 거래 상품의 판매자만 예약 취소 가능 + if (!transactions.getTransChatRoom().getSeller().getId().equals(user.getId())) { + throw new CustomException(ErrorStatus.PERMISSION_DENIED_TRANSACTIONS); + } + + if (!transactions.getTransChatRoom().getTransProduct().getTransactionStatus().equals(Status.BOOKED)) { + throw new CustomException(ErrorStatus.SHOULD_BE_BOOKED); + } + } + +} From 7a1d6fca65d001f55f760d12842e7aefa32c4448 Mon Sep 17 00:00:00 2001 From: Leeyoujin00 Date: Thu, 23 Jan 2025 18:57:38 +0900 Subject: [PATCH 11/12] =?UTF-8?q?[CHORE]=20.gitignore=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 0 .gitignore | 3 +-- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 .env diff --git a/.env b/.env deleted file mode 100644 index e69de29..0000000 diff --git a/.gitignore b/.gitignore index 6b049c1..cf64bb1 100644 --- a/.gitignore +++ b/.gitignore @@ -30,8 +30,7 @@ out/ !**/src/test/**/out/ ### env ### -./.env -src/main/java/com/app/treen/.env +.env ### NetBeans ### /nbproject/private/ From 107f1a522a4f353e787b9627e6ab43fedbdd27eb Mon Sep 17 00:00:00 2001 From: Leeyoujin00 Date: Tue, 28 Jan 2025 22:24:00 +0900 Subject: [PATCH 12/12] =?UTF-8?q?[FEAT]=20=EC=83=81=ED=92=88=EA=B1=B0?= =?UTF-8?q?=EB=9E=98=20=EC=98=88=EC=95=BD=20=EC=83=9D=EC=84=B1,=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C,=20=EC=B7=A8=EC=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jpa/repository/trade/TradeRepository.java | 7 ++++++ .../treen/products/entity/TransProduct.java | 12 +++++++++ .../trade/controller/TradeController.java | 13 ++++++++++ .../com/app/treen/trade/entity/Trade.java | 25 +++++++++++++++++++ .../app/treen/trade/service/TradeService.java | 20 +++++++++++++++ 5 files changed, 77 insertions(+) create mode 100644 src/main/java/com/app/treen/jpa/repository/trade/TradeRepository.java create mode 100644 src/main/java/com/app/treen/trade/controller/TradeController.java create mode 100644 src/main/java/com/app/treen/trade/entity/Trade.java create mode 100644 src/main/java/com/app/treen/trade/service/TradeService.java diff --git a/src/main/java/com/app/treen/jpa/repository/trade/TradeRepository.java b/src/main/java/com/app/treen/jpa/repository/trade/TradeRepository.java new file mode 100644 index 0000000..57e334d --- /dev/null +++ b/src/main/java/com/app/treen/jpa/repository/trade/TradeRepository.java @@ -0,0 +1,7 @@ +package com.app.treen.jpa.repository.trade; + +import com.app.treen.trade.entity.Trade; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface TradeRepository extends JpaRepository { +} diff --git a/src/main/java/com/app/treen/products/entity/TransProduct.java b/src/main/java/com/app/treen/products/entity/TransProduct.java index f3f67e2..c235adc 100644 --- a/src/main/java/com/app/treen/products/entity/TransProduct.java +++ b/src/main/java/com/app/treen/products/entity/TransProduct.java @@ -104,4 +104,16 @@ public void updateDetails( this.method = method; this.category = category; } + + public void bookTransaction() { + this.transactionStatus = Status.BOOKED; + } + + public void cancelTransaction() { + this.transactionStatus = Status.BEFORE; + } + + public void completeTransaction() { + this.transactionStatus = Status.COMPLETED; + } } \ No newline at end of file diff --git a/src/main/java/com/app/treen/trade/controller/TradeController.java b/src/main/java/com/app/treen/trade/controller/TradeController.java new file mode 100644 index 0000000..bb1218e --- /dev/null +++ b/src/main/java/com/app/treen/trade/controller/TradeController.java @@ -0,0 +1,13 @@ +package com.app.treen.trade.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/trade") +public class TradeController { + + +} diff --git a/src/main/java/com/app/treen/trade/entity/Trade.java b/src/main/java/com/app/treen/trade/entity/Trade.java new file mode 100644 index 0000000..e352937 --- /dev/null +++ b/src/main/java/com/app/treen/trade/entity/Trade.java @@ -0,0 +1,25 @@ +package com.app.treen.trade.entity; + +import com.app.treen.BaseTimeEntity; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class Trade extends BaseTimeEntity { // 승인 완료된 자유교환 + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "trade_id") + private Long id; + + + + +} diff --git a/src/main/java/com/app/treen/trade/service/TradeService.java b/src/main/java/com/app/treen/trade/service/TradeService.java new file mode 100644 index 0000000..b21599f --- /dev/null +++ b/src/main/java/com/app/treen/trade/service/TradeService.java @@ -0,0 +1,20 @@ +package com.app.treen.trade.service; + +import com.app.treen.jpa.repository.trade.TradeRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class TradeService { + + private final TradeRepository tradeRepository; + + // 자유교환 신청 생성 + @Transactional + public void saveTradeRequest() { + + } +}