From aacc9604e411bcf8e939b1b11273884258e7223e Mon Sep 17 00:00:00 2001 From: singsangssong Date: Sat, 2 Nov 2024 08:15:15 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20project,=20admin=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80,=20particip?= =?UTF-8?q?ant=20-=20student=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/controller/AdminController.java | 30 +++++----- .../tgwing/tech/admin/dto/StudentQuery.java | 8 +++ .../tech/admin/service/AdminService.java | 23 +++++--- .../kr/tgwing/tech/config/TestDataLoader.java | 10 +++- .../project/controller/ProjectController.java | 18 +++--- .../tech/project/domain/Participant.java | 16 +++++- .../tgwing/tech/project/domain/Project.java | 1 + .../project/domain/ProjectSpecification.java | 38 +++++++++++++ .../tech/project/dto/ParticipantDTO.java | 10 +++- .../tech/project/dto/ProjectBriefDTO.java | 15 ++++- .../tgwing/tech/project/dto/ProjectQuery.java | 8 +++ .../project/repository/ProjectRepository.java | 6 +- .../project/service/ProjectServiceImpl.java | 55 ++++++++----------- .../user/controller/ProfileController.java | 33 ++++++----- .../tech/user/entity/UserSpecification.java | 33 +++++++++++ .../user/repository/TempUserRepository.java | 5 +- .../tech/user/repository/UserRepository.java | 3 +- .../tgwing/tech/user/service/UserService.java | 6 +- .../tech/user/service/UserServiceImpl.java | 16 +++++- .../resources/application-test.properties | 24 ++++---- 20 files changed, 256 insertions(+), 102 deletions(-) create mode 100644 src/main/java/kr/tgwing/tech/admin/dto/StudentQuery.java create mode 100644 src/main/java/kr/tgwing/tech/project/domain/ProjectSpecification.java create mode 100644 src/main/java/kr/tgwing/tech/project/dto/ProjectQuery.java create mode 100644 src/main/java/kr/tgwing/tech/user/entity/UserSpecification.java 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 2acc551..878874f 100644 --- a/src/main/java/kr/tgwing/tech/admin/controller/AdminController.java +++ b/src/main/java/kr/tgwing/tech/admin/controller/AdminController.java @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import kr.tgwing.tech.admin.dto.AdminCheckUserDto; import kr.tgwing.tech.admin.dto.AllStudentsDto; +import kr.tgwing.tech.admin.dto.StudentQuery; import kr.tgwing.tech.admin.service.AdminService; import kr.tgwing.tech.common.ApiResponse; import lombok.RequiredArgsConstructor; @@ -14,8 +15,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import java.util.List; - @RestController @RequestMapping("/admin") @RequiredArgsConstructor @@ -26,18 +25,29 @@ public class AdminController { private final AdminService adminService; @Operation(summary = "동아리 가입요청 목록 조회") - @GetMapping("") + @GetMapping public ResponseEntity>> checkAllAssignments( - @PageableDefault Pageable pageable) { - Page allAssignments = adminService.checkAssingments(pageable); + @PageableDefault Pageable pageable, + @ModelAttribute StudentQuery studentQuery + ) { + Page allAssignments = adminService.checkAssingments(studentQuery, pageable); return ResponseEntity.ok(ApiResponse.ok(allAssignments)); } + @Operation(summary = "동아리원 목록 조회") + @GetMapping("/student") + public ResponseEntity> checkAllStudents( + @PageableDefault Pageable pageable, + @ModelAttribute StudentQuery studentQuery + ) { + Page allStudents = adminService.checkAllStudents(studentQuery, pageable); + return ResponseEntity.ok(ApiResponse.ok(allStudents)); + } + @Operation(summary = "가입요청 수락") @PostMapping("/{id}") public ResponseEntity> registerAssignment(@PathVariable("id") Long id) { Long registerId = adminService.registerUsers(id); - return ResponseEntity.ok(ApiResponse.updated(registerId)); } @@ -45,16 +55,10 @@ public ResponseEntity> registerAssignment(@PathVariable("id") @DeleteMapping("/{id}") public ResponseEntity> refuseAssignment(@PathVariable("id") Long id) { Long refusedId = adminService.refuseUsers(id); - return ResponseEntity.ok(ApiResponse.delete(refusedId)); } - @Operation(summary = "동아리원 목록 조회") - @GetMapping("/student") - public ResponseEntity> checkAllStudents(@PageableDefault Pageable pageable) { - Page allStudents = adminService.checkAllStudents(pageable); - return ResponseEntity.ok(ApiResponse.ok(allStudents)); - } + @Operation(summary = "동아리원 강제 삭제") @DeleteMapping("/stdent/{id}") diff --git a/src/main/java/kr/tgwing/tech/admin/dto/StudentQuery.java b/src/main/java/kr/tgwing/tech/admin/dto/StudentQuery.java new file mode 100644 index 0000000..4af4f03 --- /dev/null +++ b/src/main/java/kr/tgwing/tech/admin/dto/StudentQuery.java @@ -0,0 +1,8 @@ +package kr.tgwing.tech.admin.dto; + +import lombok.Data; + +@Data +public class StudentQuery { + private String keyword = ""; +} diff --git a/src/main/java/kr/tgwing/tech/admin/service/AdminService.java b/src/main/java/kr/tgwing/tech/admin/service/AdminService.java index c761278..6b74be4 100644 --- a/src/main/java/kr/tgwing/tech/admin/service/AdminService.java +++ b/src/main/java/kr/tgwing/tech/admin/service/AdminService.java @@ -2,18 +2,21 @@ import kr.tgwing.tech.admin.dto.AdminCheckUserDto; import kr.tgwing.tech.admin.dto.AllStudentsDto; +import kr.tgwing.tech.admin.dto.StudentQuery; import kr.tgwing.tech.blog.entity.Comment; import kr.tgwing.tech.blog.entity.Post; import kr.tgwing.tech.blog.repository.PostRepository; import kr.tgwing.tech.blog.repository.ReplyRepository; import kr.tgwing.tech.user.entity.TempUser; import kr.tgwing.tech.user.entity.User; +import kr.tgwing.tech.user.entity.UserSpecification; import kr.tgwing.tech.user.exception.UserNotFoundException; import kr.tgwing.tech.user.repository.TempUserRepository; import kr.tgwing.tech.user.repository.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -27,10 +30,10 @@ public class AdminService { private final UserRepository userRepository; private final TempUserRepository tempUserRepository; private final PostRepository postRepository; - private final ReplyRepository replyRepository; - public Page checkAssingments(Pageable pageable) { - Page allAssignments = tempUserRepository.findAll(pageable); + public Page checkAssingments(StudentQuery studentQuery, Pageable pageable) { + Specification spec = UserSpecification.hasKeywordInTempUser(studentQuery.getKeyword()); + Page allAssignments = tempUserRepository.findAll(spec, pageable); if(allAssignments.isEmpty()) return null; // 여기 값은 예외로 보내야하느건지, 어떻게 보내야하는거지? return allAssignments.map(assignment -> AdminCheckUserDto.of(assignment)); @@ -43,6 +46,13 @@ public Page checkAssingments(Pageable pageable) { // return dtoList; } + public Page checkAllStudents(StudentQuery studentQuery, Pageable pageable) { + Specification spec = UserSpecification.hasKeywordInUser(studentQuery.getKeyword()); + Page allStudents = userRepository.findAll(spec, pageable); + if(allStudents.isEmpty()) return null; + return allStudents.map( student -> AllStudentsDto.of(student)); + } + @Transactional public Long registerUsers(Long studentId) { TempUser notUser = tempUserRepository.findById(studentId).orElseThrow(UserNotFoundException::new); @@ -61,12 +71,7 @@ public Long refuseUsers(Long studentId) { return user.getStudentId(); } - public Page checkAllStudents(Pageable pageable) { - Page allStudents = userRepository.findAll(pageable); - if(allStudents.isEmpty()) return null; - return allStudents.map( student -> AllStudentsDto.of(student)); - } - + @Transactional public void deleteStudent(Long studentId) { User user = userRepository.findById(studentId).orElseThrow(UserNotFoundException::new); diff --git a/src/main/java/kr/tgwing/tech/config/TestDataLoader.java b/src/main/java/kr/tgwing/tech/config/TestDataLoader.java index 70c1ba6..3bf63c9 100644 --- a/src/main/java/kr/tgwing/tech/config/TestDataLoader.java +++ b/src/main/java/kr/tgwing/tech/config/TestDataLoader.java @@ -158,6 +158,7 @@ public CommandLineRunner loadTestData(PostRepository postRepository, .participants(new ArrayList<>()) .build(); + Image image1 = Image.builder() .imageUrl("img1") .project(project) @@ -184,21 +185,26 @@ public CommandLineRunner loadTestData(PostRepository postRepository, Participant participant1 = Participant.builder() .part(Part.DESIGNER) + .studentNumber("2020211121") .name("design") .major("디자인과") .project(project) .build(); Participant participant2 = Participant.builder() .part(Part.BACK) - .name("song") + .studentNumber("2018000000") + .name("늙은이") .major("컴공") .project(project) + .user(writer1) .build(); Participant participant3 = Participant.builder() .part(Part.FRONT) - .name("jin") + .studentNumber("2022000000") + .name("젊은이") .project(project) .major("컴공") + .user(writer2) .build(); project.getParticipants().add(participant1); 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 fe3241a..5f85bd0 100644 --- a/src/main/java/kr/tgwing/tech/project/controller/ProjectController.java +++ b/src/main/java/kr/tgwing/tech/project/controller/ProjectController.java @@ -4,13 +4,12 @@ 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.dto.*; import kr.tgwing.tech.project.service.ProjectServiceImpl; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; @@ -26,9 +25,12 @@ public class ProjectController { private final ProjectServiceImpl projectServiceImpl; @Operation(summary = "프로젝트 전체 조회") - @GetMapping("") - public ResponseEntity getProjects(){ - List projects = projectServiceImpl.getProjects(); + @GetMapping + public ResponseEntity getProjects( + Pageable pageable, + @ModelAttribute ProjectQuery query + ){ + Page projects = projectServiceImpl.getProjects(pageable, query); return ResponseEntity.ok(ApiResponse.ok(projects)); } @@ -40,7 +42,7 @@ public ResponseEntity getOneProject(@PathVariable("project_id") Long project_ } @Operation(summary = "프로젝트 생성") - @PostMapping("") + @PostMapping public ResponseEntity postProject( @Valid @RequestBody ProjectCreateDTO projectCreateDTO) { Long projectId = projectServiceImpl.createProjects(projectCreateDTO); diff --git a/src/main/java/kr/tgwing/tech/project/domain/Participant.java b/src/main/java/kr/tgwing/tech/project/domain/Participant.java index 4453a9f..54cc86e 100644 --- a/src/main/java/kr/tgwing/tech/project/domain/Participant.java +++ b/src/main/java/kr/tgwing/tech/project/domain/Participant.java @@ -4,6 +4,7 @@ import kr.tgwing.tech.common.BaseEntity; import kr.tgwing.tech.project.domain.Enum.Part; import kr.tgwing.tech.project.dto.ParticipantDTO; +import kr.tgwing.tech.user.entity.User; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -26,6 +27,12 @@ public class Participant extends BaseEntity { @JoinColumn(name="project_id") private Project project; + @ManyToOne + @JoinColumn(name="student_id") + private User user; + + private String studentNumber; + @Enumerated(EnumType.STRING) private Part part; @@ -36,19 +43,26 @@ public class Participant extends BaseEntity { public void setProject(Project project) { this.project = project; } + public void setUser(User user) { + this.user = user; + } public static ParticipantDTO toDTO(Participant participant) { return ParticipantDTO.builder() .username(participant.getName()) + .studentNumber(participant.getStudentNumber()) .major(participant.getMajor()) .part(participant.getPart()) + .user(participant.getUser()) .build(); } @Builder - public Participant(Long id, Project project, Part part, String name, String major) { + public Participant(Long id, Project project, Part part, String name, String major, User user, String studentNumber) { this.id = id; + this.studentNumber = studentNumber; this.project = project; + this.user = user; 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 index c87ce2f..9e59d8f 100644 --- a/src/main/java/kr/tgwing/tech/project/domain/Project.java +++ b/src/main/java/kr/tgwing/tech/project/domain/Project.java @@ -31,6 +31,7 @@ public class Project extends BaseEntity { @Lob @Column(columnDefinition = "TEXT") private String description; + @DateTimeFormat(pattern = "yyyy-MM-dd") @Column(name = "start_date") private LocalDate startDate; diff --git a/src/main/java/kr/tgwing/tech/project/domain/ProjectSpecification.java b/src/main/java/kr/tgwing/tech/project/domain/ProjectSpecification.java new file mode 100644 index 0000000..d08ef57 --- /dev/null +++ b/src/main/java/kr/tgwing/tech/project/domain/ProjectSpecification.java @@ -0,0 +1,38 @@ +package kr.tgwing.tech.project.domain; + +import jakarta.persistence.criteria.Join; +import jakarta.persistence.criteria.Predicate; +import kr.tgwing.tech.user.entity.User; +import org.springframework.data.jpa.domain.Specification; + +public class ProjectSpecification { + public static Specification hasKeywordInProject(String keyword) { + return (root, query, cb) -> { + String pattern = "%" + keyword + "%"; + + Predicate title = cb.like(root.get("title"), pattern); + Predicate description = cb.like(root.get("description"), pattern); + + Join participantJoin = root.join("participant"); + Predicate name = cb.like(participantJoin.get("name"), pattern); + Predicate studentNumber = cb.like(participantJoin.get("studentNumber"), pattern); + + return cb.or(title, description, name, studentNumber); + }; + } + + public static Specification hasKeywordInMyProject(String keyword) { + return (root, query, cb) -> { + String pattern = "%" + keyword + "%"; + + Predicate title = cb.like(root.get("title"), pattern); + Predicate description = cb.like(root.get("description"), pattern); + + Join participantJoin = root.join("participant"); + Predicate studentNumber = cb.equal(participantJoin.get("studentNumber"), pattern); + + return cb.or(title, description, studentNumber); + }; + } + +} 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 d884931..55dabed 100644 --- a/src/main/java/kr/tgwing/tech/project/dto/ParticipantDTO.java +++ b/src/main/java/kr/tgwing/tech/project/dto/ParticipantDTO.java @@ -2,6 +2,7 @@ import kr.tgwing.tech.project.domain.Enum.Part; import kr.tgwing.tech.project.domain.Participant; +import kr.tgwing.tech.user.entity.User; import lombok.AccessLevel; import lombok.Builder; import lombok.Data; @@ -11,19 +12,26 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) public class ParticipantDTO { private Part part; + private String studentNumber; private String username; private String major; + private User user; @Builder - public ParticipantDTO(Part part, String username, String major) { + public ParticipantDTO(Part part, String username, String major, String studentNumber, User user) { this.part = part; + this.studentNumber = studentNumber; + this.user = user; this.username = username; this.major = major; } + public static Participant toParticipantEntity(ParticipantDTO participantDTO){ return Participant.builder() .part(participantDTO.getPart()) + .studentNumber(participantDTO.getStudentNumber()) + .user(participantDTO.getUser()) .name(participantDTO.getUsername()) .major(participantDTO.getMajor()) .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 12bf87c..5dd4f0a 100644 --- a/src/main/java/kr/tgwing/tech/project/dto/ProjectBriefDTO.java +++ b/src/main/java/kr/tgwing/tech/project/dto/ProjectBriefDTO.java @@ -1,12 +1,12 @@ package kr.tgwing.tech.project.dto; +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; @Data @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -31,4 +31,17 @@ public ProjectBriefDTO(Long id, String title, LocalDate start, LocalDate end, St this.devStatus = devStatus; this.devType = devType; } + + public static ProjectBriefDTO of(Project project) { + return ProjectBriefDTO.builder() + .id(project.getId()) + .title(project.getTitle()) + .start(project.getStartDate()) + .end(project.getEndDate()) + .description(project.getDescription()) + .thumbnail(project.getImageUrls().stream().findFirst().get().getImageUrl()) + .devStatus(project.getDevStatus()) + .devType(project.getDevType()) + .build(); + } } diff --git a/src/main/java/kr/tgwing/tech/project/dto/ProjectQuery.java b/src/main/java/kr/tgwing/tech/project/dto/ProjectQuery.java new file mode 100644 index 0000000..73f334f --- /dev/null +++ b/src/main/java/kr/tgwing/tech/project/dto/ProjectQuery.java @@ -0,0 +1,8 @@ +package kr.tgwing.tech.project.dto; + +import lombok.Data; + +@Data +public class ProjectQuery { + private String keyword = ""; +} diff --git a/src/main/java/kr/tgwing/tech/project/repository/ProjectRepository.java b/src/main/java/kr/tgwing/tech/project/repository/ProjectRepository.java index 7cd95bf..503729f 100644 --- a/src/main/java/kr/tgwing/tech/project/repository/ProjectRepository.java +++ b/src/main/java/kr/tgwing/tech/project/repository/ProjectRepository.java @@ -2,8 +2,8 @@ import kr.tgwing.tech.project.domain.Project; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface ProjectRepository extends JpaRepository, JpaSpecificationExecutor { -@Repository -public interface ProjectRepository extends JpaRepository { } 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 d948c12..64cfde3 100644 --- a/src/main/java/kr/tgwing/tech/project/service/ProjectServiceImpl.java +++ b/src/main/java/kr/tgwing/tech/project/service/ProjectServiceImpl.java @@ -1,17 +1,20 @@ package kr.tgwing.tech.project.service; -import kr.tgwing.tech.project.domain.Image; -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.domain.*; import kr.tgwing.tech.project.dto.*; import kr.tgwing.tech.project.exception.ProjectNotFoundException; import kr.tgwing.tech.project.repository.ImageRepository; import kr.tgwing.tech.project.repository.ParticipantRepository; import kr.tgwing.tech.project.repository.LinkRepository; import kr.tgwing.tech.project.repository.ProjectRepository; +import kr.tgwing.tech.user.entity.User; +import kr.tgwing.tech.user.exception.UserNotFoundException; +import kr.tgwing.tech.user.repository.UserRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -27,20 +30,24 @@ public class ProjectServiceImpl{ private final ParticipantRepository participantRepository; private final LinkRepository linkRepository; private final ImageRepository imageRepository; + private final UserRepository userRepository; - /* - * 페이지네이션 안함. - * */ @Transactional - public List getProjects() { - List projects = projectRepository.findAll(); - if(projects == null){ return null; } - else { return projects.stream() - .map(ProjectServiceImpl::entity2ProjectBriefDTO) - .toList(); } + public Page getProjects(Pageable pageable, ProjectQuery query) { + Specification spec = ProjectSpecification.hasKeywordInProject(query.getKeyword()); + Page projects = projectRepository.findAll(spec, pageable); + if(projects == null) return null; + return projects.map( project -> ProjectBriefDTO.of(project)); } public Long createProjects(ProjectCreateDTO projectCreateDTO) { + projectCreateDTO.getParticipants().stream().forEach( + participantDTO -> { + Optional user = userRepository.findByStudentNumber(participantDTO.getStudentNumber()); + if(user.isPresent()) participantDTO.setUser(user.get()); + } + ); + Project project = projectCreateDTO.toEntity(projectCreateDTO); project.getParticipants().stream().forEach(participant -> participant.setProject(project)); project.getLinks().stream().forEach(link -> link.setProject(project)); @@ -61,14 +68,6 @@ public ProjectDetailDTO getOneProject(Long project_id) { public Long updateProject(ProjectUpdateDTO projectUpdateDTO, Long project_id) { Project findProject = projectRepository.findById(project_id) .orElseThrow(ProjectNotFoundException::new); -// findProject.updateProject(projectUpdateDTO); -// -// findProject.setParticipants(projectUpdateDTO.getParticipants()); -// findProject.getParticipants().forEach(participant -> participant.setProject(findProject)); -// findProject.setLinks(projectUpdateDTO.getLinks()); -// findProject.getLinks().forEach(link -> link.setProject(findProject)); -// findProject.setImageUrls(projectUpdateDTO.getImageUrls()); -// findProject.getImageUrls().forEach(url -> url.setProject(findProject)); List findParticipants = participantRepository.findAllByProjectId(project_id); List findLinks = linkRepository.findAllByProjectId(project_id); @@ -124,6 +123,8 @@ public void updateParticipants(Project project, List findParticipan for (Participant newParticipant : newParticipants) { if (!findParticipants.contains(newParticipant)) { + Optional user = userRepository.findByStudentNumber(newParticipant.getStudentNumber()); + if(user.isPresent()) newParticipant.setUser(user.get()); newParticipant.setProject(project); participantRepository.save(newParticipant); } @@ -146,18 +147,6 @@ public void updateImage(Project project, List findImgs, List newIm } } - public static ProjectBriefDTO entity2ProjectBriefDTO(Project project){ - return ProjectBriefDTO.builder() - .id(project.getId()) - .title(project.getTitle()) - .start(project.getStartDate()) - .end(project.getEndDate()) - .description(project.getDescription()) - .thumbnail(project.getImageUrls().stream().findFirst().get().getImageUrl()) - .devStatus(project.getDevStatus()) - .devType(project.getDevType()) - .build(); - } public static ProjectDetailDTO entity2ProjectDetailDTO(Project project){ return ProjectDetailDTO.builder() 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 4e38f89..44d1bdf 100644 --- a/src/main/java/kr/tgwing/tech/user/controller/ProfileController.java +++ b/src/main/java/kr/tgwing/tech/user/controller/ProfileController.java @@ -4,16 +4,13 @@ import java.util.List; import kr.tgwing.tech.blog.dto.PostOverview; +import kr.tgwing.tech.project.dto.ProjectBriefDTO; +import kr.tgwing.tech.project.dto.ProjectQuery; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -33,40 +30,46 @@ public class ProfileController { private final UserService userService; @Operation(summary = "회원정보 조회") - @GetMapping("") + @GetMapping public ResponseEntity> showProfile( Principal principal){ ProfileDTO profileDTO = userService.showUser(principal.getName()); - return ResponseEntity.ok(ApiResponse.ok(profileDTO)); } @Operation(summary = "내 글 가져오기" ) @GetMapping("/blog") - public ResponseEntity>> showMyPost( + public ResponseEntity>> getMyPost( Principal principal, @PageableDefault Pageable pageable){ - Page blog = userService.showMyBlog(principal.getName(), pageable); + Page blog = userService.getMyBlog(principal.getName(), pageable); return ResponseEntity.ok(ApiResponse.ok(blog)); } @Operation(summary = "회원정보 수정" ) - @PutMapping("") + @PutMapping public ResponseEntity> changeProfile( @RequestBody ProfileReqDTO request, Principal principal) { Long change = userService.changeUser(principal.getName(), request); - return ResponseEntity.ok(ApiResponse.updated(change)); } @Operation(summary = "회원 탈퇴") - @DeleteMapping("") + @DeleteMapping public ResponseEntity> removeProfile(Principal principal){ Long remove = userService.removeUser(principal.getName()); - return ResponseEntity.ok(ApiResponse.delete(remove)); } -// @GetMapping("/myPosting") + + @Operation(summary = "내 프로젝트 가져오기") + @GetMapping("/myPosting") + public ResponseEntity getMyProject( + Principal principal, + @PageableDefault Pageable pageable, + @ModelAttribute ProjectQuery query) { + Page myProjects = userService.getMyProject(pageable, query, principal.getName()); + return ResponseEntity.ok(ApiResponse.created(myProjects)); + } } diff --git a/src/main/java/kr/tgwing/tech/user/entity/UserSpecification.java b/src/main/java/kr/tgwing/tech/user/entity/UserSpecification.java new file mode 100644 index 0000000..eeb84fb --- /dev/null +++ b/src/main/java/kr/tgwing/tech/user/entity/UserSpecification.java @@ -0,0 +1,33 @@ +package kr.tgwing.tech.user.entity; + +import jakarta.persistence.criteria.Predicate; +import org.springframework.data.jpa.domain.Specification; + + +public class UserSpecification { + + // 다중 검색 기능(학번, 이름, 이메일) -> 동아리원 + public static Specification hasKeywordInUser(String keyword) { + return (root, query, cb) -> { + String pattern = "%" + keyword + "%"; + + Predicate studentNumberPredicate = cb.like(root.get("studentNumber"), pattern); + Predicate namePredicate = cb.like(root.get("name"), pattern); + Predicate emailPredicate = cb.like(root.get("email"), pattern); + + return cb.or(studentNumberPredicate, namePredicate, emailPredicate); + }; + } + + public static Specification hasKeywordInTempUser(String keyword) { + return (root, query, cb) -> { + String pattern = "%" + keyword + "%"; + + Predicate studentNumberPredicate = cb.like(root.get("studentNumber"), pattern); + Predicate namePredicate = cb.like(root.get("name"), pattern); + Predicate emailPredicate = cb.like(root.get("email"), pattern); + + return cb.or(studentNumberPredicate, namePredicate, emailPredicate); + }; + } +} diff --git a/src/main/java/kr/tgwing/tech/user/repository/TempUserRepository.java b/src/main/java/kr/tgwing/tech/user/repository/TempUserRepository.java index a1a1387..11e782a 100644 --- a/src/main/java/kr/tgwing/tech/user/repository/TempUserRepository.java +++ b/src/main/java/kr/tgwing/tech/user/repository/TempUserRepository.java @@ -2,7 +2,10 @@ import kr.tgwing.tech.user.entity.TempUser; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -public interface TempUserRepository extends JpaRepository { +import java.awt.print.Pageable; + +public interface TempUserRepository extends JpaRepository, JpaSpecificationExecutor { } 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 340b4fa..9d3894b 100644 --- a/src/main/java/kr/tgwing/tech/user/repository/UserRepository.java +++ b/src/main/java/kr/tgwing/tech/user/repository/UserRepository.java @@ -3,6 +3,7 @@ import jakarta.transaction.Transactional; import kr.tgwing.tech.user.entity.User; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; @@ -11,7 +12,7 @@ import java.util.Optional; @Repository -public interface UserRepository extends JpaRepository { +public interface UserRepository extends JpaRepository, JpaSpecificationExecutor { Optional findByStudentNumber(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 a029ceb..050fca5 100644 --- a/src/main/java/kr/tgwing/tech/user/service/UserService.java +++ b/src/main/java/kr/tgwing/tech/user/service/UserService.java @@ -5,6 +5,8 @@ import kr.tgwing.tech.blog.dto.PostOverview; import kr.tgwing.tech.blog.entity.Post; +import kr.tgwing.tech.project.dto.ProjectBriefDTO; +import kr.tgwing.tech.project.dto.ProjectQuery; import kr.tgwing.tech.user.dto.EmailMessageDTO; import kr.tgwing.tech.user.dto.checkdto.CheckNumberDTO; import kr.tgwing.tech.user.dto.checkdto.CheckUserDTO; @@ -28,7 +30,7 @@ public interface UserService{ ProfileDTO showUser(String name); - Page showMyBlog(String studentNumber, Pageable pageable); + Page getMyBlog(String studentNumber, Pageable pageable); void checkUser(CheckUserDTO checkUserDTO); // 본인 확인하기 @@ -37,4 +39,6 @@ public interface UserService{ Long setNewPassword(String studentNumber, PasswordCheckDTO password); void checkCode(String code, CheckNumberDTO checkNumberDTO); + + Page getMyProject(Pageable pageable, ProjectQuery query, String studentNumber); } 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 4a4ef56..56dfb33 100644 --- a/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java +++ b/src/main/java/kr/tgwing/tech/user/service/UserServiceImpl.java @@ -6,8 +6,14 @@ import jakarta.mail.MessagingException; import jakarta.mail.internet.MimeMessage; +import kr.tgwing.tech.project.domain.Project; +import kr.tgwing.tech.project.domain.ProjectSpecification; +import kr.tgwing.tech.project.dto.ProjectBriefDTO; +import kr.tgwing.tech.project.dto.ProjectQuery; +import kr.tgwing.tech.project.repository.ProjectRepository; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @@ -52,6 +58,7 @@ public class UserServiceImpl implements UserService { private final BCryptPasswordEncoder bCryptPasswordEncoder; private final TempUserRepository tempUserRepository; private final LikeHistoryRepository likeHistoryRepository; + private final ProjectRepository projectRepository; @Override public Long register(UserDTO userDTO){ @@ -101,7 +108,7 @@ public ProfileDTO showUser(String studentNumber){ } @Override - public Page showMyBlog(String studentNumber, Pageable pageable){ + public Page getMyBlog(String studentNumber, Pageable pageable){ User user = userRepository.findByStudentNumber(studentNumber) .orElseThrow(UserNotFoundException::new); @@ -141,6 +148,13 @@ public String sendEmail(EmailMessageDTO emailMessageDTO) { return authNum; } + @Override + public Page getMyProject(Pageable pageable, ProjectQuery query, String studentNumber) { + Specification spec = ProjectSpecification.hasKeywordInMyProject(query.getKeyword()); + Page myProjects = projectRepository.findAll(spec, pageable); + return myProjects.map(myProject -> ProjectBriefDTO.of(myProject)); + } + public String createCode() { Random random = new Random(); StringBuffer key = new StringBuffer(); diff --git a/src/main/resources/application-test.properties b/src/main/resources/application-test.properties index ee83716..626632e 100644 --- a/src/main/resources/application-test.properties +++ b/src/main/resources/application-test.properties @@ -1,12 +1,12 @@ -spring.application.name=tech -server.servlet.context-path=/api - -# Database connection -spring.datasource.url=jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1 -spring.datasource.username=sa -spring.datasource.password= -spring.datasource.driver-class-name=org.h2.Driver -# spring.datasource.dialect=org.hibernate.dialect.H2Dialect -spring.jpa.database-platform=org.hibernate.dialect.H2Dialect - -spring.jpa.hibernate.ddl-auto=update +#spring.application.name=tech +#server.servlet.context-path=/api +# +## Database connection +#spring.datasource.url=jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1 +#spring.datasource.username=sa +#spring.datasource.password= +#spring.datasource.driver-class-name=org.h2.Driver +## spring.datasource.dialect=org.hibernate.dialect.H2Dialect +#spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +# +#spring.jpa.hibernate.ddl-auto=update