作品审核: - 批量通过/批量拒绝 + 撤销审核机制 - 默认筛选待审核,表格加描述预览+审核时间列 - 详情Drawer加上一个/下一个导航,审核后自动跳下一个 - 操作日志时间线展示,筛选下拉自动查询 作品管理: - 修复筛选/排序失效,新增推荐中筛选 - 下架改为弹窗选择原因,取消推荐二次确认 - 详情Drawer补全描述/标签/操作按钮/操作日志 - 统计卡片可点击筛选,下架自动取消推荐 标签管理: - 按分类分组卡片式展示,分类改为下拉选择 - 新增标签颜色字段(预设色+自定义) - 上移/下移排序按钮,使用次数可点击跳转作品管理 - 新增/编辑时实时预览用户端标签效果 广场推荐: - 新增推荐作品列表接口 GET /public/gallery/recommended - 广场顶部新增「编辑推荐」横向滚动栏 文档更新:内容管理设计文档补充实施记录,UGC开发计划P1-1标记已完成 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
137 lines
3.7 KiB
TypeScript
137 lines
3.7 KiB
TypeScript
import { Injectable, NotFoundException } from '@nestjs/common';
|
|
import { PrismaService } from '../prisma/prisma.service';
|
|
|
|
@Injectable()
|
|
export class GalleryService {
|
|
constructor(private prisma: PrismaService) {}
|
|
|
|
/** 作品广场列表(已审核通过的公开作品) */
|
|
async getGalleryList(params: {
|
|
page?: number;
|
|
pageSize?: number;
|
|
tagId?: number;
|
|
category?: string;
|
|
sortBy?: string;
|
|
keyword?: string;
|
|
}) {
|
|
const { page = 1, pageSize = 12, tagId, category, sortBy = 'latest', keyword } = params;
|
|
const skip = (page - 1) * pageSize;
|
|
|
|
const where: any = {
|
|
status: 'published',
|
|
visibility: 'public',
|
|
isDeleted: 0,
|
|
};
|
|
|
|
if (keyword) {
|
|
where.OR = [
|
|
{ title: { contains: keyword } },
|
|
{ description: { contains: keyword } },
|
|
];
|
|
}
|
|
|
|
if (tagId) {
|
|
where.tags = { some: { tagId } };
|
|
}
|
|
|
|
if (category) {
|
|
where.tags = {
|
|
...where.tags,
|
|
some: { ...where.tags?.some, tag: { category } },
|
|
};
|
|
}
|
|
|
|
const orderBy: any = sortBy === 'hot'
|
|
? [{ likeCount: 'desc' }, { viewCount: 'desc' }]
|
|
: sortBy === 'views'
|
|
? { viewCount: 'desc' }
|
|
: { publishTime: 'desc' };
|
|
|
|
const [list, total] = await Promise.all([
|
|
this.prisma.userWork.findMany({
|
|
where,
|
|
skip,
|
|
take: pageSize,
|
|
orderBy,
|
|
include: {
|
|
creator: { select: { id: true, nickname: true, avatar: true } },
|
|
tags: { include: { tag: { select: { id: true, name: true } } } },
|
|
_count: { select: { pages: true, likes: true, comments: true } },
|
|
},
|
|
}),
|
|
this.prisma.userWork.count({ where }),
|
|
]);
|
|
|
|
return { list, total, page, pageSize };
|
|
}
|
|
|
|
/** 广场作品详情(增加浏览量) */
|
|
async getGalleryDetail(id: number) {
|
|
const work = await this.prisma.userWork.findFirst({
|
|
where: { id, status: 'published', visibility: 'public', isDeleted: 0 },
|
|
include: {
|
|
pages: { orderBy: { pageNo: 'asc' } },
|
|
creator: { select: { id: true, nickname: true, avatar: true, username: true } },
|
|
tags: { include: { tag: true } },
|
|
_count: { select: { pages: true, likes: true, favorites: true, comments: true } },
|
|
},
|
|
});
|
|
|
|
if (!work) throw new NotFoundException('作品不存在');
|
|
|
|
// 异步增加浏览量
|
|
this.prisma.userWork.update({
|
|
where: { id },
|
|
data: { viewCount: { increment: 1 } },
|
|
}).catch(() => {});
|
|
|
|
return work;
|
|
}
|
|
|
|
/** 推荐作品列表 */
|
|
async getRecommendedWorks(limit = 10) {
|
|
return this.prisma.userWork.findMany({
|
|
where: {
|
|
isRecommended: true,
|
|
status: 'published',
|
|
visibility: 'public',
|
|
isDeleted: 0,
|
|
},
|
|
take: limit,
|
|
orderBy: [{ likeCount: 'desc' }, { publishTime: 'desc' }],
|
|
include: {
|
|
creator: { select: { id: true, nickname: true, avatar: true } },
|
|
},
|
|
});
|
|
}
|
|
|
|
/** 某用户的公开作品列表 */
|
|
async getUserPublicWorks(userId: number, params: { page?: number; pageSize?: number }) {
|
|
const { page = 1, pageSize = 12 } = params;
|
|
const skip = (page - 1) * pageSize;
|
|
|
|
const where = {
|
|
userId,
|
|
status: 'published',
|
|
visibility: 'public',
|
|
isDeleted: 0,
|
|
};
|
|
|
|
const [list, total] = await Promise.all([
|
|
this.prisma.userWork.findMany({
|
|
where,
|
|
skip,
|
|
take: pageSize,
|
|
orderBy: { publishTime: 'desc' },
|
|
include: {
|
|
tags: { include: { tag: { select: { id: true, name: true } } } },
|
|
_count: { select: { pages: true, likes: true } },
|
|
},
|
|
}),
|
|
this.prisma.userWork.count({ where }),
|
|
]);
|
|
|
|
return { list, total, page, pageSize };
|
|
}
|
|
}
|