|
|
|
<?php
|
|
|
|
// +----------------------------------------------------------------------
|
|
|
|
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
|
|
|
|
// +----------------------------------------------------------------------
|
|
|
|
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
|
|
|
|
// +----------------------------------------------------------------------
|
|
|
|
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
|
|
|
|
// +----------------------------------------------------------------------
|
|
|
|
// | Author: 萤火科技 <admin@yiovo.com>
|
|
|
|
// +----------------------------------------------------------------------
|
|
|
|
declare (strict_types=1);
|
|
|
|
|
|
|
|
namespace app\store\model\goods;
|
|
|
|
|
|
|
|
use think\Paginator;
|
|
|
|
use think\db\exception\DbException;
|
|
|
|
use PhpOffice\PhpSpreadsheet\Exception;
|
|
|
|
use app\job\controller\goods\AdminImport as GoodsAdminImportJob;
|
|
|
|
use app\common\enum\goods\ImportStatus as GoodsImportStatusEnum;
|
|
|
|
use app\common\library\FileLocal;
|
|
|
|
use app\common\library\helper;
|
|
|
|
use app\common\library\phpoffice\ReadExecl;
|
|
|
|
use app\common\model\goods\Import as ImportModel;
|
|
|
|
use cores\exception\BaseException;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 商品批量导入记录模型
|
|
|
|
* Class Import
|
|
|
|
* @package app\store\model\goods
|
|
|
|
*/
|
|
|
|
class Import extends ImportModel
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* 获取导出记录
|
|
|
|
* @param array $param
|
|
|
|
* @return Paginator
|
|
|
|
* @throws DbException
|
|
|
|
*/
|
|
|
|
public function getList(array $param = []): \think\Paginator
|
|
|
|
{
|
|
|
|
return $this->where($this->getFilter($param))
|
|
|
|
->where('is_delete', '=', 0)
|
|
|
|
->order(['create_time' => 'desc', $this->getPk()])
|
|
|
|
->paginate();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 获取查询条件
|
|
|
|
* @param array $param
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
private function getFilter(array $param = []): array
|
|
|
|
{
|
|
|
|
// 默认查询参数
|
|
|
|
$params = $this->setQueryDefaultValue($param, ['goods_type' => -1, 'status' => -1]);
|
|
|
|
// 检索查询条件
|
|
|
|
$filter = [];
|
|
|
|
$params['status'] > -1 && $filter[] = ['status', '=', (int)$params['status']];
|
|
|
|
isset($params['store_id']) && $params['store_id'] > -1 && $filter[] = ['store_id', '=', (int)$params['store_id']];
|
|
|
|
return $filter;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* 执行批量导入
|
|
|
|
* @param array $form
|
|
|
|
* @return bool
|
|
|
|
* @throws BaseException
|
|
|
|
* @throws Exception
|
|
|
|
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
|
|
|
|
*/
|
|
|
|
public function adminBatch(array $form): bool
|
|
|
|
{
|
|
|
|
// 读取excel文件内容
|
|
|
|
$execlData = $this->readExecl();
|
|
|
|
// 验证导入的商品数量是否合法
|
|
|
|
$this->checkLimit($execlData);
|
|
|
|
// 格式化导入的商品列表数据
|
|
|
|
$goodsList = $this->adminFormatGoodsList($execlData);
|
|
|
|
// echo "<pre>";
|
|
|
|
// print_r($goodsList);
|
|
|
|
// exit();
|
|
|
|
//总后台的店铺id设置为0
|
|
|
|
self::$storeId = 0;
|
|
|
|
// 新增商品导入记录
|
|
|
|
$recordId = $this->addRecord(\count($goodsList));
|
|
|
|
// 调度计划任务
|
|
|
|
$this->adminDispatchJob($goodsList, $recordId);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* 调度队列服务执行商品导入
|
|
|
|
* @param array $goodsList 商品列表
|
|
|
|
* @param int $recordId 商品导入记录ID
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
private function adminDispatchJob(array $goodsList, int $recordId)
|
|
|
|
{
|
|
|
|
// 分批每次导入20条
|
|
|
|
$limit = 20;
|
|
|
|
// 根据商品总数量计算需要的队列任务数量
|
|
|
|
$jobCount = \count($goodsList) / $limit;
|
|
|
|
// 逐次发布队列任务
|
|
|
|
for ($i = 0; $i < $jobCount; $i++) {
|
|
|
|
$data = array_slice($goodsList, $i * $limit, $limit);
|
|
|
|
GoodsAdminImportJob::dispatch([
|
|
|
|
'list' => $data,
|
|
|
|
'recordId' => $recordId,
|
|
|
|
'storeId' => self::$storeId,
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* 格式化导入的商品列表数据
|
|
|
|
* @param array $execlData
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
private function adminFormatGoodsList(array $execlData): array
|
|
|
|
{
|
|
|
|
$goodsList = [];
|
|
|
|
foreach ($execlData as $row) {
|
|
|
|
// 不存在商品序号的记录视为空行跳过
|
|
|
|
if (!is_numeric($row['A'])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// SKU信息
|
|
|
|
$skuInfo = helper::pick($row, ['G', 'H', 'I', 'J', 'K', 'L', 'M', 'N']);
|
|
|
|
// 商品数据里不存在相同序号, 代表是第一次记录
|
|
|
|
if (!isset($goodsList[$row['A']])) {
|
|
|
|
$goodsList[$row['A']] = $row;
|
|
|
|
// 存在规格值组合则商品是多规格
|
|
|
|
if (!empty($row['H'])) {
|
|
|
|
$goodsList[$row['A']]['skuList'] = [$skuInfo];
|
|
|
|
}
|
|
|
|
} elseif (isset($goodsList[$row['A']]['skuList'])) {
|
|
|
|
// 否则代表是多规格商品的SKU
|
|
|
|
$goodsList[$row['A']]['skuList'][] = $skuInfo;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return array_values($goodsList);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 执行批量导入
|
|
|
|
* @param array $form
|
|
|
|
* @return bool
|
|
|
|
* @throws BaseException
|
|
|
|
* @throws Exception
|
|
|
|
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
|
|
|
|
*/
|
|
|
|
public function batch(array $form): bool
|
|
|
|
{
|
|
|
|
// 读取excel文件内容
|
|
|
|
$execlData = $this->readExecl();
|
|
|
|
// 验证导入的商品数量是否合法
|
|
|
|
$this->checkLimit($execlData);
|
|
|
|
// 格式化导入的商品列表数据
|
|
|
|
$goodsList = $this->formatGoodsList($execlData);
|
|
|
|
// 新增商品导入记录
|
|
|
|
$recordId = $this->addRecord(\count($goodsList));
|
|
|
|
// 调度计划任务
|
|
|
|
$this->dispatchJob($goodsList, $recordId);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 调度队列服务执行商品导入
|
|
|
|
* @param array $goodsList 商品列表
|
|
|
|
* @param int $recordId 商品导入记录ID
|
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
private function dispatchJob(array $goodsList, int $recordId)
|
|
|
|
{
|
|
|
|
// 分批每次导入20条
|
|
|
|
$limit = 20;
|
|
|
|
// 根据商品总数量计算需要的队列任务数量
|
|
|
|
$jobCount = \count($goodsList) / $limit;
|
|
|
|
// 逐次发布队列任务
|
|
|
|
for ($i = 0; $i < $jobCount; $i++) {
|
|
|
|
$data = array_slice($goodsList, $i * $limit, $limit);
|
|
|
|
GoodsImportJob::dispatch([
|
|
|
|
'list' => $data,
|
|
|
|
'recordId' => $recordId,
|
|
|
|
'storeId' => self::$storeId,
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 新增商品导入记录
|
|
|
|
* @param int $totalCount 商品总数量
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
private function addRecord(int $totalCount): int
|
|
|
|
{
|
|
|
|
$this->save([
|
|
|
|
'total_count' => $totalCount,
|
|
|
|
'start_time' => \time(),
|
|
|
|
'fail_log' => [],
|
|
|
|
'status' => GoodsImportStatusEnum::NORMAL,
|
|
|
|
'store_id' => self::$storeId
|
|
|
|
]);
|
|
|
|
return (int)$this['id'];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 格式化导入的商品列表数据
|
|
|
|
* @param array $execlData
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
private function formatGoodsList(array $execlData): array
|
|
|
|
{
|
|
|
|
$goodsList = [];
|
|
|
|
foreach ($execlData as $row) {
|
|
|
|
// 不存在商品序号的记录视为空行跳过
|
|
|
|
if (!is_numeric($row['A'])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// SKU信息
|
|
|
|
$skuInfo = helper::pick($row, ['G', 'H', 'I', 'J', 'K', 'L', 'M', 'N']);
|
|
|
|
// 商品数据里不存在相同序号, 代表是第一次记录
|
|
|
|
if (!isset($goodsList[$row['A']])) {
|
|
|
|
$goodsList[$row['A']] = $row;
|
|
|
|
// 存在规格值组合则商品是多规格
|
|
|
|
if (!empty($row['H'])) {
|
|
|
|
$goodsList[$row['A']]['skuList'] = [$skuInfo];
|
|
|
|
}
|
|
|
|
} elseif (isset($goodsList[$row['A']]['skuList'])) {
|
|
|
|
// 否则代表是多规格商品的SKU
|
|
|
|
$goodsList[$row['A']]['skuList'][] = $skuInfo;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return array_values($goodsList);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 验证导入的商品数量是否合法
|
|
|
|
* @param array $execlData
|
|
|
|
* @return void
|
|
|
|
* @throws BaseException
|
|
|
|
*/
|
|
|
|
private function checkLimit(array $execlData): void
|
|
|
|
{
|
|
|
|
// 判断商品数据是否为空
|
|
|
|
if (empty($execlData)) {
|
|
|
|
throwError('很抱歉,模板文件中商品数量为空');
|
|
|
|
}
|
|
|
|
// 过滤重复的商品序号
|
|
|
|
$originalUnique = helper::arrayUnique($execlData, 'A');
|
|
|
|
// 判断商品数量是否超出500条
|
|
|
|
if (\count($originalUnique) > 500) {
|
|
|
|
throwError('很抱歉,模板文件中最多不能超过500个商品');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 读取excel文件内容
|
|
|
|
* @return array
|
|
|
|
* @throws BaseException
|
|
|
|
* @throws \PhpOffice\PhpSpreadsheet\Exception
|
|
|
|
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
|
|
|
|
*/
|
|
|
|
private function readExecl(): array
|
|
|
|
{
|
|
|
|
// 接收用户上传的模板文件
|
|
|
|
$file = request()->file('file');
|
|
|
|
empty($file) && throwError('很抱歉,您没有上传模板文件');
|
|
|
|
// 写入到本地临时目录
|
|
|
|
$path = FileLocal::writeFile($file, 'batch-goods', self::$storeId);
|
|
|
|
// 读取excel数据
|
|
|
|
$original = ReadExecl::load($path, 0, 21);
|
|
|
|
// 去除标题数据
|
|
|
|
unset($original[1]);
|
|
|
|
// 过滤空行和无效的内容
|
|
|
|
foreach ($original as $key => $row) {
|
|
|
|
if (!is_numeric($row['A']) || empty($row['A'])) {
|
|
|
|
unset($original[$key]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $original;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 删除记录
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function setDelete(): bool
|
|
|
|
{
|
|
|
|
if ($this['status'] == GoodsImportStatusEnum::NORMAL) {
|
|
|
|
$this->error = '很抱歉,当前任务没有结束不能删除';
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return $this->save(['is_delete' => 1]);
|
|
|
|
}
|
|
|
|
}
|