Skip to content
Merged
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
@@ -1,13 +1,16 @@
package com.swyp.picke.domain.admin.dto.battle.request;

import com.swyp.picke.domain.battle.enums.BattleStatus;
import java.time.LocalDate;
import java.util.List;

public record AdminBattleCreateRequest(
String title,
String summary,
String description,
String thumbnailUrl,
LocalDate targetDate,
Integer audioDuration,
BattleStatus status,
List<Long> tagIds,
List<AdminBattleOptionRequest> options
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package com.swyp.picke.domain.admin.dto.battle.request;

import com.swyp.picke.domain.battle.enums.BattleStatus;
import java.time.LocalDate;
import java.util.List;

public record AdminBattleUpdateRequest(
String title,
String summary,
String description,
String thumbnailUrl,
LocalDate targetDate,
Integer audioDuration,
BattleStatus status,
List<Long> tagIds,
List<AdminBattleOptionRequest> options
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.swyp.picke.domain.admin.dto.poll.request;

import com.swyp.picke.domain.poll.enums.PollStatus;
import java.time.LocalDate;
import java.util.List;

public record AdminPollCreateRequest(
String titlePrefix,
String titleSuffix,
LocalDate targetDate,
PollStatus status,
List<AdminPollOptionRequest> options
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

public record AdminPollOptionRequest(
PollOptionLabel label,
String title
String title,
Integer displayOrder
) {}


Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.swyp.picke.domain.admin.dto.quiz.request;

import com.swyp.picke.domain.quiz.enums.QuizStatus;
import java.time.LocalDate;
import java.util.List;

public record AdminQuizCreateRequest(
String title,
LocalDate targetDate,
QuizStatus status,
List<AdminQuizOptionRequest> options
) {}
) {}
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ public record AdminQuizOptionRequest(
QuizOptionLabel label,
String text,
String detailText,
Boolean isCorrect
) {}
Boolean isCorrect,
Integer displayOrder
) {}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public Battle toEntity(AdminBattleCreateRequest request, User admin) {
.summary(request.summary())
.description(request.description())
.thumbnailUrl(request.thumbnailUrl())
.targetDate(request.targetDate())
.audioDuration(request.audioDuration())
.status(request.status())
.creatorType(BattleCreatorType.ADMIN)
.creator(admin)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ public void update(
String summary,
String description,
String thumbnailUrl,
LocalDate targetDate,
Integer audioDuration,
BattleStatus status
) {
if (title != null) {
Expand Down Expand Up @@ -155,4 +157,4 @@ public void updateTargetDate(LocalDate targetDate) {
this.targetDate = targetDate;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,8 @@ public AdminBattleDetailResponse createBattle(AdminBattleCreateRequest request,
request.summary(),
request.description(),
resolvedThumbnailKey,
request.targetDate(),
request.audioDuration(),
request.status()
);
battle = battleRepository.save(battle);
Expand Down Expand Up @@ -379,6 +381,8 @@ public AdminBattleDetailResponse updateBattle(Long battleId, AdminBattleUpdateRe
request.summary(),
request.description(),
resolvedThumbnailKey,
request.targetDate(),
request.audioDuration(),
request.status()
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public Poll toEntity(AdminPollCreateRequest request) {
return Poll.builder()
.titlePrefix(request.titlePrefix())
.titleSuffix(request.titleSuffix())
.targetDate(request.targetDate())
.status(request.status())
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public PollOption(Poll poll, PollOptionLabel label, String title, Integer displa
}


public void update(String title) {
public void update(String title, Integer displayOrder) {
if (title != null) this.title = title;
if (displayOrder != null) this.displayOrder = displayOrder;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,14 @@ public AdminPollDetailResponse createPoll(AdminPollCreateRequest request) {

List<PollOption> savedOptions = new ArrayList<>();
if (request.options() != null) {
for (AdminPollOptionRequest optionRequest : request.options()) {
for (int i = 0; i < request.options().size(); i++) {
AdminPollOptionRequest optionRequest = request.options().get(i);
int displayOrder = resolveDisplayOrder(optionRequest.displayOrder(), i + 1);
PollOption option = PollOption.builder()
.poll(poll)
.label(optionRequest.label())
.title(optionRequest.title())
.displayOrder(displayOrder)
.build();
option = pollOptionRepository.save(option);
savedOptions.add(option);
Expand Down Expand Up @@ -132,7 +135,9 @@ public AdminPollDetailResponse updatePoll(Long pollId, AdminPollUpdateRequest re
}

Set<PollOptionLabel> requestedLabels = new HashSet<>();
for (AdminPollOptionRequest optionRequest : request.options()) {
for (int i = 0; i < request.options().size(); i++) {
AdminPollOptionRequest optionRequest = request.options().get(i);
int displayOrder = resolveDisplayOrder(optionRequest.displayOrder(), i + 1);
requestedLabels.add(optionRequest.label());
PollOption option = existingOptionMap.get(optionRequest.label());

Expand All @@ -141,10 +146,11 @@ public AdminPollDetailResponse updatePoll(Long pollId, AdminPollUpdateRequest re
.poll(poll)
.label(optionRequest.label())
.title(optionRequest.title())
.displayOrder(displayOrder)
.build();
option = pollOptionRepository.save(option);
} else {
option.update(optionRequest.title());
option.update(optionRequest.title(), displayOrder);
}
}

Expand All @@ -168,6 +174,13 @@ public AdminPollDeleteResponse deletePoll(Long pollId) {
return new AdminPollDeleteResponse(true, LocalDateTime.now());
}

private int resolveDisplayOrder(Integer requestedOrder, int fallbackOrder) {
if (requestedOrder == null || requestedOrder < 1) {
return fallbackOrder;
}
return requestedOrder;
}

private void ensureTodayPicks(LocalDate today, int requiredCount) {
List<Poll> todays = pollRepository.findTodayPicks(PollStatus.PUBLISHED, today, PageRequest.of(0, requiredCount));
int missingCount = requiredCount - todays.size();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class QuizConverter {
public Quiz toEntity(AdminQuizCreateRequest request) {
return Quiz.builder()
.title(request.title())
.targetDate(request.targetDate())
.status(request.status())
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ void assignQuiz(Quiz quiz) {
this.quiz = quiz;
}

public void update(String text, String detailText, Boolean isCorrect) {
public void update(String text, String detailText, Boolean isCorrect, Integer displayOrder) {
if (text != null) this.text = text;
if (detailText != null) this.detailText = detailText;
if (isCorrect != null) this.isCorrect = isCorrect;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,16 @@ public AdminQuizDetailResponse createQuiz(AdminQuizCreateRequest request) {

List<QuizOption> savedOptions = new ArrayList<>();
if (request.options() != null) {
for (AdminQuizOptionRequest optionRequest : request.options()) {
for (int i = 0; i < request.options().size(); i++) {
AdminQuizOptionRequest optionRequest = request.options().get(i);
int displayOrder = resolveDisplayOrder(optionRequest.displayOrder(), i + 1);
QuizOption option = QuizOption.builder()
.quiz(quiz)
.label(optionRequest.label())
.text(optionRequest.text())
.detailText(optionRequest.detailText())
.isCorrect(optionRequest.isCorrect())
.displayOrder(displayOrder)
.build();
option = quizOptionRepository.save(option);
savedOptions.add(option);
Expand All @@ -129,7 +132,9 @@ public AdminQuizDetailResponse updateQuiz(Long quizId, AdminQuizUpdateRequest re
}

Set<QuizOptionLabel> requestedLabels = new HashSet<>();
for (AdminQuizOptionRequest optionRequest : request.options()) {
for (int i = 0; i < request.options().size(); i++) {
AdminQuizOptionRequest optionRequest = request.options().get(i);
int displayOrder = resolveDisplayOrder(optionRequest.displayOrder(), i + 1);
requestedLabels.add(optionRequest.label());
QuizOption option = existingOptionMap.get(optionRequest.label());

Expand All @@ -140,13 +145,15 @@ public AdminQuizDetailResponse updateQuiz(Long quizId, AdminQuizUpdateRequest re
.text(optionRequest.text())
.detailText(optionRequest.detailText())
.isCorrect(optionRequest.isCorrect())
.displayOrder(displayOrder)
.build();
option = quizOptionRepository.save(option);
} else {
option.update(
optionRequest.text(),
optionRequest.detailText(),
optionRequest.isCorrect()
optionRequest.isCorrect(),
displayOrder
);
}
}
Expand All @@ -171,6 +178,13 @@ public AdminQuizDeleteResponse deleteQuiz(Long quizId) {
return new AdminQuizDeleteResponse(true, LocalDateTime.now());
}

private int resolveDisplayOrder(Integer requestedOrder, int fallbackOrder) {
if (requestedOrder == null || requestedOrder < 1) {
return fallbackOrder;
}
return requestedOrder;
}

private void ensureTodayPicks(LocalDate today, int requiredCount) {
List<Quiz> todays = quizRepository.findTodayPicks(QuizStatus.PUBLISHED, today, PageRequest.of(0, requiredCount));
int missingCount = requiredCount - todays.size();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ public void updateScenarioContent(Long scenarioId, ScenarioCreateRequest request
if (mergedAudioUrl != null) s3Service.deleteFile(mergedAudioUrl);
}
scenario.clearAudios();

// 발행 상태에서 시나리오가 변경되면 merged 오디오를 다시 생성한다.
if (scenario.getStatus() == ScenarioStatus.PUBLISHED) {
triggerAudioPipeline(scenarioId);
}
}
}

Expand Down Expand Up @@ -259,12 +264,17 @@ private boolean smartUpdateNodesToScenario(Scenario scenario, ScenarioCreateRequ
}

private void triggerAudioPipeline(Long scenarioId) {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
audioPipelineService.generateAndMergeAudioAsync(scenarioId);
}
});
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
audioPipelineService.generateAndMergeAudioAsync(scenarioId);
}
});
return;
}

audioPipelineService.generateAndMergeAudioAsync(scenarioId);
}

private boolean updateScriptsSmartly(ScenarioNode existingNode, java.util.List<ScriptRequest> requestedScripts, Map<String, String> speakerMap) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@Getter
public enum CreditType {
BATTLE_VOTE(5), // 배틀 참여 보상: 사후 투표 완료 시 즉시 지급
BATTLE_ENTRY(-10), // 지난 배틀 이용 비용: 사전 투표 최초 진입 시 차감
MAJORITY_WIN(10), // 다수결 보상: 월요일 배치, 2주 전 배틀 TOP≥10 대상
BEST_COMMENT(50), // 베댓 보상: 월요일 배치, 2주 전 배틀 좋아요 1위
WEEKLY_CHARGE(40), // 주간 자동 충전: 매주 월요일 00:00 활성 사용자 전체
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,9 @@ public interface UserRepository extends JpaRepository<User, Long> {
@Query("update User u set u.credit = u.credit + :amount where u.id = :id")
int incrementCredit(@Param("id") Long id, @Param("amount") int amount);

@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query("update User u set u.credit = u.credit - :amount where u.id = :id and u.credit >= :amount")
int decrementCreditIfEnough(@Param("id") Long id, @Param("amount") int amount);

List<User> findAllByStatus(UserStatus status);
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public void addCredit(Long userId, CreditType creditType, Long referenceId) {
* CreditType의 기본 포인트가 아닌 가변 포인트가 필요한 경우(FREE_CHARGE 랜덤 박스 등)에서 사용.
* 예: creditService.addCredit(userId, CreditType.FREE_CHARGE, 15, boxId);
*
* 적립이 성공하면 User.credit 캐시를 동기 증감하여 {@link #getTotalPoints}가 전체 히스토리를 재집계하지 않도록 한다.
* 적립/차감이 성공하면 User.credit 캐시를 동기 증감하여 {@link #getTotalPoints}가 전체 히스토리를 재집계하지 않도록 한다.
* (user, creditType, referenceId) 중복 시 조용히 무시(멱등).
*/
@Transactional
Expand All @@ -75,7 +75,15 @@ public void addCredit(Long userId, CreditType creditType, int amount, Long refer
}
throw new CustomException(ErrorCode.CREDIT_SAVE_FAILED);
}
if (userRepository.incrementCredit(userId, amount) == 0) {

int updated = amount >= 0
? userRepository.incrementCredit(userId, amount)
: userRepository.decrementCreditIfEnough(userId, -amount);

if (updated == 0) {
if (amount < 0) {
throw new CustomException(ErrorCode.CREDIT_NOT_ENOUGH);
}
throw new CustomException(ErrorCode.USER_NOT_FOUND);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
import com.swyp.picke.domain.vote.sse.VoteUpdatedEvent;
import com.swyp.picke.global.common.exception.CustomException;
import com.swyp.picke.global.common.exception.ErrorCode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
Expand All @@ -34,6 +36,8 @@
@Transactional(readOnly = true)
public class BattleVoteServiceImpl implements BattleVoteService {

private static final ZoneId KST = ZoneId.of("Asia/Seoul");

private final BattleVoteRepository battleVoteRepository;
private final BattleService battleService;
private final BattleOptionRepository battleOptionRepository;
Expand Down Expand Up @@ -122,6 +126,9 @@ public VoteResultResponse preVote(Long battleId, Long userId, VoteRequest reques
vote = existingVote.get();
vote.updatePreVote(option);
} else {
if (shouldChargeBattleEntryCredit(battle)) {
creditService.addCredit(user.getId(), CreditType.BATTLE_ENTRY, battle.getId());
}
vote = BattleVote.createPreVote(user, battle, option);
battleVoteRepository.save(vote);
battle.addParticipant();
Expand Down Expand Up @@ -191,4 +198,9 @@ public void completeTts(Long battleId, Long userId) {

userBattleService.upsertStep(user, battle, UserBattleStep.POST_VOTE);
}
}

private boolean shouldChargeBattleEntryCredit(Battle battle) {
LocalDate today = LocalDate.now(KST);
return battle.getTargetDate() == null || !battle.getTargetDate().isEqual(today);
}
}
Loading