forked from bai/curriculum-project-hub
feat(filelib): 操作日志模块——防篡改哈希链、组合查询与 CSV 导出
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -52,6 +52,7 @@ model Organization {
|
||||
auditEntries AuditEntry[] @relation("organizationAudit")
|
||||
projectSearchDocuments ProjectSearchDocument[]
|
||||
fileLibNodes FileLibNode[]
|
||||
fileLibAuditLogs FileLibAuditLog[] @relation("organizationFileLibAudit")
|
||||
|
||||
@@index([status])
|
||||
}
|
||||
@@ -1140,3 +1141,86 @@ model FileLibExportJob {
|
||||
|
||||
@@index([organizationId, status])
|
||||
}
|
||||
|
||||
// --- 文件库审计日志(横切模块) -------------------------------------------
|
||||
|
||||
/// 审计对象类型。objectId 故意不建 FK:审计是不可变历史事实,对象被彻底
|
||||
/// 删除(node.purge)后日志仍须完整可读,不能被级联带走。
|
||||
enum FileLibAuditObjectType {
|
||||
FOLDER
|
||||
PROJECT
|
||||
FILE
|
||||
GRANT
|
||||
EXPORT_JOB
|
||||
GROUP
|
||||
SYSTEM
|
||||
}
|
||||
|
||||
enum FileLibAuditResult {
|
||||
SUCCESS
|
||||
FAILURE
|
||||
}
|
||||
|
||||
/// 文件库审计日志(ADR-0039)。与既有 `AuditEntry`(Project/Run 口径、
|
||||
/// best-effort)及 Platform Audit(ADR-0023)三者分离:本表是文件版本管理与
|
||||
/// 权限管理子模块的合规证据,字段结构化、只追加、带哈希链防篡改。
|
||||
///
|
||||
/// 不可篡改:服务路径只 INSERT/SELECT。迁移 SQL 里装了 BEFORE UPDATE OR
|
||||
/// DELETE 触发器,任何改写/删除一律抛异常 —— 应用层写错也拦得住。保留策略
|
||||
/// 归档(≥180 天)是唯一合法的出场方式,走 archivedAt 打标而非物理删除。
|
||||
///
|
||||
/// 哈希链:每条 entryHash = sha256(规范化载荷 + prevHash),按 org 串成单链,
|
||||
/// seq 单调递增。整链可离线校验(见 auditVerify),断链即篡改或漏写。
|
||||
model FileLibAuditLog {
|
||||
id String @id @default(cuid())
|
||||
organizationId String
|
||||
/// org 内单调递增序号,哈希链的顺序权威(createdAt 同秒不可排序)。
|
||||
seq BigInt
|
||||
/// 操作时间。DB 侧 timestamptz,读出即带时区。
|
||||
occurredAt DateTime @default(now())
|
||||
action String
|
||||
result FileLibAuditResult @default(SUCCESS)
|
||||
/// 失败原因(result=FAILURE 时的错误码 + 消息);成功为 null。
|
||||
failureReason String?
|
||||
|
||||
/// 操作人 id。故意不建 FK:用户注销后审计仍须留痕。
|
||||
actorUserId String
|
||||
/// 操作人姓名快照 —— 记录当时的显示名,不随用户改名而变。
|
||||
actorName String
|
||||
/// 操作时是否以网站管理员身份行事(高危操作甄别)。
|
||||
actorIsAdmin Boolean @default(false)
|
||||
|
||||
objectType FileLibAuditObjectType
|
||||
objectId String
|
||||
/// 对象名称;节点为 name,文件为文件名,组为组名。
|
||||
objectName String
|
||||
/// 对象路径:节点 pathIds,文件为 "<pathIds>:<filePath>"。前缀匹配可查子树。
|
||||
objectPath String
|
||||
|
||||
/// 操作前值(结构化)。创建类操作为 null。
|
||||
beforeValue Json?
|
||||
/// 操作后值(结构化)。删除类操作为 null。
|
||||
afterValue Json?
|
||||
/// 附加上下文:冲突基线版本、导出参数、commit message 等。
|
||||
context Json?
|
||||
|
||||
clientIp String?
|
||||
userAgent String?
|
||||
|
||||
/// 本条的链哈希;prevHash 为 org 内上一条的 entryHash(首条为 null)。
|
||||
entryHash String
|
||||
prevHash String?
|
||||
|
||||
/// 保留策略归档时间(≥180 天后打标)。非 null 即已归档,默认查询不返回。
|
||||
archivedAt DateTime?
|
||||
|
||||
organization Organization @relation("organizationFileLibAudit", fields: [organizationId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([organizationId, seq])
|
||||
@@index([organizationId, occurredAt])
|
||||
@@index([organizationId, actorUserId, occurredAt])
|
||||
@@index([organizationId, action, occurredAt])
|
||||
@@index([organizationId, objectType, objectId])
|
||||
@@index([organizationId, objectPath])
|
||||
@@index([organizationId, archivedAt])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user