Merge branch 'main' of git.njrzwl.cn:wangmingchuan/yanzong into main

wysf
ztt 1 year ago
commit 9a58e4e223
  1. 88
      app/admin/controller/Category.php
  2. 149
      app/admin/controller/Files.php
  3. 164
      app/admin/controller/Goods.php
  4. 80
      app/admin/controller/Goods1.php
  5. 86
      app/admin/controller/files/Group.php
  6. 71
      app/admin/controller/goods/Import.php
  7. 50
      app/api/service/Goods.php
  8. 1
      app/common/model/Category.php
  9. 58
      app/common/validate/goods/AdminImport.php
  10. 49
      app/job/controller/goods/AdminImport.php
  11. 318
      app/job/service/goods/AdminImport.php
  12. 38
      app/job/service/goods/Collector.php
  13. 2
      app/store/model/Category.php
  14. 65
      app/store/model/goods/Import.php

@ -0,0 +1,88 @@
<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\admin\controller;
use think\response\Json;
use app\store\model\Category as CategoryModel;
/**
* 商品分类
* Class Category
* @package app\store\controller\goods
*/
class Category extends Controller
{
/**
* 商品分类列表
* @return Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function list(): Json
{
$model = new CategoryModel;
$list = $model->getList(['store_id' =>0]);
return $this->renderSuccess(compact('list'));
}
/**
* 删除商品分类
* @param int $categoryId
* @return Json
*/
public function delete(int $categoryId): Json
{
// 分类详情
$model = CategoryModel::detail($categoryId);
if (!$model->remove()) {
return $this->renderError($model->getError() ?: '删除失败');
}
return $this->renderSuccess('删除成功');
}
/**
* 添加商品分类
* @return Json
*/
public function add(): Json
{
// 新增记录
$model = new CategoryModel;
$params = $this->postForm();
$params['store_id'] = 0;
if ($model->add($params)) {
return $this->renderSuccess('添加成功');
}
return $this->renderError($model->getError() ?: '添加失败');
}
/**
* 编辑商品分类
* @param int $categoryId
* @return Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function edit(int $categoryId): Json
{
// 分类详情
$model = CategoryModel::detail($categoryId, ['image']);
// 更新记录
if ($model->edit($this->postForm())) {
return $this->renderSuccess('更新成功');
}
return $this->renderError($model->getError() ?: '更新失败');
}
}

@ -0,0 +1,149 @@
<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\admin\controller;
use think\response\Json;
use cores\library\Version;
use app\store\model\UploadFile as UploadFileModel;
/**
* 文件库管理
* Class Files
* @package app\store\controller
*/
class Files extends Controller
{
/**
* 文件列表
* @return Json
* @throws \cores\exception\BaseException
* @throws \think\db\exception\DbException
*/
public function list(): Json
{
$this->env();
$model = new UploadFileModel;
$list = $model->getList($this->request->param());
return $this->renderSuccess(compact('list'));
}
/**
* 编辑文件
* @param int $fileId
* @return Json
*/
public function edit(int $fileId): Json
{
// 文件详情
$model = UploadFileModel::detail($fileId);
// 更新记录
if ($model->edit($this->postForm())) {
return $this->renderSuccess('更新成功');
}
return $this->renderError($model->getError() ?: '更新失败');
}
/**
* 删除文件(批量)
* @param array $fileIds 文件id集
* @return Json
* @throws \think\Exception
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function delete(array $fileIds): Json
{
$model = new UploadFileModel;
if (!$model->setDelete($fileIds)) {
return $this->renderError($model->getError() ?: '操作失败');
}
return $this->renderSuccess('操作成功');
}
/**
* 移动文件到指定分组(批量)
* @param int $groupId
* @param array $fileIds
* @return Json
*/
public function moveGroup(int $groupId, array $fileIds): Json
{
$model = new UploadFileModel;
if (!$model->moveGroup($groupId, $fileIds)) {
return $this->renderError($model->getError() ?: '操作失败');
}
return $this->renderSuccess('操作成功');
}
/**
* 临时方法:环境检测并删除废弃的库文件
* 文件:vendor/topthink/framework/src/think/Filesystem.php
* 文件:vendor/topthink/framework/src/think/facade/Filesystem.php
* 文件:vendor/topthink/framework/tests/FilesystemTest.php
* 目录:vendor/topthink/framework/src/think/filesystem
* @return void
* @throws \cores\exception\BaseException
*/
private function env()
{
// 判断当前版本小于2.2.7则不执行
if (Version::compare(Version::getVersion(), '2.2.7') === -1) {
return;
}
// 要删除的文件列表
$files = [
'vendor/topthink/framework/src/think/Filesystem.php',
'vendor/topthink/framework/src/think/facade/Filesystem.php',
'vendor/topthink/framework/tests/FilesystemTest.php'
];
foreach ($files as $file) {
$filePath = root_path() . $file;
file_exists($filePath) && unlink($filePath);
}
// 要删除的目录列表
$folders = ['vendor/topthink/framework/src/think/filesystem/'];
foreach ($folders as $folder) {
$folderPath = root_path() . $folder;
is_dir($folderPath) && $this->deleteFolder($folderPath);
}
}
/**
* 临时方法:递归删除指定目录下所有文件
* @param $path
* @return void
*/
private function deleteFolder($path): void
{
if (!is_dir($path)) {
return;
}
// 扫描一个文件夹内的所有文件夹和文件
foreach (scandir($path) as $val) {
// 排除目录中的.和..
if (!in_array($val, ['.', '..', '.gitignore'])) {
// 如果是目录则递归子目录,继续操作
if (is_dir($path . $val)) {
// 子目录中操作删除文件夹和文件
$this->deleteFolder($path . $val . '/');
// 目录清空后删除空文件夹
rmdir($path . $val . '/');
} else {
// 如果是文件直接删除
unlink($path . $val);
}
}
}
}
}

@ -12,69 +12,141 @@ declare (strict_types=1);
namespace app\admin\controller;
use think\facade\Db;
use think\db\exception\DbException;
use think\response\Json;
use cores\exception\BaseException;
use app\store\model\Goods as GoodsModel;
/**
* 商城管理
* Class Store
* @package app\admin\controller
* 商品管理控制器
* Class Goods
* @package app\store\controller
*/
class Goods extends Controller
{
/**
* 强制验证当前访问的控制器方法method
* @var array
* 商品列表
* @return Json
* @throws DbException
*/
protected array $methodRules = [
'index' => 'GET',
'recycle' => 'GET',
'add' => 'POST',
'move' => 'POST',
'delete' => 'POST',
];
public function list(): Json
{
// 获取列表记录
$model = new GoodsModel;
$list= $model->getList($this->request->param());
return $this->renderSuccess(compact('list'));
}
/**
* 商城列表
* 根据商品ID集获取列表记录
* @param array $goodsIds
* @return Json
* @throws \think\db\exception\DbException
*/
public function index(): Json
public function listByIds(array $goodsIds): Json
{
// 获取列表记录
$model = new GoodsModel;
$list = $model->getListByIds($goodsIds);
return $this->renderSuccess(compact('list'));
}
$params = $this->request->param();
$sort = $this->request->param('sort', 'id');
$order = $this->request->param('order', 'desc');
$where = [];
if (isset($params['channel']) && $params['channel']) {
$where[] = ['channel', '=', $params['channel']];
}
if (isset($params['min_profit_rate']) && $params['min_profit_rate']) {
$where[] = ['profit_rate', '>=', $params['min_profit_rate']];
}
if (isset($params['max_profit_rate']) && $params['max_profit_rate']) {
$where[] = ['profit_rate', '<=', $params['max_profit_rate']];
}
if (isset($params['min_price']) && $params['min_price']) {
$where[] = ['net_price', '>=', $params['min_price']];
}
if (isset($params['max_price']) && $params['max_price']) {
$where[] = ['net_price', '<=', $params['max_price']];
/**
* 商品详情(详细信息)
* @param int $goodsId
* @return Json
* @throws BaseException
* @throws \think\db\exception\DataNotFoundException
* @throws DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function detail(int $goodsId): Json
{
// 获取商品详情
$model = new GoodsModel;
$goodsInfo = $model->getDetail($goodsId);
return $this->renderSuccess(compact('goodsInfo'));
}
/**
* 商品详情(基础信息)
* @param int $goodsId
* @return Json
* @throws BaseException
* @throws \think\db\exception\DataNotFoundException
* @throws DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function basic(int $goodsId): Json
{
// 获取商品详情
$model = new GoodsModel;
$detail = $model->getBasic($goodsId);
return $this->renderSuccess(compact('detail'));
}
/**
* 添加商品
* @return Json
* @throws BaseException
* @throws \think\db\exception\DataNotFoundException
* @throws DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function add(): Json
{
$model = new GoodsModel;
if ($model->add($this->postForm())) {
return $this->renderSuccess('添加成功');
}
if (!empty($params['catalog_name'])) {
$where[] = ['catalog_name', 'like', "%{$params["catalog_name"]}%"];
return $this->renderError($model->getError() ?: '添加失败');
}
/**
* 商品编辑
* @param int $goodsId
* @return Json
* @throws BaseException
* @throws \think\db\exception\DataNotFoundException
* @throws DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function edit(int $goodsId): Json
{
// 商品详情
$model = GoodsModel::detail($goodsId);
// 更新记录
if ($model->edit($this->postForm())) {
return $this->renderSuccess('更新成功');
}
$list = Db::connect("dataCenterMysql")->table('goods_sku')
->where($where)
->order($sort, $order)
->paginate($this->request->param('per_page', 15))->each(function ($item, $key) {
$item['create_time'] = date("Y-m-d H:i:s", $item['create_time']);
$item['update_time'] = date("Y-m-d H:i:s", $item['update_time']);
$item['channel'] = config('app.platformList')[$item['channel']] ?? "";
return $item;
});
return $this->renderSuccess(compact('list'));
return $this->renderError($model->getError() ?: '更新失败');
}
/**
* 修改商品状态(上下架)
* @param array $goodsIds 商品id集
* @param bool $state 为true表示上架
* @return Json
*/
public function state(array $goodsIds, bool $state): Json
{
$model = new GoodsModel;
if (!$model->setStatus($goodsIds, $state)) {
return $this->renderError($model->getError() ?: '操作失败');
}
return $this->renderSuccess('操作成功');
}
/**
* 删除商品
* @param array $goodsIds
* @return Json
*/
public function delete(array $goodsIds): Json
{
$model = new GoodsModel;
if (!$model->setDelete($goodsIds)) {
return $this->renderError($model->getError() ?: '删除失败');
}
return $this->renderSuccess('删除成功');
}
}

@ -0,0 +1,80 @@
<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\admin\controller;
use think\facade\Db;
use think\response\Json;
/**
* 商城管理
* Class Store
* @package app\admin\controller
*/
class Goods extends Controller
{
/**
* 强制验证当前访问的控制器方法method
* @var array
*/
protected array $methodRules = [
'index' => 'GET',
'recycle' => 'GET',
'add' => 'POST',
'move' => 'POST',
'delete' => 'POST',
];
/**
* 商城列表
* @return Json
* @throws \think\db\exception\DbException
*/
public function index(): Json
{
$params = $this->request->param();
$sort = $this->request->param('sort', 'id');
$order = $this->request->param('order', 'desc');
$where = [];
if (isset($params['channel']) && $params['channel']) {
$where[] = ['channel', '=', $params['channel']];
}
if (isset($params['min_profit_rate']) && $params['min_profit_rate']) {
$where[] = ['profit_rate', '>=', $params['min_profit_rate']];
}
if (isset($params['max_profit_rate']) && $params['max_profit_rate']) {
$where[] = ['profit_rate', '<=', $params['max_profit_rate']];
}
if (isset($params['min_price']) && $params['min_price']) {
$where[] = ['net_price', '>=', $params['min_price']];
}
if (isset($params['max_price']) && $params['max_price']) {
$where[] = ['net_price', '<=', $params['max_price']];
}
if (!empty($params['catalog_name'])) {
$where[] = ['catalog_name', 'like', "%{$params["catalog_name"]}%"];
}
$list = Db::connect("dataCenterMysql")->table('goods_sku')
->where($where)
->order($sort, $order)
->paginate($this->request->param('per_page', 15))->each(function ($item, $key) {
$item['create_time'] = date("Y-m-d H:i:s", $item['create_time']);
$item['update_time'] = date("Y-m-d H:i:s", $item['update_time']);
$item['channel'] = config('app.platformList')[$item['channel']] ?? "";
return $item;
});
return $this->renderSuccess(compact('list'));
}
}

@ -0,0 +1,86 @@
<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\admin\controller\files;
use think\response\Json;
use app\admin\controller\Controller;
use app\store\model\UploadGroup as GroupModel;
/**
* 文件分组
* Class Group
* @package app\store\controller\content
*/
class Group extends Controller
{
/**
* 文件分组列表
* @return Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function list(): Json
{
$model = new GroupModel;
$list = $model->getList();
return $this->renderSuccess(compact('list'));
}
/**
* 添加文件分组
* @return Json
*/
public function add(): Json
{
// 新增记录
$model = new GroupModel;
if ($model->add($this->postForm())) {
return $this->renderSuccess('添加成功');
}
return $this->renderError($model->getError() ?: '添加失败');
}
/**
* 编辑文件分组
* @param int $groupId
* @return Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function edit(int $groupId): Json
{
// 分组详情
$model = GroupModel::detail($groupId);
// 更新记录
if ($model->edit($this->postForm())) {
return $this->renderSuccess('更新成功');
}
return $this->renderError($model->getError() ?: '更新失败');
}
/**
* 删除文件分组
* @param int $groupId
* @return Json
*/
public function delete(int $groupId): Json
{
$model = GroupModel::detail($groupId);
if (!$model->remove()) {
return $this->renderError($model->getError() ?: '删除失败');
}
return $this->renderSuccess('删除成功');
}
}

@ -0,0 +1,71 @@
<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\admin\controller\goods;
use think\response\Json;
use app\admin\controller\Controller;
use app\store\model\goods\Import as ImportModel;
use cores\exception\BaseException;
/**
* 商品批量导入管理
* Class Import
* @package app\store\controller\goods
*/
class Import extends Controller
{
/**
* 获取列表记录
* @return Json
* @throws \think\db\exception\DbException
*/
public function list(): Json
{
$params = $this->request->param();
$params['store_id'] = 0;
$model = new ImportModel;
$list = $model->getList($params);
return $this->renderSuccess(compact('list'));
}
/**
* 执行批量导入
* @return Json
* @throws BaseException
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
*/
public function batch(): Json
{
// 新增记录
$model = new ImportModel;
if ($model->adminBatch($this->postData())) {
return $this->renderSuccess('已添加到导入任务中,请在历史导入记录中查看结果');
}
return $this->renderError($model->getError() ?: '操作失败');
}
/**
* 删除记录
* @param int $id
* @return Json
*/
public function delete(int $id): Json
{
$model = ImportModel::detail($id);
if (!$model->setDelete()) {
return $this->renderError($model->getError() ?: '删除失败');
}
return $this->renderSuccess('删除成功');
}
}

@ -137,8 +137,10 @@ class Goods extends GoodsService
return [];
}
$list = $goodsList->toArray();
$list['data'] = $this->formatGoodsList($goodsList);
// echo "<pre>";
// print_r($list);
// exit()
$list['data'] = $this->newFormatGoodsList($list['data']);
return $list;
}
@ -177,7 +179,7 @@ class Goods extends GoodsService
}
$list = $goodsList->toArray();
$list['data'] = $this->formatGoodsList($goodsList);
$list['data'] = $this->newFormatGoodsList($list['data']);
return $list;
}
@ -203,7 +205,7 @@ class Goods extends GoodsService
], 3);
$list2 = $goodsList->toArray()['data'];
$v['goods_list'] = $this->formatGoodsList($list2);
$v['goods_list'] = $this->newFormatGoodsList($list2);
}
return $list;
@ -317,6 +319,46 @@ class Goods extends GoodsService
}
return $price;
}
/**
* 格式化商品列表
* @param $goodsList
* @return array
*/
private function newFormatGoodsList($goodsList): array
{
$data = [];
foreach ($goodsList as $goods) {
$temp = [
'goods_id' => $goods['goods_id'],
'goods_name' => $goods['goods_name'],
'selling_point' => $goods['selling_point'],
'goods_image' => $goods['goods_image'],
'goods_price_min' => $goods['goods_price_min'],//商品价格
'goods_price_max' => $goods['goods_price_max'],
'line_price_min' => $goods['goods_price_min'],//划线价格等于市场价
'line_price_max' => $goods['line_price_max'],
'goods_sales' => $goods['goods_sales'],
'remaizhishu' => $goods['remaizhishu'],
];
if (UserService::isLogin()) {
$catService = new \app\store\model\GoodsCategoryRel();
$catIds = $catService->where(['goods_id' => $goods['goods_id']])->column('category_id');
//价格判断
if (UserService::isstore()) {
$temp['goods_price_min'] = $goods['cost_price_min'];
} elseif (UserService::isPlusMember()) {
$temp['goods_price_min'] = \app\common\model\PriceSet::membershipPrice($goods['goods_price_min'], $goods['cost_price_min'], $catIds);
} elseif (UserService::isDealerMember()) {
$priceArr = \app\common\model\PriceSet::distributionPrice($goods['goods_price_min'], $goods['cost_price_min'], $catIds);
$temp['goods_price_min'] = $priceArr['goods_price_min_dealer'];
}
}
$data[] = $temp;
}
return $data;
}
/**
* 格式化商品列表
* @param $goodsList

@ -94,6 +94,7 @@ class Category extends BaseModel
if (isset($param['is_in_store']) && $param['is_in_store'] != "") {
$filter[] = ['is_in_store', '=', $params['is_in_store'] ?? 0];
}
isset($params['store_id']) && $params['store_id'] > -1 && $filter[] = ['store_id', '=', (int)$params['store_id']];
// 查询列表数据
return $this->with(['image'])

@ -0,0 +1,58 @@
<?php
namespace app\common\validate\goods;
use think\Validate;
/**
* 验证类:商品导入
* Class Import
* @package app\common\service\goods
*/
class AdminImport extends Validate
{
// 验证场景
protected $scene = [
'goods' => ['goods_name', 'delivery_id', 'imagesIds', 'categoryIds', 'serviceIds'],
'skuInfo' => ['goods_price', 'stock_num', 'goods_weight'],
];
/**
* 验证规则
* @var array
*/
protected $rule = [
'goods_name|商品名称' => ['require'],
'delivery_id|运费模板ID' => ['require', 'number'],
'imagesIds|商品图片ID集' => ['require', 'array', 'validateIds'],
'categoryIds|商品分类ID集' => ['require', 'array', 'validateIds'],
'serviceIds|服务与承诺' => ['array', 'validateIds'],
'goods_price|商品价格' => ['require', 'float', '>=:0.01'],
'stock_num|库存数量' => ['require', 'integer', '>=:0'],
'goods_weight|商品重量' => ['require', 'float', '>=:0'],
];
/**
* 错误信息
* @var string[]
*/
protected $message = [
// 'delivery_id.number' => '运费模板ID必须是数字',
];
/**
* 验证ID集格式是否正确
* @param $value
* @return bool
*/
protected function validateIds($value): bool
{
foreach ($value as $val) {
if (!is_numeric($val)) {
return false;
}
}
return true;
}
}

