8.6 KiB
8.6 KiB
Модель БД (PostgreSQL)
Общие принципы
- Используется PostgreSQL, развёрнутый локально (например, на Synology).
- Основные сущности:
accounts— банковские счета пользователя;transactions— движения средств по счетам;category_rules— правила автоматической категоризации транзакций (подготовка к SPA-редактору правил).
- Все суммы хранятся в минорных единицах (копейки) как
BIGINT. - Время хранится в
TIMESTAMPTZ(временная зона сохраняется).
Таблица accounts
Предназначение: хранение информации о счетах, по которым загружаются выписки.
Структура:
id BIGSERIAL PRIMARY KEY— внутренний идентификатор счёта в системе.bank TEXT NOT NULL— код/имя банка (например,"VTB").account_number TEXT NOT NULL— номер счёта в банке (как в выписке).currency TEXT NOT NULL— код валюты счёта (например,"RUB").
Ограничения и индексы:
- Уникальность комбинации
(bank, account_number)— один и тот же счёт в банке не должен дублироваться.
Рекомендуемый DDL:
CREATE TABLE accounts (
id BIGSERIAL PRIMARY KEY,
bank TEXT NOT NULL,
account_number TEXT NOT NULL,
currency TEXT NOT NULL
);
CREATE UNIQUE INDEX ux_accounts_bank_number
ON accounts(bank, account_number);
Таблица transactions
Предназначение: хранение всех операций по счетам с привязкой к accounts.
Структура:
id BIGSERIAL PRIMARY KEY— внутренний идентификатор транзакции.account_id BIGINT NOT NULL— внешний ключ (FK) наaccounts(id); каждая транзакция жёстко привязана к одному счёту.operation_at TIMESTAMPTZ NOT NULL— дата и время операции.amount_signed BIGINT NOT NULL— сумма операции в копейках; знак отражает тип движения (приход/расход).commission BIGINT NOT NULL— комиссия по операции в копейках.description TEXT NOT NULL— описание операции из выписки.direction TEXT NOT NULL— направление движения:"income"— приход;"expense"— расход;"transfer"— перевод между своими счетами / на другие свои счета;"internal"— служебные/внутренние движения (опционально, по мере необходимости).
fingerprint TEXT NOT NULL— вычисляемый хэш для обеспечения идемпотентности импорта.category_id BIGINT— ссылка на таблицу категорий (будет определена позже).created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()— время создания записи в БД.updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()— время последнего обновления записи.
Ограничения и индексы:
- Внешний ключ
account_idссылается наaccounts(id)и обеспечивает целостность (нельзя создать транзакцию для несуществующего счёта). - Уникальный индекс
(account_id, fingerprint)обеспечивает идемпотентность: одна и та же операция не может быть загружена дважды. - Опционально — CHECK-ограничение на поле
direction.
Рекомендуемый DDL:
CREATE TABLE transactions (
id BIGSERIAL PRIMARY KEY,
account_id BIGINT NOT NULL REFERENCES accounts(id),
operation_at TIMESTAMPTZ NOT NULL,
amount_signed BIGINT NOT NULL,
commission BIGINT NOT NULL,
description TEXT NOT NULL,
direction TEXT NOT NULL,
fingerprint TEXT NOT NULL,
category_id BIGINT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE UNIQUE INDEX ux_transactions_account_fingerprint
ON transactions(account_id, fingerprint);
ALTER TABLE transactions
ADD CONSTRAINT chk_transactions_direction
CHECK (direction IN ('income', 'expense', 'transfer', 'internal'));
Таблица category_rules
Предназначение: хранение правил автоматической категоризации транзакций на основе текста описания.
Структура:
id BIGSERIAL PRIMARY KEY— идентификатор правила.pattern TEXT NOT NULL— строка-шаблон, вводимая пользователем через SPA (в простом виде).match_type TEXT NOT NULL— тип сопоставления:"contains"— простое вхождение подстроки;"starts_with"— строка начинается с шаблона;"regex"— регулярное выражение (формируется и/или проверяется в коде на основе пользовательского ввода).
category_id BIGINT NOT NULL— ссылка на категорию (таблица категорий будет описана отдельно).priority INT NOT NULL DEFAULT 0— приоритет правила; чем выше число, тем раньше правило применяется при конфликте.is_active BOOLEAN NOT NULL DEFAULT TRUE— активно ли правило.created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()— время создания правила.
Логика приоритета:
- При авто-категоризации для транзакции ищутся все правила, которые ей соответствуют.
- Если совпало несколько правил, выбирается правило с максимальным
priority. - Это позволяет задавать общие правила с низким приоритетом и более точные (например, по конкретным мерчантам) с высоким приоритетом.
Рекомендуемый DDL:
CREATE TABLE category_rules (
id BIGSERIAL PRIMARY KEY,
pattern TEXT NOT NULL,
match_type TEXT NOT NULL,
category_id BIGINT NOT NULL,
priority INT NOT NULL DEFAULT 0,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
Взаимосвязь JSON → БД при импорте
-
По полям
bankиstatement.accountNumberищется или создаётся запись вaccounts. -
Для каждой транзакции из
transactions:- преобразуются суммы в копейки (
amountSigned,commission→BIGINT); - вычисляется
fingerprintна основе комбинации полей (например,accountNumber + operationAt + amountSigned + commission + normalizedDescription); - определяется
directionпо знаку суммы и/или шаблонам текста (приход/расход/перевод); - выполняется попытка вставки в
transactions; - при срабатывании уникального ограничения
(account_id, fingerprint)запись считается дубликатом и пропускается. - При импорте новых транзакций is_category_confirmed всегда = FALSE.
- При автокатегоризации обычными правилами, если правило “жёсткое” (не маркетплейс) — TRUE, для маркетплейсов — всегда FALSE.
- преобразуются суммы в копейки (
-
Категория (
category_id) пока может оставатьсяNULLи заполняться на следующих этапах (правила, ИИ-агент, ручное редактирование в SPA).