This commit is contained in:
ditiqi 2025-02-24 19:33:18 +08:00
parent 5a5d4ec3ff
commit 7e482f8c53
11 changed files with 152 additions and 79 deletions

View File

@ -1,4 +1,4 @@
import { db, Prisma, PrismaClient } from "@nice/common";
import { db, Prisma, PrismaClient } from '@nice/common';
export type Operations =
| 'aggregate'
@ -13,7 +13,9 @@ export type Operations =
| 'update'
| 'updateMany'
| 'upsert';
export type DelegateFuncs = { [K in Operations]: (args: any) => Promise<unknown> }
export type DelegateFuncs = {
[K in Operations]: (args: any) => Promise<unknown>;
};
export type DelegateArgs<T> = {
[K in keyof T]: T[K] extends (args: infer A) => Promise<any> ? A : never;
};
@ -28,15 +30,15 @@ export type DataArgs<T> = T extends { data: infer D } ? D : never;
export type IncludeArgs<T> = T extends { include: infer I } ? I : never;
export type OrderByArgs<T> = T extends { orderBy: infer O } ? O : never;
export type UpdateOrderArgs = {
id: string
overId: string
}
id: string;
overId: string;
};
export interface FindManyWithCursorType<T extends DelegateFuncs> {
cursor?: string;
limit?: number;
where?: WhereArgs<DelegateArgs<T>['findUnique']>;
select?: SelectArgs<DelegateArgs<T>['findUnique']>;
orderBy?: OrderByArgs<DelegateArgs<T>['findMany']>
orderBy?: OrderByArgs<DelegateArgs<T>['findMany']>;
}
export type TransactionType = Omit<
PrismaClient,

View File

@ -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<Prisma.PostCreateArgs> = z.any();
const PostUpdateArgsSchema: ZodType<Prisma.PostUpdateArgs> = z.any();
const PostUpdateOrderArgsSchema: ZodType<UpdateOrderArgs> = z.any();
const PostFindFirstArgsSchema: ZodType<Prisma.PostFindFirstArgs> = z.any();
const PostFindManyArgsSchema: ZodType<Prisma.PostFindManyArgs> = z.any();
const PostDeleteManyArgsSchema: ZodType<Prisma.PostDeleteManyArgs> = 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);
}),
});
}

View File

@ -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<Prisma.PostDelegate> {
@ -43,6 +44,7 @@ export class PostService extends BaseTreeService<Prisma.PostDelegate> {
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<Prisma.PostDelegate> {
parentId: courseId,
title: title,
authorId: staff?.id,
updatedAt: dayjs().toDate(),
} as any,
},
{ tx },
@ -152,6 +155,7 @@ export class PostService extends BaseTreeService<Prisma.PostDelegate> {
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<Prisma.PostDelegate> {
}
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<Prisma.PostDelegate> {
}[];
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 = {

View File

@ -37,5 +37,4 @@ export class PostQueueService implements OnModuleInit {
debounce: { id: `${QueueJobType.UPDATE_POST_STATE}_${data.id}` },
});
}
}

View File

@ -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, Term[]> = {
[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,

View File

@ -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",

View File

@ -48,7 +48,7 @@ export const LectureList: React.FC<LectureListProps> = ({
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<LectureListProps> = ({
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),
});
};

View File

@ -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 (
// <div className="max-w-2xl mx-auto space-y-6 p-6">
// <Form.Item name="requirements" label="前置要求">
// <InputList placeholder="请输入前置要求"></InputList>
// </Form.Item>
// <Form.Item name="objectives" label="学习目标">
// <InputList placeholder="请输入学习目标"></InputList>
// </Form.Item>
// </div>
// );
// }

View File

@ -1,26 +0,0 @@
// import AvatarUploader from "@web/src/components/common/uploader/AvatarUploader";
// import { Form, Input } from "antd";
// export default function CourseSettingForm() {
// return (
// <div className="max-w-2xl mx-auto space-y-6 p-6">
// <Form.Item
// name="title"
// label="课程预览图"
// >
// <AvatarUploader
// style={
// {
// width: "120px",
// height: "120px",
// margin:" 0 10px"
// }
// }
// onChange={(value) => {
// console.log(value);
// }}
// ></AvatarUploader>
// </Form.Item>
// </div>
// )
// }

View File

@ -113,5 +113,8 @@ export function useEntity<T extends keyof RouterInputs>(
T,
"updateOrder"
>, // 更新实体顺序的 mutation 函数
updateOrderByIds: createMutationHandler(
"updateOrderByIds"
) as MutationResult<T, "updateOrderByIds">, // 更新实体顺序的 mutation 函数
};
}

View File

@ -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")