diff --git a/.gitmodules b/.gitmodules index 1787b1de..f06fbc70 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "src/main/resources/config"] path = src/main/resources/config - url = https://github.com/team-moabam/config.git + url = https://github.com/team-moabam/config.git \ No newline at end of file diff --git a/infra/docker-compose-dev.yml b/infra/docker-compose-dev.yml index 6419c55b..83677dba 100644 --- a/infra/docker-compose-dev.yml +++ b/infra/docker-compose-dev.yml @@ -1,77 +1,77 @@ version: '3.7' services: - nginx: - image: nginx:latest - container_name: nginx - platform: linux/arm64/v8 - restart: always - ports: - - "80:80" - # - "443:443" - volumes: - - ./nginx/nginx.conf:/etc/nginx/nginx.conf - - ./nginx/conf.d:/etc/nginx/conf.d - # - ./nginx/certbot/conf:/etc/letsencrypt - # - ./nginx/certbot/www:/var/www/certbot - - ../logs/nginx:/var/log/nginx - certbot: - image: certbot/certbot:latest - container_name: certbot - platform: linux/arm64 - restart: unless-stopped - volumes: - - ./nginx/certbot/conf:/etc/letsencrypt - - ./nginx/certbot/www:/var/www/certbot - entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'" - moabam-blue: - image: ${DOCKER_HUB_USERNAME}/${DOCKER_HUB_REPOSITORY}:${DOCKER_HUB_TAG} - container_name: ${BLUE_CONTAINER} - restart: unless-stopped - expose: - - ${SERVER_PORT} - depends_on: - - redis - - mysql - environment: - SPRING_ACTIVE_PROFILES: ${SPRING_ACTIVE_PROFILES} - moabam-green: - image: ${DOCKER_HUB_USERNAME}/${DOCKER_HUB_REPOSITORY}:${DOCKER_HUB_TAG} - container_name: ${GREEN_CONTAINER} - restart: unless-stopped - expose: - - ${SERVER_PORT} - depends_on: - - redis - - mysql - environment: - SPRING_ACTIVE_PROFILES: ${SPRING_ACTIVE_PROFILES} - redis: - image: redis:alpine - container_name: redis - platform: linux/arm64 - restart: always - command: redis-server - ports: - - "6379:6379" - volumes: - - ./data/redis:/data - mysql: - image: mysql:8.0.33 - container_name: mysql - platform: linux/arm64/v8 - restart: always - ports: - - "3306:3306" - environment: - MYSQL_DATABASE: ${DEV_MYSQL_DATABASE} - MYSQL_USERNAME: ${DEV_MYSQL_USERNAME} - MYSQL_ROOT_PASSWORD: ${DEV_MYSQL_PASSWORD} - TZ: Asia/Seoul - command: - - --character-set-server=utf8mb4 - - --collation-server=utf8mb4_unicode_ci - - --skip-character-set-client-handshake - volumes: - - ./data/mysql:/var/lib/mysql - - ./mysql/initdb.d:/docker-entrypoint-initdb.d + nginx: + image: nginx:latest + container_name: nginx + platform: linux/arm64/v8 + restart: always + ports: + - "80:80" + # - "443:443" + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf + - ./nginx/conf.d:/etc/nginx/conf.d + # - ./nginx/certbot/conf:/etc/letsencrypt + # - ./nginx/certbot/www:/var/www/certbot + - ../logs/nginx:/var/log/nginx + certbot: + image: certbot/certbot:latest + container_name: certbot + platform: linux/arm64 + restart: unless-stopped + volumes: + - ./nginx/certbot/conf:/etc/letsencrypt + - ./nginx/certbot/www:/var/www/certbot + entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'" + moabam-blue: + image: ${DOCKER_HUB_USERNAME}/${DOCKER_HUB_REPOSITORY}:${DOCKER_HUB_TAG} + container_name: ${BLUE_CONTAINER} + restart: unless-stopped + expose: + - ${SERVER_PORT} + depends_on: + - redis + - mysql + environment: + SPRING_ACTIVE_PROFILES: ${SPRING_ACTIVE_PROFILES} + moabam-green: + image: ${DOCKER_HUB_USERNAME}/${DOCKER_HUB_REPOSITORY}:${DOCKER_HUB_TAG} + container_name: ${GREEN_CONTAINER} + restart: unless-stopped + expose: + - ${SERVER_PORT} + depends_on: + - redis + - mysql + environment: + SPRING_ACTIVE_PROFILES: ${SPRING_ACTIVE_PROFILES} + redis: + image: redis:alpine + container_name: redis + platform: linux/arm64 + restart: always + command: redis-server + ports: + - "6379:6379" + volumes: + - ./data/redis:/data + mysql: + image: mysql:8.0.33 + container_name: mysql + platform: linux/arm64/v8 + restart: always + ports: + - "3306:3306" + environment: + MYSQL_DATABASE: ${DEV_MYSQL_DATABASE} + MYSQL_USERNAME: ${DEV_MYSQL_USERNAME} + MYSQL_ROOT_PASSWORD: ${DEV_MYSQL_PASSWORD} + TZ: Asia/Seoul + command: + - --character-set-server=utf8mb4 + - --collation-server=utf8mb4_unicode_ci + - --skip-character-set-client-handshake + volumes: + - ./data/mysql:/var/lib/mysql + - ./mysql/initdb.d:/docker-entrypoint-initdb.d diff --git a/src/main/java/com/moabam/api/application/member/MemberReadService.java b/src/main/java/com/moabam/api/application/member/MemberReadService.java new file mode 100644 index 00000000..973b9c56 --- /dev/null +++ b/src/main/java/com/moabam/api/application/member/MemberReadService.java @@ -0,0 +1,90 @@ +package com.moabam.api.application.member; + +import static com.moabam.global.error.model.ErrorMessage.*; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import org.springframework.stereotype.Service; + +import com.moabam.api.domain.member.Member; +import com.moabam.api.domain.member.repository.MemberRepository; +import com.moabam.api.domain.member.repository.MemberSearchRepository; +import com.moabam.api.domain.room.Participant; +import com.moabam.api.domain.room.repository.ParticipantSearchRepository; +import com.moabam.api.dto.member.MemberInfo; +import com.moabam.api.dto.member.MemberInfoSearchResponse; +import com.moabam.global.error.exception.BadRequestException; +import com.moabam.global.error.exception.ConflictException; +import com.moabam.global.error.exception.NotFoundException; + +import io.micrometer.common.util.StringUtils; +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class MemberReadService { + + private final MemberRepository memberRepository; + private final MemberSearchRepository memberSearchRepository; + private final ParticipantSearchRepository participantSearchRepository; + + public Member readMember(Long id) { + return memberSearchRepository.findMember(id) + .orElseThrow(() -> new NotFoundException(MEMBER_NOT_FOUND)); + } + + public Optional findMember(String socialId) { + return memberRepository.findBySocialId(socialId); + } + + public List getRoomMembers(List memberIds) { + return memberRepository.findAllById(memberIds); + } + + public void validateMemberToDelete(Long memberId) { + List participants = memberSearchRepository.findParticipantByMemberId(memberId); + + if (!participants.isEmpty()) { + throw new NotFoundException(MEMBER_NOT_FOUND); + } + } + + public MemberInfoSearchResponse readMemberInfos(Long searchId, boolean isMe) { + List memberInfos = memberSearchRepository.findMemberAndBadges(searchId, isMe); + + if (memberInfos.isEmpty()) { + throw new BadRequestException(MEMBER_NOT_FOUND); + } + + return MemberMapper.toMemberInfoSearchResponse(memberInfos); + } + + public List findAllMembers() { + return memberSearchRepository.findAllMembers(); + } + + public void validateParticipants(Long memberId) { + List participants = participantSearchRepository.findAllByMemberIdParticipant(memberId); + + if (!participants.isEmpty()) { + throw new BadRequestException(NEED_TO_EXIT_ALL_ROOMS); + } + } + + public void validateNickname(String myName, String nickname) { + if (Objects.isNull(nickname)) { + return; + } + if (StringUtils.isBlank(nickname)) { + throw new NotFoundException(NICKNAME_NOT_NULL); + } + if (!memberRepository.existsByNickname(nickname)) { + return; + } + if (!myName.equals(nickname)) { + throw new ConflictException(NICKNAME_CONFLICT); + } + } +} diff --git a/src/main/java/com/moabam/api/application/member/MemberService.java b/src/main/java/com/moabam/api/application/member/MemberService.java index 7c900af5..7b164261 100644 --- a/src/main/java/com/moabam/api/application/member/MemberService.java +++ b/src/main/java/com/moabam/api/application/member/MemberService.java @@ -1,30 +1,17 @@ package com.moabam.api.application.member; -import static com.moabam.global.error.model.ErrorMessage.*; - import java.util.List; import java.util.Objects; import java.util.Optional; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.moabam.api.application.auth.mapper.AuthMapper; import com.moabam.api.application.ranking.RankingService; -import com.moabam.api.domain.item.Inventory; -import com.moabam.api.domain.item.Item; -import com.moabam.api.domain.item.repository.InventoryRepository; -import com.moabam.api.domain.item.repository.ItemRepository; import com.moabam.api.domain.member.Member; -import com.moabam.api.domain.member.repository.MemberRepository; -import com.moabam.api.domain.member.repository.MemberSearchRepository; -import com.moabam.api.domain.room.Participant; -import com.moabam.api.domain.room.repository.ParticipantRepository; -import com.moabam.api.domain.room.repository.ParticipantSearchRepository; import com.moabam.api.dto.auth.AuthorizationTokenInfoResponse; import com.moabam.api.dto.auth.LoginResponse; -import com.moabam.api.dto.member.MemberInfo; import com.moabam.api.dto.member.MemberInfoResponse; import com.moabam.api.dto.member.MemberInfoSearchResponse; import com.moabam.api.dto.member.ModifyMemberRequest; @@ -32,13 +19,7 @@ import com.moabam.api.dto.ranking.UpdateRanking; import com.moabam.api.infrastructure.fcm.FcmService; import com.moabam.global.auth.model.AuthMember; -import com.moabam.global.common.util.BaseDataCode; -import com.moabam.global.common.util.ClockHolder; -import com.moabam.global.error.exception.BadRequestException; -import com.moabam.global.error.exception.ConflictException; -import com.moabam.global.error.exception.NotFoundException; -import io.micrometer.common.util.StringUtils; import lombok.RequiredArgsConstructor; @Service @@ -46,52 +27,35 @@ @RequiredArgsConstructor public class MemberService { + private final MemberReadService memberReadService; + private final MemberWriteService memberWriteService; private final RankingService rankingService; private final FcmService fcmService; - private final MemberRepository memberRepository; - private final InventoryRepository inventoryRepository; - private final ItemRepository itemRepository; - private final MemberSearchRepository memberSearchRepository; - private final ParticipantSearchRepository participantSearchRepository; - private final ParticipantRepository participantRepository; - private final ClockHolder clockHolder; public Member findMember(Long memberId) { - return memberSearchRepository.findMember(memberId) - .orElseThrow(() -> new NotFoundException(MEMBER_NOT_FOUND)); + return memberReadService.readMember(memberId); } @Transactional public LoginResponse login(AuthorizationTokenInfoResponse authorizationTokenInfoResponse) { - Optional member = memberRepository.findBySocialId(String.valueOf(authorizationTokenInfoResponse.id())); + Optional member = memberReadService.findMember(String.valueOf(authorizationTokenInfoResponse.id())); Member loginMember = member.orElseGet(() -> signUp(authorizationTokenInfoResponse.id())); return AuthMapper.toLoginResponse(loginMember, member.isEmpty()); } public List getRoomMembers(List memberIds) { - return memberRepository.findAllById(memberIds); + return memberReadService.getRoomMembers(memberIds); } public void validateMemberToDelete(Long memberId) { - List participants = memberSearchRepository.findParticipantByMemberId(memberId); - - if (!participants.isEmpty()) { - throw new NotFoundException(MEMBER_NOT_FOUND); - } + memberReadService.validateMemberToDelete(memberId); } @Transactional public void delete(Member member) { - List participants = participantSearchRepository.findAllByMemberIdParticipant(member.getId()); - - if (!participants.isEmpty()) { - throw new BadRequestException(NEED_TO_EXIT_ALL_ROOMS); - } - - member.delete(clockHolder.dateTime()); - memberRepository.flush(); - memberRepository.delete(member); + memberReadService.validateParticipants(member.getId()); + memberWriteService.delete(member); rankingService.removeRanking(MemberMapper.toRankingInfo(member)); fcmService.deleteTokenByMemberId(member.getId()); } @@ -103,31 +67,20 @@ public MemberInfoResponse searchInfo(AuthMember authMember, Long memberId) { if (!isMe) { searchId = memberId; } - MemberInfoSearchResponse memberInfoSearchResponse = findMemberInfo(searchId, isMe); + MemberInfoSearchResponse memberInfoSearchResponse = memberReadService.readMemberInfos(searchId, isMe); return MemberMapper.toMemberInfoResponse(memberInfoSearchResponse); } @Transactional public void modifyInfo(AuthMember authMember, ModifyMemberRequest modifyMemberRequest, String newProfileUri) { - validateNickname(modifyMemberRequest.nickname()); - Member member = memberSearchRepository.findMember(authMember.id()) - .orElseThrow(() -> new NotFoundException(MEMBER_NOT_FOUND)); + Member member = memberReadService.readMember(authMember.id()); + memberReadService.validateNickname(member.getNickname(), modifyMemberRequest.nickname()); RankingInfo beforeInfo = MemberMapper.toRankingInfo(member); - member.changeNickName(modifyMemberRequest.nickname()); - - boolean nickNameChanged = member.changeNickName(modifyMemberRequest.nickname()); - member.changeIntro(modifyMemberRequest.intro()); - member.changeProfileUri(newProfileUri); - memberRepository.save(member); - + memberWriteService.changeInfo(member, modifyMemberRequest, newProfileUri); RankingInfo afterInfo = MemberMapper.toRankingInfo(member); rankingService.changeInfos(beforeInfo, afterInfo); - - if (nickNameChanged) { - changeNickname(authMember.id(), modifyMemberRequest.nickname()); - } } public UpdateRanking getRankingInfo(AuthMember authMember) { @@ -136,68 +89,11 @@ public UpdateRanking getRankingInfo(AuthMember authMember) { return MemberMapper.toUpdateRanking(member); } - @Scheduled(cron = "0 11 * * * *") - public void updateAllRanking() { - List members = memberSearchRepository.findAllMembers(); - List updateRankings = members.stream() - .map(MemberMapper::toUpdateRanking) - .toList(); - - rankingService.updateScores(updateRankings); - } - - private void changeNickname(Long memberId, String changedName) { - List participants = participantSearchRepository.findAllRoomMangerByMemberId(memberId); - - for (Participant participant : participants) { - participant.getRoom().changeManagerNickname(changedName); - } - } - - private void validateNickname(String nickname) { - if (Objects.isNull(nickname)) { - return; - } - if (StringUtils.isEmpty(nickname) && memberRepository.existsByNickname(nickname)) { - throw new ConflictException(NICKNAME_CONFLICT); - } - } - private Member signUp(Long socialId) { - Member member = MemberMapper.toMember(socialId); - Member savedMember = memberRepository.save(member); - saveMyEgg(savedMember); + Member member = memberWriteService.signUp(socialId); rankingService.addRanking(MemberMapper.toRankingInfo(member), member.getTotalCertifyCount()); - return savedMember; - } - - private void saveMyEgg(Member member) { - List items = getBasicEggs(); - List inventories = items.stream() - .map(item -> MemberMapper.toInventory(member.getId(), item)) - .toList(); - inventoryRepository.saveAll(inventories); - } - - private List getBasicEggs() { - List items = itemRepository.findAllById(List.of(BaseDataCode.MORNING_EGG, BaseDataCode.NIGHT_EGG)); - - if (items.isEmpty()) { - throw new BadRequestException(BASIC_SKIN_NOT_FOUND); - } - - return items; - } - - private MemberInfoSearchResponse findMemberInfo(Long searchId, boolean isMe) { - List memberInfos = memberSearchRepository.findMemberAndBadges(searchId, isMe); - - if (memberInfos.isEmpty()) { - throw new BadRequestException(MEMBER_NOT_FOUND); - } - - return MemberMapper.toMemberInfoSearchResponse(memberInfos); + return member; } private boolean confirmMe(Long myId, Long memberId) { diff --git a/src/main/java/com/moabam/api/application/member/MemberWriteService.java b/src/main/java/com/moabam/api/application/member/MemberWriteService.java new file mode 100644 index 00000000..c9e4c3b9 --- /dev/null +++ b/src/main/java/com/moabam/api/application/member/MemberWriteService.java @@ -0,0 +1,89 @@ +package com.moabam.api.application.member; + +import static com.moabam.global.error.model.ErrorMessage.*; + +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.moabam.api.domain.item.Inventory; +import com.moabam.api.domain.item.Item; +import com.moabam.api.domain.item.repository.InventoryRepository; +import com.moabam.api.domain.item.repository.ItemRepository; +import com.moabam.api.domain.member.Member; +import com.moabam.api.domain.member.repository.MemberRepository; +import com.moabam.api.domain.room.Participant; +import com.moabam.api.domain.room.repository.ParticipantSearchRepository; +import com.moabam.api.dto.member.ModifyMemberRequest; +import com.moabam.global.common.util.BaseDataCode; +import com.moabam.global.common.util.ClockHolder; +import com.moabam.global.error.exception.BadRequestException; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class MemberWriteService { + + private final MemberRepository memberRepository; + private final ItemRepository itemRepository; + private final InventoryRepository inventoryRepository; + private final ParticipantSearchRepository participantSearchRepository; + + private final ClockHolder clockHolder; + + public Member signUp(Long socialId) { + Member savedMember = memberRepository.save(MemberMapper.toMember(socialId)); + saveMyEgg(savedMember); + + return savedMember; + } + + public void delete(Member member) { + member.delete(clockHolder.dateTime()); + memberRepository.delete(member); + memberRepository.flush(); + } + + public void softDelete(Member member) { + member.delete(clockHolder.dateTime()); + memberRepository.flush(); + } + + public void changeInfo(Member member, ModifyMemberRequest modifyMemberRequest, String newProfileUri) { + boolean nickNameChanged = member.changeNickName(modifyMemberRequest.nickname()); + member.changeIntro(modifyMemberRequest.intro()); + member.changeProfileUri(newProfileUri); + memberRepository.save(member); + + if (nickNameChanged) { + changeNickname(member.getId(), modifyMemberRequest.nickname()); + } + } + + private void saveMyEgg(Member member) { + List items = getBasicEggs(); + List inventories = items.stream() + .map(item -> MemberMapper.toInventory(member.getId(), item)) + .toList(); + inventoryRepository.saveAll(inventories); + } + + private List getBasicEggs() { + List items = itemRepository.findAllById(List.of(BaseDataCode.MORNING_EGG, BaseDataCode.NIGHT_EGG)); + + if (items.isEmpty()) { + throw new BadRequestException(BASIC_SKIN_NOT_FOUND); + } + + return items; + } + + private void changeNickname(Long memberId, String changedName) { + List participants = participantSearchRepository.findAllRoomMangerByMemberId(memberId); + + for (Participant participant : participants) { + participant.getRoom().changeManagerNickname(changedName); + } + } +} diff --git a/src/main/java/com/moabam/api/application/ranking/RankingMapper.java b/src/main/java/com/moabam/api/application/ranking/RankingMapper.java index 5d17e5d1..7e6886f5 100644 --- a/src/main/java/com/moabam/api/application/ranking/RankingMapper.java +++ b/src/main/java/com/moabam/api/application/ranking/RankingMapper.java @@ -23,9 +23,9 @@ public static TopRankingInfo topRankingResponse(int rank, long score, RankingInf .build(); } - public static TopRankingInfo topRankingResponse(int rank, UpdateRanking updateRanking) { + public static TopRankingInfo topRankingResponse(Long rank, UpdateRanking updateRanking) { return TopRankingInfo.builder() - .rank(rank) + .rank(rank.intValue()) .score(updateRanking.score()) .nickname(updateRanking.rankingInfo().nickname()) .image(updateRanking.rankingInfo().image()) diff --git a/src/main/java/com/moabam/api/application/ranking/RankingReadService.java b/src/main/java/com/moabam/api/application/ranking/RankingReadService.java new file mode 100644 index 00000000..f522e569 --- /dev/null +++ b/src/main/java/com/moabam/api/application/ranking/RankingReadService.java @@ -0,0 +1,51 @@ +package com.moabam.api.application.ranking; + +import static java.util.Objects.*; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.springframework.data.redis.core.ZSetOperations; +import org.springframework.stereotype.Service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.moabam.api.dto.ranking.RankingInfo; +import com.moabam.api.dto.ranking.TopRankingInfo; +import com.moabam.api.infrastructure.redis.ZSetRedisRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class RankingReadService { + + private static final int START_INDEX = 0; + private static final int LIMIT_INDEX = 9; + + private final ObjectMapper objectMapper; + private final ZSetRedisRepository zSetRedisRepository; + + public Long readRank(String key, RankingInfo rankingInfo) { + return zSetRedisRepository.reverseRank(key, rankingInfo) + 1; + } + + public List readTopRankings(String key) { + Set> topRankings = zSetRedisRepository.rangeJson(key, START_INDEX, + LIMIT_INDEX); + + Set scoreSet = new HashSet<>(); + List topRankingInfo = new ArrayList<>(); + + for (ZSetOperations.TypedTuple topRanking : topRankings) { + long score = requireNonNull(topRanking.getScore()).longValue(); + scoreSet.add(score); + + RankingInfo rankingInfo = objectMapper.convertValue(topRanking.getValue(), RankingInfo.class); + topRankingInfo.add(RankingMapper.topRankingResponse(scoreSet.size(), score, rankingInfo)); + } + + return topRankingInfo; + } +} diff --git a/src/main/java/com/moabam/api/application/ranking/RankingService.java b/src/main/java/com/moabam/api/application/ranking/RankingService.java index 4b7b1e9e..905ffa7c 100644 --- a/src/main/java/com/moabam/api/application/ranking/RankingService.java +++ b/src/main/java/com/moabam/api/application/ranking/RankingService.java @@ -1,23 +1,19 @@ package com.moabam.api.application.ranking; -import static java.util.Objects.*; - -import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.Set; -import org.springframework.data.redis.core.ZSetOperations; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.moabam.api.application.member.MemberMapper; +import com.moabam.api.application.member.MemberReadService; +import com.moabam.api.domain.member.Member; import com.moabam.api.dto.ranking.RankingInfo; import com.moabam.api.dto.ranking.TopRankingInfo; import com.moabam.api.dto.ranking.TopRankingResponse; import com.moabam.api.dto.ranking.UpdateRanking; -import com.moabam.api.infrastructure.redis.ZSetRedisRepository; import lombok.RequiredArgsConstructor; @@ -26,61 +22,55 @@ public class RankingService { private static final String RANKING = "Ranking"; - private static final int START_INDEX = 0; - private static final int LIMIT_INDEX = 9; - private final ObjectMapper objectMapper; - private final ZSetRedisRepository zSetRedisRepository; + private final RankingReadService rankingReadService; + private final RankingWriteService rankingWriteService; + private final MemberReadService memberReadService; + + @Scheduled(cron = "0 11 * * * *") + public void updateAllRanking() { + List members = memberReadService.findAllMembers(); + List updateRankings = members.stream() + .map(MemberMapper::toUpdateRanking) + .toList(); + + updateScores(updateRankings); + } public void addRanking(RankingInfo rankingInfo, Long totalCertifyCount) { - zSetRedisRepository.add(RANKING, rankingInfo, totalCertifyCount); + rankingWriteService.addRanking(RANKING, rankingInfo, totalCertifyCount); } public void updateScores(List updateRankings) { - updateRankings.forEach( - updateRanking -> zSetRedisRepository.add(RANKING, updateRanking.rankingInfo(), updateRanking.score())); + rankingWriteService.updateScores(RANKING, updateRankings); } public void changeInfos(RankingInfo before, RankingInfo after) { - zSetRedisRepository.changeMember(RANKING, before, after); + rankingWriteService.changeInfos(RANKING, before, after); } public void removeRanking(RankingInfo rankingInfo) { - zSetRedisRepository.delete(RANKING, rankingInfo); + rankingWriteService.removeRanking(RANKING, rankingInfo); } public TopRankingResponse getMemberRanking(UpdateRanking myRankingInfo) { - List topRankings = getTopRankings(); - long myRanking = zSetRedisRepository.reverseRank(RANKING, myRankingInfo.rankingInfo()) + 1; + List topRankings = rankingReadService.readTopRankings(RANKING); + Long myRanking = rankingReadService.readRank(RANKING, myRankingInfo.rankingInfo()); + + if (Objects.isNull(myRanking)) { + updateAllRanking(); + } Optional myTopRanking = topRankings.stream() .filter(topRankingInfo -> Objects.equals(topRankingInfo.memberId(), myRankingInfo.rankingInfo().memberId())) .findFirst(); if (myTopRanking.isPresent()) { - myRanking = myTopRanking.get().rank(); + myRanking = (long)myTopRanking.get().rank(); } - TopRankingInfo myRankingInfoResponse = RankingMapper.topRankingResponse((int)myRanking, myRankingInfo); + TopRankingInfo myRankingInfoResponse = RankingMapper.topRankingResponse(myRanking, myRankingInfo); return RankingMapper.topRankingResponses(myRankingInfoResponse, topRankings); } - - private List getTopRankings() { - Set> topRankings = zSetRedisRepository.rangeJson(RANKING, START_INDEX, - LIMIT_INDEX); - - Set scoreSet = new HashSet<>(); - List topRankingInfo = new ArrayList<>(); - - for (ZSetOperations.TypedTuple topRanking : topRankings) { - long score = requireNonNull(topRanking.getScore()).longValue(); - scoreSet.add(score); - - RankingInfo rankingInfo = objectMapper.convertValue(topRanking.getValue(), RankingInfo.class); - topRankingInfo.add(RankingMapper.topRankingResponse(scoreSet.size(), score, rankingInfo)); - } - - return topRankingInfo; - } } diff --git a/src/main/java/com/moabam/api/application/ranking/RankingWriteService.java b/src/main/java/com/moabam/api/application/ranking/RankingWriteService.java new file mode 100644 index 00000000..5d93f988 --- /dev/null +++ b/src/main/java/com/moabam/api/application/ranking/RankingWriteService.java @@ -0,0 +1,35 @@ +package com.moabam.api.application.ranking; + +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.moabam.api.dto.ranking.RankingInfo; +import com.moabam.api.dto.ranking.UpdateRanking; +import com.moabam.api.infrastructure.redis.ZSetRedisRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class RankingWriteService { + + private final ZSetRedisRepository zSetRedisRepository; + + public void addRanking(String key, RankingInfo rankingInfo, Long totalCertifyCount) { + zSetRedisRepository.add(key, rankingInfo, totalCertifyCount); + } + + public void updateScores(String key, List updateRankings) { + updateRankings.forEach( + updateRanking -> zSetRedisRepository.add(key, updateRanking.rankingInfo(), updateRanking.score())); + } + + public void changeInfos(String key, RankingInfo before, RankingInfo after) { + zSetRedisRepository.changeMember(key, before, after); + } + + public void removeRanking(String key, RankingInfo rankingInfo) { + zSetRedisRepository.delete(key, rankingInfo); + } +} diff --git a/src/main/java/com/moabam/api/domain/member/Member.java b/src/main/java/com/moabam/api/domain/member/Member.java index 51e8d9c3..57c85e31 100644 --- a/src/main/java/com/moabam/api/domain/member/Member.java +++ b/src/main/java/com/moabam/api/domain/member/Member.java @@ -7,7 +7,6 @@ import static java.util.Objects.*; import java.time.LocalDateTime; -import java.util.Objects; import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.SQLDelete; @@ -19,6 +18,7 @@ import com.moabam.global.common.entity.BaseTimeEntity; import com.moabam.global.error.exception.NotFoundException; +import io.micrometer.common.util.StringUtils; import jakarta.persistence.Column; import jakarta.persistence.Embedded; import jakarta.persistence.Entity; @@ -139,7 +139,7 @@ public void delete(LocalDateTime now) { } public boolean changeNickName(String nickname) { - if (Objects.isNull(nickname)) { + if (StringUtils.isEmpty(nickname)) { return false; } this.nickname = nickname; diff --git a/src/main/java/com/moabam/api/domain/member/repository/MemberRepository.java b/src/main/java/com/moabam/api/domain/member/repository/MemberRepository.java index f0cb499b..2455d7d2 100644 --- a/src/main/java/com/moabam/api/domain/member/repository/MemberRepository.java +++ b/src/main/java/com/moabam/api/domain/member/repository/MemberRepository.java @@ -10,5 +10,7 @@ public interface MemberRepository extends JpaRepository { Optional findBySocialId(String id); + Optional findByNickname(String nickname); + boolean existsByNickname(String nickname); } diff --git a/src/main/java/com/moabam/global/auth/filter/PathFilter.java b/src/main/java/com/moabam/global/auth/filter/PathFilter.java index 8c7266ee..44667131 100644 --- a/src/main/java/com/moabam/global/auth/filter/PathFilter.java +++ b/src/main/java/com/moabam/global/auth/filter/PathFilter.java @@ -27,6 +27,12 @@ public class PathFilter extends OncePerRequestFilter { public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { Optional matchedPath = pathResolver.permitPathMatch(request.getRequestURI()); + Optional authenticatedPath = pathResolver.authenticationsPatterns(request.getRequestURI()); + authenticatedPath.ifPresent(path -> { + if (path.httpMethods().stream().anyMatch(httpMethod -> httpMethod.matches(request.getMethod()))) { + request.setAttribute("isPermit", true); + } + }); matchedPath.ifPresent(path -> { if (path.httpMethods().stream() diff --git a/src/main/java/com/moabam/global/error/model/ErrorMessage.java b/src/main/java/com/moabam/global/error/model/ErrorMessage.java index 62428059..e8a0e35c 100644 --- a/src/main/java/com/moabam/global/error/model/ErrorMessage.java +++ b/src/main/java/com/moabam/global/error/model/ErrorMessage.java @@ -51,6 +51,7 @@ public enum ErrorMessage { MEMBER_ROOM_EXCEED("참여할 수 있는 방의 개수가 모두 찼습니다."), UNLINK_REQUEST_FAIL_ROLLBACK_SUCCESS("카카오 연결 요청 실패로 Rollback하였습니다."), NICKNAME_CONFLICT("이미 존재하는 닉네임입니다."), + NICKNAME_NOT_NULL("닉네임은 공백이면 안됩니다."), BASIC_SKIN_NOT_FOUND("기본 스킨 오류 발생, 관리자에게 문의하세요"), INVALID_DEFAULT_SKIN_SIZE("기본 스킨은 2개여야 합니다. 관리자에게 문의하세요"), diff --git a/src/main/resources/config b/src/main/resources/config index 9c4b72d9..05316d99 160000 --- a/src/main/resources/config +++ b/src/main/resources/config @@ -1 +1 @@ -Subproject commit 9c4b72d99a8c789257c85b5f3057d54266009785 +Subproject commit 05316d99c521b3a26772206d4063cc069e08c848 diff --git a/src/main/resources/static/docs/coupon.html b/src/main/resources/static/docs/coupon.html new file mode 100644 index 00000000..ccacc78d --- /dev/null +++ b/src/main/resources/static/docs/coupon.html @@ -0,0 +1,716 @@ + + + + + + + +쿠폰(Coupon) + + + + + +
+
+

