Skip to content

Commit

Permalink
Merge pull request #122 from dnd-side-project/feature/#118
Browse files Browse the repository at this point in the history
Add Word List Api For Admin
  • Loading branch information
miraexhoi committed Sep 11, 2024
2 parents 1cd0d1f + c2d3164 commit 4a6ae5d
Show file tree
Hide file tree
Showing 11 changed files with 309 additions and 0 deletions.
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()
);
}
}
}

0 comments on commit 4a6ae5d

Please sign in to comment.