Add issue change history and daily issue stats
Gitea Actions Demo / Deploy (push) Successful in 32s

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:
vadim committed 2026-10-08 09:55:21 +03:00
1 parent f44590b8ca
commit cb29c7d2c4
26 files changed
+1026 -16

No files matched your search

+28
View File
@@ -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")
}