Skip to content

Commit

Permalink
Merge pull request #66 from Tune-Fun/feature/vote/action
Browse files Browse the repository at this point in the history
Feature/vote/action
  • Loading branch information
habinkim authored May 2, 2024
2 parents 090a7ec + b0c27aa commit b3318c6
Show file tree
Hide file tree
Showing 45 changed files with 648 additions and 146 deletions.
1 change: 0 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,6 @@ dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'org.springframework.boot:spring-boot-devtools'
testImplementation 'com.c4-soft.springaddons:spring-addons-oauth2-test:7.6.12'
testImplementation 'org.springframework.kafka:spring-kafka-test'
testImplementation 'org.apache.commons:commons-lang3:3.14.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@ public boolean hasPermission(Authentication authentication, Serializable targetI

return switch (TargetType.valueOf(targetType)) {
case VOTE_PAPER -> hasPermissionForVotePaper(principal, targetId);
case VOTE -> false;
default -> false;
};

}

public boolean hasPermissionForVotePaper(User principal, Serializable targetId) {
Expand All @@ -50,6 +47,5 @@ public boolean hasPermissionForVotePaper(User principal, Serializable targetId)

private enum TargetType {
VOTE_PAPER,
VOTE
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
Expand Down Expand Up @@ -109,7 +110,7 @@ public SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospe
@Bean
public MethodSecurityExpressionHandler methodSecurityExpressionHandler() {
log.info("MethodSecurityExpressionHandler Activated");

DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
handler.setPermissionEvaluator(permissionPolicyEvaluator);

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/tune_fun/v1/common/config/Uris.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ private Uris() {
public static final String REGISTER_VOTE_PAPER = VOTE_ROOT + "/paper";
public static final String SET_VOTE_PAPER_DELIVERY_DATE = VOTE_ROOT + "/paper/delivery-date";

public static final String REGISTER_VOTE = VOTE_ROOT + "/register";
public static final String REGISTER_VOTE = VOTE_ROOT + "/{votePaperId}" + "/register" + "/{voteChoiceId}";

public static final String SWAGGER_UI_ROOT = "/swagger-ui";
public static final String SWAGGER_UI = "/swagger-ui.html";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public enum MessageCode {
VOTE_POLICY_ONE_VOTE_PAPER_PER_USER(BAD_REQUEST, "3201"),
VOTE_POLICY_ONLY_AUTHOR_CAN_UPDATE_DELIVERY_DATE(BAD_REQUEST, "3202"),
VOTE_PAPER_NOT_FOUND(BAD_REQUEST, "3203"),
VOTE_POLICY_ONE_VOTE_PER_USER(BAD_REQUEST, "3204"),

// 분산 락 관련
LOCK_ACQUISITION_FAILED_ERROR(INTERNAL_SERVER_ERROR, "4001"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
package com.tune_fun.v1.vote.adapter.input.rest;

import com.tune_fun.v1.account.domain.value.CurrentUser;
import com.tune_fun.v1.common.config.Uris;
import com.tune_fun.v1.common.hexagon.WebAdapter;
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.VoteCommands;
import com.tune_fun.v1.vote.application.port.input.usecase.RegisterVoteUseCase;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.userdetails.User;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;


Expand All @@ -23,8 +25,10 @@ public class VoteController {
private final ResponseMapper responseMapper;

@PostMapping(value = Uris.REGISTER_VOTE)
public ResponseEntity<Response<?>> registerVote(@Valid @RequestBody VoteCommands.Register command) {
registerVoteUseCase.register(command);
public ResponseEntity<Response<?>> registerVote(@PathVariable(name = "votePaperId") @NotNull(message = "{vote.paper.id.not_null}") final Long votePaperId,
@PathVariable(name = "voteChoiceId") @NotNull(message = "{vote.choice.id.not_null}") final Long voteChoiceId,
@CurrentUser final User user) {
registerVoteUseCase.register(votePaperId, voteChoiceId, user);
return responseMapper.ok();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import java.util.List;

public interface VoteChoiceRepository extends JpaRepository<VoteChoiceJpaEntity, String> {
public interface VoteChoiceRepository extends JpaRepository<VoteChoiceJpaEntity, Long> {

@EntityGraph(attributePaths = {"votePaper"})
List<VoteChoiceJpaEntity> findAllByVotePaperId(final Long votePaperId);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package com.tune_fun.v1.vote.adapter.output.persistence;

import com.tune_fun.v1.vote.domain.value.RegisteredVote;

import java.util.List;
import java.util.Optional;

public interface VoteCustomRepository {

List<Long> findVoterIdsByVotePaperUuid(final String uuid);

Optional<RegisteredVote> findByVoterUsernameAndVotePaperId(final String voter, final Long votePaperId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@

import com.querydsl.jpa.impl.JPAQueryFactory;
import com.tune_fun.v1.account.adapter.output.persistence.QAccountJpaEntity;
import com.tune_fun.v1.vote.domain.value.RegisteredVote;
import lombok.RequiredArgsConstructor;

import java.util.List;
import java.util.Optional;

import static com.querydsl.core.types.Projections.fields;

@RequiredArgsConstructor
public class VoteCustomRepositoryImpl implements VoteCustomRepository {
Expand All @@ -30,4 +34,28 @@ public List<Long> findVoterIdsByVotePaperUuid(String uuid) {
.where(VOTE_PAPER.uuid.eq(uuid))
.fetch();
}

@Override
public Optional<RegisteredVote> findByVoterUsernameAndVotePaperId(String voter, Long votePaperId) {
RegisteredVote registeredVote = queryFactory.select(fields(RegisteredVote.class,
VOTE.id,
VOTE.uuid,
ACCOUNT.username,
VOTE_PAPER.id.as("votePaperId"),
VOTE_CHOICE.offer.music,
VOTE_CHOICE.offer.artistName
)
)
.from(VOTE)
.join(VOTE.voteChoice, VOTE_CHOICE)
.join(VOTE_CHOICE.votePaper, VOTE_PAPER)
.join(VOTE.voter, ACCOUNT)
.where(
ACCOUNT.username.eq(voter),
VOTE_PAPER.id.eq(votePaperId)
)
.fetchOne();

return Optional.ofNullable(registeredVote);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,8 @@ public class VotePaperJpaEntity extends BaseEntity {
@Comment("커버 영상 등록일")
private LocalDateTime deliveryAt;

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

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
import com.tune_fun.v1.account.adapter.output.persistence.AccountJpaEntity;
import com.tune_fun.v1.account.adapter.output.persistence.AccountPersistenceAdapter;
import com.tune_fun.v1.common.hexagon.PersistenceAdapter;
import com.tune_fun.v1.common.util.StringUtil;
import com.tune_fun.v1.vote.application.port.output.*;
import com.tune_fun.v1.vote.domain.behavior.SaveVoteChoice;
import com.tune_fun.v1.vote.domain.behavior.SaveVotePaper;
import com.tune_fun.v1.vote.domain.value.RegisteredVote;
import com.tune_fun.v1.vote.domain.value.RegisteredVoteChoice;
import com.tune_fun.v1.vote.domain.value.RegisteredVotePaper;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -42,6 +44,28 @@ public List<Long> loadVoterIdsByVotePaperUuid(final String uuid) {
return voteRepository.findVoterIdsByVotePaperUuid(uuid);
}

@Override
public Optional<RegisteredVote> loadVoteByVoterAndVotePaperId(String voter, Long voteChoiceId) {
return voteRepository.findByVoterUsernameAndVotePaperId(voter, voteChoiceId);
}

@Override
public void saveVote(Long voteChoiceId, String username) {
VoteChoiceJpaEntity voteChoice = voteChoiceRepository.findById(voteChoiceId)
.orElseThrow(() -> new IllegalArgumentException("VoteChoice not found"));

AccountJpaEntity account = accountPersistenceAdapter.loadAccountByUsername(username)
.orElseThrow(() -> new IllegalArgumentException("Account not found"));

VoteJpaEntity vote = VoteJpaEntity.builder()
.uuid(StringUtil.uuid())
.voteChoice(voteChoice)
.voter(account)
.build();

voteRepository.save(vote);
}

@Override
public Optional<RegisteredVotePaper> loadRegisteredVotePaper(final String username) {
return findAvailableVotePaperByAuthor(username)
Expand All @@ -68,6 +92,12 @@ public RegisteredVotePaper updateDeliveryAt(final Long votePaperId, final LocalD
return votePaperMapper.registeredVotePaper(savedVotePaper);
}

@Override
public List<RegisteredVoteChoice> loadRegisteredVoteChoice(Long votePaperId) {
List<VoteChoiceJpaEntity> voteChoices = findAllByVotePaperId(votePaperId);
return voteChoiceMapper.registeredVoteChoices(voteChoices);
}

@Override
public void saveVoteChoice(final Long votePaperId, final Set<SaveVoteChoice> behavior) {
VotePaperJpaEntity votePaperJpaEntity = findProgressingVotePaperById(votePaperId)
Expand All @@ -94,9 +124,7 @@ public Optional<VotePaperJpaEntity> findAvailableVotePaperByAuthor(final String
return votePaperRepository.findByVoteEndAtAfterAndAuthorUsername(now(), username);
}

@Override
public List<RegisteredVoteChoice> loadRegisteredVoteChoice(Long votePaperId) {
List<VoteChoiceJpaEntity> voteChoices = voteChoiceRepository.findAllByVotePaperId(votePaperId);
return voteChoiceMapper.registeredVoteChoices(voteChoices);
public List<VoteChoiceJpaEntity> findAllByVotePaperId(Long votePaperId) {
return voteChoiceRepository.findAllByVotePaperId(votePaperId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
import org.springframework.data.jpa.repository.JpaRepository;

public interface VoteRepository extends JpaRepository<VoteJpaEntity, Long>, VoteCustomRepository {

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,4 @@

public class VoteCommands {

public record Register() {
}

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.tune_fun.v1.vote.application.port.input.usecase;

import com.tune_fun.v1.vote.application.port.input.command.VoteCommands;
import org.springframework.security.core.userdetails.User;

@FunctionalInterface
public interface RegisterVoteUseCase {

void register(final VoteCommands.Register command);
void register(Long votePaperId, final Long voteChoiceId, User user);

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package com.tune_fun.v1.vote.application.port.output;

import com.tune_fun.v1.vote.domain.value.RegisteredVote;

import java.util.List;
import java.util.Optional;

public interface LoadVotePort {
List<Long> loadVoterIdsByVotePaperUuid(final String uuid);

Optional<RegisteredVote> loadVoteByVoterAndVotePaperId(final String voter, final Long voteChoiceId);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package com.tune_fun.v1.vote.application.port.output;

public interface SaveVotePort {
void saveVote(final Long voteChoiceId, final String username);
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public class RegisterVotePaperService implements RegisterVotePaperUseCase {
@Override
public void register(final VotePaperCommands.Register command, final User user) throws JsonProcessingException {
validateRegistrableVotePaperCount(user);
RegisteredVotePaper registeredVotePaper = saveVotePaper(command);
RegisteredVotePaper registeredVotePaper = saveVotePaper(command, user);
saveVoteChoiceByRegisteredVotePaper(command, registeredVotePaper);

VotePaperRegisterEvent votePaperRegisterEventBehavior = getProduceVotePaperUploadEventBehavior(registeredVotePaper);
Expand All @@ -57,8 +57,8 @@ public void validateRegistrableVotePaperCount(final User user) {
}

@Transactional
public RegisteredVotePaper saveVotePaper(VotePaperCommands.Register command) {
SaveVotePaper saveVotePaperBehavior = voteBehaviorMapper.saveVotePaper(command);
public RegisteredVotePaper saveVotePaper(final VotePaperCommands.Register command, final User user) {
SaveVotePaper saveVotePaperBehavior = voteBehaviorMapper.saveVotePaper(command, user);
return saveVotePaperPort.saveVotePaper(saveVotePaperBehavior);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
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.lock.DistributionLock;
import com.tune_fun.v1.vote.application.port.input.command.VoteCommands;
import com.tune_fun.v1.vote.application.port.input.usecase.RegisterVoteUseCase;
import com.tune_fun.v1.vote.application.port.output.LoadVotePort;
import com.tune_fun.v1.vote.application.port.output.SaveVotePort;
import com.tune_fun.v1.vote.application.port.output.SendVoteFcmPort;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Service;

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


@Service
@UseCase
@RequiredArgsConstructor
public class RegisterVoteService implements RegisterVoteUseCase {

private final LoadVotePort loadVotePort;
private final SaveVotePort saveVotePort;
private final SendVoteFcmPort sendVoteFcmPort;

@Override
@DistributionLock(key = "registerVote")
public void register(final VoteCommands.Register command) {

// TODO
public void register(Long votePaperId, final Long voteChoiceId, final User user) {
if (loadVotePort.loadVoteByVoterAndVotePaperId(user.getUsername(), votePaperId).isPresent())
throw new CommonApplicationException(VOTE_POLICY_ONE_VOTE_PER_USER);

saveVotePort.saveVote(voteChoiceId, user.getUsername());
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public class SendVotePaperRegisterFcmService implements SendVotePaperRegisterFcm
@Override
public void send(VotePaperRegisterEvent votePaperRegisterEvent) throws JsonProcessingException, FirebaseMessagingException {
// TODO : Follower Aggregate Root................... -> accountIds

List<NotificationApprovedDevice> notificationApprovedDevices = loadDevicePort.
loadNotificationApprovedDevice(true, null, null, null);
log.info("notificationApprovedDevices: \n{}", objectUtil.objectToPrettyJson(notificationApprovedDevices));
Expand All @@ -49,12 +49,12 @@ public void send(VotePaperRegisterEvent votePaperRegisterEvent) throws JsonProce

@Retryable(retryFor = FirebaseMessagingException.class, recover = "recoverSendVotePaperRegisterFcm", backoff = @Backoff(delay = 2000, multiplier = 1.5, maxDelay = 10000))
private void sendVotePaperRegisterFcm(final VotePaperRegisterEvent votePaperRegisterEvent, final List<NotificationApprovedDevice> notificationApprovedDevices) throws FirebaseMessagingException {
RetryContext retryContext = RetrySynchronizationManager.getContext();

Throwable e = retryContext.getLastThrowable();
int retryCount = retryContext.getRetryCount();
if (RetrySynchronizationManager.getContext() != null) {
RetryContext retryContext = RetrySynchronizationManager.getContext();
log.info("Retry count: {}", retryContext.getRetryCount());

if (retryCount > 0) log.error("sendVotePaperRegisterFcm FAILED. \n{}\nRetry count: {}", e, retryCount);
log.error("sendVotePaperRegisterFcm FAILED. \n{}\nRetry count: {}", retryContext.getLastThrowable(), retryContext.getRetryCount());
}

SendVotePaperRegisterFcm sendVotePaperRegisterFcmBehavior = voteBehaviorMapper
.sendVotePaperRegisterFcm(votePaperRegisterEvent, notificationApprovedDevices);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.springframework.security.core.userdetails.User;

import java.util.List;
import java.util.Set;
Expand All @@ -22,7 +23,8 @@
@Mapper(config = BaseMapperConfig.class)
public abstract class VoteBehaviorMapper {

public abstract SaveVotePaper saveVotePaper(final VotePaperCommands.Register command);
@Mapping(target = "author", source = "user.username")
public abstract SaveVotePaper saveVotePaper(final VotePaperCommands.Register command, final User user);

@IterableMapping(qualifiedByName = "saveVoteChoice")
public abstract Set<SaveVoteChoice> saveVoteChoices(final Set<VotePaperCommands.Offer> offers);
Expand Down
9 changes: 0 additions & 9 deletions src/main/java/com/tune_fun/v1/vote/domain/Album.java

This file was deleted.

Loading

0 comments on commit b3318c6

Please sign in to comment.