Skip to content

Commit

Permalink
v1.4.0 (#283)
Browse files Browse the repository at this point in the history
  • Loading branch information
kdomo authored Feb 7, 2024
2 parents 516c556 + f1d578c commit f35c630
Show file tree
Hide file tree
Showing 19 changed files with 275 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public SocialLoginResponse socialLoginMember(IdTokenRequest request, OauthProvid

private Member fetchOrCreate(OidcUser oidcUser) {
return memberRepository
.findByOauthInfo(extractOauthInfo(oidcUser))
.findByOauthInfoAndStatus(extractOauthInfo(oidcUser), MemberStatus.NORMAL)
.orElseGet(() -> saveMember(oidcUser));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.depromeet.domain.follow.dto.request.FollowDeleteRequest;
import com.depromeet.domain.follow.dto.response.FollowFindMeInfoResponse;
import com.depromeet.domain.follow.dto.response.FollowFindTargetInfoResponse;
import com.depromeet.domain.follow.dto.response.FollowListResponse;
import com.depromeet.domain.follow.dto.response.MemberFollowedResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
Expand Down Expand Up @@ -56,4 +57,10 @@ public FollowFindMeInfoResponse followFindMe() {
public List<MemberFollowedResponse> followedUserFindAll() {
return followService.findAllFollowedMember();
}

@GetMapping("/{targetId}/list")
@Operation(summary = "팔로잉, 팔로워 유저 리스트를 반환합니다", description = "팔로잉, 팔로워 유저들을 반환합니다.")
public FollowListResponse followList(@PathVariable Long targetId) {
return followService.findFollowList(targetId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@
import com.depromeet.domain.follow.domain.MemberRelation;
import com.depromeet.domain.follow.dto.request.FollowCreateRequest;
import com.depromeet.domain.follow.dto.request.FollowDeleteRequest;
import com.depromeet.domain.follow.dto.response.FollowFindMeInfoResponse;
import com.depromeet.domain.follow.dto.response.FollowFindTargetInfoResponse;
import com.depromeet.domain.follow.dto.response.FollowStatus;
import com.depromeet.domain.follow.dto.response.MemberFollowedResponse;
import com.depromeet.domain.follow.dto.response.*;
import com.depromeet.domain.member.dao.MemberRepository;
import com.depromeet.domain.member.domain.Member;
import com.depromeet.domain.member.dto.response.MemberSearchResponse;
import com.depromeet.domain.mission.domain.Mission;
import com.depromeet.domain.missionRecord.domain.ImageUploadStatus;
import com.depromeet.domain.missionRecord.domain.MissionRecord;
Expand Down Expand Up @@ -188,4 +186,78 @@ private Member getTargetMember(Long targetId) {
ErrorCode.FOLLOW_TARGET_MEMBER_NOT_FOUND));
return targetMember;
}

public FollowListResponse findFollowList(Long targetId) {
final Member currentMember = memberUtil.getCurrentMember();
Member targetMember = getTargetMember(targetId);

List<MemberSearchResponse> followingList = new ArrayList<>();
List<MemberSearchResponse> followerList = new ArrayList<>();

List<MemberRelation> targetMemberSources =
memberRelationRepository.findAllBySourceId(targetMember.getId());
List<MemberRelation> targetMemberTargets =
memberRelationRepository.findAllByTargetId(targetMember.getId());

List<MemberRelation> currentMemberSources =
memberRelationRepository.findAllBySourceId(currentMember.getId());
List<MemberRelation> currentMemberTargets =
memberRelationRepository.findAllByTargetId(currentMember.getId());

// target 유저의 팔로잉
List<Member> followingMembers =
targetMemberSources.stream().map(MemberRelation::getTarget).toList();

// target 유저의 팔로워
List<Member> followerMembers =
targetMemberTargets.stream().map(MemberRelation::getSource).toList();

// 팔로잉 리스트 구하기
getFollowStatusIncludeList(
followingMembers, currentMemberSources, followingList, currentMemberTargets);

// 팔로워 리스트 구하기
getFollowStatusIncludeList(
followerMembers, currentMemberSources, followerList, currentMemberTargets);

return FollowListResponse.of(
targetMember.getProfile().getNickname(), followingList, followerList);
}

private static void getFollowStatusIncludeList(
List<Member> targetMembers,
List<MemberRelation> currentMemberSources,
List<MemberSearchResponse> resultList,
List<MemberRelation> currentMemberTargets) {
for (Member member : targetMembers) {
boolean existRelation = false;
for (MemberRelation memberRelation : currentMemberSources) {
if (member.getId().equals(memberRelation.getTarget().getId())) {
existRelation = true;
break;
}
}

if (existRelation) { // 조회 된 애들 중 내가 팔로우한 애라면
resultList.add(MemberSearchResponse.toFollowingResponse(member));
continue;
}

// 내가 팔로우를 하지 않았을 때
Optional<MemberRelation> optionalMemberRelation =
currentMemberTargets.stream()
.filter(
memberRelation ->
member.getId()
.equals(memberRelation.getSource().getId()))
.findFirst();
if (optionalMemberRelation.isPresent()) { // 상대방만 나를 팔로우 하고 있을 때
resultList.add(MemberSearchResponse.toFollowedByMeResponse(member));
continue;
}

// 아니라면 서로 팔로우가 아닌 상태
resultList.add(MemberSearchResponse.toNotFollowingResponse(member));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.depromeet.domain.follow.dto.response;

import com.depromeet.domain.member.dto.response.MemberSearchResponse;
import java.util.List;

public record FollowListResponse(
String targetNickname,
List<MemberSearchResponse> followingList,
List<MemberSearchResponse> followerList) {
public static FollowListResponse of(
String targetNickname,
List<MemberSearchResponse> followingList,
List<MemberSearchResponse> followerList) {
return new FollowListResponse(targetNickname, followingList, followerList);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.depromeet.domain.member.dto.response.MemberFindOneResponse;
import com.depromeet.domain.member.dto.response.MemberSearchResponse;
import com.depromeet.domain.member.dto.response.MemberSocialInfoResponse;
import com.depromeet.global.util.CookieUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
Expand All @@ -23,6 +24,7 @@
public class MemberController {

private final MemberService memberService;
private final CookieUtil cookieUtil;

@Operation(summary = "회원 정보 확인", description = "로그인 된 회원의 정보를 확인합니다.")
@GetMapping("/me")
Expand All @@ -44,7 +46,7 @@ public ResponseEntity<Void> memberUsernameCheck(
return ResponseEntity.ok().build();
}

@Operation(summary = "닉네임 중복 체크", description = "닉네임 중복 체크를 진행합니다.")
@Operation(summary = "닉네임 유효성 체크", description = "닉네임 유효성 체크를 진행합니다.")
@PostMapping("/check-nickname")
public ResponseEntity<Void> memberNicknameCheck(
@Valid @RequestBody NicknameCheckRequest request) {
Expand All @@ -54,16 +56,17 @@ public ResponseEntity<Void> memberNicknameCheck(

@Operation(summary = "닉네임으로 회원 검색", description = "닉네임으로 회원을 검색합니다.")
@GetMapping("/search")
public List<MemberSearchResponse> memberNicknameSearch(@RequestParam String nickname) {
public List<MemberSearchResponse> memberNicknameSearch(
@RequestParam(required = false) String nickname) {
return memberService.searchMemberNickname(nickname);
}

// TODO: 테스트 코드 작성 필요
@Operation(summary = "회원 탈퇴", description = "회원탈퇴를 진행합니다.")
@DeleteMapping("/withdrawal")
public ResponseEntity<Void> memberWithdrawal(@Valid @RequestBody UsernameCheckRequest request) {
memberService.withdrawal(request);
return ResponseEntity.ok().build();
public ResponseEntity<Void> memberWithdrawal() {
memberService.withdrawal();
return ResponseEntity.ok().headers(cookieUtil.deleteTokenCookies()).build();
}

@Operation(summary = "소셜 로그인 정보 조회하기", description = "소셜 로그인 정보를 조회합니다.")
Expand All @@ -76,8 +79,8 @@ public ResponseEntity<MemberSocialInfoResponse> memberSocialInfoFind() {
@Operation(summary = "회원 닉네임 변경", description = "회원 닉네임을 변경합니다.")
@PutMapping("/me/nickname")
public ResponseEntity<Void> memberNicknameUpdate(
@Valid @RequestBody NicknameUpdateRequest reqest) {
memberService.updateMemberNickname(reqest);
@Valid @RequestBody NicknameUpdateRequest request) {
memberService.updateMemberNickname(request);
return ResponseEntity.ok().build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ public void checkUsername(UsernameCheckRequest request) {
@Transactional(readOnly = true)
public void checkNickname(NicknameCheckRequest request) {
validateNicknameNotDuplicate(request.nickname());
if (validateNicknameText(request.nickname())) {
throw new CustomException(ErrorCode.MEMBER_INVALID_NICKNAME);
}
}

private boolean validateNicknameText(String nickname) {
return nickname == null || nickname.trim().isEmpty();
}

private void validateNicknameNotDuplicate(String nickname) {
Expand All @@ -73,8 +80,11 @@ private void validateNicknameNotDuplicate(String nickname) {
@Transactional(readOnly = true)
public List<MemberSearchResponse> searchMemberNickname(String nickname) {
final Member currentMember = memberUtil.getCurrentMember();
final String escapingNickname = escapeSpecialCharacters(nickname);

List<Member> members =
memberRepository.nicknameSearch(nickname, currentMember.getProfile().getNickname());
memberRepository.nicknameSearch(
escapingNickname, currentMember.getProfile().getNickname());
List<MemberRelation> memberRelationBySourceId =
memberRelationRepository.findAllBySourceIdAndTargetIn(
currentMember.getId(), members);
Expand Down Expand Up @@ -125,12 +135,8 @@ public List<MemberSearchResponse> searchMemberNickname(String nickname) {
return response;
}

public void withdrawal(UsernameCheckRequest request) {
final Member member =
memberRepository
.findByUsername(request.username())
.orElseThrow(() -> new CustomException(ErrorCode.MEMBER_NOT_FOUND));

public void withdrawal() {
final Member member = memberUtil.getCurrentMember();
refreshTokenRepository.deleteById(member.getId());
member.withdrawal();
}
Expand All @@ -147,10 +153,10 @@ private void validateSocialInfoNotNull(Member member) {
}
}

public void updateMemberNickname(NicknameUpdateRequest reqest) {
public void updateMemberNickname(NicknameUpdateRequest request) {
final Member currentMember = memberUtil.getCurrentMember();
validateNicknameNotDuplicate(reqest.nickname());
currentMember.updateNickname(reqest.nickname());
validateNicknameNotDuplicate(request.nickname());
currentMember.updateNickname(escapeSpecialCharacters(request.nickname()));
}

private ImageFileExtension getImageFileExtension(Profile profile) {
Expand All @@ -174,4 +180,9 @@ public void updateFcmToken(UpdateFcmTokenRequest updateFcmTokenRequest) {
final Member currentMember = memberUtil.getCurrentMember();
currentMember.updateFcmToken(currentMember.getFcmInfo(), updateFcmTokenRequest.fcmToken());
}

private String escapeSpecialCharacters(String nickname) {
// 여기서 특수문자를 '_'로 대체할 수 있도록 정규표현식을 활용하여 구현
return nickname == null ? "" : nickname.replaceAll("[^0-9a-zA-Z가-힣 ]", "_");
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package com.depromeet.domain.member.dao;

import com.depromeet.domain.member.domain.Member;
import com.depromeet.domain.member.domain.MemberStatus;
import com.depromeet.domain.member.domain.OauthInfo;
import io.lettuce.core.dynamic.annotation.Param;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

public interface MemberRepository extends JpaRepository<Member, Long> {

Optional<Member> findByOauthInfo(OauthInfo oauthInfo);
Optional<Member> findByOauthInfoAndStatus(OauthInfo oauthInfo, MemberStatus status);

boolean existsByUsername(String username);

Expand All @@ -20,6 +22,7 @@ public interface MemberRepository extends JpaRepository<Member, Long> {
Optional<Member> findByProfileNickname(String nickname);

@Query(
"SELECT m FROM Member m WHERE m.profile.nickname like %:searchNickname% AND m.profile.nickname != :myNickname")
List<Member> nicknameSearch(String searchNickname, String myNickname);
"SELECT m FROM Member m WHERE m.profile.nickname LIKE CONCAT('%', :searchNickname, '%') escape '_' AND m.profile.nickname != :myNickname")
List<Member> nicknameSearch(
@Param("searchNickname") String searchNickname, @Param("myNickname") String myNickname);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ private FcmInfo(String fcmToken, Boolean appAlarm) {
}

public static FcmInfo createFcmInfo() {
return FcmInfo.builder().fcmToken("").appAlarm(true).build();
return FcmInfo.builder().appAlarm(true).build();
}

public static FcmInfo toggleAlarm(FcmInfo fcmState) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public class Member extends BaseTimeEntity {

@Embedded private OauthInfo oauthInfo;

@Embedded private FcmInfo fcmInfo = FcmInfo.createFcmInfo();
@Embedded private FcmInfo fcmInfo;

@Enumerated(EnumType.STRING)
private MemberStatus status;
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/depromeet/domain/member/domain/Profile.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package com.depromeet.domain.member.domain;

import jakarta.persistence.Embeddable;
import jakarta.validation.constraints.Pattern;
import lombok.*;

@Embeddable
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Profile {

@Pattern(regexp = "[^0-9a-zA-Z가-힣 ]", message = "올바르지 않은 닉네임 표현입니다.")
private String nickname;

@Getter private String profileImageUrl;

@Builder(access = AccessLevel.PRIVATE)
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package com.depromeet.global.common.constants;

import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

@Getter
@AllArgsConstructor
public enum EnvironmentConstants {
PROD("prod"),
DEV("dev"),
LOCAL("local"),
;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class EnvironmentConstants {

private String value;
public static final String PROD = "prod";
public static final String DEV = "dev";
public static final String LOCAL = "local";
public static final List<String> PROD_AND_DEV = List.of(PROD, DEV);
}
3 changes: 3 additions & 0 deletions src/main/java/com/depromeet/global/config/fcm/FcmService.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ public ApiFuture<BatchResponse> sendGroupMessageAsync(
}

public ApiFuture<String> sendMessageSync(String token, String title, String content) {
if (token == null || token.isEmpty()) {
return null;
}
Message message =
Message.builder()
.setToken(token)
Expand Down
Loading

0 comments on commit f35c630

Please sign in to comment.