AccountService — это REST API-сервис для управления банковскими счетами и транзакциями.
Сервис реализован на ASP.NET Core 9.
- Создание, обновление, удаление счетов
- Получение списка счетов пользователя
- Проверка принадлежности счета пользователю
- Получение выписки по транзакциям за период
- Добавление транзакций (списание, пополнение)
- Перевод между счетами
- Получение JWT токена
- Начисление процентов по вкладам через хранимую процедуру accrue_interest
- Ежедневный Cron-Job HangFire для автоматического начисления процентов
- Закрытие вклада с последующим начислением процентов
- Оптимистичная блокировка через concurrency-token (xmin)
- Покрытие модульными и интеграционными тестами
- Использование составного индекса
- Отправка событий в RabbitMq
- Получение событий, их обработка
- ASP.NET Core 9
- VSA + CQRS + MediatR
- FluentValidation
- AutoMapper
- Swagger (OpenAPI)
- Docker, Docker Compose
- Keycloak
- HangFire
- xUnit, Testcontainers, Moq
- RabbitMq + Outbox pattern
- Serilog
В Visual Studio можно выбрать проект Docker Compose в качестве стартового (startup project) и запустить его через стандартный запуск Debug/Run. Так вы запустите все контейнеры (Keycloak + AccountService) вместе.
- Перейдите в папку
docker:
cd docker- Запустите:
docker compose up -d --build- После запуска сервис будет доступен по адресу: http://localhost:80/swagger
API содержит эндпоинт для получения тестового токена:
GET /auth/tokenИспользуются следующие тестовые учётные данные:
- username:
testuser - password:
password - client_id:
account-api
В ответе возвращается JSON с access_token и другими параметрами.
Для тестирования авторизации через Swagger UI выполните следующие шаги:
-
Нажмите кнопку Authorize (справа сверху) в Swagger UI.
-
В открывшемся окне будет OAuth2 форма авторизации.
-
Нажмите кнопку Authorize — вы будете перенаправлены на страницу входа Keycloak.
-
Введите ваши тестовые учётные данные:
- username:
testuser - password:
password
- username:
-
После успешного входа Keycloak перенаправит обратно в Swagger UI, и токен будет автоматически зарегистрирован.
-
Теперь можно выполнять защищённые запросы — Swagger UI будет автоматически добавлять полученный токен в заголовок
Authorization: Bearer <token>.
Примечание:
Если вы хотите использовать токен вручную, получите его через эндпоинт /auth/token и введите в поле Value в окне Authorize в формате:
Bearer <ваш_access_token>
В проекте для стандартного результата используется универсальный класс MbResult<T>
MbResult<T>содержит либо успешный результат (Result), либо ошибку (Error).- Используется для унифицированной обработки ошибок и результатов в бизнес-логике.
- Валидация через FluentValidation интегрирована с
MbResult— в случае ошибки создаётся объектMbErrorс детальной информацией. - Добавлен метод расширения класса ControllerBase, возвращающий HTTP-код, соответствующий состоянию MbResult
Для автоматического ежедневного начисления процентов по вкладам используется Hangfire.
- Проверить работу можно путём создания счёта с типом Deposit и выполнением job'ы по адресу: http://localhost/hangfire/recurring
В сервисе используется RabbitMQ для обработки событий.
-
Outbox pattern — реализован для гарантированной доставки сообщений. Все события сначала записываются в таблицу
Outbox, после чего публикуются в очередь RabbitMQ. -
Консьюмеры:
AuditConsumer— потребитель событий аудита.AntifraudConsumer— потребитель событий для разморозки/заморозки клиента.
-
Карантин — в случае ошибок при обработке сообщений они помещаются таблицу карантина.
-
Логин/пароль по умолчанию:
- username:
guest - password:
guest
- username:
-
Веб-интерфейс RabbitMQ доступен по адресу: http://localhost:15672/
При ASPNETCORE_ENVIRONMENT=Development включаются упрощения:
- CORS = AllowAll
- Jwt.ValidateAudience = false
- Jwt.RequireHttps = false
-
Валюта указывается в формате ISO 4217 (В качестве заглушки есть 3 валюты -
RUB,USD,EUR) -
Поле
typeдля счёта:0—Checking1—Deposit2—Credit
-
Поле
typeдля транзакции:0—Credit1—Debit
Тестовые пользователи (в заглушке, не в Keycloak)
| ID | Примечание |
|---|---|
1d22cb6b-4d05-4c80-aa9d-8a4e5eb37656 |
Пользователь #1 |
43007588-4211-492f-ace0-f5b10aefe26b |
Пользователь #2 |
4650ec28-5afc-4bb2-8f47-90550012646e |
Пользователь #3 |
Подключение к базе данных API:
- localhost:5433
- db - account_db
- user - postgres
- password - postgres
Для проверки использования индексов можно использовать команду PostgreSQL:
EXPLAIN ANALYZE
SELECT *
FROM public."Transactions"
WHERE "AccountId" = '{account_id}'
AND "Date" between '2025-08-01' AND '2025-08-31'
ORDER BY "Date";
curl -X GET "http://localhost:80/accounts/1d22cb6b-4d05-4c80-aa9d-8a4e5eb37656"curl -X POST "http://localhost:80/accounts" \
-H "Content-Type: application/json" \
-d '{
"account": {
"ownerId": "1d22cb6b-4d05-4c80-aa9d-8a4e5eb37656",
"type": 1,
"currency": "RUB",
"balance": 1000.0,
"percentageRate": 1.5
}
}'curl -X PUT "http://localhost:80/accounts/{accountId}" \
-H "Content-Type: application/json" \
-d '{
"account": {
"ownerId": "1d22cb6b-4d05-4c80-aa9d-8a4e5eb37656",
"type": 1,
"currency": "RUB",
"balance": 1200.0,
"percentageRate": 2.0,
"openDate": "2025-07-28T19:37:07.6422334Z"
}
}'curl -X DELETE "http://localhost:80/accounts/{accountId}"curl -X GET "http://localhost:80/accounts/{accountId}/owner/1d22cb6b-4d05-4c80-aa9d-8a4e5eb37656/exists"curl -X GET "http://localhost:80/accounts/{accountId}/statement?from=2025-07-28&to=2025-07-29"curl -X POST "http://localhost:80/transactions" \
-H "Content-Type: application/json" \
-d '{
"transaction": {
"accountId": "{accountId_1}",
"amount": 500.0,
"currency": "RUB",
"type": Debit,
"description": "Information",
"date": "2025-07-28T10:00:00Z"
}
}'curl -X POST "http://localhost:80/transactions/transfer" \
-H "Content-Type: application/json" \
-d '{
"payloadModel": {
"fromAccountId": "{accountId_1}",
"toAccountId": "{accountId_2}",
"amount": 500,
"currency": "RUB",
"description": "Перевод на другой счёт"
}
}'curl -X POST "http://localhost:80/accounts/{accountId}/close-deposit"curl -X POST "http://localhost:80/accounts/{clientId}/block"curl -X POST "http://localhost:80/accounts/{clientId}/unblock"├───AccountService
│ ├───AutoMapper
│ ├───CurrencyService (Заглушка сервиса валют)
│ │ ├───Abstractions
│ │ └───Implementations
│ ├───Features
│ │ ├───Account
│ │ │ ├───AccrueInterest
│ │ │ ├───AddAccount
│ │ │ ├───CheckAccountOwnership
│ │ │ ├───CloseDeposit
│ │ │ ├───Consumers
│ │ │ ├───DeleteAccount
│ │ │ ├───FreezeAccount
│ │ │ ├───GetAccount
│ │ │ ├───GetAccountBalance
│ │ │ ├───GetAccountsByOwnerId
│ │ │ ├───GetAccountStatement
│ │ │ └───UpdateAccount
│ │ ├───Health
│ │ │ └───HealthCheck
│ │ ├───Outbox
│ │ │ ├───Dispatcher
│ │ │ └───Service
│ │ └───Transaction
│ │ ├───AddTransaction
│ │ └───TransferBetweenAccounts
│ ├───Filters
│ ├───Infrastructure
│ │ ├───Helpers
│ │ ├───Messaging
│ │ └───Repository
│ │ ├───Abstractions
│ │ └───Implementations
│ ├───Jobs
│ ├───Migrations
│ ├───PipelineBehaviors
│ ├───Properties
│ ├───Startup
│ │ ├───Auth
│ │ └───Middleware
│ ├───UserService (Заглушка сервиса пользователей)
│ │ ├───Abstractions
│ │ └───Implementations
│ └───wwwroot
└───AccountService.Tests
├───Integration
│ └───Common
└───Unit
- TG: holo21k
- Email: [email protected]