作者 赵彬吉

update

@@ -17,3 +17,4 @@ yarn-error.log @@ -17,3 +17,4 @@ yarn-error.log
17 composer.lock 17 composer.lock
18 app/Console/Commands/Test/Demo.php 18 app/Console/Commands/Test/Demo.php
19 /public/upload 19 /public/upload
  20 +/public/runtime
@@ -2,11 +2,16 @@ @@ -2,11 +2,16 @@
2 2
3 namespace App\Http\Controllers\Bside; 3 namespace App\Http\Controllers\Bside;
4 4
  5 +use App\Exceptions\BsideGlobalException;
5 use App\Helper\Arr; 6 use App\Helper\Arr;
6 use App\Http\Logic\Bside\InquiryLogic; 7 use App\Http\Logic\Bside\InquiryLogic;
7 use App\Http\Requests\Bside\InquiryRequest; 8 use App\Http\Requests\Bside\InquiryRequest;
8 use App\Rules\Ids; 9 use App\Rules\Ids;
  10 +use App\Services\BatchExportService;
9 use Illuminate\Http\Request; 11 use Illuminate\Http\Request;
  12 +use Illuminate\Support\Facades\Storage;
  13 +use Illuminate\Validation\ValidationException;
  14 +use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
10 15
11 /** 16 /**
12 * 精准询盘 17 * 精准询盘
@@ -51,4 +56,41 @@ class InquiryController extends BaseController @@ -51,4 +56,41 @@ class InquiryController extends BaseController
51 return $this->success($data); 56 return $this->success($data);
52 } 57 }
53 58
  59 + /**
  60 + * 导出
  61 + * @param InquiryLogic $logic
  62 + * @return \Illuminate\Http\JsonResponse
  63 + * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
  64 + * @author zbj
  65 + * @date 2023/5/8
  66 + */
  67 + public function export(InquiryLogic $logic)
  68 + {
  69 + $sort = ['id' => 'desc'];
  70 + //最多到1w条
  71 + $data = $logic->getList([], $sort, ['name', 'email', 'phone', 'url', 'ip', 'ip_country', 'content', 'created_at'], 10000);
  72 + $data = $data['list'] ?? [];
  73 + foreach ($data as &$item){
  74 + $item['ip_address'] = "{$item['ip_country']}({$item['ip']})";
  75 + }
  76 +
  77 + $map = [
  78 + 'created_at' => '询盘发送时间',
  79 + 'name' => '姓名',
  80 + 'email' => '邮箱',
  81 + 'phone' => '电话',
  82 + 'ip_address' => '访问国家/地区(IP)',
  83 + 'url' => '发送页面',
  84 + 'content' => '询盘内容',
  85 + ];
  86 +
  87 + //生成文件,发送到客户端
  88 + $table = new BatchExportService("询盘数据导出");
  89 + $file = $table->head($map)->data($data)->save();
  90 + if (!$file) {
  91 + throw new \Exception('路由不能为空');
  92 + }
  93 + $fileurl = Storage::disk('runtime')->url($file);
  94 + return $this->success(['url' => $fileurl]);
  95 + }
54 } 96 }
@@ -25,11 +25,6 @@ class RouteController extends BaseController @@ -25,11 +25,6 @@ class RouteController extends BaseController
25 $source_id = $request->input('source_id'); 25 $source_id = $request->input('source_id');
26 $project_id = $this->user['project_id']; 26 $project_id = $this->user['project_id'];
27 27
28 - //有中文先翻译  
29 - if(preg_match('/[\x{4e00}-\x{9fa5}]/u', $title)){  
30 - $title = Translate::tran($title, 'en');  
31 - }  
32 -  
33 $route = RouteMap::generateRoute($title, $source, $source_id, $project_id); 28 $route = RouteMap::generateRoute($title, $source, $source_id, $project_id);
34 return $this->success(['route' => $route]); 29 return $this->success(['route' => $route]);
35 } 30 }
@@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
2 2
3 namespace App\Models; 3 namespace App\Models;
4 4
  5 +use App\Helper\Translate;
