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

견적서 관련 작업 #58

Merged
merged 20 commits into from
Oct 8, 2023
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 @@ -16,8 +16,10 @@ public enum BaseException {
USER_UNEXPECTED_ERROR("[User] 예상치 못한 문제가 발생했습니다.", 500),
PORTFOLIO_NOT_FOUND("포트폴리오를 찾을 수 없습니다.", 404),
PORTFOLIO_IMAGE_NOT_FOUND("포트폴리오 이미지를 불러올 수 없습니다.", 404),
PERMISSION_DENIED_METHOD_ACCESS("사용할 수 없는 기능입니다.", 403);

PERMISSION_DENIED_METHOD_ACCESS("사용할 수 없는 기능입니다.", 403),
QUOTATIONS_NOT_ALL_CONFIRMED("확정되지 않은 견적서가 있습니다.",400),
NO_QUOTATION_TO_CONFIRM("확정할 견적서가 없습니다",400),
MATCHING_NOT_FOUND("매칭 내역을 찾을 수 없습니다.", 400);


@Getter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.kakao.sunsuwedding.user.planner.Planner;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

Expand All @@ -16,7 +17,7 @@
public class Match {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
private Planner planner;
Expand All @@ -25,14 +26,45 @@ public class Match {
private Couple couple;

@Column(nullable = false)
private String status;
@Enumerated(EnumType.STRING)
private MatchStatus status;

@Column(nullable = false)
private long price;
private Long price;

@Column
private LocalDateTime comfirmedAt;
private LocalDateTime confirmed_at;

@Column(nullable = false)
private LocalDateTime createdAt;
private LocalDateTime created_at;

@Column(nullable = false)
private Boolean is_active;

@Builder
public void Match(Long id, Planner planner, Couple couple, Long price) {
this.id = id;
this.planner = planner;
this.couple = couple;
this.status = MatchStatus.UNCONFIRMED;
this.price = price;
this.created_at = LocalDateTime.now();
this.is_active = true;
}

public void updateStatus(MatchStatus status) {
this.status = status;
}

public void updatePrice(Long price) {
this.price = price;
}

public void updateConfirmedAt(LocalDateTime confirmed_at) {
this.confirmed_at = confirmed_at;
}

public void updateIsActive(Boolean is_active) {
this.is_active = is_active;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.kakao.sunsuwedding.match;

import com.kakao.sunsuwedding._core.utils.ApiUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RequiredArgsConstructor
@RestController
@RequestMapping("/chat")
public class MatchRestController {

private final MatchService matchService;

// Match Delete : isActive 필드 false
@DeleteMapping("/{matchId}")
public ResponseEntity<?> deleteChat(@PathVariable Long matchId) {
matchService.deleteChat(matchId);
return ResponseEntity.ok().body(ApiUtils.success(null));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.kakao.sunsuwedding.match;

import com.kakao.sunsuwedding._core.errors.BaseException;
import com.kakao.sunsuwedding._core.errors.exception.Exception400;
import com.kakao.sunsuwedding._core.errors.exception.Exception404;
import com.kakao.sunsuwedding.match.Quotation.Quotation;
import com.kakao.sunsuwedding.match.Quotation.QuotationJPARepository;
import com.kakao.sunsuwedding.match.Quotation.QuotationStatus;
import lombok.RequiredArgsConstructor;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;

@Transactional(readOnly = true)
@RequiredArgsConstructor
@Service
public class MatchService {
private final MatchJPARepository matchJPARepository;
private final QuotationJPARepository quotationJPARepository;

// Match Update : 확정 상태, 가격, 확정 날짜
@Transactional
public void confirmAll(Long matchId) {
Match match = matchJPARepository.findById(matchId).orElseThrow(
() -> new Exception404(BaseException.MATCHING_NOT_FOUND.getMessage()));
// 플래너가 1개씩 전부 확정한 후에 예비 부부가 전체 확정 가능
Pair<Boolean, Long> result = isAllConfirmed(match);
// 모든 견적서 확정 완료 시
if (result.getFirst()) {
match.updateStatus(MatchStatus.CONFIRMED);
match.updatePrice(result.getSecond());
match.updateConfirmedAt(LocalDateTime.now());
}
// 확정되지 않은 견적서가 있을 때
else {
throw new Exception400(BaseException.QUOTATIONS_NOT_ALL_CONFIRMED.getMessage());
}
}

// Match Delete : isActive 필드 false로
// 채팅방 삭제 - 전체 확정 후 / 견적서 없을 때
@Transactional
public void deleteChat(Long matchId) {
Match match = matchJPARepository.findById(matchId).orElseThrow(
() -> new Exception404(BaseException.MATCHING_NOT_FOUND.getMessage()));
List<Quotation> quotations = quotationJPARepository.findAllByMatch(match);
// 견적서 존재하는데 전체 확정이 되지 않은 경우, 채팅방 삭제 불가
if ((!quotations.isEmpty()) && (match.getStatus().equals(MatchStatus.UNCONFIRMED))) {
throw new Exception400(BaseException.QUOTATIONS_NOT_ALL_CONFIRMED.getMessage());
}
// 전체확정 됐거나, 견적서가 없는 경우 채팅방 삭제
match.updateIsActive(false);
}

private Pair<Boolean, Long> isAllConfirmed(Match match) {
List<Quotation> quotations = quotationJPARepository.findAllByMatch(match);
if (quotations.isEmpty()) {
throw new Exception400(BaseException.NO_QUOTATION_TO_CONFIRM.getMessage());
}
else {
// 모든 견적서 확정 됐는지 여부 구하기
Boolean allConfirmed = quotations.stream().allMatch(quotation -> quotation.getStatus().equals(QuotationStatus.CONFIRMED));
quotations.stream().forEach(quotation -> System.out.println(quotation.getStatus()));

// Total Price 구하기
Long totalPrice = quotations.stream().mapToLong(Quotation::getPrice).sum();

return Pair.of(allConfirmed, totalPrice);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.kakao.sunsuwedding.match;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public enum MatchStatus {

CONFIRMED("완료"),
UNCONFIRMED("미완료");

@Getter
private final String status;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.kakao.sunsuwedding.match.Match;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

Expand All @@ -15,7 +16,7 @@
public class Quotation {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private Long id;

@ManyToOne
private Match match;
Expand All @@ -24,7 +25,7 @@ public class Quotation {
private String title;

@Column(nullable = false)
private long price;
private Long price;

@Column
private String company;
Expand All @@ -33,11 +34,58 @@ public class Quotation {
private String description;

@Column(nullable = false)
private String status;
@Enumerated(EnumType.STRING)
private QuotationStatus status;

@Column
private LocalDateTime modified_at;

@Column(nullable = false)
private LocalDateTime modifiedAt;
private LocalDateTime created_at;

@Column(nullable = false)
private LocalDateTime createdAt;
private Boolean is_active;

@Builder
public Quotation(long id, Match match, String title, long price, String company, String description, QuotationStatus status, LocalDateTime createdAt) {
this.id = id;
this.match = match;
this.title = title;
this.price = price;
this.company = company;
this.description = description;
this.status = (status == null? QuotationStatus.UNCONFIRMED : status);
this.created_at = (createdAt == null? LocalDateTime.now() : createdAt);
this.is_active = true;
}

public void updateTitle(String title) {
this.title = title;
this.modified_at = LocalDateTime.now();
}

public void updatePrice(long price) {
this.price = price;
this.modified_at = LocalDateTime.now();
}

public void updateCompany(String company) {
this.company = company;
this.modified_at = LocalDateTime.now();
}

public void updateDescription(String description) {
this.description = description;
this.modified_at = LocalDateTime.now();
}

public void updateStatus(QuotationStatus status) {
this.status = status;
this.modified_at = LocalDateTime.now();
}

public void updateIsActive(Boolean is_active) {
this.is_active = is_active;
this.modified_at = LocalDateTime.now();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.kakao.sunsuwedding.match.Quotation;

import java.util.List;

public class QuotationDTOConverter {
public static List<QuotationResponse.QuotationDTO> toFindByMatchIdDTO(List<Quotation> quotations) {
return quotations
.stream()
.map(quotation -> new QuotationResponse.QuotationDTO(
quotation.getId(), quotation.getTitle(), quotation.getPrice(), quotation.getCompany(), quotation.getDescription(), quotation.getStatus().toString(), quotation.getModified_at()
))
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.kakao.sunsuwedding.match.Quotation;

import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import org.hibernate.validator.constraints.Length;

public class QuotationRequest {
public record addQuotation(
@NotNull @Length(min = 2, max = 50)
String title,
@NotNull @Min(0)
Long price,
String company,
String description
) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.kakao.sunsuwedding.match.Quotation;

import java.time.LocalDateTime;
import java.util.List;

public class QuotationResponse {
public record findAllByMatchId(
String status,
List<QuotationDTO> quotations
) {}

public record QuotationDTO(
Long id,
String title,
Long price,
String company,
String description,
String status,
LocalDateTime modifiedAt
) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.kakao.sunsuwedding.match.Quotation;

import com.kakao.sunsuwedding._core.security.CustomUserDetails;
import com.kakao.sunsuwedding._core.utils.ApiUtils;
import com.kakao.sunsuwedding.match.MatchService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
@RequestMapping("/quotations")
public class QuotationRestController {
private final QuotationService quotationService;
private final MatchService matchService;

@PostMapping("")
public ResponseEntity<?> createQuotation(@AuthenticationPrincipal CustomUserDetails userDetails,
@RequestParam Long matchId,
@Valid @RequestBody QuotationRequest.addQuotation request) {
quotationService.insertQuotation(userDetails.getInfo(), matchId, request);
return ResponseEntity.ok().body(ApiUtils.success(null));
}

@GetMapping("")
public ResponseEntity<?> findQuotations(@AuthenticationPrincipal CustomUserDetails userDetails,
@RequestParam Long matchId) {
QuotationResponse.findAllByMatchId response = quotationService.findQuotationsByMatchId(matchId);
return ResponseEntity.ok().body(ApiUtils.success(response));
}

@PostMapping("/confirmAll/{matchId}")
public ResponseEntity<?> confirmAll(@PathVariable Long matchId) {
matchService.confirmAll(matchId);
return ResponseEntity.ok().body(ApiUtils.success(null));
}
}
Loading
Loading