diff --git a/app/admin/controller/Category.php b/app/admin/controller/Category.php new file mode 100644 index 00000000..f652e730 --- /dev/null +++ b/app/admin/controller/Category.php @@ -0,0 +1,88 @@ + +// +---------------------------------------------------------------------- +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() ?: '更新失败'); + } +} diff --git a/app/admin/controller/Files.php b/app/admin/controller/Files.php new file mode 100644 index 00000000..03653749 --- /dev/null +++ b/app/admin/controller/Files.php @@ -0,0 +1,149 @@ + +// +---------------------------------------------------------------------- +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); + } + } + } + } +} diff --git a/app/admin/controller/Goods.php b/app/admin/controller/Goods.php index 47bd25e3..2b05a80c 100644 --- a/app/admin/controller/Goods.php +++ b/app/admin/controller/Goods.php @@ -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('删除成功'); + } } diff --git a/app/admin/controller/Goods1.php b/app/admin/controller/Goods1.php new file mode 100644 index 00000000..47bd25e3 --- /dev/null +++ b/app/admin/controller/Goods1.php @@ -0,0 +1,80 @@ + +// +---------------------------------------------------------------------- +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')); + } + + +} diff --git a/app/admin/controller/files/Group.php b/app/admin/controller/files/Group.php new file mode 100644 index 00000000..5384529e --- /dev/null +++ b/app/admin/controller/files/Group.php @@ -0,0 +1,86 @@ + +// +---------------------------------------------------------------------- +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('删除成功'); + } +} diff --git a/app/admin/controller/goods/Import.php b/app/admin/controller/goods/Import.php new file mode 100644 index 00000000..c338b42f --- /dev/null +++ b/app/admin/controller/goods/Import.php @@ -0,0 +1,71 @@ + +// +---------------------------------------------------------------------- +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('删除成功'); + } +} diff --git a/app/api/service/Goods.php b/app/api/service/Goods.php index 00753a70..8b5958cb 100644 --- a/app/api/service/Goods.php +++ b/app/api/service/Goods.php @@ -137,8 +137,10 @@ class Goods extends GoodsService return []; } $list = $goodsList->toArray(); - - $list['data'] = $this->formatGoodsList($goodsList); + // echo "
";
+        // 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
diff --git a/app/common/model/Category.php b/app/common/model/Category.php
index cefeeff2..bd49e997 100644
--- a/app/common/model/Category.php
+++ b/app/common/model/Category.php
@@ -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'])
diff --git a/app/common/validate/goods/AdminImport.php b/app/common/validate/goods/AdminImport.php
new file mode 100644
index 00000000..2413d4b3
--- /dev/null
+++ b/app/common/validate/goods/AdminImport.php
@@ -0,0 +1,58 @@
+ ['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;
+    }
+}
\ No newline at end of file
diff --git a/app/job/controller/goods/AdminImport.php b/app/job/controller/goods/AdminImport.php
new file mode 100644
index 00000000..044d0e67
--- /dev/null
+++ b/app/job/controller/goods/AdminImport.php
@@ -0,0 +1,49 @@
+
+// +----------------------------------------------------------------------
+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']);
+    }
+}
\ No newline at end of file
diff --git a/app/job/service/goods/AdminImport.php b/app/job/service/goods/AdminImport.php
new file mode 100644
index 00000000..5069bb34
--- /dev/null
+++ b/app/job/service/goods/AdminImport.php
@@ -0,0 +1,318 @@
+
+// +----------------------------------------------------------------------
+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;
+    }
+}
\ No newline at end of file
diff --git a/app/job/service/goods/Collector.php b/app/job/service/goods/Collector.php
index 757d84b1..2ccfe71b 100644
--- a/app/job/service/goods/Collector.php
+++ b/app/job/service/goods/Collector.php
@@ -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']);
diff --git a/app/store/model/Category.php b/app/store/model/Category.php
index 99ab22a2..908a4997 100644
--- a/app/store/model/Category.php
+++ b/app/store/model/Category.php
@@ -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);
     }
 
diff --git a/app/store/model/goods/Import.php b/app/store/model/goods/Import.php
index e855c62d..ef553737 100644
--- a/app/store/model/goods/Import.php
+++ b/app/store/model/goods/Import.php
@@ -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