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 { constructor(dataSource: DataSource) { super(dataSource, RouteEntity, { userTracking: { createdByField: 'createdBy', updatedByField: 'updatedBy' } }); } /** * 搜索路线 */ async searchRoutes(params: RouteSearchParams): Promise { 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 { return this.getById(id, ['startLocation', 'endLocation', 'activity', 'activity.venueLocation']); } /** * 获取热门路线(按预订次数排序) */ async getPopularRoutes(limit: number = 10): Promise { // 这里可以添加更复杂的逻辑,比如按预订次数排序 // 目前先返回最新的路线 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 { const route = await this.getById(routeId); return route?.availableSeats || 0; } /** * 更新座位数 */ async updateSeats(routeId: number, bookedSeats: number): Promise { 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; } }