feat(content): 短剧类型与内容类型支持多选

This commit is contained in:
Zhou Weidong committed 2026-10-21 17:01:12 +08:00
1 parent 030df5c5dc
commit 4c614a5ece
13 files changed
+665 -50

No files matched your search

@@ -71,7 +71,7 @@ class DramaServiceFeedTest {
.contains("view_count")
.contains("id");
assertThat(query.getParamNameValuePairs().values())
.contains("published", "ANIME", 30, 1200L, 9L);
.contains("published", "%,ANIME,%", 30, 1200L, 9L);
}
@Test
@@ -0,0 +1,192 @@
// Author: fruit.com.cn
package com.fruitshorts.app.service;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fruitshorts.content.dal.entity.Drama;
import com.fruitshorts.content.dal.mapper.DramaEpisodeMapper;
import com.fruitshorts.content.dal.mapper.DramaMapper;
import com.fruitshorts.content.dal.mapper.DramaReviewRecordMapper;
import com.fruitshorts.content.service.DramaCategoryService;
import com.fruitshorts.content.service.DramaService;
import com.fruitshorts.content.vo.DramaPageReqVO;
import com.fruitshorts.content.vo.DramaRespVO;
import com.fruitshorts.content.vo.DramaSaveReqVO;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/** K93 短剧分类与内容类型多选。 */
class DramaServiceMultiTagTest {
private DramaMapper dramaMapper;
private DramaCategoryService categoryService;
private DramaService dramaService;
@BeforeAll
static void initTableInfo() {
TableInfoHelper.initTableInfo(
new MapperBuilderAssistant(new MybatisConfiguration(), ""), Drama.class);
}
@BeforeEach
void setUp() {
dramaMapper = mock(DramaMapper.class);
categoryService = mock(DramaCategoryService.class);
dramaService = new DramaService(
dramaMapper,
mock(DramaEpisodeMapper.class),
mock(DramaReviewRecordMapper.class),
categoryService);
}
@Test
void should_save_distinct_multi_tags_and_write_first_value_as_primary() {
DramaSaveReqVO req = new DramaSaveReqVO();
req.setTitle("多标签短剧");
req.setDramaTypes(List.of("live", "ANIME", "LIVE", " anime "));
req.setCategoryIds(Arrays.asList(3L, 1L, 3L, 0L, null));
req.setGenre("类型三 / 类型一");
dramaService.create(req, 1L);
ArgumentCaptor<Drama> captor = ArgumentCaptor.forClass(Drama.class);
verify(dramaMapper).insert(captor.capture());
Drama created = captor.getValue();
assertThat(created.getDramaType()).isEqualTo("LIVE");
assertThat(created.getDramaTypes()).isEqualTo("LIVE,ANIME");
assertThat(created.getCategoryId()).isEqualTo(3L);
assertThat(created.getCategoryIds()).isEqualTo("3,1");
}
@Test
void should_keep_single_value_behavior_when_lists_are_absent() {
Drama existing = drama(7L);
existing.setDramaType("ANIME");
existing.setDramaTypes("ANIME");
existing.setCategoryId(2L);
existing.setCategoryIds("2");
given(dramaMapper.selectById(7L)).willReturn(existing);
DramaSaveReqVO req = new DramaSaveReqVO();
req.setTitle("单值更新");
req.setDramaType("live");
req.setCategoryId(4L);
dramaService.update(7L, req);
ArgumentCaptor<Drama> captor = ArgumentCaptor.forClass(Drama.class);
verify(dramaMapper).updateById(captor.capture());
assertThat(captor.getValue().getDramaType()).isEqualTo("LIVE");
assertThat(captor.getValue().getDramaTypes()).isEqualTo("LIVE");
assertThat(captor.getValue().getCategoryId()).isEqualTo(4L);
assertThat(captor.getValue().getCategoryIds()).isEqualTo("4");
}
@Test
@SuppressWarnings("unchecked")
void should_apply_token_filters_for_admin_page_multi_tags() {
Page<Drama> page = new Page<>(1, 10);
page.setRecords(List.of());
given(dramaMapper.selectPage(any(Page.class), any())).willReturn(page);
DramaPageReqVO req = new DramaPageReqVO();
req.setDramaTypes(List.of("ANIME", "LIVE"));
req.setCategoryIds(List.of(2L, 3L));
ArgumentCaptor<LambdaQueryWrapper<Drama>> captor =
ArgumentCaptor.forClass(LambdaQueryWrapper.class);
dramaService.page(req);
verify(dramaMapper).selectPage(any(), captor.capture());
assertThat(captor.getValue().getSqlSegment())
.contains("drama_types")
.contains("category_ids")
.contains("LIKE");
assertThat(captor.getValue().getParamNameValuePairs().values())
.contains("%,ANIME,%", "%,LIVE,%", "%,2,%", "%,3,%");
}
@Test
@SuppressWarnings("unchecked")
void should_allow_multi_tag_cursor_marker_for_each_filter_type() {
Drama marker = drama(9L);
marker.setDramaType("ANIME");
marker.setDramaTypes("ANIME,LIVE");
marker.setRecommendWeight(30);
marker.setViewCount(1200L);
given(dramaMapper.selectById(9L)).willReturn(marker);
given(dramaMapper.selectList(any())).willReturn(List.of());
ArgumentCaptor<QueryWrapper<Drama>> captor = ArgumentCaptor.forClass(QueryWrapper.class);
dramaService.listPublishedFeed(List.of("ANIME"), 9L, 5);
dramaService.listPublishedFeed(List.of("LIVE"), 9L, 5);
verify(dramaMapper, times(2)).selectList(captor.capture());
assertThat(captor.getAllValues()).allSatisfy(query ->
assertThat(query.getSqlSegment())
.contains("drama_types")
.contains("LIKE"));
}
@Test
@SuppressWarnings("unchecked")
void should_match_multi_category_when_loading_app_category_section() {
Page<Drama> page = new Page<>(1, 10);
page.setRecords(List.of());
given(dramaMapper.selectPage(any(Page.class), any())).willReturn(page);
ArgumentCaptor<LambdaQueryWrapper<Drama>> captor =
ArgumentCaptor.forClass(LambdaQueryWrapper.class);
dramaService.listPublished(3L, 10);
verify(dramaMapper).selectPage(any(), captor.capture());
assertThat(captor.getValue().getSqlSegment())
.contains("category_ids")
.contains("LIKE");
assertThat(captor.getValue().getParamNameValuePairs().values()).contains("%,3,%");
}
@Test
void should_parse_multi_values_and_keep_category_name_order_in_response() {
Drama drama = drama(7L);
drama.setDramaType("LIVE");
drama.setDramaTypes("LIVE,ANIME");
drama.setCategoryId(3L);
drama.setCategoryIds("3,1");
drama.setGenre("Fallback Genre");
given(dramaMapper.selectById(7L)).willReturn(drama);
given(categoryService.nameMap()).willReturn(Map.of(1L, "Type A", 3L, "Type B"));
DramaRespVO vo = dramaService.get(7L);
assertThat(vo.getDramaTypes()).containsExactly("LIVE", "ANIME");
assertThat(vo.getCategoryIds()).containsExactly(3L, 1L);
assertThat(vo.getCategoryNames()).containsExactly("Type B", "Type A");
assertThat(vo.getDramaType()).isEqualTo("LIVE");
assertThat(vo.getCategoryId()).isEqualTo(3L);
}
private Drama drama(Long id) {
Drama drama = new Drama();
drama.setId(id);
drama.setStatus("published");
drama.setViewCount(0L);
return drama;
}
}