diff --git a/.gitignore b/.gitignore index c2065bc..d611f15 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,6 @@ out/ ### VS Code ### .vscode/ +src/main/java/mos/mosback/web/dto/PostsListResponseDto.java +src/main/java/mos/mosback/web/dto/PostsListResponseDto.java +src/main/java/mos/mosback/web/dto/PostsListResponseDto.java diff --git a/build.gradle b/build.gradle index 233df49..5b888b9 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,17 @@ -plugins { - id 'org.springframework.boot' version '2.7.3' - id 'io.spring.dependency-management' version '1.0.13.RELEASE' +plugins { // (1) + id 'org.springframework.boot' version '2.4.1' + id 'io.spring.dependency-management' version '1.0.10.RELEASE' id 'java' + id 'maven-publish' } -group = 'login' -version = '0.0.1-SNAPSHOT' -sourceCompatibility = '1.8' +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +group = 'mos' +version '1.0-SNAPSHOT' configurations { compileOnly { @@ -16,31 +21,70 @@ configurations { repositories { mavenCentral() + maven { + url 'https://repo.clojars.org' + name 'Clojars' + } +} +jar { + enabled = false +} + +jar { + manifest { + attributes 'Main-Class': 'mos.mosback.Application' + } +} +// for Junit 5 +test { // (2) + useJUnitPlatform() } dependencies { + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' - implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'com.auth0:java-jwt:4.2.1' + implementation 'org.springframework.boot:spring-boot-devtools' + implementation 'io:io:0.2.1' + testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' + implementation 'org.mariadb.jdbc:mariadb-java-client' annotationProcessor 'org.projectlombok:lombok' - testCompileOnly 'org.projectlombok:lombok' - testAnnotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' - testImplementation 'org.springframework.security:spring-security-test' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'mysql:mysql-connector-java:8.0.34' implementation 'org.springframework.boot:spring-boot-starter-mail' //메일 의존성 -// implementation 'org.springframework.boot:spring-boot-starter-log4j2' - // 로그인 jwt 의존성 + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.springframework.security:spring-security-test' + +// 로그인 jwt 의존성 implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.2' runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.2' runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.2' implementation group: 'org.postgresql', name: 'postgresql', version: '42.2.23' -} -tasks.named('test') { - useJUnitPlatform() + implementation 'org.mariadb.jdbc:mariadb-java-client:3.1.2' + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' + implementation 'com.auth0:java-jwt:4.2.1' + implementation 'mysql:mysql-connector-java:8.0.26' + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' + implementation 'com.auth0:java-jwt:4.2.1' + + implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' + implementation "software.amazon.awssdk:s3:2.13.0" + + + // lombok + implementation('org.projectlombok:lombok:1.18.20') + testImplementation 'junit:junit:4.13.1' + annotationProcessor('org.projectlombok:lombok:1.18.20') + testImplementation('org.projectlombok:lombok:1.18.20') + testAnnotationProcessor('org.projectlombok:lombok:1.18.20') + + } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 249e583..e69de29 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/src/main/java/mos/mosback/Application.java b/src/main/java/mos/mosback/Application.java index 6248da1..699a667 100644 --- a/src/main/java/mos/mosback/Application.java +++ b/src/main/java/mos/mosback/Application.java @@ -5,9 +5,14 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + @Slf4j -@EnableJpaAuditing //JPA Auditing 활성화 -@SpringBootApplication(exclude = {SecurityAutoConfiguration.class}) +@EnableJpaAuditing +@SpringBootApplication(exclude = {SecurityAutoConfiguration.class, + org.springframework.cloud.aws.autoconfigure.context.ContextInstanceDataAutoConfiguration.class, + org.springframework.cloud.aws.autoconfigure.context.ContextStackAutoConfiguration.class, + org.springframework.cloud.aws.autoconfigure.context.ContextRegionProviderAutoConfiguration.class +}) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); diff --git a/src/main/java/mos/mosback/login/domain/user/User.java b/src/main/java/mos/mosback/login/domain/user/User.java index 093f811..6935789 100644 --- a/src/main/java/mos/mosback/login/domain/user/User.java +++ b/src/main/java/mos/mosback/login/domain/user/User.java @@ -2,11 +2,14 @@ import lombok.*; +import mos.mosback.stRoom.domain.stRoom.StRoomEntity; +import mos.mosback.stRoom.domain.stRoom.StudyMemberEntity; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import javax.persistence.*; -import java.util.Date; +import java.util.*; +import java.util.stream.Collectors; @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -19,19 +22,64 @@ public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "user_id") + private Long id; + @Column(name ="room_id") + private Long roomId; + + @OneToMany + private List stRooms= new ArrayList<>(); + + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL) + private List studyMemberships = new ArrayList<>(); + + public List getStudyMemberships() { + return studyMemberships; + } + public void addStudyMembership(StudyMemberEntity studyMember) { + this.studyMemberships.add(studyMember); + studyMember.setUser(this); + } + private String email; // 이메일 private String password; // 비밀번호 private String name; + private String nickname; // 닉네임 + + + @Column(name = "image_url") private String imageUrl; // 프로필 이미지 private Date str_duration; private Date end_duration; private String message; + private String company; + + private String tend1; + private String tend2; + + + public String getTend1() { + return tend1; + } + + public void setTend1(String tend1) { + this.tend1 = tend1; + } + + public String getTend2() { + return tend2; + } + + public void setTend2(String tend2) { + this.tend2 = tend2; + } + + @Enumerated(EnumType.STRING) private Role role; @@ -42,6 +90,7 @@ public class User { private String refreshToken; // 리프레시 토큰 + // 유저 권한 설정 메소드 public void authorizeUser() { this.role = Role.USER; @@ -53,12 +102,18 @@ public void passwordEncode(PasswordEncoder passwordEncoder) { } //==setter==// + public Long getRoomId() { + return roomId; + } - - public Long getId() { - return id; + public void setRoomId(Long roomId) { + this.roomId = roomId; } + + + + public void setId(Long id) { this.id = id; } @@ -133,6 +188,14 @@ public void setCompany(String company) { this.company = company; } + public Role getRole() { + return role; + } + + public void setRole(Role role) { + this.role = role; + } + //== 유저 필드 업데이트 ==// public void updateNickname(String updateNickname) { this.nickname = updateNickname; @@ -154,13 +217,6 @@ public void updateCompany(String updateCompany) { this.company = updateCompany; } - public Role getRole() { - return role; - } - - public void setRole(Role role) { - this.role = role; - } public void updatePassword(String updatePassword, PasswordEncoder passwordEncoder) { this.password = passwordEncoder.encode(updatePassword); @@ -178,4 +234,4 @@ public String cryptopassword(String password) { public void updateRefreshToken(String updateRefreshToken) { this.refreshToken = updateRefreshToken; } -} +} \ No newline at end of file diff --git a/src/main/java/mos/mosback/login/domain/user/conrtoller/UserController.java b/src/main/java/mos/mosback/login/domain/user/conrtoller/UserController.java deleted file mode 100644 index 35fa278..0000000 --- a/src/main/java/mos/mosback/login/domain/user/conrtoller/UserController.java +++ /dev/null @@ -1,85 +0,0 @@ -package mos.mosback.login.domain.user.conrtoller; - -import lombok.RequiredArgsConstructor; -import mos.mosback.login.domain.user.User; -import mos.mosback.login.domain.user.dto.*; -import mos.mosback.login.domain.user.repository.UserRepository; -import mos.mosback.login.domain.user.service.UserService; -import mos.mosback.login.global.jwt.service.JwtService; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.bind.annotation.*; - -import javax.transaction.Transactional; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -@RestController -@RequiredArgsConstructor -//@RequestMapping("/api") -public class UserController { - - private final UserService userService; - private final UserRepository userRepository; - - private Map userMap = new HashMap<>(); - - private JwtService jwtService; - @PostMapping("/sign-up") - public String signUp(@RequestBody UserSignUpDto userSignUpDto) throws Exception { - userService.signUp(userSignUpDto); - return "회원가입 성공"; - } - - - @PutMapping("/update/password") - public ResponseEntity updateUserPassword(@RequestBody UserSignUpDto userSignUpDto) throws Exception { - // 현재 로그인한 사용자의 정보 가져오기 - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - String currentEmail = authentication.getName(); // 현재 사용자의 이메일 - try{ - // 회원 정보 업데이트 - userService.upadateUserPassword(currentEmail, userSignUpDto); - - return ResponseEntity.ok("비밀번호 수정이 완료되었습니다."); - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("오류가 발생했습니다."); - } - } - - - @GetMapping("/jwt-test") - public String jwtTest() { - return "jwtTest 요청 성공"; - } - - - @GetMapping("/user-emails/{email}/exists") - public ResponseEntity checkEmailExists(@PathVariable String email) { - Optional existingUser = userRepository.findByEmail(email); - - if (existingUser.isPresent()) { - User user = existingUser.get(); - return ResponseEntity.ok("이메일이 이미 존재합니다."); - } else { - return ResponseEntity.ok("사용할 수 있는 이메일입니다."); - } - } - - - @Transactional - @PostMapping("/send/email") - public ResponseEntity sendTemporaryPassword(@RequestBody FindPWDto findPWDto) { - if (userService.findPassword(findPWDto)) { - MailDto mailDto = userService.createMailAndChangePassword(findPWDto.getEmail()); - userService.mailSend(mailDto); - return ResponseEntity.ok("임시 비밀번호 메일 전송 및 변경 완료"); - } else { - return ResponseEntity.status(400).body("이메일이 존재하지 않거나 이름이 일치하지 않습니다."); - } - } -} diff --git a/src/main/java/mos/mosback/login/domain/user/conrtoller/UserProfileController.java b/src/main/java/mos/mosback/login/domain/user/conrtoller/UserProfileController.java deleted file mode 100644 index 16a88ce..0000000 --- a/src/main/java/mos/mosback/login/domain/user/conrtoller/UserProfileController.java +++ /dev/null @@ -1,70 +0,0 @@ -package mos.mosback.login.domain.user.conrtoller; - -import mos.mosback.login.domain.user.dto.UserProfileDto; -import mos.mosback.login.domain.user.repository.UserRepository; -import mos.mosback.login.domain.user.service.UserService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.bind.annotation.*; - -@RestController -@RequestMapping("/profile") -public class UserProfileController { - - // 현재 로그인한 사용자의 정보 가져오기 -// Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); -// String currentEmail = authentication.getName(); // 현재 사용자의 이메일 - - private final UserService userService; - private final UserRepository userRepository; - - - @Autowired - public UserProfileController(UserService userService, UserRepository userRepository) { - - this.userService = userService; - this.userRepository = userRepository; - - } - - - @PostMapping - public ResponseEntity createUserProfile(@RequestBody UserProfileDto userProfileDto) throws Exception { - // 현재 로그인한 사용자의 정보 가져오기 - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - String currentEmail = authentication.getName(); // 현재 사용자의 이메일 - try{ - // 회원 정보 생성 - userService.createUser(currentEmail, userProfileDto); - - return ResponseEntity.ok("회원 정보가 생성되었습니다."); - } catch (Exception e){ - e.printStackTrace(); // 이 코드는 예외의 스택 트레이스를 콘솔에 출력합니다. - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("오류가 발생했습니다."); - } - - } - - @PutMapping - public ResponseEntity updateUserProfile(@RequestBody UserProfileDto userProfileDto) throws Exception { - // 현재 로그인한 사용자의 정보 가져오기 - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - String currentEmail = authentication.getName(); // 현재 사용자의 이메일 - try{ - // 회원 정보 업데이트 - userService.updateUserProfile(currentEmail, userProfileDto); - - return ResponseEntity.ok("회원 정보가 업데이트되었습니다."); - } catch (Exception e) { - e.printStackTrace(); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("오류가 발생했습니다."); - } - } - - - -} - diff --git a/src/main/java/mos/mosback/login/domain/user/controller/UserController.java b/src/main/java/mos/mosback/login/domain/user/controller/UserController.java new file mode 100644 index 0000000..6abd5c9 --- /dev/null +++ b/src/main/java/mos/mosback/login/domain/user/controller/UserController.java @@ -0,0 +1,149 @@ +package mos.mosback.login.domain.user.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import javassist.NotFoundException; +import lombok.RequiredArgsConstructor; +import mos.mosback.login.domain.user.User; +import mos.mosback.login.domain.user.dto.*; +import mos.mosback.login.domain.user.repository.UserRepository; +import mos.mosback.login.domain.user.service.UserService; +import mos.mosback.login.global.jwt.service.JwtService; +import mos.mosback.stRoom.domain.stRoom.StRoomEntity; +import mos.mosback.stRoom.dto.StRoomResponseDto; +import mos.mosback.stRoom.service.StRoomService; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.transaction.Transactional; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@RestController +@RequiredArgsConstructor +public class UserController { + + private final UserService userService; + private final UserRepository userRepository; + + private final StRoomService stRoomService; + + private Map userMap = new HashMap<>(); + + private final JwtService jwtService; + + @PostMapping("/sign-up") + public ResponseEntity> signUp(@RequestBody UserSignUpDto userSignUpDto) { + Map response = new HashMap<>(); + try { + userService.signUp(userSignUpDto); + response.put("status", 200); + response.put("success", true); + response.put("message", "회원가입 성공"); + return ResponseEntity.ok(response); + } catch (Exception ex) { + response.put("status", 500); + response.put("success", false); + response.put("message", "회원가입 실패: " + ex.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + + @PutMapping("/update/password") + public Map updateUserPassword(@RequestBody UserSignUpDto userSignUpDto) throws Exception { + // 현재 로그인한 사용자의 정보 가져오기 + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String currentEmail = authentication.getName(); // 현재 사용자의 이메일 + Map response = new HashMap<>(); + try{ + // 회원 정보 업데이트 + userService.upadateUserPassword(currentEmail, userSignUpDto); + response.put("status", 200); + response.put("success", true); + response.put("message", "비밀번호 수정이 완료되었습니다."); + } catch (Exception e) { + response.put("status", 500); + response.put("success", false); + response.put("message", "비밀번호 수정 실패: " + e.getMessage()); + } + return response; + } + + + @GetMapping("/jwt-test") + public String jwtTest() { + return "jwtTest 요청 성공"; + } + + + @GetMapping("/user-emails/{email}/exists") + public Map checkEmailExists(@PathVariable String email) { + Map response = new HashMap<>(); + Optional existingUser = userRepository.findByEmail(email); + + if (existingUser.isPresent()) { + response.put("status", 500); + response.put("success", true); + response.put("message", "이메일이 이미 존재합니다."); + } else { + response.put("status", 200); + response.put("success", true); + response.put("message", "사용할 수 있는 이메일입니다."); + } + return response; + } + + + + @Transactional + @PostMapping("/send/email") + public ResponseEntity> sendTemporaryPassword(@RequestBody FindPWDto findPWDto) { + Map response = new HashMap<>(); + if (userService.findPassword(findPWDto)) { + MailDto mailDto = userService.createMailAndChangePassword(findPWDto.getEmail()); + userService.mailSend(mailDto); + response.put("status", 200); + response.put("success", true); + response.put("message", "임시 비밀번호 메일 전송 및 변경 완료"); + return ResponseEntity.ok(response); + } else { + response.put("status", 400); + response.put("success", false); + response.put("message", "이메일이 존재하지 않거나 이름이 일치하지 않습니다."); + return ResponseEntity.status(400).body(response); + } + } + + + @GetMapping("/user/list") + public ResponseEntity> getLeaderStudies() { + Map response = new HashMap<>(); + try { + // 현재 로그인한 사용자의 정보 가져오기 + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String userEmail = authentication.getName(); // 현재 사용자의 이메일 + + List leaderStudies = stRoomService.getLeaderStudies(userEmail); + + response.put("status", HttpStatus.OK.value()); + response.put("success", true); + response.put("data", leaderStudies); + + return new ResponseEntity<>(response, HttpStatus.OK); + } catch (Exception e) { + response.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value()); + response.put("success", false); + response.put("error", "Internal Server Error: " + e.getMessage()); + + return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + +} diff --git a/src/main/java/mos/mosback/login/domain/user/controller/UserProfileController.java b/src/main/java/mos/mosback/login/domain/user/controller/UserProfileController.java new file mode 100644 index 0000000..f93cd95 --- /dev/null +++ b/src/main/java/mos/mosback/login/domain/user/controller/UserProfileController.java @@ -0,0 +1,185 @@ +package mos.mosback.login.domain.user.controller; + +import mos.mosback.login.domain.user.User; +import mos.mosback.login.domain.user.dto.UserProfileDto; +import mos.mosback.login.domain.user.repository.UserRepository; +import mos.mosback.login.domain.user.service.FileService; +import mos.mosback.login.domain.user.service.UserService; +import mos.mosback.stRoom.dto.StudyMembershipStatusResponseDto; +import mos.mosback.stRoom.repository.StRoomRepository; +import mos.mosback.stRoom.repository.StudyMemberRepository; +import mos.mosback.stRoom.service.StRoomService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.*; + +@RestController +@RequestMapping("/profile") +public class UserProfileController { +// private static final Logger logger = LoggerFactory.getLogger(UserProfileController.class); + + // 현재 로그인한 사용자의 정보 가져오기 +// Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); +// String currentEmail = authentication.getName(); // 현재 사용자의 이메일 + + private final UserService userService; + private final UserRepository userRepository; + + private final StRoomService stRoomService; + private final StudyMemberRepository studyMemberRepository; + private final StRoomRepository stRoomRepository; + + private final FileService fileService; + + @Autowired + public UserProfileController(UserService userService, UserRepository userRepository, StRoomService stRoomService, StudyMemberRepository studyMemberRepository, StRoomRepository stRoomRepository, FileService fileService) { + + this.userService = userService; + this.userRepository = userRepository; + this.stRoomService = stRoomService; + this.studyMemberRepository = studyMemberRepository; + this.stRoomRepository = stRoomRepository; + this.fileService = fileService; + } + + + + @PutMapping + public Map updateUserProfile(UserProfileDto userProfileDto) throws Exception { + // 현재 로그인한 사용자의 정보 가져오기 + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String currentEmail = authentication.getName(); // 현재 사용자의 이메일 + Map response = new HashMap<>(); + try { + // 회원 정보 업데이트 + userService.updateUserProfile(currentEmail, userProfileDto); + response.put("status", 200); + response.put("success", true); + response.put("message", "회원 정보가 업데이트되었습니다."); + } catch (Exception e) { + e.printStackTrace(); + response.put("status", 500); + response.put("success", false); + response.put("message", "오류가 발생했습니다."); + } + + return response; + } + @PostMapping + public Map createUserProfile(@RequestPart(value = "file", required = false) MultipartFile file, + @RequestPart(value = "userProfileDto") UserProfileDto userProfileDto) { + Map response = new HashMap<>(); + try { + // 현재 로그인한 사용자의 정보 가져오기 + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String currentEmail = authentication.getName(); // 현재 사용자의 이메일 + + // 회원 정보 생성 + userService.createUser(currentEmail, userProfileDto); + //파일이 업로드되었을 때만 이미지 업로드 및 URL 저장 + if (file != null && !file.isEmpty()) { + String imageUrl = fileService.uploadFile(file, userProfileDto.getId()); + userProfileDto.setImageUrl(imageUrl); + // 사용자 정보에 이미지 URL 업데이트 + userService.updateUserProfileImageUrl(userProfileDto.getId(), imageUrl); + } + + + response.put("status", 200); + response.put("success", true); + response.put("message", "회원 정보가 생성되었습니다."); + } catch (Exception e) { + e.printStackTrace(); // 이 코드는 예외의 스택 트레이스를 콘솔에 출력합니다. + response.put("status", 500); + response.put("success", false); + response.put("message", "오류가 발생했습니다."); + } + return response; + } + @GetMapping + public ResponseEntity> getUserProfile() { + Map response = new HashMap<>(); + try { + // 현재 로그인한 사용자의 정보 가져오기 + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String currentEmail = authentication.getName(); // 현재 사용자의 이메일 + + // 현재 사용자의 이메일을 이용하여 프로필 정보를 가져옵니다. + UserProfileDto userProfileDto = userService.getUserProfileByEmail(currentEmail); + + response.put("status", 200); + response.put("success", true); + response.put("data", userProfileDto); + + // 프로필 정보를 클라이언트에 응답으로 반환합니다. + return ResponseEntity.ok(response); + } catch (Exception e) { + // 에러 발생 시 500 Internal Server Error 반환 + response.put("status", 500); + response.put("success", false); + response.put("message", "오류가 발생했습니다."); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + + @GetMapping("/status") + public ResponseEntity> getStudyMembershipStatus() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String userEmail = authentication.getName(); // 현재 사용자의 이메일 + + Map response = new HashMap<>(); + HttpStatus httpStatus; + boolean success = true; + List membershipStatusList; + + try { + // userService에서 스터디 멤버십 상태 정보를 가져옵니다. + membershipStatusList = userService.getStudyMembershipStatus(userEmail); + httpStatus = HttpStatus.OK; + } catch (Exception e) { + membershipStatusList = Collections.emptyList(); + httpStatus = HttpStatus.INTERNAL_SERVER_ERROR; + success = false; + response.put("error", e.getMessage()); + } + + response.put("status", httpStatus.value()); + response.put("success", success); + response.put("data", membershipStatusList); + + return ResponseEntity.status(httpStatus).body(response); + } + + @PostMapping("/{id}/image") + public ResponseEntity uploadUserImage(@PathVariable Long id, + @RequestParam("file") MultipartFile file, + @ModelAttribute UserProfileDto userProfileDto) { + try { + userService.uploadAndSaveImage(id, file); + return new ResponseEntity<>("User image uploaded successfully", HttpStatus.OK); + } catch (IOException e) { + return new ResponseEntity<>("Error occurred while uploading image", HttpStatus.BAD_REQUEST); + } + } + + @GetMapping("/{id}/image") + public ResponseEntity getUserImage(@PathVariable Long id) { + User user = userService.findById(id); + if (user != null) { + String imageUrl = user.getImageUrl(); + return new ResponseEntity<>(imageUrl, HttpStatus.OK); + } else { + return new ResponseEntity<>("User not found", HttpStatus.NOT_FOUND); + } + } + + +} + diff --git a/src/main/java/mos/mosback/login/domain/user/dto/UserInfo.java b/src/main/java/mos/mosback/login/domain/user/dto/UserInfo.java index 02b77e0..73e015d 100644 --- a/src/main/java/mos/mosback/login/domain/user/dto/UserInfo.java +++ b/src/main/java/mos/mosback/login/domain/user/dto/UserInfo.java @@ -4,37 +4,19 @@ public class UserInfo { private String email; private String password; + private String nickname; // 생성자, 게터/세터 등 생략 - public String getEmail() { return email; } - public void setEmail(String email) { - this.email = email; - } - public String getPassword() { return password; } - public void setPassword(String password) { - this.password = password; - } - - private String nickname; - - public UserInfo(String nickname) { - this.nickname = nickname; - } - - public String getNickname() { + public String getNickname(){ return nickname; } - - public void setNickname(String nickname) { - this.nickname = nickname; - } } \ No newline at end of file diff --git a/src/main/java/mos/mosback/login/domain/user/dto/UserProfileDto.java b/src/main/java/mos/mosback/login/domain/user/dto/UserProfileDto.java index fc9f27d..124b030 100644 --- a/src/main/java/mos/mosback/login/domain/user/dto/UserProfileDto.java +++ b/src/main/java/mos/mosback/login/domain/user/dto/UserProfileDto.java @@ -1,19 +1,77 @@ package mos.mosback.login.domain.user.dto; import java.util.Date; + + import lombok.Getter; import lombok.NoArgsConstructor; +import org.springframework.stereotype.Component; + +import javax.persistence.Column; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; -import java.util.Date; @NoArgsConstructor @Getter +@Component public class UserProfileDto { - private String nickname; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "user_id") + private Long id; + + @Column(nullable = false) + private String nickname; private String name; private Date str_duration; private Date end_duration; private String message; private String company; + + private String tend1; + private String tend2; + + private Long roomId; + + @Column(name = "image_url") + private String imageUrl; + + + public UserProfileDto(Long id,String nickname, String name, Date str_duration, + Date end_duration, String message, + String company, String tend1, String tend2, Long roomId, String imageUrl) { + this.id = id; + this.nickname = nickname; + this.name = name; + this.str_duration = str_duration; + this.end_duration = end_duration; + this.message = message; + this.company = company; + this.roomId = roomId; + this.tend1 = tend1; + this.tend2= tend2; + this.imageUrl=imageUrl; + + + } + public Long getRoomId() { + return roomId; + } + + public void setRoomId(Long roomId) { + this.roomId = roomId; + } + public String getImageUrl() { + return imageUrl; + } + + public void setImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + } + } + + diff --git a/src/main/java/mos/mosback/login/domain/user/repository/UserRepository.java b/src/main/java/mos/mosback/login/domain/user/repository/UserRepository.java index 783a108..c04b8e4 100644 --- a/src/main/java/mos/mosback/login/domain/user/repository/UserRepository.java +++ b/src/main/java/mos/mosback/login/domain/user/repository/UserRepository.java @@ -11,16 +11,12 @@ public interface UserRepository extends JpaRepository { Optional findByEmail(String email); - - //Optional findByNickName(String nickname); - Optional findByNickname(String nickname); boolean existsByEmail(String email); Optional findByRefreshToken(String refreshToken); - //String findByPassword(); /** * 소셜 타입과 소셜의 식별값으로 회원 찾는 메소드 * 정보 제공을 동의한 순간 DB에 저장해야하지만, 아직 추가 정보(사는 도시, 나이 등)를 입력받지 않았으므로 diff --git a/src/main/java/mos/mosback/login/domain/user/service/FileService.java b/src/main/java/mos/mosback/login/domain/user/service/FileService.java new file mode 100644 index 0000000..f67e5f8 --- /dev/null +++ b/src/main/java/mos/mosback/login/domain/user/service/FileService.java @@ -0,0 +1,44 @@ +package mos.mosback.login.domain.user.service; + +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.PutObjectRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.*; + +@Service +public class FileService { + + @Value("${application.bucket.name}") + private String bucketName; + + @Value("${cloud.aws.region.static}") + private String region; + @Autowired + private AmazonS3 s3Client; + + + public String uploadFile(MultipartFile file, Long memberId) { + File fileObj = convertMultiPartFileToFile(file); + String fileName = Long.toString(memberId); + s3Client.putObject(new PutObjectRequest(bucketName, fileName, fileObj)); + fileObj.delete(); + + // 업로드된 파일의 S3 URL 반환 + return String.format("https://%s.s3.%s.amazonaws.com/%s", bucketName, region, fileName); + } + + private File convertMultiPartFileToFile(MultipartFile file) { + File convertFile = new File(file.getOriginalFilename()); + try (FileOutputStream fos = new FileOutputStream(convertFile)) { + fos.write(file.getBytes()); + } catch (IOException e) { + e.printStackTrace(); + } + return convertFile; + } +} + diff --git a/src/main/java/mos/mosback/login/domain/user/service/UserService.java b/src/main/java/mos/mosback/login/domain/user/service/UserService.java index efd1aea..d8fa5d8 100644 --- a/src/main/java/mos/mosback/login/domain/user/service/UserService.java +++ b/src/main/java/mos/mosback/login/domain/user/service/UserService.java @@ -1,5 +1,6 @@ package mos.mosback.login.domain.user.service; +import javassist.NotFoundException; import mos.mosback.login.domain.user.Role; import mos.mosback.login.domain.user.User; import mos.mosback.login.domain.user.dto.FindPWDto; @@ -8,27 +9,64 @@ import mos.mosback.login.domain.user.dto.UserProfileDto; import mos.mosback.login.domain.user.dto.UserSignUpDto; import mos.mosback.login.domain.user.repository.UserRepository; + import lombok.RequiredArgsConstructor; +import mos.mosback.stRoom.domain.stRoom.StRoomEntity; +import mos.mosback.stRoom.domain.stRoom.StudyMemberEntity; +import mos.mosback.stRoom.dto.StudyMembershipStatusResponseDto; +import mos.mosback.stRoom.repository.StRoomRepository; +import mos.mosback.stRoom.repository.StudyMemberRepository; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import javax.persistence.EntityNotFoundException; import javax.transaction.Transactional; -import java.util.Optional; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static com.amazonaws.services.ec2.model.Scope.Region; + @Service @Transactional @RequiredArgsConstructor public class UserService { - private final UserRepository userRepository; - private final PasswordEncoder passwordEncoder; - private final JavaMailSender mailSender; + @Autowired + private UserRepository userRepository; + + @Autowired + private PasswordEncoder passwordEncoder; + + @Autowired + private JavaMailSender mailSender; + + @Autowired + private StudyMemberRepository studyMemberRepository; + + private StRoomRepository stRoomRepository; + + @Autowired + private final FileService fileService; + + public User getUserById(Long id) throws Exception { + Optional optionalUser = userRepository.findById(id); + if (optionalUser.isPresent()) { + return optionalUser.get(); + } else { + throw new Exception("현재 로그인한 사용자를 찾을 수 없습니다."); + } + } public void signUp(UserSignUpDto userSignUpDto) throws Exception { @@ -48,15 +86,16 @@ public void signUp(UserSignUpDto userSignUpDto) throws Exception { - private User getUserByEmail(String email) throws Exception { + public User getUserByEmail(String email) throws Exception { Optional optionalUser = userRepository.findByEmail(email); if (optionalUser.isPresent()) { return optionalUser.get(); } else { - throw new Exception("현재 로그인한 사용자를 찾을 수 없습니다."); + throw new Exception("해당 이메일의 사용자를 찾을 수 없습니다: " + email); } } + //마이페이지정보입력 public void createUser(String currentEmail, UserProfileDto userProfileDto) throws Exception { if (userRepository.findByNickname(userProfileDto.getNickname()).isPresent()) { @@ -68,29 +107,51 @@ public void createUser(String currentEmail, UserProfileDto userProfileDto) throw // 회원 정보 생성 user.setNickname(userProfileDto.getNickname()); + user.setName(userProfileDto.getName()); user.setStr_duration(userProfileDto.getStr_duration()); user.setEnd_duration(userProfileDto.getEnd_duration()); user.setMessage(userProfileDto.getMessage()); user.setCompany(userProfileDto.getCompany()); - + user.setTend1(userProfileDto.getTend1()); + user.setTend2(userProfileDto.getTend2()); user.setRole(Role.USER); + userRepository.save(user); } catch (Exception e) { throw new Exception("회원 정보를 생성하는 동안 오류가 발생했습니다.", e); } } + public void updateUserProfileImageUrl(Long userId, String imageUrl) throws Exception { + Optional optionalUser = userRepository.findById(userId); + if (optionalUser.isPresent()) { + User user = optionalUser.get(); + user.setImageUrl(imageUrl); + userRepository.save(user); + } else { + throw new Exception("해당 ID의 사용자를 찾을 수 없습니다: " + userId); + } + } + + //마이페이지 업데이트 - public void updateUserProfile(String currentEmail, UserProfileDto userProfileDto) throws Exception { + public void updateUserProfile(String currentEmail, UserProfileDto userProfileDto ) throws Exception { try { User user = getUserByEmail(currentEmail); // 회원 정보 업데이트 user.setNickname(userProfileDto.getNickname()); + user.setName(userProfileDto.getName()); user.setStr_duration(userProfileDto.getStr_duration()); user.setEnd_duration(userProfileDto.getEnd_duration()); user.setMessage(userProfileDto.getMessage()); user.setCompany(userProfileDto.getCompany()); + user.setTend1(userProfileDto.getTend1()); + user.setTend2(userProfileDto.getTend2()); + user.setRoomId(userProfileDto.getRoomId()); + + + userRepository.save(user); } catch (Exception e) { @@ -177,6 +238,139 @@ public Optional loadUserByUsername(String userEmail) throws UsernameNotFou return userRepository.findByEmail(userEmail); } + // 사용자의 이미지 URL 업데이트 메서드 + public void uploadAndSaveImage(Long id, MultipartFile file) throws IOException { + // id를 사용하여 사용자 정보를 데이터베이스에서 조회합니다. + User user = userRepository.findById(id).get(); + + if (user != null) { + // 이미지를 S3에 업로드하고 URL을 받아옵니다. + String imageUrl = fileService.uploadFile(file, user.getId()); + + // 사용자 정보가 존재하면 이미지 URL을 업데이트합니다. + user.setImageUrl(imageUrl); + userRepository.save(user); // 변경된 정보를 저장합니다. + } else { + throw new IllegalArgumentException("해당 ID의 사용자를 찾을 수 없습니다: " + id); + } + } + + public User findById(Long id) { + return userRepository.findById(id).orElse(null); + } + + public UserProfileDto getUserProfileByEmail(String email) throws Exception { + Optional optionalUser = userRepository.findByEmail(email); + if (optionalUser.isPresent()) { + User user = optionalUser.get(); + // 엔터티 정보를 DTO로 매핑하여 반환 + return new UserProfileDto( + user.getId(), + user.getNickname(), + user.getName(), + user.getStr_duration(), + user.getEnd_duration(), + user.getMessage(), + user.getCompany(), + user.getTend1(), + user.getTend2(), + user.getRoomId(), + user.getImageUrl() + ); + } else { + throw new Exception("해당 이메일의 사용자를 찾을 수 없습니다: " + email); + } + } + + + public List getStudyGroupsForUserByEmail(String email) throws NotFoundException { + Optional userOptional = userRepository.findByEmail(email); + if (userOptional.isPresent()) { + User user = userOptional.get(); + return user.getStRooms(); + } else { + throw new NotFoundException("사용자를 찾을 수 없습니다."); + } + } + + + + public List getStudyGroupsForUserByMemberId(Long memberId) { + // 사용자의 memberId를 이용해 사용자가 참여한 스터디 그룹 목록을 조회합니다. + List userStudyMemberships = studyMemberRepository.findAllByMemberId(memberId); + + // 사용자가 참여한 스터디 그룹 목록을 담을 리스트를 생성합니다. + List userStudyGroups = stRoomRepository.findByMembersIn(userStudyMemberships); + + return userStudyGroups; + } + +// public List getStudyMembershipStatus(String userEmail) throws NotFoundException { +// User user = userRepository.findByEmail(userEmail) +// .orElseThrow(() -> new NotFoundException("사용자를 찾을 수 없습니다.")); +// +// Long memberId = user.getId(); +// List userStudyMemberships = studyMemberRepository.findAllByMemberId(memberId); +// +// Map latestStudyMembershipsMap = userStudyMemberships.stream() +// .collect(Collectors.toMap( +// StudyMemberEntity::getStRoom, +// Function.identity(), +// (m1, m2) -> m1.getJoinedAt().isAfter(m2.getJoinedAt()) ? m1 : m2 +// )); +// +// List membershipStatusList = latestStudyMembershipsMap.entrySet().stream() +// .map(entry -> { +// StRoomEntity stRoom = entry.getKey(); +// StudyMemberEntity studyMembership = entry.getValue(); +// String status = studyMembership.getStatus().name(); +// return new StudyMembershipStatusResponseDto(stRoom.getTitle(), status); +// }) +// .collect(Collectors.toList()); +// +// return membershipStatusList; +// } + +// 내가 가입한 스터디의 상태를 보여줌(하나만 보여주는 문제) + public List getStudyMembershipStatus(String userEmail) throws NotFoundException { + User user = userRepository.findByEmail(userEmail) + .orElseThrow(() -> new NotFoundException("사용자를 찾을 수 없습니다.")); + + Long memberId = user.getId(); + List userStudyMemberships = studyMemberRepository.findAllByMemberId(memberId); + + List membershipStatusList = userStudyMemberships.stream() + .map(member -> { + StRoomEntity stRoom = member.getStRoom(); + String status = member.getStatus().name(); + return new StudyMembershipStatusResponseDto(stRoom.getTitle(), status); + }) + .collect(Collectors.toList()); + + return membershipStatusList; + } + + +// public List getStudyMembershipStatus(String userEmail) throws NotFoundException { +// User user = userRepository.findByEmail(userEmail) +// .orElseThrow(() -> new NotFoundException("사용자를 찾을 수 없습니다.")); +// +// Long memberId = user.getId(); +// List userStudyMemberships = studyMemberRepository.findAllByMemberId(memberId); +// +// List membershipStatusList = userStudyMemberships.stream() +// .map(member -> { +// StRoomEntity stRoom = member.getStRoom(); +// String status = member.getStatus().name(); +// return new StudyMembershipStatusResponseDto(stRoom.getTitle(), status); +// }) +// .collect(Collectors.toList()); +// +// return membershipStatusList; +// } + + + } diff --git a/src/main/java/mos/mosback/login/global/config/MailConfig.java b/src/main/java/mos/mosback/login/global/config/MailConfig.java index 54eb8e7..a4768ed 100644 --- a/src/main/java/mos/mosback/login/global/config/MailConfig.java +++ b/src/main/java/mos/mosback/login/global/config/MailConfig.java @@ -1,5 +1,6 @@ package mos.mosback.login.global.config; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.mail.javamail.JavaMailSender; @@ -9,14 +10,21 @@ @Configuration public class MailConfig { + + @Value("${spring.mail.username}") + private String mailUsername; + + @Value("${spring.mail.password}") + private String mailPassword; @Bean public JavaMailSender javaMailService() { + + JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl(); javaMailSender.setHost("smtp.naver.com"); - javaMailSender.setUsername("이메일"); - javaMailSender.setPassword("비밀번호"); - + javaMailSender.setUsername(mailUsername); + javaMailSender.setPassword(mailPassword); javaMailSender.setPort(465); javaMailSender.setJavaMailProperties(getMailProperties()); diff --git a/src/main/java/mos/mosback/login/global/config/SecurityConfig.java b/src/main/java/mos/mosback/login/global/config/SecurityConfig.java index a902246..c39c990 100644 --- a/src/main/java/mos/mosback/login/global/config/SecurityConfig.java +++ b/src/main/java/mos/mosback/login/global/config/SecurityConfig.java @@ -67,7 +67,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { // 아이콘, css, js 관련 // 기본 페이지, css, image, js 하위 폴더에 있는 자료들은 모두 접근 가능, h2-console에 접근 가능 .antMatchers("/","/css/**","/images/**","/js/**","/favicon.ico","/h2-console/**").permitAll() - .antMatchers("/sign-up").permitAll() // 회원가입 접근 가능 + .antMatchers("/sign-up","/login").permitAll() // 회원가입 접근 가능 .antMatchers("/user-emails/{email}/exists").permitAll() // 중복 체크 엔드포인트를 예외로 처리 .anyRequest().authenticated() // 위의 경로 이외에는 모두 인증된 사용자만 접근 가능 .and() @@ -86,23 +86,6 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { return http.build(); } -// @Bean -// public PasswordEncoder passwordEncoder() { -// return PasswordEncoderFactories.createDelegatingPasswordEncoder(); -// } - -// @Bean -// public PasswordEncoder passwordEncoder() { -// String idForEncode = "bcrypt"; // 기본적으로 BCrypt 알고리즘을 사용합니다. -// PasswordEncoder defaultEncoder = new BCryptPasswordEncoder(); -// PasswordEncoder customEncoder = new Pbkdf2PasswordEncoder(); // 원하는 다른 인코딩 알고리즘을 추가할 수 있습니다. -// -// DelegatingPasswordEncoder delegatingPasswordEncoder = new DelegatingPasswordEncoder(idForEncode, (Map) defaultEncoder); -// delegatingPasswordEncoder.setDefaultPasswordEncoderForMatches(customEncoder); -// -// return delegatingPasswordEncoder; -// } - @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); diff --git a/src/main/java/mos/mosback/login/global/config/StorageConfig.java b/src/main/java/mos/mosback/login/global/config/StorageConfig.java new file mode 100644 index 0000000..4f45414 --- /dev/null +++ b/src/main/java/mos/mosback/login/global/config/StorageConfig.java @@ -0,0 +1,31 @@ +package mos.mosback.login.global.config; + +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class StorageConfig { + + @Value("${cloud.aws.credentials.access-key}") + private String accessKey; + + @Value("${cloud.aws.credentials.secret-key}") + private String accessSecret; + @Value("${cloud.aws.region.static}") + private String region; + + @Bean + public AmazonS3 s3Client() { + AWSCredentials credentials = new BasicAWSCredentials(accessKey, accessSecret); + return AmazonS3ClientBuilder.standard() + .withCredentials(new AWSStaticCredentialsProvider(credentials)) + .withRegion(region).build(); + } + +} \ No newline at end of file diff --git a/src/main/java/mos/mosback/login/global/jwt/filter/JwtAuthenticationProcessingFilter.java b/src/main/java/mos/mosback/login/global/jwt/filter/JwtAuthenticationProcessingFilter.java index 52fc4da..df31628 100644 --- a/src/main/java/mos/mosback/login/global/jwt/filter/JwtAuthenticationProcessingFilter.java +++ b/src/main/java/mos/mosback/login/global/jwt/filter/JwtAuthenticationProcessingFilter.java @@ -86,11 +86,12 @@ public void checkRefreshTokenAndReIssueAccessToken(HttpServletResponse response, userRepository.findByRefreshToken(refreshToken) .ifPresent(user -> { String reIssuedRefreshToken = reIssueRefreshToken(user); - jwtService.sendAccessAndRefreshToken(response, jwtService.createAccessToken(user.getEmail()), + jwtService.sendAccessAndRefreshToken(response, jwtService.createJwt(user.getEmail()), reIssuedRefreshToken); }); } + /** * [리프레시 토큰 재발급 & DB에 리프레시 토큰 업데이트 메소드] * jwtService.createRefreshToken()으로 리프레시 토큰 재발급 후 @@ -116,7 +117,7 @@ public void checkAccessTokenAndAuthentication(HttpServletRequest request, HttpSe log.info("checkAccessTokenAndAuthentication() 호출"); jwtService.extractAccessToken(request) .filter(jwtService::isTokenValid) - .ifPresent(accessToken -> jwtService.extractEmail(accessToken) + .ifPresent(jwt -> jwtService.extractEmailFromJwt(jwt) .ifPresent(email -> userRepository.findByEmail(email) .ifPresent(this::saveAuthentication))); diff --git a/src/main/java/mos/mosback/login/global/jwt/service/JwtService.java b/src/main/java/mos/mosback/login/global/jwt/service/JwtService.java index 83881cf..e1670b2 100644 --- a/src/main/java/mos/mosback/login/global/jwt/service/JwtService.java +++ b/src/main/java/mos/mosback/login/global/jwt/service/JwtService.java @@ -42,7 +42,7 @@ public class JwtService { * JWT의 Subject와 Claim으로 email 사용 -> 클레임의 name을 "email"으로 설정 * JWT의 헤더에 들어오는 값 : 'Authorization(Key) = Bearer {토큰} (Value)' 형식 */ - private static final String ACCESS_TOKEN_SUBJECT = "AccessToken"; + private static final String ACCESS_TOKEN_SUBJECT = "accessToken"; private static final String REFRESH_TOKEN_SUBJECT = "RefreshToken"; private static final String EMAIL_CLAIM = "email"; private static final String BEARER = "Bearer "; @@ -52,19 +52,17 @@ public class JwtService { /** * AccessToken 생성 메소드 */ - public String createAccessToken(String email) { + public String createJwt(String email) { Date now = new Date(); - return JWT.create() // JWT 토큰을 생성하는 빌더 반환 - .withSubject(ACCESS_TOKEN_SUBJECT) // JWT의 Subject 지정 -> AccessToken이므로 AccessToken - .withExpiresAt(new Date(now.getTime() + accessTokenExpirationPeriod)) // 토큰 만료 시간 설정 - - //클레임으로는 저희는 email 하나만 사용합니다. - //추가적으로 식별자나, 이름 등의 정보를 더 추가하셔도 됩니다. - //추가하실 경우 .withClaim(클래임 이름, 클래임 값) 으로 설정해주시면 됩니다 + return JWT.create() + .withSubject(ACCESS_TOKEN_SUBJECT) + .withExpiresAt(new Date(now.getTime() + accessTokenExpirationPeriod)) .withClaim(EMAIL_CLAIM, email) - .sign(Algorithm.HMAC512(secretKey)); // HMAC512 알고리즘 사용, application-jwt.yml에서 지정한 secret 키로 암호화 + .sign(Algorithm.HMAC512(secretKey)); } + + /** * RefreshToken 생성 * RefreshToken은 Claim에 email도 넣지 않으므로 withClaim() X @@ -116,8 +114,8 @@ public Optional extractRefreshToken(HttpServletRequest request) { */ public Optional extractAccessToken(HttpServletRequest request) { return Optional.ofNullable(request.getHeader(accessHeader)) - .filter(refreshToken -> refreshToken.startsWith(BEARER)) - .map(refreshToken -> refreshToken.replace(BEARER, "")); + .filter(token -> token.startsWith(BEARER)) + .map(token -> token.replace(BEARER, "")); } /** @@ -127,20 +125,20 @@ public Optional extractAccessToken(HttpServletRequest request) { * 유효하다면 getClaim()으로 이메일 추출 * 유효하지 않다면 빈 Optional 객체 반환 */ - public Optional extractEmail(String accessToken) { + public Optional extractEmailFromJwt(String jwt) { try { - // 토큰 유효성 검사하는 데에 사용할 알고리즘이 있는 JWT verifier builder 반환 return Optional.ofNullable(JWT.require(Algorithm.HMAC512(secretKey)) - .build() // 반환된 빌더로 JWT verifier 생성 - .verify(accessToken) // accessToken을 검증하고 유효하지 않다면 예외 발생 - .getClaim(EMAIL_CLAIM) // claim(Emial) 가져오기 + .build() + .verify(jwt) + .getClaim(EMAIL_CLAIM) .asString()); } catch (Exception e) { - log.error("액세스 토큰이 유효하지 않습니다."); + log.error("유효하지 않은 JWT입니다. {}", e.getMessage()); return Optional.empty(); } } + /** * AccessToken 헤더 설정 */ diff --git a/src/main/java/mos/mosback/login/global/login/handler/LoginFailureHandler.java b/src/main/java/mos/mosback/login/global/login/handler/LoginFailureHandler.java index 5e34917..5718db2 100644 --- a/src/main/java/mos/mosback/login/global/login/handler/LoginFailureHandler.java +++ b/src/main/java/mos/mosback/login/global/login/handler/LoginFailureHandler.java @@ -1,5 +1,6 @@ package mos.mosback.login.global.login.handler; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; @@ -7,6 +8,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; /** * JWT 로그인 실패 시 처리하는 핸들러 @@ -21,7 +24,14 @@ public void onAuthenticationFailure(HttpServletRequest request, HttpServletRespo response.setStatus(HttpServletResponse.SC_BAD_REQUEST); response.setCharacterEncoding("UTF-8"); response.setContentType("text/plain;charset=UTF-8"); - response.getWriter().write("로그인 실패! 이메일이나 비밀번호를 확인해주세요."); log.info("로그인에 실패했습니다. 메시지 : {}", exception.getMessage()); + + Map responseData = new HashMap<>(); + responseData.put("status", 500); + responseData.put("success", false); + responseData.put("message" , exception.getMessage()); + response.setContentType("application/json"); + response.setStatus(HttpServletResponse.SC_OK); + response.getWriter().write(new ObjectMapper().writeValueAsString(responseData)); } } diff --git a/src/main/java/mos/mosback/login/global/login/handler/LoginSuccessHandler.java b/src/main/java/mos/mosback/login/global/login/handler/LoginSuccessHandler.java index 0a31bab..0669a4a 100644 --- a/src/main/java/mos/mosback/login/global/login/handler/LoginSuccessHandler.java +++ b/src/main/java/mos/mosback/login/global/login/handler/LoginSuccessHandler.java @@ -1,16 +1,22 @@ package mos.mosback.login.global.login.handler; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import mos.mosback.login.domain.user.dto.UserInfo; import mos.mosback.login.domain.user.repository.UserRepository; import mos.mosback.login.global.jwt.service.JwtService; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; +import org.springframework.web.bind.annotation.RequestBody; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; @Slf4j @RequiredArgsConstructor @@ -19,17 +25,16 @@ public class LoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { private final JwtService jwtService; private final UserRepository userRepository; + @Value("${jwt.access.expiration}") private String accessTokenExpiration; - @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, - Authentication authentication) { + Authentication authentication) throws IOException { String email = extractUsername(authentication); // 인증 정보에서 Username(email) 추출 - String accessToken = jwtService.createAccessToken(email); // JwtService의 createAccessToken을 사용하여 AccessToken 발급 + String jwt = jwtService.createJwt(email); // JwtService의 createJwt를 사용하여 Jwt 토큰 발급 String refreshToken = jwtService.createRefreshToken(); // JwtService의 createRefreshToken을 사용하여 RefreshToken 발급 - - jwtService.sendAccessAndRefreshToken(response, accessToken, refreshToken); // 응답 헤더에 AccessToken, RefreshToken 실어서 응답 + jwtService.sendAccessAndRefreshToken(response, jwt, refreshToken); // 응답 헤더에 Jwt, RefreshToken 실어서 응답 String currentEmail = email; userRepository.findByEmail(currentEmail) @@ -38,10 +43,24 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo userRepository.saveAndFlush(user); }); log.info("로그인에 성공하였습니다. 이메일 : {}", currentEmail); - log.info("로그인에 성공하였습니다. AccessToken : {}", accessToken); - log.info("발급된 AccessToken 만료 기간 : {}", accessTokenExpiration); + log.info("로그인에 성공하였습니다. Jwt : {}", jwt); + log.info("발급된 Jwt 만료 기간 : {}", accessTokenExpiration); + + Map result = new HashMap<>(); + result.put("jwt", jwt); + + Map responseData = new HashMap<>(); + responseData.put("status", 200); + responseData.put("success", true); + responseData.put("result", result); + + response.setContentType("application/json"); + response.setStatus(HttpServletResponse.SC_OK); + response.getWriter().write(new ObjectMapper().writeValueAsString(responseData)); } + + private String extractUsername(Authentication authentication) { UserDetails userDetails = (UserDetails) authentication.getPrincipal(); return userDetails.getUsername(); diff --git a/src/main/java/mos/mosback/login/global/oauth2/handler/OAuth2LoginSuccessHandler.java b/src/main/java/mos/mosback/login/global/oauth2/handler/OAuth2LoginSuccessHandler.java index b143a24..ba301e0 100644 --- a/src/main/java/mos/mosback/login/global/oauth2/handler/OAuth2LoginSuccessHandler.java +++ b/src/main/java/mos/mosback/login/global/oauth2/handler/OAuth2LoginSuccessHandler.java @@ -33,37 +33,32 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo CustomOAuth2User oAuth2User = (CustomOAuth2User) authentication.getPrincipal(); // User의 Role이 GUEST일 경우 처음 요청한 회원이므로 회원가입 페이지로 리다이렉트 - if(oAuth2User.getRole() == Role.GUEST) { - - + if (oAuth2User.getRole() == Role.GUEST) { String email = oAuth2User.getAttribute("email"); String name = oAuth2User.getAttribute("name"); - String accessToken = jwtService.createAccessToken(oAuth2User.getEmail()); - response.addHeader(jwtService.getAccessHeader(), "Bearer " + accessToken); - jwtService.sendAccessAndRefreshToken(response, accessToken, null); + String jwt = jwtService.createJwt(oAuth2User.getEmail()); // JwtService의 createJwt를 사용하여 Jwt 토큰 발급 + response.addHeader(jwtService.getAccessHeader(), "Bearer " + jwt); + jwtService.sendAccessAndRefreshToken(response, jwt, null); response.sendRedirect("/profile"); // 프론트의 회원가입 추가 정보 입력 폼으로 리다이렉트 -// User findUser = userRepository.findByEmail(oAuth2User.getEmail()) -// .orElseThrow(() -> new IllegalArgumentException("이메일에 해당하는 유저가 없습니다.")); -// findUser.authorizeUser(); } else { loginSuccess(response, oAuth2User); // 로그인에 성공한 경우 access, refresh 토큰 생성 } } catch (Exception e) { throw e; } - } // TODO : 소셜 로그인 시에도 무조건 토큰 생성하지 말고 JWT 인증 필터처럼 RefreshToken 유/무에 따라 다르게 처리해보기 private void loginSuccess(HttpServletResponse response, CustomOAuth2User oAuth2User) throws IOException { - String accessToken = jwtService.createAccessToken(oAuth2User.getEmail()); - String refreshToken = jwtService.createRefreshToken(); - response.addHeader(jwtService.getAccessHeader(), "Bearer " + accessToken); + String jwt = jwtService.createJwt(oAuth2User.getEmail()); // JwtService의 createJwt를 사용하여 Jwt 토큰 발급 + String refreshToken = jwtService.createRefreshToken(); // JwtService의 createRefreshToken을 사용하여 RefreshToken 발급 + response.addHeader(jwtService.getAccessHeader(), "Bearer " + jwt); response.addHeader(jwtService.getRefreshHeader(), "Bearer " + refreshToken); - jwtService.sendAccessAndRefreshToken(response, accessToken, refreshToken); + jwtService.sendAccessAndRefreshToken(response, jwt, refreshToken); jwtService.updateRefreshToken(oAuth2User.getEmail(), refreshToken); } + } \ No newline at end of file diff --git a/src/main/java/mos/mosback/stRoom/controller/StRoomController.java b/src/main/java/mos/mosback/stRoom/controller/StRoomController.java new file mode 100644 index 0000000..10138c5 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/controller/StRoomController.java @@ -0,0 +1,379 @@ +package mos.mosback.stRoom.controller; +import lombok.RequiredArgsConstructor; +import mos.mosback.login.domain.user.User; +import mos.mosback.login.domain.user.dto.UserProfileDto; +import mos.mosback.stRoom.domain.stRoom.MemberStatus; +import mos.mosback.stRoom.domain.stRoom.StRoomEntity; +import mos.mosback.stRoom.dto.*; +import mos.mosback.stRoom.service.StRoomService; +import mos.mosback.stRoom.service.ToDoService; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.*; +import software.amazon.ion.NullValueException; + +import javax.persistence.EntityNotFoundException; +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +@RequiredArgsConstructor +@RestController +@RequestMapping("/studyRoom") //URL 패턴 +public class StRoomController { + + private final StRoomService stRoomService; // stRoomService를 주입. + private final ToDoService toDoService; + + @PostMapping("/create") + public ResponseEntity> saveRoom(@RequestBody StRoomSaveRequestDto requestDto, HttpServletRequest req) { + // 현재 로그인한 사용자의 정보 가져오기 + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String currentEmail = authentication.getName(); // 현재 사용자의 이메일 + requestDto.setEmail(currentEmail); + Long stroomId = stRoomService.save(requestDto, req); + Map response = new HashMap<>(); + if (stroomId != null) { + + response.put("status", 201); + response.put("message", "스터디생성완료"); + response.put("roomId",stroomId); + response.put("success", true); + return ResponseEntity.status(HttpStatus.OK).body(response); + + } else { + response.put("status", 500); + response.put("message", "서버내부오류"); + response.put("success", false); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + @GetMapping("/my{roomId}") + public ResponseEntity FindByID (@PathVariable Long roomId) { + + StRoomResponseDto stroom = stRoomService.findById(roomId); + + try { + List studyRoomMemberList = stRoomService.getStudyRoomMemberList(roomId); + StRoomMemberResponseDto leaderInfo = studyRoomMemberList.stream() + .filter(data -> MemberStatus.Leader.name().equals(data.getStatus().name())) + .findFirst().orElseThrow(() -> new EntityNotFoundException("leader info not found")); + User leaderUser = stRoomService.getUserInfo(leaderInfo.getMemberId()); + stroom.setNickname(leaderUser.getNickname()); + stroom.setMemberNum(studyRoomMemberList.size()); + + return new ResponseEntity<>(stroom, HttpStatus.OK); + }catch (Exception e){ + Map response = new HashMap<>(); + response.put("status", 404); + response.put("message", "해당 스터디룸 없음" ); + response.put("success", false); + + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); + } + } + + @GetMapping("/search") + public ResponseEntity searchRoom(@RequestParam String keyword) { + try { + List strooms = stRoomService.findByTitleContaining(keyword); + return new ResponseEntity<>(strooms, HttpStatus.OK); + } catch (IllegalArgumentException e) { + Map response = new HashMap<>(); + response.put("status", 404); + response.put("message", "존재하지 않는 키워드: " + keyword); + response.put("success", false); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); + } + } + @PutMapping("/update/{roomId}") + public ResponseEntity> UpdateRoom(@PathVariable Long roomId, @RequestBody StRoomUpdateRequestDto requestDto) { + try { + Map response = new HashMap<>(); + stRoomService.update(roomId, requestDto); + response.put("status", 201); + response.put("message", "수정완료"); + response.put("success", true); + return ResponseEntity.status(HttpStatus.CREATED).body(response); + } catch (IllegalArgumentException e) { + Map response = new HashMap<>(); + response.put("status", 404); + response.put("message", "해당 스터디룸 없음"); + response.put("success", false); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); + } + } + + @DeleteMapping("/{roomId}") + public ResponseEntity> deleteRoom(@PathVariable Long roomId) { + Map response = new HashMap<>(); + try { + stRoomService.delete(roomId); + response.put("status", 200); + response.put("success", true); + response.put("message", "삭제완료 : "+"'"+roomId+"'"); + return ResponseEntity.ok(response); + } catch (EntityNotFoundException ex) { + response.put("status", 404); + response.put("success", false); + response.put("message", "해당 스터디룸 없음"); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); + } + } + + @GetMapping("/all") + public ResponseEntity> findAllRoomsDesc(){ + List rooms = stRoomService.findAllRoomsDesc(); + return ResponseEntity.ok(rooms); + } + + @GetMapping("/popular") + public ResponseEntity> getPopularRooms() { + List rooms = stRoomService.findPopularRoom(); + return ResponseEntity.ok(rooms); + } + + @GetMapping("/home/studyRoom") + public ResponseEntity> findRoomsInHome(){ + List roomsInhome = stRoomService.findRoomsInHome(); + + return new ResponseEntity<>(roomsInhome,HttpStatus.OK); + } + + @GetMapping("/byCategory/{category}") + public ResponseEntity> getStudyRoomsByCategory(@PathVariable String category) { + Map response = new HashMap<>(); + List studyRooms = stRoomService.findByCategory(category); + + if (studyRooms.isEmpty()) { + response.put("status", HttpStatus.NOT_FOUND.value()); + response.put("success", false); + response.put("message", "NOT FOUND: " + category); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); + } + + response.put("status", HttpStatus.OK.value()); + response.put("success", true); + response.put("studyRooms", studyRooms); // 데이터를 직접 넣음 + return ResponseEntity.ok(response); + } + + + @GetMapping("/question/{roomId}") + public ResponseEntity getQuestionById(@PathVariable Long roomId) { + try { + QuestionDto question = stRoomService.getQuestionById(roomId); + return ResponseEntity.ok(question); + } catch (EntityNotFoundException ex) { + Map response = new HashMap<>(); + response.put("status:", HttpStatus.NOT_FOUND.value()); + response.put("success", false); + response.put("message", "해당 스터디룸 없음"); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); + } + } + + @GetMapping("/myPage/{memberId}") + public ResponseEntity> getUserProfile(@PathVariable Long memberId) { + Map response = new HashMap<>(); + try { + UserProfileDto userProfileDto = stRoomService.getMemberProfileById(memberId); + if(userProfileDto != null) { + response.put("status", 200); + response.put("success", true); + response.put("data", userProfileDto); + return ResponseEntity.ok(response); + } + else{ + response.put("status", 404); + response.put("success", false); + response.put("message", "해당멤버를 찾을 수 없음"); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); + } + + } catch (Exception e) { + + response.put("status", 500); + response.put("success", false); + response.put("message", "서버내부오류"); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); + } + } + + + @GetMapping("/QnA/{memberId}/{roomId}") + public ResponseEntity getQuestionandAnswerById(@PathVariable("memberId") Long memberId, @PathVariable("roomId") Long roomId) { + { + try { + QuestionAndAnswerResponseDto QnA = stRoomService.getQuestionAndAnswerById(memberId,roomId); + return ResponseEntity.ok(QnA); + }catch (EntityNotFoundException ex) { + Map response = new HashMap<>(); + response.put("status:", HttpStatus.NOT_FOUND.value()); + response.put("success", false); + response.put("message", "요청을 찾을 수 없음"); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); + } + + } + } + @PostMapping("/memberjoin/{roomId}") + public ResponseEntity> memberJoin(@PathVariable Long roomId, + @RequestBody StRoomMemberJoinRequestDto requestDto) { + // 현재 로그인한 사용자의 정보 가져오기 + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String currentEmail = authentication.getName(); // 현재 사용자의 이메일 + requestDto.setEmail(currentEmail); + requestDto.setRoomId(roomId); + stRoomService.memberJoin(requestDto); + try{ + Map response = new HashMap<>(); + response.put("status:", HttpStatus.OK.value()); + response.put("success", true); + response.put("message", "가입완료"); + return ResponseEntity.status(HttpStatus.OK).body(response); + }catch (IllegalArgumentException e){ + Map response = new HashMap<>(); + response.put("status:", HttpStatus.NOT_FOUND.value()); + response.put("success", false); + response.put("message", "해당스터디룸 없음"); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); + } + } + + @PostMapping("/accept/{roomId}") + public ResponseEntity> acceptMember(@PathVariable Long roomId, + @RequestBody AcceptMemberRequestDto requestDto) { + try { + requestDto.setRoomId(roomId); + stRoomService.acceptMember(requestDto); + Map response = new HashMap<>(); + response.put("status:", HttpStatus.OK.value()); + response.put("success", true); + response.put("message", "승인완료"); + return ResponseEntity.status(HttpStatus.OK).body(response); + }catch (IllegalArgumentException ex){ + Map response = new HashMap<>(); + response.put("status:", HttpStatus.BAD_REQUEST.value()); + response.put("success", false); + response.put("message", "잘못된 요청"); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); + } + } + @PostMapping("/reject/{roomId}") + public ResponseEntity> rejectMember(@PathVariable Long roomId, + @RequestBody AcceptMemberRequestDto requestDto) { + try { + requestDto.setRoomId(roomId); + stRoomService.rejectMember(requestDto); + Map response = new HashMap<>(); + response.put("status:", HttpStatus.OK.value()); + response.put("success", true); + response.put("message", "승인거절완료"); + return ResponseEntity.status(HttpStatus.OK).body(response); + }catch (IllegalArgumentException ex){ + Map response = new HashMap<>(); + response.put("status:", HttpStatus.BAD_REQUEST.value()); + response.put("success", false); + response.put("message", "잘못된 요청"); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); + } + } + + + + @GetMapping("/recruiting") + public ResponseEntity getRecruitingStudies() { + List recruitingStudies = stRoomService.getRecruitingStudies(); + + if (!recruitingStudies.isEmpty()) { + return ResponseEntity.ok(recruitingStudies); + } else { + Map response = new HashMap<>(); + response.put("status", HttpStatus.NOT_FOUND.value()); + response.put("success", false); + response.put("message", "모집중인 스터디룸을 찾을 수 없음"); + + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); + } + } + + /** + * 스터디 가입여부를 조회하는 API + * @return + */ + @GetMapping("/my-info") + public ResponseEntity> getMyInfo() throws Exception { + // 현재 로그인한 사용자의 정보 가져오기 + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String email = authentication.getName(); // 현재 사용자의 이메일 + String joinYn = stRoomService.getMyInfo(email); + Map response = new HashMap<>(); + response.put("joinYn", joinYn); + response.put("success", true); + + return ResponseEntity.status(HttpStatus.CREATED).body(response); + } + + /** + * 스터디 가입여부를 조회하는 API + * @return + */ + @GetMapping("/my-study-member-history") + public ResponseEntity> getMyStudyMemberHistory() throws Exception { + // 현재 로그인한 사용자의 정보 가져오기 + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String email = authentication.getName(); // 현재 사용자의 이메일 + List memberHistoryList = stRoomService.getMyStudyMemberHistory(email); + Map response = new HashMap<>(); + response.put("memberHistoryList", memberHistoryList); + response.put("success", true); + + return ResponseEntity.status(HttpStatus.CREATED).body(response); + } + + @GetMapping("/Info/{roomId}") + public ResponseEntity> recruitInfo(@PathVariable Long roomId) { + String recruitInfo = stRoomService.isRecruiting(roomId); + StRoomDetailResponseDto stroom = stRoomService.findByRoomId(roomId); + List todoList = toDoService.findStRoomTodoByRoomId(roomId); + List studyRoomMemberList = stRoomService.getStudyRoomMemberList(roomId); + StRoomMemberResponseDto leaderInfo = studyRoomMemberList.stream() + .filter(data -> MemberStatus.Leader.name().equals(data.getStatus().name())) + .findFirst().orElseThrow(() -> new EntityNotFoundException("leader info not found")); + User leaderUser = stRoomService.getUserInfo(leaderInfo.getMemberId()); + stroom.setNickname(leaderUser.getNickname()); + stroom.setMemberNum(studyRoomMemberList.size()); + + Map response = new HashMap<>(); + response.put("모집", recruitInfo); + response.put("StudyRoom", stroom); + response.put("todoList", todoList); + response.put("studyRoomMemberList", studyRoomMemberList); + return ResponseEntity.status(HttpStatus.OK).body(response); + + } + + +} + + +//핸들러 메서드 : + +//create: 새 게시물을 생성 +///MyStudy/{roomId}: 특정 roomId에 해당하는 게시물을 조회( 스터디그룹 -마이스터디화면 조회 ) +//search: 제목에 키워드가 포함된 게시물을 검색하는 엔드포인트. +///update/{roomId}: 게시물 수정 +///(Delete) {roomId}: 게시물을 삭제 +//getPopularstrooms: 조회순으로 게시물을 조회(인기순 조회시) +//all: 전체 스터디 조회 +///question/{roomId}: 스터디장이 남긴 질문 조회 +//memberjoin - 스터디 가입 API +///byCategory/{category} : 카테고리별로 조회하는 엔프포인트 +//https://blog.pumpkin-raccoon.com/115#:~:text=1%201.%20%EB%B3%B5%EC%88%98%20%3E%20%EB%8B%A8%EC%88%98%20REST%20API%EC%97%90%EC%84%9C%EB%8A%94%20post%2C,%EC%BB%AC%EB%A0%89%EC%85%98%20%ED%95%84%ED%84%B0%EB%A7%81%3A%20URL%20%EC%BF%BC%EB%A6%AC%20%3E%20%EC%83%88%EB%A1%9C%EC%9A%B4%20API%20 +///recruiting : 모집중인 게시물을 조회하는 엔드포인트. +///my-info : 스터디 가입여부 조회 \ No newline at end of file diff --git a/src/main/java/mos/mosback/stRoom/controller/TodoController.java b/src/main/java/mos/mosback/stRoom/controller/TodoController.java new file mode 100644 index 0000000..3b85b26 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/controller/TodoController.java @@ -0,0 +1,211 @@ +package mos.mosback.stRoom.controller; +import lombok.RequiredArgsConstructor; +import mos.mosback.stRoom.domain.stRoom.StudyMemberTodoEntity; +import mos.mosback.stRoom.domain.stRoom.ToDoEntity; +import mos.mosback.stRoom.dto.*; +import mos.mosback.stRoom.service.ToDoService; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.*; +import java.time.LocalDate; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +@RequiredArgsConstructor +@RestController +public class TodoController { + + private final ToDoService toDoService; + + @PostMapping("todo/add/{roomId}") + public ResponseEntity> addTodo(@RequestBody stRoomToDoRequestDto requestDto, @PathVariable Long roomId) { + try{ + ToDoEntity todo = toDoService.addTodo(requestDto, roomId); + Map response = new HashMap<>(); + response.put("status:", HttpStatus.OK.value()); + response.put("success",true); + response.put("index",todo.getTodoId()); + response.put("message", "todo추가완료"); + return ResponseEntity.ok(response); + }catch(IllegalArgumentException e) { + Map response = new HashMap<>(); + response.put("status:", HttpStatus.INTERNAL_SERVER_ERROR.value()); + response.put("success",false); + response.put("message","서버내부오류"); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + + } + @PostMapping("/member/todo/add") + public ResponseEntity> addMemberTodo(@RequestBody StudyMemberToDoRequestDto requestDto) throws Exception { + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String currentEmail = authentication.getName(); // 현재 사용자의 이메일 + requestDto.setCurrentEmail(currentEmail); + StudyMemberTodoEntity todo = toDoService.addMemberTodo(requestDto); + Map response = new HashMap<>(); + response.put("status:", HttpStatus.OK.value()); + response.put("success", true); + response.put("index", todo.getIdx()); + response.put("message", "todo추가완료"); + return ResponseEntity.status(HttpStatus.OK).body(response); + } + + + @PutMapping("todo/{todoId}") + public ResponseEntity> updateTodo(@PathVariable Long todoId, @RequestBody stRoomToDoRequestDto requestDto) { + try { + ToDoEntity updatedToDo = toDoService.updateTodo(todoId, requestDto.getTodoContent(),requestDto.getStatus()); + Map response = new HashMap<>(); + if(updatedToDo != null) { + response.put("status:", HttpStatus.OK.value()); + response.put("success", true); + response.put("message", "Todo수정완료"); + return ResponseEntity.status(HttpStatus.OK).body(response); + }else { + response.put("status:", HttpStatus.NOT_FOUND.value()); + response.put("success", false); + response.put("message", "해당Todo없음"); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); + } + } catch (IllegalArgumentException e) { + Map response = new HashMap<>(); + response.put("status:", HttpStatus.INTERNAL_SERVER_ERROR.value()); + response.put("success",false); + response.put("message","서버내부오류"); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + + @PutMapping("/member/todo/{todoIdx}") + public ResponseEntity> updateMemberTodo(@PathVariable Long todoIdx, + @RequestBody StudyMemberToDoRequestDto requestDto) throws Exception { + // 현재 로그인한 사용자의 정보 가져오기 + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String currentEmail = authentication.getName(); // 현재 사용자의 이메일 + requestDto.setCurrentEmail(currentEmail); + try { + StudyMemberTodoEntity updatedToDo = toDoService.updateMemberTodo(todoIdx, requestDto.getTodoContent(), + requestDto.getStatus(), currentEmail); + Map response = new HashMap<>(); + if(updatedToDo != null) { + response.put("status:", HttpStatus.OK.value()); + response.put("success", true); + response.put("message", "Todo수정완료"); + return ResponseEntity.status(HttpStatus.OK).body(response); + }else { + response.put("status:", HttpStatus.NOT_FOUND.value()); + response.put("success", false); + response.put("message", "해당Todo없음"); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); + } + } catch (IllegalArgumentException e) { + Map response = new HashMap<>(); + response.put("status:", HttpStatus.INTERNAL_SERVER_ERROR.value()); + response.put("success",false); + response.put("message","서버내부오류"); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + + @DeleteMapping("todo/{todoId}") + public ResponseEntity> deleteTodo(@PathVariable Long todoId) { + try { + toDoService.deleteTodo(todoId); + Map response = new HashMap<>(); + response.put("status:", HttpStatus.OK.value()); + response.put("success", true); + response.put("message", "Todo삭제완료"); + return ResponseEntity.status(HttpStatus.OK).body(response); + } catch (IllegalArgumentException e) { + Map response = new HashMap<>(); + response.put("status:", HttpStatus.INTERNAL_SERVER_ERROR.value()); + response.put("success",false); + response.put("message","서버내부오류"); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + } + + @DeleteMapping("/member/todo/{todoIdx}") + public ResponseEntity> deleteMemberTodo(@PathVariable Long todoIdx) throws Exception{ + // 현재 로그인한 사용자의 정보 가져오기 + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String currentEmail = authentication.getName(); // 현재 사용자의 이메일 + try { + toDoService.deleteMemberTodo(todoIdx, currentEmail); + Map response = new HashMap<>(); + response.put("status:", HttpStatus.OK.value()); + response.put("success", true); + response.put("message", "Todo삭제완료"); + return ResponseEntity.status(HttpStatus.OK).body(response); + } catch (IllegalArgumentException e) { + Map response = new HashMap<>(); + response.put("status:", HttpStatus.INTERNAL_SERVER_ERROR.value()); + response.put("success",false); + response.put("message","서버내부오류"); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); + } + + } + + @GetMapping("todo/{roomId}") + public ResponseEntity> findStRoomTodoByRoomId(@PathVariable Long roomId) { + List todo = toDoService.findStRoomTodoByRoomId(roomId); + Map response = new HashMap<>(); + + if (!todo.isEmpty()) { + response.put("status", HttpStatus.OK.value()); + response.put("success", true); + response.put("todo", todo); + return ResponseEntity.status(HttpStatus.OK).body(response); + } else { + response.put("status", HttpStatus.NOT_FOUND.value()); + response.put("success", false); + response.put("message", "Todo를 찾을 수 없음"); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); + } + } + + @GetMapping("/member/room/{roomId}") + public ResponseEntity getMemberRoomInfo(@PathVariable Long roomId) throws Exception { + // 현재 로그인한 사용자의 정보 가져오기 + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String currentEmail = authentication.getName(); // 현재 사용자의 이메일 + StudyMemberRoomInfoResponseDto todo = toDoService.getMemberRoomInfo(roomId, currentEmail); + return new ResponseEntity<>(todo, HttpStatus.OK); + } + + + + @GetMapping("/todoRank/{roomId}") + public ResponseEntity> getMemberTodoRank(@PathVariable Long roomId) throws Exception { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String currentEmail = authentication.getName(); // 현재 사용자의 이메일 + List todoList = toDoService.getMemberTodoProgress(roomId, currentEmail); + MemberTodoProgressResponseDto todoProgress = toDoService.getProgressInfo(roomId,currentEmail); + Map response = new HashMap<>(); + response.put("data", todoProgress); + response.put("TodoRank", todoList); + return ResponseEntity.status(HttpStatus.OK).body(response); + + + } + @GetMapping("/myTodo/{roomId}/{date}") + public ResponseEntity> getTodoByDateAndMemberIdAndRoomId + (@PathVariable Long roomId, @PathVariable @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date) throws Exception { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String email = authentication.getName(); // 현재 사용자의 이메일 + List todoList = toDoService.getTodoByDateAndMemberIdAndRoomId(date, roomId,email); + + Map response = new HashMap<>(); + response.put("todoList", todoList); + + return ResponseEntity.ok(response); + } +} + diff --git a/src/main/java/mos/mosback/stRoom/domain/stRoom/BaseTimeEntity.java b/src/main/java/mos/mosback/stRoom/domain/stRoom/BaseTimeEntity.java new file mode 100644 index 0000000..69db1d5 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/domain/stRoom/BaseTimeEntity.java @@ -0,0 +1,19 @@ +package mos.mosback.stRoom.domain.stRoom; +import lombok.Getter; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.EntityListeners; +import javax.persistence.MappedSuperclass; +import java.time.LocalDateTime; + +@Getter +@MappedSuperclass //JPA Entity 클래스들이 BaseTimeEntity를 상속할 경우 필드들도 칼럼으로 인식하도록 한다 +@EntityListeners(AuditingEntityListener.class) //BaseTimeEntity 클래스에 Auditing 기능을 포함한다 +public abstract class BaseTimeEntity { + + @CreatedDate //Entity가 생성되어 저장될때 시간이 자동저장 + private LocalDateTime createdDate; + +} +//모든 Entity의 상위 클래스가 되어 엔티티들의 생성시간을 자동으로 관리하는 클래스 \ No newline at end of file diff --git a/src/main/java/mos/mosback/stRoom/domain/stRoom/MemberStatus.java b/src/main/java/mos/mosback/stRoom/domain/stRoom/MemberStatus.java new file mode 100644 index 0000000..be50f57 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/domain/stRoom/MemberStatus.java @@ -0,0 +1,8 @@ +package mos.mosback.stRoom.domain.stRoom; + +public enum MemberStatus { + Leader, + Member, + Waiting, + Rejected + } diff --git a/src/main/java/mos/mosback/stRoom/domain/stRoom/MemberTodoRankProjection.java b/src/main/java/mos/mosback/stRoom/domain/stRoom/MemberTodoRankProjection.java new file mode 100644 index 0000000..c9f445d --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/domain/stRoom/MemberTodoRankProjection.java @@ -0,0 +1,8 @@ +package mos.mosback.stRoom.domain.stRoom; + +public interface MemberTodoRankProjection { + Long getMemberId(); + double getProgress(); + String getNickname(); + +} diff --git a/src/main/java/mos/mosback/stRoom/domain/stRoom/StRoomEntity.java b/src/main/java/mos/mosback/stRoom/domain/stRoom/StRoomEntity.java new file mode 100644 index 0000000..2642be5 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/domain/stRoom/StRoomEntity.java @@ -0,0 +1,106 @@ +package mos.mosback.stRoom.domain.stRoom; +import com.fasterxml.jackson.annotation.JsonManagedReference; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import mos.mosback.login.domain.user.User; + +import javax.persistence.*; +import java.time.LocalDate; +import java.util.*; + +@Getter // 롬복 어노테이션 +@Setter +@NoArgsConstructor // 롬복 어노테이션 (필수는 아님 그냥 코드 단순화용) +@Entity //JPA 어노테이션 (주요어노테이션) : 테이블과 링크될 클래스임을 나타냄 +public class StRoomEntity extends BaseTimeEntity { + //strooms 클래스 - > 실제 DB테이블과 매칭될 클래스 ( Entity 클래스 ) + @Id //해당 테이블 PK 필드 나타냄 + @GeneratedValue(strategy = GenerationType.IDENTITY) //PK 생성규칙 나타냄 스프링 2.0은 + //GenerationType.IDENTITY 옵션을 추가해야만 auto_increment가 된다. + private Long roomId; + //웬만하면 Entity의 Pk는 long타입의 auto_increment 쓰는게 좋음 + @Column(length = 500, nullable = false) + private String title; + + @Column(nullable = false) + private String goal; //스터디 목표 + @Column(nullable = false) + private String rules; //스터디 규칙 + @Column(nullable = false) + private String quest; //생성 시 질문 + @Column(nullable = false) + private String category; // 스터디 카테고리 + private String intro; //스터디 소개 + private int memberNum; //현재 멤버수 + private int maxMember; //모집 멤버수 + @Column(nullable = false) + private String mod; //스터디 분위기 + private int click;// 클릭횟수 (인기순 조회) + private boolean onOff; //진행방식 (온오프) + private String location; + private int online; //온라인 + @Column(nullable = false) + private LocalDate startDate; //스터디 시작 날짜 + @Column(nullable = false) + private LocalDate endDate; //스터디 끝나는 날짜 + private boolean recruiting; //모집여부 + private LocalDate deadline; // 모집 마감날짜 + + @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + private List studyDayEntities = new ArrayList<>(); + + @OneToMany + private List members = new ArrayList<>(); + + @ManyToOne + @JoinColumn(name = "created_by_user_email") + private User createdByUser; + + + @Builder + public StRoomEntity(String title, String goal, String rules, String quest, + String category, String intro, int memberNum, int maxMember + ,String mod, boolean onOff, String location,int online, + LocalDate startDate, LocalDate endDate, List studyDayEntities + ) { + this.title = title; + this.goal = goal; + this.rules = rules; + this.quest = quest; + this.category = category; + this.intro = intro; + this.memberNum = memberNum; + this.maxMember = maxMember; + this.mod = mod; + this.onOff = onOff; + this.location = location; + this.online = online; + this.startDate = startDate; + this.endDate = endDate; + this.studyDayEntities = studyDayEntities; + } + + + public void update(String title, String goal, String rules, String quest, + String category, String intro, int maxMember, + String mod, boolean onOff,String location,int online,LocalDate startDate, + LocalDate endDate,List studyDayEntities) { + this.title = title; + this.goal = goal; + this.rules = rules; + this.quest = quest; + this.category = category; + this.intro = intro; + this.maxMember = maxMember; + this.mod = mod; + this.onOff = onOff; + this.location = location; + this.online = online; + this.startDate = startDate; + this.endDate = endDate; + this.studyDayEntities = studyDayEntities; + } + +} \ No newline at end of file diff --git a/src/main/java/mos/mosback/stRoom/domain/stRoom/StRoomTodoFindProjection.java b/src/main/java/mos/mosback/stRoom/domain/stRoom/StRoomTodoFindProjection.java new file mode 100644 index 0000000..4996004 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/domain/stRoom/StRoomTodoFindProjection.java @@ -0,0 +1,12 @@ +package mos.mosback.stRoom.domain.stRoom; + +import java.time.LocalDate; + +public interface StRoomTodoFindProjection { + Long getRoomId(); + LocalDate getDate(); + String getTodoContent(); + + Long getIdx(); + TodoStatus getStatus(); +} diff --git a/src/main/java/mos/mosback/stRoom/domain/stRoom/StRoomTodoInfoProjection.java b/src/main/java/mos/mosback/stRoom/domain/stRoom/StRoomTodoInfoProjection.java new file mode 100644 index 0000000..b26c830 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/domain/stRoom/StRoomTodoInfoProjection.java @@ -0,0 +1,7 @@ +package mos.mosback.stRoom.domain.stRoom; + +public interface StRoomTodoInfoProjection { + + int getCompletedCount(); + int getTotalCount(); +} diff --git a/src/main/java/mos/mosback/stRoom/domain/stRoom/StudyDaysEntity.java b/src/main/java/mos/mosback/stRoom/domain/stRoom/StudyDaysEntity.java new file mode 100644 index 0000000..e4be36d --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/domain/stRoom/StudyDaysEntity.java @@ -0,0 +1,19 @@ +package mos.mosback.stRoom.domain.stRoom; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.io.Serializable; + +@Getter // 롬복 어노테이션 +@NoArgsConstructor // 롬복 어노테이션 (필수는 아님 그냥 코드 단순화용) +@Entity //JPA 어노테이션 (주요어노테이션) : 테이블과 링크될 클래스임을 나타냄 +public class StudyDaysEntity implements Serializable { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long dayIdx; + @Column(name = "STUDY_DAYS") + private String studyDays; + +} \ No newline at end of file diff --git a/src/main/java/mos/mosback/stRoom/domain/stRoom/StudyMemberEntity.java b/src/main/java/mos/mosback/stRoom/domain/stRoom/StudyMemberEntity.java new file mode 100644 index 0000000..3f5722b --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/domain/stRoom/StudyMemberEntity.java @@ -0,0 +1,45 @@ +package mos.mosback.stRoom.domain.stRoom; +import com.fasterxml.jackson.annotation.JsonBackReference; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import mos.mosback.login.domain.user.User; + +import javax.persistence.*; +import java.io.Serializable; +import java.time.LocalDateTime; + +@Getter // 롬복 어노테이션 +@Setter +@NoArgsConstructor +@Entity +public class StudyMemberEntity implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long memberId; + + @ManyToOne + @JoinColumn(name = "user_id") + private User user; + public Long getUserId() { + return this.user.getId(); + } + + + @JsonBackReference + @ManyToOne + @JoinColumn(name = "roomId") + private StRoomEntity stRoom; + + // 다른 필드와 매핑 + + @Enumerated(EnumType.STRING) + private MemberStatus status; + + @Column + private String answer; // 스터디 답변 + + @Column + private LocalDateTime joinedAt; +} \ No newline at end of file diff --git a/src/main/java/mos/mosback/stRoom/domain/stRoom/StudyMemberTodoEntity.java b/src/main/java/mos/mosback/stRoom/domain/stRoom/StudyMemberTodoEntity.java new file mode 100644 index 0000000..f38ebdb --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/domain/stRoom/StudyMemberTodoEntity.java @@ -0,0 +1,43 @@ +package mos.mosback.stRoom.domain.stRoom; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.*; +import java.io.Serializable; +import java.time.LocalDate; + +@Getter // 롬복 어노테이션 +@Setter +@NoArgsConstructor +@Entity +public class StudyMemberTodoEntity implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long idx; + + @Column(name = "memberID") + private Long memberId; + + @Column(name = "todo_content") + private String todoContent; + + @ManyToOne + @JoinColumn(name = "roomId") + private StRoomEntity stRoom; + + // 다른 필드와 매핑 + @Enumerated(EnumType.STRING) + private TodoStatus status; + + private LocalDate date; + + + public void update(String todoContent, TodoStatus status) { + this.todoContent = todoContent; + this.status = status; + } + +} \ No newline at end of file diff --git a/src/main/java/mos/mosback/stRoom/domain/stRoom/StudyMemberTodoKey.java b/src/main/java/mos/mosback/stRoom/domain/stRoom/StudyMemberTodoKey.java new file mode 100644 index 0000000..be28fef --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/domain/stRoom/StudyMemberTodoKey.java @@ -0,0 +1,17 @@ +package mos.mosback.stRoom.domain.stRoom; + +import lombok.Data; + +import javax.persistence.*; +import java.io.Serializable; + +@Data +@Embeddable +public class StudyMemberTodoKey implements Serializable { + + @Column(name = "memberID") + private Long memberId; + + @Column(name = "todoContent") + private String todoContent; +} \ No newline at end of file diff --git a/src/main/java/mos/mosback/stRoom/domain/stRoom/ToDoEntity.java b/src/main/java/mos/mosback/stRoom/domain/stRoom/ToDoEntity.java new file mode 100644 index 0000000..9b89e43 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/domain/stRoom/ToDoEntity.java @@ -0,0 +1,38 @@ +package mos.mosback.stRoom.domain.stRoom; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.*; + +@Setter +@Getter // 롬복 어노테이션 +@NoArgsConstructor +@Entity //JPA 어노테이션 (주요어노테이션) : 테이블과 링크될 클래스 +public class ToDoEntity extends BaseTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long todoId; + + @ManyToOne + @JoinColumn(name = "roomId") + private StRoomEntity stRoom; + + @Column + private String todoContent; + + @Enumerated(EnumType.STRING) + private TodoStatus status; + + public void update(String todoContent, TodoStatus status) + { + + this.todoContent = todoContent; + this.status = status; + } + + + +// 스터디 방에 대한 투두 정보를 담는 엔티티 + +} \ No newline at end of file diff --git a/src/main/java/mos/mosback/stRoom/domain/stRoom/TodoStatus.java b/src/main/java/mos/mosback/stRoom/domain/stRoom/TodoStatus.java new file mode 100644 index 0000000..0e49f2e --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/domain/stRoom/TodoStatus.java @@ -0,0 +1,6 @@ +package mos.mosback.stRoom.domain.stRoom; + +public enum TodoStatus { + Waiting, + Completed +} \ No newline at end of file diff --git a/src/main/java/mos/mosback/stRoom/dto/AcceptMemberRequestDto.java b/src/main/java/mos/mosback/stRoom/dto/AcceptMemberRequestDto.java new file mode 100644 index 0000000..5dee6fe --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/dto/AcceptMemberRequestDto.java @@ -0,0 +1,12 @@ +package mos.mosback.stRoom.dto; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Setter +@Getter +@NoArgsConstructor +public class AcceptMemberRequestDto { + private Long roomId; + private Long memberId; +} diff --git a/src/main/java/mos/mosback/stRoom/dto/Home_RoomResponseDto.java b/src/main/java/mos/mosback/stRoom/dto/Home_RoomResponseDto.java new file mode 100644 index 0000000..c6c113b --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/dto/Home_RoomResponseDto.java @@ -0,0 +1,34 @@ +package mos.mosback.stRoom.dto; +import lombok.Getter; +import lombok.Setter; +import mos.mosback.stRoom.domain.stRoom.StRoomEntity; +import mos.mosback.stRoom.service.ToDoService; + +import java.time.LocalDate; +import java.util.List; + +@Setter +@Getter +public class Home_RoomResponseDto { + private String title; + private String category; // 스터디 카테고리 + private int memberNum; //현재 멤버수 + private int maxMember; //모집 멤버수 + private String location; //스터디장소 + private int online; //온라인 + private LocalDate startDate; //스터디 시작날짜 + private LocalDate endDate; + //+ 유저프로필사진 + + public Home_RoomResponseDto(StRoomEntity entity) { + this.title = entity.getTitle(); + this.startDate = entity.getStartDate(); + this.endDate = entity.getEndDate(); + this.location = entity.getLocation(); + this.online = entity.getOnline(); + this.category = entity.getCategory(); + this.memberNum = entity.getMemberNum(); + this.maxMember = entity.getMaxMember(); + } + +} diff --git a/src/main/java/mos/mosback/stRoom/dto/MemberTodoProgressResponseDto.java b/src/main/java/mos/mosback/stRoom/dto/MemberTodoProgressResponseDto.java new file mode 100644 index 0000000..878fe4e --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/dto/MemberTodoProgressResponseDto.java @@ -0,0 +1,11 @@ +package mos.mosback.stRoom.dto; + +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +public class MemberTodoProgressResponseDto { + private double avg; + private String userNick; +} diff --git a/src/main/java/mos/mosback/stRoom/dto/MemberTodoRankResponseDto.java b/src/main/java/mos/mosback/stRoom/dto/MemberTodoRankResponseDto.java new file mode 100644 index 0000000..eb1c0f4 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/dto/MemberTodoRankResponseDto.java @@ -0,0 +1,12 @@ +package mos.mosback.stRoom.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class MemberTodoRankResponseDto { + private String nickname; + private double progress; + +} diff --git a/src/main/java/mos/mosback/stRoom/dto/MemberTodoResponseDto.java b/src/main/java/mos/mosback/stRoom/dto/MemberTodoResponseDto.java new file mode 100644 index 0000000..00e5e45 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/dto/MemberTodoResponseDto.java @@ -0,0 +1,13 @@ +package mos.mosback.stRoom.dto; +import lombok.Getter; +import lombok.Setter; +import mos.mosback.stRoom.domain.stRoom.TodoStatus; + +@Getter +@Setter +public class MemberTodoResponseDto { + private String todoContent; + private Long idx; + private TodoStatus status; + +} diff --git a/src/main/java/mos/mosback/stRoom/dto/QuestionAndAnswerResponseDto.java b/src/main/java/mos/mosback/stRoom/dto/QuestionAndAnswerResponseDto.java new file mode 100644 index 0000000..ffcc9f3 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/dto/QuestionAndAnswerResponseDto.java @@ -0,0 +1,14 @@ +package mos.mosback.stRoom.dto; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.Getter; + +@Setter +@Getter +@NoArgsConstructor +public class QuestionAndAnswerResponseDto { + private Long memberId; + private String question; + private String answer; + +} diff --git a/src/main/java/mos/mosback/stRoom/dto/QuestionDto.java b/src/main/java/mos/mosback/stRoom/dto/QuestionDto.java new file mode 100644 index 0000000..70a5928 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/dto/QuestionDto.java @@ -0,0 +1,17 @@ +package mos.mosback.stRoom.dto; +import lombok.NoArgsConstructor; +import lombok.Setter; +import mos.mosback.stRoom.domain.stRoom.StRoomEntity; +import lombok.Getter; + +@Setter +@Getter +@NoArgsConstructor +public class QuestionDto { + private String question; + + public QuestionDto(StRoomEntity entity) { + + this.question = entity.getQuest(); + } +} diff --git a/src/main/java/mos/mosback/stRoom/dto/StRoomDetailResponseDto.java b/src/main/java/mos/mosback/stRoom/dto/StRoomDetailResponseDto.java new file mode 100644 index 0000000..756878b --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/dto/StRoomDetailResponseDto.java @@ -0,0 +1,53 @@ +package mos.mosback.stRoom.dto; +import lombok.Getter; +import lombok.Setter; +import mos.mosback.stRoom.domain.stRoom.StRoomEntity; +import mos.mosback.stRoom.domain.stRoom.StudyDaysEntity; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +@Getter +@Setter +public class StRoomDetailResponseDto { + private Long roomId; + private String title; + private String category; + private String location; + private LocalDate startDate; + private LocalDate endDate; + private boolean onOff; //진행방식 (온오프) + private int memberNum; //현재 멤버수 + private int maxMember; //모집 멤버수 + private String mod; //스터디 분위기 + private List studyDayEntities; + private String goal; + private String rules; + private String intro; + private LocalDateTime createdDate; + private LocalDate deadline; + private String nickname; + + public StRoomDetailResponseDto(StRoomEntity entity) { + + this.roomId = entity.getRoomId(); + this.createdDate = entity.getCreatedDate(); + this.title = entity.getTitle(); + this.location = entity.getLocation(); + this.intro = entity.getIntro(); + this.goal = entity.getGoal(); + this.rules = entity.getRules(); + this.category = entity.getCategory(); + this.memberNum = entity.getMemberNum(); + this.maxMember = entity.getMaxMember(); + this.mod = entity.getMod(); + this.onOff = entity.isOnOff(); + this.startDate = entity.getStartDate(); + this.endDate = entity.getEndDate(); + this.deadline = entity.getStartDate().minusDays(1); + this.studyDayEntities = entity.getStudyDayEntities(); + + + + } //스터디 + 투두 리스트의 스터디룸 상세화면 +} + diff --git a/src/main/java/mos/mosback/stRoom/dto/StRoomListResponseDto.java b/src/main/java/mos/mosback/stRoom/dto/StRoomListResponseDto.java new file mode 100644 index 0000000..7138047 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/dto/StRoomListResponseDto.java @@ -0,0 +1,55 @@ +package mos.mosback.stRoom.dto; +import lombok.Getter; +import mos.mosback.stRoom.domain.stRoom.StRoomEntity; +import mos.mosback.stRoom.domain.stRoom.StudyDaysEntity; + +import java.time.LocalDate; +import java.util.List; + + +@Getter +public class StRoomListResponseDto { + + private String title; + private String goal; //스터디 목표 + private String rules; //스터디 규칙 + private String quest; + private String category; // 스터디 카테고리 + private String intro; //스터디 소개 + private int memberNum; //멤버수 + private int maxMember; + private String mod; //스터디 분위기 + private int click;// 클릭횟수 (인기순 조회) + private boolean onOff; //진행방식 (온오프) + private LocalDate startDate; //스터디 시작 날짜 + private LocalDate endDate; //스터디 끝나는 날짜 + private String location; + private int online; //온라인 + + private List studyDayEntities; + + public StRoomListResponseDto(StRoomEntity entity) { + + this.title = entity.getTitle(); + this.goal = entity.getGoal(); + this.rules = entity.getRules(); + this.quest =entity.getQuest(); + this.category =entity.getCategory(); + this.intro =entity.getIntro(); + this.memberNum = entity.getMemberNum(); + this.maxMember = entity.getMaxMember(); + this.mod = entity.getMod(); + this.onOff = entity.isOnOff(); + this.startDate = entity.getStartDate(); + this.endDate = entity.getEndDate(); + this.studyDayEntities = entity.getStudyDayEntities(); + } + public StRoomListResponseDto(Home_RoomResponseDto homeRoomResponseDto) { + this.title = homeRoomResponseDto.getTitle(); + this.location = homeRoomResponseDto.getLocation(); + this.online = homeRoomResponseDto.getOnline(); + this.category = homeRoomResponseDto.getCategory(); + this.memberNum = homeRoomResponseDto.getMemberNum(); + this.maxMember = homeRoomResponseDto.getMaxMember(); + } +} \ No newline at end of file diff --git a/src/main/java/mos/mosback/stRoom/dto/StRoomMemberJoinRequestDto.java b/src/main/java/mos/mosback/stRoom/dto/StRoomMemberJoinRequestDto.java new file mode 100644 index 0000000..7f53f6f --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/dto/StRoomMemberJoinRequestDto.java @@ -0,0 +1,13 @@ +package mos.mosback.stRoom.dto; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Setter +@Getter +@NoArgsConstructor +public class StRoomMemberJoinRequestDto { + private Long roomId; + private String answer; + private String email; +} diff --git a/src/main/java/mos/mosback/stRoom/dto/StRoomMemberResponseDto.java b/src/main/java/mos/mosback/stRoom/dto/StRoomMemberResponseDto.java new file mode 100644 index 0000000..f3495ea --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/dto/StRoomMemberResponseDto.java @@ -0,0 +1,12 @@ +package mos.mosback.stRoom.dto; + +import lombok.Data; +import mos.mosback.stRoom.domain.stRoom.MemberStatus; + +@Data +public class StRoomMemberResponseDto { + private Long memberId; + private MemberStatus status; + + +} diff --git a/src/main/java/mos/mosback/stRoom/dto/StRoomMemberToDoResponseDto.java b/src/main/java/mos/mosback/stRoom/dto/StRoomMemberToDoResponseDto.java new file mode 100644 index 0000000..64f448d --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/dto/StRoomMemberToDoResponseDto.java @@ -0,0 +1,19 @@ +package mos.mosback.stRoom.dto; + +import lombok.Data; +import mos.mosback.stRoom.domain.stRoom.StudyMemberTodoEntity; +import mos.mosback.stRoom.domain.stRoom.TodoStatus; + +@Data +public class StRoomMemberToDoResponseDto { + private Long idx; + private String todoContent; + private TodoStatus status; + + + public StRoomMemberToDoResponseDto (StudyMemberTodoEntity entity) { + this.idx = entity.getIdx(); + this.status = entity.getStatus(); + this.todoContent = entity.getTodoContent(); + } +} diff --git a/src/main/java/mos/mosback/stRoom/dto/StRoomResponseDto.java b/src/main/java/mos/mosback/stRoom/dto/StRoomResponseDto.java new file mode 100644 index 0000000..1b47c42 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/dto/StRoomResponseDto.java @@ -0,0 +1,49 @@ +package mos.mosback.stRoom.dto; +import lombok.Getter; +import lombok.Setter; +import mos.mosback.stRoom.domain.stRoom.StRoomEntity; +import mos.mosback.stRoom.domain.stRoom.StudyDaysEntity; + +import java.time.LocalDate; +import java.util.List; + + +@Setter +@Getter +public class StRoomResponseDto { + Long roomId; + private String title; + private String goal; //스터디 목표 + private String rules; //스터디 규칙 + private String category; // 스터디 카테고리 + private int memberNum; //현재 멤버수 + private String mod; //스터디 분위기 + private boolean onOff; //진행방식 (온오프) + private LocalDate startDate; //스터디 시작 날짜 + private LocalDate endDate; //스터디 끝나는 날짜 + private List studyDayEntities; + private String nickname; + /*유저프로필 + 사진*/ + + + public StRoomResponseDto(StRoomEntity entity) { + + this.roomId = entity.getRoomId(); + this.title = entity.getTitle(); + this.goal = entity.getGoal(); + this.rules = entity.getRules(); + this.category =entity.getCategory(); + this.memberNum = entity.getMemberNum(); + this.mod = entity.getMod(); + this.onOff = entity.isOnOff(); + this.startDate = entity.getStartDate(); + this.endDate = entity.getEndDate(); + this.studyDayEntities =entity.getStudyDayEntities(); + + + } //스터디 + 투두 리스트의 스터디룸 상세화면 + + +} +//Entity의 필드 이루만 사용하므로 생성자로 Entity를 받아 필드에 값을 넣어줌 +//상세정보에 노출 될 필드 \ No newline at end of file diff --git a/src/main/java/mos/mosback/stRoom/dto/StRoomSaveRequestDto.java b/src/main/java/mos/mosback/stRoom/dto/StRoomSaveRequestDto.java new file mode 100644 index 0000000..f84e7bb --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/dto/StRoomSaveRequestDto.java @@ -0,0 +1,73 @@ +package mos.mosback.stRoom.dto; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import mos.mosback.stRoom.domain.stRoom.StRoomEntity; +import mos.mosback.stRoom.domain.stRoom.StudyDaysEntity; + +import java.time.LocalDate; +import java.util.List; + +@Getter +@Setter +@NoArgsConstructor +public class StRoomSaveRequestDto { + Long roomId; + private String title; + private String goal; //스터디 목표 + private String rules; //스터디 규칙 + private String quest; //생성 시 질문 + private String category; // 스터디 카테고리 + private String intro; //스터디 소개 + private int maxMember; + private String mod; //스터디 분위기 + private boolean onOff; //진행방식 (온오프) + private String location; //스터디 장소 + private int online; // 온라인일 경우 1 : 줌 2 : 디코 3: 구글미트 4: 기타 + private LocalDate startDate; //스터디 시작 날짜 + private LocalDate endDate; //스터디 끝나는 날짜 + private String email; // 사용자 이메일 + private List studyDayEntities; + + + @Builder + public StRoomSaveRequestDto(String title, String goal, String rules, String quest, String category, + String intro, int maxMember, String mod, boolean onOff,String location,int online, + LocalDate startDate, LocalDate endDate,List studyDayEntities) { + this.title = title; + this.goal = goal; + this.rules = rules; + this.quest = quest; + this.category = category; + this.intro = intro; + this.mod = mod; + this.maxMember = maxMember; + this.onOff = onOff; + this.location = location; + this.online = online; + this.startDate = startDate; + this.endDate = endDate; + this.studyDayEntities = studyDayEntities; + + } + + public StRoomEntity toEntity() { + return StRoomEntity.builder() + .title(title) + .goal(goal) + .rules(rules) + .quest(quest) + .category(category) + .intro(intro) + .mod(mod) + .maxMember(maxMember) + .onOff(onOff) + .location(location) + .online(online) + .startDate(startDate) + .endDate(endDate) + .studyDayEntities(studyDayEntities) + .build(); + } +} //스터디 수정 시 생성 필드에 들어가는 내용수정 diff --git a/src/main/java/mos/mosback/stRoom/dto/StRoomToDoResponseDto.java b/src/main/java/mos/mosback/stRoom/dto/StRoomToDoResponseDto.java new file mode 100644 index 0000000..8cd2b17 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/dto/StRoomToDoResponseDto.java @@ -0,0 +1,19 @@ +package mos.mosback.stRoom.dto; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import mos.mosback.stRoom.domain.stRoom.ToDoEntity; +import mos.mosback.stRoom.domain.stRoom.TodoStatus; + +@Setter +@Getter +@NoArgsConstructor +public class StRoomToDoResponseDto { + private String todoContent; + private TodoStatus status; + + public StRoomToDoResponseDto(ToDoEntity entity) { + this.todoContent = entity.getTodoContent(); + this.status=entity.getStatus(); + } +} \ No newline at end of file diff --git a/src/main/java/mos/mosback/stRoom/dto/StRoomUpdateRequestDto.java b/src/main/java/mos/mosback/stRoom/dto/StRoomUpdateRequestDto.java new file mode 100644 index 0000000..6e78b10 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/dto/StRoomUpdateRequestDto.java @@ -0,0 +1,55 @@ +package mos.mosback.stRoom.dto; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import mos.mosback.stRoom.domain.stRoom.StudyDaysEntity; + +import java.time.LocalDate; +import java.util.List; + + +@Getter +@NoArgsConstructor +public class StRoomUpdateRequestDto { + + Long roomId; + private String title; + private String goal; //스터디 목표 + private String rules; //스터디 규칙 + private String quest; //생성 시 질문 + private String category; // 스터디 카테고리 + private String intro; //스터디 소개 + private int maxMember; // 모집 멤버수 + private String mod; //스터디 분위기 + private boolean onOff; //진행방식 (온오프) + private String location; //스터디 장소 + private int online; // 온라인일 경우 + // 1 : 줌 2 : 디코 3: 구글미트 4: 기타 + private LocalDate startDate; //스터디 시작 날짜 + private LocalDate endDate; //스터디 끝나는 날짜 + private List studyDayEntities; + + + @Builder + public StRoomUpdateRequestDto(String title, String goal, String rules, String quest, String category, + String intro, int maxMember ,String mod, boolean onOff,String location,int online, + LocalDate startDate, LocalDate endDate, List studyDayEntities) { + this.title = title; + this.goal = goal; + this.rules = rules; + this.quest = quest; + this.category = category; + this.intro = intro; + this.mod = mod; + this.maxMember = maxMember; + this.onOff = onOff; + this.location = location; + this.online = online; + this.startDate = startDate; + this.endDate = endDate; + this.studyDayEntities = studyDayEntities; + + } + + +} \ No newline at end of file diff --git a/src/main/java/mos/mosback/stRoom/dto/StudyMemberHistoryDto.java b/src/main/java/mos/mosback/stRoom/dto/StudyMemberHistoryDto.java new file mode 100644 index 0000000..34a6419 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/dto/StudyMemberHistoryDto.java @@ -0,0 +1,10 @@ +package mos.mosback.stRoom.dto; + +import lombok.Data; +import mos.mosback.stRoom.domain.stRoom.MemberStatus; + +@Data +public class StudyMemberHistoryDto { + private MemberStatus status; // 스터디 상태 + private String title; // 스터디 제목 +} diff --git a/src/main/java/mos/mosback/stRoom/dto/StudyMemberRoomInfoResponseDto.java b/src/main/java/mos/mosback/stRoom/dto/StudyMemberRoomInfoResponseDto.java new file mode 100644 index 0000000..015cbf3 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/dto/StudyMemberRoomInfoResponseDto.java @@ -0,0 +1,16 @@ +package mos.mosback.stRoom.dto; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import java.util.List; + +@Setter +@Getter +@NoArgsConstructor +public class StudyMemberRoomInfoResponseDto { + private String category; + private double avg; // 평균값 +/* private List todoList; // 사용자 투두정보*/ + private List roomDayList; + +} \ No newline at end of file diff --git a/src/main/java/mos/mosback/stRoom/dto/StudyMemberToDoRequestDto.java b/src/main/java/mos/mosback/stRoom/dto/StudyMemberToDoRequestDto.java new file mode 100644 index 0000000..a0290d7 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/dto/StudyMemberToDoRequestDto.java @@ -0,0 +1,22 @@ +package mos.mosback.stRoom.dto; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import mos.mosback.stRoom.domain.stRoom.TodoStatus; + +import java.time.LocalDate; + + +@Setter +@Getter +@NoArgsConstructor +public class StudyMemberToDoRequestDto { + private String todoContent; + private TodoStatus status; + private Long roomId; + private Long todoId; + private String currentEmail; + private LocalDate date; + +} \ No newline at end of file diff --git a/src/main/java/mos/mosback/stRoom/dto/StudyMembershipStatusResponseDto.java b/src/main/java/mos/mosback/stRoom/dto/StudyMembershipStatusResponseDto.java new file mode 100644 index 0000000..c62233f --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/dto/StudyMembershipStatusResponseDto.java @@ -0,0 +1,25 @@ +package mos.mosback.stRoom.dto; + +public class StudyMembershipStatusResponseDto { + + + private String title; + private String status; + + public StudyMembershipStatusResponseDto(String title, String status) { + this.title = title; + this.status = status; + } + + public String getTitle() { + return title; + } + + public String getStatus() { + return status; + } + + + + +} \ No newline at end of file diff --git a/src/main/java/mos/mosback/stRoom/dto/StudyRoomDayDto.java b/src/main/java/mos/mosback/stRoom/dto/StudyRoomDayDto.java new file mode 100644 index 0000000..7d6d92e --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/dto/StudyRoomDayDto.java @@ -0,0 +1,14 @@ +package mos.mosback.stRoom.dto; +import lombok.Data; + +import java.time.LocalDate; +import java.util.Date; +; + +@Data +public class StudyRoomDayDto { + private int year; + private int month; + private String dayOfWeek; // "월" ~ "일" 값 + private int dayVal; // 날짜 값 +} diff --git a/src/main/java/mos/mosback/stRoom/dto/StudyRoomTodoInfoDto.java b/src/main/java/mos/mosback/stRoom/dto/StudyRoomTodoInfoDto.java new file mode 100644 index 0000000..08490a2 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/dto/StudyRoomTodoInfoDto.java @@ -0,0 +1,9 @@ +package mos.mosback.stRoom.dto; + +import lombok.Data; + +@Data +public class StudyRoomTodoInfoDto { + private int totalCount; + private int completedCount; +} diff --git a/src/main/java/mos/mosback/stRoom/dto/stRoomToDoRequestDto.java b/src/main/java/mos/mosback/stRoom/dto/stRoomToDoRequestDto.java new file mode 100644 index 0000000..68e92ce --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/dto/stRoomToDoRequestDto.java @@ -0,0 +1,19 @@ +package mos.mosback.stRoom.dto; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import mos.mosback.stRoom.domain.stRoom.TodoStatus; + +import java.time.LocalDate; + + +@Setter +@Getter +@NoArgsConstructor +public class stRoomToDoRequestDto { + + private String todoContent; + private TodoStatus status; + private Long roomId; + +} \ No newline at end of file diff --git a/src/main/java/mos/mosback/stRoom/repository/MemberTodoRepository.java b/src/main/java/mos/mosback/stRoom/repository/MemberTodoRepository.java new file mode 100644 index 0000000..e2035b3 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/repository/MemberTodoRepository.java @@ -0,0 +1,45 @@ +package mos.mosback.stRoom.repository; +import mos.mosback.stRoom.domain.stRoom.MemberTodoRankProjection; +import mos.mosback.stRoom.domain.stRoom.StRoomTodoFindProjection; +import mos.mosback.stRoom.domain.stRoom.StRoomTodoInfoProjection; +import mos.mosback.stRoom.domain.stRoom.StudyMemberTodoEntity; +import mos.mosback.stRoom.dto.StudyRoomTodoInfoDto; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; + + +public interface MemberTodoRepository extends JpaRepository{ + + @Query(value = "SELECT * FROM STUDY_MEMBER_TODO_ENTITY WHERE room_id = :roomId AND memberID = :memberID", nativeQuery = true) + List findAllByStRoom(@Param("roomId") Long roomId, @Param("memberID") Long memberID); + @Query(value = "SELECT * FROM (\n" + + "SELECT COUNT(*) AS totalCount FROM STUDY_MEMBER_TODO_ENTITY where room_id = :roomId \n" + + ") A, (\n" + + "SELECT COUNT(*) AS completedCount FROM STUDY_MEMBER_TODO_ENTITY where room_id = :roomId AND status = 'Completed'\n" + + ") B", nativeQuery = true) + List getStudyRoomTodoAverage(@Param("roomId") Long roomId); + + @Query(value = "SELECT MEMBERID AS memberId, ROUND((SUM(CASE WHEN STATUS = 'Completed' THEN 1.0 ELSE 0.0 END) / COUNT(*)) * 100) AS progress " + + "FROM STUDY_MEMBER_TODO_ENTITY " + + "WHERE STATUS IN ('Completed', 'Waiting') AND ROOM_ID = :roomId " + + "GROUP BY MEMBERID ORDER BY progress DESC", nativeQuery = true) + + List getRankByStRoom(@Param("roomId") Long roomId); + + @Query(value = "SELECT IDX, STATUS, TODO_CONTENT AS todoContent FROM STUDY_MEMBER_TODO_ENTITY " + + "WHERE DATE = :date " + + "AND MEMBERID = :memberId " + + "AND ROOM_ID = :roomId", + nativeQuery = true) + List findTodoByDateAndMemberIdAndRoomId( + @Param("date") LocalDate date, + @Param("memberId") Long memberId, + @Param("roomId") Long roomId); + + +} diff --git a/src/main/java/mos/mosback/stRoom/repository/StRoomRepository.java b/src/main/java/mos/mosback/stRoom/repository/StRoomRepository.java new file mode 100644 index 0000000..57337ae --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/repository/StRoomRepository.java @@ -0,0 +1,39 @@ +package mos.mosback.stRoom.repository; + +//Entity 클래스와 Entity레파지토리 위치 같아야함 +import mos.mosback.stRoom.domain.stRoom.StRoomEntity; +import mos.mosback.stRoom.domain.stRoom.StudyMemberEntity; +import mos.mosback.stRoom.dto.Home_RoomResponseDto; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import java.util.List; + +//인터페이스 생성 후 JpaRepository를 상속하면 기본적인 CRUD 메소드 자동으로 생성됨 +public interface StRoomRepository extends JpaRepository { + + @Query("SELECT new mos.mosback.stRoom.dto.Home_RoomResponseDto(s) FROM StRoomEntity s ORDER BY s.roomId DESC") + List findAllDesc(); + + @Query("SELECT new mos.mosback.stRoom.dto.Home_RoomResponseDto(s) FROM StRoomEntity s WHERE s.title LIKE %:keyword% OR s.intro LIKE %:keyword%") + List findByTitleContaining(@Param("keyword") String keyword);//키워드를 통해 스터디그룹을 검색 할 수 있다 + + + @Query(value = "SELECT new mos.mosback.stRoom.dto.Home_RoomResponseDto(s)FROM StRoomEntity s ORDER BY s.click DESC") + List findPopularRoom(); + + @Query("SELECT new mos.mosback.stRoom.dto.Home_RoomResponseDto(s) FROM StRoomEntity s") + List findHomeStRoomField(); + @Query("SELECT new mos.mosback.stRoom.dto.Home_RoomResponseDto(s) FROM StRoomEntity s WHERE s.category = :category") + List findByCategory(@Param("category") String category); + + + @Query("SELECT new mos.mosback.stRoom.dto.Home_RoomResponseDto(s) FROM StRoomEntity s WHERE s.startDate > current_timestamp") + List findRecruitingStudies(); + + + List findByMembersIn(List studyMemberships); + + List findByCreatedByUserEmail(String userEmail); + +} \ No newline at end of file diff --git a/src/main/java/mos/mosback/stRoom/repository/StudyMemberRepository.java b/src/main/java/mos/mosback/stRoom/repository/StudyMemberRepository.java new file mode 100644 index 0000000..824a7f1 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/repository/StudyMemberRepository.java @@ -0,0 +1,17 @@ +package mos.mosback.stRoom.repository; + +import mos.mosback.stRoom.domain.stRoom.StRoomEntity; +import mos.mosback.stRoom.domain.stRoom.StudyMemberEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface StudyMemberRepository extends JpaRepository { + + + List findAllByStRoom(StRoomEntity stRoom); + List findAllByMemberId(Long memberId); + List findByStRoom(StRoomEntity stRoom); + + +} diff --git a/src/main/java/mos/mosback/stRoom/repository/ToDoRepository.java b/src/main/java/mos/mosback/stRoom/repository/ToDoRepository.java new file mode 100644 index 0000000..0b47ab8 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/repository/ToDoRepository.java @@ -0,0 +1,20 @@ +package mos.mosback.stRoom.repository; +import mos.mosback.stRoom.domain.stRoom.StudyMemberTodoEntity; +import mos.mosback.stRoom.domain.stRoom.ToDoEntity; +import mos.mosback.stRoom.dto.StRoomToDoResponseDto; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.time.LocalDate; +import java.util.List; + +public interface ToDoRepository extends JpaRepository { + @Query("SELECT t FROM ToDoEntity t ORDER BY t.todoId") + List findAllDesc(); + + @Query("SELECT NEW mos.mosback.stRoom.dto.StRoomToDoResponseDto(t) FROM ToDoEntity t WHERE t.stRoom.roomId= :roomId ORDER BY t.todoId") + List findByStRoomId(@Param("roomId") Long roomId); + + +} diff --git a/src/main/java/mos/mosback/stRoom/service/StRoomService.java b/src/main/java/mos/mosback/stRoom/service/StRoomService.java new file mode 100644 index 0000000..474eb99 --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/service/StRoomService.java @@ -0,0 +1,322 @@ +package mos.mosback.stRoom.service; +import lombok.RequiredArgsConstructor; +import mos.mosback.login.domain.user.dto.UserProfileDto; +import mos.mosback.login.domain.user.repository.UserRepository; +import mos.mosback.stRoom.domain.stRoom.MemberStatus; +import mos.mosback.stRoom.domain.stRoom.StRoomEntity; +import mos.mosback.stRoom.domain.stRoom.StudyMemberEntity; +import mos.mosback.login.domain.user.User; +import mos.mosback.login.domain.user.service.UserService; +import mos.mosback.login.global.jwt.service.JwtService; +import mos.mosback.stRoom.repository.StRoomRepository; +import mos.mosback.stRoom.repository.StudyMemberRepository; +import mos.mosback.stRoom.dto.*; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.persistence.EntityNotFoundException; +import javax.servlet.http.HttpServletRequest; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + + +@RequiredArgsConstructor +@Service +public class StRoomService { + private final StRoomRepository stRoomRepository; + private final StudyMemberRepository studyMemberRepository; + private final UserService userService; + private final JwtService jwtService; + private final UserRepository userRepository; + + public Long save(StRoomSaveRequestDto requestDto, HttpServletRequest req) { + try { + // 1. Study Room 저장 + StRoomEntity stRoom = stRoomRepository.save(requestDto.toEntity()); + + // 2. 스터디 멤버 정보 저장할 변수 선언 + StudyMemberEntity studyMember = new StudyMemberEntity(); + + // 3. Access Token 가져오기 + String accessToken = Optional.ofNullable(jwtService.extractAccessToken(req)).get().orElse(""); + + // 4. Access Token으로 스터디 멤버 정보 가져오기 (User Entity를) + String loginUserEmail = Optional.ofNullable(jwtService.extractEmailFromJwt(accessToken)).get().orElse(""); + User user = userService.getUserByEmail(loginUserEmail); + + + // 5. Study Member 저장 + studyMember.setMemberId(user.getId()); + studyMember.setStRoom(stRoom); + studyMember.setStatus(MemberStatus.Leader); + studyMemberRepository.save(studyMember); + + return stRoom.getRoomId(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + + } + + @Transactional + public void update(Long roomId, StRoomUpdateRequestDto requestDto) { + StRoomEntity stroomEntity = stRoomRepository.findById(roomId) + .orElseThrow(() -> new IllegalArgumentException(roomId + " NOT FOUND")); + + // 나머지 필드 업데이트 + stroomEntity.update( + requestDto.getTitle(), requestDto.getGoal(), requestDto.getRules(), + requestDto.getQuest(), requestDto.getCategory(), + requestDto.getIntro(), requestDto.getMaxMember(), + requestDto.getMod(), requestDto.isOnOff(), requestDto.getLocation(), + requestDto.getOnline(), requestDto.getStartDate(), + requestDto.getEndDate(), requestDto.getStudyDayEntities()); + + stRoomRepository.save(stroomEntity); + } //stroomsRepository를 사용하여 데이터베이스에서 주어진 id에 해당하는 게시물을 찾기 + + + @Transactional(readOnly = true) + public StRoomResponseDto findById(Long roomId) { + try { + + StRoomEntity stRoomEntity = stRoomRepository.findById(roomId) + .orElseThrow(() -> new IllegalArgumentException("해당 게시물이 없습니다. id=" + roomId)); + + return new StRoomResponseDto(stRoomEntity); + }catch (Exception e){ + e.printStackTrace(); + return null; + } + } + + @Transactional(readOnly = true) + public StRoomDetailResponseDto findByRoomId(Long roomId) { + StRoomEntity stRoomEntity = stRoomRepository.findById(roomId) + .orElseThrow(() -> new IllegalArgumentException("해당 게시물이 없습니다. id=" + roomId)); + + return new StRoomDetailResponseDto(stRoomEntity); + } + public List findAllRoomsDesc() { + + + return stRoomRepository.findAllDesc(); + } + + @Transactional + public void delete(Long roomId) { + StRoomEntity stroomEntity = stRoomRepository.findById(roomId) + .orElseThrow(() -> new IllegalArgumentException("해당 게시물이 없습니다. id =" + roomId)); + + stRoomRepository.delete(stroomEntity); //JpaRepository 에서 이미 delete 메소드를 지원하고 있으므로 활용 + //엔티티를 파라미터로 살제할 수도 있고, deleteById 메서드를 이용하면 id로 삭제할 수 있음 + // 존재하는 strooms인지 확인을 위해 엔티티 조회 후 그대로 삭제함 + // --> 서비스에서 delete 메서드를 만들면 컨트롤러가 사용하도록 컨트롤러에 코드 추가하기. + } + + @Transactional(readOnly = true) + public List findByTitleContaining(String keyword) { + return stRoomRepository.findByTitleContaining(keyword); + } + + + @Transactional(readOnly = true) + public List findPopularRoom() { + return stRoomRepository.findPopularRoom(); + } + + @Transactional(readOnly = true) + public List findRoomsInHome() { + + return stRoomRepository.findHomeStRoomField(); + } + + public List findByCategory(String category) { + return stRoomRepository.findByCategory(category); + } + + public void memberJoin(StRoomMemberJoinRequestDto requestDto) { + try { + // 1. save할 변수 선언 + StudyMemberEntity studyMember = new StudyMemberEntity(); + + // 2. 가입 시에는 룸ID를 요청 파라미터에서 받아서 StRoom 조회 + StRoomEntity stRoomEntity = stRoomRepository.findById(requestDto.getRoomId()) + .orElseThrow(() -> new IllegalArgumentException("해당 게시물이 없습니다. id =" + requestDto.getRoomId())); + + // 3. 사용자 이메일 조회해서 save 전에 주입 + User user = userService.getUserByEmail(requestDto.getEmail()); + studyMember.setMemberId(user.getId()); + + // 4. 조회된 정보 토대로 StudyMember save 처리 + studyMember.setStRoom(stRoomEntity); + studyMember.setStatus(MemberStatus.Waiting); + studyMember.setAnswer(requestDto.getAnswer()); + studyMemberRepository.save(studyMember); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + + public List getRecruitingStudies() { + + LocalDateTime now = LocalDateTime.now(); + List recruitingStudies = stRoomRepository.findRecruitingStudies(); + + return recruitingStudies; + } + + public QuestionDto getQuestionById(Long roomId) { + StRoomEntity stRoomEntity = stRoomRepository.findById(roomId) + .orElseThrow(() -> new EntityNotFoundException("Room not found")); + + QuestionDto responseDTO = new QuestionDto(); + responseDTO.setQuestion(stRoomEntity.getQuest()); + + return responseDTO; + } + + public QuestionAndAnswerResponseDto getQuestionAndAnswerById(Long memberId, Long roomId) { + + StudyMemberEntity memberEntity = studyMemberRepository.findById(memberId) + .orElseThrow(() -> new EntityNotFoundException("Member not found")); + + StRoomEntity stRoomEntity = stRoomRepository.findById(roomId) + .orElseThrow(() -> new EntityNotFoundException("Room not found")); + + QuestionAndAnswerResponseDto responseDto = new QuestionAndAnswerResponseDto(); + responseDto.setMemberId(memberEntity.getMemberId()); + responseDto.setAnswer(memberEntity.getAnswer()); + responseDto.setQuestion(stRoomEntity.getQuest()); + + + return responseDto; + } + public UserProfileDto getMemberProfileById(Long memberId) throws Exception { + + Optional optionalUser = userRepository.findById(memberId); + if (optionalUser.isPresent()) { + User user = optionalUser.get(); + // 엔터티 정보를 DTO로 매핑하여 반환 + return new UserProfileDto( + user.getId(), + user.getNickname(), + user.getName(), + user.getStr_duration(), + user.getEnd_duration(), + user.getMessage(), + user.getCompany(), + user.getTend1(), + user.getTend2(), + user.getRoomId(), + user.getImageUrl() + ); + } else { + throw new Exception("해당 이메일의 사용자를 찾을 수 없습니다: " + memberId); + } + } + + public String getMyInfo(String email) throws Exception { + // 3. 사용자 이메일 조회해서 save 전에 주입 + User user = userService.getUserByEmail(email); + + List memberJoinList = studyMemberRepository.findAllByMemberId(user.getId()); + + // study member 가입이력이 있다면 "Y" , 없으면 "N" + return !memberJoinList.isEmpty() ? "Y" : "N"; + } + + public String isRecruiting(Long roomId) { + Optional optionalRoom = stRoomRepository.findById(roomId); + StRoomEntity stRoom = optionalRoom.get(); + if (stRoom.getStartDate().isAfter(LocalDate.now())) { + stRoom.setRecruiting(true); + } else { + stRoom.setRecruiting(false); + } + return stRoom.isRecruiting() ? "true" : "false"; + } + + public void acceptMember(AcceptMemberRequestDto requestDto){ + // 1. save할 변수 선언 + StudyMemberEntity studyMember = new StudyMemberEntity(); + + // 2. 가입 시에는 룸ID를 요청 파라미터에서 받아서 StRoom 조회 + StRoomEntity stRoomEntity = stRoomRepository.findById(requestDto.getRoomId()) + .orElseThrow(() -> new IllegalArgumentException("해당 게시물이 없습니다. id =" + requestDto.getRoomId())); + + studyMember.setMemberId(requestDto.getMemberId()); + studyMember.setStRoom(stRoomEntity); + studyMember.setStatus(MemberStatus.Member); + studyMemberRepository.save(studyMember); + + } + public void rejectMember(AcceptMemberRequestDto requestDto){ + // 1. save할 변수 선언 + StudyMemberEntity studyMember = new StudyMemberEntity(); + + // 2. 가입 시에는 룸ID를 요청 파라미터에서 받아서 StRoom 조회 + StRoomEntity stRoomEntity = stRoomRepository.findById(requestDto.getRoomId()) + .orElseThrow(() -> new IllegalArgumentException("해당 게시물이 없습니다. id =" + requestDto.getRoomId())); + + studyMember.setMemberId(requestDto.getMemberId()); + studyMember.setStRoom(stRoomEntity); + studyMember.setStatus(MemberStatus.Rejected); + studyMemberRepository.save(studyMember); + + } + + public List getStudyRoomMemberList(Long roomId) { + StRoomEntity stRoom = stRoomRepository.findById(roomId) + .orElseThrow(() -> new EntityNotFoundException("Room not found")); + List memberEntityList = studyMemberRepository.findAllByStRoom(stRoom); + List memberList = new ArrayList<>(); + for (StudyMemberEntity item : memberEntityList) { + StRoomMemberResponseDto dto = new StRoomMemberResponseDto(); + dto.setMemberId(item.getMemberId()); + dto.setStatus(item.getStatus()); + memberList.add(dto); + } + return memberList; + } + + public User getUserInfo(Long memberId) { + return userRepository.findById(memberId).orElseThrow(() -> new EntityNotFoundException("user info not found")); + } + + public List getMyStudyMemberHistory(String email) throws Exception { + // 3. 사용자 이메일 조회해서 save 전에 주입 + User user = userService.getUserByEmail(email); + List memberJoinList = studyMemberRepository.findAllByMemberId(user.getId()); + memberJoinList = memberJoinList.stream() + .filter(data -> !data.getStatus().equals(MemberStatus.Leader)) + .collect(Collectors.toList()); + List result = new ArrayList<>(); + for (StudyMemberEntity item : memberJoinList) { + StudyMemberHistoryDto data = new StudyMemberHistoryDto(); + data.setTitle(item.getStRoom().getTitle()); + data.setStatus(item.getStatus()); + result.add(data); + } + return result; + } + + + public List getLeaderStudies(String userEmail) { + // 사용자의 이메일을 기반으로 Leader로 등록된 스터디룸을 조회하여 StRoomEntity 목록을 얻어옴 + List leaderStudies = stRoomRepository.findByCreatedByUserEmail(userEmail); + + // StRoomEntity 목록을 StRoomResponseDto 목록으로 변환하여 반환 + return leaderStudies.stream() + .map(StRoomResponseDto::new) + .collect(Collectors.toList()); + } + +} \ No newline at end of file diff --git a/src/main/java/mos/mosback/stRoom/service/ToDoService.java b/src/main/java/mos/mosback/stRoom/service/ToDoService.java new file mode 100644 index 0000000..282218a --- /dev/null +++ b/src/main/java/mos/mosback/stRoom/service/ToDoService.java @@ -0,0 +1,249 @@ +package mos.mosback.stRoom.service; +import lombok.RequiredArgsConstructor; +import mos.mosback.login.domain.user.User; +import mos.mosback.login.domain.user.repository.UserRepository; +import mos.mosback.login.domain.user.service.UserService; +import mos.mosback.stRoom.domain.stRoom.*; +import mos.mosback.stRoom.dto.*; +import mos.mosback.stRoom.repository.MemberTodoRepository; +import mos.mosback.stRoom.repository.StRoomRepository; +import mos.mosback.stRoom.repository.ToDoRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + + + +@RequiredArgsConstructor +@Service +public class ToDoService { + + private final ToDoRepository toDoRepository; + private final StRoomRepository stRoomRepository; + private final MemberTodoRepository studyMemberToDoRepository; + private final UserService userService; + private final StRoomService stRoomService; + + + + @Transactional + public ToDoEntity addTodo(stRoomToDoRequestDto requestDto, Long roomId) { + ToDoEntity toDoEntity = new ToDoEntity(); + toDoEntity.setTodoContent(requestDto.getTodoContent()); + toDoEntity.setStatus(TodoStatus.Waiting); + StRoomEntity stRoomEntity = stRoomRepository.findById(roomId) + .orElseThrow(() -> new IllegalArgumentException("해당 게시물이 없습니다. id =" + roomId)); + + toDoEntity.setStRoom(stRoomEntity); + return toDoRepository.save(toDoEntity); + } + + + @Transactional + public ToDoEntity updateTodo(Long todoId, String todoContent,TodoStatus status) { + ToDoEntity toDoEntity = toDoRepository.findById(todoId) + .orElseThrow(() -> new IllegalArgumentException("해당 ToDo를 찾을 수 없습니다.")); + + toDoEntity.update(todoContent, status); + return toDoEntity; + } + + + @Transactional + public StudyMemberTodoEntity updateMemberTodo(Long todoIdx, String todoContent, TodoStatus status, String currentEmail) throws Exception { + User user = userService.getUserByEmail(currentEmail); + StudyMemberTodoEntity studyMemberTodoEntity = studyMemberToDoRepository.findById(todoIdx) + .orElseThrow(() -> new IllegalArgumentException("해당 ToDo를 찾을 수 없습니다.")); + + studyMemberTodoEntity.update(todoContent, status); + return studyMemberTodoEntity; + } + + @Transactional + public void deleteTodo(Long todoId) { + ToDoEntity toDoEntity = toDoRepository.findById(todoId) + .orElseThrow(() -> new IllegalArgumentException("해당 ToDo를 찾을 수 없습니다.")); + + toDoRepository.delete(toDoEntity); + } + + @Transactional + public void deleteMemberTodo(Long todoIdx, String currentEmail) throws Exception { + User user = userService.getUserByEmail(currentEmail); + StudyMemberTodoEntity studyMemberTodoEntity = studyMemberToDoRepository.findById(todoIdx) + .orElseThrow(() -> new IllegalArgumentException("해당 ToDo를 찾을 수 없습니다.")); + + studyMemberToDoRepository.delete(studyMemberTodoEntity); + } + + @Transactional + public List findStRoomTodoByRoomId(Long roomId) { + return toDoRepository.findByStRoomId(roomId); + } + + public StudyMemberTodoEntity addMemberTodo(StudyMemberToDoRequestDto requestDto) throws Exception { + StudyMemberTodoEntity toDoEntity = new StudyMemberTodoEntity(); + StRoomEntity stRoomEntity = stRoomRepository.findById(requestDto.getRoomId()) + .orElseThrow(() -> new IllegalArgumentException("해당 게시물이 없습니다. id =" + requestDto.getRoomId())); + // 사용자 이메일 조회해서 save 전에 주입 + User user = userService.getUserByEmail(requestDto.getCurrentEmail()); + toDoEntity.setMemberId(user.getId()); + toDoEntity.setStatus(TodoStatus.Waiting); + toDoEntity.setStRoom(stRoomEntity); + toDoEntity.setTodoContent(requestDto.getTodoContent()); + toDoEntity.setDate(requestDto.getDate()); + return studyMemberToDoRepository.save(toDoEntity); + } + + public StudyMemberRoomInfoResponseDto getMemberRoomInfo(Long roomId, String currentEmail) throws Exception { + StudyMemberRoomInfoResponseDto result = new StudyMemberRoomInfoResponseDto(); + + StRoomEntity stRoom = stRoomRepository.findById(roomId) + .orElseThrow(() -> new IllegalArgumentException("해당 게시물이 없습니다. id =" + roomId)); + + User user = userService.getUserByEmail(currentEmail); + + List todoEntityList = studyMemberToDoRepository.findAllByStRoom(roomId, user.getId()); + List todoList = new ArrayList<>(); + for (StudyMemberTodoEntity item : todoEntityList) { + todoList.add(new StRoomMemberToDoResponseDto(item)); + } + List studyRoomTodoAverage = null; + double average = 0; + try { + studyRoomTodoAverage = studyMemberToDoRepository.getStudyRoomTodoAverage(roomId); // studyRoomTodoAverage를 초기화 + } catch (Exception e) { + e.printStackTrace(); + } + if (studyRoomTodoAverage != null && !studyRoomTodoAverage.isEmpty()) { + StRoomTodoInfoProjection projection = studyRoomTodoAverage.get(0); + // DB에 데이터가 존재할 때에만 해당 로직 수행 + int totalCount = projection.getTotalCount(); + int completedCount = projection.getCompletedCount(); + if (totalCount > 0) { + average = ((double) completedCount / totalCount) * 100; + } + } + List roomDayList = new ArrayList<>(); + + Date now = new Date(); + Calendar cal1 = Calendar.getInstance(); + cal1.setTime(now); + LocalDate date = LocalDate.now(); + + for (int i=0; i<7; i++) { + StudyRoomDayDto dayDto = new StudyRoomDayDto(); + + dayDto.setYear(date.getYear()); // 날짜 객체 셋팅 + dayDto.setMonth(date.getMonthValue()); + dayDto.setDayVal(cal1.get(Calendar.DATE)); // 날짜 셋팅 + dayDto.setDayOfWeek(getDayOfKoreanWeek(cal1.get(Calendar.DAY_OF_WEEK))); // "월"~"일" 셋팅 + roomDayList.add(dayDto); + + cal1.add(Calendar.DATE, 1); // 일 계산 하루씩 추가 + } + + result.setCategory(stRoom.getCategory()); + /* result.setTodoList(todoList);*/ + result.setAvg(average); // 평균값 + result.setRoomDayList(roomDayList); + return result; + } + + public String getDayOfKoreanWeek(int dayOfWeek) { + switch (dayOfWeek) { + case Calendar.SUNDAY: + return "일"; + case Calendar.MONDAY: + return "월"; + case Calendar.TUESDAY: + return "화"; + case Calendar.WEDNESDAY: + return "수"; + case Calendar.THURSDAY: + return "목"; + case Calendar.FRIDAY: + return "금"; + case Calendar.SATURDAY: + return "토"; + default: + return ""; + } + } + + public List getMemberTodoProgress(Long roomId,String currentEmail) throws Exception { + List progressList = new ArrayList<>(); + List progressProjections = studyMemberToDoRepository.getRankByStRoom(roomId); + List memberList = stRoomService.getStudyRoomMemberList(roomId); + + for (MemberTodoRankProjection progressProjection : progressProjections) { + User user = stRoomService.getUserInfo(progressProjection.getMemberId()); + + MemberTodoRankResponseDto progress = new MemberTodoRankResponseDto(); + progress.setProgress(progressProjection.getProgress()); + progress.setNickname(user.getNickname()); + progressList.add(progress); + } + + return progressList; + } + + public MemberTodoProgressResponseDto getProgressInfo(Long roomId, String currentEmail) throws Exception { + MemberTodoProgressResponseDto todoProgress = new MemberTodoProgressResponseDto(); + User user = userService.getUserByEmail(currentEmail); + todoProgress.setUserNick(user.getNickname()); + + List studyRoomTodoAverage = null; + double average = 0; + try { + studyRoomTodoAverage = studyMemberToDoRepository.getStudyRoomTodoAverage(roomId); + } catch (Exception e) { + e.printStackTrace(); + } + if (studyRoomTodoAverage != null && !studyRoomTodoAverage.isEmpty()) { + StRoomTodoInfoProjection projection = studyRoomTodoAverage.get(0); + // DB에 데이터가 존재할 때에만 해당 로직 수행 + int totalCount = projection.getTotalCount(); + int completedCount = projection.getCompletedCount(); + if (totalCount > 0) { + average = ((double) completedCount / totalCount) * 100; + } + } + + todoProgress.setAvg(average); + return todoProgress; + } + + public List getTodoByDateAndMemberIdAndRoomId(LocalDate date, Long roomId,String email) throws Exception{ + List dto = new ArrayList<>(); + User user= userService.getUserByEmail(email); + List projection = studyMemberToDoRepository.findTodoByDateAndMemberIdAndRoomId(date,user.getId(),roomId); + //projection null check + if(projection == null || projection.isEmpty()) + { + System.out.println("**********"); + } + for (StRoomTodoFindProjection projections : projection) { + MemberTodoResponseDto responseDto = new MemberTodoResponseDto(); + responseDto.setStatus(projections.getStatus()); + responseDto.setTodoContent(projections.getTodoContent()); + responseDto.setIdx(projections.getIdx()); + + //projection select result check + System.out.println("=========[TO DO LIST"+responseDto.getIdx()+"]==========="); + System.out.println(responseDto.getStatus()); + System.out.println(responseDto.getTodoContent()); + + dto.add(responseDto); + } + + return dto; + } + +} diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/main/resources/application-local.yml @@ -0,0 +1 @@ + diff --git a/src/main/resources/application-oauth.yml b/src/main/resources/application-oauth.yml index e2c5fe1..4300e4c 100644 --- a/src/main/resources/application-oauth.yml +++ b/src/main/resources/application-oauth.yml @@ -5,13 +5,13 @@ spring: registration: google: client-id: 729954756779-2usa0d48vh0048r4vmmljtlrlstvcjdh.apps.googleusercontent.com - client-secret: 구글시크릿 + client-secret: scope: profile, email naver: client-id: jg5_V_oaR2k60xfasDRa - client-secret: 네이버시크릿 + client-secret: redirect-uri: http://localhost:8080/login/oauth2/code/naver authorization-grant-type: authorization_code scope: name, email, profile_image @@ -20,7 +20,7 @@ spring: kakao: client-id: bbe971abb2538851ddabe4ef20d76744 - client-secret: 카카오시크릿 + client-secret: redirect-uri: http://localhost:8080/login/oauth2/code/kakao client-authentication-method: POST authorization-grant-type: authorization_code diff --git a/src/main/resources/application-real.yml b/src/main/resources/application-real.yml new file mode 100644 index 0000000..9a9b169 --- /dev/null +++ b/src/main/resources/application-real.yml @@ -0,0 +1,9 @@ +spring: + profiles: + include: oauth,real-db + jpa: + properties: + hibernate: + dialect: org.hibernate.dialect.MySQL5InnoDBDialect + session: + store-type: jdbc diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 376086d..8b13789 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,55 +1 @@ -spring: - profiles: - group: - "local" : "local, jwt, oauth" - active : local ---- -spring: - config: - activate: - on-profile: "local" - - h2: - console: - enabled: true - path: /h2-console - - datasource: - driver-class-name: org.h2.Driver - url: jdbc:h2:mem:testdb - username: sa - password: - - server: - port: 8080 - - main: - allow-circular-references: true - - jpa: - show-sql: true - database-platform: org.hibernate.dialect.H2Dialect - properties: - hibernate: - format_sql: true - show_sql: true - - hibernate: - ddl-auto: create - - mail: - host: smtp.naver.com # 이메일 서버 호스트 (Gmail 사용 예) - port: 587 # SMTP 포트 (Gmail의 경우 465 또는 587) - username: 이메일계정 # 이메일 계정 - password: 비밀번호 # 이메일 계정의 암호 또는 앱 비밀번호 (보안 설정에 따라 다름) - properties: - mail: - smtp: - auth: true - starttls: - enable: true # TLS를 사용하려면 true, SSL을 사용하려면 false - debug: true # 디버그 모드 활성화 - logging: - level: - root: DEBUG diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index c87dcc5..201c426 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -1,3 +1,8 @@ Kakao Login
Google Login
-Naver Login
\ No newline at end of file +Naver Login
+ +
+ + +
\ No newline at end of file diff --git a/src/test/java/mos/mosback/MosbackApplicationTests.java b/src/test/java/mos/mosback/MosbackApplicationTests.java deleted file mode 100644 index 4a39173..0000000 --- a/src/test/java/mos/mosback/MosbackApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package mos.mosback; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class MosbackApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/src/test/java/mos/mosback/UserServiceTest.java b/src/test/java/mos/mosback/UserServiceTest.java new file mode 100644 index 0000000..064de69 --- /dev/null +++ b/src/test/java/mos/mosback/UserServiceTest.java @@ -0,0 +1,57 @@ +package mos.mosback; + +import mos.mosback.stRoom.domain.stRoom.StRoomEntity; +import mos.mosback.stRoom.repository.StRoomRepository; +import mos.mosback.login.domain.user.service.UserService; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@SpringBootTest +public class UserServiceTest { + + @Autowired + private UserService userService; + + @Autowired + private StRoomRepository stRoomRepository; + + @Test + public void testGetStudyGroupsForUserByMemberId() { + // Arrange: 테스트용 memberId와 연관된 스터디 그룹 데이터를 미리 데이터베이스에 저장합니다. + Long memberId = 1L; // 테스트용 memberId + // TODO: 스터디 그룹 데이터베이스에 저장 (예: StRoomEntity 객체 생성 및 stRoomRepository.save()) + + // Act: getStudyGroupsForUserByMemberId 메서드를 호출하여 실제 반환값을 가져옵니다. + List studyGroups = userService.getStudyGroupsForUserByMemberId(memberId); + List expectedStudyGroups = new ArrayList<>(); + +// 예상되는 스터디 그룹을 추가합니다. + StRoomEntity studyGroup1 = new StRoomEntity(); + studyGroup1.setTitle("Study Group 1"); +// TODO: studyGroup1의 다른 필드를 설정 + + StRoomEntity studyGroup2 = new StRoomEntity(); + studyGroup2.setTitle("Study Group 2"); +// TODO: studyGroup2의 다른 필드를 설정 + + expectedStudyGroups.add(studyGroup1); + expectedStudyGroups.add(studyGroup2); + // TODO: expectedStudyGroups를 설정 (실제로 기대하는 스터디 그룹 데이터를 데이터베이스에 저장하고 가져와서 설정) + + // Assert: 반환된 스터디 그룹 목록이 예상한 값과 일치하는지를 검증합니다. + // 예상한 스터디 그룹 개수와 일치하는지 검증 + assertEquals(expectedStudyGroups.size(), studyGroups.size()); + + // 각 스터디 그룹에 대한 추가적인 검증 (예: 제목, 설명 등) + for (int i = 0; i < expectedStudyGroups.size(); i++) { + assertEquals(expectedStudyGroups.get(i).getTitle(), studyGroups.get(i).getTitle()); + // TODO: 다른 필드들을 비교하여 일치하는지 확인 + } + } +} diff --git a/src/test/java/mos/mosback/login/domain/user/controller/LeaderStudyControllerTest.java b/src/test/java/mos/mosback/login/domain/user/controller/LeaderStudyControllerTest.java new file mode 100644 index 0000000..6b5228d --- /dev/null +++ b/src/test/java/mos/mosback/login/domain/user/controller/LeaderStudyControllerTest.java @@ -0,0 +1,7 @@ +package mos.mosback.login.domain.user.controller; + +import static org.junit.jupiter.api.Assertions.*; + +class LeaderStudyControllerTest { + +} \ No newline at end of file