Skip to content

Commit

Permalink
Merge pull request Kernel360#54 from fingersdanny/feat-login
Browse files Browse the repository at this point in the history
[feat] : 로그인 기능
  • Loading branch information
hodadako authored Jan 4, 2024
2 parents f4a4d5c + 056e983 commit 695373e
Show file tree
Hide file tree
Showing 49 changed files with 1,735 additions and 863 deletions.
38 changes: 25 additions & 13 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,31 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
java-version: '21'
distribution: 'temurin'
- name: Run chmod to make gradlew executable
run: chmod +x ./gradlew

- name: Build with Gradle
uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # v2.6.0
with:
arguments: build
- uses: actions/checkout@v3
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
java-version: '21'
distribution: 'temurin'
- name: Set test yml
uses: microsoft/variable-substitution@v1
with:
files: ./src/test/resources/application.yml
env:
spring.security.jwt.secret: ${{ secrets.JWT_SECRET_KEY }}
- name: Set main yml
uses: microsoft/variable-substitution@v1
with:
files: ./src/main/resources/application.yml
env:
spring.security.jwt.secret: ${{ secrets.JWT_SECRET_KEY }}
- name: Run chmod to make gradlew executable
run: chmod +x ./gradlew

- name: Build with Gradle
uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # v2.6.0
with:
arguments: build

# 나중을 위한 자동 CI/CD
# - name: Make Zip File
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,30 @@
import com.kernel360.kernelsquare.domain.answer.entity.Answer;
import com.kernel360.kernelsquare.domain.member.entity.Member;
import com.kernel360.kernelsquare.domain.question.entity.Question;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Builder;

