WARCH3/WCHECK1/WEXT1 完成受控上线后续自动化

Issue: WARCH3, WCHECK1, WEXT1
这个提交包含在:
2026-05-20 13:58:40 +08:00
父节点 512a58d40e
当前提交 56b720b8ab
共修改 28 个文件,包含 2909 行新增和 72 行删除
@@ -0,0 +1,85 @@
-- WCHECK1: 企微打卡摘要留存。
-- 仅保存规范化摘要、规则快照和 hash;不保存位置、人脸、设备明文或原始 payload。
CREATE TABLE "wecom_checkin_daily_summaries" (
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
"employee_id" UUID NOT NULL,
"external_user_id_prefix" VARCHAR(32),
"checkin_date" DATE NOT NULL,
"record_count" INTEGER NOT NULL DEFAULT 0,
"first_checkin_at" TIMESTAMP(3),
"last_checkin_at" TIMESTAMP(3),
"exception_count" INTEGER NOT NULL DEFAULT 0,
"abnormal_count" INTEGER NOT NULL DEFAULT 0,
"checkin_type_summary" JSONB,
"result_summary" JSONB,
"source_type_summary" JSONB,
"raw_hash_digest" VARCHAR(64),
"source_record_count" INTEGER NOT NULL DEFAULT 0,
"last_sync_run_id" UUID,
"archived_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "wecom_checkin_daily_summaries_pkey" PRIMARY KEY ("id")
);
CREATE TABLE "wecom_checkin_monthly_summaries" (
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
"employee_id" UUID NOT NULL,
"month" VARCHAR(7) NOT NULL,
"start_date" DATE NOT NULL,
"end_date" DATE NOT NULL,
"summary_day_count" INTEGER NOT NULL DEFAULT 0,
"checkin_day_count" INTEGER NOT NULL DEFAULT 0,
"exception_day_count" INTEGER NOT NULL DEFAULT 0,
"abnormal_record_count" INTEGER NOT NULL DEFAULT 0,
"missing_day_candidate_count" INTEGER NOT NULL DEFAULT 0,
"first_checkin_at" TIMESTAMP(3),
"last_checkin_at" TIMESTAMP(3),
"result_summary" JSONB,
"raw_hash_digest" VARCHAR(64),
"archived_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "wecom_checkin_monthly_summaries_pkey" PRIMARY KEY ("id")
);
CREATE TABLE "wecom_checkin_rule_snapshots" (
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
"employee_id" UUID,
"external_user_id_prefix" VARCHAR(32),
"snapshot_date" DATE NOT NULL,
"rule_hash" VARCHAR(64) NOT NULL,
"rule_summary" JSONB,
"source_endpoint" VARCHAR(120) NOT NULL DEFAULT '/cgi-bin/checkin/getcheckinoption',
"archived_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "wecom_checkin_rule_snapshots_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "wecom_checkin_daily_summaries_emp_date_ux" ON "wecom_checkin_daily_summaries"("employee_id", "checkin_date");
CREATE INDEX "idx_wecom_checkin_daily_summaries_date" ON "wecom_checkin_daily_summaries"("checkin_date");
CREATE INDEX "idx_wecom_checkin_daily_summaries_run" ON "wecom_checkin_daily_summaries"("last_sync_run_id");
CREATE UNIQUE INDEX "wecom_checkin_monthly_summaries_emp_month_ux" ON "wecom_checkin_monthly_summaries"("employee_id", "month");
CREATE INDEX "idx_wecom_checkin_monthly_summaries_month" ON "wecom_checkin_monthly_summaries"("month");
CREATE UNIQUE INDEX "wecom_checkin_rule_snapshots_emp_date_hash_ux" ON "wecom_checkin_rule_snapshots"("employee_id", "snapshot_date", "rule_hash");
CREATE INDEX "idx_wecom_checkin_rule_snapshots_date" ON "wecom_checkin_rule_snapshots"("snapshot_date");
CREATE INDEX "idx_wecom_checkin_rule_snapshots_hash" ON "wecom_checkin_rule_snapshots"("rule_hash");
ALTER TABLE "wecom_checkin_daily_summaries"
ADD CONSTRAINT "wecom_checkin_daily_summaries_employee_id_fkey"
FOREIGN KEY ("employee_id") REFERENCES "employees"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE "wecom_checkin_monthly_summaries"
ADD CONSTRAINT "wecom_checkin_monthly_summaries_employee_id_fkey"
FOREIGN KEY ("employee_id") REFERENCES "employees"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE "wecom_checkin_rule_snapshots"
ADD CONSTRAINT "wecom_checkin_rule_snapshots_employee_id_fkey"
FOREIGN KEY ("employee_id") REFERENCES "employees"("id") ON DELETE SET NULL ON UPDATE CASCADE;
+82 -2
查看文件
@@ -619,6 +619,9 @@ model Employee {
leave_requests LeaveRequest[]
leave_ledgers LeaveLedger[]
wecom_checkin_records WecomCheckinRecord[]
wecom_checkin_daily_summaries WecomCheckinDailySummary[]
wecom_checkin_monthly_summaries WecomCheckinMonthlySummary[]
wecom_checkin_rule_snapshots WecomCheckinRuleSnapshot[]
wecom_checkin_sync_runs WecomCheckinSyncRun[] @relation("WecomCheckinSyncRunTrigger")
wecom_directory_sync_runs WecomDirectorySyncRun[] @relation("WecomDirectorySyncRunEmployee")
triggered_wecom_directory_sync_runs WecomDirectorySyncRun[] @relation("WecomDirectorySyncRunTrigger")
@@ -704,7 +707,7 @@ model PasswordResetCode {
created_ip String?
created_user_agent String?
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade)
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Restrict)
@@index([employee_id, created_at])
@@index([expires_at])
@@ -718,7 +721,7 @@ model EmployeeRole {
created_at DateTime @default(now())
created_by String? @db.Uuid
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade)
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Restrict)
@@id([employee_id, role_code])
@@map("employee_roles")
@@ -1998,6 +2001,83 @@ model WecomCheckinRecord {
@@map("wecom_checkin_records")
}
model WecomCheckinDailySummary {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
external_user_id_prefix String? @db.VarChar(32)
checkin_date DateTime @db.Date
record_count Int @default(0)
first_checkin_at DateTime?
last_checkin_at DateTime?
exception_count Int @default(0)
abnormal_count Int @default(0)
checkin_type_summary Json?
result_summary Json?
source_type_summary Json?
raw_hash_digest String? @db.VarChar(64)
source_record_count Int @default(0)
last_sync_run_id String? @db.Uuid
archived_at DateTime @default(now())
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade)
@@unique([employee_id, checkin_date], map: "wecom_checkin_daily_summaries_emp_date_ux")
@@index([checkin_date], map: "idx_wecom_checkin_daily_summaries_date")
@@index([last_sync_run_id], map: "idx_wecom_checkin_daily_summaries_run")
@@map("wecom_checkin_daily_summaries")
}
model WecomCheckinMonthlySummary {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
month String @db.VarChar(7)
start_date DateTime @db.Date
end_date DateTime @db.Date
summary_day_count Int @default(0)
checkin_day_count Int @default(0)
exception_day_count Int @default(0)
abnormal_record_count Int @default(0)
missing_day_candidate_count Int @default(0)
first_checkin_at DateTime?
last_checkin_at DateTime?
result_summary Json?
raw_hash_digest String? @db.VarChar(64)
archived_at DateTime @default(now())
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade)
@@unique([employee_id, month], map: "wecom_checkin_monthly_summaries_emp_month_ux")
@@index([month], map: "idx_wecom_checkin_monthly_summaries_month")
@@map("wecom_checkin_monthly_summaries")
}
model WecomCheckinRuleSnapshot {
id String @id @default(uuid()) @db.Uuid
employee_id String? @db.Uuid
external_user_id_prefix String? @db.VarChar(32)
snapshot_date DateTime @db.Date
rule_hash String @db.VarChar(64)
rule_summary Json?
source_endpoint String @default("/cgi-bin/checkin/getcheckinoption") @db.VarChar(120)
archived_at DateTime @default(now())
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
employee Employee? @relation(fields: [employee_id], references: [id], onDelete: SetNull)
@@unique([employee_id, snapshot_date, rule_hash], map: "wecom_checkin_rule_snapshots_emp_date_hash_ux")
@@index([snapshot_date], map: "idx_wecom_checkin_rule_snapshots_date")
@@index([rule_hash], map: "idx_wecom_checkin_rule_snapshots_hash")
@@map("wecom_checkin_rule_snapshots")
}
model WecomDirectorySyncRun {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid