Files
marketing-parser/IMAGE_GENERATION_FRONTEND.md
2025-12-05 01:16:16 +05:00

19 KiB
Raw Permalink Blame History

Документация для фронтенда: Генерация изображений для постов

Обзор изменений

В систему добавлена автоматическая генерация изображений для постов в социальных сетях. Теперь при создании маркетинговой стратегии для каждого поста автоматически генерируется уникальное изображение с помощью OpenAI DALL-E API. Изображения сохраняются в MinIO и прикрепляются к постам при публикации в Facebook.


Новые поля в API

1. PostCalendarItem (Календарь постов)

В объекте PostCalendarItem добавлены два новых поля для работы с изображениями:

Поле Тип Описание Обязательное
imageUrl string Имя файла изображения (используется для получения через API) Нет
imageFilename string Имя файла изображения Нет

Важно:

  • Поля могут быть null, если генерация изображения не удалась
  • В этом случае пост публикуется без изображения
  • Оба поля содержат одинаковое значение (имя файла)
  • Для получения изображения используйте API эндпоинт /api/marketing/analysis/images/{imageFilename}

2. PostingTask (Задачи публикации)

В объекте PostingTask также добавлены поля для изображений:

Поле Тип Описание Обязательное
imageUrl string Имя файла изображения (используется для получения через API) Нет
imageFilename string Имя файла изображения Нет

Изменения в API эндпоинтах

GET /api/marketing/strategy/{strategyId}

Ответ теперь включает поля изображений в каждом элементе календаря постов.

Пример ответа

{
  "success": true,
  "data": {
    "strategyId": "507f1f77bcf86cd799439011",
    "analysisId": "507f1f77bcf86cd799439012",
    "status": "completed",
    "strategy": {
      "postCalendar": [
        {
          "publishDate": "2024-01-15T10:00:00",
          "platform": "Facebook",
          "contentType": "пост",
          "theme": "Презентация нового продукта",
          "postText": "Мы рады представить наш новый продукт...",
          "hashtags": ["#новинка", "#продукт", "#маркетинг"],
          "publishTime": "10:00",
          "imageUrl": "post_image_1705312800000_1234567890.png",
          "imageFilename": "post_image_1705312800000_1234567890.png"
        }
      ]
    }
  }
}

GET /api/marketing/analysis/{analysisId}/strategy

Аналогично, ответ включает поля изображений в календаре постов.


Получение изображений

Через бэкенд API

Все изображения должны получаться через бэкенд API. Прямой доступ к MinIO с фронтенда не предусмотрен.

Эндпоинт для получения изображения

GET /api/marketing/analysis/images/{imageFilename}

Возвращает изображение поста в формате PNG.

Параметры пути

Параметр Тип Описание
imageFilename string Имя файла изображения

Заголовки запроса

Заголовок Тип Обязательный Описание
Authorization string Да JWT токен в формате Bearer {token}

Пример запроса

const imageUrl = `/api/marketing/analysis/images/${post.imageFilename}`;

fetch(imageUrl, {
  headers: {
    Authorization: `Bearer ${token}`,
  },
})
  .then((response) => {
    if (response.ok) {
      return response.blob();
    }
    throw new Error('Failed to load image');
  })
  .then((blob) => {
    const imageObjectUrl = URL.createObjectURL(blob);
    // Используйте imageObjectUrl для отображения
  });

Пример ответа

  • Успешный ответ (200 OK):

    • Content-Type: image/png
    • Body: бинарные данные изображения PNG
  • Ошибка 401 Unauthorized:

    • Токен отсутствует или невалиден
  • Ошибка 404 Not Found:

    • Изображение не найдено на сервере
  • Ошибка 500 Internal Server Error:

    • Внутренняя ошибка сервера при загрузке изображения

Кэширование

Сервер возвращает заголовок Cache-Control: public, max-age=3600, что позволяет браузеру кэшировать изображения на 1 час.


Рекомендации по отображению

1. Проверка наличия изображения

Всегда проверяйте наличие изображения перед отображением:

// Пример на JavaScript/TypeScript
const PostCard = ({ post, apiBaseUrl, authToken }) => {
  const hasImage = post.imageUrl && post.imageFilename;
  const imageUrl = hasImage
    ? `${apiBaseUrl}/api/marketing/analysis/images/${post.imageFilename}`
    : null;

  return (
    <div className='post-card'>
      <h3>{post.theme}</h3>
      <p>{post.postText}</p>

      {imageUrl ? (
        <img
          src={imageUrl}
          alt={post.theme}
          onError={(e) => {
            // Fallback если изображение не загрузилось
            e.target.style.display = 'none';
          }}
          // Если требуется авторизация, используйте fetch с заголовками
        />
      ) : (
        <div className='no-image-placeholder'>Изображение не сгенерировано</div>
      )}

      <div className='hashtags'>
        {post.hashtags.map((tag) => (
          <span key={tag}>#{tag}</span>
        ))}
      </div>
    </div>
  );
};

