Skip to content

Commit

Permalink
Merge pull request #27 from kakao-tech-campus-2nd-step3/feat/#20-infl…
Browse files Browse the repository at this point in the history
…uencer

[Feat] #20 로그인 없을 때 인플루언서 반환을 구현했어요
  • Loading branch information
sanghee0820 authored Oct 4, 2024
2 parents c71e1aa + 979c245 commit e81ae55
Show file tree
Hide file tree
Showing 14 changed files with 325 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package team7.inplace.influencer.application;

import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import team7.inplace.influencer.application.dto.InfluencerInfo;
import team7.inplace.influencer.persistence.InfluencerRepository;

@RequiredArgsConstructor
@Service
public class InfluencerService {

private final InfluencerRepository influencerRepository;

@Transactional(readOnly = true)
public List<InfluencerInfo> getAllInfluencers() {
return influencerRepository.findAll().stream()
.map(InfluencerInfo::from)
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package team7.inplace.influencer.application.dto;

import team7.inplace.influencer.domain.Influencer;

public record InfluencerInfo(
Long influencerId,
String influencerName,
String influencerImgUrl,
String influencerJob,
boolean likes
) {

public static InfluencerInfo from(Influencer influencer) {
return new InfluencerInfo(
influencer.getId(),
influencer.getName(),
influencer.getImgUrl(),
influencer.getJob(),
false // 좋아요 기능 추가할 때 로직 추가 예정
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package team7.inplace.influencer.config;

import lombok.RequiredArgsConstructor;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import team7.inplace.influencer.domain.Influencer;
import team7.inplace.influencer.persistence.InfluencerRepository;

@RequiredArgsConstructor
@Component
public class InfluencerDataLoader implements ApplicationRunner {

private final InfluencerRepository influencerRepository;

@Override
public void run(ApplicationArguments args) throws Exception {
Influencer influencer1 = new Influencer("Influencer 1", "job1", "imgUrl1");
Influencer influencer2 = new Influencer("Influencer 2", "job2", "imgUrl2");
Influencer influencer3 = new Influencer("Influencer 3", "job3", "imgUrl3");

influencerRepository.save(influencer1);
influencerRepository.save(influencer2);
influencerRepository.save(influencer3);
}
}
34 changes: 19 additions & 15 deletions src/main/java/team7/inplace/influencer/domain/Influencer.java
Original file line number Diff line number Diff line change
@@ -1,34 +1,38 @@
package team7.inplace.influencer.domain;

import static lombok.AccessLevel.PROTECTED;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@RequiredArgsConstructor // 테스팅을 위한 부분 추가, 협의 하에 다른 방식 채택 가능
@NoArgsConstructor(access = PROTECTED)
@Entity
public class Influencer {
/*
* 더미 데이터 입니다 !!!
*/

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

@Column(nullable = false, length = 30)
private String name;
@Column
@NonNull

@Column(nullable = false, length = 20)
private String job;
@Column
@NonNull

@Column(nullable = false, columnDefinition = "TEXT")
private String imgUrl;

@Builder
public Influencer(String name, String job, String imgUrl) {
this.name = name;
this.job = job;
this.imgUrl = imgUrl;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import team7.inplace.influencer.domain.Influencer;

public interface InfluencerRepository extends JpaRepository<Influencer, Long> {
// 더미 데이터 입니다!!

@Override
List<Influencer> findAllById(Iterable<Long> longs);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package team7.inplace.influencer.presentation;

import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import team7.inplace.influencer.application.InfluencerService;
import team7.inplace.influencer.application.dto.InfluencerInfo;
import team7.inplace.influencer.presentation.dto.InfluencerListResponse;
import team7.inplace.influencer.presentation.dto.InfluencerResponse;

@RequiredArgsConstructor
@RestController
public class InfluencerController {

private final InfluencerService influencerService;

@GetMapping("/influencers")
public ResponseEntity<InfluencerListResponse> getAllInfluencers() {
List<InfluencerInfo> influencersDtoList = influencerService.getAllInfluencers();
List<InfluencerResponse> influencers = influencersDtoList.stream()
.map(InfluencerResponse::from)
.toList();
InfluencerListResponse response = new InfluencerListResponse(influencers);

return new ResponseEntity<>(response, HttpStatus.OK);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package team7.inplace.influencer.presentation.dto;

import java.util.List;

public record InfluencerListResponse(
List<InfluencerResponse> influencers
) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package team7.inplace.influencer.presentation.dto;

import team7.inplace.influencer.application.dto.InfluencerInfo;

public record InfluencerResponse(
Long influencerId,
String influencerName,
String influencerImgUrl,
String influencerJob,
boolean likes
) {

public static InfluencerResponse from(InfluencerInfo influencerInfo) {
return new InfluencerResponse(
influencerInfo.influencerId(),
influencerInfo.influencerName(),
influencerInfo.influencerImgUrl(),
influencerInfo.influencerJob(),
influencerInfo.likes()
);
}
}
21 changes: 11 additions & 10 deletions src/main/java/team7/inplace/video/application/VideoService.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@
@Service
@RequiredArgsConstructor
public class VideoService {

private final VideoRepository videoRepository;
private final InfluencerRepository influencerRepository;
private final PlaceRepository placeRepository;

public List<VideoInfo> getByVideosInfluencer(List<String> influencers) {
// 인플루언서 정보 처리
List<Long> influencerIds = influencerRepository.findByNameIn(influencers).stream()
.map(Influencer::getId)
.toList();
.map(Influencer::getId)
.toList();

// 인플루언서 정보로 필터링한 비디오 정보 불러오기
List<Video> savedVideos = videoRepository.findVideosByInfluencerIdIn(influencerIds);
Expand Down Expand Up @@ -67,16 +68,16 @@ private List<VideoInfo> videoToInfo(List<Video> savedVideos) {
for (Video savedVideo : savedVideos) {
Place place = savedVideo.getPlace();
String alias = AliasUtil.makeAlias(
savedVideo.getInfluencer().getName(),
place.getCategory()
savedVideo.getInfluencer().getName(),
place.getCategory()
);
videoInfos.add(
new VideoInfo(
savedVideo.getId(),
alias,
savedVideo.getVideoUrl(),
PlaceForVideo.of(place.getId(), place.getName())
)
new VideoInfo(
savedVideo.getId(),
alias,
savedVideo.getVideoUrl(),
PlaceForVideo.of(place.getId(), place.getName())
)
);
}
return videoInfos;
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/team7/inplace/video/domain/Video.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package team7.inplace.video.domain;

import static lombok.AccessLevel.PROTECTED;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
Expand All @@ -21,6 +23,7 @@
@NoArgsConstructor(access = PROTECTED)
@RequiredArgsConstructor // 테스팅을 위한 부분 추가, 협의 하에 다른 방식 채택 가능
public class Video {

@Id
@GeneratedValue(strategy = IDENTITY)
private Long id;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package team7.inplace.influencer;

import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import team7.inplace.influencer.application.InfluencerService;
import team7.inplace.influencer.application.dto.InfluencerInfo;
import team7.inplace.influencer.presentation.InfluencerController;
import team7.inplace.influencer.presentation.dto.InfluencerListResponse;
import team7.inplace.influencer.presentation.dto.InfluencerResponse;

@ExtendWith(MockitoExtension.class)
public class InfluencerControllerTest {

private MockMvc mockMvc;

@Mock
private InfluencerService influencerService;

@InjectMocks
private InfluencerController influencerController;

private ObjectMapper objectMapper;

@BeforeEach
public void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(influencerController).build();
objectMapper = new ObjectMapper();
}

@Test
public void getAllInfluencersTest() throws Exception {
InfluencerInfo influencerInfo1 = new InfluencerInfo(1L, "influencer1", "imgUrl1", "job1",
false);
InfluencerInfo influencerInfo2 = new InfluencerInfo(2L, "influencer2", "imgUrl2", "job2",
false);
List<InfluencerInfo> influencerInfoList = List.of(influencerInfo1, influencerInfo2);
given(influencerService.getAllInfluencers()).willReturn(influencerInfoList);

// 예상 json 값
List<InfluencerResponse> responseList = influencerInfoList.stream()
.map(InfluencerResponse::from)
.toList();
InfluencerListResponse expectedResponse = new InfluencerListResponse(responseList);
String expectedJson = objectMapper.writeValueAsString(expectedResponse);

mockMvc.perform(get("/influencers")
.contentType(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().json(expectedJson));
verify(influencerService, times(1)).getAllInfluencers(); // 서비스 호출 확인
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package team7.inplace.influencer;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import team7.inplace.influencer.domain.Influencer;
import team7.inplace.influencer.persistence.InfluencerRepository;

@DataJpaTest
public class InfluencerRepositoryTest {

@Autowired
private InfluencerRepository influencerRepository;

@Test
public void findAllTest() {
Influencer influencer1 = Influencer.builder()
.name("influencer1")
.job("job1")
.imgUrl("imgUrl1")
.build();
Influencer influencer2 = Influencer.builder()
.name("influencer2")
.job("job2")
.imgUrl("imgUrl2")
.build();
influencerRepository.save(influencer1);
influencerRepository.save(influencer2);

List<Influencer> savedInfluencers = influencerRepository.findAll();

assertThat(savedInfluencers.get(0)).usingRecursiveComparison().isEqualTo(influencer1);
assertThat(savedInfluencers.get(1)).usingRecursiveComparison().isEqualTo(influencer2);
}

}
Loading

0 comments on commit e81ae55

Please sign in to comment.