Skip to content

Commit

Permalink
feat: 야간잔류 신청 기능 구현
Browse files Browse the repository at this point in the history
* Application 엔티티에 컬럼 추가
* ApplicationController에서 ApiResponse를 이용한 응답 및 에러 처리
* ApplicationRequest에서 요청 DTO 구현
* ApplicationResponse에서 응답 DTO 구현
* ApplicationRepository와 MemberRepository 생성
* ApplicationService 구현

Ref: #21
  • Loading branch information
ahyeonkong committed Nov 22, 2024
1 parent cd1a52c commit a608539
Show file tree
Hide file tree
Showing 11 changed files with 254 additions and 71 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ repositories {
dependencies {
implementation 'com.h2database:h2'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
// implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.kyonggi.teampu.domain.application.controller;

import com.kyonggi.teampu.domain.application.dto.ApplicationRequest;
import com.kyonggi.teampu.domain.application.dto.ApplicationResponse;
import com.kyonggi.teampu.domain.application.service.ApplicationService;
import com.kyonggi.teampu.global.response.ApiResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
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;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/apply")
public class ApplicationController {
private final ApplicationService applicationService;

@PostMapping
public ResponseEntity<ApiResponse<ApplicationResponse>> createApplication(@RequestBody ApplicationRequest applicationRequest) {
// 400 에러 처리
if (applicationRequest.getMemberId() == null || applicationRequest.getStartTime() == null || applicationRequest.getEndTime() == null) {
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(new ApiResponse<>(
new ApiResponse.Status(HttpStatus.BAD_REQUEST, "필수 값이 누락되었습니다"),
null
));
}

try {
ApplicationResponse applicationResponse = applicationService.createApplication(applicationRequest);
return ResponseEntity.ok(new ApiResponse<>(
new ApiResponse.Status(HttpStatus.CREATED, "신청이 완료되었습니다"),
applicationResponse
));
} catch (IllegalArgumentException e) {
// 401 에러 처리
return ResponseEntity
.status(HttpStatus.UNAUTHORIZED)
.body(new ApiResponse<>(
new ApiResponse.Status(HttpStatus.UNAUTHORIZED, "권한이 없습니다"),
null
));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,7 @@
package com.kyonggi.teampu.domain.application.domain;

import com.kyonggi.teampu.domain.member.domain.Member;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
Expand All @@ -20,6 +10,8 @@

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Entity
@Getter
Expand All @@ -46,6 +38,16 @@ public class Application {
@Column(name = "status", nullable = false)
private ApplicationStatus status;

@Column(name = "participant_count")
private Integer participantCount;

@ElementCollection // JPA에서 값 타입 컬렉션을 매핑할 때 사용하는 어노테이션
@Column(name = "co_participant_names")
private List<String> coParticipantNames = new ArrayList<>();

@Column(name = "privacy_agreement")
private Boolean privacyAgreement;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.kyonggi.teampu.domain.application.dto;

import com.kyonggi.teampu.domain.application.domain.Application;
import com.kyonggi.teampu.domain.application.domain.ApplicationStatus;
import com.kyonggi.teampu.domain.member.domain.Member;
import com.kyonggi.teampu.domain.member.domain.MemberType;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

@Getter
@NoArgsConstructor
public class ApplicationRequest {
private String name;
private MemberType type;
private Long memberId;
private String loginId;
private String phoneNumber;
private String email;
private Integer participantCount;
private List<String> coParticipantNames;
private Boolean privacyAgreement;
private LocalDateTime startTime;
private LocalDateTime endTime;

public Application toEntity(Member member) {
return Application.builder()
.member(member)
.participantCount(participantCount)
.coParticipantNames(coParticipantNames)
.privacyAgreement(privacyAgreement)
.startTime(startTime)
.endTime(endTime)
.appliedDate(LocalDate.now())
.status(ApplicationStatus.PENDING)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.kyonggi.teampu.domain.application.dto;

import com.kyonggi.teampu.domain.application.domain.Application;
import com.kyonggi.teampu.domain.member.domain.MemberType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

import java.time.LocalDateTime;
import java.util.List;

@Getter
@Builder
@AllArgsConstructor
public class ApplicationResponse {
private String loginId;
private String name;
private MemberType type;
private Long memberId;
private String phoneNumber;
private String email;
private Integer participantCount;
private List<String> coParticipantNames;
private Boolean privacyAgreement;
private LocalDateTime startTime;
private LocalDateTime endTime;

public static ApplicationResponse of(Application application) {
return ApplicationResponse.builder()
.loginId(application.getMember().getLoginId())
.name(application.getMember().getName())
.type(application.getMember().getType())
.memberId(application.getMember().getId())
.phoneNumber(application.getMember().getPhoneNumber())
.email(application.getMember().getEmail())
.participantCount(application.getParticipantCount())
.coParticipantNames(application.getCoParticipantNames())
.privacyAgreement(application.getPrivacyAgreement())
.startTime(application.getStartTime())
.endTime(application.getEndTime())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.kyonggi.teampu.domain.application.repository;

import com.kyonggi.teampu.domain.application.domain.Application;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ApplicationRepository extends JpaRepository<Application, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.kyonggi.teampu.domain.application.service;

import com.kyonggi.teampu.domain.application.domain.Application;
import com.kyonggi.teampu.domain.application.dto.ApplicationRequest;
import com.kyonggi.teampu.domain.application.dto.ApplicationResponse;
import com.kyonggi.teampu.domain.application.repository.ApplicationRepository;
import com.kyonggi.teampu.domain.member.domain.Member;
import com.kyonggi.teampu.domain.member.repository.MemberRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class ApplicationService {
private final ApplicationRepository applicationRepository;
private final MemberRepository memberRepository;

@Transactional
public ApplicationResponse createApplication(ApplicationRequest request) {
Member member = memberRepository.findById(request.getMemberId())
.orElseThrow(() -> new IllegalArgumentException("회원을 찾을 수 없습니다"));

Application application = request.toEntity(member);
Application savedApplication = applicationRepository.save(application);
ApplicationResponse applicationResponse = ApplicationResponse.of(savedApplication);

return ApplicationResponse.of(applicationRepository.save(application));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.kyonggi.teampu.domain.member.repository;

import com.kyonggi.teampu.domain.member.domain.Member;
import org.springframework.data.jpa.repository.JpaRepository;

public interface MemberRepository extends JpaRepository<Member, Long> {
}
116 changes: 58 additions & 58 deletions src/main/java/com/kyonggi/teampu/global/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -1,58 +1,58 @@
package com.kyonggi.teampu.global.config;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private static final String[] PUBLIC_URLS = {
"/**"
};

@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}

@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
//CORS 설정
http.cors((cors) -> cors
.configurationSource(request -> {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000", "http://localhost:8080"));
configuration.setAllowedMethods(Collections.singletonList("*"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Collections.singletonList("*"));
configuration.setMaxAge(3600L);
configuration.setExposedHeaders(List.of("Authorization"));
return configuration;
}));

http.csrf(AbstractHttpConfigurer::disable);
http.formLogin(AbstractHttpConfigurer::disable);
http.httpBasic(AbstractHttpConfigurer::disable);

return http.build();
}
}
//package com.kyonggi.teampu.global.config;
//
//import lombok.RequiredArgsConstructor;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.security.authentication.AuthenticationManager;
//import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
//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.crypto.bcrypt.BCryptPasswordEncoder;
//import org.springframework.security.web.SecurityFilterChain;
//import org.springframework.web.cors.CorsConfiguration;
//
//import java.util.Arrays;
//import java.util.Collections;
//import java.util.List;
//
//@Configuration
//@EnableWebSecurity
//@RequiredArgsConstructor
//public class SecurityConfig {
// private static final String[] PUBLIC_URLS = {
// "/**"
// };
//
// @Bean
// public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
// return configuration.getAuthenticationManager();
// }
//
// @Bean
// public BCryptPasswordEncoder bCryptPasswordEncoder() {
// return new BCryptPasswordEncoder();
// }
//
// @Bean
// public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// //CORS 설정
// http.cors((cors) -> cors
// .configurationSource(request -> {
// CorsConfiguration configuration = new CorsConfiguration();
// configuration.setAllowedOrigins(Arrays.asList("http://localhost:3000", "http://localhost:8080"));
// configuration.setAllowedMethods(Collections.singletonList("*"));
// configuration.setAllowCredentials(true);
// configuration.setAllowedHeaders(Collections.singletonList("*"));
// configuration.setMaxAge(3600L);
// configuration.setExposedHeaders(List.of("Authorization"));
// return configuration;
// }));
//
// http.csrf(AbstractHttpConfigurer::disable);
// http.formLogin(AbstractHttpConfigurer::disable);
// http.httpBasic(AbstractHttpConfigurer::disable);
//
// return http.build();
// }
//}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public static <T> ApiResponse<T> ok(T body) {

@Getter
@AllArgsConstructor
private static class Status {
public static class Status {
private HttpStatus httpStatus;
private String message;
}
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
spring:
h2:
console:
enabled: true
path: /h2-console
datasource:
url: jdbc:h2:mem:test
username: sa
Expand Down

0 comments on commit a608539

Please sign in to comment.