正在显示
8 个修改的文件
包含
230 行增加
和
7 行删除
| @@ -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; |
app/Services/BatchExportService.php
0 → 100644
| 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 | //生成路由 |
-
请 注册 或 登录 后发表评论