import { BadRequestException } from '@nestjs/common'; /** * Engine tính toán chi phí dựa trên công thức trong ARCHITECTURE.md * T = N_adult * X + N_child * (X * (1 - P_child_discount)) */ export class ExpenseEngine { /** * Tính toán phân chia chi phí giữa người lớn và trẻ em * @param totalCost Tổng chi phí (T) * @param adultCount Số lượng người lớn (N_adult) * @param childCount Số lượng trẻ em (N_child) * @param childDiscountPercent Phần trăm giảm giá trẻ em (P_child_discount, ví dụ: 30) */ calculateSplit( totalCost: number, adultCount: number, childCount: number, childDiscountPercent: number ) { // 1. Validation: Kiểm tra dữ liệu đầu vào âm if (totalCost < 0 || adultCount < 0 || childCount < 0 || childDiscountPercent < 0) { throw new BadRequestException('Dữ liệu đầu vào không được là số âm.'); } if (childDiscountPercent > 100) { throw new BadRequestException('Phần trăm giảm giá không thể vượt quá 100%.'); } // 2. Handle division by zero: Không có người tham gia if (adultCount + childCount === 0) { throw new BadRequestException('Số lượng người tham gia phải lớn hơn 0.'); } const discountRate = childDiscountPercent / 100; const childRateFactor = 1 - discountRate; /** * Giải phương trình: * T = X * N_adult + X * (1 - P) * N_child * T = X * (N_adult + N_child * (1 - P)) * => X = T / (N_adult + N_child * (1 - P)) */ const weightedParticipantCount = adultCount + (childCount * childRateFactor); const adultPriceRaw = totalCost / weightedParticipantCount; const childPriceRaw = adultPriceRaw * childRateFactor; // Làm tròn để thực tế thanh toán (ví dụ: làm tròn đến hàng đơn vị) const perAdult = Math.round(adultPriceRaw); const perChild = Math.round(childPriceRaw); const adultsTotal = perAdult * adultCount; const childrenTotal = perChild * childCount; const actualSum = adultsTotal + childrenTotal; return { targetTotal: totalCost, actualTotal: actualSum, perAdult, perChild, roundingDifference: totalCost - actualSum, // Chênh lệch do làm tròn (nếu có) adultsTotal, childrenTotal, }; } }