commit 7669a1c77b3dbb0ff5807c349b016513f209d34c Author: Luckaskl Date: Mon May 18 17:54:09 2026 -0500 criação inicial do corpo do projeto diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d6a07fa --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +FROM python:3.11-slim + +WORKDIR /app + +# Instalar dependências do sistema +RUN apt-get update && apt-get install -y \ + gcc \ + libpq-dev \ + && rm -rf /var/lib/apt/lists/* + +COPY requirements.txt . + +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"] diff --git a/app/api/v1/api.py b/app/api/v1/api.py new file mode 100644 index 0000000..24ccd96 --- /dev/null +++ b/app/api/v1/api.py @@ -0,0 +1,5 @@ +from fastapi import APIRouter +from app.api.v1.endpoints import games + +api_router = APIRouter() +api_router.include_router(games.router, prefix="/catalog/games", tags=["games"]) diff --git a/app/api/v1/endpoints/games.py b/app/api/v1/endpoints/games.py new file mode 100644 index 0000000..b3633cb --- /dev/null +++ b/app/api/v1/endpoints/games.py @@ -0,0 +1,28 @@ +from fastapi import APIRouter, Depends, HTTPException, status +from sqlalchemy.orm import Session +from typing import List, Optional + +from app.schemas.game import GameCreate, GameUpdate, GameResponse, StandardResponse +from app.db.database import get_db + +router = APIRouter() + +@router.get("/", response_model=StandardResponse) +def read_games(skip: int = 0, limit: int = 100, genre: Optional[str] = None, platform: Optional[str] = None, db: Session = Depends(get_db)): + # Lógica de listagem e filtragem será implementada aqui + return {"success": True, "message": "Lista de jogos", "data": []} + +@router.post("/", response_model=StandardResponse, status_code=status.HTTP_201_CREATED) +def create_game(game: GameCreate, db: Session = Depends(get_db)): + # Lógica de criação será implementada aqui + return {"success": True, "message": "Jogo criado com sucesso", "data": {}} + +@router.get("/{id_ou_slug}", response_model=StandardResponse) +def read_game(id_ou_slug: str, db: Session = Depends(get_db)): + # Lógica de leitura de um único jogo será implementada aqui + return {"success": True, "message": "Detalhes do jogo", "data": {}} + +@router.patch("/{id}", response_model=StandardResponse) +def update_game(id: str, game: GameUpdate, db: Session = Depends(get_db)): + # Lógica de atualização parcial será implementada aqui + return {"success": True, "message": "Jogo atualizado com sucesso", "data": {}} diff --git a/app/core/config.py b/app/core/config.py new file mode 100644 index 0000000..ed560f8 --- /dev/null +++ b/app/core/config.py @@ -0,0 +1,12 @@ +from pydantic_settings import BaseSettings + +class Settings(BaseSettings): + PROJECT_NAME: str = "Microsserviço de Catálogo de Jogos" + API_V1_STR: str = "/api/v1" + DATABASE_URL: str + + class Config: + env_file = ".env" + case_sensitive = True + +settings = Settings() diff --git a/app/db/database.py b/app/db/database.py new file mode 100644 index 0000000..9c992ad --- /dev/null +++ b/app/db/database.py @@ -0,0 +1,16 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import declarative_base +from sqlalchemy.orm import sessionmaker +from app.core.config import settings + +engine = create_engine(settings.DATABASE_URL) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +Base = declarative_base() + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..8dc643e --- /dev/null +++ b/app/main.py @@ -0,0 +1,18 @@ +from fastapi import FastAPI +from app.core.config import settings +from app.api.v1.api import api_router +from app.db.database import Base, engine + +# Cria as tabelas do banco de dados (idealmente usaríamos Alembic em produção) +Base.metadata.create_all(bind=engine) + +app = FastAPI( + title=settings.PROJECT_NAME, + openapi_url=f"{settings.API_V1_STR}/openapi.json" +) + +app.include_router(api_router, prefix=settings.API_V1_STR) + +@app.get("/") +def root(): + return {"message": "Bem-vindo ao Microsserviço de Catálogo do GameVerse"} diff --git a/app/models/game.py b/app/models/game.py new file mode 100644 index 0000000..2cabe14 --- /dev/null +++ b/app/models/game.py @@ -0,0 +1,20 @@ +from sqlalchemy import Column, String, Boolean, JSON +from app.db.database import Base +import uuid + +class Game(Base): + __tablename__ = "games" + + id = Column(String, primary_key=True, default=lambda: f"gv-{uuid.uuid4().hex[:8]}") + title = Column(String, index=True, nullable=False) + slug = Column(String, unique=True, index=True, nullable=False) + description = Column(String) + developer = Column(String) + active = Column(Boolean, default=True) + + # Usando JSON para simplificar arrays no MVP conforme os requisitos. + # Em um banco relacional robusto, genres e platforms seriam tabelas M:N. + genres = Column(JSON) + platforms = Column(JSON) + images = Column(JSON) + system_requirements = Column(JSON) diff --git a/app/schemas/game.py b/app/schemas/game.py new file mode 100644 index 0000000..e0d6ec8 --- /dev/null +++ b/app/schemas/game.py @@ -0,0 +1,46 @@ +from pydantic import BaseModel, HttpUrl +from typing import List, Optional, Dict, Any + +class GameImages(BaseModel): + thumbnail: Optional[HttpUrl] = None + header: Optional[HttpUrl] = None + +class SystemRequirements(BaseModel): + cpu: Optional[str] = None + gpu: Optional[str] = None + ram: Optional[str] = None + +class GameBase(BaseModel): + title: str + description: str + genres: List[str] + platforms: List[str] + developer: str + images: Optional[GameImages] = None + system_requirements: Optional[SystemRequirements] = None + active: bool = True + +class GameCreate(GameBase): + pass + +class GameUpdate(BaseModel): + title: Optional[str] = None + description: Optional[str] = None + genres: Optional[List[str]] = None + platforms: Optional[List[str]] = None + developer: Optional[str] = None + images: Optional[GameImages] = None + system_requirements: Optional[SystemRequirements] = None + active: Optional[bool] = None + +class GameResponse(GameBase): + id: str + slug: str + + class Config: + from_attributes = True + +class StandardResponse(BaseModel): + success: bool + message: str + data: Optional[Any] = None diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6aa7433 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,33 @@ +version: '3.8' + +services: + web: + build: . + ports: + - "8000:8000" + environment: + - DATABASE_URL=postgresql://postgres:postgres@db:5432/gameverse_catalog + volumes: + - .:/app + depends_on: + db: + condition: service_healthy + + db: + image: postgres:15 + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: gameverse_catalog + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 5s + retries: 5 + +volumes: + postgres_data: diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..64cd4d3 --- /dev/null +++ b/readme.md @@ -0,0 +1,84 @@ +Este é um planejamento detalhado para o microsserviço de **Catálogo de Jogos**, estruturado para atender a todos os requisitos do projeto GameVerse. +## 1. Nome do Microsserviço +**Microsserviço de Catálogo e Metadados de Jogos** +## 2. Integrantes do grupo +[Inserir nome completo de todos os integrantes aqui] +## 3. Objetivo do microsserviço +O objetivo deste serviço é centralizar e gerenciar todo o inventário de títulos disponíveis na plataforma GameVerse. Ele atua como a "vitrine" do ecossistema, resolvendo o problema de fragmentação de informações sobre os jogos. Sem ele, o sistema não teria uma fonte única de verdade para descrições, requisitos técnicos ou categorização. Ele é fundamental porque fornece os dados necessários para que a **Loja** exiba produtos e para que a **Biblioteca do Usuário** saiba exatamente o que o jogador adquiriu. +## 4. Responsabilidades do microsserviço + * **Gerenciar o inventário:** Cadastrar, atualizar e remover jogos do catálogo. + * **Categorização:** Administrar gêneros (RPG, FPS, Indie) e plataformas (PC, Console, Cloud). + * **Curadoria de Conteúdo:** Armazenar descrições detalhadas, notas de patch e classificações indicativas. + * **Gestão de Mídia:** Vincular URLs de imagens, trailers e capas aos respectivos títulos. + * **Busca e Filtragem:** Permitir que outros serviços consultem jogos por critérios específicos. +## 5. Dados que o serviço precisa receber +Para o cadastro ou atualização de um jogo, o serviço requer: + * **Nome do jogo:** (String) + * **Descrição:** (Text/Markdown) + * **Gêneros:** (Array de IDs ou nomes) + * **Plataformas:** (Array de IDs - ex: Steam, Epic, Xbox) + * **Mídia:** (URLs de imagens de capa e galeria) + * **Desenvolvedora/Distribuidora:** (String) + * **Requisitos de Sistema:** (Objeto JSON com CPU, GPU, RAM mínimos e recomendados) +## 6. Dados que o serviço deve retornar +Exemplo de resposta ao consultar um jogo específico: +```json +{ + "success": true, + "message": "Jogo localizado com sucesso", + "data": { + "game_id": "gv-8829", + "title": "Cyber-Acre 2077", + "slug": "cyber-acre-2077", + "platforms": ["PC", "Linux"], + "genres": ["Action", "RPG"], + "description": "Um RPG de ação em um futuro distópico no norte do Brasil.", + "images": { + "thumbnail": "https://cdn.gameverse.com/covers/ca2077_thumb.jpg", + "header": "https://cdn.gameverse.com/headers/ca2077_wide.jpg" + }, + "active": true + } +} + +``` +## 7. Com quais serviços ele precisa se comunicar + * **Loja de Jogos (Storefront):** Para fornecer os dados que serão exibidos ao comprador. + * **Biblioteca do Usuário:** Para validar se o ID do jogo que o usuário possui ainda existe e está atualizado. + * **Busca (Search Service):** Para indexar novos títulos em motores de busca como Elasticsearch. + * **Laravel Central/API Gateway:** Para autenticação de administradores que gerenciam o catálogo. +## 8. Fluxo principal do serviço + 1. **Administrador** envia uma requisição de cadastro com os dados do novo jogo. + 2. O serviço **valida** se o título já existe e se todos os campos obrigatórios estão presentes. + 3. O serviço **persiste** os dados no banco de dados (Gêneros, Plataformas e Dados Gerais). + 4. O serviço **emite um evento** (via RabbitMQ/Kafka ou Webhook) informando que um novo jogo foi adicionado. + 5. A **Loja** recebe a atualização e passa a exibir o jogo para os usuários. + 6. O **Usuário** realiza uma busca e o serviço retorna a lista filtrada de jogos. +## 9. Rotas iniciais da API + * GET /api/v1/catalog/games (Listagem com paginação e filtros de gênero/plataforma) + * POST /api/v1/catalog/games (Criação de novo título - Restrito a Admin) + * GET /api/v1/catalog/games/{id_ou_slug} (Detalhes completos de um jogo específico) + * PATCH /api/v1/catalog/games/{id} (Atualização parcial de dados como descrição ou imagens) +## 10. Possíveis erros + * **Jogo não encontrado:** Quando um ID inválido é solicitado. + * **Título Duplicado:** Tentativa de cadastrar um jogo com nome ou slug já existente. + * **Falha na persistência de mídia:** Erro ao tentar vincular links de imagens inválidos. + * **Dados incompletos:** Envio de formulário sem campos obrigatórios (ex: sem plataforma definida). + * **Incompatibilidade de Versão:** Tentativa de atualizar um jogo que foi removido ou arquivado. + +# DEVE SER FEITO EM FAST API (python) + +## 11. Como Executar (Docker) +Este projeto foi containerizado para facilitar a execução. + +### Pré-requisitos +- [Docker](https://www.docker.com/) e [Docker Compose](https://docs.docker.com/compose/) instalados. + +### Passos +1. Execute o script na raiz do projeto: + ```bash + ./run.sh + ``` +2. A aplicação FastAPI estará disponível em: http://localhost:8000 +3. A documentação interativa (Swagger UI) pode ser acessada em: http://localhost:8000/docs +4. O banco de dados PostgreSQL estará rodando internamente na porta `5432`. \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..06e3828 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,8 @@ +fastapi>=0.109.0 +uvicorn[standard]>=0.27.0 +pydantic>=2.5.0 +pydantic-settings>=2.1.0 +sqlalchemy>=2.0.25 +psycopg2-binary>=2.9.9 +alembic>=1.13.1 +pytest>=8.0.0 diff --git a/run.sh b/run.sh new file mode 100644 index 0000000..c38dd40 --- /dev/null +++ b/run.sh @@ -0,0 +1,5 @@ +#!/bin/bash +echo "Subindo containers..." +docker compose up --build -d +echo "Aplicação disponível em http://localhost:8000" +echo "Documentação disponível em http://localhost:8000/docs"