Skip to content

Commit

Permalink
[CHORE] /v2 배포
Browse files Browse the repository at this point in the history
[CHORE] /v2 배포
  • Loading branch information
sunnyineverywhere authored Sep 27, 2023
2 parents 5cb7d3c + b5c0023 commit 3f5882b
Show file tree
Hide file tree
Showing 52 changed files with 874 additions and 106 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ name: Java CI with Gradle

on:
push:
branches: [ "chore/ci", "main" ]
branches: [ "main" ]

permissions:
contents: read
Expand Down
7 changes: 6 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ subprojects {

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

implementation 'org.springframework.boot:spring-boot-starter-web'

compileOnly 'org.projectlombok:lombok'
Expand All @@ -44,5 +50,4 @@ subprojects {
tasks.named('test') {
useJUnitPlatform()
}

}
3 changes: 3 additions & 0 deletions q-admin/src/main/java/com/qcard/config/JwtSecurityConfig.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.qcard.config;

import com.qcard.filter.JwtExceptionFilter;
import com.qcard.jwt.JwtFilter;
import com.qcard.jwt.JwtUtil;
import lombok.RequiredArgsConstructor;
Expand All @@ -14,7 +15,9 @@ public class JwtSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurity

@Override
public void configure(HttpSecurity httpSecurity) {
JwtExceptionFilter jwtExceptionFilter = new JwtExceptionFilter();
JwtFilter jwtFilter = new JwtFilter(jwtUtil);
httpSecurity.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
httpSecurity.addFilterBefore(jwtExceptionFilter, JwtFilter.class);
}
}
4 changes: 2 additions & 2 deletions q-admin/src/main/java/com/qcard/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;


@RequiredArgsConstructor
@EnableWebSecurity
@Configuration
Expand Down
55 changes: 55 additions & 0 deletions q-admin/src/main/java/com/qcard/filter/JwtExceptionFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.qcard.filter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qcard.common.exception.ErrorResponse;
import com.qcard.common.exception.ErrorStatus;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

public class JwtExceptionFilter extends OncePerRequestFilter {

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try{
filterChain.doFilter(request, response);
} catch (ExpiredJwtException e){
throwException(response, "JWT EXPIRED", e);
} catch (JwtException | ServletException e){
throwException(response, "JWT INVALID", e);
}
}

