diff --git a/src/main/java/kr/tgwing/tech/admin/controller/AdminController.java b/src/main/java/kr/tgwing/tech/admin/controller/AdminController.java index 262af7b..589b45e 100644 --- a/src/main/java/kr/tgwing/tech/admin/controller/AdminController.java +++ b/src/main/java/kr/tgwing/tech/admin/controller/AdminController.java @@ -16,12 +16,12 @@ @RequestMapping("/admin") @RequiredArgsConstructor @Slf4j -@Tag(name = "관리자 기능") +@Tag(name = "관리자") public class AdminController { private final AdminServiceImpl adminService; - @Operation(summary = "회원 요청 목록 확인하기") + @Operation(summary = "요청 목록 확인") @GetMapping("") public ResponseEntity>> checkUsers() { List dtoList = adminService.checkUser(); @@ -29,7 +29,7 @@ public ResponseEntity>> checkUsers() { return ResponseEntity.ok(ApiResponse.ok(dtoList)); } - @Operation(summary = "회원 요청 수락하기") + @Operation(summary = "회원요청 수락") @PostMapping("/{id}") public ResponseEntity> registerUsers(@PathVariable("id") Long id) { Long registerId = adminService.registerUsers(id); @@ -37,7 +37,7 @@ public ResponseEntity> registerUsers(@PathVariable("id") Long return ResponseEntity.ok(ApiResponse.updated(registerId)); } - @Operation(summary = "회원 요청 거부하기") + @Operation(summary = "회원요청 거부") @DeleteMapping("/{id}") public ResponseEntity> refuseUsers(@PathVariable("id") Long id) { Long refusedId = adminService.refuseUsers(id); diff --git a/src/main/java/kr/tgwing/tech/admin/dto/AdminCheckUserDto.java b/src/main/java/kr/tgwing/tech/admin/dto/AdminCheckUserDto.java index 5ccbffd..62a3093 100644 --- a/src/main/java/kr/tgwing/tech/admin/dto/AdminCheckUserDto.java +++ b/src/main/java/kr/tgwing/tech/admin/dto/AdminCheckUserDto.java @@ -10,8 +10,8 @@ @NoArgsConstructor @AllArgsConstructor public class AdminCheckUserDto { - private Long id; - private String studentId; + private Long studentId; + private String studentNumber; private String email; private String name; private String phoneNumber; diff --git a/src/main/java/kr/tgwing/tech/admin/service/AdminServiceImpl.java b/src/main/java/kr/tgwing/tech/admin/service/AdminServiceImpl.java index 1c7b467..431487f 100644 --- a/src/main/java/kr/tgwing/tech/admin/service/AdminServiceImpl.java +++ b/src/main/java/kr/tgwing/tech/admin/service/AdminServiceImpl.java @@ -35,21 +35,21 @@ public List checkUser() { } @Transactional - public Long registerUsers(Long id) { - TempUser notUser = tempUserRepository.findById(id).orElseThrow(UserNotFoundException::new); + public Long registerUsers(Long studentId) { + TempUser notUser = tempUserRepository.findById(studentId).orElseThrow(UserNotFoundException::new); User user = notUser.toUser(notUser); - tempUserRepository.deleteById(id); + tempUserRepository.deleteById(studentId); user.setRole("ROLE_USER"); userRepository.save(user); - return user.getId(); + return user.getStudentId(); } - public Long refuseUsers(Long id) { - TempUser user = tempUserRepository.findById(id).orElseThrow(UserNotFoundException::new); - userRepository.deleteById(user.getId()); + public Long refuseUsers(Long studentId) { + TempUser user = tempUserRepository.findById(studentId).orElseThrow(UserNotFoundException::new); + userRepository.deleteById(user.getStudentId()); - return user.getId(); + return user.getStudentId(); } } diff --git a/src/main/java/kr/tgwing/tech/blog/entity/HashTagEntity.java b/src/main/java/kr/tgwing/tech/blog/entity/HashTagEntity.java index 0ee1098..a1b8fc9 100644 --- a/src/main/java/kr/tgwing/tech/blog/entity/HashTagEntity.java +++ b/src/main/java/kr/tgwing/tech/blog/entity/HashTagEntity.java @@ -15,11 +15,17 @@ @Table(name = "hashtag") public class HashTagEntity { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "hashtag_id") private Long id; + + @Column(name = "hashtag_name") private String name; +// @Column(name = "creator_id") +// @JoinColumn(referencedColumnName = "student_id", table = "student") +// private Long creatorId; + @OneToMany(mappedBy = "hashtag") @Builder.Default private Set postTags = new HashSet<>(); diff --git a/src/main/java/kr/tgwing/tech/blog/entity/PostEntity.java b/src/main/java/kr/tgwing/tech/blog/entity/PostEntity.java index def1f5b..28993d6 100644 --- a/src/main/java/kr/tgwing/tech/blog/entity/PostEntity.java +++ b/src/main/java/kr/tgwing/tech/blog/entity/PostEntity.java @@ -15,17 +15,19 @@ @ToString @NoArgsConstructor @AllArgsConstructor -@Table(name="post") +@Table(name="blog") public class PostEntity extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; - @JoinColumn(referencedColumnName = "id", table = "user") + @JoinColumn(referencedColumnName = "student_id", table = "student") private Long writer; @Column(nullable = false) private String title; @Column(nullable = false) private String content; + + @Column(name = "thumbnail_url") private String thumbnail; private int replyCount; diff --git a/src/main/java/kr/tgwing/tech/blog/entity/PostTagEntity.java b/src/main/java/kr/tgwing/tech/blog/entity/PostTagEntity.java index e1d3e0a..b083d51 100644 --- a/src/main/java/kr/tgwing/tech/blog/entity/PostTagEntity.java +++ b/src/main/java/kr/tgwing/tech/blog/entity/PostTagEntity.java @@ -13,7 +13,7 @@ @AllArgsConstructor @Builder @IdClass(PostTagId.class) -@Table(name = "post_hashtag") +@Table(name = "blog_hashtag") public class PostTagEntity extends BaseEntity { @Id diff --git a/src/main/java/kr/tgwing/tech/blog/entity/ReplyEntity.java b/src/main/java/kr/tgwing/tech/blog/entity/ReplyEntity.java index c62cbcf..0fb77d9 100644 --- a/src/main/java/kr/tgwing/tech/blog/entity/ReplyEntity.java +++ b/src/main/java/kr/tgwing/tech/blog/entity/ReplyEntity.java @@ -13,7 +13,7 @@ @ToString @NoArgsConstructor @AllArgsConstructor -@Table(name = "reply") +@Table(name = "blog_comment_reply") public class ReplyEntity extends BaseEntity { @Id diff --git a/src/main/java/kr/tgwing/tech/blog/service/PostServiceImpl.java b/src/main/java/kr/tgwing/tech/blog/service/PostServiceImpl.java index 98eac83..e11407c 100644 --- a/src/main/java/kr/tgwing/tech/blog/service/PostServiceImpl.java +++ b/src/main/java/kr/tgwing/tech/blog/service/PostServiceImpl.java @@ -72,14 +72,14 @@ private Page getAllPosts(PageRequest pageRequest) { // 모든 공지 } @Override - public PostDto createPost(PostCreationDto requestDto, String utilStudentId) // 공지 생성하기 + public PostDto createPost(PostCreationDto requestDto, String studentNumber) // 공지 생성하기 { // 글을 작성할 user 조회 - Optional byStudentId = userRepository.findByStudentId(utilStudentId); + Optional byStudentId = userRepository.findByStudentNumber(studentNumber); User userEntity = byStudentId.orElseThrow(UserNotFoundException::new); // 현재 사용자와 요청 시 들어온 writer가 같은지 확인 - if (!Objects.equals(userEntity.getStudentId(), utilStudentId)) { + if (!Objects.equals(userEntity.getStudentId(), studentNumber)) { throw new WrongPostRequestException(); } @@ -89,7 +89,7 @@ public PostDto createPost(PostCreationDto requestDto, String utilStudentId) // //getHashtag - 요청으로 들어온 해시태그 집합을 데이터베이스에 블로그 연관 테이블, 해시태그 테이블에 저장 및 엔티티 집합 반환 Set hashtags = getHashtag(requestDto.getHashtags()); PostEntity postEntity = PostCreationDto.toEntity(requestDto); - postEntity.setWriter(userEntity.getId()); + postEntity.setWriter(userEntity.getStudentId()); PostEntity savedEntity = postRepository.save(postEntity); hashtags.forEach(tag -> { diff --git a/src/main/java/kr/tgwing/tech/common/BaseEntity.java b/src/main/java/kr/tgwing/tech/common/BaseEntity.java index de2919e..8a17a78 100644 --- a/src/main/java/kr/tgwing/tech/common/BaseEntity.java +++ b/src/main/java/kr/tgwing/tech/common/BaseEntity.java @@ -20,11 +20,11 @@ public abstract class BaseEntity { @CreatedDate - @Column(name = "regdate", updatable = false) + @Column(name = "created_at", updatable = false) private LocalDateTime regDate; @LastModifiedDate - @Column(name ="moddate" ) + @Column(name ="updated_at") private LocalDateTime modDate; } diff --git a/src/main/java/kr/tgwing/tech/common/exception/ExceptionMapper.java b/src/main/java/kr/tgwing/tech/common/exception/ExceptionMapper.java index 88efff9..8fd087b 100644 --- a/src/main/java/kr/tgwing/tech/common/exception/ExceptionMapper.java +++ b/src/main/java/kr/tgwing/tech/common/exception/ExceptionMapper.java @@ -4,6 +4,7 @@ import kr.tgwing.tech.blog.exception.reply.ReplyBadRequestException; import kr.tgwing.tech.blog.exception.reply.ReplyForbiddenException; import kr.tgwing.tech.blog.exception.reply.ReplyNotFoundException; +import kr.tgwing.tech.project.exception.ProjectNotFoundException; import kr.tgwing.tech.user.exception.*; import org.springframework.http.HttpStatus; @@ -18,6 +19,7 @@ public class ExceptionMapper { // 예외 객체 -> 예외 상태로 바꿔주는 setUpUserException(); setUpPostException(); setUpReplyException(); + setUpProjectException(); // setUpReplyException(); } @@ -58,6 +60,11 @@ private static void setUpReplyException() { ExceptionSituation.of("요청한 사용자에게 권한이 없습니다.", HttpStatus.FORBIDDEN, 5502)); } + private static void setUpProjectException() { + mapper.put(ProjectNotFoundException.class, + ExceptionSituation.of("찾고자 하는 프로젝트가 존재하지 않습니다.", HttpStatus.NOT_FOUND, 6600)); + } + public static ExceptionSituation getSituationOf(Exception exception) { return mapper.get(exception.getClass()); diff --git a/src/main/java/kr/tgwing/tech/common/RedisConfig.java b/src/main/java/kr/tgwing/tech/config/RedisConfig.java similarity index 98% rename from src/main/java/kr/tgwing/tech/common/RedisConfig.java rename to src/main/java/kr/tgwing/tech/config/RedisConfig.java index 55301e6..703ad5b 100644 --- a/src/main/java/kr/tgwing/tech/common/RedisConfig.java +++ b/src/main/java/kr/tgwing/tech/config/RedisConfig.java @@ -1,4 +1,4 @@ -package kr.tgwing.tech.common; +package kr.tgwing.tech.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.annotation.EnableCaching; diff --git a/src/main/java/kr/tgwing/tech/config/TestDataLoader.java b/src/main/java/kr/tgwing/tech/config/TestDataLoader.java index 58fe6ae..00753e7 100644 --- a/src/main/java/kr/tgwing/tech/config/TestDataLoader.java +++ b/src/main/java/kr/tgwing/tech/config/TestDataLoader.java @@ -1,6 +1,7 @@ package kr.tgwing.tech.config; import java.sql.Date; +import java.time.LocalDate; import kr.tgwing.tech.user.dto.registerdto.UserDTO; import org.springframework.boot.CommandLineRunner; @@ -26,20 +27,20 @@ public CommandLineRunner loadTestData(UserService userService, ReplyRepository replyRepository) { return args -> { userService.register(UserDTO.builder() - .studentId("2018000000") + .studentNumber("2018000000") .phoneNumber("01000000000") .email("oldman@khu.ac.kr") .name("늙은이") .password("12345678") - .birth(Date.valueOf("1999-01-01")) + .birth(LocalDate.parse("1999-01-01")) .build()); userService.register(UserDTO.builder() - .studentId("2022000000") + .studentNumber("2022000000") .phoneNumber("01011111111") .email("youngman@khu.ac.kr") .name("젊은이") .password("12345678") - .birth(Date.valueOf("2003-01-01")) + .birth(LocalDate.parse("2003-01-01")) .build()); postRepository.save(PostEntity.builder() .writer(1L) diff --git a/src/main/java/kr/tgwing/tech/project/advice/ProjectControllerAdvice.java b/src/main/java/kr/tgwing/tech/project/advice/ProjectControllerAdvice.java deleted file mode 100644 index 58cefa3..0000000 --- a/src/main/java/kr/tgwing/tech/project/advice/ProjectControllerAdvice.java +++ /dev/null @@ -1,33 +0,0 @@ -package kr.tgwing.tech.project.advice; - -import kr.tgwing.tech.project.dto.ErrorResultDTO; -import kr.tgwing.tech.project.exception.BadRequestException; -import kr.tgwing.tech.project.exception.NotFoundException; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestControllerAdvice; - -@Slf4j -public class ProjectControllerAdvice { - @ExceptionHandler(NotFoundException.class) - public ResponseEntity notFoundExHandler(NotFoundException e){ - ErrorResultDTO errorResult = new ErrorResultDTO("NOT_FOUND", e.getMessage()); - return new ResponseEntity<>(errorResult, HttpStatus.NOT_FOUND); - } - - @ExceptionHandler(BadRequestException.class) - public ResponseEntity badRequestException(BadRequestException e) { - ErrorResultDTO errorResult = new ErrorResultDTO("BAD_REQUEST", e.getMessage()); - return new ResponseEntity<>(errorResult, HttpStatus.BAD_REQUEST); - } - - @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) - @ExceptionHandler - public ResponseEntity baseException(Exception e){ - ErrorResultDTO errorResult = new ErrorResultDTO("INTERNAL_SERVER_ERROR", e.getMessage()); - return new ResponseEntity<>(errorResult, HttpStatus.INTERNAL_SERVER_ERROR); - } -} diff --git a/src/main/java/kr/tgwing/tech/project/controller/ProjectController.java b/src/main/java/kr/tgwing/tech/project/controller/ProjectController.java index 5a29f9f..32dbb0f 100644 --- a/src/main/java/kr/tgwing/tech/project/controller/ProjectController.java +++ b/src/main/java/kr/tgwing/tech/project/controller/ProjectController.java @@ -1,12 +1,13 @@ package kr.tgwing.tech.project.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; +import kr.tgwing.tech.common.ApiResponse; import kr.tgwing.tech.project.dto.ProjectBriefDTO; import kr.tgwing.tech.project.dto.ProjectCreateDTO; import kr.tgwing.tech.project.dto.ProjectDetailDTO; import kr.tgwing.tech.project.dto.ProjectUpdateDTO; -import kr.tgwing.tech.project.exception.BadRequestException; -import kr.tgwing.tech.project.exception.NotFoundException; import kr.tgwing.tech.project.service.ProjectServiceImpl; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -16,50 +17,49 @@ import java.util.List; +@Tag(name = "프로젝트") @RestController @RequiredArgsConstructor +@RequestMapping("/project") @Slf4j public class ProjectController { private final ProjectServiceImpl projectServiceImpl; - @GetMapping("/projects") + @Operation(summary = "프로젝트 전체 조회") + @GetMapping("") public ResponseEntity getProjects(){ List projects = projectServiceImpl.getProjects(); - return ResponseEntity.ok(projects); + return ResponseEntity.ok(ApiResponse.ok(projects)); } - @GetMapping("/projects/{project_id}") - public ResponseEntity getOneProject(@PathVariable("project_id") Long project_id) throws NotFoundException{ + @Operation(summary = "프로젝트 상세 조회") + @GetMapping("/{project_id}") + public ResponseEntity getOneProject(@PathVariable("project_id") Long project_id){ ProjectDetailDTO project = projectServiceImpl.getOneProject(project_id); - if(project == null){ - throw new NotFoundException("개별 프로젝트 가져오기 - 찾을 수 없음"); - } - return ResponseEntity.ok(project); - } + return ResponseEntity.ok(ApiResponse.ok(project)); + } - @PostMapping("/projects") - public ResponseEntity postProject(@Valid @RequestBody ProjectCreateDTO projectCreateDTO, BindingResult bindingResult) throws BadRequestException { - if(bindingResult.hasErrors()){ - throw new BadRequestException("프로젝트 생성 - 잘못된 정보 입력"); - } + @Operation(summary = "프로젝트 생성") + @PostMapping("") + public ResponseEntity postProject( + @Valid @RequestBody ProjectCreateDTO projectCreateDTO) { Long projectId = projectServiceImpl.createProjects(projectCreateDTO); - return ResponseEntity.ok(projectId); + return ResponseEntity.ok(ApiResponse.created(projectId)); } - - @PutMapping("/projects/{project_id}") - public ResponseEntity updateProject(@PathVariable("project_id") Long project_id, @Valid @RequestBody ProjectUpdateDTO projectUpdateDTO, BindingResult bindingResult) throws NotFoundException, BadRequestException{ - if(bindingResult.hasErrors()){ - throw new BadRequestException("프로젝트 수정 - 잘못된 정보 입력"); - } + @Operation(summary = "프로젝트 수정") + @PutMapping("/{project_id}") + public ResponseEntity updateProject( + @PathVariable("project_id") Long project_id, + @Valid @RequestBody ProjectUpdateDTO projectUpdateDTO){ Long projectId = projectServiceImpl.updateProject(projectUpdateDTO, project_id); - return ResponseEntity.ok(projectId); + return ResponseEntity.ok(ApiResponse.updated(projectId)); } - - @DeleteMapping("/projects/{project_id}") + @Operation(summary = "프로젝트 삭제") + @DeleteMapping("/{project_id}") public ResponseEntity deleteProject(@PathVariable("project_id") Long project_id){ projectServiceImpl.deleteProject(project_id); - return ResponseEntity.ok("delete ok"); + return ResponseEntity.ok(ApiResponse.delete(project_id)); } diff --git a/src/main/java/kr/tgwing/tech/project/domain/Enum/DevRole.java b/src/main/java/kr/tgwing/tech/project/domain/Enum/Part.java similarity index 80% rename from src/main/java/kr/tgwing/tech/project/domain/Enum/DevRole.java rename to src/main/java/kr/tgwing/tech/project/domain/Enum/Part.java index e5c7dd4..6aea090 100644 --- a/src/main/java/kr/tgwing/tech/project/domain/Enum/DevRole.java +++ b/src/main/java/kr/tgwing/tech/project/domain/Enum/Part.java @@ -1,6 +1,6 @@ package kr.tgwing.tech.project.domain.Enum; -public enum DevRole { +public enum Part { LEADER("LEADER"), PM("PM"), FRONT("FRONT"), @@ -9,7 +9,7 @@ public enum DevRole { private String value; - DevRole(String value) { + Part(String value) { this.value = value; } } diff --git a/src/main/java/kr/tgwing/tech/project/domain/LinkEntity.java b/src/main/java/kr/tgwing/tech/project/domain/Link.java similarity index 61% rename from src/main/java/kr/tgwing/tech/project/domain/LinkEntity.java rename to src/main/java/kr/tgwing/tech/project/domain/Link.java index ce50336..2ba034f 100644 --- a/src/main/java/kr/tgwing/tech/project/domain/LinkEntity.java +++ b/src/main/java/kr/tgwing/tech/project/domain/Link.java @@ -3,6 +3,7 @@ import jakarta.persistence.*; import kr.tgwing.tech.common.BaseEntity; +import kr.tgwing.tech.project.dto.LinkDTO; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -11,12 +12,11 @@ @Getter -@Builder @DynamicInsert @NoArgsConstructor @Table(name= "link") @Entity -public class LinkEntity extends BaseEntity { +public class Link extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name="link_id") @@ -24,23 +24,31 @@ public class LinkEntity extends BaseEntity { @ManyToOne @JoinColumn(name="project_id") - private ProjectEntity project; + private Project project; + @Column(name = "link_url") private String url; private String description; - public void setProject(ProjectEntity project) { + public void setProject(Project project) { this.project = project; } @Builder - public LinkEntity(Long id, ProjectEntity project, String url, String description) { + public Link(Long id, Project project, String url, String description) { this.id = id; this.project = project; this.url = url; this.description = description; } + public static LinkDTO toDTO(Link link){ + return LinkDTO.builder() + .description(link.getDescription()) + .url(link.getUrl()) + .build(); + } + } diff --git a/src/main/java/kr/tgwing/tech/project/domain/ParticipantEntity.java b/src/main/java/kr/tgwing/tech/project/domain/Participant.java similarity index 51% rename from src/main/java/kr/tgwing/tech/project/domain/ParticipantEntity.java rename to src/main/java/kr/tgwing/tech/project/domain/Participant.java index a2ecd05..88abde8 100644 --- a/src/main/java/kr/tgwing/tech/project/domain/ParticipantEntity.java +++ b/src/main/java/kr/tgwing/tech/project/domain/Participant.java @@ -2,19 +2,21 @@ import jakarta.persistence.*; import kr.tgwing.tech.common.BaseEntity; -import kr.tgwing.tech.project.domain.Enum.DevRole; +import kr.tgwing.tech.project.domain.Enum.Part; +import kr.tgwing.tech.project.dto.ParticipantDTO; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import org.hibernate.annotations.DynamicInsert; +import java.util.List; + @Entity @Getter -@Builder @DynamicInsert @NoArgsConstructor @Table(name= "participant") -public class ParticipantEntity extends BaseEntity { +public class Participant extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name="participant_id") @@ -22,26 +24,34 @@ public class ParticipantEntity extends BaseEntity { @ManyToOne @JoinColumn(name="project_id") - private ProjectEntity project; + private Project project; @Enumerated(EnumType.STRING) - private DevRole devRole; + private Part part; - private String username; + private String name; private String major; // project create 할 떄 한번 썻읍니다..허허 - public void setProject(ProjectEntity project) { + public void setProject(Project project) { this.project = project; } + public static ParticipantDTO toDTO(Participant participant) { + return ParticipantDTO.builder() + .username(participant.getName()) + .major(participant.getMajor()) + .part(participant.getPart()) + .build(); + } + @Builder - public ParticipantEntity(Long id, ProjectEntity project, DevRole devRole, String username, String major) { + public Participant(Long id, Project project, Part part, String name, String major) { this.id = id; this.project = project; - this.devRole = devRole; - this.username = username; + this.part = part; + this.name = name; this.major = major; } } diff --git a/src/main/java/kr/tgwing/tech/project/domain/Project.java b/src/main/java/kr/tgwing/tech/project/domain/Project.java new file mode 100644 index 0000000..8b3edf2 --- /dev/null +++ b/src/main/java/kr/tgwing/tech/project/domain/Project.java @@ -0,0 +1,94 @@ +package kr.tgwing.tech.project.domain; + +import jakarta.persistence.*; +import kr.tgwing.tech.common.BaseEntity; +import kr.tgwing.tech.project.dto.LinkDTO; +import kr.tgwing.tech.project.dto.ParticipantDTO; +import kr.tgwing.tech.project.dto.ProjectUpdateDTO; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.DynamicInsert; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@Entity +@Getter +@DynamicInsert +@NoArgsConstructor +@Table(name= "project") +public class Project extends BaseEntity { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name="project_id") + private Long id; + + private String title; + private String description; + @DateTimeFormat(pattern = "yyyy-MM-dd") + @Column(name = "start_date") + private LocalDate startDate; + + @DateTimeFormat(pattern = "yyyy-MM-dd") + @Column(name = "end_date") + private LocalDate endDate; + + @Column(name = "thumbnail_url") + private String thumbnail; + + @Column(name = "dev_status") + private String devStatus; + + @Column(name = "dev_type") + private String devType; + + @OneToMany(mappedBy = "project", fetch = FetchType.LAZY, cascade = CascadeType.ALL) + private List participants = new ArrayList<>(); + + @OneToMany(mappedBy = "project", fetch = FetchType.LAZY, cascade = CascadeType.ALL) + private List links = new ArrayList<>(); + + @Builder + public Project(Long id, String title, String description, LocalDate start, LocalDate end, String thumbnail, String devStatus, String devType, List participants, List links) { + this.id = id; + this.title = title; + this.description = description; + this.startDate = start; + this.endDate = end; + this.thumbnail = thumbnail; + this.devStatus = devStatus; + this.devType = devType; + this.participants = participants; + this.links = links; + } + + public void setParticipants(List participants) { + List update = participants.stream() + .map(ParticipantDTO::toParticipantEntity) + .toList(); + update.stream().forEach( + participant -> participant.setProject(Project.this) + ); + this.participants = update; + } + + public void setLinks(List links) { + List update = links.stream() + .map(LinkDTO::toLinkEntity) + .toList(); + this.links = update; + } + public void updateProject(ProjectUpdateDTO projectUpdateDTO) { + this.title = projectUpdateDTO.getTitle(); + this.description = projectUpdateDTO.getDescription(); + this.startDate = projectUpdateDTO.getStart(); + this.endDate = projectUpdateDTO.getEnd(); + this.thumbnail = projectUpdateDTO.getThumbnail(); + this.devStatus = projectUpdateDTO.getDevStatus(); + this.devType = projectUpdateDTO.getDevType(); + } + +} diff --git a/src/main/java/kr/tgwing/tech/project/domain/ProjectEntity.java b/src/main/java/kr/tgwing/tech/project/domain/ProjectEntity.java deleted file mode 100644 index c2fe384..0000000 --- a/src/main/java/kr/tgwing/tech/project/domain/ProjectEntity.java +++ /dev/null @@ -1,52 +0,0 @@ -package kr.tgwing.tech.project.domain; - -import jakarta.persistence.*; -import kr.tgwing.tech.common.BaseEntity; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.hibernate.annotations.DynamicInsert; - -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; - -@Entity -@Getter -@Builder -@DynamicInsert -@NoArgsConstructor -@Table(name= "project") -public class ProjectEntity extends BaseEntity { - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name="project_id") - private Long id; - - private String title; - private String description; - private LocalDateTime start; - private LocalDateTime end; - private String thumbnail; - private String devStatus; - private String devType; - - @OneToMany(mappedBy = "project", fetch = FetchType.LAZY) - private List participants = new ArrayList<>(); - - @OneToMany(mappedBy = "project", fetch = FetchType.LAZY) - private List links = new ArrayList<>(); - - @Builder - public ProjectEntity(Long id, String title, String description, LocalDateTime start, LocalDateTime end, String thumbnail, String devStatus, String devType, List participants, List links) { - this.id = id; - this.title = title; - this.description = description; - this.start = start; - this.end = end; - this.thumbnail = thumbnail; - this.devStatus = devStatus; - this.devType = devType; - this.participants = participants; - this.links = links; - } -} diff --git a/src/main/java/kr/tgwing/tech/project/dto/ErrorResultDTO.java b/src/main/java/kr/tgwing/tech/project/dto/ErrorResultDTO.java deleted file mode 100644 index a946eb0..0000000 --- a/src/main/java/kr/tgwing/tech/project/dto/ErrorResultDTO.java +++ /dev/null @@ -1,11 +0,0 @@ -package kr.tgwing.tech.project.dto; - -import lombok.AllArgsConstructor; -import lombok.Data; - -@Data -@AllArgsConstructor -public class ErrorResultDTO { - private String code; - private String message; -} \ No newline at end of file diff --git a/src/main/java/kr/tgwing/tech/project/dto/LinkDTO.java b/src/main/java/kr/tgwing/tech/project/dto/LinkDTO.java index 462d2c5..cbf1d2c 100644 --- a/src/main/java/kr/tgwing/tech/project/dto/LinkDTO.java +++ b/src/main/java/kr/tgwing/tech/project/dto/LinkDTO.java @@ -1,7 +1,7 @@ package kr.tgwing.tech.project.dto; -import kr.tgwing.tech.project.domain.LinkEntity; -import kr.tgwing.tech.project.domain.ProjectEntity; +import kr.tgwing.tech.project.domain.Link; +import kr.tgwing.tech.project.domain.Project; import lombok.AccessLevel; import lombok.Builder; import lombok.Data; @@ -12,20 +12,17 @@ public class LinkDTO { private String url; private String description; - private ProjectEntity project; @Builder - public LinkDTO(String url, String description, ProjectEntity project) { + public LinkDTO(String url, String description) { this.url = url; this.description = description; - this.project = project; } - public static LinkEntity toLinkEntity(LinkDTO linkDTO){ - return LinkEntity.builder() + public static Link toLinkEntity(LinkDTO linkDTO){ + return Link.builder() .url(linkDTO.getUrl()) .description(linkDTO.getDescription()) - .project(linkDTO.getProject()) .build(); } } diff --git a/src/main/java/kr/tgwing/tech/project/dto/ParticipantDTO.java b/src/main/java/kr/tgwing/tech/project/dto/ParticipantDTO.java index fd60567..d884931 100644 --- a/src/main/java/kr/tgwing/tech/project/dto/ParticipantDTO.java +++ b/src/main/java/kr/tgwing/tech/project/dto/ParticipantDTO.java @@ -1,8 +1,7 @@ package kr.tgwing.tech.project.dto; -import kr.tgwing.tech.project.domain.Enum.DevRole; -import kr.tgwing.tech.project.domain.ParticipantEntity; -import kr.tgwing.tech.project.domain.ProjectEntity; +import kr.tgwing.tech.project.domain.Enum.Part; +import kr.tgwing.tech.project.domain.Participant; import lombok.AccessLevel; import lombok.Builder; import lombok.Data; @@ -11,25 +10,22 @@ @Data @NoArgsConstructor(access = AccessLevel.PROTECTED) public class ParticipantDTO { - private DevRole devRole; + private Part part; private String username; private String major; - private ProjectEntity project; @Builder - public ParticipantDTO(DevRole devRole, String username, String studentId, String major, ProjectEntity project) { - this.devRole = devRole; + public ParticipantDTO(Part part, String username, String major) { + this.part = part; this.username = username; this.major = major; - this.project = project; } - public static ParticipantEntity toParticipantEntity(ParticipantDTO participantDTO){ - return ParticipantEntity.builder() - .devRole(participantDTO.getDevRole()) - .username(participantDTO.getUsername()) + public static Participant toParticipantEntity(ParticipantDTO participantDTO){ + return Participant.builder() + .part(participantDTO.getPart()) + .name(participantDTO.getUsername()) .major(participantDTO.getMajor()) - .project(participantDTO.getProject()) .build(); } diff --git a/src/main/java/kr/tgwing/tech/project/dto/ProjectBriefDTO.java b/src/main/java/kr/tgwing/tech/project/dto/ProjectBriefDTO.java index ba05137..f5ae112 100644 --- a/src/main/java/kr/tgwing/tech/project/dto/ProjectBriefDTO.java +++ b/src/main/java/kr/tgwing/tech/project/dto/ProjectBriefDTO.java @@ -5,21 +5,23 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.time.LocalDate; import java.time.LocalDateTime; @Data @NoArgsConstructor(access = AccessLevel.PROTECTED) public class ProjectBriefDTO { - + private Long id; private String title; - private LocalDateTime start; - private LocalDateTime end; + private LocalDate start; + private LocalDate end; private String thumbnail; private String devStatus; private String devType; @Builder - public ProjectBriefDTO(String title, LocalDateTime start, LocalDateTime end, String thumbnail, String devStatus, String devType) { + public ProjectBriefDTO(Long id, String title, LocalDate start, LocalDate end, String thumbnail, String devStatus, String devType) { + this.id = id; this.title = title; this.start = start; this.end = end; diff --git a/src/main/java/kr/tgwing/tech/project/dto/ProjectCreateDTO.java b/src/main/java/kr/tgwing/tech/project/dto/ProjectCreateDTO.java index f978a3e..60c2d52 100644 --- a/src/main/java/kr/tgwing/tech/project/dto/ProjectCreateDTO.java +++ b/src/main/java/kr/tgwing/tech/project/dto/ProjectCreateDTO.java @@ -1,11 +1,12 @@ package kr.tgwing.tech.project.dto; -import kr.tgwing.tech.project.domain.ProjectEntity; +import kr.tgwing.tech.project.domain.Project; import lombok.AccessLevel; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -15,8 +16,8 @@ public class ProjectCreateDTO { private String title; private String description; - private LocalDateTime start; - private LocalDateTime end; + private LocalDate start; + private LocalDate end; private String thumbnail; private String devStatus; private String devType; @@ -24,7 +25,7 @@ public class ProjectCreateDTO { private List links = new ArrayList<>(); @Builder - public ProjectCreateDTO(String title, String description, LocalDateTime start, LocalDateTime end, String thumbnail, String devStatus, String devType, List participants, List links) { + public ProjectCreateDTO(String title, String description, LocalDate start, LocalDate end, String thumbnail, String devStatus, String devType, List participants, List links) { this.title = title; this.description = description; this.start = start; @@ -36,9 +37,9 @@ public ProjectCreateDTO(String title, String description, LocalDateTime start, L this.participants = participants; } - public static ProjectEntity toEntity(ProjectCreateDTO projectCreateDTO){ + public Project toEntity(ProjectCreateDTO projectCreateDTO){ - return ProjectEntity.builder() + return Project.builder() .title(projectCreateDTO.getTitle()) .description(projectCreateDTO.getDescription()) .start(projectCreateDTO.getStart()) diff --git a/src/main/java/kr/tgwing/tech/project/dto/ProjectDetailDTO.java b/src/main/java/kr/tgwing/tech/project/dto/ProjectDetailDTO.java index 1824a23..72858c3 100644 --- a/src/main/java/kr/tgwing/tech/project/dto/ProjectDetailDTO.java +++ b/src/main/java/kr/tgwing/tech/project/dto/ProjectDetailDTO.java @@ -1,13 +1,14 @@ package kr.tgwing.tech.project.dto; -import kr.tgwing.tech.project.domain.LinkEntity; -import kr.tgwing.tech.project.domain.ParticipantEntity; -import kr.tgwing.tech.project.domain.ProjectEntity; +import kr.tgwing.tech.project.domain.Link; +import kr.tgwing.tech.project.domain.Participant; +import kr.tgwing.tech.project.domain.Project; import lombok.AccessLevel; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -18,19 +19,19 @@ public class ProjectDetailDTO { private Long id; private String title; private String description; - private LocalDateTime start; - private LocalDateTime end; + private LocalDate start; + private LocalDate end; private String thumbnail; private String devStatus; private String devType; // 참여인원에 대해 ParticipateEntity; - private List participants = new ArrayList<>(); + private List participants = new ArrayList<>(); // link - private List links = new ArrayList<>(); + private List links = new ArrayList<>(); @Builder - public ProjectDetailDTO(Long id, String title, String description, LocalDateTime start, LocalDateTime end, String thumbnail, String devStatus, String devType, List participants, List links) { + public ProjectDetailDTO(Long id, String title, String description, LocalDate start, LocalDate end, String thumbnail, String devStatus, String devType, List participants, List links) { this.id = id; this.title = title; this.description = description; @@ -43,8 +44,8 @@ public ProjectDetailDTO(Long id, String title, String description, LocalDateTime this.links = links; } - public static ProjectEntity toEntity(ProjectDetailDTO projectDetailDTO){ - return ProjectEntity.builder() + public static Project toEntity(ProjectDetailDTO projectDetailDTO){ + return Project.builder() .id(projectDetailDTO.getId()) .title(projectDetailDTO.getTitle()) .description(projectDetailDTO.getDescription()) @@ -52,8 +53,12 @@ public static ProjectEntity toEntity(ProjectDetailDTO projectDetailDTO){ .end(projectDetailDTO.getEnd()) .devStatus(projectDetailDTO.getDevStatus()) .devType(projectDetailDTO.getDevType()) - .participants(projectDetailDTO.getParticipants()) - .links(projectDetailDTO.getLinks()) + .participants(projectDetailDTO.getParticipants().stream() + .map(ParticipantDTO::toParticipantEntity) + .toList()) + .links(projectDetailDTO.getLinks().stream() + .map(LinkDTO::toLinkEntity) + .toList()) .build(); } } diff --git a/src/main/java/kr/tgwing/tech/project/dto/ProjectUpdateDTO.java b/src/main/java/kr/tgwing/tech/project/dto/ProjectUpdateDTO.java index 2f6ee58..e9d6474 100644 --- a/src/main/java/kr/tgwing/tech/project/dto/ProjectUpdateDTO.java +++ b/src/main/java/kr/tgwing/tech/project/dto/ProjectUpdateDTO.java @@ -1,13 +1,14 @@ package kr.tgwing.tech.project.dto; -import kr.tgwing.tech.project.domain.LinkEntity; -import kr.tgwing.tech.project.domain.ParticipantEntity; -import kr.tgwing.tech.project.domain.ProjectEntity; +import kr.tgwing.tech.project.domain.Link; +import kr.tgwing.tech.project.domain.Participant; +import kr.tgwing.tech.project.domain.Project; import lombok.AccessLevel; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -17,16 +18,16 @@ public class ProjectUpdateDTO { private String title; private String description; - private LocalDateTime start; - private LocalDateTime end; + private LocalDate start; + private LocalDate end; private String thumbnail; private String devStatus; private String devType; - private List participants = new ArrayList<>(); - private List links = new ArrayList<>(); + private List participants = new ArrayList<>(); + private List links = new ArrayList<>(); @Builder - public ProjectUpdateDTO(String title, String description, LocalDateTime start, LocalDateTime end, String thumbnail, String devStatus, String devType, List participants, List links) { + public ProjectUpdateDTO(String title, String description, LocalDate start, LocalDate end, String thumbnail, String devStatus, String devType, List participants, List links) { this.title = title; this.description = description; this.start = start; @@ -38,16 +39,18 @@ public ProjectUpdateDTO(String title, String description, LocalDateTime start, L this.links = links; } - public static ProjectEntity toEntity(ProjectUpdateDTO projectUpdateDTO){ - return ProjectEntity.builder() + public static Project toEntity(ProjectUpdateDTO projectUpdateDTO){ + return Project.builder() .title(projectUpdateDTO.getTitle()) .description(projectUpdateDTO.getDescription()) .start(projectUpdateDTO.getStart()) .end(projectUpdateDTO.getEnd()) .devStatus(projectUpdateDTO.getDevStatus()) .devType(projectUpdateDTO.getDevType()) - .participants(projectUpdateDTO.getParticipants()) - .links(projectUpdateDTO.getLinks()) + .participants(projectUpdateDTO.getParticipants().stream() + .map(ParticipantDTO::toParticipantEntity).toList()) + .links(projectUpdateDTO.getLinks().stream() + .map(LinkDTO::toLinkEntity).toList()) .build(); } } diff --git a/src/main/java/kr/tgwing/tech/project/exception/BadRequestException.java b/src/main/java/kr/tgwing/tech/project/exception/BadRequestException.java deleted file mode 100644 index 563516e..0000000 --- a/src/main/java/kr/tgwing/tech/project/exception/BadRequestException.java +++ /dev/null @@ -1,29 +0,0 @@ -package kr.tgwing.tech.project.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(code= HttpStatus.BAD_REQUEST) -public class BadRequestException extends RuntimeException{ - - public BadRequestException() { - super(); - } - - public BadRequestException(String message) { - super(message); - } - - public BadRequestException(String message, Throwable cause) { - super(message, cause); - } - - public BadRequestException(Throwable cause) { - super(cause); - } - - protected BadRequestException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } - -} \ No newline at end of file diff --git a/src/main/java/kr/tgwing/tech/project/exception/NotFoundException.java b/src/main/java/kr/tgwing/tech/project/exception/NotFoundException.java deleted file mode 100644 index 1d73645..0000000 --- a/src/main/java/kr/tgwing/tech/project/exception/NotFoundException.java +++ /dev/null @@ -1,27 +0,0 @@ -package kr.tgwing.tech.project.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(HttpStatus.NOT_FOUND) -public class NotFoundException extends RuntimeException{ - public NotFoundException() { - super(); - } - - public NotFoundException(String message) { - super(message); - } - - public NotFoundException(String message, Throwable cause) { - super(message, cause); - } - - public NotFoundException(Throwable cause) { - super(cause); - } - - protected NotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } -} \ No newline at end of file diff --git a/src/main/java/kr/tgwing/tech/project/exception/ProjectNotFoundException.java b/src/main/java/kr/tgwing/tech/project/exception/ProjectNotFoundException.java new file mode 100644 index 0000000..96c9f97 --- /dev/null +++ b/src/main/java/kr/tgwing/tech/project/exception/ProjectNotFoundException.java @@ -0,0 +1,6 @@ +package kr.tgwing.tech.project.exception; + +import kr.tgwing.tech.common.exception.CommonException; + +public class ProjectNotFoundException extends CommonException { +} diff --git a/src/main/java/kr/tgwing/tech/project/repository/LinkRepository.java b/src/main/java/kr/tgwing/tech/project/repository/LinkRepository.java new file mode 100644 index 0000000..0c79bdf --- /dev/null +++ b/src/main/java/kr/tgwing/tech/project/repository/LinkRepository.java @@ -0,0 +1,17 @@ +package kr.tgwing.tech.project.repository; + +import kr.tgwing.tech.project.domain.Link; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; + +public interface LinkRepository extends JpaRepository { + List findAllByProjectId(Long projectId); + + @Modifying + @Query("DELETE FROM Link l WHERE l.project IS NULL") + void deleteAllIfProjectIdIsNull(); + +} diff --git a/src/main/java/kr/tgwing/tech/project/repository/ParticipantRepository.java b/src/main/java/kr/tgwing/tech/project/repository/ParticipantRepository.java new file mode 100644 index 0000000..762cd16 --- /dev/null +++ b/src/main/java/kr/tgwing/tech/project/repository/ParticipantRepository.java @@ -0,0 +1,17 @@ +package kr.tgwing.tech.project.repository; + +import kr.tgwing.tech.project.domain.Participant; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; +import java.util.Optional; + +public interface ParticipantRepository extends JpaRepository { + List findAllByProjectId(Long projectId); + + @Modifying + @Query("DELETE FROM Participant p WHERE p.project IS NULL") + void deleteAllIfProjectIdIsNull(); +} diff --git a/src/main/java/kr/tgwing/tech/project/respository/project/ProjectRepository.java b/src/main/java/kr/tgwing/tech/project/repository/ProjectRepository.java similarity index 61% rename from src/main/java/kr/tgwing/tech/project/respository/project/ProjectRepository.java rename to src/main/java/kr/tgwing/tech/project/repository/ProjectRepository.java index 86b77af..7cd95bf 100644 --- a/src/main/java/kr/tgwing/tech/project/respository/project/ProjectRepository.java +++ b/src/main/java/kr/tgwing/tech/project/repository/ProjectRepository.java @@ -1,9 +1,9 @@ -package kr.tgwing.tech.project.respository.project; +package kr.tgwing.tech.project.repository; -import kr.tgwing.tech.project.domain.ProjectEntity; +import kr.tgwing.tech.project.domain.Project; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository -public interface ProjectRepository extends JpaRepository { +public interface ProjectRepository extends JpaRepository { } diff --git a/src/main/java/kr/tgwing/tech/project/respository/link/LinkRepository.java b/src/main/java/kr/tgwing/tech/project/respository/link/LinkRepository.java deleted file mode 100644 index 616be24..0000000 --- a/src/main/java/kr/tgwing/tech/project/respository/link/LinkRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package kr.tgwing.tech.project.respository.link; - -import kr.tgwing.tech.project.domain.LinkEntity; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface LinkRepository extends JpaRepository { -} diff --git a/src/main/java/kr/tgwing/tech/project/respository/participant/ParticipantRepository.java b/src/main/java/kr/tgwing/tech/project/respository/participant/ParticipantRepository.java deleted file mode 100644 index d20dbc9..0000000 --- a/src/main/java/kr/tgwing/tech/project/respository/participant/ParticipantRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package kr.tgwing.tech.project.respository.participant; - -import kr.tgwing.tech.project.domain.ParticipantEntity; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface ParticipantRepository extends JpaRepository { -} diff --git a/src/main/java/kr/tgwing/tech/project/respository/project/CustomProjectRepository.java b/src/main/java/kr/tgwing/tech/project/respository/project/CustomProjectRepository.java deleted file mode 100644 index 5b85cc5..0000000 --- a/src/main/java/kr/tgwing/tech/project/respository/project/CustomProjectRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package kr.tgwing.tech.project.respository.project; - -import jakarta.persistence.EntityManager; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Repository; - -@Repository -@Slf4j -@RequiredArgsConstructor -public class CustomProjectRepository { - private final EntityManager em; -} diff --git a/src/main/java/kr/tgwing/tech/project/service/ProjectService.java b/src/main/java/kr/tgwing/tech/project/service/ProjectService.java deleted file mode 100644 index 84feb9b..0000000 --- a/src/main/java/kr/tgwing/tech/project/service/ProjectService.java +++ /dev/null @@ -1,21 +0,0 @@ -package kr.tgwing.tech.project.service; - -import kr.tgwing.tech.project.dto.ProjectBriefDTO; -import kr.tgwing.tech.project.dto.ProjectCreateDTO; -import kr.tgwing.tech.project.dto.ProjectDetailDTO; -import kr.tgwing.tech.project.dto.ProjectUpdateDTO; - -import java.util.List; - -public interface ProjectService { - - List getProjects(); - - Long createProjects(ProjectCreateDTO projectCreateDTO); - - ProjectDetailDTO getOneProject(Long project_id); - - Long updateProject(ProjectUpdateDTO projectUpdateDTO, Long project_id); - - void deleteProject(Long project_id); -} diff --git a/src/main/java/kr/tgwing/tech/project/service/ProjectServiceImpl.java b/src/main/java/kr/tgwing/tech/project/service/ProjectServiceImpl.java index e81d8c0..cf617a0 100644 --- a/src/main/java/kr/tgwing/tech/project/service/ProjectServiceImpl.java +++ b/src/main/java/kr/tgwing/tech/project/service/ProjectServiceImpl.java @@ -1,15 +1,17 @@ package kr.tgwing.tech.project.service; -import kr.tgwing.tech.project.domain.LinkEntity; -import kr.tgwing.tech.project.domain.ParticipantEntity; -import kr.tgwing.tech.project.domain.ProjectEntity; +import kr.tgwing.tech.project.domain.Link; +import kr.tgwing.tech.project.domain.Participant; +import kr.tgwing.tech.project.domain.Project; import kr.tgwing.tech.project.dto.*; -import kr.tgwing.tech.project.respository.participant.ParticipantRepository; -import kr.tgwing.tech.project.respository.link.LinkRepository; -import kr.tgwing.tech.project.respository.project.ProjectRepository; +import kr.tgwing.tech.project.exception.ProjectNotFoundException; +import kr.tgwing.tech.project.repository.ParticipantRepository; +import kr.tgwing.tech.project.repository.LinkRepository; +import kr.tgwing.tech.project.repository.ProjectRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.Optional; @@ -17,107 +19,136 @@ @Service @RequiredArgsConstructor @Slf4j -public class ProjectServiceImpl implements ProjectService{ +public class ProjectServiceImpl{ private final ProjectRepository projectRepository; private final ParticipantRepository participantRepository; private final LinkRepository linkRepository; - @Override public List getProjects() { - List projects = projectRepository.findAll(); + List projects = projectRepository.findAll(); if(projects == null){ return null; - }else{ + } else { return projects.stream() .map(ProjectServiceImpl::entity2ProjectBriefDTO) .toList(); } } - @Override public Long createProjects(ProjectCreateDTO projectCreateDTO) { - List participantList = projectCreateDTO.getParticipants().stream() + Project project = projectCreateDTO.toEntity(projectCreateDTO); + project.getParticipants().stream().forEach( + participant -> participant.setProject(project) + ); + project.getLinks().stream().forEach( + link -> link.setProject(project) + ); + + Project saveProject = projectRepository.save(project); + + return saveProject.getId(); + } + + @Transactional + public ProjectDetailDTO getOneProject(Long project_id) { + Project project = projectRepository.findById(project_id) + .orElseThrow(ProjectNotFoundException::new); + + return ProjectServiceImpl.entity2ProjectDetailDTO(project); + } + + @Transactional + public Long updateProject(ProjectUpdateDTO projectUpdateDTO, Long project_id) { + Project findProject = projectRepository.findById(project_id) + .orElseThrow(ProjectNotFoundException::new); + List findParticipants = participantRepository.findAllByProjectId(project_id); + List findLinks = linkRepository.findAllByProjectId(project_id); + + List participants = projectUpdateDTO.getParticipants().stream() .map(ParticipantDTO::toParticipantEntity) .toList(); - for(ParticipantEntity participant: participantList){ - participantRepository.save(participant); - } + updateParticipants(findProject, findParticipants, participants); - List linkList = projectCreateDTO.getLinks().stream() + List links = projectUpdateDTO.getLinks().stream() .map(LinkDTO::toLinkEntity) .toList(); - for(LinkEntity link: linkList){ - linkRepository.save(link); - } + updateLinks(findProject, findLinks, links); - ProjectEntity saveProject = projectRepository.save(ProjectCreateDTO.toEntity(projectCreateDTO)); + findProject.updateProject(projectUpdateDTO); - for(ParticipantEntity participant: participantList){ - participant.setProject(saveProject); - participantRepository.save(participant); - } - for(LinkEntity link: linkList){ - link.setProject(saveProject); - linkRepository.save(link); - } - return saveProject.getId(); + return findProject.getId(); } - @Override - public ProjectDetailDTO getOneProject(Long project_id) { - Optional findProject = projectRepository.findById(project_id); - if(findProject.isPresent()){ - ProjectEntity project = findProject.get(); - return ProjectServiceImpl.entity2ProjectDetailDTO(project); - } - return null; + public void deleteProject(Long project_id) { + Project findProject = projectRepository.findById(project_id) + .orElseThrow(ProjectNotFoundException::new); + linkRepository.deleteAllIfProjectIdIsNull(); + participantRepository.deleteAllIfProjectIdIsNull(); + projectRepository.deleteById(project_id); } - @Override - public Long updateProject(ProjectUpdateDTO projectUpdateDTO, Long project_id) { - Optional findProject = projectRepository.findById(project_id); - if (findProject.isPresent()){ - ProjectEntity project = findProject.get(); - projectRepository.save(project); - return project.getId(); + //-------------------------------------- + + @Transactional + public void updateParticipants(Project project, List findParticipants, List newParticipants) { + for (Participant participant : findParticipants) { + if (!newParticipants.contains(participant)) { + participantRepository.delete(participant); + } + } + + for (Participant newParticipant : newParticipants) { + if (!findParticipants.contains(newParticipant)) { + newParticipant.setProject(project); + participantRepository.save(newParticipant); + } } - return null; } - @Override - public void deleteProject(Long project_id) { - Optional findProject = projectRepository.findById(project_id); - if (findProject.isPresent()){ - ProjectEntity project = findProject.get(); - projectRepository.delete(project); + @Transactional + public void updateLinks(Project project, List findLinks, List newLinks) { + for (Link link : findLinks) { + if (!newLinks.contains(link)) { + linkRepository.delete(link); + } + } + + for (Link newLink : newLinks) { + if (!findLinks.contains(newLink)) { + newLink.setProject(project); + linkRepository.save(newLink); + } } } - //-------------------------------------- - public static ProjectBriefDTO entity2ProjectBriefDTO(ProjectEntity projectEntity){ + public static ProjectBriefDTO entity2ProjectBriefDTO(Project project){ return ProjectBriefDTO.builder() - .title(projectEntity.getTitle()) - .start(projectEntity.getStart()) - .end(projectEntity.getEnd()) - .thumbnail(projectEntity.getThumbnail()) - .devStatus(projectEntity.getDevStatus()) - .devType(projectEntity.getDevType()) + .id(project.getId()) + .title(project.getTitle()) + .start(project.getStartDate()) + .end(project.getEndDate()) + .thumbnail(project.getThumbnail()) + .devStatus(project.getDevStatus()) + .devType(project.getDevType()) .build(); } - public static ProjectDetailDTO entity2ProjectDetailDTO(ProjectEntity projectEntity){ + public static ProjectDetailDTO entity2ProjectDetailDTO(Project project){ return ProjectDetailDTO.builder() - .title(projectEntity.getTitle()) - .description(projectEntity.getDescription()) - .start(projectEntity.getStart()) - .end(projectEntity.getEnd()) - .thumbnail(projectEntity.getThumbnail()) - .devStatus(projectEntity.getDevStatus()) - .devType(projectEntity.getDevType()) - .participants(projectEntity.getParticipants()) - .links(projectEntity.getLinks()) + .id(project.getId()) + .title(project.getTitle()) + .description(project.getDescription()) + .start(project.getStartDate()) + .end(project.getEndDate()) + .thumbnail(project.getThumbnail()) + .devStatus(project.getDevStatus()) + .devType(project.getDevType()) + .participants(project.getParticipants().stream() + .map(Participant::toDTO).toList()) + .links(project.getLinks().stream() + .map(Link::toDTO).toList()) .build(); } diff --git a/src/main/java/kr/tgwing/tech/security/config/SecurityConfig.java b/src/main/java/kr/tgwing/tech/security/config/SecurityConfig.java index e9cd752..6426594 100644 --- a/src/main/java/kr/tgwing/tech/security/config/SecurityConfig.java +++ b/src/main/java/kr/tgwing/tech/security/config/SecurityConfig.java @@ -1,6 +1,8 @@ package kr.tgwing.tech.security.config; +import jakarta.servlet.http.HttpSession; import kr.tgwing.tech.security.filter.JwtFilter; +import kr.tgwing.tech.security.service.JwtBlackListService; import kr.tgwing.tech.security.util.JwtUtil; import kr.tgwing.tech.security.filter.LoginFilter; import lombok.extern.log4j.Log4j2; @@ -9,21 +11,27 @@ import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.util.matcher.AndRequestMatcher; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @EnableWebSecurity @Log4j2 @Configuration public class SecurityConfig { - + private final AuthenticationConfiguration authenticationConfiguration; + private final JwtUtil jwtUtil; + private final JwtBlackListService jwtBlackListService; private static final String[] PERMIT_URL_ARRAY = { /* swagger v2 */ "/v2/api-docs", @@ -46,7 +54,12 @@ public class SecurityConfig { "/api-docs/json/swagger-config", "/api-docs/json" }; + public SecurityConfig(AuthenticationConfiguration authenticationConfiguration, JwtUtil jwtUtil, JwtBlackListService jwtBlackListService) { + this.authenticationConfiguration = authenticationConfiguration; + this.jwtUtil = jwtUtil; + this.jwtBlackListService = jwtBlackListService; + } @Bean public WebSecurityCustomizer webSecurityCustomizer() { log.info("WebSecurity......................"); @@ -55,21 +68,15 @@ public WebSecurityCustomizer webSecurityCustomizer() { .requestMatchers(PathRequest.toStaticResources().atCommonLocations()); } - private final AuthenticationConfiguration authenticationConfiguration; - - private final JwtUtil jwtUtil; - - public SecurityConfig(AuthenticationConfiguration authenticationConfiguration, JwtUtil jwtUtil) { - - this.authenticationConfiguration = authenticationConfiguration; - this.jwtUtil = jwtUtil; - } - @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception { return configuration.getAuthenticationManager(); } + @Bean + public BCryptPasswordEncoder bCryptPasswordEncoder() { + return new BCryptPasswordEncoder(); + } // 생성자 선언해둬야함... 해야되나? @Bean // 요청이 들어오면 SecurityFilterChain이 가로채서 인증, 인가를 체크함. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { @@ -80,22 +87,34 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .cors(AbstractHttpConfigurer::disable) // .formLogin(Customizer.withDefaults())// -> 로그인 화면 구성되면 사용해야함. - // .logout((logout) -> logout - // .clearAuthentication(true)) - + .logout((logout) -> logout + .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) + .clearAuthentication(true) + .invalidateHttpSession(true) + .logoutSuccessUrl("/user/login") // 로그아웃 후 리디렉션할 URL 지정 + .deleteCookies("JSESSIONID")) // 세션 쿠키 삭제 +// .logout((logout) -> ) +// .logoutUrl("/logout") +// .addLogoutHandler((request, response, authentication) -> { +// HttpSession session = request.getSession(); +// if (session != null) { +// session.invalidate(); +// } +// }) +// .logoutSuccessHandler((request, response, authentication) -> { +// response.sendRedirect("/login"); +// }) .authorizeHttpRequests(request -> request .requestMatchers(PERMIT_URL_ARRAY) .permitAll() // .requestMatchers("/user/**", "/login", "/admin/**") // .permitAll() -//// .requestMatchers("/admin/**") -//// .hasRole("ADMIN") -// .requestMatchers(HttpMethod.GET, "/file/**") -// .permitAll() +// .requestMatchers("/admin/**") +// .hasRole("ADMIN") .requestMatchers("/**") .permitAll() .anyRequest().authenticated()) - .addFilterBefore(new JwtFilter(jwtUtil), LoginFilter.class) + .addFilterBefore(new JwtFilter(jwtUtil, jwtBlackListService), LoginFilter.class) .addFilterAt(new LoginFilter(authenticationManager(authenticationConfiguration), jwtUtil), UsernamePasswordAuthenticationFilter.class) .sessionManagement((session) -> session @@ -103,6 +122,11 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .build(); } +// public HttpSecurity logout(Customizer> logoutCustomizer) throws Exception { +// logoutCustomizer.customize(getOrApply(new LogoutConfigurer<>())); +// return HttpSecurity.this; +// } + // @Bean // @ConditionalOnMissingBean(UserDetailsService.class) // InMemoryUserDetailsManager inMemoryUserDetailsManager() { @@ -115,8 +139,4 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti // return new InMemoryUserDetailsManager(admin); // } - @Bean - public BCryptPasswordEncoder bCryptPasswordEncoder() { - return new BCryptPasswordEncoder(); - } // 생성자 선언해둬야함... 해야되나? } diff --git a/src/main/java/kr/tgwing/tech/security/filter/JwtFilter.java b/src/main/java/kr/tgwing/tech/security/filter/JwtFilter.java index 18916cc..ee71026 100644 --- a/src/main/java/kr/tgwing/tech/security/filter/JwtFilter.java +++ b/src/main/java/kr/tgwing/tech/security/filter/JwtFilter.java @@ -5,6 +5,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import kr.tgwing.tech.security.service.CustomUserDetails; +import kr.tgwing.tech.security.service.JwtBlackListService; import kr.tgwing.tech.security.util.JwtUtil; import kr.tgwing.tech.user.entity.User; import lombok.extern.slf4j.Slf4j; @@ -17,9 +18,11 @@ @Slf4j public class JwtFilter extends OncePerRequestFilter { private final JwtUtil jwtUtil; + private final JwtBlackListService jwtBlackListService; - public JwtFilter(JwtUtil jwtUtil) { + public JwtFilter(JwtUtil jwtUtil, JwtBlackListService jwtBlackListService) { this.jwtUtil = jwtUtil; + this.jwtBlackListService = jwtBlackListService; } @@ -32,11 +35,9 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse //Authorization 헤더 검증 if (authorization == null || !authorization.startsWith("Bearer ")) { - log.info("token is null"); filterChain.doFilter(request, response); - //조건이 해당되면 메소드 종료 (필수) return; } @@ -45,13 +46,12 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse //Bearer 부분 제거 후 순수 토큰만 획득 String token = authorization.split(" ")[1]; - //토큰 소멸 시간 검증 - if (jwtUtil.isExpired(token)) { - log.info("token is expired."); + //토큰 소멸 시간 검증 + if (jwtUtil.isExpired(token) || jwtBlackListService.isBlacklisted(token)) { + log.info("token is useless..."); filterChain.doFilter(request, response); - //조건이 해당되면 메소드 종료 (필수) return; } @@ -61,7 +61,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse //userEntity를 생성하여 값 set User userEntity = new User(); - userEntity.setStudentId(studentId); + userEntity.setStudentNumber(studentId); userEntity.setPassword("temppassword"); userEntity.setRole(role); diff --git a/src/main/java/kr/tgwing/tech/security/filter/LoginFilter.java b/src/main/java/kr/tgwing/tech/security/filter/LoginFilter.java index a57e101..e380dab 100644 --- a/src/main/java/kr/tgwing/tech/security/filter/LoginFilter.java +++ b/src/main/java/kr/tgwing/tech/security/filter/LoginFilter.java @@ -19,9 +19,7 @@ public class LoginFilter extends UsernamePasswordAuthenticationFilter { private final AuthenticationManager authenticationManager; private final JwtUtil jwtUtil; - public LoginFilter(AuthenticationManager authenticationManager, JwtUtil jwtUtil) { - this.authenticationManager = authenticationManager; this.jwtUtil = jwtUtil; diff --git a/src/main/java/kr/tgwing/tech/security/service/CustomUserDetails.java b/src/main/java/kr/tgwing/tech/security/service/CustomUserDetails.java index 49d92e7..e87e722 100644 --- a/src/main/java/kr/tgwing/tech/security/service/CustomUserDetails.java +++ b/src/main/java/kr/tgwing/tech/security/service/CustomUserDetails.java @@ -41,7 +41,7 @@ public String getPassword() { @Override public String getUsername() { - return userEntity.getStudentId(); + return userEntity.getStudentNumber(); } public String getProfilePicture() { diff --git a/src/main/java/kr/tgwing/tech/security/service/CustomUserDetailsService.java b/src/main/java/kr/tgwing/tech/security/service/CustomUserDetailsService.java index 932c671..6b1c053 100644 --- a/src/main/java/kr/tgwing/tech/security/service/CustomUserDetailsService.java +++ b/src/main/java/kr/tgwing/tech/security/service/CustomUserDetailsService.java @@ -14,15 +14,14 @@ public class CustomUserDetailsService implements UserDetailsService { private final UserRepository userRepository; public CustomUserDetailsService(UserRepository userRepository) { - this.userRepository = userRepository; } @Override - public UserDetails loadUserByUsername(String studentId) throws UsernameNotFoundException { + public UserDetails loadUserByUsername(String studentNumber) throws UsernameNotFoundException { //DB에서 조회 - Optional userData = userRepository.findByStudentId(studentId); + Optional userData = userRepository.findByStudentNumber(studentNumber); if (userData.isPresent()) { //UserDetails에 담아서 return하면 AutneticationManager가 검증 함 diff --git a/src/main/java/kr/tgwing/tech/security/service/JwtBlackListService.java b/src/main/java/kr/tgwing/tech/security/service/JwtBlackListService.java new file mode 100644 index 0000000..9422dcf --- /dev/null +++ b/src/main/java/kr/tgwing/tech/security/service/JwtBlackListService.java @@ -0,0 +1,18 @@ +package kr.tgwing.tech.security.service; + +import org.springframework.stereotype.Service; + +import java.util.Set; +import java.util.concurrent.ConcurrentSkipListSet; + +@Service +public class JwtBlackListService { + private final Set blackList = new ConcurrentSkipListSet<>(); + + public void addToBlacklist(String token) { + blackList.add(token); + } + + public boolean isBlacklisted(String token) { + return blackList.contains(token); + }} diff --git a/src/main/java/kr/tgwing/tech/user/controller/ProfileController.java b/src/main/java/kr/tgwing/tech/user/controller/ProfileController.java index 1fab120..09580f5 100644 --- a/src/main/java/kr/tgwing/tech/user/controller/ProfileController.java +++ b/src/main/java/kr/tgwing/tech/user/controller/ProfileController.java @@ -17,47 +17,43 @@ @RestController @RequiredArgsConstructor @RequestMapping("/profile") -@Tag(name = "회원정보 수정하기") +@Tag(name = "회원정보") public class ProfileController { private final UserService userService; - @Operation(summary = "회원정보 조회하기") + @Operation(summary = "회원정보 조회") @GetMapping("") public ResponseEntity> showProfile( Principal principal){ - String studentId = principal.getName(); - ProfileDTO profileDTO = userService.showUser(studentId); + ProfileDTO profileDTO = userService.showUser(principal.getName()); return ResponseEntity.ok(ApiResponse.ok(profileDTO)); } - @Operation(summary = "내가 쓴 글 가져오기" ) - @GetMapping("/mine") + @Operation(summary = "내 글 가져오기" ) + @GetMapping("/blog") public ResponseEntity>> showMyPost(Principal principal){ - String studentId = principal.getName(); - List blog = userService.showMyBlog(studentId); + List blog = userService.showMyBlog(principal.getName()); return ResponseEntity.ok(ApiResponse.ok(blog)); } - @Operation(summary = "유저 정보 수정 완료" ) + @Operation(summary = "회원정보 수정" ) @PutMapping("") public ResponseEntity> changeProfile( @RequestBody ProfileReqDTO request, Principal principal) { - String studentId = principal.getName(); - Long change = userService.changeUser(studentId, request); + Long change = userService.changeUser(principal.getName(), request); return ResponseEntity.ok(ApiResponse.updated(change)); } - @Operation(summary = "회원 탈퇴" ) + @Operation(summary = "회원 탈퇴") @DeleteMapping("") public ResponseEntity> removeProfile(Principal principal){ - String studentId = principal.getName(); - Long remove = userService.removeUser(studentId); + Long remove = userService.removeUser(principal.getName()); return ResponseEntity.ok(ApiResponse.delete(remove)); diff --git a/src/main/java/kr/tgwing/tech/user/controller/UserController.java b/src/main/java/kr/tgwing/tech/user/controller/UserController.java index 2ef1ba7..cffd9d6 100644 --- a/src/main/java/kr/tgwing/tech/user/controller/UserController.java +++ b/src/main/java/kr/tgwing/tech/user/controller/UserController.java @@ -3,6 +3,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import kr.tgwing.tech.common.ApiResponse; +import kr.tgwing.tech.security.service.JwtBlackListService; import kr.tgwing.tech.security.util.JwtUtil; import kr.tgwing.tech.user.dto.*; import kr.tgwing.tech.user.dto.checkdto.CheckNumberDTO; @@ -23,93 +24,69 @@ import java.util.UUID; + +@Tag(name = "회원가입/비밀번호 확인") @RestController @RequestMapping("/user") @RequiredArgsConstructor @Log4j2 -@Tag(name = "로그인 하기 전 기능 + 로그아웃") public class UserController { private final UserService userService; private final RedisTemplate redisTemplate; + private final JwtBlackListService jwtBlackListService; - @Operation(summary = "티지윙 회원가입 1: 이메일 인증 요청하기") + @Operation(summary = "회원가입 1단계: 인증코드 이메일로 전송") @PostMapping("/register/email") public ResponseEntity> register1(@RequestBody EmailDto emailDto) { - EmailMessageDTO emailMessageDTO = EmailMessageDTO.builder() - .receiver(emailDto.getEmail()) - .subject("[TGWING] Email 인증코드 발급") - .build(); + EmailMessageDTO emailMessageDTO = emailDto.toRegister1(emailDto); ValueOperations valueOperations = redisTemplate.opsForValue(); String emailKey = UUID.randomUUID().toString(); - String code = userService.sendEmail(emailMessageDTO); - valueOperations.set(emailKey, code); + valueOperations.set(emailKey, userService.sendEmail(emailMessageDTO)); return ResponseEntity.ok(ApiResponse.ok(emailKey)); } - @Operation(summary = "티지윙 회원가입 2: 이메일 인증 확인하기") + @Operation(summary = "회원가입 2단계: 인증코드 확인") @PostMapping("/register/check") public ResponseEntity> register2( @RequestParam("emailKey") String emailKey, @RequestBody CheckNumberDTO checkNumberDTO) { - ValueOperations valueOperations = redisTemplate.opsForValue(); - Object code = valueOperations.get(emailKey); - if(!code.equals(checkNumberDTO.getCode())) throw new EmailCodeException(); // 인증코드가 일치하지 않습니다. + String code = redisTemplate.opsForValue().get(emailKey); + userService.checkCode(code, checkNumberDTO); return ResponseEntity.ok(ApiResponse.ok("이메일이 인증되었습니다.")); } - @Operation(summary = "회원 등록하기") + @Operation(summary = "회원가입 3단계 :회원 등록(임시회원)") @PostMapping("/register") public ResponseEntity> register(@RequestBody UserDTO userDTO) { Long userId = userService.register(userDTO); - return ResponseEntity.ok(ApiResponse.created(userId)); } - - // TODO : 로그아웃 기능 만들기. 대신 제대로 좀 알고.. 지금은 단일토큰이니까 프론트에서 때달라해야하나? -// @Operation(summary = "로그아웃하기") -// @PostMapping("/logout") -// public ResponseEntity> logout( -// HttpServletRequest request, -// HttpServletResponse response) { -// String authorization = request.getHeader("authorization"); -// String token = authorization.split(" ")[1]; -// String studentId = jwtUtil.getStudentId(token); -// -// Long userId = userService.logout(studentId); -// response.setHeader("authorization", null); -// -// return ResponseEntity.ok(ApiResponse.ok(userId)); -// } - - @Operation(summary = "비밀전호 재설정하기 전, 본인확인하기") + @Operation(summary = "비밀번호 확인 1단계: 본인 확인") @PostMapping("/password") public ResponseEntity>> checkUser( @RequestBody CheckUserDTO checkUserDTO) { - Boolean isExist = userService.checkUser(checkUserDTO); + userService.checkUser(checkUserDTO); + EmailMessageDTO emailMessageDTO = checkUserDTO.toCheckPassword(checkUserDTO); - EmailMessageDTO emailMessageDTO = EmailMessageDTO.builder() - .receiver(checkUserDTO.getEmail()) - .subject("[TGWING] Email 인증코드 발급") - .build(); String code = userService.sendEmail(emailMessageDTO); ValueOperations valueOperations = redisTemplate.opsForValue(); String studentKey = UUID.randomUUID().toString(); String emailKey = UUID.randomUUID().toString(); - valueOperations.set(studentKey, checkUserDTO.getStudentId()); + valueOperations.set(studentKey, checkUserDTO.getStudentNumber()); valueOperations.set(emailKey, code); return ResponseEntity.ok(ApiResponse.created(new Pair<>(studentKey, emailKey))); } - @Operation(summary = "본인확인 이메일 인증하기") + @Operation(summary = "비밀번호 확인 2단계: 본인 이메일 인증") @PostMapping("/password/check") public ResponseEntity> checkNumber( @RequestParam("studentKey") String studentKey, @@ -117,26 +94,38 @@ public ResponseEntity> checkNumber( @RequestBody CheckNumberDTO checkNumberDTO) { // redis로 가져와서 code의 숫자값과 입력으로 들어온 숫자가 같은지 확인하기 - ValueOperations valueOperations = redisTemplate.opsForValue(); - Object code = valueOperations.get(emailKey); - - if(!code.equals(checkNumberDTO.getCode())) throw new EmailCodeException(); // 인증코드가 일치하지 않습니다. + String code = redisTemplate.opsForValue().get(emailKey); + userService.checkCode(code, checkNumberDTO); return ResponseEntity.ok(ApiResponse.created(studentKey)); } - @Operation(summary = "비밀번호 재설정하기") - @PostMapping("/password/reset") // 쿼리 파라미터로 넘겨받기 + @Operation(summary = "비밀번호 확인 3단계: 비밀번호 재설정") + @PutMapping("/password") // 쿼리 파라미터로 넘겨받기 public ResponseEntity> setNewPassword( @RequestParam("studentKey") String studentKey, @RequestBody PasswordCheckDTO passwordCheckDTO) { - ValueOperations valueOperations = redisTemplate.opsForValue(); - Object studentId = valueOperations.get(studentKey); + String studentId = redisTemplate.opsForValue().get(studentKey); Long userId = userService.setNewPassword(studentId, passwordCheckDTO); return ResponseEntity.ok(ApiResponse.updated(userId)); } + @Operation(summary = "로그아웃") + @PostMapping("/logout") + public ResponseEntity> logout( + @RequestHeader(name = "Authorization") String authorization) { + String token = authorization.split(" ")[1]; + jwtBlackListService.addToBlacklist(token); + + return ResponseEntity.ok(ApiResponse.ok()); + } + + @Operation(summary = "로그아웃 시, 리다이렉트(임시용)") + @GetMapping("/login") + public String login() { + return "login"; + } } diff --git a/src/main/java/kr/tgwing/tech/user/dto/EmailMessageDTO.java b/src/main/java/kr/tgwing/tech/user/dto/EmailMessageDTO.java index 52c75e1..c5f91fe 100644 --- a/src/main/java/kr/tgwing/tech/user/dto/EmailMessageDTO.java +++ b/src/main/java/kr/tgwing/tech/user/dto/EmailMessageDTO.java @@ -1,5 +1,6 @@ package kr.tgwing.tech.user.dto; +import kr.tgwing.tech.user.dto.registerdto.EmailDto; import lombok.Builder; import lombok.Getter; import lombok.Setter; @@ -11,4 +12,6 @@ public class EmailMessageDTO { private String receiver; private String subject; private String message; + + } diff --git a/src/main/java/kr/tgwing/tech/user/dto/LoginDTO.java b/src/main/java/kr/tgwing/tech/user/dto/LoginDTO.java index 5a37dfb..c72580d 100644 --- a/src/main/java/kr/tgwing/tech/user/dto/LoginDTO.java +++ b/src/main/java/kr/tgwing/tech/user/dto/LoginDTO.java @@ -10,6 +10,6 @@ @Builder @NoArgsConstructor @AllArgsConstructor public class LoginDTO { - private String studentId; + private String studentNumber; private String password; } diff --git a/src/main/java/kr/tgwing/tech/user/dto/checkdto/CheckUserDTO.java b/src/main/java/kr/tgwing/tech/user/dto/checkdto/CheckUserDTO.java index 53bb6e8..54f919a 100644 --- a/src/main/java/kr/tgwing/tech/user/dto/checkdto/CheckUserDTO.java +++ b/src/main/java/kr/tgwing/tech/user/dto/checkdto/CheckUserDTO.java @@ -1,5 +1,7 @@ package kr.tgwing.tech.user.dto.checkdto; +import kr.tgwing.tech.user.dto.EmailMessageDTO; +import kr.tgwing.tech.user.dto.registerdto.EmailDto; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -9,7 +11,14 @@ @Builder @NoArgsConstructor @AllArgsConstructor public class CheckUserDTO { - private String studentId; + private String studentNumber; private String name; private String email; + + public EmailMessageDTO toCheckPassword(CheckUserDTO checkUserDTO) { + return EmailMessageDTO.builder() + .receiver(checkUserDTO.getEmail()) + .subject("[TGWING} 인증코드") + .build(); + } } diff --git a/src/main/java/kr/tgwing/tech/user/dto/profiledto/ProfileDTO.java b/src/main/java/kr/tgwing/tech/user/dto/profiledto/ProfileDTO.java index a0d9721..08b0b1d 100644 --- a/src/main/java/kr/tgwing/tech/user/dto/profiledto/ProfileDTO.java +++ b/src/main/java/kr/tgwing/tech/user/dto/profiledto/ProfileDTO.java @@ -6,6 +6,7 @@ import lombok.NoArgsConstructor; import java.sql.Date; +import java.time.LocalDate; @Getter @Builder @@ -13,10 +14,10 @@ @AllArgsConstructor public class ProfileDTO { - private String studentId; + private String studentNumber; private String email; private String name; // 이름 - private Date birth; + private LocalDate birth; private String phoneNumber; private String profilePicture; diff --git a/src/main/java/kr/tgwing/tech/user/dto/registerdto/EmailDto.java b/src/main/java/kr/tgwing/tech/user/dto/registerdto/EmailDto.java index 449ba55..310925c 100644 --- a/src/main/java/kr/tgwing/tech/user/dto/registerdto/EmailDto.java +++ b/src/main/java/kr/tgwing/tech/user/dto/registerdto/EmailDto.java @@ -1,9 +1,7 @@ package kr.tgwing.tech.user.dto.registerdto; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; +import kr.tgwing.tech.user.dto.EmailMessageDTO; +import lombok.*; @Getter @Setter @@ -11,4 +9,11 @@ @AllArgsConstructor public class EmailDto { private String email; + + public EmailMessageDTO toRegister1(EmailDto emailDto) { + return EmailMessageDTO.builder() + .receiver(emailDto.getEmail()) + .subject("[TGWING} 인증코드") + .build(); + } } diff --git a/src/main/java/kr/tgwing/tech/user/dto/registerdto/UserDTO.java b/src/main/java/kr/tgwing/tech/user/dto/registerdto/UserDTO.java index 6363c28..ba7e15d 100644 --- a/src/main/java/kr/tgwing/tech/user/dto/registerdto/UserDTO.java +++ b/src/main/java/kr/tgwing/tech/user/dto/registerdto/UserDTO.java @@ -5,22 +5,23 @@ import lombok.*; import java.sql.Date; +import java.time.LocalDate; @Data @Builder @NoArgsConstructor @AllArgsConstructor public class UserDTO { - private String studentId; + private String studentNumber; private String password; private String email; private String name; // 이름 - private Date birth; + private LocalDate birth; private String phoneNumber; public static TempUser toTempUser(UserDTO userDTO) { return TempUser.builder() - .studentId(userDTO.getStudentId()) + .studentNumber(userDTO.getStudentNumber()) .password(userDTO.getPassword()) .name(userDTO.getName()) .email(userDTO.getEmail()) @@ -31,6 +32,4 @@ public static TempUser toTempUser(UserDTO userDTO) { } - - } diff --git a/src/main/java/kr/tgwing/tech/user/entity/BaseUser.java b/src/main/java/kr/tgwing/tech/user/entity/BaseUser.java index 5354a5c..c43b496 100644 --- a/src/main/java/kr/tgwing/tech/user/entity/BaseUser.java +++ b/src/main/java/kr/tgwing/tech/user/entity/BaseUser.java @@ -4,9 +4,9 @@ import kr.tgwing.tech.common.BaseEntity; import lombok.*; import org.springframework.format.annotation.DateTimeFormat; - import java.io.Serializable; import java.sql.Date; +import java.time.LocalDate; @NoArgsConstructor @@ -14,12 +14,12 @@ @Getter public abstract class BaseUser extends BaseEntity implements Serializable { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "student_id") + private Long studentId; - @Column(length = 10, unique = true, nullable = false) - private String studentId; + @Column(name = "student_number", length = 10, unique = true, nullable = false) + private String studentNumber; @Column(nullable = false) private String password; @@ -32,13 +32,13 @@ public abstract class BaseUser extends BaseEntity implements Serializable { @Column(nullable = false) @DateTimeFormat(pattern = "yyyy-MM-dd") - private Date birth; + private LocalDate birth; - @Column(nullable = false, length = 13) + @Column(name = "phone_number", nullable = false, length = 13) private String phoneNumber; - public BaseUser(String studentId, String password, String email, String name, Date birth, String phoneNumber) { - this.studentId = studentId; + public BaseUser(String studentNumber, String password, String email, String name, LocalDate birth, String phoneNumber) { + this.studentNumber = studentNumber; this.password = password; this.email = email; this.name = name; @@ -49,8 +49,8 @@ public void setPassword(String password) { this.password = password; } - public void setStudentId(String studentId) { - this.studentId = studentId; + public void setStudentNumber(String studentNumber) { + this.studentNumber = studentNumber; } } diff --git a/src/main/java/kr/tgwing/tech/user/entity/TempUser.java b/src/main/java/kr/tgwing/tech/user/entity/TempUser.java index 005483b..11f1449 100644 --- a/src/main/java/kr/tgwing/tech/user/entity/TempUser.java +++ b/src/main/java/kr/tgwing/tech/user/entity/TempUser.java @@ -1,6 +1,5 @@ package kr.tgwing.tech.user.entity; -import jakarta.persistence.DiscriminatorColumn; import jakarta.persistence.DiscriminatorValue; import jakarta.persistence.Entity; import jakarta.persistence.Table; @@ -11,32 +10,33 @@ import org.springframework.security.crypto.password.PasswordEncoder; import java.sql.Date; +import java.time.LocalDate; @Entity @Getter -@Table(name = "temp_user") +@Table(name = "assignment_request") @DiscriminatorValue("F") @NoArgsConstructor public class TempUser extends BaseUser{ @Builder - public TempUser(String studentId, String password, String email, String name, Date birth, String phoneNumber) { - super(studentId, password, email, name, birth, phoneNumber); + public TempUser(String studentNumber, String password, String email, String name, LocalDate birth, String phoneNumber) { + super(studentNumber, password, email, name, birth, phoneNumber); } public AdminCheckUserDto toAdminCheckUserDto(TempUser user) { return AdminCheckUserDto.builder() - .id(user.getId()) + .studentId(user.getStudentId()) .name(user.getName()) .email(user.getEmail()) - .studentId(user.getStudentId()) + .studentNumber(user.getStudentNumber()) .phoneNumber(user.getPhoneNumber()) .build(); } public User toUser(TempUser user) { return User.builder() - .studentId(user.getStudentId()) + .studentNumber(user.getStudentNumber()) .password(user.getPassword()) .email(user.getEmail()) .name(user.getName()) diff --git a/src/main/java/kr/tgwing/tech/user/entity/User.java b/src/main/java/kr/tgwing/tech/user/entity/User.java index cf0b2f9..9d8423e 100644 --- a/src/main/java/kr/tgwing/tech/user/entity/User.java +++ b/src/main/java/kr/tgwing/tech/user/entity/User.java @@ -3,26 +3,26 @@ import jakarta.persistence.*; import kr.tgwing.tech.user.dto.profiledto.ProfileDTO; import lombok.*; -import org.springframework.format.annotation.DateTimeFormat; import java.sql.Date; +import java.time.LocalDate; @Entity @Getter @DiscriminatorValue("M") @NoArgsConstructor -@Table(name = "user") +@Table(name = "student") public class User extends BaseUser { @Column private String role; - @Column + @Column(name = "profile_picture") private String profilePicture; @Builder - public User(String studentId, String password, String email, String name, Date birth, String phoneNumber, String role, String profilePicture) { - super(studentId, password, email, name, birth, phoneNumber); + public User(String studentNumber, String password, String email, String name, LocalDate birth, String phoneNumber, String role, String profilePicture) { + super(studentNumber, password, email, name, birth, phoneNumber); this.role = role; this.profilePicture = profilePicture; } @@ -33,7 +33,7 @@ public void setRole(String role) { public ProfileDTO toProfileDTO(User user) { return ProfileDTO.builder() - .studentId(user.getStudentId()) + .studentNumber(user.getStudentNumber()) .email(user.getEmail()) .name(user.getName()) .birth(user.getBirth()) diff --git a/src/main/java/kr/tgwing/tech/user/repository/UserRepository.java b/src/main/java/kr/tgwing/tech/user/repository/UserRepository.java index d351e81..b1d97a5 100644 --- a/src/main/java/kr/tgwing/tech/user/repository/UserRepository.java +++ b/src/main/java/kr/tgwing/tech/user/repository/UserRepository.java @@ -12,10 +12,10 @@ @Repository public interface UserRepository extends JpaRepository { - Optional findByStudentId(String studentId); + Optional findByStudentNumber(String studentNumber); @Transactional - void deleteByStudentId(String studentId); + void deleteByStudentNumber(String studentNumber); @Transactional diff --git a/src/main/java/kr/tgwing/tech/user/service/UserService.java b/src/main/java/kr/tgwing/tech/user/service/UserService.java index e8aa1c1..8bee9fb 100644 --- a/src/main/java/kr/tgwing/tech/user/service/UserService.java +++ b/src/main/java/kr/tgwing/tech/user/service/UserService.java @@ -3,6 +3,7 @@ import kr.tgwing.tech.blog.entity.PostEntity; import kr.tgwing.tech.user.dto.*; +import kr.tgwing.tech.user.dto.checkdto.CheckNumberDTO; import kr.tgwing.tech.user.dto.checkdto.CheckUserDTO; import kr.tgwing.tech.user.dto.checkdto.PasswordCheckDTO; import kr.tgwing.tech.user.dto.profiledto.ProfileDTO; @@ -16,7 +17,7 @@ public interface UserService{ Long register(UserDTO userDTO); - Long logout(String studentId); + Long logout(String studentNumber); Long changeUser(String name, ProfileReqDTO request); @@ -25,11 +26,13 @@ public interface UserService{ ProfileDTO showUser(String name); - List showMyBlog(String studentId); + List showMyBlog(String studentNumber); - Boolean checkUser(CheckUserDTO checkUserDTO); // 본인 확인하기 + void checkUser(CheckUserDTO checkUserDTO); // 본인 확인하기 String sendEmail(EmailMessageDTO emailMessage); // 메일로 인증번호 전송하기 - Long setNewPassword(Object studentId, PasswordCheckDTO password); + Long setNewPassword(String studentNumber, PasswordCheckDTO password); + + void checkCode(String code, CheckNumberDTO checkNumberDTO); } diff --git a/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java b/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java index 32830ba..a12ac69 100644 --- a/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java +++ b/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java @@ -5,6 +5,7 @@ import kr.tgwing.tech.blog.entity.PostEntity; import kr.tgwing.tech.blog.repository.PostRepository; import kr.tgwing.tech.user.dto.*; +import kr.tgwing.tech.user.dto.checkdto.CheckNumberDTO; import kr.tgwing.tech.user.dto.checkdto.CheckUserDTO; import kr.tgwing.tech.user.dto.checkdto.PasswordCheckDTO; import kr.tgwing.tech.user.dto.profiledto.ProfileDTO; @@ -12,10 +13,7 @@ import kr.tgwing.tech.user.dto.registerdto.UserDTO; import kr.tgwing.tech.user.entity.TempUser; import kr.tgwing.tech.user.entity.User; -import kr.tgwing.tech.user.exception.MessageException; -import kr.tgwing.tech.user.exception.PasswordException; -import kr.tgwing.tech.user.exception.UserDuplicatedException; -import kr.tgwing.tech.user.exception.UserNotFoundException; +import kr.tgwing.tech.user.exception.*; import kr.tgwing.tech.user.repository.TempUserRepository; import kr.tgwing.tech.user.repository.UserRepository; import lombok.RequiredArgsConstructor; @@ -45,44 +43,44 @@ public class UserServiceImpl implements UserService { @Override public Long register(UserDTO userDTO){ - Optional user = userRepository.findByStudentId(userDTO.getStudentId()); + Optional user = userRepository.findByStudentNumber(userDTO.getStudentNumber()); if(user.isPresent()) throw new UserDuplicatedException(); TempUser tempUser = UserDTO.toTempUser(userDTO); tempUser.hashPassword(bCryptPasswordEncoder); - return tempUserRepository.save(tempUser).getId(); + return tempUserRepository.save(tempUser).getStudentId(); } @Override - public Long logout(String studentId) { - User user = userRepository.findByStudentId(studentId).orElseThrow(UserNotFoundException::new); + public Long logout(String studentNumber) { + User user = userRepository.findByStudentNumber(studentNumber).orElseThrow(UserNotFoundException::new); - return user.getId(); + return user.getStudentId(); } // user 정보 수정하기 @Override - public Long changeUser(String studentId, ProfileReqDTO request){ - User userEntity = userRepository.findByStudentId(studentId) + public Long changeUser(String studentNumber, ProfileReqDTO request){ + User userEntity = userRepository.findByStudentNumber(studentNumber) .orElseThrow(UserNotFoundException::new); - userRepository.changeUser(studentId, request.getName(), request.getPhoneNumber(), request.getProfilePicture()); - Long id = userEntity.getId(); + userRepository.changeUser(studentNumber, request.getName(), request.getPhoneNumber(), request.getProfilePicture()); + Long id = userEntity.getStudentId(); return id; }; @Override - public Long removeUser(String studentId){ - userRepository.findByStudentId(studentId) + public Long removeUser(String studentNumber){ + userRepository.findByStudentNumber(studentNumber) .orElseThrow(UserNotFoundException::new); // user의 존재여부 확인 - userRepository.deleteByStudentId(studentId); + userRepository.deleteByStudentNumber(studentNumber); return null; } @Override - public ProfileDTO showUser(String studentId){ - User user = userRepository.findByStudentId(studentId) + public ProfileDTO showUser(String studentNumber){ + User user = userRepository.findByStudentNumber(studentNumber) .orElseThrow(UserNotFoundException::new); // 만약 사용자 정보가 존재한다면 업데이트를 수행 ProfileDTO profileDTO = user.toProfileDTO(user); @@ -91,20 +89,20 @@ public ProfileDTO showUser(String studentId){ } @Override - public List showMyBlog(String studentId){ - User user = userRepository.findByStudentId(studentId) + public List showMyBlog(String studentNumber){ + User user = userRepository.findByStudentNumber(studentNumber) .orElseThrow(UserNotFoundException::new); - List myBlog = postRepository.findByWriter(user.getId()); + List myBlog = postRepository.findByWriter(user.getStudentId()); return myBlog; } @Override - public Boolean checkUser(CheckUserDTO checkUserDTO) { - User user = userRepository.findByStudentId(checkUserDTO.getStudentId()) + public void checkUser(CheckUserDTO checkUserDTO) { + User user = userRepository.findByStudentNumber(checkUserDTO.getStudentNumber()) .orElseThrow(UserNotFoundException::new); - if(user.getEmail().equals(checkUserDTO.getEmail()) && user.getName().equals(checkUserDTO.getName())) return true; - else throw new UserNotFoundException(); + if(!user.getEmail().equals(checkUserDTO.getEmail()) || !user.getName().equals(checkUserDTO.getName())) + throw new UserNotFoundException(); } @Override @@ -149,18 +147,22 @@ public String setContext(String code, String type) { } @Override - public Long setNewPassword(Object studentId, PasswordCheckDTO password) { + public Long setNewPassword(String studentNumber, PasswordCheckDTO password) { String newPassword = password.getNewPassword(); if(newPassword.equals(password.getCheckPassword())) { - User user = userRepository.findByStudentId(studentId.toString()).orElseThrow(UserNotFoundException::new); + User user = userRepository.findByStudentNumber(studentNumber.toString()).orElseThrow(UserNotFoundException::new); user.setPassword(bCryptPasswordEncoder.encode(newPassword)); - return userRepository.save(user).getId(); + return userRepository.save(user).getStudentId(); } else { throw new PasswordException();// 비밀번호가 서로 일치하지 않습니다. } } + @Override + public void checkCode(String code, CheckNumberDTO checkNumberDTO) { + if(!code.equals(checkNumberDTO.getCode())) throw new EmailCodeException(); // 인증코드가 일치하지 않습니다. + } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index f00ab70..a0e6504 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -10,7 +10,7 @@ spring.datasource.password=tgwing spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl spring.jpa.show-sql=true -spring.jpa.hibernate.ddl-auto=create-drop +spring.jpa.hibernate.ddl-auto=create spring.jpa.open-in-view=false spring.jpa.generate-ddl=true spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html new file mode 100644 index 0000000..10d4fb5 --- /dev/null +++ b/src/main/resources/templates/login.html @@ -0,0 +1,20 @@ + +
+
+
+
+ 사용자ID 또는 비밀번호를 확인해 주세요. +
+
+
+ + +
+
+ + +
+ +
+
+