1358 lines
71 KiB
SQL
1358 lines
71 KiB
SQL
-- Idempotent schema for CTMS (derived from current SQLAlchemy models)
|
||
|
||
DO $$
|
||
BEGIN
|
||
CREATE TYPE public.user_role AS ENUM ('ADMIN', 'PM', 'CRA', 'PV', 'IMP', 'QA');
|
||
EXCEPTION
|
||
WHEN duplicate_object THEN null;
|
||
END $$;
|
||
|
||
DO $$
|
||
BEGIN
|
||
ALTER TYPE public.user_role ADD VALUE IF NOT EXISTS 'QA';
|
||
EXCEPTION
|
||
WHEN duplicate_object THEN null;
|
||
END $$;
|
||
|
||
DO $$
|
||
BEGIN
|
||
CREATE TYPE public.user_status AS ENUM ('PENDING', 'ACTIVE', 'REJECTED', 'DISABLED');
|
||
EXCEPTION
|
||
WHEN duplicate_object THEN null;
|
||
END $$;
|
||
|
||
CREATE TABLE IF NOT EXISTS public.users (
|
||
id uuid PRIMARY KEY,
|
||
email character varying(255) NOT NULL,
|
||
password_hash character varying(255) NOT NULL,
|
||
full_name character varying(255) NOT NULL,
|
||
role public.user_role NOT NULL,
|
||
department character varying(255) NOT NULL,
|
||
status public.user_status NOT NULL DEFAULT 'PENDING',
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
updated_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
approved_by uuid,
|
||
approved_at timestamp with time zone,
|
||
avatar_url character varying(500),
|
||
CONSTRAINT uq_users_email UNIQUE (email)
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.studies (
|
||
id uuid PRIMARY KEY,
|
||
code character varying(50) NOT NULL,
|
||
name character varying(200) NOT NULL,
|
||
sponsor character varying(200),
|
||
protocol_no character varying(100),
|
||
phase character varying(50),
|
||
status character varying(20) NOT NULL DEFAULT 'DRAFT',
|
||
is_locked boolean NOT NULL DEFAULT false,
|
||
visit_schedule jsonb NOT NULL DEFAULT '[]'::jsonb,
|
||
created_by uuid,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT uq_studies_code UNIQUE (code),
|
||
CONSTRAINT fk_studies_created_by FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.sites (
|
||
id uuid PRIMARY KEY,
|
||
study_id uuid NOT NULL,
|
||
name character varying(200) NOT NULL,
|
||
city character varying(100),
|
||
pi_name character varying(100),
|
||
contact character varying(100),
|
||
is_active boolean NOT NULL DEFAULT true,
|
||
enrollment_target integer,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT fk_sites_study_id FOREIGN KEY (study_id) REFERENCES public.studies(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.study_members (
|
||
id uuid PRIMARY KEY,
|
||
study_id uuid NOT NULL,
|
||
user_id uuid NOT NULL,
|
||
role_in_study character varying(20) NOT NULL,
|
||
is_active boolean NOT NULL DEFAULT true,
|
||
added_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT uq_study_member UNIQUE (study_id, user_id),
|
||
CONSTRAINT fk_study_members_study_id FOREIGN KEY (study_id) REFERENCES public.studies(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_study_members_user_id FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.milestones (
|
||
id uuid PRIMARY KEY,
|
||
study_id uuid NOT NULL,
|
||
type character varying(50) NOT NULL,
|
||
name character varying(200) NOT NULL,
|
||
planned_date date,
|
||
adjusted_start_date date,
|
||
adjusted_end_date date,
|
||
actual_start_date date,
|
||
actual_end_date date,
|
||
actual_date date,
|
||
status character varying(20) NOT NULL DEFAULT 'NOT_STARTED',
|
||
site_id uuid,
|
||
owner_id uuid,
|
||
notes text,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
updated_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT fk_milestones_study_id FOREIGN KEY (study_id) REFERENCES public.studies(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_milestones_site_id FOREIGN KEY (site_id) REFERENCES public.sites(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_milestones_owner_id FOREIGN KEY (owner_id) REFERENCES public.users(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.subjects (
|
||
id uuid PRIMARY KEY,
|
||
study_id uuid NOT NULL,
|
||
site_id uuid NOT NULL,
|
||
subject_no character varying(50) NOT NULL,
|
||
status character varying(20) NOT NULL DEFAULT 'SCREENING',
|
||
screening_date date,
|
||
consent_date date,
|
||
enrollment_date date,
|
||
baseline_date date,
|
||
completion_date date,
|
||
drop_reason text,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
updated_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT uq_subject_study_no UNIQUE (study_id, subject_no),
|
||
CONSTRAINT fk_subjects_study_id FOREIGN KEY (study_id) REFERENCES public.studies(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_subjects_site_id FOREIGN KEY (site_id) REFERENCES public.sites(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.subject_histories (
|
||
id uuid PRIMARY KEY,
|
||
study_id uuid NOT NULL,
|
||
subject_id uuid NOT NULL,
|
||
record_date date,
|
||
content text NOT NULL,
|
||
created_by uuid,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
updated_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT fk_subject_histories_study_id FOREIGN KEY (study_id) REFERENCES public.studies(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_subject_histories_subject_id FOREIGN KEY (subject_id) REFERENCES public.subjects(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_subject_histories_created_by FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.subject_pds (
|
||
id uuid PRIMARY KEY,
|
||
study_id uuid NOT NULL,
|
||
subject_id uuid NOT NULL,
|
||
pd_no character varying(64) NOT NULL,
|
||
pd_type character varying(100) NOT NULL,
|
||
pd_level character varying(20) NOT NULL DEFAULT 'GENERAL',
|
||
approval_status character varying(20) NOT NULL DEFAULT 'DRAFT',
|
||
description text,
|
||
status character varying(20) NOT NULL DEFAULT 'OPEN',
|
||
created_by uuid,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
updated_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT uq_subject_pds_study_pd_no UNIQUE (study_id, pd_no),
|
||
CONSTRAINT fk_subject_pds_study_id FOREIGN KEY (study_id) REFERENCES public.studies(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_subject_pds_subject_id FOREIGN KEY (subject_id) REFERENCES public.subjects(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_subject_pds_created_by FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.visits (
|
||
id uuid PRIMARY KEY,
|
||
study_id uuid NOT NULL,
|
||
subject_id uuid NOT NULL,
|
||
visit_code character varying(50) NOT NULL,
|
||
planned_date date,
|
||
actual_date date,
|
||
status character varying(20) NOT NULL DEFAULT 'PLANNED',
|
||
window_start date,
|
||
window_end date,
|
||
notes text,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
updated_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT fk_visits_study_id FOREIGN KEY (study_id) REFERENCES public.studies(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_visits_subject_id FOREIGN KEY (subject_id) REFERENCES public.subjects(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.adverse_events (
|
||
id uuid PRIMARY KEY,
|
||
study_id uuid NOT NULL,
|
||
site_id uuid,
|
||
subject_id uuid,
|
||
visit_id uuid,
|
||
term character varying(255) NOT NULL,
|
||
onset_date date,
|
||
resolution_date date,
|
||
seriousness character varying(50) NOT NULL,
|
||
severity character varying(50) NOT NULL,
|
||
causality character varying(50),
|
||
action_taken text,
|
||
outcome character varying(50),
|
||
reported_to_sponsor boolean NOT NULL DEFAULT false,
|
||
is_sae boolean NOT NULL DEFAULT false,
|
||
is_susar boolean NOT NULL DEFAULT false,
|
||
report_due_date date,
|
||
status character varying(20) NOT NULL DEFAULT 'NEW',
|
||
description text,
|
||
created_by uuid NOT NULL,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
updated_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT fk_adverse_events_study_id FOREIGN KEY (study_id) REFERENCES public.studies(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_adverse_events_site_id FOREIGN KEY (site_id) REFERENCES public.sites(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_adverse_events_subject_id FOREIGN KEY (subject_id) REFERENCES public.subjects(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_adverse_events_created_by FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.finance_contracts (
|
||
id uuid PRIMARY KEY,
|
||
study_id uuid NOT NULL,
|
||
site_name character varying(255) NOT NULL,
|
||
contract_no character varying(100) NOT NULL,
|
||
signed_date date,
|
||
amount numeric(12, 2) NOT NULL,
|
||
currency character varying(10) NOT NULL DEFAULT 'CNY',
|
||
remark text,
|
||
created_by uuid,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
updated_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT fk_finance_contracts_study_id FOREIGN KEY (study_id) REFERENCES public.studies(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_finance_contracts_created_by FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.contract_fees (
|
||
id uuid PRIMARY KEY,
|
||
project_id uuid NOT NULL,
|
||
center_id uuid NOT NULL,
|
||
contract_amount numeric(12, 2) NOT NULL,
|
||
contract_cases integer NOT NULL,
|
||
actual_cases integer,
|
||
settlement_amount numeric(12, 2),
|
||
final_payment_amount numeric(12, 2),
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
updated_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT uq_contract_fees_project_center UNIQUE (project_id, center_id),
|
||
CONSTRAINT fk_contract_fees_project_id FOREIGN KEY (project_id) REFERENCES public.studies(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_contract_fees_center_id FOREIGN KEY (center_id) REFERENCES public.sites(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.contract_fee_payments (
|
||
id uuid PRIMARY KEY,
|
||
contract_fee_id uuid NOT NULL,
|
||
seq integer NOT NULL,
|
||
amount numeric(12, 2) NOT NULL,
|
||
paid_date date,
|
||
verified_date date,
|
||
is_paid boolean NOT NULL DEFAULT false,
|
||
is_verified boolean NOT NULL DEFAULT false,
|
||
remark text,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
updated_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT fk_contract_fee_payments_contract_fee_id FOREIGN KEY (contract_fee_id) REFERENCES public.contract_fees(id) ON DELETE CASCADE
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.fee_attachments (
|
||
id uuid PRIMARY KEY,
|
||
entity_type character varying(50) NOT NULL,
|
||
entity_id uuid NOT NULL,
|
||
file_type character varying(30) NOT NULL,
|
||
filename character varying(255) NOT NULL,
|
||
mime_type character varying(100),
|
||
size integer NOT NULL,
|
||
storage_key character varying(500),
|
||
url text,
|
||
uploaded_by uuid NOT NULL,
|
||
uploaded_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
is_deleted boolean NOT NULL DEFAULT false,
|
||
CONSTRAINT fk_fee_attachments_uploaded_by FOREIGN KEY (uploaded_by) REFERENCES public.users(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.finance_items (
|
||
id uuid PRIMARY KEY,
|
||
study_id uuid NOT NULL,
|
||
site_id uuid,
|
||
subject_id uuid,
|
||
visit_id uuid,
|
||
category character varying(50) NOT NULL,
|
||
title character varying(255) NOT NULL,
|
||
description text,
|
||
currency character varying(10) NOT NULL DEFAULT 'CNY',
|
||
amount numeric(12, 2) NOT NULL,
|
||
occur_date date NOT NULL,
|
||
status character varying(20) NOT NULL DEFAULT 'DRAFT',
|
||
submitted_at timestamp with time zone,
|
||
approved_at timestamp with time zone,
|
||
rejected_at timestamp with time zone,
|
||
paid_at timestamp with time zone,
|
||
approver_id uuid,
|
||
payer_id uuid,
|
||
reject_reason text,
|
||
created_by uuid NOT NULL,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
updated_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT fk_finance_items_study_id FOREIGN KEY (study_id) REFERENCES public.studies(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_finance_items_site_id FOREIGN KEY (site_id) REFERENCES public.sites(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_finance_items_subject_id FOREIGN KEY (subject_id) REFERENCES public.subjects(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_finance_items_approver_id FOREIGN KEY (approver_id) REFERENCES public.users(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_finance_items_payer_id FOREIGN KEY (payer_id) REFERENCES public.users(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_finance_items_created_by FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.drug_shipments (
|
||
id uuid PRIMARY KEY,
|
||
study_id uuid NOT NULL,
|
||
direction character varying(20) NOT NULL,
|
||
center_id uuid,
|
||
site_name character varying(255) NOT NULL,
|
||
ship_date date,
|
||
receive_date date,
|
||
quantity integer,
|
||
batch_no character varying(100),
|
||
carrier character varying(100),
|
||
tracking_no character varying(100),
|
||
status character varying(30) NOT NULL,
|
||
remark text,
|
||
created_by uuid,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
updated_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT fk_drug_shipments_study_id FOREIGN KEY (study_id) REFERENCES public.studies(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_drug_shipments_center_id FOREIGN KEY (center_id) REFERENCES public.sites(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_drug_shipments_created_by FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.material_equipments (
|
||
id uuid PRIMARY KEY,
|
||
study_id uuid NOT NULL,
|
||
name character varying(255) NOT NULL,
|
||
spec_model character varying(255) NOT NULL,
|
||
unit character varying(50),
|
||
brand character varying(100) NOT NULL,
|
||
origin character varying(255),
|
||
production_permit_file_name character varying(255),
|
||
tech_index_file_name character varying(255),
|
||
need_calibration boolean NOT NULL DEFAULT false,
|
||
calibration_cycle_days integer,
|
||
created_by uuid,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
updated_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT fk_material_equipments_study_id FOREIGN KEY (study_id) REFERENCES public.studies(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_material_equipments_created_by FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.startup_feasibility (
|
||
id uuid PRIMARY KEY,
|
||
study_id uuid NOT NULL,
|
||
site_id uuid,
|
||
submit_date date,
|
||
accept_date date,
|
||
approved_date date,
|
||
project_no character varying(100),
|
||
created_by uuid,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
updated_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT fk_startup_feasibility_study_id FOREIGN KEY (study_id) REFERENCES public.studies(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_startup_feasibility_site_id FOREIGN KEY (site_id) REFERENCES public.sites(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_startup_feasibility_created_by FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.startup_ethics (
|
||
id uuid PRIMARY KEY,
|
||
study_id uuid NOT NULL,
|
||
site_id uuid,
|
||
submit_date date,
|
||
accept_date date,
|
||
meeting_date date,
|
||
approved_date date,
|
||
approval_no character varying(100),
|
||
created_by uuid,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
updated_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT fk_startup_ethics_study_id FOREIGN KEY (study_id) REFERENCES public.studies(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_startup_ethics_site_id FOREIGN KEY (site_id) REFERENCES public.sites(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_startup_ethics_created_by FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.kickoff_meetings (
|
||
id uuid PRIMARY KEY,
|
||
study_id uuid NOT NULL,
|
||
site_id uuid,
|
||
kickoff_date date,
|
||
attendees json,
|
||
created_by uuid,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
updated_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT fk_kickoff_meetings_study_id FOREIGN KEY (study_id) REFERENCES public.studies(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_kickoff_meetings_site_id FOREIGN KEY (site_id) REFERENCES public.sites(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_kickoff_meetings_created_by FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.training_authorizations (
|
||
id uuid PRIMARY KEY,
|
||
study_id uuid NOT NULL,
|
||
name character varying(100) NOT NULL,
|
||
role character varying(100),
|
||
site_name character varying(255),
|
||
trained boolean NOT NULL DEFAULT false,
|
||
authorized boolean NOT NULL DEFAULT false,
|
||
trained_date date,
|
||
authorized_date date,
|
||
remark text,
|
||
created_by uuid,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
updated_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT fk_training_authorizations_study_id FOREIGN KEY (study_id) REFERENCES public.studies(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_training_authorizations_created_by FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.knowledge_notes (
|
||
id uuid PRIMARY KEY,
|
||
study_id uuid NOT NULL,
|
||
site_name character varying(255) NOT NULL,
|
||
title character varying(255) NOT NULL,
|
||
content text NOT NULL,
|
||
level character varying(50),
|
||
created_by uuid,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
updated_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT fk_knowledge_notes_study_id FOREIGN KEY (study_id) REFERENCES public.studies(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_knowledge_notes_created_by FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.faq_categories (
|
||
id uuid PRIMARY KEY,
|
||
study_id uuid,
|
||
name character varying(100) NOT NULL,
|
||
description text,
|
||
sort_order integer NOT NULL DEFAULT 0,
|
||
is_active boolean NOT NULL DEFAULT true,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
updated_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT uq_faq_category_name UNIQUE (study_id, name),
|
||
CONSTRAINT fk_faq_categories_study_id FOREIGN KEY (study_id) REFERENCES public.studies(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.faq_items (
|
||
id uuid PRIMARY KEY,
|
||
study_id uuid,
|
||
category_id uuid NOT NULL,
|
||
best_reply_id uuid,
|
||
status character varying(20) NOT NULL DEFAULT 'PENDING',
|
||
resolved_by_confirm boolean NOT NULL DEFAULT false,
|
||
question text NOT NULL,
|
||
answer text NOT NULL,
|
||
keywords character varying(255),
|
||
version integer NOT NULL DEFAULT 1,
|
||
is_active boolean NOT NULL DEFAULT true,
|
||
created_by uuid NOT NULL,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
updated_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT fk_faq_items_study_id FOREIGN KEY (study_id) REFERENCES public.studies(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_faq_items_category_id FOREIGN KEY (category_id) REFERENCES public.faq_categories(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_faq_items_created_by FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.faq_replies (
|
||
id uuid PRIMARY KEY,
|
||
faq_id uuid NOT NULL,
|
||
study_id uuid,
|
||
content text NOT NULL,
|
||
created_by uuid NOT NULL,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
quote_reply_id uuid,
|
||
is_deleted boolean NOT NULL DEFAULT false,
|
||
CONSTRAINT fk_faq_replies_faq_id FOREIGN KEY (faq_id) REFERENCES public.faq_items(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_faq_replies_study_id FOREIGN KEY (study_id) REFERENCES public.studies(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_faq_replies_created_by FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_faq_replies_quote_reply_id FOREIGN KEY (quote_reply_id) REFERENCES public.faq_replies(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
DO $$
|
||
BEGIN
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM pg_constraint WHERE conname = 'fk_faq_items_best_reply_id'
|
||
) THEN
|
||
ALTER TABLE public.faq_items
|
||
ADD CONSTRAINT fk_faq_items_best_reply_id
|
||
FOREIGN KEY (best_reply_id) REFERENCES public.faq_replies(id) ON DELETE SET NULL;
|
||
END IF;
|
||
END $$;
|
||
|
||
CREATE TABLE IF NOT EXISTS public.attachments (
|
||
id uuid PRIMARY KEY,
|
||
study_id uuid NOT NULL,
|
||
entity_type character varying(50) NOT NULL,
|
||
entity_id uuid NOT NULL,
|
||
filename character varying(255) NOT NULL,
|
||
file_path character varying(500) NOT NULL,
|
||
file_size integer NOT NULL,
|
||
content_type character varying(100),
|
||
uploaded_by uuid NOT NULL,
|
||
uploaded_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
is_deleted boolean NOT NULL DEFAULT false,
|
||
CONSTRAINT fk_attachments_study_id FOREIGN KEY (study_id) REFERENCES public.studies(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_attachments_uploaded_by FOREIGN KEY (uploaded_by) REFERENCES public.users(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.audit_logs (
|
||
id uuid PRIMARY KEY,
|
||
study_id uuid,
|
||
entity_type character varying(50) NOT NULL,
|
||
entity_id uuid,
|
||
action character varying(50) NOT NULL,
|
||
detail text,
|
||
operator_id uuid NOT NULL,
|
||
operator_role character varying(50) NOT NULL,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT fk_audit_logs_study_id FOREIGN KEY (study_id) REFERENCES public.studies(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_audit_logs_operator_id FOREIGN KEY (operator_id) REFERENCES public.users(id) ON DELETE RESTRICT
|
||
);
|
||
|
||
CREATE INDEX IF NOT EXISTS ix_sites_study_id ON public.sites (study_id);
|
||
CREATE INDEX IF NOT EXISTS ix_study_members_study_id ON public.study_members (study_id);
|
||
CREATE INDEX IF NOT EXISTS ix_milestones_study_id ON public.milestones (study_id);
|
||
CREATE INDEX IF NOT EXISTS ix_subjects_study_id ON public.subjects (study_id);
|
||
CREATE INDEX IF NOT EXISTS ix_subjects_site_id ON public.subjects (site_id);
|
||
CREATE INDEX IF NOT EXISTS ix_subject_histories_study_id ON public.subject_histories (study_id);
|
||
CREATE INDEX IF NOT EXISTS ix_subject_histories_subject_id ON public.subject_histories (subject_id);
|
||
CREATE INDEX IF NOT EXISTS ix_visits_subject_id ON public.visits (subject_id);
|
||
CREATE INDEX IF NOT EXISTS ix_adverse_events_study_id ON public.adverse_events (study_id);
|
||
CREATE INDEX IF NOT EXISTS ix_adverse_events_site_id ON public.adverse_events (site_id);
|
||
CREATE INDEX IF NOT EXISTS ix_adverse_events_subject_id ON public.adverse_events (subject_id);
|
||
CREATE INDEX IF NOT EXISTS ix_finance_contracts_study_id ON public.finance_contracts (study_id);
|
||
CREATE INDEX IF NOT EXISTS ix_finance_items_study_id ON public.finance_items (study_id);
|
||
CREATE INDEX IF NOT EXISTS ix_finance_items_site_id ON public.finance_items (site_id);
|
||
CREATE INDEX IF NOT EXISTS ix_finance_items_subject_id ON public.finance_items (subject_id);
|
||
CREATE INDEX IF NOT EXISTS ix_drug_shipments_study_id ON public.drug_shipments (study_id);
|
||
CREATE INDEX IF NOT EXISTS ix_drug_shipments_center_id ON public.drug_shipments (center_id);
|
||
CREATE INDEX IF NOT EXISTS ix_material_equipments_study_id ON public.material_equipments (study_id);
|
||
CREATE INDEX IF NOT EXISTS ix_startup_feasibility_study_id ON public.startup_feasibility (study_id);
|
||
CREATE INDEX IF NOT EXISTS ix_startup_ethics_study_id ON public.startup_ethics (study_id);
|
||
CREATE INDEX IF NOT EXISTS ix_kickoff_meetings_study_id ON public.kickoff_meetings (study_id);
|
||
CREATE INDEX IF NOT EXISTS ix_kickoff_meetings_site_id ON public.kickoff_meetings (site_id);
|
||
CREATE INDEX IF NOT EXISTS ix_training_authorizations_study_id ON public.training_authorizations (study_id);
|
||
CREATE INDEX IF NOT EXISTS ix_knowledge_notes_study_id ON public.knowledge_notes (study_id);
|
||
CREATE INDEX IF NOT EXISTS ix_faq_categories_study_id ON public.faq_categories (study_id);
|
||
CREATE INDEX IF NOT EXISTS ix_faq_items_study_id ON public.faq_items (study_id);
|
||
CREATE INDEX IF NOT EXISTS ix_faq_items_category_id ON public.faq_items (category_id);
|
||
CREATE INDEX IF NOT EXISTS ix_faq_replies_faq_id ON public.faq_replies (faq_id);
|
||
CREATE INDEX IF NOT EXISTS ix_faq_replies_study_id ON public.faq_replies (study_id);
|
||
|
||
-- =========================================
|
||
-- DEMO SEED DATA (idempotent)
|
||
-- =========================================
|
||
INSERT INTO public.users (
|
||
id, email, password_hash, full_name, role, department, status, created_at, updated_at
|
||
) VALUES
|
||
('11111111-1111-1111-1111-111111111111', 'admin@example.com', '$2y$12$FMfyBt7YfZWeh/x8WMEIA.S6VUTIMryk2NxWHyOOuJvd6YrDwHAdu', 'System Admin', 'ADMIN', 'SYSTEM', 'ACTIVE', '2025-01-05 08:00:00+00', '2025-01-05 08:00:00+00'),
|
||
('22222222-2222-2222-2222-222222222222', 'pm@example.com', '$2y$12$P9UDKu48ru84uVrlquqhXufmw/CfMEzQosC0X8eT2EmY/PgM/03j2', '项目经理', 'PM', 'PMO', 'ACTIVE', '2025-01-05 08:10:00+00', '2025-01-05 08:10:00+00'),
|
||
('33333333-3333-3333-3333-333333333333', 'cra@example.com', '$2y$12$D/.ZeDrUByPZFPnAl9MnKuzKn2G4ctKQ9IAS/CqS2sfMdE/5NaKAK', 'CRA 用户', 'CRA', 'CRA', 'ACTIVE', '2025-01-05 08:20:00+00', '2025-01-05 08:20:00+00'),
|
||
('44444444-4444-4444-4444-444444444444', 'qa@example.com', '$2y$12$P9UDKu48ru84uVrlquqhXufmw/CfMEzQosC0X8eT2EmY/PgM/03j2', 'QA 用户', 'QA', 'QA', 'ACTIVE', '2025-01-05 08:30:00+00', '2025-01-05 08:30:00+00'),
|
||
('55555555-5555-5555-5555-555555555555', 'pv@example.com', '$2y$12$P9UDKu48ru84uVrlquqhXufmw/CfMEzQosC0X8eT2EmY/PgM/03j2', 'PV 用户', 'PV', 'PV', 'ACTIVE', '2025-01-05 08:40:00+00', '2025-01-05 08:40:00+00'),
|
||
('66666666-6666-6666-6666-666666666666', 'imp@example.com', '$2y$12$P9UDKu48ru84uVrlquqhXufmw/CfMEzQosC0X8eT2EmY/PgM/03j2', '药品管理员', 'IMP', 'IMP', 'ACTIVE', '2025-01-05 08:50:00+00', '2025-01-05 08:50:00+00')
|
||
ON CONFLICT (email) DO UPDATE SET
|
||
password_hash = EXCLUDED.password_hash,
|
||
full_name = EXCLUDED.full_name,
|
||
role = EXCLUDED.role,
|
||
department = EXCLUDED.department,
|
||
status = EXCLUDED.status,
|
||
updated_at = EXCLUDED.updated_at;
|
||
|
||
INSERT INTO public.studies (
|
||
id, code, name, sponsor, protocol_no, phase, status, visit_schedule, created_by, created_at
|
||
) VALUES (
|
||
'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
|
||
'DEMO-CTMS',
|
||
'示例项目:糖尿病新药',
|
||
'Demo Pharma',
|
||
'DP-001',
|
||
'Phase II',
|
||
'ACTIVE',
|
||
'[
|
||
{"visit_code": "基线访视", "baseline_offset_days": 0, "window_before_days": 0, "window_after_days": 0},
|
||
{"visit_code": "V1", "baseline_offset_days": 7, "window_before_days": 2, "window_after_days": 2},
|
||
{"visit_code": "V2", "baseline_offset_days": 15, "window_before_days": 2, "window_after_days": 2},
|
||
{"visit_code": "V3", "baseline_offset_days": 23, "window_before_days": 3, "window_after_days": 3}
|
||
]'::jsonb,
|
||
(SELECT id FROM public.users WHERE email = 'admin@example.com'),
|
||
'2025-01-06 08:00:00+00'
|
||
) ON CONFLICT (code) DO UPDATE SET
|
||
name = EXCLUDED.name,
|
||
sponsor = EXCLUDED.sponsor,
|
||
protocol_no = EXCLUDED.protocol_no,
|
||
phase = EXCLUDED.phase,
|
||
status = EXCLUDED.status,
|
||
visit_schedule = EXCLUDED.visit_schedule,
|
||
created_by = EXCLUDED.created_by;
|
||
|
||
INSERT INTO public.sites (
|
||
id, study_id, name, city, pi_name, contact, enrollment_target, is_active, created_at
|
||
) VALUES
|
||
('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '北京协和医院', '北京', '李主任', '010-12345678', 80, true, '2025-01-06 09:00:00+00'),
|
||
('cccccccc-cccc-cccc-cccc-cccccccccccc', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '上海瑞金医院', '上海', '王主任', '021-12345678', 60, true, '2025-01-06 09:10:00+00'),
|
||
('dddddddd-cccc-bbbb-aaaa-eeeeeeeeeeee', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '示例中心 3', '北京', '赵主任', '010-12340000', 40, true, '2025-01-06 09:20:00+00')
|
||
ON CONFLICT (id) DO NOTHING;
|
||
|
||
INSERT INTO public.study_members (
|
||
id, study_id, user_id, role_in_study, is_active, added_at
|
||
) VALUES
|
||
('dddddddd-dddd-dddd-dddd-dddddddddddd', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), (SELECT id FROM public.users WHERE email = 'admin@example.com'), 'ADMIN', true, '2025-01-06 10:00:00+00'),
|
||
('eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), (SELECT id FROM public.users WHERE email = 'pm@example.com'), 'PM', true, '2025-01-06 10:05:00+00'),
|
||
('ffffffff-ffff-ffff-ffff-ffffffffffff', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), (SELECT id FROM public.users WHERE email = 'cra@example.com'), 'CRA', true, '2025-01-06 10:10:00+00'),
|
||
('11111111-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), (SELECT id FROM public.users WHERE email = 'qa@example.com'), 'QA', true, '2025-01-06 10:15:00+00'),
|
||
('22222222-3333-4444-5555-666666666666', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), (SELECT id FROM public.users WHERE email = 'pv@example.com'), 'PV', true, '2025-01-06 10:20:00+00'),
|
||
('33333333-4444-5555-6666-777777777777', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), (SELECT id FROM public.users WHERE email = 'imp@example.com'), 'IMP', true, '2025-01-06 10:25:00+00')
|
||
ON CONFLICT (study_id, user_id) DO UPDATE SET
|
||
role_in_study = EXCLUDED.role_in_study,
|
||
is_active = EXCLUDED.is_active;
|
||
|
||
INSERT INTO public.milestones (
|
||
id, study_id, type, name, planned_date, actual_date, status, site_id, owner_id, notes, created_at, updated_at
|
||
) VALUES
|
||
('10101010-aaaa-bbbb-cccc-111111111111', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), 'SIV', '启动会', '2025-01-10', '2025-01-12', 'DONE', 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', (SELECT id FROM public.users WHERE email = 'pm@example.com'), '现场启动完成', '2025-01-06 12:00:00+00', '2025-01-12 10:00:00+00'),
|
||
('20202020-aaaa-bbbb-cccc-222222222222', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), 'LPI', '首例入组', '2025-02-01', NULL, 'NOT_STARTED', 'cccccccc-cccc-cccc-cccc-cccccccccccc', (SELECT id FROM public.users WHERE email = 'cra@example.com'), '待完成', '2025-01-06 12:10:00+00', '2025-01-06 12:10:00+00')
|
||
ON CONFLICT (id) DO NOTHING;
|
||
|
||
INSERT INTO public.finance_contracts (
|
||
id, study_id, site_name, contract_no, signed_date, amount, currency, remark, created_by, created_at, updated_at
|
||
) VALUES
|
||
('77777777-aaaa-bbbb-cccc-111111111111', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '北京协和医院', 'CON-001', '2025-01-08', 1200000.00, 'CNY', '首批合同签署', (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-08 09:00:00+00', '2025-01-08 09:00:00+00'),
|
||
('77777777-aaaa-bbbb-cccc-222222222222', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '上海瑞金医院', 'CON-002', '2025-01-10', 980000.00, 'CNY', '分中心合同签署', (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-10 09:00:00+00', '2025-01-10 09:00:00+00')
|
||
ON CONFLICT (id) DO NOTHING;
|
||
|
||
INSERT INTO public.finance_items (
|
||
id, study_id, site_id, subject_id, visit_id, category, title, description, currency, amount, occur_date, status, submitted_at, approved_at, rejected_at, paid_at, approver_id, payer_id, reject_reason, created_by, created_at, updated_at
|
||
) VALUES
|
||
('99999999-aaaa-bbbb-cccc-111111111111', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', NULL, NULL, 'SITE_FEE', '启动费', '中心启动费用', 'CNY', 30000.00, '2025-01-12', 'PAID', '2025-01-13 08:00:00+00', '2025-01-13 10:00:00+00', NULL, '2025-01-15 09:00:00+00', (SELECT id FROM public.users WHERE email = 'pm@example.com'), (SELECT id FROM public.users WHERE email = 'admin@example.com'), NULL, (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-12 09:00:00+00', '2025-01-15 09:00:00+00'),
|
||
('99999999-aaaa-bbbb-cccc-222222222222', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), 'cccccccc-cccc-cccc-cccc-cccccccccccc', NULL, NULL, 'TRAVEL', '监查差旅', 'CRA 监查差旅报销', 'CNY', 4800.00, '2025-01-20', 'APPROVED', '2025-01-21 08:00:00+00', '2025-01-21 10:00:00+00', NULL, NULL, (SELECT id FROM public.users WHERE email = 'pm@example.com'), NULL, NULL, (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-20 10:00:00+00', '2025-01-21 10:00:00+00')
|
||
ON CONFLICT (id) DO NOTHING;
|
||
|
||
INSERT INTO public.drug_shipments (
|
||
id, study_id, direction, center_id, site_name, ship_date, receive_date, quantity, batch_no, carrier, tracking_no, status, remark, created_by, created_at, updated_at
|
||
) VALUES
|
||
('aaaa1111-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '寄送', 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', '北京协和医院', '2025-01-12', NULL, 200, 'DP-001', '顺丰', 'SF100001', '运输中', '首批寄送:DP-001 片剂 50mg × 200 盒', (SELECT id FROM public.users WHERE email = 'cra@example.com'), '2025-01-12 11:00:00+00', '2025-01-12 11:00:00+00'),
|
||
('bbbb1111-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '寄送', 'cccccccc-cccc-cccc-cccc-cccccccccccc', '上海瑞金医院', '2025-01-13', '2025-01-14', 150, 'DP-001', '京东物流', 'JD200002', '已签收', '分中心补货:DP-001 片剂 50mg × 150 盒', (SELECT id FROM public.users WHERE email = 'cra@example.com'), '2025-01-13 11:00:00+00', '2025-01-14 09:00:00+00'),
|
||
('cccc1111-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '回收', 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', '北京协和医院', '2025-02-05', '2025-02-06', NULL, 'DP-001', '顺丰', 'SF100003', '已回收', '首批回收:空药盒及剩余药品', (SELECT id FROM public.users WHERE email = 'cra@example.com'), '2025-02-05 14:00:00+00', '2025-02-06 09:00:00+00'),
|
||
('dddd1111-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '回收', 'cccccccc-cccc-cccc-cccc-cccccccccccc', '上海瑞金医院', '2025-02-08', NULL, NULL, 'DP-001', '德邦', 'DB300004', '运输中', '回收途中:冷链箱回收', (SELECT id FROM public.users WHERE email = 'cra@example.com'), '2025-02-08 16:00:00+00', '2025-02-08 16:00:00+00')
|
||
ON CONFLICT (id) DO NOTHING;
|
||
|
||
INSERT INTO public.startup_feasibility (
|
||
id, study_id, site_id, submit_date, accept_date, approved_date, project_no, created_by, created_at, updated_at
|
||
) VALUES
|
||
('eeee1111-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', '2025-01-02', '2025-01-05', '2025-01-10', 'FEA-BJ-001', (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-10 10:00:00+00', '2025-01-10 10:00:00+00'),
|
||
('ffff1111-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), 'cccccccc-cccc-cccc-cccc-cccccccccccc', '2025-01-03', '2025-01-06', '2025-01-12', 'FEA-SH-002', (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-12 10:00:00+00', '2025-01-12 10:00:00+00')
|
||
ON CONFLICT (id) DO NOTHING;
|
||
|
||
INSERT INTO public.startup_ethics (
|
||
id, study_id, site_id, submit_date, accept_date, meeting_date, approved_date, approval_no, created_by, created_at, updated_at
|
||
) VALUES
|
||
('1111aaaa-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', '2025-01-04', '2025-01-07', '2025-01-15', '2025-01-20', 'ETH-BJ-001', (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-20 11:00:00+00', '2025-01-20 11:00:00+00'),
|
||
('2222aaaa-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), 'cccccccc-cccc-cccc-cccc-cccccccccccc', '2025-01-05', '2025-01-08', '2025-01-16', '2025-01-22', 'ETH-SH-002', (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-22 11:00:00+00', '2025-01-22 11:00:00+00')
|
||
ON CONFLICT (id) DO NOTHING;
|
||
|
||
UPDATE public.startup_feasibility
|
||
SET site_id = (SELECT id FROM public.sites WHERE name ILIKE '%北京%' OR city ILIKE '%北京%' LIMIT 1)
|
||
WHERE site_id IS NULL AND project_no ILIKE '%-BJ-%';
|
||
|
||
UPDATE public.startup_feasibility
|
||
SET site_id = (SELECT id FROM public.sites WHERE name ILIKE '%上海%' OR city ILIKE '%上海%' LIMIT 1)
|
||
WHERE site_id IS NULL AND project_no ILIKE '%-SH-%';
|
||
|
||
UPDATE public.startup_ethics
|
||
SET site_id = (SELECT id FROM public.sites WHERE name ILIKE '%北京%' OR city ILIKE '%北京%' LIMIT 1)
|
||
WHERE site_id IS NULL AND approval_no ILIKE '%-BJ-%';
|
||
|
||
UPDATE public.startup_ethics
|
||
SET site_id = (SELECT id FROM public.sites WHERE name ILIKE '%上海%' OR city ILIKE '%上海%' LIMIT 1)
|
||
WHERE site_id IS NULL AND approval_no ILIKE '%-SH-%';
|
||
|
||
INSERT INTO public.kickoff_meetings (
|
||
id, study_id, site_id, kickoff_date, attendees, created_by, created_at, updated_at
|
||
) VALUES
|
||
(
|
||
'3333aaaa-2222-3333-4444-555555555555',
|
||
(SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'),
|
||
'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
|
||
'2025-01-12',
|
||
'["张三","李四","王五"]'::json,
|
||
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
|
||
'2025-01-12 13:00:00+00',
|
||
'2025-01-12 13:00:00+00'
|
||
),
|
||
(
|
||
'3333aaaa-2222-3333-4444-666666666666',
|
||
(SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'),
|
||
'cccccccc-cccc-cccc-cccc-cccccccccccc',
|
||
'2025-01-14',
|
||
'["赵六","钱七","孙八"]'::json,
|
||
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
|
||
'2025-01-14 13:00:00+00',
|
||
'2025-01-14 13:00:00+00'
|
||
),
|
||
(
|
||
'3333aaaa-2222-3333-4444-777777777777',
|
||
(SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'),
|
||
'dddddddd-cccc-bbbb-aaaa-eeeeeeeeeeee',
|
||
'2025-01-16',
|
||
'["周九","吴十"]'::json,
|
||
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
|
||
'2025-01-16 13:00:00+00',
|
||
'2025-01-16 13:00:00+00'
|
||
)
|
||
ON CONFLICT (id) DO NOTHING;
|
||
|
||
INSERT INTO public.training_authorizations (
|
||
id, study_id, name, role, site_name, trained, authorized, trained_date, authorized_date, remark, created_by, created_at, updated_at
|
||
) VALUES
|
||
('4444bbbb-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '赵敏', '研究医生', '北京协和医院', true, false, '2025-01-12', NULL, '待授权', (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-12 15:00:00+00', '2025-01-12 15:00:00+00'),
|
||
('5555bbbb-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '李雷', 'CRC', '上海瑞金医院', false, true, NULL, '2025-01-18', '先授权后补训', (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-18 15:00:00+00', '2025-01-18 15:00:00+00'),
|
||
('6666bbbb-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '韩梅梅', '药房管理员', '北京协和医院', true, true, '2025-01-10', '2025-01-12', '已完成', (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-12 16:00:00+00', '2025-01-12 16:00:00+00'),
|
||
('7777bbbb-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '王强', 'CRA', '上海瑞金医院', false, false, NULL, NULL, '待安排', (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-20 16:00:00+00', '2025-01-20 16:00:00+00'),
|
||
('8888bbbb-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '刘晨', '研究护士', '北京协和医院', true, false, '2025-01-14', NULL, '仅完成培训', (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-14 16:00:00+00', '2025-01-14 16:00:00+00')
|
||
ON CONFLICT (id) DO NOTHING;
|
||
|
||
INSERT INTO public.subjects (
|
||
id, study_id, site_id, subject_no, status, screening_date, consent_date, enrollment_date, baseline_date, completion_date, drop_reason, created_at, updated_at
|
||
) VALUES
|
||
('11111111-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'SUBJ-001', 'ENROLLED', '2025-01-05', '2025-01-06', '2025-01-10', '2025-01-10', NULL, NULL, '2025-01-10 10:00:00+00', '2025-01-10 10:00:00+00'),
|
||
('22222222-3333-4444-5555-666666666666', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), 'cccccccc-cccc-cccc-cccc-cccccccccccc', 'SUBJ-002', 'SCREENING', '2025-01-12', '2025-01-13', NULL, NULL, NULL, NULL, '2025-01-12 10:00:00+00', '2025-01-12 10:00:00+00'),
|
||
('33333333-4444-5555-6666-777777777777', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 'SUBJ-003', 'COMPLETED', '2024-12-20', '2024-12-21', '2024-12-28', '2024-12-28', '2025-02-05', NULL, '2025-02-05 10:00:00+00', '2025-02-05 10:00:00+00')
|
||
ON CONFLICT (study_id, subject_no) DO UPDATE SET
|
||
site_id = EXCLUDED.site_id,
|
||
status = EXCLUDED.status,
|
||
screening_date = EXCLUDED.screening_date,
|
||
consent_date = EXCLUDED.consent_date,
|
||
enrollment_date = EXCLUDED.enrollment_date,
|
||
baseline_date = EXCLUDED.baseline_date,
|
||
completion_date = EXCLUDED.completion_date,
|
||
drop_reason = EXCLUDED.drop_reason,
|
||
updated_at = EXCLUDED.updated_at;
|
||
|
||
INSERT INTO public.subject_pds (
|
||
id, study_id, subject_id, pd_no, pd_type, pd_level, approval_status, description, status, created_by, created_at, updated_at
|
||
) VALUES
|
||
(
|
||
'77777777-aaaa-bbbb-cccc-111111111111',
|
||
(SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'),
|
||
'11111111-2222-3333-4444-555555555555',
|
||
'PD0001',
|
||
'知情同意',
|
||
'MAJOR',
|
||
'DRAFT',
|
||
'知情记录不完整,缺少签署时间记录。',
|
||
'OPEN',
|
||
(SELECT id FROM public.users WHERE email = 'cra@example.com'),
|
||
'2025-01-11 10:00:00+00',
|
||
'2025-01-11 10:00:00+00'
|
||
),
|
||
(
|
||
'77777777-aaaa-bbbb-cccc-222222222222',
|
||
(SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'),
|
||
'11111111-2222-3333-4444-555555555555',
|
||
'PD0002',
|
||
'排除标准',
|
||
'CRITICAL',
|
||
'APPROVED',
|
||
'排除标准判定延期,已补充说明并审批通过。',
|
||
'OPEN',
|
||
(SELECT id FROM public.users WHERE email = 'cra@example.com'),
|
||
'2025-01-18 10:00:00+00',
|
||
'2025-01-19 09:00:00+00'
|
||
),
|
||
(
|
||
'77777777-aaaa-bbbb-cccc-333333333333',
|
||
(SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'),
|
||
'33333333-4444-5555-6666-777777777777',
|
||
'PD0003',
|
||
'访视流程',
|
||
'GENERAL',
|
||
'SUBMITTED',
|
||
'访视窗口外补录,已提交审批。',
|
||
'CLOSED',
|
||
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
|
||
'2025-02-06 10:00:00+00',
|
||
'2025-02-06 10:00:00+00'
|
||
)
|
||
ON CONFLICT (study_id, pd_no) DO UPDATE SET
|
||
subject_id = EXCLUDED.subject_id,
|
||
pd_type = EXCLUDED.pd_type,
|
||
pd_level = EXCLUDED.pd_level,
|
||
approval_status = EXCLUDED.approval_status,
|
||
description = EXCLUDED.description,
|
||
status = EXCLUDED.status,
|
||
created_by = EXCLUDED.created_by,
|
||
updated_at = EXCLUDED.updated_at;
|
||
|
||
INSERT INTO public.subject_histories (
|
||
id, study_id, subject_id, record_date, content, created_by, created_at, updated_at
|
||
) VALUES
|
||
('55555555-aaaa-bbbb-cccc-111111111111', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '11111111-2222-3333-4444-555555555555', '2025-01-08', '既往有高血压病史,长期服药控制。', (SELECT id FROM public.users WHERE email = 'cra@example.com'), '2025-01-08 11:00:00+00', '2025-01-08 11:00:00+00'),
|
||
('55555555-aaaa-bbbb-cccc-222222222222', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '22222222-3333-4444-5555-666666666666', '2025-01-13', '糖耐量异常,需进一步评估。', (SELECT id FROM public.users WHERE email = 'cra@example.com'), '2025-01-13 11:00:00+00', '2025-01-13 11:00:00+00'),
|
||
('55555555-aaaa-bbbb-cccc-333333333333', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '33333333-4444-5555-6666-777777777777', '2025-01-02', '既往病史稳定,未见严重合并症。', (SELECT id FROM public.users WHERE email = 'cra@example.com'), '2025-01-02 11:00:00+00', '2025-01-02 11:00:00+00')
|
||
ON CONFLICT (id) DO NOTHING;
|
||
|
||
INSERT INTO public.visits (
|
||
id, study_id, subject_id, visit_code, planned_date, actual_date, status, window_start, window_end, notes, created_at, updated_at
|
||
) VALUES
|
||
('44444444-aaaa-bbbb-cccc-111111111111', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '11111111-2222-3333-4444-555555555555', 'V1', '2025-01-09', '2025-01-10', 'DONE', '2025-01-07', '2025-01-12', '按期完成', '2025-01-10 12:00:00+00', '2025-01-10 12:00:00+00'),
|
||
('44444444-aaaa-bbbb-cccc-222222222222', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '11111111-2222-3333-4444-555555555555', 'FU1', '2025-02-09', NULL, 'PLANNED', '2025-02-07', '2025-02-12', NULL, '2025-01-20 12:00:00+00', '2025-01-20 12:00:00+00'),
|
||
('44444444-aaaa-bbbb-cccc-333333333333', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '11111111-2222-3333-4444-555555555555', 'FU2', '2025-03-11', NULL, 'PLANNED', '2025-03-09', '2025-03-14', NULL, '2025-02-01 12:00:00+00', '2025-02-01 12:00:00+00')
|
||
ON CONFLICT (id) DO NOTHING;
|
||
|
||
INSERT INTO public.adverse_events (
|
||
id, study_id, site_id, subject_id, visit_id, term, onset_date, resolution_date, seriousness, severity, causality, action_taken, outcome, reported_to_sponsor, report_due_date, status, description, created_by, created_at, updated_at
|
||
) VALUES
|
||
('66666666-aaaa-bbbb-cccc-111111111111', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', '11111111-2222-3333-4444-555555555555', '44444444-aaaa-bbbb-cccc-111111111111', '轻微头痛', '2025-01-10', '2025-01-11', 'NON_SERIOUS', 'MILD', '可能相关', '休息观察', '恢复', true, '2025-01-20', 'CLOSED', '参与者轻微头痛,次日缓解。', (SELECT id FROM public.users WHERE email = 'cra@example.com'), '2025-01-10 13:00:00+00', '2025-01-11 09:00:00+00'),
|
||
('66666666-aaaa-bbbb-cccc-222222222222', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), 'cccccccc-cccc-cccc-cccc-cccccccccccc', '22222222-3333-4444-5555-666666666666', NULL, '血糖升高', '2025-01-18', NULL, 'SERIOUS', 'SEVERE', '相关', '调整用药', '未恢复', false, '2025-01-28', 'NEW', '筛选期血糖异常。', (SELECT id FROM public.users WHERE email = 'cra@example.com'), '2025-01-18 13:00:00+00', '2025-01-18 13:00:00+00'),
|
||
('66666666-aaaa-bbbb-cccc-333333333333', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', '11111111-2222-3333-4444-555555555555', NULL, '轻度皮疹', '2025-02-10', NULL, 'NON_SERIOUS', 'MODERATE', '待评估', NULL, '未恢复', false, '2025-02-20', 'FOLLOW_UP', '出现皮疹,持续观察。', (SELECT id FROM public.users WHERE email = 'cra@example.com'), '2025-02-10 13:00:00+00', '2025-02-10 13:00:00+00')
|
||
ON CONFLICT (id) DO NOTHING;
|
||
|
||
INSERT INTO public.knowledge_notes (
|
||
id, study_id, site_name, title, content, level, created_by, created_at, updated_at
|
||
) VALUES
|
||
('4444aaaa-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '北京协和医院', '样本采集注意', '采血需在早晨空腹完成,样本 2 小时内送达。', '高', (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-12 18:00:00+00', '2025-01-12 18:00:00+00'),
|
||
('5555aaaa-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '上海瑞金医院', '访视资料上传', '访视完成后 24 小时内完成资料上传与核对。', '中', (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-15 18:00:00+00', '2025-01-15 18:00:00+00'),
|
||
('6666aaaa-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '北京协和医院', '冷链运输提醒', '冷链记录必须随药品一起封存。', '低', (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-18 18:00:00+00', '2025-01-18 18:00:00+00')
|
||
ON CONFLICT (id) DO NOTHING;
|
||
|
||
INSERT INTO public.faq_categories (
|
||
id, study_id, name, description, sort_order, is_active, created_at, updated_at
|
||
) VALUES
|
||
('7777aaaa-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '访视相关', '访视常见问题', 1, true, '2025-01-10 08:00:00+00', '2025-01-10 08:00:00+00'),
|
||
('8888aaaa-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '用药相关', '用药与回收问题', 2, true, '2025-01-10 08:10:00+00', '2025-01-10 08:10:00+00')
|
||
ON CONFLICT (study_id, name) DO UPDATE SET
|
||
description = EXCLUDED.description,
|
||
sort_order = EXCLUDED.sort_order,
|
||
is_active = EXCLUDED.is_active,
|
||
updated_at = EXCLUDED.updated_at;
|
||
|
||
INSERT INTO public.faq_items (
|
||
id, study_id, category_id, best_reply_id, status, resolved_by_confirm, question, answer, keywords, version, is_active, created_by, created_at, updated_at
|
||
) VALUES
|
||
('9999aaaa-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '7777aaaa-2222-3333-4444-555555555555', NULL, 'PENDING', false, 'V1 访视需要携带什么?', '携带知情同意书、病历及用药记录本。', '访视,携带物品', 1, true, (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-10 09:00:00+00', '2025-01-10 09:00:00+00'),
|
||
('aaaa9999-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '7777aaaa-2222-3333-4444-555555555555', NULL, 'PENDING', false, '访视延迟是否需要重新排期?', '若延迟超过窗口期需重新安排并备注原因。', '访视,窗口期', 1, true, (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-11 09:00:00+00', '2025-01-11 09:00:00+00'),
|
||
('bbbb9999-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '8888aaaa-2222-3333-4444-555555555555', NULL, 'PENDING', false, '药品回收需要哪些材料?', '需提供回收清单、交接单及冷链记录。', '回收,交接单', 1, true, (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-12 09:00:00+00', '2025-01-12 09:00:00+00'),
|
||
('cccc9999-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '8888aaaa-2222-3333-4444-555555555555', NULL, 'PENDING', false, '药品签收后如何登记?', '签收后在药品流向中登记并上传快递单。', '药品,签收', 1, true, (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-13 09:00:00+00', '2025-01-13 09:00:00+00'),
|
||
('dddd9999-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '7777aaaa-2222-3333-4444-555555555555', NULL, 'PENDING', false, '随访资料上传时间要求?', '访视结束 24 小时内上传。', '随访,资料上传', 1, true, (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-14 09:00:00+00', '2025-01-14 09:00:00+00')
|
||
ON CONFLICT (id) DO NOTHING;
|
||
|
||
INSERT INTO public.faq_replies (
|
||
id, faq_id, study_id, content, created_by, created_at, quote_reply_id, is_deleted
|
||
) VALUES
|
||
('eeee9999-2222-3333-4444-555555555555', '9999aaaa-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '如有特殊情况请提前沟通 PM。', (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-10 10:00:00+00', NULL, false),
|
||
('ffff9999-2222-3333-4444-555555555555', 'bbbb9999-2222-3333-4444-555555555555', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), '回收材料可在下载中心获取模板。', (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-12 10:00:00+00', NULL, false)
|
||
ON CONFLICT (id) DO NOTHING;
|
||
|
||
UPDATE public.faq_items
|
||
SET best_reply_id = 'eeee9999-2222-3333-4444-555555555555'
|
||
WHERE id = '9999aaaa-2222-3333-4444-555555555555'
|
||
AND best_reply_id IS NULL;
|
||
|
||
UPDATE public.faq_items
|
||
SET best_reply_id = 'ffff9999-2222-3333-4444-555555555555'
|
||
WHERE id = 'bbbb9999-2222-3333-4444-555555555555'
|
||
AND best_reply_id IS NULL;
|
||
|
||
INSERT INTO public.attachments (
|
||
id, study_id, entity_type, entity_id, filename, file_path, file_size, content_type, uploaded_by, uploaded_at, is_deleted
|
||
) VALUES
|
||
('aaaa0000-1111-2222-3333-444455556666', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), 'finance_contract', '77777777-aaaa-bbbb-cccc-111111111111', 'contract_beijing.pdf', '/code/app/uploads/study_aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/finance_contract_77777777-aaaa-bbbb-cccc-111111111111/contract_beijing.pdf', 1024, 'application/pdf', (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-08 10:00:00+00', false),
|
||
('bbbb0000-1111-2222-3333-444455556666', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), 'drug_shipment', 'aaaa1111-2222-3333-4444-555555555555', 'shipment_beijing.pdf', '/code/app/uploads/study_aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/drug_shipment_aaaa1111-2222-3333-4444-555555555555/shipment_beijing.pdf', 2048, 'application/pdf', (SELECT id FROM public.users WHERE email = 'cra@example.com'), '2025-01-12 12:00:00+00', false),
|
||
('cccc0000-1111-2222-3333-444455556666', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), 'startup_ethics', '1111aaaa-2222-3333-4444-555555555555', 'ethics_beijing.pdf', '/code/app/uploads/study_aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/startup_ethics_1111aaaa-2222-3333-4444-555555555555/ethics_beijing.pdf', 1536, 'application/pdf', (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-20 12:00:00+00', false),
|
||
('dddd0000-1111-2222-3333-444455556666', (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'), 'knowledge_note', '4444aaaa-2222-3333-4444-555555555555', 'note_sample.txt', '/code/app/uploads/study_aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/knowledge_note_4444aaaa-2222-3333-4444-555555555555/note_sample.txt', 512, 'text/plain', (SELECT id FROM public.users WHERE email = 'pm@example.com'), '2025-01-12 19:00:00+00', false)
|
||
ON CONFLICT (id) DO NOTHING;
|
||
|
||
-- =========================================
|
||
-- Document version management module
|
||
-- =========================================
|
||
|
||
DO $$
|
||
BEGIN
|
||
CREATE TYPE public.document_scope_type AS ENUM ('GLOBAL', 'SITE', 'DERIVED');
|
||
EXCEPTION
|
||
WHEN duplicate_object THEN null;
|
||
END $$;
|
||
|
||
DO $$
|
||
BEGIN
|
||
CREATE TYPE public.document_status AS ENUM ('DRAFT', 'ACTIVE', 'ARCHIVED');
|
||
EXCEPTION
|
||
WHEN duplicate_object THEN null;
|
||
END $$;
|
||
|
||
DO $$
|
||
BEGIN
|
||
CREATE TYPE public.document_version_status AS ENUM (
|
||
'DRAFT', 'SUBMITTED', 'APPROVED', 'EFFECTIVE', 'SUPERSEDED', 'ARCHIVED', 'WITHDRAWN'
|
||
);
|
||
EXCEPTION
|
||
WHEN duplicate_object THEN null;
|
||
END $$;
|
||
|
||
DO $$
|
||
BEGIN
|
||
CREATE TYPE public.distribution_target_type AS ENUM ('SITE', 'ROLE', 'USER');
|
||
EXCEPTION
|
||
WHEN duplicate_object THEN null;
|
||
END $$;
|
||
|
||
DO $$
|
||
BEGIN
|
||
CREATE TYPE public.distribution_status AS ENUM ('ACTIVE', 'CLOSED');
|
||
EXCEPTION
|
||
WHEN duplicate_object THEN null;
|
||
END $$;
|
||
|
||
DO $$
|
||
BEGIN
|
||
CREATE TYPE public.acknowledgement_type AS ENUM ('RECEIVED', 'READ', 'TRAINED');
|
||
EXCEPTION
|
||
WHEN duplicate_object THEN null;
|
||
END $$;
|
||
|
||
CREATE TABLE IF NOT EXISTS public.documents (
|
||
id uuid PRIMARY KEY,
|
||
trial_id uuid NOT NULL,
|
||
site_id uuid,
|
||
doc_no character varying(50) NOT NULL,
|
||
doc_type character varying(50) NOT NULL,
|
||
title character varying(200) NOT NULL,
|
||
scope_type public.document_scope_type NOT NULL DEFAULT 'GLOBAL',
|
||
owner_id uuid,
|
||
status public.document_status NOT NULL DEFAULT 'ACTIVE',
|
||
current_effective_version_id uuid,
|
||
description text,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
updated_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT uq_documents_trial_doc_no UNIQUE (trial_id, doc_no),
|
||
CONSTRAINT fk_documents_trial_id FOREIGN KEY (trial_id) REFERENCES public.studies(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_documents_site_id FOREIGN KEY (site_id) REFERENCES public.sites(id) ON DELETE SET NULL,
|
||
CONSTRAINT fk_documents_owner_id FOREIGN KEY (owner_id) REFERENCES public.users(id) ON DELETE SET NULL
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.document_versions (
|
||
id uuid PRIMARY KEY,
|
||
document_id uuid NOT NULL,
|
||
version_no character varying(50) NOT NULL,
|
||
parent_version_id uuid,
|
||
derived_from_version_id uuid,
|
||
status public.document_version_status NOT NULL DEFAULT 'DRAFT',
|
||
effective_at timestamp with time zone,
|
||
superseded_at timestamp with time zone,
|
||
file_uri character varying(500) NOT NULL,
|
||
file_hash character varying(128) NOT NULL,
|
||
file_size bigint NOT NULL,
|
||
mime_type character varying(100),
|
||
change_summary text,
|
||
created_by uuid,
|
||
submitted_at timestamp with time zone,
|
||
approved_at timestamp with time zone,
|
||
withdrawn_at timestamp with time zone,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT uq_document_versions_doc_no UNIQUE (document_id, version_no),
|
||
CONSTRAINT fk_document_versions_document_id FOREIGN KEY (document_id) REFERENCES public.documents(id) ON DELETE CASCADE,
|
||
CONSTRAINT fk_document_versions_parent_id FOREIGN KEY (parent_version_id) REFERENCES public.document_versions(id) ON DELETE SET NULL,
|
||
CONSTRAINT fk_document_versions_derived_id FOREIGN KEY (derived_from_version_id) REFERENCES public.document_versions(id) ON DELETE SET NULL,
|
||
CONSTRAINT fk_document_versions_created_by FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE SET NULL
|
||
);
|
||
|
||
DO $$
|
||
BEGIN
|
||
ALTER TABLE public.documents
|
||
ADD CONSTRAINT fk_documents_current_effective_version_id
|
||
FOREIGN KEY (current_effective_version_id)
|
||
REFERENCES public.document_versions(id)
|
||
ON DELETE SET NULL;
|
||
EXCEPTION
|
||
WHEN duplicate_object THEN null;
|
||
END $$;
|
||
|
||
CREATE TABLE IF NOT EXISTS public.distributions (
|
||
id uuid PRIMARY KEY,
|
||
document_id uuid NOT NULL,
|
||
version_id uuid NOT NULL,
|
||
target_type public.distribution_target_type NOT NULL,
|
||
target_id character varying(50) NOT NULL,
|
||
status public.distribution_status NOT NULL DEFAULT 'ACTIVE',
|
||
due_at timestamp with time zone,
|
||
closed_at timestamp with time zone,
|
||
created_by uuid,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT fk_distributions_document_id FOREIGN KEY (document_id) REFERENCES public.documents(id) ON DELETE CASCADE,
|
||
CONSTRAINT fk_distributions_version_id FOREIGN KEY (version_id) REFERENCES public.document_versions(id) ON DELETE CASCADE,
|
||
CONSTRAINT fk_distributions_created_by FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE SET NULL
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS public.acknowledgements (
|
||
id uuid PRIMARY KEY,
|
||
distribution_id uuid NOT NULL,
|
||
user_id uuid NOT NULL,
|
||
site_id uuid,
|
||
ack_type public.acknowledgement_type NOT NULL,
|
||
due_at timestamp with time zone,
|
||
acked_at timestamp with time zone,
|
||
note text,
|
||
created_at timestamp with time zone NOT NULL DEFAULT now(),
|
||
CONSTRAINT uq_ack_distribution_user_type UNIQUE (distribution_id, user_id, ack_type),
|
||
CONSTRAINT fk_acknowledgements_distribution_id FOREIGN KEY (distribution_id) REFERENCES public.distributions(id) ON DELETE CASCADE,
|
||
CONSTRAINT fk_acknowledgements_user_id FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE RESTRICT,
|
||
CONSTRAINT fk_acknowledgements_site_id FOREIGN KEY (site_id) REFERENCES public.sites(id) ON DELETE SET NULL
|
||
);
|
||
|
||
CREATE UNIQUE INDEX IF NOT EXISTS ix_documents_trial_doc_no ON public.documents (trial_id, doc_no);
|
||
CREATE INDEX IF NOT EXISTS ix_documents_site_id ON public.documents (site_id);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS ix_document_versions_doc_no ON public.document_versions (document_id, version_no);
|
||
CREATE INDEX IF NOT EXISTS ix_document_versions_status ON public.document_versions (document_id, status);
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uq_document_versions_pending ON public.document_versions (document_id)
|
||
WHERE status IN ('SUBMITTED', 'APPROVED');
|
||
CREATE INDEX IF NOT EXISTS ix_distributions_version_target ON public.distributions (version_id, target_type, target_id);
|
||
CREATE INDEX IF NOT EXISTS ix_acknowledgements_distribution_type ON public.acknowledgements (distribution_id, ack_type);
|
||
CREATE INDEX IF NOT EXISTS ix_audit_logs_entity_at ON public.audit_logs (entity_type, entity_id, created_at);
|
||
|
||
DO $$
|
||
BEGIN
|
||
ALTER TABLE public.kickoff_meetings
|
||
ADD CONSTRAINT uq_kickoff_meetings_study_site UNIQUE (study_id, site_id);
|
||
EXCEPTION
|
||
WHEN duplicate_object THEN null;
|
||
END $$;
|
||
|
||
-- =========================================
|
||
-- Demo data for document version management
|
||
-- =========================================
|
||
|
||
INSERT INTO public.documents (
|
||
id, trial_id, site_id, doc_no, doc_type, title, scope_type, owner_id, status, current_effective_version_id, description, created_at, updated_at
|
||
) VALUES
|
||
(
|
||
'33333333-aaaa-bbbb-cccc-333333333333',
|
||
(SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'),
|
||
NULL,
|
||
'SOP-001',
|
||
'SOP',
|
||
'中心文件管理 SOP',
|
||
'GLOBAL',
|
||
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
|
||
'ACTIVE',
|
||
NULL,
|
||
'Demo SOP document',
|
||
'2025-01-08 09:00:00+00',
|
||
'2025-01-12 09:00:00+00'
|
||
),
|
||
(
|
||
'44444444-aaaa-bbbb-cccc-444444444444',
|
||
(SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'),
|
||
NULL,
|
||
'TRN-101',
|
||
'Training',
|
||
'中心培训材料',
|
||
'GLOBAL',
|
||
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
|
||
'ACTIVE',
|
||
NULL,
|
||
'Training deck',
|
||
'2025-01-08 10:00:00+00',
|
||
'2025-01-10 10:00:00+00'
|
||
),
|
||
(
|
||
'55555555-aaaa-bbbb-cccc-555555555555',
|
||
(SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'),
|
||
(SELECT id FROM public.sites WHERE name = '北京协和医院' AND study_id = (SELECT id FROM public.studies WHERE code = 'DEMO-CTMS')),
|
||
'SITE-SOP-001',
|
||
'SOP',
|
||
'北京协和中心 SOP',
|
||
'SITE',
|
||
(SELECT id FROM public.users WHERE email = 'cra@example.com'),
|
||
'ACTIVE',
|
||
NULL,
|
||
'Beijing site SOP',
|
||
'2025-01-09 09:00:00+00',
|
||
'2025-01-12 09:00:00+00'
|
||
)
|
||
ON CONFLICT (trial_id, doc_no) DO NOTHING;
|
||
|
||
INSERT INTO public.document_versions (
|
||
id, document_id, version_no, parent_version_id, derived_from_version_id, status, effective_at, superseded_at,
|
||
file_uri, file_hash, file_size, mime_type, change_summary, created_by, submitted_at, approved_at, withdrawn_at, created_at
|
||
) VALUES
|
||
(
|
||
'55555555-aaaa-bbbb-cccc-555555555555',
|
||
'33333333-aaaa-bbbb-cccc-333333333333',
|
||
'1.0',
|
||
NULL,
|
||
NULL,
|
||
'EFFECTIVE',
|
||
'2025-01-10 09:00:00+00',
|
||
NULL,
|
||
'/code/app/uploads/documents/demo/SOP-001-v1.pdf',
|
||
'b7c52b35f0b8e4a3c6d5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3',
|
||
2048,
|
||
'application/pdf',
|
||
'首次发布',
|
||
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
|
||
'2025-01-09 09:00:00+00',
|
||
'2025-01-10 08:30:00+00',
|
||
NULL,
|
||
'2025-01-08 09:30:00+00'
|
||
),
|
||
(
|
||
'66666666-aaaa-bbbb-cccc-666666666666',
|
||
'33333333-aaaa-bbbb-cccc-333333333333',
|
||
'1.1',
|
||
'55555555-aaaa-bbbb-cccc-555555555555',
|
||
NULL,
|
||
'APPROVED',
|
||
NULL,
|
||
NULL,
|
||
'/code/app/uploads/documents/demo/SOP-001-v1.1.pdf',
|
||
'c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4',
|
||
3072,
|
||
'application/pdf',
|
||
'小幅更新',
|
||
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
|
||
'2025-01-12 10:00:00+00',
|
||
'2025-01-12 11:00:00+00',
|
||
NULL,
|
||
'2025-01-12 09:30:00+00'
|
||
),
|
||
(
|
||
'77777777-aaaa-bbbb-cccc-777777777777',
|
||
'44444444-aaaa-bbbb-cccc-444444444444',
|
||
'1.0',
|
||
NULL,
|
||
NULL,
|
||
'DRAFT',
|
||
NULL,
|
||
NULL,
|
||
'/code/app/uploads/documents/demo/TRN-101-v1.pdf',
|
||
'd2c1b0a9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1',
|
||
4096,
|
||
'application/pdf',
|
||
'培训材料草稿',
|
||
(SELECT id FROM public.users WHERE email = 'cra@example.com'),
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
'2025-01-10 10:30:00+00'
|
||
),
|
||
(
|
||
'88888888-aaaa-bbbb-cccc-888888888888',
|
||
'55555555-aaaa-bbbb-cccc-555555555555',
|
||
'1.0',
|
||
NULL,
|
||
NULL,
|
||
'EFFECTIVE',
|
||
'2025-01-10 09:00:00+00',
|
||
NULL,
|
||
'/code/app/uploads/documents/demo/SITE-SOP-001-v1.pdf',
|
||
'a3b2c1d0e9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2',
|
||
2048,
|
||
'application/pdf',
|
||
'分中心 SOP 首版',
|
||
(SELECT id FROM public.users WHERE email = 'cra@example.com'),
|
||
'2025-01-09 09:30:00+00',
|
||
'2025-01-10 08:30:00+00',
|
||
NULL,
|
||
'2025-01-09 09:00:00+00'
|
||
)
|
||
ON CONFLICT (document_id, version_no) DO NOTHING;
|
||
|
||
UPDATE public.documents
|
||
SET current_effective_version_id = '55555555-aaaa-bbbb-cccc-555555555555',
|
||
updated_at = '2025-01-12 09:00:00+00'
|
||
WHERE id = '33333333-aaaa-bbbb-cccc-333333333333'
|
||
AND current_effective_version_id IS NULL;
|
||
|
||
UPDATE public.documents
|
||
SET current_effective_version_id = '88888888-aaaa-bbbb-cccc-888888888888',
|
||
updated_at = '2025-01-10 09:10:00+00'
|
||
WHERE id = '55555555-aaaa-bbbb-cccc-555555555555'
|
||
AND current_effective_version_id IS NULL;
|
||
|
||
INSERT INTO public.distributions (
|
||
id, document_id, version_id, target_type, target_id, status, due_at, closed_at, created_by, created_at
|
||
) VALUES
|
||
(
|
||
'bbbb0000-1111-2222-3333-444444444444',
|
||
'33333333-aaaa-bbbb-cccc-333333333333',
|
||
'55555555-aaaa-bbbb-cccc-555555555555',
|
||
'SITE',
|
||
'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
|
||
'ACTIVE',
|
||
'2025-01-20 18:00:00+00',
|
||
NULL,
|
||
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
|
||
'2025-01-12 12:00:00+00'
|
||
),
|
||
(
|
||
'cccc0000-1111-2222-3333-555555555555',
|
||
'33333333-aaaa-bbbb-cccc-333333333333',
|
||
'55555555-aaaa-bbbb-cccc-555555555555',
|
||
'SITE',
|
||
'cccccccc-cccc-cccc-cccc-cccccccccccc',
|
||
'ACTIVE',
|
||
'2025-01-20 18:00:00+00',
|
||
NULL,
|
||
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
|
||
'2025-01-12 12:05:00+00'
|
||
),
|
||
(
|
||
'dddd0000-1111-2222-3333-666666666666',
|
||
'33333333-aaaa-bbbb-cccc-333333333333',
|
||
'55555555-aaaa-bbbb-cccc-555555555555',
|
||
'USER',
|
||
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
|
||
'ACTIVE',
|
||
'2025-01-20 18:00:00+00',
|
||
NULL,
|
||
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
|
||
'2025-01-12 12:10:00+00'
|
||
),
|
||
(
|
||
'eeee0000-1111-2222-3333-777777777777',
|
||
'33333333-aaaa-bbbb-cccc-333333333333',
|
||
'55555555-aaaa-bbbb-cccc-555555555555',
|
||
'USER',
|
||
(SELECT id FROM public.users WHERE email = 'cra@example.com'),
|
||
'ACTIVE',
|
||
'2025-01-20 18:00:00+00',
|
||
NULL,
|
||
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
|
||
'2025-01-12 12:15:00+00'
|
||
)
|
||
ON CONFLICT (id) DO NOTHING;
|
||
|
||
INSERT INTO public.acknowledgements (
|
||
id, distribution_id, user_id, site_id, ack_type, due_at, acked_at, note, created_at
|
||
) VALUES
|
||
(
|
||
'ffff0000-1111-2222-3333-888888888888',
|
||
'dddd0000-1111-2222-3333-666666666666',
|
||
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
|
||
NULL,
|
||
'RECEIVED',
|
||
'2025-01-20 18:00:00+00',
|
||
'2025-01-12 13:00:00+00',
|
||
'已收到',
|
||
'2025-01-12 13:00:00+00'
|
||
),
|
||
(
|
||
'11110000-2222-3333-4444-999999999999',
|
||
'eeee0000-1111-2222-3333-777777777777',
|
||
(SELECT id FROM public.users WHERE email = 'cra@example.com'),
|
||
NULL,
|
||
'READ',
|
||
'2025-01-20 18:00:00+00',
|
||
'2025-01-12 14:00:00+00',
|
||
'已阅读',
|
||
'2025-01-12 14:00:00+00'
|
||
)
|
||
ON CONFLICT (id) DO NOTHING;
|
||
|
||
INSERT INTO public.audit_logs (
|
||
id, study_id, entity_type, entity_id, action, detail, operator_id, operator_role, created_at
|
||
) VALUES
|
||
(
|
||
'22220000-3333-4444-5555-aaaaaaaaaaaa',
|
||
(SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'),
|
||
'DOCUMENT',
|
||
'33333333-aaaa-bbbb-cccc-333333333333',
|
||
'DOCUMENT_CREATED',
|
||
'{"before":null,"after":{"doc_no":"SOP-001","status":"ACTIVE"}}',
|
||
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
|
||
'PM',
|
||
'2025-01-08 09:00:00+00'
|
||
),
|
||
(
|
||
'33330000-4444-5555-6666-bbbbbbbbbbbb',
|
||
(SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'),
|
||
'DOCUMENT_VERSION',
|
||
'55555555-aaaa-bbbb-cccc-555555555555',
|
||
'VERSION_EFFECTIVE',
|
||
'{"before":{"status":"APPROVED"},"after":{"status":"EFFECTIVE"}}',
|
||
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
|
||
'PM',
|
||
'2025-01-10 09:00:00+00'
|
||
),
|
||
(
|
||
'44440000-5555-6666-7777-cccccccccccc',
|
||
(SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'),
|
||
'DISTRIBUTION',
|
||
'bbbb0000-1111-2222-3333-444444444444',
|
||
'DISTRIBUTION_CREATED',
|
||
'{"version_id":"55555555-aaaa-bbbb-cccc-555555555555","target_id":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}',
|
||
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
|
||
'PM',
|
||
'2025-01-12 12:00:00+00'
|
||
),
|
||
(
|
||
'55550000-6666-7777-8888-dddddddddddd',
|
||
(SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'),
|
||
'ACKNOWLEDGEMENT',
|
||
'ffff0000-1111-2222-3333-888888888888',
|
||
'ACK_CREATED',
|
||
'{"distribution_id":"dddd0000-1111-2222-3333-666666666666","ack_type":"RECEIVED"}',
|
||
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
|
||
'PM',
|
||
'2025-01-12 13:00:00+00'
|
||
)
|
||
ON CONFLICT (id) DO NOTHING;
|
||
|
||
-- Demo transition: v1.1 effective, v1.0 superseded
|
||
UPDATE public.document_versions
|
||
SET status = 'SUPERSEDED',
|
||
superseded_at = '2025-01-15 09:00:00+00'
|
||
WHERE id = '55555555-aaaa-bbbb-cccc-555555555555'
|
||
AND status = 'EFFECTIVE';
|
||
|
||
UPDATE public.document_versions
|
||
SET status = 'EFFECTIVE',
|
||
effective_at = '2025-01-15 09:00:00+00'
|
||
WHERE id = '66666666-aaaa-bbbb-cccc-666666666666'
|
||
AND status = 'APPROVED';
|
||
|
||
UPDATE public.documents
|
||
SET current_effective_version_id = '66666666-aaaa-bbbb-cccc-666666666666',
|
||
updated_at = '2025-01-15 09:00:00+00'
|
||
WHERE id = '33333333-aaaa-bbbb-cccc-333333333333';
|
||
|
||
-- Lightweight demo: one extra draft document
|
||
INSERT INTO public.documents (
|
||
id, trial_id, site_id, doc_no, doc_type, title, scope_type, owner_id, status, current_effective_version_id, description, created_at, updated_at
|
||
) VALUES
|
||
(
|
||
'99999999-eeee-ffff-aaaa-000000000001',
|
||
(SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'),
|
||
NULL,
|
||
'QRG-001',
|
||
'Guidance',
|
||
'研究快速指引',
|
||
'GLOBAL',
|
||
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
|
||
'ACTIVE',
|
||
NULL,
|
||
'Quick reference guide (draft)',
|
||
'2025-01-16 09:00:00+00',
|
||
'2025-01-16 09:00:00+00'
|
||
)
|
||
ON CONFLICT (trial_id, doc_no) DO NOTHING;
|
||
|
||
INSERT INTO public.document_versions (
|
||
id, document_id, version_no, parent_version_id, derived_from_version_id, status, effective_at, superseded_at,
|
||
file_uri, file_hash, file_size, mime_type, change_summary, created_by, submitted_at, approved_at, withdrawn_at, created_at
|
||
) VALUES
|
||
(
|
||
'99999999-eeee-ffff-aaaa-000000000002',
|
||
'99999999-eeee-ffff-aaaa-000000000001',
|
||
'0.1',
|
||
NULL,
|
||
NULL,
|
||
'DRAFT',
|
||
NULL,
|
||
NULL,
|
||
'/code/app/uploads/documents/demo/QRG-001-v0.1.pdf',
|
||
'a1b2c3d4e5f60718293a4b5c6d7e8f90123456789abcdef0123456789abcdef0',
|
||
1536,
|
||
'application/pdf',
|
||
'初稿',
|
||
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
'2025-01-16 09:10:00+00'
|
||
)
|
||
ON CONFLICT (document_id, version_no) DO NOTHING;
|