完成导入

develop
vine_liutk 2023-06-29 16:54:07 +08:00
parent f7d8af798c
commit d96d0106c8
14 changed files with 456 additions and 3 deletions

View File

@ -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();
}
}

View File

@ -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');

View File

@ -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()
{
}
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Exceptions;
class ImportException extends BizException
{
public function __construct(string $message = '导入出错')
{
parent::__construct($message);
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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',
];
}

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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",

77
composer.lock generated
View File

@ -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",

View File

@ -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');
}
};