Files
filam3d/CLAUDE.md
2026-03-22 12:40:33 +03:00

29 KiB
Raw Permalink Blame History

3D Print Calculator — Техническое задание на MVP

Обзор проекта

Сервис 3D-печати на заказ с автоматическим расчётом стоимости. Клиент загружает 3D-модель (STL, 3MF, OBJ), выбирает материал, получает мгновенный расчёт цены и оформляет заказ. AI-ассистент помогает выбрать оптимальный материал под задачу.

Бизнес-модель: B2B — корпуса для электроники, функциональные запчасти, прототипы.

Стек: Python (FastAPI) + Vue 3 (Vite) + PostgreSQL + Nginx + Docker Compose.

Хостинг: VPS (Ubuntu 24), деплой через Docker Compose.


Архитектура

┌─────────────────────────────────────────────┐
│              Nginx (reverse proxy)           │
│         :80 → frontend, /api → backend      │
└──────────┬──────────────┬───────────────────┘
           │              │
┌──────────▼──────┐ ┌─────▼──────────────────┐
│   Vue 3 (Vite)  │ │   FastAPI (Python)      │
│   SPA, static   │ │   REST API + WebSocket  │
│   Port: 5173    │ │   Port: 8000            │
└─────────────────┘ └──────┬─────┬────────────┘
                           │     │
                    ┌──────▼┐ ┌──▼───────────┐
                    │ PostgreSQL │ File Storage │
                    │  :5432    │ (local /uploads) │
                    └───────┘ └──────────────┘

Контейнеры Docker Compose

  1. frontend — Node 20 + Vite dev server (в проде — собранная статика через Nginx)
  2. backend — Python 3.12 + FastAPI + Uvicorn
  3. db — PostgreSQL 16
  4. nginx — reverse proxy, SSL termination

Backend (FastAPI)

Структура проекта

backend/
├── app/
│   ├── main.py              # FastAPI app, CORS, middleware
│   ├── config.py            # Settings (pydantic-settings)
│   ├── database.py          # SQLAlchemy async engine + session
│   ├── models/
│   │   ├── material.py      # Material ORM model
│   │   ├── order.py         # Order ORM model
│   │   └── file_upload.py   # Uploaded file metadata
│   ├── schemas/
│   │   ├── calculate.py     # Request/Response для калькулятора
│   │   ├── material.py      # Material schemas
│   │   └── order.py         # Order schemas
│   ├── routers/
│   │   ├── calculate.py     # POST /api/calculate
│   │   ├── materials.py     # GET /api/materials
│   │   ├── orders.py        # POST /api/orders
│   │   └── ai_advisor.py    # POST /api/advisor
│   ├── services/
│   │   ├── file_parser.py   # Парсинг STL/3MF/OBJ → геометрия
│   │   ├── price_engine.py  # Расчёт стоимости
│   │   ├── ai_advisor.py    # Интеграция с Claude API
│   │   └── telegram_notify.py # Уведомления в Telegram
│   └── seed/
│       └── materials.py     # Начальные данные по материалам
├── requirements.txt
├── Dockerfile
└── alembic/                 # Миграции БД

API Endpoints

1. POST /api/calculate

Основной endpoint калькулятора. Принимает файл и параметры, возвращает расчёт.

Request: multipart/form-data

  • file (File, required) — 3D-модель (STL, 3MF, OBJ). Макс. размер: 50MB.
  • material_id (int, required) — ID выбранного материала
  • infill_percent (int, optional, default=30) — Процент заполнения (10-100)
  • layer_height_mm (float, optional, default=0.2) — Высота слоя (0.08-0.4)
  • quantity (int, optional, default=1) — Количество экземпляров (1-500)
  • post_processing (str[], optional) — Постобработка: ["sanding", "painting", "threading"]

Response: application/json

{
  "success": true,
  "file_info": {
    "filename": "case_v3.stl",
    "format": "stl",
    "volume_cm3": 42.7,
    "surface_area_cm2": 198.3,
    "bounding_box_mm": {"x": 120.0, "y": 80.0, "z": 35.0},
    "is_watertight": true,
    "triangle_count": 12840
  },
  "calculation": {
    "material": {
      "id": 3,
      "name": "PA (Nylon)",
      "density_g_cm3": 1.14,
      "price_per_gram": 50.0
    },
    "weight_grams": 48.7,
    "material_cost_rub": 2435.0,
    "print_time_hours": 4.2,
    "time_cost_rub": 840.0,
    "post_processing_cost_rub": 500.0,
    "subtotal_rub": 3775.0,
    "quantity": 2,
    "quantity_discount_percent": 5,
    "total_rub": 7172.5,
    "estimated_days": 3
  }
}

