完成导入
parent
f7d8af798c
commit
d96d0106c8
|
|
@ -7,7 +7,6 @@ use Slowlyo\OwlAdmin\Models\AdminSetting;
|
|||
use Slowlyo\OwlAdmin\Renderers\Page;
|
||||
use Slowlyo\OwlAdmin\Renderers\Form;
|
||||
use Slowlyo\OwlAdmin\Renderers\TableColumn;
|
||||
use Slowlyo\OwlAdmin\Renderers\TextControl;
|
||||
use Slowlyo\OwlAdmin\Controllers\AdminController;
|
||||
use App\Services\Admin\OldmenService;
|
||||
use Illuminate\Http\Request;
|
||||
|
|
@ -15,6 +14,9 @@ use App\Admin\Components;
|
|||
use App\Models\ConstFlow;
|
||||
use App\Models\Oldmen;
|
||||
use Carbon\Carbon;
|
||||
use App\Services\Admin\ImportService;
|
||||
use App\Models\ImportJob;
|
||||
use App\Imports\Oldmen as OldmenImport;
|
||||
|
||||
/**
|
||||
* @property OldmenService $service
|
||||
|
|
@ -40,7 +42,7 @@ class OldmenController extends AdminController
|
|||
amisMake()->DialogAction()->dialog(
|
||||
amisMake()->Dialog()->title('导入客人信息')->body([
|
||||
amisMake()->Form()->title('')
|
||||
->api('')//处理实际上传逻辑-todo
|
||||
->api(admin_url('oldmen-import'))//处理实际上传逻辑
|
||||
->body([
|
||||
amisMake()->FileControl('file', '导入文件')->accept('.xlsx')->receiver('/upload_file'),//文件上传地址待处理
|
||||
]),
|
||||
|
|
@ -502,4 +504,19 @@ class OldmenController extends AdminController
|
|||
}
|
||||
return $this->response()->success($page);
|
||||
}
|
||||
|
||||
public function import(Request $request){
|
||||
|
||||
|
||||
$job = new ImportJob();
|
||||
$job->name = '客人基础信息导入【'.now()->toDateTimeString().'】';
|
||||
$job->file = $request->input('file');
|
||||
$job->type = OldmenImport::class;
|
||||
$job->status = 1;
|
||||
$job->save();
|
||||
$importService = new ImportService();
|
||||
$importService->import($job);
|
||||
|
||||
return $this->response()->success();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ Route::group([
|
|||
|
||||
//客人管理
|
||||
$router->resource('oldmen', \App\Admin\Controllers\OldmenController::class)->names('oldmen');
|
||||
$router->post('oldmen-import', '\App\Admin\Controllers\OldmenController@import');
|
||||
|
||||
$router->get('live-feelist', '\App\Admin\Controllers\OldmenController@liveFeelist');
|
||||
$router->get('exit-feelist', '\App\Admin\Controllers\OldmenController@exitFeelist');
|
||||
$router->get('live-fee-form', '\App\Admin\Controllers\OldmenController@liveSchemaForm');
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class BizException extends Exception
|
||||
{
|
||||
/**
|
||||
* 用于响应的 HTTP 状态代码
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $status = 400;
|
||||
|
||||
/**
|
||||
* 设置用于响应的 HTTP 状态代码
|
||||
*
|
||||
* @param int $status
|
||||
* @return $this
|
||||
*/
|
||||
public function status(int $status)
|
||||
{
|
||||
$this->status = $status;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 报告异常
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function report()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
class ImportException extends BizException
|
||||
{
|
||||
public function __construct(string $message = '导入出错')
|
||||
{
|
||||
parent::__construct($message);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace App\Imports;
|
||||
|
||||
use App\Exceptions\ImportException;
|
||||
use Box\Spout\Reader\Common\Creator\ReaderEntityFactory;
|
||||
use Illuminate\Http\File;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Throwable;
|
||||
|
||||
class ImportBase
|
||||
{
|
||||
public function readFileByUrl($url)
|
||||
{
|
||||
$file_name = substr($url, strrpos($url, '/') + 1);
|
||||
|
||||
$path = DIRECTORY_SEPARATOR.'import'.DIRECTORY_SEPARATOR.date('Y-m-d').DIRECTORY_SEPARATOR.'import'.$file_name;
|
||||
Storage::disk('local')->put($path, file_get_contents(str_replace(config('filesystems.disks.aliyun.domain'), config('filesystems.disks.aliyun.bucket').'.'.config('filesystems.disks.aliyun.endpoint'), $url)));
|
||||
$file = new File(storage_path('app'.$path));
|
||||
return $this->readFile($file);
|
||||
}
|
||||
|
||||
public function readFile($file)
|
||||
{
|
||||
$reader = ReaderEntityFactory::createXLSXReader();
|
||||
$reader->open($file);
|
||||
|
||||
$success = 0;
|
||||
$fails = 0;
|
||||
$errors = [];
|
||||
|
||||
foreach ($reader->getSheetIterator() as $sheet) {
|
||||
foreach ($sheet->getRowIterator() as $num => $row) {
|
||||
if ($num === 1) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
$this->loadRow($row);
|
||||
$success++;
|
||||
DB::commit();
|
||||
} catch (ImportException $e) {
|
||||
DB::rollBack();
|
||||
$fails++;
|
||||
$errors[] = [
|
||||
'row'=>$num,
|
||||
'reason'=>$e->getMessage(),
|
||||
];
|
||||
} catch (Throwable $e) {
|
||||
DB::rollBack();
|
||||
$fails++;
|
||||
$errors[] = [
|
||||
'row'=>$num,
|
||||
'reason'=>$e->getMessage(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$reader->close();
|
||||
return [
|
||||
'success'=>$success,
|
||||
'fails'=>$fails,
|
||||
'errors'=>$errors,
|
||||
];
|
||||
}
|
||||
|
||||
public function loadRow($row)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace App\Imports;
|
||||
|
||||
use App\Exceptions\ImportException;
|
||||
use App\Models\Keyword;
|
||||
use App\Models\Oldmen as ModelsOldmen;
|
||||
use App\Models\Zone;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class Oldmen extends ImportBase
|
||||
{
|
||||
public function loadRow($row)
|
||||
{
|
||||
$cardNo = $row->getCellAtIndex(0)?->getValue() ?? throw new ImportException('未填写身份证号码');//身份证号
|
||||
$name = $row->getCellAtIndex(1)?->getValue() ?? throw new ImportException('未填写姓名');//姓名
|
||||
$sex = $row->getCellAtIndex(2)?->getValue() ?? throw new ImportException('未填写性别');//性别
|
||||
$birthDate = $row->getCellAtIndex(3)?->getValue() ?? throw new ImportException('未填写生日');//生日
|
||||
$cardCity = $row->getCellAtIndex(4)?->getValue() ?? throw new ImportException('未填写地址省份');//省
|
||||
$cardProvince = $row->getCellAtIndex(5)?->getValue() ?? throw new ImportException('未填写地址-市');//市
|
||||
$cardArea = $row->getCellAtIndex(6)?->getValue() ?? throw new ImportException('未填写地址-区');//区
|
||||
$cardAddress = $row->getCellAtIndex(7)?->getValue() ?? throw new ImportException('未填写详细地址');//详细地址
|
||||
$agreementNo = $row->getCellAtIndex(8)?->getValue() ?? '';//协议号码
|
||||
$nurseLvName = $row->getCellAtIndex(9)?->getValue() ?? throw new ImportException('未填写护理等级');//护理等级
|
||||
$clientName = $row->getCellAtIndex(10)?->getValue() ?? throw new ImportException('未填写监护人姓名');//监护人姓名
|
||||
$clientPhone = $row->getCellAtIndex(11)?->getValue() ?? throw new ImportException('未填写监护人手机号码');//监护人手机号
|
||||
$clientCity = $row->getCellAtIndex(12)?->getValue() ?? throw new ImportException('未填写监护人地址省份');
|
||||
$clientProvince = $row->getCellAtIndex(13)?->getValue() ?? throw new ImportException('未填写监护人地址-市');//市
|
||||
$clientArea = $row->getCellAtIndex(14)?->getValue() ?? throw new ImportException('未填写监护人地址-区');//区
|
||||
$clientAddress = $row->getCellAtIndex(15)?->getValue() ?? throw new ImportException('未填写监护人详细地址');//详细地址
|
||||
|
||||
if(ModelsOldmen::where('card_no', $cardNo)->exists()){//如果已存在,则为更新
|
||||
$oldman = ModelsOldmen::where('card_no', $cardNo)->first();
|
||||
$newLv = Keyword::where(['type_key'=>'nurse_lv', 'name'=>$nurseLvName])->value('value');
|
||||
if($oldman->nurse_lv !== $newLv && $oldman->live_in > 0){
|
||||
throw new ImportException('当前入住状态无法直接变更护理等级');
|
||||
}K;
|
||||
}else{
|
||||
$oldman = new ModelsOldmen();
|
||||
$oldman->card_no = $cardNo;
|
||||
$oldman->nurse_lv = Keyword::where(['type_key'=>'nurse_lv', 'name'=>$nurseLvName])->value('value');
|
||||
}
|
||||
$oldman->name = $name;
|
||||
$sexArr = [
|
||||
'未知'=>0,
|
||||
'男'=>1,
|
||||
'女'=>2
|
||||
];
|
||||
$oldman->sex = $sexArr[$sex];
|
||||
$oldman->birthday = Carbon::parse($birthDate);
|
||||
$oldman->card_city_code = Zone::where(['name' => $cardArea, 'type'=>'area'])->value('code') ?? '';
|
||||
$oldman->card_address = $cardAddress;
|
||||
$oldman->agreement_no = $agreementNo;
|
||||
$oldman->client_name = $clientName;
|
||||
$oldman->client_phone = $clientPhone;
|
||||
$oldman->client_city_code = Zone::where(['name' => $clientArea, 'type'=>'area'])->value('code') ?? '';
|
||||
$oldman->client_address = $clientAddress;
|
||||
$oldman->save();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\ImportJob as ImportJobModel;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use App\Admin\Services\ImportService;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
|
||||
class ImportJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
protected $importJob;
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(ImportJobModel $job)
|
||||
{
|
||||
//
|
||||
$this->importJob = $job;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if(env('APP_DEBUG')){
|
||||
\Log::info('执行文件导入');
|
||||
}
|
||||
(new ImportService())->import($this->importJob);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
class ImportJob extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'status',
|
||||
'success',
|
||||
'fails',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ImportJobLog extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
public const STATUS_FAILED = 0;
|
||||
public const STATUS_SUCCESS = 1;
|
||||
|
||||
public static $statusTexts = [
|
||||
self::STATUS_FAILED=>'失败',
|
||||
self::STATUS_SUCCESS=>'成功',
|
||||
];
|
||||
|
||||
public function job()
|
||||
{
|
||||
return $this->belongsTo(ImportJob::class);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\Admin;
|
||||
|
||||
use App\Models\ImportJob;
|
||||
use App\Models\ImportJobLog;
|
||||
use Illuminate\Http\File;
|
||||
|
||||
class ImportService
|
||||
{
|
||||
protected $driver;
|
||||
|
||||
public function import(ImportJob $job)
|
||||
{
|
||||
if ($job->status == 1) {
|
||||
$this->driver = new $job->type();
|
||||
if(strpos($job->file, 'http') !== false) {
|
||||
$res = $this->driver->readFileByUrl($job->file);
|
||||
}else{
|
||||
$file = new File(public_path('storage/'.$job->file));
|
||||
$res = $this->driver->readFile($file);
|
||||
}
|
||||
|
||||
if ($res) {
|
||||
$job->update([
|
||||
'status'=>2,
|
||||
'success' => $res['success']??0,
|
||||
'fails'=> $res['fails']??0,
|
||||
]);
|
||||
}
|
||||
if (isset($res['errors']) && count($res['errors']) > 0) {
|
||||
$this->createErrorLogs($job, $res['errors']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function createErrorLogs(ImportJob $job, array $errors)
|
||||
{
|
||||
ImportJobLog::insert(array_map(function ($value) use ($job) {
|
||||
return array_merge($value, [
|
||||
'job_id'=>$job->id,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}, $errors));
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"box/spout": "^3.3",
|
||||
"guzzlehttp/guzzle": "^7.2",
|
||||
"laravel/framework": "^10.10",
|
||||
"laravel/sanctum": "^3.2",
|
||||
|
|
|
|||
|
|
@ -4,8 +4,83 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "a483aafe1cb9cc540a4db897e1c35ac4",
|
||||
"content-hash": "4a5297a88397d5f65572b83c55b33701",
|
||||
"packages": [
|
||||
{
|
||||
"name": "box/spout",
|
||||
"version": "v3.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/box/spout.git",
|
||||
"reference": "9bdb027d312b732515b884a341c0ad70372c6295"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/box/spout/zipball/9bdb027d312b732515b884a341c0ad70372c6295",
|
||||
"reference": "9bdb027d312b732515b884a341c0ad70372c6295",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-xmlreader": "*",
|
||||
"ext-zip": "*",
|
||||
"php": ">=7.2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^2",
|
||||
"phpunit/phpunit": "^8"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-iconv": "To handle non UTF-8 CSV files (if \"php-intl\" is not already installed or is too limited)",
|
||||
"ext-intl": "To handle non UTF-8 CSV files (if \"iconv\" is not already installed)"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Box\\Spout\\": "src/Spout"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Apache-2.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Adrien Loison",
|
||||
"email": "adrien@box.com"
|
||||
}
|
||||
],
|
||||
"description": "PHP Library to read and write spreadsheet files (CSV, XLSX and ODS), in a fast and scalable way",
|
||||
"homepage": "https://www.github.com/box/spout",
|
||||
"keywords": [
|
||||
"OOXML",
|
||||
"csv",
|
||||
"excel",
|
||||
"memory",
|
||||
"odf",
|
||||
"ods",
|
||||
"office",
|
||||
"open",
|
||||
"php",
|
||||
"read",
|
||||
"scale",
|
||||
"spreadsheet",
|
||||
"stream",
|
||||
"write",
|
||||
"xlsx"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/box/spout/issues",
|
||||
"source": "https://github.com/box/spout/tree/v3.3.0"
|
||||
},
|
||||
"abandoned": true,
|
||||
"time": "2021-05-14T21:18:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "brick/math",
|
||||
"version": "0.11.0",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('import_jobs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name')->nullable()->comment('名称');
|
||||
$table->string('file')->comment('导入的文件路径');
|
||||
$table->string('type')->comment('导入执行类');
|
||||
$table->unsignedTinyInteger('status')->default(0)->comment('状态:0未开始,1导入中,2完成');
|
||||
$table->unsignedInteger('success')->default(0)->comment('成功条数');
|
||||
$table->unsignedInteger('fails')->default(0)->comment('失败条数');
|
||||
// $table->unsignedBigInteger('')
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('import_job_logs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('job_id')->comment('任务ID');
|
||||
$table->unsignedInteger('row')->default(0)->comment('行数');
|
||||
$table->unsignedTinyInteger('status')->default(0)->comment('状态1成功');
|
||||
$table->text('reason')->nullable()->comment('原因');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('import_jobs');
|
||||
Schema::dropIfExists('import_job_logs');
|
||||
}
|
||||
};
|
||||
Binary file not shown.
Loading…
Reference in New Issue