120 lines
9.2 KiB
Markdown
120 lines
9.2 KiB
Markdown
# Структура БД и модель данных
|
||
|
||
Документ описывает схемы PostgreSQL, которые используются приложением, а также слой абстракции на базе Ktorm ORM.
|
||
|
||
## Технологический стек
|
||
|
||
- **PostgreSQL** – основная СУБД. Подключение выполняется через HikariCP (см. `app/Database.kt`), параметры берутся из переменных окружения `DB_*`.
|
||
- **Ktorm ORM** – легковесная ORM, которая:
|
||
- предоставляет интерфейсы `Entity` для объектного представления строк;
|
||
- описывает таблицы через `object Table<T>` (например, `object NewsT : Table<News>("t_news")`);
|
||
- дает `Database.sequenceOf(Table)` для CRUD-операций и построения SQL через DSL.
|
||
|
||
Каждая доменная сущность имеет:
|
||
|
||
1. `interface <Entity> : Entity<<Entity>>` – декларация полей.
|
||
2. `object <Table> : Table<<Entity>>("table_name")` – описание колонок/ключей и связей через `bindTo`/`references`.
|
||
3. DTO для сериализации в API.
|
||
|
||
## Таблицы
|
||
|
||
### `t_admins`
|
||
|
||
| Колонка | Тип | Описание |
|
||
|----------------|------------------|---------------------------------------|
|
||
| `id` | `bigint` (PK) | Идентификатор администратора |
|
||
| `username` | `varchar` | Уникальное имя пользователя |
|
||
| `password_hash`| `varchar` | Хеш пароля (bcrypt) |
|
||
| `created_at` | `timestamp` | Дата регистрации |
|
||
| `last_login_at`| `timestamp` nul. | Время последней авторизации |
|
||
|
||
Сущность: `AdminEntity`, таблица: `AdminUsers`. Используется модулем `admin` для регистрации, логина, смены паролей и удаления аккаунтов.
|
||
|
||
### `t_news`
|
||
|
||
| Колонка | Тип | Описание |
|
||
|----------------|------------------|--------------------------------------------------|
|
||
| `id` | `bigint` (PK) | Идентификатор новости |
|
||
| `title` | `varchar` | Заголовок |
|
||
| `slug` | `varchar` | Уникальный slug для ссылок |
|
||
| `summary` | `varchar` | Краткое описание |
|
||
| `content` | `text` | Основной текст |
|
||
| `status` | `varchar` | `DRAFT` \| `PUBLISHED` \| `ARCHIVED` |
|
||
| `published_at` | `timestamp` nul. | Дата публикации |
|
||
| `image_url` | `varchar` nul. | Ссылка на изображение |
|
||
| `created_at` | `timestamp` | Дата создания |
|
||
| `updated_at` | `timestamp` | Дата последнего обновления |
|
||
|
||
Сущность: `News`, таблица: `NewsT`. Репозиторий использует фильтры по статусу и slug для публичных и админских запросов.
|
||
|
||
### `t_service_categories`
|
||
|
||
| Колонка | Тип | Описание |
|
||
|---------|---------------|-------------------------|
|
||
| `id` | `bigint` (PK) | Идентификатор категории |
|
||
| `name` | `varchar` | Название |
|
||
| `slug` | `varchar` | Уникальный slug |
|
||
|
||
Сущность: `ServiceCategoryEntity`, таблица: `ServiceCategories`. Используется как справочник категорий услуг.
|
||
|
||
### `t_services`
|
||
|
||
| Колонка | Тип | Описание |
|
||
|--------------|-------------------|----------------------------------------------------|
|
||
| `id` | `bigint` (PK) | Идентификатор услуги |
|
||
| `title` | `varchar` | Название |
|
||
| `slug` | `varchar` | Уникальный slug |
|
||
| `description`| `text` | Подробное описание |
|
||
| `price_from` | `decimal` nul. | Нижняя граница стоимости |
|
||
| `image_url` | `varchar` nul. | Изображение |
|
||
| `status` | `varchar` | `PUBLISHED` \| `DRAFT` \| `ARCHIVED` |
|
||
| `category_id`| `bigint` FK | Ссылка на `t_service_categories.id` (может быть `NULL`) |
|
||
| `created_at` | `timestamp` | Дата создания |
|
||
| `updated_at` | `timestamp` | Дата обновления |
|
||
|
||
Сущность: `ServiceEntity`, таблица: `Services`. Поле `category` смоделировано через `references(ServiceCategories)` и возвращает `ServiceCategoryEntity?`. Сервис объединяет услуги и категории для публичного и админского API.
|
||
|
||
### `t_users` (лиды)
|
||
|
||
| Колонка | Тип | Описание |
|
||
|-------------|-----------------|------------------------------------------|
|
||
| `id` | `bigint` (PK) | Идентификатор лида |
|
||
| `full_name` | `varchar` | Имя и фамилия |
|
||
| `email` | `varchar` | Контактный email |
|
||
| `phone` | `varchar` nul. | Телефон |
|
||
| `created_at`| `timestamp` | Дата поступления заявки |
|
||
|
||
Сущность: `LeadEntity`, таблица: `Leads`. Несмотря на название таблицы `t_users`, фактически хранит только заявки. Админский модуль использует пагинацию и поиск по `full_name`/`email`.
|
||
|
||
## Связи между сущностями
|
||
|
||
- **Service → ServiceCategory (многие к одному)**: `Services.category_id` ссылается на `ServiceCategories.id`. Ktorm позволяет навигировать через `ServiceEntity.category`. При выборе услуг можно жадно загружать категорию и формировать вложенный DTO.
|
||
- **Admin, News, Leads** – независимые сущности без внешних ключей на другие таблицы (в текущей версии).
|
||
- **Status/enum поля** находятся в бизнес-логике: нет отдельных таблиц для статусов, их значения валидируются сервисами.
|
||
|
||
## Использование Ktorm
|
||
|
||
- **Entity интерфейсы** – объявляют свойства и их типы. Экземпляры создаются через `Entity.Factory`.
|
||
- **Table объекты** – задают имя таблицы, колонки и связи. Пример:
|
||
|
||
```kotlin
|
||
object Services : Table<ServiceEntity>("t_services") {
|
||
val title = varchar("title").bindTo { it.title }
|
||
val category = long("category_id").references(ServiceCategories) { it.category }
|
||
}
|
||
```
|
||
|
||
- **Расширения Database** – в каждом модуле есть свой `val Database.<entities>` (например, `Database.news`) для получения `sequenceOf(Table)`:
|
||
|
||
```kotlin
|
||
val Database.news get() = this.sequenceOf(NewsT)
|
||
```
|
||
|
||
Это скрывает детали доступа к данным и дает типобезопасные операции (`filter`, `sortedBy`, `take`, `drop`, `insert`, `update` и т.д.).
|
||
|
||
- **DTO слой** – отделяет Ktorm-entity от сериализуемых объектов (например, `NewsDTO`, `ServiceDTO`). Преобразование выполняется в сервисах.
|
||
|
||
## Итог
|
||
|
||
База данных состоит из пяти основных таблиц, объединенных единым пулом соединений и общими практиками (таймстемпы, статусы, slug). Слабое связывание между сущностями делает схему гибкой, а Ktorm обеспечивает лаконичный и типобезопасный доступ к данным без тяжелых ORM-схем. Диаграмма CRUD-слоя (`docs/crud-model.svg`) дополняет данное описание визуальным представлением потока данных.
|