feat: 完成 R13/R14/R15/R16 小团队管理核心能力

这个提交包含在:
2026-03-21 20:31:34 +08:00
父节点 0826693a8f
当前提交 1e52f1c225
共修改 47 个文件,包含 4298 行新增和 306 行删除
@@ -0,0 +1,205 @@
-- CreateEnum
CREATE TYPE "WorkspaceProfile" AS ENUM ('small_team_internal');
-- CreateEnum
CREATE TYPE "WorkspaceOrgMode" AS ENUM ('single_company');
-- CreateEnum
CREATE TYPE "WorkspacePrimaryEntryMode" AS ENUM ('password');
-- CreateEnum
CREATE TYPE "WorkspaceNotificationStrategy" AS ENUM ('in_app');
-- CreateEnum
CREATE TYPE "WorkspaceDevicePriority" AS ENUM ('desktop');
-- CreateEnum
CREATE TYPE "EmploymentLifecycleStatus" AS ENUM ('onboarding', 'probation', 'active', 'offboarding', 'inactive');
-- CreateEnum
CREATE TYPE "EmployeeLifecycleEventType" AS ENUM ('onboard', 'regularize', 'transfer', 'offboard');
-- CreateEnum
CREATE TYPE "EmployeeChecklistScene" AS ENUM ('onboarding', 'offboarding', 'transfer');
-- CreateEnum
CREATE TYPE "EmployeeChecklistTaskType" AS ENUM ('account_setup', 'asset_issue', 'policy_ack', 'asset_return', 'access_revoke', 'expense_clearance', 'handover_note');
-- CreateEnum
CREATE TYPE "EmployeeChecklistTaskStatus" AS ENUM ('pending', 'completed', 'skipped');
-- CreateEnum
CREATE TYPE "EmployeePresenceStatus" AS ENUM ('office', 'remote', 'out', 'travel', 'leave');
-- CreateEnum
CREATE TYPE "EmployeePresenceTimeRange" AS ENUM ('full_day', 'morning', 'afternoon');
-- CreateEnum
CREATE TYPE "EmployeePresenceSource" AS ENUM ('manual', 'leave_sync');
-- CreateEnum
CREATE TYPE "WeeklyReportStatus" AS ENUM ('draft', 'submitted');
-- CreateEnum
CREATE TYPE "TodoPriority" AS ENUM ('low', 'normal', 'high');
-- CreateEnum
CREATE TYPE "TodoStatus" AS ENUM ('todo', 'doing', 'done', 'cancelled');
-- CreateEnum
CREATE TYPE "TodoSource" AS ENUM ('manual', 'weekly_report');
-- AlterTable
ALTER TABLE "employees" ADD COLUMN "emergency_contact_name" TEXT,
ADD COLUMN "emergency_contact_phone" TEXT,
ADD COLUMN "employment_status" "EmploymentLifecycleStatus" NOT NULL DEFAULT 'active',
ADD COLUMN "last_working_date" DATE,
ADD COLUMN "notes" TEXT,
ADD COLUMN "probation_end_date" DATE,
ADD COLUMN "work_location" TEXT;
-- CreateTable
CREATE TABLE "workspace_settings" (
"id" UUID NOT NULL,
"profile" "WorkspaceProfile" NOT NULL DEFAULT 'small_team_internal',
"org_mode" "WorkspaceOrgMode" NOT NULL DEFAULT 'single_company',
"primary_entry_mode" "WorkspacePrimaryEntryMode" NOT NULL DEFAULT 'password',
"notification_strategy" "WorkspaceNotificationStrategy" NOT NULL DEFAULT 'in_app',
"device_priority" "WorkspaceDevicePriority" NOT NULL DEFAULT 'desktop',
"module_visibility_json" JSONB,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_by" UUID,
CONSTRAINT "workspace_settings_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "employee_lifecycle_events" (
"id" UUID NOT NULL,
"employee_id" UUID NOT NULL,
"event_type" "EmployeeLifecycleEventType" NOT NULL,
"effective_date" DATE NOT NULL,
"payload_json" JSONB,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"created_by" UUID,
CONSTRAINT "employee_lifecycle_events_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "employee_checklist_tasks" (
"id" UUID NOT NULL,
"employee_id" UUID NOT NULL,
"scene" "EmployeeChecklistScene" NOT NULL,
"task_type" "EmployeeChecklistTaskType" NOT NULL,
"title" TEXT NOT NULL,
"status" "EmployeeChecklistTaskStatus" NOT NULL DEFAULT 'pending',
"due_date" DATE,
"completed_at" TIMESTAMP(3),
"completed_by" UUID,
"payload_json" JSONB,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "employee_checklist_tasks_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "employee_presence" (
"id" UUID NOT NULL,
"employee_id" UUID NOT NULL,
"date" DATE NOT NULL,
"status" "EmployeePresenceStatus" NOT NULL,
"time_range" "EmployeePresenceTimeRange" NOT NULL DEFAULT 'full_day',
"note" TEXT,
"source" "EmployeePresenceSource" NOT NULL DEFAULT 'manual',
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_by" UUID,
CONSTRAINT "employee_presence_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "weekly_reports" (
"id" UUID NOT NULL,
"employee_id" UUID NOT NULL,
"week_start" DATE NOT NULL,
"status" "WeeklyReportStatus" NOT NULL DEFAULT 'draft',
"highlights_text" TEXT NOT NULL DEFAULT '',
"blockers_text" TEXT NOT NULL DEFAULT '',
"next_week_text" TEXT NOT NULL DEFAULT '',
"needs_help" BOOLEAN NOT NULL DEFAULT false,
"submitted_at" TIMESTAMP(3),
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "weekly_reports_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "todo_items" (
"id" UUID NOT NULL,
"title" TEXT NOT NULL,
"description" TEXT,
"assignee_employee_id" UUID NOT NULL,
"created_by" UUID NOT NULL,
"due_date" DATE,
"priority" "TodoPriority" NOT NULL DEFAULT 'normal',
"status" "TodoStatus" NOT NULL DEFAULT 'todo',
"source" "TodoSource" NOT NULL DEFAULT 'manual',
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"completed_at" TIMESTAMP(3),
CONSTRAINT "todo_items_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "idx_employee_lifecycle_events_employee_date" ON "employee_lifecycle_events"("employee_id", "effective_date");
-- CreateIndex
CREATE INDEX "idx_employee_lifecycle_events_type_created" ON "employee_lifecycle_events"("event_type", "created_at");
-- CreateIndex
CREATE INDEX "idx_employee_checklist_tasks_employee_scene_status" ON "employee_checklist_tasks"("employee_id", "scene", "status");
-- CreateIndex
CREATE INDEX "idx_employee_checklist_tasks_scene_due" ON "employee_checklist_tasks"("scene", "due_date");
-- CreateIndex
CREATE INDEX "idx_employee_presence_date_status" ON "employee_presence"("date", "status");
-- CreateIndex
CREATE UNIQUE INDEX "employee_presence_employee_date_ux" ON "employee_presence"("employee_id", "date");
-- CreateIndex
CREATE INDEX "idx_weekly_reports_week_status" ON "weekly_reports"("week_start", "status");
-- CreateIndex
CREATE UNIQUE INDEX "weekly_reports_employee_week_start_ux" ON "weekly_reports"("employee_id", "week_start");
-- CreateIndex
CREATE INDEX "idx_todo_items_assignee_status_due" ON "todo_items"("assignee_employee_id", "status", "due_date");
-- CreateIndex
CREATE INDEX "idx_todo_items_creator_created" ON "todo_items"("created_by", "created_at");
-- AddForeignKey
ALTER TABLE "employee_lifecycle_events" ADD CONSTRAINT "employee_lifecycle_events_employee_id_fkey" FOREIGN KEY ("employee_id") REFERENCES "employees"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "employee_checklist_tasks" ADD CONSTRAINT "employee_checklist_tasks_employee_id_fkey" FOREIGN KEY ("employee_id") REFERENCES "employees"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "employee_presence" ADD CONSTRAINT "employee_presence_employee_id_fkey" FOREIGN KEY ("employee_id") REFERENCES "employees"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "weekly_reports" ADD CONSTRAINT "weekly_reports_employee_id_fkey" FOREIGN KEY ("employee_id") REFERENCES "employees"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "todo_items" ADD CONSTRAINT "todo_items_assignee_employee_id_fkey" FOREIGN KEY ("assignee_employee_id") REFERENCES "employees"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "todo_items" ADD CONSTRAINT "todo_items_created_by_fkey" FOREIGN KEY ("created_by") REFERENCES "employees"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
+243 -13
查看文件
@@ -265,6 +265,105 @@ enum BudgetControlRuleEntityType {
travel_request
}
enum WorkspaceProfile {
small_team_internal
}
enum WorkspaceOrgMode {
single_company
}
enum WorkspacePrimaryEntryMode {
password
}
enum WorkspaceNotificationStrategy {
in_app
}
enum WorkspaceDevicePriority {
desktop
}
enum EmploymentLifecycleStatus {
onboarding
probation
active
offboarding
inactive
}
enum EmployeeLifecycleEventType {
onboard
regularize
transfer
offboard
}
enum EmployeeChecklistScene {
onboarding
offboarding
transfer
}
enum EmployeeChecklistTaskType {
account_setup
asset_issue
policy_ack
asset_return
access_revoke
expense_clearance
handover_note
}
enum EmployeeChecklistTaskStatus {
pending
completed
skipped
}
enum EmployeePresenceStatus {
office
remote
out
travel
leave
}
enum EmployeePresenceTimeRange {
full_day
morning
afternoon
}
enum EmployeePresenceSource {
manual
leave_sync
}
enum WeeklyReportStatus {
draft
submitted
}
enum TodoPriority {
low
normal
high
}
enum TodoStatus {
todo
doing
done
cancelled
}
enum TodoSource {
manual
weekly_report
}
model Department {
id String @id @default(uuid()) @db.Uuid
name String
@@ -296,17 +395,24 @@ model Department {
}
model Employee {
id String @id @default(uuid()) @db.Uuid
employee_no String @unique
name String
phone String @unique
email String @unique
department_id String @db.Uuid
legal_entity_id String? @db.Uuid
title String
direct_manager_id String? @db.Uuid
hire_date DateTime @db.Date
status EmployeeStatus @default(active)
id String @id @default(uuid()) @db.Uuid
employee_no String @unique
name String
phone String @unique
email String @unique
department_id String @db.Uuid
legal_entity_id String? @db.Uuid
title String
direct_manager_id String? @db.Uuid
hire_date DateTime @db.Date
status EmployeeStatus @default(active)
employment_status EmploymentLifecycleStatus @default(active)
probation_end_date DateTime? @db.Date
last_working_date DateTime? @db.Date
work_location String?
emergency_contact_name String?
emergency_contact_phone String?
notes String?
bank_account_encrypted String
bank_name String
@@ -373,9 +479,15 @@ model Employee {
workflow_tasks_assigned WorkflowTask[] @relation("WorkflowTaskAssignee")
workflow_tasks_completed WorkflowTask[] @relation("WorkflowTaskCompleter")
approval_transfers_from ApprovalTransfer[] @relation("TransferFrom")
approval_transfers_to ApprovalTransfer[] @relation("TransferTo")
approval_transfers_from ApprovalTransfer[] @relation("TransferFrom")
approval_transfers_to ApprovalTransfer[] @relation("TransferTo")
password_reset_codes PasswordResetCode[]
lifecycle_events EmployeeLifecycleEvent[]
checklist_tasks EmployeeChecklistTask[]
presence_records EmployeePresence[]
weekly_reports WeeklyReport[]
todo_items_assigned TodoItem[] @relation("TodoAssignee")
todo_items_created TodoItem[] @relation("TodoCreator")
@@index([department_id])
@@index([legal_entity_id], map: "idx_employees_legal_entity")
@@ -1408,6 +1520,124 @@ model PortalAnnouncement {
@@map("portal_announcements")
}
model WorkspaceSetting {
id String @id @default(uuid()) @db.Uuid
profile WorkspaceProfile @default(small_team_internal)
org_mode WorkspaceOrgMode @default(single_company)
primary_entry_mode WorkspacePrimaryEntryMode @default(password)
notification_strategy WorkspaceNotificationStrategy @default(in_app)
device_priority WorkspaceDevicePriority @default(desktop)
module_visibility_json Json?
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
@@map("workspace_settings")
}
model EmployeeLifecycleEvent {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
event_type EmployeeLifecycleEventType
effective_date DateTime @db.Date
payload_json Json?
created_at DateTime @default(now())
created_by String? @db.Uuid
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade)
@@index([employee_id, effective_date], map: "idx_employee_lifecycle_events_employee_date")
@@index([event_type, created_at], map: "idx_employee_lifecycle_events_type_created")
@@map("employee_lifecycle_events")
}
model EmployeeChecklistTask {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
scene EmployeeChecklistScene
task_type EmployeeChecklistTaskType
title String
status EmployeeChecklistTaskStatus @default(pending)
due_date DateTime? @db.Date
completed_at DateTime?
completed_by String? @db.Uuid
payload_json Json?
created_at DateTime @default(now())
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade)
@@index([employee_id, scene, status], map: "idx_employee_checklist_tasks_employee_scene_status")
@@index([scene, due_date], map: "idx_employee_checklist_tasks_scene_due")
@@map("employee_checklist_tasks")
}
model EmployeePresence {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
date DateTime @db.Date
status EmployeePresenceStatus
time_range EmployeePresenceTimeRange @default(full_day)
note String?
source EmployeePresenceSource @default(manual)
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
updated_by String? @db.Uuid
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade)
@@unique([employee_id, date], map: "employee_presence_employee_date_ux")
@@index([date, status], map: "idx_employee_presence_date_status")
@@map("employee_presence")
}
model WeeklyReport {
id String @id @default(uuid()) @db.Uuid
employee_id String @db.Uuid
week_start DateTime @db.Date
status WeeklyReportStatus @default(draft)
highlights_text String @default("")
blockers_text String @default("")
next_week_text String @default("")
needs_help Boolean @default(false)
submitted_at DateTime?
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
employee Employee @relation(fields: [employee_id], references: [id], onDelete: Cascade)
@@unique([employee_id, week_start], map: "weekly_reports_employee_week_start_ux")
@@index([week_start, status], map: "idx_weekly_reports_week_status")
@@map("weekly_reports")
}
model TodoItem {
id String @id @default(uuid()) @db.Uuid
title String
description String?
assignee_employee_id String @db.Uuid
created_by String @db.Uuid
due_date DateTime? @db.Date
priority TodoPriority @default(normal)
status TodoStatus @default(todo)
source TodoSource @default(manual)
created_at DateTime @default(now())
updated_at DateTime @default(now()) @updatedAt
completed_at DateTime?
assignee Employee @relation("TodoAssignee", fields: [assignee_employee_id], references: [id], onDelete: Restrict)
creator Employee @relation("TodoCreator", fields: [created_by], references: [id], onDelete: Restrict)
@@index([assignee_employee_id, status, due_date], map: "idx_todo_items_assignee_status_due")
@@index([created_by, created_at], map: "idx_todo_items_creator_created")
@@map("todo_items")
}
model MigrationRun {
id String @id @default(uuid()) @db.Uuid
run_type String @db.VarChar(32)