作品审核: - 批量通过/批量拒绝 + 撤销审核机制 - 默认筛选待审核,表格加描述预览+审核时间列 - 详情Drawer加上一个/下一个导航,审核后自动跳下一个 - 操作日志时间线展示,筛选下拉自动查询 作品管理: - 修复筛选/排序失效,新增推荐中筛选 - 下架改为弹窗选择原因,取消推荐二次确认 - 详情Drawer补全描述/标签/操作按钮/操作日志 - 统计卡片可点击筛选,下架自动取消推荐 标签管理: - 按分类分组卡片式展示,分类改为下拉选择 - 新增标签颜色字段(预设色+自定义) - 上移/下移排序按钮,使用次数可点击跳转作品管理 - 新增/编辑时实时预览用户端标签效果 广场推荐: - 新增推荐作品列表接口 GET /public/gallery/recommended - 广场顶部新增「编辑推荐」横向滚动栏 文档更新:内容管理设计文档补充实施记录,UGC开发计划P1-1标记已完成 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
122 lines
3.3 KiB
TypeScript
122 lines
3.3 KiB
TypeScript
import {
|
|
Controller, Get, Post, Param, Query, Body,
|
|
ParseIntPipe, UseGuards, Request,
|
|
} from '@nestjs/common';
|
|
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
|
import { ContentReviewService } from './content-review.service';
|
|
|
|
@Controller('content-review')
|
|
@UseGuards(JwtAuthGuard)
|
|
export class ContentReviewController {
|
|
constructor(private readonly reviewService: ContentReviewService) {}
|
|
|
|
// ========== 作品审核 ==========
|
|
|
|
@Get('works/stats')
|
|
getWorkStats() {
|
|
return this.reviewService.getWorkStats();
|
|
}
|
|
|
|
@Get('works')
|
|
getWorkQueue(
|
|
@Query('page') page?: string,
|
|
@Query('pageSize') pageSize?: string,
|
|
@Query('status') status?: string,
|
|
@Query('keyword') keyword?: string,
|
|
@Query('startTime') startTime?: string,
|
|
@Query('endTime') endTime?: string,
|
|
@Query('sortBy') sortBy?: string,
|
|
@Query('isRecommended') isRecommended?: string,
|
|
) {
|
|
return this.reviewService.getWorkQueue({
|
|
page: page ? parseInt(page) : 1,
|
|
pageSize: pageSize ? parseInt(pageSize) : 10,
|
|
status,
|
|
keyword,
|
|
startTime,
|
|
endTime,
|
|
sortBy,
|
|
isRecommended: isRecommended === '1',
|
|
});
|
|
}
|
|
|
|
@Get('works/:id')
|
|
getWorkDetail(@Param('id', ParseIntPipe) id: number) {
|
|
return this.reviewService.getWorkDetail(id);
|
|
}
|
|
|
|
@Post('works/batch-approve')
|
|
batchApprove(@Request() req, @Body() dto: { ids: number[] }) {
|
|
return this.reviewService.batchApprove(dto.ids || [], req.user.userId);
|
|
}
|
|
|
|
@Post('works/batch-reject')
|
|
batchReject(@Request() req, @Body() dto: { ids: number[]; reason: string }) {
|
|
return this.reviewService.batchReject(dto.ids || [], req.user.userId, dto.reason);
|
|
}
|
|
|
|
@Post('works/:id/approve')
|
|
approveWork(
|
|
@Param('id', ParseIntPipe) id: number,
|
|
@Request() req,
|
|
@Body() dto: { note?: string },
|
|
) {
|
|
return this.reviewService.approve(id, req.user.userId, dto.note);
|
|
}
|
|
|
|
@Post('works/:id/reject')
|
|
rejectWork(
|
|
@Param('id', ParseIntPipe) id: number,
|
|
@Request() req,
|
|
@Body() dto: { reason: string; note?: string },
|
|
) {
|
|
return this.reviewService.reject(id, req.user.userId, dto.reason, dto.note);
|
|
}
|
|
|
|
@Post('works/:id/revoke')
|
|
revokeWork(@Param('id', ParseIntPipe) id: number, @Request() req) {
|
|
return this.reviewService.revoke(id, req.user.userId);
|
|
}
|
|
|
|
@Post('works/:id/takedown')
|
|
takedownWork(
|
|
@Param('id', ParseIntPipe) id: number,
|
|
@Request() req,
|
|
@Body() dto: { reason: string },
|
|
) {
|
|
return this.reviewService.takedown(id, req.user.userId, dto.reason);
|
|
}
|
|
|
|
@Post('works/:id/restore')
|
|
restoreWork(@Param('id', ParseIntPipe) id: number, @Request() req) {
|
|
return this.reviewService.restore(id, req.user.userId);
|
|
}
|
|
|
|
@Post('works/:id/recommend')
|
|
toggleRecommend(@Param('id', ParseIntPipe) id: number) {
|
|
return this.reviewService.toggleRecommend(id);
|
|
}
|
|
|
|
// ========== 作品管理 ==========
|
|
|
|
@Get('management/stats')
|
|
getManagementStats() {
|
|
return this.reviewService.getManagementStats();
|
|
}
|
|
|
|
// ========== 审核日志 ==========
|
|
|
|
@Get('logs')
|
|
getLogs(
|
|
@Query('page') page?: string,
|
|
@Query('pageSize') pageSize?: string,
|
|
@Query('workId') workId?: string,
|
|
) {
|
|
return this.reviewService.getLogs({
|
|
page: page ? parseInt(page) : 1,
|
|
pageSize: pageSize ? parseInt(pageSize) : 20,
|
|
workId: workId ? parseInt(workId) : undefined,
|
|
});
|
|
}
|
|
}
|