쿠폰(Coupon)

+
+
+
+
쿠폰에 대해 생성/삭제/조회/발급/사용 기능을 제공합니다.
+
+
+
+
+

쿠폰 생성

+
+
+
관리자가 쿠폰을 생성합니다.
+
+
+

요청

+
+
+
POST /admins/coupons HTTP/1.1
+Content-Type: application/json;charset=UTF-8
+Content-Length: 186
+Host: localhost:8080
+
+{
+  "name" : "couponName",
+  "description" : "coupon description",
+  "type" : "황금",
+  "point" : 10,
+  "maxCount" : 10,
+  "startAt" : "2023-02-01",
+  "openAt" : "2023-01-01"
+}
+
+
+

응답

+
+
+
HTTP/1.1 201 Created
+Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
+Access-Control-Allow-Headers: Origin, Accept, Access-Control-Request-Method, Access-Control-Request-Headers, X-Requested-With,Content-Type, Referer
+Access-Control-Allow-Credentials: true
+Access-Control-Max-Age: 3600
+
+
+
+
+
+

쿠폰 삭제

+
+
+
관리자가 쿠폰 ID와 일치하는 쿠폰을 삭제합니다.
+
+
+

요청

+
+
+
DELETE /admins/coupons/36 HTTP/1.1
+Host: localhost:8080
+
+
+

