training_data/apps/server/src/models/post/post.service.ts

347 lines
9.7 KiB
TypeScript
Raw Normal View History

2024-12-30 08:26:40 +08:00
import { Injectable } from '@nestjs/common';
import {
db,
Prisma,
UserProfile,
VisitType,
Post,
PostType,
RolePerms,
ResPerm,
ObjectType,
2025-02-06 16:32:52 +08:00
LectureType,
CourseMethodSchema,
2025-01-06 08:45:23 +08:00
} from '@nice/common';
2024-12-30 08:26:40 +08:00
import { MessageService } from '../message/message.service';
import { BaseService } from '../base/base.service';
import { DepartmentService } from '../department/department.service';
2025-02-21 08:14:04 +08:00
import { setCourseInfo, setPostRelation } from './utils';
2024-12-30 08:26:40 +08:00
import EventBus, { CrudOperation } from '@server/utils/event-bus';
2025-02-06 16:32:52 +08:00
import { BaseTreeService } from '../base/base.tree.service';
import { z } from 'zod';
2025-02-24 11:50:56 +08:00
import { DefaultArgs } from '@prisma/client/runtime/library';
2025-02-24 19:33:18 +08:00
import dayjs from 'dayjs';
2024-12-30 08:26:40 +08:00
@Injectable()
2025-02-06 16:32:52 +08:00
export class PostService extends BaseTreeService<Prisma.PostDelegate> {
2024-12-30 08:26:40 +08:00
constructor(
private readonly messageService: MessageService,
private readonly departmentService: DepartmentService,
) {
2025-02-06 16:32:52 +08:00
super(db, ObjectType.POST, 'postAncestry', true);
}
async createLecture(
lecture: z.infer<typeof CourseMethodSchema.createLecture>,
params: { staff?: UserProfile; tx?: Prisma.TransactionClient },
) {
const { sectionId, type, title, content, resourceIds = [] } = lecture;
const { staff, tx } = params;
return await this.create(
{
data: {
type: PostType.LECTURE,
parentId: sectionId,
content: content,
title: title,
authorId: params?.staff?.id,
2025-02-24 19:33:18 +08:00
updatedAt: dayjs().toDate(),
2025-02-06 16:32:52 +08:00
resources: {
connect: resourceIds.map((fileId) => ({ fileId })),
},
meta: {
type: type,
},
2025-02-24 12:19:09 +08:00
} as any,
2025-02-06 16:32:52 +08:00
},
{ tx },
);
}
async createSection(
section: z.infer<typeof CourseMethodSchema.createSection>,
params: {
staff?: UserProfile;
tx?: Prisma.TransactionClient;
},
) {
const { title, courseId, lectures } = section;
const { staff, tx } = params;
// Create section first
const createdSection = await this.create(
{
data: {
type: PostType.SECTION,
parentId: courseId,
title: title,
authorId: staff?.id,
2025-02-24 19:33:18 +08:00
updatedAt: dayjs().toDate(),
2025-02-24 12:19:09 +08:00
} as any,
2025-02-06 16:32:52 +08:00
},
{ tx },
);
// If lectures are provided, create them
if (lectures && lectures.length > 0) {
const lecturePromises = lectures.map((lecture) =>
this.createLecture(
{
sectionId: createdSection.id,
...lecture,
},
params,
),
);
// Create all lectures in parallel
await Promise.all(lecturePromises);
}
return createdSection;
}
async createCourse(
args: {
courseDetail: Prisma.PostCreateArgs;
},
params: { staff?: UserProfile; tx?: Prisma.TransactionClient },
) {
2025-02-24 20:53:22 +08:00
// const await db.post.findMany({
// where: {
// type: PostType.COURSE,
// },
// });
2025-02-24 19:56:43 +08:00
const { courseDetail } = args;
2025-02-06 16:32:52 +08:00
// If no transaction is provided, create a new one
if (!params.tx) {
return await db.$transaction(async (tx) => {
const courseParams = { ...params, tx };
// Create the course first
const createdCourse = await this.create(courseDetail, courseParams);
// If sections are provided, create them
return createdCourse;
});
}
// If transaction is provided, use it directly
console.log('courseDetail', courseDetail);
const createdCourse = await this.create(courseDetail, params);
// If sections are provided, create them
return createdCourse;
2024-12-30 08:26:40 +08:00
}
async create(
args: Prisma.PostCreateArgs,
2025-02-06 16:32:52 +08:00
params?: { staff?: UserProfile; tx?: Prisma.TransactionClient },
2024-12-30 08:26:40 +08:00
) {
2024-12-31 15:57:32 +08:00
args.data.authorId = params?.staff?.id;
2025-02-24 19:33:18 +08:00
args.data.updatedAt = dayjs().toDate();
2024-12-30 08:26:40 +08:00
const result = await super.create(args);
EventBus.emit('dataChanged', {
type: ObjectType.POST,
operation: CrudOperation.CREATED,
data: result,
});
return result;
}
async update(args: Prisma.PostUpdateArgs, staff?: UserProfile) {
2024-12-31 15:57:32 +08:00
args.data.authorId = staff?.id;
2025-02-24 19:33:18 +08:00
args.data.updatedAt = dayjs().toDate();
2025-01-03 09:24:46 +08:00
const result = await super.update(args);
EventBus.emit('dataChanged', {
type: ObjectType.POST,
operation: CrudOperation.UPDATED,
data: result,
});
2025-02-06 16:32:52 +08:00
return result;
}
async findFirst(
args?: Prisma.PostFindFirstArgs,
staff?: UserProfile,
clientIp?: string,
) {
const transDto = await this.wrapResult(
super.findFirst(args),
async (result) => {
if (result) {
await setPostRelation({ data: result, staff });
await this.setPerms(result, staff);
2025-02-21 08:14:04 +08:00
await setCourseInfo({ data: result });
2025-02-06 16:32:52 +08:00
}
2025-02-26 16:15:56 +08:00
// console.log(result);
2025-02-06 16:32:52 +08:00
return result;
},
);
return transDto;
2024-12-30 08:26:40 +08:00
}
2025-02-06 19:23:11 +08:00
// async findMany(args: Prisma.PostFindManyArgs, staff?: UserProfile) {
// if (!args.where) args.where = {};
// args.where.OR = await this.preFilter(args.where.OR, staff);
// return this.wrapResult(super.findMany(args), async (result) => {
// await Promise.all(
// result.map(async (item) => {
// await setPostRelation({ data: item, staff });
// await this.setPerms(item, staff);
// }),
// );
// return { ...result };
// });
// }
2024-12-30 08:26:40 +08:00
async findManyWithCursor(args: Prisma.PostFindManyArgs, staff?: UserProfile) {
2025-02-06 16:32:52 +08:00
if (!args.where) args.where = {};
2024-12-30 08:26:40 +08:00
args.where.OR = await this.preFilter(args.where.OR, staff);
return this.wrapResult(super.findManyWithCursor(args), async (result) => {
2025-02-06 16:32:52 +08:00
const { items } = result;
2024-12-30 08:26:40 +08:00
await Promise.all(
items.map(async (item) => {
await setPostRelation({ data: item, staff });
await this.setPerms(item, staff);
}),
);
return { ...result, items };
});
}
2025-02-24 12:19:09 +08:00
async findManyWithPagination(args: {
page?: number;
pageSize?: number;
where?: Prisma.PostWhereInput;
select?: Prisma.PostSelect<DefaultArgs>;
}): Promise<{
items: {
id: string;
type: string | null;
level: string | null;
state: string | null;
title: string | null;
subTitle: string | null;
content: string | null;
important: boolean | null;
domainId: string | null;
order: number | null;
duration: number | null;
rating: number | null;
createdAt: Date;
publishedAt: Date | null;
updatedAt: Date;
deletedAt: Date | null;
authorId: string | null;
parentId: string | null;
hasChildren: boolean | null;
meta: Prisma.JsonValue | null;
}[];
totalPages: number;
}> {
2025-02-24 19:33:18 +08:00
// super.updateOrder;
2025-02-24 12:19:09 +08:00
return super.findManyWithPagination(args);
}
2025-02-24 19:33:18 +08:00
async updateOrderByIds(ids: string[]) {
const posts = await db.post.findMany({
where: { id: { in: ids } },
select: { id: true, order: true },
});
const postMap = new Map(posts.map((post) => [post.id, post]));
const orderedPosts = ids
.map((id) => postMap.get(id))
.filter((post): post is { id: string; order: number } => !!post);
// 生成仅需更新的操作
const updates = orderedPosts
.map((post, index) => ({
id: post.id,
newOrder: index, // 按数组索引设置新顺序
currentOrder: post.order,
}))
.filter(({ newOrder, currentOrder }) => newOrder !== currentOrder)
.map(({ id, newOrder }) =>
db.post.update({
where: { id },
data: { order: newOrder },
}),
);
// 批量执行更新
return updates.length > 0 ? await db.$transaction(updates) : [];
}
2024-12-31 15:57:32 +08:00
protected async setPerms(data: Post, staff?: UserProfile) {
2024-12-30 08:26:40 +08:00
if (!staff) return;
const perms: ResPerm = {
delete: false,
};
const isMySelf = data?.authorId === staff?.id;
const isDomain = staff.domainId === data.domainId;
const setManagePermissions = (perms: ResPerm) => {
Object.assign(perms, {
delete: true,
// edit: true,
});
};
if (isMySelf) {
perms.delete = true;
// perms.edit = true;
}
staff.permissions.forEach((permission) => {
switch (permission) {
case RolePerms.MANAGE_ANY_POST:
setManagePermissions(perms);
break;
case RolePerms.MANAGE_DOM_POST:
if (isDomain) {
setManagePermissions(perms);
}
break;
}
});
Object.assign(data, { perms });
}
async preFilter(OR?: Prisma.PostWhereInput[], staff?: UserProfile) {
const preFilter = (await this.getPostPreFilter(staff)) || [];
const outOR = OR ? [...OR, ...preFilter].filter(Boolean) : preFilter;
return outOR?.length > 0 ? outOR : undefined;
}
2024-12-31 15:57:32 +08:00
async getPostPreFilter(staff?: UserProfile) {
2025-02-06 16:32:52 +08:00
if (!staff) return;
2024-12-30 08:26:40 +08:00
const { deptId, domainId } = staff;
if (
staff.permissions.includes(RolePerms.READ_ANY_POST) ||
staff.permissions.includes(RolePerms.MANAGE_ANY_POST)
) {
return undefined;
}
const parentDeptIds =
(await this.departmentService.getAncestorIds(staff.deptId)) || [];
const orCondition: Prisma.PostWhereInput[] = [
staff?.id && {
authorId: staff.id,
},
staff?.id && {
2024-12-30 13:44:30 +08:00
watchableStaffs: {
2024-12-30 08:26:40 +08:00
some: {
id: staff.id,
},
},
},
deptId && {
2024-12-30 13:44:30 +08:00
watchableDepts: {
2024-12-30 08:26:40 +08:00
some: {
id: {
in: parentDeptIds,
},
},
},
},
{
AND: [
{
2024-12-30 13:44:30 +08:00
watchableStaffs: {
none: {}, // 匹配 watchableStaffs 为空
2024-12-30 08:26:40 +08:00
},
},
{
2024-12-30 13:44:30 +08:00
watchableDepts: {
none: {}, // 匹配 watchableDepts 为空
2024-12-30 08:26:40 +08:00
},
},
],
},
].filter(Boolean);
if (orCondition?.length > 0) return orCondition;
return undefined;
}
}