diff --git a/application/admin/controller/statistics/Kpidispatcher.php b/application/admin/controller/statistics/Kpidispatcher.php index ef1d1e7..37a1feb 100644 --- a/application/admin/controller/statistics/Kpidispatcher.php +++ b/application/admin/controller/statistics/Kpidispatcher.php @@ -246,92 +246,67 @@ class Kpidispatcher extends Backend $max_score = $template->max_score??100; if(!empty($data)){ - foreach ($data as &$datum){ - //利润率 = 总业绩/总成效额 - $datum->performance_rate = $this->_calc($datum->performance,$datum->total,4,true); - //转化率 = 完单数 / 总订单数 - $datum->trans_rate = $this->_calc($datum->finish_num,$datum->count_num,4,true); - //变现值 = 总业绩 / 总订单数 - $datum->cash_value = $this->_calc($datum->performance,$datum->count_num,2); - //客单利润 = 总利润 / 完单数 - $datum->performance_avg = $this->_calc($datum->performance,$datum->finish_num,2); - //客单价 = 总成效额 / 完单数 - $datum->total_avg = $this->_calc($datum->total,$datum->finish_num,2); - if(!empty($datum->dispatch_admin_id)){ - $datum->admin_user = Admin::where('id',$datum->dispatch_admin_id)->value('nickname')??'系统'; - }else{ - $datum->admin_user = '系统'; - } - $datum->avg_time_diff = $this->_calc($datum->avg_time_diff,3600,4); + foreach ($data as &$datum) { + // 常规字段计算 + $datum->performance_rate = $this->_calc($datum->performance, $datum->total, 4, true); + $datum->trans_rate = $this->_calc($datum->finish_num, $datum->count_num, 4, true); + $datum->cash_value = $this->_calc($datum->performance, $datum->count_num, 2); + $datum->performance_avg = $this->_calc($datum->performance, $datum->finish_num, 2); + $datum->total_avg = $this->_calc($datum->total, $datum->finish_num, 2); + $datum->avg_time_diff = $this->_calc($datum->avg_time_diff, 3600, 4); + + // 管理员名称 + //派单员数量不多,循环中查 + $datum->admin_user = !empty($datum->dispatch_admin_id) + ? Admin::where('id', $datum->dispatch_admin_id)->value('nickname') ?? '系统' + : '系统'; $datum->id = $datum->dispatch_admin_id; + // 初始化 KPI 总分 $kpi_total = 0; - //kpi - //转化率 - $datum->zhl_score = 0; - $datum->zhl_target_value = $kpiItem[KpiItem::ATTR_ZHL]['target_value']??0; - $datum->unit = ''; - if(!empty($kpiItem[KpiItem::ATTR_ZHL])){ - $datum->zhl_score = $this->_kpi_score($datum->trans_rate,$kpiItem[KpiItem::ATTR_ZHL]); - $kpi_total += $datum->zhl_score; - //$datum->zhl_unit = $kpiItem[KpiItem::ATTR_ZHL]['unit']; - } - //利润率 - $datum->lrl_score = 0; - $datum->lrl_target_value = $kpiItem[KpiItem::ATTR_LRL]['target_value']??0; - if (!empty($kpiItem[KpiItem::ATTR_LRL])) { - $datum->lrl_score = $this->_kpi_score($datum->performance_rate, $kpiItem[KpiItem::ATTR_LRL]); - $kpi_total += $datum->lrl_score; - //$datum->lrl_unit = $kpiItem[KpiItem::ATTR_LRL]['unit']; - } + // 定义 KPI 计算配置 + $kpiMap = [ + KpiItem::ATTR_ZHL => ['field' => 'trans_rate', 'score_field' => 'zhl_score', 'target_field' => 'zhl_target_value'], + KpiItem::ATTR_LRL => ['field' => 'performance_rate','score_field' => 'lrl_score', 'target_field' => 'lrl_target_value'], + KpiItem::ATTR_PDSX => ['field' => 'avg_time_diff', 'score_field' => 'pdsx_score', 'target_field' => 'pdsx_target_value', 'reverse' => true], + KpiItem::ATTR_PCCGL => ['field' => 'succ_rate', 'score_field' => 'pccgl_score', 'target_field' => 'pccgl_target_value'], + KpiItem::ATTR_LRSFS => ['field' => 'worker_num', 'score_field' => 'lrsfs_score', 'target_field' => 'lrsfs_target_value'], + ]; - //派单时效 - $datum->pdsx_score = 0; - $datum->pdsx_target_value = $kpiItem[KpiItem::ATTR_PDSX]['target_value']??0; //秒 - if (!empty($kpiItem[KpiItem::ATTR_PDSX])) { - $datum->pdsx_score = $this->_kpi_score($datum->avg_time_diff, $kpiItem[KpiItem::ATTR_PDSX],$fande=true); - $kpi_total += $datum->pdsx_score; - //$datum->pdsx_unit = $kpiItem[KpiItem::ATTR_PDSX]['unit']; - } + // 单独处理 success rate(派单成功率) + $datum->succ_rate = $this->_calc($datum->finish_num, $datum->count_num, 4, true); + $datum->worker_num = $datum->worker_num ?? 0; - //派单成功率 - $datum->succ_rate = $this->_calc($datum->finish_num,$datum->count_num,4,true); - $datum->pccgl_score = 0; - $datum->pccgl_target_value = $kpiItem[KpiItem::ATTR_PCCGL]['target_value']??0; - if (!empty($kpiItem[KpiItem::ATTR_PCCGL])) { - $datum->pccgl_score = $this->_kpi_score($datum->succ_rate, $kpiItem[KpiItem::ATTR_PCCGL]); - $kpi_total += $datum->pccgl_score; - //$datum->pccgl_unit = $kpiItem[KpiItem::ATTR_PCCGL]['unit']; - } + // 批量处理 KPI 项 + foreach ($kpiMap as $attr => $conf) { + $item = $kpiItem[$attr] ?? null; + $datum->{$conf['score_field']} = 0; + $datum->{$conf['target_field']} = $item['target_value'] ?? 0; - - //录单师傅数 - $datum->lrsfs_score = 0; - $datum->lrsfs_target_value = $kpiItem[KpiItem::ATTR_LRSFS]['target_value']??0; - $datum->worker_num = $datum->worker_num??0; - if (!empty($kpiItem[KpiItem::ATTR_LRSFS])) { - $datum->lrsfs_score = $this->_kpi_score($datum->worker_num, $kpiItem[KpiItem::ATTR_LRSFS]); - $kpi_total += $datum->lrsfs_score; - } - - $datum->kpi_total = bcadd($kpi_total,0,2); - - $datum->kpi_value = $this->_calc($kpi_total,$template->score ?: 1,2); - - if($datum->kpi_value <= 0){ - $datum->kpi_money = 0; - }else{ - $datum->kpi_money = bcmul($datum->performance,$datum->kpi_value,4); - - if($datum->kpi_money <= 0){ - $datum->kpi_money = 0; - }else{ - $datum->kpi_money = bcdiv($datum->kpi_money,100,2); + if ($item) { + $reverse = $conf['reverse'] ?? false; + $value = $datum->{$conf['field']}; + $score = $this->_kpi_score($value, $item, $reverse); + $datum->{$conf['score_field']} = $score; + $kpi_total += $score; } } + + // KPI 总分和比值 + $datum->kpi_total = bcadd($kpi_total, 0, 2); + $datum->kpi_value = $this->_calc($kpi_total, $template->score ?: 1, 2); + + // KPI 奖金 + if ($datum->kpi_value <= 0) { + $datum->kpi_money = 0; + } else { + $money = bcmul($datum->performance, $datum->kpi_value, 4); + $datum->kpi_money = $money > 0 ? bcdiv($money, 100, 2) : 0; + } + $newData[] = $datum->toArray(); } } @@ -365,39 +340,35 @@ class Kpidispatcher extends Backend } - private function _kpi_score($num,$item,$fande=false) + private function _kpi_score($num, $item, $fande = false) { - //完成值/目标值*单个指标分*权重 + $target = $item['target_value'] ?? 1; + $target = $target == 0 ? 1 : $target; // 避免除以0 - if($item['target_value'] == 0){ - $item['target_value'] = 1; - } - if($fande){ - //哦哦 如果定的是10分钟,完成的为15分钟,则1-(15-10)/10 - //这个还要区分是超过10还是小于10,超过10了就上面的,没超过就是1 + (10-完成值)/10 - if($num > $item['target_value']){ - $rate = bcdiv(($num-$item['target_value']),$item['target_value'],4); - $rate = bcsub(1,$rate,4); - }else{ - $rate = bcdiv(($item['target_value']-$num),$item['target_value'],4); - $rate = bcadd(1,$rate,4); - } - }else{ - $rate = bcdiv($num,$item['target_value'],4); + // 计算得分比例 + if ($fande) { + $diff = bcsub($num, $target, 4); + $rate = bcdiv(abs($diff), $target, 4); + + // 方向判断:小于目标加分,超过目标扣分 + $rate = $num > $target + ? bcsub(1, $rate, 4) + : bcadd(1, $rate, 4); + } else { + $rate = bcdiv($num, $target, 4); } - $score = bcmul($item['score'],$rate,4); - $score = bcmul($score,$item['pivot']['rate']/100,2); + // 得分 = 比例 × 指标分数 × 权重 + $score = bcmul($item['score'], $rate, 4); + $weight = isset($item['pivot']['rate']) ? $item['pivot']['rate'] : 100; + $score = bcmul($score, bcdiv($weight, 100, 4), 2); - if($score<= 0){ - $score = 0; - } - - return $score; + return max($score, 0); // 保证非负 } + /** * 获取指定年月的开始和结束时间戳(到秒) * @param string $yearMonth 格式为 "YYYY-MM" 的日期字符串