Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ [FEATURE] 제안 관련 기능 개발 #33

Merged
merged 6 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading