feat(pipeline): 增强人脸匹配流水线的健壮性

- 在BuildSourceRelationStage中增加sampleListIds空值检查与降级处理
- 在PersistRelationsStage中增加memberSourceList空值检查与提前跳过逻辑
- 为BuildSourceRelationStage、DeleteOldRelationsStage和PersistRelationsStage添加完整的单元测试覆盖
- 实现异常情况下的优雅降级与错误日志记录
- 完善上下文状态管理与阶段跳过机制
This commit is contained in:
2025-12-03 18:49:03 +08:00
parent b165840176
commit 8c08c8947e
5 changed files with 705 additions and 0 deletions

View File

@@ -67,6 +67,23 @@ public class BuildSourceRelationStage extends AbstractFaceMatchingStage<FaceMatc
List<Long> sampleListIds = context.getSampleListIds();
Long faceId = context.getFaceId();
// 防御性检查:sampleListIds为空
if (sampleListIds == null || sampleListIds.isEmpty()) {
// 尝试从searchResult中获取
if (context.getSearchResult() != null) {
sampleListIds = context.getSearchResult().getSampleListIds();
if (sampleListIds != null && !sampleListIds.isEmpty()) {
context.setSampleListIds(sampleListIds);
} else {
log.debug("sampleListIds为空,跳过源文件关联,faceId={}", faceId);
return StageResult.skipped("sampleListIds为空");
}
} else {
log.debug("sampleListIds为空,跳过源文件关联,faceId={}", faceId);
return StageResult.skipped("sampleListIds为空");
}
}
try {
// 处理源文件关联
List<MemberSourceEntity> memberSourceEntityList =

View File

@@ -60,6 +60,12 @@ public class PersistRelationsStage extends AbstractFaceMatchingStage<FaceMatchin
List<MemberSourceEntity> memberSourceEntityList = context.getMemberSourceList();
Long faceId = context.getFaceId();
// 防御性检查:memberSourceList为空
if (memberSourceEntityList == null || memberSourceEntityList.isEmpty()) {
log.debug("memberSourceList为空,跳过持久化,faceId={}", faceId);
return StageResult.skipped("memberSourceList为空");
}
try {
// 1. 过滤已存在的关联关系
List<MemberSourceEntity> existingFiltered = sourceMapper.filterExistingRelations(memberSourceEntityList);

View File

@@ -0,0 +1,245 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.model.pc.source.entity.MemberSourceEntity;
import com.ycwl.basic.model.task.resp.SearchFaceRespVo;
import com.ycwl.basic.service.pc.processor.SourceRelationProcessor;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.Mockito.*;
/**
* BuildSourceRelationStage 单元测试
*/
@ExtendWith(MockitoExtension.class)
class BuildSourceRelationStageTest {
@Mock
private SourceRelationProcessor sourceRelationProcessor;
@InjectMocks
private BuildSourceRelationStage stage;
private FaceMatchingContext context;
private FaceEntity face;
@BeforeEach
void setUp() {
context = FaceMatchingContext.forAutoMatching(1L, true);
face = new FaceEntity();
face.setId(1L);
face.setMemberId(100L);
face.setScenicId(10L);
context.setFace(face);
}
@Test
void testExecute_Success() {
// Given
List<Long> sampleListIds = Arrays.asList(101L, 102L, 103L);
context.setSampleListIds(sampleListIds);
List<MemberSourceEntity> memberSourceList = createMemberSourceList(3);
when(sourceRelationProcessor.processMemberSources(sampleListIds, face))
.thenReturn(memberSourceList);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
assertTrue(result.getMessage().contains("构建了3个源文件关联"));
assertEquals(memberSourceList, context.getMemberSourceList());
verify(sourceRelationProcessor, times(1)).processMemberSources(sampleListIds, face);
}
@Test
void testExecute_SampleListIdsNull_FromSearchResult() {
// Given: sampleListIds为null,但searchResult有值
context.setSampleListIds(null);
SearchFaceRespVo searchResult = new SearchFaceRespVo();
searchResult.setSampleListIds(Arrays.asList(101L, 102L));
context.setSearchResult(searchResult);
List<MemberSourceEntity> memberSourceList = createMemberSourceList(2);
when(sourceRelationProcessor.processMemberSources(anyList(), any()))
.thenReturn(memberSourceList);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
assertEquals(Arrays.asList(101L, 102L), context.getSampleListIds()); // 从searchResult复制过来
verify(sourceRelationProcessor, times(1)).processMemberSources(anyList(), any());
}
@Test
void testExecute_SampleListIdsEmpty_Skipped() {
// Given
context.setSampleListIds(new ArrayList<>());
context.setSearchResult(null);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSkipped());
assertTrue(result.getMessage().contains("sampleListIds为空"));
verify(sourceRelationProcessor, never()).processMemberSources(anyList(), any());
}
@Test
void testExecute_BothSampleListIdsAndSearchResultEmpty_Skipped() {
// Given
context.setSampleListIds(null);
SearchFaceRespVo searchResult = new SearchFaceRespVo();
searchResult.setSampleListIds(null);
context.setSearchResult(searchResult);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSkipped());
verify(sourceRelationProcessor, never()).processMemberSources(anyList(), any());
}
@Test
void testExecute_ProcessorReturnsNull_Skipped() {
// Given
List<Long> sampleListIds = Arrays.asList(101L, 102L);
context.setSampleListIds(sampleListIds);
when(sourceRelationProcessor.processMemberSources(sampleListIds, face))
.thenReturn(null);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSkipped());
assertTrue(result.getMessage().contains("未找到有效的源文件"));
assertNull(context.getMemberSourceList());
}
@Test
void testExecute_ProcessorReturnsEmpty_Skipped() {
// Given
List<Long> sampleListIds = Arrays.asList(101L, 102L);
context.setSampleListIds(sampleListIds);
when(sourceRelationProcessor.processMemberSources(sampleListIds, face))
.thenReturn(new ArrayList<>());
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSkipped());
assertTrue(result.getMessage().contains("未找到有效的源文件"));
}
@Test
void testExecute_ProcessorThrowsException_Degraded() {
// Given
List<Long> sampleListIds = Arrays.asList(101L, 102L);
context.setSampleListIds(sampleListIds);
when(sourceRelationProcessor.processMemberSources(sampleListIds, face))
.thenThrow(new RuntimeException("Processing error"));
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isDegraded()); // 降级处理
assertTrue(result.getMessage().contains("构建源文件关联失败"));
verify(sourceRelationProcessor, times(1)).processMemberSources(sampleListIds, face);
}
@Test
void testExecute_SingleSample() {
// Given: 只有1个样本
List<Long> sampleListIds = Arrays.asList(101L);
context.setSampleListIds(sampleListIds);
List<MemberSourceEntity> memberSourceList = createMemberSourceList(1);
when(sourceRelationProcessor.processMemberSources(sampleListIds, face))
.thenReturn(memberSourceList);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
assertTrue(result.getMessage().contains("构建了1个源文件关联"));
}
@Test
void testExecute_ManySamples() {
// Given: 大量样本
List<Long> sampleListIds = Arrays.asList(101L, 102L, 103L, 104L, 105L, 106L, 107L, 108L, 109L, 110L);
context.setSampleListIds(sampleListIds);
List<MemberSourceEntity> memberSourceList = createMemberSourceList(10);
when(sourceRelationProcessor.processMemberSources(sampleListIds, face))
.thenReturn(memberSourceList);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
assertTrue(result.getMessage().contains("构建了10个源文件关联"));
assertEquals(10, context.getMemberSourceList().size());
}
@Test
void testExecute_SampleListIdsDifferentFromResult() {
// Given: sampleListIds与processMemberSources返回的数量不同(部分无效)
List<Long> sampleListIds = Arrays.asList(101L, 102L, 103L, 104L, 105L);
context.setSampleListIds(sampleListIds);
List<MemberSourceEntity> memberSourceList = createMemberSourceList(3); // 只有3个有效
when(sourceRelationProcessor.processMemberSources(sampleListIds, face))
.thenReturn(memberSourceList);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
assertTrue(result.getMessage().contains("构建了3个源文件关联"));
assertEquals(3, context.getMemberSourceList().size());
}
private List<MemberSourceEntity> createMemberSourceList(int count) {
List<MemberSourceEntity> list = new ArrayList<>();
for (int i = 0; i < count; i++) {
MemberSourceEntity entity = new MemberSourceEntity();
entity.setMemberId(100L);
entity.setSourceId((long) (i + 1));
list.add(entity);
}
return list;
}
}