응답

+
+
+
HTTP/1.1 200 OK
+Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
+Access-Control-Allow-Headers: Origin, Accept, Access-Control-Request-Method, Access-Control-Request-Headers, X-Requested-With,Content-Type, Referer
+Access-Control-Allow-Credentials: true
+Access-Control-Max-Age: 3600
+
+
+
+
+
+

특정 쿠폰 조회

+
+
+
관리자 혹은 사용자가 특정 ID와 일치하는 쿠폰을 조회합니다.
+
+
+
+

요청

+
+
+
GET /coupons/24 HTTP/1.1
+Host: localhost:8080
+
+
+

응답

+
+
+
HTTP/1.1 200 OK
+Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
+Access-Control-Allow-Headers: Origin, Accept, Access-Control-Request-Method, Access-Control-Request-Headers, X-Requested-With,Content-Type, Referer
+Access-Control-Allow-Credentials: true
+Access-Control-Max-Age: 3600
+Content-Type: application/json
+Content-Length: 202
+
+{
+  "id" : 24,
+  "adminId" : 1,
+  "name" : "couponName",
+  "description" : "",
+  "point" : 10,
+  "maxCount" : 100,
+  "type" : "MORNING",
+  "startAt" : "2023-02-01",
+  "openAt" : "2023-01-01"
+}
+
+
+
+
+
+
+

