Skip to content

Commit

Permalink
Feat: 후원 API 개발 (#58)
Browse files Browse the repository at this point in the history
* Feat(#51): 후원 관련 엔티티 칼럼 설정 및 엔티티간 매핑

* Feat(#51): 커스텀 예외 설정

* Feat(#51): 후원 및 응원 api 개발

* Feat(#51): 후원 및 응원 관련 dto 작성

* Feat(#51): 후원 홈 목록 조회 관련 API 개발
  • Loading branch information
nohy6630 authored Nov 12, 2023
1 parent 3cc62cf commit c893353
Show file tree
Hide file tree
Showing 17 changed files with 505 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package com.numberone.backend.domain.member.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import com.numberone.backend.domain.support.entity.Support;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.Comment;

import java.util.ArrayList;
import java.util.List;

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
Expand All @@ -19,6 +21,12 @@ public class Member {

private String email;

@Comment("마음 갯수")
private Integer heartCnt;

@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
private List<Support> supports = new ArrayList<>();

@Builder
public Member(String email) {
this.email = email;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.numberone.backend.domain.sponsor.controller;

import com.numberone.backend.domain.sponsor.dto.response.SponsorHomeResponse;
import com.numberone.backend.domain.sponsor.dto.response.SponsorResponse;
import com.numberone.backend.domain.sponsor.service.SponsorService;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;

@Tag(name = "sponsors", description = "후원 관련 API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/sponsor")
public class SponsorController {
private final SponsorService sponsorService;

@GetMapping("/{sponsorId}")
public ResponseEntity<SponsorResponse> getSponsorDetail(@PathVariable Long sponsorId){
return ResponseEntity.ok(sponsorService.getSponsorDetail(sponsorId));
}

@GetMapping("/latest")
public ResponseEntity<SponsorHomeResponse> getSponsorHomeLatest(Authentication authentication){
return ResponseEntity.ok(sponsorService.getSponsorHomeLatest(authentication.getName()));
}

@GetMapping("/popular")
public ResponseEntity<SponsorHomeResponse> getSponsorHomePopular(Authentication authentication){
return ResponseEntity.ok(sponsorService.getSponsorHomePopular(authentication.getName()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.numberone.backend.domain.sponsor.dto.response;

import lombok.*;

import java.util.ArrayList;
import java.util.List;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
public class SponsorHomeResponse {
private Long supporterCnt;
private List<String> messages = new ArrayList<>();
private String nickname;
private List<SponsorResponse> sponsors = new ArrayList<>();

public static SponsorHomeResponse of(Long supporterCnt, List<String> messages, String nickname, List<SponsorResponse> sponsors) {
return SponsorHomeResponse.builder()
.supporterCnt(supporterCnt)
.messages(messages)
.nickname(nickname)
.sponsors(sponsors)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.numberone.backend.domain.sponsor.dto.response;

import com.numberone.backend.domain.sponsor.entity.Sponsor;
import lombok.*;

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

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
public class SponsorResponse {
private Long id;
private Boolean isSupported;
private String disasterType;
private String dday;
private String title;
private String subtitle;
private String content;
private Integer targetHeart;
private Integer currentHeart;
private String sponsorName;
private String period;

public static SponsorResponse of(Boolean isSupported, Sponsor sponsor) {
int dday = Period.between(LocalDate.now(), sponsor.getDueDate()).getDays();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy. MM. dd");
String startDate = sponsor.getStartDate().format(formatter);
String dueDate = sponsor.getDueDate().format(formatter);

return SponsorResponse.builder()
.id(sponsor.getId())
.isSupported(isSupported)
.disasterType(sponsor.getDisasterType().getDescription())
.dday("D-" + dday)
.title(sponsor.getTitle())
.subtitle(sponsor.getSubtitle())
.content(sponsor.getContent())
.targetHeart(sponsor.getTargetHeart())
.currentHeart(sponsor.getCurrentHeart())
.sponsorName(sponsor.getSponsorName())
.period(startDate + " ~ " + dueDate)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.numberone.backend.domain.sponsor.entity;

import com.numberone.backend.domain.disaster.util.DisasterType;
import com.numberone.backend.domain.support.entity.Support;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.Comment;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class Sponsor {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Comment("재난 유형")
@Enumerated(EnumType.STRING)
private DisasterType disasterType;

@Comment("제목")
private String title;

@Comment("부제목")
private String subtitle;

@Comment("세부 내용")
private String content;

@Comment("시작 날짜")
private LocalDate startDate;

@Comment("마감 날짜")
private LocalDate dueDate;

@Comment("후원 받을 마음 목표치")
private Integer targetHeart;

@Comment("현재 후원받은 마음")
private Integer currentHeart;

@Comment("후원사 이름")
private String sponsorName;

@OneToMany(mappedBy = "sponsor", cascade = CascadeType.ALL)
private List<Support> supports = new ArrayList<>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.numberone.backend.domain.sponsor.repository;

import com.numberone.backend.domain.sponsor.entity.Sponsor;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface SponsorRepository extends JpaRepository<Sponsor,Long> {
List<Sponsor> findAllByOrderByStartDateDesc();
List<Sponsor> findAllByOrderByCurrentHeartDesc();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package com.numberone.backend.domain.sponsor.service;

import com.numberone.backend.domain.member.entity.Member;
import com.numberone.backend.domain.member.repository.MemberRepository;
import com.numberone.backend.domain.sponsor.dto.response.SponsorHomeResponse;
import com.numberone.backend.domain.sponsor.dto.response.SponsorResponse;
import com.numberone.backend.domain.sponsor.entity.Sponsor;
import com.numberone.backend.domain.sponsor.repository.SponsorRepository;
import com.numberone.backend.domain.support.entity.Support;
import com.numberone.backend.domain.support.repository.SupportRepository;
import com.numberone.backend.exception.notfound.NotFoundMemberException;
import com.numberone.backend.exception.notfound.NotFoundSponsorException;
import lombok.RequiredArgsConstructor;
import org.apache.tomcat.util.net.openssl.ciphers.Authentication;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class SponsorService {
private final SponsorRepository sponsorRepository;
private final MemberRepository memberRepository;
private final SupportRepository supportRepository;

public SponsorResponse getSponsorDetail(Long sponsorId) {
Sponsor sponsor = sponsorRepository.findById(sponsorId)
.orElseThrow(NotFoundSponsorException::new);
return SponsorResponse.of(
null,
sponsor
);
}

public SponsorHomeResponse getSponsorHomeLatest(String email) {
List<SponsorResponse> sponsorResponses = new ArrayList<>();
Member member = memberRepository.findByEmail(email)
.orElseThrow(NotFoundMemberException::new);
List<Sponsor> sponsors = sponsorRepository.findAllByOrderByStartDateDesc();
for (Sponsor sponsor : sponsors) {
boolean isSupported = false;
for (Support support : sponsor.getSupports()) {
if (support.getMember().getId().equals(member.getId())) {
isSupported = true;
break;
}
}
sponsorResponses.add(SponsorResponse.of(isSupported, sponsor));
}

List<String> messages = new ArrayList<>();
List<Support> supports = supportRepository.findAll();
for (Support support : supports) {
if (support.getMessage() != null)
messages.add(support.getMessage());
}

return SponsorHomeResponse.of(
supportRepository.getSupportCnt(),
messages,
email,
sponsorResponses
);
}

public SponsorHomeResponse getSponsorHomePopular(String email) {
List<SponsorResponse> sponsorResponses = new ArrayList<>();
Member member = memberRepository.findByEmail(email)
.orElseThrow(NotFoundMemberException::new);
List<Sponsor> sponsors = sponsorRepository.findAllByOrderByCurrentHeartDesc();
for (Sponsor sponsor : sponsors) {
boolean isSupported = false;
for (Support support : sponsor.getSupports()) {
if (support.getMember().getId().equals(member.getId())) {
isSupported = true;
break;
}
}
sponsorResponses.add(SponsorResponse.of(isSupported, sponsor));
}

List<String> messages = new ArrayList<>();
List<Support> supports = supportRepository.findAll();
for (Support support : supports) {
if (support.getMessage() != null)
messages.add(support.getMessage());
}

return SponsorHomeResponse.of(
supportRepository.getSupportCnt(),
messages,
email,
sponsorResponses
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.numberone.backend.domain.support.controller;

import com.numberone.backend.domain.support.dto.request.CreateSupportRequest;
import com.numberone.backend.domain.support.dto.request.EditMessageRequest;
import com.numberone.backend.domain.support.dto.response.CreateSupportResponse;
import com.numberone.backend.domain.support.service.SupportService;
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.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;

@Tag(name = "supports", description = "응원 API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/support")
public class SupportController {
private final SupportService supportService;

@Operation(summary = "응원 메시지 등록하기", description =
"""
응원 메시지를 등록하는 api입니다. api 요청시 응원 메시지 내용을 바디에 같이 전달해주세요.
요청 경로는 /api/sponsor/{supportId}입니다.
support Id값은 후원 api 이용시 반환되는 값을 기억했다가 응원 메시지 api 요청시 경로에 같이 전달해주세요.
""")
@PostMapping("/{supportId}")
public void editMessage(@Valid @RequestBody EditMessageRequest editMessageRequest, @PathVariable Long supportId) {
supportService.editMessage(editMessageRequest, supportId);
}

@Operation(summary = "후원하기", description =
"""
후원하는 api입니다.
후원에 대한 id인 sponsor ID와 후원할 마음 갯수를 같이 전달해주세요.
""")
@PostMapping()
public ResponseEntity<CreateSupportResponse> create(Authentication authentication, @Valid @RequestBody CreateSupportRequest createSupportRequest) {
CreateSupportResponse createSupportResponse = supportService.create(createSupportRequest, authentication.getName());
return ResponseEntity.ok(createSupportResponse);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.numberone.backend.domain.support.dto.request;

import jakarta.validation.constraints.NotNull;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class CreateSupportRequest {
@NotNull(message = "어느 것에 후원할지 sponsor id를 입력해주세요")
private Long sponsorId;
@NotNull(message = "후원할 마음 갯수를 입력해주세요")
private Integer heartCnt;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.numberone.backend.domain.support.dto.request;

import jakarta.validation.constraints.NotEmpty;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class EditMessageRequest {
@NotEmpty(message = "응원 메시지 내용이 비어있습니다.")
public String message;
}
Loading

0 comments on commit c893353

Please sign in to comment.