import { Body, Controller, Get, Param, Patch, Post, Query, UploadedFile, UseGuards, UseInterceptors } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { TripsService, RequestTripDto } from './trips.service'; import { TripAudioService } from './trip-audio.service'; import { TripStatus } from './entities/trip.entity'; import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; import { RolesGuard } from '../auth/guards/roles.guard'; import { Roles } from '../auth/decorators/roles.decorator'; import { CurrentUser, AuthUser } from '../auth/decorators/current-user.decorator'; @ApiTags('trips') @ApiBearerAuth() @UseGuards(JwtAuthGuard) @Controller('trips') export class TripsController { constructor( private readonly trips: TripsService, private readonly audio: TripAudioService, ) {} /** سرد رحلات المستأجر — للوحة الأدمن، بترشيح الحالة (docs/22 — N4). */ @UseGuards(RolesGuard) @Roles('admin', 'dispatcher') @Get('admin/list') adminList( @CurrentUser() user: AuthUser, @Query('status') status?: string, @Query('rider') rider?: string, ) { return this.trips.listByTenant(user.tenantId, status, rider); } // الراكب يطلب رحلة @Post() request(@CurrentUser() user: AuthUser, @Body() body: RequestTripDto) { return this.trips.request(user.tenantId, user.userId, body); } @Get('mine') mine(@CurrentUser() user: AuthUser) { return this.trips.listForRider(user.tenantId, user.userId); } // السائق يسحب الطلبات المتاحة قربه (بديل/مكمّل للعرض المباشر) @Get('available') available(@CurrentUser() user: AuthUser) { return this.trips.availableForDriver(user.tenantId, user.userId); } @Get(':id') get(@CurrentUser() user: AuthUser, @Param('id') id: string) { return this.trips.get(user.tenantId, id); } // السائق يقبل الرحلة @Post(':id/accept') accept(@CurrentUser() user: AuthUser, @Param('id') id: string) { return this.trips.accept(user.tenantId, id, user.userId); } // تحديث الحالة (driver_arriving / driver_arrived / in_progress / completed / paid) @Patch(':id/status') status( @CurrentUser() user: AuthUser, @Param('id') id: string, @Body('status') status: TripStatus, ) { const actor = user.role === 'driver' ? 'driver' : 'rider'; return this.trips.updateStatus(user.tenantId, id, status, actor); } @Post(':id/cancel') cancel(@CurrentUser() user: AuthUser, @Param('id') id: string) { const actor = user.role === 'driver' ? 'driver' : 'rider'; return this.trips.cancel(user.tenantId, id, actor, user.userId); } // ---- التسجيل الصوتي (R2) ---- /** رفع مقطع صوتي مرتبط بالرحلة. */ @Post(':id/audio') @UseInterceptors(FileInterceptor('file', { limits: { fileSize: 50 * 1024 * 1024 } })) uploadAudio( @CurrentUser() user: AuthUser, @Param('id') id: string, @UploadedFile() file: any, @Body('duration_sec') durationSec?: string, @Body('destination_name') destName?: string, ) { const role = user.role === 'driver' ? 'driver' as const : 'rider' as const; return this.audio.upload(user.tenantId, id, user.userId, role, file, { duration_sec: durationSec ? parseInt(durationSec, 10) : undefined, destination_name: destName, }); } /** كل المقاطع الصوتية للرحلة. */ @Get(':id/audio') listAudio(@CurrentUser() user: AuthUser, @Param('id') id: string) { return this.audio.listByTrip(user.tenantId, id); } /** رابط تحميل مقطع صوتي. */ @Get(':id/audio/:audioId') getAudioUrl( @CurrentUser() user: AuthUser, @Param('id') id: string, @Param('audioId') audioId: string, ) { return this.audio.getDownloadUrl(user.tenantId, audioId); } }