fix(media): 补齐分集转码绑定与产物回填
This commit is contained in:
1 parent
6bdd245a83
commit
7d563f2cd6
18 files changed
+812
-25
No files matched your search
@@ -25,6 +25,10 @@
|
||||
<groupId>com.fruitshorts</groupId>
|
||||
<artifactId>fruitshorts-module-system</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fruitshorts</groupId>
|
||||
<artifactId>fruitshorts-module-media</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
|
||||
+32
-1
@@ -15,6 +15,8 @@ import com.fruitshorts.content.dal.mapper.DramaEpisodeMapper;
|
||||
import com.fruitshorts.content.enums.ContentStatus;
|
||||
import com.fruitshorts.content.vo.EpisodeRespVO;
|
||||
import com.fruitshorts.content.vo.EpisodeSaveReqVO;
|
||||
import com.fruitshorts.media.service.EpisodeTranscodeBindingService;
|
||||
import com.fruitshorts.media.service.EpisodeTranscodeCompletionPort;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -29,7 +31,7 @@ import java.util.Objects;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class EpisodeService {
|
||||
public class EpisodeService implements EpisodeTranscodeCompletionPort {
|
||||
|
||||
private static final int DEFAULT_PAGE_INDEX = 1;
|
||||
private static final int DEFAULT_PAGE_SIZE = 100;
|
||||
@@ -37,6 +39,7 @@ public class EpisodeService {
|
||||
|
||||
private final DramaEpisodeMapper dramaEpisodeMapper;
|
||||
private final DramaService dramaService;
|
||||
private final EpisodeTranscodeBindingService transcodeBindingService;
|
||||
|
||||
/** 后台:按短剧分页查询分集,status 在 SQL 层过滤;排序稳定(sort、ep_no)。 */
|
||||
public PageResult<EpisodeRespVO> pageByDrama(Long dramaId, String status,
|
||||
@@ -135,6 +138,7 @@ public class EpisodeService {
|
||||
episode.setLikeCount(nonNegativeOrZero(req.getLikeCount()));
|
||||
episode.setVideoAssetId(0L);
|
||||
dramaEpisodeMapper.insert(episode);
|
||||
bindTranscodeTask(episode);
|
||||
dramaService.syncTotalEpisodes(req.getDramaId());
|
||||
return episode.getId();
|
||||
}
|
||||
@@ -152,6 +156,7 @@ public class EpisodeService {
|
||||
episode.setStatus(req.getStatus());
|
||||
}
|
||||
dramaEpisodeMapper.updateById(episode);
|
||||
bindTranscodeTask(episode);
|
||||
if (!req.getDramaId().equals(episode.getDramaId())) {
|
||||
dramaService.syncTotalEpisodes(req.getDramaId());
|
||||
}
|
||||
@@ -252,6 +257,32 @@ public class EpisodeService {
|
||||
episode.setSort(req.getSort() == null ? (req.getNumber() == null ? 0 : req.getNumber()) : req.getSort());
|
||||
}
|
||||
|
||||
private void bindTranscodeTask(DramaEpisode episode) {
|
||||
Long assetId = transcodeBindingService.bind(
|
||||
episode.getId(), episode.getDramaId(), episode.getVideoUrl());
|
||||
if (assetId != null && assetId > 0
|
||||
&& !Objects.equals(assetId, episode.getVideoAssetId())) {
|
||||
episode.setVideoAssetId(assetId);
|
||||
dramaEpisodeMapper.updateById(episode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void markEpisodeReady(Long episodeId, String hlsUrl, Integer durationSec) {
|
||||
if (episodeId == null || episodeId <= 0 || !StringUtils.hasText(hlsUrl)) {
|
||||
return;
|
||||
}
|
||||
var update = Wrappers.<DramaEpisode>lambdaUpdate()
|
||||
.eq(DramaEpisode::getId, episodeId)
|
||||
.set(DramaEpisode::getHlsUrl, hlsUrl)
|
||||
.set(DramaEpisode::getTranscodeStatus, "success");
|
||||
if (durationSec != null && durationSec > 0) {
|
||||
update.set(DramaEpisode::getDurationSec, durationSec);
|
||||
}
|
||||
dramaEpisodeMapper.update(null, update);
|
||||
}
|
||||
|
||||
private EpisodeRespVO toResp(DramaEpisode episode) {
|
||||
EpisodeRespVO vo = new EpisodeRespVO();
|
||||
vo.setId(episode.getId());
|
||||
|
||||
+48
-1
@@ -3,6 +3,7 @@
|
||||
package com.fruitshorts.content.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.fruitshorts.common.pojo.PageResult;
|
||||
@@ -10,6 +11,8 @@ import com.fruitshorts.content.dal.entity.Drama;
|
||||
import com.fruitshorts.content.dal.entity.DramaEpisode;
|
||||
import com.fruitshorts.content.dal.mapper.DramaEpisodeMapper;
|
||||
import com.fruitshorts.content.vo.EpisodeRespVO;
|
||||
import com.fruitshorts.content.vo.EpisodeSaveReqVO;
|
||||
import com.fruitshorts.media.service.EpisodeTranscodeBindingService;
|
||||
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -20,7 +23,9 @@ import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@@ -33,6 +38,7 @@ class EpisodeServicePageTest {
|
||||
|
||||
private DramaEpisodeMapper dramaEpisodeMapper;
|
||||
private DramaService dramaService;
|
||||
private EpisodeTranscodeBindingService transcodeBindingService;
|
||||
private EpisodeService episodeService;
|
||||
|
||||
@BeforeAll
|
||||
@@ -45,10 +51,51 @@ class EpisodeServicePageTest {
|
||||
void setUp() {
|
||||
dramaEpisodeMapper = mock(DramaEpisodeMapper.class);
|
||||
dramaService = mock(DramaService.class);
|
||||
episodeService = new EpisodeService(dramaEpisodeMapper, dramaService);
|
||||
transcodeBindingService = mock(EpisodeTranscodeBindingService.class);
|
||||
episodeService = new EpisodeService(
|
||||
dramaEpisodeMapper, dramaService, transcodeBindingService);
|
||||
given(dramaService.getExisting(DRAMA_ID)).willReturn(new Drama());
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_bind_media_asset_and_transcode_task_when_episode_video_is_saved() {
|
||||
Drama drama = new Drama();
|
||||
drama.setId(DRAMA_ID);
|
||||
given(dramaService.getExisting(DRAMA_ID)).willReturn(drama);
|
||||
doAnswer(invocation -> {
|
||||
DramaEpisode episode = invocation.getArgument(0);
|
||||
episode.setId(31L);
|
||||
return 1;
|
||||
}).when(dramaEpisodeMapper).insert(any(DramaEpisode.class));
|
||||
given(transcodeBindingService.bind(
|
||||
31L, DRAMA_ID, "/files/episode/202610/video.mp4")).willReturn(88L);
|
||||
EpisodeSaveReqVO req = new EpisodeSaveReqVO();
|
||||
req.setDramaId(DRAMA_ID);
|
||||
req.setNumber(2);
|
||||
req.setVideoUrl("/files/episode/202610/video.mp4");
|
||||
|
||||
Long id = episodeService.create(req);
|
||||
|
||||
assertThat(id).isEqualTo(31L);
|
||||
verify(transcodeBindingService).bind(
|
||||
31L, DRAMA_ID, "/files/episode/202610/video.mp4");
|
||||
ArgumentCaptor<DramaEpisode> updated = ArgumentCaptor.forClass(DramaEpisode.class);
|
||||
verify(dramaEpisodeMapper).updateById(updated.capture());
|
||||
assertThat(updated.getValue().getVideoAssetId()).isEqualTo(88L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_write_hls_url_and_success_status_when_transcode_finishes() {
|
||||
episodeService.markEpisodeReady(31L, "/files/transcoded/88/master.m3u8", 4);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
ArgumentCaptor<LambdaUpdateWrapper<DramaEpisode>> captor =
|
||||
ArgumentCaptor.forClass(LambdaUpdateWrapper.class);
|
||||
verify(dramaEpisodeMapper).update(any(), captor.capture());
|
||||
assertThat(captor.getValue().getSqlSet()).contains("hls_url");
|
||||
assertThat(captor.getValue().getSqlSet()).contains("transcode_status");
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_use_default_page_size_100_when_page_size_omitted() {
|
||||
stubSelectPage(0L, List.of());
|
||||
|
||||
Reference in new issue
Block a user