Ошибки:

  • 400 — Неподдерживаемый формат файла
  • 400 — Файл повреждён или не является 3D-моделью
  • 400 — Модель не является водонепроницаемой (warning, не blocking)
  • 413 — Файл слишком большой (>50MB)

2. GET /api/materials

Список доступных материалов с характеристиками.

Response:

[
  {
    "id": 1,
    "name": "PLA",
    "category": "basic",
    "price_per_gram": 25.0,
    "density_g_cm3": 1.24,
    "properties": {
      "max_temp_c": 60,
      "strength": "medium",
      "flexibility": "low",
      "chemical_resistance": "low",
      "food_safe": true
    },
    "description": "Базовый пластик, подходит для прототипов и декоративных изделий",
    "color_options": ["white", "black", "gray", "red", "blue", "green", "natural"]
  }
]

3. POST /api/advisor

AI-ассистент для выбора материала. Принимает описание задачи, возвращает рекомендацию.

Request:

{
  "task_description": "Нужен корпус для уличного датчика температуры. Будет стоять на улице, диапазон температур от -30 до +50. Должен быть водонепроницаемым.",
  "budget_preference": "optimal",
  "file_info": {
    "volume_cm3": 42.7,
    "bounding_box_mm": {"x": 120, "y": 80, "z": 35}
  }
}

Response:

{
  "recommended_material_id": 4,
  "recommended_material_name": "PETG",
  "reasoning": "Для уличного корпуса датчика рекомендую PETG: термостойкость до +80°C, морозостойкость до -40°C, хорошая UV-стойкость и водонепроницаемость. ABS тоже подошёл бы, но PETG проще в печати и не требует закрытой камеры.",
  "alternatives": [
    {
      "material_id": 2,
      "name": "ABS",
      "why": "Выше ударопрочность, но требует постобработки для герметичности"
    },
    {
      "material_id": 5,
      "name": "ASA",
      "why": "Лучшая UV-стойкость, но дороже"
    }
  ]
}

Реализация: Отправляем запрос к Claude API (модель claude-sonnet-4-20250514) с системным промптом, содержащим каталог материалов и их свойства. Ключ API хранится в переменной окружения ANTHROPIC_API_KEY.

4. POST /api/orders

Оформление заказа.

Request:

{
  "calculation_id": "uuid-of-saved-calculation",
  "client_name": "Иван Петров",
  "client_phone": "+79001234567",
  "client_email": "ivan@example.com",
  "client_company": "ООО Технопарк",
  "delivery_method": "pickup",
  "comment": "Нужно к пятнице, нанести резьбу M4 в двух отверстиях"
}

Response:

{
  "order_id": "ORD-2026-0042",
  "status": "pending",
  "total_rub": 7172.5,
  "estimated_ready_date": "2026-04-02"
}

Side effect: Отправляет уведомление в Telegram-бот владельца с деталями заказа.

Сервис парсинга файлов (file_parser.py)

Используемые библиотеки:

  • trimesh — основной парсер. Читает STL (binary + ASCII), OBJ, 3MF, PLY, GLTF.
  • numpy-stl — запасной вариант для STL, если trimesh не справился.

Что извлекаем из файла:

import trimesh

def parse_3d_file(file_path: str, file_extension: str) -> FileInfo:
    """
    Парсит 3D-файл и возвращает геометрические характеристики.
    
    Поддерживаемые форматы: .stl, .3mf, .obj
    STEP-файлы (.step, .stp) — в v2 (требует cadquery/OCP).
    """
    mesh = trimesh.load(file_path, file_type=file_extension)
    
    # Если 3MF содержит несколько тел — объединяем
    if isinstance(mesh, trimesh.Scene):
        mesh = trimesh.util.concatenate(mesh.dump())
    
    return FileInfo(
        volume_cm3=mesh.volume / 1000,        # mm³ → cm³
        surface_area_cm2=mesh.area / 100,      # mm² → cm²
        bounding_box_mm={
            "x": mesh.bounding_box.extents[0],
            "y": mesh.bounding_box.extents[1],
            "z": mesh.bounding_box.extents[2],
        },
        is_watertight=mesh.is_watertight,
        triangle_count=len(mesh.faces),
    )

