travelmarine-backend/src/catalog/catalog.service.ts

543 lines
23 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Injectable } from '@nestjs/common';
import { UsersService } from '../users/users.service';
import { ReservationsService } from '../reservations/reservations.service';
import { ReviewsService } from '../reviews/reviews.service';
import {
CatalogItemShortDto,
CatalogItemLongDto,
} from './dto/catalog-item.dto';
import { CatalogParamsDto } from './dto/catalog-params.dto';
import { CatalogResponseDto } from './dto/catalog-response.dto';
import { MainPageCatalogResponseDto } from './dto/main-page-catalog-response.dto';
@Injectable()
export class CatalogService {
constructor(
private readonly usersService: UsersService,
private readonly reservationsService: ReservationsService,
private readonly reviewsService: ReviewsService,
) {}
private catalogItems: CatalogItemLongDto[] = [
{
id: 1,
name: 'Азимут 55',
length: 16.7,
speed: 32,
minCost: 85000,
mainImageUrl: 'api/uploads/1765727362318-238005198.jpg',
galleryUrls: [
'api/uploads/1765727362318-238005198.jpg',
'api/uploads/1765727637471-474231444.webp',
'api/uploads/1765727743466-187581713.webp',
'api/uploads/1765727819793-158598111.webp',
'api/uploads/1765727903961-559930753.webp',
'api/uploads/1765727928577-55944164.webp',
'api/uploads/1765727948691-389175954.webp',
'api/uploads/1765727961591-243584836.webp',
'api/uploads/1765727992064-37229339.webp',
],
hasQuickRent: true,
isFeatured: true,
year: 2022,
comfortCapacity: 8,
maxCapacity: 12,
width: 4.8,
cabinsCount: 3,
matherial: 'Стеклопластик',
power: 1200,
description:
'Роскошная моторная яхта Азимут 55 - это воплощение итальянского стиля и российского качества. Идеально подходит для прогулок по Финскому заливу, корпоративных мероприятий и романтических свиданий. На борту: три комфортабельные каюты, просторный салон с панорамным остеклением, полностью оборудованная кухня и две ванные комнаты. Максимальная скорость 32 узла позволяет быстро добраться до самых живописных мест Карельского перешейка.',
owner: null as any,
reviews: [],
reservations: [],
},
{
id: 2,
name: 'Сансикер Манхэттен 52',
length: 15.8,
speed: 34,
minCost: 92000,
mainImageUrl: 'api/uploads/1765728068963-634185622.webp',
galleryUrls: [
'api/uploads/1765728068963-634185622.webp',
'api/uploads/1765727637471-474231444.webp',
'api/uploads/1765727743466-187581713.webp',
'api/uploads/1765727819793-158598111.webp',
'api/uploads/1765727903961-559930753.webp',
'api/uploads/1765727928577-55944164.webp',
'api/uploads/1765727948691-389175954.webp',
'api/uploads/1765727961591-243584836.webp',
'api/uploads/1765727992064-37229339.webp',
],
hasQuickRent: false,
isFeatured: false,
topText: '🔥 Лучшее предложение',
year: 2023,
comfortCapacity: 6,
maxCapacity: 10,
width: 4.5,
cabinsCount: 3,
matherial: 'Стеклопластик',
power: 1400,
description:
'Британский шик и русская душа в одной яхте! Сансикер Манхэттен 52 - выбор настоящих ценителей морских путешествий. Просторный кокпит с мягкими диванами, бар на 8 персон, система мультимедиа премиум-класса. Идеальна для празднования дня рождения на воде или деловой встречи с партнерами. Отличная маневренность позволяет заходить в марины Санкт-Петербурга и Кронштадта.',
owner: null as any,
reviews: [],
reservations: [],
},
{
id: 3,
name: 'Принцесс V55',
length: 16.7,
speed: 33,
minCost: 78000,
mainImageUrl: 'api/uploads/1765728152179-305707681.webp',
galleryUrls: [
'api/uploads/1765728152179-305707681.webp',
'api/uploads/1765727637471-474231444.webp',
'api/uploads/1765727743466-187581713.webp',
'api/uploads/1765727819793-158598111.webp',
'api/uploads/1765727903961-559930753.webp',
'api/uploads/1765727928577-55944164.webp',
'api/uploads/1765727948691-389175954.webp',
'api/uploads/1765727961591-243584836.webp',
'api/uploads/1765727992064-37229339.webp',
],
hasQuickRent: true,
isFeatured: false,
topText: '🍷 Идеальна для заката с бокалом вина',
year: 2021,
comfortCapacity: 8,
maxCapacity: 12,
width: 4.7,
cabinsCount: 4,
matherial: 'Стеклопластик',
power: 1100,
description:
'Принцесс V55 - королева российских вод! Эта яхта создана для тех, кто ценит комфорт и элегантность. Четыре уютные каюты с кондиционером, гальюн с душем, полностью оборудованная камбузная зона. Особенность - огромный платц с гидравлическим трапом для купания в Ладожском озере. Отличный выбор для семейного отдыха или рыбалки с друзьями.',
owner: null as any,
reviews: [],
reservations: [],
},
{
id: 4,
name: 'Ферретти 500',
length: 15.2,
speed: 31,
minCost: 68000,
mainImageUrl: 'api/uploads/1765728169750-667674217.webp',
galleryUrls: [
'api/uploads/1765728169750-667674217.webp',
'api/uploads/1765727637471-474231444.webp',
'api/uploads/1765727743466-187581713.webp',
'api/uploads/1765727819793-158598111.webp',
'api/uploads/1765727903961-559930753.webp',
'api/uploads/1765727928577-55944164.webp',
'api/uploads/1765727948691-389175954.webp',
'api/uploads/1765727961591-243584836.webp',
'api/uploads/1765727992064-37229339.webp',
],
hasQuickRent: true,
isFeatured: false,
topText: '⏳ Часто бронируется - успей',
year: 2020,
comfortCapacity: 6,
maxCapacity: 8,
width: 4.3,
cabinsCount: 3,
matherial: 'Стеклопластик',
power: 900,
description:
'Итальянская страсть в русской стихии! Ферретти 500 сочетает в себе средиземноморский шарм и надежность для суровых условий Балтики. Просторный салон с панорамными окнами, обеденная зона на 6 человек, современная навигационная система. Идеально подходит для фотосессий на фоне разводных мостов Петербурга или романтического ужина под звуки волн.',
owner: null as any,
reviews: [],
reservations: [],
},
{
id: 5,
name: 'Си Рей 510 Сандансер',
length: 15.5,
speed: 35,
minCost: 72000,
mainImageUrl: 'api/uploads/1765728190017-763752836.webp',
galleryUrls: [
'api/uploads/1765728190017-763752836.webp',
'api/uploads/1765727637471-474231444.webp',
'api/uploads/1765727743466-187581713.webp',
'api/uploads/1765727819793-158598111.webp',
'api/uploads/1765727903961-559930753.webp',
'api/uploads/1765727928577-55944164.webp',
'api/uploads/1765727948691-389175954.webp',
'api/uploads/1765727961591-243584836.webp',
'api/uploads/1765727992064-37229339.webp',
],
hasQuickRent: false,
isFeatured: false,
year: 2023,
comfortCapacity: 8,
maxCapacity: 10,
width: 4.6,
cabinsCount: 3,
matherial: 'Стеклопластик',
power: 1300,
description:
'Американская мощь для русского моря! Си Рей 510 Сандансер - самая быстрая яхта в нашем флоте. Развивает скорость до 35 узлов, что позволяет за день обогнуть весь Финский залив. Три комфортабельные каюты, система стабилизации на стоянке, мощная аудиосистема с сабвуфером. Отличный выбор для любителей острых ощущений и скоростных прогулок.',
owner: null as any,
reviews: [],
reservations: [],
},
{
id: 6,
name: 'Бавария SR41',
length: 12.5,
speed: 28,
minCost: 45000,
mainImageUrl: 'api/uploads/1765728208376-854378188.webp',
galleryUrls: [
'api/uploads/1765728208376-854378188.webp',
'api/uploads/1765727637471-474231444.webp',
'api/uploads/1765727743466-187581713.webp',
'api/uploads/1765727819793-158598111.webp',
'api/uploads/1765727903961-559930753.webp',
'api/uploads/1765727928577-55944164.webp',
'api/uploads/1765727948691-389175954.webp',
'api/uploads/1765727961591-243584836.webp',
'api/uploads/1765727992064-37229339.webp',
],
hasQuickRent: true,
isFeatured: false,
year: 2019,
comfortCapacity: 6,
maxCapacity: 8,
width: 3.9,
cabinsCount: 2,
matherial: 'Стеклопластик',
power: 320,
description:
'Немецкое качество для русского характера! Бавария SR41 - надежная и экономичная яхта для спокойных прогулок по Ладоге. Две уютные каюты, просторный кокпит с тентом от дождя, лебедка для подъема парусов. Идеальный выбор для начинающих яхтсменов или семейного отдыха с детьми. Расход топлива всего 15 литров в час!',
owner: null as any,
reviews: [],
reservations: [],
},
{
id: 7,
name: 'Жанно Мери Фишер 895',
length: 8.9,
speed: 25,
minCost: 32000,
mainImageUrl: 'api/uploads/1765728227998-695635756.webp',
galleryUrls: [
'api/uploads/1765728227998-695635756.webp',
'api/uploads/1765727637471-474231444.webp',
'api/uploads/1765727743466-187581713.webp',
'api/uploads/1765727819793-158598111.webp',
'api/uploads/1765727903961-559930753.webp',
'api/uploads/1765727928577-55944164.webp',
'api/uploads/1765727948691-389175954.webp',
'api/uploads/1765727961591-243584836.webp',
'api/uploads/1765727992064-37229339.webp',
],
hasQuickRent: true,
isFeatured: false,
year: 2022,
comfortCapacity: 4,
maxCapacity: 6,
width: 3.0,
cabinsCount: 1,
matherial: 'Стеклопластик',
power: 250,
description:
'Французская элегантность для русского простора! Жанно Мери Фишер 895 - компактная, но вместительная яхта для рыбалки и пикников. Одна просторная каюта, открытый кокпит, столик для барбекю. Отлично подходит для выездов на природу, ночевки в бухтах или обучения детей управлению яхтой. Самый экономичный вариант в нашем флоте!',
owner: null as any,
reviews: [],
reservations: [],
},
{
id: 8,
name: 'Бенето Свифт Троулер 41',
length: 12.5,
speed: 22,
minCost: 55000,
mainImageUrl: 'api/uploads/1765728068963-634185622.webp',
galleryUrls: [
'api/uploads/1765728068963-634185622.webp',
'api/uploads/1765727637471-474231444.webp',
'api/uploads/1765727743466-187581713.webp',
'api/uploads/1765727819793-158598111.webp',
'api/uploads/1765727903961-559930753.webp',
'api/uploads/1765727928577-55944164.webp',
'api/uploads/1765727948691-389175954.webp',
'api/uploads/1765727961591-243584836.webp',
'api/uploads/1765727992064-37229339.webp',
],
hasQuickRent: false,
isFeatured: false,
year: 2021,
comfortCapacity: 6,
maxCapacity: 8,
width: 4.2,
cabinsCount: 2,
matherial: 'Стеклопластик',
power: 425,
description:
'Французский траулер для русского севера! Бенето Свифт Троулер 41 создан для длительных путешествий по Белому морю. Экономичный дизельный двигатель, большой запас топлива, система опреснения воды. Две комфортабельные каюты с подогревом пола. Идеальный выбор для экспедиций или многодневных круизов по северным островам.',
owner: null as any,
reviews: [],
reservations: [],
},
{
id: 9,
name: 'Лагун 450',
length: 13.5,
speed: 20,
minCost: 65000,
mainImageUrl: 'api/uploads/1765728152179-305707681.webp',
galleryUrls: [
'api/uploads/1765728152179-305707681.webp',
'api/uploads/1765727637471-474231444.webp',
'api/uploads/1765727743466-187581713.webp',
'api/uploads/1765727819793-158598111.webp',
'api/uploads/1765727903961-559930753.webp',
'api/uploads/1765727928577-55944164.webp',
'api/uploads/1765727948691-389175954.webp',
'api/uploads/1765727961591-243584836.webp',
'api/uploads/1765727992064-37229339.webp',
],
hasQuickRent: true,
isFeatured: false,
year: 2020,
comfortCapacity: 8,
maxCapacity: 10,
width: 7.8,
cabinsCount: 4,
matherial: 'Стеклопластик',
power: 90,
description:
'Французский катамаран для русского размаха! Лагун 450 - невероятно устойчивая и просторная яхта. Четыре отдельные каюты с санузлами, огромный салон-трансформер, две кухни. Идеально подходит для больших компаний, свадебных церемоний на воде или длительных круизов всей семьей. Не кренится даже в шторм!',
owner: null as any,
reviews: [],
reservations: [],
},
{
id: 10,
name: 'Фонтен Пажо Люсия 40',
length: 11.7,
speed: 18,
minCost: 58000,
mainImageUrl: 'api/uploads/1765728169750-667674217.webp',
galleryUrls: [
'api/uploads/1765728169750-667674217.webp',
'api/uploads/1765727637471-474231444.webp',
'api/uploads/1765727743466-187581713.webp',
'api/uploads/1765727819793-158598111.webp',
'api/uploads/1765727903961-559930753.webp',
'api/uploads/1765727928577-55944164.webp',
'api/uploads/1765727948691-389175954.webp',
'api/uploads/1765727961591-243584836.webp',
'api/uploads/1765727992064-37229339.webp',
],
hasQuickRent: true,
isFeatured: false,
year: 2023,
comfortCapacity: 8,
maxCapacity: 10,
width: 7.1,
cabinsCount: 4,
matherial: 'Стеклопластик',
power: 80,
description:
'Французский катамаран класса люкс! Фонтен Пажо Люсия 40 - это плавающий пятизвездочный отель. Четыре каюты-люкс с джакузи, салон с камином, профессиональная кухня с шеф-поваром. Система стабилизации на якоре, гидромассажный бассейн на палубе. Выбор настоящих ценителей роскоши и комфорта на воде.',
owner: null as any,
reviews: [],
reservations: [],
},
{
id: 11,
name: 'Дюфур 460',
length: 14.1,
speed: 26,
minCost: 62000,
mainImageUrl: 'api/uploads/1765728190017-763752836.webp',
galleryUrls: [
'api/uploads/1765728190017-763752836.webp',
'api/uploads/1765727637471-474231444.webp',
'api/uploads/1765727743466-187581713.webp',
'api/uploads/1765727819793-158598111.webp',
'api/uploads/1765727903961-559930753.webp',
'api/uploads/1765727928577-55944164.webp',
'api/uploads/1765727948691-389175954.webp',
'api/uploads/1765727961591-243584836.webp',
'api/uploads/1765727992064-37229339.webp',
],
hasQuickRent: false,
isFeatured: false,
year: 2022,
comfortCapacity: 8,
maxCapacity: 10,
width: 4.5,
cabinsCount: 3,
matherial: 'Стеклопластик',
power: 380,
description:
'Французская парусная яхта для русского ветра! Дюфур 460 - мечта любого яхтсмена. Три просторные каюты, кокпит с мягкими сиденьями, современное парусное вооружение. Идеально сбалансированная, легко управляется даже новичками. Отличный выбор для регат, обучения парусному спорту или романтических круизов под парусами.',
owner: null as any,
reviews: [],
reservations: [],
},
{
id: 12,
name: 'Гранд Бэнкс 60',
length: 18.3,
speed: 24,
minCost: 125000,
mainImageUrl: 'api/uploads/1765728208376-854378188.webp',
galleryUrls: [
'api/uploads/1765728208376-854378188.webp',
'api/uploads/1765727637471-474231444.webp',
'api/uploads/1765727743466-187581713.webp',
'api/uploads/1765727819793-158598111.webp',
'api/uploads/1765727903961-559930753.webp',
'api/uploads/1765727928577-55944164.webp',
'api/uploads/1765727948691-389175954.webp',
'api/uploads/1765727961591-243584836.webp',
'api/uploads/1765727992064-37229339.webp',
],
hasQuickRent: true,
isFeatured: false,
year: 2023,
comfortCapacity: 6,
maxCapacity: 8,
width: 5.2,
cabinsCount: 3,
matherial: 'Стеклопластик',
power: 1600,
description:
'Американская легенда для русского океана! Гранд Бэнкс 60 - экспедиционная яхта для самых смелых путешествий. Три каюты-люкс, салон с библиотекой, зимний сад, сауна. Автономность плавания - 30 дней! Способна пересечь Баренцево море и дойти до Шпицбергена. Выбор для настоящих морских волков и исследователей Арктики.',
owner: null as any,
reviews: [],
reservations: [],
},
];
private toShortDto(item: CatalogItemLongDto): CatalogItemShortDto {
const {
id,
name,
length,
speed,
minCost,
mainImageUrl,
galleryUrls,
hasQuickRent,
isFeatured,
topText,
} = item;
return {
id,
name,
length,
speed,
minCost,
mainImageUrl,
galleryUrls,
hasQuickRent,
isFeatured,
topText,
};
}
async getCatalogItemById(id: number): Promise<CatalogItemLongDto | null> {
const item = this.catalogItems.find((item) => item.id === id);
if (!item) return null;
const ownerId = [1, 2][(id - 1) % 2];
const owner = await this.usersService.findById(ownerId);
if (owner) {
item.owner = owner;
}
item.reviews = this.reviewsService.getReviewsByYachtId(id);
item.reservations = this.reservationsService.getReservationsByYachtId(id);
return item;
}
async getMainPageCatalog(): Promise<MainPageCatalogResponseDto | null> {
const clonedCatalog = [...this.catalogItems];
const filteredCatalog = clonedCatalog.filter(
({ isFeatured }) => !isFeatured,
);
const featuredYachtIndex = clonedCatalog.findIndex(
(item) => item.isFeatured === true,
);
if (featuredYachtIndex !== -1) {
const minCost = Math.min(
...filteredCatalog.map((item) => item.minCost || Infinity),
);
const mappedRestYachts = filteredCatalog.slice(0, 6).map((item) => ({
...this.toShortDto(item),
isBestOffer: item.minCost === minCost,
}));
return {
featuredYacht: this.toShortDto(clonedCatalog[featuredYachtIndex]),
restYachts: mappedRestYachts,
};
}
return null;
}
async getCatalog(params?: CatalogParamsDto): Promise<CatalogResponseDto> {
let filteredItems = this.catalogItems.map((item) => this.toShortDto(item));
if (params?.search) {
const searchTerm = params.search.toLowerCase();
filteredItems = filteredItems.filter((item) =>
item.name.toLowerCase().includes(searchTerm),
);
}
if (params?.filter?.price) {
const { minvalue, maxvalue } = params.filter.price;
filteredItems = filteredItems.filter((item) => {
if (minvalue !== undefined && item.minCost < minvalue) return false;
if (maxvalue !== undefined && item.minCost > maxvalue) return false;
return true;
});
}
if (params?.sort?.field) {
const { field, direction = 'asc' } = params.sort;
filteredItems.sort((a, b) => {
let valueA = a[field];
let valueB = b[field];
if (typeof valueA === 'string') valueA = valueA.toLowerCase();
if (typeof valueB === 'string') valueB = valueB.toLowerCase();
if (valueA < valueB) return direction === 'asc' ? -1 : 1;
if (valueA > valueB) return direction === 'asc' ? 1 : -1;
return 0;
});
}
return {
items: filteredItems,
total: filteredItems.length,
filters: params?.filter,
sort: params?.sort,
search: params?.search,
};
}
async getAllCatalogItems(): Promise<CatalogItemShortDto[]> {
return this.catalogItems.map((item) => this.toShortDto(item));
}
}