Issue changes are recorded per field in the issue_change table, in the same transaction as the issue itself. The new history module exposes: - GET /api/issues/:issueId/history — change timeline grouped by save - GET /api/issues/stats/daily — created, status transitions and status snapshot per day, with time zone support The migration backfills history for existing issues. Setting a field to its current value no longer bumps updatedAt or emits issue.updated.
This commit is contained in:
1 parent
f44590b8ca
commit
cb29c7d2c4
26 files changed
+1026
-16
No files matched your search
@@ -0,0 +1,28 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "issue_change" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"issueId" INTEGER NOT NULL,
|
||||
"changeId" TEXT NOT NULL,
|
||||
"field" TEXT NOT NULL,
|
||||
"oldValue" TEXT,
|
||||
"newValue" TEXT,
|
||||
"actorId" TEXT,
|
||||
"changedAt" TIMESTAMPTZ(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "issue_change_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "issue_change_issueId_changedAt_idx" ON "issue_change"("issueId", "changedAt");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "issue_change_field_changedAt_idx" ON "issue_change"("field", "changedAt");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "issue_change_actorId_idx" ON "issue_change"("actorId");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "issue_change" ADD CONSTRAINT "issue_change_issueId_fkey" FOREIGN KEY ("issueId") REFERENCES "issue"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "issue_change" ADD CONSTRAINT "issue_change_actorId_fkey" FOREIGN KEY ("actorId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
@@ -26,6 +26,7 @@ model User {
|
||||
notificationMessages NotificationMessage[]
|
||||
comments Comment[]
|
||||
uploadedFiles File[]
|
||||
issueChanges IssueChange[]
|
||||
|
||||
@@unique([email])
|
||||
@@map("user")
|
||||
@@ -96,6 +97,7 @@ model Issue {
|
||||
dueDate DateTime? @db.Timestamptz(3)
|
||||
comments Comment[]
|
||||
attachments IssueAttachment[]
|
||||
changes IssueChange[]
|
||||
createdAt DateTime @default(now()) @db.Timestamptz(3)
|
||||
updatedAt DateTime @updatedAt @db.Timestamptz(3)
|
||||
|
||||
@@ -264,3 +266,29 @@ model CommentAttachment {
|
||||
@@index([fileId])
|
||||
@@map("comment_attachment")
|
||||
}
|
||||
|
||||
/// История изменений задачи — одна строка на изменённое поле.
|
||||
/// Строки одного сохранения задачи объединены общим changeId.
|
||||
/// field: created | title | content | status | priority | assignee | dueDate
|
||||
/// (валидируется в коде, см. ISSUE_CHANGE_FIELDS). Значения — строки:
|
||||
/// priority — число, dueDate — ISO-дата, assignee — id пользователя.
|
||||
/// При создании задачи пишутся две строки: created и status null -> OPEN,
|
||||
/// поэтому статус задачи на любой момент восстанавливается по строкам status.
|
||||
/// У задач, созданных до появления истории, строк нет — в статистику они не попадают.
|
||||
model IssueChange {
|
||||
id Int @id @default(autoincrement())
|
||||
issueId Int
|
||||
issue Issue @relation(fields: [issueId], references: [id], onDelete: Cascade)
|
||||
changeId String
|
||||
field String
|
||||
oldValue String?
|
||||
newValue String?
|
||||
actorId String?
|
||||
actor User? @relation(fields: [actorId], references: [id], onDelete: SetNull)
|
||||
changedAt DateTime @db.Timestamptz(3)
|
||||
|
||||
@@index([issueId, changedAt])
|
||||
@@index([field, changedAt])
|
||||
@@index([actorId])
|
||||
@@map("issue_change")
|
||||
}
|
||||
Reference in new issue
Block a user