diff --git a/apps/server/src/models/base/row-model.service.ts b/apps/server/src/models/base/row-model.service.ts index a0cbb6f..d43769f 100644 --- a/apps/server/src/models/base/row-model.service.ts +++ b/apps/server/src/models/base/row-model.service.ts @@ -21,7 +21,7 @@ export abstract class RowModelService { // 添加更多需要引号的关键词 ]); protected logger = new Logger(this.tableName); - protected constructor(protected tableName: string) {} + protected constructor(protected tableName: string) { } protected async getRowDto(row: any, staff?: UserProfile): Promise { return row; } @@ -52,7 +52,7 @@ export abstract class RowModelService { ]); SQL = await this.getRowsSqlWrapper(SQL, request, staff); - this.logger.debug('getrows', SQL); + // this.logger.debug('getrows', SQL); const results: any[] = (await db?.$queryRawUnsafe(SQL)) || []; @@ -140,11 +140,11 @@ export abstract class RowModelService { private buildFilterConditions(filterModel: any): LogicalCondition[] { return filterModel ? Object.entries(filterModel)?.map(([key, item]) => - SQLBuilder.createFilterSql( - key === 'ag-Grid-AutoColumn' ? 'name' : key, - item, - ), - ) + SQLBuilder.createFilterSql( + key === 'ag-Grid-AutoColumn' ? 'name' : key, + item, + ), + ) : []; } diff --git a/apps/server/src/models/term/term.service.ts b/apps/server/src/models/term/term.service.ts index 7f21041..cc119e6 100755 --- a/apps/server/src/models/term/term.service.ts +++ b/apps/server/src/models/term/term.service.ts @@ -298,12 +298,12 @@ export class TermService extends BaseTreeService { ...(hasAnyPerms ? {} // 当有全局权限时,不添加任何额外条件 : { - // 当无全局权限时,添加域ID过滤 - OR: [ - { domainId: null }, // 通用记录 - { domainId: domainId }, // 特定域记录 - ], - }), + // 当无全局权限时,添加域ID过滤 + OR: [ + { domainId: null }, // 通用记录 + { domainId: domainId }, // 特定域记录 + ], + }), }, ancestorId: parentId, relDepth: 1, @@ -315,29 +315,29 @@ export class TermService extends BaseTreeService { }), termIds ? db.term.findMany({ - where: { - ...(termIds && { + where: { + ...(termIds && { + OR: [ + ...(validTermIds.length + ? [{ id: { in: validTermIds } }] + : []), + ], + }), + taxonomyId: taxonomyId, + // 动态权限控制条件 + ...(hasAnyPerms + ? {} // 当有全局权限时,不添加任何额外条件 + : { + // 当无全局权限时,添加域ID过滤 OR: [ - ...(validTermIds.length - ? [{ id: { in: validTermIds } }] - : []), + { domainId: null }, // 通用记录 + { domainId: domainId }, // 特定域记录 ], }), - taxonomyId: taxonomyId, - // 动态权限控制条件 - ...(hasAnyPerms - ? {} // 当有全局权限时,不添加任何额外条件 - : { - // 当无全局权限时,添加域ID过滤 - OR: [ - { domainId: null }, // 通用记录 - { domainId: domainId }, // 特定域记录 - ], - }), - }, - include: { children: true }, - orderBy: { order: 'asc' }, - }) + }, + include: { children: true }, + orderBy: { order: 'asc' }, + }) : [], ]); const children = childrenData @@ -371,12 +371,12 @@ export class TermService extends BaseTreeService { ...(hasAnyPerms ? {} // 当有全局权限时,不添加任何额外条件 : { - // 当无全局权限时,添加域ID过滤 - OR: [ - { domainId: null }, // 通用记录 - { domainId: domainId }, // 特定域记录 - ], - }), + // 当无全局权限时,添加域ID过滤 + OR: [ + { domainId: null }, // 通用记录 + { domainId: domainId }, // 特定域记录 + ], + }), }, }, include: { @@ -398,12 +398,12 @@ export class TermService extends BaseTreeService { ...(hasAnyPerms ? {} // 当有全局权限时,不添加任何额外条件 : { - // 当无全局权限时,添加域ID过滤 - OR: [ - { domainId: null }, // 通用记录 - { domainId: domainId }, // 特定域记录 - ], - }), + // 当无全局权限时,添加域ID过滤 + OR: [ + { domainId: null }, // 通用记录 + { domainId: domainId }, // 特定域记录 + ], + }), }, include: { children: true }, // 包含子节点信息 orderBy: { order: 'asc' }, // 按顺序升序排序 diff --git a/apps/server/src/tasks/init/gendev.service.ts b/apps/server/src/tasks/init/gendev.service.ts index fca5ca3..addb264 100644 --- a/apps/server/src/tasks/init/gendev.service.ts +++ b/apps/server/src/tasks/init/gendev.service.ts @@ -21,7 +21,6 @@ export class GenDevService { deptStaffRecord: Record = {}; terms: Record = { [TaxonomySlug.CATEGORY]: [], - [TaxonomySlug.UNIT]: [], [TaxonomySlug.TAG]: [], [TaxonomySlug.LEVEL]: [], }; @@ -36,7 +35,7 @@ export class GenDevService { private readonly departmentService: DepartmentService, private readonly staffService: StaffService, private readonly termService: TermService, - ) {} + ) { } async genDataEvent() { EventBus.emit('genDataEvent', { type: 'start' }); try { @@ -62,7 +61,7 @@ export class GenDevService { const domains = this.depts.filter((item) => item.isDomain); for (const domain of domains) { await this.createTerms(domain, TaxonomySlug.CATEGORY, depth, count); - await this.createTerms(domain, TaxonomySlug.UNIT, depth, count); + // await this.createTerms(domain, TaxonomySlug.UNIT, depth, count); } } const termCount = await db.term.count(); diff --git a/apps/web/.env.example b/apps/web/.env.example index 2d66081..4d30872 100755 --- a/apps/web/.env.example +++ b/apps/web/.env.example @@ -1,7 +1,5 @@ -VITE_APP_TUS_URL=http://localhost:8080 -VITE_APP_API_URL=http://localhost:3000 VITE_APP_SERVER_IP=192.168.252.239 VITE_APP_SERVER_PORT=3000 -VITE_APP_UPLOAD_PORT=80 +VITE_APP_FILE_PORT=80 VITE_APP_VERSION=0.3.0 VITE_APP_APP_NAME=MOOC diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index 7f00070..9e6304c 100755 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -14,7 +14,6 @@ import { Toaster } from 'react-hot-toast'; dayjs.locale("zh-cn"); function App() { - return ( <> diff --git a/apps/web/src/app/error.tsx b/apps/web/src/app/error.tsx index c32c11b..c51af76 100755 --- a/apps/web/src/app/error.tsx +++ b/apps/web/src/app/error.tsx @@ -1,11 +1,41 @@ +/** + * 错误处理模块 - 全局路由级错误展示组件 + * 功能: 捕获React Router路由层级错误并展示可视化错误信息 + * 特性: + * - 自动解析路由错误对象 + * - 自适应错误信息展示 + * - 响应式布局设计 + */ import { useRouteError } from "react-router-dom"; +/** + * 错误展示页面组件 + * @核心功能 呈现标准化错误界面,用于处理应用程序的路由层级错误 + * @设计模式 采用展示型组件模式,完全解耦业务逻辑实现纯UI展示 + * @使用示例 在React Router的RouterProvider中配置errorElement={} + */ export default function ErrorPage() { + // 使用React Router提供的Hook获取路由错误对象 + // 类型定义为any以兼容React Router不同版本的类型差异 const error: any = useRouteError(); - return
-
-
哦?页面似乎出错了...
-
{error?.statusText || error?.message}
+ + return ( + // 主容器: 基于Flex的垂直水平双居中布局 + // pt-64: 顶部留白实现视觉层次结构 +
+ {/* 内容区块: 采用纵向弹性布局控制内部元素间距 */} +
+ {/* 主标题: 强调性文字样式配置 */} +
+ 哦?页面似乎出错了... +
+ + {/* 错误详情: 动态渲染错误信息,实现优雅降级策略 */} + {/* 使用可选链操作符防止未定义错误,信息优先级: statusText > message */} +
+ {error?.statusText || error?.message} +
+
-
+ ) } \ No newline at end of file diff --git a/apps/web/src/app/main/courses/page.tsx b/apps/web/src/app/main/courses/page.tsx index cf2162e..30cd309 100644 --- a/apps/web/src/app/main/courses/page.tsx +++ b/apps/web/src/app/main/courses/page.tsx @@ -5,6 +5,7 @@ import CourseList from "./components/CourseList"; import { api } from "@nice/client"; import { LectureType, PostType } from "@nice/common"; + export default function CoursesPage() { const [currentPage, setCurrentPage] = useState(1); const [selectedCategory, setSelectedCategory] = useState(""); diff --git a/apps/web/src/app/main/home/page.tsx b/apps/web/src/app/main/home/page.tsx index 0225772..62f9d64 100644 --- a/apps/web/src/app/main/home/page.tsx +++ b/apps/web/src/app/main/home/page.tsx @@ -2,6 +2,7 @@ import HeroSection from './components/HeroSection'; import CategorySection from './components/CategorySection'; import CoursesSection from './components/CoursesSection'; import FeaturedTeachersSection from './components/FeaturedTeachersSection'; +import { TusUploader } from '@web/src/components/common/uploader/TusUploader'; const HomePage = () => { const mockCourses = [ { @@ -105,6 +106,7 @@ const HomePage = () => { return (
+ + return } diff --git a/apps/web/src/components/common/editor/MindEditor.tsx b/apps/web/src/components/common/editor/MindEditor.tsx index 66583c1..9618322 100644 --- a/apps/web/src/components/common/editor/MindEditor.tsx +++ b/apps/web/src/components/common/editor/MindEditor.tsx @@ -1,27 +1,26 @@ -// import { MindElixirInstance } from "packages/mind-elixir-core/dist/types"; +import { MindElixirInstance } from "mind-elixir"; import { useRef, useEffect } from "react"; -// import MindElixir from "mind-elixir"; +import MindElixir from "mind-elixir"; export default function MindEditor() { - // const me = useRef(); - // useEffect(() => { - // const instance = new MindElixir({ - // el: "#map", - // direction: MindElixir.SIDE, - // draggable: true, // default true - // contextMenu: true, // default true - // toolBar: true, // default true - // nodeMenu: true, // default true - // keypress: true, // default true - // }); - // // instance.install(NodeMenu); - // instance.init(MindElixir.new("新主题")); - // me.current = instance; - // }, []); - // return ( - //
- //
1
- //
- //
- // ); + const me = useRef(); + useEffect(() => { + const instance = new MindElixir({ + el: "#map", + direction: MindElixir.SIDE, + draggable: true, // default true + contextMenu: true, // default true + toolBar: true, // default true + nodeMenu: true, // default true + keypress: true // default true + + }); + // instance.install(NodeMenu); + instance.init(MindElixir.new("新主题")); + me.current = instance; + }, []); + return
+ +
+
} diff --git a/apps/web/src/components/common/uploader/TusUploader.tsx b/apps/web/src/components/common/uploader/TusUploader.tsx index 27b120c..e9fa45b 100644 --- a/apps/web/src/components/common/uploader/TusUploader.tsx +++ b/apps/web/src/components/common/uploader/TusUploader.tsx @@ -5,12 +5,8 @@ import { DeleteOutlined, } from "@ant-design/icons"; import { Upload, Progress, Button } from "antd"; -import type { UploadFile } from "antd"; import { useTusUpload } from "@web/src/hooks/useTusUpload"; import toast from "react-hot-toast"; -import { getCompressedImageUrl } from "@nice/utils"; -import { api } from "@nice/client"; - export interface TusUploaderProps { value?: string[]; onChange?: (value: string[]) => void; @@ -30,16 +26,7 @@ export const TusUploader = ({ onChange, multiple = true, }: TusUploaderProps) => { - const { data: files } = api.resource.findMany.useQuery({ - where: { - fileId: { in: value }, - }, - select: { - id: true, - fileId: true, - title: true, - }, - }); + const { handleFileUpload, uploadProgress } = useTusUpload(); const [uploadingFiles, setUploadingFiles] = useState([]); const [completedFiles, setCompletedFiles] = useState( @@ -74,6 +61,7 @@ export const TusUploader = ({ const handleBeforeUpload = useCallback( (file: File) => { + const fileKey = `${file.name}-${Date.now()}`; setUploadingFiles((prev) => [ @@ -151,7 +139,7 @@ export const TusUploader = ({ name="files" multiple={multiple} showUploadList={false} - style={{ background: "transparent", borderStyle: "none" }} + beforeUpload={handleBeforeUpload}>

@@ -177,10 +165,10 @@ export const TusUploader = ({ file.status === "done" ? 100 : Math.round( - uploadProgress?.[ - file.fileKey! - ] || 0 - ) + uploadProgress?.[ + file.fileKey! + ] || 0 + ) } status={ file.status === "error" 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 0ef99e7..8395c94 100644 --- a/apps/web/src/components/models/course/editor/form/CourseContentForm/LectureList.tsx +++ b/apps/web/src/components/models/course/editor/form/CourseContentForm/LectureList.tsx @@ -33,15 +33,9 @@ import { useSortable, verticalListSortingStrategy, } from "@dnd-kit/sortable"; -import { CSS } from "@dnd-kit/utilities"; -import QuillEditor from "@web/src/components/common/editor/quill/QuillEditor"; -import { TusUploader } from "@web/src/components/common/uploader/TusUploader"; import { Lecture, LectureType, PostType } from "@nice/common"; import { useCourseEditor } from "../../context/CourseEditorContext"; import { usePost } from "@nice/client"; -import toast from "react-hot-toast"; -import { CourseContentFormHeader } from "./CourseContentFormHeader"; -import { CourseSectionEmpty } from "./CourseSectionEmpty"; import { LectureData, SectionData } from "./interface"; import { SortableLecture } from "./SortableLecture"; diff --git a/apps/web/src/components/models/course/editor/form/CourseContentForm/SortableSection.tsx b/apps/web/src/components/models/course/editor/form/CourseContentForm/SortableSection.tsx index 0f0b7d5..5ef0937 100644 --- a/apps/web/src/components/models/course/editor/form/CourseContentForm/SortableSection.tsx +++ b/apps/web/src/components/models/course/editor/form/CourseContentForm/SortableSection.tsx @@ -17,31 +17,12 @@ import { } from "antd"; import React, { useCallback, useEffect, useState } from "react"; import { - DndContext, - closestCenter, - KeyboardSensor, - PointerSensor, - useSensor, - useSensors, - DragEndEvent, -} from "@dnd-kit/core"; -import { api, emitDataChange } from "@nice/client"; -import { - arrayMove, - SortableContext, - sortableKeyboardCoordinates, useSortable, - verticalListSortingStrategy, } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; -import QuillEditor from "@web/src/components/common/editor/quill/QuillEditor"; -import { TusUploader } from "@web/src/components/common/uploader/TusUploader"; import { Lecture, LectureType, PostType } from "@nice/common"; -import { useCourseEditor } from "../../context/CourseEditorContext"; import { usePost } from "@nice/client"; import toast from "react-hot-toast"; -import { CourseContentFormHeader } from "./CourseContentFormHeader"; -import { CourseSectionEmpty } from "./CourseSectionEmpty"; import { LectureData, SectionData } from "./interface"; interface SortableSectionProps { courseId?: string; diff --git a/apps/web/src/env.ts b/apps/web/src/env.ts index 30278b3..956a74b 100755 --- a/apps/web/src/env.ts +++ b/apps/web/src/env.ts @@ -2,7 +2,7 @@ export const env: { APP_NAME: string; SERVER_IP: string; VERSION: string; - UPLOAD_PORT: string; + FILE_PORT: string; SERVER_PORT: string; } = { APP_NAME: import.meta.env.PROD @@ -11,9 +11,9 @@ export const env: { SERVER_IP: import.meta.env.PROD ? (window as any).env.VITE_APP_SERVER_IP : import.meta.env.VITE_APP_SERVER_IP, - UPLOAD_PORT: import.meta.env.PROD - ? (window as any).env.VITE_APP_UPLOAD_PORT - : import.meta.env.VITE_APP_UPLOAD_PORT, + FILE_PORT: import.meta.env.PROD + ? (window as any).env.VITE_APP_FILE_PORT + : import.meta.env.VITE_APP_FILE_PORT, SERVER_PORT: import.meta.env.PROD ? (window as any).env.VITE_APP_SERVER_PORT : import.meta.env.VITE_APP_SERVER_PORT, diff --git a/apps/web/src/hooks/useTusUpload.ts b/apps/web/src/hooks/useTusUpload.ts index 9c2ea7e..a4589a5 100644 --- a/apps/web/src/hooks/useTusUpload.ts +++ b/apps/web/src/hooks/useTusUpload.ts @@ -2,11 +2,6 @@ import { useState } from "react"; import * as tus from "tus-js-client"; import { env } from "../env"; import { getCompressedImageUrl } from "@nice/utils"; -// useTusUpload.ts -interface UploadProgress { - fileId: string; - progress: number; -} interface UploadResult { compressedUrl: string; @@ -35,8 +30,7 @@ export function useTusUpload() { if (uploadIndex === -1 || uploadIndex + 4 >= parts.length) { throw new Error("Invalid upload URL format"); } - const resUrl = `http://${env.SERVER_IP}:${env.UPLOAD_PORT}/uploads/${parts.slice(uploadIndex + 1, uploadIndex + 6).join("/")}`; - + const resUrl = `http://${env.SERVER_IP}:${env.FILE_PORT}/uploads/${parts.slice(uploadIndex + 1, uploadIndex + 6).join("/")}`; return resUrl; }; const handleFileUpload = async ( @@ -45,11 +39,13 @@ export function useTusUpload() { onError: (error: Error) => void, fileKey: string // 添加文件唯一标识 ) => { + // console.log() setIsUploading(true); setUploadProgress((prev) => ({ ...prev, [fileKey]: 0 })); setUploadError(null); try { + console.log(`http://${env.SERVER_IP}:${env.SERVER_PORT}/upload`); const upload = new tus.Upload(file, { endpoint: `http://${env.SERVER_IP}:${env.SERVER_PORT}/upload`, retryDelays: [0, 1000, 3000, 5000], @@ -96,6 +92,7 @@ export function useTusUpload() { onError: (error) => { setIsUploading(false); setUploadError(error.message); + console.log(error); onError(error); }, }); diff --git a/apps/web/src/index.css b/apps/web/src/index.css index ab429a2..45d7dc7 100755 --- a/apps/web/src/index.css +++ b/apps/web/src/index.css @@ -128,4 +128,5 @@ #map { height: 600px; width: 100%; -} \ No newline at end of file +} + diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx index c0ee5ee..962b763 100755 --- a/apps/web/src/main.tsx +++ b/apps/web/src/main.tsx @@ -3,13 +3,9 @@ import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App.js"; import "./index.css"; - import { ModuleRegistry } from "@ag-grid-community/core"; import { LicenseManager } from "@ag-grid-enterprise/core"; - import { ClientSideRowModelModule } from "@ag-grid-community/client-side-row-model"; - - ModuleRegistry.registerModules([ClientSideRowModelModule]); LicenseManager.setLicenseKey( diff --git a/package.json b/package.json index 40e6ac1..8a85a11 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "dev": "pnpm run --parallel dev" + "dev": "pnpm run --parallel dev", + "db:clear": "pnpm --filter common run db:clear" }, "keywords": [], "author": "insiinc", diff --git a/packages/common/prisma/schema.prisma b/packages/common/prisma/schema.prisma index 103ea10..01bb99e 100644 --- a/packages/common/prisma/schema.prisma +++ b/packages/common/prisma/schema.prisma @@ -44,7 +44,8 @@ model Term { createdBy String? @map("created_by") depts Department[] @relation("department_term") hasChildren Boolean? @default(false) @map("has_children") - posts Post[] @relation("post_term") + posts Post[] @relation("post_term") + @@index([name]) // 对name字段建立索引,以加快基于name的查找速度 @@index([parentId]) // 对parentId字段建立索引,以加快基于parentId的查找速度 @@map("term") @@ -87,14 +88,14 @@ model Staff { deletedAt DateTime? @map("deleted_at") officerId String? @map("officer_id") - watchedPost Post[] @relation("post_watch_staff") + watchedPost Post[] @relation("post_watch_staff") visits Visit[] posts Post[] - sentMsgs Message[] @relation("message_sender") - receivedMsgs Message[] @relation("message_receiver") + sentMsgs Message[] @relation("message_sender") + receivedMsgs Message[] @relation("message_receiver") registerToken String? enrollments Enrollment[] - teachedPosts PostInstructor[] + teachedPosts PostInstructor[] ownedResources Resource[] @@index([officerId]) @@ -186,12 +187,12 @@ model AppConfig { model Post { // 字符串类型字段 - id String @id @default(cuid()) // 帖子唯一标识,使用 cuid() 生成默认值 + id String @id @default(cuid()) // 帖子唯一标识,使用 cuid() 生成默认值 type String? // Post类型,课程、章节、小节、讨论都用Post实现 - level String? - state String? + level String? + state String? title String? // 帖子标题,可为空 - subTitle String? + subTitle String? content String? // 帖子内容,可为空 important Boolean? //是否重要/精选/突出 domainId String? @map("domain_id") @@ -201,27 +202,28 @@ model Post { rating Int? @default(0) // 索引 // 日期时间类型字段 - createdAt DateTime @default(now()) @map("created_at") - publishedAt DateTime? @map("published_at") // 发布时间 - updatedAt DateTime @updatedAt @map("updated_at") - deletedAt DateTime? @map("deleted_at") // 删除时间,可为空 - instructors PostInstructor[] + createdAt DateTime @default(now()) @map("created_at") + publishedAt DateTime? @map("published_at") // 发布时间 + updatedAt DateTime @updatedAt @map("updated_at") + deletedAt DateTime? @map("deleted_at") // 删除时间,可为空 + instructors PostInstructor[] // 关系类型字段 - authorId String? @map("author_id") - author Staff? @relation(fields: [authorId], references: [id]) // 帖子作者,关联 Staff 模型 - enrollments Enrollment[] // 学生报名记录 - visits Visit[] // 访问记录,关联 Visit 模型 - parentId String? @map("parent_id") - parent Post? @relation("PostChildren", fields: [parentId], references: [id]) // 父级帖子,关联 Post 模型 - children Post[] @relation("PostChildren") // 子级帖子列表,关联 Post 模型 - hasChildren Boolean? @default(false) @map("has_children") - // 闭包表关系 - ancestors PostAncestry[] @relation("DescendantPosts") - descendants PostAncestry[] @relation("AncestorPosts") - resources Resource[] // 附件列表 - watchableStaffs Staff[] @relation("post_watch_staff") // 可观看的员工列表,关联 Staff 模型 - watchableDepts Department[] @relation("post_watch_dept") // 可观看的部门列表,关联 Department 模型 - meta Json? // 封面url 视频url objectives具体的学习目标 rating评分Int + authorId String? @map("author_id") + author Staff? @relation(fields: [authorId], references: [id]) // 帖子作者,关联 Staff 模型 + enrollments Enrollment[] // 学生报名记录 + visits Visit[] // 访问记录,关联 Visit 模型 + parentId String? @map("parent_id") + parent Post? @relation("PostChildren", fields: [parentId], references: [id]) // 父级帖子,关联 Post 模型 + children Post[] @relation("PostChildren") // 子级帖子列表,关联 Post 模型 + hasChildren Boolean? @default(false) @map("has_children") + // 闭包表关系 + ancestors PostAncestry[] @relation("DescendantPosts") + descendants PostAncestry[] @relation("AncestorPosts") + resources Resource[] // 附件列表 + watchableStaffs Staff[] @relation("post_watch_staff") // 可观看的员工列表,关联 Staff 模型 + watchableDepts Department[] @relation("post_watch_dept") // 可观看的部门列表,关联 Department 模型 + meta Json? // 封面url 视频url objectives具体的学习目标 rating评分Int + // 索引 @@index([type, domainId]) @@index([authorId, type]) @@ -244,7 +246,7 @@ model PostAncestry { ancestor Post? @relation("AncestorPosts", fields: [ancestorId], references: [id]) descendant Post @relation("DescendantPosts", fields: [descendantId], references: [id]) // 复合索引优化 - // 索引建议 + // 索引建议 @@index([ancestorId]) // 针对祖先的查询 @@index([descendantId]) // 针对后代的查询 @@index([ancestorId, descendantId]) // 组合索引,用于查询特定的祖先-后代关系 @@ -292,18 +294,16 @@ model Visit { // totalWatchTime Int? @default(0) @map("total_watch_time") // 总观看时长(秒) // // 时间记录 // lastWatchedAt DateTime? @map("last_watched_at") // 最后观看时间 - createdAt DateTime @default(now()) @map("created_at") // 创建时间 - updatedAt DateTime @updatedAt @map("updated_at") // 更新时间 + createdAt DateTime @default(now()) @map("created_at") // 创建时间 + updatedAt DateTime @updatedAt @map("updated_at") // 更新时间 deletedAt DateTime? @map("deleted_at") // 删除时间,可为空 - meta Json? + meta Json? @@index([postId, type, visitorId]) @@index([messageId, type, visitorId]) @@map("visit") } - - model Enrollment { id String @id @default(cuid()) @map("id") status String @map("status") @@ -316,8 +316,8 @@ model Enrollment { // 关联关系 student Staff @relation(fields: [studentId], references: [id]) studentId String @map("student_id") - post Post @relation(fields: [postId], references: [id]) - postId String @map("post_id") + post Post @relation(fields: [postId], references: [id]) + postId String @map("post_id") @@unique([studentId, postId]) @@index([status]) @@ -326,14 +326,14 @@ model Enrollment { } model PostInstructor { - postId String @map("post_id") + postId String @map("post_id") instructorId String @map("instructor_id") role String @map("role") createdAt DateTime @default(now()) @map("created_at") order Float? @default(0) @map("order") - post Post @relation(fields: [postId], references: [id]) - instructor Staff @relation(fields: [instructorId], references: [id]) + post Post @relation(fields: [postId], references: [id]) + instructor Staff @relation(fields: [instructorId], references: [id]) @@id([postId, instructorId]) @@map("post_instructor") @@ -347,7 +347,7 @@ model Resource { fileId String? @unique url String? // 元数据 - meta Json? @map("meta") + meta Json? @map("meta") // 处理状态控制 status String? createdAt DateTime? @default(now()) @map("created_at") @@ -360,7 +360,7 @@ model Resource { ownerId String? @map("owner_id") post Post? @relation(fields: [postId], references: [id]) postId String? @map("post_id") - + // 索引 @@index([type]) @@index([createdAt]) @@ -404,3 +404,19 @@ model NodeEdge { @@index([targetId]) @@map("node_edge") } + +model Animal { + id String @id @default(cuid()) + name String + age Int + gender Boolean + personId String? + person Person? @relation(fields: [personId], references: [id]) +} +model Person { + id String @id @default(cuid()) + name String + age Int + gender Boolean + animals Animal[] +} diff --git a/packages/common/src/enum.ts b/packages/common/src/enum.ts index 9598a55..32903b2 100755 --- a/packages/common/src/enum.ts +++ b/packages/common/src/enum.ts @@ -15,7 +15,6 @@ export enum LectureType { } export enum TaxonomySlug { CATEGORY = "category", - UNIT = "unit", TAG = "tag", LEVEL = "level", } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ca657ff..bce2ffd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,7 +12,7 @@ importers: dependencies: '@nestjs/bullmq': specifier: ^10.2.0 - version: 10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))(bullmq@5.34.8) + version: 10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)(bullmq@5.34.8) '@nestjs/common': specifier: ^10.3.10 version: 10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -33,7 +33,7 @@ importers: version: 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.15)(rxjs@7.8.1) '@nestjs/schedule': specifier: ^4.1.0 - version: 4.1.2(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1)) + version: 4.1.2(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15) '@nestjs/websockets': specifier: ^10.3.10 version: 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)(@nestjs/platform-socket.io@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -148,7 +148,7 @@ importers: version: 10.2.3(chokidar@3.6.0)(typescript@5.7.2) '@nestjs/testing': specifier: ^10.0.0 - version: 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)) + version: 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)(@nestjs/platform-express@10.4.15) '@types/exceljs': specifier: ^1.3.0 version: 1.3.2 @@ -673,7 +673,7 @@ importers: specifier: ^5.62.0 version: 5.62.0(eslint@8.57.1)(typescript@5.7.2) '@viselect/vanilla': - specifier: ^3.5.1 + specifier: ^3.9.0 version: 3.9.0 eslint: specifier: ^8.57.0 @@ -9665,15 +9665,15 @@ snapshots: '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': optional: true - '@nestjs/bull-shared@10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))': + '@nestjs/bull-shared@10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)': dependencies: '@nestjs/common': 10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1) tslib: 2.8.1 - '@nestjs/bullmq@10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))(bullmq@5.34.8)': + '@nestjs/bullmq@10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)(bullmq@5.34.8)': dependencies: - '@nestjs/bull-shared': 10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1)) + '@nestjs/bull-shared': 10.2.3(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15) '@nestjs/common': 10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1) bullmq: 5.34.8 @@ -9770,7 +9770,7 @@ snapshots: - supports-color - utf-8-validate - '@nestjs/schedule@4.1.2(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))': + '@nestjs/schedule@4.1.2(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)': dependencies: '@nestjs/common': 10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -9788,7 +9788,7 @@ snapshots: transitivePeerDependencies: - chokidar - '@nestjs/testing@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15))': + '@nestjs/testing@10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.15)(@nestjs/platform-express@10.4.15)': dependencies: '@nestjs/common': 10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.15(@nestjs/common@10.4.15(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.15)(@nestjs/websockets@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.1)