fix: sửa tính năng hiển thị quãng đường tại điểm đến kế tiếp
This commit is contained in:
+112
-11
@@ -178,6 +178,11 @@ export class TourRoleGuard implements CanActivate {
|
||||
if (!rolesToCheck.some(r => role === r)) {
|
||||
throw new ForbiddenException('Bạn không có quyền thực hiện hành động này trong tour này.');
|
||||
}
|
||||
|
||||
// Ensure tourId is attached to the request object for controllers to use
|
||||
if (tourId) {
|
||||
(request as any).tourId = tourId;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -350,6 +355,7 @@ class TourController {
|
||||
if (!leg) throw new NotFoundException('Không tìm thấy chặng phù hợp để thêm địa điểm');
|
||||
|
||||
return this.prisma.location.create({
|
||||
// ...
|
||||
data: {
|
||||
name: body.name,
|
||||
address: body.address,
|
||||
@@ -374,7 +380,13 @@ class TourController {
|
||||
}
|
||||
});
|
||||
}
|
||||
return loc;
|
||||
// Xóa triệt để các loại cache của Tour (cả key UUID và key URL của Interceptor)
|
||||
await Promise.all([
|
||||
this.cacheManager.del(tourId),
|
||||
this.cacheManager.del(`/api/v1/tours/${tourId}`),
|
||||
this.cacheManager.del(`/api/v1/tours/${tourId}/public`),
|
||||
]);
|
||||
return loc; // Return the created location
|
||||
});
|
||||
}
|
||||
|
||||
@@ -413,6 +425,13 @@ class TourController {
|
||||
legId: firstLeg.id,
|
||||
plannedStart: new Date(0),
|
||||
}
|
||||
}).then(async (loc) => {
|
||||
await Promise.all([
|
||||
this.cacheManager.del(tourId),
|
||||
this.cacheManager.del(`/api/v1/tours/${tourId}`),
|
||||
this.cacheManager.del(`/api/v1/tours/${tourId}/public`),
|
||||
]);
|
||||
return loc;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -449,6 +468,13 @@ class TourController {
|
||||
legId: lastLeg.id,
|
||||
plannedEnd: new Date(0), // Đánh dấu đây là điểm kết thúc đặc biệt
|
||||
}
|
||||
}).then(async (loc) => {
|
||||
await Promise.all([
|
||||
this.cacheManager.del(tourId),
|
||||
this.cacheManager.del(`/api/v1/tours/${tourId}`),
|
||||
this.cacheManager.del(`/api/v1/tours/${tourId}/public`),
|
||||
]);
|
||||
return loc;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -495,7 +521,13 @@ class TourController {
|
||||
});
|
||||
}
|
||||
|
||||
return allLegs;
|
||||
// Invalidate cache for the tour after initializing legs
|
||||
await Promise.all([
|
||||
this.cacheManager.del(tourId),
|
||||
this.cacheManager.del(`/api/v1/tours/${tourId}`),
|
||||
this.cacheManager.del(`/api/v1/tours/${tourId}/public`),
|
||||
]);
|
||||
return allLegs; // Return all legs
|
||||
}
|
||||
|
||||
@Roles(ParticipantRole.OWNER, ParticipantRole.MANAGER) // Only owner/manager can add legs
|
||||
@@ -514,6 +546,13 @@ class TourController {
|
||||
sequence: tour.legs.length + 1,
|
||||
note: body.note || `Chặng ${tour.legs.length + 1}`
|
||||
}
|
||||
}).then(async (leg) => {
|
||||
await Promise.all([
|
||||
this.cacheManager.del(tourId),
|
||||
this.cacheManager.del(`/api/v1/tours/${tourId}`),
|
||||
this.cacheManager.del(`/api/v1/tours/${tourId}/public`),
|
||||
]);
|
||||
return leg;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -534,6 +573,13 @@ class TourController {
|
||||
childCount: body.childCount !== undefined ? Number(body.childCount) : undefined,
|
||||
childDiscount: body.childDiscount !== undefined ? Number(body.childDiscount) : undefined,
|
||||
},
|
||||
}).then(async (tour) => {
|
||||
await Promise.all([
|
||||
this.cacheManager.del(id),
|
||||
this.cacheManager.del(`/api/v1/tours/${id}`),
|
||||
this.cacheManager.del(`/api/v1/tours/${id}/public`),
|
||||
]);
|
||||
return tour;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -938,10 +984,11 @@ class TourController {
|
||||
@Controller('locations')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
class LocationController {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
constructor(private prisma: PrismaService, @Inject(CACHE_MANAGER) private cacheManager: Cache) {}
|
||||
|
||||
@Patch(':id')
|
||||
async updateLocation(@Param('id', ParseUUIDPipe) id: string, @Body() body: any) {
|
||||
@UseGuards(TourRoleGuard) // Apply TourRoleGuard to get tourId
|
||||
async updateLocation(@Param('id', ParseUUIDPipe) id: string, @Body() body: any, @Req() req: any) {
|
||||
const { expenseAmount, expenseCategory, ...data } = body;
|
||||
|
||||
const location = await this.prisma.location.update({
|
||||
@@ -982,12 +1029,35 @@ class LocationController {
|
||||
}
|
||||
}
|
||||
|
||||
if (req.tourId) {
|
||||
await Promise.all([
|
||||
this.cacheManager.del(req.tourId),
|
||||
this.cacheManager.del(`/api/v1/tours/${req.tourId}`),
|
||||
this.cacheManager.del(`/api/v1/tours/${req.tourId}/public`),
|
||||
]);
|
||||
}
|
||||
return location;
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
async deleteLocation(@Param('id', ParseUUIDPipe) id: string) {
|
||||
await this.prisma.location.delete({ where: { id } });
|
||||
@UseGuards(TourRoleGuard) // Apply TourRoleGuard to get tourId
|
||||
async deleteLocation(@Param('id', ParseUUIDPipe) id: string, @Req() req: any) {
|
||||
try {
|
||||
await this.prisma.location.delete({ where: { id } });
|
||||
} catch (e) {
|
||||
// Nếu bản ghi đã bị xóa trước đó, không ném lỗi 500 để đảm bảo tính an toàn (idempotency)
|
||||
}
|
||||
|
||||
// Xóa mapping cache để Guard không bị đánh lừa ở lần truy cập sau
|
||||
await this.cacheManager.del(`res-to-tour:${id}`);
|
||||
|
||||
if (req.tourId) {
|
||||
await Promise.all([
|
||||
this.cacheManager.del(req.tourId),
|
||||
this.cacheManager.del(`/api/v1/tours/${req.tourId}`),
|
||||
this.cacheManager.del(`/api/v1/tours/${req.tourId}/public`),
|
||||
]);
|
||||
}
|
||||
return { success: true };
|
||||
}
|
||||
}
|
||||
@@ -996,10 +1066,11 @@ class LocationController {
|
||||
@Roles(ParticipantRole.OWNER, ParticipantRole.MANAGER) // Default roles for Leg operations
|
||||
@UseGuards(JwtAuthGuard, TourRoleGuard)
|
||||
class LegController {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
constructor(private prisma: PrismaService, @Inject(CACHE_MANAGER) private cacheManager: Cache) {}
|
||||
|
||||
@Patch(':id')
|
||||
async updateLeg(@Param('id', ParseUUIDPipe) id: string, @Body() body: any) {
|
||||
@UseGuards(TourRoleGuard) // Apply TourRoleGuard to get tourId
|
||||
async updateLeg(@Param('id', ParseUUIDPipe) id: string, @Body() body: any, @Req() req: any) {
|
||||
return this.prisma.leg.update({
|
||||
where: { id },
|
||||
data: {
|
||||
@@ -1009,10 +1080,18 @@ class LegController {
|
||||
endDate: body.endDate ? new Date(body.endDate) : undefined,
|
||||
description: body.description,
|
||||
}
|
||||
}).then(async (leg) => {
|
||||
await Promise.all([
|
||||
this.cacheManager.del(req.tourId),
|
||||
this.cacheManager.del(`/api/v1/tours/${req.tourId}`),
|
||||
this.cacheManager.del(`/api/v1/tours/${req.tourId}/public`),
|
||||
]);
|
||||
return leg;
|
||||
});
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@UseGuards(TourRoleGuard) // Apply TourRoleGuard to get tourId
|
||||
async deleteLeg(@Param('id', ParseUUIDPipe) id: string) {
|
||||
const leg = await this.prisma.leg.findUnique({
|
||||
where: { id },
|
||||
@@ -1023,7 +1102,21 @@ class LegController {
|
||||
throw new BadRequestException('Không thể xóa chặng đang có địa điểm. Hãy xóa địa điểm trước.');
|
||||
}
|
||||
|
||||
await this.prisma.leg.delete({ where: { id } });
|
||||
try {
|
||||
const deletedLeg = await this.prisma.leg.delete({ where: { id } });
|
||||
await this.cacheManager.del(`res-to-tour:${id}`);
|
||||
|
||||
if (deletedLeg.tourId) {
|
||||
await Promise.all([
|
||||
this.cacheManager.del(deletedLeg.tourId),
|
||||
this.cacheManager.del(`/api/v1/tours/${deletedLeg.tourId}`),
|
||||
this.cacheManager.del(`/api/v1/tours/${deletedLeg.tourId}/public`),
|
||||
]);
|
||||
}
|
||||
} catch (e) {
|
||||
// Idempotency
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
}
|
||||
@@ -1044,10 +1137,11 @@ function calculateDistance(lat1: number, lon1: number, lat2: number, lon2: numbe
|
||||
@Roles(ParticipantRole.OWNER, ParticipantRole.MANAGER) // Default roles for Routing operations
|
||||
@UseGuards(JwtAuthGuard, TourRoleGuard)
|
||||
class RoutingController {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
constructor(private prisma: PrismaService, @Inject(CACHE_MANAGER) private cacheManager: Cache) {}
|
||||
|
||||
@Post('optimize/:legId')
|
||||
async optimize(@Param('legId', ParseUUIDPipe) legId: string) {
|
||||
@UseGuards(TourRoleGuard) // Apply TourRoleGuard to get tourId
|
||||
async optimize(@Param('legId', ParseUUIDPipe) legId: string, @Req() req: any) {
|
||||
const currentLeg = await this.prisma.leg.findUnique({
|
||||
where: { id: legId },
|
||||
});
|
||||
@@ -1148,6 +1242,13 @@ class RoutingController {
|
||||
orderBy: { plannedStart: 'asc' }
|
||||
});
|
||||
|
||||
if (req.tourId) {
|
||||
await Promise.all([
|
||||
this.cacheManager.del(req.tourId),
|
||||
this.cacheManager.del(`/api/v1/tours/${req.tourId}`),
|
||||
this.cacheManager.del(`/api/v1/tours/${req.tourId}/public`),
|
||||
]);
|
||||
}
|
||||
return {
|
||||
locations: updatedLocations,
|
||||
totalDistance: parseFloat(totalDistance.toFixed(2))
|
||||
|
||||
Reference in New Issue
Block a user