lqmac 8 months ago
parent cb47195bdf
commit aed8414ea1
  1. 11
      app/admin/controller/Goods.php
  2. 2
      app/common/model/Goods.php
  3. 49
      app/job/controller/goods/GoodsUpdateImport.php
  4. 76
      app/job/service/goods/Collector.php
  5. 221
      app/job/service/goods/GoodsUpdateImport.php
  6. 83
      app/store/model/goods/Import.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() ?: '操作失败');
}

@ -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);

@ -0,0 +1,49 @@
<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
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']);
}
}

@ -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 "<pre>";
// 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'],

@ -0,0 +1,221 @@
<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
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;
}
}

@ -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 "<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 商品列表

Loading…
Cancel
Save