This commit is contained in:
ditiqi 2025-02-27 23:50:07 +08:00
parent 9d6e9136cf
commit 4ff101ab7b
12 changed files with 63 additions and 23 deletions

View File

@ -200,6 +200,8 @@ export class PostService extends BaseTreeService<Prisma.PostDelegate> {
rating: number | null;
createdAt: Date;
views: number;
hates: number;
likes: number;
publishedAt: Date | null;
updatedAt: Date;
deletedAt: Date | null;

View File

@ -15,13 +15,13 @@ export class VisitRouter {
private readonly visitService: VisitService,
) {}
router = this.trpc.router({
create: this.trpc.protectProcedure
create: this.trpc.procedure
.input(VisitCreateArgsSchema)
.mutation(async ({ ctx, input }) => {
const { staff } = ctx;
return await this.visitService.create(input, staff);
}),
createMany: this.trpc.protectProcedure
createMany: this.trpc.procedure
.input(z.array(VisitCreateManyInputSchema))
.mutation(async ({ ctx, input }) => {
const { staff } = ctx;

View File

@ -8,7 +8,7 @@ import {
export async function updateTotalCourseViewCount(type: VisitType) {
const posts = await db.post.findMany({
where: {
type: { in: [PostType.COURSE, PostType.LECTURE] },
// type: { in: [PostType.COURSE, PostType.LECTURE,] },
deletedAt: null,
},
select: { id: true, type: true },
@ -66,27 +66,34 @@ export async function updatePostViewCount(id: string, type: VisitType) {
where: { id },
select: { id: true, meta: true, type: true },
});
console.log(post?.type);
console.log('updatePostViewCount');
const metaFieldMap = {
[VisitType.READED]: 'views',
[VisitType.LIKE]: 'likes',
[VisitType.HATE]: 'hates',
};
if (post?.type === PostType.LECTURE) {
const course = await db.postAncestry.findFirst({
const courseAncestry = await db.postAncestry.findFirst({
where: {
descendantId: post?.id,
ancestor: {
type: PostType.COURSE,
},
},
select: { id: true },
select: { id: true, ancestorId: true },
});
const lectures = await db.postAncestry.findMany({
const course = { id: courseAncestry.ancestorId };
const lecturesAncestry = await db.postAncestry.findMany({
where: { ancestorId: course.id, descendant: { type: PostType.LECTURE } },
select: {
id: true,
descendantId: true,
},
});
const lectures = lecturesAncestry.map((ancestry) => ({
id: ancestry.descendantId,
}));
const courseViews = await db.visit.aggregate({
_sum: {
views: true,
@ -98,9 +105,11 @@ export async function updatePostViewCount(id: string, type: VisitType) {
type: type,
},
});
console.log(courseViews);
await db.post.update({
where: { id: course.id },
data: {
[metaFieldMap[type]]: courseViews._sum.views || 0,
meta: {
...((post?.meta as any) || {}),
[metaFieldMap[type]]: courseViews._sum.views || 0,
@ -117,9 +126,11 @@ export async function updatePostViewCount(id: string, type: VisitType) {
type: type,
},
});
console.log('totalViews', totalViews);
await db.post.update({
where: { id },
data: {
[metaFieldMap[type]]: totalViews._sum.views || 0,
meta: {
...((post?.meta as any) || {}),
[metaFieldMap[type]]: totalViews._sum.views || 0,

View File

@ -24,7 +24,7 @@ const DeptInfo = ({ post }: { post: PostDto }) => {
<div className="flex items-center gap-2">
<span className="gap-1 text-xs font-medium text-gray-500 flex items-center">
<EyeOutlined />
{`${post?.meta?.views || 0}`}
{`${post?.views || 0}`}
</span>
{post?.studentIds && post?.studentIds?.length > 0 && (
<span className="gap-1 text-xs font-medium text-gray-500 flex items-center">

View File

@ -80,6 +80,7 @@ export function CourseDetailProvider({
useEffect(() => {
if (lecture?.id) {
console.log(123);
read.mutateAsync({
data: {
visitorId: user?.id || null,

View File

@ -36,11 +36,11 @@ export const CourseDetailDescription: React.FC = () => {
{!selectedLectureId && (
<div className="relative mb-4 overflow-hidden flex justify-center items-center">
{
<Image
src={course.meta.thumbnail}
preview={false}
className="w-full h-full object-cover z-0"
fallback="/placeholder.webp"
<div
className="w-full rounded-xl aspect-video bg-cover bg-center z-0"
style={{
backgroundImage: `url(${course?.meta?.thumbnail || "/placeholder.webp"})`,
}}
/>
}
<div
@ -59,7 +59,7 @@ export const CourseDetailDescription: React.FC = () => {
});
}
}}
className="w-full h-full absolute top-0 z-10 bg-[rgba(0,0,0,0.3)] transition-all duration-300 ease-in-out hover:bg-[rgba(0,0,0,0.7)] cursor-pointer group">
className="absolute rounded-xl top-0 left-0 right-0 bottom-0 z-10 bg-[rgba(0,0,0,0.3)] transition-all duration-300 ease-in-out hover:bg-[rgba(0,0,0,0.7)] cursor-pointer group">
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-white text-4xl z-10 opacity-0 group-hover:opacity-100 transition-opacity duration-300">
</div>

View File

@ -12,11 +12,12 @@ import dayjs from "dayjs";
import CourseOperationBtns from "./JoinLearingButton";
export default function CourseDetailTitle() {
const { course } = useContext(CourseDetailContext);
const { course, lecture, selectedLectureId } =
useContext(CourseDetailContext);
return (
<div className="flex justify-center flex-col items-center gap-2 w-full my-2 px-6">
<div className="flex justify-start w-full text-2xl font-bold">
{course?.title}
{!selectedLectureId ? course?.title : lecture?.title}
</div>
<div className="text-gray-600 flex w-full justify-start items-center gap-5">
{course?.author?.showname && (
@ -36,15 +37,25 @@ export default function CourseDetailTitle() {
<div className="flex gap-1">
<CalendarOutlined></CalendarOutlined>
{"发布于:"}
{dayjs(course?.createdAt).format("YYYY年M月D日")}
{dayjs(
!selectedLectureId
? course?.createdAt
: lecture?.createdAt
).format("YYYY年M月D日")}
</div>
<div className="flex gap-1">
{"最后更新:"}
{dayjs(course?.updatedAt).format("YYYY年M月D日")}
{dayjs(
!selectedLectureId
? course?.updatedAt
: lecture?.updatedAt
).format("YYYY年M月D日")}
</div>
<div className="flex gap-1">
<EyeOutlined></EyeOutlined>
<div>{`观看次数${course?.meta?.views || 0}`}</div>
<div>{`观看次数${
!selectedLectureId ? course?.views : lecture?.views || 0
}`}</div>
</div>
<div className="flex gap-1">
<BookOutlined />

View File

@ -45,7 +45,9 @@ export const LectureItem: React.FC<LectureItemProps> = ({
</div>
)}
<div className="flex-grow flex justify-between items-center w-2/3 realative">
<h4 className="font-medium text-gray-800 w-4/5">{lecture.title}</h4>
<h4 className="font-medium text-gray-800 w-4/5">
{lecture.title}
</h4>
{lecture.subTitle && (
<span className="text-sm text-gray-500 mt-1 w-4/5">
{lecture.subTitle}
@ -53,7 +55,9 @@ export const LectureItem: React.FC<LectureItemProps> = ({
)}
<div className="text-gray-500 whitespace-normal">
<EyeOutlined></EyeOutlined>
<span className="ml-2">{lecture?.meta?.views ? lecture?.meta?.views : 0}</span>
<span className="ml-2">
{lecture?.views ? lecture?.views : 0}
</span>
</div>
</div>
</div>

View File

@ -101,13 +101,17 @@ export function CourseFormProvider({
terms:
termIds?.length > 0
? {
set: termIds.map((id) => ({ id })), // 转换成 connect 格式
[editId ? "set" : "connect"]: termIds.map((id) => ({
id,
})), // 转换成 connect 格式
}
: undefined,
depts:
deptIds?.length > 0
? {
set: deptIds.map((id) => ({ id })),
[editId ? "set" : "connect"]: deptIds.map((id) => ({
id,
})),
}
: undefined,
};

View File

@ -115,7 +115,7 @@ export const SortableLecture: React.FC<SortableLectureProps> = ({
resources:
[videoUrlId, ...fileIds].filter(Boolean)?.length > 0
? {
connect: [videoUrlId, ...fileIds]
set: [videoUrlId, ...fileIds]
.filter(Boolean)
.map((fileId) => ({
fileId,

View File

@ -207,6 +207,8 @@ model Post {
students Staff[] @relation("post_student")
depts Department[] @relation("post_dept")
views Int @default(0) @map("views")
hates Int @default(0) @map("hates")
likes Int @default(0) @map("likes")
// 索引
// 日期时间类型字段
createdAt DateTime @default(now()) @map("created_at")

View File

@ -45,11 +45,13 @@ export const postDetailSelect: Prisma.PostSelect = {
},
},
meta: true,
views: true,
};
export const postUnDetailSelect: Prisma.PostSelect = {
id: true,
type: true,
title: true,
views: true,
parent: true,
parentId: true,
content: true,
@ -79,6 +81,7 @@ export const messageDetailSelect: Prisma.MessageSelect = {
id: true,
sender: true,
content: true,
title: true,
url: true,
option: true,
@ -88,6 +91,7 @@ export const courseDetailSelect: Prisma.PostSelect = {
id: true,
title: true,
subTitle: true,
views: true,
type: true,
author: true,
authorId: true,
@ -124,6 +128,7 @@ export const lectureDetailSelect: Prisma.PostSelect = {
subTitle: true,
content: true,
resources: true,
views: true,
createdAt: true,
updatedAt: true,
// 关联表选择