diff --git a/app/Core/AbstractExport.php b/app/Core/AbstractExport.php index d7025be6..b3c34722 100644 --- a/app/Core/AbstractExport.php +++ b/app/Core/AbstractExport.php @@ -26,6 +26,7 @@ abstract class AbstractExport implements WithEvents, WithTitle, ShouldAutoSize \App\Domains\Virtual\Exports\CardExport::class => '客户列表', \App\Domains\Stats\Exports\CompanyCountExport::class => '企业统计', \App\Domains\Stats\Exports\OrderExport::class => '订单统计', + \App\Domains\Stats\Exports\OrderDetailExport::class => '订单明细', ]; public $sn; @@ -39,6 +40,8 @@ abstract class AbstractExport implements WithEvents, WithTitle, ShouldAutoSize $this->sn = $this->sn(); $this->tag = $this->tag(); $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 */ - private function sn(): string + protected function sn(): string { 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 */ - private function filename(): string + protected function filename(): string { $title = $this->tag(); @@ -181,7 +184,7 @@ abstract class AbstractExport implements WithEvents, WithTitle, ShouldAutoSize * * @return void */ - private function tag() + protected function tag() { if (!$tag = self::$classes[get_class($this)]) { throw new NotAllowedException('类型不允许'); diff --git a/app/Domains/Export/Services/ExportService.php b/app/Domains/Export/Services/ExportService.php index 66986c5a..8ea68984 100644 --- a/app/Domains/Export/Services/ExportService.php +++ b/app/Domains/Export/Services/ExportService.php @@ -15,8 +15,6 @@ class ExportService extends Service 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) : ''; - $conditions = ''; - - foreach (array_except($item->conditions, ['page', 'limit', 'orderBy', 'sortedBy']) as $key => $value) { - $conditions .= "$key: $value\n"; - } + $conditions = json_encode(array_except($item->conditions, ['page', 'limit', 'orderBy', 'sortedBy']), 256); return [ '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); $url = Storage::disk($disk)->url($export->filename); return $url; diff --git a/app/Domains/Stats/Exports/CompanyCountExport.php b/app/Domains/Stats/Exports/CompanyCountExport.php index b915c3c4..25c23ec7 100644 --- a/app/Domains/Stats/Exports/CompanyCountExport.php +++ b/app/Domains/Stats/Exports/CompanyCountExport.php @@ -6,10 +6,9 @@ use App\Core\AbstractExport; use Dipper\Excel\Concerns\Exportable; use Dipper\Excel\Concerns\WithHeadings; use Dipper\Excel\Concerns\FromCollection; -use Dipper\Excel\Concerns\WithHeadingRow; use App\Domains\Stats\Services\CompanyCountService; -class CompanyCountExport extends AbstractExport implements FromCollection, WithHeadings, WithHeadingRow +class CompanyCountExport extends AbstractExport implements FromCollection, WithHeadings { public $conditions; @@ -21,8 +20,6 @@ class CompanyCountExport extends AbstractExport implements FromCollection, WithH public function collection() { - set_time_limit(-1); - $companies = app(CompanyCountService::class)->index($this->conditions)->toArray(); $companies = collect($companies); diff --git a/app/Domains/Stats/Exports/OrderDetailExport.php b/app/Domains/Stats/Exports/OrderDetailExport.php new file mode 100644 index 00000000..14b41bf9 --- /dev/null +++ b/app/Domains/Stats/Exports/OrderDetailExport.php @@ -0,0 +1,109 @@ +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; + } +} diff --git a/app/Domains/Stats/Exports/OrderExport.php b/app/Domains/Stats/Exports/OrderExport.php index df4c2e71..5a50c14b 100644 --- a/app/Domains/Stats/Exports/OrderExport.php +++ b/app/Domains/Stats/Exports/OrderExport.php @@ -4,12 +4,12 @@ namespace App\Domains\Stats\Exports; use App\Core\AbstractExport; use Dipper\Excel\Concerns\Exportable; +use App\Exceptions\NotAllowedException; use Dipper\Excel\Concerns\WithHeadings; use Dipper\Excel\Concerns\FromCollection; -use Dipper\Excel\Concerns\WithHeadingRow; use App\Domains\Stats\Services\OrderService; -class OrderExport extends AbstractExport implements FromCollection, WithHeadings, WithHeadingRow +class OrderExport extends AbstractExport implements FromCollection, WithHeadings { public $conditions; @@ -23,9 +23,6 @@ class OrderExport extends AbstractExport implements FromCollection, WithHeadings public function collection() { - set_time_limit(-1); - - $orders = app(OrderService::class)->index($this->conditions)->map(function ($item) { return collect([ '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 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"; + return $tag; } } diff --git a/app/Domains/Stats/Http/Controllers/CompanyCountController.php b/app/Domains/Stats/Http/Controllers/CompanyCountController.php index 605291ad..28dd9973 100644 --- a/app/Domains/Stats/Http/Controllers/CompanyCountController.php +++ b/app/Domains/Stats/Http/Controllers/CompanyCountController.php @@ -44,7 +44,7 @@ class CompanyCountController extends Controller */ public function export() { - $conditions = $this->request->all(); + $conditions = $this->request->except(['page', 'limit']); $conditions['limit'] = 0; try { diff --git a/app/Domains/Stats/Http/Controllers/OrderController.php b/app/Domains/Stats/Http/Controllers/OrderController.php index 8721e203..6fbbb337 100644 --- a/app/Domains/Stats/Http/Controllers/OrderController.php +++ b/app/Domains/Stats/Http/Controllers/OrderController.php @@ -2,9 +2,11 @@ namespace App\Domains\Stats\Http\Controllers; use Illuminate\Http\Request; +use App\Exceptions\NotAllowedException; use App\Domains\Stats\Exports\OrderExport; use App\Domains\Stats\Services\OrderService; use App\Domains\Export\Services\ExportService; +use App\Domains\Stats\Exports\OrderDetailExport; class OrderController extends Controller { @@ -41,7 +43,7 @@ class OrderController extends Controller */ public function export() { - $conditions = $this->request->all(); + $conditions = $this->request->except(['page', 'limit']); $conditions['limit'] = 0; try { @@ -76,12 +78,23 @@ class OrderController extends Controller */ public function detailExport() { - $conditions = $this->request->all(); - $conditions['limit'] = 0; + $conditions = $this->request->except(['page', 'limit']); + $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 { - $export = new OrderExport($conditions); - $url = ExportService::store($export, $this->disk); + $export = new OrderDetailExport($conditions); + + $queue = $total > 6000; + + $url = ExportService::store($export, $this->disk, $queue); } catch (\Exception $e) { throw $e; } diff --git a/app/Domains/Stats/Services/OrderService.php b/app/Domains/Stats/Services/OrderService.php index 0fd20be0..f7569fc4 100644 --- a/app/Domains/Stats/Services/OrderService.php +++ b/app/Domains/Stats/Services/OrderService.php @@ -16,7 +16,7 @@ class OrderService extends Service protected $packageRepository; protected $orderRepository; - protected static $classes = [ + public static $classes = [ \App\Domains\Virtual\Repositories\OrderCardRepository::class, \App\Domains\Virtual\Repositories\OrderRenewalCardRepository::class, \App\Domains\Virtual\Repositories\OrderRenewalPackageCardRepository::class, @@ -89,23 +89,36 @@ class OrderService extends Service $repository = app($class); - $companies = $this->companyRepository->withTrashed()->get()->pluck('name', 'id')->toArray(); - $packages = $this->packageRepository->withTrashed()->get()->keyBy('id')->toArray(); + $cards = $repository->withConditions($conditions)->applyConditions()->paginate($conditions['limit']); - $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]; + $order = $orders[$item->order_id]; $item->company_name = $companies[$item->company_id]; $item->package_name = $package['name']; $item->carrier_operator_name = $carrierOperators[$package['carrier_operator']]; $item->service_months = $package['service_months']; - $item->unit_price = $item->order['unit_price']; - $item->pay_channel_name = CommonService::namePayChannel($item->order['pay_channel']); - $item->order_at = $item->order['order_at']; + $item->unit_price = sprintf('%.02f', $order['unit_price']/100); + $item->pay_channel_name = CommonService::namePayChannel($order['pay_channel']); + $item->order_at = $order['order_at']; }); return $cards; diff --git a/app/Domains/Virtual/Exports/CardExport.php b/app/Domains/Virtual/Exports/CardExport.php index b7ee93a9..bb31d660 100644 --- a/app/Domains/Virtual/Exports/CardExport.php +++ b/app/Domains/Virtual/Exports/CardExport.php @@ -24,8 +24,6 @@ class CardExport extends AbstractExport implements FromQuery, WithHeadings, With public function query() { - set_time_limit(-1); - return app(OrderCardRepository::class)->withConditions($this->conditions)->applyConditions(); } diff --git a/app/Domains/Virtual/Http/Controllers/CardController.php b/app/Domains/Virtual/Http/Controllers/CardController.php index ad1fb899..09abfb29 100644 --- a/app/Domains/Virtual/Http/Controllers/CardController.php +++ b/app/Domains/Virtual/Http/Controllers/CardController.php @@ -48,8 +48,7 @@ class CardController extends Controller */ public function export() { - $conditions = $this->request->all(); - $conditions['limit'] = 0; + $conditions = $this->request->except(['page', 'limit']); if ($conditions['id']) { $conditions['id'] = intval(str_replace('No', '', $conditions['id'])); @@ -59,7 +58,8 @@ class CardController extends Controller try { $export = new CardExport($conditions); - $url = ExportService::store($export, $disk = 'public', $total); + $queue = $total > 30000; + $url = ExportService::store($export, $disk = 'public', $queue); } catch (\Exception $e) { throw $e; } diff --git a/frontend/src/service/service.js b/frontend/src/service/service.js index d65098d9..6436b617 100644 --- a/frontend/src/service/service.js +++ b/frontend/src/service/service.js @@ -6,7 +6,7 @@ let domain = window.CONFIG.url; // 创建axios实例 export const service = axios.create({ - timeout: 10000, + timeout: 1800000, headers: { post: { 'Content-Type': 'application/x-www-form-urlencoded' @@ -16,7 +16,7 @@ export const service = axios.create({ }); export const serviceForm = axios.create({ - timeout: 10000, + timeout: 1800000, headers: { post: { 'Content-Type': 'multipart/form-data' diff --git a/frontend/src/views/exports/js/index.js b/frontend/src/views/exports/js/index.js index d2db132d..3c680dd3 100644 --- a/frontend/src/views/exports/js/index.js +++ b/frontend/src/views/exports/js/index.js @@ -36,7 +36,7 @@ export default { { title: '查询条件', key: '', - width: 300, + width: 500, render: (h, { row, column, index }) => { return h('pre', row.conditions); } diff --git a/frontend/src/views/stats/order/js/detail.js b/frontend/src/views/stats/order/js/detail.js index 668616a7..d28a8916 100644 --- a/frontend/src/views/stats/order/js/detail.js +++ b/frontend/src/views/stats/order/js/detail.js @@ -95,11 +95,17 @@ export default { exportExcel() { this.isShowLoading(true); let params = this.options; - params.page = page; service.get('api/stats/order/detail/export', { params }).then((res) => { if (res.code === 0) { - this.downloadFile(res.data); + if (res.data) { + this.downloadFile(res.data); + } else { + this.$Modal.success({ + title: '提示', + content: '当前导出数据量大,已进入后台队列导出模式,请稍后至导出列表查看下载。' + }); + } } this.isShowLoading(false); diff --git a/frontend/src/views/stats/order/js/index.js b/frontend/src/views/stats/order/js/index.js index 1e353773..c0d75a78 100644 --- a/frontend/src/views/stats/order/js/index.js +++ b/frontend/src/views/stats/order/js/index.js @@ -93,7 +93,9 @@ export default { sortedBy: 'asc' }; - service.get('api/stats/order/detail', { params }).then(res => { + service.get('api/stats/order/detail', { + params + }).then(res => { this.isShowLoading(false); if (res.code == 0) { this.detailObj = { @@ -132,6 +134,7 @@ export default { * @return {[type]} [description] */ index() { + this.isShowLoading(true); this.type = Number(this.$route.params.type); this.data = []; @@ -146,8 +149,6 @@ export default { limit: 0 }, options); - this.isShowLoading(true); - service.get('api/stats/order', { params }).then(res => { @@ -223,16 +224,18 @@ export default { exportExcel() { this.isShowLoading(true); + this.type = Number(this.$route.params.type); + let options = Object.assign({ orderBy: 'company_id', - sortedBy: 'asc' + sortedBy: 'asc', + type: this.type }, - this.options); - let params = this.searchDataHandle({}, {}, options); - - this.isShowLoading(true); + let params = this.searchDataHandle({}, { + limit: 0 + }, options); service.get('api/stats/order/export', { params diff --git a/vendor/dipper/foundation/src/Repository/Repository.php b/vendor/dipper/foundation/src/Repository/Repository.php index a8536d58..20db9071 100644 --- a/vendor/dipper/foundation/src/Repository/Repository.php +++ b/vendor/dipper/foundation/src/Repository/Repository.php @@ -91,7 +91,7 @@ abstract class Repository implements RepositoryInterface, CacheableInterface, Cr */ 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) { - $field = $this->getModel()->getQualifiedKeyName(); + $field = $this->makeModel()->getQualifiedKeyName(); } elseif ($this->model instanceof Model) { - $field = $this->getModel()->getQualifiedKeyName(); + $field = $this->makeModel()->getQualifiedKeyName(); } else { $field = 'id'; } @@ -511,7 +511,7 @@ abstract class Repository implements RepositoryInterface, CacheableInterface, Cr } $this->resetModel(); - event(new RepositoryModelUpdated($this, $this->getModel())); + event(new RepositoryModelUpdated($this, $this->makeModel())); return $model; } @@ -527,7 +527,7 @@ abstract class Repository implements RepositoryInterface, CacheableInterface, Cr $this->resetModel(); - event(new RepositoryModelUpdated($this, $this->getModel())); + event(new RepositoryModelUpdated($this, $this->makeModel())); return $this->model; } @@ -545,7 +545,7 @@ abstract class Repository implements RepositoryInterface, CacheableInterface, Cr $deleted = $this->model->delete(); - event(new RepositoryModelDeleted($this, $this->getModel())); + event(new RepositoryModelDeleted($this, $this->makeModel())); $this->resetModel(); @@ -565,11 +565,11 @@ abstract class Repository implements RepositoryInterface, CacheableInterface, Cr if ($this->model instanceof Model) { $deleted = $this->model->destroy($ids); } 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(); @@ -585,7 +585,7 @@ abstract class Repository implements RepositoryInterface, CacheableInterface, Cr { $deleted = $this->model->restore(); - event(new RepositoryModelRestore($this, $this->getModel())); + event(new RepositoryModelRestore($this, $this->makeModel())); $this->resetModel(); @@ -628,7 +628,7 @@ abstract class Repository implements RepositoryInterface, CacheableInterface, Cr { if ($this->model instanceof EloquentBuilder) { 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) { $results = $this->model->newCollection(); } elseif ($this->model instanceof EloquentBuilder) { - $results = $this->getModel()->newCollection(); + $results = $this->makeModel()->newCollection(); } else { $results = collect(); }