You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
yanzong/app/store/model/goods/Import.php

581 lines
20 KiB

<?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\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 "<pre>";
// print_r($execlData);
// exit();
// 验证导入的商品数量是否合法
$this->checkLimit($execlData);
self::$merchantId = $form['merchant_id'] ?? 0;
self::$storeId = $form['store_id'] ?? 0;
foreach ($execlData as &$value) {
$value['B'] = $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 "<pre>";
// 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 "<pre>";
// 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 "<pre>";
// // 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 "<pre>";
// 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 "<pre>";
// 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 "<pre>";
// print_r($line);
// exit();
if ($i == 0) {
$i++;
continue;
}
// $line是当前行的所有数据,可以根据实际需求进行处理
$execlData[] = $line; // 将每一行数据保存到$data数组中
$i++;
}
fclose($file);
// echo "<pre>";
// 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 "<pre>";
// // 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]);
}
}