상태에 따른 쿠폰들을 조회

+
+
+
관리자 혹은 사용자가 날짜 상태에 따라 쿠폰들을 조회합니다.
+
+
+
+

요청

+
+
+
POST /coupons/search HTTP/1.1
+Content-Type: application/json;charset=UTF-8
+Content-Length: 44
+Host: localhost:8080
+
+{
+  "opened" : false,
+  "ended" : false
+}
+
+
+

응답

+
+
+
HTTP/1.1 200 OK
+Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
+Access-Control-Allow-Headers: Origin, Accept, Access-Control-Request-Method, Access-Control-Request-Headers, X-Requested-With,Content-Type, Referer
+Access-Control-Allow-Credentials: true
+Access-Control-Max-Age: 3600
+Content-Type: application/json
+Content-Length: 203
+
+[ {
+  "id" : 25,
+  "adminId" : 1,
+  "name" : "coupon1",
+  "description" : "",
+  "point" : 10,
+  "maxCount" : 100,
+  "type" : "MORNING",
+  "startAt" : "2023-03-01",
+  "openAt" : "2023-01-01"
+} ]
+
+
+
+
+
+
+

특정 쿠폰에 대해 발급

+
+
+
사용자가 발급 가능한 쿠폰을 선착순으로 발급 받습니다.
+
+
+
+

