Skip to content
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 @@ -32,7 +32,7 @@ public class SecurityConfig {
"/swagger-ui/**", "/v3/api-docs/**", "/error", "/favicon.ico", "/", "/actuator/**",
"/v1/api/alcohols/**", "/v1/api/terms/**", "/v1/api/images",
"/v1/api/members/**", "/v1/api/shared-space/tasting-notes/**", "/v1/api/notifications/**",
"/v1/api/daily-lives/**", "/v1/api/alcoholicDrinks/**", "v1/api/follow" , "/**"
"/v1/api/daily-lives/**", "/v1/api/alcoholicDrinks/**", "v1/api/follow" , "/**", "/v1/api/reports"
};

private static final String[] ALLOW_ORIGINS = {
Expand All @@ -41,6 +41,7 @@ public class SecurityConfig {
"http://localhost:5173",
"http://localhost:3000",
"https://api.juulabel.com",
"https://dev.juulabel.com",
"https://qa.juulabel.com",
"https://juulabel.com",
"https://juulabel.shop",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.juu.juulabel.common.exception.handler;

import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.juu.juulabel.common.exception.BaseException;
import com.juu.juulabel.common.exception.code.ErrorCode;
import com.juu.juulabel.common.response.CommonResponse;
Expand All @@ -8,11 +9,13 @@
import io.sentry.Sentry;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.resource.NoResourceFoundException;

import java.util.Arrays;
import java.util.Objects;

@Slf4j
Expand Down Expand Up @@ -60,8 +63,25 @@ public ResponseEntity<CommonResponse<String>> handle(MalformedJwtException e) {

@ExceptionHandler(NoResourceFoundException.class)
public void handle(NoResourceFoundException e) {
// 이거 키면 출력이 너무 많이 됨
//log.warn("NoResourceFoundException : {}", e.getMessage());
// 이거 키면 출력이 너무 많이 됨
//log.warn("NoResourceFoundException : {}", e.getMessage());
}

@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<CommonResponse<String>> handleValidationException(HttpMessageNotReadableException exception) {
String errorDetails = "";
log.error("HttpMessageNotReadableException :", exception);
if (exception.getCause() instanceof InvalidFormatException invalidFormatException) {
if (invalidFormatException.getTargetType() != null && invalidFormatException.getTargetType().isEnum()) {
errorDetails = String.format("'%s'. 값은 다음 중 하나여야 합니다: %s.",
invalidFormatException.getPath().getLast().getFieldName(),
Arrays.toString(invalidFormatException.getTargetType().getEnumConstants())
);
}
}
if (errorDetails.isEmpty()) {
errorDetails = exception.getMessage();
}
return CommonResponse.fail(ErrorCode.VALIDATION_ERROR, errorDetails);
}
}
131 changes: 70 additions & 61 deletions src/main/java/com/juu/juulabel/member/service/MemberService.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.juu.juulabel.common.constants.AuthConstants;
import com.juu.juulabel.common.dto.request.*;
import com.juu.juulabel.common.dto.response.*;
import com.juu.juulabel.common.exception.BaseException;
import com.juu.juulabel.common.exception.InvalidParamException;
import com.juu.juulabel.common.exception.code.ErrorCode;
import com.juu.juulabel.common.factory.OAuthProviderFactory;
Expand All @@ -20,6 +21,7 @@
import com.juu.juulabel.follow.repository.FollowReader;
import com.juu.juulabel.member.domain.*;
import com.juu.juulabel.member.repository.*;
import com.juu.juulabel.member.repository.jpa.MemberJpaRepository;
import com.juu.juulabel.member.request.OAuthLoginInfo;
import com.juu.juulabel.member.request.OAuthUser;
import com.juu.juulabel.member.request.OAuthUserInfo;
Expand Down Expand Up @@ -63,6 +65,7 @@ public class MemberService {
private final WithdrawalRecordWriter withdrawalRecordWriter;
private final WithdrawalRecordReader withdrawalRecordReader;
private final FollowReader followReader;
private final MemberJpaRepository memberJpaRepository;


@Transactional
Expand All @@ -72,9 +75,9 @@ public LoginResponse login(OAuthLoginRequest oAuthLoginRequest) {

// 인가 코드를 이용해 토큰 발급 요청
String accessToken = providerFactory.getAccessToken(
provider,
authLoginInfo.propertyMap().get(AuthConstants.REDIRECT_URI),
authLoginInfo.propertyMap().get(AuthConstants.CODE)
provider,
authLoginInfo.propertyMap().get(AuthConstants.REDIRECT_URI),
authLoginInfo.propertyMap().get(AuthConstants.CODE)
);

// 토큰을 이용해 사용자 정보 가져오기
Expand All @@ -89,9 +92,9 @@ public LoginResponse login(OAuthLoginRequest oAuthLoginRequest) {
Token token = createTokenForMember(isNewMember, email); // TODO : 카카오와 구글 이메일이 같다면 토큰 중복 사용 가능 여부 확인

return new LoginResponse(
token,
isNewMember,
new OAuthUserInfo(email, oAuthUser.id(), provider)
token,
isNewMember,
new OAuthUserInfo(email, oAuthUser.id(), provider)
);
}

Expand All @@ -105,23 +108,23 @@ public SignUpMemberResponse signUp(SignUpMemberRequest signUpRequest) { // TODO

// 선호전통주 주종 등록
List<MemberAlcoholType> memberAlcoholTypeList =
getMemberAlcoholTypeList(member, signUpRequest.alcoholTypeIds());
getMemberAlcoholTypeList(member, signUpRequest.alcoholTypeIds());
if (!memberAlcoholTypeList.isEmpty()) {
memberAlcoholTypeWriter.storeAll(memberAlcoholTypeList);
}

// 약관 등록
List<MemberTerms> memberTerms =
getAndValidateTermsWithMapping(member, signUpRequest.termsAgreements());
getAndValidateTermsWithMapping(member, signUpRequest.termsAgreements());
if (!memberTerms.isEmpty()) {
memberTermsWriter.storeAll(memberTerms);
}

String token = jwtTokenProvider.createAccessToken(member.getEmail());

return new SignUpMemberResponse(
member.getId(),
new Token(token, jwtTokenProvider.getExpirationByToken(token))
member.getId(),
new Token(token, jwtTokenProvider.getExpirationByToken(token))
);
}

Expand All @@ -136,11 +139,11 @@ private Token createTokenForMember(boolean isNewMember, String email) {

private List<MemberAlcoholType> getMemberAlcoholTypeList(Member member, List<Long> alcoholTypeIdList) {
return alcoholTypeIdList.stream()
.map(alcoholTypeId -> {
AlcoholType alcoholType = alcoholTypeReader.getById(alcoholTypeId);
return MemberAlcoholType.create(member, alcoholType);
})
.toList();
.map(alcoholTypeId -> {
AlcoholType alcoholType = alcoholTypeReader.getById(alcoholTypeId);
return MemberAlcoholType.create(member, alcoholType);
})
.toList();
}

private List<MemberTerms> getAndValidateTermsWithMapping(Member member, List<TermsAgreement> termsAgreements) {
Expand All @@ -165,9 +168,9 @@ private List<MemberTerms> getMemberTermsList(Member member, List<Terms> usedTerm

usedTermsList.forEach(terms -> {
TermsAgreement termsAgreement = termsAgreements.stream()
.filter(agreement -> agreement.termsId().equals(terms.getId()))
.findFirst()
.orElseThrow(() -> new InvalidParamException(ErrorCode.NOT_FOUND_TERMS));
.filter(agreement -> agreement.termsId().equals(terms.getId()))
.findFirst()
.orElseThrow(() -> new InvalidParamException(ErrorCode.NOT_FOUND_TERMS));

final boolean isAgreed = termsAgreement.isAgreed();

Expand Down Expand Up @@ -210,15 +213,15 @@ public UpdateProfileResponse updateProfile(Member loginMember, UpdateProfileRequ
@Transactional(readOnly = true)
public MyDailyLifeListResponse loadMyDailyLifeList(Member member, DailyLifeListRequest request) {
Slice<MyDailyLifeSummary> myDailyLifeList =
dailyLifeReader.getAllMyDailyLives(member, request.lastDailyLifeId(), request.pageSize());
dailyLifeReader.getAllMyDailyLives(member, request.lastDailyLifeId(), request.pageSize());

return new MyDailyLifeListResponse(myDailyLifeList);
}

@Transactional(readOnly = true)
public MyTastingNoteListResponse loadMyTastingNoteList(Member member, TastingNoteListRequest request) {
Slice<MyTastingNoteSummary> myTastingNoteList =
tastingNoteReader.getAllMyTastingNotes(member, request.lastTastingNoteId(), request.pageSize());
tastingNoteReader.getAllMyTastingNotes(member, request.lastTastingNoteId(), request.pageSize());

return new MyTastingNoteListResponse(myTastingNoteList);
}
Expand All @@ -227,24 +230,24 @@ public MyTastingNoteListResponse loadMyTastingNoteList(Member member, TastingNot
public boolean saveAlcoholicDrinks(Member member, Long alcoholicDrinksId) {
AlcoholicDrinks alcoholicDrinks = alcoholicDrinksReader.getById(alcoholicDrinksId);
Optional<MemberAlcoholicDrinks> memberAlcoholicDrinks =
memberAlcoholicDrinksReader.findByMemberAndAlcoholicDrinks(member, alcoholicDrinks);
memberAlcoholicDrinksReader.findByMemberAndAlcoholicDrinks(member, alcoholicDrinks);

// 전통주가 이미 저장되어 있다면 삭제, 저장되어 있지 않다면 등록
return memberAlcoholicDrinks
.map(save -> {
memberAlcoholicDrinksWriter.delete(save);
return false;
})
.orElseGet(() -> {
memberAlcoholicDrinksWriter.store(member, alcoholicDrinks);
return true;
});
.map(save -> {
memberAlcoholicDrinksWriter.delete(save);
return false;
})
.orElseGet(() -> {
memberAlcoholicDrinksWriter.store(member, alcoholicDrinks);
return true;
});
}

@Transactional(readOnly = true)
public MyAlcoholicDrinksListResponse loadMyAlcoholicDrinks(Member member, MyAlcoholicDrinksListRequest request) {
Slice<AlcoholicDrinksSummary> alcoholicDrinksSummaries =
alcoholicDrinksReader.getAllMyAlcoholicDrinks(member, request.lastAlcoholicDrinksId(), request.pageSize());
alcoholicDrinksReader.getAllMyAlcoholicDrinks(member, request.lastAlcoholicDrinksId(), request.pageSize());

return new MyAlcoholicDrinksListResponse(alcoholicDrinksSummaries);
}
Expand All @@ -257,39 +260,39 @@ public MySpaceResponse getMySpace(Member member) {
long followerCount = followReader.countFollower(member);

return new MySpaceResponse(
member.getId(),
member.getProfileImage(),
member.getNickname(),
member.getIntroduction(),
member.isHasBadge(),
tastingNoteCount,
dailyLifeCount,
followingCount,
followerCount,
0 // TODO : 시음노트 저장 기능 추가 시 수정 필요
member.getId(),
member.getProfileImage(),
member.getNickname(),
member.getIntroduction(),
member.isHasBadge(),
tastingNoteCount,
dailyLifeCount,
followingCount,
followerCount,
0 // TODO : 시음노트 저장 기능 추가 시 수정 필요
);
}

@Transactional(readOnly = true)
public MyInfoResponse getMyInfo(Member member) {
List<Long> alcoholTypeIdList = memberAlcoholTypeReader.getIdListByMember(member);
return new MyInfoResponse(
member.getId(),
member.getNickname(),
member.getEmail(),
member.isHasBadge(),
member.isNotificationsAllowed(),
member.getIntroduction(),
member.getProfileImage(),
member.getGender(),
alcoholTypeIdList
member.getId(),
member.getNickname(),
member.getEmail(),
member.isHasBadge(),
member.isNotificationsAllowed(),
member.getIntroduction(),
member.getProfileImage(),
member.getGender(),
alcoholTypeIdList
);
}

@Transactional(readOnly = true)
public DailyLifeListResponse loadMemberDailyLifeList(Member loginMember, DailyLifeListRequest request, Long memberId) {
Slice<DailyLifeSummary> dailyLifeList =
dailyLifeReader.getAllDailyLivesByMember(loginMember, memberId, request.lastDailyLifeId(), request.pageSize());
dailyLifeReader.getAllDailyLivesByMember(loginMember, memberId, request.lastDailyLifeId(), request.pageSize());

return new DailyLifeListResponse(dailyLifeList);
}
Expand All @@ -298,7 +301,7 @@ public DailyLifeListResponse loadMemberDailyLifeList(Member loginMember, DailyLi
public TastingNoteListResponse loadMemberTastingNoteList(Member loginMember, TastingNoteListRequest request, Long memberId) {
// TODO : 해당 회원(loginMember) 차단 여부 검증 로직
Slice<TastingNoteSummary> tastingNoteList =
tastingNoteReader.getAllTastingNotesByMember(loginMember, memberId, request.lastTastingNoteId(), request.pageSize());
tastingNoteReader.getAllTastingNotesByMember(loginMember, memberId, request.lastTastingNoteId(), request.pageSize());

return new TastingNoteListResponse(tastingNoteList);
}
Expand All @@ -314,15 +317,15 @@ public MemberProfileResponse getMemberProfile(Member loginMember, Long memberId)
boolean isFollowing = followReader.isFollowing(loginMember, member);

return new MemberProfileResponse(
member.getId(),
member.getNickname(),
member.getProfileImage(),
member.getIntroduction(),
member.isHasBadge(),
tastingNoteCount,
dailyLifeCount,
followingCount,
followerCount,
member.getId(),
member.getNickname(),
member.getProfileImage(),
member.getIntroduction(),
member.isHasBadge(),
tastingNoteCount,
dailyLifeCount,
followingCount,
followerCount,
isFollowing
);
}
Expand All @@ -349,8 +352,14 @@ private void validateEmail(String email) {
public void deleteAccount(Member loginMember, WithdrawalRequest request) {
loginMember.deleteAccount();
withdrawalRecordWriter.store(
WithdrawalRecord.create(request.withdrawalReason(), loginMember.getEmail(), loginMember.getNickname())
WithdrawalRecord.create(request.withdrawalReason(), loginMember.getEmail(), loginMember.getNickname())
);
}

@Transactional(readOnly = true)
public Member findById(Long memberId) {
return memberJpaRepository.findById(memberId)
.orElseThrow(() -> new BaseException(ErrorCode.NOT_FOUND_MEMBER));
}
}

48 changes: 48 additions & 0 deletions src/main/java/com/juu/juulabel/report/Report.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.juu.juulabel.report;

import com.juu.juulabel.common.base.BaseTimeEntity;
import com.juu.juulabel.member.domain.Member;
import jakarta.persistence.*;
import lombok.*;

import java.time.LocalDateTime;

@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Entity
@Table(name = "report")
public class Report extends BaseTimeEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "report_id")
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "reporter_id", nullable = false)
private Member reporter;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "reported_user_id", nullable = false)
private Member reportedUser;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "reviewer")
private Member reviewer;

@Column(name = "reason", nullable = false, length = 255)
private String reason;

@Enumerated(EnumType.STRING)
@Column(name = "type", nullable = false)
private ReportType type;

@Enumerated(EnumType.STRING)
@Column(name = "status", nullable = false)
private ReportStatus status;

@Column(name = "reviewed_at")
private LocalDateTime reviewedAt;
}
Loading
Loading