2. Обработка ошибок загрузки

Всегда предусматривайте fallback для случаев, когда:

  • Изображение не сгенерировано (imageUrl = null)
  • Изображение не найдено на сервере
  • Ошибка при загрузке изображения
const [imageError, setImageError] = useState(false);

const getImageUrl = (imageFilename) => {
  if (!imageFilename) return null;
  return `${API_BASE_URL}/api/marketing/analysis/images/${imageFilename}`;
};

const handleImageError = () => {
  setImageError(true);
};

return (
  <>
    {post.imageUrl && !imageError ? (
      <img
        src={getImageUrl(post.imageFilename)}
        alt={post.theme}
        onError={handleImageError}
      />
    ) : (
      <div className='image-placeholder'>
        <Icon name='image' />
        <span>Изображение недоступно</span>
      </div>
    )}
  </>
);

3. Оптимизация загрузки

Рекомендуется использовать lazy loading для изображений:

<img
  src={
    post.imageUrl
      ? `${API_BASE_URL}/api/marketing/analysis/images/${post.imageFilename}`
      : null
  }
  alt={post.theme}
  loading='lazy'
  className='post-image'
/>

4. Размеры изображений

Изображения генерируются в размере 1024x1024 пикселей (квадратные). При отображении учитывайте это при настройке CSS:

.post-image {
  width: 100%;
  max-width: 512px;
  height: auto;
  border-radius: 8px;
  object-fit: cover;
}

Примеры использования

React компонент для отображения поста

import React, { useState } from 'react';

interface PostCalendarItem {
  publishDate: string;
  platform: string;
  contentType: string;
  theme: string;
  postText: string;
  hashtags: string[];
  publishTime: string;
  imageUrl?: string | null;
  imageFilename?: string | null;
}

interface PostCardProps {
  post: PostCalendarItem;
  apiBaseUrl: string;
}

const PostCard: React.FC<PostCardProps> = ({ post, apiBaseUrl }) => {
  const [imageError, setImageError] = useState(false);

  const getImageUrl = (): string | null => {
    if (!post.imageFilename) return null;
    return `${apiBaseUrl}/api/marketing/analysis/images/${post.imageFilename}`;
  };

  const imageUrl = getImageUrl();

  return (
    <div className='post-card'>
      <div className='post-header'>
        <span className='platform-badge'>{post.platform}</span>
        <span className='content-type'>{post.contentType}</span>
        <span className='publish-time'>{post.publishTime}</span>
      </div>

      <h3 className='post-theme'>{post.theme}</h3>

      {imageUrl && !imageError ? (
        <div className='post-image-container'>
          <img
            src={imageUrl}
            alt={post.theme}
            className='post-image'
            loading='lazy'
            onError={() => setImageError(true)}
          />
        </div>
      ) : (
        <div className='no-image-placeholder'>
          <svg width='64' height='64' viewBox='0 0 24 24' fill='none'>
            <path
              d='M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z'
              fill='currentColor'
            />
          </svg>
          <p>Изображение не сгенерировано</p>
        </div>
      )}

      <p className='post-text'>{post.postText}</p>

      <div className='post-hashtags'>
        {post.hashtags.map((tag, index) => (
          <span key={index} className='hashtag'>
            {tag.startsWith('#') ? tag : `#${tag}`}
          </span>
        ))}
      </div>

      <div className='post-footer'>
        <span className='publish-date'>
          {new Date(post.publishDate).toLocaleDateString('ru-RU')}
        </span>
      </div>
    </div>
  );
};

export default PostCard;

Vue компонент

<template>
  <div class="post-card">
    <div class="post-header">
      <span class="platform-badge">{{ post.platform }}</span>
      <span class="content-type">{{ post.contentType }}</span>
      <span class="publish-time">{{ post.publishTime }}</span>
    </div>

    <h3 class="post-theme">{{ post.theme }}</h3>

    <div v-if="imageUrl && !imageError" class="post-image-container">
      <img
        :src="imageUrl"
        :alt="post.theme"
        class="post-image"
        loading="lazy"
        @error="imageError = true"
      />
    </div>
    <div v-else class="no-image-placeholder">
      <Icon name="image" />
      <p>Изображение не сгенерировано</p>
    </div>

    <p class="post-text">{{ post.postText }}</p>

    <div class="post-hashtags">
      <span v-for="(tag, index) in post.hashtags" :key="index" class="hashtag">
        {{ tag.startsWith('#') ? tag : `#${tag}` }}
      </span>
    </div>

    <div class="post-footer">
      <span class="publish-date">
        {{ formatDate(post.publishDate) }}
      </span>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, ref } from 'vue';

