Платформа для ботанических садов (SaaS) — SRS-сборка¶
Эта страница собирает разделы для спецификации требований к ПО: от контекста и проблемы до безопасности, качества и эксплуатации.
Содержание¶
- Краткое описание
- Контекст и проблема
- Цели, требования и ограничения
- Роль и обязанности
- Модель системы
- Архитектура и интеграции
- Безопасность, качество и эксплуатация
Краткое описание¶
Статус¶
MVP / проведение пилота
Роль¶
Сооснователь, технический владелец архитектуры, системный проектировщик
Стек¶
Java 21, Spring Boot, PostgreSQL/PostGIS, MinIO/S3-compatible storage, Angular, OpenLayers, Docker
Ценность проекта¶
Для ЦА (ботанические сады, питомники, селекционеры, держатели научных коллекций и частных собраний растений):
-
переход от разрозненных Excel/локальных БД к единой web-платформе;
-
единый таксономический слой как основа сопоставимости коллекционных данных;
-
управляемое раскрытие данных, публичные страницы и QR-сценарии;
-
база для сетевых сценариев обмена между организациями.
Для профессионального профиля:
-
проектирование и реализация SaaS от доменной идеи до MVP и пилотного запуска;
-
работа с multi-tenancy, RBAC, доменной моделью данных, API, GIS, AI-assisted импортом, подготовкой публичных данных и приведение в соответствие законодательным нормам (152-ФЗ);
-
осознанное и экономичное применение AI без передачи архитектурных решений модели.
Что демонстрирует¶
Этот проект демонстрирует мои способности:
-
работать на стыке системного анализа, системного дизайна, имплементации и деплоя.
-
довести идею до запуска системы с несколькими зонами доступности и дорожной картой масштабирования
-
стратегически планировать имплементацию SaaS малого-среднего масштаба
-
безопасно и контролируемо применять LLM-генерацию кода для имплементации фичей
Контекст и проблема¶
Контекст¶
У целевой аудитории уже есть данные, но они рассыпаны по бумажным журналам, Excel, Word/PDF, Access/FileMaker, локальным БД, старым desktop-системам и частично по специализированным решениям. Поэтому проект не продаёт "учёт с нуля", а продаёт переход из хаотичного, несопоставимого и плохо публикуемого состояния в структурированную систему.
Для ботанических садов грубая оценка такая: Excel остаётся главным операционным слоем у 35–50%, бумага значима у 10–25%, локальные БД и desktop-системы у 20–30%, современные web/cloud-системы у 5–15%. Специализированные системы типа IrisBG, BG-BASE, BRAHMS и Hortis закрывают профессиональный верх рынка, но не доминируют глобально.
BGCI не является прямым конкурентом как рабочая система учёта. BGCI важен как глобальный справочный и агрегационный слой: GardenSearch показывает организации, PlantSearch агрегирует taxon-level данные из коллекций. Но он не заменяет повседневный учёт экземпляров, перемещений, выпадов, фото, GIS, QR, внутренние роли, импорт и отчётность.
По сегментам картина различается. Частные коллекционеры в основном используют Excel, Google Sheets, бумагу, Notion/Airtable или простые hobby-инструменты. Селекционеры ведут учёт крестов, сеянцев, линий и испытаний, часто в Excel или внутренних базах. Регистраторы культиваров работают с таблицами, PDF, локальными БД и публичными регистрами. Музеи сильнее цифровизированы в preserved collections, но живые коллекции часто учитываются ближе к ботсадам. Питомники чаще имеют ERP/web-учёт, но их фокус - склад, продажи и партии, а не научная коллекция.
Главная боль рынка - несопоставимость данных. В Excel один и тот же таксон может быть записан латиницей, с автором, без автора, по-русски, через синоним, с опечаткой или старым названием. Поэтому автоматический обмен, поиск, отчётность и сравнение коллекций почти невозможны без единого taxon layer.
Проблема¶
Ботанические коллекции часто ведутся в разрозненных таблицах, локальных базах и внутренних документах отдельных организаций. Это затрудняет:
- поддержание консистентных записей о растениях;
- привязку растений к проверенной научной таксономии;
- отслеживание местоположения растений и изменений их состояния;
- управление доступом между подразделениями и организациями;
- безопасный импорт исторических данных;
- публичную публикацию выбранных данных о коллекции;
- обмен информацией между учреждениями и коллекционерами (обмен семенным фондом в рамках повышения биоразнообразия видов).
Botanical SaaS решает эту проблему как единая операционная платформа для живых коллекций растений.
Цели, требования и ограничения¶
Цели и нецели¶
Основные цели проекта¶
- Перевести учёт живых коллекций растений из Excel, бумажных журналов и локальных баз в единую web-систему.
- Обеспечить научно корректную привязку записей к глобальному источнику таксономии, культиварам и грексам.
- Дать ботаническим садам, коллекционерам, музеям и питомникам инструмент для учёта экземпляров, мест размещения, фото, статусов, поступлений и выбытий.
- Реализовать GIS-ядро: карта территории, участки, оранжереи, грядки, точки растений и полигоны их размещения.
- Упростить миграцию существующих данных через импорт Excel, сопоставление столбцов, проверку названий и отчёты по ошибкам.
- Создать публичную витрину организаций: страницы садов, публичные списки, карточки растений и QR-этикетки.
- Поддержать избирательное раскрытие данных.
- Построить сетевой слой для ботанического сообщества: списки обмена, wishlists и автоматическое сопоставление желаемых таксонов с доступным материалом.
- Дать администрациям и кураторам отчётность по коллекционным фондам, подразделениям, таксонам, поступлениям и выбытиям.
- Сформировать основу для долгосрочного контура культиваров, регистраторов и отраслевой стандартизации данных.
- Обеспечить генерацию этикеток с QR-кодами, ведущими на карточку растения в системе.
Что не входит в проект¶
- Не является заменой BGCI как глобального справочника ботанических садов и агрегатора данных уровня биологических таксонов.
- Не является гербарной системой полного цикла для гербарных коллекций, музейных фондов.
- Не является ERP для коммерческих питомников с фокусом на продажи, склад, закупки, производство, кассы и бухгалтерию.
- Не является CRM, тасктрекером, системой документооборота или корпоративным порталом организации.
- Не включает физическую печать, изготовление, установку и обслуживание QR-этикеток на территории организации.
- Не решает юридическую регистрацию культиваров вместо официальных ICRA/регистраторов; система может только поддерживать цифровой контур данных и заявок.
- Не гарантирует автоматическую очистку всех исторических данных без участия эксперта: спорные названия, синонимы, сорта и ошибки требуют валидации.
- Не раскрывает приватные коллекции по умолчанию; публикация данных остаётся управляемым решением владельца.
- Не является полноценной BI/аналитической платформой общего назначения; отчётность ограничена задачами живых коллекций, поступления и выбытия растений.
- Не включает на первом этапе тяжёлый enterprise-контур: биллинг, SLA, multi-region replication, госзакупки, реестр ПО, кастомные интеграции и отдельные инсталляции под каждого клиента.
Бизнес-Требования¶
-
BR-001. Централизовать учёт живых коллекций растений.
Проект должен предоставить ботаническим организациям, коллекционерам и смежным участникам рынка единую web-систему для замены разрозненных Excel-файлов, бумажных журналов, Word/PDF-документов и локальных баз. -
BR-002. Повысить научную корректность данных о растениях.
Проект должен обеспечить привязку записей о растениях к авторитетной таксономической базе, поддерживать иерархию семейство -> род -> вид, а также отдельный контур культиваров и грексов. -
BR-003. Снизить стоимость и сложность миграции существующих коллекционных данных.
Проект должен позволить импортировать исторические данные из Excel и других табличных источников с сопоставлением столбцов, нормализацией значений, проверкой названий и отчётами по ошибкам. -
BR-004. Обеспечить операционный учёт экземпляров растений.
Проект должен поддерживать ведение карточек конкретных экземпляров растений с инвентарными номерами, статусами, происхождением, формой поступления, местом размещения, фото, пользовательскими полями и историей изменений (полный список атрибутов согласован отдельно). -
BR-005. Связать коллекционные данные с пространственным контекстом.
Проект должен дать организациям возможность картировать территорию, подразделения, участки, оранжереи, грядки и отдельные растения с использованием точек, полигонов и интерактивной карты. -
BR-006. Дать организациям публичную цифровую витрину.
Проект должен позволить создавать публичные страницы организаций, публичные карточки растений, публичные списки и карту коллекций без необходимости разрабатывать отдельный сайт. -
BR-007. Связать физические растения с цифровыми карточками.
Проект должен поддерживать генерацию QR-этикеток, ведущих посетителя или сотрудника к публичной или внутренней карточке растения. -
BR-008. Поддержать управляемое раскрытие данных.
Проект должен позволить владельцам коллекций управлять видимостью данных: приватно, для зарегистрированных пользователей, для сообщества или публично. -
BR-009. Создать сетевой слой обмена между участниками ботанического сообщества.
Проект должен поддерживать списки коллекции, списки обмена и списки желаний, чтобы участники могли сопоставлять желаемые таксоны с доступным материалом у других организаций, соуществляя обмен. -
BR-010. Обеспечить отчётность для кураторов и администрации.
Проект должен предоставлять отчёты по составу коллекций, таксонам, подразделениям, поступлениям, выбытиям, спискам и состоянию коллекционных фондов.
... В публичной версии приведён сокращённый фрагмент требований. Полная детализация не раскрывается из-за объёма и продуктовых ограничений.
Правила и Ограничения¶
Business Rules¶
-
RULE-001. Данные организации закрыты по умолчанию. Коллекционные данные новой организации считаются приватными, пока владелец явно не изменит уровень видимости.
-
RULE-002. Владелец данных управляет публичностью. Организация самостоятельно определяет, какие растения, списки, фото, координаты и страницы доступны публично, сообществу или только внутренним пользователям.
-
RULE-003. Пользователь не может получать доступ к данным чужой организации без разрешения. Доступ к растениям, спискам, местам, фото и импорту должен ограничиваться организацией пользователя и выданными ему ролями.
-
RULE-004. Species-записи должны быть привязаны к справочной таксономии. Пользователи не должны создавать произвольные видовые записи без связи с авторитетным таксономическим справочником.
-
RULE-005. Локальные культивары принадлежат организации-создателю. Культивар, созданный внутри организации, считается локальным и не становится глобально доступным автоматически (только через процедуру регистрации уполномоченным регистратором).
... В публичной версии приведён сокращённый фрагмент бизнес-правил. Полная детализация не раскрывается из-за объёма и продуктовых ограничений.
Ограничения¶
-
CON-001. Система должна быть web/SaaS-решением. Продукт проектируется как web-платформа с централизованным доступом, а не как desktop-приложение.
-
CON-004. Excel должен поддерживаться как основной формат миграции. Система должна учитывать, что исходные данные целевой аудитории чаще всего находятся в Excel-файлах.
-
CON-005. Исходные данные могут быть грязными и неоднородными. Импорт должен учитывать опечатки, синонимы, локальные названия, неполные значения, нестандартные столбцы и исторические форматы учёта.
-
CON-006. GIS-точность ограничена качеством исходных данных. Система может хранить и отображать точки и полигоны, но фактическая точность зависит от действий пользователя и качества полевых данных.
... В публичной версии приведён сокращённый фрагмент ограничений. Полная детализация не раскрывается из-за объёма и продуктовых ограничений.
Роль и обязанности¶
Моя роль¶
Я выступал как сооснователь и технический владелец архитектуры системы.
Моя работа включала:
- перевод экспертных знаний предметной области в структурированную SaaS-модель;
- проектирование backend-архитектуры, API, структур данных, доменной модели;
- реализацию backend-функциональности и интеграционной логики;
- проектирование RBAC с учетом прав доступа в контексте организации (тенанта);
- моделирование экземпляров растений, таксономии, мест размещения, списков, фотографий, импортов и публичных страниц;
- подготовку архитектурной документации, C4-диаграмм и ADR;
- согласование технических решений с экспертом предметной области;
- использование AI-assisted development tools для ускорения реализации при сохранении ручного контроля над архитектурой, моделью данных, API-контрактами, ревью и deployment-решениями.
Применение ИИ¶
Проект разрабатывался с использованием AI.
LLM применялись для ускорения реализации, генерации шаблонного кода и быстрых итераций. Ключевые решения оставались под ручным контролем:
- интерпретация требований;
- доменное моделирование;
- архитектурные решения;
- границы данных;
- модель доступа;
- код ревью;
- дебаг;
- решения по развертыванию;
- техническая документация.
Модель системы¶
Доменная модель¶
Научная таксономия¶
Записи о растениях связаны с централизованным таксономическим слоем (не раскрывается публично).
Модель поддерживает семейства, роды, виды, культивары, грексы, глобальные и локальные культивары, принадлежащие отдельным тенантам.
Модель данных¶
High-Level Data Model¶
erDiagram
ORGANIZATION ||--o{ ORG_UNIT : contains
ORGANIZATION ||--o{ PLANT : owns
ORGANIZATION ||--o{ PLACE : manages
ORGANIZATION ||--o{ PLANT_LIST : maintains
ORGANIZATION ||--o{ PUBLIC_PAGE : publishes
ORG_UNIT ||--o{ PLANT : curates
PLACE ||--o{ PLACE : contains
PLACE ||--o{ PLANT : locates
PLANT }o--|| SYSTEM_TAXON : "required taxon_id"
PLANT ||--o{ PHOTO : documents
PLANT }o--o{ PLANT_LIST : included_in
SYSTEM_TAXON ||--o| GLOBAL_TAXON : "species subtype"
SYSTEM_TAXON ||--o| CULTIVATED_ENTITY : "cultivar/grex subtype"
PUBLIC_PAGE ||--o{ PLANT_LIST : exposes
PUBLIC_PAGE ||--o{ PLANT : exposes_selected
ORGANIZATION {
uuid id PK
string public_name
string slug
string visibility
}
ORG_UNIT {
uuid id PK
uuid organization_id FK
uuid parent_id FK
string name
}
PLANT {
uuid id PK
uuid organization_id FK
uuid org_unit_id FK
uuid place_id FK
uuid taxon_id FK "mandatory"
string accession_number
string individual_code
string status
string provenance_type
geometry point
jsonb custom_fields
}
SYSTEM_TAXON {
uuid id PK
string display_name
string normalized_name
string taxon_type
string rank
}
GLOBAL_TAXON {
uuid id PK
uuid system_taxon_id FK
string id
string ipni_id
string family
string genus
string species_epithet
string scientific_name
}
CULTIVATED_ENTITY {
uuid id PK
uuid system_taxon_id FK
string genus
string cultivar_or_grex_name
string breeder_name
string registrar_name
int registration_year
string visibility_scope
}
PLACE {
uuid id PK
uuid organization_id FK
uuid parent_id FK
string name
geometry point
geometry polygon
}
PLANT_LIST {
uuid id PK
uuid organization_id FK
string name
string list_type
string visibility
}
PHOTO {
uuid id PK
uuid PLANT_id FK
string caption
string photo_tag
boolean publication_allowed
}
PUBLIC_PAGE {
uuid id PK
uuid organization_id FK
string slug
boolean visible
} Ключевая идея модели¶
Центральная сущность системы - Plant, цифровой двойник конкретного растения в живой коллекции. Это не просто строка справочника и не абстрактный вид, а учётная запись конкретного экземпляра: с инвентарным номером, статусом, местом размещения, принадлежностью к организации, фотографиями, списками и публичным представлением.
Обязательный атрибут taxon_id является ядром модели. Он связывает каждый экземпляр растения с единой корневой сущностью SystemTaxon. Благодаря этому все операционные сценарии - учёт, импорт, поиск, списки, обмен, QR-страницы, отчётность и публичная карта -работают не с произвольным текстовым названием растения, а с устойчивым идентификатором таксона.
SystemTaxon выступает общей корневой сущностью для двух крупных таксономических контуров:
- globalTaxon - видовые таксоны, поступающие из авторитетного каталога.
- CultivatedEntity - культивары и грексы, которые могут быть признанными глобальными записями, поступающими от регистраторов, либо локальными записями, созданными внутри конкретной организации.
Такой подход позволяет пользователю выбирать растение из единого taxon lookup, не разделяя искусственно поиск по видам и поиск по культиварам. Для пользователя это выглядит как единый справочник растений, а внутри системы сохраняется различие между научными видовыми таксонами, глобальными культиварами, грексами и локальными культиварами организации.
Архитектурная ценность решения состоит в том, что все коллекционные данные становятся сопоставимыми между организациями. Один и тот же taxon_id может использоваться в карточках растений, списках коллекций, wishlists, списках обмена, публичных страницах и отчётах. Это создаёт основу для сетевых сценариев: система может сопоставлять, какие таксоны одна организация ищет, а другая готова передать, не полагаясь на нестабильные текстовые названия, синонимы, локальные варианты и опечатки.
В портфолио модель показана верхнеуровнево. Внутренние вспомогательные сущности, механизмы разграничения видимости, таблицы доступа, workflow глобализации культиваров и технические детали реализации намеренно опущены.
API-контракты¶
Названия конечных точек и атрибуты намеренно изменены для публичного пространства.
Подход к проектированию API¶
Backend предоставляет REST/JSON API для управления живыми коллекциями растений в мульти-тенантной SaaS-среде. API-контракты организованы вокруг доменных возможностей: учёт растений, поиск по таксономии, массовые операции, импорт, публичные страницы, доступ к медиа и сценарии обмена.
Безопасность обеспечивается на нескольких уровнях:
- JWT-аутентификация через Spring Security.
- Method-level authorization для чувствительных операций.
- Изоляция тенантов через контекст организации / корневого тенанта.
- Фильтрация данных на уровне владения тенантом.
- Bean Validation для request DTO.
- Контролируемые public DTO для анонимных endpoints.
- Soft delete для обратимых удалений.
- Централизованная обработка ошибок со стандартными HTTP-статусами.
1. Поиск экземпляров растений¶
Назначение¶
Получить постраничный список экземпляров растений, доступных текущему пользователю в активном контексте организации.
Контракт¶
GET /api/inventory/plants
Query Parameters¶
keyword необязательный текстовый поиск
organization необязательный контекст тенанта / корневой организации
unit необязательный фильтр по подразделению или коллекционной единице
list необязательный фильтр по списку
recursive необязательный флаг поиска по вложенным подразделениям
filter необязательные фасетные фильтры
page параметр пагинации
size параметр пагинации
sort параметр сортировки
Response¶
{
"items": [
{
"id": "uuid",
"displayName": "string",
"taxon": "summary",
"inventoryCode": "string",
"status": "string",
"location": "summary",
"visibility": "string"
}
],
"page": {
"number": 0,
"size": 25,
"totalElements": 0
}
}
Безопасность¶
- Требуется аутентифицированный пользователь.
- Результат ограничивается tenant-контекстом, доступным текущему пользователю.
- Query specifications применяют tenant-фильтрацию до возврата данных.
- Filter expressions разбираются в allowlisted-критерии, а не выполняются как сырые динамические запросы.
- Пагинация и сортировка используют framework-level pageable parameters.
2. Получение деталей экземпляра растения¶
Назначение¶
Получить один экземпляр растения как цифровую запись реального растения в живой коллекции.
Контракт¶
GET /api/inventory/plants/{plantId}
Response¶
{
"id": "uuid",
"taxon": {
"id": "uuid",
"displayName": "string",
"type": "species | cultivar | grex"
},
"inventory": {
"accessionNumber": "string",
"individualCode": "string",
"status": "string"
},
"organization": "summary",
"location": "summary",
"photos": ["summary"],
"customFields": {}
}
Безопасность¶
- Требуется аутентифицированный пользователь.
- Доступ проверяется по принадлежности растения тенанту и членству пользователя в организации.
- Внутренние поля не раскрываются через публичные контракты.
- Несуществующие или недоступные ресурсы возвращаются через стандартные error responses.
3. Создание экземпляра растения¶
Назначение¶
Создать новый экземпляр растения в живой коллекции организации.
Контракт¶
POST /api/inventory/plants
Request¶
{
"taxonId": "uuid",
"organizationUnitId": "uuid",
"placeId": "uuid",
"inventoryData": {},
"status": "string",
"customFields": {}
}
Response¶
201 Created
{
"id": "uuid",
"displayName": "string",
"taxon": "summary",
"inventoryCode": "string",
"status": "string"
}
Безопасность¶
- Требуется аутентифицированный пользователь.
- Требуется административный или редакторский доступ к целевому подразделению организации.
- Request body валидируется до выполнения доменной логики.
taxonIdобязателен и должен ссылаться на разрешённый вид, культивар или грекс, видимый в текущем tenant-контексте.- Создание передаётся в service layer после авторизации и валидации.
4. Обновление экземпляра растения¶
Назначение¶
Обновить существующий экземпляр растения: инвентарные данные, таксономическую привязку, статус или организационное размещение.
Контракт¶
PUT /api/inventory/plants/{plantId}
Request¶
{
"taxonId": "uuid",
"organizationUnitId": "uuid",
"placeId": "uuid",
"inventoryData": {},
"status": "string",
"customFields": {}
}
Response¶
{
"id": "uuid",
"displayName": "string",
"taxon": "summary",
"status": "string",
"location": "summary"
}
Безопасность¶
- Требуется аутентифицированный пользователь.
- Требуется write access к существующей записи растения.
- Если растение перемещается в другое подразделение, должен быть проверен доступ и к текущему, и к целевому контексту.
- Недопустимые попытки перемещения отклоняются с ошибкой доступа.
- Валидация запроса выполняется через типизированные request DTO и Bean Validation.
5. Soft Delete экземпляра растения¶
Назначение¶
Переместить запись растения в корзину без физического удаления из базы данных.
Контракт¶
DELETE /api/inventory/plants/{plantId}
Response¶
204 No Content
Безопасность¶
- Требуется аутентифицированный пользователь.
- Требуется административный доступ к организационному контексту растения.
- Удаление по умолчанию является обратимым.
- Soft-deleted записи исключаются из обычных запросов.
- Permanent delete ограничен повышенными platform-level ролями.
6. Массовые операции с экземплярами растений¶
Назначение¶
Выполнить массовые действия над выбранными экземплярами растений: перемещение, обновление, клонирование, генерация данных для этикеток или отметка этикеток как напечатанных.
Контракты¶
POST /api/inventory/plants/batch/move
POST /api/inventory/plants/batch/update
POST /api/inventory/plants/batch/clone
POST /api/inventory/plants/batch/label-data
POST /api/inventory/plants/batch/mark-printed
Request¶
{
"plantIds": ["uuid"],
"operationPayload": {}
}
Response¶
{
"successCount": 0,
"failureCount": 0,
"results": [
{
"id": "uuid",
"status": "success | failed",
"message": "string"
}
]
}
Безопасность¶
- Требуется аутентифицированный пользователь.
- Каждое затронутое растение должно быть проверено на tenant ownership и права пользователя.
- Batch operations не должны обходить per-resource authorization.
- Для операций, где часть записей может не пройти валидацию или авторизацию, поддерживается partial success.
- Входные коллекции валидируются до обработки.
7. Получение справочников для форм растений¶
Назначение¶
Вернуть контролируемые справочные значения, используемые в формах растений: жизненные статусы, условия выращивания, формы поступления и другие значения.
Контракт¶
GET /api/inventory/dictionaries/{dictionaryType}
Response¶
[
{
"id": "string",
"code": "string",
"label": "string"
}
]
Безопасность¶
- Для внутренних справочников требуется аутентифицированный пользователь.
- Ответы справочников содержат только безопасные reference data.
- Значения контролируются платформой или tenant-конфигурацией, а не произвольным free text.
8. Поиск таксона¶
Назначение¶
Предоставить единый поиск по видам, культиварам и грексам при создании или редактировании экземпляра растения.
Контракт¶
GET /api/taxonomy/search
Query Parameters¶
query поисковая строка
type необязательный фильтр по типу таксона
page параметр пагинации
size параметр пагинации
Response¶
{
"items": [
{
"id": "uuid",
"displayName": "string",
"taxonType": "species | cultivar | grex",
"source": "reference | global | local"
}
]
}
Безопасность¶
- Глобальные reference species доступны всем тенантам.
- Глобальные cultivated entities доступны всем тенантам.
- Локальные cultivated entities видимы только разрешённым tenant-контекстам.
- API скрывает внутреннюю механику видимости и отдаёт только selectable taxon summaries.
9. Workflow умного импорта¶
Назначение¶
Импортировать существующие коллекционные данные из таблиц через staged workflow.
Контракты¶
POST /api/import/sessions
POST /api/import/sessions/{sessionId}/sheet
POST /api/import/sessions/{sessionId}/mapping
POST /api/import/sessions/{sessionId}/resolve
POST /api/import/sessions/{sessionId}/execute
GET /api/import/sessions/{sessionId}/status
GET /api/import/sessions/{sessionId}/error-report
Workflow¶
Загрузка файла
→ Выбор листа
→ Сопоставление столбцов
→ Разрешение значений
→ Подтверждение неоднозначных совпадений
→ Выполнение импорта
→ Просмотр результатов
Response Example¶
{
"sessionId": "uuid",
"status": "uploaded | mapped | resolving | ready | executing | completed | failed",
"progress": {
"processedRows": 0,
"totalRows": 0
}
}
Безопасность¶
- Требуется аутентифицированный пользователь.
- Требуется tenant-контекст и permission на импорт.
- Загруженные файлы хранятся вне реляционной базы данных.
- Import sessions являются tenant-scoped.
- Неоднозначные совпадения требуют подтверждения пользователя.
- Некорректные строки отражаются в отчёте, но не блокируют все валидные строки.
- Для долгих этапов импорта используются background jobs.
10. Публичная страница растения¶
Назначение¶
Предоставить безопасное публичное представление записи растения, обычно используемое как цель QR-этикетки.
Контракт¶
GET /api/public/plants/{publicSlug}
Response¶
{
"displayName": "string",
"taxon": "public summary",
"organization": "public summary",
"photos": ["public media reference"],
"publicDescription": "string"
}
Безопасность¶
- Аутентификация не требуется.
- Возвращаются только public DTO.
- Исключаются внутренние идентификаторы, tenant metadata, приватные заметки, внутренние инвентарные поля и закрытые фотографии.
- Требуются публичные настройки видимости у организации-владельца и контекста растения / списка.
- Доступ к медиа использует short-lived signed URLs или эквивалентный контролируемый механизм доставки.
11. Поиск совпадений для обмена¶
Назначение¶
Найти потенциальные совпадения между wishlist одной организации и exchange lists других организаций.
Контракт¶
GET /api/exchange/matches/{wishlistId}
Response¶
{
"wishlist": "summary",
"matches": [
{
"taxon": "summary",
"sourceOrganization": "public or shared summary",
"availableMaterial": "summary",
"visibility": "string"
}
]
}
Безопасность¶
- Требуется аутентифицированный пользователь.
- Пользователь должен иметь доступ к исходному wishlist.
- Candidate matches ограничены данными, явно расшаренными для обмена или видимыми в соответствующем community scope.
- Matching основан на taxon identifiers, а не на raw text names.
- Private collection data не раскрывается через exchange API.
Паттерн обработки ошибок¶
{
"status": 400,
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": []
}
Типовые HTTP-статусы:
200 OK успешное чтение или обновление
201 Created ресурс создан
204 No Content успешное удаление
400 Bad Request некорректный ввод или ошибка доменной валидации
403 Forbidden пользователь аутентифицирован, но доступ запрещён
404 Not Found ресурс отсутствует или не видим пользователю
409 Conflict конфликт состояния или ресурс используется
429 Too Many Requests превышен rate limit
Summary по безопасности¶
API-дизайн сочетает framework-level и domain-level protection:
- Spring Security аутентифицирует запросы и формирует контекст пользователя.
- JWT несёт идентичность, а роли и права резолвятся на серверной стороне.
- Method-level проверки защищают операции записи и административные действия.
- Tenant-scoped запросы предотвращают утечку данных между организациями.
- DTO validation блокирует некорректные запросы до выполнения бизнес-логики.
- Public endpoints используют отдельные DTO с ограниченным набором полей.
- Soft delete защищает от случайной потери данных.
- Операции уровня всей платформы отделены от операций тенантов.
- Audit и login events обеспечивают трассируемость чувствительных действий.
Архитектура и интеграции¶
Архитектура¶
Контейнерная диаграмма (C4 Container)¶
C4Container
title Диаграмма контейнеров для Botanical SaaS MVP
Person(user, "Пользователь")
System_Boundary(sys, "Система") {
Container(spa, "Single Page Application", "Angular 21, OpenLayers", "Пользовательский интерфейс")
Container(static, "Frontend Static", "nginx", "Контейнер для хранения<br/> статических frontend-файлов")
Container(backend, "Backend Business Logic", "Spring Boot", "Бизнес-логика")
ContainerDb(reldb, "Relational DB", "PostgreSQL + PostGIS", "Экземпляры растений, списки,<br/> пользователи, RBAC-роли")
ContainerDb(objStore, "Object Storage", "MinIO", "Фотографии и мультимедиа.<br/> Файлы импорта коллекций")
}
Boundary(ext, "Внешние системы", "") {
Container_Ext(global, "global", "Таксономический справочник")
Container_Ext(vernacular, "WikiData", "Справочник народных названий<br/> растений")
Container_Ext(llm, "LLM", "Интеллектуальное распознавание<br/> видов")
}
Rel(user, spa, "Использует для<br/> управления коллекциями растений")
Rel(spa, static, "Получает Angular<br/> static UI bundles", "HTTPS")
Rel(spa, backend, "Отправляет API-вызовы", "HTTPS REST")
Rel(backend, reldb, "Читает/записывает данные", "SQL")
Rel(backend, objStore, "Загружает/читает медиа", "S3 API")
Rel(backend, global, "Запрашивает таксоны/культивары,<br/> записывает культивары", "HTTPS/REST")
Rel(backend, vernacular, "Получает названия видов<br/> на национальных языках", "HTTPS/REST")
Rel(backend, llm, "Использует LLM API<br/> для получения подсказок по названиям видов", "HTTPS/OpenAI API compatible")
UpdateLayoutConfig($c4ShapeInRow="5", $c4BoundaryInRow="3")
Потоки интеграции¶
Импорт каталога таксономии¶
Система включает ручной механизм обновления внутреннего справочника таксонов из выгрузки xls с сохранением идентификаторов.
Пополнение национальных названий таксонов¶
Система включает автоматический механизм пополнения внутреннего справочника народных названий растений из открытых источников с учетом ограничений публичного API.
Smart Import¶
Платформа включает мастер импорта XLS для переноса существующих коллекций растений в систему.
Поток поддерживает загрузку файла, выбор листа, сопоставление колонок (в том числе с помощью ИИ), определение значений, нечеткое совпадение, асинхронную обработку, построчные результаты, экспорт Excel с ошибочными строками.
Для сопоставления названий колонок атрибутам сущностей системы, а также для более точного распознавания вида, культивара или перечисления(enum'а) предусмотрена интеграция с LLM и легкая обвязка(harness). Подтверждение распознавания (если оно не было 100%) осуществляется пользователем.
Безопасность, качество и эксплуатация¶
Модель безопасности и доступа¶
Мультитенантность и контроль доступа¶
Платформа использует soft multi-tenancy model на базе root organization unit pattern.
Пользователь может состоять в нескольких организациях и иметь разные роли в зависимости от текущего организационного контекста. Проверки доступа применяются на уровне сервисов, репозиториев, API и UI.
Публичная витрина¶
Организации могут публиковать выбранные данные через публичные страницы, публичные карточки растений, публичные списки, QR-label targets и глобальную карту.
Публичный слой использует отдельные DTO и visibility rules, чтобы не раскрывать внутренние поля и данные тенанта.
Двойной контур RBAC¶
В системе предусмотрена одна сущность User и две модели доступа по ролям: организационные роли и платформенные роли. Один пользователь может быть руководителем нескольких организаций и при этом он будет модератором платформы, другой пользователь выполняет роль инженера поддержки, поэтому должен иметь ограниченный доступ к любому тенанту и объекту, третий пользователь является стейкхолдером и ему доступны только дашборд-модули. Все три пользователя при этом авторизуются через единый флоу авторизации и аутентификации. Подробности механизма не раскрываются.
Нефункциональные требования¶
Ключевые атрибуты качества¶
| Атрибут | Сценарий | Подход |
|---|---|---|
| Security | Пользователь организации A не должен получить данные организации B | tenant-scoped queries, RBAC, public DTO, integration tests |
| Reliability | Зависший импорт не должен блокировать систему | background jobs, stale detection, retry/manual restart |
| Maintainability | Доменная модель активно меняется на MVP-стадии | modular monolith, package boundaries, DTO/service layers |
| Performance | Поиск по справочнику таксонов должен оставаться приемлемым при росте каталога | indexes, normalized names, pagination, fuzzy matching strategy |
| Operability | Сбой VPS не должен приводить к полной потере данных | backups, restore plan, migration path |
Режимы отказа¶
| Риск | Последствие | Митигирующая мера |
|---|---|---|
| ошибка tenant filtering | утечка данных | тесты, AccessControlService, scoped queries |
| падение VPS | недоступность сервиса | backup, restore plan, migration path |
| потеря MinIO volume | потеря фото/импортов | object storage backup |
| зависший import job | блокировка импорта | job lifecycle, stale detection |
| public DTO leakage | раскрытие внутренних данных | отдельные DTO, visibility rules |
Оценка масштаба и стоимости¶
Основные драйверы нагрузки¶
- количество организаций;
- количество экземпляров растений;
- количество фото на растение;
- размер Excel-импортов;
- частота Smart Import;
- объём публичного трафика на карты, QR-страницы и изображения;
- объём справочника таксонов.
Основные драйверы стоимости¶
- VPS / compute;
- PostgreSQL/PostGIS storage;
- object storage для фото и импортов;
- резервные копии;
- LLM calls для Smart Import;
- трафик публичных страниц и изображений;
- мониторинг и хранение логов.
Масштабные уровни¶
См. Дорожную карту