feat(dramas): add play count field for drama records
1. 新增plays字段到DramaRecord接口和swagger/openapi文档 2. 在剧集创建时默认设置plays为0 3. 新增getPlayCountsByDramaIds方法聚合剧集播放量 4. 在后台和前台剧集列表中添加播放量数据 5. 更新测试用例适配新增的plays字段
This commit is contained in:
@@ -8,6 +8,15 @@ import { UsersService } from '../src/modules/users/users.service';
|
||||
|
||||
function createRepository<T extends { id?: number }>() {
|
||||
const records: T[] = [];
|
||||
const matchesWhere = (item: T, where?: Partial<T>) => {
|
||||
if (!where) {
|
||||
return true;
|
||||
}
|
||||
return Object.entries(where).every(
|
||||
([key, value]) => item[key as keyof T] === value,
|
||||
);
|
||||
};
|
||||
|
||||
return {
|
||||
records,
|
||||
create: jest.fn((input: Partial<T>) => input as T),
|
||||
@@ -24,14 +33,18 @@ function createRepository<T extends { id?: number }>() {
|
||||
return input;
|
||||
}),
|
||||
findOne: jest.fn(async ({ where }: { where: Partial<T> }) =>
|
||||
records.find((item) =>
|
||||
Object.entries(where).every(
|
||||
([key, value]) => item[key as keyof T] === value,
|
||||
),
|
||||
) ?? null,
|
||||
records.find((item) => matchesWhere(item, where)) ?? null,
|
||||
),
|
||||
find: jest.fn(async () => [...records]),
|
||||
count: jest.fn(async () => records.length),
|
||||
findAndCount: jest.fn(
|
||||
async ({ where, skip = 0, take = records.length }: { where?: Partial<T>; skip?: number; take?: number }) => {
|
||||
const filtered = records.filter((item) => matchesWhere(item, where));
|
||||
return [filtered.slice(skip, skip + take), filtered.length];
|
||||
},
|
||||
),
|
||||
count: jest.fn(async ({ where }: { where?: Partial<T> } = {}) =>
|
||||
records.filter((item) => matchesWhere(item, where)).length,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -182,4 +195,72 @@ describe('database-backed persistence', () => {
|
||||
expect(episodeMetricRepository.save).toHaveBeenCalled();
|
||||
expect(syncJobRepository.save).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('aggregates episode metric plays for database-backed drama lists', async () => {
|
||||
const now = new Date('2026-07-02T00:00:00.000Z');
|
||||
const dramaRepository = createRepository<any>();
|
||||
const episodeRepository = createRepository<any>();
|
||||
const queryBuilder = {
|
||||
select: jest.fn().mockReturnThis(),
|
||||
addSelect: jest.fn().mockReturnThis(),
|
||||
where: jest.fn().mockReturnThis(),
|
||||
groupBy: jest.fn().mockReturnThis(),
|
||||
getRawMany: jest.fn(async () => [{ dramaId: '1', plays: '321' }]),
|
||||
};
|
||||
const episodeMetricRepository = {
|
||||
createQueryBuilder: jest.fn(() => queryBuilder),
|
||||
};
|
||||
const dataSource = createDataSource({
|
||||
Drama: dramaRepository,
|
||||
Episode: episodeRepository,
|
||||
EpisodeMetric: episodeMetricRepository,
|
||||
});
|
||||
const service = new DramasService(dataSource as never);
|
||||
|
||||
await dramaRepository.save({
|
||||
id: 1,
|
||||
title: 'DB Plays Drama',
|
||||
coverUrl: 'https://cdn.example.com/db-plays.jpg',
|
||||
type: 'action',
|
||||
tags: [],
|
||||
status: PublishStatus.Published,
|
||||
sortOrder: 0,
|
||||
isRecommended: false,
|
||||
onlineVersion: 1,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
});
|
||||
await episodeRepository.save({
|
||||
id: 1,
|
||||
dramaId: 1,
|
||||
episodeNo: 1,
|
||||
title: 'Episode 1',
|
||||
coverUrl: 'https://cdn.example.com/ep1.jpg',
|
||||
durationSeconds: 180,
|
||||
isPaid: false,
|
||||
freeType: 'free',
|
||||
price: 0,
|
||||
status: PublishStatus.Published,
|
||||
publishStatus: PublishStatus.Published,
|
||||
reviewStatus: 'approved',
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
});
|
||||
|
||||
const adminList = await service.listAdminDramas({ page: 1, pageSize: 10 });
|
||||
const appList = await service.listPublishedDramas({ page: 1, pageSize: 10 });
|
||||
|
||||
expect(adminList.list[0]).toMatchObject({
|
||||
title: 'DB Plays Drama',
|
||||
plays: 321,
|
||||
});
|
||||
expect(appList.list[0]).toMatchObject({
|
||||
title: 'DB Plays Drama',
|
||||
plays: 321,
|
||||
});
|
||||
expect(queryBuilder.where).toHaveBeenCalledWith(
|
||||
'metric.dramaId IN (:...dramaIds)',
|
||||
{ dramaIds: [1] },
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -264,7 +264,10 @@ describe('Dramas and episodes', () => {
|
||||
|
||||
expect(dramas.body.data.list).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ title: 'Published Drama' }),
|
||||
expect.objectContaining({
|
||||
title: 'Published Drama',
|
||||
plays: 0,
|
||||
}),
|
||||
]),
|
||||
);
|
||||
const publishedDrama = dramas.body.data.list.find(
|
||||
@@ -365,6 +368,7 @@ describe('Dramas and episodes', () => {
|
||||
expect.objectContaining({
|
||||
title: 'Recommended Search Drama',
|
||||
isRecommended: true,
|
||||
plays: 0,
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user