Skip to content

Commit

Permalink
Feat: 온보딩 API 구현 (#62)
Browse files Browse the repository at this point in the history
* Feat(#60): 온보딩 API 구현

* Feat(#60): 회원가입시 소설에서 사용자 이름정보 받아오기
  • Loading branch information
nohy6630 authored Nov 15, 2023
1 parent f3d9922 commit 4208e22
Show file tree
Hide file tree
Showing 12 changed files with 253 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
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;
import io.swagger.v3.oas.annotations.Operation;
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;
Expand Down Expand Up @@ -36,4 +43,12 @@ public ResponseEntity<HeartCntResponse> buyHeart(@RequestBody @Valid BuyHeartReq
public ResponseEntity<HeartCntResponse> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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<OnboardingAddress> addresses;

private List<OnboardingDisasterType> disasterTypes;
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<Support> supports = new ArrayList<>();

@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
private List<NotificationDisaster> notificationDisasters = new ArrayList<>();

@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
private List<NotificationRegion> 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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -14,15 +22,40 @@
@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)
.orElseThrow(NotFoundMemberException::new);
}

@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
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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<NotificationDisaster, Long> {
void deleteAllByMemberId(Long memberId);
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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<NotificationRegion, Long> {
void deleteAllByMemberId(Long memberId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ public GetTokenResponse loginKakao(GetTokenRequest tokenRequest) {
try {
ResponseEntity<KakaoInfoResponse> 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();
}
Expand All @@ -57,29 +58,30 @@ public GetTokenResponse loginNaver(GetTokenRequest tokenRequest) {
try {
ResponseEntity<NaverInfoResponse> 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();
}
}

@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);
Expand Down

0 comments on commit 4208e22

Please sign in to comment.