private void throwException(
HttpServletResponse response,
String message,
Exception e
) {
ObjectMapper objectMapper = new ObjectMapper();
response.setStatus(ErrorStatus.INTERNAL_SERVER_ERROR.getStatusCode());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
try {
response.getWriter().write(objectMapper.writeValueAsString(
ErrorResponse
.builder()
.error(ErrorStatus.INTERNAL_SERVER_ERROR.getCode())
.details(e.getMessage())
.code(ErrorStatus.INTERNAL_SERVER_ERROR.getCode())
.message(message)
.build()
));
} catch (JsonProcessingException ex) {
throw new RuntimeException(ex);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}
1 change: 0 additions & 1 deletion q-admin/src/main/java/com/qcard/jwt/JwtService.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ public TokenRes createJwt(String email, String password) {
Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
TokenRes tokenRes = jwtUtil.generateToken(authentication);
redisService.setValues(tokenRes.getRefreshToken(), email, Duration.ofDays(14));

return tokenRes;
}

Expand Down
5 changes: 3 additions & 2 deletions q-admin/src/main/java/com/qcard/jwt/JwtUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.springframework.stereotype.Component;

import java.security.Key;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
Expand All @@ -22,8 +23,8 @@
public class JwtUtil {
private static final String AUTHORITIES_KEY = "role";
private static final String BEARER_TYPE = "Bearer";
private static final Long ACCESS_TOKEN_EXPIRE_TIME = (long) (1000 * 60 * 30 * 24 * 3);
private static final Long REFRESH_TOKEN_EXPIRE_TIME = (long) (1000 * 60 * 60 * 24 * 7);
private static final Long ACCESS_TOKEN_EXPIRE_TIME = Duration.ofHours(1).toMillis();
private static final Long REFRESH_TOKEN_EXPIRE_TIME = Duration.ofDays(14).toMillis();
private final Key key;

public JwtUtil(@Value("${spring.jwt.secret}") String secretKey) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer m

final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

if (authentication == null || authentication.getName() == null) {
throw new RuntimeException("Security Context 에 인증 정보가 없습니다.");
if (authentication.getPrincipal() == "anonymousUser") {
return null;
}

return accountDomainService.findAccountByEmail(authentication.getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,14 @@ public ResponseEntity<LogOutRes> logOut(HttpServletRequest request) {

@GetMapping("/profile")
public ResponseEntity<AccountRes> myAccountInfo(@AuthAccount Account account) {
AccountRes response = new AccountRes(account.getName(), account.getEmail());
AccountRes response = new AccountRes(account);
return new ResponseEntity<>(response, HttpStatus.OK);
}

@PutMapping("/profile")
public ResponseEntity<AccountRes> modifyAccountInfo(@AuthAccount Account account, @RequestBody AccountModifyReq request) {
AccountRes response = accountService.modifyAccount(account, request);
return new ResponseEntity<>(response, HttpStatus.OK);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.qcard.api.account.dto;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;

@Getter
public class AccountModifyReq {
private String name;
private String email;
private String profile;
}
9 changes: 6 additions & 3 deletions q-api/src/main/java/com/qcard/api/account/dto/AccountRes.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.qcard.api.account.dto;

import com.qcard.domains.account.entity.Account;
import lombok.Getter;
import lombok.NoArgsConstructor;

Expand All @@ -8,9 +9,11 @@
public class AccountRes {
private String name;
private String email;
private String profile;

public AccountRes(String name, String email) {
this.name = name;
this.email = email;
public AccountRes(Account account) {
this.name = account.getName();
this.email = account.getEmail();
this.profile = account.getProfile();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,8 @@ public LogOutRes logout(String refreshToken) {
throw new IllegalArgumentException("잘못된 refresh token입니다.");
}
}

public AccountRes modifyAccount(Account account, AccountModifyReq request) {
return new AccountRes(accountDomainService.updateAccount(account, request.getName(), request.getEmail(), request.getProfile()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
import com.qcard.api.account.service.AccountService;
import com.qcard.api.answer.dto.*;
import com.qcard.api.answer.service.AnswerService;
import com.qcard.common.enums.Category;
import com.qcard.resolver.AuthAccount;
import com.qcard.domains.account.entity.Account;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
Expand All @@ -19,7 +22,6 @@
public class AnswerController {

private final AnswerService answerService;
private final AccountService accountService;

@PostMapping("")
public ResponseEntity<AnswerCreateRes> answerCreate(@AuthAccount Account account, @RequestBody AnswerReq answerReq) {
Expand All @@ -28,8 +30,8 @@ public ResponseEntity<AnswerCreateRes> answerCreate(@AuthAccount Account account
}

@GetMapping("/me")
public ResponseEntity<List<AnswerMeRes>> answersByAuth(@AuthAccount Account account) {
List<AnswerMeRes> response = answerService.getAnswersByAuth(account);
public ResponseEntity<List<AnswerMeRes>> answersByAuth(@AuthAccount Account account, @RequestParam Category category) {
List<AnswerMeRes> response = answerService.getAnswersByAuth(account, category);
return new ResponseEntity<>(response, HttpStatus.OK);
}

Expand Down
7 changes: 3 additions & 4 deletions q-api/src/main/java/com/qcard/api/answer/dto/AnswerMeRes.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
package com.qcard.api.answer.dto;

import com.qcard.api.account.dto.AccountRes;
import com.qcard.common.enums.Type;
import com.qcard.common.enums.AnswerType;
import com.qcard.domains.account.entity.Account;
import com.qcard.domains.question.entity.Answer;
import com.qcard.domains.question.entity.Question;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;
import java.util.Map;

@Getter
@NoArgsConstructor
public class AnswerMeRes {
private Question question;
private Long answerId;
private Type type;
private AnswerType type;
private AccountRes account;
private String content;

Expand All @@ -36,6 +35,6 @@ public AnswerMeRes(Integer hearCount, Answer answer) {
}

private AccountRes createdAccountRes(Account account) {
return new AccountRes(account.getName(), account.getEmail());
return new AccountRes(account);
}
}
9 changes: 3 additions & 6 deletions q-api/src/main/java/com/qcard/api/answer/dto/AnswerRes.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
package com.qcard.api.answer.dto;

import com.qcard.api.account.dto.AccountRes;
import com.qcard.common.enums.Type;
import com.qcard.common.enums.AnswerType;
import com.qcard.domains.account.entity.Account;
import com.qcard.domains.heart.entity.Heart;
import com.qcard.domains.question.entity.Answer;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.http.StreamingHttpOutputMessage;

import java.time.LocalDateTime;
import java.util.List;
Expand All @@ -17,7 +14,7 @@
@NoArgsConstructor
public class AnswerRes {
private Long answerId;
private Type type;
private AnswerType type;
private AccountRes account;
private String content;

Expand Down Expand Up @@ -56,6 +53,6 @@ public AnswerRes(Answer answer) {
}

private AccountRes createdAccountRes(Account account) {
return new AccountRes(account.getName(), account.getEmail());
return new AccountRes(account);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@
import com.qcard.api.answer.dto.AnswerReq;
import com.qcard.api.answer.dto.AnswerUpdateReq;
import com.qcard.api.question.dto.QuestionDetailRes;
import com.qcard.common.enums.Category;
import com.qcard.common.enums.SortType;
import com.qcard.domains.account.entity.Account;
import com.qcard.domains.heart.service.HeartDomainService;
import com.qcard.domains.question.entity.Question;
import com.qcard.domains.question.service.AnswerDomainService;
import com.qcard.domains.question.entity.Answer;
import com.qcard.domains.question.service.QuestionDomainService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.util.Pair;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import java.nio.file.AccessDeniedException;
Expand All @@ -29,6 +32,11 @@ public class AnswerService {
private final QuestionDomainService questionDomainService;

public AnswerCreateRes createAnswer(Account account, AnswerReq answerReq) {
Answer befAnswer = answerDomainService.findAnswerById(answerReq.getQuestionId());
if(befAnswer != null) {
throw new IllegalArgumentException("사용자의 답변이 이미 존재합니다.");
}

Answer answer = answerDomainService.createAnswer(
answerReq.getQuestionId(),
account,
Expand All @@ -37,7 +45,7 @@ public AnswerCreateRes createAnswer(Account account, AnswerReq answerReq) {
return new AnswerCreateRes(answer.getContent());
}

public QuestionDetailRes findAnswerByQuestionId(Account account, Long questionId) {
public QuestionDetailRes findAnswerByQuestionId(Account account, Long questionId, SortType sort) {
List<Answer> entities = answerDomainService.findAnswerByQuestionId(questionId);
if(entities.isEmpty()) {
Question question = questionDomainService.findQuestionById(questionId);
Expand All @@ -48,13 +56,12 @@ public QuestionDetailRes findAnswerByQuestionId(Account account, Long questionId
List<Long> heartedAnswerList = heartDomainService.findHeartByAccount(account)
.stream().map(heart -> heart.getAnswer().getId()).toList();

return new QuestionDetailRes(entities, account, heartedAnswerList, heartCounts);
return new QuestionDetailRes(entities, account, heartedAnswerList, heartCounts, sort);
}

public List<AnswerMeRes> getAnswersByAuth(Account account) {
List<Answer> entities = answerDomainService.findAnswerByAccount(account);
public List<AnswerMeRes> getAnswersByAuth(Account account, Category category) {
List<Answer> entities = answerDomainService.findAnswerByAccount(account, category);
if(entities.isEmpty()) return new ArrayList<>();

Map<Long, Integer> heartCounts = countHearts(entities);
return entities.stream().map(entity -> new AnswerMeRes(heartCounts.get(entity.getId()), entity)).collect(Collectors.toList());
}
Expand All @@ -71,6 +78,11 @@ public AnswerMeRes updateAnswer(Account account, Long answerId, AnswerUpdateReq
}
}

public Map<Long, Integer> countHearts(Page<Answer> entities) {
List<Long> answerIds = entities.stream().map(Answer::getId).toList();
return answerIds.stream().collect(Collectors.toMap(id -> id, heartDomainService::countHeartByAnswerId));
}

public Map<Long, Integer> countHearts(List<Answer> entities) {
List<Long> answerIds = entities.stream().map(Answer::getId).toList();
return answerIds.stream().collect(Collectors.toMap(id -> id, heartDomainService::countHeartByAnswerId));
Expand Down
Loading

0 comments on commit 3f5882b

Please sign in to comment.