interface PostCalendarItem {
  publishDate: string;
  platform: string;
  contentType: string;
  theme: string;
  postText: string;
  hashtags: string[];
  publishTime: string;
  imageUrl?: string | null;
  imageFilename?: string | null;
}

const props = defineProps<{
  post: PostCalendarItem;
  apiBaseUrl: string;
}>();

const imageError = ref(false);

const imageUrl = computed(() => {
  if (!props.post.imageFilename) return null;
  return `${props.apiBaseUrl}/api/marketing/analysis/images/${props.post.imageFilename}`;
});

const formatDate = (dateString: string) => {
  return new Date(dateString).toLocaleDateString('ru-RU');
};
</script>

Важные замечания

1. Генерация изображений

  • Изображения генерируются автоматически при создании стратегии
  • Процесс генерации может занять время (обычно 10-30 секунд на изображение)
  • Если генерация не удалась, пост все равно будет создан, но без изображения

2. Хранение изображений

  • Все изображения хранятся на бэкенде
  • Формат изображений: PNG
  • Размер изображений: 1024x1024 пикселей
  • Имя файла уникально для каждого поста
  • Доступ к изображениям только через API эндпоинт

3. Публикация постов

  • При публикации поста в Facebook изображение автоматически прикрепляется
  • Если изображение отсутствует, пост публикуется только с текстом
  • Это не влияет на успешность публикации

4. Обратная совместимость

  • Старые стратегии, созданные до добавления этой функции, не будут иметь изображений
  • Поля imageUrl и imageFilename будут null для таких постов
  • Фронтенд должен корректно обрабатывать null значения

Конфигурация

Для работы с изображениями вам понадобится базовый URL вашего API:

// config.ts
export const API_CONFIG = {
  baseUrl: 'http://your-backend-url', // URL вашего бэкенда
  // Например: 'http://localhost:8080' или 'https://api.example.com'
};

Миграция существующего кода

Если у вас уже есть компоненты для отображения постов, обновите их следующим образом:

  1. Добавьте проверку наличия изображения:

    const hasImage = post.imageUrl && post.imageFilename;
    
  2. Добавьте отображение изображения:

    {
      hasImage && (
        <img
          src={`${API_BASE_URL}/api/marketing/analysis/images/${post.imageFilename}`}
          alt={post.theme}
        />
      );
    }
    
  3. Обновите типы/интерфейсы:

    interface PostCalendarItem {
      // ... существующие поля
      imageUrl?: string | null;
      imageFilename?: string | null;
    }
    

Поддержка

При возникновении проблем:

  1. Проверьте, что imageUrl и imageFilename не null
  2. Убедитесь, что используете правильный API эндпоинт: /api/marketing/analysis/images/{imageFilename}
  3. Проверьте, что JWT токен валиден и передается в заголовке Authorization
  4. Проверьте консоль браузера на наличие ошибок сети или авторизации
  5. Убедитесь, что используете правильный базовый URL API

Пример полного ответа API

{
  "success": true,
  "data": {
    "strategyId": "507f1f77bcf86cd799439011",
    "analysisId": "507f1f77bcf86cd799439012",
    "status": "completed",
    "createdAt": "2024-01-15T10:00:00",
    "completedAt": "2024-01-15T10:05:00",
    "durationWeeks": 4,
    "priorityPlatforms": ["Facebook", "Instagram"],
    "strategy": {
      "weeklyPlans": [
        {
          "weekNumber": 1,
          "mainThemes": ["Презентация продукта", "Преимущества"],
          "contentRecommendations": "Создавайте контент...",
          "priorityPlatforms": ["Facebook"]
        }
      ],
      "postCalendar": [
        {
          "publishDate": "2024-01-16T10:00:00",
          "platform": "Facebook",
          "contentType": "пост",
          "theme": "Презентация нового продукта",
          "postText": "Мы рады представить наш новый продукт, который поможет вам...",
          "hashtags": ["#новинка", "#продукт", "#маркетинг"],
          "publishTime": "10:00",
          "imageUrl": "post_image_1705312800000_1234567890.png",
          "imageFilename": "post_image_1705312800000_1234567890.png"
        },
        {
          "publishDate": "2024-01-18T14:00:00",
          "platform": "Instagram",
          "contentType": "сторис",
          "theme": "Преимущества продукта",
          "postText": "Узнайте о главных преимуществах нашего продукта...",
          "hashtags": ["#преимущества", "#качество"],
          "publishTime": "14:00",
          "imageUrl": null,
          "imageFilename": null
        }
      ]
    }
  }
}

Обратите внимание, что второй пост не имеет изображения (imageUrl и imageFilename равны null). Это нормальная ситуация, если генерация изображения не удалась.


Дата обновления: 2024-01-15
Версия API: 1.0