// +---------------------------------------------------------------------- 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 GoodsStoreImport 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, int $merchantId): bool { $service = new \app\job\service\goods\Collector(); foreach ($list as $item) { //sku存在了就不抓取了 $info = GoodsModel::where('goods_no', $item['C'])->where('store_id', $storeId)->where('merchant_id', $merchantId)->where('channel', $item['channel'])->where('is_delete',0)->find(); if ($info) { $this->successCount++; continue; } $data = $this->createData($item, $storeId); $ret = $service->single($item['D'], $data, $storeId); if ($ret == false) { continue; } // 记录导入成功 $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 */ public function createData(array $original, int $storeId): array { //批量导入销售区域处理 $region = []; $region_text = []; if ($original["L"]) { $sale_areas = $original["L"] ? explode("、", $original["L"]) : []; $cityNames = []; foreach ($sale_areas as $value) { if (strpos($value, "-") === false) { $province = Region::withoutGlobalScope()->where('name', $value)->where('level', 1)->find(); $citys = Region::withoutGlobalScope()->where('pid', $province['id'] ?? 0)->where('level', 2)->select()->toArray(); $citys = array_column($citys, "name"); } else { list($province, $city) = explode("-", $value); $citys = [$city]; } $cityNames = array_merge($cityNames, $citys); } $regions = Region::withoutGlobalScope()->whereIn('name', $cityNames)->where('level', 2)->select()->toArray(); $regionsnew = []; foreach ($regions as $key => $value) { $regionsnew[$value['pid']][] = $value; } // echo "
";
            // print_r($regionsnew);
            foreach ($regionsnew as $pid => $value) {
                $privince = Region::withoutGlobalScope()->where('id', $pid)->find();
                $region_text[$pid]['name'] = $privince['name'] ?? "";
                $citys = [];

                foreach ($value as $val) {
                    $region[] = $val['id'];
                    $citys[] = ['name' => $val['name']];
                }
                $region_text[$pid]['citys'] = $citys;
            }
            unset($pid);

            foreach ($region_text as $pid => &$item) {
                //如果传入的是整个省份
                if (count($item['citys']) == Region::withoutGlobalScope()->whereIn('pid', $pid)->count()) {
                    $item['citys'] = [];
                }
            }
        }
        // echo "
";
        //     print_r($region_text);
        //     print_r($region);
        //     exit();
        // 整理商品数据
        $data = [
            'cmmdty_model' => $original["A"],
            'channel' => $original["channel"] ?? "zy",
            'goods_source' => $original["B"],
            'goods_no' => $original["C"],
            'delivery_time' => $original["F"],
            'is_check' => $original["G"],
            'goods_price' => $original["H"],
            'cost_price' => $original["I"],
            'stock_num' => $original["J"],
            'remark' => $original["K"],
            'delivery_id' => $original["M"],
            //'link' => $original["D"],
            'categoryIds' => $this->ids2array($original["E"]),
            'imageStorage' => 20,//下载图片到本地
            'goods_type' => 10,//实物
            'goods_status' => 10,//上架
            'store_id' => $storeId,
            //'sale_areas' => $arr ? implode("、", $arr) : "",
            'region' => $region ? json_encode($region, JSON_UNESCAPED_UNICODE) : "",
            'region_text' => $region_text ? json_encode(array_values($region_text), JSON_UNESCAPED_UNICODE) : "",
            'import' => 1,//是否是导入采集
        ];
        // echo "
";
        // print_r($data);
        // exit();
        // 过滤不存在的ID集数据
        $data['categoryIds'] = CategoryModel::filterCategoryIds($data['categoryIds'], $storeId);

        //处理分类,设置最后一级一直往上查找
        $model = new \app\store\model\Goods();
        $data['categoryIds'] = $model->dealCategory($data['categoryIds']);

        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;
    }
}