Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.function.Supplier;

public enum DraftErrorCode implements ErrorCode {

Copy link
Preview

Copilot AI Sep 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The empty line and comment placement creates inconsistent formatting. Remove the blank line after the enum declaration or move the comment to align with other comment groupings.

Suggested change

Copilot uses AI. Check for mistakes.

// user input
DRAFT_BLOG_ID_REQUIRED("블로그 ID를 반드시 제공해야 합니다.", HttpStatus.BAD_REQUEST),
DRAFT_TITLE_MIN_LENGTH("블로그 제목은 반드시 3글자 이상입니다.", HttpStatus.BAD_REQUEST),
Expand All @@ -19,7 +20,13 @@ public enum DraftErrorCode implements ErrorCode {
DRAFT_GONE("더 이상 존재하지 않는 게시물입니다.", HttpStatus.GONE),
DRAFT_FORBIDDEN("권한이 없습니다.", HttpStatus.FORBIDDEN),
DRAFT_ALREADY_EXIST("임시글이 이미 존재합니다.", HttpStatus.CONFLICT),
DEFAULT("임시글 조작 오류", HttpStatus.INTERNAL_SERVER_ERROR);
DEFAULT("임시글 조작 오류", HttpStatus.INTERNAL_SERVER_ERROR),
BLOG_MISMATCH("드래프트와 시리즈가 같은 블로그에 속하지 않습니다.", HttpStatus.BAD_REQUEST),

// series status
SERIES_ARTICLE_NOT_FOUND("시리즈 아티클을 찾을 수 없습니다.", HttpStatus.NOT_FOUND),
SERIES_NOT_FOUND("시리즈를 찾을 수 없습니다.", HttpStatus.NOT_FOUND),
SERIES_ALREADY_REGISTERED("이미 등록된 시리즈 아티클입니다.", HttpStatus.CONFLICT);

private final String message;
private final HttpStatus httpStatus;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,28 @@

import nettee.blolet.article.domain.Draft;
import nettee.blolet.article.domain.DraftImage;
import nettee.blolet.article.domain.SeriesArticle;
import nettee.blolet.article.domain.sub.DraftStatus;
import nettee.blolet.article.readmodel.DraftReadModels.DraftDetail;
import nettee.blolet.article.readmodel.SeriesQueryModels.SeriesDetail;

import java.util.Optional;

public interface DraftCommandPort {

Optional<DraftDetail> findById(String id);
Optional<DraftDetail> findDraftById(String id);
Optional<SeriesDetail> findSeriesById(String seriesId);

boolean existsSeriesArticle(String draftId, String seriesId);

Draft save(Draft draft);
DraftImage save(DraftImage draftImage);
SeriesArticle createSeriesArticle(String draftId, String seriesId, String articleId);

Draft update(Draft draft);

Draft updateTitle(String draftId, String title);
Draft updatePath(String draftId, String path);
SeriesArticle updateSeriesArticle(String draftId, String seriesId, String articleId);
Copy link
Preview

Copilot AI Sep 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updateSeriesArticle method is declared in the port interface but is not implemented or used in the service. This unused method should be removed to avoid confusion.

Suggested change
SeriesArticle updateSeriesArticle(String draftId, String seriesId, String articleId);

Copilot uses AI. Check for mistakes.

void updateDraftSeriesInfo(String draftId, String seriesId);
void updateStatus(String id, DraftStatus draftStatus);

DraftImage save(DraftImage draftImage);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,32 @@
import nettee.blolet.article.application.usecase.DraftCreateUseCase;
import nettee.blolet.article.application.usecase.DraftDeleteUseCase;
import nettee.blolet.article.application.usecase.DraftImageCreateUseCase;
import nettee.blolet.article.application.usecase.DraftPatchUseCase;
import nettee.blolet.article.application.usecase.DraftUpdateUseCase;
import nettee.blolet.article.domain.Draft;
import nettee.blolet.article.domain.DraftImage;
import nettee.blolet.article.domain.SeriesArticle;
import nettee.blolet.article.domain.sub.DraftStatus;
import nettee.blolet.blog.export.client.api.BlogClient;
import nettee.upload.port.ImageStorage;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import static nettee.blolet.article.exception.DraftErrorCode.SERIES_ALREADY_REGISTERED;
import static nettee.blolet.article.exception.DraftErrorCode.BLOG_MISMATCH;
import static nettee.blolet.article.exception.DraftErrorCode.DRAFT_FORBIDDEN;
import static nettee.blolet.article.exception.DraftErrorCode.DRAFT_NOT_FOUND;
import static nettee.blolet.article.exception.DraftErrorCode.SERIES_NOT_FOUND;

@Service
@RequiredArgsConstructor
public class DraftCommandService implements DraftCreateUseCase, DraftUpdateUseCase, DraftDeleteUseCase, DraftImageCreateUseCase {
public class DraftCommandService implements
DraftCreateUseCase,
DraftUpdateUseCase,
DraftDeleteUseCase,
DraftImageCreateUseCase,
DraftPatchUseCase {

private final DraftCommandPort draftCommandPort;
private final BlogClient blogClient;
private final ImageStorage imageStorage;
Expand All @@ -38,7 +49,7 @@ public Draft updateDraft(String userId, Draft draft) {

@Override
public void deleteDraft(String userId, String draftId) {
var blogId = draftCommandPort.findById(draftId)
var blogId = draftCommandPort.findDraftById(draftId)
.orElseThrow(DRAFT_NOT_FOUND::exception)
.blogId();
validateOwnership(userId, blogId);
Expand All @@ -48,7 +59,7 @@ public void deleteDraft(String userId, String draftId) {

@Override
public DraftImage createDraftImage(String userId, String draftId, MultipartFile file, String targetName) {
var blogId = draftCommandPort.findById(draftId)
var blogId = draftCommandPort.findDraftById(draftId)
.orElseThrow(DRAFT_NOT_FOUND::exception)
.blogId();
validateOwnership(userId, blogId);
Expand All @@ -64,6 +75,58 @@ public DraftImage createDraftImage(String userId, String draftId, MultipartFile
return draftCommandPort.save(draftImage);
}

@Override
public Draft patchTitle(String userId, String draftId, String title) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎨 이 순서로 배치하는 게 유지보수하기에 좋습니다!

계속 추가하다 보면 자주 실수할 수 있는 영역이네요.

class {
    fields

    public methods

    private methods
}

var draft = draftCommandPort.findDraftById(draftId)
.orElseThrow(DRAFT_NOT_FOUND::exception);

validateOwnership(userId, draft.blogId());

return draftCommandPort.updateTitle(draft.id(), title);
}

@Override
public Draft patchPath(String userId, String draftId, String path) {
var draft = draftCommandPort.findDraftById(draftId)
.orElseThrow(DRAFT_NOT_FOUND::exception);

validateOwnership(userId, draft.blogId());

return draftCommandPort.updatePath(draft.id(), path);
}

@Override
public SeriesArticle registerSeriesArticle(String userId, String draftId, String seriesId, String articleId) {
// 드래프트, 시리즈 조회
var draft = draftCommandPort.findDraftById(draftId)
.orElseThrow(DRAFT_NOT_FOUND::exception);

var series = draftCommandPort.findSeriesById(seriesId)
.orElseThrow(SERIES_NOT_FOUND::exception);

// 권한 검증
validateOwnership(userId, draft.blogId());
validateOwnership(userId, series.blogId());

// 블로그 동일 여부 확인
if (!draft.blogId().equals(series.blogId())) {
throw BLOG_MISMATCH.exception();
}

// 시리즈 존재 여부 확인
if (draftCommandPort.existsSeriesArticle(draftId, seriesId)) {
throw SERIES_ALREADY_REGISTERED.exception();
}

// 시리즈 등록
var seriesArticle = draftCommandPort.createSeriesArticle(draftId, seriesId, articleId);

// 드래프트에 시리즈 정보 업데이트
draftCommandPort.updateDraftSeriesInfo(draftId, seriesId);

return seriesArticle;
}

private void validateOwnership(String userId, String draftId) {
var isOwner = blogClient.verifyOwnership(userId, draftId)
.isOwner();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package nettee.blolet.article.application.usecase;

import nettee.blolet.article.domain.Draft;
import nettee.blolet.article.domain.SeriesArticle;

public interface DraftPatchUseCase {

Draft patchTitle(String userId, String draftId, String title);

Draft patchPath(String userId, String draftId, String path);

SeriesArticle registerSeriesArticle(String userId, String draftId, String seriesId, String articleId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class DraftCommandAdapter implements DraftCommandPort {
private final DraftEntityMapper mapper;

@Override
public Optional<DraftDetail> findById(String id) {
public Optional<DraftDetail> findDraftById(String id) {
var draft = draftJpaRepository.findById(id)
.orElseThrow(DRAFT_NOT_FOUND::exception);
return mapper.toOptionalDraftDetail(draft);
Expand Down