Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v1.4.0 #283

Merged
merged 5 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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);
}
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
Loading