351 lines
10 KiB
Markdown
351 lines
10 KiB
Markdown
# Nashel Backend
|
||
|
||
Бэкенд для маркетплейса услуг "Nashel" — платформа для поиска мастеров и исполнителей.
|
||
|
||
## 🏗️ Архитектура
|
||
|
||
Проект построен на архитектуре **Clean Architecture** с модульным подходом:
|
||
|
||
```
|
||
nashel-backend/
|
||
├── src/
|
||
│ ├── BuildingBlocks/ # Общие блоки для всех модулей
|
||
│ │ ├── Application/ # Общие абстракции и поведения
|
||
│ │ ├── Domain/ # Базовые доменные сущности
|
||
│ │ └── Infrastructure/ # Общая инфраструктура
|
||
│ ├── Modules/ # Модули бизнес-логики
|
||
│ │ ├── Identity/ # Модуль авторизации и профилей
|
||
│ │ └── Order/ # Модуль заказов
|
||
│ └── Host/ # Точка входа приложения
|
||
└── tests/ # Тесты
|
||
```
|
||
|
||
### Модульность
|
||
|
||
Каждый модуль (Identity, Order) является автономным и содержит:
|
||
- **Domain** — бизнес-логика, сущности, репозитории
|
||
- **Application** — Use Cases, команды, запросы, валидация
|
||
- **Infrastructure** — реализация репозиториев, EF Core
|
||
- **Presentation** — API endpoints
|
||
|
||
## 🚀 Технологический стек
|
||
|
||
- **.NET 10** — фреймворк
|
||
- **ASP.NET Core** — веб-фреймворк
|
||
- **Entity Framework Core** — ORM
|
||
- **PostgreSQL** — база данных
|
||
- **MediatR** — паттерн CQRS/Mediator
|
||
- **FluentValidation** — валидация
|
||
- **JWT** — аутентификация
|
||
- **Docker** — контейнеризация
|
||
- **Swagger/OpenAPI** — документация API
|
||
|
||
## 📦 Установка и запуск
|
||
|
||
### Требования
|
||
|
||
- .NET 10 SDK
|
||
- Docker и Docker Compose
|
||
- PostgreSQL
|
||
|
||
### Запуск через Docker Compose
|
||
|
||
```bash
|
||
# Клонирование репозитория
|
||
git clone <repository-url>
|
||
cd nashel-backend
|
||
|
||
# Запуск всех сервисов
|
||
docker-compose up -d
|
||
|
||
# Просмотр логов
|
||
docker-compose logs -f app
|
||
|
||
# Остановка сервисов
|
||
docker-compose down
|
||
```
|
||
|
||
### Локальный запуск
|
||
|
||
```bash
|
||
# Восстановление зависимостей
|
||
dotnet restore
|
||
|
||
# Настройка переменных окружения
|
||
cp src/Host/appsettings.json src/Host/appsettings.Development.json
|
||
# Отредактируйте ConnectionStrings__DefaultConnection
|
||
|
||
# Применение миграций
|
||
dotnet ef database update --project src/Host --startup-project src/Host
|
||
|
||
# Запуск
|
||
dotnet run --project src/Host
|
||
```
|
||
|
||
## 🔧 Переменные окружения
|
||
|
||
| Переменная | Описание | Пример |
|
||
|------------|----------|--------|
|
||
| `ASPNETCORE_ENVIRONMENT` | Окружение | `Development`, `Production` |
|
||
| `ConnectionStrings__DefaultConnection` | Строка подключения к БД | `Host=db;Port=5432;Database=nashel;Username=postgres;Password=postgres` |
|
||
| `JWT__Secret` | Секретный ключ JWT | `your-super-secret-key` |
|
||
| `JWT__Issuer` | Издатель токена | `nashel-api` |
|
||
| `JWT__Audience` | Аудитория токена | `nashel-client` |
|
||
| `JWT__ExpirationMinutes` | Время жизни токена (мин) | `60` |
|
||
|
||
## 📚 API Документация
|
||
|
||
Swagger доступен по адресу: `http://localhost:5000/swagger`
|
||
|
||
### Основные эндпоинты
|
||
|
||
#### Аутентификация
|
||
|
||
```http
|
||
POST /api/auth/register
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"phone": "+79001234567",
|
||
"firstName": "Иван",
|
||
"lastName": "Иванов",
|
||
"password": "password123",
|
||
"confirmPassword": "password123",
|
||
"isCompany": false,
|
||
"companyName": "ООО 'Ромашка'",
|
||
"inn": "1234567890"
|
||
}
|
||
```
|
||
|
||
```http
|
||
POST /api/auth/login
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"phone": "+79001234567",
|
||
"password": "password123"
|
||
}
|
||
```
|
||
|
||
#### Профиль пользователя
|
||
|
||
```http
|
||
GET /api/profile
|
||
Authorization: Bearer {access_token}
|
||
|
||
PUT /api/profile
|
||
Authorization: Bearer {access_token}
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"firstName": "Иван",
|
||
"lastName": "Иванов",
|
||
"patronymic": "Иванович",
|
||
"companyName": "ООО 'Ромашка'",
|
||
"inn": "1234567890"
|
||
}
|
||
|
||
POST /api/profile/change-phone
|
||
Authorization: Bearer {access_token}
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"newPhone": "+79009876543"
|
||
}
|
||
|
||
POST /api/profile/become-performer
|
||
Authorization: Bearer {access_token}
|
||
|
||
POST /api/profile/avatar
|
||
Authorization: Bearer {access_token}
|
||
Content-Type: multipart/form-data
|
||
|
||
avatar: <file>
|
||
|
||
PUT /api/profile/competencies
|
||
Authorization: Bearer {access_token}
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"Competencies": ["сантехника", "электрика"]
|
||
}
|
||
```
|
||
|
||
#### Компетенции
|
||
|
||
```http
|
||
GET /api/competencies/search?q=сантех
|
||
Authorization: Bearer {access_token}
|
||
```
|
||
|
||
## 🎯 Реализованный функционал
|
||
|
||
### Модуль Identity
|
||
|
||
#### Аутентификация и авторизация
|
||
- ✅ Регистрация пользователей (частные лица и компании)
|
||
- ✅ Вход в систему (JWT токены)
|
||
- ✅ Обновление токенов
|
||
- ✅ Выход из системы
|
||
- ✅ Ролевая модель (Client, Master, Candidate, Company, Admin)
|
||
|
||
#### Профиль пользователя
|
||
- ✅ Получение профиля
|
||
- ✅ Редактирование профиля
|
||
- ✅ Изменение номера телефона
|
||
- ✅ Загрузка аватара с кропом
|
||
- ✅ Управление компетенциями
|
||
- ✅ Стать исполнителем
|
||
|
||
#### Компетенции
|
||
- ✅ Поиск компетенций по подстроке
|
||
- ✅ Общий пул компетенций для всех исполнителей
|
||
- ✅ Автоматическое создание новых компетенций
|
||
|
||
#### Компании
|
||
- ✅ Регистрация компаний с ИНН
|
||
- ✅ Управление данными компании
|
||
- ✅ Отдельная логика для компаний
|
||
|
||
### Модуль Order (в разработке)
|
||
- ⏳ Создание заказов
|
||
- ⏳ Поиск заказов на карте
|
||
- ⏳ SLA таймер для отклика на заказы
|
||
|
||
## 🗄️ База данных
|
||
|
||
### Схема
|
||
|
||
```
|
||
accounts (аккаунты пользователей)
|
||
├── id (PK)
|
||
├── phone (уникальный)
|
||
├── password_hash
|
||
├── roles (массив ролей)
|
||
└── profile_id (FK)
|
||
|
||
user_profiles (профили пользователей)
|
||
├── id (PK)
|
||
├── account_id (FK)
|
||
├── first_name
|
||
├── last_name
|
||
├── patronymic
|
||
├── company_name
|
||
├── inn (для компаний)
|
||
├── avatar_url
|
||
└── competency_profiles (связь с компетенциями)
|
||
|
||
competencies (компетенции/навыки)
|
||
├── id (PK)
|
||
├── name (уникальное, в нижнем регистре)
|
||
|
||
competency_profiles (связь профилей и компетенций)
|
||
├── id (PK)
|
||
├── profile_id (FK)
|
||
├── competency_id (FK)
|
||
```
|
||
|
||
### Миграции
|
||
|
||
```bash
|
||
# Создание новой миграции
|
||
dotnet ef migrations add AddNewFeature --project src/Infrastructure --startup-project src/Host
|
||
|
||
# Применение миграций
|
||
dotnet ef database update --project src/Infrastructure --startup-project src/Host
|
||
|
||
# Откат последней миграции
|
||
dotnet ef database update --project src/Infrastructure --startup-project src/Host
|
||
```
|
||
|
||
## 🔐 Безопасность
|
||
|
||
- Хеширование паролей (BCrypt)
|
||
- JWT токены с refresh token
|
||
- Валидация входных данных (FluentValidation)
|
||
- CORS политика
|
||
- Rate limiting (в планах)
|
||
|
||
## 🧪 Тестирование
|
||
|
||
```bash
|
||
# Запуск всех тестов
|
||
dotnet test
|
||
|
||
# Запуск тестов с покрытием
|
||
dotnet test --collect:"XPlat Code Coverage"
|
||
|
||
# Запуск конкретного теста
|
||
dotnet test --filter "FullyQualifiedName~TestName"
|
||
```
|
||
|
||
## 📝 Конвенции кода
|
||
|
||
### CQRS Pattern
|
||
|
||
Используем MediatR для реализации CQRS:
|
||
|
||
```csharp
|
||
// Команда (Command)
|
||
public record UpdateProfileCommand(...) : IRequest;
|
||
|
||
// Обработчик команды (Handler)
|
||
public class UpdateProfileCommandHandler : IRequestHandler<UpdateProfileCommand>
|
||
{
|
||
public async Task Handle(UpdateProfileCommand request, CancellationToken cancellationToken)
|
||
{
|
||
// Логика
|
||
}
|
||
}
|
||
```
|
||
|
||
### Валидация
|
||
|
||
Используем FluentValidation:
|
||
|
||
```csharp
|
||
public class UpdateProfileCommandValidator : AbstractValidator<UpdateProfileCommand>
|
||
{
|
||
public UpdateProfileCommandValidator()
|
||
{
|
||
RuleFor(x => x.FirstName).NotEmpty().MinimumLength(2);
|
||
RuleFor(x => x.LastName).NotEmpty().MinimumLength(2);
|
||
}
|
||
}
|
||
```
|
||
|
||
## 🚨 Обработка ошибок
|
||
|
||
- Глобальный exception handler
|
||
- Стандартные HTTP статусы (400, 401, 403, 404, 500)
|
||
- Подробные сообщения об ошибках в Development режиме
|
||
- Обобщенные сообщения в Production режиме
|
||
|
||
## 📦 Статические файлы
|
||
|
||
Загруженные аватары хранятся в `wwwroot/uploads/` и доступны по URL:
|
||
```
|
||
http://localhost:5000/uploads/{filename}
|
||
```
|
||
|
||
## 🔄 CI/CD
|
||
|
||
- GitHub Actions для автоматического тестирования
|
||
- Docker Hub для хранения образов
|
||
- Автоматический деплой (в планах)
|
||
|
||
## 📞 Контакты
|
||
|
||
- **Проект:** Nashel
|
||
- **Версия:** 1.0.0
|
||
- **Лицензия:** MIT
|
||
|
||
## 🤝 Вклад в проект
|
||
|
||
1. Fork репозитория
|
||
2. Создайте ветку для фичи (`git checkout -b feature/AmazingFeature`)
|
||
3. Закоммитьте изменения (`git commit -m 'Add some AmazingFeature'`)
|
||
4. Запушьте в ветку (`git push origin feature/AmazingFeature`)
|
||
5. Откройте Pull Request
|
||
|