docs: adds rules and agents specs
This commit is contained in:
147
docs/backlog/db.md
Normal file
147
docs/backlog/db.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# Модель БД (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:
|
||||
|
||||
```sql
|
||||
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:
|
||||
|
||||
```sql
|
||||
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:
|
||||
|
||||
```sql
|
||||
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 → БД при импорте
|
||||
|
||||
1. По полям `bank` и `statement.accountNumber` ищется или создаётся запись в `accounts`.
|
||||
2. Для каждой транзакции из `transactions`:
|
||||
- преобразуются суммы в копейки (`amountSigned`, `commission` → `BIGINT`);
|
||||
- вычисляется `fingerprint` на основе комбинации полей (например,
|
||||
`accountNumber + operationAt + amountSigned + commission + normalizedDescription`);
|
||||
- определяется `direction` по знаку суммы и/или шаблонам текста (приход/расход/перевод);
|
||||
- выполняется попытка вставки в `transactions`;
|
||||
- при срабатывании уникального ограничения `(account_id, fingerprint)` запись считается дубликатом и пропускается.
|
||||
- При импорте новых транзакций is_category_confirmed всегда = FALSE.
|
||||
- При автокатегоризации обычными правилами, если правило “жёсткое” (не маркетплейс) — TRUE, для маркетплейсов — всегда FALSE.
|
||||
|
||||
3. Категория (`category_id`) пока может оставаться `NULL` и заполняться на следующих этапах
|
||||
(правила, ИИ-агент, ручное редактирование в SPA).
|
||||
Reference in New Issue
Block a user