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
22 changes: 20 additions & 2 deletions src/main/resources/static/js/admin/api/api-save.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@
return Number.isNaN(parsed) ? null : parsed;
};

const setHiddenImageValue = (id, value) => {
const input = document.getElementById(id);
if (input) input.value = value || '';
};

const getTargetDate = (type) => {
const inputIdByType = {
BATTLE: 'battle-target-date',
Expand All @@ -52,7 +57,9 @@
const previousStatus = PickeData.currentStatus;
const targetDate = getTargetDate(currentType);
const resolvedStatus = getStatus(currentType);
const shouldUploadAssets = action === 'PUBLISHED' || action === 'PUBLISH';
const hasNewBattleImageUploads = currentType === 'BATTLE'
&& !!(PickeData.uploadedFiles.thumbnail || PickeData.uploadedFiles.charA || PickeData.uploadedFiles.charB);
const shouldUploadAssets = action === 'PUBLISHED' || action === 'PUBLISH' || (action === 'EDIT' && hasNewBattleImageUploads);
const shouldUploadLocalDraft = action === 'PENDING';

PickeData.currentTargetDate = targetDate;
Expand Down Expand Up @@ -84,6 +91,14 @@
charAUrl = toUrlString(charAUrl);
charBUrl = toUrlString(charBUrl);

PickeData.existingUrls.thumbnail = thumbnailUrl || null;
PickeData.existingUrls.charA = charAUrl || null;
PickeData.existingUrls.charB = charBUrl || null;

setHiddenImageValue('battle-thumbnail-url', thumbnailUrl);
setHiddenImageValue('char-a-image-url', charAUrl);
setHiddenImageValue('char-b-image-url', charBUrl);

let payload = null;
let requestUrl = '';

Expand Down Expand Up @@ -283,7 +298,10 @@
}

if (scenarioExisted && PickeData.scenarioId) {
const shouldPatchScenarioStatus = action === 'PUBLISH' || previousStatus !== resolvedStatus;
const shouldPatchScenarioStatus =
action === 'PUBLISHED'
|| action === 'PUBLISH'
|| previousStatus !== resolvedStatus;
if (shouldPatchScenarioStatus) {
const statusRes = await fetch(`/api/v1/admin/scenarios/${PickeData.scenarioId}`, {
method: 'PATCH',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ <h2 class="text-lg font-bold flex items-center"><span class="bg-black text-white
</div>
<input id="thumbnail-upload" type="file" class="hidden" accept="image/*" />
</label>
<input id="battle-thumbnail-url" type="hidden" />
</div>

<div>
<label class="block text-sm font-bold text-gray-700 mb-2">타겟 날짜 <span class="text-red-500">*</span></label>
<input id="battle-target-date" type="date" class="w-full bg-gray-50 border border-gray-200 rounded-lg p-3 text-sm outline-none focus:border-black transition-all" />
</div>
</div>
</section>
Expand All @@ -63,6 +69,7 @@ <h3 class="text-blue-600 font-bold text-sm border-b border-blue-100 pb-2">선택
<span id="char-a-img-placeholder" class="text-xs font-bold text-gray-400 z-10">A 철학자 이미지</span>
<input type="file" id="char-a-img-upload" class="hidden" accept="image/*" />
</label>
<input id="char-a-image-url" type="hidden" />

<label class="block text-[11px] font-bold text-gray-500">철학자 태그</label>
<div class="flex flex-wrap gap-2 items-center bg-white p-2 border border-gray-100 rounded-lg min-h-[42px]" id="battle-a-philosopher-tags-container">
Expand All @@ -86,6 +93,7 @@ <h3 class="text-red-600 font-bold text-sm border-b border-red-100 pb-2">선택
<span id="char-b-img-placeholder" class="text-xs font-bold text-gray-400 z-10">B 철학자 이미지</span>
<input type="file" id="char-b-img-upload" class="hidden" accept="image/*" />
</label>
<input id="char-b-image-url" type="hidden" />

<label class="block text-[11px] font-bold text-gray-500">철학자 태그</label>
<div class="flex flex-wrap gap-2 items-center bg-white p-2 border border-gray-100 rounded-lg min-h-[42px]" id="battle-b-philosopher-tags-container">
Expand Down
Loading