Thêm tính năng trong mục 4.1 của ARCHITECTURE.md
This commit is contained in:
@@ -121,6 +121,85 @@ class TourController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper tính khoảng cách giữa 2 tọa độ (Haversine formula)
|
||||
*/
|
||||
function calculateDistance(lat1: number, lon1: number, lat2: number, lon2: number) {
|
||||
const p = 0.017453292519943295; // Math.PI / 180
|
||||
const c = Math.cos;
|
||||
const a = 0.5 - c((lat2 - lat1) * p) / 2 +
|
||||
c(lat1 * p) * c(lat2 * p) *
|
||||
(1 - c((lon2 - lon1) * p)) / 2;
|
||||
return 12742 * Math.asin(Math.sqrt(a)); // 2 * R; R = 6371 km
|
||||
}
|
||||
|
||||
@Controller('v1/routing')
|
||||
class RoutingController {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
@Post('optimize/:legId')
|
||||
async optimize(@Param('legId', ParseUUIDPipe) legId: string) {
|
||||
const locations = await this.prisma.location.findMany({
|
||||
where: { legId },
|
||||
});
|
||||
|
||||
if (locations.length <= 2) return locations;
|
||||
|
||||
// Thuật toán Greedy TSP đơn giản để tối ưu hóa lộ trình
|
||||
const optimized = [];
|
||||
const unvisited = [...locations];
|
||||
|
||||
// Bắt đầu với địa điểm có thời gian dự kiến sớm nhất hiện tại
|
||||
let current = unvisited.sort((a, b) =>
|
||||
(a.plannedStart?.getTime() || 0) - (b.plannedStart?.getTime() || 0)
|
||||
).shift()!;
|
||||
optimized.push(current);
|
||||
|
||||
while (unvisited.length > 0) {
|
||||
let nearestIdx = 0;
|
||||
let minDist = Infinity;
|
||||
|
||||
for (let i = 0; i < unvisited.length; i++) {
|
||||
const d = calculateDistance(current.latitude, current.longitude, unvisited[i].latitude, unvisited[i].longitude);
|
||||
if (d < minDist) {
|
||||
minDist = d;
|
||||
nearestIdx = i;
|
||||
}
|
||||
}
|
||||
current = unvisited.splice(nearestIdx, 1)[0];
|
||||
optimized.push(current);
|
||||
}
|
||||
|
||||
// Tính toán tổng quãng đường di chuyển của chặng (km)
|
||||
let totalDistance = 0;
|
||||
for (let i = 0; i < optimized.length - 1; i++) {
|
||||
totalDistance += calculateDistance(
|
||||
optimized[i].latitude, optimized[i].longitude,
|
||||
optimized[i+1].latitude, optimized[i+1].longitude
|
||||
);
|
||||
}
|
||||
|
||||
// Cập nhật lại thời gian plannedStart trong DB để phản ánh thứ tự mới (mỗi điểm cách nhau 1 giờ giả định)
|
||||
const baseTime = optimized[0].plannedStart || new Date();
|
||||
await Promise.all(optimized.map((loc, index) =>
|
||||
this.prisma.location.update({
|
||||
where: { id: loc.id },
|
||||
data: { plannedStart: new Date(baseTime.getTime() + index * 3600000) },
|
||||
})
|
||||
));
|
||||
|
||||
const updatedLocations = await this.prisma.location.findMany({
|
||||
where: { legId },
|
||||
orderBy: { plannedStart: 'asc' }
|
||||
});
|
||||
|
||||
return {
|
||||
locations: updatedLocations,
|
||||
totalDistance: parseFloat(totalDistance.toFixed(2))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Controller('v1/users')
|
||||
@UseGuards(JwtAuthGuard, AdminGuard)
|
||||
class UserController {
|
||||
@@ -177,7 +256,7 @@ class UserController {
|
||||
signOptions: { expiresIn: '1d' },
|
||||
}),
|
||||
],
|
||||
controllers: [AppController, AuthController, TourController, UserController],
|
||||
controllers: [AppController, AuthController, TourController, UserController, RoutingController],
|
||||
providers: [PrismaService, JwtStrategy],
|
||||
exports: [PrismaService]
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user