Skip to content

Commit

Permalink
[BE] feat: 축제 목록 기능 구현, 학교, 아티스트 CRUD 구현(#657) (#688)
Browse files Browse the repository at this point in the history
* chore: flyway 파일 추가

- 아티스트 관련

* feat: Artist 추가

* feat: SchoolRegion 추가

* feat: 아티스트 이벤트 객체 추가

* [BE] feat: CI 스크립트 대상 브랜치 feat/** 추가 (#662) (#663)

feat: 대상 브랜치 `feat/**` 추가
(cherry picked from commit 325a5c2)

* feat: 축제 목록 조회 구현 (#658) (#667)

* feat: 축제 목록 조회 API 생성

* refactor: FestivalIndexRequest 로 모든 검증 처리

* feat: 지역별 학교 조회 기능 구현

* feat: 축제 목록 검색 구현

* feat: 테스트 수정

* feat: queryService 로직 수정

* feat: queryService 로직 수정 및 테스트 작성

* chore: QueryService Class 레벨에 readOnly 명시

* feat: orderBy 명시

* feat: artistInfo 를 varchar -> text 로 변경

* refactor: FestivalInfo 가 Festival 을 간접참조 하도록 변경

* refactor: converter -> serializer 로 네이밍 변경

* refactor: FestivalV1ListResponse 변환 로직을 service 로 이동

* chore: 잘못된 네이밍 변경

* chore: 축제 api 경로 수정

* chore: Repository 네이밍 변경

* chore: Test 네이밍 변경

* fix: 잘못된 축제 커서 검증 수정

* chore: dto 위치 변경

* chore: dto 필드명 수정 및 queryProjection 용 생성자 추가

* refactor: Slice 를 사용하도록 변경

* refactor: Pageable 사용

* chore: 사용하지 않는 class 삭제

* fix: override 삭제

* chore: 테스트용 출력 삭제

* chore: 메서드 순서 변경

* chore: json 저장 및 반환 방식 변경

* chore: 사용하지 않는 dto 삭제 및 메서드 명 변경

* fix: 잘못 적힌 page 수정

* fix: paging 요청 만큼 데이터를 반환한다

* refactor: dto 생성 과정 중 ValidException 을 발생시키고 처리한다.

* refactor: dto 로직 제거

* [BE] feat: 아티스트 CRUD 기능을 새롭게 추가 (#665) (#666)

* feat: ArtistCommandService 구현

* feat: ArtistQueryService 구현

* test: ArtistCommandService 통합테스트 추가

* feat: Artist Admin API 구현

* feat: Artist Admin 삭제 API 구현

* [BE] feat: CI 스크립트 대상 브랜치 feat/** 추가 (#662) (#663)

feat: 대상 브랜치 `feat/**` 추가
(cherry picked from commit 325a5c2)

* refactor: 명시적인 read-only = false 설정 삭제

* test: 컨벤션에 맞게 test 코드 변경

* refactor: 클래스명에 버저닝 적용

* refactor: 클래스명에 버저닝 적용

* fix: 버저닝 적용 잘못된 것 수정

* refactor: 컨벤션에 맞게 체이닝 변경

---------

Co-authored-by: Seokjin Jeon <[email protected]>

* [BE] feat: 새로운 학교 CRUD 기능 추가 (#658) (#661)

* feat: SchoolV1Controller 추가

- /api/v1/schools
- 전체 조회는 검색 필터, 페이징 적용
- 기존 SchoolController, Response Deprecated 처리

* feat: AdminSchoolV1Controller 추가

- /admin/api/v1/schools
- 기존 API Deprecated 처리

* feat: SchoolDeleteService 추가

* feat: SchoolCommandService 추가

* feat: SchoolV1QueryService 추가

* fix: 불필요한 `@Repository` 삭제

* fix: 구현되지 않은 학교 지역 변경 주석 처리

- 추후 학교 도메인 이슈에서 주석 해제 할 것

* refactor: fetchOne Optional 반환하도록 변경

* refactor: var 제거, 명시적인 타입 지정

* refactor: QueryDslRepositorySupport 불필요한 설정 제거

- EntityManager 필드 제거
- validate 메서드 제거

* fix: assertSoftly softly 누락 수정

* fix: 학교를 수정할 때 수정할 학교의 정보와 같을 때 예외가 발생하는 버그 수정

* refactor: 필요하지 않은 DTO 클래스 제거

* [BE] feat: CI 스크립트 대상 브랜치 feat/** 추가 (#662) (#663)

feat: 대상 브랜치 `feat/**` 추가
(cherry picked from commit 325a5c2)

* [BE] refactor: FestivalV1QueryService에서 FestivalV1QueryDslRepository을 의존하도록 변경 (#678) (#679)

* refactor: FestivalV1QueryService에서 FestivalRepository 의존을 FestivalV1QueryDslRepository 의존하도록 변경

* refactor: JPA Repository QueryDSL 의존 제거

* [BE] test: 큐컴버 테스트 추가 (#658) (#674)

* feat: SchoolV1Controller 추가

- /api/v1/schools
- 전체 조회는 검색 필터, 페이징 적용
- 기존 SchoolController, Response Deprecated 처리

* feat: AdminSchoolV1Controller 추가

- /admin/api/v1/schools
- 기존 API Deprecated 처리

* feat: SchoolDeleteService 추가

* feat: SchoolCommandService 추가

* feat: SchoolV1QueryService 추가

* fix: 불필요한 `@Repository` 삭제

* fix: 구현되지 않은 학교 지역 변경 주석 처리

- 추후 학교 도메인 이슈에서 주석 해제 할 것

* refactor: fetchOne Optional 반환하도록 변경

* refactor: var 제거, 명시적인 타입 지정

* refactor: QueryDslRepositorySupport 불필요한 설정 제거

- EntityManager 필드 제거
- validate 메서드 제거

* fix: assertSoftly softly 누락 수정

* fix: 학교를 수정할 때 수정할 학교의 정보와 같을 때 예외가 발생하는 버그 수정

* refactor: 필요하지 않은 DTO 클래스 제거

* [BE] feat: CI 스크립트 대상 브랜치 feat/** 추가 (#662) (#663)

feat: 대상 브랜치 `feat/**` 추가
(cherry picked from commit 325a5c2)

* test: 큐컴버 테스트 추가

* refactor: static 키워드 제거

* [BE] feat: 인기 축제 목록 구현(#657) (#682)

* fix: 명세에 맞게끔 dto 변경

* feat: 인기 축제 목록 반환 구현

* chore: readonly 설정

* fix: conflict 오류 수정

* [BE] feat: Pageable 최대 size 검증하는 AOP 추가 (#658) (#675)

* feat: SchoolV1Controller 추가

- /api/v1/schools
- 전체 조회는 검색 필터, 페이징 적용
- 기존 SchoolController, Response Deprecated 처리

* feat: AdminSchoolV1Controller 추가

- /admin/api/v1/schools
- 기존 API Deprecated 처리

* feat: SchoolDeleteService 추가

* feat: SchoolCommandService 추가

* feat: SchoolV1QueryService 추가

* fix: 불필요한 `@Repository` 삭제

* fix: 구현되지 않은 학교 지역 변경 주석 처리

- 추후 학교 도메인 이슈에서 주석 해제 할 것

* refactor: fetchOne Optional 반환하도록 변경

* refactor: var 제거, 명시적인 타입 지정

* refactor: QueryDslRepositorySupport 불필요한 설정 제거

- EntityManager 필드 제거
- validate 메서드 제거

* fix: assertSoftly softly 누락 수정

* fix: 학교를 수정할 때 수정할 학교의 정보와 같을 때 예외가 발생하는 버그 수정

* refactor: 필요하지 않은 DTO 클래스 제거

* [BE] feat: CI 스크립트 대상 브랜치 feat/** 추가 (#662) (#663)

feat: 대상 브랜치 `feat/**` 추가
(cherry picked from commit 325a5c2)

* feat: ValidPageable AOP 추가

- Pageable의 최대 size 검증용

* fix: 오타 수정

mas -> max

---------

Co-authored-by: BGuga <[email protected]>

* [BE] refactor: FestivalV1Controller에서 Pageable을 받도록 변경, FestivalV1QueryService 통합 테스트 개선 (#680) (#681)

* refactor: FestivalV1Controller Pageable 받도록 변경, FestivalV1QueryServiceTest 가독성 높게 변경

* test: 커서 기반 페이징 검증 테스트 추가

* test: actual -> response 변경

- expect가 없으므로, 더 명확한 response로 변경

---------

Co-authored-by: BGuga <[email protected]>

* fix: 컴파일 오류 수정

* [BE] feat: FesitvalQueryInfo에 BaseTimeEntity 상속, 유니크 인덱스 추가 (#685) (#686)

* feat: FestivalQueryInfo BaseTimeEntity 상속 추가

* feat: festival_query_info 테이블 festival_id 유니크 인덱스 추가

* feat: festival 테이블 start_date 내림차순 인덱스 추가

* [BE] feat: Swagger annotaion 추가 (#683) (#687)

chore: swagger 추가

---------

Co-authored-by: carsago <[email protected]>
Co-authored-by: Seokjin Jeon <[email protected]>
Co-authored-by: Hyun-Seo Oh / 오현서 <[email protected]>
  • Loading branch information
4 people authored Jan 30, 2024
1 parent 505f4d7 commit 34600aa
Show file tree
Hide file tree
Showing 98 changed files with 3,588 additions and 181 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.festago.admin.dto;

import jakarta.validation.constraints.NotBlank;

public record ArtistCreateRequest(
@NotBlank(message = "아티스트 이름은 비어있을 수 없습니다.")
String name,
@NotBlank(message = "아티스트 이미지는 비어있을 수 없습니다.")
String profileImage
) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.festago.admin.dto;

import jakarta.validation.constraints.NotBlank;

public record ArtistUpdateRequest(
@NotBlank(message = "아티스트 이름은 비어있을 수 없습니다.")
String name,
@NotBlank(message = "아티스트 이미지는 비어있을 수 없습니다.")
String profileImage
) {

}
14 changes: 14 additions & 0 deletions backend/src/main/java/com/festago/admin/dto/ArtistV1Response.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.festago.admin.dto;

import com.festago.artist.domain.Artist;

public record ArtistV1Response(
Long id,
String name,
String profileImage
) {

public static ArtistV1Response from(Artist artist) {
return new ArtistV1Response(artist.getId(), artist.getName(), artist.getProfileImage());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,21 @@ public ResponseEntity<TicketCreateResponse> createTicket(@RequestBody @Valid Tic
.body(response);
}

/**
* @deprecated API 버저닝이 적용되면 해당 메서드 삭제
*/
@Deprecated(forRemoval = true)
@PostMapping("/schools")
public ResponseEntity<SchoolResponse> createSchool(@RequestBody @Valid SchoolCreateRequest request) {
SchoolResponse response = schoolService.create(request);
return ResponseEntity.ok()
.body(response);
}

/**
* @deprecated API 버저닝이 적용되면 해당 메서드 삭제
*/
@Deprecated(forRemoval = true)
@PatchMapping("/schools/{schoolId}")
public ResponseEntity<Void> updateSchool(@RequestBody @Valid SchoolUpdateRequest request,
@PathVariable Long schoolId) {
Expand All @@ -113,6 +121,10 @@ public ResponseEntity<Void> updateSchool(@RequestBody @Valid SchoolUpdateRequest
.build();
}

/**
* @deprecated API 버저닝이 적용되면 해당 메서드 삭제
*/
@Deprecated(forRemoval = true)
@DeleteMapping("/schools/{schoolId}")
public ResponseEntity<Void> deleteSchool(@PathVariable Long schoolId) {
schoolService.delete(schoolId);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.festago.admin.presentation.v1;

import com.festago.admin.dto.ArtistCreateRequest;
import com.festago.admin.dto.ArtistUpdateRequest;
import com.festago.admin.dto.ArtistV1Response;
import com.festago.artist.application.ArtistCommandService;
import com.festago.artist.application.ArtistV1QueryService;
import io.swagger.v3.oas.annotations.Hidden;
import jakarta.validation.Valid;
import java.net.URI;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/admin/api/v1/artists")
@RequiredArgsConstructor
@Hidden
public class AdminArtistV1Controller {

private final ArtistCommandService artistCommandService;
private final ArtistV1QueryService artistV1QueryService;

@PostMapping
public ResponseEntity<Void> create(@RequestBody @Valid ArtistCreateRequest request) {
Long artistId = artistCommandService.save(request);
return ResponseEntity.created(URI.create("/admin/api/v1/artists/" + artistId))
.build();
}

@PutMapping("/{artistId}")
public ResponseEntity<Void> update(@RequestBody @Valid ArtistUpdateRequest request,
@PathVariable Long artistId) {
artistCommandService.update(request, artistId);
return ResponseEntity.ok().build();
}

@DeleteMapping("/{artistId}")
public ResponseEntity<Void> delete(@PathVariable Long artistId) {
artistCommandService.delete(artistId);
return ResponseEntity.noContent().build();
}

@GetMapping("/{artistId}")
public ResponseEntity<ArtistV1Response> findById(@PathVariable Long artistId) {
ArtistV1Response response = artistV1QueryService.findById(artistId);
return ResponseEntity.ok(response);
}

@GetMapping
public ResponseEntity<List<ArtistV1Response>> findAll() {
List<ArtistV1Response> response = artistV1QueryService.findAll();
return ResponseEntity.ok(response);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.festago.admin.presentation.v1;

import com.festago.admin.presentation.v1.dto.SchoolV1CreateRequest;
import com.festago.admin.presentation.v1.dto.SchoolV1UpdateRequest;
import com.festago.school.application.SchoolCommandService;
import com.festago.school.application.SchoolDeleteService;
import io.swagger.v3.oas.annotations.Hidden;
import java.net.URI;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/admin/api/v1/schools")
@RequiredArgsConstructor
@Hidden
public class AdminSchoolV1Controller {

private final SchoolCommandService schoolCommandService;
private final SchoolDeleteService schoolDeleteService;

@PostMapping
public ResponseEntity<Void> createSchool(
@RequestBody SchoolV1CreateRequest request
) {
Long schoolId = schoolCommandService.createSchool(request.toCommand());
return ResponseEntity.created(URI.create("/api/v1/schools/" + schoolId))
.build();
}

@PatchMapping("/{schoolId}")
public ResponseEntity<Void> updateSchool(
@PathVariable Long schoolId,
@RequestBody SchoolV1UpdateRequest request
) {
schoolCommandService.updateSchool(schoolId, request.toCommand());
return ResponseEntity.ok()
.build();
}

@DeleteMapping("/{schoolId}")
public ResponseEntity<Void> deleteSchool(
@PathVariable Long schoolId
) {
schoolDeleteService.deleteSchool(schoolId);
return ResponseEntity.noContent()
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.festago.admin.presentation.v1.dto;

import com.festago.school.domain.SchoolRegion;
import com.festago.school.dto.SchoolCreateCommand;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

public record SchoolV1CreateRequest(
@NotBlank(message = "name은 공백일 수 없습니다.")
String name,
@NotBlank(message = "domain은 공백일 수 없습니다.")
String domain,
@NotNull(message = "region은 null일 수 없습니다.")
SchoolRegion region
) {

public SchoolCreateCommand toCommand() {
return new SchoolCreateCommand(name, domain, region);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.festago.admin.presentation.v1.dto;

import com.festago.school.domain.SchoolRegion;
import com.festago.school.dto.SchoolUpdateCommand;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;

public record SchoolV1UpdateRequest(
@NotBlank(message = "name은 공백일 수 없습니다.")
String name,
@NotBlank(message = "domain은 공백일 수 없습니다.")
String domain,
@NotNull(message = "region은 null일 수 없습니다.")
SchoolRegion region
) {

public SchoolUpdateCommand toCommand() {
return new SchoolUpdateCommand(name, domain, region);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.festago.artist.application;

import com.festago.admin.dto.ArtistCreateRequest;
import com.festago.admin.dto.ArtistUpdateRequest;
import com.festago.artist.domain.Artist;
import com.festago.artist.repository.ArtistRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
@RequiredArgsConstructor
public class ArtistCommandService {

private final ArtistRepository artistRepository;

public Long save(ArtistCreateRequest request) {
Artist artist = artistRepository.save(new Artist(request.name(), request.profileImage()));
return artist.getId();
}

public void update(ArtistUpdateRequest request, Long artistId) {
Artist artist = artistRepository.getOrThrow(artistId);
artist.update(request.name(), request.profileImage());
}

public void delete(Long artistId) {
artistRepository.deleteById(artistId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.festago.artist.application;

import com.festago.admin.dto.ArtistV1Response;
import com.festago.artist.domain.Artist;
import com.festago.artist.repository.ArtistRepository;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class ArtistV1QueryService {

private final ArtistRepository artistRepository;

public ArtistV1Response findById(Long artistId) {
Artist artist = artistRepository.getOrThrow(artistId);
return ArtistV1Response.from(artist);
}

public List<ArtistV1Response> findAll() {
return artistRepository.findAll().stream()
.map(ArtistV1Response::from)
.toList();
}
}
46 changes: 46 additions & 0 deletions backend/src/main/java/com/festago/artist/domain/Artist.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.festago.artist.domain;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Artist {

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

public Artist(String name, String profileImage) {
this(null, name, profileImage);
}

public Artist(Long id, String name, String profileImage) {
this.id = id;
this.name = name;
this.profileImage = profileImage;
}

public Long getId() {
return id;
}

public String getName() {
return name;
}

public String getProfileImage() {
return profileImage;
}

public void update(String name, String profileImage) {
this.name = name;
this.profileImage = profileImage;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.festago.artist.dto.event;

public record ArtistDeleteEvent(
Long artistId
) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.festago.artist.dto.event;

public record ArtistUpdateEvent(
Long artistId
) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.festago.artist.repository;

import com.festago.artist.domain.Artist;
import com.festago.common.exception.ErrorCode;
import com.festago.common.exception.NotFoundException;
import java.util.List;
import java.util.Optional;
import org.springframework.data.repository.Repository;

public interface ArtistRepository extends Repository<Artist, Long> {

default Artist getOrThrow(Long artistId) {
return findById(artistId)
.orElseThrow(() -> new NotFoundException(ErrorCode.ARTIST_NOT_FOUND));
}

Artist save(Artist artist);

void deleteById(Long artistId);

Optional<Artist> findById(Long id);

List<Artist> findAll();
}
Loading

0 comments on commit 34600aa

Please sign in to comment.