6
0
Fork 0

完善导入导出

release
vine_liutk 2022-01-07 17:54:26 +08:00
parent da69a69dfd
commit bce3450216
22 changed files with 1067 additions and 465 deletions

View File

@ -39,6 +39,14 @@ class AfterSaleController extends AdminController
$grid->model()->where('order_product_id', $orderProductId);
}
$grid->column('id')->sortable()->if(function () {
return Admin::user()->can('dcat.admin.after_sales.show');
})->then(function (Column $column) {
$column->link(function ($value) {
return admin_route('after_sales.show', ['after_sale' => $value]);
});
});
$grid->column('sn')->copyable();
$grid->column('tags', '标签')->display(function ($tags) {
$array = [];
@ -91,7 +99,10 @@ class AfterSaleController extends AdminController
$grid->model()->orderBy('created_at', 'desc');
$grid->actions(function (Grid\Displayers\Actions $actions) {
$actions->disableView(false);
if (Admin::user()->can('dcat.admin.after_sales.show')) {
$actions->disableView(false);
}
if (Admin::user()->can('dcat.admin.after_sales.tags')) {
$actions->append(new AfterSaleSetTag());
}

View File

@ -0,0 +1,79 @@
<?php
namespace App\Admin\Controllers;
use App\Admin\Repositories\ImportJobLog;
use App\Models\ImportJobLog as ImportJobLogModel;
use Dcat\Admin\Form;
use Dcat\Admin\Grid;
use Dcat\Admin\Http\Controllers\AdminController;
use Dcat\Admin\Show;
class ImportJobLogController extends AdminController
{
/**
* Make a grid builder.
*
* @return Grid
*/
protected function grid()
{
$builder = ImportJobLog::with('job');
return Grid::make($builder, function (Grid $grid) {
$grid->column('id')->sortable();
$grid->column('job.name');
$grid->column('row')->label();
$grid->column('status')->using(ImportJobLogModel::$statusTexts)->dot([
0=>'danger',
1=>'success',
]);
$grid->column('reason');
$grid->column('created_at')->sortable();
$grid->model()->orderBy('id', 'desc');
$grid->filter(function (Grid\Filter $filter) {
$filter->panel();
$filter->like('job.name')->width(3);
});
});
}
/**
* Make a show builder.
*
* @param mixed $id
*
* @return Show
*/
protected function detail($id)
{
return Show::make($id, new ImportJobLog(), function (Show $show) {
$show->field('id');
$show->field('job_id');
$show->field('row');
$show->field('status');
$show->field('reason');
$show->field('created_at');
$show->field('updated_at');
});
}
/**
* Make a form builder.
*
* @return Form
*/
protected function form()
{
return Form::make(new ImportJobLog(), function (Form $form) {
$form->display('id');
$form->text('job_id');
$form->text('row');
$form->text('status');
$form->text('reason');
$form->display('created_at');
$form->display('updated_at');
});
}
}

View File

@ -112,7 +112,10 @@ class OrderController extends AdminController
$grid->model()->orderBy('created_at', 'desc');
$grid->actions(function (Grid\Displayers\Actions $actions) {
$actions->disableView(false);
if (Admin::user()->can('dcat.admin.orders.show')) {
$actions->disableView(false);
}
if (Admin::user()->can('dcat.admin.orders.tags')) {
$actions->append(new OrderSetTag());
}
@ -407,13 +410,14 @@ class OrderController extends AdminController
$writer->openToBrowser('测试'.date('Ymd').'.xlsx');
$writer->addRow(WriterEntityFactory::createRowFromArray([
'订单编号', '商品编号', '商品名称', '数量', '姓名', '电话', '地址', '下单时间', '发货数量', '发货单号',
'订单编号', '下单手机号', '商品编号', '商品名称', '数量', '姓名', '电话', '地址', '下单时间', '快递公司', '发货单号', '发货数量',
]));
foreach (OrderModel::with('products')->needShipping()->cursor() as $order) {
foreach (OrderModel::with('products', 'user')->needShipping()->cursor() as $order) {
foreach ($order->products as $product) {
if ($product->remain_quantity > 0) {
$writer->addRow(WriterEntityFactory::createRowFromArray([
$order->sn,
$order->user->phone,
$product->sku_id,
$product->name.'数量:'.$product->remain_quantity,
$product->remain_quantity,

View File

@ -6,6 +6,7 @@ use App\Admin\Actions\Grid\KuaidiInfo;
use App\Admin\Actions\Grid\OrderPackageEdit;
use App\Admin\Actions\Grid\OrderPackageFailed;
use App\Admin\Actions\Grid\OrderPackageSetTag;
use App\Admin\Extensions\Grid\Tools\Package\Import;
use App\Admin\Renderable\PackageProductSimpleTable;
use App\Admin\Repositories\OrderPackage;
use App\Exceptions\BizException;
@ -30,7 +31,10 @@ class OrderPackageController extends AdminController
{
$builder = OrderPackage::with(['order', 'tags']);
return Grid::make($builder, function (Grid $grid) {
// $grid->column('id')->sortable();
$grid->tools(function (Grid\Tools $tools) {
$tools->append(new Import());
});
$grid->column('id')->sortable();
$grid->column('order.sn')->if(function () {
return Admin::user()->can('dcat.admin.orders.show');
})
@ -52,7 +56,7 @@ class OrderPackageController extends AdminController
$grid->column('order.consignee_name');
$grid->column('order.consignee_telephone');
$grid->column('address', '收货地址')->display(function () {
return $this->order->consignee_zone.$this->order->consignee_address;
return $this->order?->consignee_zone.$this->order?->consignee_address;
});
// $grid->column('order.consignee_zone');
// $grid->column('order.consignee_address');

View File

@ -0,0 +1,42 @@
<?php
namespace App\Admin\Extensions\Grid\Tools\Package;
use App\Admin\Forms\Imports\OrderPackageImport;
use Dcat\Admin\Grid\Tools\AbstractTool;
use Dcat\Admin\Widgets\Modal;
class Import extends AbstractTool
{
protected function authorize($user): bool
{
return $user->can('dcat.admin.order_packages.import');
}
/**
* 按钮样式定义,默认 btn btn-white waves-effect
*
* @var string
*/
protected $style = 'btn btn btn-danger';
/**
* 按钮文本
*
* @return string|void
*/
public function title()
{
return '导入发货单';
}
public function render()
{
$form = OrderPackageImport::make();
return Modal::make()
->lg()
->title($this->title())
->body($form)
->button($this->html());
}
}

View File

@ -0,0 +1,67 @@
<?php
namespace App\Admin\Forms\Imports;
use App\Admin\Imports\OrderPackage;
use App\Admin\Services\ImportService;
use App\Models\ImportJob;
use Carbon\Carbon;
use Dcat\Admin\Contracts\LazyRenderable;
use Dcat\Admin\Traits\LazyWidget;
use Dcat\Admin\Widgets\Form;
class OrderPackageImport extends Form implements LazyRenderable
{
use LazyWidget;
/**
* 权限判断,如不需要可以删除此方法
*
* @param Model|Authenticatable|HasPermissions|null $user
*
* @return bool
*/
protected function authorize($user): bool
{
return $user->can('dcat.admin.order_packages.import');
}
/**
* Handle the form request.
*
* @param array $input
*
* @return mixed
*/
public function handle(array $input)
{
$job = new ImportJob();
$job->file = $input['import_file'];
$job->type = OrderPackage::class;
$job->status = 1;
$job->name = $input['name'];
$job->save();
$importService = new ImportService();
$importService->import($job);
return $this->response()
->success('导入成功')
->refresh();
}
/**
* Build a form here.
*/
public function form()
{
$this->text('name', '备注')->required();
$this->file('import_file', '导入文件')->accept('xlsx')->rules('mimes:xlsx')
->move('imports/packages/'.Carbon::now()->toDateString())
->maxSize(102400)//默认最大100M
->saveFullUrl()
->removable(false)
->retainable()
->autoSave(false)
->autoUpload()
->required();
}
}

View File

@ -0,0 +1,70 @@
<?php
namespace App\Admin\Imports;
use App\Exceptions\ImportException;
use Box\Spout\Reader\Common\Creator\ReaderEntityFactory;
use Illuminate\Http\File;
use Illuminate\Support\Facades\Storage;
use Throwable;
class Import
{
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::put($path, file_get_contents($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 {
$this->loadRow($row);
$success++;
} catch (ImportException $e) {
$fails++;
$errors[] = [
'row'=>$num,
'reason'=>$e->getMessage(),
];
} catch (Throwable $e) {
$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,74 @@
<?php
namespace App\Admin\Imports;
use App\Admin\Services\OrderPackageService;
use App\Exceptions\ImportException;
use App\Models\Order;
use App\Models\OrderPackage as OrderPackageModel;
use App\Services\Kuaidi100Service;
use Illuminate\Support\Arr;
class OrderPackage extends Import
{
public function loadRow($row)
{
/**校验行数据 **/
//获取订单号
$orderSn = $row->getCellAtIndex(0)?->getValue();
if (empty($orderSn)) {
throw new ImportException('未输入订单编号');
}
//获取商品ID;
$skuId = $row->getCellAtIndex(2)?->getValue();
if (empty($skuId)) {
throw new ImportException('未输入商品编号');
}
//获取快递公司#8
$shippingCompany = $row->getCellAtIndex(9)?->getValue();
if (empty($shippingCompany)) {
throw new ImportException('未输入快递公司');
}
if (!(OrderPackageModel::$shippingCompanyTexts[$shippingCompany] ?? '')) {
throw new ImportException('未找到快递公司:'.$shippingCompany);
}
//获取运单号#9
$shippingNumber = $row->getCellAtIndex(10)?->getValue();
if (empty($shippingNumber)) {
throw new ImportException('未输入快递单号');
}
//获取发货数量#10
$quantity = $row->getCellAtIndex(11)?->getValue();
if (empty($quantity)) {
throw new ImportException('发货数量不能为0');
}
//找到对应订单
$order = Order::where('sn', $orderSn)->first();
if ($order) {
$orderPackageService = new OrderPackageService();
$orderPackage = OrderPackageModel::where([
'order_id' =>$order->id,
'shipping_code'=>Arr::get(Kuaidi100Service::$codeArr, $shippingCompany, ''),
'shipping_number'=>$shippingNumber,
])->first();
$orderProduct = $order->load('products')->products->where('sku_id', $skuId)->first();
if (!$orderProduct) {
throw new ImportException('未找到订单商品');
}
$orderPackageService->createPackage($order, [
'shipping_company'=>$shippingCompany,
'shipping_number'=>$shippingNumber,
'packages'=>[
[
'order_product_id'=>$orderProduct->id,
'quantity' =>$quantity,
],
],
], $orderPackage);
} else {
throw new ImportException('未找到订单:'.$orderSn);
}
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace App\Admin\Repositories;
use App\Models\ImportJobLog as Model;
use Dcat\Admin\Repositories\EloquentRepository;
class ImportJobLog extends EloquentRepository
{
/**
* Model.
*
* @var string
*/
protected $eloquentClass = Model::class;
}

View File

@ -0,0 +1,41 @@
<?php
namespace App\Admin\Services;
use App\Models\ImportJob;
use App\Models\ImportJobLog;
class ImportService
{
protected $driver;
public function import(ImportJob $job)
{
if ($job->status == 1) {
$this->driver = new $job->type();
$res = $this->driver->readFileByUrl($job->file);
// dd($res);
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

@ -17,7 +17,7 @@ class OrderPackageService
*
* @return void
*/
public function createPackage(Order $order, array $params)
public function createPackage(Order $order, array $params, ?OrderPackage $package = null)
{
//合并同样商品,并检验是否能发货
$packageProducts = [];
@ -51,32 +51,44 @@ class OrderPackageService
$_orderProduct->decrement('remain_quantity', $product['quantity']);
}
//创建发货单数据
$package = new OrderPackage();
$package->order_id = $order->id;
$package->user_id = $order->user_id;
$package->consignee_name = $order->consignee_name;
$package->consignee_telephone = $order->consignee_telephone;
$package->consignee_zone = $order->consignee_zone;
$package->consignee_address = $order->consignee_address;
$package->shipping_company = $params['shipping_company'];
$package->shipping_code = Arr::get(Kuaidi100Service::$codeArr, $package->shipping_company, '');
$package->shipping_number = $params['shipping_number'];
//保存发货单
$package->save();
//保存发货单商品
OrderPackageProduct::insert(array_map(function ($value) use ($package) {
return array_merge($value, [
'order_package_id' => $package->id,
'created_at' => $package->created_at,
'updated_at' => $package->created_at,
]);
}, $packageProducts));
if (app_settings('kuaidi100.is_use')) {
$kuaidi100Service = new Kuaidi100Service();
$kuaidi100Service->poll($package->shipping_number, $package->shipping_code, $package->consignee_telephone);
if (!$package) {
//创建发货单数据
$package = new OrderPackage();
$package->order_id = $order->id;
$package->user_id = $order->user_id;
$package->consignee_name = $order->consignee_name;
$package->consignee_telephone = $order->consignee_telephone;
$package->consignee_zone = $order->consignee_zone;
$package->consignee_address = $order->consignee_address;
$package->shipping_company = $params['shipping_company'];
$package->shipping_code = Arr::get(Kuaidi100Service::$codeArr, $package->shipping_company, '');
$package->shipping_number = $params['shipping_number'];
//保存发货单
$package->save();
//保存发货单商品
OrderPackageProduct::insert(array_map(function ($value) use ($package) {
return array_merge($value, [
'order_package_id' => $package->id,
'created_at' => $package->created_at,
'updated_at' => $package->created_at,
]);
}, $packageProducts));
if (app_settings('kuaidi100.is_use')) {
$kuaidi100Service = new Kuaidi100Service();
$kuaidi100Service->poll($package->shipping_number, $package->shipping_code, $package->consignee_telephone);
}
} else {
//更新现有的包裹商品
foreach ($packageProducts as $packageProduct) {
OrderPackageProduct::where([
'order_package_id'=>$package->id,
'order_product_id'=>$packageProduct['order_product_id'],
])->increment('quantity', $packageProduct['quantity']);
}
}
//更新订单状态

View File

@ -155,6 +155,8 @@ Route::group([
'index',
])->names('order_refund');
$router->get('import-job-logs', 'ImportJobLogController@index')->name('import_job_logs.index');
/** api接口 **/
$router->get('api/product-categories', 'ProductCategoryController@categories')->name('api.product_categories');
$router->get('api/product-group-details', 'ProductGroupController@details')->name('api.product_group_details');

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,19 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class ImportJob extends Model
{
use HasFactory;
protected $fillable = [
'status',
'success',
'fails',
'created_at',
'updated_at',
];
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Models;
use Dcat\Admin\Traits\HasDateTimeFormatter;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class ImportJobLog extends Model
{
use HasFactory;
use HasDateTimeFormatter;
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

@ -21,6 +21,17 @@ class OrderPackage extends Model
public const STATUS_OTHER = 10;//其他
public const STATUS_AUTOCHECK = 11;//自动签收
public static $shippingCompanyTexts = [
'圆通速递'=>'圆通速递',
'韵达快递'=>'韵达快递',
'中通快递'=>'中通快递',
'申通快递'=>'申通快递',
'百世快递'=>'百世快递',
'EMS'=>'EMS',
'顺丰速运'=>'顺丰速运',
'德邦快递'=>'德邦快递',
];
public static $kuaidi100StatusMap = [
'wait' => self::STATUS_WAIT,
'on_the_way' => self::STATUS_ONTHEWAY,

View File

@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateImportJobsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
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();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('import_jobs');
}
}

View File

@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateImportJobLogsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
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->string('reason')->nullable()->comment('原因');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('import_job_logs');
}
}

View File

@ -203,6 +203,11 @@ class AdminMenuSeeder extends Seeder
'icon' => '',
'uri' => 'package-tags?type=2',
],
[
'title' =>'失败导入',
'icon' => '',
'uri' =>'import-job-logs',
],
],
],
[

View File

@ -263,17 +263,21 @@ class AdminPermissionSeeder extends Seeder
'name' =>'预收益',
'curd' => ['index'],
],
'quota-v1-send-jobs'=>[
'quota_v1_send_jobs'=>[
'name' => '老配额分红',
'curd' => ['index', 'create', 'store', 'edit', 'update', 'destroy'],
'children' => [
'log_list'=>['name' =>'分红记录'],
],
],
'order-refunds'=>[
'order_refunds'=>[
'name' => '售后记录',
'curd' => ['index'],
],
'import_job_logs'=>[
'name' =>'导入记录',
'curd'=>['index'],
],
];
try {
DB::begintransaction();

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
<?php
return [
'labels' => [
'ImportJobLog' => '导入记录',
'import-job-log' => '导入记录',
],
'fields' => [
'job_id' => '任务',
'job' =>[
'name'=>'备注',
],
'row' => '行数',
'status' => '状态',
'reason' => '原因',
],
'options' => [
],
];