allocatr/application/admin/controller/statistics/Item.php
2025-07-07 16:05:27 +08:00

294 lines
10 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace app\admin\controller\statistics;
use app\admin\model\Admin;
use app\admin\model\Aftersale;
use app\admin\model\Order;
use app\admin\model\OrderDispatch;
use app\admin\model\OrderReview;
use app\common\controller\Backend;
use PDOStatement;
use think\Collection;
use think\Db;
use think\Exception;
use think\exception\DbException;
use think\Loader;
use think\Model;
use think\response\Json;
use function Symfony\Component\Clock\now;
/**
* 服务项目统计
*
* @icon fa fa-circle-o
*/
class Item extends Backend
{
protected $itemsformattedTree = null;
protected $noNeedRight = ['list','chartData'];
protected $relationSearch = true;
public function _initialize()
{
parent::_initialize();
$sources = Db::name('source')
->where('status', 1)
->field(['id', 'title', 'key_word', 'pid'])
->order('pid', 'asc')
->order('sort', 'desc')
->select();
$filtered = array_filter($sources, function ($item) {
return $item['pid'] == 0;
});
$pid_map = array_column($filtered, null, 'id');
$res = [];
foreach ($sources as $item) {
if ($item['pid'] != 0 && isset($pid_map[$item['pid']])) {
$res [] = [
...$item, 'ptitle' => $pid_map[$item['pid']]['title']
];
}
}
$items = Db::name('item')
->where('status', 1)
->field(['id', 'title', 'key_word', 'pid'])
->order('pid', 'asc')
->order('sort', 'desc')
->select();
$tree = $this->buildTree($items);
$formattedTree = $this->formatTree($tree);
$this->items = $items;
$this->itemsformattedTree = $formattedTree;
$this->view->assign("sources", $res);
$this->view->assign("items", $formattedTree);
}
public function index()
{
$start = now()->modify('-7 days')->format('Y-m-d');
$end_at = now()->format('Y-m-d');
$default_daterange = $start . ' - ' . $end_at;
return $this->fetch('index',[
'default_daterange' => $default_daterange
]);
}
public function list()
{
$build = new Order();
$start = now()->modify('-7 days')->format('Y-m-d');
$end_at = now()->format('Y-m-d 23:29:59');
$filter = request()->get('range','');
if (!empty($filter)){
$arr = explode(' - ', $filter);
if (trim($arr[0])) {
$start = trim($arr[0]);
}
if (trim($arr[1])) {
$end_at = trim($arr[1]) . ' 23:29:59';
}
}
$area_id = request()->get('area_id');
if ($area_id) {
$area_id = $this->trimSpecialZeros($area_id);
$build->where('area_id', 'like', $area_id . '%');
}
$build->whereBetween('create_time', [$start, $end_at])
->field([
'item_title name', // 类型
'sum(total) total', // 营业额
'count(id) count_num', // 单量
'count(if(status=60,1,null)) finish_num', // 单量
'sum(performance) performance', // 收益
'sum(cost) cost', // 收益
'sum(material_cost) material_cost', // 收益
'sum(refund_amount) refund_amount', // 公司退款
'sum(worker_refund_amount) worker_refund_amount', // 工人退款
"IFNULL(AVG(CASE WHEN status > 10 THEN UNIX_TIMESTAMP(dispatch_time) - UNIX_TIMESTAMP(create_time) END), 0) AS avg_time_diff", //派单时效
])->group('item_title')
->order('count_num', 'desc');
// dd($total);
$res = $build->paginate();
$total = $res->total();
$ress = $res->items();
$data = [];
// dd($res);
foreach ($ress as $re) {
$re = $re->toArray();
// dd($re);
$cost_total = $re['cost'] + $re['material_cost'];
$name = explode('/', $re['name']);
$re['name'] = str_replace(' ', '', array_pop($name));
$re['performance_rate'] = $this->mydiv($re['performance'],$re['total']);
$re['trans_rate'] = $this->mydiv($re['finish_num'],$re['count_num']);
$re['cash_value'] = $this->mydiv($re['performance'],$re['count_num'],2,false);
$re['total_avg'] = $this->mydiv($re['total'],$re['count_num'],2,false);
$re['performance_avg'] = $this->mydiv($re['performance'],$re['finish_num'],2,false);
$re['avg_time_diff'] = $this->mydiv($re['avg_time_diff'],3600,2,false);
$re['cost_total'] = $cost_total;
// $re['id'] = $re['item_id'];
$data [] = $re;
}
return [
'rows' => $data,
'total' => $total
];
}
function trimSpecialZeros($str) {
if (strlen($str) !== 6) {
return $str; // 非6位字符串直接返回
}
if (substr($str, -4) === "0000") {
return substr($str, 0, 2); // 去掉后4位
} elseif (substr($str, -2) === "00") {
return substr($str, 0, 4); // 去掉后2位
}
return $str; // 不符合条件则原样返回
}
public function chartData()
{
$build = new Order();
$start = now()->modify('-14 days')->format('Y-m-d');
$end_at = now()->format('Y-m-d 23:29:59');
$filter ['daterange'] = request()->post('daterange');
if (!empty($filter['daterange'])) {
$arr = explode(' - ', $filter['daterange']);
if (trim($arr[0])) {
$start = trim($arr[0]);
}
if (trim($arr[1])) {
$end_at = trim($arr[1]) . ' 23:29:59';
}
}
$build = $build
->whereBetween('create_time', [$start, $end_at]);
//来源
if(!empty(request()->post('source',null))){
$build->where('source',request()->post('source'));
}
//城市
if(!empty(request()->post('area_id',null))){
$build->where('area_id','LIKE',request()->post('source').'%');
}
if(!empty(request()->post('item_id',null))){
$item_id =request()->post('item_id');
$item_ids = $this->getItemsById($item_id);
$item_ids [] = $item_id;
$build->whereIn('item_id', $item_ids);
}
$res = $build->field([
'item_title name', // 类型
'sum(total) total', // 营业额
'count(id) count', // 单量
'count(if(status=60,1,null)) finish_num', // 单量
'sum(performance) performance', // 收益
'sum(refund_amount) refund_amount', // 公司退款
'sum(worker_refund_amount) worker_refund_amount', // 工人退款
])->group('item_title')
->order('count', 'desc')->select();
$data = [];
foreach ($res as $re) {
$re = $re->getData();
$name = explode('/', $re['name']);
$re['name'] = str_replace(' ', '', array_pop($name));
$data [] = $re;
}
// dd($data);
$xAxis = [];
$totalPerformance = [];
$conversionRate = []; // 假设转化率 = performance / total或自定义逻辑
$profitRate = []; // 假设利润率 = (performance - worker_refund_amount) / performance
$refundRate = []; // 假设退款率 = refund_amount / total
$monetizedValue = []; // 假设变现值 = performance - refund_amount
foreach ($data as $item) {
$name = $item['name'];
$total = (float)$item['total'];
$finish_num = (int)$item['finish_num'];
$performance = (float)$item['performance'];
$refund = (float)($item['refund_amount'] + $item['worker_refund_amount']);
$workerRefund = (float)$item['worker_refund_amount'];
$count = max((int)$item['count'], 1); // 避免除以0
$xAxis[] = $name;
$totalPerformance[] = $total;
$conversionRate[] = $total > 0 ? round($finish_num / $count * 100, 2) : 0;
$profitRate[] = $total > 0 ? round(($performance) / $total * 100, 2) : 0;
$refundRate[] = $total > 0 ? round($refund / $total * 100, 2) : 0;
$monetizedValue[] = round($total / $count, 2) ;
}
if ($totalPerformance){
$result = [
'xAxis' => $xAxis,
'series' => [
['name' => '总业绩(¥)', 'type' => 'bar', 'yAxisIndex' => 0, 'data' => $totalPerformance],
['name' => '转化率(%)', 'type' => 'bar', 'yAxisIndex' => 1, 'data' => $conversionRate],
['name' => '利润率(%)', 'type' => 'bar', 'yAxisIndex' => 1, 'data' => $profitRate],
['name' => '退款率(%)', 'type' => 'bar', 'yAxisIndex' => 1, 'data' => $refundRate],
['name' => '变现值(¥)', 'type' => 'line', 'yAxisIndex' => 0, 'data' => $monetizedValue],
]
];
}else{
$result = [
'xAxis' => ['无'],
'series' => [
['name' => '总业绩(¥)', 'type' => 'bar', 'yAxisIndex' => 0, 'data' => [0]],
['name' => '转化率(%)', 'type' => 'bar', 'yAxisIndex' => 1, 'data' => [0]],
['name' => '利润率(%)', 'type' => 'bar', 'yAxisIndex' => 1, 'data' => [0]],
['name' => '退款率(%)', 'type' => 'bar', 'yAxisIndex' => 1, 'data' => [0]],
['name' => '变现值(¥)', 'type' => 'line', 'yAxisIndex' => 0, 'data' => [0]],
]
];
}
return $result;
}
private function mydiv($a, $b, int $scale = 4, $is_percent = true): int|string
{
$val = $b > 0 ? bcdiv($a, $b, $scale) : 0;
if ($is_percent) {
return bcmul($val, 100, 2);
}
return $val;
}
}