public record CreateAnswerRequest (
@NotNull
Long memberId,
@NotBlank
String content,
String imageUrl
@Builder
public record CreateAnswerRequest(
@NotNull
Long memberId,
@NotBlank
String content,
String imageUrl
) {
public static Answer toEntity(
CreateAnswerRequest createAnswerRequest,
Question question,
Member member
) {
return Answer.builder()
.imageUrl(createAnswerRequest.imageUrl())
.member(member)
.question(question)
.content(createAnswerRequest.content())
.voteCount(0L)
.build();
}
public static Answer toEntity(
CreateAnswerRequest createAnswerRequest,
Question question,
Member member
) {
return Answer.builder()
.imageUrl(createAnswerRequest.imageUrl())
.member(member)
.question(question)
.content(createAnswerRequest.content())
.voteCount(0L)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package com.kernel360.kernelsquare.domain.answer.service;

import java.util.List;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.kernel360.kernelsquare.domain.answer.dto.CreateAnswerRequest;
import com.kernel360.kernelsquare.domain.answer.dto.FindAnswerResponse;
import com.kernel360.kernelsquare.domain.answer.dto.UpdateAnswerRequest;
Expand All @@ -13,48 +18,48 @@
import com.kernel360.kernelsquare.global.common_response.error.code.MemberErrorCode;
import com.kernel360.kernelsquare.global.common_response.error.code.QuestionErrorCode;
import com.kernel360.kernelsquare.global.common_response.error.exception.BusinessException;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;


@Service
@RequiredArgsConstructor
public class AnswerService {
private final AnswerRepository answerRepository;
private final MemberRepository memberRepository;
private final QuestionRepository questionRepository;

@Transactional(readOnly = true)
public List<FindAnswerResponse> findAllAnswer(Long questionId) {
return answerRepository.findAnswersByQuestionIdSortedByCreationDate(questionId)
.stream()
.map(FindAnswerResponse::from)
.toList();
}

@Transactional
public Long createAnswer(CreateAnswerRequest createAnswerRequest, Long questionId) {
Member member = memberRepository.findById(createAnswerRequest.memberId())
.orElseThrow(() -> new BusinessException(MemberErrorCode.MEMBER_NOT_FOUND));
Question question = questionRepository.findById(questionId)
.orElseThrow(() -> new BusinessException(QuestionErrorCode.QUESTION_NOT_FOUND));
Answer answer = CreateAnswerRequest.toEntity(createAnswerRequest, question, member);
answerRepository.save(answer);
return answer.getId();
}

@Transactional
public void updateAnswer(UpdateAnswerRequest updateAnswerRequest, Long answerId) {
Answer answer = answerRepository.findById(answerId)
.orElseThrow(() -> new BusinessException(AnswerErrorCode.ANSWER_NOT_FOUND));

answer.update(updateAnswerRequest.content(), updateAnswerRequest.imageUrl());
}

@Transactional
public void deleteAnswer(Long answerId) {
answerRepository.deleteById(answerId);
}
private final AnswerRepository answerRepository;
private final MemberRepository memberRepository;
private final QuestionRepository questionRepository;

@Transactional(readOnly = true)
public List<FindAnswerResponse> findAllAnswer(Long questionId) {
return answerRepository.findAnswersByQuestionIdSortedByCreationDate(questionId)
.stream()
.map(FindAnswerResponse::from)
.toList();
}

@Transactional
public Long createAnswer(CreateAnswerRequest createAnswerRequest, Long questionId) {
Member member = memberRepository.findById(createAnswerRequest.memberId())
.orElseThrow(() -> new BusinessException(MemberErrorCode.MEMBER_NOT_FOUND));
Question question = questionRepository.findById(questionId)
.orElseThrow(() -> new BusinessException(QuestionErrorCode.QUESTION_NOT_FOUND));
Answer answer = CreateAnswerRequest.toEntity(createAnswerRequest, question, member);
answerRepository.save(answer);
return answer.getId();
}

@Transactional
public void updateAnswer(UpdateAnswerRequest updateAnswerRequest, Long answerId) {
Answer answer = answerRepository.findById(answerId)
.orElseThrow(() -> new BusinessException(AnswerErrorCode.ANSWER_NOT_FOUND));

answer.update(updateAnswerRequest.content(), updateAnswerRequest.imageUrl());
}

@Transactional
public void deleteAnswer(Long answerId) {
answerRepository.deleteById(answerId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.kernel360.kernelsquare.domain.auth.controller;

import static com.kernel360.kernelsquare.global.common_response.response.code.AuthResponseCode.*;

import org.springframework.http.ResponseEntity;
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 com.kernel360.kernelsquare.domain.auth.dto.LoginRequest;
import com.kernel360.kernelsquare.domain.auth.dto.LoginResponse;
import com.kernel360.kernelsquare.domain.auth.dto.SignUpRequest;
import com.kernel360.kernelsquare.domain.auth.dto.SignUpResponse;
import com.kernel360.kernelsquare.domain.auth.dto.TokenDto;
import com.kernel360.kernelsquare.domain.auth.service.AuthService;
import com.kernel360.kernelsquare.global.common_response.ApiResponse;
import com.kernel360.kernelsquare.global.common_response.ResponseEntityFactory;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping("/api/v1")
@RequiredArgsConstructor
public class AuthController {
private final AuthService authService;

@PostMapping("/auth/login")
public ResponseEntity<ApiResponse<LoginResponse>> login(final @RequestBody @Valid LoginRequest loginRequest) {
LoginResponse loginResponse = authService.login(loginRequest);
return ResponseEntityFactory.toResponseEntity(LOGIN_SUCCESS, loginResponse);
}

@PostMapping("/auth/signup")
public ResponseEntity<ApiResponse<SignUpResponse>> signUp(final @RequestBody @Valid SignUpRequest signUpRequest) {
SignUpResponse signUpResponse = authService.signUp(signUpRequest);
return ResponseEntityFactory.toResponseEntity(SIGN_UP_SUCCESS, signUpResponse);
}

@PostMapping("/auth/reissue")
public ResponseEntity<ApiResponse<TokenDto>> reissueAccessToken(final @RequestBody TokenDto requestTokenDto) {
TokenDto tokenDto = authService.reissueAccessToken(requestTokenDto);
return ResponseEntityFactory.toResponseEntity(ACCESS_TOKEN_REISSUED, tokenDto);
}

@PostMapping("/auth/check/email")
public ResponseEntity<ApiResponse> isEmailUnique(final @RequestBody SignUpRequest signUpRequest) {
authService.isEmailUnique(signUpRequest.email());
return ResponseEntityFactory.toResponseEntity(EMAIL_UNIQUE_VALIDATED);
}

@PostMapping("/auth/check/nickname")
public ResponseEntity<ApiResponse> isNicknameUnique(final @RequestBody SignUpRequest signUpRequest) {
authService.isNicknameUnique(signUpRequest.nickname());
return ResponseEntityFactory.toResponseEntity(NICKNAME_UNIQUE_VALIDATED);
}

@PostMapping("/auth/logout")
public ResponseEntity<ApiResponse> logout(final @RequestBody TokenDto tokenDto) {
authService.logout(tokenDto);
return ResponseEntityFactory.toResponseEntity(LOGOUT_SUCCESS);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.kernel360.kernelsquare.domain.auth.dto;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public record LoginRequest(
@NotBlank(message = "이메일은 필수 입력 항목입니다.")
@Email(message = "이메일 형식을 확인해 주세요")
@Size(min = 5, max = 40, message = "이메일 길이를 확인해 주세요")
String email,

@NotBlank(message = "비밀번호는 필수 입력 항목입니다.")
@Size(min = 8, max = 16, message = "비밀번호 길이를 확인해 주세요")
String password
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.kernel360.kernelsquare.domain.auth.dto;

import java.util.Collection;
import java.util.List;

import org.springframework.security.core.GrantedAuthority;

import com.kernel360.kernelsquare.domain.member.entity.Member;

import lombok.Builder;

@Builder
public record LoginResponse(
Long memberId,
String nickname,
Long experience,
String introduction,
String imageUrl,
Long level,
List<String> roles,
TokenDto tokenDto
) {

public static LoginResponse of(Member member, Collection<? extends GrantedAuthority> authorities,
TokenDto tokenDto) {
List<String> roles = authorities.stream()
.map(String::valueOf)
.toList();

return LoginResponse.builder()
.memberId(member.getId())
.nickname(member.getNickname())
.experience(member.getExperience())
.introduction(member.getIntroduction())
.imageUrl(member.getImageUrl())
.level(member.getLevel().getName())
.roles(roles)
.tokenDto(tokenDto)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.kernel360.kernelsquare.domain.auth.dto;

import com.kernel360.kernelsquare.domain.level.entity.Level;
import com.kernel360.kernelsquare.domain.member.entity.Member;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public record SignUpRequest(
@NotBlank(message = "이메일은 필수 입력 항목입니다.")
@Email(message = "이메일 형식을 확인해 주세요")
@Size(min = 5, max = 40, message = "이메일 길이를 확인해 주세요")
String email,

@NotBlank(message = "닉네임은 필수 입력 항목입니다.")
@Size(min = 2, max = 8, message = "닉네임 길이를 확인해 주세요")
String nickname,

@NotBlank(message = "비밀번호는 필수 입력 항목입니다.")
@Size(min = 8, max = 16, message = "비밀번호 길이를 확인해 주세요")
String password
) {

public static Member toEntity(SignUpRequest signUpRequest, String password, Level
level) {
return Member.builder()
.email(signUpRequest.email())
.nickname(signUpRequest.nickname())
.password(password)
.experience(0L)
.imageUrl(null)
.introduction(null)
.level(level)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.kernel360.kernelsquare.domain.auth.dto;

import com.kernel360.kernelsquare.domain.member.entity.Member;

public record SignUpResponse(
Long memberId
) {

public static SignUpResponse of(Member member) {
return new SignUpResponse(member.getId());
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.kernel360.kernelsquare.domain.auth.dto;

import lombok.Builder;

@Builder
public record TokenDto(
String accessToken,
String refreshToken) {
Expand All @@ -8,8 +11,10 @@ public static TokenDto of(
String accessToken,
String refreshToken
) {
return new TokenDto(
accessToken,
refreshToken);
return TokenDto
.builder()
.accessToken(accessToken)
.refreshToken(refreshToken)
.build();
}
}
Loading

0 comments on commit 695373e

Please sign in to comment.