allocatr/application/admin/controller/Orderplan.php
2025-07-07 14:42:30 +08:00

674 lines
19 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;
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);
}
}
}