Skip to content

Commit

Permalink
Merge pull request #121 from KCY-Fit-a-Pet/feat/39
Browse files Browse the repository at this point in the history
โœจ ๋ฐ˜๋ ค๋™๋ฌผ ์ˆ˜์ •/์‚ญ์ œ API ๋ฐ ์‚ฌ์šฉ์ž ์„ค์ • ์ด๋ฆ„ ๋ฐ˜ํ™˜ API
  • Loading branch information
heejinnn authored Mar 2, 2024
2 parents 36dd4cc + 0a3ac58 commit 41475f9
Show file tree
Hide file tree
Showing 25 changed files with 350 additions and 20 deletions.
1 change: 1 addition & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ jobs:
- name: docker image build & push
run: |
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
cat ${{ secrets.IOS_FIREBASE_ADMINSDK }} > fitapet-app-external-api/src/main/resources/fitapet-ios-firebase-adminsdk-ethnn-6ec10fe329.json
docker build -t ${{ secrets.DOCKER_NICKNAME }}/fitapet:latest .
docker push ${{ secrets.DOCKER_NICKNAME }}/fitapet:latest
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package kr.co.fitapet.api.apis.notification.controller;

import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Tag(name = "์•Œ๋ฆผ API")
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v2/notifications")
public class NotificationApi {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package kr.co.fitapet.api.apis.notification.usecase;

import kr.co.fitapet.common.annotation.UseCase;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@UseCase
@RequiredArgsConstructor
public class NotificationUseCase {
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

@Tag(name = "๋ฐ˜๋ ค๋™๋ฌผ ๊ด€๋ฆฌ API")
@RestController
@RequestMapping("/api/v2/users/{user_id}/pets")
@RequestMapping("/api/v2/pets")
@RequiredArgsConstructor
@Slf4j
public class PetApi {
Expand All @@ -44,34 +44,59 @@ public class PetApi {
@PostMapping("")
@PreAuthorize("isAuthenticated()")
public ResponseEntity<?> savePet(@RequestBody @Valid PetSaveReq req, @AuthenticationPrincipal CustomUserDetails user) {
petUseCase.savePet(req.toPetEntity(), user.getUserId());
petUseCase.savePet(req.toEntity(), user.getUserId());

return ResponseEntity.ok(SuccessResponse.noContent());
}

@Operation(summary = "๊ด€๋ฆฌ ์ค‘์ธ ๋ฐ˜๋ ค๋™๋ฌผ ๋ฆฌ์ŠคํŠธ ์กฐํšŒ")
@GetMapping("")
@PreAuthorize("isAuthenticated() and #userId == principal.userId")
public ResponseEntity<?> getPets(@PathVariable("user_id") Long userId) {
PetInfoRes pets = petUseCase.getPets(userId);
@PreAuthorize("isAuthenticated()")
public ResponseEntity<?> getPets(@AuthenticationPrincipal CustomUserDetails user) {
PetInfoRes pets = petUseCase.findPets(user.getUserId());
return ResponseEntity.ok(SuccessResponse.from("pets", pets.getPets()));
}

@Operation(summary = "๊ด€๋ฆฌ ์ค‘์ธ ๋ฐ˜๋ ค๋™๋ฌผ ๋ชฉ๋ก ์กฐํšŒ")
@Parameter(name = "user_id", description = "์กฐํšŒํ•  ์œ ์ € ID", required = true)
@Operation(summary = "๊ด€๋ฆฌ ์ค‘์ธ ๋ฐ˜๋ ค๋™๋ฌผ ์š”์•ฝ ๋ชฉ๋ก ์กฐํšŒ")
@GetMapping("/summary")
@PreAuthorize("isAuthenticated() and #userId == principal.userId")
public ResponseEntity<?> findPets(@PathVariable("user_id") Long userId) {
List<?> pets = petUseCase.findPetsSummaryByUserId(userId).getPets();
@PreAuthorize("isAuthenticated()")
public ResponseEntity<?> findPets(@AuthenticationPrincipal CustomUserDetails user) {
List<?> pets = petUseCase.findPetsSummaryByUserId(user.getUserId()).getPets();
return ResponseEntity.ok(SuccessResponse.from("pets", pets));
}

@Operation(summary = "๋ฐ˜๋ ค๋™๋ฌผ ์ •๋ณด ์กฐํšŒ")
@GetMapping("/{pet_id}")
@PreAuthorize("isAuthenticated() and @managerAuthorize.isManager(principal.userId, #petId)")
public ResponseEntity<?> getPet(@PathVariable("pet_id") Long petId) {
return ResponseEntity.ok(SuccessResponse.from("pet", petUseCase.findPet(petId)));
}

@Operation(summary = "๋ฐ˜๋ ค๋™๋ฌผ ์ผ€์–ด ์นดํ…Œ๊ณ ๋ฆฌ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ")
@Parameter(name = "user_id", description = "์กฐํšŒํ•  ์œ ์ € ID", in = ParameterIn.PATH, required = true)
@PostMapping("/categories-check")
@PreAuthorize("isAuthenticated() and #userId == principal.userId")
public ResponseEntity<?> checkCategoryExist(@PathVariable("user_id") Long userId, @RequestBody @Valid CareCategoryInfo.CareCategoryExistRequest request) {
List<?> result = petUseCase.checkCategoryExist(userId, request.categoryName(), request.pets());
@PreAuthorize("isAuthenticated()")
public ResponseEntity<?> checkCategoryExist(@AuthenticationPrincipal CustomUserDetails user, @RequestBody @Valid CareCategoryInfo.CareCategoryExistRequest request) {
List<?> result = petUseCase.checkCategoryExist(user.getUserId(), request.categoryName(), request.pets());
return ResponseEntity.ok(SuccessResponse.from("categories", result));
}

@Operation(summary = "๋ฐ˜๋ ค๋™๋ฌผ ์ •๋ณด ์ˆ˜์ •")
@Parameter(name = "pet_id", description = "์ˆ˜์ •ํ•  ๋ฐ˜๋ ค๋™๋ฌผ ID", in = ParameterIn.PATH, required = true)
@PutMapping("/{pet_id}")
@PreAuthorize("isAuthenticated() and @managerAuthorize.isMaster(principal.userId, #petId)")
public ResponseEntity<?> updatePet(@PathVariable("pet_id") Long petId, @RequestBody @Valid PetSaveReq req) {
petUseCase.updatePet(petId, req);
return ResponseEntity.ok(SuccessResponse.noContent());
}

@Operation(summary = "๋ฐ˜๋ ค๋™๋ฌผ ์‚ญ์ œ")
@Parameter(name = "pet_id", description = "์‚ญ์ œํ•  ๋ฐ˜๋ ค๋™๋ฌผ ID", in = ParameterIn.PATH, required = true)
@DeleteMapping("/{pet_id}")
@PreAuthorize("isAuthenticated() and @managerAuthorize.isMaster(principal.userId, #petId)")
public ResponseEntity<?> deletePet(@PathVariable("pet_id") Long petId, @AuthenticationPrincipal CustomUserDetails user) {
petUseCase.deletePet(petId, user.getUserId());
return ResponseEntity.ok(SuccessResponse.noContent());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package kr.co.fitapet.api.apis.pet.dto;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import kr.co.fitapet.domain.domains.pet.domain.Pet;
import kr.co.fitapet.domain.domains.pet.type.GenderType;

import java.time.LocalDate;
import java.util.Objects;

public record PetDetailRes(
Long id,
String petName,
String petProfileImage,
GenderType gender,
Boolean neutered,
@JsonSerialize(using = LocalDateSerializer.class)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "Asia/Seoul")
LocalDate birthdate,
String species,
String feed
) {
public static PetDetailRes from(Pet pet) {
return new PetDetailRes(
pet.getId(),
pet.getPetName(),
Objects.toString(pet.getPetProfileImg(), ""),
pet.getGender(),
pet.isNeutered(),
pet.getBirthdate(),
pet.getSpecies(),
Objects.toString(pet.getFeed(), "")
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package kr.co.fitapet.api.apis.pet.mapper;

import kr.co.fitapet.common.annotation.Mapper;
import kr.co.fitapet.domain.domains.memo.service.MemoSearchService;
import kr.co.fitapet.domain.domains.pet.domain.Pet;
import kr.co.fitapet.domain.domains.pet.service.PetSearchService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

@Slf4j
@Mapper
@RequiredArgsConstructor
public class PetImageSearchMapper {
private final PetSearchService petSearchService;
private final MemoSearchService memoSearchService;

/**
* ๋ฐ˜๋ ค๋™๋ฌผ ํ”„๋กœํ•„ ์ด๋ฏธ์ง€์™€ ๋ฉ”๋ชจ์— ๋“ฑ๋ก๋œ ๋ชจ๋“  ์ด๋ฏธ์ง€ ์กฐํšŒ
*/
@Transactional(readOnly = true)
public List<String> findPetProfileImageAndMemoImageUrls(Long petId) {
List<String> images = new ArrayList<>();

Pet pet = petSearchService.findPetById(petId);
if (pet.getPetProfileImg() != null)
images.add(pet.getPetProfileImg());
List<Long> memoIds = memoSearchService.findMemoIdsByPetId(pet.getId());
List<String> memoImageUrls = memoSearchService.findMemoImageUrlsByMemoIds(memoIds);
images.addAll(memoImageUrls);

return images;
}
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,42 @@
package kr.co.fitapet.api.apis.pet.usecase;

import kr.co.fitapet.api.apis.pet.dto.PetDetailRes;
import kr.co.fitapet.api.apis.pet.helper.PetCareHelper;
import kr.co.fitapet.api.apis.pet.mapper.PetImageSearchMapper;
import kr.co.fitapet.api.apis.pet.mapper.PetManagerMapper;
import kr.co.fitapet.api.common.security.jwt.exception.AuthErrorCode;
import kr.co.fitapet.api.common.security.jwt.exception.AuthErrorException;
import kr.co.fitapet.common.annotation.UseCase;
import kr.co.fitapet.domain.domains.care.service.CareSearchService;
import kr.co.fitapet.domain.domains.memo.domain.MemoCategory;
import kr.co.fitapet.domain.domains.pet.domain.Pet;
import kr.co.fitapet.domain.domains.pet.dto.PetInfoRes;
import kr.co.fitapet.domain.domains.pet.dto.PetSaveReq;
import kr.co.fitapet.domain.domains.pet.service.PetDeleteService;
import kr.co.fitapet.domain.domains.pet.service.PetSaveService;
import kr.co.fitapet.domain.domains.pet.service.PetSearchService;
import kr.co.fitapet.infra.common.event.ObjectStorageImageDeleteEvent;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@UseCase
@Slf4j
@RequiredArgsConstructor
public class PetUseCase {
private final PetManagerMapper petManagerMapper;
private final PetImageSearchMapper petImageSearchMapper;

private final PetSearchService petSearchService;
private final PetSaveService petSaveService;
private final PetDeleteService petDeleteService;
private final PetCareHelper petCareHelper;

private final ApplicationEventPublisher eventPublisher;

@Transactional
public void savePet(Pet pet, Long memberId) {
pet = petSaveService.savePet(pet);
Expand All @@ -45,8 +60,31 @@ public List<?> checkCategoryExist(Long userId, String categoryName, List<Long> p
}

@Transactional(readOnly = true)
public PetInfoRes getPets(Long userId) {
public PetInfoRes findPets(Long userId) {
List<Pet> pets = petManagerMapper.findAllManagerByMemberId(userId);
return PetInfoRes.ofPetInfo(pets);
}

@Transactional(readOnly = true)
public PetDetailRes findPet(Long petId) {
Pet pet = petSearchService.findPetById(petId);
return PetDetailRes.from(pet);
}

@Transactional
public void updatePet(Long petId, PetSaveReq req) {
Pet pet = petSearchService.findPetById(petId);
pet.updatePet(req.toEntity());
petSaveService.savePet(pet);
}

@Transactional
@CacheEvict(value = "master", key = "#masterId + '@' + #petId", cacheManager = "managerCacheManager")
public void deletePet(Long petId, Long masterId) {
log.info("deletePet: {}", petId);
List<String> deleteImageUrls = petImageSearchMapper.findPetProfileImageAndMemoImageUrls(petId);
log.info("deleteImageUrls: {}", deleteImageUrls);
petDeleteService.deletePet(petId);
eventPublisher.publishEvent(ObjectStorageImageDeleteEvent.valueOf(deleteImageUrls));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ public ResponseEntity<?> getProfile(@PathVariable("id") Long id) {
return ResponseEntity.ok(SuccessResponse.from(member));
}

@Operation(summary = "์‚ฌ์šฉ์ž ์„ค์ • ์ด๋ฆ„ ์กฐํšŒ")
@Parameter(name = "id", description = "์กฐํšŒํ•  ํ”„๋กœํ•„ ID", in = ParameterIn.PATH, required = true)
@GetMapping("/{id}/name")
@PreAuthorize("isAuthenticated()")
public ResponseEntity<?> getUserName(@PathVariable("id") Long id) {
return ResponseEntity.ok(SuccessResponse.from("name", memberAccountUseCase.findUserName(id)));
}

@Operation(summary = "๋””๋ฐ”์ด์Šค ํ† ํฐ ๋“ฑ๋ก")
@PostMapping("/{id}/device-token")
@PreAuthorize("isAuthenticated() and #id == principal.userId")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ public AccountProfileRes getProfile(Long userId) {
return AccountProfileRes.from(member);
}

@Transactional(readOnly = true)
public String findUserName(Long userId) {
return memberSearchService.findById(userId).getName();
}

@Transactional
public void registerDeviceToken(Long memberId, DeviceTokenReq req) {
Member member = memberSearchService.findById(memberId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

Expand All @@ -17,7 +18,7 @@
public class NotificationTestController {
private final NotificationTestService notificationTestService;

@RequestMapping("/push")
@GetMapping("/push")
@PreAuthorize("isAuthenticated()")
public void push(@AuthenticationPrincipal CustomUserDetails user) {
notificationTestService.push(user.getUserId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,5 @@ public Map<Long, LocalDateTime> findAll(String petId) {
public void delete(String petId, Long invitedId) {
ops.delete(KEY + ":" + petId, invitedId);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,18 @@ public void save(Long invitedId, Long petId) {
@Override
public List<InvitationDto> findAll(Long petId) {
Map<Long, LocalDateTime> all = managerInvitationRepository.findAll(petId.toString());

all.forEach((k, v) -> {
boolean expired = v.isBefore(LocalDateTime.now());

if (expired) {
log.warn("manager invitation expired. about User : {}", k);
delete(k, petId);
}
});

return all.entrySet().stream()
.filter(entry -> !entry.getValue().isBefore(LocalDateTime.now()))
.map(entry -> InvitationDto.of(petId, entry.getKey(), entry.getValue()))
.toList();
}
Expand All @@ -44,7 +55,14 @@ public boolean expired(Long invitedId, Long petId) {
LocalDateTime ttl = managerInvitationRepository.getTtl(petId.toString(), invitedId);
log.info("manager invitation ttl : {}", ttl);

return ttl.isBefore(LocalDateTime.now());
boolean expired = ttl.isBefore(LocalDateTime.now());

if (expired) {
log.warn("manager invitation expired. about User : {}", invitedId);
delete(invitedId, petId);
return true;
}
return false;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package kr.co.fitapet.domain.domains.memo.repository;

import java.util.List;

public interface MemoImageQueryDslRepository {
List<String> findMemoImageUrlsByMemoIds(List<Long> memoIds);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package kr.co.fitapet.domain.domains.memo.repository;

import com.querydsl.jpa.impl.JPAQueryFactory;
import kr.co.fitapet.domain.domains.memo.domain.QMemo;
import kr.co.fitapet.domain.domains.memo.domain.QMemoImage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
@RequiredArgsConstructor
@Slf4j
public class MemoImageQueryDslRepositoryImpl implements MemoImageQueryDslRepository {
private final JPAQueryFactory queryFactory;
private final QMemo memo = QMemo.memo;
private final QMemoImage memoImage = QMemoImage.memoImage;

@Override
public List<String> findMemoImageUrlsByMemoIds(List<Long> memoIds) {
return queryFactory
.select(memoImage.imgUrl)
.from(memoImage)
.where(memoImage.memo.id.in(memoIds))
.fetch();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import java.util.List;

public interface MemoImageRepository extends ExtendedRepository<MemoImage, Long> {
public interface MemoImageRepository extends ExtendedRepository<MemoImage, Long>, MemoImageQueryDslRepository {
List<MemoImage> findByMemo_Id(Long memoId);
@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query("delete from MemoImage mi where mi in :memoImages")
Expand Down
Loading

0 comments on commit 41475f9

Please sign in to comment.