在一种项目中,订单的金额是以时长来计算价格的,比如共享单车、自助洗车这类的项目。订单一般有一个开始时间,有一个结束时间,然后用duration来计算订单总额。
一般来说有两种计价方式:
- 一种是前x分钟多少钱,超时每分钟多少钱
- 第二种是分闲忙时,不同的时间段内,每分钟是什么价格
今天介绍的是第二种,闲忙时,不同时段有不同价格来计费。其中有两个重点:
- 跨时段的情况
- 跨天的情况(暂时不考虑跨好几天的情况,只考虑跨两天)
class OrderModel
{
/**
* 订单结算方法
* $start_time 订单开始时间戳
* $end_time 订单结束时间戳
*/
public function settlement($start_time,$end_time){
//1 计算总价
$amount = 0;
/*判断是否跨天*/
$startDate = date("Y-m-d", $start_time);
$endDate = date("Y-m-d", $end_time);
//把时间戳转为日期格式,如果不在同一天,则把时间分为2段
if($startDate < $endDate){
$day1_end= strtotime(date("Y-m-d 23:59:59", $start_time)); //第一天的结束时间就是start_time日期的最后一分钟
$day2_start= strtotime(date("Y-m-d 00:00:00", $end_time)); //第二天的开始时间
//将时段分为 【开始时间至开始日期的最后一秒】和【结束日期的第一秒至结束时间】
$amount += $this->computePriceSectionOnDay($start_time,$day1_end);
$amount += $this->computePriceSectionOnDay($day2_start,$end_time);
}else{
//不跨天的情况
$amount = $this->computePriceSectionOnDay($start_time,$end_time);
}
return $amount;
}
/**
* 分时段计算价格,在一天之内,不考虑跨天情况。
* (跨天的处理在上层方法)
* @param $start_time
* @param $end_time
* @return int|null
*/
private function computePriceSectionOnDay($start_time,$end_time){
//从系统配置里获取时段计费规则 section_time=时间区间 section_price=区间价格/分钟
$rates = [
[
"section_time"=>"00:00-09:59",
"section_price"=>5
],
[
"section_time"=>"10:00-14:59",
"section_price"=>8
],
[
"section_time"=>"15:00-23:59",
"section_price"=>5
],
];
// 遍历计费规则
//如果跨2个区间,则用 区间1的endtime - 订单开始时间 = 落在区间一的时长,订单结束时间 - 区间2的starttime = 落在区间二的时长
$amount = 0;
$stemp_start = $start_time; //用于截断时长时,标记新的计费开始时间点
foreach ($rates as $rate) {
$timeRange = explode('-', $rate['section_time']);
$section_start = strtotime($timeRange[0]);
$section_end = strtotime($timeRange[1]);
//stemp_time 是否落在这个区间,yes,则开始计算费用
if($stemp_start>=$section_start && $stemp_start < $section_end){
if($end_time<= $section_end+1){
//最后一个区间,包含了订单结束时间
$amount += $this->computePriceTool($stemp_start,$end_time,$rate['section_price']);
Log::debug('包含订单结束,金额为'.$amount);
break;
}else{
//订单end_time大于该区间的结束时间,则跨时段了,截断计价
$amount += $this->computePriceTool($stemp_start,$section_end+1,$rate['section_price']);
//下一个循环的开始时间是区间的结束+1秒
$stemp_start = $section_end+1;
}
}
}
return $amount;
}
/**
* 计算价格辅助方法
* @param $start_time 开始时间戳 11位到秒
* @param $end_time 结束时间戳
* @param $price 价格,每分钟计价
* @return void
*/
private function computePriceTool($start_time,$end_time,$price){
$duration = $end_time - $start_time;
$amount = $duration * $price/60;
return $amount;
}
}
```