Skip to content

Commit

Permalink
Merge pull request #199 from Gongjakso/feat/apply
Browse files Browse the repository at this point in the history
feat: Apply 관련 기능 개발
  • Loading branch information
yumzen authored Sep 16, 2024
2 parents 1ad330b + 3e3cc09 commit 59a55cc
Show file tree
Hide file tree
Showing 23 changed files with 757 additions and 123 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.gongjakso.server.domain.apply.controller;

import com.gongjakso.server.domain.apply.service.ApplyService;
import com.gongjakso.server.domain.apply.dto.request.ApplyReq;
import com.gongjakso.server.domain.apply.dto.response.ApplyRes;
import com.gongjakso.server.domain.apply.dto.request.StatusReq;
import com.gongjakso.server.global.common.ApplicationResponse;
import com.gongjakso.server.global.security.PrincipalDetails;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;


@RestController
@RequestMapping("/api/v2/apply")
@RequiredArgsConstructor
@Tag(name = "Apply", description = "지원 관련 API")
public class ApplyController {

private final ApplyService applyService;

@Operation(summary = "지원하기", description = "팀에 지원하는 API")
@PostMapping("/{team_id}")
public ApplicationResponse<ApplyRes> apply(@AuthenticationPrincipal PrincipalDetails principalDetails,
@PathVariable("team_id") Long teamId,
@Valid @RequestBody ApplyReq req) {
return ApplicationResponse.ok(applyService.apply(principalDetails.getMember(), teamId, req));
}

@Operation(summary = "내가 지원한 팀 조회", description = "내가 지원한 팀을 페이징 조회하는 API")
@GetMapping("/my")
public ApplicationResponse<Page<ApplyRes>> getMyApplies(@AuthenticationPrincipal PrincipalDetails principalDetails,
@RequestParam Long page) {
Pageable pageable = PageRequest.of(page.intValue(), 6);
return ApplicationResponse.ok(applyService.getMyApplies(principalDetails.getMember(), pageable));
}

@Operation(summary = "특정 지원자 지원서 열람하기", description = "특정 지원자의 지원서를 열람하는 API")
@GetMapping("/{apply_id}")
public ApplicationResponse<ApplyRes> getApply(@AuthenticationPrincipal PrincipalDetails principalDetails,
@PathVariable("apply_id") Long applyId) {
return ApplicationResponse.ok(applyService.getApply(principalDetails.getMember(), applyId));
}

@Operation(summary = "지원서 선발/미선발", description = "지원자를 선발/미선발하는 API")
@PatchMapping("/select/{apply_id}")
public ApplicationResponse<ApplyRes> selectApply(@AuthenticationPrincipal PrincipalDetails principalDetails,
@PathVariable("apply_id") Long applyId,
@Valid @RequestBody StatusReq req) {
return ApplicationResponse.ok(applyService.selectApply(principalDetails.getMember(), applyId, req));
}

@Operation(summary = "지원 취소", description = "지원자가 지원 취소하는 API")
@DeleteMapping("/{apply_id}")
public ApplicationResponse<Void> cancelApply(@AuthenticationPrincipal PrincipalDetails principalDetails,
@PathVariable("apply_id") Long applyId) {
applyService.cancelApply(principalDetails.getMember(), applyId);
return ApplicationResponse.ok();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.gongjakso.server.domain.apply.dto.request;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.gongjakso.server.domain.member.entity.Member;
import com.gongjakso.server.domain.portfolio.entity.Portfolio;
import com.gongjakso.server.domain.apply.entity.Apply;
import com.gongjakso.server.domain.apply.entity.PortfolioInfo;
import com.gongjakso.server.domain.team.entity.Team;
import com.gongjakso.server.domain.apply.enumerate.ApplyStatus;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Nullable;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Builder;

@JsonInclude(JsonInclude.Include.NON_NULL)
@Builder
public record ApplyReq(

@Nullable
@Schema(description = "포트폴리오 ID", example = "1")
Long portfolioId,

@NotNull
@Schema(description = "포트폴리오 공개 설정", example = "false")
Boolean isPrivate,

@Size(max = 500)
@Schema(description = "지원 이유", example = "저는 데이터 사이언스 과목을 수강하며 데이터에 관한 기본적인 내용들을 배우며 이런 데이터를 잘 활용하고, 이용하는 것이 중요한 역량이 될 것 같다고 판단했습니다. 그래서 관련된 역량을 쌓고자 공모전에 출품하고 싶다는 생각을 가지게 되었고, 공공데이터 공모전이 적합하다고 생각했습니다!")
String body,

@Size(max = 20)
@NotNull
@Schema(description = "지원 상태", example = "COMPLETED")
ApplyStatus status,

@Size(max = 20)
@NotNull
@Schema(description = "지원 파트", example = "기획")
String part
) {
public static Apply toEntity(ApplyReq req, Team team, Member member, @Nullable Portfolio portfolio) {
PortfolioInfo portfolioInfo = portfolio != null
? PortfolioInfo.ofPortfolio(portfolio)
: PortfolioInfo.ofPrivate();

return Apply.builder()
.team(team)
.member(member)
.portfolioInfo(portfolioInfo)
.body(req.body())
.status(req.status())
.part(req.part())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.gongjakso.server.domain.apply.dto.request;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.gongjakso.server.domain.apply.enumerate.ApplyStatus;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Builder;

@JsonInclude(JsonInclude.Include.NON_NULL)
@Builder
public record StatusReq(

@Size(max = 20)
@NotNull
@Schema(description = "지원 상태", example = "SELECTED")
ApplyStatus status
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.gongjakso.server.domain.apply.dto.response;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.gongjakso.server.domain.apply.entity.Apply;
import jakarta.annotation.Nullable;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Builder;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Period;

@JsonInclude(JsonInclude.Include.NON_NULL)
@Builder
public record ApplyRes(

@NotNull
Long applyId,

@NotNull
Long teamId,

@NotNull
Long memberId,

@Nullable
Long portfolioId,

@Nullable
String portfolioTitle,

@Size(max = 500)
String body,

@Size(max = 20)
@NotNull
String status,

@Size(max = 20)
@NotNull
String part,

int scrapCount,

int remainingDays,

LocalDate startedAt,

LocalDate finishedAt,

String teamName,

String leaderName,

Boolean isViewed,

LocalDateTime deleteAt

) {
public static ApplyRes of(Apply apply) {
return ApplyRes.builder()
.applyId(apply.getId())
.teamId(apply.getTeam().getId())
.teamName(apply.getTeam().getTitle())
.memberId(apply.getMember().getId())
.leaderName(apply.getTeam().getMember().getName())
.portfolioId(apply.getPortfolioInfo().getPortfolio() != null ? apply.getPortfolioInfo().getPortfolio().getId() : null)
.portfolioTitle(apply.getPortfolioInfo().getPortfolio() != null ? apply.getPortfolioInfo().getPortfolio().getTitle() : null)
.body(apply.getBody())
.status(apply.getStatus().getDescription())
.part(apply.getPart())
.isViewed(apply.isViewed())
.deleteAt(apply.getDeletedAt())
.scrapCount(apply.getTeam().getScrapCount())
.startedAt(apply.getTeam().getStartedAt())
.finishedAt(apply.getTeam().getFinishedAt())
.remainingDays(Period.between(LocalDate.now(), apply.getTeam().getFinishedAt()).getDays())
.build();
}
}
68 changes: 68 additions & 0 deletions src/main/java/com/gongjakso/server/domain/apply/entity/Apply.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.gongjakso.server.domain.apply.entity;

import com.gongjakso.server.domain.member.entity.Member;
import com.gongjakso.server.domain.team.entity.Team;
import com.gongjakso.server.domain.apply.enumerate.ApplyStatus;
import com.gongjakso.server.global.common.BaseTimeEntity;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.SQLDelete;

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

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

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "team_id", nullable = false, columnDefinition = "bigint")
private Team team;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="member_id", nullable = false, columnDefinition = "bigint")
private Member member;

@Embedded
private PortfolioInfo portfolioInfo;

@Column(nullable = false, columnDefinition = "varchar(500)")
private String body;

@Column(nullable = false, columnDefinition = "varchar(20)")
@Enumerated(EnumType.STRING)
private ApplyStatus status;

@Column(nullable = false, columnDefinition = "varchar(20)")
private String part;

@Column(nullable = false, columnDefinition = "tinyint(1) default 0")
private boolean isViewed;

@Builder
public Apply(Team team, Member member, PortfolioInfo portfolioInfo, String body, ApplyStatus status, String part) {
this.team = team;
this.member = member;
this.portfolioInfo = portfolioInfo;
this.body = body;
this.status = status;
this.part = part;
this.isViewed = false;
}

public void select(ApplyStatus status) {
this.status = status;
}

public void setViewed() {
this.isViewed = true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.gongjakso.server.domain.apply.entity;

import com.gongjakso.server.domain.portfolio.entity.Portfolio;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Embeddable
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class PortfolioInfo {

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "portfolio_id", columnDefinition = "bigint")
private Portfolio portfolio;

private boolean isPrivate;

public static PortfolioInfo ofPortfolio(Portfolio portfolio) {
return PortfolioInfo.builder()
.portfolio(portfolio)
.isPrivate(false)
.build();
}

public static PortfolioInfo ofPrivate() {
return PortfolioInfo.builder()
.portfolio(null)
.isPrivate(true)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.gongjakso.server.domain.apply.enumerate;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public enum ApplyStatus {
COMPLETED("지원 완료"),
ACCEPTED("합격"),
REJECTED("불합격");

private final String description;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.gongjakso.server.domain.apply.repository;

import com.gongjakso.server.domain.apply.entity.Apply;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface ApplyRepository extends JpaRepository<Apply, Long>, ApplyRepositoryCustom {
Boolean existsByMemberIdAndTeamIdAndDeletedAtIsNull(Long memberId, Long teamId);
Optional<Apply> findByIdAndDeletedAtIsNull(Long applyId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.gongjakso.server.domain.apply.repository;

import com.gongjakso.server.domain.member.entity.Member;
import com.gongjakso.server.domain.apply.dto.response.ApplyRes;
import com.gongjakso.server.domain.apply.entity.Apply;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.Optional;

public interface ApplyRepositoryCustom {
Page<ApplyRes> findByMemberAndPage(Member member, Pageable pageable);
Optional<Apply> findApplyDetails(Long applyId);
}
Loading

0 comments on commit 59a55cc

Please sign in to comment.