5 use Illuminate\Database\Eloquent\Model; 6 use Illuminate\Database\Eloquent\Model;
6 use Illuminate\Support\Facades\Log; 7 use Illuminate\Support\Facades\Log;
7 8
@@ -36,6 +37,11 @@ class RouteMap extends Model @@ -36,6 +37,11 @@ class RouteMap extends Model
36 * @date 2023/4/17 37 * @date 2023/4/17
37 */ 38 */
38 public static function generateRoute($title, $source, $source_id, $project_id){ 39 public static function generateRoute($title, $source, $source_id, $project_id){
  40 + //有中文先翻译
  41 + if(preg_match('/[\x{4e00}-\x{9fa5}]/u', $title)){
  42 + $title = Translate::tran($title, 'en');
  43 + }
  44 +
39 $i=1; 45 $i=1;
40 $sign = generateRoute($title); 46 $sign = generateRoute($title);
41 $route = $sign; 47 $route = $sign;
  1 +<?php
  2 +
  3 +namespace App\Services;
  4 +
  5 +use Illuminate\Support\Facades\File;
  6 +use Illuminate\Support\Facades\Storage;
  7 +use PhpOffice\PhpSpreadsheet\Spreadsheet;
  8 +use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
  9 +
  10 +/**
  11 + * 批量导出
  12 + * Class BatchExportService
  13 + * @package App\Services
  14 + * @author zbj
  15 + * @date 2023/5/8
  16 + */
  17 +class BatchExportService
  18 +{
  19 +
  20 + private $name = '';
  21 + /**
  22 + * @var array
  23 + */
  24 + private $data = [];
  25 +
  26 +
  27 + /**
  28 + * @var null | \PhpOffice\PhpSpreadsheet\Spreadsheet;
  29 + */
  30 + private $spreadsheet = null;
  31 +
  32 +
  33 + /**
  34 + * 包头与字段映射
  35 + * @var array
  36 + */
  37 + private $head = [];
  38 +
  39 +
  40 + public function __construct($name)
  41 + {
  42 + $this->name = $name;
  43 + $this->spreadsheet = new Spreadsheet();
  44 + $this->spreadsheet->setActiveSheetIndex(0);
  45 + }
  46 +
  47 + /**
  48 + * config hook
  49 + * @param $callback
  50 + * @return $this
  51 + * @throws \PhpOffice\PhpSpreadsheet\Exception
  52 + */
  53 + public function config($callback)
  54 + {
  55 + if(is_callable($callback)) {
  56 + $callback($this->spreadsheet->getActiveSheet(),$this->spreadsheet);
  57 + }
  58 + return $this;
  59 + }
  60 +
  61 + /**
  62 + * 设置表头
  63 + * @param array $data
  64 + * @return $this
  65 + */
  66 + public function head($map = [])
  67 + {
  68 + $this->head = $map;
  69 + return $this;
  70 + }
  71 +
  72 + /**
  73 + * @param $data
  74 + * @return $this
  75 + */
  76 + public function data($data)
  77 + {
  78 + $this->data = $data;
  79 + return $this;
  80 + }
  81 +
  82 + /**
  83 + * @return false|string
  84 + * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
  85 + * @author zbj
  86 + * @date 2023/5/8
  87 + */
  88 + public function save()
  89 + {
  90 + $file = "runtime/export/".date('Y-m-d')."/{$this->name}_" . date('YmdHis') . ".xlsx";
  91 + $filename = public_path($file);
  92 +
  93 + if (!is_dir(dirname($filename))) {
  94 + @mkdir(dirname($filename),0755, true);
  95 + }
  96 + $this->render();
  97 +
  98 + $writer = new Xlsx($this->spreadsheet);
  99 + $writer->save($filename);
  100 + if(file_exists($filename) && filesize($filename)) {
  101 + return $file;
  102 + }
  103 + return false;
  104 + }
  105 +
  106 +
  107 + /**
  108 + * 进行数据渲染
  109 + * 遍历所有的单元格处理文件,进行数据处理
  110 + * @return $this
  111 + * @author zbj
  112 + * @date 2023/5/8
  113 + */
  114 + protected function render()
  115 + {
  116 +
  117 + $map = [];
  118 + $sheet = $this->spreadsheet->getActiveSheet();
  119 + $i = 0;
  120 + foreach ($this->head as $k=>$title) {
  121 + $chr = chr(65+$i);
  122 + $map[$chr] = $k;
  123 + if(is_array($title)) {
  124 + $title = $title[0];
  125 + }
  126 + $sheet->setCellValue($chr . "1", $title);
  127 + $i++;
  128 + }
  129 + foreach ($this->data as $k=>$item) {
  130 + foreach ($map as $chr => $field) {
  131 + if(is_array($this->head[$field]) && is_callable($this->head[$field][1])) { //TODO 进行字段内容的处理
  132 + $this->head[$field][1]($sheet, $chr . ($k + 2), $item[$field]);
  133 + }
  134 + $sheet->setCellValue($chr . ($k + 2), $this->getDataValue($field, $item));
  135 + }
  136 + }
  137 + return $this;
  138 + }
  139 +
  140 + /**
  141 + * 读取指定字段的value, 支持"."形式指定多维数组
  142 + * @param $name
  143 + * @param $data
  144 + * @return mixed|null
  145 + * @author zbj
  146 + * @date 2023/5/8
  147 + */
  148 + protected function getDataValue($name, $data)
  149 + {
  150 + if(false === strpos($name, '.')) {
  151 + return $data[$name] ?? null;
  152 + }
  153 + $name = explode('.', $name);
  154 + $name[0] = strtolower($name[0]);
  155 +
  156 + // 按.拆分成多维数组进行判断
  157 + foreach ($name as $val) {
  158 + if (isset($data[$val])) {
  159 + $data = $data[$val];
  160 + } else {
  161 + $data = null;
  162 + break;
  163 + }
  164 + }
  165 + return $data;
  166 + }
  167 +}
@@ -13,7 +13,8 @@ @@ -13,7 +13,8 @@
13 "intervention/image": "^2.7", 13 "intervention/image": "^2.7",
14 "laravel/framework": "^8.75", 14 "laravel/framework": "^8.75",
15 "laravel/sanctum": "^2.11", 15 "laravel/sanctum": "^2.11",
16 - "laravel/tinker": "^2.5" 16 + "laravel/tinker": "^2.5",
  17 + "phpoffice/phpspreadsheet": "^1.28"
17 }, 18 },
18 "require-dev": { 19 "require-dev": {
19 "facade/ignition": "^2.5", 20 "facade/ignition": "^2.5",
@@ -58,7 +58,17 @@ return [ @@ -58,7 +58,17 @@ return [
58 'root' => env('UPLOAD_ROOT_PATH') ?: public_path('upload'), 58 'root' => env('UPLOAD_ROOT_PATH') ?: public_path('upload'),
59 'url' => env('UPLOAD_LOCAL_URL') ?: env('APP_URL') . '/upload', 59 'url' => env('UPLOAD_LOCAL_URL') ?: env('APP_URL') . '/upload',
60 'visibility' => 'public', 60 'visibility' => 'public',
61 - ] 61 + ],
  62 +
  63 + 'runtime' => [
  64 + 'driver' => 'local',
  65 + 'root' => public_path('runtime'),
  66 + 'url' => env('APP_URL'),
  67 + 'visibility' => 'public',
  68 + ],
  69 +
  70 +
  71 +
62 72
63 ], 73 ],
64 74
@@ -187,6 +187,7 @@ Route::middleware(['bloginauth'])->group(function () { @@ -187,6 +187,7 @@ Route::middleware(['bloginauth'])->group(function () {
187 Route::get('/', [\App\Http\Controllers\Bside\InquiryController::class, 'index'])->name('inquiry'); 187 Route::get('/', [\App\Http\Controllers\Bside\InquiryController::class, 'index'])->name('inquiry');
188 Route::get('/info', [\App\Http\Controllers\Bside\InquiryController::class, 'info'])->name('inquiry_info'); 188 Route::get('/info', [\App\Http\Controllers\Bside\InquiryController::class, 'info'])->name('inquiry_info');
189 Route::any('/delete', [\App\Http\Controllers\Bside\InquiryController::class, 'delete'])->name('inquiry_delete'); 189 Route::any('/delete', [\App\Http\Controllers\Bside\InquiryController::class, 'delete'])->name('inquiry_delete');
  190 + Route::any('/export', [\App\Http\Controllers\Bside\InquiryController::class, 'export'])->name('inquiry_export');
190 }); 191 });
191 192
192 //生成路由 193 //生成路由