feat: 完成举报拉黑和审核处置闭环

This commit is contained in:
root
2026-09-16 20:41:01 +08:00
parent 02189ebb2e
commit ce8e1db279
19 changed files with 1778 additions and 5 deletions
@@ -0,0 +1,17 @@
CREATE TYPE "AccountRole" AS ENUM ('USER', 'ADMIN');
ALTER TABLE "accounts" ADD COLUMN "role" "AccountRole" NOT NULL DEFAULT 'USER';
ALTER TABLE "reports" ADD COLUMN "idempotency_key" VARCHAR(128) DEFAULT gen_random_uuid()::text;
UPDATE "reports" SET "idempotency_key" = "id"::text WHERE "idempotency_key" IS NULL;
ALTER TABLE "reports" ALTER COLUMN "idempotency_key" SET NOT NULL;
CREATE UNIQUE INDEX "reports_reporter_id_idempotency_key_key" ON "reports"("reporter_id", "idempotency_key");
ALTER TABLE "notifications" ADD COLUMN "dedupe_key" VARCHAR(255);
UPDATE "notifications" SET "dedupe_key" = "id"::text WHERE "dedupe_key" IS NULL;
ALTER TABLE "notifications" ALTER COLUMN "dedupe_key" SET NOT NULL;
CREATE UNIQUE INDEX "notifications_account_id_dedupe_key_key" ON "notifications"("account_id", "dedupe_key");
CREATE TABLE "push_preferences" (
"account_id" UUID NOT NULL,
"in_app_enabled" BOOLEAN NOT NULL DEFAULT true,
"updated_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "push_preferences_pkey" PRIMARY KEY ("account_id"),
CONSTRAINT "push_preferences_account_id_fkey" FOREIGN KEY ("account_id") REFERENCES "accounts"("id") ON DELETE CASCADE ON UPDATE CASCADE
);
+20
View File
@@ -13,6 +13,11 @@ enum AccountStatus {
DELETED
}
enum AccountRole {
USER
ADMIN
}
enum AuthProvider {
PHONE
APPLE
@@ -97,6 +102,7 @@ model Account {
phoneCiphertext Bytes @map("phone_ciphertext")
phoneHmac String @unique @map("phone_hmac") @db.VarChar(128)
status AccountStatus @default(ACTIVE)
role AccountRole @default(USER)
tokenVersion Int @default(0) @map("token_version")
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
@@ -119,6 +125,7 @@ model Account {
auditLogs AuditLog[] @relation("AuditActor")
dailyUsage DailyUsage[]
pickRequests BottlePickRequest[]
pushPreference PushPreference?
@@map("accounts")
}
@@ -368,6 +375,7 @@ model Report {
/// Exactly one nullable target foreign key must be set; enforced in 0001_init SQL.
id String @id @default(uuid()) @db.Uuid
reporterId String @map("reporter_id") @db.Uuid
idempotencyKey String @default(uuid()) @map("idempotency_key") @db.VarChar(128)
reportedAccountId String? @map("reported_account_id") @db.Uuid
bottleId String? @map("bottle_id") @db.Uuid
conversationId String? @map("conversation_id") @db.Uuid
@@ -388,6 +396,7 @@ model Report {
@@index([status, createdAt])
@@index([reportedAccountId, createdAt])
@@unique([reporterId, idempotencyKey])
@@map("reports")
}
@@ -439,6 +448,7 @@ model Sanction {
model Notification {
id String @id @default(uuid()) @db.Uuid
accountId String @map("account_id") @db.Uuid
dedupeKey String @map("dedupe_key") @db.VarChar(255)
type String @db.VarChar(100)
payload Json
status NotificationStatus @default(PENDING)
@@ -449,9 +459,19 @@ model Notification {
account Account @relation(fields: [accountId], references: [id], onDelete: Cascade)
@@index([accountId, status, createdAt])
@@unique([accountId, dedupeKey])
@@map("notifications")
}
model PushPreference {
accountId String @id @map("account_id") @db.Uuid
inAppEnabled Boolean @default(true) @map("in_app_enabled")
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
account Account @relation(fields: [accountId], references: [id], onDelete: Cascade)
@@map("push_preferences")
}
model OutboxEvent {
/// Processing lease index outbox_events_processing_lease_idx is managed in 0007 SQL.
id String @id @default(uuid()) @db.Uuid
+8 -1
View File
@@ -23,10 +23,16 @@ async function seed(): Promise<void> {
nickname: "星河",
publicId: "00000000-0000-4000-8000-000000000002",
},
{
key: "demo-admin",
nickname: "管理员",
publicId: "00000000-0000-4000-8000-000000000003",
},
].map(({ key, nickname, publicId }) =>
prisma.account.upsert({
where: { phoneHmac: id(`drift-bottle:${key}:phone-hmac`) },
update: {
role: key === "demo-admin" ? "ADMIN" : "USER",
anonymousProfile: {
upsert: {
create: {
@@ -44,6 +50,7 @@ async function seed(): Promise<void> {
},
},
create: {
role: key === "demo-admin" ? "ADMIN" : "USER",
phoneCiphertext: Buffer.from(
id(`drift-bottle:${key}:ciphertext`),
"hex",
@@ -78,7 +85,7 @@ async function seed(): Promise<void> {
});
console.info(
"Seed complete: 2 demo accounts, 2 anonymous profiles, 1 in-pool bottle.",
"Seed complete: 3 demo accounts (including admin), 3 anonymous profiles, 1 in-pool bottle.",
);
}