Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
e5696e7
feat: 선편지 엔티티 생성
yuhyun1 Jul 11, 2025
027c00b
feat: 선편지 기능 on/off 값 생성
yuhyun1 Jul 11, 2025
14d570a
fix: mysql 예약어 read -> readStatus로 수정
yuhyun1 Jul 11, 2025
f74f988
feat: 기본 디폴트값 off
yuhyun1 Jul 14, 2025
d2456c5
feat: 선편지 ON/OFF 기능 추가
yuhyun1 Jul 15, 2025
fb1695b
style: 사용하지 않는 import 제거
yuhyun1 Jul 15, 2025
9d4aac4
feat: 선편지-펫 등록 테이블 생성
yuhyun1 Jul 15, 2025
fdef0d8
feat: 선편지-펫 등록 기능 추가
yuhyun1 Jul 16, 2025
1b070ac
feat: 선편지-펫 삭제 기능 추가 및 중복 코드 메소드화
yuhyun1 Jul 16, 2025
83063dd
feat: 편지 리스트 조회 시 sequence 응답값 제거 및 선편지 응답값 추가
yuhyun1 Jul 18, 2025
6bd12ee
feat: 선편지 펫 등록/삭제 API 응답 구조 지정 및 리팩토링
yuhyun1 Jul 18, 2025
94ba903
feat: 선편지 펫 등록/삭제 시 선편지 기능 ON인지 검증 로직 추가
yuhyun1 Jul 18, 2025
b6028cd
fix: restful url
yuhyun1 Jul 18, 2025
8ff51f2
feat: 유저 선편지 기능 조회 (ON/OFF)
yuhyun1 Jul 18, 2025
44889b9
feat: 마이페이지 조회 시 선편지 ON/OFF 응답값 추가
yuhyun1 Jul 18, 2025
07f11eb
feat: 선편지 펫 리스트 조회 API 추가
yuhyun1 Jul 18, 2025
8a20bcb
feat: 선편지 리스트 조회 기능 추가 ing
yuhyun1 Jul 18, 2025
38767ad
feat: 선편지 리스트 조회 기능 추가
yuhyun1 Jul 20, 2025
92a1bb2
feat: 선편지 상세 조회에 필요한 userId 응답값 추가
yuhyun1 Jul 20, 2025
02e06d8
feat: 선편지 상세 조회 ing
yuhyun1 Jul 20, 2025
2736614
feat: 선편지 상세 조회 기능 추가
yuhyun1 Jul 21, 2025
92a0002
feat: 선편지 수정 기능 추가
yuhyun1 Jul 21, 2025
a39d685
fix: 기존 DB 풀스캔에서 ID 1과 2만 조회하게끔 수정 (선편지 정보 넣기 전)
yuhyun1 Jul 24, 2025
1ec1c8b
feat: 선편지 AiSetting 조회 API 추가
yuhyun1 Jul 24, 2025
62627f5
feat: 선편지 AiConfig 수정 API 추가
yuhyun1 Jul 24, 2025
ad6426b
feat: 선편지-AI 관련 API 정리 및 구분
yuhyun1 Jul 28, 2025
6a864f8
fix: 테스트용 로그 생성 메소드 제거
yuhyun1 Jul 30, 2025
410fa18
feat: 선편지 GPT 재생성 API 추가 - ing
yuhyun1 Jul 30, 2025
d3e39a4
fix: 프롬프트에서 반려동물 성격 추가 내용 제거 (AI가 반영 못함)
yuhyun1 Jul 31, 2025
9b5fd70
feat: 발송 대기 상태인 것만 GPT 재생성 가능하게 조건 추가
yuhyun1 Aug 4, 2025
b183808
feat: GPT 재생성 기능 추가
yuhyun1 Aug 7, 2025
fb243c5
feat: GPT 재생성 기능 추가
yuhyun1 Aug 7, 2025
de8b773
feat: 선편지 강제 발송 기능 추가 ing
yuhyun1 Aug 8, 2025
bb464e4
feat: 선편지 강제 발송 기능 추가 ing (메일 알림)
yuhyun1 Aug 11, 2025
d3d24ee
feat: 선편지 강제 발송 기능 추가 ing (메일 알림 2)
yuhyun1 Aug 12, 2025
ec62e26
feat: messages_ko.properties에 선편지 메일 텍스트 추가
yuhyun1 Aug 13, 2025
d807c56
feat: messages.properties에 선편지 메일 텍스트 추가
yuhyun1 Aug 13, 2025
acfb33e
fix: 줄바꿈 추가
yuhyun1 Aug 13, 2025
ad6bb05
fix: 줄바꿈 추가
yuhyun1 Aug 13, 2025
55662c5
fix: 줄바꿈 수정
yuhyun1 Aug 13, 2025
e9082ef
fix: 줄바꿈 수정
yuhyun1 Aug 13, 2025
3cd96f2
fix: 줄바꿈 수정
yuhyun1 Aug 13, 2025
626a10b
feat: 선편지 강제 발송 기능 추가
yuhyun1 Aug 13, 2025
6765825
feat: 메일 발송 로직 리팩토링 - 후에 새로운 템플릿이 추가되어도 기존 로직 수정하지 않고 추가할 수 있게 확장성 고려…
yuhyun1 Aug 13, 2025
a4a00fd
feat: 알림톡 발송 로직 추가
yuhyun1 Aug 13, 2025
3289912
feat: 공유링크 - 선편지 상세 조회 기능 추가
yuhyun1 Aug 13, 2025
1cd83cd
feat: 선편지 기능 OFF 시 선편지 pet 리스트 초기화 로직 추가
yuhyun1 Aug 18, 2025
94d3423
feat: 선편지 리스트 생성 스케줄링 추가 + GPT 호출 및 편지 내용 생성 로직 추가
yuhyun1 Aug 18, 2025
84c1ca6
feat: 알림톡 템플릿 코드 추가
yuhyun1 Aug 19, 2025
e0e28f4
fix: 편지함 조회 시 letterStatus를 통해 읽음 처리 확인 가능하게끔 수정
yuhyun1 Aug 19, 2025
7bec134
feat: 편지함 조회 시 읽음 여부 응답값 추가
yuhyun1 Aug 19, 2025
07dd586
fix: save로 DB 적재
yuhyun1 Aug 19, 2025
f6a6388
refactor: 사용하지 않는 기능 제거 및 DB 반영 (수동 SQL)
yuhyun1 Aug 19, 2025
1640d5b
feat: 선편지 스케줄링 자동 발송 기능 구축
yuhyun1 Aug 20, 2025
4ef67fc
feat: 선편지 AI 생성 실패 시 슬랙 에러 발송 기능 추가 및 중복 코드 리팩토링
yuhyun1 Aug 20, 2025
85c2de1
fix: QA 기간동안 매일 발송되게 수정
yuhyun1 Aug 20, 2025
b104403
refactor: GPT 재생성 조건 삭제 (status 관계없이 재생성 가능)
yuhyun1 Aug 20, 2025
336e114
feat: 파라미터에 반려동물 성격 값 추가 (편지에 아이 성격 반영 가능)
yuhyun1 Aug 21, 2025
a1b669c
fix: 알림톡 템플릿에 따라 파라미터 수정
yuhyun1 Aug 21, 2025
87aee91
fix: 공유 URL 경로 수정
yuhyun1 Aug 23, 2025
f21b169
feat: 이미지 키 응답값 추가
yuhyun1 Aug 23, 2025
8f9aab8
feat: 사용자 - 선편지 상세 조회 API 추가
yuhyun1 Aug 23, 2025
5f6f575
fix: 알림톡 템플릿 변경 및 공유 링크 수정
yuhyun1 Aug 25, 2025
c6d813c
fix: 알림톡 템플릿 변경
yuhyun1 Aug 26, 2025
d7a6c45
fix: 알림톡 템플릿 변경
yuhyun1 Aug 26, 2025
466d076
feat: 선편지 생성 실패 처리 및 슬랙 알림 추가
yuhyun1 Aug 27, 2025
c8c1f5c
feat: 선편지 슬랙 리포트 기능 구축
yuhyun1 Aug 27, 2025
82b3b27
feat: 외부 API 호출 실패 시 일괄 재시도를 위해 스케줄링 구축
yuhyun1 Aug 28, 2025
ae98f59
feat: 편지 생성 실패 시 재시도 스케줄링 구축
yuhyun1 Aug 28, 2025
cf975d4
feat: 편지 발송 실패 시 재시도 스케줄링 구축 및 트랜잭션 분리로 인한 편지 발송 독립 처리
yuhyun1 Sep 1, 2025
6867396
feat: 선편지 목록 조회 API에 petId 응답값 추가
yuhyun1 Sep 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions src/docs/asciidoc/pet.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ operation::pet-e2e-test/should_-pet-response_-when_-authenticated[snippets='http

operation::pet-e2e-test/should_-create-pet_-when_-valid-request[snippets='http-request,request-headers,request-fields,http-response,response-headers']

=== 좋아요 증가

operation::pet-e2e-test/should_-pet-increase-favorite_-when_-valid-request[snippets='http-request,path-parameters,request-headers,http-response']

=== 수정

operation::pet-e2e-test/should_-update-pet_-when_-valid-request[snippets='http-request,path-parameters,request-headers,request-fields,http-response']
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.rainbowletter.server.ai.application.port.in.GetAiPromptParameterUseCase;
import com.rainbowletter.server.ai.application.port.in.dto.AiPromptParameterResponse;
import com.rainbowletter.server.common.annotation.WebAdapter;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
Expand All @@ -14,11 +15,12 @@
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/admins/ai")
@Tag(name = "ai")
@Tag(name = "ai", description = "AI 관련")
class GetAiPromptParameterController {

private final GetAiPromptParameterUseCase getAiPromptParameterUseCase;

@Operation(summary = "파라미터 조회")
@GetMapping("/parameters")
ResponseEntity<AiPromptParameterResponse> getParameters() {
final AiPromptParameterResponse response = getAiPromptParameterUseCase.getPromptParameters();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.rainbowletter.server.ai.application.port.in.GetAiSettingUseCase;
import com.rainbowletter.server.ai.application.port.in.dto.AiSettingResponse;
import com.rainbowletter.server.common.annotation.WebAdapter;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
Expand All @@ -14,15 +15,23 @@
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/admins/ai")
@Tag(name = "ai")
@Tag(name = "ai", description = "AI 관련")
class GetAiSettingController {

private final GetAiSettingUseCase getAiSettingUseCase;

@Operation(summary = "일반 편지 프롬프트 옵션 조회")
@GetMapping("/setting")
ResponseEntity<AiSettingResponse> getSetting() {
final AiSettingResponse response = getAiSettingUseCase.getSetting();
return ResponseEntity.ok(response);
}

@Operation(summary = "선편지 프롬프트 옵션 조회")
@GetMapping("/setting/pet-initiated-letter")
ResponseEntity<AiSettingResponse> getPetInitiatedLetterSetting() {
final AiSettingResponse response = getAiSettingUseCase.getPetInitiatedLetterSetting();
return ResponseEntity.ok(response);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.rainbowletter.server.ai.application.port.in.UpdateAiConfigCommand;
import com.rainbowletter.server.ai.application.port.in.UpdateAiConfigUseCase;
import com.rainbowletter.server.common.annotation.WebAdapter;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PutMapping;
Expand All @@ -15,15 +16,23 @@
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/admins/ai")
@Tag(name = "ai")
@Tag(name = "ai", description = "AI 관련")
class UpdateAiConfigController {

private final UpdateAiConfigUseCase updateAiConfigUseCase;

@Operation(summary = "일반 편지 프롬프트 타입/AB 테스트 설정")
@PutMapping("/config")
void updateConfig(@RequestBody final UpdateAiConfigRequest request) {
final var command = new UpdateAiConfigCommand(request.useABTest(), request.selectPrompt());
updateAiConfigUseCase.updateConfig(command);
}

@Operation(summary = "선편지 프롬프트 타입/AB 테스트 설정")
@PutMapping("/config/pet-initiated-letter")
void updatePetInitiatedLetterConfig(@RequestBody final UpdateAiConfigRequest request) {
final var command = new UpdateAiConfigCommand(request.useABTest(), request.selectPrompt());
updateAiConfigUseCase.updatePetInitiatedLetterConfig(command);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.rainbowletter.server.ai.application.port.in.UpdateAiOptionCommand;
import com.rainbowletter.server.ai.application.port.in.UpdateAiOptionUseCase;
import com.rainbowletter.server.common.annotation.WebAdapter;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PathVariable;
Expand All @@ -18,11 +19,12 @@
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/admins/ai")
@Tag(name = "ai")
@Tag(name = "ai", description = "AI 관련")
class UpdateAiOptionController {

private final UpdateAiOptionUseCase updateAiOptionUseCase;

@Operation(summary = "프롬프트 옵션 수정")
@PutMapping("/options/{id}")
void updatePrompt(
@PathVariable("id") final Long id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.rainbowletter.server.ai.application.port.in.UpdateAiPromptCommand;
import com.rainbowletter.server.ai.application.port.in.UpdateAiPromptUseCase;
import com.rainbowletter.server.common.annotation.WebAdapter;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PathVariable;
Expand All @@ -17,11 +18,12 @@
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/admins/ai")
@Tag(name = "ai")
@Tag(name = "ai", description = "AI 관련")
class UpdateAiPromptController {

private final UpdateAiPromptUseCase updateAiPromptUseCase;

@Operation(summary = "프롬프트 수정")
@PutMapping("/prompts/{id}")
void updatePrompt(
@PathVariable("id") final Long id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
import com.rainbowletter.server.ai.application.port.out.UpdateSettingStatePort;
import com.rainbowletter.server.common.annotation.PersistenceAdapter;
import com.rainbowletter.server.common.application.domain.exception.RainbowLetterException;
import java.util.List;
import lombok.RequiredArgsConstructor;

import java.util.List;
import java.util.stream.Stream;

@PersistenceAdapter
@RequiredArgsConstructor
class AiSettingPersistenceAdapter implements LoadSettingPort, UpdateSettingStatePort {
Expand All @@ -26,14 +28,22 @@ public AiSetting loadSetting() {
final AiConfigJpaEntity aiConfig = aiConfigJpaRepository.findById(1L)
.orElseThrow(() -> new RainbowLetterException("not.exists.ai.setting"));

final List<AiPromptJpaEntity> aiPromptJpaEntities = aiPromptJpaRepository.findAll();
if (aiPromptJpaEntities.isEmpty()) {
throw new RainbowLetterException("not.exists.ai.prompt");
}
List<AiPrompt> prompts = Stream.of(1L, 2L)
.map(id -> loadPrompt(new AiPromptId(id)))
.toList();

return aiSettingMapper.mapToDomain(aiConfig, prompts);
}

final List<AiPrompt> prompts = aiPromptJpaEntities.stream()
.map(aiPromptJpaEntity -> loadPrompt(new AiPromptId(aiPromptJpaEntity.getId())))
@Override
public AiSetting loadPetInitiatedLetterSetting() {
final AiConfigJpaEntity aiConfig = aiConfigJpaRepository.findById(2L)
.orElseThrow(() -> new RainbowLetterException("not.exists.ai.letter.setting"));

List<AiPrompt> prompts = Stream.of(3L, 4L)
.map(id -> loadPrompt(new AiPromptId(id)))
.toList();

return aiSettingMapper.mapToDomain(aiConfig, prompts);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import com.rainbowletter.server.letter.application.domain.model.Letter;
import com.rainbowletter.server.pet.application.domain.model.Pet;
import java.util.Arrays;
import java.util.List;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

Expand All @@ -13,42 +15,50 @@ public enum Parameter {
PET_NAME(Pet.class) {
@Override
public String value(final Object instance) {
Parameter.validateInstanceType(instance, Pet.class);
validateInstanceType(instance, Pet.class);
return ((Pet) instance).getName();
}
},
PET_OWNER(Pet.class) {
@Override
public String value(final Object instance) {
Parameter.validateInstanceType(instance, Pet.class);
validateInstanceType(instance, Pet.class);
return ((Pet) instance).getOwner();
}
},
PET_SPECIES(Pet.class) {
@Override
public String value(final Object instance) {
Parameter.validateInstanceType(instance, Pet.class);
validateInstanceType(instance, Pet.class);
return ((Pet) instance).getSpecies();
}
},
PET_CHARACTER(Pet.class) {
@Override
public String value(final Object instance) {
validateInstanceType(instance, Pet.class);
List<String> personalities = ((Pet) instance).getPersonalities();
return String.join(", ", personalities);
}
},
LETTER_CONTENT(Letter.class) {
@Override
public String value(final Object instance) {
Parameter.validateInstanceType(instance, Letter.class);
validateInstanceType(instance, Letter.class);
return ((Letter) instance).getContent();
}
},
LETTER_COUNT(LetterCount.class) {
@Override
public String value(final Object instance) {
Parameter.validateInstanceType(instance, LetterCount.class);
validateInstanceType(instance, LetterCount.class);
return String.valueOf(instance);
}
},
FIRST_LETTER(FirstLetter.class) {
@Override
public String value(final Object instance) {
Parameter.validateInstanceType(instance, FirstLetter.class);
validateInstanceType(instance, FirstLetter.class);
return String.valueOf(instance);
}
};
Expand All @@ -70,12 +80,8 @@ public static Parameter get(final String name) {

public abstract String value(Object instance);

public record LetterCount(Long count) {
public record LetterCount(Long count) { }

}

public record FirstLetter(Boolean isFirstLetter) {

}
public record FirstLetter(Boolean isFirstLetter) { }

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,10 @@ public AiSettingResponse getSetting() {
return AiSettingResponse.from(aiSetting);
}

@Override
public AiSettingResponse getPetInitiatedLetterSetting() {
final AiSetting aiSetting = loadSettingPort.loadPetInitiatedLetterSetting();
return AiSettingResponse.from(aiSetting);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,11 @@ public void updateConfig(final UpdateAiConfigCommand command) {
updateSettingStatePort.updateConfig(aiSetting);
}

@Override
public void updatePetInitiatedLetterConfig(UpdateAiConfigCommand command) {
final AiSetting aiSetting = loadSettingPort.loadPetInitiatedLetterSetting();
aiSetting.update(command.getUseABTest(), command.getSelectPrompt());
updateSettingStatePort.updateConfig(aiSetting);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ public interface GetAiSettingUseCase {

AiSettingResponse getSetting();

AiSettingResponse getPetInitiatedLetterSetting();

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ public interface UpdateAiConfigUseCase {

void updateConfig(UpdateAiConfigCommand command);

void updatePetInitiatedLetterConfig(UpdateAiConfigCommand command);

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ public AiClientCommand(final AiPrompt aiPrompt, final List<Object> parameterInst
this.messages = buildMessages(aiPrompt, parameterInstances, recentLetters);
}

public AiClientCommand(final AiPrompt aiPrompt, final List<Object> parameterInstances) {
this(aiPrompt, parameterInstances, List.of());
}

private List<Message> buildMessages(
final AiPrompt aiPrompt,
final List<Object> parameterInstances,
Expand All @@ -53,27 +57,21 @@ private List<Message> buildMessages(
aiPrompt.getParameters(),
parameterInstances
);
messages.add(new Message(USER, currentPrompt));

messages.add(new Message(USER, currentPrompt));
return messages;
}

private String combineUserParameters(
final String userPrompt,
final List<Parameter> parameters,
final List<Object> parameterInstances
) {
final ArrayList<String> formatValues = new ArrayList<>();
for (final Parameter parameter : parameters) {
final String value = parameter.value(
parameterInstances.stream()
.filter(parameterInstance -> parameter.getClazz().isInstance(parameterInstance))
private String combineUserParameters(String userPrompt, List<Parameter> parameters, List<Object> instances) {
List<String> values = parameters.stream()
.map(parameter -> parameter.value(
instances.stream()
.filter(parameter.getClazz()::isInstance)
.findAny()
.orElseThrow(() -> new RainbowLetterException("Ai 파라미터 인스턴스가 존재하지 않습니다."))
);
formatValues.add(value);
}
return String.format(userPrompt, formatValues.toArray());
.orElseThrow(() -> new RainbowLetterException("Ai 파라미터 인스턴스가 존재하지 않습니다."))))
.toList();

return String.format(userPrompt, values.toArray());
}

@Value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public interface LoadSettingPort {

AiSetting loadSetting();

AiSetting loadPetInitiatedLetterSetting();

AiPrompt loadPrompt(AiPromptId id);

AiOption loadOption(AiOptionId id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ private void configureAuthorizationSecurity(final HttpSecurity http) throws Exce
.requestMatchers(convertUriToPathMatcher(AnonymousAllowUri.values())).anonymous()
.requestMatchers(convertUriToPathMatcher(AdminAllowUri.values())).hasRole("ADMIN")
.requestMatchers(HttpMethod.GET, "/api/letters/**").permitAll()
.requestMatchers(HttpMethod.GET, "/api/pet-initiated-letters/**").permitAll()
.requestMatchers(PERMIT_PATHS).permitAll()
.anyRequest().authenticated()
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public enum AccessAllowUri implements AllowUri {
GET_FAQS("/api/faqs"),
GET_IMAGES("/api/images/resources/**"),
SHARE_LETTER("/api/letters/share/**"),
SHARE_PET_INITIATED_LETTER("/api/pet-initiated-letters/share/**"),
LOG_USER_AGENT("/api/data/**"),
;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public void handleImageUpload(ImageUploadEvent event) {
storageService.uploadFile(webpData, "image/webp", event.filePath());
} catch (Exception e) {
log.error("[이벤트 처리 중 이미지 업로드 실패] {}", event.filePath(), e);
slackErrorReportService.sendErrorReportToSlack(event.filePath(), e);
slackErrorReportService.sendImageUploadErrorReportToSlack(event.filePath(), e);
}
}

Expand Down
Loading