// +---------------------------------------------------------------------- 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\job\controller\goods\GoodsStoreImport as GoodsStoreImportJob; use app\job\controller\goods\GoodsUpdateImport as GoodsUpdateImportJob; 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; use app\common\model\Goods as GoodsModel; use app\common\model\GoodsSku as GoodsSkuModel; use app\store\model\GoodsCategoryRel as GoodsCategoryRelModel; /** * 商品批量导入记录模型 * 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 { $where = $this->getFilter($param); if (isset($param['channels']) && $param['channels']) { $where[] = ['channel', 'in', $param['channels']]; } return $this->where($where) ->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']]; isset($params['merchant_id']) && $params['merchant_id'] > -1 && $filter[] = ['merchant_id', '=', (int)$params['merchant_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(); // echo "
"; // print_r($execlData); // exit(); // 验证导入的商品数量是否合法 $this->checkLimit($execlData); self::$merchantId = $form['merchant_id'] ?? 0; self::$storeId = $form['store_id'] ?? 0; foreach ($execlData as &$value) { $value['A0'] = $form['channel']??'zy'; } // $obj = new \app\job\service\goods\AdminImport(); // $service = new \app\job\service\goods\Collector(); // foreach ($execlData as $item) { // $info = \app\common\model\Goods::where('goods_no', $item['C'])->where('store_id', self::$storeId)->where('channel', $item['B'])->where('is_delete',0)->find(); // // var_dump($item['C']); // // var_dump($info); // if ($info) { // var_dump($info->goods_id); // $this->successCount++; // continue; // } // //var_dump($info->goods_id); // //exit(); // $data = $obj->createData($item, self::$storeId); // $service->single($item['D'], $data, self::$storeId); // // // 记录导入成功 // // $this->successCount++; // } // var_dump(\count($execlData)); // exit(); // 新增商品导入记录 $recordId = $this->addRecord(\count($execlData), $form['channel'] ?? 'zy'); // 调度计划任务 $this->adminDispatchJob($execlData, $recordId); return true; } /** * 执行批量导入 * @param array $form * @return bool * @throws BaseException * @throws Exception * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception */ public function storeBatch(array $form): bool { // 读取excel文件内容 $execlData = $this->readExecl(); // echo ""; // print_r($execlData); // exit(); // 验证导入的商品数量是否合法 $this->checkLimit($execlData); self::$storeId = $form['store_id'] ?? 0; self::$merchantId = $form['merchant_id'] ?? 0; foreach ($execlData as &$value) { $value['channel'] = $form['channel']??'zy'; } // $obj = new \app\job\service\goods\GoodsStoreImport(); // $service = new \app\job\service\goods\Collector(); // foreach ($execlData as $item) { // $info = \app\common\model\Goods::where('goods_no', $item['C'])->where('store_id', self::$storeId)->where('merchant_id', self::$merchantId)->where('channel', $item['channel'])->where('is_delete',0)->find(); // // var_dump($item['C']); // // var_dump($info); // if ($info) { // var_dump($info->goods_id); // $this->successCount++; // continue; // } // //var_dump($info->goods_id); // //exit(); // $data = $obj->createData($item, self::$storeId, self::$merchantId); // $service->single($item['D'], $data, self::$storeId); // exit(); // // // 记录导入成功 // // $this->successCount++; // } // var_dump(\count($execlData)); // exit(); // 新增商品导入记录 $recordId = $this->addRecord(\count($execlData), $form['channel'] ?? 'zy'); // 调度计划任务 $this->storeDispatchJob($execlData, $recordId); return true; } /** * 执行批量更新 * @param array $form * @return bool * @throws BaseException * @throws Exception * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception */ public function goodsUpdateBatch (array $form): bool { // 读取excel文件内容 $execlData = $this->readExecl(); // echo ""; // print_r($execlData); // exit(); // 验证导入的商品数量是否合法 $this->checkLimit($execlData); self::$storeId = $form['store_id'] ?? 0; self::$merchantId = $form['merchant_id'] ?? 0; // $obj = new \app\job\service\goods\GoodsUpdateImport(); // $service = new \app\job\service\goods\Collector(); // foreach ($execlData as $item) { // $data = $obj->createData($item, self::$storeId); // // echo ""; // // print_r($data); // // exit(); // $service->updateGoods($item['G'], $data, self::$storeId); // exit(); // // // 记录导入成功 // // $this->successCount++; // } // 新增商品导入记录 $recordId = $this->addRecord(\count($execlData), $form['channel'] ?? 'zy'); // 调度计划任务 $this->goodsUpdateJob($execlData, $recordId); return true; } /** * 执行批量更新 * @param array $form * @return bool * @throws BaseException * @throws Exception * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception */ public function storeGoodsUpdateBatch(array $form): bool { // 读取excel文件内容 $execlData = $this->readExecl(); // echo ""; // print_r($execlData); // exit(); // 验证导入的商品数量是否合法 $this->checkLimit($execlData); self::$storeId = $form['store_id'] ?? 0; foreach ($execlData as $key => $value) { $upData = [ 'goods_name'=> $value['B'], 'cmmdty_model'=> $value['C'], 'goods_source'=> $value['D'], //'goods_no'=> $value['E'], 'delivery_time'=> $value['G'], 'is_check'=> $value['H'], 'delivery_id'=> $value['I'], 'status'=> $value['J'], // 'cost_price_min'=> $value['K'], // 'goods_price_min'=> $value['L'], // 'goods_price_max'=> $value['L'], // 'line_price_min'=> $value['L'], // 'line_price_max'=> $value['L'], 'stock_total'=> $value['M'], 'remark'=> $value['N'], ]; $goods = GoodsModel::where('goods_id', $value['A'])->find(); if (!$goods) { continue; } //自营数据才能更新价格 if (in_array($goods['channel'], ['zy'])) { $upData['goods_no'] = $value['E']; $upData['cost_price_min'] = $value['K']; $upData['goods_price_min' ]= $value['L']; $upData['goods_price_max'] = $value['L']; $upData['line_price_min'] = $value['L']; $upData['line_price_max'] = $value['L']; $profit = (float)$upData['goods_price_min'] - (float)$upData['cost_price_min']; $profit_rate = (float)$upData['goods_price_min'] > 0 ? bcmul(bcdiv((string)$profit, (string)$upData['goods_price_min'], 2), (string)100, 2) : 0.00; $upData['profit'] = $profit; $upData['profit_rate'] = $profit_rate; GoodsSkuModel::where('goods_id', $value['A'])->update(['cost_price' => $value['K'], 'goods_price' => $value['L']]); } // echo ""; // print_r(explode(",", $value['F'])); // exit(); GoodsModel::where('goods_id', $value['A'])->update($upData); GoodsCategoryRelModel::updates((int)$value['A'], explode(",", $value['F'])); } return true; } /** * 执行批量导入 * @param array $form * @return bool * @throws BaseException * @throws Exception * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception */ public function goodsUpdateBatch1(array $form): bool { // 读取excel文件内容 //$execlData = $this->readExecl(); // 接收用户上传的模板文件 $file = request()->file('file'); empty($file) && throwError('很抱歉,您没有上传模板文件'); // 写入到本地临时目录 $path = FileLocal::writeFile($file, 'batch-goods', self::$storeId); $file = fopen($path, "r"); $data = array(); $i = 0; while (($line = fgetcsv($file)) !== false) { // foreach ($line as $key => &$value) { // $value = mb_convert_encoding($value, 'UTF-8', "GBK"); // } // echo ""; // print_r($line); // exit(); if ($i == 0) { $i++; continue; } // $line是当前行的所有数据,可以根据实际需求进行处理 $execlData[] = $line; // 将每一行数据保存到$data数组中 $i++; } fclose($file); // echo ""; // print_r($execlData); // exit(); // 验证导入的商品数量是否合法 $this->checkLimit($execlData); self::$storeId = $form['store_id'] ?? 0; // $obj = new \app\job\service\goods\GoodsUpdateImport(); // $service = new \app\job\service\goods\Collector(); // foreach ($execlData as $item) { // $data = $obj->createData($item, self::$storeId); // // echo ""; // // print_r($data); // // exit(); // $service->updateGoods($item[6], $data, self::$storeId); // exit(); // // // 记录导入成功 // // $this->successCount++; // } // 新增商品导入记录 $recordId = $this->addRecord(\count($execlData)); // 调度计划任务 $this->goodsUpdateJob($execlData, $recordId); return true; } /** * 调度队列服务执行商品导入 * @param array $goodsList 商品列表 * @param int $recordId 商品导入记录ID * @return void */ private function goodsUpdateJob(array $goodsList, int $recordId) { // 分批每次导入20条 $limit = 20; // 根据商品总数量计算需要的队列任务数量 $jobCount = \count($goodsList) / $limit; // 逐次发布队列任务 for ($i = 0; $i < $jobCount; $i++) { $data = array_slice($goodsList, $i * $limit, $limit); GoodsUpdateImportJob::dispatch([ 'list' => $data, 'recordId' => $recordId, 'storeId' => self::$storeId, ]); } } /** * 调度队列服务执行商品导入 * @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 $goodsList 商品列表 * @param int $recordId 商品导入记录ID * @return void */ private function storeDispatchJob(array $goodsList, int $recordId) { // 分批每次导入20条 $limit = 20; // 根据商品总数量计算需要的队列任务数量 $jobCount = \count($goodsList) / $limit; // 逐次发布队列任务 for ($i = 0; $i < $jobCount; $i++) { $data = array_slice($goodsList, $i * $limit, $limit); GoodsStoreImportJob::dispatch([ 'list' => $data, 'recordId' => $recordId, 'storeId' => self::$storeId, 'merchantId' => self::$merchantId, ]); } } /** * 执行批量导入 * @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, string $channel = 'zy'): int { $this->save([ 'total_count' => $totalCount, 'start_time' => \time(), 'fail_log' => [], 'status' => GoodsImportStatusEnum::NORMAL, 'store_id' => self::$storeId, 'merchant_id' => self::$merchantId, 'channel' => $channel, ]); 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]); } }