feat: 添加频道管理功能,关联短剧与频道绑定逻辑
1. 新增频道模块,包含类型定义、实体、DTO、控制器和服务 2. 为短剧添加频道ID和频道名字段,支持关联频道 3. 新增短剧删除API和权限配置 4. 调整tsconfig和依赖配置,更新权限列表 5. 新增相关测试用例验证频道与短剧关联逻辑
This commit is contained in:
@@ -24,6 +24,210 @@ describe('Dramas and episodes', () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('lists the default channel for admin selectors', async () => {
|
||||
const response = await request(app.getHttpServer())
|
||||
.get('/api/admin/v1/channels')
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.data.list).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
name: '默认频道',
|
||||
code: 'default',
|
||||
isDefault: true,
|
||||
status: 'active',
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it('assigns dramas to the default channel when no channel is selected', async () => {
|
||||
const dramaResponse = await request(app.getHttpServer())
|
||||
.post('/api/admin/v1/dramas')
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.send({
|
||||
title: 'Default Channel Drama',
|
||||
coverUrl: 'https://cdn.example.com/default-channel.jpg',
|
||||
type: 'romance',
|
||||
status: 'draft',
|
||||
})
|
||||
.expect(201);
|
||||
|
||||
expect(dramaResponse.body.data).toMatchObject({
|
||||
title: 'Default Channel Drama',
|
||||
channelId: expect.any(Number),
|
||||
channelName: '默认频道',
|
||||
});
|
||||
|
||||
const detail = await request(app.getHttpServer())
|
||||
.get(`/api/admin/v1/dramas/${dramaResponse.body.data.id}`)
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.expect(200);
|
||||
|
||||
expect(detail.body.data).toMatchObject({
|
||||
id: dramaResponse.body.data.id,
|
||||
channelId: dramaResponse.body.data.channelId,
|
||||
channelName: '默认频道',
|
||||
});
|
||||
});
|
||||
|
||||
it('allows creating a drama without a type', async () => {
|
||||
const response = await request(app.getHttpServer())
|
||||
.post('/api/admin/v1/dramas')
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.send({
|
||||
title: 'No Type Drama',
|
||||
coverUrl: 'https://cdn.example.com/no-type.jpg',
|
||||
status: 'draft',
|
||||
})
|
||||
.expect(201);
|
||||
|
||||
expect(response.body.data).toMatchObject({
|
||||
title: 'No Type Drama',
|
||||
type: '',
|
||||
channelName: '默认频道',
|
||||
});
|
||||
});
|
||||
|
||||
it('deletes a drama and its episodes from admin APIs', async () => {
|
||||
const drama = await request(app.getHttpServer())
|
||||
.post('/api/admin/v1/dramas')
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.send({
|
||||
title: 'Delete Drama Test',
|
||||
coverUrl: 'https://cdn.example.com/delete-drama.jpg',
|
||||
type: 'action',
|
||||
status: 'draft',
|
||||
})
|
||||
.expect(201);
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.post('/api/admin/v1/episodes')
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.send({
|
||||
dramaId: drama.body.data.id,
|
||||
episodeNo: 1,
|
||||
title: 'Delete Episode',
|
||||
coverUrl: 'https://cdn.example.com/delete-episode.jpg',
|
||||
durationSeconds: 300,
|
||||
status: 'draft',
|
||||
})
|
||||
.expect(201);
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.delete(`/api/admin/v1/dramas/${drama.body.data.id}`)
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.expect(200);
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.get(`/api/admin/v1/dramas/${drama.body.data.id}`)
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.expect(404);
|
||||
|
||||
const list = await request(app.getHttpServer())
|
||||
.get('/api/admin/v1/dramas')
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.query({ title: 'Delete Drama Test' })
|
||||
.expect(200);
|
||||
|
||||
expect(list.body.data.list).toEqual([]);
|
||||
});
|
||||
|
||||
it('supports assigning a drama to a selected channel and rejects missing channels', async () => {
|
||||
const channelResponse = await request(app.getHttpServer())
|
||||
.post('/api/admin/v1/channels')
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.send({
|
||||
name: '测试频道',
|
||||
code: 'test-channel',
|
||||
sortOrder: 10,
|
||||
})
|
||||
.expect(201);
|
||||
|
||||
const dramaResponse = await request(app.getHttpServer())
|
||||
.post('/api/admin/v1/dramas')
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.send({
|
||||
title: 'Selected Channel Drama',
|
||||
coverUrl: 'https://cdn.example.com/selected-channel.jpg',
|
||||
type: 'action',
|
||||
status: 'draft',
|
||||
channelId: channelResponse.body.data.id,
|
||||
})
|
||||
.expect(201);
|
||||
|
||||
expect(dramaResponse.body.data).toMatchObject({
|
||||
title: 'Selected Channel Drama',
|
||||
channelId: channelResponse.body.data.id,
|
||||
channelName: '测试频道',
|
||||
});
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.post('/api/admin/v1/dramas')
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.send({
|
||||
title: 'Missing Channel Drama',
|
||||
coverUrl: 'https://cdn.example.com/missing-channel.jpg',
|
||||
type: 'action',
|
||||
status: 'draft',
|
||||
channelId: 999999,
|
||||
})
|
||||
.expect(404);
|
||||
});
|
||||
|
||||
it('updates and deletes non-default channels but keeps the default channel protected', async () => {
|
||||
const created = await request(app.getHttpServer())
|
||||
.post('/api/admin/v1/channels')
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.send({
|
||||
name: '待编辑频道',
|
||||
code: 'editable-channel',
|
||||
sortOrder: 1,
|
||||
})
|
||||
.expect(201);
|
||||
|
||||
const updated = await request(app.getHttpServer())
|
||||
.patch(`/api/admin/v1/channels/${created.body.data.id}`)
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.send({
|
||||
name: '已编辑频道',
|
||||
sortOrder: 2,
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(updated.body.data).toMatchObject({
|
||||
id: created.body.data.id,
|
||||
name: '已编辑频道',
|
||||
sortOrder: 2,
|
||||
});
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.delete(`/api/admin/v1/channels/${created.body.data.id}`)
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.expect(200);
|
||||
|
||||
const channels = await request(app.getHttpServer())
|
||||
.get('/api/admin/v1/channels')
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.expect(200);
|
||||
|
||||
expect(channels.body.data.list).not.toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ code: 'editable-channel' }),
|
||||
]),
|
||||
);
|
||||
|
||||
const defaultChannel = channels.body.data.list.find(
|
||||
(channel: { code: string }) => channel.code === 'default',
|
||||
);
|
||||
await request(app.getHttpServer())
|
||||
.delete(`/api/admin/v1/channels/${defaultChannel.id}`)
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.expect(422);
|
||||
});
|
||||
|
||||
it('allows admin APIs to create a drama and episodes with release metadata', async () => {
|
||||
const dramaResponse = await request(app.getHttpServer())
|
||||
.post('/api/admin/v1/dramas')
|
||||
|
||||
Reference in New Issue
Block a user