Skip to content

Commit

Permalink
재활운동 영상 제공 완료 (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
limehee authored Jul 24, 2024
1 parent 5553e59 commit 3747a02
Show file tree
Hide file tree
Showing 12 changed files with 368 additions and 0 deletions.
10 changes: 10 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ plugins {
group = 'com.stempo'
version = '0.0.1-SNAPSHOT'

jar {
enabled = false
}

bootJar {
archivesBaseName = "stempo"
archiveFileName = "stempo.jar"
archiveVersion = "0.0.1"
}

java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.stempo.api.domain.application.service;

import com.stempo.api.domain.presentation.dto.request.VideoRequestDto;
import com.stempo.api.domain.presentation.dto.request.VideoUpdateRequestDto;
import com.stempo.api.domain.presentation.dto.response.VideoResponseDto;

import java.util.List;

public interface VideoService {

Long registerVideo(VideoRequestDto requestDto);

List<VideoResponseDto> getVideos();

Long updateVideo(Long videoId, VideoUpdateRequestDto requestDto);

Long deleteVideo(Long id);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.stempo.api.domain.application.service;

import com.stempo.api.domain.domain.model.Video;
import com.stempo.api.domain.domain.repository.VideoRepository;
import com.stempo.api.domain.presentation.dto.request.VideoRequestDto;
import com.stempo.api.domain.presentation.dto.request.VideoUpdateRequestDto;
import com.stempo.api.domain.presentation.dto.response.VideoResponseDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@RequiredArgsConstructor
public class VideoServiceImpl implements VideoService {

private final VideoRepository videoRepository;

@Override
public Long registerVideo(VideoRequestDto requestDto) {
Video video = VideoRequestDto.toDomain(requestDto);
return videoRepository.save(video).getId();
}

@Override
public List<VideoResponseDto> getVideos() {
List<Video> videos = videoRepository.findAll();
return videos.stream()
.map(VideoResponseDto::toDto)
.toList();
}

@Override
public Long updateVideo(Long videoId, VideoUpdateRequestDto requestDto) {
Video video = videoRepository.findByIdOrThrow(videoId);
video.update(requestDto);
return videoRepository.save(video).getId();
}

@Override
public Long deleteVideo(Long id) {
Video video = videoRepository.findByIdOrThrow(id);
video.delete();
return videoRepository.save(video).getId();
}
}
43 changes: 43 additions & 0 deletions src/main/java/com/stempo/api/domain/domain/model/Video.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.stempo.api.domain.domain.model;

import com.stempo.api.domain.presentation.dto.request.VideoUpdateRequestDto;
import lombok.*;

import java.time.LocalDateTime;
import java.util.Optional;

@Getter
@Setter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class Video {

private Long id;
private String title;
private String content;
private String thumbnailUrl;
private String videoUrl;
private LocalDateTime createdAt;
private boolean deleted;

public static Video create(String title, String content, String thumbnailUrl, String videoUrl) {
return Video.builder()
.title(title)
.content(content)
.thumbnailUrl(thumbnailUrl)
.videoUrl(videoUrl)
.build();
}

public void update(VideoUpdateRequestDto requestDto) {
Optional.ofNullable(requestDto.getTitle()).ifPresent(this::setTitle);
Optional.ofNullable(requestDto.getContent()).ifPresent(this::setContent);
Optional.ofNullable(requestDto.getThumbnailUrl()).ifPresent(this::setThumbnailUrl);
Optional.ofNullable(requestDto.getVideoUrl()).ifPresent(this::setVideoUrl);
}

public void delete() {
setDeleted(true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.stempo.api.domain.domain.repository;

import com.stempo.api.domain.domain.model.Video;

import java.util.List;

public interface VideoRepository {

Video save(Video video);

List<Video> findAll();

Video findByIdOrThrow(Long videoId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.stempo.api.domain.persistence.mappper;

import com.stempo.api.domain.domain.model.Video;
import com.stempo.api.domain.persistence.entity.VideoEntity;
import org.springframework.stereotype.Component;

@Component
public class VideoMapper {

public VideoEntity toEntity(Video video) {
return VideoEntity.builder()
.id(video.getId())
.title(video.getTitle())
.content(video.getContent())
.thumbnailUrl(video.getThumbnailUrl())
.videoUrl(video.getVideoUrl())
.deleted(video.isDeleted())
.build();
}

public Video toDomain(VideoEntity entity) {
return Video.builder()
.id(entity.getId())
.title(entity.getTitle())
.content(entity.getContent())
.thumbnailUrl(entity.getThumbnailUrl())
.videoUrl(entity.getVideoUrl())
.deleted(entity.isDeleted())
.createdAt(entity.getCreatedAt())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.stempo.api.domain.persistence.repository;

import com.stempo.api.domain.persistence.entity.VideoEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import java.util.List;

public interface VideoJpaRepository extends JpaRepository<VideoEntity, Long> {

@Query("SELECT v " +
"FROM VideoEntity v " +
"WHERE v.deleted = false " +
"ORDER BY v.createdAt ASC")
List<VideoEntity> findAllVideos();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.stempo.api.domain.persistence.repository;

import com.stempo.api.domain.domain.model.Video;
import com.stempo.api.domain.domain.repository.VideoRepository;
import com.stempo.api.domain.persistence.entity.VideoEntity;
import com.stempo.api.domain.persistence.mappper.VideoMapper;
import com.stempo.api.global.exception.NotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
@RequiredArgsConstructor
public class VideoRepositoryImpl implements VideoRepository {

private final VideoJpaRepository repository;
private final VideoMapper mapper;

@Override
public Video save(Video video) {
VideoEntity jpaEntity = mapper.toEntity(video);
VideoEntity savedEntity = repository.save(jpaEntity);
return mapper.toDomain(savedEntity);
}

@Override
public List<Video> findAll() {
List<VideoEntity> videos = repository.findAllVideos();
return videos.stream()
.map(mapper::toDomain)
.toList();
}

@Override
public Video findByIdOrThrow(Long videoId) {
return repository.findById(videoId)
.map(mapper::toDomain)
.orElseThrow(() -> new NotFoundException("[Video] id: " + videoId + " not found"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.stempo.api.domain.presentation;

import com.stempo.api.domain.application.service.VideoService;
import com.stempo.api.domain.presentation.dto.request.VideoRequestDto;
import com.stempo.api.domain.presentation.dto.request.VideoUpdateRequestDto;
import com.stempo.api.domain.presentation.dto.response.VideoResponseDto;
import com.stempo.api.global.common.dto.ApiResponse;
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.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequiredArgsConstructor
@Tag(name = "Video", description = "재활운동 영상")
public class VideoController {

private final VideoService videoService;

@Operation(summary = "[A] 재활운동 영상 등록", description = "ROLE_ADMIN 이상의 권한이 필요함")
@Secured({ "ROLE_ADMIN" })
@PostMapping("/api/v1/videos")
public ApiResponse<Long> registerVideo(
@Valid @RequestBody VideoRequestDto requestDto
) {
Long id = videoService.registerVideo(requestDto);
return ApiResponse.success(id);
}

@Operation(summary = "[U] 재활운동 영상 조회", description = "ROLE_USER 이상의 권한이 필요함")
@Secured({ "ROLE_USER", "ROLE_ADMIN" })
@GetMapping("/api/v1/videos")
public ApiResponse<List<VideoResponseDto>> getVideos() {
List<VideoResponseDto> videoResponseDtos = videoService.getVideos();
return ApiResponse.success(videoResponseDtos);
}

@Operation(summary = "[U] 재활운동 영상 수정", description = "ROLE_USER 이상의 권한이 필요함")
@Secured({ "ROLE_USER", "ROLE_ADMIN" })
@PatchMapping("/api/v1/videos/{videoId}")
public ApiResponse<Long> updateVideo(
@PathVariable(name = "videoId") Long videoId,
@Valid @RequestBody VideoUpdateRequestDto requestDto
) {
Long id = videoService.updateVideo(videoId, requestDto);
return ApiResponse.success(id);
}

@Operation(summary = "[A] 재활운동 영상 삭제", description = "ROLE_ADMIN 이상의 권한이 필요함")
@Secured({ "ROLE_ADMIN" })
@DeleteMapping("/api/v1/videos/{videoId}")
public ApiResponse<Long> deleteVideo(
@PathVariable Long videoId
) {
Long id = videoService.deleteVideo(videoId);
return ApiResponse.success(id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.stempo.api.domain.presentation.dto.request;

import com.stempo.api.domain.domain.model.Video;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class VideoRequestDto {

@NotNull
@Schema(description = "영상 제목", example = "영상 제목", minLength = 1, maxLength = 100, requiredMode = Schema.RequiredMode.REQUIRED)
private String title;

@NotNull
@Schema(description = "영상 내용", example = "영상 내용", minLength = 1, maxLength = 10000, requiredMode = Schema.RequiredMode.REQUIRED)
private String content;

@Schema(description = "썸네일 URL", example = "https://example.com/thumbnail.jpg", minLength = 1, maxLength = 255)
private String thumbnailUrl;

@NotNull
@Schema(description = "영상 URL", example = "https://example.com/video.mp4", minLength = 1, maxLength = 255, requiredMode = Schema.RequiredMode.REQUIRED)
private String videoUrl;

public static Video toDomain(VideoRequestDto requestDto) {
return Video.builder()
.title(requestDto.getTitle())
.content(requestDto.getContent())
.thumbnailUrl(requestDto.getThumbnailUrl())
.videoUrl(requestDto.getVideoUrl())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.stempo.api.domain.presentation.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class VideoUpdateRequestDto {

@Schema(description = "영상 제목", example = "영상 제목", minLength = 1, maxLength = 100)
private String title;

@Schema(description = "영상 내용", example = "영상 내용", minLength = 1, maxLength = 10000)
private String content;

@Schema(description = "썸네일 URL", example = "https://example.com/thumbnail.jpg", minLength = 1, maxLength = 255)
private String thumbnailUrl;

@Schema(description = "영상 URL", example = "https://example.com/video.mp4", minLength = 1, maxLength = 255)
private String videoUrl;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.stempo.api.domain.presentation.dto.response;

import com.stempo.api.domain.domain.model.Video;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class VideoResponseDto {

private final Long id;
private final String title;
private final String content;
private final String thumbnailUrl;
private final String videoUrl;
private final String createdAt;

public static VideoResponseDto toDto(Video video) {
return VideoResponseDto.builder()
.id(video.getId())
.title(video.getTitle())
.content(video.getContent())
.thumbnailUrl(video.getThumbnailUrl())
.videoUrl(video.getVideoUrl())
.createdAt(video.getCreatedAt().toString())
.build();
}
}

0 comments on commit 3747a02

Please sign in to comment.