Добавлен план реализации Core сервиса

This commit is contained in:
2025-08-27 14:34:14 +04:00
parent 771d8ef99e
commit 725d4c4474

View File

@@ -0,0 +1,504 @@
# Детальный план разработки 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: Очистка и настройка проекта
- [ ] Удалить зависимости: `grpc`, `redis`, `prometheus`
- [ ] Обновить `go.mod` - оставить только необходимые пакеты
- [ ] Настроить структуру проекта согласно Go standards
- [ ] Добавить `.env` для конфигурации
**Файлы для изменения:**
```
go.mod - удалить grpc, redis, prometheus
cmd/main.go - убрать redis, grpc клиенты
internal/config/config.go - упростить конфигурацию
```
### Шаг 1.2: Базовая конфигурация
- [ ] Создать `internal/config/config.go` с упрощённой структурой
- [ ] Добавить поддержку `.env` файлов
- [ ] Настроить логирование через logrus
- [ ] Добавить 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: Подключение к базе данных
- [ ] Создать `internal/database/connection.go`
- [ ] Настроить подключение к PostgreSQL
- [ ] Добавить миграции через `golang-migrate`
- [ ] Создать базовые таблицы
**Структура БД (упрощённая):**
```sql
-- organizations
CREATE TABLE organizations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
type VARCHAR(100),
settings JSONB,
created_at TIMESTAMP DEFAULT NOW()
);
-- users
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID REFERENCES organizations(id),
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 gen_random_uuid(),
organization_id UUID REFERENCES organizations(id),
parent_id UUID REFERENCES storage_locations(id),
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 gen_random_uuid(),
organization_id UUID REFERENCES organizations(id),
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 gen_random_uuid(),
organization_id UUID REFERENCES organizations(id),
item_id UUID REFERENCES items(id),
location_id UUID REFERENCES storage_locations(id),
quantity INTEGER DEFAULT 1,
created_at TIMESTAMP DEFAULT NOW()
);
```
### Шаг 1.4: Базовые модели
- [ ] Создать `internal/models/` с основными структурами
- [ ] Добавить валидацию через `validator`
- [ ] Реализовать 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"`
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 аутентификация
- [ ] Создать `internal/auth/jwt.go`
- [ ] Реализовать генерацию и валидацию JWT токенов
- [ ] Добавить organization-scope в токены
- [ ] Создать 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: Хеширование паролей
- [ ] Создать `internal/auth/password.go`
- [ ] Использовать bcrypt для хеширования
- [ ] Добавить функции проверки паролей
### Шаг 2.3: API endpoints для аутентификации
- [ ] `POST /api/auth/register` - регистрация организации и пользователя
- [ ] `POST /api/auth/login` - вход в систему
- [ ] `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. **Мониторинг** - сбор метрик и обратной связи