요청

+
+
+
POST /coupons HTTP/1.1
+Content-Type: application/x-www-form-urlencoded
+Host: localhost:8080
+Content-Length: 21
+
+couponName=couponName
+
+
+

응답

+
+
+
HTTP/1.1 409 Conflict
+Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
+Access-Control-Allow-Headers: Origin, Accept, Access-Control-Request-Method, Access-Control-Request-Headers, X-Requested-With,Content-Type, Referer
+Access-Control-Allow-Credentials: true
+Access-Control-Max-Age: 3600
+Content-Type: application/json
+Content-Length: 65
+
+{
+  "message" : "이미 쿠폰 발급에 성공했습니다!"
+}
+
+
+
+
+
+
+

특정 사용자의 쿠폰 보관함을 조회

+
+
+
사용자가 자신의 보관함에 있는 쿠폰들을 조회합니다.
+
+
+
+

요청

+
+
+
GET /my-coupons HTTP/1.1
+Host: localhost:8080
+
+
+

응답

+
+
+
HTTP/1.1 200 OK
+Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
+Access-Control-Allow-Headers: Origin, Accept, Access-Control-Request-Method, Access-Control-Request-Headers, X-Requested-With,Content-Type, Referer
+Access-Control-Allow-Credentials: true
+Access-Control-Max-Age: 3600
+Content-Type: application/json
+Content-Length: 3
+
+[ ]
+
+
+
+
+
+
+