View File

@@ -0,0 +1,175 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.mapper.SourceMapper;
import com.ycwl.basic.mapper.VideoMapper;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.repository.MemberRelationRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.*;
/**
* DeleteOldRelationsStage 单元测试
*/
@ExtendWith(MockitoExtension.class)
class DeleteOldRelationsStageTest {
@Mock
private SourceMapper sourceMapper;
@Mock
private VideoMapper videoMapper;
@Mock
private MemberRelationRepository memberRelationRepository;
@InjectMocks
private DeleteOldRelationsStage stage;
private FaceMatchingContext context;
private FaceEntity face;
@BeforeEach
void setUp() {
context = FaceMatchingContext.forAutoMatching(1L, true);
face = new FaceEntity();
face.setId(1L);
face.setMemberId(100L);
face.setScenicId(10L);
context.setFace(face);
}
@Test
void testExecute_Success() {
// Given - mock方法默认不做任何事,无需doNothing()
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
assertTrue(result.getMessage().contains("旧关系数据已删除"));
verify(sourceMapper, times(1)).deleteNotBuyFaceRelation(100L, 1L);
verify(videoMapper, times(1)).deleteNotBuyFaceRelations(100L, 1L);
verify(memberRelationRepository, times(1)).clearSCacheByFace(1L);
}
@Test
void testExecute_SourceMapperFailed_Degraded() {
// Given
doThrow(new RuntimeException("Database error"))
.when(sourceMapper).deleteNotBuyFaceRelation(100L, 1L);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isDegraded()); // 降级处理,不影响主流程
assertTrue(result.getMessage().contains("删除旧关系数据失败"));
verify(sourceMapper, times(1)).deleteNotBuyFaceRelation(100L, 1L);
verify(videoMapper, never()).deleteNotBuyFaceRelations(anyLong(), anyLong());
verify(memberRelationRepository, never()).clearSCacheByFace(anyLong());
}
@Test
void testExecute_VideoMapperFailed_Degraded() {
// Given - sourceMapper正常执行(无需doNothing)
doThrow(new RuntimeException("Delete video error"))
.when(videoMapper).deleteNotBuyFaceRelations(100L, 1L);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isDegraded());
verify(sourceMapper, times(1)).deleteNotBuyFaceRelation(100L, 1L);
verify(videoMapper, times(1)).deleteNotBuyFaceRelations(100L, 1L);
verify(memberRelationRepository, never()).clearSCacheByFace(anyLong());
}
@Test
void testExecute_CacheClearFailed_Degraded() {
// Given - sourceMapper和videoMapper正常执行
doThrow(new RuntimeException("Cache error"))
.when(memberRelationRepository).clearSCacheByFace(1L);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isDegraded());
verify(memberRelationRepository, times(1)).clearSCacheByFace(1L);
}
@Test
void testExecute_DifferentMemberId() {
// Given: 不同的memberId
face.setMemberId(999L);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
verify(sourceMapper, times(1)).deleteNotBuyFaceRelation(999L, 1L);
verify(videoMapper, times(1)).deleteNotBuyFaceRelations(999L, 1L);
}
@Test
void testExecute_DifferentFaceId() {
// Given: 不同的faceId
context = FaceMatchingContext.forAutoMatching(888L, true);
face.setId(888L);
context.setFace(face);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
verify(sourceMapper, times(1)).deleteNotBuyFaceRelation(100L, 888L);
verify(videoMapper, times(1)).deleteNotBuyFaceRelations(100L, 888L);
verify(memberRelationRepository, times(1)).clearSCacheByFace(888L);
}
@Test
void testExecute_NullPointerException_Degraded() {
// Given
doThrow(new NullPointerException("Null member"))
.when(sourceMapper).deleteNotBuyFaceRelation(100L, 1L);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isDegraded());
assertTrue(result.getMessage().contains("删除旧关系数据失败"));
}
@Test
void testExecute_PartialSuccess_Degraded() {
// Given: sourceMapper成功,但videoMapper失败
doThrow(new RuntimeException("Video deletion failed"))
.when(videoMapper).deleteNotBuyFaceRelations(100L, 1L);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isDegraded());
// source删除成功,但video删除失败
verify(sourceMapper, times(1)).deleteNotBuyFaceRelation(100L, 1L);
verify(videoMapper, times(1)).deleteNotBuyFaceRelations(100L, 1L);
}
}

