# Детальный план разработки Core Service ## 🎯 Цель Реализация Core Service для ERP MVP с упрощённой архитектурой: Go + PostgreSQL + REST API без gRPC, Redis и Document Service. ## 📋 Общие принципы - **REST API** вместо gRPC - **PostgreSQL** как единственная БД (без Redis) - **JWT аутентификация** с organization-scope - **Структурированное логирование** (без Prometheus на MVP) - **Валидация данных** на всех уровнях --- ## 🚀 Этап 1: Фундамент (Недели 1-2) ### Шаг 1.1: Очистка и настройка проекта - [x] Удалить зависимости: `grpc`, `redis`, `prometheus` - [x] Обновить `go.mod` - оставить только необходимые пакеты - [x] Настроить структуру проекта согласно Go standards - [x] Добавить `.env` для конфигурации **Файлы для изменения:** ``` go.mod - удалить grpc, redis, prometheus ✅ cmd/main.go - убрать redis, grpc клиенты ✅ internal/config/config.go - упростить конфигурацию ✅ ``` ### Шаг 1.2: Базовая конфигурация - [x] Создать `internal/config/config.go` с упрощённой структурой - [x] Добавить поддержку `.env` файлов - [x] Настроить логирование через logrus - [x] Добавить health check endpoint **Структура конфигурации:** ```go type Config struct { Server ServerConfig Database DatabaseConfig JWT JWTConfig } type ServerConfig struct { Port string Host string } type DatabaseConfig struct { Host string Port string User string Password string DBName string SSLMode string } type JWTConfig struct { Secret string TTL time.Duration } ``` ### Шаг 1.3: Подключение к базе данных - [x] Создать `internal/database/connection.go` - [x] Настроить подключение к PostgreSQL - [x] Добавить миграции через `golang-migrate` - [x] Создать базовые таблицы **Структура БД (упрощённая):** ```sql -- organizations CREATE TABLE organizations ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name VARCHAR(255) NOT NULL, type VARCHAR(100), settings JSONB, created_at TIMESTAMP DEFAULT NOW() ); -- users CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), organization_id UUID REFERENCES organizations(id) ON DELETE CASCADE, email VARCHAR(255) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, role VARCHAR(50) DEFAULT 'user', created_at TIMESTAMP DEFAULT NOW() ); -- storage_locations CREATE TABLE storage_locations ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), organization_id UUID REFERENCES organizations(id) ON DELETE CASCADE, parent_id UUID REFERENCES storage_locations(id) ON DELETE CASCADE, name VARCHAR(255) NOT NULL, address VARCHAR(100) NOT NULL, type VARCHAR(50) NOT NULL, coordinates JSONB, created_at TIMESTAMP DEFAULT NOW() ); -- items CREATE TABLE items ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), organization_id UUID REFERENCES organizations(id) ON DELETE CASCADE, name VARCHAR(255) NOT NULL, description TEXT, category VARCHAR(100), created_at TIMESTAMP DEFAULT NOW() ); -- item_placements CREATE TABLE item_placements ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), organization_id UUID REFERENCES organizations(id) ON DELETE CASCADE, item_id UUID REFERENCES items(id) ON DELETE CASCADE, location_id UUID REFERENCES storage_locations(id) ON DELETE CASCADE, quantity INTEGER DEFAULT 1, created_at TIMESTAMP DEFAULT NOW() ); ``` ### Шаг 1.4: Базовые модели - [x] Создать `internal/models/` с основными структурами - [x] Добавить валидацию через `validator` - [x] Реализовать JSON теги для API **Основные модели:** ```go // internal/models/organization.go type Organization struct { ID uuid.UUID `json:"id" db:"id"` Name string `json:"name" validate:"required"` Type string `json:"type"` Settings JSON `json:"settings"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // internal/models/user.go type User struct { ID uuid.UUID `json:"id" db:"id"` OrganizationID uuid.UUID `json:"organization_id" db:"organization_id"` Email string `json:"email" validate:"required,email"` PasswordHash string `json:"-" db:"password_hash"` Role string `json:"role"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // internal/models/storage_location.go type StorageLocation struct { ID uuid.UUID `json:"id" db:"id"` OrganizationID uuid.UUID `json:"organization_id" db:"organization_id"` ParentID *uuid.UUID `json:"parent_id,omitempty" db:"parent_id"` Name string `json:"name" validate:"required"` Address string `json:"address" validate:"required"` Type string `json:"type" validate:"required"` Coordinates JSON `json:"coordinates"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // internal/models/item.go type Item struct { ID uuid.UUID `json:"id" db:"id"` OrganizationID uuid.UUID `json:"organization_id" db:"organization_id"` Name string `json:"name" validate:"required"` Description string `json:"description"` Category string `json:"category"` CreatedAt time.Time `json:"created_at" db:"created_at"` } // internal/models/item_placement.go type ItemPlacement struct { ID uuid.UUID `json:"id" db:"id"` OrganizationID uuid.UUID `json:"organization_id" db:"organization_id"` ItemID uuid.UUID `json:"item_id" db:"item_id"` LocationID uuid.UUID `json:"location_id" db:"location_id"` Quantity int `json:"quantity" validate:"min=1"` CreatedAt time.Time `json:"created_at" db:"created_at"` } ``` --- ## 🔐 Этап 2: Аутентификация (Неделя 2) ### Шаг 2.1: JWT аутентификация - [x] Создать `internal/auth/jwt.go` - [x] Реализовать генерацию и валидацию JWT токенов - [x] Добавить organization-scope в токены - [x] Создать middleware для проверки аутентификации **JWT структура:** ```go type Claims struct { UserID uuid.UUID `json:"user_id"` OrganizationID uuid.UUID `json:"organization_id"` Email string `json:"email"` Role string `json:"role"` jwt.RegisteredClaims } ``` ### Шаг 2.2: Хеширование паролей - [x] Создать `internal/auth/password.go` - [x] Использовать bcrypt для хеширования - [x] Добавить функции проверки паролей ### Шаг 2.3: API endpoints для аутентификации - [x] `POST /api/auth/register` - регистрация организации и пользователя - [x] `POST /api/auth/login` - вход в систему - [x] `POST /api/auth/refresh` - обновление токена (опционально) **Структура запросов:** ```go type RegisterRequest struct { OrganizationName string `json:"organization_name" validate:"required"` UserEmail string `json:"user_email" validate:"required,email"` UserPassword string `json:"user_password" validate:"required,min=8"` OrganizationType string `json:"organization_type"` } type LoginRequest struct { Email string `json:"email" validate:"required,email"` Password string `json:"password" validate:"required"` } ``` --- ## 🏗️ Этап 3: API структура (Неделя 3) ### Шаг 3.1: Базовые handlers - [ ] Создать `internal/api/handlers/` с базовыми структурами - [ ] Реализовать middleware для CORS, логирования, аутентификации - [ ] Добавить обработку ошибок **Структура handlers:** ``` internal/api/ ├── handlers/ │ ├── auth.go │ ├── organizations.go │ ├── locations.go │ ├── items.go │ └── operations.go ├── middleware/ │ ├── auth.go │ ├── cors.go │ ├── logging.go │ └── error_handler.go └── server.go ``` ### Шаг 3.2: Repository pattern - [ ] Создать `internal/repository/` для работы с БД - [ ] Реализовать CRUD операции для всех сущностей - [ ] Добавить organization-scope фильтрацию **Основные репозитории:** ```go // internal/repository/organizations.go type OrganizationRepository interface { Create(ctx context.Context, org *models.Organization) error GetByID(ctx context.Context, id uuid.UUID) (*models.Organization, error) Update(ctx context.Context, org *models.Organization) error } // internal/repository/users.go type UserRepository interface { Create(ctx context.Context, user *models.User, password string) error GetByEmail(ctx context.Context, email string) (*models.User, error) GetByID(ctx context.Context, id uuid.UUID) (*models.User, error) } // internal/repository/locations.go type LocationRepository interface { Create(ctx context.Context, location *models.StorageLocation) error GetByID(ctx context.Context, id uuid.UUID, orgID uuid.UUID) (*models.StorageLocation, error) GetByOrganization(ctx context.Context, orgID uuid.UUID) ([]*models.StorageLocation, error) Update(ctx context.Context, location *models.StorageLocation) error Delete(ctx context.Context, id uuid.UUID, orgID uuid.UUID) error } // internal/repository/items.go type ItemRepository interface { Create(ctx context.Context, item *models.Item) error GetByID(ctx context.Context, id uuid.UUID, orgID uuid.UUID) (*models.Item, error) GetByOrganization(ctx context.Context, orgID uuid.UUID) ([]*models.Item, error) Search(ctx context.Context, orgID uuid.UUID, query string) ([]*models.Item, error) Update(ctx context.Context, item *models.Item) error Delete(ctx context.Context, id uuid.UUID, orgID uuid.UUID) error } ``` ### Шаг 3.3: Service layer - [ ] Создать `internal/service/` для бизнес-логики - [ ] Реализовать валидацию и обработку данных - [ ] Добавить транзакции для сложных операций --- ## 📍 Этап 4: Шаблоны помещений (Неделя 4) ### Шаг 4.1: Система шаблонов - [ ] Создать `internal/templates/` для шаблонов помещений - [ ] Реализовать 3 базовых шаблона: Гараж, Мастерская, Склад - [ ] Добавить генерацию адресов мест **Шаблоны:** ```go type Template struct { ID string `json:"id"` Name string `json:"name"` Description string `json:"description"` Zones []Zone `json:"zones"` AddressRules []AddressRule `json:"address_rules"` } type Zone struct { Name string `json:"name"` Type string `json:"type"` // "cabinet", "floor", "table" Rows int `json:"rows"` Columns int `json:"columns"` Position Position `json:"position"` } type AddressRule struct { ZoneType string `json:"zone_type"` Prefix string `json:"prefix"` Format string `json:"format"` // "Ш{zone}-П{row}-Я{col}" } ``` ### Шаг 4.2: API для шаблонов - [ ] `GET /api/templates` - список доступных шаблонов - [ ] `POST /api/templates/:id/apply` - применение шаблона к организации - [ ] Генерация мест хранения из шаблона ### Шаг 4.3: Адресация мест - [ ] Реализовать систему адресации (Ш1-П2-Я3, З1-У2) - [ ] Автоматическая генерация адресов при создании мест - [ ] Валидация уникальности адресов в рамках организации --- ## 🔍 Этап 5: Операции (Неделя 5) ### Шаг 5.1: Размещение товаров - [ ] `POST /api/operations/place-item` - размещение товара - [ ] Валидация доступности места - [ ] Обновление статуса места - [ ] Логирование операций **Структура запроса:** ```go type PlaceItemRequest struct { ItemID uuid.UUID `json:"item_id" validate:"required"` LocationID uuid.UUID `json:"location_id" validate:"required"` Quantity int `json:"quantity" validate:"required,min=1"` } ``` ### Шаг 5.2: Поиск товаров - [ ] `GET /api/operations/search` - поиск по названию, адресу, категории - [ ] Оптимизация запросов через индексы - [ ] Пагинация результатов **Параметры поиска:** ```go type SearchRequest struct { Query string `form:"q"` Category string `form:"category"` Address string `form:"address"` Page int `form:"page,default=1"` PageSize int `form:"page_size,default=20"` } ``` ### Шаг 5.3: Перемещение товаров - [ ] `POST /api/operations/move-item` - перемещение между местами - [ ] Валидация наличия товара в исходном месте - [ ] Атомарность операции через транзакции --- ## 🧪 Этап 6: Тестирование и полировка (Неделя 6) ### Шаг 6.1: Unit тесты - [ ] Тесты для всех репозиториев - [ ] Тесты для сервисов - [ ] Тесты для handlers - [ ] Покрытие кода > 80% ### Шаг 6.2: Интеграционные тесты - [ ] Тесты API endpoints - [ ] Тесты с реальной БД - [ ] Тесты аутентификации ### Шаг 6.3: Производительность - [ ] Оптимизация SQL запросов - [ ] Добавление индексов - [ ] Тестирование под нагрузкой - [ ] Мониторинг медленных запросов ### Шаг 6.4: Документация API - [ ] Swagger/OpenAPI документация - [ ] Примеры запросов и ответов - [ ] Описание ошибок --- ## 📊 Критерии готовности ### Функциональные критерии - [ ] Все CRUD операции работают для items, locations - [ ] Аутентификация с organization-scope - [ ] Шаблоны помещений генерируют структуру - [ ] Операции размещения/поиска/перемещения работают - [ ] API отвечает на все запросы ### Технические критерии - [ ] Время отклика API ≤ 200мс - [ ] Покрытие тестами > 80% - [ ] Все endpoints документированы - [ ] Логирование работает корректно - [ ] Graceful shutdown реализован ### Безопасность - [ ] JWT токены валидируются - [ ] Organization-scope на всех данных - [ ] Валидация входных данных - [ ] SQL injection protection --- ## 🚨 Риски и митигация ### Высокий риск - **Производительность БД при большом количестве данных** - Митигация: Индексы, пагинация, мониторинг запросов ### Средний риск - **JWT токены без refresh механизма** - Митигация: Короткий TTL, возможность перелогина ### Низкий риск - **Отсутствие кэширования** - Митигация: Оптимизация SQL, индексы --- ## 📁 Структура проекта (финальная) ``` app/core-service/ ├── cmd/ │ └── main.go ├── internal/ │ ├── api/ │ │ ├── handlers/ │ │ ├── middleware/ │ │ └── server.go │ ├── auth/ │ │ ├── jwt.go │ │ └── password.go │ ├── config/ │ │ └── config.go │ ├── database/ │ │ ├── connection.go │ │ └── migrations/ │ ├── models/ │ │ ├── organization.go │ │ ├── user.go │ │ ├── storage_location.go │ │ ├── item.go │ │ └── item_placement.go │ ├── repository/ │ │ ├── organizations.go │ │ ├── users.go │ │ ├── locations.go │ │ └── items.go │ ├── service/ │ │ ├── auth_service.go │ │ ├── location_service.go │ │ └── item_service.go │ └── templates/ │ ├── templates.go │ └── address_rules.go ├── migrations/ ├── tests/ ├── go.mod ├── go.sum ├── Dockerfile └── .env.example ``` --- ## 🎯 Следующие шаги после Core Service 1. **Frontend разработка** - Angular PWA с QR сканером 2. **Интеграция** - тестирование API с фронтендом 3. **Пилот** - тестирование с реальными пользователями 4. **Мониторинг** - сбор метрик и обратной связи