Skip to content

VladPeskovDev/betting-api-service

Repository files navigation

betting-api-service

Тестовое задание: Разработка сервиса интеграции с API платформы ставок.

Стек технологий: Фреймворк: Express Язык: TypeScript База данных: PostgreSQL ORM: Prisma Пакетный менеджер: pnpm Тестирование: Jest Контейнеризация: Docker и Docker Compose

-------------->Инструкция по запуску проекта<-----------

git clone <URLРЕПОЗИТОРИЯ> или ssh cd <НАЗВАНИЕПРОЕКТА>

Установка зависимостей pnpm install

Создаем файл .env криденшелы берем из .env.example

Применяем миграции для создания таблиц в базе данных pnpm prisma migrate deploy

Наполняем базу данных сидами pnpm prisma db seed

Запуск проект

pnpm run dev - режим разработки pnpm run start - продакш режим pnpm run test - запуск тестов (тестами покрыта вся логика взаимодействия пользователя с внешним api)

Также этот проект разворачивается в Docker-контейнерах и включает: Node.js приложение PostgreSQL базу данных Prisma ORM для управления схемой БД

Убедится, что есть и заполнен .env.docker файл docker-compose up --build <-------Сборка и запуск docker-compose down <-------Остановка контейнеров docker-compose up <-------Повторный запуск

Для Аутентификации и получения токена, который необходим для теста каждого endpointa использовать пользователей который сидами внесены в бд это:

{ "username": "Vladislav" } <------ обычный пользователь (тело запроса)

{ "username": "admin" } <------ пользователь с правами администратора (тело запроса)

документацию по API (Swagger) ------> http://localhost:3000/api-docs (при запущенном сервере)

--------->Аутентификация<-----------

  1. В нашем сервисе

    http://localhost:3000/api/auth/login { "username": "Vladislav" } (Можно тестировать через Thunder Client или Postman)

  2. В стороннем сервисе по API (Примеры тестирования через curl везде нужно добавлять токен полученный при аутентификации)

    curl -X POST http://localhost:3000/api/authOut
    -H "Content-Type: application/json"
    -H "Authorization: Bearer {token}"
    -d '{ "user_id": "12" }'

    ----------->Управление ставками<----------

  3. Получаем все ставки (история ставок)

    curl -X GET http://localhost:3000/api/bets
    -H "Content-Type: application/json"
    -H "Authorization: Bearer eyJhbGciOiJIUzI1Ni..."

  4. Получение ставки по id

    curl -X GET http://localhost:3000/api/bets/:id
    -H "Content-Type: application/json"
    -H "Authorization: Bearer eyJhbGciOiJIUzI1Ni..."

  5. Рекомендуемые ставки

    curl -X GET http://localhost:3000/api/bets/recommended
    -H "Content-Type: application/json"
    -H "Authorization: Bearer eyJhbGciOiJIUzI1Ni..."

  6. Сделать ставку

    curl -X POST http://localhost:3000/api/bets
    -H "Content-Type: application/json"
    -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
    -H "Idempotency-Key: 123e4567-e89b-12d3-a456-426614174000"
    -d '{ "amount": 3 }'

  7. Получить результат ставки

    curl -X POST "http://localhost:3000/api/win"
    -H "Content-Type: application/json"
    -H "Authorization: Bearer eyJhbGciOiJIUzI"
    -d '{"bet_id": 41}'

----------->Управление балансом<---------------

  1. Установить баланс

    curl -X POST http://localhost:3000/api/balance
    -H "Content-Type: application/json"
    -H "Authorization: Bearer "
    -d '{ "balance": 1000 }'

  2. Получить баланс

    curl -X POST http://localhost:3000/api/balance
    -H "Content-Type: application/json"
    -H "Authorization: Bearer "
    -d '{}'

    либо

    curl -X GET http://localhost:3000/api/balance
    -H "Content-Type: application/json"
    -H "Authorization: Bearer "

  3. История Транзакций

    curl -X GET "http://localhost:3000/api/transactions?page=1&limit=2"
    -H "Content-Type: application/json"
    -H "Authorization: Bearer <ваш-токен>"

  4. Проверка работоспособности сервиса

curl -X GET "http://localhost:3000/api/health"
-H "Content-Type: application/json"

--------------->Admin<----------------

  1. Тестирования аутентификации в api ставок

curl -X POST http://localhost:3000/api/internal/auth
-H "Content-Type: application/json"
-H "Authorization: Bearer {admin_token}"
-d '{ "user_id": "5" }'

  1. Тестирование получения рекомендуемой ставки от API

curl -X GET "http://localhost:3000/api/internal/bet"
-H "Content-Type: application/json"
-H "Authorization: Bearer {admin_token}"

  1. Тестирование размещения ставки в API

curl -X POST http://localhost:3000/api/internal/bet
-H "Content-Type: application/json"
-H "Authorization: Bearer {admin_token}"
-d '{ "user_id": "12", "bet": 3 }'

  1. Тестирование получения результата ставки от API

curl -X POST http://localhost:3000/api/internal/win
-H "Content-Type: application/json"
-H "Authorization: Bearer {admin_token}"
-d '{ "user_id": "5", "bet_id": "456" }'

  1. Установка баланса пользователя

curl -X POST http://localhost:3000/api/internal/balance
-H "Content-Type: application/json"
-H "Authorization: Bearer {admin_token}"
-d '{ "user_id": "5", "balance": 1000 }'

  1. Получение баланса пользователя