Сервис расчёта цены (price_engine.py)

def calculate_price(
    file_info: FileInfo,
    material: Material,
    infill_percent: int = 30,
    layer_height_mm: float = 0.2,
    quantity: int = 1,
    post_processing: list[str] = [],
) -> Calculation:
    """
    Формула:
    1. effective_volume = volume_cm3 * (infill_percent / 100) * 0.7 + volume_cm3 * 0.3
       (70% объёма масштабируется по infill, 30% — стенки, всегда 100%)
    2. weight_g = effective_volume * material.density_g_cm3
    3. material_cost = weight_g * material.price_per_gram
    4. print_time_h = estimate_print_time(file_info, layer_height_mm, material)
    5. time_cost = print_time_h * TIME_RATE_PER_HOUR  # ~200 руб/час
    6. post_processing_cost = sum стоимостей выбранных операций
    7. subtotal = material_cost + time_cost + post_processing_cost
    8. total = subtotal * quantity * (1 - volume_discount(quantity))
    """

Оценка времени печати (упрощённая):

def estimate_print_time(file_info, layer_height_mm, material):
    """
    Упрощённая оценка без полного слайсинга.
    
    layers = bounding_box_z / layer_height
    volume_per_layer = volume_cm3 / layers * 1000  # mm³
    time_per_layer = volume_per_layer / material.flow_rate_mm3_s / 60  # минуты
    travel_time_per_layer ≈ 0.3 мин (константа для Bambu Lab)
    total = layers * (time_per_layer + travel_time) + setup_time
    """

Скидки за количество:

  • 1 шт — 0%
  • 2-5 шт — 5%
  • 6-20 шт — 10%
  • 21-100 шт — 15%
  • 101-500 шт — 20%

Стоимость постобработки:

  • sanding (шлифовка) — 300 руб/шт
  • painting (покраска) — 500 руб/шт
  • threading (нарезка резьбы) — 200 руб/отверстие
  • acetone_smoothing (ацетоновая обработка, только ABS) — 400 руб/шт

Справочник материалов (seed data)

MATERIALS = [
    {
        "name": "PLA",
        "category": "basic",
        "density_g_cm3": 1.24,
        "price_per_gram": 25.0,
        "flow_rate_mm3_s": 15.0,
        "max_temp_c": 60,
        "min_temp_c": -20,
        "strength": "medium",
        "flexibility": "low",
        "chemical_resistance": "low",
        "uv_resistance": "low",
        "food_safe": True,
        "description": "Базовый пластик. Лёгкий в печати, хорошая детализация. Для прототипов и декора.",
    },
    {
        "name": "PETG",
        "category": "basic",
        "density_g_cm3": 1.27,
        "price_per_gram": 28.0,
        "flow_rate_mm3_s": 12.0,
        "max_temp_c": 80,
        "min_temp_c": -40,
        "strength": "high",
        "flexibility": "medium",
        "chemical_resistance": "medium",
        "uv_resistance": "medium",
        "food_safe": True,
        "description": "Универсальный инженерный пластик. Прочный, химстойкий, подходит для улицы.",
    },
    {
        "name": "ABS",
        "category": "basic",
        "density_g_cm3": 1.04,
        "price_per_gram": 25.0,
        "flow_rate_mm3_s": 12.0,
        "max_temp_c": 100,
        "min_temp_c": -30,
        "strength": "high",
        "flexibility": "low",
        "chemical_resistance": "medium",
        "uv_resistance": "low",
        "food_safe": False,
        "description": "Термостойкий, ударопрочный. Требует закрытой камеры. Обрабатывается ацетоном.",
    },
    {
        "name": "PA (Nylon)",
        "category": "engineering",
        "density_g_cm3": 1.14,
        "price_per_gram": 50.0,
        "flow_rate_mm3_s": 10.0,
        "max_temp_c": 120,
        "min_temp_c": -40,
        "strength": "very_high",
        "flexibility": "medium",
        "chemical_resistance": "high",
        "uv_resistance": "medium",
        "food_safe": False,
        "description": "Инженерный пластик. Высокая прочность, износостойкость. Для шестерён, креплений.",
    },
    {
        "name": "PC (Поликарбонат)",
        "category": "engineering",
        "density_g_cm3": 1.20,
        "price_per_gram": 60.0,
        "flow_rate_mm3_s": 8.0,
        "max_temp_c": 140,
        "min_temp_c": -40,
        "strength": "very_high",
        "flexibility": "low",
        "chemical_resistance": "high",
        "uv_resistance": "high",
        "food_safe": False,
        "description": "Максимальная термостойкость и прочность. Для корпусов, работающих при высоких температурах.",
    },
    {
        "name": "TPU",
        "category": "engineering",
        "density_g_cm3": 1.21,
        "price_per_gram": 40.0,
        "flow_rate_mm3_s": 6.0,
        "max_temp_c": 80,
        "min_temp_c": -30,
        "strength": "medium",
        "flexibility": "very_high",
        "chemical_resistance": "high",
        "uv_resistance": "medium",
        "food_safe": False,
        "description": "Эластичный пластик, аналог резины. Для прокладок, амортизаторов, гибких деталей.",
    },
    {
        "name": "PA-CF (Нейлон + углеволокно)",
        "category": "composite",
        "density_g_cm3": 1.18,
        "price_per_gram": 75.0,
        "flow_rate_mm3_s": 8.0,
        "max_temp_c": 150,
        "min_temp_c": -40,
        "strength": "extreme",
        "flexibility": "low",
        "chemical_resistance": "very_high",
        "uv_resistance": "high",
        "food_safe": False,
        "description": "Композит с углеволокном. Максимальная жёсткость и прочность. Замена алюминия.",
    },
]

