From 1826f947507c14a16056f033abde8acd3e573292 Mon Sep 17 00:00:00 2001 From: gaeunee2 Date: Thu, 19 Feb 2026 03:44:48 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20canvas=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=20URL=20=EB=B3=80=ED=99=98=20=EC=98=A4=EB=A5=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20-=20presigned=20URL=EB=A1=9C=20=EB=B3=80=ED=99=98?= =?UTF-8?q?=20=ED=9B=84=20AI=20=EC=B2=98=EB=A6=AC=20=EA=B0=80=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../conversation/service/ChatServiceImpl.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.java b/src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.java index d00da2e..f5dfce6 100644 --- a/src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.java +++ b/src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.java @@ -179,9 +179,17 @@ public Flux streamConversation(Long userId, ConversationReq .build(); ChatMessage savedAiMessage = chatMessageRepository.save(aiMessage); - // 6. 자산 URL 변환 - List filesUrl = convertAssetIdsToUrls(request.getMentionedAssetIds(), userId); - log.info("[Chat] 자산 URL 변환 완료 - assetIds: {}, filesUrl: {}", request.getMentionedAssetIds(), filesUrl); + // 6. 자산 URL 변환 (mentionedAssetIds + canvasImageIds 모두 포함) + List allAssetIdsForUrl = new ArrayList<>(); + if (request.getMentionedAssetIds() != null) { + allAssetIdsForUrl.addAll(request.getMentionedAssetIds()); + } + if (request.getCanvasImageIds() != null) { + allAssetIdsForUrl.addAll(request.getCanvasImageIds()); + } + List filesUrl = convertAssetIdsToUrls(allAssetIdsForUrl, userId); + log.info("[Chat] 자산 URL 변환 완료 - mentionedAssetIds: {}, canvasImageIds: {}, filesUrl: {}", + request.getMentionedAssetIds(), request.getCanvasImageIds(), filesUrl); return new StreamInitData(chatSession, note, savedAiMessage, threadIdToUse, filesUrl); }); From ce0cf8637d5ecb44166f9cd569272b799d824787 Mon Sep 17 00:00:00 2001 From: gaeunee2 Date: Thu, 19 Feb 2026 03:55:54 +0900 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=EC=A4=91=EB=B3=B5=20ID=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../conversation/service/ChatServiceImpl.java | 62 +++++++++++-------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.java b/src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.java index f5dfce6..39d3192 100644 --- a/src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.java +++ b/src/main/java/com/proovy/domain/conversation/service/ChatServiceImpl.java @@ -141,14 +141,8 @@ public Flux streamConversation(Long userId, ConversationReq ChatMessage savedUserMessage = chatMessageRepository.save(userMessage); chatMessageRepository.flush(); // ID 확보 후 MessageAsset 저장 가능 - // 4-1. 메시지-자산 연결 저장 (mentionedAssetIds + canvasImageIds) - List allAssetIds = new ArrayList<>(); - if (request.getMentionedAssetIds() != null) { - allAssetIds.addAll(request.getMentionedAssetIds()); - } - if (request.getCanvasImageIds() != null) { - allAssetIds.addAll(request.getCanvasImageIds()); - } + // 4-1. 메시지-자산 연결 저장 (mentionedAssetIds + canvasImageIds, 중복 제거) + List allAssetIds = mergeDistinctAssetIds(request.getMentionedAssetIds(), request.getCanvasImageIds()); if (!allAssetIds.isEmpty()) { List assets = assetRepository.findAllByIdInAndUserId(allAssetIds, userId); if (assets.size() != allAssetIds.size()) { @@ -179,17 +173,10 @@ public Flux streamConversation(Long userId, ConversationReq .build(); ChatMessage savedAiMessage = chatMessageRepository.save(aiMessage); - // 6. 자산 URL 변환 (mentionedAssetIds + canvasImageIds 모두 포함) - List allAssetIdsForUrl = new ArrayList<>(); - if (request.getMentionedAssetIds() != null) { - allAssetIdsForUrl.addAll(request.getMentionedAssetIds()); - } - if (request.getCanvasImageIds() != null) { - allAssetIdsForUrl.addAll(request.getCanvasImageIds()); - } - List filesUrl = convertAssetIdsToUrls(allAssetIdsForUrl, userId); - log.info("[Chat] 자산 URL 변환 완료 - mentionedAssetIds: {}, canvasImageIds: {}, filesUrl: {}", - request.getMentionedAssetIds(), request.getCanvasImageIds(), filesUrl); + // 6. 자산 URL 변환 (mentionedAssetIds + canvasImageIds 모두 포함, 중복 제거) + List filesUrl = convertAssetIdsToUrls(allAssetIds, userId); + log.info("[Chat] 자산 URL 변환 완료 - mentionedAssetIds: {}, canvasImageIds: {}, filesUrl count: {}", + request.getMentionedAssetIds(), request.getCanvasImageIds(), filesUrl.size()); return new StreamInitData(chatSession, note, savedAiMessage, threadIdToUse, filesUrl); }); @@ -219,8 +206,8 @@ public Flux streamConversation(Long userId, ConversationReq .authToken(accessToken) // Spring 인증 토큰 전달 .build(); - log.info("[Chat] Proovy-ai 요청 생성 - threadId: {}, filesUrl: {}, message: {}", - finalThreadIdToUse, filesUrl, request.getText()); + log.info("[Chat] Proovy-ai 요청 생성 - threadId: {}, filesUrl count: {}, message: {}", + finalThreadIdToUse, filesUrl.size(), request.getText()); // 8. SSE 스트리밍 호출 final StringBuilder contentBuilder = new StringBuilder(); @@ -493,12 +480,21 @@ private List convertAssetIdsToUrls(List assetIds, Long userId) { return Collections.emptyList(); } - List assets = assetRepository.findAllByIdInAndUserId(assetIds, userId); - log.info("[Chat] 자산 조회 결과 - 요청: {}, 조회됨: {}, userId: {}", assetIds, assets.size(), userId); + List distinctAssetIds = assetIds.stream() + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); + + if (distinctAssetIds.size() != assetIds.size()) { + log.debug("[Chat] 자산 ID 중복/NULL 제거 - 원본: {}, 정제 후: {}", assetIds.size(), distinctAssetIds.size()); + } + + List assets = assetRepository.findAllByIdInAndUserId(distinctAssetIds, userId); + log.info("[Chat] 자산 조회 결과 - 요청: {}, 조회됨: {}, userId: {}", distinctAssetIds, assets.size(), userId); - if (assets.size() != assetIds.size()) { + if (assets.size() != distinctAssetIds.size()) { log.warn("[Chat] 일부 자산 조회 실패 - 요청: {}, 조회됨: {}", - assetIds.size(), assets.size()); + distinctAssetIds.size(), assets.size()); } List urls = assets.stream() @@ -506,8 +502,8 @@ private List convertAssetIdsToUrls(List assetIds, Long userId) { // Presigned URL 생성 (15분 유효) String url = s3Service.generatePresignedDownloadUrl( asset.getS3Key(), asset.getFileName(), 15); - log.info("[Chat] Presigned URL 생성 - assetId: {}, s3Key: {}, url: {}", - asset.getId(), asset.getS3Key(), url); + log.debug("[Chat] Presigned URL 생성 - assetId: {}, s3Key: {}", + asset.getId(), asset.getS3Key()); return url; }) .collect(Collectors.toList()); @@ -515,6 +511,18 @@ private List convertAssetIdsToUrls(List assetIds, Long userId) { return urls; } + private List mergeDistinctAssetIds(List mentionedAssetIds, List canvasImageIds) { + LinkedHashSet merged = new LinkedHashSet<>(); + if (mentionedAssetIds != null) { + merged.addAll(mentionedAssetIds); + } + if (canvasImageIds != null) { + merged.addAll(canvasImageIds); + } + merged.remove(null); + return new ArrayList<>(merged); + } + /** * 기능 검증 */