Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Word List Api For Admin #122

Merged
merged 14 commits into from
Sep 11, 2024
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package com.dnd.spaced.domain.admin.application;

import com.dnd.spaced.domain.admin.application.dto.request.AdminWordConditionInfoDto;
import com.dnd.spaced.domain.admin.application.dto.request.AdminWordRequestDto;
import com.dnd.spaced.domain.admin.application.dto.response.ReportInfoDto;
import com.dnd.spaced.domain.admin.application.exception.ReportNotFoundException;
import com.dnd.spaced.domain.admin.domain.repository.AdminRepository;
import com.dnd.spaced.domain.admin.domain.repository.AdminRepositoryMapper;
import com.dnd.spaced.domain.admin.domain.repository.dto.request.AdminWordConditionDto;
import com.dnd.spaced.domain.admin.presentation.dto.response.AdminWordResponse;
import com.dnd.spaced.domain.comment.application.exception.CommentNotFoundException;
import com.dnd.spaced.domain.comment.domain.Comment;
Expand All @@ -28,6 +32,18 @@ public class AdminService {
private final WordRepository wordRepository;
private final ReportRepository reportRepository;
private final CommentRepository commentRepository;
private final AdminRepository adminRepository;

public List<AdminWordResponse> findAllBy(AdminWordConditionInfoDto dto) {
AdminWordConditionDto adminWordConditionDto = AdminRepositoryMapper.to(
dto.categoryName(),
dto.lastWordName(),
dto.pageable()
);
List<Word> result = adminRepository.findAllBy(adminWordConditionDto);

return AdminServiceMapper.toAdminWordResponseList(result);
}

@Transactional
public Long createWord(AdminWordRequestDto wordRequestDto) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.dnd.spaced.domain.admin.application;

import com.dnd.spaced.domain.admin.application.dto.request.AdminWordConditionInfoDto;
import com.dnd.spaced.domain.admin.application.dto.request.AdminWordRequestDto;
import com.dnd.spaced.domain.admin.application.dto.response.ReportInfoDto;
import com.dnd.spaced.domain.admin.presentation.dto.response.AdminWordResponse;
Expand All @@ -8,10 +9,26 @@
import com.dnd.spaced.domain.word.domain.Word;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.data.domain.Pageable;
import java.util.List;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class AdminServiceMapper {

public static AdminWordConditionInfoDto to(
String categoryName,
String lastWordName,
Pageable pageable
) {
return new AdminWordConditionInfoDto(categoryName, lastWordName, pageable);
}

public static List<AdminWordResponse> toAdminWordResponseList(List<Word> words) {
return words.stream()
.map(AdminServiceMapper::toResponseDto)
.toList();
}

public static Word fromCreateRequest(AdminWordRequestDto dto) {
return Word.builder()
.name(dto.name())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.dnd.spaced.domain.admin.application.dto.request;

import org.springframework.data.domain.Pageable;

public record AdminWordConditionInfoDto(
String categoryName,
String lastWordName,
Pageable pageable)
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.dnd.spaced.domain.admin.application.dto.response;

import com.dnd.spaced.domain.word.domain.Word;

import java.time.LocalDateTime;

public record AdminListWordInfoDto(
Long id,
String name,
String meaning,
String category,
int commentCount,
int viewCount,
String example,
LocalDateTime createdAt,
LocalDateTime updatedAt
) {

public static AdminListWordInfoDto from(Word word) {
return new AdminListWordInfoDto(
word.getId(),
word.getName(),
word.getMeaning(),
word.getCategory().getName(),
word.getCommentCount(),
word.getViewCount(),
word.getExample().toString(),
word.getCreatedAt(),
word.getUpdatedAt()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.dnd.spaced.domain.admin.domain.repository;

import com.dnd.spaced.domain.admin.domain.repository.dto.request.AdminWordConditionDto;
import com.dnd.spaced.domain.word.domain.Word;

import java.util.List;

public interface AdminRepository {
List<Word> findAllBy(AdminWordConditionDto adminWordConditionDto);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.dnd.spaced.domain.admin.domain.repository;

import com.dnd.spaced.domain.admin.domain.repository.dto.request.AdminWordConditionDto;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.data.domain.Pageable;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class AdminRepositoryMapper {

public static AdminWordConditionDto to(String categoryName, String lastWordName, Pageable pageable) {
return new AdminWordConditionDto(categoryName, lastWordName, pageable);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.dnd.spaced.domain.admin.domain.repository;

import com.dnd.spaced.domain.admin.domain.repository.dto.request.AdminWordConditionDto;
import com.dnd.spaced.domain.word.domain.Category;
import com.dnd.spaced.domain.word.domain.Word;
import com.dnd.spaced.domain.word.domain.exception.InvalidCategoryException;
import com.dnd.spaced.domain.word.domain.repository.exception.UnsupportedWordSortConditionException;
import com.dnd.spaced.global.repository.OrderByNull;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Repository;

import java.util.List;

import static com.dnd.spaced.domain.word.domain.QWord.word;

@Repository
@RequiredArgsConstructor
public class QuerydslAdminRepository implements AdminRepository{

private static final String SORT_CONDITION = "name";
private static final String IGNORE_CATEGORY = "전체";

private final JPAQueryFactory queryFactory;

@Override
public List<Word> findAllBy(AdminWordConditionDto adminWordConditionDto) {
Sort.Order order = findOrder(adminWordConditionDto.pageable());

return queryFactory.selectFrom(word)
.where(
categoryEq(adminWordConditionDto.categoryName()),
lastWordNameLt(adminWordConditionDto.lastWordName())
)
.orderBy(orderByName(order))
.limit(adminWordConditionDto.pageable().getPageSize())
.fetch();
}

private BooleanExpression lastWordNameLt(String lastWordName) {
if (lastWordName == null) {
return null;
}

return word.name.gt(lastWordName);
}

@SuppressWarnings("unchecked")
private OrderSpecifier<String> orderByName(Sort.Order order) {
if (order == null) {
return OrderByNull.DEFAULT;
}

validateSortCondition(order);

return calculateOrderSpecifier(order);
}

private void validateSortCondition(Sort.Order order) {
String sortCondition = order.getProperty();

if (!SORT_CONDITION.equals(sortCondition)) {
throw new UnsupportedWordSortConditionException();
}
}

private OrderSpecifier<String> calculateOrderSpecifier(Sort.Order order) {
String sortOrder = order.getDirection().toString();

if (sortOrder == null || Sort.Direction.ASC.name().equalsIgnoreCase(sortOrder)) {
return word.name.asc();
}

return word.name.desc();
}

private BooleanExpression categoryEq(String category) {
if (category == null || IGNORE_CATEGORY.equals(category)) {
return null;
}

try {
Category categoryEnum = Category.findBy(category);

return word.category.eq(categoryEnum);
} catch (InvalidCategoryException e) {
return null;
}
}

private Sort.Order findOrder(Pageable pageable) {
return pageable.getSort()
.get()
.findAny()
.orElse(null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.dnd.spaced.domain.admin.domain.repository.dto.request;

import org.springframework.data.domain.Pageable;

public record AdminWordConditionDto(
String categoryName,
String lastWordName,
Pageable pageable)
{
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package com.dnd.spaced.domain.admin.presentation;

import com.dnd.spaced.domain.admin.application.AdminService;
import com.dnd.spaced.domain.admin.application.AdminServiceMapper;
import com.dnd.spaced.domain.admin.application.dto.request.AdminWordRequestDto;
import com.dnd.spaced.domain.admin.application.dto.response.ReportInfoDto;
import com.dnd.spaced.domain.admin.presentation.dto.AdminControllerMapper;
import com.dnd.spaced.domain.admin.presentation.dto.request.AdminMultipleWordConditionRequest;
import com.dnd.spaced.domain.admin.presentation.dto.response.AdminWordResponse;
import com.dnd.spaced.domain.admin.presentation.dto.response.ReportListResponse;
import com.dnd.spaced.global.resolver.word.WordSortCondition;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
Expand All @@ -23,6 +27,22 @@ public class AdminController implements SwaggerAdminController {

private final AdminService adminService;

@GetMapping("/allwords")
public ResponseEntity<List<AdminWordResponse>> findAllBy(
AdminMultipleWordConditionRequest request,
@WordSortCondition Pageable pageable
) {
List<AdminWordResponse> result = adminService.findAllBy(
AdminServiceMapper.to(
request.category(),
request.lastWordName(),
pageable
)
);

return ResponseEntity.ok(result);
}

@PostMapping("/words")
public ResponseEntity<Void> createWord(Authentication authentication,
@Valid @RequestBody AdminWordRequestDto wordRequestDto) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.dnd.spaced.domain.admin.presentation.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;

public record AdminMultipleWordConditionRequest(

@Schema(
description = "μš©μ–΄ μΉ΄ν…Œκ³ λ¦¬",
allowableValues = {"전체", "λ””μžμΈ", "개발", "λΉ„μ¦ˆλ‹ˆμŠ€"},
requiredMode = Schema.RequiredMode.NOT_REQUIRED
)
String category,

@Schema(description = "λ§ˆμ§€λ§‰μœΌλ‘œ μ‘°νšŒν•œ μš©μ–΄ 이름", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
String lastWordName
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.dnd.spaced.domain.admin.presentation.dto.response;

import com.dnd.spaced.domain.admin.application.dto.response.AdminListWordInfoDto;
import io.swagger.v3.oas.annotations.media.Schema;

import java.util.Collections;
import java.util.List;

public record AdminListWordInfoResponse(

@Schema(description = "μš©μ–΄ 정보")
List<AdminWordInfoResponse> words,

@Schema(description = "λ§ˆμ§€λ§‰μœΌλ‘œ μ‘°νšŒν•œ μš©μ–΄ 이름", nullable = true)
String lastWordName
) {

public static AdminListWordInfoResponse from(List<AdminListWordInfoDto> dtos) {
if (dtos == null || dtos.isEmpty()) {
return new AdminListWordInfoResponse(Collections.emptyList(), null);
}

List<AdminWordInfoResponse> words = dtos.stream()
.map(AdminWordInfoResponse::from)
.toList();

return new AdminListWordInfoResponse(words, words.get(words.size() - 1).name());
}

private record AdminWordInfoResponse(
@Schema(description = "μš©μ–΄ ID")
Long id,

@Schema(description = "μš©μ–΄ 이름")
String name,

@Schema(description = "μš©μ–΄ 뜻")
String meaning,

@Schema(description = "μš©μ–΄ μΉ΄ν…Œκ³ λ¦¬", allowableValues = {"개발", "λΉ„μ¦ˆλ‹ˆμŠ€", "λ””μžμΈ"})
String category,

@Schema(description = "λŒ“κΈ€ 개수")
int commentCount,

@Schema(description = "쑰회수")
int viewCount
) {

public static AdminWordInfoResponse from(AdminListWordInfoDto dto) {
return new AdminWordInfoResponse(
dto.id(),
dto.name(),
dto.meaning(),
dto.category(),
dto.commentCount(),
dto.viewCount()
);
}
}
}
Loading