curl -X POST http://localhost:3000/api/internal/balance
-H "Content-Type: application/json"
-H "Authorization: Bearer {admin_token}"
-d '{ "user_id": "5" }'

  1. Проверка корректности баланса

curl -X POST http://localhost:3000/api/internal/check-balance
-H "Content-Type: application/json"
-H "Authorization: Bearer {admin_token}"
-d '{ "user_id": "5", "expected_balance": 1006 }'

----------------------> Архитектура решения тестового задания <----------------- Решение построено на трёхслойной архитектуре (контроллеры, сервисы, API-клиенты) с использованием TypeScript, Express и PostgreSQL.

  1. Основные компоненты

КОНТРОЛЛЕРЫ (Routes) Отвечают за обработку HTTP-запросов. Вызывают соответствующие сервисы и возвращают ответы клиенту. Примеры: bets.controller.ts — управление ставками. balance.controller.ts — работа с балансом.

СЕРВИСЫ (Services) Содержат бизнес-логику приложения. Обрабатывают данные и взаимодействуют с базой через ORM (Prisma). Примеры: bet.service.ts — обработка ставок, проверка выигрыша. balance.service.ts — управление балансом пользователя. auth.service.ts — логика авторизации.

API-клиенты (API Clients) Взаимодействуют с внешними API. Отправляют запросы и обрабатывают ответы. Примеры: betApiClient.ts — работа с API ставок. balanceApiClient.ts — работа с API баланса. winApiClient.ts — проверка результатов ставок.

Middleware (Промежуточные слои) Проверяют JWT-токен (авторизация пользователя). Валидация user-id в заголовках. Обрабатывают идемпотентность запросов.

БАЗА ДАННЫХ (PostgreSQL, Prisma ORM) Основные таблицы: users — пользователи. bets — ставки. transactions — транзакции. balances — баланс пользователей. Используются индексы для ускорения поиска (idempotencyKey, userId, status).

  1. Взаимодействие компонентов (Поставить ставку) Клиент отправляет запрос (например, создание ставки POST /api/bets). Контроллер принимает запрос, передаёт данные в сервис. Сервис: Проверяет баланс пользователя. Отправляет ставку во внешний API через betApiClient. Сохраняет данные в PostgreSQL через Prisma, при этом изменяя баланс пользователя на сумму ставки. Если запрос повторяется, проверяется идемпотентность по idempotencyKey. Все взаимодейтсвие пользователя логируется с записью данных в бд. Ответ возвращается клиенту.

  2. Взаимодействие компонентов (API /win) Клиент отправляет запрос (POST /api/bets/win) с bet_id, чтобы проверить результат ставки. Контроллер (checkBetWin) принимает запрос и передаёт данные в сервис (checkBetResult). Сервис: Проверяет, существует ли ставка в базе (ищет по bet_id). Проверяет статус ставки: Если статус "pending" → отправляет запрос во внешний API (winApiClient) для получения результата. Если ставка уже имеет статус "completed", возвращает сохранённый результат (идемпотентность запроса). В случае выигрыша: Обновляет статус ставки на "won", фиксирует выигрышную сумму. Обновляет баланс пользователя. Создаёт транзакцию в БД (история операций). В случае проигрыша: Обновляет статус. Идемпотентность обеспечивается проверкой статуса ставки: Если статус "completed", повторный запрос не вызывает внешний API, а просто возвращает сохранённый результат. Ответ возвращается клиенту с информацией о результате ставки.

  3. Взаимодействие с внешними API Каждое обращение требует заголовков user-id и x-signature. Реализована аутентификация (POST /api/auth), которая проверяет пользователя через authApiClient. Вся работа с балансом, ставками и результатами ведётся через внешний API.

  4. Безопасность и обработка ошибок Используется JWT-токен (verifyAccessToken). Запросы проверяются на идемпотентность (ключ idempotencyKey). Ошибки логируются в консоль и возвращаются в формате JSON. Вывод Архитектура построена по принципу разделения ответственности. Каждый компонент выполняет строго свою задачу, что делает код гибким и расширяемым.

  5. Взаимодействие компонентов (Пул эндпоинтов для администратора) Общий принцип работы админских эндпоинтов В системе реализован админ без отдельной роли "админ", а через конкретного пользователя-администратора, которого добавили в сиды при начальной инициализации базы данных. Все запросы к административным эндпоинтам требуют аутентификации через JWT-токен, а проверка на админа осуществляется через сравнение userId с предустановленным ID администратора. Безопасность опеспечена: Проверка JWT-токена (verifyAccessToken) → в res.locals.user сохраняется userId. Проверка, что userId совпадает с ID админа, который хранится в сидовых данных.

  6. В тестовом задании предлагается отправлять userId в теле запроса на эндпоинт GET /api/internal/bet для тестирования получения рекомендуемой ставки от API. Однако, согласно стандартам HTTP, метод GET не поддерживает тело запроса, и большинство серверов и прокси его игнорируют. Поэтому я изменил запрос и направляю его без тела. Несмотря на это, API возвращает 200 OK. Наиболее правильным решением было бы использовать POST, поскольку он допускает тело запроса, но в тестовом задании уже присутствует эндпоинт POST /api/internal/auth.

http://localhost:3000/api-docs

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors