This commit is contained in:
邓皓元 2019-01-09 10:42:33 +08:00
parent d56d4e4047
commit 767f575333
15 changed files with 206 additions and 87 deletions

View File

@ -26,6 +26,7 @@ abstract class AbstractExport implements WithEvents, WithTitle, ShouldAutoSize
\App\Domains\Virtual\Exports\CardExport::class => '客户列表', \App\Domains\Virtual\Exports\CardExport::class => '客户列表',
\App\Domains\Stats\Exports\CompanyCountExport::class => '企业统计', \App\Domains\Stats\Exports\CompanyCountExport::class => '企业统计',
\App\Domains\Stats\Exports\OrderExport::class => '订单统计', \App\Domains\Stats\Exports\OrderExport::class => '订单统计',
\App\Domains\Stats\Exports\OrderDetailExport::class => '订单明细',
]; ];
public $sn; public $sn;
@ -39,6 +40,8 @@ abstract class AbstractExport implements WithEvents, WithTitle, ShouldAutoSize
$this->sn = $this->sn(); $this->sn = $this->sn();
$this->tag = $this->tag(); $this->tag = $this->tag();
$this->filename = $this->filename(); $this->filename = $this->filename();
set_time_limit(-1);
ini_set('memory_limit', '4096m');
} }
/** /**
@ -157,7 +160,7 @@ abstract class AbstractExport implements WithEvents, WithTitle, ShouldAutoSize
* *
* @return string * @return string
*/ */
private function sn(): string protected function sn(): string
{ {
return date('YmdHis') .sprintf('%04d', explode('.', microtime(true))[1]) . sprintf('%02d', rand(0, 99)); return date('YmdHis') .sprintf('%04d', explode('.', microtime(true))[1]) . sprintf('%02d', rand(0, 99));
} }
@ -167,7 +170,7 @@ abstract class AbstractExport implements WithEvents, WithTitle, ShouldAutoSize
* *
* @return string * @return string
*/ */
private function filename(): string protected function filename(): string
{ {
$title = $this->tag(); $title = $this->tag();
@ -181,7 +184,7 @@ abstract class AbstractExport implements WithEvents, WithTitle, ShouldAutoSize
* *
* @return void * @return void
*/ */
private function tag() protected function tag()
{ {
if (!$tag = self::$classes[get_class($this)]) { if (!$tag = self::$classes[get_class($this)]) {
throw new NotAllowedException('类型不允许'); throw new NotAllowedException('类型不允许');

View File

@ -15,8 +15,6 @@ class ExportService extends Service
protected static $status = ['数据准备中', '开始写入', '写入结束', '保存成功', '任务失败']; protected static $status = ['数据准备中', '开始写入', '写入结束', '保存成功', '任务失败'];
protected static $maxRow = 30000;
/** /**
* 构造函数 * 构造函数
* *
@ -52,11 +50,7 @@ class ExportService extends Service
$url = $item->status === 3 ? Storage::disk($item->disk)->url($item->filename) : ''; $url = $item->status === 3 ? Storage::disk($item->disk)->url($item->filename) : '';
$conditions = ''; $conditions = json_encode(array_except($item->conditions, ['page', 'limit', 'orderBy', 'sortedBy']), 256);
foreach (array_except($item->conditions, ['page', 'limit', 'orderBy', 'sortedBy']) as $key => $value) {
$conditions .= "$key: $value\n";
}
return [ return [
'id' => $item->id, 'id' => $item->id,
@ -92,9 +86,9 @@ class ExportService extends Service
/** /**
* 导出 * 导出
*/ */
public static function store($export, $disk, $total = 0) public static function store($export, $disk, $queue = false)
{ {
if ($total < self::$maxRow) { if (!$queue) {
Excel::store($export, $export->filename, $disk); Excel::store($export, $export->filename, $disk);
$url = Storage::disk($disk)->url($export->filename); $url = Storage::disk($disk)->url($export->filename);
return $url; return $url;

View File

@ -6,10 +6,9 @@ use App\Core\AbstractExport;
use Dipper\Excel\Concerns\Exportable; use Dipper\Excel\Concerns\Exportable;
use Dipper\Excel\Concerns\WithHeadings; use Dipper\Excel\Concerns\WithHeadings;
use Dipper\Excel\Concerns\FromCollection; use Dipper\Excel\Concerns\FromCollection;
use Dipper\Excel\Concerns\WithHeadingRow;
use App\Domains\Stats\Services\CompanyCountService; use App\Domains\Stats\Services\CompanyCountService;
class CompanyCountExport extends AbstractExport implements FromCollection, WithHeadings, WithHeadingRow class CompanyCountExport extends AbstractExport implements FromCollection, WithHeadings
{ {
public $conditions; public $conditions;
@ -21,8 +20,6 @@ class CompanyCountExport extends AbstractExport implements FromCollection, WithH
public function collection() public function collection()
{ {
set_time_limit(-1);
$companies = app(CompanyCountService::class)->index($this->conditions)->toArray(); $companies = app(CompanyCountService::class)->index($this->conditions)->toArray();
$companies = collect($companies); $companies = collect($companies);

View File

@ -0,0 +1,109 @@
<?php
namespace App\Domains\Stats\Exports;
use App\Core\AbstractExport;
use Illuminate\Support\Collection;
use Dipper\Excel\Concerns\WithRows;
use Dipper\Excel\Concerns\FromQuery;
use Dipper\Excel\Concerns\Exportable;
use App\Exceptions\NotAllowedException;
use Dipper\Excel\Concerns\WithHeadings;
use Illuminate\Database\Eloquent\Builder;
use App\Domains\Stats\Services\OrderService;
use Dipper\Excel\Concerns\WithColumnFormatting;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
class OrderDetailExport extends AbstractExport implements FromQuery, WithHeadings, WithRows, WithColumnFormatting
{
public $conditions;
public static $types = ['销售', '续费', '续费包', '加油包'];
public function __construct(array $conditions = [])
{
$this->conditions = $conditions;
parent::__construct();
}
/**
* @return Builder
*/
public function query()
{
if (!$class = OrderService::$classes[$this->conditions['type']]) {
throw new NotAllowedException('统计类型不存在');
}
$repository = app($class);
$builder = $repository->withConditions($this->conditions)->applyConditions()->getModel();
return $builder;
}
public function headings(): array
{
return [
'SIM',
'企业名称',
'套餐名称',
'套餐周期',
'支付方式',
'价格',
'订单时间',
];
}
/**
* @param mixed $row
*
* @return mixed
*/
public function rows($rows)
{
$rows = OrderService::detailTransformer($rows);
$rows->transform(function ($item) {
return [
$item['sim'],
$item['company_name'],
$item['package_name'],
$item['service_months'],
$item['pay_channel_name'],
$item['unit_price'],
$item['order_at'],
];
});
return $rows;
}
/**
* @return array
*/
public function columnFormats(): array
{
return [
'A' => NumberFormat::FORMAT_NUMBER,
'F' => NumberFormat::FORMAT_NUMBER_00,
];
}
/**
* 类型
*
* @return void
*/
protected function tag()
{
if ((!$tag = self::$classes[get_class($this)]) || !self::$types[$this->conditions['type']]) {
throw new NotAllowedException('类型不允许');
}
$tag = self::$types[$this->conditions['type']] . $tag;
return $tag;
}
}

View File

@ -4,12 +4,12 @@ namespace App\Domains\Stats\Exports;
use App\Core\AbstractExport; use App\Core\AbstractExport;
use Dipper\Excel\Concerns\Exportable; use Dipper\Excel\Concerns\Exportable;
use App\Exceptions\NotAllowedException;
use Dipper\Excel\Concerns\WithHeadings; use Dipper\Excel\Concerns\WithHeadings;
use Dipper\Excel\Concerns\FromCollection; use Dipper\Excel\Concerns\FromCollection;
use Dipper\Excel\Concerns\WithHeadingRow;
use App\Domains\Stats\Services\OrderService; use App\Domains\Stats\Services\OrderService;
class OrderExport extends AbstractExport implements FromCollection, WithHeadings, WithHeadingRow class OrderExport extends AbstractExport implements FromCollection, WithHeadings
{ {
public $conditions; public $conditions;
@ -23,9 +23,6 @@ class OrderExport extends AbstractExport implements FromCollection, WithHeadings
public function collection() public function collection()
{ {
set_time_limit(-1);
$orders = app(OrderService::class)->index($this->conditions)->map(function ($item) { $orders = app(OrderService::class)->index($this->conditions)->map(function ($item) {
return collect([ return collect([
'company_name' => $item->company_name, 'company_name' => $item->company_name,
@ -62,32 +59,18 @@ class OrderExport extends AbstractExport implements FromCollection, WithHeadings
} }
/** /**
* 表格标题 * 类型
* *
* @return string * @return void
*/ */
public function title(): string protected function tag()
{ {
$title = parent::title(); if ((!$tag = self::$classes[get_class($this)]) || !self::$types[$this->conditions['type']]) {
throw new NotAllowedException('类型不允许');
}
$title = self::$types[$this->conditions['type']] . $title; $tag = self::$types[$this->conditions['type']] . $tag;
return $title; return $tag;
}
/**
* 文件名称
*
* @return string
*/
private function filename(): string
{
$title = self::$classes[get_class($this)];
$filename = $title . date('YmdHis');
$filename = self::$types[$this->conditions['type']] . $filename;
return "export/{$filename}.xlsx";
} }
} }

View File

@ -44,7 +44,7 @@ class CompanyCountController extends Controller
*/ */
public function export() public function export()
{ {
$conditions = $this->request->all(); $conditions = $this->request->except(['page', 'limit']);
$conditions['limit'] = 0; $conditions['limit'] = 0;
try { try {

View File

@ -2,9 +2,11 @@
namespace App\Domains\Stats\Http\Controllers; namespace App\Domains\Stats\Http\Controllers;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Exceptions\NotAllowedException;
use App\Domains\Stats\Exports\OrderExport; use App\Domains\Stats\Exports\OrderExport;
use App\Domains\Stats\Services\OrderService; use App\Domains\Stats\Services\OrderService;
use App\Domains\Export\Services\ExportService; use App\Domains\Export\Services\ExportService;
use App\Domains\Stats\Exports\OrderDetailExport;
class OrderController extends Controller class OrderController extends Controller
{ {
@ -41,7 +43,7 @@ class OrderController extends Controller
*/ */
public function export() public function export()
{ {
$conditions = $this->request->all(); $conditions = $this->request->except(['page', 'limit']);
$conditions['limit'] = 0; $conditions['limit'] = 0;
try { try {
@ -76,12 +78,23 @@ class OrderController extends Controller
*/ */
public function detailExport() public function detailExport()
{ {
$conditions = $this->request->all(); $conditions = $this->request->except(['page', 'limit']);
$conditions['limit'] = 0; $conditions['order_id'] = $this->request->ids('order_id');
if (!$class = OrderService::$classes[$conditions['type']]) {
throw new NotAllowedException('统计类型不存在');
}
$repository = app($class);
$total = $repository->withConditions($conditions)->applyConditions()->getCountForPagination();
try { try {
$export = new OrderExport($conditions); $export = new OrderDetailExport($conditions);
$url = ExportService::store($export, $this->disk);
$queue = $total > 6000;
$url = ExportService::store($export, $this->disk, $queue);
} catch (\Exception $e) { } catch (\Exception $e) {
throw $e; throw $e;
} }

View File

@ -16,7 +16,7 @@ class OrderService extends Service
protected $packageRepository; protected $packageRepository;
protected $orderRepository; protected $orderRepository;
protected static $classes = [ public static $classes = [
\App\Domains\Virtual\Repositories\OrderCardRepository::class, \App\Domains\Virtual\Repositories\OrderCardRepository::class,
\App\Domains\Virtual\Repositories\OrderRenewalCardRepository::class, \App\Domains\Virtual\Repositories\OrderRenewalCardRepository::class,
\App\Domains\Virtual\Repositories\OrderRenewalPackageCardRepository::class, \App\Domains\Virtual\Repositories\OrderRenewalPackageCardRepository::class,
@ -89,23 +89,36 @@ class OrderService extends Service
$repository = app($class); $repository = app($class);
$companies = $this->companyRepository->withTrashed()->get()->pluck('name', 'id')->toArray(); $cards = $repository->withConditions($conditions)->applyConditions()->paginate($conditions['limit']);
$packages = $this->packageRepository->withTrashed()->get()->keyBy('id')->toArray();
$carrierOperators = app(Dicts::class)->get('carrier_operator'); return self::detailTransformer($cards);
}
$cards = $repository->with('order:id,unit_price,pay_channel,order_at')->withConditions($conditions)->applyConditions()->paginate($conditions['limit']); /**
* 格式转化
*
* @param mixed $cards
* @return mixed
*/
public static function detailTransformer($cards)
{
$companies = app(CompanyRepository::class)->withTrashed()->get()->pluck('name', 'id')->toArray();
$packages = app(PackageRepository::class)->withTrashed()->get()->keyBy('id')->toArray();
$orders = app(OrderRepository::class)->withTrashed()
->select(['id', 'unit_price', 'pay_channel', 'order_at'])
->withConditions(['id' => array_unique($cards->pluck('order_id')->toArray())])->get()->keyBy('id')->toArray();
$cards->map(function ($item) use ($companies, $packages, $carrierOperators) { $cards->map(function ($item) use ($companies, $packages, $carrierOperators, $orders) {
$package = $packages[$item->package_id]; $package = $packages[$item->package_id];
$order = $orders[$item->order_id];
$item->company_name = $companies[$item->company_id]; $item->company_name = $companies[$item->company_id];
$item->package_name = $package['name']; $item->package_name = $package['name'];
$item->carrier_operator_name = $carrierOperators[$package['carrier_operator']]; $item->carrier_operator_name = $carrierOperators[$package['carrier_operator']];
$item->service_months = $package['service_months']; $item->service_months = $package['service_months'];
$item->unit_price = $item->order['unit_price']; $item->unit_price = sprintf('%.02f', $order['unit_price']/100);
$item->pay_channel_name = CommonService::namePayChannel($item->order['pay_channel']); $item->pay_channel_name = CommonService::namePayChannel($order['pay_channel']);
$item->order_at = $item->order['order_at']; $item->order_at = $order['order_at'];
}); });
return $cards; return $cards;

View File

@ -24,8 +24,6 @@ class CardExport extends AbstractExport implements FromQuery, WithHeadings, With
public function query() public function query()
{ {
set_time_limit(-1);
return app(OrderCardRepository::class)->withConditions($this->conditions)->applyConditions(); return app(OrderCardRepository::class)->withConditions($this->conditions)->applyConditions();
} }

View File

@ -48,8 +48,7 @@ class CardController extends Controller
*/ */
public function export() public function export()
{ {
$conditions = $this->request->all(); $conditions = $this->request->except(['page', 'limit']);
$conditions['limit'] = 0;
if ($conditions['id']) { if ($conditions['id']) {
$conditions['id'] = intval(str_replace('No', '', $conditions['id'])); $conditions['id'] = intval(str_replace('No', '', $conditions['id']));
@ -59,7 +58,8 @@ class CardController extends Controller
try { try {
$export = new CardExport($conditions); $export = new CardExport($conditions);
$url = ExportService::store($export, $disk = 'public', $total); $queue = $total > 30000;
$url = ExportService::store($export, $disk = 'public', $queue);
} catch (\Exception $e) { } catch (\Exception $e) {
throw $e; throw $e;
} }

View File

@ -6,7 +6,7 @@ let domain = window.CONFIG.url;
// 创建axios实例 // 创建axios实例
export const service = axios.create({ export const service = axios.create({
timeout: 10000, timeout: 1800000,
headers: { headers: {
post: { post: {
'Content-Type': 'application/x-www-form-urlencoded' 'Content-Type': 'application/x-www-form-urlencoded'
@ -16,7 +16,7 @@ export const service = axios.create({
}); });
export const serviceForm = axios.create({ export const serviceForm = axios.create({
timeout: 10000, timeout: 1800000,
headers: { headers: {
post: { post: {
'Content-Type': 'multipart/form-data' 'Content-Type': 'multipart/form-data'

View File

@ -36,7 +36,7 @@ export default {
{ {
title: '查询条件', title: '查询条件',
key: '', key: '',
width: 300, width: 500,
render: (h, { row, column, index }) => { render: (h, { row, column, index }) => {
return h('pre', row.conditions); return h('pre', row.conditions);
} }

View File

@ -95,11 +95,17 @@ export default {
exportExcel() { exportExcel() {
this.isShowLoading(true); this.isShowLoading(true);
let params = this.options; let params = this.options;
params.page = page;
service.get('api/stats/order/detail/export', { params }).then((res) => { service.get('api/stats/order/detail/export', { params }).then((res) => {
if (res.code === 0) { if (res.code === 0) {
this.downloadFile(res.data); if (res.data) {
this.downloadFile(res.data);
} else {
this.$Modal.success({
title: '提示',
content: '当前导出数据量大,已进入后台队列导出模式,请稍后至导出列表查看下载。'
});
}
} }
this.isShowLoading(false); this.isShowLoading(false);

View File

@ -93,7 +93,9 @@ export default {
sortedBy: 'asc' sortedBy: 'asc'
}; };
service.get('api/stats/order/detail', { params }).then(res => { service.get('api/stats/order/detail', {
params
}).then(res => {
this.isShowLoading(false); this.isShowLoading(false);
if (res.code == 0) { if (res.code == 0) {
this.detailObj = { this.detailObj = {
@ -132,6 +134,7 @@ export default {
* @return {[type]} [description] * @return {[type]} [description]
*/ */
index() { index() {
this.isShowLoading(true);
this.type = Number(this.$route.params.type); this.type = Number(this.$route.params.type);
this.data = []; this.data = [];
@ -146,8 +149,6 @@ export default {
limit: 0 limit: 0
}, options); }, options);
this.isShowLoading(true);
service.get('api/stats/order', { service.get('api/stats/order', {
params params
}).then(res => { }).then(res => {
@ -223,16 +224,18 @@ export default {
exportExcel() { exportExcel() {
this.isShowLoading(true); this.isShowLoading(true);
this.type = Number(this.$route.params.type);
let options = Object.assign({ let options = Object.assign({
orderBy: 'company_id', orderBy: 'company_id',
sortedBy: 'asc' sortedBy: 'asc',
type: this.type
}, },
this.options); this.options);
let params = this.searchDataHandle({}, {}, options); let params = this.searchDataHandle({}, {
limit: 0
this.isShowLoading(true); }, options);
service.get('api/stats/order/export', { service.get('api/stats/order/export', {
params params

View File

@ -91,7 +91,7 @@ abstract class Repository implements RepositoryInterface, CacheableInterface, Cr
*/ */
public function getModel() public function getModel()
{ {
return $this->makeModel(); return $this->model;
} }
/** /**
@ -279,9 +279,9 @@ abstract class Repository implements RepositoryInterface, CacheableInterface, Cr
} }
if ($this->model instanceof EloquentBuilder) { if ($this->model instanceof EloquentBuilder) {
$field = $this->getModel()->getQualifiedKeyName(); $field = $this->makeModel()->getQualifiedKeyName();
} elseif ($this->model instanceof Model) { } elseif ($this->model instanceof Model) {
$field = $this->getModel()->getQualifiedKeyName(); $field = $this->makeModel()->getQualifiedKeyName();
} else { } else {
$field = 'id'; $field = 'id';
} }
@ -511,7 +511,7 @@ abstract class Repository implements RepositoryInterface, CacheableInterface, Cr
} }
$this->resetModel(); $this->resetModel();
event(new RepositoryModelUpdated($this, $this->getModel())); event(new RepositoryModelUpdated($this, $this->makeModel()));
return $model; return $model;
} }
@ -527,7 +527,7 @@ abstract class Repository implements RepositoryInterface, CacheableInterface, Cr
$this->resetModel(); $this->resetModel();
event(new RepositoryModelUpdated($this, $this->getModel())); event(new RepositoryModelUpdated($this, $this->makeModel()));
return $this->model; return $this->model;
} }
@ -545,7 +545,7 @@ abstract class Repository implements RepositoryInterface, CacheableInterface, Cr
$deleted = $this->model->delete(); $deleted = $this->model->delete();
event(new RepositoryModelDeleted($this, $this->getModel())); event(new RepositoryModelDeleted($this, $this->makeModel()));
$this->resetModel(); $this->resetModel();
@ -565,11 +565,11 @@ abstract class Repository implements RepositoryInterface, CacheableInterface, Cr
if ($this->model instanceof Model) { if ($this->model instanceof Model) {
$deleted = $this->model->destroy($ids); $deleted = $this->model->destroy($ids);
} else { } else {
$this->model->whereIn($this->getModel()->getKeyName(), $ids)->delete(); $this->model->whereIn($this->makeModel()->getKeyName(), $ids)->delete();
} }
event(new RepositoryModelDeleted($this, $this->getModel())); event(new RepositoryModelDeleted($this, $this->makeModel()));
$this->resetModel(); $this->resetModel();
@ -585,7 +585,7 @@ abstract class Repository implements RepositoryInterface, CacheableInterface, Cr
{ {
$deleted = $this->model->restore(); $deleted = $this->model->restore();
event(new RepositoryModelRestore($this, $this->getModel())); event(new RepositoryModelRestore($this, $this->makeModel()));
$this->resetModel(); $this->resetModel();
@ -628,7 +628,7 @@ abstract class Repository implements RepositoryInterface, CacheableInterface, Cr
{ {
if ($this->model instanceof EloquentBuilder) { if ($this->model instanceof EloquentBuilder) {
if (empty($this->model->getQuery()->orders) && empty($this->model->getQuery()->unionOrders)) { if (empty($this->model->getQuery()->orders) && empty($this->model->getQuery()->unionOrders)) {
$this->orderBy($this->getModel()->getQualifiedKeyName(), 'asc'); $this->orderBy($this->makeModel()->getQualifiedKeyName(), 'asc');
} }
} }
} }
@ -643,7 +643,7 @@ abstract class Repository implements RepositoryInterface, CacheableInterface, Cr
if ($this->model instanceof Model) { if ($this->model instanceof Model) {
$results = $this->model->newCollection(); $results = $this->model->newCollection();
} elseif ($this->model instanceof EloquentBuilder) { } elseif ($this->model instanceof EloquentBuilder) {
$results = $this->getModel()->newCollection(); $results = $this->makeModel()->newCollection();
} else { } else {
$results = collect(); $results = collect();
} }