쿠폰을 사용

+
+
+
사용자가 자신의 보관함에 있는 쿠폰들을 사용합니다.
+
+
+
+

요청

+
+
+
POST /my-coupons/8 HTTP/1.1
+Host: localhost:8080
+Content-Type: application/x-www-form-urlencoded
+
+
+

응답

+
+
+
HTTP/1.1 200 OK
+Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
+Access-Control-Allow-Headers: Origin, Accept, Access-Control-Request-Method, Access-Control-Request-Headers, X-Requested-With,Content-Type, Referer
+Access-Control-Allow-Credentials: true
+Access-Control-Max-Age: 3600
+
+
+
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/src/main/resources/static/docs/index.html b/src/main/resources/static/docs/index.html new file mode 100644 index 00000000..5037db7b --- /dev/null +++ b/src/main/resources/static/docs/index.html @@ -0,0 +1,630 @@ + + + + + + + +MOABAM API 문서 + + + + + + + +
+
+

1. 개요

+
+
+

이 API 문서는 'MOABAM' 프로젝트의 산출물입니다.

+
+
+

1.1. API 서버 경로

+ +++++ + + + + + + + + + + + + + + + + + +

환경

DNS

비고

개발(dev)

dev.moabam.com

운영(prod)

www.moabam.com

+
+ + + + + +
+ + +
+

해당 프로젝트 API 문서는 [특이사항]입니다.

+
+
+
+
+ + + + + +
+ + +
+

해당 프로젝트 API 문서는 [주의사항]입니다.

+
+
+
+
+
+

1.2. 응답형식

+
+

프로젝트는 다음과 같은 응답형식을 제공합니다.

+
+
+

1.2.1. 정상(2XX)

+ ++++ + + + + + + + + + + + + +
응답데이터가 없는 경우응답데이터가 있는 경우
+
+
{
+
+}
+
+
+
+
{
+  "name": "Hong-Dosan"
+}
+
+
+
+
+

1.2.2. 상태코드(HttpStatus)

+
+

응답시 다음과 같은 응답상태 헤더, 응답코드 및 응답메시지를 제공합니다.

+
+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HttpStatus설명

OK(200)

정상 응답

CREATED(201)

새로운 리소스 생성

BAD_REQUEST(400)

요청값 누락, 잘못된 기입

UNAUTHORIZED(401)

비인증 요청

NOT_FOUND(404)

요청값 누락, 잘못된 기입, 비인가 접속 등

CONFLICT(409)

요청값 중복

INTERNAL_SERVER_ERROR(500)

알 수 없는 서버 에러가 발생했습니다. 관리자에게 문의하세요.

+
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/src/main/resources/static/docs/notification.html b/src/main/resources/static/docs/notification.html new file mode 100644 index 00000000..65a377f7 --- /dev/null +++ b/src/main/resources/static/docs/notification.html @@ -0,0 +1,518 @@ + + + + + + + +알림(Notification) + + + + + +
+
+

