/** * 价格计算工具类 * 根据当前地址、日期、宠物类型等因素计算价格,保留2位小数 */ class PriceCalculator { constructor(priceConfig) { this.priceConfig = priceConfig || this.getDefaultPriceConfig(); } // 获取默认价格配置 getDefaultPriceConfig() { return { basePrice: { normal: 75, holiday: 85, weekend: 80 }, memberDiscount: { 'new': 0.95, 'regular': 0.9, 'silver': 0.88, 'gold': 0.85 }, preFamiliarize: { price: 40, holidayRate: 1.2 }, multiService: { two: { price: 45, holidayRate: 1.1 }, three: { price: 130, holidayRate: 1.1 } }, petExtra: { largeDog: { price: 40, holidayRate: 1.1 }, mediumDog: { price: 30, holidayRate: 1.1 }, smallDog: { price: 15, holidayRate: 1.1 }, cat: { price: 10, holidayRate: 1.1 } }, freeQuota: { threshold: 30, rules: [ { type: 'cat', count: 3, freeAmount: 30, description: '3只及以上猫免费30元' }, { type: 'smallDog', count: 2, freeAmount: 30, description: '2只及以上小型犬免费30元' }, { type: 'mediumDog', count: 1, freeAmount: 30, description: '1只及以上中型犬免费30元' }, { type: 'mixed', count: 0, freeAmount: 25, description: '混合类型免费25元(1猫1小型犬)' } ] }, holidays: [ '2024-07-15', '2024-07-16', '2024-07-17', '2024-10-01', '2024-10-02', '2024-10-03' ], weekends: [0, 6], customServices: { priceConfig: {}, holidayRate: 1.1 }, cityConfig: { priceRates: { 'beijing': 1.2, 'shanghai': 1.15, 'guangzhou': 1.1, 'shenzhen': 1.15, 'default': 1.0 } } }; } // 保留2位小数 roundToTwoDecimals(value) { return Math.round(value * 100) / 100; } // 根据地址获取城市代码 getCityCode(address) { if (!address) return 'default'; const cityMapping = { '北京': 'beijing', '上海': 'shanghai', '广州': 'guangzhou', '深圳': 'shenzhen' }; for (const [cityName, cityCode] of Object.entries(cityMapping)) { if (address.includes(cityName)) { return cityCode; } } return 'default'; } // 获取城市价格倍率 getCityPriceRate(address) { const cityCode = this.getCityCode(address); return this.priceConfig.cityConfig.priceRates[cityCode] || 1.0; } // 判断日期类型 getDatePriceType(dateString) { const date = new Date(dateString); const dayOfWeek = date.getDay(); if (this.priceConfig.holidays.includes(dateString)) { return 'holiday'; } if (this.priceConfig.weekends.includes(dayOfWeek)) { return 'weekend'; } return 'normal'; } // 获取基础服务价格 getBasePriceByDate(dateString) { const priceType = this.getDatePriceType(dateString); return this.priceConfig.basePrice[priceType] || this.priceConfig.basePrice.normal; } // 获取宠物额外费用 getPetExtraCost(pet, dateString) { const priceType = this.getDatePriceType(dateString); let baseCost = 0; let holidayRate = 1; if (pet.petType === 'cat') { baseCost = this.priceConfig.petExtra.cat.price; holidayRate = this.priceConfig.petExtra.cat.holidayRate; } else if (pet.petType === 'dog' && pet.bodyType.includes('小型')) { baseCost = this.priceConfig.petExtra.smallDog.price; holidayRate = this.priceConfig.petExtra.smallDog.holidayRate; } else if (pet.petType === 'dog' && pet.bodyType.includes('中型')) { baseCost = this.priceConfig.petExtra.mediumDog.price; holidayRate = this.priceConfig.petExtra.mediumDog.holidayRate; } else if (pet.petType === 'dog' && pet.bodyType.includes('大型')) { baseCost = this.priceConfig.petExtra.largeDog.price; holidayRate = this.priceConfig.petExtra.largeDog.holidayRate; } if (priceType === 'holiday') { baseCost = baseCost * holidayRate; } return this.roundToTwoDecimals(baseCost); } // 获取多次服务费用 getMultiServiceCost(feedCount, dateString) { const priceType = this.getDatePriceType(dateString); let baseCost = 0; let holidayRate = 1; if (feedCount === 2) { baseCost = this.priceConfig.multiService.two.price; holidayRate = this.priceConfig.multiService.two.holidayRate; } else if (feedCount === 3) { baseCost = this.priceConfig.multiService.three.price; holidayRate = this.priceConfig.multiService.three.holidayRate; } if (priceType === 'holiday') { baseCost = baseCost * holidayRate; } return this.roundToTwoDecimals(baseCost); } // 获取提前熟悉费用 getPreFamiliarizeCost(dateString) { const priceType = this.getDatePriceType(dateString); let baseCost = this.priceConfig.preFamiliarize.price; if (priceType === 'holiday') { baseCost = baseCost * this.priceConfig.preFamiliarize.holidayRate; } return this.roundToTwoDecimals(baseCost); } // 计算套餐免费额度 calculateFreeQuota(pets) { let totalPetCost = pets.reduce((acc, pet) => { return acc + this.getPetExtraCost(pet, pet.serviceDate); }, 0); if (totalPetCost <= this.priceConfig.freeQuota.threshold) { return 0; } let freeAmount = 0; const petCounts = { cat: pets.filter(pet => pet.petType === 'cat').length, smallDog: pets.filter(pet => pet.petType === 'dog' && pet.bodyType.includes('小型')).length, mediumDog: pets.filter(pet => pet.petType === 'dog' && pet.bodyType.includes('中型')).length, largeDog: pets.filter(pet => pet.petType === 'dog' && pet.bodyType.includes('大型')).length }; for (const rule of this.priceConfig.freeQuota.rules) { if (rule.type === 'cat' && petCounts.cat >= rule.count) { freeAmount += rule.freeAmount; petCounts.cat -= rule.count; } else if (rule.type === 'smallDog' && petCounts.smallDog >= rule.count) { freeAmount += rule.freeAmount; petCounts.smallDog -= rule.count; } else if (rule.type === 'mediumDog' && petCounts.mediumDog >= rule.count) { freeAmount += rule.freeAmount; petCounts.mediumDog -= rule.count; } else if (rule.type === 'mixed' && petCounts.cat >= 1 && petCounts.smallDog >= 1) { freeAmount += rule.freeAmount; petCounts.cat -= 1; petCounts.smallDog -= 1; } } return this.roundToTwoDecimals(freeAmount); } // 计算会员折扣 calculateMemberDiscount(originalPrice, memberLevel) { const discount = this.priceConfig.memberDiscount[memberLevel] || 1; const discountedPrice = originalPrice * discount; return this.roundToTwoDecimals(discountedPrice); } // 计算定制服务费用 calculateCustomServiceCost(customServices, dateString) { const priceType = this.getDatePriceType(dateString); let totalCost = 0; customServices.forEach(service => { let servicePrice = service.price * service.quantity; if (priceType === 'holiday') { servicePrice = servicePrice * this.priceConfig.customServices.holidayRate; } totalCost += servicePrice; }); return this.roundToTwoDecimals(totalCost); } // 计算单日总价 calculateDailyPrice(pets, dateString, feedCount = 1, customServices = []) { // 明细数组 let priceDetails = []; // 基础服务费用 let baseServiceCost = this.getBasePriceByDate(dateString); priceDetails.push({ name: '专业喂养', formula: `¥${baseServiceCost.toFixed(2)} x ${pets.length}天`, amount: this.roundToTwoDecimals(baseServiceCost * pets.length) }); // 宠物额外费用 let petExtraCost = pets.reduce((acc, pet) => { return acc + this.getPetExtraCost(pet, dateString); }, 0); if (petExtraCost > 0) { // 细分每只宠物 pets.forEach(pet => { const extra = this.getPetExtraCost(pet, dateString); if (extra > 0) { priceDetails.push({ name: '额外宠物费用', formula: `${pet.petType === 'cat' ? '猫' : pet.bodyType}${pet.petType === 'dog' ? '犬' : ''} 1只 x ¥${extra.toFixed(2)}`, amount: extra }); } }); } // 多次服务费用 let multiServiceCost = this.getMultiServiceCost(feedCount, dateString); if (multiServiceCost > 0) { priceDetails.push({ name: '上门次数', formula: `${feedCount === 2 ? '1天2次' : feedCount === 3 ? '1天3次' : '1天1次'} x ¥${multiServiceCost.toFixed(2)}`, amount: multiServiceCost }); } // 定制服务费用 let customServiceCost = this.calculateCustomServiceCost(customServices, dateString); if (customServiceCost > 0) { customServices.forEach(service => { if (service.quantity > 0) { priceDetails.push({ name: '定制服务', formula: `${service.name} | ¥${service.price} x ${service.quantity}次`, amount: this.roundToTwoDecimals(service.price * service.quantity) }); } }); } // 计算免费额度 let freeQuota = this.calculateFreeQuota(pets); if (freeQuota > 0) { priceDetails.push({ name: '套餐免费额度', formula: `-¥${freeQuota.toFixed(2)}`, amount: -freeQuota }); } // 总价(未折扣) let totalOriginalPrice = baseServiceCost + petExtraCost + multiServiceCost + customServiceCost - freeQuota; return { baseServiceCost: this.roundToTwoDecimals(baseServiceCost), petExtraCost: this.roundToTwoDecimals(petExtraCost), multiServiceCost: this.roundToTwoDecimals(multiServiceCost), customServiceCost: this.roundToTwoDecimals(customServiceCost), freeQuota: this.roundToTwoDecimals(freeQuota), totalOriginalPrice: this.roundToTwoDecimals(totalOriginalPrice), priceType: this.getDatePriceType(dateString), priceDetails // 新增明细 }; } // 计算订单总价(含城市倍率和会员折扣) calculateOrderTotal(orderData, userAddress, memberLevel = 'new') { const cityRate = this.getCityPriceRate(userAddress); // 计算每日费用 const dailyPrices = orderData.pets.map(pet => { const dailyPrice = this.calculateDailyPrice( [pet], pet.serviceDate, pet.feedCount, pet.customServices || [] ); // 应用城市倍率 dailyPrice.totalWithCityRate = this.roundToTwoDecimals(dailyPrice.totalOriginalPrice * cityRate); // 明细也加上城市倍率 dailyPrice.priceDetails = dailyPrice.priceDetails.map(detail => ({ ...detail, amount: this.roundToTwoDecimals(detail.amount * cityRate), formula: cityRate !== 1.0 ? `${detail.formula} x 城市倍率${cityRate}` : detail.formula })); return dailyPrice; }); // 汇总明细 let priceDetails = []; dailyPrices.forEach((d, idx) => { d.priceDetails.forEach(item => { priceDetails.push({ ...item, date: orderData.pets[idx]?.serviceDate || '' }); }); }); // 提前熟悉费用 let preFamiliarizeCost = 0; if (orderData.needPreFamiliarize && orderData.needPreFamiliarize.length > 0) { const firstServiceDate = orderData.pets[0]?.serviceDate; if (firstServiceDate) { preFamiliarizeCost = this.getPreFamiliarizeCost(firstServiceDate); preFamiliarizeCost = this.roundToTwoDecimals(preFamiliarizeCost * cityRate); priceDetails.push({ name: '提前熟悉', formula: `¥${this.getPreFamiliarizeCost(firstServiceDate)} x 城市倍率${cityRate}`, amount: preFamiliarizeCost }); } } // 计算总价 let totalOriginalPrice = dailyPrices.reduce((acc, daily) => { return acc + daily.totalWithCityRate; }, 0) + preFamiliarizeCost; // 应用会员折扣 const totalWithDiscount = this.calculateMemberDiscount(totalOriginalPrice, memberLevel); return { dailyPrices, preFamiliarizeCost, totalOriginalPrice: this.roundToTwoDecimals(totalOriginalPrice), totalWithDiscount: this.roundToTwoDecimals(totalWithDiscount), cityRate: this.roundToTwoDecimals(cityRate), memberDiscount: this.roundToTwoDecimals(totalOriginalPrice - totalWithDiscount), memberLevel, priceDetails // 新增明细 }; } } export default PriceCalculator;