Skip to content

Commit

Permalink
[BE] feat: 소셜미디어 CRUD 기능 추가 (#802) (#848)
Browse files Browse the repository at this point in the history
* chore: 줄바꿈 제거

* feat: 소셜미디어 추가 기능 추가

* feat: 소셜미디어 수정 기능 추가

* test: 소셜미디어 추가 테스트 검증 변경

* feat: 소셜미디어 삭제 기능 추가

* feat: AdminSocialMediaV1Controller 추가

* chore: SocialMediaCommandService validate 메서드명 명확하게 변경

- validate -> validateCreate

* refactor: MemorySocialMediaRepository AbstractMemoryRepository 상속하도록 변경

* feat: AdminSocialMediaV1QueryService, QueryDslRepository 추가

* feat: 관리자용 소셜미디어 조회 API 추가

- findByOwnerIdAndOwnerType: 목록 조회
- findById: 단건 조회

* feat: AdminSocialMediaV1Response 필드 추가

- ownerId, ownerType
  • Loading branch information
seokjin8678 authored Apr 11, 2024
1 parent 8858eb8 commit e4b753a
Show file tree
Hide file tree
Showing 17 changed files with 956 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.festago.admin.application;

import com.festago.admin.dto.socialmedia.AdminSocialMediaV1Response;
import com.festago.admin.repository.AdminSocialMediaV1QueryDslRepository;
import com.festago.common.exception.ErrorCode;
import com.festago.common.exception.NotFoundException;
import com.festago.socialmedia.domain.OwnerType;
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 AdminSocialMediaV1QueryService {

private final AdminSocialMediaV1QueryDslRepository adminSocialMediaV1QueryDslRepository;

public AdminSocialMediaV1Response findById(Long socialMediaId) {
return adminSocialMediaV1QueryDslRepository.findById(socialMediaId)
.orElseThrow(() -> new NotFoundException(ErrorCode.SOCIAL_MEDIA_NOT_FOUND));
}

public List<AdminSocialMediaV1Response> findByOwnerIdAndOwnerType(Long ownerId, OwnerType ownerType) {
return adminSocialMediaV1QueryDslRepository.findByOwnerIdAndOwnerType(ownerId, ownerType);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.festago.admin.dto.socialmedia;

import com.festago.socialmedia.domain.OwnerType;
import com.festago.socialmedia.domain.SocialMediaType;
import com.querydsl.core.annotations.QueryProjection;

public record AdminSocialMediaV1Response(
Long id,
Long ownerId,
OwnerType ownerType,
SocialMediaType socialMediaType,
String name,
String logoUrl,
String url
) {

@QueryProjection
public AdminSocialMediaV1Response {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.festago.admin.dto.socialmedia;

import com.festago.socialmedia.domain.OwnerType;
import com.festago.socialmedia.domain.SocialMediaType;
import com.festago.socialmedia.dto.command.SocialMediaCreateCommand;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Builder;

@Builder
public record SocialMediaCreateV1Request(
@NotNull
Long ownerId,
@NotNull
OwnerType ownerType,
@NotNull
SocialMediaType socialMediaType,
@NotBlank
String name,
@NotBlank
String logoUrl,
@NotBlank
String url
) {

public SocialMediaCreateCommand toCommand() {
return new SocialMediaCreateCommand(
ownerId,
ownerType,
socialMediaType,
name,
logoUrl,
url
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.festago.admin.dto.socialmedia;

import com.festago.socialmedia.dto.command.SocialMediaUpdateCommand;
import jakarta.validation.constraints.NotBlank;
import lombok.Builder;

@Builder
public record SocialMediaUpdateV1Request(
@NotBlank
String name,
@NotBlank
String logoUrl,
@NotBlank
String url
) {

public SocialMediaUpdateCommand toCommand() {
return new SocialMediaUpdateCommand(
name,
logoUrl,
url
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package com.festago.admin.presentation.v1;

import com.festago.admin.application.AdminSocialMediaV1QueryService;
import com.festago.admin.dto.socialmedia.AdminSocialMediaV1Response;
import com.festago.admin.dto.socialmedia.SocialMediaCreateV1Request;
import com.festago.admin.dto.socialmedia.SocialMediaUpdateV1Request;
import com.festago.socialmedia.application.SocialMediaCommandService;
import com.festago.socialmedia.domain.OwnerType;
import io.swagger.v3.oas.annotations.Hidden;
import jakarta.validation.Valid;
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.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.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/admin/api/v1/socialmedias")
@RequiredArgsConstructor
@Hidden
public class AdminSocialMediaV1Controller {

private final SocialMediaCommandService socialMediaCommandService;
private final AdminSocialMediaV1QueryService adminSocialMediaV1QueryService;

@GetMapping
public ResponseEntity<List<AdminSocialMediaV1Response>> findByOwnerIdAndOwnerType(
@RequestParam Long ownerId,
@RequestParam OwnerType ownerType
) {
return ResponseEntity.ok()
.body(adminSocialMediaV1QueryService.findByOwnerIdAndOwnerType(ownerId, ownerType));
}

@GetMapping("/{socialMediaId}")
public ResponseEntity<AdminSocialMediaV1Response> findById(
@PathVariable Long socialMediaId
) {
return ResponseEntity.ok()
.body(adminSocialMediaV1QueryService.findById(socialMediaId));
}

@PostMapping
public ResponseEntity<Void> createSocialMedia(
@RequestBody @Valid SocialMediaCreateV1Request request
) {
socialMediaCommandService.createSocialMedia(request.toCommand());
return ResponseEntity.ok()
.build();
}

@PatchMapping("/{socialMediaId}")
public ResponseEntity<Void> updateSocialMedia(
@PathVariable Long socialMediaId,
@RequestBody @Valid SocialMediaUpdateV1Request request
) {
socialMediaCommandService.updateSocialMedia(socialMediaId, request.toCommand());
return ResponseEntity.ok()
.build();
}

@DeleteMapping("/{socialMediaId}")
public ResponseEntity<Void> deleteSocialMedia(
@PathVariable Long socialMediaId
) {
socialMediaCommandService.deleteSocialMedia(socialMediaId);
return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.festago.admin.repository;

import static com.festago.socialmedia.domain.QSocialMedia.socialMedia;

import com.festago.admin.dto.socialmedia.AdminSocialMediaV1Response;
import com.festago.admin.dto.socialmedia.QAdminSocialMediaV1Response;
import com.festago.common.querydsl.QueryDslRepositorySupport;
import com.festago.socialmedia.domain.OwnerType;
import com.festago.socialmedia.domain.SocialMedia;
import java.util.List;
import java.util.Optional;
import org.springframework.stereotype.Repository;

@Repository
public class AdminSocialMediaV1QueryDslRepository extends QueryDslRepositorySupport {

public AdminSocialMediaV1QueryDslRepository() {
super(SocialMedia.class);
}

public Optional<AdminSocialMediaV1Response> findById(Long socialMediaId) {
return fetchOne(query -> query.select(new QAdminSocialMediaV1Response(
socialMedia.id,
socialMedia.ownerId,
socialMedia.ownerType,
socialMedia.mediaType,
socialMedia.name,
socialMedia.logoUrl,
socialMedia.url
))
.from(socialMedia)
.where(socialMedia.id.eq(socialMediaId))
);
}

// SocialMedia의 도메인 특성 상 SocialMediaType 개수만큼 row 생성이 제한되기에 limit을 사용하지 않음
public List<AdminSocialMediaV1Response> findByOwnerIdAndOwnerType(Long ownerId, OwnerType ownerType) {
return select(new QAdminSocialMediaV1Response(
socialMedia.id,
socialMedia.ownerId,
socialMedia.ownerType,
socialMedia.mediaType,
socialMedia.name,
socialMedia.logoUrl,
socialMedia.url
))
.from(socialMedia)
.where(socialMedia.ownerId.eq(ownerId).and(socialMedia.ownerType.eq(ownerType)))
.fetch();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public enum ErrorCode {
BOOKMARK_LIMIT_EXCEEDED("최대 북마크 갯수를 초과했습니다"),
BROAD_SEARCH_KEYWORD("더 자세한 검색어로 입력해야합니다."),
INVALID_KEYWORD("유효하지 않은 키워드 입니다."),
DUPLICATE_SOCIAL_MEDIA("이미 존재하는 소셜미디어 입니다."),

// 401
EXPIRED_AUTH_TOKEN("만료된 로그인 토큰입니다."),
Expand All @@ -61,6 +62,7 @@ public enum ErrorCode {
TICKET_NOT_FOUND("존재하지 않는 티켓입니다."),
SCHOOL_NOT_FOUND("존재하지 않는 학교입니다."),
ARTIST_NOT_FOUND("존재하지 않는 아티스트입니다."),
SOCIAL_MEDIA_NOT_FOUND("존재하지 않는 소셜미디어입니다."),

// 429
TOO_FREQUENT_REQUESTS("너무 잦은 요청입니다. 잠시 후 다시 시도해주세요."),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.festago.socialmedia.application;

import com.festago.artist.repository.ArtistRepository;
import com.festago.common.exception.BadRequestException;
import com.festago.common.exception.ErrorCode;
import com.festago.common.exception.NotFoundException;
import com.festago.school.repository.SchoolRepository;
import com.festago.socialmedia.domain.OwnerType;
import com.festago.socialmedia.domain.SocialMedia;
import com.festago.socialmedia.domain.SocialMediaType;
import com.festago.socialmedia.dto.command.SocialMediaCreateCommand;
import com.festago.socialmedia.dto.command.SocialMediaUpdateCommand;
import com.festago.socialmedia.repository.SocialMediaRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional
public class SocialMediaCommandService {

private final SocialMediaRepository socialMediaRepository;
private final SchoolRepository schoolRepository;
private final ArtistRepository artistRepository;

public Long createSocialMedia(SocialMediaCreateCommand command) {
validateCreate(command);
SocialMedia socialMedia = socialMediaRepository.save(command.toEntity());
return socialMedia.getId();
}

private void validateCreate(SocialMediaCreateCommand command) {
Long ownerId = command.ownerId();
OwnerType ownerType = command.ownerType();
SocialMediaType socialMediaType = command.socialMediaType();
if (socialMediaRepository.existsByOwnerIdAndOwnerTypeAndMediaType(ownerId, ownerType, socialMediaType)) {
throw new BadRequestException(ErrorCode.DUPLICATE_SOCIAL_MEDIA);
}
// TODO 추상적인 에러 코드가 필요할지? ex) ErrorCode.SOCIAL_MEDIA_OWNER_NOT_FOUND
if (ownerType == OwnerType.ARTIST && !artistRepository.existsById(ownerId)) {
throw new NotFoundException(ErrorCode.ARTIST_NOT_FOUND);
}
if (ownerType == OwnerType.SCHOOL && !schoolRepository.existsById(ownerId)) {
throw new NotFoundException(ErrorCode.SCHOOL_NOT_FOUND);
}
}

public void updateSocialMedia(Long socialMediaId, SocialMediaUpdateCommand command) {
SocialMedia socialMedia = socialMediaRepository.getOrThrow(socialMediaId);
socialMedia.changeName(command.name());
socialMedia.changeLogoUrl(command.logoUrl());
socialMedia.changeUrl(command.url());
}

public void deleteSocialMedia(Long socialMediaId) {
socialMediaRepository.deleteById(socialMediaId);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.festago.socialmedia.domain;

public enum OwnerType {

ARTIST,
SCHOOL,
;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,18 @@ public SocialMedia(Long ownerId, OwnerType ownerType, SocialMediaType mediaType,
this(null, ownerId, ownerType, mediaType, name, logoUrl, url);
}

public void changeName(String name) {
this.name = name;
}

public void changeUrl(String url) {
this.url = url;
}

public void changeLogoUrl(String logoUrl) {
this.logoUrl = logoUrl;
}

public Long getId() {
return id;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.festago.socialmedia.dto.command;

import com.festago.socialmedia.domain.OwnerType;
import com.festago.socialmedia.domain.SocialMedia;
import com.festago.socialmedia.domain.SocialMediaType;

public record SocialMediaCreateCommand(
Long ownerId,
OwnerType ownerType,
SocialMediaType socialMediaType,
String name,
String logoUrl,
String url
) {

public SocialMedia toEntity() {
return new SocialMedia(
ownerId,
ownerType,
socialMediaType,
name,
logoUrl,
url
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.festago.socialmedia.dto.command;

public record SocialMediaUpdateCommand(
String name,
String url,
String logoUrl
) {

}
Loading

0 comments on commit e4b753a

Please sign in to comment.