Skip to content

Commit

Permalink
Merge pull request #69 from Tune-Fun/feature/vote/paper_delete
Browse files Browse the repository at this point in the history
Feature/vote/paper delete
  • Loading branch information
habinkim authored May 3, 2024
2 parents 58e7569 + e1b63a7 commit 9db0b55
Show file tree
Hide file tree
Showing 17 changed files with 256 additions and 67 deletions.
6 changes: 6 additions & 0 deletions src/docs/asciidoc/vote-api.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ operation::update-delivery-date-success[snippets='http-request,http-response,pat

operation::update-video-url-success[snippets='http-request,http-response,path-parameters,request-fields,response-fields']

=== 투표 게시물 삭제 API

==== 성공

operation::delete-vote-paper-success[snippets='http-request,http-response,query-parameters,response-fields']

== Vote API

=== 투표 등록 API
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public enum MessageCode {
VOTE_PAPER_NOT_FOUND(BAD_REQUEST, "3203"),
VOTE_POLICY_ONE_VOTE_PER_USER(BAD_REQUEST, "3204"),
VOTE_POLICY_ONLY_AUTHOR_CAN_UPDATE_VIDEO_URL(BAD_REQUEST, "3205"),
VOTE_POLICY_ONLY_AUTHOR_CAN_DELETE(BAD_REQUEST, "3206"),

// 분산 락 관련
LOCK_ACQUISITION_FAILED_ERROR(INTERNAL_SERVER_ERROR, "4001"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@
import com.tune_fun.v1.common.response.Response;
import com.tune_fun.v1.common.response.ResponseMapper;
import com.tune_fun.v1.vote.application.port.input.command.VotePaperCommands;
import com.tune_fun.v1.vote.application.port.input.usecase.RegisterVotePaperUseCase;
import com.tune_fun.v1.vote.application.port.input.usecase.ScrollVotePaperUseCase;
import com.tune_fun.v1.vote.application.port.input.usecase.UpdateVotePaperDeliveryDateUseCase;
import com.tune_fun.v1.vote.application.port.input.usecase.UpdateVotePaperVideoUrlUseCase;
import com.tune_fun.v1.vote.application.port.input.usecase.*;
import com.tune_fun.v1.vote.domain.value.ScrollableVotePaper;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
Expand All @@ -31,10 +28,14 @@ public class VotePaperController {
private final ResponseMapper responseMapper;

private final ScrollVotePaperUseCase scrollVotePaperUseCase;

private final RegisterVotePaperUseCase registerVotePaperUseCase;

private final UpdateVotePaperDeliveryDateUseCase updateVotePaperDeliveryDateUseCase;
private final UpdateVotePaperVideoUrlUseCase updateVotePaperVideoUrlUseCase;

private final DeleteVotePaperUseCase deleteVotePaperUseCase;

@GetMapping(value = Uris.VOTE_PAPER_ROOT)
public ResponseEntity<Response<ScrollVotePaperResponse>> getVotePapers(@RequestParam(name = "last_id") Integer lastId,
@RequestParam(name = "sort_type", required = false, defaultValue = "RECENT") SortType sortType,
Expand Down Expand Up @@ -70,6 +71,14 @@ public ResponseEntity<Response<BasePayload>> updateVideoUrl(@PathVariable("voteP
return responseMapper.ok(MessageCode.SUCCESS);
}

@PreAuthorize("hasRole('ARTIST')")
@DeleteMapping(value = Uris.VOTE_PAPER_ROOT)
public ResponseEntity<Response<BasePayload>> deleteVotePaper(@RequestParam(name = "vote_paper_id") Long votePaperId,
@CurrentUser User user) {
deleteVotePaperUseCase.delete(votePaperId, user);
return responseMapper.ok(MessageCode.SUCCESS);
}

public enum SortType {
RECENT, VOTE_COUNT
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Singular;
import lombok.experimental.SuperBuilder;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;

import java.util.List;

@SuperBuilder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
Expand All @@ -32,7 +35,7 @@ public class VoteChoiceJpaEntity extends BaseEntity {
@Comment("고유번호")
private String uuid;

@ManyToOne(fetch = FetchType.LAZY)
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "vote_paper_id", nullable = false, updatable = false, referencedColumnName = "id")
@Comment("투표 게시물 ID")
private VotePaperJpaEntity votePaper;
Expand All @@ -42,4 +45,8 @@ public class VoteChoiceJpaEntity extends BaseEntity {
@Comment("선택지 제안사항")
private Offer offer;

@Singular
@OneToMany(mappedBy = "voteChoice", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<VoteJpaEntity> votes;

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public class VoteJpaEntity extends BaseEntity {
@Comment("고유번호")
private String uuid;

@ManyToOne(fetch = FetchType.LAZY)
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "vote_choice_id", nullable = false, updatable = false, referencedColumnName = "id")
@Comment("투표 선택지 ID")
private VoteChoiceJpaEntity voteChoice;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.*;
import lombok.experimental.SuperBuilder;
import org.hibernate.annotations.Comment;
import org.springframework.format.annotation.DateTimeFormat;

import java.time.LocalDateTime;
import java.util.List;

@SuperBuilder(toBuilder = true)
@NoArgsConstructor
Expand Down Expand Up @@ -67,8 +66,21 @@ public class VotePaperJpaEntity extends BaseEntity {
@Comment("커버 영상 등록일")
private LocalDateTime deliveryAt;

@Builder.Default
@Column(name = "enabled")
@Comment("사용 여부")
private boolean enabled = true;

@Column(name = "video_url")
@Comment("커버 영상 URL")
private String videoUrl;

@Singular
@OneToMany(mappedBy = "votePaper", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<VoteChoiceJpaEntity> choices;

public void disable() {
this.enabled = false;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@
import java.time.LocalDateTime;
import java.util.Optional;

public interface VotePaperRepository extends JpaRepository<VotePaperJpaEntity, String>, VotePaperCustomRepository {
public interface VotePaperRepository extends JpaRepository<VotePaperJpaEntity, Long>, VotePaperCustomRepository {

@EntityGraph(attributePaths = {"author"})
Optional<VotePaperJpaEntity> findByVoteEndAtAfterAndAuthorUsername(LocalDateTime voteEndAt, String author);
Optional<VotePaperJpaEntity> findByVoteEndAtAfterAndAuthorUsernameAndEnabledTrue(LocalDateTime voteEndAt, String author);

Optional<VotePaperJpaEntity> findByVoteEndAtAfterAndId(LocalDateTime voteEndAt, Long id);
Optional<VotePaperJpaEntity> findByVoteEndAtAfterAndIdAndEnabledTrue(LocalDateTime voteEndAt, Long id);

Optional<VotePaperJpaEntity> findByVoteEndAtBeforeAndId(LocalDateTime voteEndAt, Long id);
Optional<VotePaperJpaEntity> findByVoteEndAtBeforeAndIdAndEnabledTrue(LocalDateTime voteEndAt, Long id);

@EntityGraph(attributePaths = {"author"})
Window<VotePaperJpaEntity> findFirst10By(KeysetScrollPosition position, Sort sort);
Window<VotePaperJpaEntity> findFirst10ByEnabledTrue(KeysetScrollPosition position, Sort sort);

}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
@RequiredArgsConstructor
public class VotePersistenceAdapter implements
LoadVotePort, SaveVotePort,
LoadVotePaperPort, SaveVotePaperPort,
LoadVotePaperPort, SaveVotePaperPort, DeleteVotePaperPort,
UpdateDeliveryAtPort, UpdateVideoUrlPort,
LoadVoteChoicePort, SaveVoteChoicePort {

Expand Down Expand Up @@ -88,7 +88,7 @@ public void saveVote(Long voteChoiceId, String username) {
public Window<ScrollableVotePaper> scrollVotePaper(final Integer lastId, final String sortType) {
KeysetScrollPosition position = forward(Map.of("id", lastId, "voteEndAt", Constants.LOCAL_DATE_TIME_MIN));
Sort sort = by(desc("id"), desc("voteEndAt"));
return votePaperRepository.findFirst10By(position, sort).map(votePaperMapper::scrollableVotePaper);
return votePaperRepository.findFirst10ByEnabledTrue(position, sort).map(votePaperMapper::scrollableVotePaper);
}

@Override
Expand All @@ -112,6 +112,15 @@ public RegisteredVotePaper saveVotePaper(final SaveVotePaper saveVotePaper) {
return votePaperMapper.registeredVotePaper(savedVotePaper);
}

@Override
public void disableVotePaper(final Long votePaperId) {
votePaperRepository.findById(votePaperId)
.ifPresent(votePaper -> {
votePaper.disable();
votePaperRepository.save(votePaper);
});
}

@Override
public RegisteredVotePaper updateDeliveryAt(final Long votePaperId, final LocalDateTime deliveryAt) {
VotePaperJpaEntity votePaper = findOneAvailable(votePaperId, Constants.NULL_STRING)
Expand Down Expand Up @@ -156,15 +165,15 @@ public Optional<VotePaperJpaEntity> findOneAvailable(final Long votePaperId, fin
}

public Optional<VotePaperJpaEntity> findCompleteVotePaperById(final Long id) {
return votePaperRepository.findByVoteEndAtBeforeAndId(now(), id);
return votePaperRepository.findByVoteEndAtBeforeAndIdAndEnabledTrue(now(), id);
}

public Optional<VotePaperJpaEntity> findProgressingVotePaperByAuthor(final String username) {
return votePaperRepository.findByVoteEndAtAfterAndAuthorUsername(now(), username);
return votePaperRepository.findByVoteEndAtAfterAndAuthorUsernameAndEnabledTrue(now(), username);
}

public Optional<VotePaperJpaEntity> findProgressingVotePaperById(final Long id) {
return votePaperRepository.findByVoteEndAtAfterAndId(now(), id);
return votePaperRepository.findByVoteEndAtAfterAndIdAndEnabledTrue(now(), id);
}

public List<VoteChoiceJpaEntity> findAllByVotePaperId(final Long votePaperId) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.tune_fun.v1.vote.application.port.input.usecase;

import org.springframework.security.core.userdetails.User;

@FunctionalInterface
public interface DeleteVotePaperUseCase {
void delete(final Long votePaperId, final User user);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.tune_fun.v1.vote.application.port.output;

public interface DeleteVotePaperPort {
void disableVotePaper(final Long votePaperId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.tune_fun.v1.vote.application.service;

import com.tune_fun.v1.common.exception.CommonApplicationException;
import com.tune_fun.v1.common.hexagon.UseCase;
import com.tune_fun.v1.common.response.MessageCode;
import com.tune_fun.v1.vote.application.port.input.usecase.DeleteVotePaperUseCase;
import com.tune_fun.v1.vote.application.port.output.DeleteVotePaperPort;
import com.tune_fun.v1.vote.application.port.output.LoadVotePaperPort;
import com.tune_fun.v1.vote.domain.value.RegisteredVotePaper;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import static com.tune_fun.v1.common.response.MessageCode.VOTE_POLICY_ONLY_AUTHOR_CAN_DELETE;

@Service
@UseCase
@RequiredArgsConstructor
public class DeleteVotePaperService implements DeleteVotePaperUseCase {

private final LoadVotePaperPort loadVotePaperPort;
private final DeleteVotePaperPort deleteVotePaperPort;

@Transactional
@Override
public void delete(final Long votePaperId, final User user) {
RegisteredVotePaper registeredVotePaper = loadVotePaperPort.loadRegisteredVotePaper(votePaperId)
.orElseThrow(() -> new CommonApplicationException(MessageCode.VOTE_PAPER_NOT_FOUND));

if (!registeredVotePaper.isAuthor(user.getUsername()))
throw new CommonApplicationException(VOTE_POLICY_ONLY_AUTHOR_CAN_DELETE);

deleteVotePaperPort.disableVotePaper(votePaperId);
}
}
1 change: 1 addition & 0 deletions src/main/resources/messages/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,6 @@
3203=\uD22C\uD45C \uAC8C\uC2DC\uBB3C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
3204=\uC0AC\uC6A9\uC790\uB294 \uD55C \uD22C\uD45C \uAC8C\uC2DC\uBB3C\uC5D0 \uD55C\uBC88\uB9CC \uD22C\uD45C\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.
3205=\uD22C\uD45C \uAC8C\uC2DC\uBB3C\uC758 \uC791\uC131\uC790\uB9CC \uC601\uC0C1\uC744 \uC5C5\uB85C\uB4DC\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.
3206=\uD22C\uD45C \uAC8C\uC2DC\uBB3C\uC758 \uC791\uC131\uC790\uB9CC \uD22C\uD45C \uAC8C\uC2DC\uBB3C\uC744 \uC0AD\uC81C\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.
4001=\uB370\uC774\uD130\uB97C \uCC98\uB9AC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
4002=\uB370\uC774\uD130\uB97C \uCC98\uB9AC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
1 change: 1 addition & 0 deletions src/main/resources/messages/messages_en_US.properties
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,6 @@
3203=Vote paper not found.
3204=User can only register vote per one vote paper.
3205=Only the author of the Vote paper can set the video url.
3206=Only the author of the Vote paper can delete the vote paper.
4001=data could not be processed.
4002=Unable to process data.
1 change: 1 addition & 0 deletions src/main/resources/messages/messages_ko_KR.properties
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,6 @@
3203=\uD22C\uD45C \uAC8C\uC2DC\uBB3C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
3204=\uC0AC\uC6A9\uC790\uB294 \uD55C \uD22C\uD45C \uAC8C\uC2DC\uBB3C\uC5D0 \uD55C\uBC88\uB9CC \uD22C\uD45C\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.
3205=\uD22C\uD45C \uAC8C\uC2DC\uBB3C\uC758 \uC791\uC131\uC790\uB9CC \uC601\uC0C1\uC744 \uC5C5\uB85C\uB4DC\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.
3206=\uD22C\uD45C \uAC8C\uC2DC\uBB3C\uC758 \uC791\uC131\uC790\uB9CC \uD22C\uD45C \uAC8C\uC2DC\uBB3C\uC744 \uC0AD\uC81C\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.
4001=\uB370\uC774\uD130\uB97C \uCC98\uB9AC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
4002=\uB370\uC774\uD130\uB97C \uCC98\uB9AC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
3 changes: 2 additions & 1 deletion src/test/java/com/tune_fun/v1/dummy/DummyService.java
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ public void initVotePaper() {
public void initVotePaperBatch() {
Long authorId = defaultArtistAccount.getId();
String sql = "INSERT INTO vote_paper (author_id, created_at, delivery_at, updated_at, vote_end_at, vote_start_at, content,\n" +
" option, title, uuid, video_url) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
" option, title, uuid, video_url, enabled) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";

for (int idx = 0; idx < 1000; idx++) {
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
Expand All @@ -258,6 +258,7 @@ public void setValues(@NotNull PreparedStatement ps, int i) throws SQLException
ps.setString(9, "First Vote Paper");
ps.setString(10, StringUtil.uuid());
ps.setString(11, "test");
ps.setBoolean(12, true);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,48 @@ void updateVideoUrlSuccess() throws Exception {
assertEquals(registeredVotePaper.videoUrl(), videoUrl);
}

@Transactional
@Test
@Order(5)
@DisplayName("투표 게시물 삭제, 성공")
void deleteVotePaperSuccess() throws Exception {
dummyService.initAndLogin();
dummyService.initArtistAndLogin();

dummyService.initVotePaper();
dummyService.registerVote();

String accessToken = dummyService.getDefaultArtistAccessToken();
Long votePaperId = dummyService.getDefaultVotePaper().getId();

ParameterDescriptor queryParameter = parameterWithName("vote_paper_id").description("투표 게시물 ID").attributes(constraint("NOT NULL"));

mockMvc.perform(
delete(Uris.VOTE_PAPER_ROOT)
.queryParam("vote_paper_id", String.valueOf(votePaperId))
.header(AUTHORIZATION, bearerToken(accessToken))
)
.andExpectAll(baseAssertion(MessageCode.SUCCESS))
.andDo(
restDocs.document(
requestHeaders(authorizationHeader),
queryParameters(queryParameter),
responseFields(baseResponseFields),
resource(
builder().
description("투표 게시물 삭제").
queryParameters(queryParameter).
responseFields(baseResponseFields)
.build()
)
)
);

assertThrows(AssertionError.class,
() -> loadVotePaperPort.loadRegisteredVotePaper(votePaperId)
.orElseThrow(() -> new AssertionError("투표 게시물을 찾을 수 없습니다.")));
}

private void awaitReceiveMessage(final String queueName) {
ThrowingRunnable receiveMessageAssertionRunnable = () ->
sqsAsyncClient.getQueueUrl(r -> r.queueName(queueName))
Expand Down
Loading

0 comments on commit 9db0b55

Please sign in to comment.