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

520 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Детальный план разработки 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"`
}
```
**Результаты тестирования:**
- ✅ Регистрация: `POST /api/auth/register` - 201 Created
- ✅ Вход: `POST /api/auth/login` - 200 OK
- ✅ Middleware: JWT токены проходят валидацию
- ✅ JSON поля: исправлена конвертация в PostgreSQL
```
---
## 🏗️ Этап 3: API структура (Неделя 3) ✅ ЗАВЕРШЁН
### Шаг 3.1: Repository pattern ✅
- [x] Создать `internal/repository/` для работы с БД
- [x] Реализовать CRUD операции для всех сущностей
- [x] Добавить organization-scope фильтрацию
**Структура 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: Service layer ✅
- [x] Создать `internal/service/` с бизнес-логикой
- [x] Реализовать сервисы для всех сущностей
- [x] Добавить валидацию и логирование
**Основные сервисы:**
```go
// internal/service/location_service.go
type LocationService interface {
CreateLocation(ctx context.Context, orgID uuid.UUID, req *models.CreateLocationRequest) (*models.StorageLocation, error)
GetLocation(ctx context.Context, id uuid.UUID, orgID uuid.UUID) (*models.StorageLocation, error)
GetLocations(ctx context.Context, orgID uuid.UUID) ([]*models.StorageLocation, error)
UpdateLocation(ctx context.Context, id uuid.UUID, orgID uuid.UUID, req *models.CreateLocationRequest) (*models.StorageLocation, error)
DeleteLocation(ctx context.Context, id uuid.UUID, orgID uuid.UUID) error
}
// internal/service/item_service.go
type ItemService interface {
CreateItem(ctx context.Context, orgID uuid.UUID, req *models.CreateItemRequest) (*models.Item, error)
GetItem(ctx context.Context, id uuid.UUID, orgID uuid.UUID) (*models.Item, error)
GetItems(ctx context.Context, orgID uuid.UUID) ([]*models.Item, error)
UpdateItem(ctx context.Context, id uuid.UUID, orgID uuid.UUID, req *models.CreateItemRequest) (*models.Item, error)
DeleteItem(ctx context.Context, id uuid.UUID, orgID uuid.UUID) error
SearchItems(ctx context.Context, orgID uuid.UUID, query string, category string) ([]*models.Item, error)
}
// internal/service/operations_service.go
type OperationsService interface {
PlaceItem(ctx context.Context, orgID uuid.UUID, req *models.PlaceItemRequest) (*models.ItemPlacement, error)
MoveItem(ctx context.Context, placementID uuid.UUID, newLocationID uuid.UUID, orgID uuid.UUID) error
GetItemPlacements(ctx context.Context, itemID uuid.UUID, orgID uuid.UUID) ([]*models.ItemPlacement, error)
GetLocationPlacements(ctx context.Context, locationID uuid.UUID, orgID uuid.UUID) ([]*models.ItemPlacement, error)
Search(ctx context.Context, orgID uuid.UUID, req *models.SearchRequest) (*models.SearchResponse, error)
}
```
### Шаг 3.3: HTTP Handlers ✅
- [x] Создать `internal/api/handlers/` с базовыми структурами
- [x] Реализовать handlers для всех API endpoints
- [x] Добавить валидацию запросов
**Результаты этапа 3:**
- ✅ Созданы репозитории для locations, items, operations
- ✅ Реализованы сервисы с бизнес-логикой
- ✅ Созданы HTTP handlers для всех API endpoints
- ✅ Добавлена функция GetClaims в middleware
- ✅ Organization-scope фильтрация во всех операциях
- ✅ Поддержка JSON полей в PostgreSQL
- ✅ Валидация всех входящих запросов
```
---
## 📍 Этап 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. **Мониторинг** - сбор метрик и обратной связи