Skip to content

Commit

Permalink
feat: 친구에게 컨텐츠 추천 로직 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
GitJIHO committed Nov 27, 2024
1 parent 680e99f commit 8cca74f
Show file tree
Hide file tree
Showing 10 changed files with 233 additions and 15 deletions.
21 changes: 21 additions & 0 deletions src/main/java/com/software/ott/content/entity/Content.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,45 @@
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(
name = "content",
indexes = {
@Index(name = "idx_title", columnList = "title"),
@Index(name = "idx_listedIn", columnList = "listedIn"),
@Index(name = "idx_director", columnList = "director"),
@Index(name = "idx_cast", columnList = "cast"),
@Index(name = "idx_title_director_listedIn_cast", columnList = "title, director, listedIn, cast")
}
)
public class Content {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String showId;
private String type;

@Column(length = 255)
private String title;

@Column(length = 255)
private String director;

@Column(name = "castMember", length = 1000)
private String cast;

private String country;
private String dateAdded;
private String releaseYear;
private String rating;
private String duration;

@Column(length = 500)
private String listedIn;

@Column(length = 1000)
private String description;

private String posterPath;
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package com.software.ott.friend.controller;

import com.software.ott.common.dto.StringTypeMessageResponse;
import com.software.ott.friend.dto.FriendEmailRequest;
import com.software.ott.friend.dto.FriendRequestResponse;
import com.software.ott.friend.dto.FriendResponse;
import com.software.ott.friend.dto.*;
import com.software.ott.friend.service.FriendRecommendService;
import com.software.ott.friend.service.FriendService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
Expand All @@ -21,6 +20,7 @@
public class FriendController {

private final FriendService friendService;
private final FriendRecommendService friendRecommendService;

@Operation(summary = "친구 요청 보내기", description = "친구의 email을 기반으로 친구요청합니다.")
@PostMapping
Expand Down Expand Up @@ -70,4 +70,25 @@ public ResponseEntity<StringTypeMessageResponse> deleteFriend(@RequestAttribute(
friendService.deleteFriend(memberId, friendRequestId);
return ResponseEntity.ok().body(new StringTypeMessageResponse("친구가 삭제되었습니다."));
}

@Operation(summary = "친구에게 컨텐츠 추천하기", description = "친구에게 컨텐츠를 이유와 함께 추천합니다.")
@PostMapping("/recommend/{friendRequestId}")
public ResponseEntity<StringTypeMessageResponse> recommendContent(@RequestAttribute("memberId") Long memberId, @PathVariable Long friendRequestId, @RequestBody RecommendContentRequest recommendContentRequest) {
friendRecommendService.sendContentRecommend(memberId, friendRequestId, recommendContentRequest);
return ResponseEntity.ok().body(new StringTypeMessageResponse("추천이 전송되었습니다."));
}

@Operation(summary = "친구에게 추천받은 컨텐츠들 보기", description = "친구에게 추천받은 컨텐츠들을 조회합니다.")
@GetMapping("/recommend")
public ResponseEntity<List<FriendRecommendResponse>> getAllRecommendedContents(@RequestAttribute("memberId") Long memberId) {
List<FriendRecommendResponse> friendRecommendResponses = friendRecommendService.getAllRecommendedContents(memberId);
return ResponseEntity.ok().body(friendRecommendResponses);
}

@Operation(summary = "친구에게 추천받은 컨텐츠 목록에서 삭제", description = "친구에게 추천받은 컨텐츠중 선택한 컨텐츠를 목록에서 삭제합니다.")
@DeleteMapping("/recommend/{friendRecommendId}")
public ResponseEntity<StringTypeMessageResponse> deleteRecommendContent(@RequestAttribute("memberId") Long memberId, @PathVariable Long friendRecommendId) {
friendRecommendService.deleteRecommend(memberId, friendRecommendId);
return ResponseEntity.ok().body(new StringTypeMessageResponse("삭제되었습니다."));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.software.ott.friend.dto;

import com.software.ott.content.entity.Content;

public record FriendRecommendResponse(
Long RecommendId,
String senderName,
String senderEmail,
Content content,
String reason
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.software.ott.friend.dto;

public record RecommendContentRequest(
Long contentId,
String reason
) {
}
4 changes: 4 additions & 0 deletions src/main/java/com/software/ott/friend/entity/Friend.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.software.ott.friend.FriendStatus;
import com.software.ott.member.entity.Member;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import lombok.*;

@Builder
Expand All @@ -17,12 +18,15 @@ public class Friend {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@NotNull
@ManyToOne
@JoinColumn(name = "requester_id")
private Member requester;
@NotNull
@ManyToOne
@JoinColumn(name = "accepter_id")
private Member accepter;
@NotNull
@Enumerated(EnumType.STRING)
private FriendStatus status;

Expand Down
45 changes: 45 additions & 0 deletions src/main/java/com/software/ott/friend/entity/FriendRecommend.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.software.ott.friend.entity;

import com.software.ott.content.entity.Content;
import com.software.ott.member.entity.Member;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import lombok.*;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;

@Entity
@Builder
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EntityListeners(AuditingEntityListener.class)
public class FriendRecommend {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@NotNull
@ManyToOne
@JoinColumn(name = "sender_id")
private Member sender;
@NotNull
@ManyToOne
@JoinColumn(name = "receiver_id")
private Member receiver;
@NotNull
@OneToOne
@JoinColumn(name = "content_id")
private Content recommendContent;
@CreatedDate
@NotNull
private LocalDateTime recommendTime;
private String reason;

public boolean NotAuth(Member member) {
return !(sender.equals(member) || receiver.equals(member));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.software.ott.friend.repository;

import com.software.ott.content.entity.Content;
import com.software.ott.friend.entity.FriendRecommend;
import com.software.ott.member.entity.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface FriendRecommendRepository extends JpaRepository<FriendRecommend, Long> {

boolean existsBySenderAndReceiverAndRecommendContent(Member sender, Member receiver, Content recommendContent);

List<FriendRecommend> getAllByReceiver(Member receiver);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.software.ott.friend.service;

import com.software.ott.common.exception.BadRequestException;
import com.software.ott.common.exception.NotFoundException;
import com.software.ott.content.entity.Content;
import com.software.ott.content.repository.ContentRepository;
import com.software.ott.friend.dto.FriendRecommendResponse;
import com.software.ott.friend.dto.RecommendContentRequest;
import com.software.ott.friend.entity.Friend;
import com.software.ott.friend.entity.FriendRecommend;
import com.software.ott.friend.repository.FriendRecommendRepository;
import com.software.ott.friend.repository.FriendRepository;
import com.software.ott.member.entity.Member;
import com.software.ott.member.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class FriendRecommendService {

private final FriendRecommendRepository friendRecommendRepository;
private final MemberRepository memberRepository;
private final FriendRepository friendRepository;
private final ContentRepository contentRepository;

@Transactional
public void sendContentRecommend(Long memberId, Long friendRequestId, RecommendContentRequest recommendContentRequest) {
Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new NotFoundException("id에 해당하는 멤버가 없습니다."));

Friend friend = friendRepository.findById(friendRequestId)
.orElseThrow(() -> new NotFoundException("friendRequestId에 해당하는 친구 요청(친구)가 없습니다."));

Content content = contentRepository.findById(recommendContentRequest.contentId())
.orElseThrow(() -> new NotFoundException("contentId에 해당하는 컨텐츠가 없습니다."));

if (!friendRepository.existsByRequesterIdOrAccepterId(memberId, friend.getAccepter().getId()) && !friendRepository.existsByRequesterIdOrAccepterId(friend.getRequester().getId(), memberId)) {
throw new BadRequestException("서로 친구 관계가 아닙니다.");
}

Member friendMember = null;

if (friend.getAccepter().equals(member)) {
friendMember = friend.getRequester();
}

if (friend.getRequester().equals(member)) {
friendMember = friend.getAccepter();
}

if (friendMember == null) {
throw new BadRequestException("정상적인 접근이 아닙니다.");
}

if (friendRecommendRepository.existsBySenderAndReceiverAndRecommendContent(member, friendMember, content)) {
throw new BadRequestException("이미 해당 컨텐츠를 상대방에게 추천했습니다.");
}

friendRecommendRepository.save(
FriendRecommend.builder()
.sender(member)
.receiver(friendMember)
.recommendContent(content)
.reason(recommendContentRequest.reason())
.build());
}

@Transactional(readOnly = true)
public List<FriendRecommendResponse> getAllRecommendedContents(Long memberId) {
Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new NotFoundException("id에 해당하는 멤버가 없습니다."));

return friendRecommendRepository.getAllByReceiver(member)
.stream().map(
FriendRecommend -> new FriendRecommendResponse(
FriendRecommend.getId(),
FriendRecommend.getSender().getName(),
FriendRecommend.getSender().getEmail(),
FriendRecommend.getRecommendContent(),
FriendRecommend.getReason()))
.collect(Collectors.toList());
}

@Transactional
public void deleteRecommend(Long memberId, Long recommendId) {
Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new NotFoundException("id에 해당하는 멤버가 없습니다."));

FriendRecommend friendRecommend = friendRecommendRepository.findById(recommendId)
.orElseThrow(() -> new NotFoundException("recommendId에 해당하는 친구의 추천이 없습니다."));

if (friendRecommend.NotAuth(member)) {
throw new BadRequestException("추천 발송인이나 수신인만 삭제가 가능합니다.");
}

friendRecommendRepository.delete(friendRecommend);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,6 @@
import lombok.*;

@Entity
@Table(
indexes = {
@Index(name = "idx_contentlike_member_id", columnList = "member_id"),
@Index(name = "idx_contentlike_content_id", columnList = "content_id")
}
)
@Getter
@Setter
@Builder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,6 @@
import java.time.LocalDateTime;

@Entity
@Table(
indexes = {
@Index(name = "idx_watchhistory_member_id", columnList = "member_id"),
@Index(name = "idx_watchhistory_content_id", columnList = "content_id")
}
)
@Getter
@Setter
@Builder
Expand Down

0 comments on commit 8cca74f

Please sign in to comment.