Files
Mini-ERP-app/core-service/.cursor/plan.md
Andrey Epifantsev 87595300b7 docs: обновлён план разработки - этап 2 завершён
- Отмечены все выполненные шаги этапа 2
- Добавлены результаты тестирования аутентификации
- Готово к переходу на этап 3
2025-08-27 15:06:12 +04:00

18 KiB
Raw Permalink Blame History

Детальный план разработки 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

Структура конфигурации:

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
  • Создать базовые таблицы

Структура БД (упрощённая):

-- 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: Базовые модели

  • Создать internal/models/ с основными структурами
  • Добавить валидацию через validator
  • Реализовать JSON теги для API

Основные модели:

// 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 аутентификация

  • Создать internal/auth/jwt.go
  • Реализовать генерацию и валидацию JWT токенов
  • Добавить organization-scope в токены
  • Создать middleware для проверки аутентификации

JWT структура:

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 - обновление токена (опционально)

Структура запросов:

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"`
}

Результаты тестирования:

  • Регистрация: POST /api/auth/register - 201 Created
  • Вход: POST /api/auth/login - 200 OK
  • Middleware: JWT токены проходят валидацию
  • JSON поля: исправлена конвертация в PostgreSQL

---

## 🏗️ Этап 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 базовых шаблона: Гараж, Мастерская, Склад
  • Добавить генерацию адресов мест

Шаблоны:

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 - размещение товара
  • Валидация доступности места
  • Обновление статуса места
  • Логирование операций

Структура запроса:

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 - поиск по названию, адресу, категории
  • Оптимизация запросов через индексы
  • Пагинация результатов

Параметры поиска:

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. Мониторинг - сбор метрик и обратной связи