308 lines
9.7 KiB
TypeScript
308 lines
9.7 KiB
TypeScript
import { Injectable, Inject, forwardRef } from '@nestjs/common';
|
|
import { PrismaService } from '../prisma/prisma.service';
|
|
import { ReservationsService } from '../reservations/reservations.service';
|
|
import { ReviewsService } from '../reviews/reviews.service';
|
|
import {
|
|
CatalogItemShortDto,
|
|
CatalogItemLongDto,
|
|
} from './dto/catalog-item.dto';
|
|
import { CatalogResponseDto } from './dto/catalog-response.dto';
|
|
import { MainPageCatalogResponseDto } from './dto/main-page-catalog-response.dto';
|
|
import { CatalogFiltersDto } from './dto/catalog-filters.dto';
|
|
import { CreateYachtDto } from './dto/create-yacht.dto';
|
|
|
|
@Injectable()
|
|
export class CatalogService {
|
|
constructor(
|
|
private readonly prisma: PrismaService,
|
|
@Inject(forwardRef(() => ReservationsService))
|
|
private readonly reservationsService: ReservationsService,
|
|
private readonly reviewsService: ReviewsService,
|
|
) {}
|
|
|
|
private yachtRowToLongDto(row: {
|
|
id: number;
|
|
name: string;
|
|
length: number;
|
|
speed: number | null;
|
|
minCost: number | null;
|
|
mainImageUrl: string | null;
|
|
galleryUrls: unknown;
|
|
hasQuickRent: boolean;
|
|
isFeatured: boolean;
|
|
topText: string | null;
|
|
year: number;
|
|
comfortCapacity: number | null;
|
|
maxCapacity: number | null;
|
|
width: number | null;
|
|
cabinsCount: number | null;
|
|
matherial: string | null;
|
|
power: number | null;
|
|
description: string | null;
|
|
userId: number;
|
|
owner?: { id: number; firstName: string; lastName: string; phone: string; email: string; companyName: string | null; inn: bigint | null; ogrn: bigint | null };
|
|
}): CatalogItemLongDto {
|
|
const galleryUrls = Array.isArray(row.galleryUrls)
|
|
? row.galleryUrls
|
|
: typeof row.galleryUrls === 'string'
|
|
? (JSON.parse(row.galleryUrls) as string[])
|
|
: [];
|
|
return {
|
|
id: row.id,
|
|
name: row.name,
|
|
length: row.length,
|
|
speed: row.speed ?? 0,
|
|
minCost: row.minCost ?? 0,
|
|
mainImageUrl: row.mainImageUrl ?? '',
|
|
galleryUrls: galleryUrls as string[],
|
|
hasQuickRent: row.hasQuickRent,
|
|
isFeatured: row.isFeatured,
|
|
topText: row.topText ?? undefined,
|
|
year: row.year,
|
|
comfortCapacity: row.comfortCapacity ?? 0,
|
|
maxCapacity: row.maxCapacity ?? 0,
|
|
width: row.width ?? 0,
|
|
cabinsCount: row.cabinsCount ?? 0,
|
|
matherial: row.matherial ?? '',
|
|
power: row.power ?? 0,
|
|
description: row.description ?? '',
|
|
owner: row.owner
|
|
? {
|
|
userId: row.owner.id,
|
|
firstName: row.owner.firstName,
|
|
lastName: row.owner.lastName,
|
|
phone: row.owner.phone,
|
|
email: row.owner.email,
|
|
companyName: row.owner.companyName ?? undefined,
|
|
inn: row.owner.inn != null ? Number(row.owner.inn) : undefined,
|
|
ogrn: row.owner.ogrn != null ? Number(row.owner.ogrn) : undefined,
|
|
}
|
|
: ({ userId: row.userId } 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 yacht = await this.prisma.yacht.findUnique({
|
|
where: { id },
|
|
include: { owner: true },
|
|
});
|
|
if (!yacht) return null;
|
|
|
|
const item = this.yachtRowToLongDto(yacht);
|
|
item.reviews = await this.reviewsService.getReviewsByYachtId(id);
|
|
item.reservations = await this.reservationsService.getReservationsByYachtId(id);
|
|
return item;
|
|
}
|
|
|
|
async getMainPageCatalog(): Promise<MainPageCatalogResponseDto | null> {
|
|
const allYachts = await this.prisma.yacht.findMany({
|
|
include: { owner: true },
|
|
orderBy: { id: 'asc' },
|
|
});
|
|
|
|
const fullItems = allYachts.map((row) => this.yachtRowToLongDto(row));
|
|
const featuredYacht = fullItems.find((item) => item.isFeatured);
|
|
const filteredCatalog = fullItems.filter((item) => !item.isFeatured);
|
|
|
|
if (!featuredYacht) return null;
|
|
|
|
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(featuredYacht),
|
|
restYachts: mappedRestYachts,
|
|
};
|
|
}
|
|
|
|
async getCatalog(filters?: CatalogFiltersDto): Promise<CatalogResponseDto> {
|
|
const allYachts = await this.prisma.yacht.findMany({
|
|
include: { owner: true },
|
|
orderBy: { id: 'asc' },
|
|
});
|
|
|
|
let filteredItems = allYachts.map((row) =>
|
|
this.toShortDto(this.yachtRowToLongDto(row)),
|
|
);
|
|
const fullItems = allYachts.map((row) => this.yachtRowToLongDto(row));
|
|
|
|
if (filters?.search) {
|
|
const searchTerm = filters.search.toLowerCase();
|
|
filteredItems = filteredItems.filter((item) => {
|
|
const fullItem = fullItems.find((fi) => fi.id === item.id);
|
|
return (
|
|
item.name.toLowerCase().includes(searchTerm) ||
|
|
(fullItem &&
|
|
fullItem.description.toLowerCase().includes(searchTerm))
|
|
);
|
|
});
|
|
}
|
|
|
|
if (filters?.minLength !== undefined) {
|
|
filteredItems = filteredItems.filter(
|
|
(item) => item.length >= filters!.minLength!,
|
|
);
|
|
}
|
|
if (filters?.maxLength !== undefined) {
|
|
filteredItems = filteredItems.filter(
|
|
(item) => item.length <= filters!.maxLength!,
|
|
);
|
|
}
|
|
|
|
if (filters?.minPrice !== undefined) {
|
|
filteredItems = filteredItems.filter(
|
|
(item) => item.minCost >= filters!.minPrice!,
|
|
);
|
|
}
|
|
if (filters?.maxPrice !== undefined) {
|
|
filteredItems = filteredItems.filter(
|
|
(item) => item.minCost <= filters!.maxPrice!,
|
|
);
|
|
}
|
|
|
|
if (filters?.minYear !== undefined || filters?.maxYear !== undefined) {
|
|
filteredItems = filteredItems.filter((item) => {
|
|
const fullItem = fullItems.find((fi) => fi.id === item.id);
|
|
if (!fullItem) return false;
|
|
if (
|
|
filters!.minYear !== undefined &&
|
|
fullItem.year < filters!.minYear!
|
|
)
|
|
return false;
|
|
if (
|
|
filters!.maxYear !== undefined &&
|
|
fullItem.year > filters!.maxYear!
|
|
)
|
|
return false;
|
|
return true;
|
|
});
|
|
}
|
|
|
|
if (filters?.guests !== undefined) {
|
|
const totalPeople = filters.guests;
|
|
filteredItems = filteredItems.filter((item) => {
|
|
const fullItem = fullItems.find((fi) => fi.id === item.id);
|
|
return fullItem != null && fullItem.maxCapacity >= totalPeople;
|
|
});
|
|
}
|
|
|
|
if (filters?.quickBooking !== undefined) {
|
|
filteredItems = filteredItems.filter(
|
|
(item) => item.hasQuickRent === filters.quickBooking,
|
|
);
|
|
}
|
|
|
|
if (filters?.hasToilet !== undefined) {
|
|
filteredItems = filteredItems.filter((item) => {
|
|
const fullItem = fullItems.find((fi) => fi.id === item.id);
|
|
if (!fullItem) return false;
|
|
return filters!.hasToilet
|
|
? fullItem.cabinsCount > 0
|
|
: fullItem.cabinsCount === 0;
|
|
});
|
|
}
|
|
|
|
if (filters?.sortByPrice === 'asc') {
|
|
filteredItems.sort((a, b) => a.minCost - b.minCost);
|
|
} else if (filters?.sortByPrice === 'desc') {
|
|
filteredItems.sort((a, b) => b.minCost - a.minCost);
|
|
} else {
|
|
filteredItems.sort((a, b) => a.name.localeCompare(b.name));
|
|
}
|
|
|
|
return {
|
|
items: filteredItems,
|
|
total: filteredItems.length,
|
|
filters,
|
|
search: filters?.search,
|
|
};
|
|
}
|
|
|
|
async getAllCatalogItems(): Promise<CatalogItemShortDto[]> {
|
|
const allYachts = await this.prisma.yacht.findMany({
|
|
orderBy: { id: 'asc' },
|
|
});
|
|
return allYachts.map((row) =>
|
|
this.toShortDto(this.yachtRowToLongDto(row)),
|
|
);
|
|
}
|
|
|
|
async getCatalogByUserId(userId: number): Promise<CatalogItemShortDto[]> {
|
|
const yachts = await this.prisma.yacht.findMany({
|
|
where: { userId },
|
|
orderBy: { id: 'asc' },
|
|
});
|
|
return yachts.map((row) =>
|
|
this.toShortDto(this.yachtRowToLongDto(row)),
|
|
);
|
|
}
|
|
|
|
async createYacht(
|
|
createYachtDto: CreateYachtDto,
|
|
): Promise<CatalogItemLongDto> {
|
|
const galleryUrls = createYachtDto.galleryUrls ?? [];
|
|
const created = await this.prisma.yacht.create({
|
|
data: {
|
|
name: createYachtDto.name,
|
|
year: createYachtDto.year,
|
|
length: createYachtDto.length,
|
|
speed: createYachtDto.speed,
|
|
minCost: createYachtDto.minCost,
|
|
mainImageUrl: createYachtDto.mainImageUrl,
|
|
galleryUrls: galleryUrls as object,
|
|
hasQuickRent: createYachtDto.hasQuickRent,
|
|
isFeatured: createYachtDto.isFeatured,
|
|
topText: createYachtDto.topText,
|
|
comfortCapacity: createYachtDto.comfortCapacity,
|
|
maxCapacity: createYachtDto.maxCapacity,
|
|
width: createYachtDto.width,
|
|
cabinsCount: createYachtDto.cabinsCount,
|
|
matherial: createYachtDto.matherial,
|
|
power: createYachtDto.power,
|
|
description: createYachtDto.description,
|
|
userId: createYachtDto.userId,
|
|
},
|
|
include: { owner: true },
|
|
});
|
|
|
|
const item = this.yachtRowToLongDto(created);
|
|
item.reviews = [];
|
|
item.reservations = [];
|
|
return item;
|
|
}
|
|
}
|