diff --git a/build.gradle b/build.gradle index 67cbe92b61..b2cb05bea3 100644 --- a/build.gradle +++ b/build.gradle @@ -75,6 +75,9 @@ dependencies { // JAXB deprecated warning implementation 'javax.xml.bind:jaxb-api:2.3.0' + + // aop + implementation 'org.springframework.boot:spring-boot-starter-aop' } tasks.named('test') { diff --git a/src/main/java/capstone/facefriend/FacefriendApplication.java b/src/main/java/capstone/facefriend/FacefriendApplication.java index 8982f0bbc1..723fc9435d 100644 --- a/src/main/java/capstone/facefriend/FacefriendApplication.java +++ b/src/main/java/capstone/facefriend/FacefriendApplication.java @@ -1,6 +1,5 @@ package capstone.facefriend; -import capstone.facefriend.resume.domain.Resume; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; diff --git a/src/main/java/capstone/facefriend/bucket/BucketService.java b/src/main/java/capstone/facefriend/bucket/BucketService.java index e0464225a9..70c3c39e8c 100644 --- a/src/main/java/capstone/facefriend/bucket/BucketService.java +++ b/src/main/java/capstone/facefriend/bucket/BucketService.java @@ -1,6 +1,13 @@ package capstone.facefriend.bucket; +import capstone.facefriend.chat.domain.ChatRoom; +import capstone.facefriend.chat.domain.ChatRoomMember; +import capstone.facefriend.chat.exception.ChatException; +import capstone.facefriend.chat.exception.ChatExceptionType; +import capstone.facefriend.chat.repository.ChatMessageRepository; +import capstone.facefriend.chat.repository.ChatRoomMemberRepository; +import capstone.facefriend.chat.repository.ChatRoomRepository; import capstone.facefriend.member.domain.member.Member; import capstone.facefriend.member.domain.member.MemberRepository; import capstone.facefriend.member.exception.member.MemberException; @@ -24,6 +31,8 @@ import java.util.List; import java.util.UUID; +import static capstone.facefriend.chat.exception.ChatExceptionType.*; + @Transactional @Slf4j @RequiredArgsConstructor @@ -31,19 +40,24 @@ public class BucketService { @Value("${spring.cloud.aws.s3.bucket}") - private String bucketName; + private String BUCKET_NAME; @Value("${spring.cloud.aws.s3.default-faceInfo-s3url}") - private String defaultFaceInfoS3url; + private String DEFAULT_FACE_INFO_S3_URL; @Value("${spring.cloud.aws.s3.origin-postfix}") - private String originPostfix; + private String ORIGIN_POSTFIX; @Value("${spring.cloud.aws.s3.generated-postfix}") - private String generatedPostfix; + private String GENERATED_POSTFIX; @Value("${spring.cloud.aws.s3.resume-postfix}") - private String resumePostfix; + private String RESUME_POSTFIX; + @Value("${spring.cloud.aws.s3.generated-by-level-postfix}") + private String GENERATED_BY_LEVEL_POSTFIX; private final AmazonS3 amazonS3; private final MemberRepository memberRepository; + private final ChatMessageRepository chatMessageRepository; + private final ChatRoomRepository chatRoomRepository; + private final ChatRoomMemberRepository chatRoomMemberRepository; // FaceInfo : origin 업로드 & generated 업로드 public List uploadOriginAndGenerated( @@ -56,16 +70,16 @@ public List uploadOriginAndGenerated( originMetadata.setContentLength(origin.getInputStream().available()); originMetadata.setContentType(origin.getContentType()); - String originObjectName = UUID.randomUUID() + originPostfix; + String originObjectName = UUID.randomUUID() + ORIGIN_POSTFIX; amazonS3.putObject( new PutObjectRequest( - bucketName, + BUCKET_NAME, originObjectName, origin.getInputStream(), // origin originMetadata ).withCannedAcl(CannedAccessControlList.PublicRead) ); - String originS3url = amazonS3.getUrl(bucketName, originObjectName).toString(); + String originS3url = amazonS3.getUrl(BUCKET_NAME, originObjectName).toString(); /** upload generated to s3 */ // set metadata @@ -73,20 +87,21 @@ public List uploadOriginAndGenerated( generatedMetadata.setContentLength(generated.getInputStream().available()); generatedMetadata.setContentType(generatedMetadata.getContentType()); - String generatedObjectName = UUID.randomUUID() + generatedPostfix; + String generatedObjectName = UUID.randomUUID() + GENERATED_POSTFIX; amazonS3.putObject( new PutObjectRequest( - bucketName, + BUCKET_NAME, generatedObjectName, generated.getInputStream(), // generated generatedMetadata ).withCannedAcl(CannedAccessControlList.PublicRead) ); - String generatedS3url = amazonS3.getUrl(bucketName, generatedObjectName).toString(); + String generatedS3url = amazonS3.getUrl(BUCKET_NAME, generatedObjectName).toString(); return List.of(originS3url, generatedS3url); } + // FaceInfo : origin 수정 -> generated 수정 public List updateOriginAndGenerated( MultipartFile origin, @@ -98,7 +113,7 @@ public List updateOriginAndGenerated( String originS3url = member.getFaceInfo().getOriginS3url(); String generatedS3url = member.getFaceInfo().getGeneratedS3url(); - if (originS3url.equals(defaultFaceInfoS3url) || generatedS3url.equals(defaultFaceInfoS3url)) { + if (originS3url.equals(DEFAULT_FACE_INFO_S3_URL) || generatedS3url.equals(DEFAULT_FACE_INFO_S3_URL)) { return uploadOriginAndGenerated(origin, generated); } @@ -114,13 +129,13 @@ public String deleteOriginAndGenerated( String originS3url = member.getFaceInfo().getOriginS3url(); String originObjectName = originS3url.substring(originS3url.lastIndexOf("/") + 1); - amazonS3.deleteObject(new DeleteObjectRequest(bucketName, originObjectName)); + amazonS3.deleteObject(new DeleteObjectRequest(BUCKET_NAME, originObjectName)); String generatedS3url = member.getFaceInfo().getGeneratedS3url(); String generatedObjectName = generatedS3url.substring(generatedS3url.lastIndexOf("/") + 1); - amazonS3.deleteObject(new DeleteObjectRequest(bucketName, generatedObjectName)); + amazonS3.deleteObject(new DeleteObjectRequest(BUCKET_NAME, generatedObjectName)); - return defaultFaceInfoS3url; + return DEFAULT_FACE_INFO_S3_URL; } @@ -137,17 +152,17 @@ public List uploadResumeImages( metadata.setContentLength(image.getInputStream().available()); metadata.setContentType(image.getContentType()); - String imageObjectName = UUID.randomUUID() + resumePostfix; + String imageObjectName = UUID.randomUUID() + RESUME_POSTFIX; amazonS3.putObject( new PutObjectRequest( - bucketName, + BUCKET_NAME, imageObjectName, image.getInputStream(), metadata ).withCannedAcl(CannedAccessControlList.PublicRead) ); - resumeImageS3urls.add(amazonS3.getUrl(bucketName, imageObjectName).toString()); + resumeImageS3urls.add(amazonS3.getUrl(BUCKET_NAME, imageObjectName).toString()); } } return resumeImageS3urls; @@ -170,10 +185,66 @@ public void deleteResumeImages( for (String resumeImageS3url : resumeImageS3urls) { String resumeImageObjectName = resumeImageS3url.substring(resumeImageS3url.lastIndexOf("/") + 1); - amazonS3.deleteObject(new DeleteObjectRequest(bucketName, resumeImageObjectName)); + amazonS3.deleteObject(new DeleteObjectRequest(BUCKET_NAME, resumeImageObjectName)); } } + + // FaceInfoByLevel : generatedByLevel 업로드 + public String uploadGeneratedByLevel( + MultipartFile generatedByLevel + ) throws IOException { + // set metadata + ObjectMetadata generatedByLevelMetaData = new ObjectMetadata(); + generatedByLevelMetaData.setContentLength(generatedByLevel.getInputStream().available()); + generatedByLevelMetaData.setContentType(generatedByLevel.getContentType()); + + String generatedByLevelObjectName = UUID.randomUUID() + GENERATED_BY_LEVEL_POSTFIX; + amazonS3.putObject( + new PutObjectRequest( + BUCKET_NAME, + generatedByLevelObjectName, + generatedByLevel.getInputStream(), // origin + generatedByLevelMetaData + ).withCannedAcl(CannedAccessControlList.PublicRead) + ); + return amazonS3.getUrl(BUCKET_NAME, generatedByLevelObjectName).toString(); + } + + // FaceInfoByLevel : generatedByLevel 수정 = generatedByLevel 삭제 후 업로드 + public String updateGeneratedByLevel( + ByteArrayMultipartFile generatedByLevel, + Long roomId + ) throws IOException { + deleteGeneratedByLevel(roomId); + return uploadGeneratedByLevel(generatedByLevel); + } + + // FaceInfo : generatedByLevel 삭제 + public void deleteGeneratedByLevel( + Long roomId // 삭제 요청된 방 + ) { + ChatRoom chatRoom = findChatRoomByRoomId(roomId); + ChatRoomMember chatRoomMember = findChatRoomMemberByChatRoom(chatRoom); + + String senderGeneratedByLevelS3url = chatRoomMember.getSenderFaceInfoByLevel().getGeneratedByLevelS3url(); + String receiverGeneratedByLevelS3url = chatRoomMember.getReceiverFaceInfoByLevel().getGeneratedByLevelS3url(); + + String senderGeneratedByLevelObjectName = senderGeneratedByLevelS3url.substring(senderGeneratedByLevelS3url.lastIndexOf("/") + 1); + String receiverGeneratedByLevelObjectName = receiverGeneratedByLevelS3url.substring(receiverGeneratedByLevelS3url.lastIndexOf("/") + 1); + + amazonS3.deleteObject(new DeleteObjectRequest(BUCKET_NAME, senderGeneratedByLevelObjectName)); + amazonS3.deleteObject(new DeleteObjectRequest(BUCKET_NAME, receiverGeneratedByLevelObjectName)); + } + + private ChatRoomMember findChatRoomMemberByChatRoom(ChatRoom chatRoom) { + return chatRoomMemberRepository.findChatRoomMemberByChatRoom(chatRoom).orElseThrow(() -> new ChatException(NOT_FOUND_CHAT_ROOM_MEMBER)); + } + + private ChatRoom findChatRoomByRoomId(Long roomId) { + return chatRoomRepository.findById(roomId).orElseThrow(() -> new ChatException(NOT_FOUND_CHAT_ROOM)); + } + private Member findMemberById(Long memberId) { return memberRepository.findById(memberId) .orElseThrow(() -> new MemberException(MemberExceptionType.NOT_FOUND)); diff --git a/src/main/java/capstone/facefriend/chat/aop/ChatAop.java b/src/main/java/capstone/facefriend/chat/aop/ChatAop.java new file mode 100644 index 0000000000..81d43d75a8 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/aop/ChatAop.java @@ -0,0 +1,270 @@ +package capstone.facefriend.chat.aop; + +import capstone.facefriend.bucket.BucketService; +import capstone.facefriend.chat.domain.ChatMessage; +import capstone.facefriend.chat.domain.ChatRoomMember; +import capstone.facefriend.chat.exception.ChatException; +import capstone.facefriend.chat.repository.ChatMessageRepository; +import capstone.facefriend.chat.repository.ChatRoomMemberRepository; +import capstone.facefriend.member.domain.faceInfo.FaceInfoByLevel; +import capstone.facefriend.member.domain.member.Member; +import capstone.facefriend.member.domain.member.MemberRepository; +import capstone.facefriend.member.exception.member.MemberException; +import capstone.facefriend.member.multipartFile.ByteArrayMultipartFile; +import capstone.facefriend.member.service.FaceInfoService; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.GetObjectRequest; +import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.S3ObjectInputStream; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.After; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Method; + +import static capstone.facefriend.chat.exception.ChatExceptionType.NOT_FOUND_CHAT_ROOM_MEMBER; +import static capstone.facefriend.member.exception.member.MemberExceptionType.NOT_FOUND; + +@Slf4j +@Aspect +@Component +@Transactional +@RequiredArgsConstructor +public class ChatAop { + + private final ChatRoomMemberRepository chatRoomMemberRepository; + private final FaceInfoService faceInfoService; + private final MemberRepository memberRepository; + private final ChatMessageRepository chatMessageRepository; + private final AmazonS3 amazonS3; + private final BucketService bucketService; + + @Value("${spring.cloud.aws.s3.bucket}") + private String BUCKET_NAME; + + public static final int LEVEL_TWO = 5; + public static final int LEVEL_THREE = 10; + public static final int LEVEL_FOUR = 15; + + @Pointcut("execution(* capstone.facefriend.chat.service.MessageService.sendHeart(..))") + private void sendHeart() { + } + + // 하트를 보낼 때 senderFaceInfoByLevel, receiverFaceInfoByLevel 의 generatedByLevel 필드를 generated 로 초기화하므로 after 이어야만 한다. + // 하트 거절하면 findChatRoomMemberBySenderAndReceiver 에서 예외 터트릴거임 + @Transactional + @After("sendHeart()") + public void afterSendHeart(JoinPoint joinPoint) throws IOException { + + Object[] params = joinPoint.getArgs(); + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + + Long senderId = -1L; + Long receiveId = -1L; + for (int i = 0; i < method.getParameters().length; i++) { + String paramName = method.getParameters()[i].getName(); + if (paramName.equals("senderId")) { + senderId = (Long) params[i]; + } + if (paramName.equals("receiveId")) { + receiveId = (Long) params[i]; + } + } + + Member sender = findMemberById(senderId); // 영속 + Member receiver = findMemberById(receiveId); // 영속 + + ChatRoomMember chatRoomMember = findChatRoomMemberBySenderAndReceiver(sender, receiver); // 영속 + + // senderGeneratedByLevel 은 generated 로 DB 저장되어있음 + String senderGeneratedByLevelS3url = chatRoomMember.getSenderFaceInfoByLevel().getGeneratedByLevelS3url(); + // generated 를 MultipartFile 형태로 얻어내고 이를 level 1 로 generatedByLevel 를 S3 업로드 (generated 자체가 level 1) + ByteArrayMultipartFile senderGeneratedByLevel = getGeneratedByLevel(senderGeneratedByLevelS3url); + String newSenderGeneratedByLevelS3url = bucketService.uploadGeneratedByLevel(senderGeneratedByLevel); + // senderGeneratedByLevelS3url 수정 + chatRoomMember.getSenderFaceInfoByLevel().setGeneratedByLevelS3url(newSenderGeneratedByLevelS3url); // dirty check + + // receiverGeneratedByLevel 은 generated 로 DB 저장되어있음 + String receiverGeneratedByLevelS3url = chatRoomMember.getReceiverFaceInfoByLevel().getGeneratedByLevelS3url(); + // generated 를 MultipartFile 형태로 얻어내고 이를 level 1 로 generatedByLevel 를 S3 업로드 (generated 자체가 level 1) + ByteArrayMultipartFile receiverGenerated = getGeneratedByLevel(receiverGeneratedByLevelS3url); + String newReceiverGeneratedByLevelS3url = bucketService.uploadGeneratedByLevel(receiverGenerated); + // receiverGeneratedByLevelS3url 수정 + chatRoomMember.getReceiverFaceInfoByLevel().setGeneratedByLevelS3url(newReceiverGeneratedByLevelS3url); // dirty check + } + + private ChatRoomMember findChatRoomMemberBySenderAndReceiver(Member sender, Member receiver) { + return chatRoomMemberRepository.findChatRoomMemberBySenderAndReceiver(sender, receiver) + .orElseThrow(() -> new ChatException(NOT_FOUND_CHAT_ROOM_MEMBER)); + } + + @Pointcut("execution(* capstone.facefriend.chat.repository.ChatMessageRepository.save(..))") + private void saveChatMessage() { + } + + // 본인이 보낸 chatMessage 를 저장하기 전마다 chatMessage 가 속한 chatRoom 의 모든 chatMessage 갯수를 count 하고 특정 수준을 넘는지를 확인한다. + // 수준에 따라 가중치를 조절해 generate_face_by_level() 호출하여 인공지능 서버에 이미지 생성을 요청한다. + // 또한 가중치 이미지를 s3에 업데이트하고 그 url을 db에 저장한다. + @Transactional + @Before("saveChatMessage()") + public void beforeSaveChatMessage(JoinPoint joinPoint) throws IOException { + + Object[] params = joinPoint.getArgs(); + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + + ChatMessage chatMessage = null; + for (int i = 0; i < method.getParameters().length; i++) { + String paramName = method.getParameters()[i].getName(); + if (paramName.equals("chatMessage")) { + chatMessage = (ChatMessage) params[i]; + } + } + + Long roomId = chatMessage.getChatRoom().getId(); + Member me = chatMessage.getSender(); + + ChatRoomMember chatRoomMember = findChatRoomMemberByRoomId(roomId); // 영속 + FaceInfoByLevel senderFaceInfoByLevel = chatRoomMember.getSenderFaceInfoByLevel(); // 영속 + FaceInfoByLevel receiverFaceInfoByLevel = chatRoomMember.getReceiverFaceInfoByLevel(); // 영속 + + // 나는 최초의 sender 일 수도, receiver 일 수도 있다. + Member sender = chatRoomMember.getSender(); + Member receiver = chatRoomMember.getReceiver(); + + // chatRoom 의 chatMessage 객체의 갯수 count + Integer chatMessageCount = chatMessageRepository.countChatMessagesByChatRoom(chatMessage.getChatRoom()); + + /** param 1 : convert originS3url into MultipartFile **/ + ByteArrayMultipartFile senderOrigin = getOrigin(sender); + ByteArrayMultipartFile receiverOrigin = getOrigin(receiver); + /** param 2 : styleId **/ + Integer senderStyleId = sender.getFaceInfo().getStyleId(); + Integer receiverStyleId = receiver.getFaceInfo().getStyleId(); + /** param 3 : memberId **/ + Long senderId = sender.getId(); + Long receiverId = receiver.getId(); + + switch (chatMessageCount) { + case LEVEL_TWO: + ByteArrayMultipartFile senderGeneratedByLevelTwo = faceInfoService.generateByLevel(senderOrigin, senderId, senderStyleId, 2); + ByteArrayMultipartFile receiverGeneratedByLevelTwo = faceInfoService.generateByLevel(receiverOrigin, receiverId, receiverStyleId, 2); + + String senderGeneratedByLevelTwoS3url = bucketService.updateGeneratedByLevel(senderGeneratedByLevelTwo, roomId); + String receiverGeneratedByLevelTwoS3url = bucketService.updateGeneratedByLevel(receiverGeneratedByLevelTwo, roomId); + + senderFaceInfoByLevel.setGeneratedByLevelS3url(senderGeneratedByLevelTwoS3url); // dirty check + receiverFaceInfoByLevel.setGeneratedByLevelS3url(receiverGeneratedByLevelTwoS3url); // dirty check + + case LEVEL_THREE: + ByteArrayMultipartFile senderGeneratedByLevelThree = faceInfoService.generateByLevel(senderOrigin, senderId, senderStyleId, 3); + ByteArrayMultipartFile receiverGeneratedByLevelThree = faceInfoService.generateByLevel(receiverOrigin, receiverId, receiverStyleId, 3); + + String senderGeneratedByLevelThreeS3url = bucketService.updateGeneratedByLevel(senderGeneratedByLevelThree, roomId); + String receiverGeneratedByLevelThreeS3url = bucketService.updateGeneratedByLevel(receiverGeneratedByLevelThree, roomId); + + senderFaceInfoByLevel.setGeneratedByLevelS3url(senderGeneratedByLevelThreeS3url); // dirty check + receiverFaceInfoByLevel.setGeneratedByLevelS3url(receiverGeneratedByLevelThreeS3url); // dirty check + + case LEVEL_FOUR: + ByteArrayMultipartFile senderGeneratedByLevelFour = faceInfoService.generateByLevel(senderOrigin, senderId, senderStyleId, 4); + ByteArrayMultipartFile receiverGeneratedByLevelFour = faceInfoService.generateByLevel(receiverOrigin, receiverId, receiverStyleId, 4); + + String senderGeneratedByLevelFourS3url = bucketService.updateGeneratedByLevel(senderGeneratedByLevelFour, roomId); + String receiverGeneratedByLevelFourS3url = bucketService.updateGeneratedByLevel(receiverGeneratedByLevelFour, roomId); + + senderFaceInfoByLevel.setGeneratedByLevelS3url(senderGeneratedByLevelFourS3url); // dirty check + receiverFaceInfoByLevel.setGeneratedByLevelS3url(receiverGeneratedByLevelFourS3url); // dirty check + } + } + + @Pointcut("execution(* capstone.facefriend.chat.service.ChatRoomService.leftRoom(..))") + private void leftRoom() { + } + + // 채팅방을 나가면 sender, receiver 의 generatedByLevel 을 모두 삭제한다. + @Transactional + @Before("leftRoom()") + public void beforeLeftRoom(JoinPoint joinPoint) { + Object[] params = joinPoint.getArgs(); + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + + Long roomId = null; + Long memberId = null; + for (int i = 0; i < method.getParameters().length; i++) { + String paramName = method.getParameters()[i].getName(); + if (paramName.equals("roomId")) { + roomId = (Long) params[i]; + } + if (paramName.equals("memberId")) { + memberId = (Long) params[i]; + } + } + bucketService.deleteGeneratedByLevel(roomId); + } + + private ByteArrayMultipartFile getGeneratedByLevel(String generatedByLevelS3url) throws IOException { + String generatedByLevelObjectName = generatedByLevelS3url.substring(generatedByLevelS3url.lastIndexOf("/") + 1); + S3Object generatedByLevelS3Object = amazonS3.getObject(new GetObjectRequest(BUCKET_NAME, generatedByLevelObjectName)); + + S3ObjectInputStream generatedByLevelS3inputStream = generatedByLevelS3Object.getObjectContent(); + ByteArrayOutputStream generatedByLevelS3outputStream = new ByteArrayOutputStream(); + + byte[] buffer = new byte[4096]; + int bytesRead; + + while ((bytesRead = generatedByLevelS3inputStream.read(buffer)) != -1) { + generatedByLevelS3outputStream.write(buffer, 0, bytesRead); + } + generatedByLevelS3outputStream.close(); + byte[] generatedByLevelS3byteArray = generatedByLevelS3outputStream.toByteArray(); + + // convert byte[] into MultipartFile + ByteArrayMultipartFile generatedByLevel = new ByteArrayMultipartFile(generatedByLevelS3byteArray, generatedByLevelObjectName); + return generatedByLevel; + } + + private ByteArrayMultipartFile getOrigin(Member me) throws IOException { + String originS3url = me.getFaceInfo().getOriginS3url(); + String originObjectName = originS3url.substring(originS3url.lastIndexOf("/") + 1); + S3Object originS3Object = amazonS3.getObject(new GetObjectRequest(BUCKET_NAME, originObjectName)); + + S3ObjectInputStream originS3inputStream = originS3Object.getObjectContent(); + ByteArrayOutputStream originS3outputStream = new ByteArrayOutputStream(); + + byte[] buffer = new byte[4096]; + int bytesRead; + + while ((bytesRead = originS3inputStream.read(buffer)) != -1) { + originS3outputStream.write(buffer, 0, bytesRead); + } + originS3outputStream.close(); + byte[] originS3byteArray = originS3outputStream.toByteArray(); + + // convert byte[] into MultipartFile + ByteArrayMultipartFile origin = new ByteArrayMultipartFile(originS3byteArray, originObjectName); + return origin; + } + + private Member findMemberById(Long memberId) { + return memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(NOT_FOUND)); // 영속 + } + + private ChatRoomMember findChatRoomMemberByRoomId(Long roomId) { + return chatRoomMemberRepository.findByChatRoomId(roomId) + .orElseThrow(() -> new ChatException(NOT_FOUND_CHAT_ROOM_MEMBER)); // 영속 + } +} diff --git a/src/main/java/capstone/facefriend/chat/config/ChatAsyncConfig.java b/src/main/java/capstone/facefriend/chat/config/ChatAsyncConfig.java new file mode 100644 index 0000000000..070d66bfc6 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/config/ChatAsyncConfig.java @@ -0,0 +1,23 @@ +package capstone.facefriend.chat.config; + + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.Executor; + +@EnableAsync +@Configuration +public class ChatAsyncConfig { + + @Bean("chatAsyncExecutor") + public Executor chatAsyncExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(10); + executor.setThreadNamePrefix("chat-"); + executor.initialize(); + return executor; + } +} diff --git a/src/main/java/capstone/facefriend/chat/domain/ChatRoomMember.java b/src/main/java/capstone/facefriend/chat/domain/ChatRoomMember.java index 5a3e250c92..5e1bdb4f8d 100644 --- a/src/main/java/capstone/facefriend/chat/domain/ChatRoomMember.java +++ b/src/main/java/capstone/facefriend/chat/domain/ChatRoomMember.java @@ -1,6 +1,7 @@ package capstone.facefriend.chat.domain; import capstone.facefriend.common.domain.BaseEntity; +import capstone.facefriend.member.domain.faceInfo.FaceInfoByLevel; import capstone.facefriend.member.domain.member.Member; import jakarta.persistence.*; import lombok.*; @@ -23,17 +24,25 @@ public class ChatRoomMember extends BaseEntity { private Long id; @ManyToOne - @JoinColumn(name = "Room_ID") + @JoinColumn(name = "ROOM_ID") private ChatRoom chatRoom; @ManyToOne @JoinColumn(name = "SENDER_ID") private Member sender; + @ManyToOne(cascade = CascadeType.ALL) + @JoinColumn(name = "SENDER_FACE_INFO_BY_LEVEL") + private FaceInfoByLevel senderFaceInfoByLevel; + @ManyToOne @JoinColumn(name = "RECEIVER_ID") private Member receiver; + @ManyToOne(cascade = CascadeType.ALL) + @JoinColumn(name = "RECEIVER_FACE_INFO_BY_LEVEL") + private FaceInfoByLevel receiverFaceInfoByLevel; + @Column private boolean isSenderExist; @@ -46,16 +55,6 @@ public class ChatRoomMember extends BaseEntity { @Column private boolean isReceiverPublic; - public void setChatRoom(ChatRoom chatRoom) { - this.chatRoom = chatRoom; - } - public void setSender(Member Sender) { - this.sender = sender; - } - public void setReceiver(Member Sender) { - this.receiver = receiver; - } - public boolean isSenderExist() {return this.isSenderExist == true;} public boolean isReceiverExist() {return this.isReceiverExist == true;} public boolean isSenderPublic() {return this.isSenderPublic == true;} diff --git a/src/main/java/capstone/facefriend/chat/exception/ChatExceptionType.java b/src/main/java/capstone/facefriend/chat/exception/ChatExceptionType.java index 0606db047d..9536de2017 100644 --- a/src/main/java/capstone/facefriend/chat/exception/ChatExceptionType.java +++ b/src/main/java/capstone/facefriend/chat/exception/ChatExceptionType.java @@ -4,11 +4,14 @@ import capstone.facefriend.common.exception.Status; public enum ChatExceptionType implements ExceptionType { - NOT_FOUND(Status.NOT_FOUND, 5001, "일치하는 채팅방이 없습니다."), - INVALIDED_CHATROOM(Status.BAD_REQUEST, 5002, "유효한 채팅방이 아닙니다"), - INVALID_ACCESS(Status.FORBIDDEN, 5003, "본인의 계정이 아닙니다."), - UNAUTHORIZED(Status.UNAUTHORIZED, 5005, "접근 정보가 잘못되었습니다."), - ALREADY_CHATROOM(Status.BAD_REQUEST, 5006, "이미 존재하는 채팅방입니다."), + NOT_FOUND_CHAT_ROOM(Status.NOT_FOUND, 5001, "일치하는 채팅방이 없습니다."), + NOT_FOUND_CHAT_ROOM_MEMBER(Status.NOT_FOUND, 5002, "채팅방에 채팅방 멤버가 존재하지 않습니다."), + FAIL_TO_SOCKET_INFO(Status.BAD_REQUEST, 5003, "소켓 연결 실패했습니다!"), + INVALIDED_CHATROOM(Status.BAD_REQUEST, 5004, "유효한 채팅방이 아닙니다"), + INVALID_ACCESS(Status.FORBIDDEN, 5005, "본인의 계정이 아닙니다."), + UNAUTHORIZED(Status.UNAUTHORIZED, 5006, "접근 정보가 잘못되었습니다."), + ALREADY_CHATROOM(Status.BAD_REQUEST, 5007, "이미 존재하는 채팅방입니다."), + FAIL_AOP_AFTER_UPDATE_ORIGIN(Status.SERVER_ERROR, 5008, "원본 사진을 업로드했지만 가중치 사진을 업로드 실패했습니다."), ; private final Status status; diff --git a/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java b/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java index ea4b3b3ddf..d596b67001 100644 --- a/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java +++ b/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java @@ -1,6 +1,7 @@ package capstone.facefriend.chat.repository; import capstone.facefriend.chat.domain.ChatMessage; +import capstone.facefriend.chat.domain.ChatRoom; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; @@ -13,4 +14,5 @@ public interface ChatMessageRepository extends JpaRepository ChatMessage save(ChatMessage chatMessage); List findChatMessagesByChatRoom_IdAndSendTimeBefore(Long roomId, LocalDateTime time, Pageable pageable); List findChatMessagesByChatRoomId(Long roomId); + Integer countChatMessagesByChatRoom(ChatRoom chatRoom); } \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java b/src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java index a1a738303a..787ae16e8b 100644 --- a/src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java +++ b/src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java @@ -1,6 +1,8 @@ package capstone.facefriend.chat.repository; +import capstone.facefriend.chat.domain.ChatRoom; import capstone.facefriend.chat.domain.ChatRoomMember; +import capstone.facefriend.member.domain.member.Member; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -15,4 +17,6 @@ public interface ChatRoomMemberRepository extends JpaRepository> findAllBySenderId(Long senderId); Optional> findAllByReceiverId(Long senderId); + Optional findChatRoomMemberByChatRoom(ChatRoom chatRoom); + Optional findChatRoomMemberBySenderAndReceiver(Member sender, Member receiver); } \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java index 0e3f03604e..035a3c1af5 100644 --- a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java +++ b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java @@ -27,6 +27,8 @@ import java.util.List; import java.util.Map; +import static capstone.facefriend.chat.exception.ChatExceptionType.*; + @Service @Slf4j @RequiredArgsConstructor @@ -57,19 +59,19 @@ private List findAllChatRoomMemberByReceiverId(Long memberId) { private ChatRoomInfo findChatRoomInfo(String chatRoomInfoId) { ChatRoomInfo chatRoomInfo = chatRoomInfoRedisRepository.findById(chatRoomInfoId) - .orElseThrow(()-> new ChatException(ChatExceptionType.NOT_FOUND)); + .orElseThrow(()-> new ChatException(NOT_FOUND_CHAT_ROOM)); return chatRoomInfo; } private ChatRoom findRoomById(Long roomId) { ChatRoom chatRoom = chatRoomRepository.findById(roomId) - .orElseThrow(()-> new ChatException(ChatExceptionType.NOT_FOUND)); + .orElseThrow(()-> new ChatException(NOT_FOUND_CHAT_ROOM)); return chatRoom; } private ChatRoomMember findChatRoomMemberByChatRoomId(Long roomId) { ChatRoomMember chatRoomMember = chatRoomMemberRepository.findByChatRoomId(roomId) - .orElseThrow(()-> new ChatException(ChatExceptionType.NOT_FOUND)); + .orElseThrow(()-> new ChatException(NOT_FOUND_CHAT_ROOM_MEMBER)); return chatRoomMember; } @@ -185,7 +187,7 @@ public String leftRoom(Long roomId, Long memberId) { String content = "상대방이 떠났습니다."; String method = "receiveLeftRoom"; LocalDateTime sendTime = LocalDateTime.now(); - ChatRoomLeftResponse chatRoomLeftResponse =ChatRoomLeftResponse.of(method, roomId, leftMember, sendTime, content); + ChatRoomLeftResponse chatRoomLeftResponse = ChatRoomLeftResponse.of(method, roomId, leftMember, sendTime, content); simpMessagingTemplate.convertAndSend("/sub/chat/" + sender.getId(),chatRoomLeftResponse); chatRoomRepository.save(chatRoom); chatRoomMemberRepository.save(chatRoomMember); diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java index 4e1ec0c789..94d4b52927 100644 --- a/src/main/java/capstone/facefriend/chat/service/MessageService.java +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -11,6 +11,7 @@ import capstone.facefriend.chat.service.dto.message.MessageListResponse; import capstone.facefriend.chat.service.dto.message.MessageRequest; import capstone.facefriend.chat.service.dto.message.MessageResponse; +import capstone.facefriend.member.domain.faceInfo.FaceInfoByLevel; import capstone.facefriend.member.domain.member.Member; import capstone.facefriend.member.domain.member.MemberRepository; import capstone.facefriend.member.exception.member.MemberException; @@ -31,6 +32,9 @@ import java.util.LinkedHashMap; import java.util.List; +import static capstone.facefriend.chat.exception.ChatExceptionType.*; +import static capstone.facefriend.member.exception.member.MemberExceptionType.*; + @Service @Slf4j @RequiredArgsConstructor @@ -49,8 +53,8 @@ private Member findMemberById(String destination, Long memberId) { Member member = memberRepository.findById(memberId) .orElse(null); if (member == null) { - simpMessagingTemplate.convertAndSend(destination, MemberExceptionType.NOT_FOUND.message()); - throw new MemberException(MemberExceptionType.NOT_FOUND); + simpMessagingTemplate.convertAndSend(destination, NOT_FOUND.message()); + throw new MemberException(NOT_FOUND); } return member; @@ -60,8 +64,8 @@ private ChatRoom findRoomById(String destination, Long roomId) { ChatRoom chatRoom = chatRoomRepository.findById(roomId) .orElse(null); if (chatRoom == null) { - simpMessagingTemplate.convertAndSend(destination, ChatExceptionType.NOT_FOUND.message()); - throw new ChatException(ChatExceptionType.NOT_FOUND); + simpMessagingTemplate.convertAndSend(destination, NOT_FOUND_CHAT_ROOM.message()); + throw new ChatException(NOT_FOUND_CHAT_ROOM); } return chatRoom; } @@ -70,8 +74,8 @@ private ChatRoomMember findSenderReceiver(String destination, Long senderId, Lon ChatRoomMember chatRoomMember = chatRoomMemberRepository.findBySenderAndReceiver(senderId, receiveId) .orElse(null); if (chatRoomMember == null) { - simpMessagingTemplate.convertAndSend(destination, ChatExceptionType.NOT_FOUND.message()); - throw new ChatException(ChatExceptionType.NOT_FOUND); + simpMessagingTemplate.convertAndSend(destination, NOT_FOUND_CHAT_ROOM_MEMBER.message()); + throw new ChatException(NOT_FOUND_CHAT_ROOM_MEMBER); } return chatRoomMember; } @@ -80,21 +84,21 @@ private ChatRoomMember findChatRoomMemberByChatRoomId(String destination, Long r ChatRoomMember chatRoomMember = chatRoomMemberRepository.findByChatRoomId(roomId) .orElse(null); if (chatRoomMember == null) { - simpMessagingTemplate.convertAndSend(destination, ChatExceptionType.NOT_FOUND.message()); - throw new ChatException(ChatExceptionType.NOT_FOUND); + simpMessagingTemplate.convertAndSend(destination, NOT_FOUND_CHAT_ROOM_MEMBER.message()); + throw new ChatException(NOT_FOUND_CHAT_ROOM_MEMBER); } return chatRoomMember; } private SocketInfo findSocketInfo(Long memberId) { SocketInfo socketInfo = socketInfoRedisRepository.findById(memberId) - .orElseThrow(()-> new ChatException(ChatExceptionType.NOT_FOUND)); + .orElseThrow(()-> new ChatException(FAIL_TO_SOCKET_INFO)); return socketInfo; } private ChatRoomInfo findChatRoomInfo(String chatRoomInfoId) { ChatRoomInfo chatRoomInfo = chatRoomInfoRedisRepository.findById(chatRoomInfoId) - .orElseThrow(()-> new ChatException(ChatExceptionType.NOT_FOUND)); + .orElseThrow(()-> new ChatException(NOT_FOUND_CHAT_ROOM)); return chatRoomInfo; } @@ -111,17 +115,16 @@ public void sendMessage(MessageRequest messageRequest, Long senderId) { chatRoomRepository.save(chatRoom); chatRoomMemberRepository.save(chatRoomMember); } else if ((chatRoom.getStatus() == ChatRoom.Status.close)) { - simpMessagingTemplate.convertAndSend(exceptionDestination, ChatExceptionType.INVALIDED_CHATROOM.message()); - throw new ChatException(ChatExceptionType.INVALIDED_CHATROOM); + simpMessagingTemplate.convertAndSend(exceptionDestination, INVALIDED_CHATROOM.message()); + throw new ChatException(INVALIDED_CHATROOM); } else if ((chatRoom.getStatus() == ChatRoom.Status.set)) { - simpMessagingTemplate.convertAndSend(exceptionDestination, ChatExceptionType.INVALIDED_CHATROOM.message()); - throw new ChatException(ChatExceptionType.INVALIDED_CHATROOM); + simpMessagingTemplate.convertAndSend(exceptionDestination, INVALIDED_CHATROOM.message()); + throw new ChatException(INVALIDED_CHATROOM); } else if ((chatRoom.getStatus() == ChatRoom.Status.delete)) { - simpMessagingTemplate.convertAndSend(exceptionDestination, ChatExceptionType.INVALIDED_CHATROOM.message()); - throw new ChatException(ChatExceptionType.INVALIDED_CHATROOM); + simpMessagingTemplate.convertAndSend(exceptionDestination, INVALIDED_CHATROOM.message()); + throw new ChatException(INVALIDED_CHATROOM); } - ChatMessage chatMessage = ChatMessage.builder() .content(messageRequest.getContent()) .sender(sender) @@ -129,16 +132,17 @@ public void sendMessage(MessageRequest messageRequest, Long senderId) { .sendTime(LocalDateTime.now()) .isRead(false) .build(); - chatMessageRepository.save(chatMessage); + ChatRoomMember chatRoomMember = findChatRoomMember(chatRoom); // 영속 + MessageResponse messageResponse = new MessageResponse(); messageResponse.setMethod("receiveChat"); messageResponse.setRoomId(chatMessage.getChatRoom().getId()); messageResponse.setSenderId(senderId); messageResponse.setReceiveId(receiver.getId()); messageResponse.setSenderNickname(sender.getBasicInfo().getNickname()); - messageResponse.setSenderFaceInfoS3Url(sender.getFaceInfo().getOriginS3url()); + messageResponse.setSenderFaceInfoS3Url(chatRoomMember.getSenderFaceInfoByLevel().getGeneratedByLevelS3url()); // 수정한 부분 messageResponse.setContent(chatMessage.getContent()); messageResponse.setType("message"); messageResponse.setCreatedAt(chatMessage.getSendTime()); @@ -149,14 +153,19 @@ public void sendMessage(MessageRequest messageRequest, Long senderId) { redisTemplate.convertAndSend(topic, messageResponse); } + + private ChatRoomMember findChatRoomMember(ChatRoom chatRoom) { + return chatRoomMemberRepository.findByChatRoomId(chatRoom.getId()).orElseThrow(() -> new ChatException(NOT_FOUND_CHAT_ROOM_MEMBER)); + } + @Transactional public void sendHeart(Long senderId, Long receiveId) { String exceptionDestination = "/sub/chat/" + senderId; if (chatRoomMemberRepository.findBySenderAndReceiver(senderId, receiveId).isPresent()){ - String exceptionMessage = ChatExceptionType.ALREADY_CHATROOM.message(); + String exceptionMessage = ALREADY_CHATROOM.message(); simpMessagingTemplate.convertAndSend(exceptionDestination, exceptionMessage); - throw new ChatException(ChatExceptionType.ALREADY_CHATROOM); + throw new ChatException(ALREADY_CHATROOM); } ChatRoom chatRoom = ChatRoom.builder() @@ -171,7 +180,17 @@ public void sendHeart(Long senderId, Long receiveId) { ChatRoomMember chatRoomMember = ChatRoomMember.builder() .chatRoom(chatRoom) .sender(sender) + .senderFaceInfoByLevel( // faceInfoByLevel is initialized by generated + FaceInfoByLevel.builder() + .generatedByLevelS3url(sender.getFaceInfo().getGeneratedS3url()) // since generated is level 1 + .build() + ) .receiver(receiver) + .receiverFaceInfoByLevel( // faceInfoByLevel is initialized by generated + FaceInfoByLevel.builder() + .generatedByLevelS3url(receiver.getFaceInfo().getGeneratedS3url()) // since generated is level 1 + .build() + ) .isSenderExist(true) .isReceiverExist(true) .isSenderPublic(false) @@ -196,6 +215,11 @@ public void sendHeart(Long senderId, Long receiveId) { simpMessagingTemplate.convertAndSend(exceptionDestination, "대화 요청 성공"); redisTemplate.convertAndSend(topic, sendHeartResponse); } + + private ChatRoomMember findChatRoomMemberBySenderAndReceiver(Member sender, Member receiver) { + return chatRoomMemberRepository.findChatRoomMemberBySenderAndReceiver(sender, receiver).orElseThrow(() -> new ChatException(NOT_FOUND_CHAT_ROOM_MEMBER)); + } + @Transactional public void heartReply(HeartReplyRequest heartReplyRequest, Long receiveId) { String exceptionDestination = "/sub/chat/" + receiveId; @@ -221,8 +245,8 @@ public void heartReply(HeartReplyRequest heartReplyRequest, Long receiveId) { simpMessagingTemplate.convertAndSend(exceptionDestination, "대화 거절"); } else { - simpMessagingTemplate.convertAndSend(exceptionDestination, ChatExceptionType.ALREADY_CHATROOM); - throw new ChatException(ChatExceptionType.ALREADY_CHATROOM); + simpMessagingTemplate.convertAndSend(exceptionDestination, ALREADY_CHATROOM); + throw new ChatException(ALREADY_CHATROOM); } // 동적으로 목적지 설정 String destination = "/sub/chat/" + sender.getId(); @@ -342,4 +366,9 @@ private void sendSentHeart(String exceptionDestination, Long receiveId) { log.warn("Sendheart list is empty."); } } + + private Member findMemberByid(Long memberId) { + return memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(NOT_FOUND)); + } } \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/member/controller/FaceInfoController.java b/src/main/java/capstone/facefriend/member/controller/FaceInfoController.java index ca4af52cad..4f2a56c8ae 100644 --- a/src/main/java/capstone/facefriend/member/controller/FaceInfoController.java +++ b/src/main/java/capstone/facefriend/member/controller/FaceInfoController.java @@ -28,7 +28,7 @@ public ResponseEntity get( @PutMapping("/face-info") public ResponseEntity update( @RequestPart("origin")MultipartFile origin, - @RequestParam("styleId") Long styleId, + @RequestParam("styleId") Integer styleId, @AuthMember Long memberId ) throws IOException { return ResponseEntity.ok(faceInfoService.updateOrigin(origin, styleId, memberId)); diff --git a/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfo.java b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfo.java index 5c2a5a8526..0dd7a30111 100644 --- a/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfo.java +++ b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfo.java @@ -16,6 +16,9 @@ public class FaceInfo { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column + private Integer styleId; + @Column private String originS3url; diff --git a/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoByLevel.java b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoByLevel.java new file mode 100644 index 0000000000..9158d4e047 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoByLevel.java @@ -0,0 +1,21 @@ +package capstone.facefriend.member.domain.faceInfo; + +import jakarta.persistence.*; +import lombok.*; + +@Getter +@Setter +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@EqualsAndHashCode(of = {"id"}, callSuper = false) +@Entity +public class FaceInfoByLevel { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column + private String generatedByLevelS3url; +} diff --git a/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoByLevelRepository.java b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoByLevelRepository.java new file mode 100644 index 0000000000..93ad93fe52 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoByLevelRepository.java @@ -0,0 +1,8 @@ +package capstone.facefriend.member.domain.faceInfo; + +import org.springframework.data.repository.Repository; + +public interface FaceInfoByLevelRepository extends Repository { + + FaceInfoByLevel save(FaceInfoByLevel faceInfoByLevel); +} diff --git a/src/main/java/capstone/facefriend/member/domain/member/Member.java b/src/main/java/capstone/facefriend/member/domain/member/Member.java index 347d49bfc8..991de3d686 100644 --- a/src/main/java/capstone/facefriend/member/domain/member/Member.java +++ b/src/main/java/capstone/facefriend/member/domain/member/Member.java @@ -4,12 +4,16 @@ import capstone.facefriend.member.domain.analysisInfo.AnalysisInfo; import capstone.facefriend.member.domain.basicInfo.BasicInfo; import capstone.facefriend.member.domain.faceInfo.FaceInfo; +import capstone.facefriend.member.domain.faceInfo.FaceInfoByLevel; import jakarta.persistence.*; import lombok.*; import lombok.extern.slf4j.Slf4j; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; +import java.util.HashMap; +import java.util.Map; + @Getter @Setter @Builder @@ -48,6 +52,13 @@ public class Member extends BaseEntity { @JoinColumn(name = "ANALYSIS_INFO_ID", nullable = false) private AnalysisInfo analysisInfo; + @Builder.Default + @ElementCollection + @CollectionTable(name = "MEMBER_IS_PUBLIC_MAP", joinColumns = @JoinColumn(name = "MEMBER_ID")) + @MapKeyColumn(name = "MEM_ID") // key = other member + @Column(name = "FAMILIARITY") // value = open or close + private Map friends = new HashMap<>(); + public Member(String email) { this.email = email; this.role = Role.USER; diff --git a/src/main/java/capstone/facefriend/member/service/FaceInfoService.java b/src/main/java/capstone/facefriend/member/service/FaceInfoService.java index 36a22246cc..644c29bfe1 100644 --- a/src/main/java/capstone/facefriend/member/service/FaceInfoService.java +++ b/src/main/java/capstone/facefriend/member/service/FaceInfoService.java @@ -41,14 +41,17 @@ public class FaceInfoService { @Value("${flask.generate-url}") - private String requestUrl; + private String GENERATE_IMAGE_REQUEST_URL; + @Value("${flask.generate-by-level-url}") + private String GENERATE_IMAGE_BY_LEVEL_REQUEST_URL; + private final RestTemplate restTemplate; private final BucketService bucketService; private final MemberRepository memberRepository; private final FaceInfoRepository faceInfoRepository; @Transactional // origin 삭제 & generated 삭제 -> origin 업로드 & generated 업로드 - public FaceInfoResponse updateOrigin(MultipartFile origin, Long styleId, Long memberId) throws IOException { + public FaceInfoResponse updateOrigin(MultipartFile origin, Integer styleId, Long memberId) throws IOException { // bucket update ByteArrayMultipartFile generated = generate(origin, styleId, memberId); List s3urls = bucketService.updateOriginAndGenerated(origin, generated, memberId); @@ -62,6 +65,7 @@ public FaceInfoResponse updateOrigin(MultipartFile origin, Long styleId, Long me faceInfo.setOriginS3url(originS3url); // dirty check faceInfo.setGeneratedS3url(generatedS3url); // dirty check + faceInfo.setStyleId(styleId); member.setFaceInfo(faceInfo); // dirty check @@ -84,6 +88,7 @@ public FaceInfoResponse deleteOriginAndGenerated(Long memberId) { FaceInfo faceInfo = FaceInfo.builder() .originS3url(defaultFaceInfoS3url) .generatedS3url(defaultFaceInfoS3url) + .styleId(-1) .build(); faceInfo.setOriginS3url(defaultFaceInfoS3url); faceInfo.setGeneratedS3url(defaultFaceInfoS3url); @@ -94,7 +99,7 @@ public FaceInfoResponse deleteOriginAndGenerated(Long memberId) { return new FaceInfoResponse(defaultFaceInfoS3url, defaultFaceInfoS3url); } - private ByteArrayMultipartFile generate(MultipartFile origin, Long styleId, Long memberId) throws IOException { + private ByteArrayMultipartFile generate(MultipartFile origin, Integer styleId, Long memberId) throws IOException { // convert MultipartFile into ByteArrayResource ByteArrayResource resource = new ByteArrayResource(origin.getBytes()) { @Override @@ -117,7 +122,7 @@ public String getFilename() { HttpEntity> requestEntity = new HttpEntity<>(body, headers); // response entity - ResponseEntity responseEntity = restTemplate.postForEntity(requestUrl, requestEntity, JsonNode.class); // 문제 + ResponseEntity responseEntity = restTemplate.postForEntity(GENERATE_IMAGE_REQUEST_URL, requestEntity, JsonNode.class); // 문제 // convert JSON into Map ObjectMapper objectMapper = new ObjectMapper(); @@ -129,6 +134,41 @@ public String getFilename() { return new ByteArrayMultipartFile(imageBinary, origin.getOriginalFilename()); } + public ByteArrayMultipartFile generateByLevel(MultipartFile origin, Long memberId, int styleId, int level) throws IOException { + // convert MultipartFile into ByteArrayResource + ByteArrayResource resource = new ByteArrayResource(origin.getBytes()) { + @Override + public String getFilename() { + return URLEncoder.encode(origin.getOriginalFilename(), StandardCharsets.UTF_8); + } + }; + + // body + LinkedMultiValueMap body = new LinkedMultiValueMap<>(); + body.add("image", resource); + body.add("user_id", memberId); + body.add("style_id", styleId); + body.add("level", level); + + // header + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + + // request entity + HttpEntity> requestEntity = new HttpEntity<>(body, headers); + + // response entity + ResponseEntity responseEntity = restTemplate.postForEntity(GENERATE_IMAGE_BY_LEVEL_REQUEST_URL, requestEntity, JsonNode.class); + + // convert JSON into Map + ObjectMapper objectMapper = new ObjectMapper(); + Map result = objectMapper.convertValue(responseEntity.getBody(), new TypeReference<>() {}); + + byte[] imageBinary = Base64.getDecoder().decode((String) result.get("image_binary")); + + return new ByteArrayMultipartFile(imageBinary, origin.getOriginalFilename()); + } + private Member findMemberById(Long memberId) { return memberRepository.findById(memberId) .orElseThrow(() -> new MemberException(MemberExceptionType.NOT_FOUND)); diff --git a/src/main/java/capstone/facefriend/member/service/MemberService.java b/src/main/java/capstone/facefriend/member/service/MemberService.java index 7dfbc017a0..10455b2ef2 100644 --- a/src/main/java/capstone/facefriend/member/service/MemberService.java +++ b/src/main/java/capstone/facefriend/member/service/MemberService.java @@ -9,6 +9,8 @@ import capstone.facefriend.member.domain.basicInfo.BasicInfo; import capstone.facefriend.member.domain.basicInfo.BasicInfoRepository; import capstone.facefriend.member.domain.faceInfo.FaceInfo; +import capstone.facefriend.member.domain.faceInfo.FaceInfoByLevel; +import capstone.facefriend.member.domain.faceInfo.FaceInfoByLevelRepository; import capstone.facefriend.member.domain.faceInfo.FaceInfoRepository; import capstone.facefriend.member.domain.member.Member; import capstone.facefriend.member.domain.member.MemberRepository; @@ -41,6 +43,7 @@ public class MemberService { private final MemberRepository memberRepository; private final BasicInfoRepository basicInfoRepository; private final FaceInfoRepository faceInfoRepository; + private final FaceInfoByLevelRepository faceInfoByLevelRepository; private final AnalysisInfoRepository analysisInfoRepository; private final PasswordEncoder passwordEncoder; @@ -114,6 +117,11 @@ public SignupResponse signUp(SignUpRequest request) { .generatedS3url(defaultFaceInfoS3url) .build(); faceInfoRepository.save(faceInfo); +// +// FaceInfoByLevel faceInfoByLevel = FaceInfoByLevel.builder() +// .generatedByLevelS3url(defaultFaceInfoS3url) +// .build(); +// faceInfoByLevelRepository.save(faceInfoByLevel); // 관상 분석 초기값 AnalysisInfo analysisInfo = AnalysisInfo.builder()