-- ============================================================ -- Homecare Migration SQL - Phase 1 (Location & Checkin) -- Date: 2026-06-05 -- Purpose: Formal migration for homecare location distance phase 1 -- Scope: Create/alter tables, indexes, foreign keys, seed data -- ID Type: All user/org/team/elder IDs unified to uuid -- Checkin Radius: 50 meters (phase 1) -- ============================================================ BEGIN; -- Needed for gen_random_uuid() defaults used by UUID primary keys. CREATE EXTENSION IF NOT EXISTS pgcrypto; -- ============================================================ -- Part 1: Create/Alter Foundation Tables -- ============================================================ -- Table: public.hc_work_order_events -- Source: 20260518_homecare_foundation.sql CREATE TABLE IF NOT EXISTS public.hc_work_order_events ( id text primary key, work_order_id uuid not null, org_id uuid not null, team_id uuid, worker_id uuid, action text not null, from_status text not null, to_status text not null, operator_id uuid not null, reason text, payload jsonb, created_at timestamptz not null default now() ); -- Table: public.hc_work_order_exceptions -- Source: 20260518_homecare_foundation.sql CREATE TABLE IF NOT EXISTS public.hc_work_order_exceptions ( id text primary key, work_order_id uuid not null, org_id uuid not null, team_id uuid, worker_id uuid, exception_type text not null, description text not null, status text not null default 'PENDING' check (status in ('PENDING', 'HANDLED')), decision text, reason text, created_by uuid not null, handled_by uuid, evidence_urls jsonb not null default '[]'::jsonb, created_at timestamptz not null default now(), handled_at timestamptz ); -- Table: public.hc_evidence_files -- Source: 20260518_homecare_foundation.sql -- Phase 1 Update: Add fields for checkin evidence support CREATE TABLE IF NOT EXISTS public.hc_evidence_files ( id text primary key, work_order_id uuid not null, org_id uuid not null, owner_id uuid, uploader_id uuid not null, file_url text not null, mime_type text, evidence_type text, -- Phase 1新增字段 file_size_bytes bigint not null default 0, upload_status text not null default 'TEMP' check (upload_status in ('TEMP', 'READY', 'BOUND', 'LOCKED', 'DELETED')), storage_bucket text, storage_path text, file_hash text, bound_action text check (bound_action in ('CHECKIN', 'CHECKOUT', 'EXCEPTION')), bound_record_id text, is_locked boolean not null default false, locked_at timestamptz, expires_at timestamptz, updated_at timestamptz not null default now(), created_at timestamptz not null default now() ); -- Table: public.hc_worker_qualifications -- Source: 20260518_homecare_foundation.sql CREATE TABLE IF NOT EXISTS public.hc_worker_qualifications ( id text primary key, org_id uuid not null, worker_id uuid not null, qualification_type text not null, qualification_no text, issue_org text, valid_from date, valid_to date, review_status text not null default 'PENDING' check (review_status in ('PENDING', 'APPROVED', 'REJECTED', 'EXPIRED')), reviewed_by uuid, reviewed_at timestamptz, file_urls jsonb not null default '[]'::jsonb, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); -- Table: public.hc_settlements -- Source: 20260518_homecare_foundation.sql CREATE TABLE IF NOT EXISTS public.hc_settlements ( id uuid primary key default gen_random_uuid(), work_order_id uuid not null unique, org_id uuid not null, finance_owner_id uuid, status text not null default 'PENDING' check (status in ('PENDING', 'READY', 'CONFIRMED')), amount numeric(12, 2) not null default 0, currency text not null default 'CNY', settled_at timestamptz, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); -- ============================================================ -- Part 2: Business Closure Extension Tables -- ============================================================ -- Table: public.hc_acceptances -- Source: 20260527_homecare_business_closure_extensions.sql CREATE TABLE IF NOT EXISTS public.hc_acceptances ( id text primary key, work_order_id uuid not null, org_id uuid not null, team_id uuid, worker_id uuid, status text not null default 'PENDING' check (status in ('PENDING', 'ACCEPTED', 'REJECTED', 'CLOSED')), result text check (result in ('PASS', 'REJECT')), rating smallint check (rating between 1 and 5), tags jsonb not null default '[]'::jsonb, comment text, rejected_reason text, accepted_by uuid, accepted_at timestamptz, created_by uuid, updated_by uuid, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); -- Table: public.hc_acceptance_issues -- Source: 20260527_homecare_business_closure_extensions.sql CREATE TABLE IF NOT EXISTS public.hc_acceptance_issues ( id text primary key, acceptance_id text not null, work_order_id uuid not null, org_id uuid not null, team_id uuid, worker_id uuid, issue_type text not null, priority text, description text not null, evidence_urls jsonb not null default '[]'::jsonb, status text not null default 'OPEN' check (status in ('OPEN', 'PROCESSING', 'RESOLVED', 'CLOSED')), reporter_id uuid not null, handler_id uuid, resolution text, resolved_at timestamptz, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); -- Table: public.hc_complaints -- Source: 20260527_homecare_business_closure_extensions.sql CREATE TABLE IF NOT EXISTS public.hc_complaints ( id text primary key, work_order_id uuid, acceptance_issue_id text, org_id uuid not null, team_id uuid, worker_id uuid, complainant_id uuid not null, complaint_type text not null, description text not null, evidence_urls jsonb not null default '[]'::jsonb, status text not null default 'PENDING' check (status in ('PENDING', 'PROCESSING', 'RESOLVED', 'CLOSED')), handler_id uuid, resolution text, resolved_at timestamptz, created_by uuid, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); -- Table: public.hc_exception_actions -- Source: 20260527_homecare_business_closure_extensions.sql CREATE TABLE IF NOT EXISTS public.hc_exception_actions ( id text primary key, exception_id text not null, work_order_id uuid not null, org_id uuid not null, team_id uuid, worker_id uuid, action_type text not null, description text, payload jsonb not null default '{}'::jsonb, operator_id uuid not null, created_at timestamptz not null default now() ); -- Table: public.hc_settlement_items CREATE TABLE IF NOT EXISTS public.hc_settlement_items ( id uuid primary key default gen_random_uuid(), settlement_id uuid not null, work_order_id uuid not null, execution_record_id uuid, item_name text not null, unit_price numeric(12, 2) not null default 0, quantity numeric(10, 2) not null default 1, actual_amount numeric(12, 2) not null default 0, deduction_amount numeric(12, 2) not null default 0, self_pay_amount numeric(12, 2) not null default 0, status text not null default 'PENDING' check (status in ('PENDING', 'APPROVED', 'REJECTED', 'WAIVED')), created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); -- Table: public.hc_payments -- Source: 20260527_homecare_business_closure_extensions.sql CREATE TABLE IF NOT EXISTS public.hc_payments ( id uuid primary key default gen_random_uuid(), settlement_id uuid not null, org_id uuid not null, payer_id uuid, payment_channel text not null, transaction_id text, amount numeric(12, 2) not null, status text not null default 'PENDING' check (status in ('PENDING', 'PAID', 'FAILED', 'CANCELLED', 'REFUNDING', 'REFUNDED')), callback_payload jsonb not null default '{}'::jsonb, paid_at timestamptz, failed_at timestamptz, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); -- Table: public.hc_refunds -- Source: 20260527_homecare_business_closure_extensions.sql CREATE TABLE IF NOT EXISTS public.hc_refunds ( id uuid primary key default gen_random_uuid(), payment_id uuid not null, settlement_id uuid not null, org_id uuid not null, amount numeric(12, 2) not null, reason text not null, status text not null default 'PENDING' check (status in ('PENDING', 'PROCESSING', 'REFUNDED', 'FAILED', 'CLOSED')), refund_transaction_id text, created_by uuid, refunded_at timestamptz, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); -- Table: public.hc_ledgers -- Source: 20260527_homecare_business_closure_extensions.sql CREATE TABLE IF NOT EXISTS public.hc_ledgers ( id uuid primary key default gen_random_uuid(), settlement_id uuid not null, org_id uuid not null, archive_period text not null, ledger_type text not null default 'SETTLEMENT', content jsonb not null default '{}'::jsonb, archived_at timestamptz, created_by uuid, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); -- Table: public.hc_archive_files -- Source: 20260527_homecare_business_closure_extensions.sql CREATE TABLE IF NOT EXISTS public.hc_archive_files ( id uuid primary key default gen_random_uuid(), ledger_id uuid not null, org_id uuid not null, file_url text not null, file_name text, file_size bigint, mime_type text, created_by uuid, created_at timestamptz not null default now() ); -- Table: public.hc_consent_records -- Source: 20260527_homecare_business_closure_extensions.sql CREATE TABLE IF NOT EXISTS public.hc_consent_records ( id text primary key, org_id uuid not null, elder_id uuid not null, consent_type text not null, consent_status text not null default 'GRANTED' check (consent_status in ('GRANTED', 'REVOKED', 'EXPIRED')), consent_version text, granted_by uuid, granted_at timestamptz, revoked_at timestamptz, expires_at timestamptz, evidence_urls jsonb not null default '[]'::jsonb, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); -- ============================================================ -- Part 3: Location Distance Phase 1 Tables -- ============================================================ -- Table: public.hc_dispatch_assignments -- Source: 20260602_homecare_location_distance_phase1_tables.sql CREATE TABLE IF NOT EXISTS public.hc_dispatch_assignments ( id text primary key, work_order_id uuid not null, org_id uuid not null, team_id uuid, assign_version integer not null default 1 check (assign_version > 0), worker_id uuid, service_latitude numeric(10,7) not null, service_longitude numeric(10,7) not null, service_coordinate_type text not null default 'gcj02', dispatch_status text not null default 'PENDING' check (dispatch_status in ('PENDING', 'ACCEPTED', 'REJECTED', 'TIMEOUT', 'REASSIGNED', 'CANCELLED')), dispatch_reason text, is_current boolean not null default true, dispatched_at timestamptz not null default now(), accepted_at timestamptz, rejected_at timestamptz, timeout_at timestamptz, reassigned_at timestamptz, cancelled_at timestamptz, created_by uuid, updated_by uuid, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint chk_hc_dispatch_assignments_lat_range check (service_latitude between -90 and 90), constraint chk_hc_dispatch_assignments_lng_range check (service_longitude between -180 and 180), constraint uq_hc_dispatch_assignments_version unique (work_order_id, assign_version) ); -- Table: public.hc_worker_locations -- Source: 20260602_homecare_location_distance_phase1_tables.sql CREATE TABLE IF NOT EXISTS public.hc_worker_locations ( id text primary key, work_order_id uuid not null, worker_id uuid not null, latitude numeric(10,7) not null, longitude numeric(10,7) not null, coordinate_type text not null default 'gcj02', accuracy numeric(10,2), location_scene text not null check (location_scene in ('ON_THE_WAY', 'CHECKIN_PRECHECK', 'CHECKIN', 'CHECKOUT')), reported_at timestamptz not null, distance_meters numeric(10,2), formatted_address text, province text, city text, district text, street text, poi_title text, geocode_provider text, geocode_status text, address_updated_at timestamptz, created_at timestamptz not null default now(), constraint chk_hc_worker_locations_lat_range check (latitude between -90 and 90), constraint chk_hc_worker_locations_lng_range check (longitude between -180 and 180) ); -- Table: public.hc_work_order_confirmations -- Source: 20260602_homecare_location_distance_phase1_tables.sql CREATE TABLE IF NOT EXISTS public.hc_work_order_confirmations ( id text primary key, work_order_id uuid not null, confirmation_type text not null default 'ARRIVAL', status text not null default 'PENDING' check (status in ('PENDING', 'CONFIRMED', 'REJECTED')), confirmed_by uuid, confirmed_at timestamptz, reason text, payload jsonb not null default '{}'::jsonb, -- Phase 1预留:二期电子签名扩展字段 signature_url text, signature_hash text, created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); -- Table: public.sys_sla_config -- Source: 20260602_homecare_location_distance_phase1_tables.sql CREATE TABLE IF NOT EXISTS public.sys_sla_config ( id text primary key, config_key text not null, config_value text not null, value_type text not null default 'string', scope_type text not null default 'GLOBAL' check (scope_type in ('WORK_ORDER', 'ORG', 'TEAM', 'GLOBAL')), scope_id text, is_active boolean not null default true, description text, updated_by uuid, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), constraint chk_sys_sla_config_scope check ( (scope_type = 'GLOBAL' and scope_id is null) or (scope_type <> 'GLOBAL') ) ); -- ============================================================ -- Part 4: Audit Extension Tables (if not exist) -- ============================================================ -- Table: public.hc_state_transitions CREATE TABLE IF NOT EXISTS public.hc_state_transitions ( id text primary key, work_order_id uuid not null, from_status text not null, to_status text not null, triggered_by uuid not null, reason text, payload jsonb, created_at timestamptz not null default now() ); -- Table: public.hc_audit_logs CREATE TABLE IF NOT EXISTS public.hc_audit_logs ( id text primary key, work_order_id uuid, org_id uuid, actor_id uuid not null, actor_role text not null, action text not null, resource_type text not null, resource_id text not null, details jsonb, ip_address inet, user_agent text, created_at timestamptz not null default now() ); -- Table: public.hc_sensitive_access_logs CREATE TABLE IF NOT EXISTS public.hc_sensitive_access_logs ( id text primary key, accessor_id uuid not null, accessed_resource_type text not null, accessed_resource_id text not null, access_purpose text not null, approved_by uuid, created_at timestamptz not null default now() ); -- ============================================================ -- Part 4.5: Legacy Compatibility Fixes -- ============================================================ -- These fixes make reruns safe when older hc_* tables already exist. -- CREATE TABLE IF NOT EXISTS does not upgrade existing table definitions, -- so referenced columns and FK columns are normalized before indexes/FKs. -- Existing settlements table may already have id uuid in current database. -- If settlement-related child tables already exist with text FK columns, -- convert them to uuid before FK creation. This succeeds only when existing -- values are empty or valid UUID strings. DO $$ BEGIN IF to_regclass('public.hc_settlement_items') IS NOT NULL AND EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='hc_settlement_items' AND column_name='settlement_id' AND udt_name <> 'uuid' ) THEN ALTER TABLE public.hc_settlement_items ALTER COLUMN settlement_id TYPE uuid USING NULLIF(settlement_id, '')::uuid; END IF; IF to_regclass('public.hc_payments') IS NOT NULL AND EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='hc_payments' AND column_name='settlement_id' AND udt_name <> 'uuid' ) THEN ALTER TABLE public.hc_payments ALTER COLUMN settlement_id TYPE uuid USING NULLIF(settlement_id, '')::uuid; END IF; IF to_regclass('public.hc_refunds') IS NOT NULL AND EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='hc_refunds' AND column_name='settlement_id' AND udt_name <> 'uuid' ) THEN ALTER TABLE public.hc_refunds ALTER COLUMN settlement_id TYPE uuid USING NULLIF(settlement_id, '')::uuid; END IF; IF to_regclass('public.hc_refunds') IS NOT NULL AND EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='hc_refunds' AND column_name='payment_id' AND udt_name <> 'uuid' ) THEN ALTER TABLE public.hc_refunds ALTER COLUMN payment_id TYPE uuid USING NULLIF(payment_id, '')::uuid; END IF; IF to_regclass('public.hc_ledgers') IS NOT NULL AND EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='hc_ledgers' AND column_name='settlement_id' AND udt_name <> 'uuid' ) THEN ALTER TABLE public.hc_ledgers ALTER COLUMN settlement_id TYPE uuid USING NULLIF(settlement_id, '')::uuid; END IF; IF to_regclass('public.hc_archive_files') IS NOT NULL AND EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_schema='public' AND table_name='hc_archive_files' AND column_name='ledger_id' AND udt_name <> 'uuid' ) THEN ALTER TABLE public.hc_archive_files ALTER COLUMN ledger_id TYPE uuid USING NULLIF(ledger_id, '')::uuid; END IF; END $$; -- ============================================================ -- Part 5: Indexes -- ============================================================ -- hc_dispatch_assignments indexes CREATE INDEX IF NOT EXISTS idx_hc_dispatch_assignments_wo_created ON public.hc_dispatch_assignments (work_order_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_hc_dispatch_assignments_current ON public.hc_dispatch_assignments (work_order_id) WHERE is_current = true; -- Ensure each work order has at most one current assignment for distance/checkin RPC lookup. CREATE UNIQUE INDEX IF NOT EXISTS uq_hc_dispatch_assignments_one_current ON public.hc_dispatch_assignments (work_order_id) WHERE is_current = true; CREATE INDEX IF NOT EXISTS idx_hc_dispatch_assignments_worker_status ON public.hc_dispatch_assignments (worker_id, dispatch_status, created_at DESC); -- hc_worker_locations indexes CREATE INDEX IF NOT EXISTS idx_hc_worker_locations_wo_reported ON public.hc_worker_locations (work_order_id, reported_at DESC); CREATE INDEX IF NOT EXISTS idx_hc_worker_locations_worker_reported ON public.hc_worker_locations (worker_id, reported_at DESC); -- hc_work_order_confirmations indexes CREATE INDEX IF NOT EXISTS idx_hc_work_order_confirmations_wo_created ON public.hc_work_order_confirmations (work_order_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_hc_work_order_confirmations_status ON public.hc_work_order_confirmations (status, updated_at DESC); -- hc_evidence_files indexes CREATE INDEX IF NOT EXISTS idx_hc_evidence_files_wo_created ON public.hc_evidence_files (work_order_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_hc_evidence_files_upload_status ON public.hc_evidence_files (upload_status) WHERE upload_status != 'DELETED'; -- sys_sla_config indexes CREATE INDEX IF NOT EXISTS idx_sys_sla_config_key_scope ON public.sys_sla_config (config_key, scope_type, scope_id, is_active) WHERE is_active = true; -- hc_work_order_exceptions indexes CREATE INDEX IF NOT EXISTS idx_hc_work_order_exceptions_wo_created ON public.hc_work_order_exceptions (work_order_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_hc_work_order_exceptions_status ON public.hc_work_order_exceptions (status, created_at DESC); -- hc_work_order_events indexes CREATE INDEX IF NOT EXISTS idx_hc_work_order_events_wo_created ON public.hc_work_order_events (work_order_id, created_at DESC); -- hc_settlements indexes CREATE INDEX IF NOT EXISTS idx_hc_settlements_wo ON public.hc_settlements (work_order_id); -- ============================================================ -- Part 6: Foreign Keys -- ============================================================ -- Make foreign key creation idempotent for reruns ALTER TABLE public.hc_work_order_events DROP CONSTRAINT IF EXISTS fk_hc_work_order_events_work_order, DROP CONSTRAINT IF EXISTS fk_hc_work_order_events_worker, DROP CONSTRAINT IF EXISTS fk_hc_work_order_events_operator; ALTER TABLE public.hc_work_order_exceptions DROP CONSTRAINT IF EXISTS fk_hc_work_order_exceptions_work_order, DROP CONSTRAINT IF EXISTS fk_hc_work_order_exceptions_worker, DROP CONSTRAINT IF EXISTS fk_hc_work_order_exceptions_created_by, DROP CONSTRAINT IF EXISTS fk_hc_work_order_exceptions_handled_by; ALTER TABLE public.hc_evidence_files DROP CONSTRAINT IF EXISTS fk_hc_evidence_files_work_order, DROP CONSTRAINT IF EXISTS fk_hc_evidence_files_uploader; ALTER TABLE public.hc_worker_qualifications DROP CONSTRAINT IF EXISTS fk_hc_worker_qualifications_worker, DROP CONSTRAINT IF EXISTS fk_hc_worker_qualifications_reviewed_by; ALTER TABLE public.hc_settlements DROP CONSTRAINT IF EXISTS fk_hc_settlements_work_order; ALTER TABLE public.hc_acceptances DROP CONSTRAINT IF EXISTS fk_hc_acceptances_work_order, DROP CONSTRAINT IF EXISTS fk_hc_acceptances_worker, DROP CONSTRAINT IF EXISTS fk_hc_acceptances_accepted_by; ALTER TABLE public.hc_acceptance_issues DROP CONSTRAINT IF EXISTS fk_hc_acceptance_issues_acceptance, DROP CONSTRAINT IF EXISTS fk_hc_acceptance_issues_work_order, DROP CONSTRAINT IF EXISTS fk_hc_acceptance_issues_worker, DROP CONSTRAINT IF EXISTS fk_hc_acceptance_issues_reporter, DROP CONSTRAINT IF EXISTS fk_hc_acceptance_issues_handler; ALTER TABLE public.hc_complaints DROP CONSTRAINT IF EXISTS fk_hc_complaints_work_order, DROP CONSTRAINT IF EXISTS fk_hc_complaints_acceptance_issue, DROP CONSTRAINT IF EXISTS fk_hc_complaints_worker, DROP CONSTRAINT IF EXISTS fk_hc_complaints_complainant, DROP CONSTRAINT IF EXISTS fk_hc_complaints_handler; ALTER TABLE public.hc_exception_actions DROP CONSTRAINT IF EXISTS fk_hc_exception_actions_exception, DROP CONSTRAINT IF EXISTS fk_hc_exception_actions_work_order, DROP CONSTRAINT IF EXISTS fk_hc_exception_actions_worker, DROP CONSTRAINT IF EXISTS fk_hc_exception_actions_operator; ALTER TABLE public.hc_settlement_items DROP CONSTRAINT IF EXISTS fk_hc_settlement_items_settlement, DROP CONSTRAINT IF EXISTS fk_hc_settlement_items_work_order, DROP CONSTRAINT IF EXISTS fk_hc_settlement_items_execution_record; ALTER TABLE public.hc_payments DROP CONSTRAINT IF EXISTS fk_hc_payments_settlement, DROP CONSTRAINT IF EXISTS fk_hc_payments_payer; ALTER TABLE public.hc_refunds DROP CONSTRAINT IF EXISTS fk_hc_refunds_payment, DROP CONSTRAINT IF EXISTS fk_hc_refunds_settlement, DROP CONSTRAINT IF EXISTS fk_hc_refunds_created_by; ALTER TABLE public.hc_ledgers DROP CONSTRAINT IF EXISTS fk_hc_ledgers_settlement, DROP CONSTRAINT IF EXISTS fk_hc_ledgers_created_by; ALTER TABLE public.hc_archive_files DROP CONSTRAINT IF EXISTS fk_hc_archive_files_ledger, DROP CONSTRAINT IF EXISTS fk_hc_archive_files_created_by; ALTER TABLE public.hc_consent_records DROP CONSTRAINT IF EXISTS fk_hc_consent_records_elder, DROP CONSTRAINT IF EXISTS fk_hc_consent_records_granted_by; ALTER TABLE public.hc_dispatch_assignments DROP CONSTRAINT IF EXISTS fk_hc_dispatch_assignments_work_order, DROP CONSTRAINT IF EXISTS fk_hc_dispatch_assignments_worker, DROP CONSTRAINT IF EXISTS fk_hc_dispatch_assignments_created_by, DROP CONSTRAINT IF EXISTS fk_hc_dispatch_assignments_updated_by; ALTER TABLE public.hc_worker_locations DROP CONSTRAINT IF EXISTS fk_hc_worker_locations_work_order, DROP CONSTRAINT IF EXISTS fk_hc_worker_locations_worker; ALTER TABLE public.hc_work_order_confirmations DROP CONSTRAINT IF EXISTS fk_hc_work_order_confirmations_work_order, DROP CONSTRAINT IF EXISTS fk_hc_work_order_confirmations_confirmed_by; ALTER TABLE public.hc_state_transitions DROP CONSTRAINT IF EXISTS fk_hc_state_transitions_work_order, DROP CONSTRAINT IF EXISTS fk_hc_state_transitions_triggered_by; ALTER TABLE public.hc_audit_logs DROP CONSTRAINT IF EXISTS fk_hc_audit_logs_work_order, DROP CONSTRAINT IF EXISTS fk_hc_audit_logs_actor; ALTER TABLE public.hc_sensitive_access_logs DROP CONSTRAINT IF EXISTS fk_hc_sensitive_access_logs_accessor; -- hc_work_order_events foreign keys ALTER TABLE public.hc_work_order_events ADD CONSTRAINT fk_hc_work_order_events_work_order FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_work_order_events_worker FOREIGN KEY (worker_id) REFERENCES public.ak_users(id) ON DELETE SET NULL, ADD CONSTRAINT fk_hc_work_order_events_operator FOREIGN KEY (operator_id) REFERENCES public.ak_users(id) ON DELETE RESTRICT; -- hc_work_order_exceptions foreign keys ALTER TABLE public.hc_work_order_exceptions ADD CONSTRAINT fk_hc_work_order_exceptions_work_order FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_work_order_exceptions_worker FOREIGN KEY (worker_id) REFERENCES public.ak_users(id) ON DELETE SET NULL, ADD CONSTRAINT fk_hc_work_order_exceptions_created_by FOREIGN KEY (created_by) REFERENCES public.ak_users(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_work_order_exceptions_handled_by FOREIGN KEY (handled_by) REFERENCES public.ak_users(id) ON DELETE SET NULL; -- hc_evidence_files foreign keys ALTER TABLE public.hc_evidence_files ADD CONSTRAINT fk_hc_evidence_files_work_order FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_evidence_files_uploader FOREIGN KEY (uploader_id) REFERENCES public.ak_users(id) ON DELETE RESTRICT; -- hc_worker_qualifications foreign keys ALTER TABLE public.hc_worker_qualifications ADD CONSTRAINT fk_hc_worker_qualifications_worker FOREIGN KEY (worker_id) REFERENCES public.ak_users(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_worker_qualifications_reviewed_by FOREIGN KEY (reviewed_by) REFERENCES public.ak_users(id) ON DELETE SET NULL; -- hc_settlements foreign keys ALTER TABLE public.hc_settlements ADD CONSTRAINT fk_hc_settlements_work_order FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT; -- hc_acceptances foreign keys ALTER TABLE public.hc_acceptances ADD CONSTRAINT fk_hc_acceptances_work_order FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_acceptances_worker FOREIGN KEY (worker_id) REFERENCES public.ak_users(id) ON DELETE SET NULL, ADD CONSTRAINT fk_hc_acceptances_accepted_by FOREIGN KEY (accepted_by) REFERENCES public.ak_users(id) ON DELETE SET NULL; -- hc_acceptance_issues foreign keys ALTER TABLE public.hc_acceptance_issues ADD CONSTRAINT fk_hc_acceptance_issues_acceptance FOREIGN KEY (acceptance_id) REFERENCES public.hc_acceptances(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_acceptance_issues_work_order FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_acceptance_issues_worker FOREIGN KEY (worker_id) REFERENCES public.ak_users(id) ON DELETE SET NULL, ADD CONSTRAINT fk_hc_acceptance_issues_reporter FOREIGN KEY (reporter_id) REFERENCES public.ak_users(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_acceptance_issues_handler FOREIGN KEY (handler_id) REFERENCES public.ak_users(id) ON DELETE SET NULL; -- hc_complaints foreign keys ALTER TABLE public.hc_complaints ADD CONSTRAINT fk_hc_complaints_work_order FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE SET NULL, ADD CONSTRAINT fk_hc_complaints_acceptance_issue FOREIGN KEY (acceptance_issue_id) REFERENCES public.hc_acceptance_issues(id) ON DELETE SET NULL, ADD CONSTRAINT fk_hc_complaints_worker FOREIGN KEY (worker_id) REFERENCES public.ak_users(id) ON DELETE SET NULL, ADD CONSTRAINT fk_hc_complaints_complainant FOREIGN KEY (complainant_id) REFERENCES public.ak_users(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_complaints_handler FOREIGN KEY (handler_id) REFERENCES public.ak_users(id) ON DELETE SET NULL; -- hc_exception_actions foreign keys ALTER TABLE public.hc_exception_actions ADD CONSTRAINT fk_hc_exception_actions_exception FOREIGN KEY (exception_id) REFERENCES public.hc_work_order_exceptions(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_exception_actions_work_order FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_exception_actions_worker FOREIGN KEY (worker_id) REFERENCES public.ak_users(id) ON DELETE SET NULL, ADD CONSTRAINT fk_hc_exception_actions_operator FOREIGN KEY (operator_id) REFERENCES public.ak_users(id) ON DELETE RESTRICT; -- hc_settlement_items foreign keys ALTER TABLE public.hc_settlement_items ADD CONSTRAINT fk_hc_settlement_items_settlement FOREIGN KEY (settlement_id) REFERENCES public.hc_settlements(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_settlement_items_work_order FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_settlement_items_execution_record FOREIGN KEY (execution_record_id) REFERENCES public.ec_care_records(id) ON DELETE SET NULL; -- hc_payments foreign keys ALTER TABLE public.hc_payments ADD CONSTRAINT fk_hc_payments_settlement FOREIGN KEY (settlement_id) REFERENCES public.hc_settlements(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_payments_payer FOREIGN KEY (payer_id) REFERENCES public.ak_users(id) ON DELETE SET NULL; -- hc_refunds foreign keys ALTER TABLE public.hc_refunds ADD CONSTRAINT fk_hc_refunds_payment FOREIGN KEY (payment_id) REFERENCES public.hc_payments(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_refunds_settlement FOREIGN KEY (settlement_id) REFERENCES public.hc_settlements(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_refunds_created_by FOREIGN KEY (created_by) REFERENCES public.ak_users(id) ON DELETE SET NULL; -- hc_ledgers foreign keys ALTER TABLE public.hc_ledgers ADD CONSTRAINT fk_hc_ledgers_settlement FOREIGN KEY (settlement_id) REFERENCES public.hc_settlements(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_ledgers_created_by FOREIGN KEY (created_by) REFERENCES public.ak_users(id) ON DELETE SET NULL; -- hc_archive_files foreign keys ALTER TABLE public.hc_archive_files ADD CONSTRAINT fk_hc_archive_files_ledger FOREIGN KEY (ledger_id) REFERENCES public.hc_ledgers(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_archive_files_created_by FOREIGN KEY (created_by) REFERENCES public.ak_users(id) ON DELETE SET NULL; -- hc_consent_records foreign keys ALTER TABLE public.hc_consent_records ADD CONSTRAINT fk_hc_consent_records_elder FOREIGN KEY (elder_id) REFERENCES public.ec_elders(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_consent_records_granted_by FOREIGN KEY (granted_by) REFERENCES public.ak_users(id) ON DELETE SET NULL; -- Location distance phase 1 foreign keys ALTER TABLE public.hc_dispatch_assignments ADD CONSTRAINT fk_hc_dispatch_assignments_work_order FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_dispatch_assignments_worker FOREIGN KEY (worker_id) REFERENCES public.ak_users(id) ON DELETE SET NULL, ADD CONSTRAINT fk_hc_dispatch_assignments_created_by FOREIGN KEY (created_by) REFERENCES public.ak_users(id) ON DELETE SET NULL, ADD CONSTRAINT fk_hc_dispatch_assignments_updated_by FOREIGN KEY (updated_by) REFERENCES public.ak_users(id) ON DELETE SET NULL; ALTER TABLE public.hc_worker_locations ADD CONSTRAINT fk_hc_worker_locations_work_order FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_worker_locations_worker FOREIGN KEY (worker_id) REFERENCES public.ak_users(id) ON DELETE RESTRICT; ALTER TABLE public.hc_work_order_confirmations ADD CONSTRAINT fk_hc_work_order_confirmations_work_order FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_work_order_confirmations_confirmed_by FOREIGN KEY (confirmed_by) REFERENCES public.ak_users(id) ON DELETE SET NULL; -- Audit extension tables foreign keys ALTER TABLE public.hc_state_transitions ADD CONSTRAINT fk_hc_state_transitions_work_order FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE RESTRICT, ADD CONSTRAINT fk_hc_state_transitions_triggered_by FOREIGN KEY (triggered_by) REFERENCES public.ak_users(id) ON DELETE RESTRICT; ALTER TABLE public.hc_audit_logs ADD CONSTRAINT fk_hc_audit_logs_work_order FOREIGN KEY (work_order_id) REFERENCES public.ec_care_tasks(id) ON DELETE SET NULL, ADD CONSTRAINT fk_hc_audit_logs_actor FOREIGN KEY (actor_id) REFERENCES public.ak_users(id) ON DELETE RESTRICT; ALTER TABLE public.hc_sensitive_access_logs ADD CONSTRAINT fk_hc_sensitive_access_logs_accessor FOREIGN KEY (accessor_id) REFERENCES public.ak_users(id) ON DELETE RESTRICT; -- ============================================================ -- Part 7: Seed Data - SLA Config -- ============================================================ -- Phase 1 SLA configuration -- Checkin radius: 50 meters (phase 1 final) INSERT INTO public.sys_sla_config (id, config_key, config_value, value_type, scope_type, description) VALUES ('sla_001', 'HOMECARE_CHECKIN_RADIUS_METERS', '50', 'integer', 'GLOBAL', '签到半径(一期50米)'), ('sla_002', 'HOMECARE_LOCATION_REFRESH_SECONDS', '30', 'integer', 'GLOBAL', '定位刷新间隔(秒)'), ('sla_003', 'HOMECARE_CHECKIN_MAX_PHOTO_COUNT', '3', 'integer', 'GLOBAL', '签到最大照片数'), ('sla_004', 'HOMECARE_CHECKIN_MAX_PHOTO_SIZE_MB', '5', 'integer', 'GLOBAL', '单张最大照片大小(MB)') ON CONFLICT (id) DO NOTHING; -- ============================================================ -- Part 8: Comments -- ============================================================ COMMENT ON TABLE public.hc_work_order_events IS '居家服务工单事件表 - 业务时间线展示'; COMMENT ON TABLE public.hc_work_order_exceptions IS '居家服务工单异常表'; COMMENT ON TABLE public.hc_evidence_files IS '居家服务证据文件表 - 签到照片、异常图片等'; COMMENT ON TABLE public.hc_worker_qualifications IS '居家服务人员资质表'; COMMENT ON TABLE public.hc_settlements IS '居家服务结算表'; COMMENT ON TABLE public.hc_acceptances IS '居家服务确认表'; COMMENT ON TABLE public.hc_acceptance_issues IS '居家服务确认问题表'; COMMENT ON TABLE public.hc_complaints IS '居家服务投诉表'; COMMENT ON TABLE public.hc_exception_actions IS '居家服务异常操作表'; COMMENT ON TABLE public.hc_settlement_items IS '居家服务结算明细表'; COMMENT ON TABLE public.hc_payments IS '居家服务支付表'; COMMENT ON TABLE public.hc_refunds IS '居家服务退款表'; COMMENT ON TABLE public.hc_ledgers IS '居家服务账簿表'; COMMENT ON TABLE public.hc_archive_files IS '居家服务归档文件表'; COMMENT ON TABLE public.hc_consent_records IS '居家服务同意记录表'; COMMENT ON TABLE public.hc_dispatch_assignments IS '居家服务派单记录表 - 定位距离一期'; COMMENT ON TABLE public.hc_worker_locations IS '居家服务人员定位表 - 定位距离一期'; COMMENT ON TABLE public.hc_work_order_confirmations IS '居家服务工单确认表 - 定位距离一期'; COMMENT ON TABLE public.sys_sla_config IS '居家服务SLA配置表'; COMMENT ON TABLE public.hc_state_transitions IS '居家服务状态变迁表 - 审计'; COMMENT ON TABLE public.hc_audit_logs IS '居家服务操作审计表'; COMMENT ON TABLE public.hc_sensitive_access_logs IS '居家服务敏感访问日志表'; COMMENT ON COLUMN public.hc_evidence_files.upload_status IS '上传状态: TEMP/READY/BOUND/LOCKED/DELETED'; COMMENT ON COLUMN public.hc_evidence_files.bound_action IS '绑定动作: CHECKIN/CHECKOUT/EXCEPTION'; COMMENT ON COLUMN public.hc_worker_locations.location_scene IS '定位场景: ON_THE_WAY/CHECKIN_PRECHECK/CHECKIN/CHECKOUT'; COMMENT ON COLUMN public.hc_work_order_confirmations.confirmation_type IS '确认类型: ARRIVAL(二期扩展CHECKOUT)'; COMMENT ON COLUMN public.hc_work_order_confirmations.signature_url IS '二期电子签名图片URL(预留)'; COMMENT ON COLUMN public.hc_work_order_confirmations.signature_hash IS '二期电子签名哈希(预留)'; COMMIT;