Skip to content

Commit

Permalink
Merge pull request #198 from Gongjakso/feat/portfolio
Browse files Browse the repository at this point in the history
feat: 포트폴리오 CRUD API (공작소 양식) 개발
  • Loading branch information
dl-00-e8 authored Sep 12, 2024
2 parents 421d3c9 + 6587c9f commit 192ba85
Show file tree
Hide file tree
Showing 9 changed files with 376 additions and 3 deletions.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ dependencies {
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

// Json Type Parsing
implementation 'com.fasterxml.jackson.module:jackson-module-jakarta-xmlbind-annotations'
}


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.gongjakso.server.domain.portfolio.controller;

import com.gongjakso.server.domain.portfolio.dto.request.PortfolioReq;
import com.gongjakso.server.domain.portfolio.dto.response.PortfolioRes;
import com.gongjakso.server.domain.portfolio.service.PortfolioService;
import com.gongjakso.server.global.common.ApplicationResponse;
import com.gongjakso.server.global.security.PrincipalDetails;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
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;

@RestController
@RequiredArgsConstructor
@RequestMapping("api/v2/mypage/portfolio")
public class PortfolioController {

private final PortfolioService portfolioService;

@Operation(description = "포트폴리오 등록 API")
@PostMapping("")
public ApplicationResponse<PortfolioRes> registerPortfolio(@AuthenticationPrincipal PrincipalDetails principalDetails,
@Valid @RequestBody PortfolioReq portfolioReq) {
return ApplicationResponse.ok(portfolioService.registerPortfolio(principalDetails.getMember(), portfolioReq));
}

@Operation(description = "포트폴리오 상세 조회 API")
@GetMapping("/{portfolio_id}")
public ApplicationResponse<PortfolioRes> getPortfolio(@AuthenticationPrincipal PrincipalDetails principalDetails,
@PathVariable("portfolio_id") Long portfolioId) {
return ApplicationResponse.ok(portfolioService.getPortfolio(principalDetails.getMember(), portfolioId));
}

@Operation(description = "포트폴리오 수정 API")
@PutMapping("/{portfolio_id}")
public ApplicationResponse<PortfolioRes> updatePortfolio(@AuthenticationPrincipal PrincipalDetails principalDetails,
@PathVariable("portfolio_id") Long portfolioId,
@Valid @RequestBody PortfolioReq portfolioReq) {
return ApplicationResponse.ok(portfolioService.updatePortfolio(principalDetails.getMember(), portfolioId, portfolioReq));
}

@Operation(description = "포트폴리오 삭제 API")
@DeleteMapping("/{portfolio_id}")
public ApplicationResponse<Void> deletePortfolio(@AuthenticationPrincipal PrincipalDetails principalDetails,
@PathVariable("portfolio_id") Long portfolioId) {
portfolioService.deletePortfolio(principalDetails.getMember(), portfolioId);

return ApplicationResponse.ok();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.gongjakso.server.domain.portfolio.dto.request;

import java.time.LocalDate;
import java.util.List;
import lombok.Builder;

@Builder
public record PortfolioReq (
List<Education> educationList,
List<Work> workList,
List<Activty> activityList,
List<Award> awardList,
List<Certificate> certificateList,
List<Sns> snsList
) {
public record Education (
String school,
String grade,
Boolean isActive
) {
}

public record Work (
String company,
String partition,
LocalDate enteredAt,
LocalDate exitedAt,
Boolean isActive,
String detail
) {
}

public record Activty (
String name,
Boolean isActive
) {
}
public record Award (
String contestName,
String awardName,
LocalDate awardDate
) {
}
public record Certificate (
String name,
String rating,
LocalDate certificationDate
) {
}
public record Sns (
String snsLink
) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.gongjakso.server.domain.portfolio.dto.response;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.gongjakso.server.domain.portfolio.entity.Portfolio;
import com.gongjakso.server.domain.portfolio.vo.PortfolioData;
import lombok.Builder;
import java.util.List;

@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
public record PortfolioRes (
Long id,
List<PortfolioData.Education> educationList,
List<PortfolioData.Work> workList,
List<PortfolioData.Activity> activityList,
List<PortfolioData.Award> awardList,
List<PortfolioData.Certificate> certificateList,
List<PortfolioData.Sns> snsList
) {
public static PortfolioRes from(Portfolio portfolio) {
PortfolioData portfolioData = portfolio.getPortfolioData();
return new PortfolioRes(
portfolio.getId(),
portfolioData.educationList(),
portfolioData.workList(),
portfolioData.activityList(),
portfolioData.awardList(),
portfolioData.certificateList(),
portfolioData.snsList()
);
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,45 @@
package com.gongjakso.server.domain.portfolio.entity;

import com.gongjakso.server.domain.member.entity.Member;
import com.gongjakso.server.domain.portfolio.vo.PortfolioData;
import com.gongjakso.server.global.common.BaseTimeEntity;
import jakarta.persistence.*;
import java.time.LocalDate;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.type.SqlTypes;

@Getter
@Entity
@Table(name = "portfolio")
@SQLDelete(sql = "UPDATE portfolio SET deleted_at = NOW() where id = ?")
@SQLDelete(sql = "UPDATE portfolio SET deleted_at = NOW() where portfolio_id = ?")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Portfolio extends BaseTimeEntity {

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

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;

@Column(name = "portfolio_data", columnDefinition = "json")
@JdbcTypeCode(SqlTypes.JSON)
private PortfolioData portfolioData;

@Builder
public Portfolio(Member member, PortfolioData portfolioData) {
this.member = member;
this.portfolioData = portfolioData;
}

public void update(PortfolioData updatedData) {
this.portfolioData = updatedData;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.gongjakso.server.domain.portfolio.repository;

import com.gongjakso.server.domain.portfolio.entity.Portfolio;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface PortfolioRepository extends JpaRepository<Portfolio, Long> {
Optional<Portfolio> findByIdAndDeletedAtIsNull(Long portfolioId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package com.gongjakso.server.domain.portfolio.service;

import com.gongjakso.server.domain.member.entity.Member;
import com.gongjakso.server.domain.portfolio.dto.request.PortfolioReq;
import com.gongjakso.server.domain.portfolio.dto.response.PortfolioRes;
import com.gongjakso.server.domain.portfolio.entity.Portfolio;
import com.gongjakso.server.domain.portfolio.vo.PortfolioData;
import com.gongjakso.server.domain.portfolio.repository.PortfolioRepository;
import com.gongjakso.server.global.exception.ApplicationException;
import com.gongjakso.server.global.exception.ErrorCode;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class PortfolioService {

private final PortfolioRepository portfolioRepository;

// PortfolioReq -> PortfolioData 변환
private PortfolioData convertToPortfolioData(PortfolioReq portfolioReq) {
List<PortfolioData.Education> educationList = portfolioReq.educationList() != null
? portfolioReq.educationList().stream()
.map(education -> new PortfolioData.Education(
education.school(),
education.grade(),
education.isActive()
))
.toList()
: List.of();

List<PortfolioData.Work> workList = portfolioReq.workList() != null
? portfolioReq.workList().stream()
.map(work -> new PortfolioData.Work(
work.company(),
work.partition(),
work.enteredAt(),
work.exitedAt(),
work.isActive(),
work.detail()
))
.toList()
: List.of();

List<PortfolioData.Activity> activityList = portfolioReq.activityList() != null
? portfolioReq.activityList().stream()
.map(activity -> new PortfolioData.Activity(
activity.name(),
activity.isActive()
))
.toList()
: List.of();

List<PortfolioData.Award> awardList = portfolioReq.awardList() != null
? portfolioReq.awardList().stream()
.map(award -> new PortfolioData.Award(
award.contestName(),
award.awardName(),
award.awardDate()
))
.toList()
: List.of();

List<PortfolioData.Certificate> certificateList = portfolioReq.certificateList() != null
? portfolioReq.certificateList().stream()
.map(certificate -> new PortfolioData.Certificate(
certificate.name(),
certificate.rating(),
certificate.certificationDate()
))
.toList()
: List.of();

List<PortfolioData.Sns> snsList = portfolioReq.snsList() != null
? portfolioReq.snsList().stream()
.map(sns -> new PortfolioData.Sns(
sns.snsLink()
))
.toList()
: List.of();

return new PortfolioData(educationList, workList, activityList, awardList, certificateList, snsList);
}

@Transactional
public PortfolioRes registerPortfolio(Member member, PortfolioReq portfolioReq) {
if (member == null) {
throw new ApplicationException(ErrorCode.UNAUTHORIZED_EXCEPTION);
}
PortfolioData portfolioData = convertToPortfolioData(portfolioReq);
Portfolio portfolio = Portfolio.builder()
.member(member)
.portfolioData(portfolioData)
.build();
Portfolio savedPortfolio = portfolioRepository.save(portfolio);

return PortfolioRes.from(savedPortfolio);
}

public PortfolioRes getPortfolio(Member member, Long portfolioId) {
Portfolio portfolio = portfolioRepository.findByIdAndDeletedAtIsNull(portfolioId)
.orElseThrow(() -> new ApplicationException(ErrorCode.PORTFOLIO_NOT_FOUND_EXCEPTION));
if (!portfolio.getMember().getId().equals(member.getId())) {
throw new ApplicationException(ErrorCode.FORBIDDEN_EXCEPTION);
}

return PortfolioRes.from(portfolio);
}

@Transactional
public PortfolioRes updatePortfolio(Member member, Long portfolioId, PortfolioReq portfolioReq) {
Portfolio portfolio = portfolioRepository.findById(portfolioId)
.orElseThrow(() -> new ApplicationException(ErrorCode.PORTFOLIO_NOT_FOUND_EXCEPTION));
if (!portfolio.getMember().getId().equals(member.getId())) {
throw new ApplicationException(ErrorCode.FORBIDDEN_EXCEPTION);
}
PortfolioData updatedPortfolioData = convertToPortfolioData(portfolioReq);
portfolio.update(updatedPortfolioData);
Portfolio updatedPortfolio = portfolioRepository.save(portfolio);

return PortfolioRes.from(updatedPortfolio);
}

@Transactional
public void deletePortfolio(Member member, Long portfolioId) {
Portfolio portfolio = portfolioRepository.findById(portfolioId)
.orElseThrow(() -> new ApplicationException(ErrorCode.PORTFOLIO_NOT_FOUND_EXCEPTION));
if (portfolio.getDeletedAt() != null) {
throw new ApplicationException(ErrorCode.ALREADY_DELETE_EXCEPTION);
}
if (!portfolio.getMember().getId().equals(member.getId())) {
throw new ApplicationException(ErrorCode.FORBIDDEN_EXCEPTION);
}
portfolioRepository.delete(portfolio);
}
}
Loading

0 comments on commit 192ba85

Please sign in to comment.