Добавлен план реализации Core сервиса
This commit is contained in:
504
core-service/.cursor/plan.md
Normal file
504
core-service/.cursor/plan.md
Normal 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. **Мониторинг** - сбор метрик и обратной связи
|
||||
Reference in New Issue
Block a user