From 7e482f8c53c4417a60854283f60f4ab95f5bd411 Mon Sep 17 00:00:00 2001 From: ditiqi Date: Mon, 24 Feb 2025 19:33:18 +0800 Subject: [PATCH] add --- apps/server/src/models/base/base.type.ts | 56 ++++++++++--------- apps/server/src/models/post/post.router.ts | 18 ++++++ apps/server/src/models/post/post.service.ts | 35 ++++++++++++ .../queue/models/post/post.queue.service.ts | 1 - apps/server/src/tasks/init/gendev.service.ts | 54 +++++++++++++++++- .../editor/context/CourseEditorContext.tsx | 2 +- .../form/CourseContentForm/LectureList.tsx | 14 ++++- .../course/editor/form/CourseGoalForm.tsx | 19 ------- .../course/editor/form/CourseSettingForm.tsx | 26 --------- packages/client/src/api/hooks/useEntity.ts | 3 + packages/common/prisma/schema.prisma | 3 + 11 files changed, 152 insertions(+), 79 deletions(-) delete mode 100755 apps/web/src/components/models/course/editor/form/CourseGoalForm.tsx delete mode 100755 apps/web/src/components/models/course/editor/form/CourseSettingForm.tsx diff --git a/apps/server/src/models/base/base.type.ts b/apps/server/src/models/base/base.type.ts index 878dffe..01254df 100755 --- a/apps/server/src/models/base/base.type.ts +++ b/apps/server/src/models/base/base.type.ts @@ -1,25 +1,27 @@ -import { db, Prisma, PrismaClient } from "@nice/common"; +import { db, Prisma, PrismaClient } from '@nice/common'; export type Operations = - | 'aggregate' - | 'count' - | 'create' - | 'createMany' - | 'delete' - | 'deleteMany' - | 'findFirst' - | 'findMany' - | 'findUnique' - | 'update' - | 'updateMany' - | 'upsert'; -export type DelegateFuncs = { [K in Operations]: (args: any) => Promise } + | 'aggregate' + | 'count' + | 'create' + | 'createMany' + | 'delete' + | 'deleteMany' + | 'findFirst' + | 'findMany' + | 'findUnique' + | 'update' + | 'updateMany' + | 'upsert'; +export type DelegateFuncs = { + [K in Operations]: (args: any) => Promise; +}; export type DelegateArgs = { - [K in keyof T]: T[K] extends (args: infer A) => Promise ? A : never; + [K in keyof T]: T[K] extends (args: infer A) => Promise ? A : never; }; export type DelegateReturnTypes = { - [K in keyof T]: T[K] extends (args: any) => Promise ? R : never; + [K in keyof T]: T[K] extends (args: any) => Promise ? R : never; }; export type WhereArgs = T extends { where?: infer W } ? W : never; @@ -28,17 +30,17 @@ export type DataArgs = T extends { data: infer D } ? D : never; export type IncludeArgs = T extends { include: infer I } ? I : never; export type OrderByArgs = T extends { orderBy: infer O } ? O : never; export type UpdateOrderArgs = { - id: string - overId: string -} + id: string; + overId: string; +}; export interface FindManyWithCursorType { - cursor?: string; - limit?: number; - where?: WhereArgs['findUnique']>; - select?: SelectArgs['findUnique']>; - orderBy?: OrderByArgs['findMany']> + cursor?: string; + limit?: number; + where?: WhereArgs['findUnique']>; + select?: SelectArgs['findUnique']>; + orderBy?: OrderByArgs['findMany']>; } export type TransactionType = Omit< - PrismaClient, - '$connect' | '$disconnect' | '$on' | '$transaction' | '$use' | '$extends' ->; \ No newline at end of file + PrismaClient, + '$connect' | '$disconnect' | '$on' | '$transaction' | '$use' | '$extends' +>; diff --git a/apps/server/src/models/post/post.router.ts b/apps/server/src/models/post/post.router.ts index 458564d..2c7ae56 100755 --- a/apps/server/src/models/post/post.router.ts +++ b/apps/server/src/models/post/post.router.ts @@ -3,8 +3,10 @@ import { TrpcService } from '@server/trpc/trpc.service'; import { CourseMethodSchema, Prisma } from '@nice/common'; import { PostService } from './post.service'; import { z, ZodType } from 'zod'; +import { UpdateOrderArgs } from '../base/base.type'; const PostCreateArgsSchema: ZodType = z.any(); const PostUpdateArgsSchema: ZodType = z.any(); +const PostUpdateOrderArgsSchema: ZodType = z.any(); const PostFindFirstArgsSchema: ZodType = z.any(); const PostFindManyArgsSchema: ZodType = z.any(); const PostDeleteManyArgsSchema: ZodType = z.any(); @@ -107,5 +109,21 @@ export class PostRouter { .query(async ({ input }) => { return await this.postService.findManyWithPagination(input); }), + updateOrder: this.trpc.protectProcedure + .input(PostUpdateOrderArgsSchema) + .mutation(async ({ ctx, input }) => { + const { staff } = ctx; + return await this.postService.updateOrder(input); + }), + updateOrderByIds: this.trpc.protectProcedure + .input( + z.object({ + ids: z.array(z.string()), + }), + ) + .mutation(async ({ ctx, input }) => { + const { staff } = ctx; + return await this.postService.updateOrderByIds(input.ids); + }), }); } diff --git a/apps/server/src/models/post/post.service.ts b/apps/server/src/models/post/post.service.ts index 30d56e8..e260698 100755 --- a/apps/server/src/models/post/post.service.ts +++ b/apps/server/src/models/post/post.service.ts @@ -20,6 +20,7 @@ import EventBus, { CrudOperation } from '@server/utils/event-bus'; import { BaseTreeService } from '../base/base.tree.service'; import { z } from 'zod'; import { DefaultArgs } from '@prisma/client/runtime/library'; +import dayjs from 'dayjs'; @Injectable() export class PostService extends BaseTreeService { @@ -43,6 +44,7 @@ export class PostService extends BaseTreeService { content: content, title: title, authorId: params?.staff?.id, + updatedAt: dayjs().toDate(), resources: { connect: resourceIds.map((fileId) => ({ fileId })), }, @@ -71,6 +73,7 @@ export class PostService extends BaseTreeService { parentId: courseId, title: title, authorId: staff?.id, + updatedAt: dayjs().toDate(), } as any, }, { tx }, @@ -152,6 +155,7 @@ export class PostService extends BaseTreeService { params?: { staff?: UserProfile; tx?: Prisma.TransactionClient }, ) { args.data.authorId = params?.staff?.id; + args.data.updatedAt = dayjs().toDate(); const result = await super.create(args); EventBus.emit('dataChanged', { type: ObjectType.POST, @@ -162,6 +166,7 @@ export class PostService extends BaseTreeService { } async update(args: Prisma.PostUpdateArgs, staff?: UserProfile) { args.data.authorId = staff?.id; + args.data.updatedAt = dayjs().toDate(); const result = await super.update(args); EventBus.emit('dataChanged', { type: ObjectType.POST, @@ -246,8 +251,38 @@ export class PostService extends BaseTreeService { }[]; totalPages: number; }> { + // super.updateOrder; return super.findManyWithPagination(args); } + + 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) : []; + } protected async setPerms(data: Post, staff?: UserProfile) { if (!staff) return; const perms: ResPerm = { diff --git a/apps/server/src/queue/models/post/post.queue.service.ts b/apps/server/src/queue/models/post/post.queue.service.ts index 452076c..f7370df 100644 --- a/apps/server/src/queue/models/post/post.queue.service.ts +++ b/apps/server/src/queue/models/post/post.queue.service.ts @@ -37,5 +37,4 @@ export class PostQueueService implements OnModuleInit { debounce: { id: `${QueueJobType.UPDATE_POST_STATE}_${data.id}` }, }); } - } diff --git a/apps/server/src/tasks/init/gendev.service.ts b/apps/server/src/tasks/init/gendev.service.ts index d7c3e01..a2f7756 100755 --- a/apps/server/src/tasks/init/gendev.service.ts +++ b/apps/server/src/tasks/init/gendev.service.ts @@ -7,6 +7,7 @@ import { Department, getRandomElement, getRandomElements, + PostType, Staff, TaxonomySlug, Term, @@ -14,6 +15,7 @@ import { import EventBus from '@server/utils/event-bus'; import { capitalizeFirstLetter, DevDataCounts, getCounts } from './utils'; import { StaffService } from '@server/models/staff/staff.service'; +import dayjs from 'dayjs'; @Injectable() export class GenDevService { private readonly logger = new Logger(GenDevService.name); @@ -22,7 +24,7 @@ export class GenDevService { terms: Record = { [TaxonomySlug.CATEGORY]: [], [TaxonomySlug.TAG]: [], - [TaxonomySlug.LEVEL]: [] + [TaxonomySlug.LEVEL]: [], }; depts: Department[] = []; domains: Department[] = []; @@ -35,7 +37,7 @@ export class GenDevService { private readonly departmentService: DepartmentService, private readonly staffService: StaffService, private readonly termService: TermService, - ) { } + ) {} async genDataEvent() { EventBus.emit('genDataEvent', { type: 'start' }); try { @@ -58,6 +60,7 @@ export class GenDevService { if (this.counts.termCount === 0) { this.logger.log('Generate terms'); await this.createTerms(null, TaxonomySlug.CATEGORY, depth, count); + await this.createLevelTerm(); const domains = this.depts.filter((item) => item.isDomain); for (const domain of domains) { await this.createTerms(domain, TaxonomySlug.CATEGORY, depth, count); @@ -174,7 +177,54 @@ export class GenDevService { } } } + private async createLevelTerm() { + try { + // 1. 获取分类时添加异常处理 + const taxLevel = await db.taxonomy.findFirst({ + where: { slug: TaxonomySlug.LEVEL }, + }); + if (!taxLevel) { + throw new Error('LEVEL taxonomy not found'); + } + + // 2. 使用数组定义初始化数据 + 名称去重 + const termsToCreate = [ + { name: '初级', taxonomyId: taxLevel.id }, + { name: '中级', taxonomyId: taxLevel.id }, + { name: '高级', taxonomyId: taxLevel.id }, // 改为高级更合理 + ]; + await this.termService.createMany({ + data: termsToCreate, + }); + console.log('created level terms'); + } catch (error) { + console.error('Failed to create level terms:', error); + throw error; // 向上抛出错误供上层处理 + } + } + private async createCourse( + title: string, + deptId: string, + cateId: string, + levelId: string, + ) { + const course = await db.post.create({ + data: { + type: PostType.COURSE, + title: title, + updatedAt: dayjs().toDate(), + depts: { + connect: { + id: deptId, + }, + }, + terms:{ + connect:[cateId,levelId].map + } + }, + }); + } private async createDepartment( name: string, parentId?: string | null, diff --git a/apps/web/src/components/models/course/editor/context/CourseEditorContext.tsx b/apps/web/src/components/models/course/editor/context/CourseEditorContext.tsx index 913bf34..82f4d4d 100755 --- a/apps/web/src/components/models/course/editor/context/CourseEditorContext.tsx +++ b/apps/web/src/components/models/course/editor/context/CourseEditorContext.tsx @@ -115,7 +115,7 @@ export function CourseFormProvider({ }); message.success("课程更新成功!"); } else { - const result = await createCourse.mutateAsync({ + const result = await create.mutateAsync({ courseDetail: { data: { title: formattedValues.title || "12345", diff --git a/apps/web/src/components/models/course/editor/form/CourseContentForm/LectureList.tsx b/apps/web/src/components/models/course/editor/form/CourseContentForm/LectureList.tsx index dac0143..7bad0ce 100755 --- a/apps/web/src/components/models/course/editor/form/CourseContentForm/LectureList.tsx +++ b/apps/web/src/components/models/course/editor/form/CourseContentForm/LectureList.tsx @@ -48,7 +48,7 @@ export const LectureList: React.FC = ({ field, sectionId, }) => { - const { softDeleteByIds } = usePost(); + const { softDeleteByIds, updateOrderByIds } = usePost(); const { data: lectures = [], isLoading } = ( api.post.findMany as any ).useQuery( @@ -87,11 +87,19 @@ export const LectureList: React.FC = ({ const handleDragEnd = (event: DragEndEvent) => { const { active, over } = event; if (!over || active.id === over.id) return; - + // updateOrder.mutateAsync({ + // id: active.id, + // overId: over.id, + // }); + let newItems = []; setItems((items) => { const oldIndex = items.findIndex((item) => item.id === active.id); const newIndex = items.findIndex((item) => item.id === over.id); - return arrayMove(items, oldIndex, newIndex); + newItems = arrayMove(items, oldIndex, newIndex); + return newItems; + }); + updateOrderByIds.mutateAsync({ + ids: newItems.map((item) => item.id), }); }; diff --git a/apps/web/src/components/models/course/editor/form/CourseGoalForm.tsx b/apps/web/src/components/models/course/editor/form/CourseGoalForm.tsx deleted file mode 100755 index 28fbe6b..0000000 --- a/apps/web/src/components/models/course/editor/form/CourseGoalForm.tsx +++ /dev/null @@ -1,19 +0,0 @@ -// import { FormArrayField } from "@web/src/components/common/form/FormArrayField"; -// import { useFormContext } from "react-hook-form"; -// import { CourseFormData } from "../context/CourseEditorContext"; -// import InputList from "@web/src/components/common/input/InputList"; -// import { useState } from "react"; -// import { Form } from "antd"; - -// export function CourseGoalForm() { -// return ( -//
-// -// -// -// -// -// -//
-// ); -// } diff --git a/apps/web/src/components/models/course/editor/form/CourseSettingForm.tsx b/apps/web/src/components/models/course/editor/form/CourseSettingForm.tsx deleted file mode 100755 index 6a1b1a9..0000000 --- a/apps/web/src/components/models/course/editor/form/CourseSettingForm.tsx +++ /dev/null @@ -1,26 +0,0 @@ -// import AvatarUploader from "@web/src/components/common/uploader/AvatarUploader"; -// import { Form, Input } from "antd"; - -// export default function CourseSettingForm() { -// return ( -//
-// -// { -// console.log(value); -// }} -// > -// -//
-// ) -// } \ No newline at end of file diff --git a/packages/client/src/api/hooks/useEntity.ts b/packages/client/src/api/hooks/useEntity.ts index 2b1dbfa..175b342 100755 --- a/packages/client/src/api/hooks/useEntity.ts +++ b/packages/client/src/api/hooks/useEntity.ts @@ -113,5 +113,8 @@ export function useEntity( T, "updateOrder" >, // 更新实体顺序的 mutation 函数 + updateOrderByIds: createMutationHandler( + "updateOrderByIds" + ) as MutationResult, // 更新实体顺序的 mutation 函数 }; } diff --git a/packages/common/prisma/schema.prisma b/packages/common/prisma/schema.prisma index c65a392..759afa5 100755 --- a/packages/common/prisma/schema.prisma +++ b/packages/common/prisma/schema.prisma @@ -110,6 +110,7 @@ model Department { id String @id @default(cuid()) name String order Float? + posts Post[] @relation("post_dept") ancestors DeptAncestry[] @relation("DescendantToAncestor") descendants DeptAncestry[] @relation("AncestorToDescendant") parentId String? @map("parent_id") @@ -200,6 +201,8 @@ model Post { order Float? @default(0) @map("order") duration Int? rating Int? @default(0) + + depts Department[] @relation("post_dept") // 索引 // 日期时间类型字段 createdAt DateTime @default(now()) @map("created_at")