diff --git a/src/main/java/com/dku/council/domain/like/model/LikeTarget.java b/src/main/java/com/dku/council/domain/like/model/LikeTarget.java index 04e19268..06010a96 100644 --- a/src/main/java/com/dku/council/domain/like/model/LikeTarget.java +++ b/src/main/java/com/dku/council/domain/like/model/LikeTarget.java @@ -2,5 +2,6 @@ public enum LikeTarget { POST, - COMMENT + COMMENT, + WITH_DANKOOK } diff --git a/src/main/java/com/dku/council/domain/with_dankook/controller/TradeController.java b/src/main/java/com/dku/council/domain/with_dankook/controller/TradeController.java index 45a8ec6c..c1f8f20d 100644 --- a/src/main/java/com/dku/council/domain/with_dankook/controller/TradeController.java +++ b/src/main/java/com/dku/council/domain/with_dankook/controller/TradeController.java @@ -1,9 +1,15 @@ package com.dku.council.domain.with_dankook.controller; +import com.dku.council.domain.like.model.LikeTarget; +import com.dku.council.domain.like.service.LikeService; import com.dku.council.domain.post.model.dto.response.ResponsePage; +import com.dku.council.domain.with_dankook.model.WithDankookStatus; import com.dku.council.domain.with_dankook.model.dto.list.SummarizedTradeDto; import com.dku.council.domain.with_dankook.model.dto.request.RequestCreateTradeDto; +import com.dku.council.domain.with_dankook.model.dto.response.ResponseSingleTradeDto; +import com.dku.council.domain.with_dankook.model.entity.type.Trade; import com.dku.council.domain.with_dankook.service.TradeService; +import com.dku.council.domain.with_dankook.service.WithDankookService; import com.dku.council.global.auth.jwt.AppAuthentication; import com.dku.council.global.auth.role.UserAuth; import com.dku.council.global.model.dto.ResponseIdDto; @@ -16,6 +22,7 @@ import org.springframework.web.bind.annotation.*; import javax.validation.Valid; +import javax.validation.constraints.NotNull; @Tag(name = "단국 거래 게시판", description = "단국 거래 게시판 API") @RestController @@ -24,6 +31,7 @@ public class TradeController { private final TradeService tradeService; + private final LikeService likeService; /** * 단국 거래 게시글 목록 조회 @@ -41,6 +49,19 @@ public ResponsePage list(@RequestParam(required = false) Str return new ResponsePage<>(list); } + /** + * 단국 거래 게시글 상세 조회 + * + * @param id 게시글 id + * @return 단국 거래 게시글 상세 정보 + */ + @GetMapping("/{id}") + @UserAuth + public ResponseSingleTradeDto findOne(AppAuthentication auth, + @PathVariable Long id) { + return tradeService.findOne(id, auth.getUserId(), auth.getUserRole()); + } + /** * 단국 거래 게시글 등록 */ @@ -55,12 +76,61 @@ public ResponseIdDto create(AppAuthentication auth, /** * 단국 거래 게시글 삭제 * - * @param auth 사용자 인증정보 - * @param id 삭제할 게시글 id + * @param id 게시글 id */ @DeleteMapping("/{id}") @UserAuth public void delete(AppAuthentication auth, @PathVariable Long id) { tradeService.delete(id, auth.getUserId(), auth.isAdmin()); } + + /** + * 단국 거래 게시글 판매 완료 처리 + * 유저가 처리하거나 관리자가 강제로 처리할 수 있습니다. + * + * @param id 게시글 id + */ + @PatchMapping("/{id}") + @UserAuth + public void close(AppAuthentication auth, @PathVariable Long id) { + tradeService.close(id, auth.getUserId()); + } + + /** + * 단국 거래 게시글 좋아요 표시 + * 중복으로 좋아요 처리해도 1개만 적용됩니다. + * + * @param id 게시글 id + */ + @PostMapping("/{id}/like") + @UserAuth + public void like(AppAuthentication auth, @PathVariable Long id) { + likeService.like(id, auth.getUserId(), LikeTarget.WITH_DANKOOK); + } + + /** + * 단국 거래 게시글 좋아요 취소 + * 중복으로 좋아요 취소해도 최초 1번만 적용됩니다. + * + * @param id 게시글 id + */ + @DeleteMapping("/{id}/like") + @UserAuth + public void cancelLike(AppAuthentication auth, @PathVariable Long id) { + likeService.cancelLike(id, auth.getUserId(), LikeTarget.WITH_DANKOOK); + } + + /** + * 내가 작성한 단국 거래 게시글 목록 조회 + * + * @param pageable 페이징 size, sort, page + * @return 페이징된 내가 쓴 단국 거래 게시판 목록 + */ + @GetMapping("/my") + @UserAuth + public ResponsePage listMyPosts(AppAuthentication auth, + @ParameterObject Pageable pageable) { + Page list = tradeService.listMyPosts(auth.getUserId(), pageable); + return new ResponsePage<>(list); + } } diff --git a/src/main/java/com/dku/council/domain/with_dankook/exception/ImageSizeExceededException.java b/src/main/java/com/dku/council/domain/with_dankook/exception/ImageSizeExceededException.java new file mode 100644 index 00000000..5f665ab8 --- /dev/null +++ b/src/main/java/com/dku/council/domain/with_dankook/exception/ImageSizeExceededException.java @@ -0,0 +1,11 @@ +package com.dku.council.domain.with_dankook.exception; + +import com.dku.council.global.error.exception.LocalizedMessageException; +import org.springframework.http.HttpStatus; + +public class ImageSizeExceededException extends LocalizedMessageException { + + public ImageSizeExceededException() { + super(HttpStatus.BAD_REQUEST, "invalid.imagesize"); + } +} diff --git a/src/main/java/com/dku/council/domain/with_dankook/exception/WithDankookNotFoundException.java b/src/main/java/com/dku/council/domain/with_dankook/exception/WithDankookNotFoundException.java new file mode 100644 index 00000000..584b842b --- /dev/null +++ b/src/main/java/com/dku/council/domain/with_dankook/exception/WithDankookNotFoundException.java @@ -0,0 +1,11 @@ +package com.dku.council.domain.with_dankook.exception; + +import com.dku.council.global.error.exception.LocalizedMessageException; +import org.springframework.http.HttpStatus; + +public class WithDankookNotFoundException extends LocalizedMessageException { + + public WithDankookNotFoundException() { + super(HttpStatus.NOT_FOUND, "notfound.withdankook"); + } +} diff --git a/src/main/java/com/dku/council/domain/with_dankook/model/dto/TradeImageDto.java b/src/main/java/com/dku/council/domain/with_dankook/model/dto/TradeImageDto.java index f911e42a..3ea0afd0 100644 --- a/src/main/java/com/dku/council/domain/with_dankook/model/dto/TradeImageDto.java +++ b/src/main/java/com/dku/council/domain/with_dankook/model/dto/TradeImageDto.java @@ -3,6 +3,7 @@ import com.dku.council.domain.with_dankook.model.entity.TradeImage; import com.dku.council.infra.nhn.s3.service.ObjectUploadContext; import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; import org.springframework.http.MediaType; import java.util.ArrayList; @@ -10,6 +11,7 @@ import java.util.Objects; import java.util.stream.Collectors; +@Getter public class TradeImageDto { @Schema(description = "이미지 아이디", example = "1") diff --git a/src/main/java/com/dku/council/domain/with_dankook/model/dto/list/SummarizedTradeDto.java b/src/main/java/com/dku/council/domain/with_dankook/model/dto/list/SummarizedTradeDto.java index cf09a345..2a53b257 100644 --- a/src/main/java/com/dku/council/domain/with_dankook/model/dto/list/SummarizedTradeDto.java +++ b/src/main/java/com/dku/council/domain/with_dankook/model/dto/list/SummarizedTradeDto.java @@ -1,10 +1,13 @@ package com.dku.council.domain.with_dankook.model.dto.list; +import com.dku.council.domain.with_dankook.model.WithDankookStatus; import com.dku.council.domain.with_dankook.model.dto.TradeImageDto; import com.dku.council.domain.with_dankook.model.entity.type.Trade; import com.dku.council.infra.nhn.s3.service.ObjectUploadContext; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; import java.util.List; @@ -26,12 +29,16 @@ public class SummarizedTradeDto extends SummarizedWithDankookDto{ @Schema(description = "이미지 목록") private final List images; - public SummarizedTradeDto(SummarizedWithDankookDto dto, Trade trade, ObjectUploadContext context){ + @Schema(description = "거래 상태", example = "거래중") + private final String status; + + public SummarizedTradeDto(SummarizedWithDankookDto dto, Trade trade, ObjectUploadContext context, MessageSource messageSource){ super(dto); this.title = trade.getTitle(); this.price = trade.getPrice(); this.content = trade.getContent(); this.tradePlace = trade.getTradePlace(); this.images = TradeImageDto.listOf(context, trade.getImages()); + this.status = messageSource.getMessage("withdankook.trade." + trade.getWithDankookStatus().name().toLowerCase(), null, LocaleContextHolder.getLocale()); } } diff --git a/src/main/java/com/dku/council/domain/with_dankook/model/dto/list/SummarizedWithDankookDto.java b/src/main/java/com/dku/council/domain/with_dankook/model/dto/list/SummarizedWithDankookDto.java index 0cea4ab5..2a308f7e 100644 --- a/src/main/java/com/dku/council/domain/with_dankook/model/dto/list/SummarizedWithDankookDto.java +++ b/src/main/java/com/dku/council/domain/with_dankook/model/dto/list/SummarizedWithDankookDto.java @@ -21,11 +21,15 @@ public class SummarizedWithDankookDto { @Schema(description = "채팅 링크", example = "https://open.kakao.com/o/ghjgjgjg") private final String chatLink; + @Schema(description = "게시글 타입", example = "TRADE") + private final String type; + public SummarizedWithDankookDto(int bodySize, WithDankook withDankook) { this.id = withDankook.getId(); this.author = withDankook.getDisplayingUsername(); this.createdAt = withDankook.getCreatedAt(); this.chatLink = withDankook.getChatLink(); + this.type = withDankook.getClass().getSimpleName().toUpperCase(); } public SummarizedWithDankookDto(SummarizedWithDankookDto copy) { @@ -33,5 +37,6 @@ public SummarizedWithDankookDto(SummarizedWithDankookDto copy) { this.author = copy.getAuthor(); this.createdAt = copy.getCreatedAt(); this.chatLink = copy.getChatLink(); + this.type = copy.getType(); } } diff --git a/src/main/java/com/dku/council/domain/with_dankook/model/dto/response/ResponseSingleTradeDto.java b/src/main/java/com/dku/council/domain/with_dankook/model/dto/response/ResponseSingleTradeDto.java new file mode 100644 index 00000000..3b2622a5 --- /dev/null +++ b/src/main/java/com/dku/council/domain/with_dankook/model/dto/response/ResponseSingleTradeDto.java @@ -0,0 +1,43 @@ +package com.dku.council.domain.with_dankook.model.dto.response; + +import com.dku.council.domain.with_dankook.model.dto.TradeImageDto; +import com.dku.council.domain.with_dankook.model.entity.type.Trade; +import com.dku.council.infra.nhn.s3.service.ObjectUploadContext; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; + +import java.util.List; + +@Getter +public class ResponseSingleTradeDto extends ResponseSingleWithDankookDto { + + @Schema(description = "제목", example = "물물교환") + private final String title; + + @Schema(description = "가격", example = "10000") + private final int price; + + @Schema(description = "내용", example = "게시글 본문") + private final String content; + + @Schema(description = "거래 장소", example = "단국대학교 정문") + private final String tradePlace; + + @Schema(description = "이미지 목록") + private final List images; + + @Schema(description = "거래 상태", example = "거래중") + private final String status; + + public ResponseSingleTradeDto(ResponseSingleWithDankookDto dto, Trade trade, ObjectUploadContext context, MessageSource messageSource) { + super(dto); + this.title = trade.getTitle(); + this.price = trade.getPrice(); + this.content = trade.getContent(); + this.tradePlace = trade.getTradePlace(); + this.images = TradeImageDto.listOf(context, trade.getImages()); + this.status = messageSource.getMessage("withdankook.trade." + trade.getWithDankookStatus().name().toLowerCase(), null, LocaleContextHolder.getLocale()); + } +} diff --git a/src/main/java/com/dku/council/domain/with_dankook/model/dto/response/ResponseSingleWithDankookDto.java b/src/main/java/com/dku/council/domain/with_dankook/model/dto/response/ResponseSingleWithDankookDto.java new file mode 100644 index 00000000..4c4017f5 --- /dev/null +++ b/src/main/java/com/dku/council/domain/with_dankook/model/dto/response/ResponseSingleWithDankookDto.java @@ -0,0 +1,57 @@ +package com.dku.council.domain.with_dankook.model.dto.response; + +import com.dku.council.domain.with_dankook.model.entity.WithDankook; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; + +import java.time.LocalDateTime; + +@Getter +public class ResponseSingleWithDankookDto { + + @Schema(description = "게시글 아이디", example = "1") + private final Long id; + + @Schema(description = "작성자", example = "익명") + private final String author; + + @Schema(description = "생성 날짜") + private final LocalDateTime createdAt; + + @Schema(description = "채팅 링크", example = "https://open.kakao.com/o/ghjgjgjg") + private final String chatLink; + + @Schema(description = "좋아요 수", example = "26") + private final int likes; + + @Schema(description = "내가 쓴 게시물인지?", example = "true") + private final boolean isMine; + + @Schema(description = "내가 좋아요를 눌렀는지?", example = "false") + private final boolean isLiked; + + @Schema(description = "닫힘 여부", example = "false") + private final boolean isClosed; + + public ResponseSingleWithDankookDto(int likes, boolean isMine, boolean isLiked, WithDankook withDankook) { + this.id = withDankook.getId(); + this.author = withDankook.getDisplayingUsername(); + this.createdAt = withDankook.getCreatedAt(); + this.chatLink = withDankook.getChatLink(); + this.likes = likes; + this.isMine = isMine; + this.isLiked = isLiked; + this.isClosed = withDankook.isClosed(); + } + + public ResponseSingleWithDankookDto(ResponseSingleWithDankookDto copy) { + this.id = copy.id; + this.author = copy.author; + this.createdAt = copy.createdAt; + this.chatLink = copy.chatLink; + this.likes = copy.likes; + this.isMine = copy.isMine; + this.isLiked = copy.isLiked; + this.isClosed = copy.isClosed; + } +} diff --git a/src/main/java/com/dku/council/domain/with_dankook/model/entity/WithDankook.java b/src/main/java/com/dku/council/domain/with_dankook/model/entity/WithDankook.java index 1daa930e..9680f452 100644 --- a/src/main/java/com/dku/council/domain/with_dankook/model/entity/WithDankook.java +++ b/src/main/java/com/dku/council/domain/with_dankook/model/entity/WithDankook.java @@ -58,4 +58,12 @@ protected WithDankook(User user, String chatLink) { public void markAsDeleted(boolean byAdmin) { this.withDankookStatus = byAdmin ? WithDankookStatus.DELETED_BY_ADMIN : WithDankookStatus.DELETED; } + + public boolean isClosed() { + return withDankookStatus == WithDankookStatus.CLOSED; + } + + public void close() { + this.withDankookStatus = WithDankookStatus.CLOSED; + } } diff --git a/src/main/java/com/dku/council/domain/with_dankook/repository/WithDankookRepository.java b/src/main/java/com/dku/council/domain/with_dankook/repository/WithDankookRepository.java index 2e7dc7f2..f802460c 100644 --- a/src/main/java/com/dku/council/domain/with_dankook/repository/WithDankookRepository.java +++ b/src/main/java/com/dku/council/domain/with_dankook/repository/WithDankookRepository.java @@ -1,6 +1,9 @@ package com.dku.council.domain.with_dankook.repository; +import com.dku.council.domain.with_dankook.model.dto.list.SummarizedTradeDto; import com.dku.council.domain.with_dankook.model.entity.WithDankook; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Query; @@ -16,4 +19,14 @@ public interface WithDankookRepository extends JpaReposit "join fetch u.major " + "where w.id=:id and w.withDankookStatus ='ACTIVE' ") Optional findById(@Param("id") Long id); + + @Query("select w from WithDankook w " + + "join fetch w.masterUser u " + + "join fetch u.major " + + "where w.id=:withDankookId and (w.withDankookStatus='CLOSED' or w.withDankookStatus='ACTIVE')") + Optional findWithClosedById(@Param("withDankookId") Long withDankookId); + + @Query("select w from WithDankook w " + + "where w.masterUser.id=:userId and (w.withDankookStatus='ACTIVE' or w.withDankookStatus='CLOSED')") + Page findAllByUserId(@Param("userId") Long userId, Pageable pageable); } diff --git a/src/main/java/com/dku/council/domain/with_dankook/service/TradeService.java b/src/main/java/com/dku/council/domain/with_dankook/service/TradeService.java index f0eab39c..511c6a08 100644 --- a/src/main/java/com/dku/council/domain/with_dankook/service/TradeService.java +++ b/src/main/java/com/dku/council/domain/with_dankook/service/TradeService.java @@ -1,16 +1,23 @@ package com.dku.council.domain.with_dankook.service; +import com.dku.council.domain.like.service.LikeService; import com.dku.council.domain.post.service.ThumbnailService; import com.dku.council.domain.user.model.entity.User; import com.dku.council.domain.user.repository.UserRepository; +import com.dku.council.domain.with_dankook.exception.ImageSizeExceededException; import com.dku.council.domain.with_dankook.exception.TradeCooltimeException; +import com.dku.council.domain.with_dankook.exception.WithDankookNotFoundException; +import com.dku.council.domain.with_dankook.model.WithDankookStatus; import com.dku.council.domain.with_dankook.model.dto.list.SummarizedTradeDto; import com.dku.council.domain.with_dankook.model.dto.request.RequestCreateTradeDto; +import com.dku.council.domain.with_dankook.model.dto.response.ResponseSingleTradeDto; import com.dku.council.domain.with_dankook.model.entity.TradeImage; import com.dku.council.domain.with_dankook.model.entity.type.Trade; import com.dku.council.domain.with_dankook.repository.TradeRepository; import com.dku.council.domain.with_dankook.repository.WithDankookMemoryRepository; import com.dku.council.domain.with_dankook.repository.spec.WithDankookSpec; +import com.dku.council.global.auth.role.UserRole; +import com.dku.council.global.error.exception.NotGrantedException; import com.dku.council.global.error.exception.UserNotFoundException; import com.dku.council.infra.nhn.s3.model.ImageRequest; import com.dku.council.infra.nhn.s3.model.UploadedImage; @@ -19,6 +26,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.MessageSource; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; @@ -31,6 +39,7 @@ import java.time.Instant; import java.util.ArrayList; import java.util.List; +import java.util.Optional; @Service @RequiredArgsConstructor @@ -45,9 +54,11 @@ public class TradeService { private final UserRepository userRepository; private final WithDankookService withDankookService; + private final LikeService likeService; private final ImageUploadService imageUploadService; private final ThumbnailService thumbnailService; private final ObjectUploadContext objectUploadContext; + private final MessageSource messageSource; private final Clock clock; @@ -80,6 +91,9 @@ public Long create(Long userId, RequestCreateTradeDto dto) { } private void attachImages(Trade trade, List dtoImages) { + if (dtoImages.size() > 10) { + throw new ImageSizeExceededException(); + } List images = imageUploadService.newContext().uploadImages( ImageRequest.ofList(dtoImages), trade.getClass().getSimpleName()); @@ -111,11 +125,44 @@ public Page list(String keyword, Pageable pageable, int body spec = spec.and(WithDankookSpec.withActive()); Page result = tradeRepository.findAll(spec, pageable); return result.map((trade) -> - new SummarizedTradeDto(withDankookService.makeListDto(bodySize, trade), trade, objectUploadContext)); + new SummarizedTradeDto(withDankookService.makeListDto(bodySize, trade), trade, objectUploadContext, messageSource)); + } + + @Transactional(readOnly = true) + public ResponseSingleTradeDto findOne(Long tradeId, Long userId, UserRole role) { + Trade trade = findTrade(tradeRepository, tradeId, role); + return new ResponseSingleTradeDto(withDankookService.makeSingleDto(userId, trade), trade, objectUploadContext, messageSource); + } + + private Trade findTrade(TradeRepository tradeRepository, Long tradeId, UserRole role) { + Optional trade; + if (role.isAdmin()) { + trade = tradeRepository.findWithClosedById(tradeId); + } else { + trade = tradeRepository.findById(tradeId); + } + return trade.orElseThrow(WithDankookNotFoundException::new); } @Transactional public void delete(Long tradeId, Long userId, boolean isAdmin) { withDankookService.delete(tradeRepository, tradeId, userId, isAdmin); } + + @Transactional + public void close(Long tradeId, Long userId) { + tradeRepository.findById(tradeId).ifPresent(trade -> { + if (trade.getMasterUser().getId().equals(userId)) { + trade.close(); + } else{ + throw new NotGrantedException(); + } + }); + } + + @Transactional(readOnly = true) + public Page listMyPosts(Long userId, Pageable pageable) { + return tradeRepository.findAllByUserId(userId, pageable) + .map(trade -> new SummarizedTradeDto(withDankookService.makeListDto(50, trade), trade, objectUploadContext, messageSource)); + } } diff --git a/src/main/java/com/dku/council/domain/with_dankook/service/WithDankookService.java b/src/main/java/com/dku/council/domain/with_dankook/service/WithDankookService.java index b599eafd..356ed3a9 100644 --- a/src/main/java/com/dku/council/domain/with_dankook/service/WithDankookService.java +++ b/src/main/java/com/dku/council/domain/with_dankook/service/WithDankookService.java @@ -1,25 +1,35 @@ package com.dku.council.domain.with_dankook.service; +import com.dku.council.domain.like.model.LikeTarget; +import com.dku.council.domain.like.service.LikeService; import com.dku.council.domain.post.exception.PostNotFoundException; import com.dku.council.domain.user.repository.UserRepository; +import com.dku.council.domain.with_dankook.exception.WithDankookNotFoundException; import com.dku.council.domain.with_dankook.model.dto.list.SummarizedWithDankookDto; +import com.dku.council.domain.with_dankook.model.dto.response.ResponseSingleWithDankookDto; import com.dku.council.domain.with_dankook.model.entity.WithDankook; import com.dku.council.domain.with_dankook.repository.WithDankookRepository; import com.dku.council.domain.with_dankook.repository.spec.WithDankookSpec; +import com.dku.council.global.auth.role.UserRole; import com.dku.council.global.error.exception.NotGrantedException; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; +import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.Optional; + @Service @RequiredArgsConstructor public class WithDankookService { protected final UserRepository userRepository; + protected final LikeService likeService; + /** * With-Dankook 게시판 글 목록 조회 */ @@ -54,6 +64,66 @@ public SummarizedWithDankookDto makeListDto(int bodySize, E withDankook) { return new SummarizedWithDankookDto(bodySize, withDankook); } + /** + * With-Dankook 게시판 글 단건 조회 + * + * @param repository 조회할 게시글 repository + * @param withDankookId 조회할 게시글 id + * @param userId 요청한 사용자 id + * @param role 요청한 사용자의 권한 + * @return 조회한 게시글 + */ + @Transactional + public ResponseSingleWithDankookDto findOne(WithDankookRepository repository, Long withDankookId, @Nullable Long userId, + UserRole role) { + E withDankook = findWithDankook(repository, withDankookId, role); + return makeSingleDto(userId, withDankook); + } + + @Transactional + public T findOne(WithDankookRepository repository, Long withDankookId, Long userId, UserRole role, + PostResultMapper mapper) { + E withDankook = findWithDankook(repository, withDankookId, role); + ResponseSingleWithDankookDto dto = makeSingleDto(userId, withDankook); + + try { + return mapper.map(dto, withDankook); + } catch (Exception e) { + throw new WithDankookNotFoundException(); + } + } + + public ResponseSingleWithDankookDto makeSingleDto(Long userId, E withDankook) { + int likes = likeService.getCountOfLikes(withDankook.getId(), LikeTarget.WITH_DANKOOK); + boolean isMine = false; + boolean isLiked = false; + + if (userId != null) { + isMine = withDankook.getMasterUser().getId().equals(userId); + isLiked = likeService.isLiked(withDankook.getId(), userId, LikeTarget.WITH_DANKOOK); + } + + return new ResponseSingleWithDankookDto(likes, isMine, isLiked, withDankook); + } + + /** + * With-Dankook 게시판 글을 조회합니다. + * + * @param repository 조회할 게시글 repository + * @param withDankookId 조회할 게시글 id + * @param role 요청한 사용자의 권한 + * @return 조회한 게시글 + */ + private E findWithDankook(WithDankookRepository repository, Long withDankookId, UserRole role) { + Optional withDankook; + if (role.isAdmin()) { + withDankook = repository.findWithClosedById(withDankookId); + } else { + withDankook = repository.findById(withDankookId); + } + return withDankook.orElseThrow(WithDankookNotFoundException::new); + } + /** * With-Dankook 게시판 글 삭제. 실제 DB에서 삭제는 하지 않는다. * diff --git a/src/main/resources/errors.properties b/src/main/resources/errors.properties index abccacac..0c638349 100644 --- a/src/main/resources/errors.properties +++ b/src/main/resources/errors.properties @@ -45,6 +45,7 @@ invalid.full-seats=\uC790\uB9AC\uAC00 \uAF49\uCC28\uC11C \uB354 \uC774\uC0C1 \uC invalid.only-jukjeon=\uC8FD\uC804 \uCEA0\uD37C\uC2A4 \uC18C\uC18D\uB9CC \uC2E0\uCCAD\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4. invalid.ticket-approval = \uC2B9\uC778\uD560 \uC218 \uC5C6\uB294 \uD2F0\uCF13\uC785\uB2C8\uB2E4. invalid.dku-auth.refresh=\uB2E8\uAD6D\uB300 \uC778\uC99D\uC774 \uAC31\uC2E0\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. +invalid.imagesize=\uC774\uBBF8\uC9C0 \uAC1C\uC218\uAC00 \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4. notfound.user=\uC720\uC800\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. notfound.post=\uD574\uB2F9 \uAC8C\uC2DC\uAE00\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. @@ -61,6 +62,7 @@ notfound.ticket=\uD574\uB2F9 \uD2F0\uCF13\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5 notfound.ticket-event=\uD574\uB2F9 \uD2F0\uCF13 \uC774\uBCA4\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. notfound.homebus=\uD574\uB2F9 \uADC0\uD5A5\uBC84\uC2A4\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. notfound.homebus-ticket=\uD574\uB2F9 \uBC84\uC2A4 \uD2F0\uCF13\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. +notfound.withdankook=\uD574\uB2F9 WithDankook \uAC8C\uC2DC\uAE00\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. notsupport.http-method=\uC798\uBABB\uB41C Method \uC694\uCCAD\uC785\uB2C8\uB2E4. diff --git a/src/main/resources/errors_en_US.properties b/src/main/resources/errors_en_US.properties index eba65ae5..3f20a116 100644 --- a/src/main/resources/errors_en_US.properties +++ b/src/main/resources/errors_en_US.properties @@ -45,6 +45,7 @@ invalid.full-seats=The seats are full. ({0} seats) invalid.only-jukjeon=Only those belonging to Jukjeon Campus can apply. invalid.ticket-approval = Invalid ticket approval. invalid.dku-auth.refresh=Dankook University Certification has not been renewed. +invalid.imagesize=The number of images has exceeded the limit. notfound.user=Cannot find that user. notfound.post=No such post was found. @@ -61,6 +62,8 @@ notfound.ticket=No such ticket was found. notfound.ticket-event=No such ticket event was found. notfound.homebus=No such homecoming bus was found. notfound.homebus-ticket=No such homecoming bus ticket was found. +notfound.withdankook=No such withDankook post was found. + notsupport.http-method=Not supported HTTP method. diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 79529f49..879ae31a 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -16,3 +16,6 @@ report.category.pornography=\uC74C\uB780\uBB3C/\uBD88\uAC74\uC804\uD55C \uB9CC\u report.category.inappropriate_content=\uAC8C\uC2DC\uD310 \uC131\uACA9\uC5D0 \uBD80\uC801\uD569 report.category.fraud=\uC720\uCD9C/\uC0AC\uCE6D/\uC0AC\uAE30 +withdankook.trade.active=\uD310\uB9E4\uC911 +withdankook.trade.closed=\uAC70\uB798\uC644\uB8CC + diff --git a/src/main/resources/messages_en_US.properties b/src/main/resources/messages_en_US.properties index 9da38e3c..e7fc1884 100644 --- a/src/main/resources/messages_en_US.properties +++ b/src/main/resources/messages_en_US.properties @@ -14,4 +14,7 @@ report.category.advertisement=advertisement report.category.politics=defamation of political figures report.category.pornography=obscenity/pornography report.category.inappropriate_content=inappropriate content -report.category.fraud=leakage/impersonation/fraud \ No newline at end of file +report.category.fraud=leakage/impersonation/fraud + +withdankook.trade.active=Selling +withdankook.trade.closed=Closed \ No newline at end of file diff --git a/src/test/java/com/dku/council/domain/with_dankook/service/TradeServiceTest.java b/src/test/java/com/dku/council/domain/with_dankook/service/TradeServiceTest.java new file mode 100644 index 00000000..5172387f --- /dev/null +++ b/src/test/java/com/dku/council/domain/with_dankook/service/TradeServiceTest.java @@ -0,0 +1,73 @@ +package com.dku.council.domain.with_dankook.service; + +import com.dku.council.domain.like.service.LikeService; +import com.dku.council.domain.post.repository.PostTimeMemoryRepository; +import com.dku.council.domain.post.service.ThumbnailService; +import com.dku.council.domain.user.repository.UserRepository; +import com.dku.council.domain.with_dankook.model.entity.type.Trade; +import com.dku.council.domain.with_dankook.repository.TradeRepository; +import com.dku.council.domain.with_dankook.repository.WithDankookMemoryRepository; +import com.dku.council.infra.nhn.s3.service.ImageUploadService; +import com.dku.council.infra.nhn.s3.service.ObjectUploadContext; +import com.dku.council.util.ClockUtil; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.Clock; +import java.time.Duration; + +import static org.junit.jupiter.api.Assertions.*; + +@ExtendWith(MockitoExtension.class) +class TradeServiceTest { + + private final Clock clock = ClockUtil.create(); + + private final Duration writeCooltime = Duration.ofDays(1); + + @Mock + private TradeRepository tradeRepository; + + @Mock + private WithDankookMemoryRepository withDankookMemoryRepository; + + @Mock + private UserRepository userRepository; + + @Mock + private WithDankookService withDankookService; + + @Mock + private LikeService likeService; + + @Mock + private ImageUploadService imageUploadService; + + @Mock + private ThumbnailService thumbnailService; + + @Mock + private ObjectUploadContext objectUploadContext; + + + + private TradeService tradeService; + +// @BeforeEach +// public void setup() { +// tradeService = new TradeService(tradeRepository, withDankookMemoryRepository, userRepository, +// withDankookService, likeService, imageUploadService, thumbnailService, objectUploadContext, +// clock, writeCooltime); +// } +// +// @Test +// @DisplayName("글 작성 - 쿨타임 이전에 작성한 적 없는 경우") +// public void create() { +// +// } + +} \ No newline at end of file