|
|
- /**
- * 价格计算工具类
- * 根据当前地址、日期、宠物类型等因素计算价格,保留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;
|