Загрузка данных
# Задача: API для скидок на ASP.NET Core MVC + EF Core + SQLite
## Постановка
Компания-агрегатор скидок хочет REST API со следующими возможностями:
1. **Список брендов** — чтобы пользователь мог выбрать бренд.
2. **Купоны по бренду** — пользователь кликает на бренд и видит доступные скидки.
3. **Активные купоны** — с возможностью фильтрации по дате (купоны, действующие в заданном диапазоне).
4. **Фильтр по размеру скидки** — «покажи всё, где скидка от 15%».
5. **Тестовые данные** — 4 бренда и 5 купонов с разными сроками действия.
## Требования к реализации
- **Платформа:** ASP.NET Core MVC (шаблон уже знаком).
- **БД:** SQLite (файл `discounts.db` в корне проекта).
- **ORM:** EF Core (`Microsoft.EntityFrameworkCore.Sqlite`).
- **Схема БД** создаётся **автоматически** при старте через `EnsureCreated`.
- **Тестовые данные** засеиваются **автоматически** при старте (если БД пустая).
- **API** возвращает JSON (стандартный механизм ASP.NET).
- **Swagger** для ручного тестирования.
## Модель данных
### Brand (бренд)
| Поле | Тип | Примечание |
|---|---|---|
| `Id` | int | PK |
| `Name` | string | обязательно |
| `Description` | string? | nullable |
| `Coupons` | `List<Coupon>` | навигационное свойство |
### Coupon (купон)
| Поле | Тип | Примечание |
|---|---|---|
| `Id` | int | PK |
| `Code` | string | например `NIKE20` |
| `DiscountPercent` | int | 20% или 30% |
| `StartDate` | DateTime | начало действия |
| `EndDate` | DateTime | окончание |
| `IsActive` | bool | включён/выключен вручную |
| `BrandId` | int | FK на `Brand` |
| `Brand` | `Brand` | навигационное свойство |
## Контракты API
| Метод | URL | Описание |
|---|---|---|
| GET | `/api/brands` | Все бренды |
| GET | `/api/brands/{id}` | Бренд по Id |
| GET | `/api/coupons/by-brand/{brandId}` | Все купоны бренда |
| GET | `/api/coupons/active?dateFrom=&dateTo=` | Активные купоны в диапазоне дат |
| GET | `/api/coupons/filter?minDiscount=&maxDiscount=` | Фильтр по размеру скидки |
## Тестовые данные
| Бренд | Код купона | Скидка | Старт | Конец | IsActive |
|---|---|---|---|---|---|
| Nike | NIKE20 | 20% | −10 дн | +20 дн | true |
| Nike | NIKE10 | 10% | −30 дн | −5 дн | false |
| Adidas | ADIDAS15 | 15% | −5 дн | +15 дн | true |
| Zara | ZARA30 | 30% | −2 дн | +10 дн | true |
| Samsung | SAMSUNG5 | 5% | −1 дн | +30 дн | true |
## Дополнительные задания (если останется время)
1. **Поиск по коду купона** — `GET /api/coupons/search?code=NIKE` (`Where(c => c.Code.Contains(code))`).
2. **Максимальная скидка по бренду** — `GET /api/coupons/max-discount-per-brand` (`GroupBy` + `Max`).
3. **Добавить поле `MinPurchaseAmount` (decimal)** в `Coupon`, обновить сидер. Придётся удалить `discounts.db` и перезапустить проект — `EnsureCreated` не обновляет существующую схему.
4. **Количество активных купонов в выдаче брендов** — добавить в `GET /api/brands` поле `activeCouponsCount`.
## Критерии готовности
- [ ] Проект создан из шаблона MVC, лишнее (Views, wwwroot, HomeController) удалено.
- [ ] Установлены `Microsoft.EntityFrameworkCore.Sqlite` и `Microsoft.EntityFrameworkCore.Design`.
- [ ] В `appsettings.json` есть `ConnectionStrings:DefaultConnection = "Data Source=discounts.db"`.
- [ ] Созданы `Models/Brand.cs` и `Models/Coupon.cs` с навигационными свойствами.
- [ ] Создан `Data/AppDbContext.cs` с двумя `DbSet`.
- [ ] В `Program.cs` есть `AddDbContext<AppDbContext>(... UseSqlite(...))`.
- [ ] Создан `Data/DbInitializer.cs` с `EnsureCreated` + сидированием.
- [ ] В `Program.cs` после `Build()` есть блок `using (var scope = app.Services.CreateScope())` с вызовом `DbInitializer.Initialize`.
- [ ] Созданы `BrandsController` и `CouponsController` с `[ApiController]` и `[Route("api/[controller]")]`, наследуются от `ControllerBase`.
- [ ] Все 5 эндпоинтов работают и проверены в Swagger.
- [ ] `discounts.db` появляется в корне проекта при первом запуске.