From 4208e22e11b9028b055faa76e4b0c8ff2f86b082 Mon Sep 17 00:00:00 2001 From: Yeongjin Noh <129354455+nohy6630@users.noreply.github.com> Date: Wed, 15 Nov 2023 22:56:24 +0900 Subject: [PATCH] =?UTF-8?q?Feat:=20=EC=98=A8=EB=B3=B4=EB=94=A9=20API=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#62)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat(#60): 온보딩 API 구현 * Feat(#60): 회원가입시 소설에서 사용자 이름정보 받아오기 --- .../member/controller/MemberController.java | 15 +++++ .../member/dto/request/OnboardingAddress.java | 19 +++++++ .../dto/request/OnboardingDisasterType.java | 13 +++++ .../member/dto/request/OnboardingRequest.java | 21 +++++++ .../backend/domain/member/entity/Member.java | 26 ++++++++- .../domain/member/service/MemberService.java | 37 +++++++++++- .../entity/NotificationDisaster.java | 40 +++++++++++++ .../NotificationDisasterRepository.java | 8 +++ .../entity/NotificationRegion.java | 56 +++++++++++++++++++ .../NotificationRegionRepository.java | 8 +++ .../token/dto/response/KakaoInfoResponse.java | 6 +- .../domain/token/service/TokenService.java | 16 +++--- 12 files changed, 253 insertions(+), 12 deletions(-) create mode 100644 src/main/java/com/numberone/backend/domain/member/dto/request/OnboardingAddress.java create mode 100644 src/main/java/com/numberone/backend/domain/member/dto/request/OnboardingDisasterType.java create mode 100644 src/main/java/com/numberone/backend/domain/member/dto/request/OnboardingRequest.java create mode 100644 src/main/java/com/numberone/backend/domain/notificationdisaster/entity/NotificationDisaster.java create mode 100644 src/main/java/com/numberone/backend/domain/notificationdisaster/repository/NotificationDisasterRepository.java create mode 100644 src/main/java/com/numberone/backend/domain/notificationregion/entity/NotificationRegion.java create mode 100644 src/main/java/com/numberone/backend/domain/notificationregion/repository/NotificationRegionRepository.java diff --git a/src/main/java/com/numberone/backend/domain/member/controller/MemberController.java b/src/main/java/com/numberone/backend/domain/member/controller/MemberController.java index 92e19892..c8609d97 100644 --- a/src/main/java/com/numberone/backend/domain/member/controller/MemberController.java +++ b/src/main/java/com/numberone/backend/domain/member/controller/MemberController.java @@ -1,5 +1,6 @@ package com.numberone.backend.domain.member.controller; +import com.numberone.backend.domain.member.dto.request.OnboardingRequest; import com.numberone.backend.domain.member.dto.request.BuyHeartRequest; import com.numberone.backend.domain.member.dto.response.HeartCntResponse; import com.numberone.backend.domain.member.service.MemberService; @@ -7,6 +8,12 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; @@ -36,4 +43,12 @@ public ResponseEntity buyHeart(@RequestBody @Valid BuyHeartReq public ResponseEntity getHeart(Authentication authentication) { return ResponseEntity.ok(memberService.getHeart(authentication.getName())); } + + @Operation(summary = "온보딩시 사용자 초기 데이터 설정하기", description = """ + 온보딩에서 선택한 닉네임, 재난유형, 알림지역 데이터를 body에 담아 전달해주세요. + """) + @PostMapping("/onboarding") + public void initMemberData(Authentication authentication, @Valid @RequestBody OnboardingRequest onboardingRequest){ + memberService.initMemberData(authentication.getName(), onboardingRequest); + } } diff --git a/src/main/java/com/numberone/backend/domain/member/dto/request/OnboardingAddress.java b/src/main/java/com/numberone/backend/domain/member/dto/request/OnboardingAddress.java new file mode 100644 index 00000000..7445c329 --- /dev/null +++ b/src/main/java/com/numberone/backend/domain/member/dto/request/OnboardingAddress.java @@ -0,0 +1,19 @@ +package com.numberone.backend.domain.member.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class OnboardingAddress { + @Schema(defaultValue = "서울특별시") + private String lv1; + + @Schema(defaultValue = "영등포구") + private String lv2; + + @Schema(defaultValue = "신길동") + private String lv3; +} diff --git a/src/main/java/com/numberone/backend/domain/member/dto/request/OnboardingDisasterType.java b/src/main/java/com/numberone/backend/domain/member/dto/request/OnboardingDisasterType.java new file mode 100644 index 00000000..556c353e --- /dev/null +++ b/src/main/java/com/numberone/backend/domain/member/dto/request/OnboardingDisasterType.java @@ -0,0 +1,13 @@ +package com.numberone.backend.domain.member.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class OnboardingDisasterType { + @Schema(defaultValue = "화재") + private String disasterType; +} diff --git a/src/main/java/com/numberone/backend/domain/member/dto/request/OnboardingRequest.java b/src/main/java/com/numberone/backend/domain/member/dto/request/OnboardingRequest.java new file mode 100644 index 00000000..7b06563d --- /dev/null +++ b/src/main/java/com/numberone/backend/domain/member/dto/request/OnboardingRequest.java @@ -0,0 +1,21 @@ +package com.numberone.backend.domain.member.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class OnboardingRequest { + @Schema(defaultValue = "초롱이") + @NotNull + private String nickname; + + private List addresses; + + private List disasterTypes; +} diff --git a/src/main/java/com/numberone/backend/domain/member/entity/Member.java b/src/main/java/com/numberone/backend/domain/member/entity/Member.java index 4106b3cf..42e29c4f 100644 --- a/src/main/java/com/numberone/backend/domain/member/entity/Member.java +++ b/src/main/java/com/numberone/backend/domain/member/entity/Member.java @@ -1,5 +1,7 @@ package com.numberone.backend.domain.member.entity; +import com.numberone.backend.domain.notificationdisaster.entity.NotificationDisaster; +import com.numberone.backend.domain.notificationregion.entity.NotificationRegion; import com.numberone.backend.domain.support.entity.Support; import jakarta.persistence.*; import lombok.AccessLevel; @@ -19,27 +21,47 @@ public class Member { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Comment("이메일") private String email; + @Comment("닉네임") + private String nickname; + + @Comment("본명") + private String realname; + @Comment("마음 갯수") private Integer heartCnt; @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) private List supports = new ArrayList<>(); + @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) + private List notificationDisasters = new ArrayList<>(); + + @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) + private List notificationRegions = new ArrayList<>(); + @Builder - public Member(String email, Integer heartCnt) { + public Member(String email, String nickname, String realname, Integer heartCnt) { this.email = email; + this.nickname = nickname; + this.realname = realname; this.heartCnt = heartCnt; } - public static Member of(String email) { + public static Member of(String email, String realname) { return Member.builder() .email(email) + .realname(realname) .heartCnt(0) .build(); } + public void updateNickname(String nickname) { + this.nickname = nickname; + } + public void plusHeart(int heart) { heartCnt += heart; } diff --git a/src/main/java/com/numberone/backend/domain/member/service/MemberService.java b/src/main/java/com/numberone/backend/domain/member/service/MemberService.java index 3da992d7..4f222276 100644 --- a/src/main/java/com/numberone/backend/domain/member/service/MemberService.java +++ b/src/main/java/com/numberone/backend/domain/member/service/MemberService.java @@ -1,9 +1,17 @@ package com.numberone.backend.domain.member.service; +import com.numberone.backend.domain.disaster.util.DisasterType; +import com.numberone.backend.domain.member.dto.request.OnboardingAddress; +import com.numberone.backend.domain.member.dto.request.OnboardingDisasterType; +import com.numberone.backend.domain.member.dto.request.OnboardingRequest; import com.numberone.backend.domain.member.dto.request.BuyHeartRequest; import com.numberone.backend.domain.member.dto.response.HeartCntResponse; import com.numberone.backend.domain.member.entity.Member; import com.numberone.backend.domain.member.repository.MemberRepository; +import com.numberone.backend.domain.notificationdisaster.entity.NotificationDisaster; +import com.numberone.backend.domain.notificationdisaster.repository.NotificationDisasterRepository; +import com.numberone.backend.domain.notificationregion.entity.NotificationRegion; +import com.numberone.backend.domain.notificationregion.repository.NotificationRegionRepository; import com.numberone.backend.exception.notfound.NotFoundMemberException; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -14,6 +22,8 @@ @Transactional(readOnly = true) public class MemberService { private final MemberRepository memberRepository; + private final NotificationDisasterRepository notificationDisasterRepository; + private final NotificationRegionRepository notificationRegionRepository; public Member findByEmail(String email) { return memberRepository.findByEmail(email) @@ -21,8 +31,31 @@ public Member findByEmail(String email) { } @Transactional - public void create(String email) { - memberRepository.save(Member.of(email)); + public void create(String email, String realname) { + memberRepository.save(Member.of(email, realname)); + } + + @Transactional + public void initMemberData(String email, OnboardingRequest onboardingRequest) { + Member member = memberRepository.findByEmail(email) + .orElseThrow(NotFoundMemberException::new); + member.updateNickname(onboardingRequest.getNickname()); + notificationDisasterRepository.deleteAllByMemberId(member.getId()); + notificationRegionRepository.deleteAllByMemberId(member.getId()); + for (OnboardingAddress address : onboardingRequest.getAddresses()) { + notificationRegionRepository.save(NotificationRegion.of( + address.getLv1(), + address.getLv2(), + address.getLv3(), + member + )); + } + for (OnboardingDisasterType disasterType : onboardingRequest.getDisasterTypes()) { + notificationDisasterRepository.save(NotificationDisaster.of( + DisasterType.kor2code(disasterType.getDisasterType()), + member + )); + } } @Transactional diff --git a/src/main/java/com/numberone/backend/domain/notificationdisaster/entity/NotificationDisaster.java b/src/main/java/com/numberone/backend/domain/notificationdisaster/entity/NotificationDisaster.java new file mode 100644 index 00000000..650272b6 --- /dev/null +++ b/src/main/java/com/numberone/backend/domain/notificationdisaster/entity/NotificationDisaster.java @@ -0,0 +1,40 @@ +package com.numberone.backend.domain.notificationdisaster.entity; + +import com.numberone.backend.domain.disaster.util.DisasterType; +import com.numberone.backend.domain.member.entity.Member; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.Comment; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class NotificationDisaster { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Comment("재난 유형") + @Enumerated(EnumType.STRING) + private DisasterType disasterType; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @Builder + public NotificationDisaster(DisasterType disasterType, Member member) { + this.disasterType = disasterType; + this.member = member; + } + + public static NotificationDisaster of(DisasterType disasterType, Member member) { + return NotificationDisaster.builder() + .disasterType(disasterType) + .member(member) + .build(); + } +} diff --git a/src/main/java/com/numberone/backend/domain/notificationdisaster/repository/NotificationDisasterRepository.java b/src/main/java/com/numberone/backend/domain/notificationdisaster/repository/NotificationDisasterRepository.java new file mode 100644 index 00000000..1d79e31e --- /dev/null +++ b/src/main/java/com/numberone/backend/domain/notificationdisaster/repository/NotificationDisasterRepository.java @@ -0,0 +1,8 @@ +package com.numberone.backend.domain.notificationdisaster.repository; + +import com.numberone.backend.domain.notificationdisaster.entity.NotificationDisaster; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface NotificationDisasterRepository extends JpaRepository { + void deleteAllByMemberId(Long memberId); +} diff --git a/src/main/java/com/numberone/backend/domain/notificationregion/entity/NotificationRegion.java b/src/main/java/com/numberone/backend/domain/notificationregion/entity/NotificationRegion.java new file mode 100644 index 00000000..5d9cee4e --- /dev/null +++ b/src/main/java/com/numberone/backend/domain/notificationregion/entity/NotificationRegion.java @@ -0,0 +1,56 @@ +package com.numberone.backend.domain.notificationregion.entity; + +import com.numberone.backend.domain.member.entity.Member; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.Comment; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class NotificationRegion { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Comment("법정동 주소") + private String location; + + @Comment("시/도") + private String lv1; + + @Comment("구/군") + private String lv2; + + @Comment("동/읍/면") + private String lv3; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @Builder + public NotificationRegion(String lv1, String lv2, String lv3, Member member) { + this.lv1 = lv1; + this.lv2 = lv2; + this.lv3 = lv3; + this.location = (lv1 + " " + lv2 + " " + lv3) + .replace("null", "") + .replace(" ", " ") + .trim(); + this.member = member; + } + + public static NotificationRegion of(String lv1, String lv2, String lv3, Member member) { + return NotificationRegion.builder() + .lv1(lv1) + .lv2(lv2) + .lv3(lv3) + .member(member) + .build(); + } +} diff --git a/src/main/java/com/numberone/backend/domain/notificationregion/repository/NotificationRegionRepository.java b/src/main/java/com/numberone/backend/domain/notificationregion/repository/NotificationRegionRepository.java new file mode 100644 index 00000000..6c9b2f09 --- /dev/null +++ b/src/main/java/com/numberone/backend/domain/notificationregion/repository/NotificationRegionRepository.java @@ -0,0 +1,8 @@ +package com.numberone.backend.domain.notificationregion.repository; + +import com.numberone.backend.domain.notificationregion.entity.NotificationRegion; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface NotificationRegionRepository extends JpaRepository { + void deleteAllByMemberId(Long memberId); +} diff --git a/src/main/java/com/numberone/backend/domain/token/dto/response/KakaoInfoResponse.java b/src/main/java/com/numberone/backend/domain/token/dto/response/KakaoInfoResponse.java index 694fb425..ec15a41f 100644 --- a/src/main/java/com/numberone/backend/domain/token/dto/response/KakaoInfoResponse.java +++ b/src/main/java/com/numberone/backend/domain/token/dto/response/KakaoInfoResponse.java @@ -31,7 +31,11 @@ public class Properties { @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) public class KakaoAccount { - static class profile { + @Getter + @NoArgsConstructor + @ToString + @JsonIgnoreProperties(ignoreUnknown = true) + public class profile { private String nickname; private String thumbnail_image_url; private String profile_image_url; diff --git a/src/main/java/com/numberone/backend/domain/token/service/TokenService.java b/src/main/java/com/numberone/backend/domain/token/service/TokenService.java index 1064362b..52658fe6 100644 --- a/src/main/java/com/numberone/backend/domain/token/service/TokenService.java +++ b/src/main/java/com/numberone/backend/domain/token/service/TokenService.java @@ -42,7 +42,8 @@ public GetTokenResponse loginKakao(GetTokenRequest tokenRequest) { try { ResponseEntity response = restTemplate.exchange(kakaoProperties.getUser_api_url(), HttpMethod.GET, new HttpEntity<>(null, headers), KakaoInfoResponse.class); String email = response.getBody().getKakao_account().getEmail(); - return getTokenResponse(email); + String realname = response.getBody().getKakao_account().getProfile().getNickname(); + return getTokenResponse(email, realname); } catch (Exception e) { throw new BadRequestSocialTokenException(); } @@ -57,7 +58,8 @@ public GetTokenResponse loginNaver(GetTokenRequest tokenRequest) { try { ResponseEntity response = restTemplate.exchange(naverProperties.getUser_api_url(), HttpMethod.GET, new HttpEntity<>(null, headers), NaverInfoResponse.class); String email = response.getBody().getResponse().getEmail(); - return getTokenResponse(email); + String realname = response.getBody().getResponse().getName(); + return getTokenResponse(email, realname); } catch (Exception e) { throw new BadRequestSocialTokenException(); } @@ -65,21 +67,21 @@ public GetTokenResponse loginNaver(GetTokenRequest tokenRequest) { @Transactional public RefreshTokenResponse refresh(RefreshTokenRequest tokenRequest) { - if(!jwtUtil.isValid(tokenRequest.getToken())) + if (!jwtUtil.isValid(tokenRequest.getToken())) throw new NotFoundRefreshTokenException(); Token token = tokenRepository.findByRefreshToken(tokenRequest.getToken()) .orElseThrow(NotFoundRefreshTokenException::new); String email = jwtUtil.getEmail(tokenRequest.getToken()); String newAccessToken = jwtUtil.createToken(email, accessPeroid); String newRefreshToken = jwtUtil.createToken(email, refreshPeroid); - token.update(newAccessToken,newRefreshToken); + token.update(newAccessToken, newRefreshToken); tokenRepository.save(token);//redis의 경우 jpa와 달리 transactional을 이용해도 데이터 수정시에 명시적으로 save를 해줘야 함 - return RefreshTokenResponse.of(newAccessToken,newRefreshToken); + return RefreshTokenResponse.of(newAccessToken, newRefreshToken); } - private GetTokenResponse getTokenResponse(String email) { + private GetTokenResponse getTokenResponse(String email, String realname) { if (!memberRepository.existsByEmail(email)) - memberService.create(email); + memberService.create(email, realname); if (tokenRepository.existsById(email)) { Token token = tokenRepository.findById(email) .orElseThrow(WrongAccessTokenException::new);