diff --git a/.github/workflows/kakao_ci.yml b/.github/workflows/kakao_ci.yml index 2e7ab725..1b6ced38 100644 --- a/.github/workflows/kakao_ci.yml +++ b/.github/workflows/kakao_ci.yml @@ -90,7 +90,7 @@ jobs: - name: run kakao-chat at macOS if: runner.os == 'macOS' - uses: psychology50/kakao-chat-ci@v1.8 + uses: psychology50/kakao-chat-ci@v1.6 env: KAKAO_CLIENT: ${{ secrets.KAKAO_CLIENT }} KAKAO_EMAIL: ${{ secrets.KAKAO_EMAIL }} diff --git a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/auth/dto/SmsRes.java b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/auth/dto/SmsRes.java index d1a312d0..161af139 100644 --- a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/auth/dto/SmsRes.java +++ b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/auth/dto/SmsRes.java @@ -1,5 +1,8 @@ package kr.co.fitapet.api.apis.auth.dto; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; @@ -17,8 +20,12 @@ public record SmsRes( @Schema(description = "수신자 번호") String to, @Schema(description = "발송 시간") + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime sendTime, @Schema(description = "만료 시간 (default: 3분)", example = "2021-08-01T00:00:00") + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime expireTime ) { public static SmsRes of(String to, LocalDateTime sendTime, LocalDateTime expireTime) { diff --git a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/memo/controller/MemoApi.java b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/memo/controller/MemoApi.java index 6bc62e48..d2c6ffdb 100644 --- a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/memo/controller/MemoApi.java +++ b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/memo/controller/MemoApi.java @@ -6,11 +6,12 @@ import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; -import kr.co.fitapet.api.apis.memo.dto.MemoPatchReq; +import kr.co.fitapet.api.apis.memo.dto.MemoCategorySaveRes; +import kr.co.fitapet.api.apis.memo.dto.MemoUpdateReq; import kr.co.fitapet.api.apis.memo.usecase.MemoUseCase; import kr.co.fitapet.api.common.response.SuccessResponse; import kr.co.fitapet.domain.domains.memo.dto.MemoInfoDto; -import kr.co.fitapet.domain.domains.memo.dto.MemoSaveReq; +import kr.co.fitapet.api.apis.memo.dto.MemoSaveReq; import kr.co.fitapet.api.apis.memo.dto.SubMemoCategorySaveReq; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -18,7 +19,6 @@ import org.springframework.data.domain.Sort; import org.springframework.data.web.PageableDefault; import org.springframework.data.web.SortDefault; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; @@ -43,10 +43,11 @@ public ResponseEntity saveSubMemoCategory( @PathVariable("root_memo_category_id") Long rootMemoCategoryId, @RequestBody @Valid SubMemoCategorySaveReq req ) { - memoUseCase.saveSubMemoCategory(petId, rootMemoCategoryId, req); - return ResponseEntity.status(HttpStatus.CREATED).body(SuccessResponse.noContent()); + MemoCategorySaveRes res = memoUseCase.saveSubMemoCategory(petId, rootMemoCategoryId, req); + return ResponseEntity.ok(SuccessResponse.from(res)); } + @Deprecated(since = "2024-02-18") @Operation(summary = "메모 카테고리 리스트 조회", description = "메모 카테고리 타입이 root인 경우, 서브 메모 카테고리 리스트도 함께 조회합니다.") @Parameters({ @Parameter(name = "pet_id", description = "반려동물 ID", in = ParameterIn.PATH, required = true), @@ -155,8 +156,7 @@ public ResponseEntity saveMemo( @PathVariable("memo_category_id") Long memoCategoryId, @RequestBody @Valid MemoSaveReq req ) { - memoUseCase.saveMemo(memoCategoryId, req); - return ResponseEntity.status(HttpStatus.CREATED).body(SuccessResponse.noContent()); + return ResponseEntity.ok(SuccessResponse.from(memoUseCase.saveMemo(memoCategoryId, req))); } @Operation(summary = "메모 삭제") @@ -182,15 +182,16 @@ public ResponseEntity deleteMemo( @Parameter(name = "memo_category_id", description = "메모 카테고리 ID", in = ParameterIn.PATH, required = true), @Parameter(name = "memo_id", description = "메모 ID", in = ParameterIn.PATH, required = true) }) - @PatchMapping("/memo-categories/{memo_category_id}/memos/{memo_id}") - @PreAuthorize("isAuthenticated() and @managerAuthorize.isManager(principal.userId, #petId) and @memoAuthorize.isValidMemoCategoryAndMemo(#memoCategoryId, #memoId, #petId)") - public ResponseEntity patchMemo( + @PutMapping("/memo-categories/{memo_category_id}/memos/{memo_id}") + @PreAuthorize("isAuthenticated() and @managerAuthorize.isManager(principal.userId, #petId) and @memoAuthorize.isValidMemoCategoryAndMemo(#memoCategoryId, #memoId, #petId)" + + "and @memoAuthorize.isValidMemoCategory(#req.memoCategoryId(), #petId)") + public ResponseEntity putMemo( @PathVariable("pet_id") Long petId, @PathVariable("memo_category_id") Long memoCategoryId, @PathVariable("memo_id") Long memoId, - @RequestBody @Valid MemoPatchReq req + @RequestBody @Valid MemoUpdateReq req ) { - memoUseCase.patchMemo(memoId, req); + memoUseCase.updateMemo(memoId, req); return ResponseEntity.ok(SuccessResponse.noContent()); } } diff --git a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/memo/dto/MemoCategorySaveRes.java b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/memo/dto/MemoCategorySaveRes.java new file mode 100644 index 00000000..92c30330 --- /dev/null +++ b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/memo/dto/MemoCategorySaveRes.java @@ -0,0 +1,12 @@ +package kr.co.fitapet.api.apis.memo.dto; + +import kr.co.fitapet.common.annotation.Dto; + +@Dto(name = "memoCategory") +public record MemoCategorySaveRes( + Long memoCategoryId +) { + public static MemoCategorySaveRes valueOf(Long memoCategoryId) { + return new MemoCategorySaveRes(memoCategoryId); + } +} diff --git a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/memo/dto/MemoPatchReq.java b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/memo/dto/MemoPatchReq.java deleted file mode 100644 index 16bdad6e..00000000 --- a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/memo/dto/MemoPatchReq.java +++ /dev/null @@ -1,22 +0,0 @@ -package kr.co.fitapet.api.apis.memo.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.Size; -import lombok.Getter; -import org.openapitools.jackson.nullable.JsonNullable; - -import java.util.List; - -@Schema(description = "메모 수정 요청") -public record MemoPatchReq( - @Schema(description = "메모 제목") - @Size(max = 21, message = "메모 제목은 21자 이하로 입력해주세요.") - String title, - @Schema(description = "메모 내용", example = "메모 내용") - String content, - @Schema(description = "기존 memoImage 리스트에서 삭제할 요소.", example = "[1, 2, 3]") - List removedMemoImageIds, - @Schema(description = "추가할 메모 이미지 URL 리스트", example = "['https://{bucket-name}.{base-url}/{object-name}']") - List addedMemoImageUrls -) { -} diff --git a/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/dto/MemoSaveReq.java b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/memo/dto/MemoSaveReq.java similarity index 96% rename from fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/dto/MemoSaveReq.java rename to fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/memo/dto/MemoSaveReq.java index f001c7ec..b25bfe80 100644 --- a/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/dto/MemoSaveReq.java +++ b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/memo/dto/MemoSaveReq.java @@ -1,4 +1,4 @@ -package kr.co.fitapet.domain.domains.memo.dto; +package kr.co.fitapet.api.apis.memo.dto; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; diff --git a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/memo/dto/MemoSaveRes.java b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/memo/dto/MemoSaveRes.java new file mode 100644 index 00000000..12e8d060 --- /dev/null +++ b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/memo/dto/MemoSaveRes.java @@ -0,0 +1,12 @@ +package kr.co.fitapet.api.apis.memo.dto; + +import kr.co.fitapet.common.annotation.Dto; + +@Dto(name = "memo") +public record MemoSaveRes( + Long memoId +) { + public static MemoSaveRes valueOf(Long memoId) { + return new MemoSaveRes(memoId); + } +} diff --git a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/memo/dto/MemoUpdateReq.java b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/memo/dto/MemoUpdateReq.java new file mode 100644 index 00000000..b2d716d9 --- /dev/null +++ b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/memo/dto/MemoUpdateReq.java @@ -0,0 +1,30 @@ +package kr.co.fitapet.api.apis.memo.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import kr.co.fitapet.domain.domains.memo.domain.Memo; + +import java.util.List; + +@Schema(description = "메모 수정 요청") +public record MemoUpdateReq( + @Schema(description = "기록 옮길 카테고리 아이디 (변경 사항 없으면 원래 아이디 값 그대로)", example = "1", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @NotNull(message = "메모 카테고리 아이디는 필수입니다.") + Long memoCategoryId, + @NotBlank(message = "제목은 필수입니다.") + @Schema(description = "제목", example = "오늘의 일기", requiredMode = Schema.RequiredMode.REQUIRED) + @Size(max = 21, message = "제목은 21자 이하로 입력해주세요.") + String title, + @NotBlank(message = "내용은 필수입니다.") + @Schema(description = "내용", example = "오늘은 매우 행복했다.", requiredMode = Schema.RequiredMode.REQUIRED) + String content, + @NotNull(message = "메모 이미지는 필수입니다. 없으면 빈 배열을 보내주세요.") + @Schema(description = "메모 이미지", example = "[\"https://fitapet.com/image/1.jpg\", \"https://fitapet.com/image/2.jpg\"]", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + List memoImageUrls +) { + public Memo toEntity() { + return Memo.of(title, content); + } +} diff --git a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/memo/usecase/MemoUseCase.java b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/memo/usecase/MemoUseCase.java index b7c3a06b..f7610ea6 100644 --- a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/memo/usecase/MemoUseCase.java +++ b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/memo/usecase/MemoUseCase.java @@ -1,16 +1,12 @@ package kr.co.fitapet.api.apis.memo.usecase; -import kr.co.fitapet.api.apis.memo.dto.MemoPatchReq; +import kr.co.fitapet.api.apis.memo.dto.*; import kr.co.fitapet.common.annotation.UseCase; -import kr.co.fitapet.common.execption.GlobalErrorException; import kr.co.fitapet.domain.domains.memo.domain.Memo; import kr.co.fitapet.domain.domains.memo.domain.MemoCategory; import kr.co.fitapet.domain.domains.memo.domain.MemoImage; import kr.co.fitapet.domain.domains.memo.dto.MemoCategoryInfoDto; import kr.co.fitapet.domain.domains.memo.dto.MemoInfoDto; -import kr.co.fitapet.domain.domains.memo.dto.MemoSaveReq; -import kr.co.fitapet.api.apis.memo.dto.SubMemoCategorySaveReq; -import kr.co.fitapet.domain.domains.memo.exception.MemoErrorCode; import kr.co.fitapet.domain.domains.memo.service.MemoDeleteService; import kr.co.fitapet.domain.domains.memo.service.MemoSaveService; import kr.co.fitapet.domain.domains.memo.service.MemoSearchService; @@ -36,21 +32,23 @@ public class MemoUseCase { private final NcpObjectStorageService ncpObjectStorageService; @Transactional - public void saveSubMemoCategory(Long petId, Long rootMemoCategoryId, SubMemoCategorySaveReq req) { - req.toEntity(memoSearchService.findMemoCategoryById(rootMemoCategoryId), petSearchService.findPetById(petId)); + public MemoCategorySaveRes saveSubMemoCategory(Long petId, Long rootMemoCategoryId, SubMemoCategorySaveReq req) { + MemoCategory memoCategory = req.toEntity(memoSearchService.findMemoCategoryById(rootMemoCategoryId), petSearchService.findPetById(petId)); + return MemoCategorySaveRes.valueOf(memoSaveService.saveMemoCategory(memoCategory).getId()); } @Transactional - public void saveMemo(Long MemoCategoryId, MemoSaveReq req) { + public MemoSaveRes saveMemo(Long MemoCategoryId, MemoSaveReq req) { MemoCategory memoCategory = memoSearchService.findMemoCategoryById(MemoCategoryId); Memo memo = req.toEntity(); memo.updateMemoCategory(memoCategory); + memoSaveService.saveMemo(memo); - if (req.memoImageUrls() != null) { - memoSaveService.saveMemo(memo); + if (req.memoImageUrls() != null) req.memoImageUrls().forEach(url -> MemoImage.of(url, memo)); - } + + return MemoSaveRes.valueOf(memo.getId()); } @Transactional(readOnly = true) @@ -83,31 +81,24 @@ public void deleteMemo(Long memoId) { } @Transactional - public void patchMemo(Long memoId, MemoPatchReq req) { + public void updateMemo(Long memoId, MemoUpdateReq req) { Memo memo = memoSearchService.findMemoById(memoId); + memo.updateMemo(req.toEntity()); - if (req.title() != null) { - if (req.title().isBlank()) throw new GlobalErrorException(MemoErrorCode.MEMO_TITLE_NOT_EMPTY); - memo.updateTitle(req.title()); - } - - if (req.content() != null) { - if (req.content().isBlank()) throw new GlobalErrorException(MemoErrorCode.MEMO_CONTENT_NOT_EMPTY); - memo.updateContent(req.content()); + if (!memo.getMemoCategory().getId().equals(req.memoCategoryId())) { + MemoCategory memoCategory = memoSearchService.findMemoCategoryById(req.memoCategoryId()); + memo.updateMemoCategory(memoCategory); + log.info("카테고리 이동 {} -> {}", memo.getMemoCategory().getId(), req.memoCategoryId()); } - if (req.removedMemoImageIds() != null) { - List memoImages = memoSearchService.findMemoImagesByMemoId(memoId); - List removedMemoImages = memoImages.stream().filter(memoImage -> req.removedMemoImageIds().contains(memoImage.getId())).toList(); - log.info("removedMemoImages: {}", removedMemoImages); + List originalMemoImages = memoSearchService.findMemoImagesByMemoId(memoId); + List addedMemoImages = req.memoImageUrls().stream().filter(url -> originalMemoImages.stream().noneMatch(memoImage -> memoImage.getImgUrl().equals(url))).map(url -> MemoImage.of(url, memo)).toList(); + List removedMemoImages = originalMemoImages.stream().filter(memoImage -> !req.memoImageUrls().contains(memoImage.getImgUrl())).toList(); + log.info("addedMemoImages: {}, removedMemoImages: {}", addedMemoImages, removedMemoImages); + if (!removedMemoImages.isEmpty()) { memoDeleteService.deleteMemoImages(removedMemoImages); ncpObjectStorageService.deleteObjects(removedMemoImages.stream().map(MemoImage::getImgUrl).toList()); } - - if (req.addedMemoImageUrls() != null) { - log.info("addedMemoImageUrls: {}", req.addedMemoImageUrls()); - req.addedMemoImageUrls().forEach(url -> MemoImage.of(url, memo)); - } } } diff --git a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/profile/dto/AccountSearchReq.java b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/profile/dto/AccountSearchReq.java index bf5e3d51..33e48a8d 100644 --- a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/profile/dto/AccountSearchReq.java +++ b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/profile/dto/AccountSearchReq.java @@ -16,9 +16,4 @@ public record AccountSearchReq( public String getNewEncodedPassword(PasswordEncoder passwordEncoder) { return passwordEncoder.encode(newPassword); } - - @Override - public String newPassword() { - throw new UnsupportedOperationException(); - } } diff --git a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/profile/usecase/MemberAccountUseCase.java b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/profile/usecase/MemberAccountUseCase.java index f81f9224..978b7277 100644 --- a/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/profile/usecase/MemberAccountUseCase.java +++ b/fitapet-app-external-api/src/main/java/kr/co/fitapet/api/apis/profile/usecase/MemberAccountUseCase.java @@ -15,8 +15,6 @@ import kr.co.fitapet.domain.domains.member.dto.MemberInfo; import kr.co.fitapet.domain.domains.member.dto.UidRes; import kr.co.fitapet.domain.domains.member.exception.AccountErrorCode; -import kr.co.fitapet.domain.domains.member.exception.AccountErrorException; -import kr.co.fitapet.domain.domains.member.service.MemberSaveService; import kr.co.fitapet.domain.domains.member.service.MemberSearchService; import kr.co.fitapet.domain.domains.member.type.MemberAttrType; import kr.co.fitapet.domain.domains.memo.dto.MemoCategoryInfoDto; @@ -93,11 +91,6 @@ public void overwritePassword(AccountSearchReq req, String code, SmsPrefix prefi validatePhone(req.phone(), code, prefix); Member member = memberSearchService.findByPhone(req.phone()); - if (!StringUtils.hasText(req.newPassword())) { - BaseErrorCode errorCode = AccountErrorCode.INVALID_PASSWORD_REQUEST; - log.warn("비밀번호 변경 실패: {}", errorCode.getExplainError()); - throw new GlobalErrorException(errorCode); - } member.updateEncodedPassword(req.getNewEncodedPassword(bCryptPasswordEncoder)); smsRedisMapper.removeCode(req.phone(), prefix); } @@ -139,7 +132,7 @@ public List getMemoCategories(Long userId) List petIds = memberSearchService.findMyPetIds(userId); log.info("userId: {}, petIds: {}", userId, petIds); - List rootMemoCategoryIds = memoSearchService.findRootMemoCategoriesIdByPetIds(petIds); + List rootMemoCategoryIds = memoSearchService.findRootMemoCategoryIdsByPetIds(petIds); List rootMemoCategories = new ArrayList<>(); for (Long rootMemoCategoryId : rootMemoCategoryIds) { diff --git a/fitapet-domain/src/main/java/kr/co/fitapet/domain/common/annotation/JavaTimeRedisTemplate.java b/fitapet-domain/src/main/java/kr/co/fitapet/domain/common/annotation/JavaTimeRedisTemplate.java new file mode 100644 index 00000000..6a463163 --- /dev/null +++ b/fitapet-domain/src/main/java/kr/co/fitapet/domain/common/annotation/JavaTimeRedisTemplate.java @@ -0,0 +1,13 @@ +package kr.co.fitapet.domain.common.annotation; + +import org.springframework.beans.factory.annotation.Qualifier; + +import java.lang.annotation.*; + +@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD, + ElementType.TYPE, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Qualifier("javaTimeRedisTemplate") +public @interface JavaTimeRedisTemplate { +} diff --git a/fitapet-domain/src/main/java/kr/co/fitapet/domain/common/redis/manager/ManagerInvitationRepositoryImpl.java b/fitapet-domain/src/main/java/kr/co/fitapet/domain/common/redis/manager/ManagerInvitationRepositoryImpl.java index 03e45c40..b9c37a4e 100644 --- a/fitapet-domain/src/main/java/kr/co/fitapet/domain/common/redis/manager/ManagerInvitationRepositoryImpl.java +++ b/fitapet-domain/src/main/java/kr/co/fitapet/domain/common/redis/manager/ManagerInvitationRepositoryImpl.java @@ -1,6 +1,7 @@ package kr.co.fitapet.domain.common.redis.manager; import jakarta.annotation.Resource; +import kr.co.fitapet.domain.common.annotation.JavaTimeRedisTemplate; import lombok.RequiredArgsConstructor; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; @@ -14,7 +15,7 @@ public class ManagerInvitationRepositoryImpl implements ManagerInvitationReposit private final HashOperations ops; private static final String KEY = "managerInvitation"; - public ManagerInvitationRepositoryImpl(RedisTemplate redisTemplate) { + public ManagerInvitationRepositoryImpl(@JavaTimeRedisTemplate RedisTemplate redisTemplate) { this.ops = redisTemplate.opsForHash(); } diff --git a/fitapet-domain/src/main/java/kr/co/fitapet/domain/config/RedisConfig.java b/fitapet-domain/src/main/java/kr/co/fitapet/domain/config/RedisConfig.java index 18debd7c..0d07cce6 100644 --- a/fitapet-domain/src/main/java/kr/co/fitapet/domain/config/RedisConfig.java +++ b/fitapet-domain/src/main/java/kr/co/fitapet/domain/config/RedisConfig.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import kr.co.fitapet.domain.common.annotation.JavaTimeRedisTemplate; import kr.co.fitapet.domain.common.annotation.RedisCacheConnectionFactory; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; @@ -46,6 +47,18 @@ public RedisConnectionFactory redisConnectionFactory() { @Primary public RedisTemplate redisTemplate() { RedisTemplate template = new RedisTemplate<>(); + + template.setConnectionFactory(redisConnectionFactory()); + + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); + return template; + } + + @Bean + @JavaTimeRedisTemplate + public RedisTemplate javaTimeRedisTemplate() { + RedisTemplate template = new RedisTemplate<>(); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new JavaTimeModule()); diff --git a/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/domain/Memo.java b/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/domain/Memo.java index 90c332a5..a0fac475 100644 --- a/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/domain/Memo.java +++ b/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/domain/Memo.java @@ -50,11 +50,8 @@ public void updateMemoCategory(MemoCategory memoCategory) { memoCategory.getMemos().add(this); } - public void updateTitle(String title) { - this.title = title; - } - - public void updateContent(String content) { - this.content = content; + public void updateMemo(Memo memo) { + this.title = memo.title; + this.content = memo.content; } } diff --git a/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/dto/MemoCategoryInfoDto.java b/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/dto/MemoCategoryInfoDto.java index f2361642..f011c087 100644 --- a/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/dto/MemoCategoryInfoDto.java +++ b/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/dto/MemoCategoryInfoDto.java @@ -1,5 +1,6 @@ package kr.co.fitapet.domain.domains.memo.dto; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import io.swagger.v3.oas.annotations.media.Schema; import kr.co.fitapet.common.annotation.Dto; @@ -27,6 +28,8 @@ public static MemoCategoryInfoDto fromMemoCategory(List memoCatego @Builder @Dto(name = "memoCategory") public record MemoCategoryInfo( + @Schema(description = "카테고리가 등록된 반려동물 ID") + Long petId, @Schema(description = "메모 카테고리 ID") Long memoCategoryId, @Schema(description = "메모 카테고리 이름") @@ -45,6 +48,7 @@ public record MemoCategoryInfo( */ public static MemoCategoryInfo from(MemoCategoryQueryDslRes res) { return MemoCategoryInfo.builder() + .petId(res.petId()) .memoCategoryId(res.memoCategoryId()) .memoCategoryName(res.memoCategoryName()) .type(res.parentId() == null ? MemoCategoryType.ROOT : MemoCategoryType.SUB) @@ -58,6 +62,7 @@ public static MemoCategoryInfo from(MemoCategoryQueryDslRes res) { */ public static MemoCategoryInfo ofRootInstance(MemoCategoryQueryDslRes rootMemoCategory, List subMemoCategories) { return MemoCategoryInfo.builder() + .petId(rootMemoCategory.petId()) .memoCategoryId(rootMemoCategory.memoCategoryId()) .memoCategoryName(rootMemoCategory.memoCategoryName()) .type(MemoCategoryType.ROOT) @@ -68,12 +73,14 @@ public static MemoCategoryInfo ofRootInstance(MemoCategoryQueryDslRes rootMemoCa } public record MemoCategoryQueryDslRes( + Long petId, Long memoCategoryId, String memoCategoryName, Long parentId, Long totalMemoCount ) { - public MemoCategoryQueryDslRes(Long memoCategoryId, String memoCategoryName, Long parentId, Long totalMemoCount) { + public MemoCategoryQueryDslRes(Long petId, Long memoCategoryId, String memoCategoryName, Long parentId, Long totalMemoCount) { + this.petId = petId; this.memoCategoryId = memoCategoryId; this.memoCategoryName = memoCategoryName; this.parentId = parentId; diff --git a/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/repository/MemoCategoryQueryDslRepository.java b/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/repository/MemoCategoryQueryDslRepository.java index 462a76f9..9e5a7bd8 100644 --- a/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/repository/MemoCategoryQueryDslRepository.java +++ b/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/repository/MemoCategoryQueryDslRepository.java @@ -7,6 +7,6 @@ public interface MemoCategoryQueryDslRepository { MemoCategoryInfoDto.MemoCategoryQueryDslRes findMemoCategoryById(Long memoCategoryId); - List findRootMemoCategoryIdByPetId(List petId); + List findRootMemoCategoryIdsByPetId(List petId); List findMemoCategoriesByParent(Long parentId); } diff --git a/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/repository/MemoCategoryQueryDslRepositoryImpl.java b/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/repository/MemoCategoryQueryDslRepositoryImpl.java index 3449f9f7..52ecea82 100644 --- a/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/repository/MemoCategoryQueryDslRepositoryImpl.java +++ b/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/repository/MemoCategoryQueryDslRepositoryImpl.java @@ -20,7 +20,7 @@ public class MemoCategoryQueryDslRepositoryImpl implements MemoCategoryQueryDslR private final QPet pet = QPet.pet; @Override - public List findRootMemoCategoryIdByPetId(List petIds) { + public List findRootMemoCategoryIdsByPetId(List petIds) { return queryFactory.select(memoCategory.id) .from(memoCategory) .leftJoin(pet).on(pet.id.eq(memoCategory.pet.id)) @@ -33,10 +33,11 @@ public MemoCategoryInfoDto.MemoCategoryQueryDslRes findMemoCategoryById(Long mem return queryFactory.select( Projections.constructor( MemoCategoryInfoDto.MemoCategoryQueryDslRes.class, + memoCategory.pet.id, memoCategory.id, memoCategory.categoryName, memoCategory.parent.id, - memoCategory.id.count() + memo.id.count() )) .from(memoCategory) .leftJoin(memo).on(memo.memoCategory.id.eq(memoCategory.id)) @@ -50,10 +51,11 @@ public List findMemoCategoriesByPar return queryFactory.select( Projections.constructor( MemoCategoryInfoDto.MemoCategoryQueryDslRes.class, + memoCategory.pet.id, memoCategory.id, memoCategory.categoryName, memoCategory.parent.id, - memoCategory.id.count() + memo.id.count() )) .from(memoCategory) .leftJoin(memo).on(memo.memoCategory.id.eq(memoCategory.id)) diff --git a/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/repository/MemoQueryDslRepositoryImpl.java b/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/repository/MemoQueryDslRepositoryImpl.java index 31d8167a..1ce38706 100644 --- a/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/repository/MemoQueryDslRepositoryImpl.java +++ b/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/repository/MemoQueryDslRepositoryImpl.java @@ -49,7 +49,7 @@ public Optional findMemoAndMemoImageUrlsById(Long memoId) memo.title, memo.content, memo.createdAt, - list( + GroupBy.list( Projections.constructor( MemoInfoDto.MemoImageInfo.class, memoImage.id, diff --git a/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/service/MemoSaveService.java b/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/service/MemoSaveService.java index d49acb47..f59337ab 100644 --- a/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/service/MemoSaveService.java +++ b/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/service/MemoSaveService.java @@ -18,11 +18,11 @@ public class MemoSaveService { private final MemoCategoryRepository memoCategoryRepository; private final MemoImageRepository memoImageRepository; - public void saveMemoCategory(MemoCategory memoCategory) { - memoCategoryRepository.save(memoCategory); + public MemoCategory saveMemoCategory(MemoCategory memoCategory) { + return memoCategoryRepository.save(memoCategory); } - public void saveMemo(Memo memo) { - memoRepository.save(memo); + public Memo saveMemo(Memo memo) { + return memoRepository.save(memo); } } diff --git a/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/service/MemoSearchService.java b/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/service/MemoSearchService.java index 5fbb0459..48e4fa3d 100644 --- a/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/service/MemoSearchService.java +++ b/fitapet-domain/src/main/java/kr/co/fitapet/domain/domains/memo/service/MemoSearchService.java @@ -15,7 +15,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; -import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; @@ -34,14 +33,13 @@ public MemoCategory findMemoCategoryById(Long memoCategoryId) { } @Transactional(readOnly = true) - public List findRootMemoCategoriesIdByPetIds(List petIds) { - return memoCategoryRepository.findRootMemoCategoryIdByPetId(petIds); + public List findRootMemoCategoryIdsByPetIds(List petIds) { + return memoCategoryRepository.findRootMemoCategoryIdsByPetId(petIds); } @Transactional(readOnly = true) public MemoCategoryInfoDto.MemoCategoryInfo findMemoCategoryWithMemoCount(Long memoCategoryId) { MemoCategoryInfoDto.MemoCategoryQueryDslRes dto = memoCategoryRepository.findMemoCategoryById(memoCategoryId); - log.info("dto: {}", dto); if (dto == null) throw new GlobalErrorException(MemoErrorCode.MEMO_CATEGORY_NOT_FOUND); if (dto.parentId() == null) {