diff --git a/app/admin/controller/Goods.php b/app/admin/controller/Goods.php index 69e06825..80814c75 100644 --- a/app/admin/controller/Goods.php +++ b/app/admin/controller/Goods.php @@ -18,6 +18,7 @@ use cores\exception\BaseException; use app\store\model\Goods as GoodsModel; use app\store\model\GoodsCategoryRel; use app\store\model\GoodsSku; +use app\store\model\goods\Import as ImportModel; /** * 商品管理控制器 @@ -203,7 +204,7 @@ class Goods extends Controller // exit(); set_time_limit(0); ini_set('memory_limit', '1024M'); - $columns = ['系统编码','标题','型号','该商品苏宁的链接','成本价','库存','京东的价拖链接','前台价',"发货区域","商品备注"]; //设置好告诉浏览器要下载excel文件的headers + $columns = ['系统编码','标题','型号','该商品苏宁的链接','成本价','库存','京东的价拖链接','前台价']; //设置好告诉浏览器要下载excel文件的headers header('Content-Encoding: UTF-8'); header('Content-Type: application/vnd.ms-excel;charset=UTF-8'); header('Content-Disposition: attachment; filename="导出数据-'.date('Y-m-d', time()).'.csv"'); @@ -284,7 +285,13 @@ class Goods extends Controller } public function import(){ - return $this->renderSuccess('加价成功'); + // 新增记录 + $model = new ImportModel; + if ($model->goodsUpdateBatch($this->postData())) { + return $this->renderSuccess('已添加到导入任务中,请在历史导入记录中查看结果'); + } + return $this->renderError($model->getError() ?: '操作失败'); + } diff --git a/app/common/model/Goods.php b/app/common/model/Goods.php index b34dcac3..ff840654 100644 --- a/app/common/model/Goods.php +++ b/app/common/model/Goods.php @@ -206,7 +206,7 @@ class Goods extends BaseModel // 执行查询 $list = $query ->alias($this->name) - ->field(['goods.goods_id','goods.goods_name','cmmdty_model','sort','cost_price_min','stock_total','link','goods_price_min','sale_areas','remark']) + ->field(['goods.goods_id','goods.goods_name','cmmdty_model','link','cost_price_min','stock_total','link_other','goods_price_min']) ->where('is_delete', '=', 0) ->order($sort) ->paginate($listRows); diff --git a/app/job/controller/goods/GoodsUpdateImport.php b/app/job/controller/goods/GoodsUpdateImport.php new file mode 100644 index 00000000..a9f8b72e --- /dev/null +++ b/app/job/controller/goods/GoodsUpdateImport.php @@ -0,0 +1,49 @@ + +// +---------------------------------------------------------------------- +declare (strict_types=1); + +namespace app\job\controller\goods; + +use app\job\service\goods\GoodsUpdateImport as GoodsUpdateImportService; +use cores\BaseJob; +use cores\traits\QueueTrait; +use cores\exception\BaseException; +use think\db\exception\DataNotFoundException; +use think\db\exception\DbException; +use think\db\exception\ModelNotFoundException; + +/** + * 队列任务:商品批量导入 + * Class Import + * @package app\job\controller + */ +class GoodsUpdateImport extends BaseJob +{ + use QueueTrait; + + /** + * 消费队列任务:商品导入 + * @param array $data 参数 [index队列顺序;totalCount商品总数量;list商品列表;storeId商城ID] + * @return bool 返回结果 + * @throws BaseException + * @throws DataNotFoundException + * @throws DbException + * @throws ModelNotFoundException + */ + public function handle(array $data): bool + { + $time = date('H:i:s'); + echo "\n ---- adminImport ---- {$time} ---- \n"; + + $service = new GoodsUpdateImportService; + return $service->batch($data['list'], $data['recordId'], $data['storeId']); + } +} \ No newline at end of file diff --git a/app/job/service/goods/Collector.php b/app/job/service/goods/Collector.php index 7fe16a83..0ccae855 100644 --- a/app/job/service/goods/Collector.php +++ b/app/job/service/goods/Collector.php @@ -18,6 +18,7 @@ use app\common\model\Spec as SpecModel; use app\common\model\Goods as GoodsModel; use app\common\model\GoodsSku as GoodsSkuModel; use app\common\model\GoodsImage as GoodsImageModel; +use app\store\model\GoodsImage as GoodsImageModel1; use app\common\model\store\Setting as SettingModel; use app\common\model\GoodsSpecRel as GoodsSpecRelModel; use app\common\model\goods\Collector as GoodsCollectorModel; @@ -194,6 +195,79 @@ class Collector extends BaseService return true; } + /** + * 后台单个采集 + * [single description] + * @param string $url [description] + * @param array $form [description] + * @param int $storeId [description] + * @return [type] [description] + */ + public function updateGoods(string $url, array $form, int $storeId): bool + { + + try { + // 采集第三方商品数据 + $original = $this->collector($url, $storeId); + + if ($original['spec_type'] == 20) { + $original['spec_type'] = 10; + + $skuList = array_column($original['specData']['skuList'], null, "goods_sku_no"); + $goods_price = $skuList[$original['goods_sku_no']]['goods_price'] ?? 0; + + $original['goods_price'] = $goods_price; + $original['line_price'] = $goods_price; + $original['data_type'] = 1; + $original['link_other'] = $url; + unset($original['specData']); + + } + $form['imageStorage'] = 10; + + // 下载远程商品图片 + $original = $this->thirdPartyImages($original, $form['imageStorage'], $storeId); + } catch (\Throwable $e) { + // var_dump($e->getMessage()); + // exit; + tre($e->getTraceAsString()); + $this->errorLog[] = ['url' => trim($url), 'message' => $e->getMessage()]; + return false; + } + if ($original['goods_price'] == 0) { + return false; + } + + $original['cost_price'] = $form['cost_price_min']; + $data['link_other'] = $url; + //$data['goods_id'] = $form['goods_id']; + $data['goods_no'] = $original['goods_sku_no']; + $data['content'] = $original['content']; + + //重新计算利润和利润率 + $data['goods_price_min'] = $original['goods_price']; + $data['line_price_min'] = $original['goods_price']; + $data['line_price_min'] = $original['goods_price']; + $data['profit'] = $original['goods_price'] - $original['cost_price']; + $profit_rate = (float)$original['goods_price'] > 0 ? ($original['goods_price'] - $original['cost_price']) / $original['goods_price'] : 0.00; + $profit_rate = $profit_rate > 0.0001 ? bcmul((string)$profit_rate, "100", 2) : 0.00; + $data['profit_rate'] = $profit_rate; + // echo "
";
+        // print_r($data);
+        // exit();
+        // 事务处理:添加商品
+        $model = new GoodsModel();
+        $model->transaction(function () use ($form, $data, $original) {
+            // 添加商品
+            GoodsModel::where('goods_id', $form['goods_id'])->update($data);
+            // 新增商品与图片关联
+            GoodsImageModel1::updates((int)$form['goods_id'], $original['imagesIds']);
+            //更新sku信息
+            GoodsSkuModel::where('goods_id', $form['goods_id'])->update(['goods_price' => $original['goods_price'], 'goods_sku_no' => $original['goods_sku_no']]);
+        });
+           
+        return true;
+    }
     /**
      * 检查采集记录状态是否异常
      * @param int $recordId
@@ -318,6 +392,7 @@ class Collector extends BaseService
             'goods_type' => $form['goods_type'],
             'delivery_id' => $form['delivery_id'] ?? 0,
             'goods_name' => $original['goods_name'],
+            'goods_no' => $original['goods_sku_no'] ?? "",
             'data_type' => $original['data_type'],//数据类型
             'link' => $original['link'],//采集地址
             'spec_type' => $original['spec_type'],
@@ -391,6 +466,7 @@ class Collector extends BaseService
         $data = [
             'goods_type' => $form['goods_type'],
             'goods_name' => $original['goods_name'],
+            'goods_no' => $original['goods_sku_no'] ?? "",
             'data_type' => $original['data_type'],//数据类型
             'link' => $original['link'],//采集地址
             'spec_type' => $original['spec_type'],
diff --git a/app/job/service/goods/GoodsUpdateImport.php b/app/job/service/goods/GoodsUpdateImport.php
new file mode 100644
index 00000000..062ee655
--- /dev/null
+++ b/app/job/service/goods/GoodsUpdateImport.php
@@ -0,0 +1,221 @@
+
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\job\service\goods;
+
+use app\common\library\helper;
+use app\common\service\BaseService;
+use app\common\model\Spec as SpecModel;
+use app\common\model\Goods as GoodsModel;
+use app\common\model\Category as CategoryModel;
+use app\common\model\GoodsSku as GoodsSkuModel;
+use app\common\model\Delivery as DeliveryModel;
+use app\common\model\UploadFile as UploadFileModel;
+use app\common\model\GoodsImage as GoodsImageModel;
+use app\common\model\goods\Import as GoodsImportModel;
+use app\common\model\GoodsSpecRel as GoodsSpecRelModel;
+use app\common\model\goods\Service as GoodsServiceModel;
+use app\common\model\goods\ServiceRel as GoodsServiceRelModel;
+use app\common\model\GoodsCategoryRel as GoodsCategoryRelModel;
+use app\common\enum\goods\Status as GoodsStatusEnum;
+use app\common\enum\goods\GoodsType as GoodsTypeEnum;
+use app\common\enum\goods\SpecType as GoodsSpecTypeEnum;
+use app\common\enum\goods\ImportStatus as GoodsImportStatusEnum;
+use app\common\enum\goods\DeductStockType as DeductStockTypeEnum;
+use app\common\validate\goods\AdminImport as GoodsAdminImportValidate;
+use cores\exception\BaseException;
+use think\facade\Log;
+use app\common\model\Channel;
+use app\common\model\Region;
+/**
+ * 服务类:商品批量导入
+ * Class Import
+ * @package app\job\service\goods
+ */
+class GoodsUpdateImport extends BaseService
+{
+    /**
+     * 错误日志记录
+     * @var array
+     */
+    private array $errorLog = [];
+
+    /**
+     * 导入成功的数量
+     * @var int
+     */
+    private int $successCount = 0;
+
+   
+    /**
+     * 批量导入商品
+     * @param array $list
+     * @param int $recordId
+     * @param int $storeId
+     * @return bool
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function batch(array $list, int $recordId, int $storeId): bool
+    {
+    	$service = new \app\job\service\goods\Collector();
+        foreach ($list as $item) {
+
+        	$data = $this->createData($item, $storeId);
+            if (!isset($item[6]) || !$item[6]) {
+                continue;
+            }
+        	$service->updateGoods($item[6], $data, $storeId);
+            // 记录导入成功
+            $this->successCount++;
+        }
+        // 更新导入记录
+        $this->updateRecord($recordId, \count($list));
+        return true;
+    }
+
+    /**
+     * 更新导入记录
+     * @param int $recordId 商品导入记录ID
+     * @param int $currentCount 当前任务导入的商品总量
+     * @return void
+     */
+    private function updateRecord(int $recordId, int $currentCount)
+    {
+        // 获取导入记录
+        $model = GoodsImportModel::detail($recordId);
+        // 计算导入失败的数量
+        $failCount = $currentCount - $this->successCount;
+        // 更新导入记录
+        $model->save([
+            'success_count' => $model['success_count'] + $this->successCount,
+            'fail_count' => $model['fail_count'] + $failCount,
+            'fail_log' => array_merge($model['fail_log'], $this->errorLog),
+        ]);
+        // 判断是否为最后一次队列
+        if ($model['total_count'] <= ($model['success_count'] + $model['fail_count'])) {
+            $model->save(['end_time' => \time(), 'status' => GoodsImportStatusEnum::COMPLETED]);
+        }
+    }
+
+   
+
+    /**
+     * 生成商品数据(用于写入数据库)
+     * @param array $original
+     * @param int $storeId
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function createData(array $original, int $storeId): array
+    {
+        
+        // 整理商品数据
+        $data = [
+            'goods_id' => $original[0] ?? 0,
+            'goods_name' => $original[1] ?? "",
+            'cmmdty_model' => $original[2] ?? "",
+            'link' => $original[3] ?? "",
+            'cost_price_min' => $original[4] ?? "",
+            'stock_total' => $original[5] ?? "",
+            'link_other' => $original[6] ?? "",
+            'goods_price_min' => $original[7] ?? "",
+           
+        ];
+        return $data;
+    }
+
+    /**
+     * ID集字符串转换成数组
+     * @param string $value
+     * @return array|false|string[]
+     */
+    private function ids2array(string $value)
+    {
+        return !empty($value) ? \explode(',', $value) : [];
+    }
+
+    /**
+     * 创建标准的商品SKU数据
+     * @param array $originalSkuList
+     * @param array $specList
+     * @return array
+     */
+    private function createSkuList(array $originalSkuList, array $specList): array
+    {
+        $data = [];
+        foreach ($originalSkuList as $item) {
+            // 设置skuKeys数据
+            foreach ($item['specNames'] as $spec) {
+                $specGroup = helper::arraySearch($specList, 'spec_name', $spec[0]);
+                $specValue = helper::arraySearch($specGroup['valueList'], 'spec_value', $spec[1]);
+                $item['skuKeys'][] = [
+                    'groupKey' => $specGroup['key'],
+                    'valueKey' => $specValue['key']
+                ];
+            }
+            // 整理SKU数据
+            $data[] = [
+                'image_id' => 0,
+                'goods_price' => $item[$this->mapSku['goods_price']],
+                'line_price' => $item[$this->mapSku['line_price']],
+                'stock_num' => $item[$this->mapSku['stock_num']],
+                'goods_weight' => $item[$this->mapSku['goods_weight']],
+                'goods_sku_no' => $item[$this->mapSku['goods_sku_no']],
+                'skuKeys' => $item['skuKeys'],
+            ];
+        }
+        return $data;
+    }
+
+    /**
+     * 创建标准的商品规格数据(树状)
+     * @param array $originalSkuList
+     * @return array
+     */
+    private function createSpecList(array &$originalSkuList): array
+    {
+        // 生成规格数据
+        $data = [];
+        foreach ($originalSkuList as &$item) {
+            $tempArr1 = \explode(',', $item['H']);  // ['颜色:白色', '尺码:小']
+            foreach ($tempArr1 as $val) {
+                $tempArr2 = \explode(':', $val);  // ['颜色','白色']
+                $data[$tempArr2[0]][$tempArr2[1]] = 1;
+                $item['specNames'][] = $tempArr2;
+            }
+        }
+        // 整理为specList格式
+        $specList = [];
+        foreach ($data as $specName => $specValues) {
+            $groupKey = \count($specList);
+            $valueList = [];
+            foreach ($specValues as $specValue => $key) {
+                $valueList[] = [
+                    'key' => \count($valueList),
+                    'groupKey' => $groupKey,
+                    'spec_value' => $specValue,
+                ];
+            }
+            $specList[] = [
+                'key' => $groupKey,
+                'spec_name' => $specName,
+                'valueList' => $valueList,
+            ];
+        }
+        return $specList;
+    }
+}
\ No newline at end of file
diff --git a/app/store/model/goods/Import.php b/app/store/model/goods/Import.php
index b6e3a702..857295dc 100644
--- a/app/store/model/goods/Import.php
+++ b/app/store/model/goods/Import.php
@@ -16,6 +16,7 @@ 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\GoodsUpdateImport as GoodsUpdateImportJob;
 use app\common\enum\goods\ImportStatus as GoodsImportStatusEnum;
 use app\common\library\FileLocal;
 use app\common\library\helper;
@@ -93,6 +94,88 @@ class Import extends ImportModel
         $this->adminDispatchJob($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();
+         // 接收用户上传的模板文件
+        $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) {
+            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 商品列表