文件版本管理-初步优化

This commit is contained in:
Cheng Zhou
2026-01-14 11:35:37 +08:00
parent ef1e67218c
commit f9ef5c109f
46 changed files with 4603 additions and 30 deletions
+704 -8
View File
@@ -2,7 +2,14 @@
DO $$
BEGIN
CREATE TYPE public.user_role AS ENUM ('ADMIN', 'PM', 'CRA', 'PV', 'IMP');
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 $$;
@@ -517,7 +524,10 @@ INSERT INTO public.users (
) 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')
('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,
@@ -566,7 +576,10 @@ INSERT INTO public.study_members (
) 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')
('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;
@@ -601,12 +614,12 @@ INSERT INTO public.finance_items (
ON CONFLICT (id) DO NOTHING;
INSERT INTO public.drug_shipments (
id, study_id, direction, site_name, drug_desc, ship_date, carrier, tracking_no, from_party, to_party, status, remark, created_by, created_at, updated_at
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'), '寄送', '北京协和医院', 'DP-001 片剂 50mg × 200 盒', '2025-01-12', '顺丰', 'SF100001', '中央库房', '北京协和医院药房', '运输中', '首批寄送', (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'), '寄送', '上海瑞金医院', 'DP-001 片剂 50mg × 150 盒', '2025-01-13', '京东物流', 'JD200002', '中央库房', '上海瑞金医院药房', '已签收', '分中心补货', (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'), '回收', '北京协和医院', '空药盒及剩余药品回收', '2025-02-05', '顺丰', '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'), '回收', '上海瑞金医院', '冷链箱回收', '2025-02-08', '德邦', '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')
('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 (
@@ -739,3 +752,686 @@ INSERT INTO public.attachments (
('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.workflow_status AS ENUM ('PENDING', 'APPROVED', 'REJECTED', 'CANCELED');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
DO $$
BEGIN
CREATE TYPE public.workflow_action_type AS ENUM ('SUBMIT', 'APPROVE', 'REJECT');
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.workflow_templates (
id uuid PRIMARY KEY,
trial_id uuid,
name character varying(200) NOT NULL,
description text,
is_active boolean NOT NULL DEFAULT true,
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_workflow_templates_trial_id FOREIGN KEY (trial_id) REFERENCES public.studies(id) ON DELETE RESTRICT,
CONSTRAINT fk_workflow_templates_created_by FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE SET NULL
);
CREATE TABLE IF NOT EXISTS public.workflow_nodes (
id uuid PRIMARY KEY,
template_id uuid NOT NULL,
node_order integer NOT NULL,
role character varying(50) NOT NULL,
name character varying(100),
is_required boolean NOT NULL DEFAULT true,
created_at timestamp with time zone NOT NULL DEFAULT now(),
CONSTRAINT uq_workflow_nodes_order UNIQUE (template_id, node_order),
CONSTRAINT fk_workflow_nodes_template_id FOREIGN KEY (template_id) REFERENCES public.workflow_templates(id) ON DELETE CASCADE
);
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.version_workflows (
id uuid PRIMARY KEY,
document_id uuid NOT NULL,
version_id uuid NOT NULL,
template_id uuid NOT NULL,
status public.workflow_status NOT NULL DEFAULT 'PENDING',
current_node integer,
submitted_by uuid,
submitted_at timestamp with time zone,
completed_at timestamp with time zone,
created_at timestamp with time zone NOT NULL DEFAULT now(),
updated_at timestamp with time zone NOT NULL DEFAULT now(),
CONSTRAINT uq_version_workflow_version UNIQUE (version_id),
CONSTRAINT fk_version_workflows_document_id FOREIGN KEY (document_id) REFERENCES public.documents(id) ON DELETE CASCADE,
CONSTRAINT fk_version_workflows_version_id FOREIGN KEY (version_id) REFERENCES public.document_versions(id) ON DELETE CASCADE,
CONSTRAINT fk_version_workflows_template_id FOREIGN KEY (template_id) REFERENCES public.workflow_templates(id) ON DELETE RESTRICT,
CONSTRAINT fk_version_workflows_submitted_by FOREIGN KEY (submitted_by) REFERENCES public.users(id) ON DELETE SET NULL
);
CREATE TABLE IF NOT EXISTS public.workflow_actions (
id uuid PRIMARY KEY,
workflow_id uuid NOT NULL,
node_order integer,
action public.workflow_action_type NOT NULL,
actor_id uuid,
comment text,
acted_at timestamp with time zone NOT NULL DEFAULT now(),
CONSTRAINT fk_workflow_actions_workflow_id FOREIGN KEY (workflow_id) REFERENCES public.version_workflows(id) ON DELETE CASCADE,
CONSTRAINT fk_workflow_actions_actor_id FOREIGN KEY (actor_id) REFERENCES public.users(id) ON DELETE SET NULL
);
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 UNIQUE INDEX IF NOT EXISTS uq_document_pending_workflow ON public.version_workflows (document_id)
WHERE status IN ('PENDING', '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);
-- =========================================
-- Demo data for document version management
-- =========================================
INSERT INTO public.workflow_templates (
id, trial_id, name, description, is_active, created_by, created_at, updated_at
) VALUES
(
'11111111-aaaa-bbbb-cccc-111111111111',
(SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'),
'标准审批(PM 单签)',
'PM 单节点审批,适用于一般文件',
true,
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
'2025-01-07 08:00:00+00',
'2025-01-07 08:00:00+00'
),
(
'11111111-aaaa-bbbb-cccc-111111111112',
(SELECT id FROM public.studies WHERE code = 'DEMO-CTMS'),
'严格审批(CRA→PM→QA',
'三节点审批,适用于关键文件',
true,
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
'2025-01-07 08:20:00+00',
'2025-01-07 08:20:00+00'
)
ON CONFLICT (id) DO UPDATE
SET name = EXCLUDED.name,
description = EXCLUDED.description,
is_active = EXCLUDED.is_active,
trial_id = EXCLUDED.trial_id,
created_by = EXCLUDED.created_by,
updated_at = EXCLUDED.updated_at;
INSERT INTO public.workflow_nodes (
id, template_id, node_order, role, name, is_required, created_at
) VALUES
(
'22222222-aaaa-bbbb-cccc-222222222222',
'11111111-aaaa-bbbb-cccc-111111111111',
1,
'PM',
'项目经理审批',
true,
'2025-01-07 08:10:00+00'
),
(
'22222222-aaaa-bbbb-cccc-222222222223',
'11111111-aaaa-bbbb-cccc-111111111112',
1,
'CRA',
'CRA 初审',
true,
'2025-01-07 08:30:00+00'
),
(
'22222222-aaaa-bbbb-cccc-222222222224',
'11111111-aaaa-bbbb-cccc-111111111112',
2,
'PM',
'项目经理复审',
true,
'2025-01-07 08:40:00+00'
),
(
'22222222-aaaa-bbbb-cccc-222222222225',
'11111111-aaaa-bbbb-cccc-111111111112',
3,
'QA',
'QA 终审',
true,
'2025-01-07 08:50:00+00'
)
ON CONFLICT (id) DO UPDATE
SET template_id = EXCLUDED.template_id,
node_order = EXCLUDED.node_order,
role = EXCLUDED.role,
name = EXCLUDED.name,
is_required = EXCLUDED.is_required;
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.version_workflows (
id, document_id, version_id, template_id, status, current_node, submitted_by, submitted_at, completed_at, created_at, updated_at
) VALUES
(
'88888888-aaaa-bbbb-cccc-888888888888',
'33333333-aaaa-bbbb-cccc-333333333333',
'66666666-aaaa-bbbb-cccc-666666666666',
'11111111-aaaa-bbbb-cccc-111111111111',
'APPROVED',
1,
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
'2025-01-12 10:00:00+00',
'2025-01-12 11:00:00+00',
'2025-01-12 10:00:00+00',
'2025-01-12 11:00:00+00'
)
ON CONFLICT (id) DO NOTHING;
INSERT INTO public.workflow_actions (
id, workflow_id, node_order, action, actor_id, comment, acted_at
) VALUES
(
'99999999-aaaa-bbbb-cccc-999999999999',
'88888888-aaaa-bbbb-cccc-888888888888',
1,
'SUBMIT',
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
'提交审批',
'2025-01-12 10:00:00+00'
),
(
'aaaa0000-bbbb-cccc-dddd-111111111111',
'88888888-aaaa-bbbb-cccc-888888888888',
1,
'APPROVE',
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
'批准通过',
'2025-01-12 11:00:00+00'
)
ON CONFLICT (id) DO NOTHING;
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;
+153
View File
@@ -0,0 +1,153 @@
-- Optional seed data for document version management module
INSERT INTO public.studies (
id, code, name, status, created_at
) VALUES (
'dddddddd-1111-2222-3333-444444444444', 'DOC-DEMO', 'Document Demo Study', 'DRAFT', now()
)
ON CONFLICT (code) DO NOTHING;
INSERT INTO public.workflow_templates (
id, trial_id, name, description, is_active, created_by, created_at, updated_at
) VALUES
(
'11111111-aaaa-bbbb-cccc-111111111211',
(SELECT id FROM public.studies WHERE code = 'DOC-DEMO'),
'标准审批(PM 单签)',
'PM 单节点审批,适用于一般文件',
true,
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
now(),
now()
),
(
'11111111-aaaa-bbbb-cccc-111111111212',
(SELECT id FROM public.studies WHERE code = 'DOC-DEMO'),
'严格审批(CRA→PM→QA',
'三节点审批,适用于关键文件',
true,
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
now(),
now()
)
ON CONFLICT (id) DO UPDATE
SET name = EXCLUDED.name,
description = EXCLUDED.description,
is_active = EXCLUDED.is_active,
trial_id = EXCLUDED.trial_id,
created_by = EXCLUDED.created_by,
updated_at = EXCLUDED.updated_at;
INSERT INTO public.workflow_nodes (
id, template_id, node_order, role, name, is_required, created_at
) VALUES
(
'22222222-aaaa-bbbb-cccc-222222222311',
'11111111-aaaa-bbbb-cccc-111111111211',
1,
'PM',
'项目经理审批',
true,
now()
),
(
'22222222-aaaa-bbbb-cccc-222222222312',
'11111111-aaaa-bbbb-cccc-111111111212',
1,
'CRA',
'CRA 初审',
true,
now()
),
(
'22222222-aaaa-bbbb-cccc-222222222313',
'11111111-aaaa-bbbb-cccc-111111111212',
2,
'PM',
'项目经理复审',
true,
now()
),
(
'22222222-aaaa-bbbb-cccc-222222222314',
'11111111-aaaa-bbbb-cccc-111111111212',
3,
'QA',
'QA 终审',
true,
now()
)
ON CONFLICT (id) DO UPDATE
SET template_id = EXCLUDED.template_id,
node_order = EXCLUDED.node_order,
role = EXCLUDED.role,
name = EXCLUDED.name,
is_required = EXCLUDED.is_required;
INSERT INTO public.documents (
id, trial_id, site_id, doc_no, doc_type, title, scope_type, owner_id, status, created_at, updated_at
) VALUES (
'eeeeeeee-1111-2222-3333-444444444444',
(SELECT id FROM public.studies WHERE code = 'DOC-DEMO'),
NULL,
'SOP-001',
'SOP',
'Sample SOP for Versioning',
'GLOBAL',
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
'ACTIVE',
now(),
now()
)
ON CONFLICT (trial_id, doc_no) DO NOTHING;
INSERT INTO public.documents (
id, trial_id, site_id, doc_no, doc_type, title, scope_type, owner_id, status, created_at, updated_at
) VALUES (
'eeeeeeee-1111-2222-3333-444444444445',
(SELECT id FROM public.studies WHERE code = 'DOC-DEMO'),
(SELECT id FROM public.sites WHERE study_id = (SELECT id FROM public.studies WHERE code = 'DOC-DEMO') LIMIT 1),
'SITE-SOP-001',
'SOP',
'DOC-DEMO 分中心 SOP',
'SITE',
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
'ACTIVE',
now(),
now()
)
ON CONFLICT (trial_id, doc_no) DO NOTHING;
INSERT INTO public.document_versions (
id, document_id, version_no, status, file_uri, file_hash, file_size, mime_type, change_summary, created_by, created_at
) VALUES (
'ffffffff-1111-2222-3333-444444444444',
(SELECT id FROM public.documents WHERE doc_no = 'SOP-001' AND trial_id = (SELECT id FROM public.studies WHERE code = 'DOC-DEMO')),
'V1.0',
'DRAFT',
'/uploads/docs/SOP-001/V1.0/sample.pdf',
'9f2b4c6d8e0f1234567890abcdef1234567890abcdef1234567890abcdef1234',
2048,
'application/pdf',
'初稿',
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
now()
)
ON CONFLICT (document_id, version_no) DO NOTHING;
INSERT INTO public.document_versions (
id, document_id, version_no, status, file_uri, file_hash, file_size, mime_type, change_summary, created_by, created_at
) VALUES (
'ffffffff-1111-2222-3333-444444444445',
(SELECT id FROM public.documents WHERE doc_no = 'SITE-SOP-001' AND trial_id = (SELECT id FROM public.studies WHERE code = 'DOC-DEMO')),
'1.0',
'EFFECTIVE',
'/code/app/uploads/documents/demo/SITE-SOP-001-v1.pdf',
'a3b2c1d0e9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2',
2048,
'application/pdf',
'分中心 SOP 首版',
(SELECT id FROM public.users WHERE email = 'pm@example.com'),
now()
)
ON CONFLICT (document_id, version_no) DO NOTHING;