알림(Notification)

+
+
+
+
콕 찌르기 알림, FCM Token 저장 기능을 제공합니다.
+
+
+
+

콕 찌르기 알림

+
+
+
1) 특정 방의 사용자가 다른 사용자를 콕 찌릅니다.
+2) 서버에서 콕 찌를 대상의 FCM Token 여부를 검증합니다.
+3) Firebase 서버에 FCM Push Messaing 알림을 비동기로 요청합니다.
+4) Firebase 서버에서 FCM Token으로 식별된 기기에 알림을 보냅니다.
+
+
+

요청

+
+
+
GET /notifications/rooms/1/members/2 HTTP/1.1
+Host: localhost:8080
+
+
+

응답

+
+
+
HTTP/1.1 200 OK
+Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
+Access-Control-Allow-Headers: Origin, Accept, Access-Control-Request-Method, Access-Control-Request-Headers, X-Requested-With,Content-Type, Referer
+Access-Control-Allow-Credentials: true
+Access-Control-Max-Age: 3600
+
+
+
+
+

FCM TOKEN 저장

+
+
+
1) 특정 사용자의 FCM-TOKEN을 받아서 REDIS DB에 저장합니다.
+
+
+

요청

+
+
+
POST /notifications HTTP/1.1
+Content-Type: application/x-www-form-urlencoded
+Host: localhost:8080
+Content-Length: 18
+
+fcmToken=FCM-TOKEN
+
+
+

응답