View File

@@ -0,0 +1,262 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.mapper.SourceMapper;
import com.ycwl.basic.model.pc.source.entity.MemberSourceEntity;
import com.ycwl.basic.repository.MemberRelationRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.*;
/**
* PersistRelationsStage 单元测试
*/
@ExtendWith(MockitoExtension.class)
class PersistRelationsStageTest {
@Mock
private SourceMapper sourceMapper;
@Mock
private MemberRelationRepository memberRelationRepository;
@InjectMocks
private PersistRelationsStage stage;
private FaceMatchingContext context;
@BeforeEach
void setUp() {
context = FaceMatchingContext.forAutoMatching(1L, true);
}
@Test
void testExecute_Success() {
// Given
List<MemberSourceEntity> memberSourceList = createMemberSourceList(3);
context.setMemberSourceList(memberSourceList);
List<MemberSourceEntity> afterExistingFilter = createMemberSourceList(2);
List<MemberSourceEntity> afterValidFilter = createMemberSourceList(2);
when(sourceMapper.filterExistingRelations(memberSourceList))
.thenReturn(afterExistingFilter);
when(sourceMapper.filterValidSourceRelations(afterExistingFilter))
.thenReturn(afterValidFilter);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
assertTrue(result.getMessage().contains("持久化了2条关联关系"));
verify(sourceMapper, times(1)).filterExistingRelations(memberSourceList);
verify(sourceMapper, times(1)).filterValidSourceRelations(afterExistingFilter);
verify(sourceMapper, times(1)).addRelations(afterValidFilter);
verify(memberRelationRepository, times(1)).clearSCacheByFace(1L);
}
@Test
void testExecute_MemberSourceListNull_Skipped() {
// Given
context.setMemberSourceList(null);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSkipped());
assertTrue(result.getMessage().contains("memberSourceList为空"));
verify(sourceMapper, never()).filterExistingRelations(anyList());
verify(sourceMapper, never()).addRelations(anyList());
}
@Test
void testExecute_MemberSourceListEmpty_Skipped() {
// Given
context.setMemberSourceList(new ArrayList<>());
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSkipped());
assertTrue(result.getMessage().contains("memberSourceList为空"));
verify(sourceMapper, never()).filterExistingRelations(anyList());
}
@Test
void testExecute_AllFilteredByExisting_Skipped() {
// Given: 所有关系都已存在
List<MemberSourceEntity> memberSourceList = createMemberSourceList(5);
context.setMemberSourceList(memberSourceList);
when(sourceMapper.filterExistingRelations(memberSourceList))
.thenReturn(new ArrayList<>()); // 全部被过滤
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSkipped());
assertTrue(result.getMessage().contains("没有有效的关联关系可创建"));
verify(sourceMapper, times(1)).filterExistingRelations(memberSourceList);
verify(sourceMapper, never()).addRelations(anyList());
verify(memberRelationRepository, never()).clearSCacheByFace(anyLong());
}
@Test
void testExecute_AllFilteredByInvalid_Skipped() {
// Given: 过滤掉已存在的后,剩余的都是无效引用
List<MemberSourceEntity> memberSourceList = createMemberSourceList(5);
context.setMemberSourceList(memberSourceList);
List<MemberSourceEntity> afterExistingFilter = createMemberSourceList(3);
when(sourceMapper.filterExistingRelations(memberSourceList))
.thenReturn(afterExistingFilter);
when(sourceMapper.filterValidSourceRelations(afterExistingFilter))
.thenReturn(new ArrayList<>()); // 全部无效
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSkipped());
assertTrue(result.getMessage().contains("没有有效的关联关系可创建"));
verify(sourceMapper, times(1)).filterValidSourceRelations(afterExistingFilter);
verify(sourceMapper, never()).addRelations(anyList());
}
@Test
void testExecute_PartialFiltered() {
// Given: 部分被过滤,部分有效
List<MemberSourceEntity> memberSourceList = createMemberSourceList(10);
context.setMemberSourceList(memberSourceList);
List<MemberSourceEntity> afterExistingFilter = createMemberSourceList(7);
List<MemberSourceEntity> afterValidFilter = createMemberSourceList(5);
when(sourceMapper.filterExistingRelations(memberSourceList))
.thenReturn(afterExistingFilter);
when(sourceMapper.filterValidSourceRelations(afterExistingFilter))
.thenReturn(afterValidFilter);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
assertTrue(result.getMessage().contains("持久化了5条关联关系"));
verify(sourceMapper, times(1)).addRelations(afterValidFilter);
}
@Test
void testExecute_FilterExistingThrowsException_Failed() {
// Given
List<MemberSourceEntity> memberSourceList = createMemberSourceList(3);
context.setMemberSourceList(memberSourceList);
when(sourceMapper.filterExistingRelations(memberSourceList))
.thenThrow(new RuntimeException("Database error"));
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isFailed());
assertTrue(result.getMessage().contains("保存关联关系失败"));
assertNotNull(result.getException());
verify(sourceMapper, never()).addRelations(anyList());
}
@Test
void testExecute_AddRelationsThrowsException_Failed() {
// Given
List<MemberSourceEntity> memberSourceList = createMemberSourceList(3);
context.setMemberSourceList(memberSourceList);
List<MemberSourceEntity> afterExistingFilter = createMemberSourceList(2);
List<MemberSourceEntity> afterValidFilter = createMemberSourceList(2);
when(sourceMapper.filterExistingRelations(memberSourceList))
.thenReturn(afterExistingFilter);
when(sourceMapper.filterValidSourceRelations(afterExistingFilter))
.thenReturn(afterValidFilter);
doThrow(new RuntimeException("Insert error")).when(sourceMapper).addRelations(afterValidFilter);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isFailed());
verify(memberRelationRepository, never()).clearSCacheByFace(anyLong()); // 失败时不清缓存
}
@Test
void testExecute_CacheClearFailed() {
// Given
List<MemberSourceEntity> memberSourceList = createMemberSourceList(3);
context.setMemberSourceList(memberSourceList);
List<MemberSourceEntity> afterExistingFilter = createMemberSourceList(2);
List<MemberSourceEntity> afterValidFilter = createMemberSourceList(2);
when(sourceMapper.filterExistingRelations(memberSourceList))
.thenReturn(afterExistingFilter);
when(sourceMapper.filterValidSourceRelations(afterExistingFilter))
.thenReturn(afterValidFilter);
doThrow(new RuntimeException("Cache clear error"))
.when(memberRelationRepository).clearSCacheByFace(1L);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isFailed()); // 缓存清理失败导致整体失败
verify(sourceMapper, times(1)).addRelations(afterValidFilter); // 但关系已保存
}
@Test
void testExecute_SingleRelation() {
// Given: 只有1条关系
List<MemberSourceEntity> memberSourceList = createMemberSourceList(1);
context.setMemberSourceList(memberSourceList);
when(sourceMapper.filterExistingRelations(memberSourceList))
.thenReturn(memberSourceList);
when(sourceMapper.filterValidSourceRelations(memberSourceList))
.thenReturn(memberSourceList);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
assertTrue(result.getMessage().contains("持久化了1条关联关系"));
}
private List<MemberSourceEntity> createMemberSourceList(int count) {
List<MemberSourceEntity> list = new ArrayList<>();
for (int i = 0; i < count; i++) {
MemberSourceEntity entity = new MemberSourceEntity();
entity.setMemberId(100L);
entity.setSourceId((long) (i + 1));
list.add(entity);
}
return list;
}
}