AI Advisor — системный промпт

Файл app/services/ai_advisor.py использует Anthropic Python SDK:

SYSTEM_PROMPT = """
Ты — эксперт по 3D-печати из инженерных пластиков по технологии FDM.
Твоя задача — рекомендовать оптимальный материал для печати на основе описания задачи клиента.

Доступные материалы:
{materials_json}

Правила:
1. Всегда рекомендуй один основной материал и 1-2 альтернативы.
2. Учитывай: температурный режим, механические нагрузки, химическое воздействие, UV, влажность.
3. Если задача не подходит для FDM-печати (слишком мелкие детали, высокая точность) — честно скажи об этом.
4. Отвечай кратко, по делу, на русском языке.
5. Если клиент не указал критичные параметры — задай уточняющие вопросы.

Формат ответа — строго JSON:
{
  "recommended_material_id": <int>,
  "reasoning": "<обоснование на русском>",
  "alternatives": [{"material_id": <int>, "name": "<str>", "why": "<причина>"}],
  "questions": ["<вопрос, если нужна доп. информация>"]  // пустой массив если вопросов нет
}
"""

Telegram-уведомления (telegram_notify.py)

При создании заказа отправляем сообщение в Telegram-бот владельца:

import httpx

async def notify_new_order(order: Order):
    """Отправляет уведомление о новом заказе в Telegram."""
    text = (
        f"🆕 Новый заказ #{order.order_id}\n"
        f"Клиент: {order.client_name}\n"
        f"Телефон: {order.client_phone}\n"
        f"Материал: {order.material_name}\n"
        f"Сумма: {order.total_rub}\n"
        f"Комментарий: {order.comment or '—'}"
    )
    await httpx.AsyncClient().post(
        f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage",
        json={"chat_id": TELEGRAM_CHAT_ID, "text": text}
    )

Переменные окружения: TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID.


Frontend (Vue 3 + Vite)

Структура проекта

frontend/
├── src/
│   ├── App.vue
│   ├── main.js
│   ├── router/
│   │   └── index.js          # Vue Router
│   ├── stores/
│   │   ├── calculator.js     # Pinia store — состояние калькулятора
│   │   └── materials.js      # Pinia store — материалы
│   ├── api/
│   │   └── client.js         # Axios instance, базовые запросы
│   ├── views/
│   │   ├── CalculatorView.vue # Главная страница с калькулятором
│   │   ├── MaterialsView.vue  # Каталог материалов
│   │   └── OrderView.vue      # Форма заказа
│   ├── components/
│   │   ├── FileUploader.vue   # Drag-and-drop загрузка файла
│   │   ├── MaterialPicker.vue # Выбор материала (карточки)
│   │   ├── PrintSettings.vue  # Настройки печати (infill, layer)
│   │   ├── PriceResult.vue    # Отображение расчёта
│   │   ├── AiAdvisor.vue      # Чат с AI-ассистентом
│   │   └── OrderForm.vue      # Форма заказа
│   └── assets/
│       └── styles/
│           └── main.css       # Tailwind CSS
├── index.html
├── vite.config.js
├── tailwind.config.js
├── package.json
└── Dockerfile