+
+
+
HTTP/1.1 200 OK
+Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
+Access-Control-Allow-Headers: Origin, Accept, Access-Control-Request-Method, Access-Control-Request-Headers, X-Requested-With,Content-Type, Referer
+Access-Control-Allow-Credentials: true
+Access-Control-Max-Age: 3600
+
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/src/test/java/com/moabam/api/application/member/MemberServiceTest.java b/src/test/java/com/moabam/api/application/member/MemberServiceTest.java index dc28bb72..975c0df4 100644 --- a/src/test/java/com/moabam/api/application/member/MemberServiceTest.java +++ b/src/test/java/com/moabam/api/application/member/MemberServiceTest.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Optional; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -20,12 +21,10 @@ import com.moabam.api.domain.item.Inventory; import com.moabam.api.domain.item.Item; import com.moabam.api.domain.item.repository.InventoryRepository; -import com.moabam.api.domain.item.repository.InventorySearchRepository; import com.moabam.api.domain.item.repository.ItemRepository; import com.moabam.api.domain.member.Member; import com.moabam.api.domain.member.repository.MemberRepository; import com.moabam.api.domain.member.repository.MemberSearchRepository; -import com.moabam.api.domain.room.repository.ParticipantRepository; import com.moabam.api.domain.room.repository.ParticipantSearchRepository; import com.moabam.api.dto.auth.AuthorizationTokenInfoResponse; import com.moabam.api.dto.auth.LoginResponse; @@ -58,29 +57,30 @@ class MemberServiceTest { @Mock MemberSearchRepository memberSearchRepository; - @Mock - ParticipantRepository participantRepository; - @Mock ParticipantSearchRepository participantSearchRepository; @Mock - InventorySearchRepository inventorySearchRepository; - - @Mock - InventoryRepository inventoryRepository; + ItemRepository itemRepository; @Mock - RankingService rankingService; + ClockHolder clockHolder; - @Mock - FcmService fcmService; + @BeforeEach + void init() { + MemberReadService memberReadService = new MemberReadService(memberRepository, + memberSearchRepository, + participantSearchRepository); - @Mock - ItemRepository itemRepository; + MemberWriteService memberWriteService = new MemberWriteService(memberRepository, + itemRepository, + mock(InventoryRepository.class), + participantSearchRepository, + clockHolder); - @Mock - ClockHolder clockHolder; + memberService = new MemberService(memberReadService, memberWriteService, mock(RankingService.class), + mock(FcmService.class)); + } @DisplayName("회원 존재하고 로그인 성공") @Test @@ -233,17 +233,4 @@ void modify_success_test(@WithMember AuthMember authMember) { () -> assertThat(member.getProfileImage()).isEqualTo("/main") ); } - - @DisplayName("모든 랭킹 업데이트") - @Test - void update_all_ranking() { - // given - Member member1 = MemberFixture.member("1"); - Member member2 = MemberFixture.member("2"); - given(memberSearchRepository.findAllMembers()) - .willReturn(List.of(member1, member2)); - - // when + Then - assertThatNoException().isThrownBy(() -> memberService.updateAllRanking()); - } } diff --git a/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java b/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java index 1dbb1843..b4f74bd1 100644 --- a/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java +++ b/src/test/java/com/moabam/api/application/ranking/RankingServiceTest.java @@ -12,12 +12,17 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ZSetOperations; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import com.moabam.api.application.member.MemberMapper; +import com.moabam.api.application.member.MemberReadService; import com.moabam.api.domain.member.Member; +import com.moabam.api.domain.member.repository.MemberRepository; +import com.moabam.api.domain.member.repository.MemberSearchRepository; +import com.moabam.api.domain.room.repository.ParticipantSearchRepository; import com.moabam.api.dto.ranking.RankingInfo; import com.moabam.api.dto.ranking.TopRankingInfo; import com.moabam.api.dto.ranking.TopRankingResponse; @@ -27,7 +32,10 @@ import com.moabam.support.fixture.BugFixture; import com.moabam.support.fixture.MemberFixture; -@SpringBootTest(classes = {EmbeddedRedisConfig.class, RankingService.class, ZSetRedisRepository.class}) +@SpringBootTest(classes = {EmbeddedRedisConfig.class, RankingService.class, + RankingReadService.class, RankingWriteService.class, MemberReadService.class, ZSetRedisRepository.class, + MemberRepository.class, MemberSearchRepository.class, ParticipantSearchRepository.class +}) public class RankingServiceTest { @Autowired @@ -39,6 +47,24 @@ public class RankingServiceTest { @Autowired RankingService rankingService; + @Autowired + RankingReadService rankingReadService; + + @Autowired + RankingWriteService rankingWriteService; + + @MockBean + MemberReadService memberReadService; + + @MockBean + MemberRepository memberRepository; + + @MockBean + MemberSearchRepository memberSearchRepository; + + @MockBean + ParticipantSearchRepository participantSearchRepository; + @BeforeEach void init() { redisTemplate.delete("Ranking"); diff --git a/src/test/java/com/moabam/api/infrastructure/fcm/FcmServiceTest.java b/src/test/java/com/moabam/api/infrastructure/fcm/FcmServiceTest.java index 9720b843..fb987b2f 100644 --- a/src/test/java/com/moabam/api/infrastructure/fcm/FcmServiceTest.java +++ b/src/test/java/com/moabam/api/infrastructure/fcm/FcmServiceTest.java @@ -11,10 +11,9 @@ import com.google.firebase.messaging.FirebaseMessaging; import com.google.firebase.messaging.Message; import com.moabam.global.config.FcmConfig; -import com.moabam.support.common.WithoutFilterSupporter; @SpringBootTest(classes = {FcmConfig.class, FcmService.class}) -class FcmServiceTest extends WithoutFilterSupporter { +class FcmServiceTest { @Autowired FcmService fcmService; diff --git a/src/test/java/com/moabam/api/presentation/BugControllerTest.java b/src/test/java/com/moabam/api/presentation/BugControllerTest.java index c7887beb..ad75795b 100644 --- a/src/test/java/com/moabam/api/presentation/BugControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/BugControllerTest.java @@ -30,6 +30,7 @@ import com.moabam.api.application.member.MemberService; import com.moabam.api.application.product.ProductMapper; import com.moabam.api.domain.bug.BugActionType; +import com.moabam.api.domain.bug.BugHistory; import com.moabam.api.domain.bug.BugType; import com.moabam.api.domain.bug.repository.BugHistoryRepository; import com.moabam.api.domain.bug.repository.BugHistorySearchRepository; @@ -44,6 +45,7 @@ import com.moabam.api.dto.product.PurchaseProductResponse; import com.moabam.support.annotation.WithMember; import com.moabam.support.common.WithoutFilterSupporter; +import com.moabam.support.fixture.BugFixture; @Transactional @SpringBootTest diff --git a/src/test/java/com/moabam/api/presentation/CouponControllerTest.java b/src/test/java/com/moabam/api/presentation/CouponControllerTest.java index f3e8d891..849a2c79 100644 --- a/src/test/java/com/moabam/api/presentation/CouponControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/CouponControllerTest.java @@ -1,8 +1,8 @@ package com.moabam.api.presentation; -import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.*; import static org.hamcrest.Matchers.*; import static org.mockito.BDDMockito.*; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.*; import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*; import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*; diff --git a/src/test/java/com/moabam/api/presentation/MemberControllerTest.java b/src/test/java/com/moabam/api/presentation/MemberControllerTest.java index 80efe377..6e76b10f 100644 --- a/src/test/java/com/moabam/api/presentation/MemberControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/MemberControllerTest.java @@ -203,7 +203,6 @@ void delete_member_success() throws Exception { Member deletedMEmber = deletedMemberOptional.get(); assertThat(deletedMEmber.getDeletedAt()).isNotNull(); - assertThat(deletedMEmber.getNickname()).isNull(); } @DisplayName("회원이 없어서 회원 삭제 실패") @@ -455,7 +454,7 @@ void search_member_failBy_default_skin_size() throws Exception { @DisplayName("회원 정보 요청 성공") @WithMember @ParameterizedTest - @CsvSource({"intro,", ", nickname", ",", "intro, nickname"}) + @CsvSource({"intro,", ", nickname2", ",", "intro, nickname3"}) void member_modify_request_success(String intro, String nickname) throws Exception { // given ModifyMemberRequest request = new ModifyMemberRequest(intro, nickname); @@ -484,13 +483,47 @@ void member_modify_request_success(String intro, String nickname) throws Excepti .characterEncoding("UTF-8")) .andExpect(status().is2xxSuccessful()) .andDo(print()); + } + + @DisplayName("회원 정보 요청 성공") + @WithMember + @Test + void member_modify_sameName_request_success() throws Exception { + // given + String intro = "intro"; + String nickname = member.getNickname(); + ModifyMemberRequest request = new ModifyMemberRequest(intro, nickname); + MockMultipartFile newProfileImage = + new MockMultipartFile( + "profileImage", + "tooth.png", + "multipart/form-data", + "uploadFile".getBytes(StandardCharsets.UTF_8)); + MockMultipartFile modifyMemberRequest = + new MockMultipartFile( + "modifyMemberRequest", + null, + "application/json", + objectMapper.writeValueAsString(request).getBytes(StandardCharsets.UTF_8)); + + willReturn(List.of("/main")) + .given(imageService).uploadImages(List.of(newProfileImage), ImageType.PROFILE_IMAGE); + + // expected + mockMvc.perform(multipart(HttpMethod.POST, "/members/modify") + .file(modifyMemberRequest) + .file(newProfileImage) + .contentType("multipart/form-data") + .characterEncoding("UTF-8")) + .andExpect(status().is2xxSuccessful()) + .andDo(print()); } @DisplayName("회원 프로필없이 성공 ") @WithMember @ParameterizedTest - @CsvSource({"intro,", ", nickname", ",", "intro, nickname"}) + @CsvSource({"intro,", ", nickname4", ",", "intro, nickname5"}) void member_modify_no_image_request_success(String intro, String nickname) throws Exception { // given ModifyMemberRequest request = new ModifyMemberRequest(intro, nickname); diff --git a/src/test/java/com/moabam/api/presentation/NotificationControllerTest.java b/src/test/java/com/moabam/api/presentation/NotificationControllerTest.java index e7457db1..b623bcb9 100644 --- a/src/test/java/com/moabam/api/presentation/NotificationControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/NotificationControllerTest.java @@ -1,7 +1,7 @@ package com.moabam.api.presentation; -import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.*; import static org.mockito.BDDMockito.*; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.*; import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*; import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*; diff --git a/src/test/java/com/moabam/support/common/DataCleanResolver.java b/src/test/java/com/moabam/support/common/DataCleanResolver.java index dfd259f0..6535ae8a 100644 --- a/src/test/java/com/moabam/support/common/DataCleanResolver.java +++ b/src/test/java/com/moabam/support/common/DataCleanResolver.java @@ -52,3 +52,4 @@ private void setForeignKeyCheck(int data) { .executeUpdate(); } } + diff --git a/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java b/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java index ea3eecad..4d700b8d 100644 --- a/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java +++ b/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java @@ -10,12 +10,10 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.context.annotation.Import; -import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver; import com.moabam.api.domain.member.Role; import com.moabam.global.auth.filter.CorsFilter; import com.moabam.global.auth.handler.PathResolver; -import com.moabam.global.config.AllowOriginConfig; @Import(DataCleanResolver.class) @ExtendWith({FilterProcessExtension.class, ClearDataExtension.class}) @@ -24,15 +22,9 @@ public class WithoutFilterSupporter { @MockBean private PathResolver pathResolver; - @MockBean - private DefaultHandlerExceptionResolver handlerExceptionResolver; - @SpyBean private CorsFilter corsFilter; - @MockBean - private AllowOriginConfig allowOriginConfig; - @BeforeEach void setUpMock() { willReturn("http://localhost:8080")