Skip to content

Commit

Permalink
Merge pull request #15 from Team-Umbba/feat/#13-onboarding_parentchil…
Browse files Browse the repository at this point in the history
…d_info

[FEAT] 온보딩 부모자식 관계 정보 입력(초대하는 측) API
  • Loading branch information
jun02160 authored Jul 9, 2023
2 parents d90142c + dccbcba commit bf96bda
Show file tree
Hide file tree
Showing 17 changed files with 305 additions and 12 deletions.
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ dependencies {

// Social Login
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:3.1.7'

// random String
implementation 'org.apache.commons:commons-lang3'

}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package sopt.org.umbbaServer.domain.parentchild.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import sopt.org.umbbaServer.domain.parentchild.controller.dto.request.OnboardingInviteRequestDto;
import sopt.org.umbbaServer.domain.parentchild.controller.dto.response.OnboardingInviteResponseDto;
import sopt.org.umbbaServer.domain.parentchild.service.ParentchildService;
import sopt.org.umbbaServer.global.common.dto.ApiResponse;
import sopt.org.umbbaServer.global.exception.SuccessType;

import javax.validation.Valid;

@RestController
@RequestMapping("/onboard")
@RequiredArgsConstructor
public class ParentchildController {

private final ParentchildService parentchildService;

@PostMapping("/invite")
@ResponseStatus(HttpStatus.CREATED)
public ApiResponse<OnboardingInviteResponseDto> onboardInvite(@Valid @RequestBody OnboardingInviteRequestDto request) {
return ApiResponse.success(SuccessType.CREATE_PARENT_CHILD_SUCCESS, parentchildService.onboardInvite(request));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package sopt.org.umbbaServer.domain.parentchild.controller.dto.request;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.format.annotation.DateTimeFormat;
import sopt.org.umbbaServer.domain.user.controller.dto.request.UserInfoDto;

import javax.validation.constraints.NotNull;
import java.time.LocalTime;

@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class OnboardingInviteRequestDto {

@NotNull
private UserInfoDto userInfo;

private boolean isInvitorChild;

private String relationInfo; // 아들 or 딸 | 아빠 or 엄마

// TODO 디폴트 값 지정해줘야 할까? -> by 디폴트 생성자
@JsonFormat(pattern = "kk:mm")
private LocalTime pushTime;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package sopt.org.umbbaServer.domain.parentchild.controller.dto.request;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import sopt.org.umbbaServer.domain.user.controller.dto.request.UserInfoDto;

@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class OnboardingReceiveRequestDto {

private UserInfoDto userInfo;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package sopt.org.umbbaServer.domain.parentchild.controller.dto.response;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class OnboadringReceiveResponseDto {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package sopt.org.umbbaServer.domain.parentchild.controller.dto.response;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import sopt.org.umbbaServer.domain.parentchild.model.Parentchild;
import sopt.org.umbbaServer.domain.user.controller.dto.request.UserInfoDto;
import sopt.org.umbbaServer.domain.user.model.User;

import java.time.LocalDate;
import java.time.LocalTime;

@Getter
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class OnboardingInviteResponseDto {

private Long parentchildId;

private UserInfoDto userInfo;

private String parentchildRelation;

private LocalTime pushTime;

private String inviteCode;

public static OnboardingInviteResponseDto of(Parentchild parentchild, User user) {
return OnboardingInviteResponseDto.builder()
.parentchildId(parentchild.getId())
.userInfo(UserInfoDto.of(user))
.parentchildRelation(parentchild.getRelation().getValue())
.pushTime(parentchild.getPushTime())
.inviteCode(parentchild.getInviteCode())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,21 @@ public class Parentchild extends AuditingTimeEntity {
@Column(nullable = false)
private String inviteCode;

@Column(nullable = false)
private Boolean isInvitorChild;
// @Column(nullable = false)
private boolean isInvitorChild;

@Column(nullable = false)
// TODO 기획에 따라 변경사항 있음
private boolean liveTogether;

// @Column(nullable = false)
@Enumerated(EnumType.STRING)
private ParentchildRelation relation;

@Column(nullable = false)
private LocalTime pushTime;
@Column(nullable = false) // TODO 푸시알림 시간 디폴트 값 없으면 nullable: true로 변경
private LocalTime pushTime; // default: 오후 11시

//== 연관관계 메서드 ==//
public void addQna(QnA qna) {
this.qnaList.add(qna);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package sopt.org.umbbaServer.domain.parentchild.respository;

import org.springframework.data.repository.Repository;
import sopt.org.umbbaServer.domain.parentchild.model.Parentchild;

import java.util.Optional;

public interface ParentchildRepository extends Repository<Parentchild, Long> {

// CREATE
void save(Parentchild parentchild);

// READ
Optional<Parentchild> findById(Long id);


// UPDATE

// DELETE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package sopt.org.umbbaServer.domain.parentchild.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.utility.RandomString;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import sopt.org.umbbaServer.domain.parentchild.controller.dto.request.OnboardingInviteRequestDto;
import sopt.org.umbbaServer.domain.parentchild.controller.dto.response.OnboardingInviteResponseDto;
import sopt.org.umbbaServer.domain.parentchild.model.Parentchild;
import sopt.org.umbbaServer.domain.parentchild.model.ParentchildRelation;
import sopt.org.umbbaServer.domain.parentchild.respository.ParentchildRepository;
import sopt.org.umbbaServer.domain.user.model.User;
import sopt.org.umbbaServer.domain.user.repository.UserRepository;
import sopt.org.umbbaServer.global.exception.CustomException;
import sopt.org.umbbaServer.global.exception.ErrorType;

@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class ParentchildService {

private final ParentchildRepository parentchildRepository;
private final UserRepository userRepository;

// [발신] 초대하는 측의 온보딩 정보 입력
@Transactional
public OnboardingInviteResponseDto onboardInvite(OnboardingInviteRequestDto request) {

User user = userRepository.findById(request.getUserInfo().getUserId()).orElseThrow(
() -> new CustomException(ErrorType.INVALID_USER)
);
user.updateOnboardingInfo(
request.getUserInfo().getName(),
request.getUserInfo().getGender(),
request.getUserInfo().getBornYear()
);

Parentchild parentchild = Parentchild.builder()
.inviteCode(generateInviteCode())
.isInvitorChild(request.isInvitorChild())
.relation(getRelation(request.getUserInfo().getGender(), request.getRelationInfo(), request.isInvitorChild()))
.pushTime(request.getPushTime()) // TODO 케이스에 따라 없을 수도 있음
.build();
parentchildRepository.save(parentchild);

log.info("userInfo: {}", request.getUserInfo().getBornYear());
return OnboardingInviteResponseDto.of(parentchild, user);
}

// [수신] 초대받는 측의 온보딩 정보 입력
// public OnboadringReceiveResponseDto

// 부모자식 관계 케이스 분류하기
private ParentchildRelation getRelation(String gender, String relationInfo, boolean isInvitorChild) {

// 내가 부모다
if (!isInvitorChild) {
if (gender.equals("남자")) { // 아빠
if (relationInfo.equals("아들")) {
return ParentchildRelation.DAD_SON;
} else if (relationInfo.equals("딸")) {
return ParentchildRelation.DAD_DAU; // TODO 클라에서 둘 중 하나의 값만 받도록 처리하니까 else if 구문 빼도 무관
}
} else if(gender.equals("여자")) { // 엄마
if (relationInfo.equals("아들")) {
return ParentchildRelation.MOM_SON;
} else if (relationInfo.equals("딸")) {
return ParentchildRelation.DAD_DAU;
}
}
} else { // 내가 자식이다
if (gender.equals("남자")) { // 아들
if (relationInfo.equals("아빠")) {
return ParentchildRelation.DAD_SON;
} else if (relationInfo.equals("엄마")) {
return ParentchildRelation.MOM_SON;
}
} else if(gender.equals("여자")) { // 딸
if (relationInfo.equals("아빠")) {
return ParentchildRelation.DAD_DAU;
} else if (relationInfo.equals("엄마")) {
return ParentchildRelation.MOM_DAU;
}
}
}

throw new CustomException(ErrorType.INVALID_PARENT_CHILD_RELATION_INFO);
}

// 초대코드 생성 (형식예시: WUHZ-iGbPX9X)
private String generateInviteCode() {
return RandomStringUtils.randomAlphabetic(4).toUpperCase() +
"-" + RandomStringUtils.randomAlphanumeric(6);
}
}
4 changes: 2 additions & 2 deletions src/main/java/sopt/org/umbbaServer/domain/qna/model/QnA.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ public class QnA extends AuditingTimeEntity {
private String childAnswer;

@Column(nullable = false)
private Boolean isParentAnswer;
private boolean isParentAnswer;

@Column(nullable = false)
private Boolean isChildAnswer;
private boolean isChildAnswer;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package sopt.org.umbbaServer.domain.user.controller.dto.request;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import sopt.org.umbbaServer.domain.user.model.User;

import javax.validation.constraints.NotBlank;

@Getter
@Builder
@AllArgsConstructor
public class UserInfoDto {

private Long userId;

@NotBlank(message = "이름은 필수 입력 값입니다.")
private String name;

@NotBlank(message = "성별은 필수 입력 값입니다.")
private String gender;

@NotBlank(message = "출생연도는 필수 입력 값입니다.")
private int bornYear;

public static UserInfoDto of(User user) {
return UserInfoDto.builder()
.userId(user.getId())
.name(user.getUsername())
.gender(user.getGender())
.bornYear(user.getBornYear())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class UserLoginResponseDto {

private String gender;

private Integer bornYear;
private int bornYear;

private TokenDto tokenDto;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class User extends AuditingTimeEntity {
private Integer bornYear;

@Column(nullable = false)
private Boolean hasAlarm;
private boolean hasAlarm;

@ManyToOne
@JoinColumn(name = "parentchild_id")
Expand Down Expand Up @@ -67,6 +67,12 @@ public void updateSocialInfo(String socialNickname, String socialProfileImage, S
// this.socialRefreshToken = socialRefreshToken;
}

public void updateOnboardingInfo(String name, String gender, Integer bornYear) {
this.username = name;
this.gender = gender;
this.bornYear = bornYear;
}

public User(SocialPlatform socialPlatform, String socialId) {
this.socialPlatform = socialPlatform;
this.socialId = socialId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class ApiResponse<T> {

private final int code;
private final int status;
private final String message;

@JsonInclude(JsonInclude.Include.NON_NULL)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
@RequiredArgsConstructor
public class JwtProvider {

private static final Long ACCESS_TOKEN_EXPIRATION_TIME = 60 * 1000L; // 액세스 토큰 만료 시간: 1분으로 지정
private static final Long REFRESH_TOKEN_EXPIRATION_TIME = 60 * 1000L * 2; // 리프레시 토큰 만료 시간: 2분으로 지정
private static final Long ACCESS_TOKEN_EXPIRATION_TIME = 60 * 1000L * 60; // 액세스 토큰 만료 시간: 1시간으로 지정
private static final Long REFRESH_TOKEN_EXPIRATION_TIME = 60 * 1000L * 180; // 리프레시 토큰 만료 시간: 3시간으로 지정

@Value("${jwt.secret}")
private String JWT_SECRET;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ public enum ErrorType {
EMPTY_PRINCIPLE_EXCEPTION(HttpStatus.BAD_REQUEST, "Principle 객체가 없습니다. (null)"),
HEADER_REQUEST_MISSING_EXCEPTION(HttpStatus.BAD_REQUEST, "요청에 필요한 헤더값이 존재하지 않습니다."),

// ParentChild - Onboarding
INVALID_PARENT_CHILD_RELATION_INFO(HttpStatus.BAD_REQUEST, "부모자식 관계를 정의할 수 없는 요청값입니다."),


/**
* 401 UNAUTHORIZED
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ public enum SuccessType {
/**
* 201 CREATED
*/
CREATE_PARENT_CHILD_SUCCESS(HttpStatus.CREATED, "온보딩 정보를 입력받아 부모자식 관계를 생성하는 데 성공했습니다."),


;

private final HttpStatus httpStatus;
Expand Down

0 comments on commit bf96bda

Please sign in to comment.