Зависимости

{
  "dependencies": {
    "vue": "^3.5",
    "vue-router": "^4.4",
    "pinia": "^2.2",
    "axios": "^1.7"
  },
  "devDependencies": {
    "vite": "^6.0",
    "@vitejs/plugin-vue": "^5.1",
    "tailwindcss": "^3.4",
    "autoprefixer": "^10.4",
    "postcss": "^8.4"
  }
}

Страницы и маршруты

Путь Компонент Описание
/ CalculatorView Главная: загрузка файла → материал → настройки → цена
/materials MaterialsView Каталог материалов с фильтрами
/order/:calcId OrderView Форма оформления заказа

Компонент FileUploader.vue

Требования:

  • Drag-and-drop зона + кнопка «Выбрать файл»
  • Принимает: .stl, .3mf, .obj (валидация по расширению на фронте)
  • Максимальный размер: 50 MB
  • Отображает имя файла, размер и иконку формата после загрузки
  • Показывает прогресс-бар при отправке на сервер
  • При ошибке парсинга — человекочитаемое сообщение

Компонент MaterialPicker.vue

Требования:

  • Отображает материалы карточками (не dropdown)
  • Карточка содержит: название, цену за грамм, ключевые свойства (иконки)
  • Разделение на категории: «Базовые», «Инженерные», «Композитные»
  • Выбранный материал подсвечивается
  • Кнопка «Помочь выбрать» открывает AI-ассистент

Компонент PrintSettings.vue

Требования:

  • Слайдер: заполнение (10%-100%, шаг 10%, default 30%)
  • Слайдер: высота слоя (0.08-0.4mm, шаг 0.04, default 0.2)
  • Поле: количество (1-500, default 1)
  • Чекбоксы: постобработка (шлифовка, покраска, резьба, ацетон)
  • Подсказки при наведении: как параметр влияет на результат

Компонент PriceResult.vue

Требования:

  • Показывает разбивку: материал, время, постобработка, скидка, итого
  • Крупно отображает итоговую цену
  • Показывает примерный срок изготовления
  • Кнопка «Оформить заказ» → переход на /order/:calcId
  • Кнопка «Скачать расчёт (PDF)» — v2, пока не реализуем

Компонент AiAdvisor.vue

Требования:

  • Модальное окно или выдвижная панель справа
  • Текстовое поле для описания задачи
  • Кнопка «Получить рекомендацию»
  • Отображает рекомендацию: основной материал + альтернативы с обоснованием
  • Кнопка «Выбрать» рядом с каждой рекомендацией — применяет материал в калькулятор

Дизайн

  • Стиль: минималистичный, светлая тема, Tailwind CSS
  • Акцентный цвет: #2563EB (синий) — кнопки, выделения
  • Шрифт: Inter (Google Fonts)
  • Адаптивность: mobile-first, работает на телефоне
  • Тёмная тема: v2 (не в MVP)

База данных (PostgreSQL)

Таблицы

materials

CREATE TABLE materials (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    category VARCHAR(50) NOT NULL,        -- basic, engineering, composite
    density_g_cm3 FLOAT NOT NULL,
    price_per_gram FLOAT NOT NULL,
    flow_rate_mm3_s FLOAT NOT NULL,
    max_temp_c INT,
    min_temp_c INT,
    strength VARCHAR(20),                 -- low, medium, high, very_high, extreme
    flexibility VARCHAR(20),
    chemical_resistance VARCHAR(20),
    uv_resistance VARCHAR(20),
    food_safe BOOLEAN DEFAULT FALSE,
    description TEXT,
    color_options JSONB DEFAULT '[]',
    is_active BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT NOW()
);

calculations

