// +---------------------------------------------------------------------- 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); self::$storeId = $form['store_id'] ?? 0; // $obj = new \app\job\service\goods\AdminImport(); // $service = new \app\job\service\goods\Collector(); // foreach ($execlData as $item) { // $data = $obj->createData($item, self::$storeId); // $service->single($item['D'], $data, self::$storeId); // // // 记录导入成功 // // $this->successCount++; // } // 新增商品导入记录 $recordId = $this->addRecord(\count($execlData)); // 调度计划任务 $this->adminDispatchJob($execlData, $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 $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]); } }