674 lines
19 KiB
PHP
674 lines
19 KiB
PHP
<?php
|
||
|
||
namespace app\admin\controller;
|
||
|
||
use app\admin\addresmart\Address;
|
||
use app\admin\model\Abnormal;
|
||
use app\admin\model\Aftersale;
|
||
use app\admin\model\AuthGroupAccess;
|
||
use app\admin\model\order\Invoice;
|
||
use app\admin\model\OrderAbnormal;
|
||
use app\admin\model\OrderDispatch;
|
||
use app\admin\model\orders\Dispatchlog;
|
||
use app\admin\model\Worker;
|
||
use app\admin\model\WorkerItem;
|
||
use app\common\controller\Backend;
|
||
use app\common\Logic\OrderLogic;
|
||
use Carbon\Carbon;
|
||
use fast\Tree;
|
||
use think\Db;
|
||
use think\Exception;
|
||
use think\exception\DbException;
|
||
use think\exception\PDOException;
|
||
use think\exception\ValidateException;
|
||
use think\Hook;
|
||
use think\Model;
|
||
use function Carbon\int;
|
||
use function Symfony\Component\Clock\now;
|
||
|
||
/**
|
||
* 订单列管理
|
||
*
|
||
* @icon fa fa-circle-o
|
||
*/
|
||
class Orderplan extends Backend
|
||
{
|
||
|
||
protected $noNeedRight = ['data'];
|
||
/**
|
||
* Order模型对象
|
||
* @var \app\admin\model\Order
|
||
*/
|
||
protected $model = null;
|
||
protected $sources = null;
|
||
protected $items = null;
|
||
|
||
public function _initialize()
|
||
{
|
||
parent::_initialize();
|
||
$this->model = new \app\admin\model\Order;
|
||
|
||
}
|
||
|
||
|
||
public function dashboard()
|
||
{
|
||
$start = now()->modify('-14 days')->format('Y-m-d');
|
||
$end_at = now()->format('Y-m-d');
|
||
$default_daterange = $start . ' - ' . $end_at;
|
||
|
||
return $this->fetch('orderplan/index',[
|
||
'default_daterange' => $default_daterange
|
||
]);
|
||
}
|
||
|
||
public function data()
|
||
{
|
||
$top = $this->getTopTotal();
|
||
$lines = $this->getLine();
|
||
$pie = $this->getPie();
|
||
$order = $this->getOrder();
|
||
$order_count = $this->getOrderCount();
|
||
$order_rank = $this->orderRank();
|
||
$item_rank = $this->itemRank();
|
||
$order_doing = $this->orderDoing();
|
||
$this->success(data: [
|
||
'top' => $top,
|
||
'lines' => $lines,
|
||
'pie' => $pie,
|
||
'order' => $order,
|
||
'order_count' => $order_count,
|
||
'order_rank' => $order_rank,
|
||
'item_rank' => $item_rank,
|
||
'order_doing' => $order_doing,
|
||
]);
|
||
}
|
||
|
||
private function getTopTotal()
|
||
{
|
||
$build = new \app\admin\model\Order();
|
||
|
||
$build->field([
|
||
'sum(total) total',
|
||
'count(id) count',
|
||
'sum(performance) performance'
|
||
])->where('status',\app\admin\model\Order::STATUS_FINISHED)->limit(1);
|
||
|
||
$res = $this->buildDate($build)->select();
|
||
|
||
$res = $res[0] ?? false;
|
||
$worker = Worker::where('status', 1)->count();
|
||
if ($res) {
|
||
return [
|
||
'total' => $res->total ?? 0,
|
||
'count' => $res->count ?? 0,
|
||
'performance' => $res->performance ?? 0,
|
||
'worker' => $worker ?? 0,
|
||
];
|
||
}
|
||
|
||
return [
|
||
'total' => 0,
|
||
'count' => 0,
|
||
'performance' => 0,
|
||
'worker' => 0,
|
||
];
|
||
}
|
||
|
||
private function getLine()
|
||
{
|
||
$build = new \app\admin\model\Order();
|
||
|
||
$start = now()->modify('-14 days')->format('Y-m-d');
|
||
$end_at = now()->format('Y-m-d 23:29:59');
|
||
$filter ['daterange'] = request()->get('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->field([
|
||
'DATE(audit_time) day',
|
||
'sum(total) total',
|
||
'count(id) count',
|
||
'sum(performance) performance'
|
||
])
|
||
->where('status',\app\admin\model\Order::STATUS_FINISHED)
|
||
->group(' DATE(audit_time)');
|
||
|
||
$res = $this->buildDate($build)->select();
|
||
$data = [];
|
||
foreach ($res as $re) {
|
||
$re = $re->getData();
|
||
$data [] = $re;
|
||
}
|
||
return $this->prepareEchartsBarData($data, $start, $end_at);
|
||
}
|
||
|
||
private function getPie()
|
||
{
|
||
$build = new OrderAbnormal();
|
||
$res = $this->buildDate($build,name:'create_time')
|
||
->field([
|
||
'abnormal_title name',
|
||
'count(id) value',
|
||
])
|
||
->group('abnormal_title')
|
||
->select();
|
||
$data = [];
|
||
foreach ($res as $re) {
|
||
$re = $re->getData();
|
||
$data [] = $re;
|
||
}
|
||
$out ['cancel'] = $data;
|
||
|
||
|
||
$build = new \app\admin\model\Order();
|
||
$res = $this->buildDate($build)
|
||
->field([
|
||
'item_title name',
|
||
'count(id) value',
|
||
])
|
||
->group('item_title')
|
||
->select();
|
||
$data = [];
|
||
foreach ($res as $re) {
|
||
$re = $re->getData();
|
||
$name = explode('/', $re['name']);
|
||
$re['name'] = str_replace(' ', '', array_pop($name));
|
||
$data [] = $re;
|
||
}
|
||
$out ['item'] = $data;
|
||
|
||
$build = new \app\admin\model\Order();
|
||
$res = $this->buildDate($build)
|
||
->field([
|
||
'SUBSTRING(area_id, 1, 2) AS name',
|
||
'count(id) value',
|
||
])
|
||
->group('name')
|
||
->select();
|
||
$data = [];
|
||
foreach ($res as $re) {
|
||
$re = $re->getData();
|
||
$re['name'] = $this->getProvinceByCode($re['name']);
|
||
$data [] = $re;
|
||
}
|
||
$out ['area'] = $data;
|
||
|
||
$build = new \app\admin\model\Order();
|
||
$res = $this->buildDate($build)
|
||
->field([
|
||
'source_shop AS name',
|
||
'count(id) value',
|
||
])
|
||
->group('name')
|
||
->select();
|
||
$data = [];
|
||
foreach ($res as $re) {
|
||
$re = $re->getData();
|
||
$data [] = $re;
|
||
}
|
||
$out ['source'] = $data;
|
||
|
||
|
||
return $out;
|
||
}
|
||
|
||
|
||
private function buildDate($build, $table_name = null,$name = 'audit_time')
|
||
{
|
||
$start = now()->modify('-14 days')->format('Y-m-d');
|
||
$end_at = now()->format('Y-m-d 23:29:59');
|
||
$filter ['daterange'] = request()->get('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';
|
||
}
|
||
}
|
||
// dd([$start, $end_at]);
|
||
if ($table_name) {
|
||
$build->where($table_name . '.'.$name, 'between', [$start, $end_at]);
|
||
} else {
|
||
$build->where($name, 'between', [$start, $end_at]);
|
||
|
||
}
|
||
return $build;
|
||
}
|
||
|
||
|
||
private function getOrder()
|
||
{
|
||
$build = new OrderAbnormal();
|
||
$res = $build
|
||
->where('status', 0)
|
||
->count('id');
|
||
$out ['abnormal'] = $res;
|
||
|
||
$build = new \app\admin\model\Order();
|
||
$res = $build
|
||
->where('status', \app\admin\model\Order::STATUS_CHECKING)
|
||
->count('id');
|
||
$out ['check'] = $res;
|
||
|
||
|
||
$build = new \app\admin\model\Order();
|
||
$res = $build
|
||
->where('status', \app\admin\model\Order::STATUS_AUDITING)
|
||
->count('id');
|
||
$out ['auditing'] = $res;
|
||
|
||
|
||
$build = new \app\admin\model\Order();
|
||
$res = $build
|
||
->alias('a')
|
||
->join('order_review b', 'a.id = b.order_id', 'left')
|
||
->where('a.status', 'in', [\app\admin\model\Order::STATUS_FINISHED, \app\admin\model\Order::STATUS_CANCEL])
|
||
->whereNull('b.id')
|
||
->count('a.id');
|
||
|
||
$out ['review'] = $res;
|
||
|
||
|
||
$build = new Aftersale();
|
||
$res = $build
|
||
->where('status', 1)
|
||
->count('id');
|
||
$out ['after_sale'] = $res;
|
||
|
||
$build = new Invoice();
|
||
$res = $build
|
||
->where('status', 0)
|
||
->count('id');
|
||
$out ['invoice'] = $res;
|
||
|
||
return $out;
|
||
}
|
||
|
||
private function getOrderCount()
|
||
{
|
||
|
||
$start = now()->format('Y-m-d 00:00:00');
|
||
$end_at = now()->format('Y-m-d 23:59:59');
|
||
|
||
$build = new \app\admin\model\Order();
|
||
$build->whereBetween('audit_time', [$start, $end_at])
|
||
->where('status',\app\admin\model\Order::STATUS_FINISHED);
|
||
$data1 = $build->field([
|
||
'sum(total) total',
|
||
'count(*) count',
|
||
])->select();
|
||
|
||
|
||
$build = new \app\admin\model\Order();
|
||
$build->whereBetween('create_time', [$start, $end_at]);
|
||
$data = $build->field([
|
||
/* 'sum(total) total',
|
||
'count(id) count',*/
|
||
'count(if(status=-10,1,null)) cancel'
|
||
])->select();
|
||
|
||
$today = [
|
||
'total' => $data1[0]->total ?? 0,
|
||
'count' => $data1[0]->count ?? 0,
|
||
'cancel' => $data[0]->cancel ?? 0,
|
||
];
|
||
|
||
$build = new Worker();
|
||
$build->whereBetween('create_time', [$start, $end_at]);
|
||
|
||
$data = $build->field([
|
||
'count(id) count',
|
||
])->select();
|
||
// dd($data);
|
||
$today['worker'] = $data[0]->count ?? 0;
|
||
|
||
$build = new Aftersale();
|
||
$build->whereBetween('create_time', [$start, $end_at]);
|
||
|
||
$data = $build->field([
|
||
'count(id) count',
|
||
])->select();
|
||
$today['after_sale'] = $data[0]->count ?? 0;
|
||
|
||
|
||
// 本月
|
||
$start = now()->format('Y-m-01 00:00:00');
|
||
$end_at = (new Carbon())->endOfMonth()->format('Y-m-d H:m:s');
|
||
// dd($start,$end_at);
|
||
$build = new \app\admin\model\Order();
|
||
$build->whereBetween('create_time', [$start, $end_at]);
|
||
$data = $build->field([
|
||
'sum(total) total',
|
||
'count(id) count',
|
||
'count(if(status=-10,1,null)) cancel'
|
||
])->select();
|
||
// dd($data,\app\admin\model\Order::getLastSql());
|
||
$month = [
|
||
'month_total' => $data[0]->total ?? 0,
|
||
'month_count' => $data[0]->count ?? 0,
|
||
'month_cancel' => $data[0]->cancel ?? 0,
|
||
];
|
||
$build = new Worker();
|
||
$build->whereBetween('create_time', [$start, $end_at]);
|
||
|
||
$data = $build->field([
|
||
'count(id) count',
|
||
])->select();
|
||
$month['month_worker'] = $data[0]->count ?? 0;
|
||
|
||
$build = new Aftersale();
|
||
$build->whereBetween('create_time', [$start, $end_at]);
|
||
|
||
$data = $build->field([
|
||
'count(id) count',
|
||
])->select();
|
||
$month['month_after_sale'] = $data[0]->count ?? 0;
|
||
|
||
return [
|
||
...$today, ...$month
|
||
];
|
||
}
|
||
|
||
|
||
private function orderRank()
|
||
{
|
||
$start = now()->format('Y-m-d 00:00:00');
|
||
$end_at = now()->format('Y-m-d 23:59:59');
|
||
|
||
|
||
$build = new \app\admin\model\Order();
|
||
|
||
$build->whereBetween('create_time', [$start, $end_at]);
|
||
|
||
$data = $build->field([
|
||
'admin_id',
|
||
'count(id) count',
|
||
])->group('admin_id')
|
||
->order('count', 'desc')
|
||
->limit(5)
|
||
->select();
|
||
|
||
$rank = [];
|
||
foreach ($data as $da) {
|
||
$rank [] = [
|
||
'name' => $da->user->nickname ?? '',
|
||
'count' => $da->count ?? '',
|
||
];
|
||
}
|
||
|
||
|
||
$build = new OrderDispatch();
|
||
|
||
$build->whereBetween('create_time', [$start, $end_at]);
|
||
|
||
$data = $build->field([
|
||
'worker_name',
|
||
'count(id) count',
|
||
])->group('worker_name')
|
||
->order('count', 'desc')
|
||
->limit(5)
|
||
->select();
|
||
|
||
$worker = [];
|
||
foreach ($data as $da) {
|
||
$worker [] = [
|
||
'name' => $da->worker_name,
|
||
'count' => $da->count ?? '',
|
||
];
|
||
}
|
||
|
||
|
||
return [
|
||
'dispatch' => $rank,
|
||
'doing' => $worker,
|
||
];
|
||
|
||
}
|
||
|
||
private function itemRank()
|
||
{
|
||
$start = now()->format('Y-m-d 00:00:00');
|
||
$end_at = now()->format('Y-m-d 23:59:59');
|
||
|
||
|
||
$build = new \app\admin\model\Order();
|
||
|
||
$build->whereBetween('create_time', [$start, $end_at]);
|
||
|
||
$total = $build->count('id');
|
||
|
||
$build = new \app\admin\model\Order();
|
||
|
||
$build->whereBetween('create_time', [$start, $end_at]);
|
||
|
||
$data = $build->field([
|
||
'item_id',
|
||
'count(id) count',
|
||
])->group('item_id')
|
||
->order('count', 'desc')
|
||
->limit(6)
|
||
->select();
|
||
|
||
$rank = [];
|
||
foreach ($data as $da) {
|
||
$rank [] = [
|
||
'name' => $da->item->title ?? '',
|
||
'count' => $da->count ?? 0,
|
||
'rate' => $this->mydiv($da->count ?? 0, $total)
|
||
];
|
||
}
|
||
|
||
$build = new \app\admin\model\Order();
|
||
$res = $build->whereBetween('create_time', [$start, $end_at])
|
||
->field([
|
||
'SUBSTRING(area_id, 1, 2) AS name',
|
||
'count(id) count',
|
||
])
|
||
->group('name')
|
||
->order('count', 'desc')
|
||
->limit(6)
|
||
->select();
|
||
$area = [];
|
||
foreach ($res as $re) {
|
||
$re = $re->getData();
|
||
$re['name'] = $this->getProvinceByCode($re['name']);
|
||
$re['rate'] = $this->mydiv($da->count ?? 0, $total);
|
||
$area [] = $re;
|
||
}
|
||
|
||
return [
|
||
'item' => $rank,
|
||
'area' => $area,
|
||
];
|
||
|
||
}
|
||
private function orderDoing()
|
||
{
|
||
|
||
$build = new \app\admin\model\Order();
|
||
|
||
$dispatch = $build->where('status',10)->count('id');
|
||
|
||
$build = new OrderDispatch();
|
||
|
||
$build->whereIn('status',[
|
||
OrderDispatch::STATUS_TOGET,
|
||
OrderDispatch::STATUS_PLANIT,
|
||
OrderDispatch::STATUS_CLOCK,
|
||
]);
|
||
$data = $build->field([
|
||
'count(if(status=0,1,null)) toget',
|
||
'count(if(status=20,1,null)) plan',
|
||
'count(if(status=30,1,null)) doing',
|
||
])->select();
|
||
|
||
|
||
return [
|
||
'dispatch' => $dispatch,
|
||
'toget' => $data[0]->toget ?? 0,
|
||
'plan' => $data[0]->plan ?? 0,
|
||
'doing' => $data[0]->doing ?? 0,
|
||
];
|
||
|
||
}
|
||
|
||
|
||
private function prepareEchartsBarData(array $data, string $startDate, string $endDate): array
|
||
{
|
||
// 将原始数据用日期作为键索引
|
||
$indexed = [];
|
||
foreach ($data as $item) {
|
||
$indexed[$item['day']] = $item;
|
||
}
|
||
|
||
$start = strtotime($startDate);
|
||
$end = strtotime($endDate);
|
||
|
||
$xAxis = [];
|
||
$series_total = [];
|
||
$series_count = [];
|
||
$series_performance = [];
|
||
$rate = [];
|
||
|
||
for ($date = $start; $date <= $end; $date += 86400) {
|
||
$day = date('Y-m-d', $date);
|
||
$xAxis[] = $day;
|
||
|
||
$total = isset($indexed[$day]) ? (float)$indexed[$day]['total'] : 0;
|
||
$count = isset($indexed[$day]) ? (int)$indexed[$day]['count'] : 0;
|
||
$performance = isset($indexed[$day]) ? (float)$indexed[$day]['performance'] : 0;
|
||
|
||
$series_total[] = $total;
|
||
$series_count[] = $count;
|
||
$series_performance[] = $performance;
|
||
$rate[] = $total ? number_format($performance / $total * 100, 2) : '0';
|
||
}
|
||
|
||
$data = [
|
||
'xAxis' => $xAxis,
|
||
'series' => [
|
||
'total' => $series_total,
|
||
'count' => $series_count,
|
||
'performance' => $series_performance,
|
||
'rate' => $rate,
|
||
],
|
||
];
|
||
|
||
|
||
// 判断是否需要按月归档
|
||
if (count($data['xAxis']) > 31) {
|
||
$monthly = [];
|
||
|
||
foreach ($data['xAxis'] as $i => $date) {
|
||
$month = date('Y-m', strtotime($date));
|
||
|
||
if (!isset($monthly[$month])) {
|
||
$monthly[$month] = [
|
||
'total' => 0,
|
||
'count' => 0,
|
||
'performance' => 0,
|
||
'rate_sum' => 0,
|
||
'rate_count' => 0,
|
||
];
|
||
}
|
||
|
||
$monthly[$month]['total'] += $data['series']['total'][$i];
|
||
$monthly[$month]['count'] += $data['series']['count'][$i];
|
||
$monthly[$month]['performance'] += $data['series']['performance'][$i];
|
||
|
||
// 处理 rate(字符串,转换为 float)
|
||
$rate = (float)$data['series']['rate'][$i];
|
||
$monthly[$month]['rate_sum'] += $rate;
|
||
$monthly[$month]['rate_count'] += 1;
|
||
}
|
||
|
||
// 重新生成 xAxis 和 series
|
||
$data['xAxis'] = array_keys($monthly);
|
||
$data['series'] = [
|
||
'total' => [],
|
||
'count' => [],
|
||
'performance' => [],
|
||
'rate' => []
|
||
];
|
||
|
||
foreach ($monthly as $month => $values) {
|
||
$data['series']['total'][] = $values['total'];
|
||
$data['series']['count'][] = $values['count'];
|
||
$data['series']['performance'][] = $values['performance'];
|
||
|
||
// 平均 rate,保留 2 位小数
|
||
$averageRate = $values['rate_count'] > 0 ? $values['rate_sum'] / $values['rate_count'] : 0;
|
||
$data['series']['rate'][] = number_format($averageRate, 2);
|
||
}
|
||
}
|
||
|
||
return $data;
|
||
}
|
||
|
||
|
||
function getProvinceByCode($code)
|
||
{
|
||
// 省级行政区域编码对应数组
|
||
$provinces = [
|
||
11 => '北京',
|
||
12 => '天津',
|
||
13 => '河北',
|
||
14 => '山西',
|
||
15 => '内蒙古',
|
||
21 => '辽宁',
|
||
22 => '吉林',
|
||
23 => '黑龙江',
|
||
31 => '上海',
|
||
32 => '江苏',
|
||
33 => '浙江',
|
||
34 => '安徽',
|
||
35 => '福建',
|
||
36 => '江西',
|
||
37 => '山东',
|
||
41 => '河南',
|
||
42 => '湖北',
|
||
43 => '湖南',
|
||
44 => '广东',
|
||
45 => '广西',
|
||
46 => '海南',
|
||
50 => '重庆',
|
||
51 => '四川',
|
||
52 => '贵州',
|
||
53 => '云南',
|
||
54 => '西藏',
|
||
61 => '陕西',
|
||
62 => '甘肃',
|
||
63 => '青海',
|
||
64 => '宁夏',
|
||
65 => '新疆',
|
||
71 => '台湾',
|
||
81 => '香港',
|
||
82 => '澳门'
|
||
];
|
||
|
||
// 如果提供的代码存在于数组中,返回对应的省名称,否则返回未知
|
||
return isset($provinces[$code]) ? $provinces[$code] : '未知';
|
||
}
|
||
|
||
|
||
private function mydiv($a, $b)
|
||
{
|
||
if ($b == 0) {
|
||
return '0';
|
||
} else {
|
||
return (int)($a / $b * 100);
|
||
}
|
||
}
|
||
|
||
}
|