CREATE TABLE calculations (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    file_name VARCHAR(255) NOT NULL,
    file_format VARCHAR(10) NOT NULL,
    file_path VARCHAR(500),
    volume_cm3 FLOAT NOT NULL,
    surface_area_cm2 FLOAT,
    bounding_box JSONB,
    is_watertight BOOLEAN,
    triangle_count INT,
    material_id INT REFERENCES materials(id),
    infill_percent INT DEFAULT 30,
    layer_height_mm FLOAT DEFAULT 0.2,
    quantity INT DEFAULT 1,
    post_processing JSONB DEFAULT '[]',
    weight_grams FLOAT,
    material_cost_rub FLOAT,
    time_cost_rub FLOAT,
    post_processing_cost_rub FLOAT,
    total_rub FLOAT,
    estimated_days INT,
    created_at TIMESTAMP DEFAULT NOW()
);

orders

CREATE TABLE orders (
    id SERIAL PRIMARY KEY,
    order_id VARCHAR(20) UNIQUE NOT NULL,  -- ORD-2026-0001
    calculation_id UUID REFERENCES calculations(id),
    client_name VARCHAR(200) NOT NULL,
    client_phone VARCHAR(20) NOT NULL,
    client_email VARCHAR(200),
    client_company VARCHAR(200),
    delivery_method VARCHAR(50) DEFAULT 'pickup', -- pickup, delivery
    comment TEXT,
    status VARCHAR(30) DEFAULT 'pending',  -- pending, confirmed, printing, ready, delivered, cancelled
    total_rub FLOAT NOT NULL,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

Docker Compose

version: "3.8"

services:
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: print3d
      POSTGRES_USER: print3d
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql/data
    ports:
      - "5432:5432"

  backend:
    build: ./backend
    environment:
      DATABASE_URL: postgresql+asyncpg://print3d:${DB_PASSWORD}@db:5432/print3d
      ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY}
      TELEGRAM_BOT_TOKEN: ${TELEGRAM_BOT_TOKEN}
      TELEGRAM_CHAT_ID: ${TELEGRAM_CHAT_ID}
      UPLOAD_DIR: /app/uploads
    volumes:
      - uploads:/app/uploads
    depends_on:
      - db
    ports:
      - "8000:8000"

  frontend:
    build: ./frontend
    ports:
      - "5173:5173"

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/certs:/etc/nginx/certs
    depends_on:
      - backend
      - frontend

volumes:
  pgdata:
  uploads:

Переменные окружения (.env)

DB_PASSWORD=<strong_password>
ANTHROPIC_API_KEY=<claude_api_key>
TELEGRAM_BOT_TOKEN=<telegram_bot_token>
TELEGRAM_CHAT_ID=<your_telegram_chat_id>

Порядок реализации

Фаза 1 — Backend core (3-4 дня)

  1. Инициализация FastAPI проекта, Docker, PostgreSQL
  2. Модели SQLAlchemy + Alembic миграции
  3. Seed данных по материалам
  4. Сервис парсинга файлов (trimesh)
  5. Сервис расчёта цены (price engine)
  6. Endpoints: POST /api/calculate, GET /api/materials

Фаза 2 — Frontend core (3-4 дня)

  1. Инициализация Vue 3 + Vite + Tailwind
  2. FileUploader компонент
  3. MaterialPicker компонент
  4. PrintSettings + PriceResult компоненты
  5. Интеграция с API (Pinia stores + Axios)

Фаза 3 — AI + Orders (2-3 дня)

  1. AI Advisor — интеграция Claude API
  2. AiAdvisor компонент (фронт)
  3. OrderForm компонент + POST /api/orders
  4. Telegram-уведомления

Фаза 4 — Деплой (1-2 дня)

  1. Docker Compose конфигурация
  2. Nginx конфиг (reverse proxy + SSL)
  3. Деплой на VPS
  4. Тестирование end-to-end

Что НЕ входит в MVP (v2)

  • 3D-превью модели в браузере (Three.js + STLLoader)
  • Парсинг STEP-файлов (требует cadquery / OpenCASCADE)
  • Личный кабинет клиента
  • Онлайн-оплата (ЮKassa / Stripe)
  • История заказов
  • Тёмная тема
  • Скачивание расчёта в PDF
  • Мультиязычность
  • SEO-оптимизация (SSR / pre-rendering)