| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- import { DataSource, In, LessThanOrEqual, MoreThanOrEqual } from 'typeorm';
- import { GenericCrudService } from '@/server/utils/generic-crud.service';
- import { RouteEntity } from './route.entity';
- import { VehicleType } from './route.schema';
- import { ActivityEntity } from '@/server/modules/activities/activity.entity';
- import { LocationEntity } from '@/server/modules/locations/location.entity';
- export interface RouteSearchParams {
- startLocationId?: number;
- endLocationId?: number;
- startAreaIds?: number[]; // 省市区ID数组 [provinceId, cityId, districtId]
- endAreaIds?: number[]; // 省市区ID数组 [provinceId, cityId, districtId]
- date?: string;
- routeType?: 'departure' | 'return';
- minPrice?: number;
- maxPrice?: number;
- vehicleType?: VehicleType;
- sortBy?: 'price' | 'departureTime';
- sortOrder?: 'ASC' | 'DESC';
- page?: number;
- pageSize?: number;
- }
- export interface RouteSearchResult {
- routes: RouteEntity[];
- activities: ActivityEntity[];
- pagination: {
- page: number;
- pageSize: number;
- total: number;
- totalPages: number;
- };
- }
- export class RouteService extends GenericCrudService<RouteEntity> {
- constructor(dataSource: DataSource) {
- super(dataSource, RouteEntity, {
- userTracking: {
- createdByField: 'createdBy',
- updatedByField: 'updatedBy'
- }
- });
- }
- /**
- * 搜索路线
- */
- async searchRoutes(params: RouteSearchParams): Promise<RouteSearchResult> {
- const {
- startLocationId,
- endLocationId,
- startAreaIds,
- endAreaIds,
- date,
- routeType,
- minPrice,
- maxPrice,
- vehicleType,
- sortBy = 'departureTime',
- sortOrder = 'ASC',
- page = 1,
- pageSize = 20
- } = params;
- // 构建查询条件
- const where: any = {
- isDeleted: 0,
- isDisabled: 0
- };
- // 精确地点查询(优先)
- if (startLocationId) {
- where.startLocationId = startLocationId;
- }
- if (endLocationId) {
- where.endLocationId = endLocationId;
- }
- // 日期筛选
- if (date) {
- const targetDate = new Date(date);
- const nextDate = new Date(targetDate);
- nextDate.setDate(targetDate.getDate() + 1);
- where.departureTime = MoreThanOrEqual(targetDate);
- where.departureTime = LessThanOrEqual(nextDate);
- }
- // 价格筛选
- if (minPrice !== undefined) {
- where.price = MoreThanOrEqual(minPrice);
- }
- if (maxPrice !== undefined) {
- where.price = LessThanOrEqual(maxPrice);
- }
- // 车型筛选
- if (vehicleType) {
- where.vehicleType = vehicleType;
- }
- // 构建排序
- const order: any = {};
- if (sortBy === 'price') {
- order.price = sortOrder;
- } else {
- order.departureTime = sortOrder;
- }
- // 获取路线列表 - 直接使用查询构建器避免关联关系冲突
- const query = this.repository.createQueryBuilder('route')
- .leftJoinAndSelect('route.startLocation', 'startLocation')
- .leftJoinAndSelect('startLocation.province', 'startLocation_province')
- .leftJoinAndSelect('startLocation.city', 'startLocation_city')
- .leftJoinAndSelect('startLocation.district', 'startLocation_district')
- .leftJoinAndSelect('route.endLocation', 'endLocation')
- .leftJoinAndSelect('endLocation.province', 'endLocation_province')
- .leftJoinAndSelect('endLocation.city', 'endLocation_city')
- .leftJoinAndSelect('endLocation.district', 'endLocation_district')
- .leftJoinAndSelect('route.activity', 'activity')
- .where(where)
- .skip((page - 1) * pageSize)
- .take(pageSize);
- // 添加排序
- if (sortBy === 'price') {
- query.orderBy('route.price', sortOrder);
- } else {
- query.orderBy('route.departureTime', sortOrder);
- }
- const [routes, total] = await query.getManyAndCount();
- // 根据省市区筛选(如果提供了省市区ID)
- let filteredRoutes = routes;
- if (startAreaIds && startAreaIds.length > 0) {
- filteredRoutes = filteredRoutes.filter(route => {
- const location = route.startLocation;
- return this.matchAreaIds(location, startAreaIds);
- });
- }
- if (endAreaIds && endAreaIds.length > 0) {
- filteredRoutes = filteredRoutes.filter(route => {
- const location = route.endLocation;
- return this.matchAreaIds(location, endAreaIds);
- });
- }
- // 根据路线类型筛选
- if (routeType) {
- filteredRoutes = filteredRoutes.filter(route => route.routeType === routeType);
- }
- // 获取去重后的活动列表
- const activityIds = Array.from(new Set(filteredRoutes.map(route => route.activityId)));
- const activities = activityIds.length > 0
- ? await this.dataSource.getRepository(ActivityEntity).find({
- where: { id: In(activityIds) },
- relations: ['venueLocation', 'venueLocation.province', 'venueLocation.city', 'venueLocation.district']
- })
- : [];
- return {
- routes: filteredRoutes,
- activities,
- pagination: {
- page,
- pageSize,
- total: filteredRoutes.length,
- totalPages: Math.ceil(filteredRoutes.length / pageSize)
- }
- };
- }
- /**
- * 检查地点是否匹配省市区ID
- */
- private matchAreaIds(location: any, areaIds: number[]): boolean {
- // 这里需要根据实际的Location实体结构来匹配
- // 假设Location实体有provinceId, cityId, districtId字段
- if (!location) return false;
- const [provinceId, cityId, districtId] = areaIds;
- // 匹配省份
- if (provinceId && location.provinceId !== provinceId) {
- return false;
- }
- // 匹配城市
- if (cityId && location.cityId !== cityId) {
- return false;
- }
- // 匹配区县
- if (districtId && location.districtId !== districtId) {
- return false;
- }
- return true;
- }
- /**
- * 根据ID获取路线详情
- */
- async getRouteById(id: number): Promise<RouteEntity | null> {
- return this.getById(id, ['startLocation', 'endLocation', 'activity', 'activity.venueLocation']);
- }
- /**
- * 获取热门路线(按预订次数排序)
- */
- async getPopularRoutes(limit: number = 10): Promise<RouteEntity[]> {
- // 这里可以添加更复杂的逻辑,比如按预订次数排序
- // 目前先返回最新的路线
- const [routes] = await this.getList(
- 1,
- limit,
- undefined,
- undefined,
- { isDeleted: 0, isDisabled: 0 },
- ['startLocation', 'endLocation', 'activity'],
- { createdAt: 'DESC' }
- );
- return routes;
- }
- /**
- * 获取可用座位数
- */
- async getAvailableSeats(routeId: number): Promise<number> {
- const route = await this.getById(routeId);
- return route?.availableSeats || 0;
- }
- /**
- * 更新座位数
- */
- async updateSeats(routeId: number, bookedSeats: number): Promise<boolean> {
- const route = await this.getById(routeId);
- if (!route) {
- return false;
- }
- const newAvailableSeats = route.availableSeats - bookedSeats;
- if (newAvailableSeats < 0) {
- return false;
- }
- await this.repository.update(routeId, { availableSeats: newAvailableSeats });
- return true;
- }
- }
|