forked from bai/curriculum-project-hub
feat(database): init database folder frontend and permission
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
-- File library (文件库) — 语义锚定:仓库根《文件库-接口契约.md》、.omo/文件库-开工计划.md (D11–D19)。
|
||||
-- 本地无 PG 时手写;有 PG 后可用 `prisma migrate diff` 核对与 schema 的一致性。
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "FileLibNodeKind" AS ENUM ('FOLDER', 'PROJECT');
|
||||
CREATE TYPE "FileLibProvisionStatus" AS ENUM ('PROVISIONING', 'READY', 'FAILED');
|
||||
CREATE TYPE "FileLibRole" AS ENUM ('VIEW', 'EDIT', 'MANAGE');
|
||||
CREATE TYPE "FileLibPrincipalType" AS ENUM ('USER', 'GROUP');
|
||||
CREATE TYPE "FileLibExportStatus" AS ENUM ('QUEUED', 'RUNNING', 'DONE', 'FAILED');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "FileLibNode" (
|
||||
"id" TEXT NOT NULL,
|
||||
"organizationId" TEXT NOT NULL,
|
||||
"parentId" TEXT,
|
||||
"kind" "FileLibNodeKind" NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"nameLower" TEXT NOT NULL,
|
||||
"pathIds" TEXT NOT NULL,
|
||||
"creatorId" TEXT NOT NULL,
|
||||
"provisionStatus" "FileLibProvisionStatus" NOT NULL DEFAULT 'READY',
|
||||
"storageDir" TEXT,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "FileLibNode_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE "FileLibGrant" (
|
||||
"id" TEXT NOT NULL,
|
||||
"organizationId" TEXT NOT NULL,
|
||||
"nodeId" TEXT NOT NULL,
|
||||
"principalType" "FileLibPrincipalType" NOT NULL,
|
||||
"principalId" TEXT NOT NULL,
|
||||
"role" "FileLibRole" NOT NULL,
|
||||
"isCreatorGrant" BOOLEAN NOT NULL DEFAULT false,
|
||||
"createdByUserId" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"revokedAt" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "FileLibGrant_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE TABLE "FileLibProjectSettings" (
|
||||
"nodeId" TEXT NOT NULL,
|
||||
"independentPermissionsEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
|
||||
CONSTRAINT "FileLibProjectSettings_pkey" PRIMARY KEY ("nodeId")
|
||||
);
|
||||
|
||||
CREATE TABLE "FileLibExportJob" (
|
||||
"id" TEXT NOT NULL,
|
||||
"organizationId" TEXT NOT NULL,
|
||||
"nodeId" TEXT NOT NULL,
|
||||
"target" TEXT NOT NULL,
|
||||
"params" JSONB NOT NULL,
|
||||
"status" "FileLibExportStatus" NOT NULL DEFAULT 'QUEUED',
|
||||
"downloadUrl" TEXT,
|
||||
"error" TEXT,
|
||||
"createdByUserId" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "FileLibExportJob_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex (schema-declared)
|
||||
CREATE INDEX "FileLibNode_organizationId_parentId_deletedAt_idx" ON "FileLibNode"("organizationId", "parentId", "deletedAt");
|
||||
CREATE INDEX "FileLibNode_organizationId_pathIds_idx" ON "FileLibNode"("organizationId", "pathIds");
|
||||
CREATE INDEX "FileLibNode_creatorId_idx" ON "FileLibNode"("creatorId");
|
||||
CREATE INDEX "FileLibGrant_organizationId_revokedAt_idx" ON "FileLibGrant"("organizationId", "revokedAt");
|
||||
CREATE INDEX "FileLibGrant_principalType_principalId_revokedAt_idx" ON "FileLibGrant"("principalType", "principalId", "revokedAt");
|
||||
CREATE INDEX "FileLibGrant_nodeId_revokedAt_idx" ON "FileLibGrant"("nodeId", "revokedAt");
|
||||
CREATE INDEX "FileLibExportJob_organizationId_status_idx" ON "FileLibExportJob"("organizationId", "status");
|
||||
|
||||
-- D14:活跃兄弟节点大小写不敏感唯一。root 的 parentId 为 NULL,用 COALESCE 归入同一键空间。
|
||||
CREATE UNIQUE INDEX "FileLibNode_active_sibling_name_key"
|
||||
ON "FileLibNode"("organizationId", COALESCE("parentId", ''), "nameLower")
|
||||
WHERE "deletedAt" IS NULL;
|
||||
|
||||
-- 契约 2.3:同一节点上同一 principal 至多一条活跃授权(Postgres 原生 UNIQUE 无法约束 NULL revokedAt)。
|
||||
CREATE UNIQUE INDEX "FileLibGrant_active_unique"
|
||||
ON "FileLibGrant"("nodeId", "principalType", "principalId")
|
||||
WHERE "revokedAt" IS NULL;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "FileLibNode" ADD CONSTRAINT "FileLibNode_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE "FileLibNode" ADD CONSTRAINT "FileLibNode_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "FileLibNode"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
ALTER TABLE "FileLibGrant" ADD CONSTRAINT "FileLibGrant_nodeId_fkey" FOREIGN KEY ("nodeId") REFERENCES "FileLibNode"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE "FileLibProjectSettings" ADD CONSTRAINT "FileLibProjectSettings_nodeId_fkey" FOREIGN KEY ("nodeId") REFERENCES "FileLibNode"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE "FileLibExportJob" ADD CONSTRAINT "FileLibExportJob_nodeId_fkey" FOREIGN KEY ("nodeId") REFERENCES "FileLibNode"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- 为 FileLibNode 加简介字段,老师在创建/概览页填写。
|
||||
ALTER TABLE "FileLibNode" ADD COLUMN "description" TEXT;
|
||||
@@ -50,6 +50,7 @@ model Organization {
|
||||
projectGroupBindings ProjectGroupBinding[]
|
||||
auditEntries AuditEntry[] @relation("organizationAudit")
|
||||
projectSearchDocuments ProjectSearchDocument[]
|
||||
fileLibNodes FileLibNode[]
|
||||
|
||||
@@index([status])
|
||||
}
|
||||
@@ -923,3 +924,127 @@ model CapabilityCredentialVersion {
|
||||
@@index([keyId])
|
||||
@@index([createdByUserId])
|
||||
}
|
||||
|
||||
// --- File library (文件库) -------------------------------------------------
|
||||
//
|
||||
// 独立模块,语义由仓库根《文件库-接口契约.md》(C/D 编号)与 .omo/文件库-开工计划.md
|
||||
// (D11–D19)锚定。与上面的 Folder/Project(hub 自己的 explorer,ADR-0021)是两套
|
||||
// 体系,不复用、不互相引用。
|
||||
|
||||
/// 文件库目录树节点:文件夹(容器)或项目(叶子,关联 git 仓库)。
|
||||
/// parentId 是树的权威关系;pathIds 是 id 编码的物化路径(派生),随 create/move
|
||||
/// 在事务内维护(计划 D12;name 不入路径,rename 不重写后代)。
|
||||
model FileLibNode {
|
||||
id String @id
|
||||
organizationId String
|
||||
parentId String?
|
||||
kind FileLibNodeKind
|
||||
name String
|
||||
/// D14:NFC+trim 的小写形式;活跃兄弟节点大小写不敏感唯一(部分唯一索引在迁移 SQL)。
|
||||
nameLower String
|
||||
/// id 编码物化路径,形如 "/rootId/childId/selfId"。祖先展开与前缀查询都用它。
|
||||
pathIds String
|
||||
/// D11:创建者不可变,自动持有 isCreatorGrant=true 的 MANAGE grant。
|
||||
/// 故意不建 FK:这是不可变历史事实,不随 User 生命周期变化。
|
||||
creatorId String
|
||||
/// 老师可填写的简介,创建/概览页展示,通俗易懂地说明这个节点的用途。
|
||||
description String?
|
||||
/// 项目 provisioning 状态机(DB/git 双写协调,Metis 风险#1);文件夹恒 READY。
|
||||
provisionStatus FileLibProvisionStatus @default(READY)
|
||||
/// 项目仓库目录(绝对路径);文件夹为 null。
|
||||
storageDir String?
|
||||
deletedAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
||||
parent FileLibNode? @relation("fileLibTree", fields: [parentId], references: [id], onDelete: Restrict)
|
||||
children FileLibNode[] @relation("fileLibTree")
|
||||
grants FileLibGrant[]
|
||||
projectSettings FileLibProjectSettings?
|
||||
exportJobs FileLibExportJob[]
|
||||
|
||||
@@index([organizationId, parentId, deletedAt])
|
||||
@@index([organizationId, pathIds])
|
||||
@@index([creatorId])
|
||||
}
|
||||
|
||||
enum FileLibNodeKind {
|
||||
FOLDER
|
||||
PROJECT
|
||||
}
|
||||
|
||||
enum FileLibProvisionStatus {
|
||||
PROVISIONING
|
||||
READY
|
||||
FAILED
|
||||
}
|
||||
|
||||
/// 文件库权限级别:MANAGE > EDIT > VIEW(契约 2.2,只取最高、无降权)。
|
||||
enum FileLibRole {
|
||||
VIEW
|
||||
EDIT
|
||||
MANAGE
|
||||
}
|
||||
|
||||
enum FileLibPrincipalType {
|
||||
USER
|
||||
GROUP
|
||||
}
|
||||
|
||||
/// 契约 2.3:grant 直接挂在节点上,最终权限 = max(个人, 递归 Group, 祖先继承)。
|
||||
/// 活跃授权唯一性由迁移里的部分唯一索引保证(revokedAt IS NULL)。
|
||||
model FileLibGrant {
|
||||
id String @id @default(cuid())
|
||||
organizationId String
|
||||
nodeId String
|
||||
principalType FileLibPrincipalType
|
||||
principalId String
|
||||
role FileLibRole
|
||||
/// D11:创建者自动 grant;独立权限开关关闭时,项目级 grant 里只有它仍生效。
|
||||
isCreatorGrant Boolean @default(false)
|
||||
createdByUserId String?
|
||||
createdAt DateTime @default(now())
|
||||
revokedAt DateTime?
|
||||
|
||||
node FileLibNode @relation(fields: [nodeId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([organizationId, revokedAt])
|
||||
@@index([principalType, principalId, revokedAt])
|
||||
@@index([nodeId, revokedAt])
|
||||
}
|
||||
|
||||
/// 契约 P5/D11:项目独立权限开关。默认关闭(仅继承);关闭时项目级非创建者
|
||||
/// grant 冻结不删除,重新开启即恢复。
|
||||
model FileLibProjectSettings {
|
||||
nodeId String @id
|
||||
independentPermissionsEnabled Boolean @default(false)
|
||||
|
||||
node FileLibNode @relation(fields: [nodeId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
enum FileLibExportStatus {
|
||||
QUEUED
|
||||
RUNNING
|
||||
DONE
|
||||
FAILED
|
||||
}
|
||||
|
||||
/// 契约 D10:导出为异步任务。外部导出工具参数 OPEN-6,adapter 就位前先建模型。
|
||||
model FileLibExportJob {
|
||||
id String @id @default(cuid())
|
||||
organizationId String
|
||||
nodeId String
|
||||
target String
|
||||
params Json
|
||||
status FileLibExportStatus @default(QUEUED)
|
||||
downloadUrl String?
|
||||
error String?
|
||||
createdByUserId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
node FileLibNode @relation(fields: [nodeId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([organizationId, status])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user