Skip to content

Commit

Permalink
✨ [FEATURE] 제안 관련 기능 개발 (#33)
Browse files Browse the repository at this point in the history
* ✨ feat: 계층별 기본 클래스 작성

* ✨ feat: createPropose 기능 구현

* ✨ feat: Propose 엔티티 수정 (제안 받는 단체, 제안 하는 단체로 구분)

* ✨ feat: 제안 목록 조회, 상세 조회 구현

* ♻️ refactor: DTO 위치 변경

* ✨ feat: 제안 업데이트 기능 구현
  • Loading branch information
kimday0326 authored Jan 23, 2024
1 parent 4b0518e commit 1f25a65
Show file tree
Hide file tree
Showing 18 changed files with 414 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,12 @@ public boolean supportsParameter(MethodParameter parameter) {
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
// TODO : Access Token 없는 경우 처리
CustomUserDetails userDetails = (CustomUserDetails)SecurityContextHolder.getContext()
.getAuthentication()
.getPrincipal();

Organization organization = organizationRepository.findById(userDetails.getId())
return organizationRepository.findById(userDetails.getId())
.orElseThrow(() -> new OrganizationException(OrganizationErrorCode.ORGANIZATION_NOT_FOUND));

return organization;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,8 @@ public class Organization extends BaseEntity {
@Builder.Default
@OneToMany(mappedBy = "organization")
private List<OrganizationLink> organizationLinks = new ArrayList<>();

public boolean isStudentOrganization() {
return this.organizationType == OrganizationType.STUDENT;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.sponus.sponusbe.domain.propose.controller;

import java.util.List;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.sponus.sponusbe.auth.annotation.AuthOrganization;
import com.sponus.sponusbe.domain.organization.entity.Organization;
import com.sponus.sponusbe.domain.propose.controller.dto.request.ProposeCreateRequest;
import com.sponus.sponusbe.domain.propose.controller.dto.request.ProposeGetCondition;
import com.sponus.sponusbe.domain.propose.controller.dto.request.ProposeUpdateRequest;
import com.sponus.sponusbe.domain.propose.controller.dto.response.ProposeCreateResponse;
import com.sponus.sponusbe.domain.propose.controller.dto.response.ProposeDetailGetResponse;
import com.sponus.sponusbe.domain.propose.controller.dto.response.ProposeSummaryGetResponse;
import com.sponus.sponusbe.domain.propose.service.ProposeQueryService;
import com.sponus.sponusbe.domain.propose.service.ProposeService;
import com.sponus.sponusbe.global.common.ApiResponse;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@RestController
public class ProposeController {

private final ProposeService proposeService;
private final ProposeQueryService proposeQueryService;

@PostMapping("/api/v1/propose")
public ApiResponse<ProposeCreateResponse> createPropose(
@AuthOrganization Organization authOrganization,
@RequestBody @Valid ProposeCreateRequest request
) {
return ApiResponse.onSuccess(proposeService.createPropose(authOrganization, request));
}

@GetMapping("/api/v1/propose/me")
public ApiResponse<List<ProposeSummaryGetResponse>> getMyProposes(
@AuthOrganization Organization authOrganization,
@ModelAttribute @Valid ProposeGetCondition condition
) {
return ApiResponse.onSuccess(proposeQueryService.getProposes(authOrganization, condition));
}

@GetMapping("/api/v1/propose/{proposeId}")
public ApiResponse<ProposeDetailGetResponse> getProposeDetail(@PathVariable Long proposeId) {
return ApiResponse.onSuccess(proposeQueryService.getProposeDetail(proposeId));
}

@PatchMapping("/api/v1/propose/{proposeId}")
public ApiResponse<Void> updatePropose(
@AuthOrganization Organization authOrganization,
@PathVariable Long proposeId,
@RequestBody @Valid ProposeUpdateRequest request
) {
proposeService.updatePropose(authOrganization, proposeId, request);
return ApiResponse.onSuccess(null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.sponus.sponusbe.domain.propose.controller.dto.request;

import com.sponus.sponusbe.domain.announcement.entity.Announcement;
import com.sponus.sponusbe.domain.organization.entity.Organization;
import com.sponus.sponusbe.domain.propose.entity.Propose;
import com.sponus.sponusbe.domain.propose.entity.ProposeStatus;

public record ProposeCreateRequest(
String title,
String content,
Long announcementId
) {
public Propose toEntity(
Announcement announcement,
Organization proposedOrganization,
Organization proposingOrganization
) {
return Propose.builder()
.title(title)
.content(content)
.status(ProposeStatus.PENDING)
.announcement(announcement)
.proposedOrganization(proposedOrganization)
.proposingOrganization(proposingOrganization)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.sponus.sponusbe.domain.propose.controller.dto.request;

import jakarta.validation.constraints.NotNull;

public record ProposeGetCondition(
@NotNull
ProposeType proposeType,
Long announcementId
) {
public boolean isSentPropose() {
return proposeType == ProposeType.SEND;
}

public boolean isReceivedPropose() {
return proposeType == ProposeType.RECEIVED;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.sponus.sponusbe.domain.propose.controller.dto.request;

public enum ProposeType {
SEND, RECEIVED;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.sponus.sponusbe.domain.propose.controller.dto.request;

import com.sponus.sponusbe.domain.propose.entity.ProposeStatus;

public record ProposeUpdateRequest(
String title,
String content,
ProposeStatus status
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.sponus.sponusbe.domain.propose.controller.dto.response;

public record ProposeCreateResponse(
Long proposeId
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.sponus.sponusbe.domain.propose.controller.dto.response;

import java.time.format.DateTimeFormatter;
import java.util.Locale;

import com.sponus.sponusbe.domain.propose.entity.Propose;

public record ProposeDetailGetResponse(
Long proposeId,
String title,
String content,
String status,
String proposeImageUrl,
Long proposedOrganizationId,
String proposedOrganizationName,
Long proposingOrganizationId,
String proposingOrganizationName,

// TODO : 공고 상세 정보 (공고에서 묶기!)
Long announcementId,
String createdDate,
String createdDay
) {
public static ProposeDetailGetResponse from(Propose propose) {
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("MM.dd");
DateTimeFormatter dayFormatter = DateTimeFormatter.ofPattern("EEE", Locale.ENGLISH);
return new ProposeDetailGetResponse(
propose.getId(),
propose.getTitle(),
propose.getContent(),
propose.getStatus().name(),
null, // TODO : 제안 이미지 URL
propose.getProposedOrganization().getId(),
propose.getProposedOrganization().getName(),
propose.getProposingOrganization().getId(),
propose.getProposingOrganization().getName(),
propose.getAnnouncement().getId(),
propose.getCreatedAt().format(dateFormatter),
propose.getUpdatedAt().format(dayFormatter)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.sponus.sponusbe.domain.propose.controller.dto.response;

import java.time.format.DateTimeFormatter;
import java.util.Locale;

import com.sponus.sponusbe.domain.propose.entity.Propose;

public record ProposeSummaryGetResponse(
Long proposeId,
String title,
String status,
String proposeImageUrl,
Long proposedOrganizationId,
String proposedOrganizationName,
Long proposingOrganizationId,
String proposingOrganizationName,

// TODO : 공고 간략 정보 (공고에서 묶기!)
Long announcementId,
String createdDate,
String createdDay
) {
public static ProposeSummaryGetResponse from(Propose propose) {
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("MM.dd");
DateTimeFormatter dayFormatter = DateTimeFormatter.ofPattern("EEE", Locale.ENGLISH);
return new ProposeSummaryGetResponse(
propose.getId(),
propose.getTitle(),
propose.getStatus().name(),
null, // TODO : 제안 이미지 URL
propose.getProposedOrganization().getId(),
propose.getProposedOrganization().getName(),
propose.getProposingOrganization().getId(),
propose.getProposingOrganization().getName(),
propose.getAnnouncement().getId(),
propose.getCreatedAt().format(dateFormatter),
propose.getUpdatedAt().format(dayFormatter)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.sponus.sponusbe.domain.announcement.entity.Announcement;
import com.sponus.sponusbe.domain.organization.entity.Organization;
import com.sponus.sponusbe.domain.report.entity.Report;
import com.sponus.sponusbe.global.common.BaseEntity;

import jakarta.persistence.Column;
import jakarta.persistence.ConstraintMode;
Expand All @@ -32,7 +33,7 @@
@Getter
@Entity
@Table(name = "propose")
public class Propose {
public class Propose extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Expand All @@ -48,14 +49,19 @@ public class Propose {
@Column(name = "propose_status", nullable = false)
private ProposeStatus status;

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "announcement_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
private Announcement announcement;

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "proposed_organization_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
private Organization proposedOrganization;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "proposing_organization_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
private Organization proposingOrganization;

// TODO : 추후에 연관관계 제거 -> 그냥 proposingOrganizationType, getStudentOrganization 메서드로 대체하는게 좋을 것 같음
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "student_organization_id", foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
private Organization studentOrganization;
Expand All @@ -71,4 +77,10 @@ public class Propose {
@Builder.Default
@OneToMany(mappedBy = "propose")
private List<ProposeAttachment> proposeAttachments = new ArrayList<>();

public void update(String title, String content, ProposeStatus status) {
this.title = title == null ? this.title : title;
this.content = content == null ? this.content : content;
this.status = status == null ? this.status : status;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package com.sponus.sponusbe.domain.propose.entity;

public enum ProposeStatus {
PENDING, ACCEPTED, REJECTED, CANCELED
PENDING, ACCEPTED, REJECTED, SUSPENDED, CANCELED, ACCEPTED_AND_PAID;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.sponus.sponusbe.domain.propose.exception;

import org.springframework.http.HttpStatus;

import com.sponus.sponusbe.global.common.ApiResponse;
import com.sponus.sponusbe.global.common.BaseErrorCode;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum ProposeErrorCode implements BaseErrorCode {
ANNOUNCEMENT_ID_IS_REQUIRED(HttpStatus.BAD_REQUEST, "4001", "공고 ID가 필요합니다."),
INVALID_ORGANIZATION(HttpStatus.BAD_REQUEST, "4002", "해당 단체의 제안이 아닙니다."),
PROPOSE_NOT_FOUND(HttpStatus.NOT_FOUND, "4041", "해당 제안이 존재하지 않습니다."),
INTERNAL_PROPOSE_EXCEPTION(HttpStatus.INTERNAL_SERVER_ERROR, "5001", "서버 에러가 발생했습니다. 관리자에게 문의해주세요.");

private final HttpStatus httpStatus;
private final String code;
private final String message;

@Override
public ApiResponse<Void> getErrorResponse() {
return ApiResponse.onFailure(code, message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.sponus.sponusbe.domain.propose.exception;

import com.sponus.sponusbe.global.common.BaseErrorCode;
import com.sponus.sponusbe.global.common.exception.CustomException;

public class ProposeException extends CustomException {

public ProposeException(BaseErrorCode errorCode) {
super(errorCode);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.sponus.sponusbe.domain.propose.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import com.sponus.sponusbe.domain.propose.entity.Propose;

public interface ProposeRepository extends JpaRepository<Propose, Long> {
@Query("SELECT p FROM Propose p WHERE p.proposingOrganization.id = :id")
List<Propose> findSentPropose(Long id);

@Query("SELECT p FROM Propose p WHERE p.proposedOrganization.id = :organizationId AND p.announcement.id = :announcementId")
List<Propose> findReceivedProposeWithAnnouncementId(Long organizationId, Long announcementId);

}
Loading

0 comments on commit 1f25a65

Please sign in to comment.