/** Page Data schema + dataset registry for MeMind Health (architecture §8). */ export const HEALTH_OBSERVATIONS_DATASET = Object.freeze({ name: 'health_observations', table: 'health_observations', description: 'MeMind Health 个人健康观测记录', actions: ['read', 'insert', 'update', 'softDelete'], columns: { read: [ 'id', 'observed_at', 'metric_type', 'value_num', 'value_text', 'unit', 'context', 'source', 'quality_flag', 'created_at', ], insert: [ 'observed_at', 'metric_type', 'value_num', 'value_text', 'unit', 'context', 'source', 'source_ref', 'time_source', 'confidence', 'quality_flag', 'raw_payload', ], update: ['observed_at', 'value_num', 'value_text', 'context', 'quality_flag'], }, }); export const HEALTH_DOCUMENTS_DATASET = Object.freeze({ name: 'health_documents', table: 'health_documents', description: 'MeMind Health 医疗报告归档', actions: ['read', 'insert', 'update', 'softDelete'], columns: { read: ['id', 'doc_type', 'report_date', 'institution', 'title', 'asset_id', 'extraction_status', 'created_at'], insert: ['doc_type', 'report_date', 'institution', 'title', 'asset_id', 'ocr_text', 'extracted_metrics', 'extraction_status'], update: ['report_date', 'institution', 'title', 'ocr_text', 'extracted_metrics', 'extraction_status'], }, }); export const HEALTH_OBSERVATION_DRAFTS_DATASET = Object.freeze({ name: 'health_observation_drafts', table: 'health_observation_drafts', description: 'MeMind Health 待确认观测草稿', actions: ['read', 'insert', 'update'], columns: { read: ['id', 'draft_key', 'metric_set', 'extracted', 'validation', 'status', 'channel', 'created_at', 'expires_at', 'committed_at'], insert: ['draft_key', 'metric_set', 'extracted', 'validation', 'status', 'channel', 'expires_at'], update: ['extracted', 'validation', 'status', 'committed_at'], }, }); export const HEALTH_BASELINES_DATASET = Object.freeze({ name: 'health_baselines', table: 'health_baselines', description: 'MeMind Health 个人基线(引擎写入)', actions: ['read', 'insert', 'update'], columns: { read: [ 'id', 'metric_type', 'context', 'window_days', 'computed_at', 'sample_count', 'mean_val', 'std_val', 'p25_val', 'p75_val', 'typical_low', 'typical_high', 'maturity', ], insert: [ 'metric_type', 'context', 'window_days', 'computed_at', 'sample_count', 'mean_val', 'std_val', 'p25_val', 'p75_val', 'typical_low', 'typical_high', 'maturity', ], update: [ 'computed_at', 'sample_count', 'mean_val', 'std_val', 'p25_val', 'p75_val', 'typical_low', 'typical_high', 'maturity', ], }, }); export const HEALTH_EVENTS_DATASET = Object.freeze({ name: 'health_events', table: 'health_events', description: 'MeMind Health 偏离与阈值事件', actions: ['read', 'insert', 'update'], columns: { read: [ 'id', 'event_type', 'severity', 'detected_at', 'rule_id', 'metrics_involved', 'evidence_observation_ids', 'agent_summary', 'status', 'created_at', ], insert: [ 'event_type', 'severity', 'detected_at', 'rule_id', 'metrics_involved', 'evidence_observation_ids', 'agent_summary', 'status', ], update: ['severity', 'agent_summary', 'status'], }, }); export const HEALTH_PAGE_DATA_DATASETS = Object.freeze([ HEALTH_OBSERVATIONS_DATASET, HEALTH_DOCUMENTS_DATASET, HEALTH_OBSERVATION_DRAFTS_DATASET, HEALTH_BASELINES_DATASET, HEALTH_EVENTS_DATASET, ]); export const HEALTH_PAGE_DATA_DDL = Object.freeze([ `CREATE TABLE IF NOT EXISTS health_observations ( id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, observed_at TIMESTAMPTZ NOT NULL, metric_type TEXT NOT NULL, value_num NUMERIC(10,2) NULL, value_text TEXT NULL, unit TEXT NULL, context TEXT NULL, source TEXT NOT NULL, source_ref TEXT NULL, time_source TEXT NOT NULL DEFAULT 'user_specified', confidence NUMERIC(4,3) NULL, quality_flag TEXT NULL, raw_payload JSONB NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, deleted_at TIMESTAMPTZ NULL )`, `CREATE INDEX IF NOT EXISTS idx_health_obs_metric_time ON health_observations (metric_type, observed_at DESC)`, `CREATE UNIQUE INDEX IF NOT EXISTS uq_health_obs_dedup ON health_observations (metric_type, observed_at, source_ref) WHERE source_ref IS NOT NULL AND deleted_at IS NULL`, `CREATE TABLE IF NOT EXISTS health_documents ( id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, doc_type TEXT NOT NULL DEFAULT 'other', report_date DATE NULL, institution TEXT NULL, title TEXT NOT NULL, asset_id TEXT NULL, ocr_text TEXT NULL, extracted_metrics JSONB NOT NULL DEFAULT '[]'::jsonb, extraction_status TEXT NOT NULL DEFAULT 'pending', created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, deleted_at TIMESTAMPTZ NULL )`, `CREATE TABLE IF NOT EXISTS health_observation_drafts ( id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, draft_key TEXT NOT NULL UNIQUE, metric_set TEXT NOT NULL, extracted JSONB NOT NULL, validation JSONB NOT NULL, status TEXT NOT NULL DEFAULT 'pending', channel TEXT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, expires_at TIMESTAMPTZ NOT NULL, committed_at TIMESTAMPTZ NULL )`, `CREATE TABLE IF NOT EXISTS health_baselines ( id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, metric_type TEXT NOT NULL, context TEXT NOT NULL, window_days INT NOT NULL, computed_at TIMESTAMPTZ NOT NULL, sample_count INT NOT NULL, mean_val NUMERIC(10,2) NULL, std_val NUMERIC(10,2) NULL, p25_val NUMERIC(10,2) NULL, p75_val NUMERIC(10,2) NULL, typical_low NUMERIC(10,2) NULL, typical_high NUMERIC(10,2) NULL, maturity TEXT NOT NULL, UNIQUE (metric_type, context, window_days) )`, `CREATE TABLE IF NOT EXISTS health_events ( id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, event_type TEXT NOT NULL, severity TEXT NOT NULL, detected_at TIMESTAMPTZ NOT NULL, rule_id TEXT NULL, metrics_involved JSONB NOT NULL DEFAULT '[]'::jsonb, evidence_observation_ids JSONB NOT NULL DEFAULT '[]'::jsonb, agent_summary TEXT NULL, status TEXT NOT NULL DEFAULT 'open', created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP )`, `CREATE INDEX IF NOT EXISTS idx_health_events_open ON health_events (status, detected_at DESC)`, ]); /** SQLite test backend (NODE_TEST_CONTEXT) — same columns, portable types. */ export const HEALTH_PAGE_DATA_DDL_SQLITE = Object.freeze([ `CREATE TABLE IF NOT EXISTS health_observations ( id INTEGER PRIMARY KEY AUTOINCREMENT, observed_at TEXT NOT NULL, metric_type TEXT NOT NULL, value_num REAL NULL, value_text TEXT NULL, unit TEXT NULL, context TEXT NULL, source TEXT NOT NULL, source_ref TEXT NULL, time_source TEXT NOT NULL DEFAULT 'user_specified', confidence REAL NULL, quality_flag TEXT NULL, raw_payload TEXT NULL, created_at TEXT NOT NULL DEFAULT (datetime('now', 'localtime')), deleted_at TEXT NULL )`, `CREATE INDEX IF NOT EXISTS idx_health_obs_metric_time ON health_observations (metric_type, observed_at)`, `CREATE TABLE IF NOT EXISTS health_documents ( id INTEGER PRIMARY KEY AUTOINCREMENT, doc_type TEXT NOT NULL DEFAULT 'other', report_date TEXT NULL, institution TEXT NULL, title TEXT NOT NULL, asset_id TEXT NULL, ocr_text TEXT NULL, extracted_metrics TEXT NOT NULL DEFAULT '[]', extraction_status TEXT NOT NULL DEFAULT 'pending', created_at TEXT NOT NULL DEFAULT (datetime('now', 'localtime')), deleted_at TEXT NULL )`, `CREATE TABLE IF NOT EXISTS health_observation_drafts ( id INTEGER PRIMARY KEY AUTOINCREMENT, draft_key TEXT NOT NULL UNIQUE, metric_set TEXT NOT NULL, extracted TEXT NOT NULL, validation TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'pending', channel TEXT NOT NULL, created_at TEXT NOT NULL DEFAULT (datetime('now', 'localtime')), expires_at TEXT NOT NULL, committed_at TEXT NULL )`, `CREATE TABLE IF NOT EXISTS health_baselines ( id INTEGER PRIMARY KEY AUTOINCREMENT, metric_type TEXT NOT NULL, context TEXT NOT NULL, window_days INTEGER NOT NULL, computed_at TEXT NOT NULL, sample_count INTEGER NOT NULL, mean_val REAL NULL, std_val REAL NULL, p25_val REAL NULL, p75_val REAL NULL, typical_low REAL NULL, typical_high REAL NULL, maturity TEXT NOT NULL, UNIQUE (metric_type, context, window_days) )`, `CREATE TABLE IF NOT EXISTS health_events ( id INTEGER PRIMARY KEY AUTOINCREMENT, event_type TEXT NOT NULL, severity TEXT NOT NULL, detected_at TEXT NOT NULL, rule_id TEXT NULL, metrics_involved TEXT NOT NULL DEFAULT '[]', evidence_observation_ids TEXT NOT NULL DEFAULT '[]', agent_summary TEXT NULL, status TEXT NOT NULL DEFAULT 'open', created_at TEXT NOT NULL DEFAULT (datetime('now', 'localtime')) )`, `CREATE INDEX IF NOT EXISTS idx_health_events_open ON health_events (status, detected_at)`, ]); export function healthPageDataDdlForBackend(backend = 'postgres') { return String(backend).toLowerCase() === 'sqlite' ? HEALTH_PAGE_DATA_DDL_SQLITE : HEALTH_PAGE_DATA_DDL; }