Skip to content

Commit

Permalink
#274 Feat: 개인 프로젝트 생성 및 조회 API
Browse files Browse the repository at this point in the history
  • Loading branch information
yumzen committed Nov 19, 2024
1 parent 5d448ac commit ef78270
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,8 @@ public void setMemberProjectMapList(List<MemberProjectMap> projects) {
public void setTeamMemberList(List<TeamMember> teamMembers) {
this.teamMemberList = teamMembers;
}

public void addProject(MemberProjectMap memberProjectMap) {
memberProjectMapList.add(memberProjectMap);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,10 @@ public class MemberProjectMap {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "project_id")
private Project project;

@Builder
public MemberProjectMap(Member member, Project project) {
this.member = member;
this.project = project;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.codiary.backend.domain.project.controller;

import com.codiary.backend.domain.member.security.CustomMemberDetails;
import com.codiary.backend.domain.project.converter.ProjectConverter;
import com.codiary.backend.domain.project.dto.response.ProjectResponseDTO;
import com.codiary.backend.domain.project.service.ProjectService;
import com.codiary.backend.global.apiPayload.ApiResponse;
import com.codiary.backend.global.apiPayload.code.status.SuccessStatus;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import com.codiary.backend.domain.project.entity.Project;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v2/project")
@Tag(name = "프로젝트 API", description = "프로젝트 조회 관련 API입니다.")
public class ProjectController {

private final ProjectService projectService;

@Operation(summary = "개인 프로젝트 생성", description = "개인 프로젝트를 생성합니다.")
@PostMapping("/create/{project_name}")
public ApiResponse<ProjectResponseDTO.SimpleProjectResponseDTO> createPersonalProject(@AuthenticationPrincipal CustomMemberDetails memberDetails,
@PathVariable("project_name") String projectName) {
Project project = projectService.createPersonalProject(memberDetails.getId(), projectName);
return ApiResponse.onSuccess(SuccessStatus.PROJECT_OK, ProjectConverter.toSimpleProjectResponseDTO(project));
}

@Operation(summary = "나의 프로젝트 조회", description = "프로젝트를 조회합니다.")
@GetMapping("/my")
public ApiResponse<List<ProjectResponseDTO.SimpleProjectResponseDTO>> getMyProject(@AuthenticationPrincipal CustomMemberDetails memberDetails) {
List<Project> projects = projectService.getMyProject(memberDetails.getId());
return ApiResponse.onSuccess(SuccessStatus.PROJECT_OK, ProjectConverter.toSimpleProjectListResponseDTO(projects));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.codiary.backend.domain.project.converter;

import com.codiary.backend.domain.member.converter.MemberConverter;
import com.codiary.backend.domain.project.entity.Project;
import com.codiary.backend.domain.project.dto.response.ProjectResponseDTO;

import java.util.List;
import java.util.stream.Collectors;

public class ProjectConverter {
public static ProjectResponseDTO.ProjectDetailResponseDTO toProjectDetailResponseDTO(Project project) {
return ProjectResponseDTO.ProjectDetailResponseDTO.builder()
.projectId(project.getProjectId())
.name(project.getProjectName())
.members(project.getMemberProjectMaps().stream()
.map(memberProjectMap -> MemberConverter.tosimpleMemberProfileResponseDto(memberProjectMap.getMember()))
.collect(Collectors.toList()))
.build();
}

public static ProjectResponseDTO.SimpleProjectResponseDTO toSimpleProjectResponseDTO(Project project) {
return ProjectResponseDTO.SimpleProjectResponseDTO.builder()
.projectId(project.getProjectId())
.name(project.getProjectName())
.build();
}

public static List<ProjectResponseDTO.SimpleProjectResponseDTO> toSimpleProjectListResponseDTO(List<Project> projects) {
return projects.stream()
.map(ProjectConverter::toSimpleProjectResponseDTO)
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
package com.codiary.backend.domain.project.dto.response;

import com.codiary.backend.domain.member.dto.response.MemberResponseDTO;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import lombok.Builder;

import java.util.List;

public class ProjectResponseDTO {

@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public record SimpleProjectResponseDTO(
Long projectId,
String name
) {
}

@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public record ProjectDetailResponseDTO(
Long projectId,
String name,
List<MemberResponseDTO.SimpleMemberProfileDTO> members
) {
}
}
Original file line number Diff line number Diff line change
@@ -1,28 +1,35 @@
package com.codiary.backend.domain.project.entity;

import com.codiary.backend.domain.member.entity.MemberProjectMap;
import com.codiary.backend.domain.post.entity.Post;
import com.codiary.backend.global.common.BaseEntity;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.SQLDelete;

import java.util.ArrayList;
import java.util.List;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Project {
@SQLDelete(sql = "UPDATE project SET deleted_at = NOW() WHERE project_id = ?")
public class Project extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "project_id", nullable = false, columnDefinition = "bigint")
private Long projectId;

@OneToMany(mappedBy = "project", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Post> postList = new ArrayList<>();

@Column(name="project_name", nullable = false, columnDefinition = "varchar(256)")
private String projectName;

@OneToMany(mappedBy = "project")
private List<MemberProjectMap> memberProjectMaps = new ArrayList<>();

@OneToMany(mappedBy = "project")
private List<Post> posts = new ArrayList<>();

@Builder
public Project(String projectName) {
this.projectName = projectName;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package com.codiary.backend.domain.project.repository;

import com.codiary.backend.domain.member.entity.Member;
import com.codiary.backend.domain.project.entity.Project;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;
import java.util.Optional;

public interface ProjectRepository extends JpaRepository<Project, Long>, ProjectRepositoryCustom {

List<Project> findAllByOrderByProjectIdDesc();

Optional<Project> findByProjectNameAndDeletedAtIsNull(String projectName);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.codiary.backend.domain.project.repository;

import com.codiary.backend.domain.member.entity.Member;
import com.codiary.backend.domain.project.entity.Project;

import java.time.LocalDate;
Expand All @@ -8,4 +9,6 @@

public interface ProjectRepositoryCustom {
Map<LocalDate, List<Project>> findProjectsForCalendar(Long memberId, LocalDate startDate, LocalDate endDate);

List<Project> findByMemberProjectMapsMember(Member member);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.codiary.backend.domain.project.repository;

import com.codiary.backend.domain.member.entity.Member;
import com.codiary.backend.domain.post.entity.Post;
import com.codiary.backend.domain.project.entity.Project;
import com.querydsl.jpa.impl.JPAQueryFactory;
Expand Down Expand Up @@ -35,4 +36,12 @@ public Map<LocalDate, List<Project>> findProjectsForCalendar(Long memberId, Loca
Collectors.mapping(Post::getProject, Collectors.collectingAndThen(Collectors.toSet(), ArrayList::new))
));
}

public List<Project> findByMemberProjectMapsMember(Member member) {
return queryFactory
.selectFrom(project)
.leftJoin(project.memberProjectMaps).fetchJoin()
.where(project.memberProjectMaps.any().member.eq(member))
.fetch();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.codiary.backend.domain.project.service;

import com.codiary.backend.domain.member.entity.Member;
import com.codiary.backend.domain.member.entity.MemberProjectMap;
import com.codiary.backend.domain.project.entity.Project;
import com.codiary.backend.domain.member.repository.MemberRepository;
import com.codiary.backend.domain.project.repository.ProjectRepository;
import com.codiary.backend.global.apiPayload.code.status.ErrorStatus;
import com.codiary.backend.global.apiPayload.exception.GeneralException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Transactional(readOnly = true)
@RequiredArgsConstructor
@Service
public class ProjectService {
private final ProjectRepository projectRepository;
private final MemberRepository memberRepository;

@Transactional
public Project createPersonalProject(Long memberId, String projectName) {
//validation
Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND));
Project project = projectRepository.findByProjectNameAndDeletedAtIsNull(projectName)
.orElse(null);

//business
if (project != null) { // 프로젝트 이름 중복 확인
throw new GeneralException(ErrorStatus.PROJECT_ALREADY_EXISTS);
} else {
project = Project.builder()
.projectName(projectName)
.build();
projectRepository.save(project);

MemberProjectMap memberProjectMap = MemberProjectMap.builder()
.member(member)
.project(project)
.build();

member.addProject(memberProjectMap);
}

//return
return project;
}

public List<Project> getMyProject(Long id) {
//validation
Member member = memberRepository.findById(id)
.orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND));

//return
return projectRepository.findByMemberProjectMapsMember(member);
}
}

0 comments on commit ef78270

Please sign in to comment.