@ -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\AdminImport as GoodsAdminImportService;
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 AdminImport 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 GoodsAdminImportService;
return $service->batch($data['list'], $data['recordId'], $data['storeId']);
}
}

@ -0,0 +1,318 @@
<?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;
/**
* 服务类:商品批量导入
* Class Import
* @package app\job\service\goods
*/
class AdminImport extends BaseService
{
/**
* 错误日志记录
* @var array
*/
private array $errorLog = [];
/**
* 导入成功的数量
* @var int
*/
private int $successCount = 0;
/**
* 商品主信息字段映射
* @var string[]
*/
private array $mapGoods = [
'goods_name' => 'B',
'goods_no' => 'C',
'categoryIds' => 'D',
'imagesIds' => 'E',
'delivery_id' => 'F',
'status' => 'G',
'sort' => 'N',
'deduct_stock_type' => 'O',
'sales_initial' => 'P',
'serviceIds' => 'Q',
'is_points_gift' => 'R',
'is_points_discount' => 'S',
'is_enable_grade' => 'T',
];
/**
* 商品SKU字段映射
* @var string[]
*/
private array $mapSku = [
'specText' => 'H',
'goods_price' => 'I',
'line_price' => 'J',
'stock_num' => 'K',
'goods_weight' => 'L',
'goods_sku_no' => 'M',
];
/**
* 批量导入商品
* @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);
$service->single($item['D'], $data, $storeId);
// 生成商品数据(用于写入数据库)
// $data = $this->createData($item, $storeId);
// Log::record("data".json_encode($data));
// // 数据验证
// if (!$this->validateGoodsData($data, $storeId)) {
// $this->errorLog[] = ['goodsSn' => $item['A'], 'message' => $this->getError()];
// continue;
// }
// // 事务处理:添加商品
// $model = new GoodsModel();
// $model->transaction(function () use ($model, $data, $storeId) {
// // 添加商品
// $model->save($data);
// // 新增商品与分类关联
// GoodsCategoryRelModel::increased((int)$model['goods_id'], $data['categoryIds'], $storeId);
// // 新增商品与图片关联
// GoodsImageModel::increased((int)$model['goods_id'], $data['imagesIds'], $storeId);
// // 新增商品与规格关联
// GoodsSpecRelModel::increased((int)$model['goods_id'], $data['newSpecList'], $storeId);
// // 新增商品sku信息
// GoodsSkuModel::add((int)$model['goods_id'], $data['spec_type'], $data['newSkuList'], $storeId);
// // 新增服务与承诺关联
// GoodsServiceRelModel::increased((int)$model['goods_id'], $data['serviceIds'], $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 $data
* @param int $storeId
* @return bool
*/
private function validateGoodsData(array $data, int $storeId): bool
{
// 验证商品信息:商品名称、分类ID集、图片ID集、运费模板ID
$validate = new GoodsAdminImportValidate;
Log::record($data);
if (!$validate->scene('goods')->check($data)) {
$this->setError($validate->getError());
return false;
}
// 验证SKU信息:商品价格、库存数量、商品重量
$skuList = $data['spec_type'] == GoodsSpecTypeEnum::MULTI ? $data['newSkuList'] : [$data['newSkuList']];
foreach ($skuList as $item) {
$validate = new GoodsAdminImportValidate;
if (!$validate->scene('skuInfo')->check($item)) {
$this->setError($validate->getError());
return false;
}
}
// 验证运费模板ID是否存在
if (!DeliveryModel::checkDeliveryId((int)$data['delivery_id'], $storeId)) {
$this->setError('运费模板ID不存在');
return false;
}
if ($data['spec_type'] == GoodsSpecTypeEnum::MULTI) {
// 判断用户填写的SKU数量是否正确
$shouldSkuTotal = SpecModel::calcSkuListTotal($data['specData']['specList']);
$originalSkuTotal = \count($data['specData']['skuList']);
if ($shouldSkuTotal !== $originalSkuTotal) {
$this->setError('商品SKU数量不正确');
return false;
}
// 判断商品SKU是否存在重复规格
$goodsSkuIdArr = helper::getArrayColumn($data['newSkuList'], 'goods_sku_id');
if (count($data['newSkuList']) != count(array_unique($goodsSkuIdArr))) {
$this->setError('商品SKU存在重复的规格值');
return false;
}
}
return true;
}
/**
* 生成商品数据(用于写入数据库)
* @param array $original
* @param int $storeId
* @return array
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
private function createData(array $original, int $storeId): array
{
// 整理商品数据
$data = [
'cmmdty_model' => $original["A"],
'channel' => "jd",
'goods_no' => $original["C"],
'categoryIds' => $this->ids2array($original["E"]),
'imageStorage' => 10,//下载图片到本地
'goods_type' => 10,//实物
'goods_status' => 10,//上架
'store_id' => $storeId,
];
// 过滤不存在的ID集数据
//$data['categoryIds'] = CategoryModel::filterCategoryIds($data['categoryIds'], $storeId);
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;
}
}

@ -116,7 +116,40 @@ class Collector extends BaseService
$this->updateRecord($recordId, \count($urls));
return true;
}
public function single(string $url, array $form, int $storeId): bool
{
try {
// 采集第三方商品数据
$original = $this->collector($url, $storeId);
// 下载远程商品图片
$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;
}
// 生成商品数据(用于写入数据库)
$data = $this->createData($original, $form, $storeId);
// 事务处理:添加商品
$model = new GoodsModel();
$model->transaction(function () use ($model, $data, $storeId) {
// 添加商品
$model->save($data);
// 新增商品与分类关联
GoodsCategoryRelModel::increased((int)$model['goods_id'], $data['categoryIds'], $storeId);
// 新增商品与图片关联
GoodsImageModel::increased((int)$model['goods_id'], $data['imagesIds'], $storeId);
// 新增商品与规格关联
GoodsSpecRelModel::increased((int)$model['goods_id'], $data['newSpecList'], $storeId);
// 新增商品sku信息
GoodsSkuModel::add((int)$model['goods_id'], $data['spec_type'], $data['newSkuList'], $storeId);
});
return true;
}
/**
* 检查采集记录状态是否异常
* @param int $recordId
@ -251,6 +284,9 @@ class Collector extends BaseService
'newSkuList' => [],
'store_id' => $storeId,
];
if (isset($form['channel']) {
$data['channel'] = $form['channel'];
}
// 整理商品的价格和库存总量
if ($data['spec_type'] === GoodsSpecTypeEnum::MULTI) {
$data['stock_total'] = GoodsSkuModel::getStockTotal($original['specData']['skuList']);

@ -28,7 +28,7 @@ class Category extends CategoryModel
*/
public function add($data): bool
{
$data['store_id'] = self::$storeId;
$data['store_id'] = $data['store_id'] ?? self::$storeId;
return $this->save($data);
}

@ -15,7 +15,7 @@ namespace app\store\model\goods;
use think\Paginator;
use think\db\exception\DbException;
use PhpOffice\PhpSpreadsheet\Exception;
use app\job\controller\goods\Import as GoodsImportJob;
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;
@ -56,9 +56,68 @@ class Import extends ImportModel
// 检索查询条件
$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 = 0;
// $service = new \app\job\service\goods\Collector();
// foreach ($execlData as $item) {
// $data = [
// 'cmmdty_model' => $item["A"],
// 'channel' => "jd",
// 'goods_no' => $item["C"],
// 'categoryIds' => explode(",", $item["E"]),
// 'imageStorage' => 10,//下载图片到本地
// 'goods_type' => 10,//实物
// 'goods_status' => 10,//上架
// 'store_id' => self::$storeId,
// ];
// $service->single($item['D'], $data, self::$storeId);
// }
// 新增商品导入记录
$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
@ -121,7 +180,7 @@ class Import extends ImportModel
]);
return (int)$this['id'];
}
/**
* 格式化导入的商品列表数据
* @param array $execlData

Loading…
Cancel
Save