feat: add v3 extraction storage and structured parsing

This commit is contained in:
Repinoid
2026-10-31 09:35:44 +03:00
parent 1878649317
commit 0a318aa006
6 changed files with 669 additions and 23 deletions
+114
View File
@@ -130,11 +130,125 @@ def _create_schema(conn):
created_at TEXT DEFAULT (datetime('now'))
);
-- Извлечённый паспорт хранится отдельно от исходного документа, чтобы
-- повторный запуск пайплайна не перезаписывал исходные байты и парсинг.
CREATE TABLE IF NOT EXISTS document_extractions (
id TEXT PRIMARY KEY,
document_id TEXT NOT NULL UNIQUE REFERENCES documents(id) ON DELETE CASCADE,
doc_class TEXT NOT NULL DEFAULT 'unknown',
intent_mode TEXT NOT NULL DEFAULT 'no_spec',
own_number TEXT,
own_date TEXT,
counterparty_name TEXT,
counterparty_inn TEXT,
referenced_numbers_json TEXT NOT NULL DEFAULT '[]',
sections_json TEXT NOT NULL DEFAULT '[]',
math_validation_json TEXT NOT NULL DEFAULT '{}',
status TEXT NOT NULL DEFAULT 'pending',
prompt_version TEXT NOT NULL DEFAULT '',
raw_llm_response TEXT NOT NULL DEFAULT '{}',
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now'))
);
-- Досье создаётся в рамках batch; отсутствие рамочного договора не
-- запрещает реконструкцию по спецификациям и дополнительным соглашениям.
CREATE TABLE IF NOT EXISTS contract_dossiers (
id TEXT PRIMARY KEY,
batch_id TEXT,
canonical_number TEXT NOT NULL,
alias_numbers_json TEXT NOT NULL DEFAULT '[]',
counterparty_name TEXT NOT NULL DEFAULT '',
counterparty_inn TEXT NOT NULL DEFAULT '',
has_frame_contract INTEGER NOT NULL DEFAULT 0,
status TEXT NOT NULL DEFAULT 'ACTIVE',
created_at TEXT DEFAULT (datetime('now'))
);
-- Связь задаёт принятую принадлежность документа и сохраняет её основание.
-- UNIQUE предотвращает дубли при повторной сборке групп одного batch.
CREATE TABLE IF NOT EXISTS dossier_documents (
id TEXT PRIMARY KEY,
dossier_id TEXT NOT NULL REFERENCES contract_dossiers(id) ON DELETE CASCADE,
document_id TEXT NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
role TEXT NOT NULL DEFAULT 'supplement',
link_status TEXT NOT NULL DEFAULT 'CONFIRMED',
link_reason TEXT NOT NULL DEFAULT '',
seq_order INTEGER NOT NULL DEFAULT 0,
created_at TEXT DEFAULT (datetime('now')),
UNIQUE (dossier_id, document_id)
);
-- Идентичность услуги отделена от параметров и цены, которые меняются
-- по временным интервалам.
CREATE TABLE IF NOT EXISTS service_items (
id TEXT PRIMARY KEY,
dossier_id TEXT NOT NULL REFERENCES contract_dossiers(id) ON DELETE CASCADE,
service_family TEXT NOT NULL,
instance_anchor TEXT NOT NULL DEFAULT '',
canonical_name TEXT NOT NULL,
catalog_sku TEXT,
sku_status TEXT NOT NULL DEFAULT 'UNKNOWN',
created_at TEXT DEFAULT (datetime('now'))
);
-- Источник интервала каскадно удаляется вместе с документом. Документ,
-- закрывший интервал, очищается через SET NULL: сам интервал остаётся.
CREATE TABLE IF NOT EXISTS timeline_intervals (
id TEXT PRIMARY KEY,
dossier_id TEXT NOT NULL REFERENCES contract_dossiers(id) ON DELETE CASCADE,
service_item_id TEXT NOT NULL REFERENCES service_items(id) ON DELETE CASCADE,
charge_type TEXT NOT NULL DEFAULT 'recurring',
valid_from TEXT,
valid_to TEXT,
date_status TEXT NOT NULL DEFAULT 'explicit',
date_basis TEXT NOT NULL DEFAULT '',
raw_name TEXT NOT NULL,
config_params_json TEXT NOT NULL DEFAULT '{}',
price TEXT,
qty TEXT,
sum TEXT,
vat_mode TEXT NOT NULL DEFAULT 'with_vat',
source_document_id TEXT REFERENCES documents(id) ON DELETE CASCADE,
closed_by_document_id TEXT REFERENCES documents(id) ON DELETE SET NULL,
table_ref TEXT NOT NULL DEFAULT '',
quote TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL DEFAULT 'VERIFIED',
created_at TEXT DEFAULT (datetime('now'))
);
-- Исключения могут быть привязаны к разным этапам и разрешаться отдельно.
CREATE TABLE IF NOT EXISTS exceptions_queue (
id TEXT PRIMARY KEY,
batch_id TEXT,
dossier_id TEXT REFERENCES contract_dossiers(id) ON DELETE CASCADE,
document_id TEXT REFERENCES documents(id) ON DELETE CASCADE,
service_item_id TEXT REFERENCES service_items(id) ON DELETE SET NULL,
severity TEXT NOT NULL DEFAULT 'CONFLICT',
stage TEXT NOT NULL,
title TEXT NOT NULL,
source_quote TEXT NOT NULL DEFAULT '',
options_json TEXT NOT NULL DEFAULT '[]',
resolution_json TEXT,
status TEXT NOT NULL DEFAULT 'OPEN',
created_at TEXT DEFAULT (datetime('now')),
resolved_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_documents_batch ON documents(batch_id);
CREATE INDEX IF NOT EXISTS idx_documents_hash ON documents(batch_id, content_hash);
CREATE INDEX IF NOT EXISTS idx_supplements_contract ON supplements(contract_id);
CREATE INDEX IF NOT EXISTS idx_spec_current_contract ON spec_current(contract_id);
CREATE INDEX IF NOT EXISTS idx_spec_events_supplement ON spec_events(supplement_id);
CREATE INDEX IF NOT EXISTS idx_extractions_document ON document_extractions(document_id);
CREATE INDEX IF NOT EXISTS idx_dossiers_batch ON contract_dossiers(batch_id);
CREATE INDEX IF NOT EXISTS idx_dossier_documents_dossier ON dossier_documents(dossier_id);
CREATE INDEX IF NOT EXISTS idx_dossier_documents_document ON dossier_documents(document_id);
CREATE INDEX IF NOT EXISTS idx_service_items_dossier ON service_items(dossier_id);
CREATE INDEX IF NOT EXISTS idx_timeline_dossier ON timeline_intervals(dossier_id);
CREATE INDEX IF NOT EXISTS idx_timeline_service ON timeline_intervals(service_item_id);
CREATE INDEX IF NOT EXISTS idx_exceptions_dossier ON exceptions_queue(dossier_id);
CREATE INDEX IF NOT EXISTS idx_exceptions_batch ON exceptions_queue(batch_id);
""")